|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T d
Length: 93685 (0x16df5)
Types: TextFile
Names: »dvitovdu.mod«
└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
└─⟦this⟧ »./DVIware/crt-viewers/others/dvitovdu/src/dvitovdu.mod«
└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
└─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z«
└─⟦ca79c7339⟧
└─⟦this⟧ »DVIware/crt-viewers/others/dvitovdu/src/dvitovdu.mod«
MODULE dvitovdu;
(* Author: Andrew Trevorrow
Implementation: Modula-2 under VAX/UNIX 4.2 BSD
Date Started: June, 1986 (based on version 1.5 under VAX/VMS 4.2)
Description:
DVItoVDU allows pages from a DVI file produced by TeX82 to be viewed on a
variety of VDU screens.
See the DVItoVDU USER GUIDE for details on the user interface.
See the DVItoVDU SYSTEM GUIDE if you wish to install or modify the program.
Notes:
- Debugging code is bracketed by (* DEBUG *) ... (* GUBED *).
Most of this code will be commented out in the final working version.
- System-dependent code is indicated by (* SYSDEP ... *).
- Uncertain, unfinished or kludgey code is indicated by the string "???".
- Procedures are defined in a top-down manner. That is, each procedure is
usually defined as soon as possible after its first use.
- The above notes are also true for all the imported modules used by DVItoVDU.
*)
(* SYSDEP: Since Modula-2 avoids the problem of system dependence by simply
not providing any input/output routines etc., the following
importations are highly VAX/UNIX dependent.
*)
FROM io IMPORT
File, Open, Close, Readc;
(* The above module is part of the VAX/UNIX Modula-2 system library.
The following modules are kept with the file you are now reading.
See the .def files for details on how the imported identifiers should be
used; implementation details can be found in the corresponding .mod files.
*)
(* DVItoVDU uses the ScreenIO routines to do all terminal i/o. *)
FROM screenio IMPORT
Read, ReadString, BusyRead,
Write, WriteString, WriteInt, WriteCard, WriteLn, WriteBuffer,
RestoreTerminal;
(* InitSysInterface carries out the task of reading the command line
and extracting the DVI file name, along with any command options.
*)
FROM sysinterface IMPORT
InitSysInterface,
stringvalue,
resolution, mag, paperwd, paperht,
fontdir, dummyfont, helpname, vdu, DVIname;
(* DVItoVDU uses the routines and data structures defined in DVIReader to move
about randomly in the DVI file and to interpret pages.
*)
FROM dvireader IMPORT
(* CONST *)
ruletablesize, chartablesize, maxfontspec, maxTeXchar,
(* TYPE *)
ruleinfo, ruleinfoptr,
fontstring, fontinfo, fontinfoptr,
charinfo, charinfoptr, pixeltable, pixeltableptr,
TeXcounters, TeXpageinfo,
DVIerrorcodes, GetByteFunction,
(* VAR *)
DVImag, totalpages, totalfonts,
currDVIpage, currTeXpage,
rulelist, ruletail, totalrules, fontlist, currfont,
minhp, minvp, maxhp, maxvp, pageempty,
DVIErrorRoutine, SpecialRoutine, PixelTableRoutine,
(* PROCEDURE *)
OpenDVIFile, SetConversionFactor,
MoveToNextPage, MoveToDVIPage, MoveToTeXPage,
PixelRound, InterpretPage, SortFonts,
CloseDVIFile;
(* DVItoVDU needs to move about randomly in PXL files when getting information
on the character widths and glyph shapes in a particular font.
Only one PXL file will be open at any one time.
*)
FROM pxlreader IMPORT
OpenPXLFile,
MoveToPXLDirectory, MoveToPXLByte,
GetPXLByte, GetTwoPXLBytes, SignedPXLPair, SignedPXLQuad,
ClosePXLFile;
(* DVItoVDU can work efficiently on a variety of VDUs without having to
know all the nitty gritty details required to drive them.
Modula-2's procedure variables and separate compilation facilities provide
a very nice mechanism for achieving the desired terminal independence.
The generic VDU parameters and routines are initialized in InitVDUInterface.
*)
FROM vduinterface IMPORT
InitVDUInterface,
DVIstatusl, windowstatusl, messagel, commandl, bottoml,
windowh, windowv, windowwd, windowht,
StartText, MoveToTextLine, ClearTextLine, ClearScreen,
StartGraphics, LoadFont, ShowChar, ShowRectangle,
ResetVDU;
(*******************************************************************************
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.
*)
CONST
(* Possible commands are: *)
(* i a positive integer; display ith DVI page *)
TeXpage = '['; (* start of a TeX page specification: [i0. ... .i9] *)
Next = 'N'; (* display next DVI page, depending on direction *)
Forwards = '>'; (* process DVI pages in ascending order *)
Backwards = '<'; (* process DVI pages in descending order *)
Window = 'W'; (* move window's top left corner to given position *)
Up = 'U'; (* move window up a given amount *)
Down = 'D'; (* move window down a given amount *)
Left = 'L'; (* move window left a given amount *)
Right = 'R'; (* move window right a given amount *)
Hsize = 'H'; (* set scaledwd: window's horizontal size *)
Vsize = 'V'; (* set scaledht: window's vertical size *)
Terse = 'T'; (* display quick and nasty chars at reference points *)
Box = 'B'; (* display box outlines of glyphs *)
Full = 'F'; (* display all pixels in glyphs *)
Inch = 'I'; (* get/show dimensions in inches *)
(* SYSDEP: changed In to Inch because compiler got In confused with IN !!! *)
Cm = 'C'; (* get/show dimensions in centimetres *)
Mm = 'M'; (* get/show dimensions in millimetres *)
PcPtPx = 'P'; (* get/show dimensions in picas/points/pixels *)
Help = '?'; (* display help on available commands *)
Show = 'S'; (* display useful statistics *)
Quit = 'Q'; (* have a guess *)
maxcommstring = 80;
commprompt = 'Command:';
NULL = 0C; (* SYSDEP: used to terminate strings *)
EOL = 12C; (* SYSDEP: newline character *)
VAR
commstring (* holds user responses *)
: ARRAY [0..maxcommstring-1] OF CHAR;
commpos : CARDINAL; (* current position in commstring *)
commlen : CARDINAL; (* length of commstring *)
command : CHAR; (* starting character of command *)
ascending : BOOLEAN; (* initially TRUE; changed by the
Forwards/Backwards commands to control
how to get the next DVI page *)
maxpix : INTEGER; (* maximum absolute pixel value;
depends on resolution *)
(* These flags are used to handle multiple commands: *)
screenjustcleared, (* has screen just been cleared? *)
paintDVIStatus, (* does DVI status line need updating? *)
paintWindowStatus, (* does window status line need updating? *)
paintwindow, (* does window region need updating? *)
pageoffpaper, (* is page off paper? *)
badcommand : BOOLEAN; (* 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 VDUInterface.
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.
For efficiency, we use two different scaling functions 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.
*)
CONST
abortkey = EOL; (* user aborts display by hitting RETURN *)
VAR
displaymode : (tersemode (* show quick and nasty chars at ref pts *)
,boxmode (* show box outlines of glyphs *)
,fullmode (* show all pixels in glyphs *)
);
currentunits : (inunits (* get/show dimensions in inches *)
,cmunits (* get/show dimensions in centimetres *)
,mmunits (* get/show dimensions in millimetres *)
,pcunits (* get/show dimensions in picas *)
,ptunits (* get/show dimensions in points *)
,pxunits (* get/show dimensions in pixels *)
);
papertop,
paperleft,
paperbottom,
paperright : INTEGER; (* these define the edges of the paper *)
windowtop,
windowleft,
windowbottom,
windowright : INTEGER; (* these define the current window edges *)
allpagevisible : BOOLEAN; (* is all of page visible in window? *)
outsidepage : BOOLEAN; (* is entire window outside page? *)
scaledht : INTEGER; (* current window height in paper pixels *)
scaledwd : INTEGER; (* current window width in paper pixels *)
vscalefactor : REAL; (* windowht / scaledht *)
hscalefactor : REAL; (* windowwd / scaledwd *)
(* Expand/ShrinkHpos and Expand/ShrinkVpos are assigned to these
procedure variables depending on the values of h/vscalefactor. *)
ScaleVpos,
ScaleHpos : PROCEDURE (INTEGER) : INTEGER;
(* TerseChar, BoxChar or FullChar1/2 are assigned to DisplayOneChar
depending on the current displaymode (which the user can change by
hitting the Terse/Box/Full commands while DisplayChars is executing). *)
DisplayOneChar : PROC;
thisruleinfo : ruleinfoptr; (* current rule info in rulelist *)
unusedfont : fontinfoptr; (* first unused font in sorted fontlist *)
thisfontinfo : fontinfoptr; (* current font info in sorted fontlist *)
thischarinfo : charinfoptr; (* current char info in charlist *)
thischar : CARDINAL; (* current index into current chartable *)
fontopen : BOOLEAN; (* is thisfontinfo^.fontspec open? *)
useraborted : BOOLEAN; (* did user abort page display? *)
charvisible : BOOLEAN; (* was character actually displayed? *)
(******************************************************************************)
PROCEDURE TopLevel;
(* Note that the implementation blocks of all imported modules have already
been executed by this stage.
*)
BEGIN
InitSysInterface; (* initialize DVIname, resolution, vdu, mag, etc. *)
InitVDUInterface; (* initialize generic VDU routines and parameters *)
Initialize; (* uses some of the above parameters *)
DVIErrorRoutine := MyDVIErrorRoutine; (* called by DVIReader upon an error *)
OpenDVIFile(DVIname); (* initialize DVImag, etc. *)
IF mag = 0 THEN (* no override given, so use DVImag *)
mag := DVImag;
END;
(* Having decided on what magnification value to use, we can now help
DVIReader calculate the number of pixels per DVI unit.
*)
SetConversionFactor(resolution,mag);
SpecialRoutine := MySpecialRoutine; (* called by InterpretPage *)
PixelTableRoutine := MyPixelTableRoutine; (* called by InterpretPage *)
StartText;
ClearScreen;
UpdateDVIStatusLine;
UpdateWindowStatusLine;
REPEAT
NextCommandLine;
UNTIL command = Quit;
Finish;
END TopLevel;
(******************************************************************************)
PROCEDURE Initialize;
BEGIN
(* TeX will not generate dimensions > than about 38 feet, so we
choose an absolute limit on our dimensions to be 40 feet.
Should we check for stupid resolution, mag, paperht, paperwd values???
*)
maxpix := 40 * 12 * resolution;
(* top left corner of paper is fixed at (-1",-1") *)
papertop := -INTEGER(resolution);
paperleft := -INTEGER(resolution);
paperbottom := papertop + INTEGER(paperht) - 1;
paperright := paperleft + INTEGER(paperwd) - 1;
(* User sees the following status values before requesting the first page.
Note that DVIReader has already initialized currDVIpage and currTeXpage.
*)
ascending := TRUE; (* process DVI pages in ascending order *)
displaymode := tersemode;
windowtop := 0; (* window location *)
windowleft := 0;
scaledht := windowht; (* window size is initially unscaled *)
scaledwd := windowwd;
minhp := 0; minvp := 0; (* page location *)
maxhp := 0; maxvp := 0;
currentunits := inunits; (* units are initially inches *)
(* initialize the scaling routines *)
vscalefactor := 1.0;
hscalefactor := 1.0;
ScaleVpos := ShrinkVpos; (* use when vscalefactor <= 1.0 *)
ScaleHpos := ShrinkHpos; (* use when hscalefactor <= 1.0 *)
END Initialize;
(******************************************************************************)
PROCEDURE MyDVIErrorRoutine (DVIerror : DVIerrorcodes);
(* DVIErrorRoutine for DVIReader which has just detected one of the errors
described in DVIReader's definition module.
*)
PROCEDURE PleaseReport;
BEGIN
WriteString(' Please tell your local TeXnician.');
END PleaseReport;
BEGIN
CASE DVIerror OF
(* these errors are detected in OpenDVIFile; they are considered fatal *)
DVIunopened :
ResetVDU; (* do before message since it might erase screen! *)
WriteString("Couldn't open ");
WriteString(DVIname); Write('!'); WriteLn;
RestoreTerminal; HALT;
| DVIempty :
ResetVDU;
WriteString(DVIname);
WriteString(' is empty!'); WriteLn;
RestoreTerminal; HALT;
| DVIbadid :
ResetVDU;
WriteString(DVIname);
WriteString(' is not a valid DVI file!'); WriteLn;
RestoreTerminal; HALT;
| DVIstackoverflow :
ResetVDU;
WriteString('Stack capacity exceeded!'); PleaseReport; WriteLn;
RestoreTerminal; HALT;
(* this error is detected in InterpretPage; we warn user but continue *)
| DVIbadchar :
WITH currfont^ DO
ClearMessageLine;
WriteString('Ignoring unknown character from ');
WriteString(fontspec); Write('!');
WaitForReturn;
END;
(* this error should never happen *)
| DVIcatastrophe :
ResetVDU; WriteLn;
WriteString('Something awful has happened!'); PleaseReport; WriteLn;
RestoreTerminal; HALT;
ELSE
(* DEBUG
ResetVDU; WriteLn;
WriteString('Bug in MyDVIErrorRoutine!'); PleaseReport; WriteLn;
RestoreTerminal; HALT;
GUBED *)
END;
END MyDVIErrorRoutine;
(******************************************************************************)
PROCEDURE 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.
*)
BEGIN
ClearTextLine(messagel);
MoveToTextLine(messagel);
END ClearMessageLine;
(******************************************************************************)
PROCEDURE WaitForReturn;
(* DVItoVDU has just displayed an important message.
To ensure message is seen we wait for user to hit the RETURN key.
*)
VAR ch : CHAR;
BEGIN
WriteString(' RETURN:');
WriteBuffer;
REPEAT Read(ch) UNTIL ch = EOL;
END WaitForReturn;
(******************************************************************************)
PROCEDURE MySpecialRoutine (totalbytes : INTEGER;
NextDVIByte : GetByteFunction);
(* SpecialRoutine for DVIReader which has just seen a \special command while
interpreting a page. It passes the number of bytes in the command and a
function to return their values one at a time.
*)
VAR i, next : INTEGER;
BEGIN
ClearMessageLine;
(* SYSDEP: compiler treats \ in a string as special; need \\ to write \ *)
WriteString('Ignoring \\special command: ');
FOR i := 1 TO totalbytes DO
next := NextDVIByte(); (* get next byte *)
IF i <= 20 THEN (* display up to 1st 20 bytes *)
IF (next >= ORD(' ')) AND (next <= ORD('~')) THEN
Write(CHR(next));
ELSE
Write('^');
IF next < ORD(' ') THEN Write(CHR(next+64)) ELSE Write('?') END;
END;
END;
END;
IF totalbytes > 20 THEN WriteString('...') END;
WaitForReturn;
END MySpecialRoutine;
(******************************************************************************)
PROCEDURE MyPixelTableRoutine;
(* SYSDEP: PixelTableRoutine for DVIReader which has just allocated a new
pixeltable for currfont^. DVIReader calls this routine from InterpretPage
only ONCE per font (the first time the font is used).
We get the pixeltable information from the font file given by fontspec.
(If this is the first time we've seen the font then we build fontspec first.
Note that the Show command also requires fontspec to be built.)
If we can't open the PXL file, we return dummyfont values but using the
current font's scaledsize.
*)
VAR i : CARDINAL;
alpha, beta,
b0, b1, b2, b3 : INTEGER; (* 4 bytes in fix width *)
BEGIN
WITH currfont^ DO
IF fontspeclen = 0 THEN (* need to build fontspec *)
BuildFontSpec(currfont);
END;
ClearMessageLine;
IF OpenPXLFile(fontspec) THEN
WriteString('Loading font data from ');
WriteString(fontspec);
WriteLn;
ELSIF OpenPXLFile(dummyfont) THEN
(* we return a pixeltable with dummyfont values *)
WriteString("Couldn't open "); WriteString(fontspec);
WriteString("! Loading dummy font.");
WaitForReturn;
ClearMessageLine;
WriteBuffer; (* user RETURN clears message line immediately *)
ELSE
ResetVDU; WriteLn;
WriteString("Couldn't open dummy font "); WriteString(dummyfont);
Write('!'); WriteLn;
RestoreTerminal; HALT;
END;
(* move to first byte of font directory *)
MoveToPXLDirectory;
FOR i := 0 TO maxTeXchar DO
WITH pixelptr^[i] DO
wd := GetTwoPXLBytes();
ht := GetTwoPXLBytes();
xo := SignedPXLPair();
yo := SignedPXLPair();
mapadr := SignedPXLQuad(); (* word (not byte!) offset in PXL file *)
b0 := GetPXLByte(); (* should be 0 or 255 *)
b1 := GetPXLByte();
b2 := GetPXLByte();
b3 := GetPXLByte();
(* Convert the fix width into the corresponding dwidth and pwidth
values using the method recommended in DVITYPE.
WARNING: DVI translators that read RST files will have to use
a different method because the widths in such files are NOT
equivalent to those in a TFM file.
*)
alpha := 16 * scaledsize; beta := 16;
WHILE scaledsize >= 40000000B DO (* 2^23 *)
scaledsize := scaledsize DIV 2;
beta := beta DIV 2;
END;
dwidth := (((((b3 * INTEGER(scaledsize)) DIV 400B) +
(b2 * INTEGER(scaledsize))) DIV 400B) +
(b1 * INTEGER(scaledsize))) DIV beta;
IF b0 > 0 THEN
IF b0 = 255 THEN
dwidth := dwidth - alpha;
ELSE
(* DEBUG
ResetVDU;
WriteLn;
WriteString('Bad fix width! 1st byte='); WriteInt(b0);
WriteLn;
RestoreTerminal; HALT;
GUBED *)
END;
END;
pwidth := PixelRound(dwidth); (* convert DVI units to pixels *)
END;
END;
ClosePXLFile;
END;
END MyPixelTableRoutine;
(******************************************************************************)
PROCEDURE BuildFontSpec (fontptr : fontinfoptr);
(* SYSDEP: Build a complete PXL file specification for the given font.
This will only be done once per font; fontspeclen will no longer be 0.
The PXL file resides in fontarea if not empty,
otherwise within fontdir (set by InitSysInterface).
WARNING: This routine is also called by ShowStatistics.
*)
VAR pxlfile : File;
i, next, pxlsize, temp : CARDINAL;
BEGIN
WITH fontptr^ DO
IF fontarealen > 0 THEN (* use explicit directory *)
fontspec := fontarea;
(* SYSDEP: what if fontarea is an environment variable??? *)
next := fontarealen;
ELSE (* fontarealen = 0, so use fontdir *)
i := 0;
next := Length(fontdir);
REPEAT
fontspec[i] := fontdir[i];
INC(i);
UNTIL (i = next) OR (i > maxfontspec);
END;
IF next >= maxfontspec THEN
fontspeclen := maxfontspec; (* fontspec truncated *)
RETURN;
ELSE
fontspec[next] := '/';
INC(next);
END;
(* fontspec contains fontarea/ or fontdir/, and next is current length.
Append "fontname.nnnnpxl" to fontspec where nnnn are 4 digits
representing the pxlsize.
*)
i := 0;
WHILE (i < fontnamelen) AND (next < maxfontspec) DO
fontspec[next] := fontname[i]; (* append fontname *)
INC(i);
INC(next);
END;
IF next+7 < maxfontspec THEN (* append .nnnnpxl *)
fontspec[next] := '.';
fontspec[next+5] := 'p';
fontspec[next+6] := 'x';
fontspec[next+7] := 'l';
fontspeclen := next+8;
(* SYSDEP: terminate fontspec with NULL *)
IF fontspeclen < maxfontspec THEN fontspec[fontspeclen] := NULL END;
ELSE
fontspeclen := maxfontspec; (* fontspec truncated *)
RETURN;
END;
INC(next);
(* next now points to 1st n in "dir/fontname.nnnnpxl" *)
(* SYSDEP: Calculate pxlsize (nnnn) and insert into fontspec. If fontspec
does not exist, we try pxlsize+1 and pxlsize-1 before giving up.
This overcomes rounding problems that can occur with magnified fonts.
e.g., if TeX source contains
\magnification=\magstep1 % mag = 1200
\font\abc=cmr10 scaled\magstep4 % s/d = 2.074
and resolution = 240 then pxlsize = 2987, NOT 2986.
Is there a better method to avoid all the file open overheads???
*)
pxlsize := TRUNC( FLOAT(mag) * (FLOAT(scaledsize) / FLOAT(designsize))
* (FLOAT(resolution) / 200.0) + 0.5 );
IF pxlsize > 9999 THEN
pxlsize := 9998; (* allow for adding 1 *)
ELSIF pxlsize = 0 THEN
pxlsize := 1; (* allow for subtracting 1 *)
END;
i := 1;
temp := pxlsize;
LOOP
fontspec[next] := CHR(ORD('0') + (temp DIV 1000));
temp := temp MOD 1000;
fontspec[next+1] := CHR(ORD('0') + (temp DIV 100));
temp := temp MOD 100;
fontspec[next+2] := CHR(ORD('0') + (temp DIV 10));
temp := temp MOD 10;
fontspec[next+3] := CHR(ORD('0') + temp);
IF i > 3 THEN (* pxlsize has been restored *)
RETURN; (* could not open fontspec *)
END;
pxlfile := Open(fontspec,"r"); (* SYSDEP: try to open for reading *)
IF pxlfile <> NIL THEN
Close(pxlfile);
RETURN; (* fontspec exists *)
ELSIF i = 1 THEN
temp := pxlsize - 1; (* try pxlsize-1 *)
ELSIF i = 2 THEN
temp := pxlsize + 1; (* try pxlsize+1 *)
ELSE
temp := pxlsize; (* restore original pxlsize *)
END;
INC(i);
END;
END;
END BuildFontSpec;
(******************************************************************************)
PROCEDURE Length (s : ARRAY OF CHAR) : CARDINAL;
(* SYSDEP: Returns the number of characters in given string, where NULL
is assumed to terminate the string (if not full).
*)
VAR i : CARDINAL;
BEGIN
i := 0;
WHILE (i <= HIGH(s)) AND (s[i] <> NULL) DO
INC(i);
END;
RETURN i;
END Length;
(******************************************************************************)
PROCEDURE UpdateDVIStatusLine;
(* Show totalpages, currDVIpage, currTeXpage, direction and displaymode. *)
VAR i, lastnonzero : CARDINAL;
BEGIN
ClearTextLine(DVIstatusl);
MoveToTextLine(DVIstatusl);
WriteString('Total pages='); WriteCard(totalpages);
WriteString(' DVI page='); WriteCard(currDVIpage);
WriteString(' TeX page='); Write('[');
lastnonzero := 9;
WHILE (lastnonzero > 0) AND (currTeXpage[lastnonzero] = 0) DO
DEC(lastnonzero); (* find last counter with non-zero value *)
END;
(* always show \count0 but don't show trailing 0 counters *)
FOR i := 0 TO lastnonzero DO
WriteInt(currTeXpage[i]);
IF i <> lastnonzero THEN
Write('.');
END;
END;
Write(']');
WriteString(' Next=');
IF ascending THEN
Write('>');
ELSE
Write('<');
END;
WriteString(' ');
CASE displaymode OF
tersemode : WriteString('Terse') |
boxmode : WriteString('Box') |
fullmode : WriteString('Full')
END;
WriteLn;
END UpdateDVIStatusLine;
(******************************************************************************)
PROCEDURE UpdateWindowStatusLine;
(* Show current window location and size, page location and size, and units. *)
BEGIN
ClearTextLine(windowstatusl);
MoveToTextLine(windowstatusl);
WriteString('Window at ('); WriteDimension(windowleft);
Write(','); WriteDimension(windowtop);
WriteString(') '); WriteDimension(scaledwd);
WriteString(' by '); WriteDimension(scaledht);
WriteString(' Page at ('); WriteDimension(minhp);
Write(','); WriteDimension(minvp);
WriteString(') '); WriteDimension(maxhp-minhp+1);
WriteString(' by '); WriteDimension(maxvp-minvp+1);
WriteString(' ');
CASE currentunits OF
inunits : WriteString('IN') |
cmunits : WriteString('CM') |
mmunits : WriteString('MM') |
pcunits : WriteString('PC') |
ptunits : WriteString('PT') |
pxunits : WriteString('PX')
END;
WriteLn;
END UpdateWindowStatusLine;
(******************************************************************************)
PROCEDURE WriteDimension (pixels : INTEGER);
(* Show the given pixel dimension in terms of currentunits. *)
VAR realdim : REAL; fracpart : CARDINAL;
BEGIN
CASE currentunits OF
inunits : realdim := FLOAT(pixels) / FLOAT(resolution) |
cmunits : realdim := FLOAT(pixels) / FLOAT(resolution) * 2.54 |
mmunits : realdim := FLOAT(pixels) / FLOAT(resolution) * 25.4 |
pcunits : realdim := FLOAT(pixels) / FLOAT(resolution) * 72.27 / 12.0 |
ptunits : realdim := FLOAT(pixels) / FLOAT(resolution) * 72.27 |
pxunits : WriteInt(pixels); RETURN
END;
(* show realdim to an accuracy of 1 decimal place *)
IF ABS(realdim) < 0.05 THEN
WriteString('0.0');
ELSE
IF realdim < 0.0 THEN
Write('-');
realdim := ABS(realdim);
END;
realdim := realdim + 0.05; (* round up to 1 decimal place *)
WriteCard(TRUNC(realdim)); (* whole part *)
Write('.');
fracpart := TRUNC((realdim - FLOAT(TRUNC(realdim))) * 10.0); (* 0..9 *)
WriteCard(fracpart);
END;
END WriteDimension;
(******************************************************************************)
PROCEDURE NextCommandLine;
(* Prompt user for next command line, parse response and call the
appropriate command handler for each command in the line.
*)
VAR n : INTEGER; (* returned by GetInteger call *)
BEGIN
ClearTextLine(commandl);
MoveToTextLine(commandl);
WriteString(commprompt);
WriteBuffer;
ReadString(commstring); (* read new command line *)
ClearMessageLine; (* erase message line at this stage *)
commlen := Length(commstring);
commpos := 0;
WHILE (commlen > 0) AND (commstring[commlen-1] = ' ') DO
DEC(commlen); (* ignore any trailing spaces *)
END;
(* initialize flags for multiple command processing *)
badcommand := FALSE;
paintWindowStatus := FALSE;
paintDVIStatus := FALSE;
paintwindow := FALSE;
screenjustcleared := FALSE;
pageoffpaper := FALSE;
WHILE (commpos < commlen) AND (NOT badcommand) DO
(* next command is defined by the next non-space character in commstring *)
WHILE commstring[commpos] = ' ' DO
INC(commpos); (* ignore any spaces *)
END;
command := CAP(commstring[commpos]);
CASE command OF
Window : INC(commpos);
WindowMove;
IF (currDVIpage <> 0) AND (NOT badcommand)
THEN paintWindowStatus := TRUE END;
|
Up,
Down : INC(commpos);
WindowUpDown;
IF currDVIpage <> 0 THEN paintWindowStatus := TRUE END;
|
Left,
Right : INC(commpos);
WindowLeftRight;
IF currDVIpage <> 0 THEN paintWindowStatus := TRUE END;
|
Hsize : INC(commpos);
SetWindowWidth;
IF currDVIpage <> 0 THEN
NewLocation(windowleft,windowtop);
END;
paintWindowStatus := TRUE;
|
Vsize : INC(commpos);
SetWindowHeight;
IF currDVIpage <> 0 THEN
NewLocation(windowleft,windowtop);
END;
paintWindowStatus := TRUE;
|
Next : INC(commpos);
IF NextPageFound() THEN
ProcessPage;
END;
|
'0'..'9' : IF GetInteger(commstring,commlen,commpos,n)
(* must be true, and commpos now after last digit *)
AND DVIPageFound(n) THEN
ProcessPage;
END;
|
TeXpage : IF TeXPageFound() THEN
(* commpos incremented in ParseTeXpage *)
ProcessPage;
END;
|
Forwards : INC(commpos);
ascending := TRUE;
paintDVIStatus := TRUE;
|
Backwards : INC(commpos);
ascending := FALSE;
paintDVIStatus := TRUE;
|
Terse : INC(commpos);
displaymode := tersemode;
paintDVIStatus := TRUE;
IF currDVIpage <> 0 THEN paintwindow := TRUE END;
|
Box : INC(commpos);
displaymode := boxmode;
paintDVIStatus := TRUE;
IF currDVIpage <> 0 THEN paintwindow := TRUE END;
|
Full : INC(commpos);
displaymode := fullmode;
paintDVIStatus := TRUE;
IF currDVIpage <> 0 THEN paintwindow := TRUE END;
|
Inch, Cm, Mm,
PcPtPx : INC(commpos);
ChangeUnits;
IF NOT badcommand THEN paintWindowStatus := TRUE END;
|
Help : INC(commpos);
ShowHelp;
|
Show : INC(commpos);
ShowStatistics;
ClearScreen;
screenjustcleared := TRUE;
paintDVIStatus := TRUE;
paintWindowStatus := TRUE;
IF currDVIpage <> 0 THEN paintwindow := TRUE END;
|
Quit : RETURN;
ELSE
INC(commpos);
ClearMessageLine;
WriteString('Unknown command! Type ');
Write(Help); WriteString(' for help.');
BadCommandMessage;
END;
END;
IF paintwindow THEN
DisplayPage; (* only update window after processing all commands *)
ELSE
IF paintDVIStatus THEN UpdateDVIStatusLine END;
IF paintWindowStatus THEN UpdateWindowStatusLine END;
END;
END NextCommandLine;
(******************************************************************************)
PROCEDURE 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).
*)
VAR hpos, vpos : INTEGER; (* move window to this new position *)
BEGIN
(* commpos is positioned after W *)
IF GetDimension(commstring,commlen,commpos,hpos) THEN
WHILE (commpos < commlen) AND (commstring[commpos] = ' ') DO
INC(commpos); (* skip any spaces before comma *)
END;
IF (commpos = commlen) OR (* , vpos is missing *)
(commstring[commpos] <> ',') THEN (* , is missing *)
ClearMessageLine;
WriteString('Comma expected!');
IF commpos < commlen THEN INC(commpos) END;
BadCommandMessage;
ELSE
INC(commpos); (* skip over comma *)
IF GetDimension(commstring,commlen,commpos,vpos) THEN
NewLocation(hpos,vpos);
ELSE
ClearMessageLine;
WriteString('Vertical coordinate expected!');
IF commpos < commlen THEN INC(commpos) END;
BadCommandMessage;
END;
END;
ELSE
NewLocation(minhp,minvp); (* hpos,vpos absent *)
END;
END WindowMove;
(******************************************************************************)
PROCEDURE GetDimension (str : ARRAY OF CHAR; (* in *)
strlen : CARDINAL; (* in *)
VAR pos : CARDINAL; (* in/out *)
VAR n : INTEGER (* out *)
) : BOOLEAN;
(* 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.
*)
VAR sign, intdim : INTEGER;
fracpart, divisor : CARDINAL;
absrealdim : REAL;
intpresent, dimtoobig : BOOLEAN;
BEGIN
(* GetInteger does not remember a sign by itself, so we need to check
for -ve dimensions like -.5 first.
*)
WHILE (pos < strlen) AND (str[pos] = ' ') DO (* skip any spaces *)
INC(pos);
END;
sign := 1;
IF (pos < strlen) AND (str[pos] = '-') THEN
sign := -1;
END;
intpresent := GetInteger(str,strlen,pos,intdim);
IF (NOT intpresent) AND ((pos = strlen) OR (str[pos] <> '.')) THEN
n := 0;
RETURN FALSE;
END;
(* dimension is valid; if no integer part then intdim will be 0; sign = +|-1 *)
IF (pos = strlen) OR (str[pos] <> '.') THEN
(* no fractional part *)
absrealdim := FLOAT(ABS(intdim));
ELSE
(* extract fractional part *)
INC(pos); (* skip over decimal point *)
divisor := 1;
fracpart := 0;
WHILE (pos < strlen) AND (str[pos] >= '0') AND (str[pos] <= '9') DO
(* only consider up to 4 decimal places *)
IF divisor < 10000 THEN
divisor := divisor * 10;
fracpart := fracpart * 10 + (ORD(str[pos]) - ORD('0'));
END;
INC(pos);
END;
absrealdim := FLOAT(ABS(intdim)) + (FLOAT(fracpart) / FLOAT (divisor));
END;
(* calculate n based on absrealdim, sign and currentunits *)
dimtoobig := FALSE;
CASE currentunits OF
inunits :
IF absrealdim > FLOAT(maxpix) / FLOAT(resolution) THEN
dimtoobig := TRUE;
ELSE
n := sign * TRUNC(absrealdim * FLOAT(resolution) + 0.5);
END; |
cmunits :
IF absrealdim > (FLOAT(maxpix) / FLOAT(resolution)) * 2.54 THEN
dimtoobig := TRUE;
ELSE
n := sign * TRUNC((absrealdim / 2.54) * FLOAT(resolution) + 0.5);
END; |
mmunits :
IF absrealdim > (FLOAT(maxpix) / FLOAT(resolution)) * 25.4 THEN
dimtoobig := TRUE;
ELSE
n := sign * TRUNC((absrealdim / 25.4) * FLOAT(resolution) + 0.5);
END; |
pcunits :
IF absrealdim > (FLOAT(maxpix) / FLOAT(resolution)) * (72.27 / 12.0) THEN
dimtoobig := TRUE;
ELSE
n := sign * TRUNC((absrealdim / 72.27) * 12.0 * FLOAT(resolution) +
0.5);
END; |
ptunits :
IF absrealdim > (FLOAT(maxpix) / FLOAT(resolution)) * 72.27 THEN
dimtoobig := TRUE;
ELSE
n := sign * TRUNC((absrealdim / 72.27) * FLOAT(resolution) + 0.5);
END; |
pxunits :
IF absrealdim > FLOAT(maxpix) THEN
dimtoobig := TRUE;
ELSE
n := sign * TRUNC(absrealdim + 0.5);
END;
END;
IF dimtoobig THEN n := sign * maxpix END;
RETURN TRUE;
END GetDimension;
(******************************************************************************)
PROCEDURE GetInteger (str : ARRAY OF CHAR; (* in *)
strlen : CARDINAL; (* in *)
VAR pos : CARDINAL; (* in/out *)
VAR n : INTEGER (* out *)
) : BOOLEAN;
(* Extract an integer from given str 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 (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.
*)
CONST limit = 2147483647; (* SYSDEP: 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! *)
threshhold = limit DIV 10; (* nearing overflow *)
VAR absval, last : CARDINAL;
sign : INTEGER;
inttoobig : BOOLEAN;
BEGIN
WHILE (pos < strlen) AND (str[pos] = ' ') DO (* skip any spaces *)
INC(pos);
END;
absval := 0; sign := 1; last := pos;
inttoobig := FALSE;
IF pos < strlen THEN
IF str[pos] = '-' THEN
sign := -1; INC(last);
ELSIF str[pos] = '+' THEN
INC(last);
END;
WHILE (last < strlen) AND
(str[last] >= '0') AND (str[last] <= '9') DO
IF (absval > threshhold) OR ((absval = threshhold) AND (str[last] > '7'))
THEN
inttoobig := TRUE;
ELSE
absval := absval * 10 + (ORD(str[last]) - ORD('0'));
END;
INC(last);
END;
END;
IF pos = last THEN
n := 0;
RETURN FALSE;
ELSE
pos := last;
IF inttoobig THEN absval := limit END;
n := sign * INTEGER(absval);
RETURN TRUE;
END;
END GetInteger;
(******************************************************************************)
PROCEDURE 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.
*)
VAR i : CARDINAL;
BEGIN
badcommand := TRUE;
ClearTextLine(commandl);
MoveToTextLine(commandl);
WriteString(commprompt);
FOR i := 0 TO commpos-1 DO Write(commstring[i]) END;
Write('!'); (* put ! after the problem character *)
IF commpos < commlen THEN
WriteString(' Ignoring:');
FOR i := commpos TO commlen-1 DO Write(commstring[i]) END;
END;
WaitForReturn;
ClearMessageLine;
ClearTextLine(commandl);
END BadCommandMessage;
(******************************************************************************)
PROCEDURE NewLocation (newhp, newvp : INTEGER);
(* Change window location to given position and update window edges.
If pageempty is TRUE then window moves to (paperleft,papertop).
If the entire window moves outside the 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.
*)
BEGIN
IF currDVIpage = 0 THEN (* message only seen after W,U,D,L,R commands *)
ClearMessageLine;
WriteString("You haven't selected a page yet!");
BadCommandMessage;
RETURN;
END;
IF pageempty THEN
newvp := papertop;
newhp := paperleft;
ELSE
(* check if new position puts window entirely outside edges;
if so then minimize the movement needed to keep this true *)
outsidepage := FALSE;
IF newvp > maxvp THEN
outsidepage := TRUE;
newvp := maxvp + 1;
ELSIF newvp < (minvp - scaledht + 1) THEN
outsidepage := TRUE;
newvp := minvp - scaledht;
END;
IF newhp > maxhp THEN
outsidepage := TRUE;
newhp := maxhp + 1;
ELSIF newhp < (minhp - scaledwd + 1) THEN
outsidepage := TRUE;
newhp := minhp - scaledwd;
END;
END;
windowtop := newvp;
windowleft := newhp;
windowbottom := windowtop + scaledht - 1;
windowright := windowleft + scaledwd - 1;
(* allpagevisible will only be sensible if not pageempty *)
allpagevisible := (minvp >= windowtop) AND (maxvp <= windowbottom) AND
(minhp >= windowleft) AND (maxhp <= windowright);
(* even if pageempty or window hasn't moved we must still call DisplayPage *)
paintwindow := TRUE;
END NewLocation;
(******************************************************************************)
PROCEDURE WindowUpDown;
VAR amount : INTEGER; (* move window up/down this many pixels *)
BEGIN
(* commpos is positioned after U or D *)
IF GetDimension(commstring,commlen,commpos,amount) THEN
(* do nothing *)
ELSE
amount := scaledht; (* if amount absent, set to window height *)
END;
IF command = Up THEN
amount := -amount;
END;
NewLocation(windowleft,windowtop+amount);
END WindowUpDown;
(******************************************************************************)
PROCEDURE WindowLeftRight;
VAR amount : INTEGER; (* move window left/right this many pixels *)
BEGIN
(* commpos is positioned after L or R *)
IF GetDimension(commstring,commlen,commpos,amount) THEN
(* do nothing *)
ELSE
amount := scaledwd; (* if amount absent, set to window width *)
END;
IF command = Left THEN
amount := -amount;
END;
NewLocation(windowleft+amount,windowtop);
END WindowLeftRight;
(******************************************************************************)
PROCEDURE 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.
*)
VAR wd : INTEGER;
BEGIN
(* commpos is positioned after H *)
IF GetDimension(commstring,commlen,commpos,wd) THEN
(* note that maximum value of wd is restricted to maxpix *)
IF wd <= 0 THEN wd := 1 END;
NewWindowWidth(wd);
ELSE
NewWindowWidth(windowwd); (* parameter absent *)
END;
END SetWindowWidth;
(******************************************************************************)
PROCEDURE NewWindowWidth (wd : INTEGER);
(* Set window width to given value (> 0 and <= max dimension). *)
BEGIN
scaledwd := wd;
hscalefactor := FLOAT(windowwd) / FLOAT(scaledwd);
(* following method avoids testing hscalefactor each time in ScaleHpos *)
IF hscalefactor > 1.0 THEN
ScaleHpos := ExpandHpos;
ELSE
ScaleHpos := ShrinkHpos;
END;
END NewWindowWidth;
(******************************************************************************)
PROCEDURE ExpandHpos (h : INTEGER) : INTEGER;
(* Return a scaled value for the given horizontal window coordinate. *)
BEGIN
RETURN TRUNC ( FLOAT(h) * hscalefactor + 0.5 ); (* hscalefactor > 1.0 *)
END ExpandHpos;
(******************************************************************************)
PROCEDURE ShrinkHpos (h : INTEGER) : INTEGER;
(* Return a scaled value for the given horizontal window coordinate. *)
BEGIN
RETURN TRUNC ( (FLOAT(h) + 0.5) * hscalefactor ); (* hscalefactor <= 1.0 *)
END ShrinkHpos;
(******************************************************************************)
PROCEDURE 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.
*)
VAR ht : INTEGER;
BEGIN
(* commpos is positioned after V *)
IF GetDimension(commstring,commlen,commpos,ht) THEN
(* note that maximum value of ht is restricted to maxpix *)
IF ht <= 0 THEN ht := 1 END;
NewWindowHeight(ht);
ELSE
NewWindowHeight(windowht); (* parameter absent *)
END;
END SetWindowHeight;
(******************************************************************************)
PROCEDURE NewWindowHeight (ht : INTEGER);
(* Set window height to given value (> 0 and <= max dimension). *)
BEGIN
scaledht := ht;
vscalefactor := FLOAT(windowht) / FLOAT(scaledht);
(* following method avoids testing vscalefactor each time in ScaleVpos *)
IF vscalefactor > 1.0 THEN
ScaleVpos := ExpandVpos;
ELSE
ScaleVpos := ShrinkVpos;
END;
END NewWindowHeight;
(******************************************************************************)
PROCEDURE ExpandVpos (v : INTEGER) : INTEGER;
(* Return a scaled value for the given vertical window coordinate. *)
BEGIN
RETURN TRUNC ( FLOAT(v) * vscalefactor + 0.5 ); (* vscalefactor > 1.0 *)
END ExpandVpos;
(******************************************************************************)
PROCEDURE ShrinkVpos (v : INTEGER) : INTEGER;
(* Return a scaled value for the given vertical window coordinate. *)
BEGIN
RETURN TRUNC ( (FLOAT(v) + 0.5) * vscalefactor ); (* vscalefactor <= 1.0 *)
END ShrinkVpos;
(******************************************************************************)
PROCEDURE NextPageFound () : BOOLEAN;
(* 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.
*)
BEGIN
IF (currDVIpage = 1) AND (NOT ascending) THEN
ClearMessageLine;
WriteString('You are looking at first DVI page!');
BadCommandMessage;
RETURN FALSE;
ELSIF (currDVIpage = totalpages) AND ascending THEN
ClearMessageLine;
WriteString('You are looking at last DVI page!');
BadCommandMessage;
RETURN FALSE;
ELSE
MoveToNextPage(ascending); (* position to next DVI page *)
RETURN TRUE;
END;
END NextPageFound;
(******************************************************************************)
PROCEDURE DVIPageFound (n : CARDINAL) : BOOLEAN;
(* User has selected a particular DVI page number.
Move to page n and return TRUE iff n is in 1..totalpages.
*)
BEGIN
IF (n < 1) OR (n > totalpages) THEN
ClearMessageLine;
IF totalpages > 1 THEN
WriteString('You can only request DVI pages 1 to ');
WriteCard(totalpages); Write('!');
ELSE
WriteString('You can only request DVI page 1!');
END;
BadCommandMessage;
RETURN FALSE;
ELSE
MoveToDVIPage(n); (* position to given DVI page *)
RETURN TRUE;
END;
END DVIPageFound;
(******************************************************************************)
PROCEDURE TeXPageFound () : BOOLEAN;
(* Return TRUE iff TeX page specification is valid and exists.
If so then position to lowest matching page.
*)
VAR newTeXpage : TeXpageinfo;
BEGIN
IF ParseTeXpage(newTeXpage) THEN
IF MoveToTeXPage(newTeXpage) THEN
RETURN TRUE; (* we found lowest matching page *)
ELSE
ClearMessageLine;
WriteString('No TeX page matches your request!');
BadCommandMessage;
RETURN FALSE;
END;
ELSE
RETURN FALSE; (* invalid TeX page specification *)
END;
END TeXPageFound;
(******************************************************************************)
PROCEDURE ParseTeXpage (VAR newTeXpage : TeXpageinfo) (* out *)
: BOOLEAN;
(* 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 [.
*)
BEGIN
WITH newTeXpage DO
lastvalue := 0;
LOOP
INC(commpos);
present[lastvalue] := GetInteger(commstring, commlen, commpos,
value[lastvalue]);
(* commpos now at commlen, space, period, non-digit or ']' *)
WHILE (commpos < commlen) AND (commstring[commpos] = ' ') DO
INC(commpos); (* skip any spaces *)
END;
IF commpos = commlen THEN (* check this first! *)
ClearMessageLine;
WriteString('] expected!');
BadCommandMessage; (* commpos at commlen *)
RETURN FALSE;
END;
IF commstring[commpos] = ']' THEN (* end of TeX page specification *)
INC(commpos); (* possibly further commands *)
EXIT;
END;
IF lastvalue < 9 THEN
INC(lastvalue);
ELSE
ClearMessageLine;
WriteString("] expected after 10 integers!");
INC(commpos);
BadCommandMessage;
RETURN FALSE;
END;
IF commstring[commpos] <> '.' THEN
ClearMessageLine;
WriteString('Period, integer or ] expected!');
INC(commpos);
BadCommandMessage;
RETURN FALSE;
END;
END;
WHILE (lastvalue > 0) AND (NOT present[lastvalue]) DO
DEC(lastvalue);
END;
END;
RETURN TRUE;
END ParseTeXpage;
(******************************************************************************)
PROCEDURE 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 (depending on the relative
sizes of the paper and unscaled window region, as well as the page location),
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.
*)
VAR halfht, halfwd : INTEGER;
BEGIN
(* 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 THEN
ClearMessageLine;
WriteString('Page off paper!'); (* the previous page *)
WaitForReturn;
END;
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 THEN
minhp := 0; maxhp := 0; minvp := 0; maxvp := 0; (* for window status *)
END;
(* Try viewing as much of paper as possible and without too much distortion: *)
IF ((paperwd < paperht) AND (windowwd >= windowht)) OR
((paperwd = paperht) AND (windowwd > windowht)) THEN
halfht := paperht DIV 2;
IF ODD(paperht) THEN INC(halfht) END; (* ensure bottom outline visible *)
NewWindowHeight(halfht); (* try top half of paper *)
NewWindowWidth(paperwd);
NewLocation(paperleft,papertop); (* top left corner of paper *)
IF (NOT pageempty) AND outsidepage THEN
NewLocation(paperleft,papertop+halfht); (* try moving down *)
END;
ELSIF ((paperwd > paperht) AND (windowwd <= windowht)) OR
((paperwd = paperht) AND (windowwd < windowht)) THEN
halfwd := paperwd DIV 2;
IF ODD(paperwd) THEN INC(halfwd) END; (* ensure right outline visible *)
NewWindowHeight(paperht);
NewWindowWidth(halfwd); (* try left half of paper *)
NewLocation(paperleft,papertop); (* top left corner of paper *)
IF (NOT pageempty) AND outsidepage THEN
NewLocation(paperleft+halfwd,papertop); (* try moving right *)
END;
ELSE
(* paper shape matches unscaled window shape *)
NewWindowHeight(paperht); (* try all of paper *)
NewWindowWidth(paperwd);
NewLocation(paperleft,papertop); (* top left corner of paper *)
END;
(* If part/all of page is off paper then we set window size and location so
user can just see ALL of paper AND ALL of page.
*)
IF (NOT pageempty) AND
((minhp < paperleft) OR (minvp < papertop) OR
(maxhp > paperright) OR (maxvp > paperbottom)) THEN
NewWindowHeight(Max(maxvp,paperbottom) - Min(minvp,papertop) + 1);
NewWindowWidth (Max(maxhp,paperright) - Min(minhp,paperleft) + 1);
NewLocation (Min(minhp,paperleft),Min(minvp,papertop));
pageoffpaper := TRUE;
ELSE
pageoffpaper := FALSE; (* page is empty or fits on paper *)
END;
paintWindowStatus := TRUE;
paintwindow := TRUE;
END ProcessPage;
(******************************************************************************)
PROCEDURE Min (a,b : INTEGER) : INTEGER;
(* Return the minimum value of a and b. *)
BEGIN
IF a < b THEN RETURN a ELSE RETURN b END;
END Min;
(******************************************************************************)
PROCEDURE Max (a,b : INTEGER) : INTEGER;
(* Return the maximum value of a and b. *)
BEGIN
IF a > b THEN RETURN a ELSE RETURN b END;
END Max;
(******************************************************************************)
PROCEDURE ChangeUnits;
(* Parse the rest of an Inch, Cm, Mm or PcPtPx command.
commpos is pointing to next position in commandstr.
*)
VAR nextch : CHAR;
BEGIN
IF commpos < commlen THEN
nextch := CAP(commstring[commpos]);
INC(commpos);
ELSE
nextch := ' ';
END;
IF (command = Inch) AND (nextch = 'N') THEN currentunits := inunits;
ELSIF (command = Cm) AND (nextch = 'M') THEN currentunits := cmunits;
ELSIF (command = Mm) AND (nextch = 'M') THEN currentunits := mmunits;
ELSIF (command = PcPtPx) AND (nextch = 'C') THEN currentunits := pcunits;
ELSIF (command = PcPtPx) AND (nextch = 'T') THEN currentunits := ptunits;
ELSIF (command = PcPtPx) AND (nextch = 'X') THEN currentunits := pxunits;
ELSE
ClearMessageLine;
WriteString('Unknown units! ');
CASE command OF
Inch : WriteString('IN') |
Cm : WriteString('CM') |
Mm : WriteString('MM') |
PcPtPx : WriteString('PC, PT or PX')
END;
WriteString(' expected.');
BadCommandMessage;
END;
END ChangeUnits;
(******************************************************************************)
PROCEDURE ShowHelp;
(* 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.
*)
CONST maxline = 80; (* SYSDEP: helpname should have <= maxline chars/line *)
VAR helpfile : File;
outline : ARRAY [0..maxline-1] OF CHAR;
i : CARDINAL;
lines : INTEGER;
ch, answer : CHAR;
BEGIN
helpfile := Open(helpname,"r"); (* SYSDEP: read only *)
IF helpfile = NIL THEN
ClearMessageLine;
WriteString("Couldn't open help file ");
WriteString(helpname); Write('!');
WaitForReturn;
ClearMessageLine;
ELSE
ClearScreen;
MoveToTextLine(1);
lines := 0;
LOOP
IF Readc(helpfile,ch) < 0 THEN (* SYSDEP: end of file *)
ClearTextLine(bottoml);
MoveToTextLine(bottoml);
WriteString('Hit RETURN key to resume page display:');
WriteBuffer;
REPEAT Read(answer) UNTIL answer = EOL;
EXIT;
ELSIF lines >= (bottoml-2) THEN (* blank line before prompt *)
ClearTextLine(bottoml);
MoveToTextLine(bottoml);
WriteString('Hit RETURN key to resume page display,');
WriteString(' or any other key for more help:');
WriteBuffer;
Read(answer);
IF answer = EOL THEN EXIT END;
ClearScreen;
MoveToTextLine(1);
lines := 0; (* reset line count *)
END;
outline := '';
i := 0;
WHILE ch <> EOL DO
IF i < maxline THEN outline[i] := ch END;
IF Readc(helpfile,ch) < 0 THEN
ch := EOL;
(* SYSDEP: this should only happen if EOF occurs before EOL *)
END;
INC(i);
END;
WriteString(outline); WriteLn;
INC(lines);
END;
Close(helpfile);
ClearScreen;
screenjustcleared := TRUE;
paintDVIStatus := TRUE;
paintWindowStatus := TRUE;
IF currDVIpage <> 0 THEN paintwindow := TRUE END;
END;
END ShowHelp;
(******************************************************************************)
PROCEDURE ShowStatistics;
(* Show command options and rule/font/character statistics.
Note that UserHitsReturn controls pagination and takes the place of WriteLn.
*)
VAR linecount, fontcount : INTEGER;
ch : CHAR;
BEGIN
ClearScreen;
MoveToTextLine(1);
linecount := 1;
WriteString('DVI file = '); WriteString(DVIname);
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('VDU = '); WriteString(vdu);
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Resolution = '); WriteCard(resolution);
WriteString(' pixels per inch');
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Magnification = '); WriteCard(mag);
IF mag <> DVImag THEN
WriteString(' (DVI mag of '); WriteCard(DVImag);
WriteString(' was overridden)');
ELSE
WriteString(' (DVI mag)');
END;
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Font directories = '); WriteString(fontdir);
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Dummy font = '); WriteString(dummyfont);
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Help file = '); WriteString(helpname);
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Paper wd by ht = ');
WriteDimension(paperwd); WriteString(' by '); WriteDimension(paperht);
CASE currentunits OF
inunits : WriteString(' IN') |
cmunits : WriteString(' CM') |
mmunits : WriteString(' MM') |
pcunits : WriteString(' PC') |
ptunits : WriteString(' PT') |
pxunits : WriteString(' PX')
END;
IF UserHitsReturn(linecount) THEN RETURN END;
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Total rules on current page = ');
WriteCard(totalrules);
IF UserHitsReturn(linecount) THEN RETURN END;
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Total fonts on ALL pages = ');
WriteCard(totalfonts);
IF UserHitsReturn(linecount) THEN RETURN END;
IF UserHitsReturn(linecount) THEN RETURN END;
WriteString('Fonts: (if used on current page then total chars given)');
IF UserHitsReturn(linecount) THEN RETURN END;
fontcount := 0;
thisfontinfo := fontlist;
WHILE thisfontinfo <> NIL DO
WITH thisfontinfo^ DO
IF fontspeclen = 0 THEN (* need to build fontspec *)
BuildFontSpec(thisfontinfo);
END;
WriteString(fontspec);
IF OpenPXLFile(fontspec) THEN
ClosePXLFile;
ELSE
WriteString(' does not exist!'); (* will use dummyfont *)
END;
IF fontused THEN
INC(fontcount);
WriteString(' (total chars = ');
WriteCard(totalchars); Write(')');
END;
IF UserHitsReturn(linecount) THEN RETURN END;
thisfontinfo := nextfont;
END;
END;
IF currDVIpage = 0 THEN
WriteString("You haven't selected a page yet.");
ELSE
WriteString('Total fonts on current page = ');
WriteInt(fontcount);
END;
IF UserHitsReturn(linecount) THEN RETURN END;
WriteLn;
MoveToTextLine(bottoml);
WriteString('Hit RETURN key to resume page display:');
WriteBuffer;
REPEAT Read(ch) UNTIL ch = EOL;
END ShowStatistics;
(******************************************************************************)
PROCEDURE UserHitsReturn (VAR linecount : INTEGER) : BOOLEAN;
(* Do a WriteLn and return TRUE iff linecount = bottoml-2 AND user hits EOL.
If linecount < bottoml-2 then return FALSE; if not, and user hits
something other than EOL, then prepare a new screen before returning FALSE.
*)
VAR ch : CHAR;
BEGIN
WriteLn;
IF linecount = bottoml-2 THEN (* prompt for next screen *)
MoveToTextLine(bottoml);
WriteString('Hit RETURN key to resume page display,');
WriteString(' or any other key for more:');
WriteBuffer;
Read(ch);
IF ch = EOL THEN RETURN TRUE END;
ClearScreen;
MoveToTextLine(1);
linecount := 1;
ELSE
INC(linecount);
END;
RETURN FALSE;
END UserHitsReturn;
(******************************************************************************)
PROCEDURE 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.
*)
VAR vispage : REAL; (* fraction of page rectangle currently visible *)
left, right, (* visible edges of page rectangle *)
top, bottom : INTEGER;
BEGIN
IF screenjustcleared THEN (* avoid doing it again *)
IF paintDVIStatus THEN UpdateDVIStatusLine END;
IF paintWindowStatus THEN UpdateWindowStatusLine END;
ELSE
ClearScreen; (* would prefer ClearWindow but some VDUs have trouble *)
UpdateDVIStatusLine;
UpdateWindowStatusLine;
END;
StartGraphics;
DisplayPaperEdges;
StartText;
IF pageempty THEN
ClearMessageLine;
WriteString('Page is empty.');
ELSIF outsidepage THEN
IF pageoffpaper THEN CheckPageEdges END;
ClearMessageLine;
WriteString('Window is ');
IF windowtop > maxvp THEN
WriteString('below ');
IF (windowleft > maxhp) OR (windowleft < minhp - scaledwd + 1) THEN
WriteString('and ');
END;
ELSIF windowtop < minvp - scaledht + 1 THEN
WriteString('above ');
IF (windowleft > maxhp) OR (windowleft < minhp - scaledwd + 1) THEN
WriteString('and ');
END;
END;
IF windowleft > maxhp THEN
WriteString('to the right of ');
ELSIF windowleft < minhp - scaledwd + 1 THEN
WriteString('to the left of ');
END;
WriteString('page.');
ELSE
(* Page is not empty and part or all of it is visible. *)
StartGraphics;
useraborted := FALSE;
DisplayRules;
IF NOT useraborted THEN
DisplayChars;
END;
StartText;
IF pageoffpaper THEN CheckPageEdges END;
IF allpagevisible THEN
ClearMessageLine;
WriteString('Entire page is visible.');
END;
END;
END DisplayPage;
(******************************************************************************)
PROCEDURE DisplayPaperEdges;
(* Display visible outlines of the imaginary sheet of paper.
Thickness of outlines = 1 screen pixel no matter what the h and v scaling.
*)
CONST
edgepixel = '.'; (* black pixel for outlines on non-graphic VDUs;
note that VDUInterface sets
TeXtoASCII['.'] := '.' *)
VAR
top, bot, left, right, (* visible edges of paper in paper pixels *)
scaledtop, scaledleft, (* scaled visible edges in screen pixels *)
scaledbot, scaledright,
scaledheight, scaledwidth (* scaled width and height *)
: INTEGER;
BEGIN
(* first check if any part of paper is visible *)
IF papertop > windowbottom THEN RETURN END;
IF paperbottom < windowtop THEN RETURN END;
IF paperleft > windowright THEN RETURN END;
IF paperright < windowleft THEN RETURN END;
(* 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 THEN
scaledbot := ScaleVpos(bot + 1 - windowtop) - 1 + windowv;
ELSE
scaledbot := ScaleVpos(bot - windowtop) + windowv;
END;
IF hscalefactor > 1.0 THEN
scaledright := ScaleHpos(right + 1 - windowleft) - 1 + windowh;
ELSE
scaledright := ScaleHpos(right - windowleft) + windowh;
END;
scaledheight := scaledbot - scaledtop + 1;
scaledwidth := scaledright - scaledleft + 1;
(* Only show visible edges if they are also paper outlines! *)
IF left = paperleft THEN (* left outline visible *)
ShowRectangle(scaledleft, scaledtop, 1, scaledheight, edgepixel);
END;
IF bot = paperbottom THEN (* bottom outline visible *)
ShowRectangle(scaledleft, scaledbot, scaledwidth, 1, edgepixel);
END;
IF top = papertop THEN (* top outline visible *)
ShowRectangle(scaledleft, scaledtop, scaledwidth, 1, edgepixel);
END;
IF right = paperright THEN (* right outline visible *)
ShowRectangle(scaledright, scaledtop, 1, scaledheight, edgepixel);
END;
END DisplayPaperEdges;
(******************************************************************************)
PROCEDURE 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.
*)
CONST
rulepixel = '*'; (* black pixel for rules on non-graphic VDUs;
note that VDUInterface sets
TeXtoASCII['*'] := '*' *)
VAR
top, bottom, left, right, (* visible edges of rule *)
scaledtop, scaledleft, (* scaled visible edges *)
scaledbot, scaledright,
scaledwidth, scaledheight (* scaled width and height *)
: INTEGER;
thisrule : CARDINAL;
keyhit : CHAR; (* returned by BusyRead if TRUE *)
BEGIN
thisruleinfo := rulelist;
WHILE thisruleinfo <> NIL DO
WITH thisruleinfo^ DO
thisrule := 0;
WHILE thisrule < rulecount DO
WITH ruletable[thisrule] DO
(* check if any part of rule is visible *)
(* vp,hp is bottom left corner of rule on page *)
IF RectangleVisible
(vp-ht+1,vp,hp,hp+wd-1, (* rule edges *)
top,bottom,left,right) (* visible rectangle *)
THEN
(* show all pixels in this rectangle *)
scaledtop := ScaleVpos(top - windowtop) + windowv;
scaledleft := ScaleHpos(left - windowleft) + windowh;
IF vscalefactor > 1.0 THEN
scaledbot := ScaleVpos(bottom+1-windowtop) - 1 + windowv;
ELSE
scaledbot := ScaleVpos(bottom-windowtop) + windowv;
END;
IF hscalefactor > 1.0 THEN
scaledright := ScaleHpos(right+1-windowleft) - 1 + windowh;
ELSE
scaledright := ScaleHpos(right-windowleft) + windowh;
END;
scaledheight := scaledbot - scaledtop + 1;
scaledwidth := scaledright - scaledleft + 1;
ShowRectangle
(scaledleft, (* h coord of top left cnr *)
scaledtop, (* v coord of top left cnr *)
scaledwidth,
scaledheight,
rulepixel);
(* SYSDEP: we check keyboard after every visible rule *)
IF BusyRead(keyhit) THEN
keyhit := CAP(keyhit);
IF (keyhit = Terse) AND (displaymode <> tersemode) THEN
displaymode := tersemode;
StartText;
UpdateDVIStatusLine;
StartGraphics;
ELSIF (keyhit = Box) AND (displaymode <> boxmode) THEN
displaymode := boxmode;
StartText;
UpdateDVIStatusLine;
StartGraphics;
ELSIF (keyhit = Full) AND (displaymode <> fullmode) THEN
displaymode := fullmode;
StartText;
UpdateDVIStatusLine;
StartGraphics;
ELSIF keyhit = abortkey THEN
useraborted := TRUE; (* checked in DisplayPage *)
RETURN;
END;
END;
END;
END;
INC(thisrule);
END;
thisruleinfo := nextrule;
END;
END;
END DisplayRules;
(******************************************************************************)
PROCEDURE RectangleVisible (intop, inbot, inleft, inright : INTEGER;
VAR outtop, outbot, outleft, outright : INTEGER
) : BOOLEAN;
(* 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.
*)
BEGIN
IF allpagevisible THEN (* all of rectangle must be visible *)
outtop := intop; outbot := inbot; outleft := inleft; outright := inright;
RETURN TRUE;
END;
IF intop > windowbottom THEN RETURN FALSE END;
IF inbot < windowtop THEN RETURN FALSE END;
IF inleft > windowright THEN RETURN FALSE END;
IF inright < windowleft THEN RETURN FALSE END;
(* 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;
END RectangleVisible;
(******************************************************************************)
PROCEDURE 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.
*)
VAR keyhit : CHAR; (* check for abort or mode change *)
BEGIN
CASE displaymode OF
tersemode : DisplayOneChar := TerseChar |
boxmode : DisplayOneChar := BoxChar |
fullmode : IF (vscalefactor < 1.0) OR (hscalefactor < 1.0) THEN
DisplayOneChar := FullChar2;
ELSE
DisplayOneChar := FullChar1;
END
END;
thisfontinfo := fontlist;
WHILE thisfontinfo <> unusedfont DO
(* SortFont makes sure we only consider used fonts *)
WITH thisfontinfo^ DO
fontopen := FALSE; (* needed for FullChar *)
(* Some VDUs may be able to simulate the given font, or even
better, be able to download the glyph maps from the PXL file.
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(fontspec,
PixelRound(scaledsize),
FLOAT(mag)/1000.0,
hscalefactor,
vscalefactor);
thischarinfo := charlist;
WHILE thischarinfo <> NIL DO (* display chars in chartable *)
WITH thischarinfo^ DO
thischar := 0;
WHILE thischar < charcount DO
DisplayOneChar;
(* SYSDEP:
We can check for abort or mode change after every visible char
because BusyRead overheads are not too high under VAX/UNIX.
*)
IF charvisible AND BusyRead(keyhit) THEN
keyhit := CAP(keyhit);
IF (keyhit = Terse) AND (displaymode <> tersemode) THEN
DisplayOneChar := TerseChar;
displaymode := tersemode;
StartText;
UpdateDVIStatusLine;
StartGraphics;
ELSIF (keyhit = Box) AND (displaymode <> boxmode) THEN
DisplayOneChar := BoxChar;
displaymode := boxmode;
StartText;
UpdateDVIStatusLine;
StartGraphics;
ELSIF (keyhit = Full) AND (displaymode <> fullmode) THEN
IF (vscalefactor < 1.0) OR (hscalefactor < 1.0) THEN
DisplayOneChar := FullChar2;
ELSE
DisplayOneChar := FullChar1;
END;
displaymode := fullmode;
StartText;
UpdateDVIStatusLine;
StartGraphics;
ELSIF keyhit = abortkey THEN
IF fontopen THEN (* must have been in FullChar *)
ClosePXLFile;
StartText;
ClearMessageLine; (* clear font opening message *)
END;
(* no need to set useraborted; DisplayRules done first *)
RETURN;
END;
END;
INC(thischar);
END;
thischarinfo := nextchar;
END;
END;
IF fontopen THEN (* must have been in FullChar *)
ClosePXLFile;
StartText;
ClearMessageLine; (* clear font opening message *)
StartGraphics; (* might be more fonts *)
END;
thisfontinfo := nextfont;
END;
END;
END DisplayChars;
(******************************************************************************)
PROCEDURE 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.
Some VDUs may be able to download a font (via previous LoadFont call)
and produce similar results to a FullChar (but much faster!).
We don't bother checking if glyph is actually all white or non-existent.
*)
BEGIN
WITH thisfontinfo^ DO
WITH thischarinfo^.chartable[thischar] DO
IF PixelVisible(hp,vp) THEN (* ref pt of char is visible *)
ShowChar(ScaleHpos(hp - windowleft) + windowh,
ScaleVpos(vp - windowtop) + windowv,
CHR(code));
charvisible := TRUE;
ELSE
charvisible := FALSE; (* checked in DisplayChars *)
END;
END;
END;
END TerseChar;
(******************************************************************************)
PROCEDURE PixelVisible (hpos, vpos : INTEGER) : BOOLEAN;
(* Return TRUE iff given paper pixel would be visible in current window. *)
BEGIN
IF allpagevisible THEN RETURN TRUE END;
IF vpos < windowtop THEN RETURN FALSE END;
IF vpos > windowbottom THEN RETURN FALSE END;
IF hpos < windowleft THEN RETURN FALSE END;
IF hpos > windowright THEN RETURN FALSE END;
RETURN TRUE;
END PixelVisible;
(******************************************************************************)
PROCEDURE BoxChar;
(* Display visible box outlines of glyph.
Thickness of outlines = 1 screen pixel no matter what the h and v scaling.
*)
VAR
vpmyo, hpmxo, (* vp-yo, hp-xo: glyph's top and left edges *)
top, bottom, left, right, (* visible edges of glyph *)
scaledtop, scaledleft, (* scaled visible edges *)
scaledbot, scaledright,
scaledheight, scaledwidth (* scaled width and height *)
: INTEGER;
ch : CHAR;
BEGIN
WITH thisfontinfo^ DO
WITH thischarinfo^.chartable[thischar] DO
WITH pixelptr^[code] DO
IF mapadr = 0 THEN RETURN END; (* glyph all white or absent *)
(* check if any part of glyph is visible *)
vpmyo := vp-yo;
hpmxo := hp-xo;
IF RectangleVisible
(vpmyo, vpmyo+ht-1, hpmxo, hpmxo+wd-1,(* glyph edges *)
top,bottom,left,right) (* visible part *)
THEN
scaledtop := ScaleVpos(top - windowtop) + windowv;
scaledleft := ScaleHpos(left - windowleft) + windowh;
IF vscalefactor > 1.0 THEN
scaledbot := ScaleVpos(bottom + 1 - windowtop) - 1 + windowv;
ELSE
scaledbot := ScaleVpos(bottom - windowtop) + windowv;
END;
IF hscalefactor > 1.0 THEN
scaledright := ScaleHpos(right + 1 - windowleft) - 1 + windowh;
ELSE
scaledright := ScaleHpos(right - windowleft) + windowh;
END;
scaledheight := scaledbot - scaledtop + 1;
scaledwidth := scaledright - scaledleft + 1;
ch := CHR(code);
(* Only show edges that are also glyph outlines!
Following method reduces the number of ShowRectangle calls needed for
very small boxes.
*)
IF ((scaledheight < 3) AND (top = vpmyo) AND (bottom = vpmyo+ht-1)) OR
((scaledwidth < 3) AND (left = hpmxo) AND (right = hpmxo+wd-1)) THEN
ShowRectangle(scaledleft, scaledtop, scaledwidth, scaledheight, ch);
ELSE
IF left = hpmxo THEN (* left outline visible *)
ShowRectangle(scaledleft, scaledtop, 1, scaledheight, ch);
END;
IF bottom = vpmyo+ht-1 THEN (* bottom outline visible *)
ShowRectangle(scaledleft, scaledbot, scaledwidth, 1, ch);
END;
IF top = vpmyo THEN (* top outline visible *)
ShowRectangle(scaledleft, scaledtop, scaledwidth, 1, ch);
END;
IF right = hpmxo+wd-1 THEN (* right outline visible *)
ShowRectangle(scaledright, scaledtop, 1, scaledheight, ch);
END;
END;
charvisible := TRUE;
ELSE
charvisible := FALSE; (* checked in DisplayChars *)
END;
END;
END;
END;
END BoxChar;
(******************************************************************************)
PROCEDURE FullChar1;
(* Display all pixels in a glyph using bitmap from PXL font file.
This procedure is assigned to DisplayOneChar when h AND vscalefactors are
>= 1.0, so we don't have to worry about scaledheights/widths being 0.
*)
VAR
vpmyo, hpmxo, (* vp-yo, hp-xo: glyph's top and left edges *)
top, bottom, left, right, (* visible edges of glyph *)
scaledv, scalednextv, (* scaled vertical positions for rows *)
scaledh, (* scaled h coord of start of run within row *)
scaledwidth, scaledheight, (* scaled width and height of row *)
thisrow, thisbit (* in paper coordinates *)
: INTEGER;
wordsperrow, (* rows of PXL glyph are word aligned *)
firstbit, lastbit, (* 0..wordsperrow*32 - 1 *)
firstword, (* 0..wordsperrow-1 *)
bitpos : CARDINAL; (* 0..31 *)
glyphword : BITSET; (* current word in bitmap *)
inrun : BOOLEAN; (* are we in a run of black pixels in row? *)
BEGIN
WITH thisfontinfo^ DO
WITH thischarinfo^.chartable[thischar] DO
WITH pixelptr^[code] DO
IF mapadr = 0 THEN RETURN END; (* glyph all white or absent *)
(* check if any part of glyph is visible *)
vpmyo := vp-yo;
hpmxo := hp-xo;
IF RectangleVisible
(vpmyo,vpmyo+ht-1,hpmxo,hpmxo+wd-1, (* glyph edges *)
top,bottom,left,right) (* visible part *)
THEN
IF NOT fontopen THEN (* only open once *)
OpenFontFile;
fontopen := TRUE;
END;
wordsperrow := (wd + 31) DIV 32; (* words in one row of bitmap *)
firstbit := CARDINAL(left-hpmxo); (* first visible bit in row *)
lastbit := CARDINAL(right-hpmxo); (* last visible bit *)
firstword := firstbit DIV 32; (* first visible word *)
(* calculate scaled v coord of first visible row *)
scaledv := ScaleVpos(top - windowtop) + windowv;
(* only consider visible rows; thisrow := top to bottom *)
thisrow := top;
LOOP
(* calculate scaled v coord of next row *)
scalednextv := ScaleVpos(thisrow + 1 - windowtop) + windowv;
scaledheight := scalednextv - scaledv; (* can't be 0 *)
(* move to first byte of first visible word in this row *)
MoveToPXLByte(4 * (mapadr + (CARDINAL(thisrow-vpmyo) * wordsperrow)
+ firstword));
glyphword := BITSET(SignedPXLQuad());
bitpos := 31 - (firstbit MOD 32); (* 31..0 *)
inrun := FALSE;
(* display black pixel runs in row, doing any h/v expansion *)
(* only consider visible bits; thisbit := left to right *)
thisbit := left;
LOOP
IF bitpos IN glyphword THEN (* start/continue run *)
IF NOT inrun THEN
inrun := TRUE;
(* remember start of run *)
scaledh := ScaleHpos(thisbit - windowleft) + windowh;
END;
ELSIF inrun THEN (* 0 bit has ended run *)
inrun := FALSE;
scaledwidth := ScaleHpos(thisbit - windowleft) + windowh
- scaledh;
ShowRectangle
(scaledh,scaledv,scaledwidth,scaledheight,CHR(code));
END;
IF thisbit = right THEN EXIT (* bit loop *) END;
IF bitpos = 0 THEN (* look at first bit in next word of row *)
glyphword := BITSET(SignedPXLQuad());
bitpos := 31;
ELSE (* look at next bit in word *)
DEC(bitpos);
END;
INC(thisbit);
END; (* bit loop *)
IF inrun THEN (* show run at end of row; INC thisbit *)
scaledwidth := ScaleHpos(thisbit + 1 - windowleft) + windowh
- scaledh;
ShowRectangle
(scaledh,scaledv,scaledwidth,scaledheight,CHR(code));
END;
IF thisrow = bottom THEN EXIT (* row loop *) END;
scaledv := scalednextv;
INC(thisrow);
END; (* row loop *)
charvisible := TRUE;
ELSE
charvisible := FALSE; (* checked in DisplayChars *)
END;
END;
END;
END;
END FullChar1;
(******************************************************************************)
PROCEDURE FullChar2;
(* Display all pixels in a glyph using bitmap from PXL font file.
This procedure is assigned to DisplayOneChar when h/vscalefactor < 1.0.
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.
*)
CONST
maxviswords = 30;
(* SYSDEP: 30 * 32 = 960 bits wide.
Some sites may have very wide glyphs (such as a logo).
960 bits represents 3.2in on a 300 dpi device.
*)
TYPE
glyphrow = ARRAY [0..maxviswords-1] OF BITSET;
(* SYSDEP: BITSET is 32 bit word with elements 31,30,29,...,0 *)
VAR
vpmyo, hpmxo, (* vp-yo, hp-xo: glyph's top and left edges *)
top, bottom, left, right, (* visible edges of glyph *)
scaledv, scalednextv, (* scaled vertical positions for rows *)
scaledh, (* scaled horizontal positions within row *)
scaledwidth, scaledheight, (* scaled width and height of row *)
thisrow, thisbit (* in paper coordinates *)
: INTEGER;
row : glyphrow; (* holds VISIBLE bits in one row of glyph;
possibly > one row if vscalefactor < 1.0 *)
wordsperrow, (* rows of PXL glyph are word aligned *)
firstbit, lastbit, (* somewhere in 0 .. wordsperrow*32-1 *)
firstword, lastword, (* somewhere in 0 .. wordsperrow-1 *)
endword, (* = visible words in row, - 1 *)
wordpos, (* 0 .. endword *)
bitpos, (* 31 .. 0 *)
i : CARDINAL;
inrun : BOOLEAN; (* are we in a run of black pixels in row? *)
BEGIN
WITH thisfontinfo^ DO
WITH thischarinfo^.chartable[thischar] DO
WITH pixelptr^[code] DO
IF mapadr = 0 THEN RETURN END; (* glyph all white or absent *)
(* check if any part of glyph is visible *)
vpmyo := vp-yo;
hpmxo := hp-xo;
IF RectangleVisible
(vpmyo,vpmyo+ht-1,hpmxo,hpmxo+wd-1, (* glyph edges *)
top,bottom,left,right) (* visible part *)
THEN
IF NOT fontopen THEN (* only open once *)
OpenFontFile;
fontopen := TRUE;
END;
wordsperrow := (wd + 31) DIV 32; (* words in one row of bitmap *)
firstbit := CARDINAL(left-hpmxo); (* first visible bit *)
lastbit := CARDINAL(right-hpmxo); (* last visible bit *)
firstword := firstbit DIV 32; (* first visible word *)
lastword := lastbit DIV 32; (* last visible word *)
endword := lastword - firstword;
(* DEBUG
(* we impose a limit on width of glyph (unlikely to be exceeded) *)
IF endword > maxviswords-1 THEN
StartText;
ClearMessageLine;
WriteString('Glyph '); WriteCard(code);
WriteString(' too wide!');
WaitForReturn;
StartGraphics;
END;
GUBED *)
(* set the visible words in row to 0 *)
FOR i := 0 TO endword DO row[i] := {} END;
(* calculate scaled v coord of first visible row *)
scaledv := ScaleVpos(top - windowtop) + windowv;
(* only consider visible rows; thisrow := top to bottom *)
thisrow := top;
LOOP
(* move to first byte of first visible word in this row *)
MoveToPXLByte(4 * (mapadr + (CARDINAL(thisrow - vpmyo) * wordsperrow)
+ firstword));
(* get row of visible words from PXL file and OR with row array *)
FOR wordpos := 0 TO endword DO
row[wordpos] := BITSET(SignedPXLQuad()) + row[wordpos];
(* set union *)
END;
(* calculate scaled v coord of next row *)
scalednextv := ScaleVpos(thisrow + 1 - windowtop) + windowv;
scaledheight := scalednextv - scaledv;
IF (scaledheight > 0) OR (thisrow = bottom) THEN
(* display black pixels in row, doing any h/v expansion *)
IF scaledheight < 1 THEN scaledheight := 1 END; (* avoid 0 *)
inrun := FALSE;
bitpos := 31 - (firstbit MOD 32); (* 31..0 *)
wordpos := 0;
(* only consider visible bits; thisbit := left to right *)
thisbit := left;
LOOP
IF bitpos IN row[wordpos] THEN (* start/continue run *)
IF NOT inrun THEN (* remember start of run *)
inrun := TRUE;
scaledh := ScaleHpos(thisbit - windowleft) + windowh;
END;
ELSIF inrun THEN (* 0 bit has ended run *)
inrun := FALSE;
scaledwidth := ScaleHpos(thisbit - windowleft) + windowh
- scaledh;
IF scaledwidth < 1 THEN scaledwidth := 1 END; (* avoid 0 *)
ShowRectangle
(scaledh,scaledv,scaledwidth,scaledheight,CHR(code));
END;
IF thisbit = right THEN EXIT (* bit loop *) END;
IF bitpos = 0 THEN (* look at first bit in next word of row *)
INC(wordpos);
bitpos := 31;
ELSE (* look at next bit in word *)
DEC(bitpos);
END;
INC(thisbit);
END; (* bit loop *)
IF inrun THEN (* show run at end of row; INC thisbit *)
scaledwidth := ScaleHpos(thisbit + 1 - windowleft) + windowh
- scaledh;
IF scaledwidth < 1 THEN scaledwidth := 1 END; (* avoid 0 *)
ShowRectangle
(scaledh,scaledv,scaledwidth,scaledheight,CHR(code));
END;
IF thisrow = bottom THEN EXIT (* row loop *) END;
(* else reset the visible words in row to 0 *)
FOR i := 0 TO endword DO row[i] := {} END;
END;
scaledv := scalednextv;
INC(thisrow);
END; (* row loop *)
charvisible := TRUE;
ELSE
charvisible := FALSE; (* checked in DisplayChars *)
END;
END;
END;
END;
END FullChar2;
(******************************************************************************)
PROCEDURE OpenFontFile;
(* If thisfontinfo^.fontspec can't be opened then pixel table has been
loaded with dummyfont values (either the user requested an invalid font
magnification or the fontspec was truncated in BuildFontSpec).
*)
BEGIN
StartText;
ClearMessageLine;
WriteString("Drawing characters from ");
WITH thisfontinfo^ DO
IF OpenPXLFile(fontspec) THEN
WriteString(fontspec);
WriteLn;
ELSIF OpenPXLFile(dummyfont) THEN
WriteString("dummy font!");
WriteLn;
ELSE
(* DEBUG
(* should never happen since MyPixelTableRoutine will detect 1st *)
ResetVDU;
WriteLn; WriteString('Bug in OpenFontFile!'); WriteLn;
RestoreTerminal; HALT;
GUBED *)
END;
END;
StartGraphics;
END OpenFontFile;
(******************************************************************************)
PROCEDURE 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.
*)
BEGIN
IF minhp < paperleft THEN
ClearMessageLine;
WriteString('Page beyond left edge by ');
WriteDimension(paperleft - minhp);
PaperMessage;
END;
IF maxhp > paperright THEN
ClearMessageLine;
WriteString('Page beyond right edge by ');
WriteDimension(maxhp - paperright);
PaperMessage;
END;
IF minvp < papertop THEN
ClearMessageLine;
WriteString('Page above top edge by ');
WriteDimension(papertop - minvp);
PaperMessage;
END;
IF maxvp > paperbottom THEN
ClearMessageLine;
WriteString('Page below bottom edge by ');
WriteDimension(maxvp - paperbottom);
PaperMessage;
END;
END CheckPageEdges;
(******************************************************************************)
PROCEDURE PaperMessage;
(* Called by CheckPageEdges to remind user of the paper size. *)
BEGIN
CASE currentunits OF
inunits : WriteString('in') |
cmunits : WriteString('cm') |
mmunits : WriteString('mm') |
pcunits : WriteString('pc') |
ptunits : WriteString('pt') |
pxunits : WriteString('px')
END;
WriteString('! (Paper is ');
WriteDimension(paperwd); WriteString(' by ');
WriteDimension(paperht); Write(')');
WaitForReturn;
ClearMessageLine;
END PaperMessage;
(******************************************************************************)
PROCEDURE Finish;
BEGIN
CloseDVIFile;
ClearScreen;
MoveToTextLine(1);
WriteLn;
ResetVDU;
RestoreTerminal;
(* And HALT.
Note that ResetVDU and RestoreTerminal should be called before each HALT.
ResetVDU is called before any final message since it might erase the screen.
*)
END Finish;
(******************************************************************************)
BEGIN
TopLevel;
END dvitovdu.