//
// "FXShowMath.cpp" Copyright A C Norman 2004-2007
//
//
// Code to layout mathematical formulae for display. Formulae are
// described in a TeX-like style. This REQUIRES some Computer Modern
// typefaces to be available.
//
/******************************************************************************
* Copyright (C) 2004 by Arthur Norman, Codemist Ltd. All Rights Reserved. *
*******************************************************************************
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; *
* version 2.1 of the License. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* *
* See also the FOX Toolkit addendum to the LGPL, which also applies to this *
* code. This addedum gives, in addition to the rights granted by the LGPL, *
* permission to distribute this code statically linked against other code *
* without any need for that other code to have its source released. *
******************************************************************************/
/* Signature: 1fc4a0cf 04-Jan-2008 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef HAVE_LIBFOX
char FXShowMath_msg[] = "No FOX so no FXShowMath";
#else
#ifdef HAVE_XFT
#include <X11/Xft/Xft.h>
#endif
#include "fwin.h"
#include <fx.h>
#if FOX_MAJOR==1 && FOX_MINOR==0
# define FXSEL(a,b) MKUINT(a,b)
#endif
#include <fxkeys.h> // not included by <fx.h>
#include "fwin.h"
#include "FXTerminal.h"
#include "FXShowMath.h"
#include "FXDCNativePrinter.h"
#include <stdlib.h>
extern "C" {
extern int directoryp(char *a, char *b, size_t n);
extern int file_readable(char *a, char *b, size_t n);
}
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#ifdef WIN32
#else
#include <X11/Xlib.h>
#endif
static int DEBUGFONT = 0;
// I make the size of my memory pool depend on the size of a pointer
// because the size of box components depends on that. The setting here
// will use 128K on a 32-bit machine or 256K on a 64-bit one and will
// cache around say 80 full lines of maths display. Beyond that the
// system would need to re-parse for redisplay.
#define memoryPoolSize (0x4000*sizeof(void *))
static void *memoryPool = NULL;
static unsigned int memoryPoolIn, memoryPoolOut;
int handleFromPoolPointer(Box *p)
{
return (int)((char *)p - (char *)memoryPool);
}
Box *poolPointerFromHandle(int i)
{
return (Box *)((char *)memoryPool + i);
}
static void setupMemoryPool()
{
memoryPool = (void *)malloc(memoryPoolSize);
if (memoryPool == NULL)
{ printf("Failed to allocate memory\n");
exit(1);
}
memoryPoolIn = memoryPoolOut = 0;
}
static void poolDestroyChunk();
static void *poolAllocate(unsigned int n)
{
if (n >= memoryPoolSize) return NULL;
for (;;)
{ int spare = memoryPoolOut - memoryPoolIn;
if (spare <= 0) spare += memoryPoolSize;
if (n < (unsigned int)spare) break;
poolDestroyChunk();
}
unsigned int next = memoryPoolIn + n;
if (next > memoryPoolSize)
{ while (memoryPoolOut > memoryPoolIn) poolDestroyChunk();
memoryPoolIn = 0;
while (memoryPoolOut <= n) poolDestroyChunk();
next = n;
}
void *r = (void *)((char *)memoryPool + memoryPoolIn);
memoryPoolIn = next;
return r;
}
struct IntVoidStar
{
void *v;
int i;
};
static void *chunkStart;
// Memory for a single formula will live in a "chunk", and chunks run
// cyclically around a region of memory. If at any stage I try to make a
// new chunk grow so that it would wrap round and overlap an earlier-allocated
// one I will discard that earlier chunk. When I do so I notify its "owner"
// so that they will not rely on it after its demise.
static void *poolStartChunk(int owner)
{
chunkStart = poolAllocate(sizeof(struct IntVoidStar));
IntVoidStar *rp = (IntVoidStar *)chunkStart;
rp->i = owner;
rp->v = NULL;
return chunkStart;
}
static void poolEndChunk()
{
IntVoidStar *rp = (IntVoidStar *)chunkStart;
rp->v = (void *)((char *)memoryPool + memoryPoolIn);
}
static void poolDestroyChunk()
{
if ((void *)((char *)memoryPool + memoryPoolOut) == chunkStart)
{ printf("Attempt to create over-large chunk\n");
exit(1);
}
IntVoidStar *rp = (IntVoidStar *)((char *)memoryPool + memoryPoolOut);
reportDestroy(rp->i); // inform owner
memoryPoolOut = (int)((char *)rp->v - (char *)memoryPool);
}
typedef struct TexState
{
unsigned char currentFont;
unsigned char insideFrac;
} TeXState;
TeXState currentState;
#define currentSize (currentState.currentFont & FontSizeMask)
// Some types. I have capacity in my encoding for up to 16 types of box.
#define BoxText 0x00
#define BoxNest 0x01
#define BoxNest3 0x02
#define BoxSym 0x03
#define BoxBracket 0x04
#define BoxMatrix 0x05
#define BoxFrame 0x06
#define BoxTop 0x07
#define NestMask 0xf0 // I use 0x0c bits for size information...
#define BoxTower 0x00 // one item over another, does not centre items
#define BoxStack 0x10 // one item over another, centred
#define BoxFraction 0x20 // one item over another, centred & with f-bar
#define BoxBeside 0x30
#define BoxAdjacent 0x40 // just like Beside, but moves boxes closer
#define BoxSubscript 0x50
#define BoxSuperscript 0x60
#define BoxBothScripts 0x70
#define BoxOverstrike 0x80
#define BoxPadSpace 0x90
#define SymTypeMask 0xf0
#define SymStar 0x00
#define SymComma 0x10
#define SymExclam 0x20
#define SymColon 0x30
#define SymSemiColon 0x40
#define SymBlank 0x50
#define SymNothing 0x60
#define SymUnderscore 0x70
#define SymRule 0x80
static Box *makeSymBox(int type)
{
Box *b = (Box *)poolAllocate(sizeof(SymBox));
b->text.type = BoxSym;
b->text.flags = type + currentSize;
b->text.height = 0;
b->text.depth = 0;
b->text.width = 0;
return b;
}
Box *makeTextBox(const char *s, int n, int fontStyle)
{
char *ss = (char *)poolAllocate(n+1);
Box *b = (Box *)poolAllocate(sizeof(TextBox));
memcpy(ss, s, n+1);
b->text.type = BoxText;
b->text.flags = fontStyle;
b->text.height = 0;
b->text.depth = 0;
b->text.width = 0;
b->text.n = n;
b->text.text = ss;
return b;
}
static Box *makeBracketBox(Box *b1, char left, char right)
{
Box *b = (Box *)poolAllocate(sizeof(BracketBox));
b->bracket.type = BoxBracket;
b->bracket.flags = currentSize;
b->bracket.height = 0;
b->bracket.depth = 0;
b->bracket.width = 0;
b->bracket.sub = b1;
b->bracket.leftBracket = left;
b->bracket.rightBracket = right;
b->bracket.dx = 0;
// \sqrt[n] will fill in any extra by hand...
b->bracket.dx1 = 0;
b->bracket.dy1 = 0;
b->bracket.sub1 = NULL;
return b;
}
static Box *makeMatrixBox(Box *b1)
{
Box *b = (Box *)poolAllocate(sizeof(MatrixBox));
b->matrix.type = BoxMatrix;
b->matrix.flags = currentSize;
b->matrix.height = 0;
b->matrix.depth = 0;
b->matrix.width = 0;
b->matrix.sub = b1;
b->matrix.dy = 0;
return b;
}
static Box *makeNestBox(int style, Box *b1, Box *b2)
{
Box *b = (Box *)poolAllocate(sizeof(NestBox));
b->nest.type = BoxNest;
b->nest.flags = style + currentSize;
b->nest.height = 0;
b->nest.depth = 0;
b->nest.width = 0;
b->nest.sub1 = b1;
b->nest.sub2 = b2;
b->nest.dx1 = b->nest.dy1 = 0;
b->nest.dx2 = b->nest.dy2 = 0;
return b;
}
static Box *makeNest3Box(int style, Box *b1, Box *b2, Box *b3)
{
Box *b = (Box *)poolAllocate(sizeof(NestBox3));
b->nest3.type = BoxNest3;
b->nest3.flags = style + currentSize;
b->nest3.height = 0;
b->nest3.depth = 0;
b->nest3.width = 0;
b->nest3.sub1 = b1;
b->nest3.sub2 = b2;
b->nest3.sub3 = b3;
b->nest3.dx1 = b->nest3.dy1 = 0;
b->nest3.dx2 = b->nest3.dy2 = 0;
b->nest3.dx3 = b->nest3.dy3 = 0;
return b;
}
static Box *makeFrameBox(Box *b0)
{
Box *b = (Box *)poolAllocate(sizeof(FrameBox));
b->frame.type = BoxFrame;
b->frame.flags = currentSize;
b->frame.height = 0;
b->frame.depth = 0;
b->frame.width = 0;
b->frame.sub = b0;
return b;
}
static Box *makeTopBox(Box *b0)
{
Box *b = (Box *)poolAllocate(sizeof(TopBox));
b->top.type = BoxTop;
b->top.flags = currentSize;
b->top.height = 0;
b->top.depth = 0;
b->top.width = 0;
b->top.sub = b0;
b->top.chunk = chunkStart;
b->top.measuredSize = -1;
return b;
}
///////////////////////////////////////////////////////////////////////////
// I will use (up to) 36 fonts here:
// Roman, Maths-italic, Symbol, Extra
// with each in main, subscript and sub-subscript sizes, but with
// a range of possible scales
// I have a set of metrics for the Computer Modern fonts that I use. They
// were extracted from the associated "AFM" files, but converted into
// static C tables for quick access. The table does not provide full
// information - it gives bounding boxes for the font as a whole together
// with widths of individual characters. At least at present I do not record
// individual character heights or kerning information there.
#include "cmfont-info.c"
static cm_font_info *findFont(const char *name)
{
char ss[32];
strcpy(ss, name);
// The info in the table has names in upper case. So I will force the
// name in my request to be in upper case to match.
for (char *p=ss; *p!=0; p++) *p = toupper(*p);
for (unsigned int i=0; i<sizeof(cm_font_widths)/sizeof(cm_font_info); i++)
{ if (strcmp(ss, cm_font_widths[i].name) == 0)
return &cm_font_widths[i];
}
return NULL;
}
// The master set of fonts, covering all scales. I extract relevant
// into into mathFont etc as necessary. If printDC is not NULL I will
// suppose I am in the middle of printing something and will use it, rather
// then the screen-access procedures that I normally use. In the printDC
// case I will use real local fonts in a Windows case when I am printing
// to a windows printing device context, but when using ANY sort of system
// if I am trying to generate Postscript I will use AFM metrics for the
// mathematical fonts that I want. In such case I will want the "font" to
// be a reference to the metric tables together with some scale information.
// @@@@ I will work on WINDOWS printing first... and assume that under Windows
// I am necessarily using a direct printer device context...
FXDCNativePrinter *printDC = NULL;
int mathFontSize = -1;
void *masterFont[36];
static int masterFontHeight[36];
static int masterFontDepth[36];
static int masterFontWidth[36];
static cm_font_info *masterFontFontInfo[36];
// The array "font" will either hold FXFont or XftFont references
// and fwin_use_xft will discriminate about which to use. The arrays and
// variables here relate to the fonts in use at the current scale.
void *mathFont[12];
int mathWidth;
static int mathFontHeight[12];
static int mathFontDepth[12];
static int mathFontWidth[12];
static cm_font_info *mathFontFontInfo[12];
// The next few are no longer used...
// static int regHeight, scriptHeight, scriptScriptHeight;
// static int regDepth, scriptDepth, scriptScriptDepth;
// static int regWidth, scriptWidth, scriptScriptWidth;
// Short-hand to reference individual fonts.
#define frm mathFont[FntRoman+FntRegular]
#define fmi mathFont[FntItalic+FntRegular]
#define fsy mathFont[FntSymbol+FntRegular]
#define fex mathFont[FntExtension+FntRegular]
#define fsrm mathFont[FntRoman+FntScript]
#define fsmi mathFont[FntItalic+FntScript]
#define fssy mathFont[FntSymbol+FntScript]
#define fsex mathFont[FntExtension+FntScript]
#define fssrm mathFont[FntRoman+FntScrScr]
#define fssmi mathFont[FntItalic+FntScrScr]
#define fsssy mathFont[FntSymbol+FntScrScr]
#define fssex mathFont[FntExtension+FntScrScr]
///////////////////////////////////////////////////////////////////////////
// When I select a font I want the version from that family that
// has design-size best matched to the size that it will appear.
// The scheme here recognises the variants made available with the
// Computer Modern set and selects what to use.
static const char *cmrSizesTable[] =
{ NULL, NULL, NULL, NULL, NULL, // for sizes 0,1,2,3,4
"cmr5", "cmr6", "cmr7", "cmr8", "cmr9", // 5, 6, 7, 8, 9
"cmr10", "cmr10", "cmr12", "cmr12", // 10, 11, 12, 13
"cmr12", "cmr12" // 14, 15
};
#define cmrSize(n) (fwin_use_xft ? "cmr10" : \
(n)<5 ? "cmr5" : \
(n) > 15 ? "cmr17" : cmrSizesTable[n])
static const char *cmmiSizesTable[] =
{ NULL, NULL, NULL, NULL, NULL, // for sizes 0,1,2,3,4
"cmmi5", "cmmi6", "cmmi7", "cmmi8", "cmmi9", // 5, 6, 7, 8, 9
"cmmi10", "cmmi10" // 10, 11
};
#define cmmiSize(n) (fwin_use_xft ? "cmmi10" : \
(n)<5 ? "cmmi5" : \
(n) > 11 ? "cmmi12" : cmmiSizesTable[n])
static const char *cmsySizesTable[] =
{ NULL, NULL, NULL, NULL, NULL, // for sizes 0,1,2,3,4
"cmsy5", "cmsy6", "cmsy7", "cmsy8", "cmsy9" // 5, 6, 7, 8, 9
};
#define cmsySize(n) (fwin_use_xft ? "cmsy10" : \
(n)<5 ? "cmsy5" : \
(n) > 9 ? "cmsy10" : cmsySizesTable[n])
#define cmexSize(n) "cmex10"
#ifdef HAVE_XFT
// At some stage I may well move all of these to be fields within a
// suitable object, but while I only have ONE window on which I will
// display things using Xft I can make them simple static varaible and hence
// local to this file.
Display *dpy = NULL;
int screen;
XftDraw *ftDraw = NULL;
Visual *ftVisual = NULL;
Colormap ftColormap;
XRenderColor ftRenderBlack = {0x0000, 0x0000, 0x0000, 0xffff};
XRenderColor ftRenderWhite = {0xffff, 0xffff, 0xffff, 0xffff};
XftColor ftBlack, ftWhite;
XftFont *ftFont = NULL;
#endif
static void *getMathsFont(FXApp *app,
const char *name, const char *fallback,
int size)
{
#ifdef HAVE_XFT
// With Xft I do not seem to have a good way to check exactly what
// font gets found, and so to try to guarantee the best sanity I can I have
// only got here if I was able to add the custom fonts that I wanted. And
// I then use my "fallback" font name here, which is the one that the
// custom fonts were intended to provide. Thus with Xft I will use exactly
// cmr10, cmmi10, cmsy10 and cmex10 and NEVER Computer Modern fonts at any
// other size. Note that HAVE_XFT or at least fwin_use_xft should never
// end up true in a Windows world.
if (fwin_use_xft)
{ return XftFontOpen(dpy, screen,
XFT_FAMILY, XftTypeString, fallback,
XFT_SIZE, XftTypeDouble, 0.1*(double)size,
(const char *)0);
}
#endif
FXFontDesc fd;
FXFont *f;
FXString an;
for (int i=0;i<2;i++)
{
strcpy(fd.face, i==0 ? name : fallback);
fd.size = size; // NB size is in DECIPOINTS here
#if (FOX_MINOR<=4)
fd.weight = FONTWEIGHT_DONTCARE;
fd.slant = FONTSLANT_DONTCARE;
fd.setwidth = FONTSETWIDTH_DONTCARE;
fd.encoding = FONTENCODING_DEFAULT;
fd.flags = FONTPITCH_DEFAULT;
#else
fd.weight = 0;
fd.slant = 0;
fd.setwidth = 0;
fd.encoding = FONTENCODING_DEFAULT;
fd.flags = 0;
#endif
f = new FXFont(app, fd);
f->create();
// Now I check that the "actual name" bears at least some resemblence
// to the name asked for... and if not I ask the user to install
// fonts properly. Note that there is a fallback font-name to use.
char ucname[32], ucactual[32];
strcpy(ucname, fd.face);
an = f->getActualName();
strcpy(ucactual, an.text());
char *p;
for (p=ucname; *p!=0; p++) *p = toupper(*p);
for (p=ucactual; *p!=0; p++) *p = toupper(*p);
if (strncmp(ucname, ucactual, strlen(ucname)) == 0) return f;
delete f;
}
// The error exit here ought only to occur when the X client and server
// are different machines and I am NOT able to use Xft. It could
// arise if the "r38.fonts" directory had not been properly set up.
return NULL;
// I will NOT generate a diagnostic here, but I WILL try to report back
// at a higher level that all this fancy display stuff can not be supported.
//- printf("Font %s apparently not available\n", fd.face);
//- printf("Nearest match found was %s. Please review and\n", an.text());
//- printf("if necessary install the Computer Modern Fonts\n");
//- printf("Note that this font is needed on your X server,\n");
//- printf("typically the one whose screen you are looking at.\n");
//- fflush(stdout);
//- exit(1);
}
#if 0
static void showMetrics(int ch, void *ff)
{
// This uses FOX calls to measure characters. The results come back in
// screen coordinates, which are typically pixels. At present this is ONLY
// used for debugging!
char b[2];
b[0] = ch;
#ifdef HAVE_XFT
if (fwin_use_xft && printDC==NULL)
{ printf("showMetric not done for Xft yet\n");
return;
}
#endif
if (printDC==NULL)
{ printf("Char %.2x font %p height = %d depth = %d width = %d\n",
ch, ff,
((FXFont *)ff)->getTextHeight(b, 1),
((FXFont *)ff)->getFontDescent(),
((FXFont *)ff)->getTextWidth(b, 1));
}
else
{ printDC->setFont((FXFont *)ff);
printf("Char %.2x font %p height = %d depth = %d width = %d\n",
ch, ff,
(int)printDC->fntGetTextHeight(b, 1),
(int)printDC->fntGetFontDescent(),
(int)printDC->fntGetTextWidth(b, 1));
}
fflush(stdout);
}
#endif
// The next function may not be used right now, but when I start
// developing the Postscript printer support I will need it to help me
// debug things...
static void showAFMMetrics(int ch, int fn)
{
// This used the AFM data to try to compute sizes. The results are returned
// in points not pixels, and so are NOT compatible with the ones above. In
// particular scaling by a "pixels per point for this screen" would be
// needed to make them agree. At present if I apply such a scaling (by hand)
// I think that widths then match, but I am still confused about heights and
// depths. Only used for debugging at present.
cm_font_info *fi = masterFontFontInfo[fn];
int cap=fi->capheight, x=fi->xheight,
asc=fi->ascent, desc=fi->descent;
int w = fi->charwidth[ch & 0xff];
printf("Char %.2x font %x2 capH=%d xH=%d ascent=%d descent=%d width=%d\n",
ch, fn, cap, x, asc, desc, w);
fflush(stdout);
}
// LONGEST_LEGAL_FILENAME is a distinct HACK, and although on early versions
// of Windows there was a limit, with XP at least very long file paths are
// possible - and by exceeding this limit they could cause crashes. Tough
// Luck for now!
#ifndef LONGEST_LEGAL_FILENAME
#define LONGEST_LEGAL_FILENAME 1024
#endif
typedef struct localFonts
{
const char *name;
char *path;
} localFonts;
static localFonts fontNames[] =
{
{"cmex10", NULL},
{"cmmi10", NULL},
{"cmr10", NULL},
{"cmsy10", NULL}
};
#ifdef WIN32
static HMODULE gdi32 = NULL;
typedef int FontResourceExType(
LPCTSTR lpszFilename, // font file name
DWORD fl, // font characteristics
PVOID pdv); // reserved
// The next two flags instruct AddFontResourceEx that a font should be
// available only to this application and that other application should
// not even be able to see that it exists. I provide definitions here
// in case MinGW32 does not have them in its header files.
#ifndef FR_PRIVATE
#define FR_PRIVATE 0x10
#endif
#ifndef FR_NOT_ENUM
#define FR_NOT_ENUM 0x20
#endif
#define PRIVATE_FONT (FR_PRIVATE | FR_NOT_ENUM)
FontResourceExType *addFontEx = NULL;
static void unloadFonts(FXApp *appl)
{
// I have had some pain with this code! So my FIRST change was that under
// Windows 2000 and XP when the "Ex" version of AddFontResource is available
// I just do not even try to unload the fonts. That should not be
// damaging because in that case I have made them local to the current
// application and not enumerable by other applications, and because in that
// case Windows should discard then when my application exits.
//
// Under older versions of Windows I maybe ought to try to remove them,
// but I will in fact cop out and leave them present (and visible to others)
// until the computer concerned is next re-booted.
return;
#ifdef SKETCH_CODE_THAT_IS_NOT_USED_AT_PRESENT
// When I tried activating this code I saw crashes that I had difficulty
// understanding. And so I will just leave local fonts under XP etc to be
// tidied up by the operating system, and leave my Maths fonts still
// loaded under 95/98.
if (gdi32 == NULL) return;
int unloaded = 0;
for (int i=0; i<(int)(sizeof(fontNames)/sizeof(fontNames[0])); i++)
{ if (fontNames[i].path == NULL) continue;
// If the font had been added using AddFontResourceEx then when the
// application closes it will automatically get unloaded for me, so I will
// not put any effort into removing it here. If however I had used the
// older-style AddFontResource (NB no "Ex") as on W95, W98, then I will
// unload it now.
if (addFontEx == NULL)
{ RemoveFontResource(fontNames[i].path);
unloaded = 1;
}
free(fontNames[i].path);
fontNames[i].path = NULL;
}
if (unloaded) SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
FreeLibrary(gdi32);
gdi32 = NULL;
#endif
}
static int fontNeeded = 0;
static int CALLBACK fontEnumProc(
const LOGFONTA *lpelfe, // logical-font data
const TEXTMETRICA *lpntme, // physical-font data
DWORD FontType, // type of font
LPARAM lParam) // application-defined data
{
fontNeeded = 0;
return 0;
}
#else // WIN32
int nExistingPaths = 0;
char **existingPaths = NULL;
char **localPathP = NULL;
static void unloadFonts(FXApp *appl)
{
if (!fwin_use_xft)
{ XSetFontPath((Display *)appl->getDisplay(),
existingPaths, nExistingPaths);
XFreeFontPath(existingPaths);
free(localPathP);
}
}
#endif // WIN32
#ifdef HAVE_XFT
static FT_UInt glyphs[100];
static int unmap(int ch);
static int convertGlyphs(const char *s, int len)
{
if (len > 100) len = 100;
for (int i=0; i<len; i++)
glyphs[i] = (unmap(s[i]) & 0xff) + 1;
return len;
}
static int xfWidth(void *ff, const char *s, int len)
{
XGlyphInfo w;
len = convertGlyphs(s, len);
XftGlyphExtents(dpy, (XftFont *)ff, glyphs, len, &w);
// Observe that I believe that I want xOff, the "advance" figure, rather
// than width here.
return w.xOff;
}
#endif
static void loadPrivateFonts(FXApp *appl)
{
for (int i=0; i<12; i++) mathFont[i] = NULL;
for (int i=0; i<36; i++) masterFont[i] = NULL;
// I want to ensure that the fonts needed will be available. On both
// Windows and X11 there are awkwardnesses about this:
// [Windows] AddFontResourceEx is only available on NT, 2000, XP, not
// on earlier systems.
// AddMemFontResource can only add TrueType fonts, and also
// does not support older systems.
// Two nuisances emerge from this. (a) the font resources
// have to be in the form of a set of file on disc; (b) the
// fonts that I temporarily install will be visible to and
// enumerable by all aother applications. I work round this
// by making a dynamic search for "AddFontResourceEx" in the
// "gdi32" DLL and using it if it is found. So on NT/2000/XP
// other applications will not be disturbed by any of my private
// fonts.
// [X11] A really very much want to have some of the Computer Modern
// maths fonts available to me. Because with X there is a logical
// distinction between the X server and client, using traditional
// X would mean that I would HAVE To have those fonts installed on
// the server - which is remote from the client upon which my code
// runs. So when it is available and unless the user has asked me
// not to I will use Xft and the Render extension to do client-
// side rendering. This is a fairly recent development in X and
// so may not always be available. Also it puts extra communication
// load on the client-server link so may not be nice when using
// a slow network. Font naming under Xft/fontconfig is very
// different from the older model, and my code here only attempts
// to access my own private Truetype/Type1 version of the CM fonts,
// where I have taken trouble to ensure that their Truetype names
// are set in the way I want them to be.
// I seem to be having some delicacies about character encoding
// and Xft. I work around this by assuming that the fonts I will
// use with Xft are JUST the ones that I supply, and I then use
// Glyph rather than Text access. BEWARE that trying to use
// fonts other than the ones I supply will cause trouble.
//
// My private font directory contains just 4 fonts
// cmr10 cmmi10 cmsy10 cmex10
// BUT when I am going to display things on the screen and unless I am
// using Xft I will try to use any from
// cmr5, 6, 7, 8, 9, 10, 12, 17
// cmmi5, 6, 7, 8, 9, 10, 12
// cmsy5, 6, 7, 8, 9, 10
// cmex10
// which come in the AMS set of Computer Modern Fonts, which can be
// downloaded freely either directly from AMS or from any major TeX
// archive. So if those extended fonts are installed you may get slightly
// more refined presentation on some platforms.
//
#ifdef WIN32
addFontEx = NULL;
gdi32 = LoadLibrary("Gdi32.dll");
if (gdi32 != NULL)
{ addFontEx = (FontResourceExType *)
GetProcAddress(gdi32, "AddFontResourceExA");
// NB the "A" on the end of the name selects the ANSI rather than the Unicode
// variant.
if (addFontEx == NULL) // Sorry not available
{ FreeLibrary(gdi32);
gdi32 = NULL;
}
}
HDC hDC = CreateCompatibleDC(NULL);
LOGFONT lf;
// I check each of the fonts that this application wants to see if they
// are already installed. If they are then there is no merit in installing
// them for myself.
int newFontAdded = 0;
for (int i=0; i<(int)(sizeof(fontNames)/sizeof(fontNames[0])); i++)
{ strcpy(lf.lfFaceName, fontNames[i].name);
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfPitchAndFamily = 0;
fontNeeded = 1;
fontNames[i].path = NULL;
EnumFontFamiliesEx(hDC, &lf, fontEnumProc, 0, 0);
if (!fontNeeded) continue;
char *ff = (char *)malloc(strlen(programDir) +
strlen(fontNames[i].name)+16);
strcpy(ff, programDir);
strcat(ff, "\\r38.fonts\\");
strcat(ff, fontNames[i].name); strcat(ff, ".ttf");
DWORD attributes = GetFileAttributes(ff);
// If the file I want to use is not present I will not even try to add it as
// a font resource. I want to move this code towards being silent in use so
// the printf() statements I had when I was first debugging this are going
// away.
if (attributes != INVALID_FILE_ATTRIBUTES &&
(attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
{ fontNames[i].path = ff;
if (addFontEx != NULL)
{ if ((*addFontEx)(ff, PRIVATE_FONT, 0) == 0)
{ continue;
printf("Failed to add font %s\n", ff);
fflush(stdout);
}
}
else if (AddFontResource(ff) == 0)
{ continue;
printf("Failed to add font %s\n", ff);
fflush(stdout);
}
newFontAdded = 1;
}
}
DeleteDC(hDC);
if (newFontAdded) SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
#else // WIN32
nExistingPaths = 0;
existingPaths = NULL;
localPathP = NULL;
if (fwin_use_xft)
{ // I have already fudged the Xft font-access configuration so
// that ONLY my own fonts will be available.
}
else
{
// On traditional X11 I will always just append my own font directory at the
// end of the existing font search path. The means that fonts already
// installed on the system will take precedence (I will suppose that they
// had been installed properly!). This lets anybody who has better-hinted
// versions of the CM fonts use them. But if they play that game they had
// BETTER ensure that their font is compatible with the one I use in all
// respects.
existingPaths = XGetFontPath((Display *)appl->getDisplay(), &nExistingPaths);
char localPath[LONGEST_LEGAL_FILENAME], lp1[LONGEST_LEGAL_FILENAME];
sprintf(localPath, "%s/r38.fonts/", programDir);
// Note that the fonts live in "r38.fonts" whatever the name of my executable
// is. If this does not exist or is not a directory I will not even try to
// add it to the fonts path. However the code here does not do anything to
// verify that the directory contains sensible things.
if (directoryp(lp1, localPath, strlen(localPath)))
{ localPathP = (char **)malloc((nExistingPaths+1)*sizeof(char *));
for (int j=0; j<nExistingPaths; j++)
localPathP[j] = existingPaths[j];
localPathP[nExistingPaths] = &localPath[0];
XSetFontPath((Display *)appl->getDisplay(),
localPathP, nExistingPaths+1);
}
}
#endif // WIN32
}
static double scaleTable[9] =
{
1.0,
1.1892,
1.4142,
1.6818,
2.0,
2.3784,
2.8282,
3.3636,
4.0
};
// this returns 0 if it fails.
int changeMathFontSize(FXApp *appl, int mainSize)
{
void *previousFont[36];
// I keep the current fonts until I have managed to open a new set, This
// may use up extra memory etc but is done so I can fall-back to retaining
// the existing set if something fails.
for (int i=0; i<36; i++)
previousFont[i] = masterFont[i];
// The user can pass a "main size" explicitly (in decipoints), but a negative
// argument instead specifies the width of the window, and I try to match
// that against 80 "M" in a half-sized font. The effect should be that SMALL
// mathematical formulae get displayed using a font that would fit around
// 40 of its widest characters across the screen, and around 60 or so
// "average width" chars. I think that seems reasonably visually pleasing...
if (mainSize < 0) // select font size based on screen width
{ int sw = -mainSize;
#define probeSize 400
// I do things in two stages here, so I need to explain why!
// I first work in terms of a main font at 40 points and hence a "subscript"
// one at 28 points. I argue that at most plausible screen resolutions the
// width of an "M" here will be enough pixels that measurements will not be
// too badly corrupted by discretisation issues, and so I will end up with
// a prediction of the correct font size that will be accurate to within a
// reasonably small error. However if the user had a full set of CM fonts
// installed when I scale down and use a 7 or 10 point (say) script font then
// the version at small visual size is liable to have been designed to be
// thicker, so metrics got be just linear scaling from a 28-point version
// cen not be treated as reliable. However those simple measurements will
// still get me reasonably close. So I will measure again at that scale and
// make a final adjustment. I will NOT bother to iterate this procedure to
// convergence!
mainSize = probeSize;
int smallSize = (mainSize+1)/2;
int smallWidth = 0;
fsmi = getMathsFont(appl, cmmiSize(smallSize), "cmmi10", smallSize);
// At this stage all the existing fonts remain untouched.
if (fsmi == NULL) return 0; // FAIL
#ifdef HAVE_XFT
if (fwin_use_xft)
smallWidth = xfWidth(fsmi, "MMMMMMMMMM", 10);
else
#endif
smallWidth = ((FXFont *)fsmi)->getTextWidth("MMMMMMMMMM", 10);
smallWidth *= 8; // width of 80 "M" in small font
mainSize = (probeSize*sw)/smallWidth;
#ifdef HAVE_XFT
if (fwin_use_xft) XftFontClose(dpy, (XftFont *)fsmi);
else
#endif
delete (FXFont *)fsmi; // may leave bad font in printDC
fsmi = NULL;
smallSize = (mainSize+1)/2;
fsmi = getMathsFont(appl, cmmiSize(smallSize), "cmmi10", smallSize);
// At this stage all the existing fonts remain untouched.
if (fsmi == NULL) return 0; // FAIL
#ifdef HAVE_XFT
if (fwin_use_xft)
smallWidth = xfWidth(fsmi, "MMMMMMMMMM", 10);
else
#endif
smallWidth = ((FXFont *)fsmi)->getTextWidth("MMMMMMMMMM", 10);
smallWidth *= 8; // width of 80 "M" in small font
mainSize = (mainSize*sw)/smallWidth;
#ifdef HAVE_XFT
if (fwin_use_xft) XftFontClose(dpy, (XftFont *)fsmi);
else
#endif
delete (FXFont *)fsmi;
fsmi = NULL;
}
// I will insist that the main font be at least 10 point. Then at smallest
// scale I will have a regular size of 5pt. That would lead to
// scripts at 4pt and scripted scripts at 2.5pt. This is a pretty grossly
// tiny lower limit and if the smallest variation involved ever gets used
// it will not be readable. However I find that on a typical screen the
// size I need is around 13 points, not too far from this limit.
// NOTE that I work in decipoints here.
if (mainSize < 100) mainSize = 100;
// Under Windows 2000/XP I make the maths fonts that I want available as
// private ones for this application. All existing fonts remain available at
// all times.
// Under Windows 95/98 I have added access to me fonts, but again existing
// ones remain available.
// Under raw X11 my fonts have been appended to the (global) list of
// font directories. As with Windows any fonts that the user has already
// installed remain available.
//
// With Xft I also have some application-specific fonts that I have
// merged in...
// Having ensured that the fonts that I need are available I create
// the 36 maths fonts that I can possibly use. I provide a font name
// that would be my first choice, tuned to the visual size that is to be
// used (eg cmr7), and a fall-back name (always 10-point visual size)
// that I guarantee by providing it in my own font directory. Under Xft I
// do NOT try the perfection-size since I do not know a way to check how well
// it works: I always go straight for the 10-point versions that I explicitly
// added as application-specific things.
int p = 0;
for (int i=0; i<9; i++)
{ int size = (int)(0.25*(double)mainSize*scaleTable[i] + 0.5);
int height=0, depth=0, width=0;
for (int j=0; j<4; j++)
{ const char *targetName, *fallbackName;
switch(j)
{
default:
case 0: targetName = cmmiSize(size/10); fallbackName = "cmmi10"; break;
case 1: targetName = cmrSize(size/10); fallbackName = "cmr10"; break;
case 2: targetName = cmsySize(size/10); fallbackName = "cmsy10"; break;
case 3: targetName = cmexSize(size/10); fallbackName = "cmex10"; break;
}
masterFont[p] = getMathsFont(appl, targetName, fallbackName, size);
if (masterFont[p] == NULL)
{ while (--p >= 0)
{ void *ff = masterFont[p];
#ifdef HAVE_XFT
if (fwin_use_xft)
XftFontClose(dpy, (XftFont *)ff);
else
#endif
delete (FXFont *)ff;
}
// Since opening fonts here failed I will restore the existing fonts, so that
// at least I have SOMETHING available to draw with.
for (int i=0; i<36; i++)
masterFont[i] = previousFont[i];
return 0; // FAIL
}
masterFontFontInfo[p] = findFont(targetName);
// There will be places where I want to position items based on the
// general size of the fonts that are in use rather than the size of the
// particular glyphs involved. So I record an "X" height and an "M" width
// for my Italic font in each of the sizes that I use.
// For cmr, rmmi and cmsy this seems to give a reasonably consistent
// idea of sizes. I find the metric returned for cmex somewhat strange!
//
// The way that I want to measure things is with "height" measuring up from
// the baseline and "depth" measuring down from it. The metrics that FXFont
// gives me record overall height, so I adjust for that here.
// Observe I set up the "cmmi" font first so I can measure it and record
// its general metrics for all the other fonts.
if (j==0)
{
#ifdef HAVE_XFT
if (fwin_use_xft)
{ height = ((XftFont *)masterFont[p])->ascent;
depth = ((XftFont *)masterFont[p])->descent;
width = xfWidth(masterFont[p], "M", 1);
}
else
#endif
{ depth = ((FXFont *)masterFont[p])->getFontDescent();
height = ((FXFont *)masterFont[p])->getTextHeight("X", 1) - depth;
width = ((FXFont *)masterFont[p])->getTextWidth("M", 1);
}
}
masterFontHeight[p] = height;
masterFontDepth[p] = depth;
masterFontWidth[p] = width;
p++;
}
}
setMathsFontScale(4);
mathFontSize = mainSize;
// Since I have managed to open the new fonts I can discard the old ones.
for (int i=0; i<36; i++)
{
#ifdef HAVE_XFT
// I will do some very magic things about font selection when I print to avoid
// problems here when Xft is around and I might otherwise get muddled between
// printer and screen font technology!
if (fwin_use_xft)
{ if (previousFont[i] != NULL)
XftFontClose(dpy, (XftFont *)previousFont[i]);
}
else
#endif
delete (FXFont *)previousFont[i];
previousFont[i] = NULL;
}
return 1; // OK!
}
// I have two interlocking ideas about font sizes. One is that for any
// formula I display I will have a Regular, Script and ScriptScript size
// of font, spanning a range of 2 in size. But as well as that I will want to
// be able to scale formulae to fit across the screen. If I always use a font
// that guarantees that even the largest formula would fit then generally
// everything looks cramped and tiny. So each set of 3 fonts will be made
// available in 5 scales: (1, 1.2, 1.4, 1.7, 2) where these will be selected
// so that "1.2" version expects 80 "M" to match my screen's width.
// changeMathsFontSize will set up the 9 fonts needed in each face
// (ie 1, 1.2, 1.4, 1.7, 2, 2.4, 2.8, 3.4, 4), while setMathsFontScale
// selects out the three that are to be used next. The argument to it
// should be 0, 1, 2, 3 or 4. Of these 4 is the default or preferred size and
// the smaller numbers are only used when formulae start to look too wide.
void setMathsFontScale(int scale)
{
if (scale < 0) scale = 0;
else if (scale > 4) scale = 4;
// In the master tables the arrangement is
// cmmi, cmr, cmsy, cmex 0.5 (ie script-script) 0,1,2,3
// cmmi, cmr, cmsy, cmex 4,5,6,7
// cmmi, cmr, cmsy, cmex 0.7 (ie script) 8,9,10,11
// cmmi, cmr, cmsy, cmex 12,13,14,15
// cmmi, cmr, cmsy, cmex 1.0 (ie regular) 16,17,18,19
// in sizes tiny and so on upwards.
int base = 4*scale;
for (int i=0; i<3; i++) // sizes
{ for (int j=0; j<4; j++) // faces
{ int x, y;
// Now I have the mess of mapping master to local font index info!
x = j+4*i;
y = (j==0?1: j==1?0 : j); // flip cmr and cmmi index values!
y = base + 16 - 8*i + y; // in mathFont biggest comes first!
mathFont[x] = masterFont[y];
mathFontHeight[x] = masterFontHeight[y];
mathFontDepth[x] = masterFontDepth[y];
mathFontWidth[x] = masterFontWidth[y];
mathFontFontInfo[x] = masterFontFontInfo[y];
}
}
mathWidth = mathFontWidth[FntItalic+FntRegular];
}
///////////////////////////////////////////////////////////////////////////
// This section measures boxes and thereby establish a layout.
static int intmax(int a, int b)
{
return (a > b) ? a : b;
}
#if 0
// These could, I guess, be wanted at some stage...
static int intmin(int a, int b)
{
return (a < b) ? a : b;
}
static int intabs(int a)
{
return (a < 0) ? -a : a;
}
#endif
static int remap(int);
// This is intended to select a "bracket" (in other words "big delimiter")
// size measured in units of 0.5 the height of the smallest such delimiter
// in the cmex font. However there is a special case! For modest size
// formulae the code returns 1 which indicates "use a glyph from cmr or
// cmsy, rather than the one from cmex".
static int bracketCount(int flags, int height, int depth)
{
int size = flags & FontSizeMask;
int h = mathFontHeight[size], d = mathFontDepth[size];
if (height<=h && depth<=d) return 1; // use simple cmsy or cmr glyph
height += depth;
h = (9*(h + d) + 4); // 8 * total height of the cmex glyph
// I round up to ensure that the delimiter can totally enclose the contents
return (16*height + h - 1)/h;
}
static int bracketWidth(char type, int flags, int height, int depth)
{
int n = bracketCount(flags, height, depth);
// n is the size of bracket needed, measured in units of 0.5 times the
// normal cmex font height
int fnt = FntExtension, ch = 0x00;
if (n == 1)
{ fnt = FntSymbol;
switch(type)
{
case '.':
return 0;
case '(':
fnt = FntRoman;
ch = 0x28;
break;
case ')':
fnt = FntRoman;
ch = 0x29;
break;
case '[':
fnt = FntRoman;
ch = 0x5b;
break;
case ']':
fnt = FntRoman;
ch = 0x5d;
break;
case '{':
ch = 0x64;
break;
case '}':
ch = 0x65;
break;
case '|': // LEFT vertical bar
ch = 0x6a;
break;
case '!': // RIGHT vertical bar
ch = 0x6a;
break;
case 'Q': // square root marker
ch = 0x70; // NB this is a descender
break;
case '/': // slash
ch = 0x36;
break;
case '\\': // backslash
ch = 0x6e;
break;
case 'L': // lfloor
ch = 0x62;
break;
case 'G': // lceil (like Gamma)
ch = 0x64;
break;
case 'J': // rfloor
ch = 0x63;
break;
case '7': // rceil
ch = 0x65;
break;
case '<': // langle
ch = 0x68;
break;
case '>': // rangle
ch = 0x69;
break;
case '#': // \| (double vertical bar)
ch = 0x6b;
break;
case '^': // uparrow
ch = 0x22;
break;
case 'v': // downarrow
ch = 0x23;
break;
case 'A': // Uparrow
ch = 0x2a;
break;
case 'V': // Downarrow
ch = 0x2b;
break;
case 'b': // updownarrow
ch = 0x6c;
break;
case 'B': // Updownarrow
ch = 0x6d;
break;
default:
printf("Attempt to measure unknown delimiter \"%c\"\n", type);
return 0;
}
}
else
{
if (n > 6) n = 6;
const char *s;
// All of this is amazingly tightly tied to the "cmex" font and the
// encoding it has for glyphs used to build up tall characters.
switch(type)
{
case '.':
return 0;
case '(':
s = "\x00\x10\x12\x20\x30";
break;
case ')':
s = "\x01\x11\x13\x21\x31";
break;
case '[':
s = "\x02\x68\x15\x22\x32";
break;
case ']':
s = "\x03\x69\x16\x23\x33";
break;
case '{':
s = "\x08\x6e\x1a\x28\x38";
break;
case '}':
s = "\x09\x6f\x1b\x29\x39";
break;
case '|': // LEFT vertical bar
s = "\x0c\x0c\x42\x42\x42";
break;
case '!': // RIGHT vertical bar
s = "\x0c\x0c\x43\x43\x43";
break;
case 'Q': // square root marker
s = "\x70\x71\x72\x73\x74";
break;
case 'L': // lfloor
s = "\x04\x6a\x16\x24\x36";
break;
case 'G': // lceil
s = "\x06\x6c\x18\x22\x32";
break;
case 'J': // rfloor
s = "\x05\x6b\x17\x25\x37";
break;
case '7': // rceil
s = "\x07\x6d\x19\x23\x33";
break;
case '^': // uparrow
s = "\x88\x78\x78\x78\x78";
break;
case 'v': // downarrow
s = "\x0c\x0c\x0c\x0c\x0c";
break;
case 'b': // updownarrow
s = "\x78\x78\x78\x78\x78";
break;
case 'A': // Uparrow
s = "\x7e\x7e\x7e\x7e\x7e";
break;
case 'V': // Downarrow
s = "\x77\x77\x77\x77\x77";
break;
case 'B': // Updownarrow
s = "\x7e\x7e\x7e\x7e\x7e";
break;
case '#': // \| double vertical line
s = "\x77\x77\x77\x77\x77";
break;
case '<': // langle
case '>': // rangle
case '\\': // backslash
case '/': // slash
default:
printf("Attempt to measure unknown delimiter \"%c\"\n", type);
return 0;
}
ch = s[n-2];
}
char ss[1];
ss[0] = remap(ch);
void *ff = mathFont[fnt + (flags & FontSizeMask)];
#ifdef HAVE_XFT
if (fwin_use_xft) return xfWidth(ff, ss, 1);
else
#endif
return ((FXFont *)ff)->getTextWidth(ss, 1);
}
static int bracketHeightDepth(char type, int flags, int height, int depth)
{
if (type == '.') return 0;
// the calculations here had better match the corresponding logic in
// paintBracket.
int size = flags & FontSizeMask;
int h = mathFontHeight[size], d = mathFontDepth[size];
int n = bracketCount(flags, height, depth);
// n is the size of bracket needed, measured in units of 0.5 times the
// normal cmex font height
// If I use a cmsy or cmr glyph its height is easy to work out!
if (n == 1)
{ if (type == 'Q') h += (h + d + 12)/25;
return (h<<16) | d;
}
int height1 = height + depth;
// the cmex glyphs seem to be (9/8) times as high as the cmr, cmmi
// and cmsy ones! So I scale here by that amount!
int height2 = (9*n*(h + d) + 8)/16;
// height2 will be the actual total height of the bracket that I draw.
int extra = (height2 - height1 + 1)/2;
if (extra > 0)
{ height += extra;
depth += extra;
}
if (type == 'Q') height += (h + d + 12)/25; // rule width.
return (height<<16) | depth;
}
// return width of column col in a BoxBeside row, or a negative
// value if there is not one.
static int widthOfColumn(Box *b, int col)
{
while (col != 0)
{ if (b->nest.type != BoxNest ||
b->nest.flags != BoxBeside) return -1;
b = b->nest.sub1;
col--;
}
if (b->nest.type != BoxNest ||
b->nest.flags != BoxBeside) return -1;
b = b->nest.sub2;
return b->text.width;
}
// return max width of any column with the given index, or a negative
// value if all rows in the matrix are shorter than that.
static int maxWidthInColumn(Box *b, int col)
{
int r = -1;
if (DEBUGFONT & 2) printf("find max width in column %d\n", col);
while (b->nest.type == BoxNest &&
b->nest.flags == BoxTower)
{ int w = widthOfColumn(b->nest.sub2, col);
if (DEBUGFONT & 2) printf("width in column %d is %d\n", col, w);
if (w > r) r = w;
if (DEBUGFONT & 2) printf("max now %d\n", r);
b = b->nest.sub1;
}
return r;
}
// Here b is a set of BoxBesides and I want to widen one of the
// ones within it.
static void setOneWidth(Box *b, int col, int w, int xgap)
{
int c0 = col;
Box *b0 = b;
if (DEBUGFONT & 2) printf("force width of column %d to %d in one row\n", col, w);
while (col != 0)
{ if (b->nest.type != BoxNest ||
b->nest.flags != BoxBeside) return;
b = b->nest.sub1;
col--;
}
if (b->nest.type != BoxNest ||
b->nest.flags != BoxBeside) return;
// Now b is a BoxBeside that represents the required cell of the
// matrix.
int w0 = b->nest.sub2->text.width; // current width of cell
int extra = w - w0;
if (DEBUGFONT & 2) printf("extra width needed = %d\n", extra);
b->nest.dx2 += extra/2; // centre the item
// Now all the enclosing boxes have got wider, so I need to adjust their
// record of their width.
if (c0 == 0)
{ b0->nest.width += extra;
return;
}
if (DEBUGFONT & 2) printf("Now need to insert intercolumn gap\n");
while (c0 > 0)
{ b0->nest.width += extra + xgap;
b0->nest.dx2 += extra + xgap;
b0 = b0->nest.sub1;
c0--;
}
b0->nest.width += extra + xgap;
return;
}
static void setWidthsAndCentre(Box *b, int col, int w, int xgap, int ygap)
{
Box *b0 = b;
int n = 0;
while (b0->nest.type == BoxNest &&
b0->nest.flags == BoxTower)
{ n++;
b0 = b0->nest.sub1;
}
int dy = ygap*(n-1);
if (DEBUGFONT & 2) printf("force width of column %d to %d\n", col, w);
while (b->nest.type == BoxNest &&
b->nest.flags == BoxTower)
{ setOneWidth(b->nest.sub2, col, w, xgap);
// I have meddled with the width of an item in one row so the node here needs
// its width changed to match.
b->nest.width = b->nest.sub2->nest.width;
if (DEBUGFONT & 2) printf("Width of row is now %d\n", b->nest.width);
b->nest.depth += dy;
b->nest.dy2 += dy;
dy -= ygap;
b = b->nest.sub1;
}
return;
}
// return 0 if column col (starting with 0) does not exist. Otherwise
// change widths etc in all relevant BoxBeside entries. Note that the
// code I have here will scan the matrix repeatedly to find columns.
// I view the costs as OK since for display I never expect to have
// seriously large matrices.
static int balanceColumns(Box *b, int col, int xgap, int ygap)
{
if (DEBUGFONT & 2) printf("balance column %d\n", col);
int w = maxWidthInColumn(b, col);
if (DEBUGFONT & 2) printf("column %d width = %d\n", col, w);
if (w < 0) return 0;
setWidthsAndCentre(b, col, w, xgap, ygap);
if (DEBUGFONT & 2) printf("column %d balanced\n", col);
return 1;
}
void measureBox(Box *b)
{
b->top.measuredSize = -1;
measureBox1(b);
}
int italicAdjust(int ch, int font)
{
// n
// In something like X
// I need to space the superscript across by a bit when the base ends in
// a character that is tall and leans across to the right. The amount of
// adjustment is produced here and is really a bit of a fudge!
int w = mathFontWidth[font];
switch (font & FontFamilyMask)
{
case FntItalic:
switch (ch)
{
case 0x00: case 0x04: case 0x06: case 0x07: // Gamma etc!
case 'f': case 'l':
case 'B': case 'C': case 'E': case 'F':
case 'H': case 'I': case 'J': case 'K':
case 'M': case 'N': case 'P': case 'S':
case 'T': case 'U': case 'V': case 'W':
case 'X': case 'Y': case 'Z':
return (w+6)/8;
case 'O': case 'Q':
return (w+8)/16;
default:
return 0;
}
default:
return 0;
}
}
void measureBox1(Box *b)
{
int f, h, w, d, dy, d1, h1;
int sqrtcount=0, sqrtunit=0;
void *ff;
TextBox *t;
SymBox *s;
NestBox *n;
NestBox3 *n3;
MatrixBox *m;
BracketBox *bb;
TopBox *tt;
if (DEBUGFONT & 4)
{ printf("measureBox1(%p)\n", b); fflush(stdout);
printf("type = %d\n", b->text.type); fflush(stdout);
}
switch (b->text.type)
{
case BoxSym:
s = &(b->sym);
if ((s->flags & SymTypeMask) == SymRule) return; // measurements fixed
s->height = 0;
s->depth = 0;
switch (s->flags & SymTypeMask)
{
case SymStar: // used as space between items that are multiplied
s->width = mathFontWidth[s->flags & FontSizeMask]/6;
return;
case SymComma: // narrow space
s->width = mathFontWidth[s->flags & FontSizeMask]/4;
return;
case SymExclam: // narrow negative space
s->width = -mathFontWidth[s->flags & FontSizeMask]/4;
return;
case SymColon: // medium space
s->width = mathFontWidth[s->flags & FontSizeMask]/2;
return;
case SymSemiColon: // wide space
s->width = mathFontWidth[s->flags & FontSizeMask];
return;
case SymBlank: // used by tmprint, it seems. Intent unclear to me!
s->width = mathFontWidth[s->flags & FontSizeMask]/3;
return;
case SymNothing: // used when I want an empty box as a placeholder
s->height = s->depth = s->width = 0;
return;
case SymUnderscore: // not a glyph in any font! So draw as a rule.
s->width = mathFontWidth[s->flags & FontSizeMask]/2;
return;
default:
printf("Unknown SymBox type %.2x to measure\n", s->flags);
return;
}
case BoxText:
t = &(b->text);
if (DEBUGFONT & 4)
{ printf("text = <%s>\n", t->text);
fflush(stdout);
}
ff = mathFont[t->flags & FontMask];
if (DEBUGFONT & 4)
{ printf("fontid = %.2x font = %p\n", t->flags, ff);
fflush(stdout);
}
h = mathFontHeight[t->flags & FontSizeMask];
d = mathFontDepth[t->flags & FontSizeMask];
#ifdef HAVE_XFT
if (fwin_use_xft) w = xfWidth(ff, t->text, t->n);
else
#endif
w = ((FXFont *)ff)->getTextWidth(t->text, t->n);
// Here I have a bit of shameless fudge. The "cmex" glyph for \int seems to
// record as its width a value that makes it finish just about at a point on
// its stem. I unilaterally add a little extra width here to get spacing that
// I like better! Furthermore I will position all "big" operators based on
// their mid-line
if (t->flags & FntBig)
{ int bigH = 0, hd = h+d;
// I know the heights of the "big" operators in the cmex font, so
// compute them here in terms of the height of my normal font. The
// numbers seem a little odd but are what I appear to observe from
// inspecting the font.
if (t->text[0] == 0x5a) // \int
{ w += mathFontWidth[t->flags & FontSizeMask]/3;
bigH = (2222*hd + 450)/900;
}
else bigH = (1400*hd + 450)/900;
hd = h - d;
h = (bigH + hd)/2;
d = (bigH - hd)/2;
}
if (DEBUGFONT & 4)
{ printf("text \"%s\" is size h=%d d=%d w=%d\n", t->text, h, d, w);
fflush(stdout);
}
t->height = h;
t->depth = d;
t->width = w;
return;
case BoxBracket: // This is for big brackets
bb = &(b->bracket);
// I have leftBracket, rightBracket and sub components here.
// The characters involved can be "(", "[", "{", "|" "." "}", "]", ")".
// "Q" (sqrt), "/", "\", "L", "G" (lceil), "J", "7" (rceil), "<", ">",
// "#" (double vertical bar), "^" (uparrow), "v" (downarrow), "A" (Uparrow)
// "V" (Downarrow), "b" (updownarrow), "B" (Updownarrow)
measureBox1(bb->sub);
bb->width = bb->sub->text.width;
h1 = bb->height = bb->sub->text.height;
d1 = bb->depth = bb->sub->text.depth;
f = bb->flags & FontSizeMask;
if (bb->leftBracket == 'Q')
{ h = mathFontHeight[f];
d = mathFontDepth[f];
h1 += (h + d + 12)/25; // make contents seem taller
// to leave gap before overbar
sqrtcount = bracketCount(f, h1, d1);
sqrtunit = h + d;
}
w = bracketWidth(bb->leftBracket, f, h1, d1);
bb->width += w;
bb->dx = w;
w = bracketWidth(bb->rightBracket, f, h1, d1);
bb->width += w;
h = bracketHeightDepth(bb->leftBracket, f, h1, d1);
d = (h & 0xffff);
h = (h >> 16) & 0xffff;
w = bracketHeightDepth(bb->rightBracket, f, h1, d1);
bb->height = intmax(bb->height, h);
bb->depth = intmax(bb->depth, d);
d = (w & 0xffff);
h = (w >> 16) & 0xffff;
bb->height = intmax(bb->height, h);
bb->depth = intmax(bb->depth, d);
if (bb->sub1 != NULL)
{ measureBox1(bb->sub1);
// OK, if I have a "sub1" box I had a radical sign that needs an explicit
// index give, as in for cube- or nth roots. I want to position the
// index just above the left end of the hook in the radical sign.
int downby = 0;
switch (sqrtcount)
{
case 1: downby = sqrtunit/2; break;
case 2: downby = (6*sqrtunit)/10; break;
case 3: downby = (15*sqrtunit)/16; break;
case 4: downby = (5*sqrtunit)/4; break;
case 5: downby = (8*sqrtunit)/5; break;
default:downby = bb->height + bb->depth - (10*sqrtunit)/5; break;
}
bb->dy1 = -(bb->height + bb->sub1->text.depth - downby);
h = bb->dy1 + bb->sub1->text.height;
bb->height = intmax(bb->height, h);
}
return;
case BoxMatrix:
m = &(b->matrix);
// I do an initial measure operation on the contents not paying any attention
// to the fact that the contents is in a matrix context. This will give me
// the "natural" size of each enclosed sub-box.
measureBox1(m->sub);
// Now I expect the contents to be (eg)
// ABOVE(ABOVE(row1,row2),row3)
// with each row a structure like BESIDE(BESIDE(col1, col2),col3).
// I need to scan this to find the greatest width in each column, and then
// re-work the BESIDE widths and offsets to centre each item in a field that
// width. I also need to ensure suitable gaps between rows and columns.
// And finally I will need to set the "dy" field to get the base-line where
// it neeeds to be.
{ int interColumnGap = mathFontWidth[m->flags & FontSizeMask];
int interRowGap = mathFontHeight[m->flags & FontSizeMask]/2;
for (int col=0;;col++)
{ if (!balanceColumns(m->sub, col,
interColumnGap, interRowGap)) break;
}
m->width = m->sub->text.width;
int mmh = m->sub->text.height + m->sub->text.depth;
m->depth = mmh/2;
m->height = mmh - m->depth;
m->dy = m->depth - m->sub->text.depth;
}
return;
case BoxNest:
n = &(b->nest);
switch (n->flags & NestMask)
{
case BoxFraction: // Aaaaaaaa
// --> --------
// Bb
{ measureBox1(n->sub1);
measureBox1(n->sub2);
n->width = intmax(n->sub1->text.width, n->sub2->text.width);
int rh = mathFontHeight[n->flags & FontSizeMask]/4;
int delta = (mathFontHeight[n->flags & FontSizeMask] -
mathFontDepth[n->flags & FontSizeMask])/2;
n->height = n->sub1->text.height +
n->sub1->text.depth + rh + delta;
n->depth = n->sub2->text.height +
n->sub2->text.depth + rh - delta;
n->dx1 = (n->width - n->sub1->text.width)/2;
n->dy1 = -(n->sub1->text.depth + rh + delta);
n->dx2 = (n->width - n->sub2->text.width)/2;
n->dy2 = n->sub2->text.height + rh - delta;
}
if (DEBUGFONT & 4)
{ printf("Above h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxStack: // Aaaaaaaa
// --> Bb
{ measureBox1(n->sub1);
measureBox1(n->sub2);
n->width = intmax(n->sub1->text.width, n->sub2->text.width);
n->height = n->sub1->text.height + n->sub1->text.depth +
n->sub2->text.height;
n->depth = n->sub2->text.depth;
n->dx1 = (n->width - n->sub1->text.width)/2;
n->dy1 = -(n->sub1->text.depth + n->sub2->text.height);
n->dx2 = (n->width - n->sub2->text.width)/2;
n->dy2 = 0;
}
if (DEBUGFONT & 4)
{ printf("Above h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxTower: // Aaaaaa
// --> Bbb
measureBox1(n->sub1);
measureBox1(n->sub2);
n->width = intmax(n->sub1->text.width, n->sub2->text.width);
n->height = n->sub1->text.height + n->sub1->text.depth + n->sub2->text.height;
n->depth = n->sub2->text.depth;
n->dx1 = 0;
n->dy1 = -(n->sub1->text.depth + n->sub2->text.height);
n->dx2 = 0;
n->dy2 = 0;
if (DEBUGFONT & 4)
{ printf("Above h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxBeside: // --> A B
measureBox1(n->sub1);
measureBox1(n->sub2);
n->width = n->sub1->text.width + n->sub2->text.width;
n->height = intmax(n->sub1->text.height, n->sub2->text.height);
n->depth = intmax(n->sub1->text.depth, n->sub2->text.depth);
n->dx1 = 0;
n->dy1 = 0;
n->dx2 = n->sub1->text.width;
n->dy2 = 0;
if (DEBUGFONT & 4)
{ printf("Beside h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxAdjacent: // --> A B
measureBox1(n->sub1);
measureBox1(n->sub2);
w = n->sub1->text.width/4; // amount to fudge by!
n->width = n->sub1->text.width + n->sub2->text.width - w;
n->height = intmax(n->sub1->text.height, n->sub2->text.height);
n->depth = intmax(n->sub1->text.depth, n->sub2->text.depth);
n->dx1 = 0;
n->dy1 = 0;
n->dx2 = n->sub1->text.width - w;
n->dy2 = 0;
if (DEBUGFONT & 4)
{ printf("Adjacent h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxSubscript: // A or in special cases A
// B B
measureBox1(n->sub1);
measureBox1(n->sub2);
// I will want to apply a special treatment here for "big" operators:
// subscripts and superscripts will be treated as forming a tower rather
// then the "usual" positioning.
if (n->sub1->text.type==BoxText && n->sub1->text.flags & FntBig)
{ n->width = intmax(n->sub1->text.width, n->sub2->text.width);
n->height = n->sub1->text.height;
n->depth = n->sub1->text.depth +
n->sub2->text.height + n->sub2->text.depth;
n->dx1 = (n->width - n->sub1->text.width)/2;
n->dy1 = 0;
n->dx2 = (n->width - n->sub2->text.width)/2;
n->dy2 = n->sub1->text.depth + n->sub2->text.height;
if (DEBUGFONT & 4)
{ printf("Special subscript h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
}
n->width = n->sub1->text.width + n->sub2->text.width;
n->dx1 = 0;
n->dy1 = 0;
n->dx2 = n->sub1->text.width;
dy = mathFontHeight[n->flags & FontSizeMask]/2;
{ int tt = n->sub1->text.depth - n->sub2->text.depth;
if (tt > dy) dy = tt;
}
n->dy2 = dy;
n->height = intmax(n->sub1->text.height, n->sub2->text.height - dy);
n->depth = intmax(n->sub1->text.depth, n->sub2->text.depth + dy);
if (DEBUGFONT & 4)
{ printf("Subscript h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxSuperscript: // B B
// A or in special cases A
measureBox1(n->sub1);
measureBox1(n->sub2);
if (n->sub1->text.type==BoxText && n->sub1->text.flags & FntBig)
{ n->width = intmax(n->sub1->text.width, n->sub2->text.width);
n->height = n->sub1->text.height +
n->sub2->text.height + n->sub2->text.depth;
n->depth = n->sub1->text.depth;
n->dx1 = (n->width - n->sub1->text.width)/2;
n->dy1 = 0;
n->dx2 = (n->width - n->sub2->text.width)/2;
n->dy2 = -(n->sub1->text.height + n->sub2->text.depth);
if (DEBUGFONT & 4)
{ printf("Special superscript h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
}
n->width = n->sub1->text.width + n->sub2->text.width;
n->dx1 = 0;
n->dy1 = 0;
n->dx2 = n->sub1->text.width;
// Now an utter hack! But it is here in part to alert me to the fact that
// similar tuning may be called for in other places. If I have "f^n" then the
// fact that "f" is tall and it slopes forward means that I want to put a tiny
// amount of extra space in. I will add some space for various cases here,
// but can imagine that further tuning could be applied!
//
if (n->sub1->text.type==BoxText)
{ const char *s = n->sub1->text.text;
int len = n->sub1->text.n;
int w = italicAdjust(s[len-1], n->sub1->text.flags & FontMask);
n->width += w;
n->dx2 += w;
}
// Superscripts are raised by at least half the height of the main font,
// but if the things that is being scripted is tall enough they line up with
// its top.
dy = mathFontHeight[n->flags & FontSizeMask]/2;
{ int tt = n->sub1->text.height - n->sub2->text.height;
if (tt > dy) dy = tt;
}
n->dy2 = -dy;
n->height = intmax(n->sub1->text.height, n->sub2->text.height + dy);
n->depth = intmax(n->sub1->text.depth, n->sub2->text.depth - dy);
if (DEBUGFONT & 4)
{ printf("Superscript h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxOverstrike: // --> A on top of B
measureBox1(n->sub1);
measureBox1(n->sub2);
n->width = intmax(n->sub1->text.width, n->sub2->text.width);
n->height = intmax(n->sub1->text.height, n->sub2->text.height);
n->depth = intmax(n->sub1->text.depth, n->sub2->text.depth);
// I centre the items horizontally wrt each other
n->dx1 = (n->width - n->sub1->text.width)/2;
n->dy1 = 0;
n->dx2 = (n->width - n->sub2->text.width)/2;
n->dy2 = 0;
if (DEBUGFONT & 4)
{ printf("Overstrike h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
case BoxPadSpace: // --> A but with extra space around A
measureBox1(n->sub1);
w = mathFontWidth[n->flags & FontSizeMask]/4;
n->width = n->sub1->text.width + 2*w;
n->height = n->sub1->text.height;
n->depth = n->sub1->text.depth;
n->dx1 = w;
n->dy1 = 0;
if (DEBUGFONT & 4)
{ printf("PadSpace h=%d d=%d w=%d\n",
n->height, n->depth, n->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n->dx1, n->dy1, n->dx2, n->dy2);
fflush(stdout);
}
return;
default:
printf("Unknown nest style %d: failing\n", n->flags);
return;
}
return;
case BoxNest3:
n3 = &(b->nest3);
switch (n3->flags & NestMask)
{
case BoxBothScripts: // C C
// A or in special cases A
// B B
// I will want to apply a special treatment here for "big" operators:
// subscripts and superscripts will be treated as forming a tower rather
// then the "usual" positioning.
measureBox1(n3->sub1);
measureBox1(n3->sub2);
measureBox1(n3->sub3);
if (n3->sub1->text.type==BoxText && n3->sub1->text.flags & FntBig)
{ n3->width =
intmax(n3->sub1->text.width,
intmax(n3->sub2->text.width, n3->sub3->text.width));
n3->height = n3->sub1->text.height +
n3->sub3->text.height + n3->sub3->text.depth;
n3->depth = n3->sub1->text.depth +
n3->sub2->text.height + n3->sub2->text.depth;
n3->dx1 = (n3->width - n3->sub1->text.width)/2;
n3->dy1 = 0;
n3->dx2 = (n3->width - n3->sub2->text.width)/2;
n3->dy2 = n3->sub1->text.depth + n3->sub2->text.height;
n3->dx3 = (n3->width - n3->sub3->text.width)/2;
n3->dy3 = -(n3->sub1->text.height + n3->sub3->text.depth);
if (DEBUGFONT & 4)
{ printf("Special Bothscripts h=%d d=%d w=%d\n",
n3->height, n3->depth, n3->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n3->dx1, n3->dy1, n3->dx2, n3->dy2);
fflush(stdout);
}
return;
}
n3->width = n3->sub1->text.width +
intmax(n3->sub2->text.width, n3->sub3->text.width);
n3->dx1 = 0;
n3->dy1 = 0;
n3->dx2 = n3->sub1->text.width;
dy = mathFontHeight[n3->flags & FontSizeMask]/2;
{ int tt = n3->sub1->text.depth - n3->sub2->text.depth;
if (tt > dy) dy = tt;
}
n3->dy2 = dy;
n3->dx3 = n3->sub1->text.width;
// Now an "italic correction" for "f^n". See the BoxSuperscript case for
// more explanation.
if (n3->sub1->text.type==BoxText)
{ const char *s = n3->sub1->text.text;
int len = n3->sub1->text.n;
int w = italicAdjust(s[len-1], n3->sub1->text.flags & FontMask);
n3->width += w;
n3->dx2 += w;
}
dy = mathFontHeight[n3->flags & FontSizeMask]/2;
{ int tt = n3->sub1->text.height - n3->sub3->text.height;
if (tt > dy) dy = tt;
}
n3->dy3 = -dy;
n3->height =
intmax(n3->sub1->text.height,
intmax(n3->sub2->text.height - dy, n3->sub3->text.height + dy));
n3->depth =
intmax(n3->sub1->text.depth,
intmax(n3->sub2->text.depth + dy, n3->sub3->text.depth - dy));
if (DEBUGFONT & 4)
{ printf("Bothscripts h=%d d=%d w=%d\n",
n3->height, n3->depth, n3->width);
printf("dx1=%d dy1=%d dx2=%d dy2=%d\n",
n3->dx1, n3->dy1, n3->dx2, n3->dy2);
fflush(stdout);
}
return;
default:
printf("Unknown nest3 style %d: failing\n", n3->flags);
return;
}
return;
case BoxFrame:
measureBox1(b->frame.sub);
b->frame.width = b->frame.sub->text.width;
b->frame.height = b->frame.sub->text.height;
b->frame.depth = b->frame.sub->text.depth;
return;
case BoxTop: // top-level wrapper for boxes.
tt = &(b->top);
// If I have measured before while the main maths font was the same
// size that it is now I will not do anything.
if (tt->measuredSize == mathFontSize) return;
measureBox1(tt->sub);
tt->width = tt->sub->text.width;
tt->height = tt->sub->text.height;
tt->depth = tt->sub->text.depth;
tt->measuredSize = mathFontSize;
return;
default:
printf("Measure unknown box type %d: failing\n", b->text.type);
fflush(stdout);
return;
}
}
///////////////////////////////////////////////////////////////////////////
// Input will be presented in a LaTeX-like form so I will want to parse that
// so I can build my box-structure. To do that I will want a way of
// recognizing LaTeX keywords.
typedef struct Keyword
{
const char *name;
unsigned char type;
unsigned char font; // includes size info
int charCode; // sometimes holds additional information!
void *ptr;
} Keyword;
typedef Box *blockHandlerFunction(int l, int r, Box *b);
typedef Box *oneArgHandlerFunction(Box *b, Box *opt);
typedef Box *twoArgsHandlerFunction(Box *b1, Box *b2);
typedef Box *keywordHandlerFunction(int w);
// single character glyph
#define TeXSymbol 0x00
// single character glyph, but subscripts and superscripts form tower
#define TeXVSymbol 0x01
// single character glyph, but allow extra space on either ide
#define TeXWSymbol 0x02
// word to be set in Roman font (eg function name like "sin")
#define TeXRoman 0x03
// keyword not taking an argument, eg \longleftarrow, \not
#define TeX0Arg 0x04
// keyword taking one arg, eg \sqrt
#define TeX1Arg 0x05
// keyword taking 2 args, eg \frac
#define TeX2Arg 0x06
// the \rule keyword
#define TeXRule 0x07
// the \begin keyword
#define TeXBeginWord 0x08
// keyword that forms a sort of "open bracket"
#define TeXBegin 0x09
// "close bracket" to match TeXBegin
#define TeXEnd 0x0a
// "^" or "_".
#define TeXScript 0x0b
#define TeXFlag 0x80
#define matchCenter 1
#define matchMatrix 2
#define matchLeftRight 3
#define matchBrace 4
#define matchParen 5
#define matchBracket 6
#define matchDollar 7
int insideDollars;
static Box *doDisplayStyle(int w)
{
return NULL; // always does displaystyle!
}
static Box *doDollar(int l, int r, Box *b1)
{
if (b1 == NULL)
{ insideDollars = 1;
currentState.currentFont = FntItalic + currentSize;
}
else insideDollars = 0;
return b1;
}
static Box *doMathBrace(int l, int r, Box *b1)
{
currentState.currentFont = FntItalic + currentSize;
return b1;
}
static Box *doMathRm(Box *b1, Box *opt)
{
if (b1 == NULL) currentState.currentFont = FntRoman + currentSize;
return b1;
}
static Box *doMathIt(Box *b1, Box *opt)
{
if (b1 == NULL) currentState.currentFont = FntItalic + currentSize;
return b1;
}
static Box *doSpaceCommand(int w)
{
switch (w)
{
default:
case ' ': return makeSymBox(SymBlank);
case '*': return makeSymBox(SymStar);
case ',': return makeSymBox(SymComma);
case '!': return makeSymBox(SymExclam);
case ':': return makeSymBox(SymColon);
case ';': return makeSymBox(SymSemiColon);
case '_': return makeSymBox(SymUnderscore);
}
}
static Box *doAdjacent(int w)
{
// This builds a composite character by putting two glyphs together.
// The glyphs are passed in a 32-bit (or wider) packed integer argument.
int f1, c1, f2, c2;
f1 = (w >> 24) & 0xff;
c1 = (w >> 16) & 0xff;
f2 = (w >> 8) & 0xff;
c2 = w & 0xff;
char s1[1], s2[1];
s1[0] = remap(c1);
s2[0] = remap(c2);
return makeNestBox(BoxAdjacent,
makeTextBox(s1, 1, f1 + currentSize),
makeTextBox(s2, 1, f2 + currentSize));
}
static Box *doLeftRight(int l, int r, Box *b)
{
if (b == NULL) return NULL;
return makeBracketBox(b, l, r);
}
static Box *doNeq(int w)
{
Box *b1 = makeTextBox("\x3d", 1, FntRoman + currentSize);
Box *b2 = makeTextBox("\x3d", 1, FntItalic + currentSize);
return makeNestBox(BoxOverstrike, b1, b2);
}
static Box *readP();
static Box *doNot(int w)
{
// Overstrike anthing (much) with a "/". Hence "\not \equiv" etc.
Box *b1 = readP();
if (b1 == NULL) return NULL;
Box *b2 = makeTextBox("\x3d", 1, FntItalic + currentSize);
return makeNestBox(BoxOverstrike, b1, b2);
}
static Box *doPmod(Box *b, Box *opt)
{
printf("pmod not handed yet");
return b;
}
static Box *doSqrt(Box *b, Box *opt)
{
if (b == NULL) return NULL;
// Note that sqrt will need to draw a rule above the item it encloses.
Box *r = makeBracketBox(b, 'Q', '.');
r->bracket.sub1 = opt;
return r;
}
static Box *doFbox(Box *b, Box *opt)
{
if (b == NULL) return NULL;
return makeFrameBox(b);
}
static Box *doSymb(Box *b, Box *opt)
{
if (b == NULL) return NULL;
return makeFrameBox(b); // At PRESENT I maye symb just a boxed number
}
static Box *doFrac(Box *b1, Box *b2)
{
if (b1 == NULL) return b2;
else if (b2 == NULL) return b1;
return makeNestBox(BoxFraction, b1, b2);
}
static Box *doLarge(int w)
{
if (currentSize != 0) currentState.currentFont-=FntScript;
return NULL;
}
static Box *doStackrel(Box *b1, Box *b2)
{
if (b1 == NULL) return b2;
else if (b2 == NULL) return b1;
return makeNestBox(BoxStack, b1, b2);
}
#if 0
// To assist debugging while I develop this code I want to be able to
// display information about a keyword.
static void printKeyword(Keyword *k)
{
if (k == NULL) printf("NULL keyword\n");
else printf("Keyword \"%s\" type %.2x font %.2x code %.2x ptr %p\n",
k->name, k->type, k->font, k->charCode, k->ptr);
}
#endif // 0
// The table that follows is initially set up in an order designed to
// have some degree of coherence of presentation, but as soon as the code
// starts to run it gers rearranged as a hash table.
#define texWordBits 9
static Keyword texWords[1<<texWordBits] =
{
// Some important control words
// name type font charCode ptr
// The next two NEVER get out from the lexer.
{"begin", TeXBeginWord, 1, 0, NULL},
{"end", TeXBeginWord, 0, 0, NULL},
// My lexer will map "\begin{center}" onto a single token "(center"
// and similarly for other uses of \begin and \end. In fact with the
// current REDUCE "tmprint" module the only brackets of this sort that
// are used are "center" and "matrix". Also in the CSL driver I will not
// actually generate the "center" case...
{"(center", TeXBegin, matchCenter, 'c', NULL},
{")center", TeXEnd, matchCenter, 'c', NULL},
{"(matrix", TeXBegin, matchMatrix, 'm', NULL},
{")matrix", TeXEnd, matchMatrix, 'm', NULL},
// Special-case bracket-like items (delimiters that get displayed
// at a size the cover the item on one size of them). The case "\right("
// is probably never used, but I include all cases here for consistency.
// A "\left" must match with a "\right", but the associated delimiter
// does not have to match.
{"left:(", TeXBegin, matchLeftRight, '(', (void *)doLeftRight},
{"right:(", TeXEnd, matchLeftRight, '(', (void *)doLeftRight},
{"left:)", TeXBegin, matchLeftRight, ')', (void *)doLeftRight},
{"right:)", TeXEnd, matchLeftRight, ')', (void *)doLeftRight},
{"left:[", TeXBegin, matchLeftRight, '[', (void *)doLeftRight},
{"right:[", TeXEnd, matchLeftRight, '[', (void *)doLeftRight},
{"left:]", TeXBegin, matchLeftRight, ']', (void *)doLeftRight},
{"right:]", TeXEnd, matchLeftRight, ']', (void *)doLeftRight},
{"left:\\{", TeXBegin, matchLeftRight, '{', (void *)doLeftRight},
{"right:\\{", TeXEnd, matchLeftRight, '{', (void *)doLeftRight},
{"left:\\}", TeXBegin, matchLeftRight, '}', (void *)doLeftRight},
{"right:\\}", TeXEnd, matchLeftRight, '}', (void *)doLeftRight},
{"left:|", TeXBegin, matchLeftRight, '|', (void *)doLeftRight},
// Note that I map left and right vertical bars onto different things (I
// abuse an exclamation mark to stand for the right one) since they may
// call for different spacings.
{"right:|", TeXEnd, matchLeftRight, '!', (void *)doLeftRight},
// "\left." and "\right." are invisible!
{"left:.", TeXBegin, matchLeftRight, '.', (void *)doLeftRight},
{"right:.", TeXEnd, matchLeftRight, '.', (void *)doLeftRight},
// "/", "\", "L", "G" (lceil), "J", "7" (rceil), "<", ">",
// "#" (double vertical bar), "^" (uparrow), "v" (downarrow), "A" (Uparrow)
// "V" (Downarrow), "b" (updownarrow), "B" (Updownarrow)
{"left:/", TeXBegin, matchLeftRight, '/', (void *)doLeftRight},
{"right:/", TeXEnd, matchLeftRight, '/', (void *)doLeftRight},
{"left:\\|", TeXBegin, matchLeftRight, '#', (void *)doLeftRight},
{"right:\\|", TeXEnd, matchLeftRight, '#', (void *)doLeftRight},
{"left:\\langle", TeXBegin, matchLeftRight, '<', (void *)doLeftRight},
{"right:\\langle",TeXEnd, matchLeftRight, '<', (void *)doLeftRight},
{"left:\\rangle", TeXBegin, matchLeftRight, '>', (void *)doLeftRight},
{"right:\\rangle",TeXEnd, matchLeftRight, '>', (void *)doLeftRight},
{"left:\\backslash", TeXBegin, matchLeftRight, '\\', (void *)doLeftRight},
{"right:\\backslash",TeXEnd, matchLeftRight, '\\', (void *)doLeftRight},
{"left:\\lfloor", TeXBegin, matchLeftRight, 'L', (void *)doLeftRight},
{"right:\\lfloor",TeXEnd, matchLeftRight, 'L', (void *)doLeftRight},
{"left:\\rfloor", TeXBegin, matchLeftRight, 'J', (void *)doLeftRight},
{"right:\\rfloor",TeXEnd, matchLeftRight, 'J', (void *)doLeftRight},
{"left:\\lceil", TeXBegin, matchLeftRight, 'G', (void *)doLeftRight},
{"right:\\lceil", TeXEnd, matchLeftRight, 'G', (void *)doLeftRight},
{"left:\\rceil", TeXBegin, matchLeftRight, '7', (void *)doLeftRight},
{"right:\\rceil", TeXEnd, matchLeftRight, '7', (void *)doLeftRight},
{"left:\\uparrow", TeXBegin, matchLeftRight, '^', (void *)doLeftRight},
{"right:\\uparrow",TeXEnd, matchLeftRight, '^', (void *)doLeftRight},
{"left:\\downarrow", TeXBegin, matchLeftRight, 'v', (void *)doLeftRight},
{"right:\\downarrow",TeXEnd, matchLeftRight, 'v', (void *)doLeftRight},
{"left:\\updownarrow", TeXBegin,matchLeftRight, 'b', (void *)doLeftRight},
{"right:\\updownarrow",TeXEnd, matchLeftRight, 'b', (void *)doLeftRight},
{"left:\\Uparrow", TeXBegin, matchLeftRight, 'A', (void *)doLeftRight},
{"right:\\Uparrow",TeXEnd, matchLeftRight, 'A', (void *)doLeftRight},
{"left:\\Downarrow", TeXBegin, matchLeftRight, 'V', (void *)doLeftRight},
{"right:\\Downarrow",TeXEnd, matchLeftRight, 'V', (void *)doLeftRight},
{"left:\\Updownarrow", TeXBegin,matchLeftRight, 'B', (void *)doLeftRight},
{"right:\\Updownarrow",TeXEnd, matchLeftRight, 'B', (void *)doLeftRight},
// Some other things that generate enclosing contexts
{"{", TeXBegin, matchBrace, '{', NULL},
{"}", TeXEnd, matchBrace, '{', NULL},
// "[" and "]" are fairly ordinary characters when they just happen in
// random places. But after "\sqrt" (and possibly other things) they
// enclose an optional argument.
{"[", TeXSymbol, FntRoman, 0x5b, NULL},
{"]", TeXSymbol, FntRoman, 0x5d, NULL},
// At present I will treate "\(" and "\[" as synonyms - both just
// enter maths mode.
{"\\(", TeXBegin, matchParen, '(', (void *)doMathBrace},
{"\\)", TeXEnd, matchParen, '(', (void *)doMathBrace},
{"\\[", TeXBegin, matchBracket, '[', (void *)doMathBrace},
{"\\]", TeXEnd, matchBracket, '[', (void *)doMathBrace},
{"$", TeXBegin, matchDollar, '$', (void *)doDollar},
// Treatment for "\&" etc that just generate literal characters.
// Note and be warned - the TeX fonts that are going to be used
// here have their own special encoding schemes so the numeric values
// that stand for even ordinary-seeming glyphs need careful attention.
{"\\{", TeXSymbol, FntSymbol, 0x66, NULL},
{"\\}", TeXSymbol, FntSymbol, 0x67, NULL},
{"\\$", TeXSymbol, FntRoman, 0x24, NULL},
{"\\&", TeXWSymbol,FntRoman, 0x26, NULL},
{"\\%", TeXSymbol, FntRoman, 0x25, NULL},
// subscripts and superscripts involve some parsing magic
{"^", TeXScript, 0, 0, NULL},
{"_", TeXScript, 1, 0, NULL},
// Simple symbols that need some care because the normal code used
// here may not match TeX font encodings.
{"+", TeXWSymbol,FntRoman, 0x2b, NULL},
{"-", TeXWSymbol,FntSymbol, 0x00, NULL}, // Roman 0x2d is hyphen
{"=", TeXWSymbol,FntRoman, 0x3d, NULL},
{"*", TeXSymbol, FntRoman, 0x2a, NULL},
{"/", TeXSymbol, FntRoman, 0x2f, NULL},
{":", TeXSymbol, FntRoman, 0x3a, NULL},
{";", TeXSymbol, FntRoman, 0x3b, NULL},
{"@", TeXSymbol, FntRoman, 0x40, NULL},
{",", TeXSymbol, FntRoman, 0x2c, NULL},
{".", TeXSymbol, FntRoman, 0x2e, NULL},
{"?", TeXSymbol, FntRoman, 0x3f, NULL},
{"!", TeXSymbol, FntRoman, 0x21, NULL},
{"|", TeXSymbol, FntSymbol, 0x6a, NULL},
{"`", TeXSymbol, FntRoman, 0x12, NULL},
// {"NOTSIGN", TeXSymbol, FntSymbol, 0x3a, NULL},
{"#", TeXSymbol, FntRoman, 0x23, NULL},
{"~", TeXSymbol, FntRoman, 0x7f, NULL},
{"(", TeXSymbol, FntRoman, 0x28, NULL},
{")", TeXSymbol, FntRoman, 0x29, NULL},
{"<", TeXSymbol, FntItalic, 0x3c, NULL},
{">", TeXSymbol, FntItalic, 0x3e, NULL},
// Lower case Greek
{"alpha", TeXSymbol, FntItalic, 0x0b, NULL},
{"beta", TeXSymbol, FntItalic, 0x0c, NULL},
{"gamma", TeXSymbol, FntItalic, 0x0d, NULL},
{"delta", TeXSymbol, FntItalic, 0x0e, NULL},
{"epsilon", TeXSymbol, FntItalic, 0x0f, NULL},
{"varepsilon", TeXSymbol, FntItalic, 0x22, NULL},
{"zeta", TeXSymbol, FntItalic, 0x10, NULL},
{"eta", TeXSymbol, FntItalic, 0x11, NULL},
{"theta", TeXSymbol, FntItalic, 0x12, NULL},
{"vartheta", TeXSymbol, FntItalic, 0x23, NULL},
{"iota", TeXSymbol, FntItalic, 0x13, NULL},
{"kappa", TeXSymbol, FntItalic, 0x14, NULL},
{"lambda", TeXSymbol, FntItalic, 0x15, NULL},
{"mu", TeXSymbol, FntItalic, 0x16, NULL},
{"nu", TeXSymbol, FntItalic, 0x17, NULL},
{"xi", TeXSymbol, FntItalic, 0x18, NULL},
{"omicron", TeXSymbol, FntItalic, 'o', NULL},
{"pi", TeXSymbol, FntItalic, 0x19, NULL},
{"varpi", TeXSymbol, FntItalic, 0x24, NULL},
{"rho", TeXSymbol, FntItalic, 0x1a, NULL},
{"varrho", TeXSymbol, FntItalic, 0x25, NULL},
{"sigma", TeXSymbol, FntItalic, 0x1b, NULL},
{"varsigma", TeXSymbol, FntItalic, 0x26, NULL},
{"tau", TeXSymbol, FntItalic, 0x1c, NULL},
{"upsilon", TeXSymbol, FntItalic, 0x1d, NULL},
{"phi", TeXSymbol, FntItalic, 0x1e, NULL},
{"varphi", TeXSymbol, FntItalic, 0x27, NULL},
{"chi", TeXSymbol, FntItalic, 0x1f, NULL},
{"psi", TeXSymbol, FntItalic, 0x20, NULL},
{"omega", TeXSymbol, FntItalic, 0x21, NULL},
//
// Upper case Greek (using Maths Italic for A, B etc where shapes are
// the same).
{"Alpha", TeXSymbol, FntItalic, 'A', NULL},
{"Beta", TeXSymbol, FntItalic, 'B', NULL},
{"Gamma", TeXSymbol, FntItalic, 0x00, NULL},
{"Delta", TeXSymbol, FntItalic, 0x01, NULL},
{"Epsilon", TeXSymbol, FntItalic, 'E', NULL},
{"Zeta", TeXSymbol, FntItalic, 'Z', NULL},
{"Eta", TeXSymbol, FntItalic, 'H', NULL},
{"Theta", TeXSymbol, FntItalic, 0x02, NULL},
{"Iota", TeXSymbol, FntItalic, 'I', NULL},
{"Kappa", TeXSymbol, FntItalic, 'K', NULL},
{"Lambda", TeXSymbol, FntItalic, 0x03, NULL},
{"Mu", TeXSymbol, FntItalic, 'M', NULL},
{"Nu", TeXSymbol, FntItalic, 'N', NULL},
{"Xi", TeXSymbol, FntItalic, 0x04, NULL},
{"Omicron", TeXSymbol, FntItalic, 'O', NULL},
{"Pi", TeXSymbol, FntItalic, 0x05, NULL},
{"Rho", TeXSymbol, FntItalic, 'R', NULL},
{"Sigma", TeXSymbol, FntItalic, 0x06, NULL},
{"Tau", TeXSymbol, FntItalic, 'T', NULL},
{"Upsilon", TeXSymbol, FntItalic, 0x07, NULL},
{"Phi", TeXSymbol, FntItalic, 0x08, NULL},
{"Chi", TeXSymbol, FntItalic, 'X', NULL},
{"Psi", TeXSymbol, FntItalic, 0x09, NULL},
{"Omega", TeXSymbol, FntItalic, 0x0a, NULL},
//
// More mathematical symbols. I have copied all the names from my LaTeX
// book and identified available characters in the cmmi or cmsy fonts where
// I can. I have at present commented out all characters for which I do not
// have a glyph. The effect will be that the TeX symbol that names them will
// be rejected. In some cases I note another font that could help.
{"pm", TeXWSymbol,FntSymbol, 0x06, NULL},
{"mp", TeXWSymbol,FntSymbol, 0x07, NULL},
{"times", TeXSymbol, FntSymbol, 0x02, NULL},
{"div", TeXSymbol, FntSymbol, 0x04, NULL},
{"ast", TeXSymbol, FntSymbol, 0x03, NULL},
{"star", TeXSymbol, FntItalic, 0x3f, NULL},
{"circ", TeXSymbol, FntSymbol, 0x0e, NULL},
{"bullet", TeXSymbol, FntSymbol, 0x0f, NULL},
{"cdot", TeXSymbol, FntSymbol, 0x01, NULL},
{"cap", TeXSymbol, FntSymbol, 0x5c, NULL},
{"cup", TeXSymbol, FntSymbol, 0x5b, NULL},
{"uplus", TeXSymbol, FntSymbol, 0x5d, NULL},
{"sqcap", TeXSymbol, FntSymbol, 0x75, NULL},
{"sqcup", TeXSymbol, FntSymbol, 0x74, NULL},
{"vee", TeXSymbol, FntSymbol, 0x5f, NULL},
{"wedge", TeXSymbol, FntSymbol, 0x5e, NULL},
// The Variable-sized symbols tend to come in two sizes, and although TeX
// only used one name (eg \int) and selects which to use based on whether
// one is in displaymath or ordinary (in-line) mode. Here I am "always" in
// displaymath mode so I always want the larger versions. But I provide
// alternative names starting "small" to keep the others available.
{"smallsum", TeXVSymbol, FntExtension, 0x50, NULL},
{"sum", TeXVSymbol, FntExtension, 0x58, NULL},
{"smallprod", TeXVSymbol, FntExtension, 0x51, NULL},
{"prod", TeXVSymbol, FntExtension, 0x59, NULL},
{"smallcoprod", TeXVSymbol, FntExtension, 0x60, NULL},
{"coprod", TeXVSymbol, FntExtension, 0x61, NULL},
{"smallint", TeXVSymbol, FntExtension, 0x52, NULL},
{"int", TeXVSymbol, FntExtension, 0x5a, NULL},
// It looks to me as if tmprint.red can generate either \int or \Int
// and maybe it expects one to be smaller than the other...???
{"Int", TeXVSymbol, FntExtension, 0x5a, NULL},
{"smalloint", TeXVSymbol, FntExtension, 0x48, NULL},
{"oint", TeXVSymbol, FntExtension, 0x49, NULL},
{"smallbigcup", TeXVSymbol, FntExtension, 0x53, NULL},
{"nigcup", TeXVSymbol, FntExtension, 0x5b, NULL},
{"smallbigcap", TeXVSymbol, FntExtension, 0x54, NULL},
{"bigcap", TeXVSymbol, FntExtension, 0x5c, NULL},
{"smallbiguplus", TeXVSymbol, FntExtension, 0x55, NULL},
{"biguplus", TeXVSymbol, FntExtension, 0x5d, NULL},
{"smallbigodot", TeXVSymbol, FntExtension, 0x4a, NULL},
{"bigodot", TeXVSymbol, FntExtension, 0x4b, NULL},
{"smallbigotimes", TeXVSymbol, FntExtension, 0x4e, NULL},
{"bigotimes", TeXVSymbol, FntExtension, 0x4f, NULL},
{"smallbigoplus", TeXVSymbol, FntExtension, 0x4c, NULL},
{"bigoplus", TeXVSymbol, FntExtension, 0x4d, NULL},
{"smallbigsqcup", TeXVSymbol, FntExtension, 0x46, NULL},
{"bigsqcup", TeXVSymbol, FntExtension, 0x47, NULL},
{"smallbigwedge", TeXVSymbol, FntExtension, 0x56, NULL},
{"bigwedge", TeXVSymbol, FntExtension, 0x5e, NULL},
{"smallbigvee", TeXVSymbol, FntExtension, 0x57, NULL},
{"bigvee", TeXVSymbol, FntExtension, 0x5f, NULL},
// end of variable sized things
{"setminus", TeXSymbol, FntSymbol, 0x6e, NULL},
{"wr", TeXSymbol, FntSymbol, 0x6f, NULL},
{"diamond", TeXSymbol, FntSymbol, 0x05, NULL},
{"bigtriangleup", TeXSymbol, FntSymbol, 0x35, NULL},
{"bigtriangledown", TeXSymbol, FntSymbol, 0x36, NULL},
{"triangleleft", TeXSymbol, FntItalic, 0x2f, NULL},
{"triangleright", TeXSymbol, FntItalic, 0x2e, NULL},
// {"lhd", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x43
// {"rhd", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x42
// {"unlhd", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x45
// {"unrhd", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x44
{"oplus", TeXSymbol, FntSymbol, 0x08, NULL},
{"ominus", TeXSymbol, FntSymbol, 0x09, NULL},
{"otimes", TeXSymbol, FntSymbol, 0x0a, NULL},
{"oslash", TeXSymbol, FntSymbol, 0x0b, NULL},
{"odot", TeXSymbol, FntSymbol, 0x0c, NULL},
{"bigcirc", TeXSymbol, FntSymbol, 0x0d, NULL},
{"dagger", TeXSymbol, FntSymbol, 0x79, NULL},
{"ddagger", TeXSymbol, FntSymbol, 0x7a, NULL},
{"amalg", TeXSymbol, FntSymbol, 0x71, NULL},
{"leq", TeXSymbol, FntSymbol, 0x14, NULL},
{"prec", TeXSymbol, FntSymbol, 0x1e, NULL},
{"preceq", TeXSymbol, FntSymbol, 0x16, NULL},
{"ll", TeXSymbol, FntSymbol, 0x1c, NULL},
{"subset", TeXSymbol, FntSymbol, 0x1a, NULL},
{"subseteq", TeXSymbol, FntSymbol, 0x12, NULL},
// {"sqsubset", TeXSymbol, FntSymbol, 0x00, NULL}, // lasy 0x3c
{"sqsubseteq", TeXSymbol, FntSymbol, 0x75, NULL},
{"in", TeXSymbol, FntSymbol, 0x32, NULL},
{"vdash", TeXSymbol, FntSymbol, 0x60, NULL},
{"geq", TeXSymbol, FntSymbol, 0x15, NULL},
{"succ", TeXSymbol, FntSymbol, 0x1f, NULL},
{"succeq", TeXSymbol, FntSymbol, 0x17, NULL},
{"gg", TeXSymbol, FntSymbol, 0x1d, NULL},
{"supset", TeXSymbol, FntSymbol, 0x1b, NULL},
{"supseteq", TeXSymbol, FntSymbol, 0x13, NULL},
// {"sqsupset", TeXSymbol, FntSymbol, 0x00, NULL}, // lasy 0x3d
{"sqsupseteq", TeXSymbol, FntSymbol, 0x77, NULL},
{"ni", TeXSymbol, FntSymbol, 0x33, NULL},
{"dashv", TeXSymbol, FntSymbol, 0x61, NULL},
{"equiv", TeXSymbol, FntSymbol, 0x11, NULL},
{"sim", TeXSymbol, FntSymbol, 0x18, NULL},
{"simeq", TeXSymbol, FntSymbol, 0x27, NULL},
{"asymp", TeXSymbol, FntSymbol, 0x10, NULL},
{"approx", TeXSymbol, FntSymbol, 0x19, NULL},
{"cong", TeXSymbol, FntSymbol, 0x00, NULL}, // msbm 0x74
// {"doteq", TeXSymbol, FntSymbol, 0x00, NULL}, // ?
// {"propto", TeXSymbol, FntSymbol, 0x2f, NULL},
{"models", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x0f
// {"perp", TeXSymbol, FntSymbol, 0x3f, NULL},
{"mid", TeXSymbol, FntSymbol, 0x6a, NULL},
{"parallel", TeXSymbol, FntSymbol, 0x6b, NULL},
// {"bowtie", TeXSymbol, FntSymbol, 0x00, NULL}, // lasy 0x31
// {"join", TeXSymbol, FntSymbol, 0x00, NULL}, // ?
{"smile", TeXSymbol, FntItalic, 0x5e, NULL},
{"frown", TeXSymbol, FntItalic, 0x5f, NULL},
{"leftarrow", TeXSymbol, FntSymbol, 0x20, NULL},
{"Leftarrow", TeXSymbol, FntSymbol, 0x28, NULL},
{"rightarrow", TeXSymbol, FntSymbol, 0x21, NULL},
{"Rightarrow", TeXSymbol, FntSymbol, 0x29, NULL},
{"leftrightarrow", TeXSymbol, FntSymbol, 0x24, NULL},
{"Leftrightarrow", TeXSymbol, FntSymbol, 0x2c, NULL},
// {"mapsto", TeXSymbol, FntSymbol, 0x00, NULL}, // ?msam 0x110
// {"hookleftarrow", TeXSymbol, FntSymbol, 0x00, NULL}, // ?
// {"leftharpoonup", TeXSymbol, FntSymbol, 0x00, NULL}, // euex 0x18
// {"leftharpoondown", TeXSymbol, FntSymbol, 0x00, NULL}, // euex 0x19
// {"rightleftharpoons",TeXSymbol, FntSymbol, 0x00, NULL}, // ?
// {"longmapsto", TeXSymbol, FntSymbol, 0x00, NULL}, // ?
// {"hookrightarrow", TeXSymbol, FntSymbol, 0x00, NULL}, // ?
// {"rightharpoonup", TeXSymbol, FntSymbol, 0x00, NULL}, // euex 0x1a
// {"rightharpoondown", TeXSymbol, FntSymbol, 0x00, NULL}, // euex 0x1b
// {"leadsto", TeXSymbol, FntSymbol, 0x00, NULL}, // lasy 0x3a
{"uparrow", TeXSymbol, FntSymbol, 0x22, NULL},
{"Uparrow", TeXSymbol, FntSymbol, 0x2a, NULL},
{"downarrow", TeXSymbol, FntSymbol, 0x23, NULL},
{"Downarrow", TeXSymbol, FntSymbol, 0x2b, NULL},
{"updownarrow", TeXSymbol, FntSymbol, 0x6c, NULL},
{"Updownarrow", TeXSymbol, FntSymbol, 0x6d, NULL},
{"nearrow", TeXSymbol, FntSymbol, 0x25, NULL},
{"searrow", TeXSymbol, FntSymbol, 0x26, NULL},
{"swarrow", TeXSymbol, FntSymbol, 0x2e, NULL},
{"nwarrow", TeXSymbol, FntSymbol, 0x2d, NULL},
{"aleph", TeXSymbol, FntSymbol, 0x40, NULL},
// {"hbar", TeXSymbol, FntSymbol, 0x00, NULL}, // msbm 0x7e
{"imath", TeXSymbol, FntItalic, 0x7b, NULL},
{"jmath", TeXSymbol, FntItalic, 0x7c, NULL},
// {"ell", TeXSymbol, FntSymbol, 0x00, NULL}, // ?
// {"wp", TeXSymbol, FntSymbol, 0x00, NULL}, // eufb 0x50
{"Re", TeXSymbol, FntSymbol, 0x3c, NULL},
{"Im", TeXSymbol, FntSymbol, 0x3d, NULL},
// {"mho", TeXSymbol, FntSymbol, 0x00, NULL}, // lasy 0x30
{"prime", TeXSymbol, FntSymbol, 0x30, NULL},
{"emptyset", TeXSymbol, FntSymbol, 0x3b, NULL},
{"nabla", TeXSymbol, FntSymbol, 0x35, NULL},
{"surd", TeXSymbol, FntSymbol, 0x72, NULL},
{"top", TeXSymbol, FntSymbol, 0x3e, NULL},
{"bot", TeXSymbol, FntSymbol, 0x3f, NULL},
{"|", TeXSymbol, FntSymbol, 0x6b, NULL},
// {"angle", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x5c
{"forall", TeXSymbol, FntSymbol, 0x38, NULL},
{"exists", TeXSymbol, FntSymbol, 0x39, NULL},
{"neg", TeXSymbol, FntSymbol, 0x3a, NULL},
{"flat", TeXSymbol, FntItalic, 0x5d, NULL},
{"natural", TeXSymbol, FntItalic, 0x5c, NULL},
{"sharp", TeXSymbol, FntItalic, 0x5b, NULL},
{"backslash", TeXSymbol, FntSymbol, 0x6e, NULL},
{"partial", TeXSymbol, FntItalic, 0x40, NULL},
{"infty", TeXSymbol, FntSymbol, 0x31, NULL},
// {"Box", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x04
// {"Diamond", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x07
// {"Triangle", TeXSymbol, FntSymbol, 0x00, NULL}, // msam 0x4d
{"clubsuit", TeXSymbol, FntSymbol, 0x7c, NULL},
{"diamondsuit", TeXSymbol, FntSymbol, 0x7d, NULL},
{"heartsuit", TeXSymbol, FntSymbol, 0x7e, NULL},
{"spadesuit", TeXSymbol, FntSymbol, 0x7f, NULL},
//
// Things to be rendered in Roman since they are function names
{"arccos", TeXRoman, 0, 0, (void *)"arccos"},
{"arcsin", TeXRoman, 0, 0, (void *)"arcsin"},
{"arctan", TeXRoman, 0, 0, (void *)"arctan"},
{"arg", TeXRoman, 0, 0, (void *)"arg"},
{"cos", TeXRoman, 0, 0, (void *)"cos"},
{"cosh", TeXRoman, 0, 0, (void *)"cosh"},
{"cot", TeXRoman, 0, 0, (void *)"cot"},
{"coth", TeXRoman, 0, 0, (void *)"coth"},
{"csc", TeXRoman, 0, 0, (void *)"csc"},
{"deg", TeXRoman, 0, 0, (void *)"deg"},
{"det", TeXRoman, 0, 0, (void *)"det"},
{"dim", TeXRoman, 0, 0, (void *)"dim"},
{"exp", TeXRoman, 0, 0, (void *)"exp"},
{"gcd", TeXRoman, 0, 0, (void *)"gcd"},
{"hom", TeXRoman, 0, 0, (void *)"hom"},
{"inf", TeXRoman, 0, 0, (void *)"inf"},
{"ker", TeXRoman, 0, 0, (void *)"ker"},
{"lg", TeXRoman, 0, 0, (void *)"lg"},
{"lim", TeXRoman, 0, 0, (void *)"lim"},
{"liminf", TeXRoman, 0, 0, (void *)"liminf"},
{"limsup", TeXRoman, 0, 0, (void *)"limsup"},
{"ln", TeXRoman, 0, 0, (void *)"ln"},
{"log", TeXRoman, 0, 0, (void *)"log"},
{"max", TeXRoman, 0, 0, (void *)"max"},
{"min", TeXRoman, 0, 0, (void *)"min"},
{"Pr", TeXRoman, 0, 0, (void *)"Pr"},
{"sec", TeXRoman, 0, 0, (void *)"sec"},
{"sin", TeXRoman, 0, 0, (void *)"sin"},
{"sinh", TeXRoman, 0, 0, (void *)"sinh"},
{"sup", TeXRoman, 0, 0, (void *)"sup"},
{"tan", TeXRoman, 0, 0, (void *)"tan"},
{"tanh", TeXRoman, 0, 0, (void *)"tanh"},
//
// TeX things that take do not take an argument, as in
// \word
// eg \displaystyle
{"displaystyle", TeX0Arg, 0, 0, (void *)doDisplayStyle},
// I only intend (at least to start with!) to support the \large
// directive in the context of \stackrel{\large A}{B} where it defeats
// the way that stackrel would otherwise shrink its first argument.
{"large", TeX0Arg, 0, 0, (void *)doLarge},
// \neq seems to be a unique horrid case for me. I can not see a glyph for
// it in the Computer Modern set and so will end up synthesizing it as
// an overstrike of "=" and "/".
{"neq", TeX0Arg, 0, 0, (void *)doNeq},
{"not", TeX0Arg, 0, 0, (void *)doNot},
// The next two are needed for matrix layout. Maybe and with luck they
// will not occur freestanding.
{"&", TeX0Arg, 0, 0, NULL},
{"\\\\", TeX0Arg, 0, 0, NULL},
// The next bunch insert spacing manually
{"\\*", TeX0Arg, 0, '*', (void *)doSpaceCommand},
{"\\ ", TeX0Arg, 0, ' ', (void *)doSpaceCommand},
{"\\,", TeX0Arg, 0, ',', (void *)doSpaceCommand},
{"\\!", TeX0Arg, 0, '!', (void *)doSpaceCommand},
{"\\:", TeX0Arg, 0, ':', (void *)doSpaceCommand},
{"\\;", TeX0Arg, 0, ';', (void *)doSpaceCommand},
{"\\_", TeX0Arg, 0, '_', (void *)doSpaceCommand},
#define pack4ints(a,b,c,d) (((a)<<24) + ((b)<<16) + ((c)<<8) + (d))
{"longleftarrow", TeX0Arg, 0,
pack4ints(FntSymbol, 0x20, FntSymbol, 0x00),
(void *)doAdjacent},
{"Longleftarrow", TeX0Arg, 0,
pack4ints(FntSymbol, 0x28, FntRoman, 0x3d),
(void *)doAdjacent},
{"longrightarrow", TeX0Arg, 0,
pack4ints(FntSymbol, 0x00, FntSymbol, 0x21),
(void *)doAdjacent},
{"Longrightarrow", TeX0Arg, 0,
pack4ints(FntRoman, 0x3d, FntSymbol, 0x29),
(void *)doAdjacent},
{"longleftrightarrow", TeX0Arg, 0,
pack4ints(FntSymbol, 0x20, FntSymbol, 0x21),
(void *)doAdjacent},
{"Longleftrightarrow", TeX0Arg, 0,
pack4ints(FntSymbol, 0x28, FntSymbol, 0x29),
(void *)doAdjacent},
//
// TeX things that take one argument, as in
// \word {arg}
// eg \pmod{whatever}
{"pmod", TeX1Arg, 0, 0, (void *)doPmod},
{"mathrm", TeX1Arg, 0, 0, (void *)doMathRm},
{"mathit", TeX1Arg, 0, 0, (void *)doMathIt},
{"sqrt", TeX1Arg, 0, 0, (void *)doSqrt},
{"fbox", TeX1Arg, 0, 0, (void *)doFbox},
{"symb", TeX1Arg, 0, 0, (void *)doSymb},
//
// TeX things that take two arguments, as in
// \word {arg1} {arg2}
// eg \frac{numerator}{denominator}
// Note that both frac and stackrel both make adjustments to the
// size of the naterial set as their arguments... but in eccentric
// and one-off ways!
{"frac", TeX2Arg, '/', 0, (void *)doFrac},
{"stackrel", TeX2Arg, 'S', 0, (void *)doStackrel},
{"rule", TeXRule, 0, 0, NULL},
{NULL, 0, 0, 0, NULL}
};
#define hashTableSize ((unsigned int)(sizeof(texWords)/sizeof(Keyword)))
// The hash function here is not especially clever or fast! But it should
// suffice. It looks at the first 4 characters of the string and returns
// a result in the range 0 to 511.
static unsigned int texHash(const char *s)
{
unsigned int h = 0x123456;
if (*s != 0)
{ h = 169*h + *s++;
if (*s != 0)
{ h = 169*h + *s++;
if (*s != 0)
{ h = 169*h + *s++;
h = 169*h + *s;
}
}
}
return (h ^ (h>>11)) % hashTableSize;
}
// returns either a reference to a Keyword record, or NULL if the word
// is not found.
static Keyword *lookupHash(const char *s)
{
unsigned int h = texHash(s);
for (;;)
{ if (texWords[h].name == NULL) return NULL;
else if (strcmp(s, texWords[h].name) == 0) return &texWords[h];
else h = (h + 1) % hashTableSize;
}
}
// On system-startup this code converts the initial neat array/table
// of keywords into a hash table.
static void rehashKeywordTable()
{
unsigned int i = 0;
// First set a flag on every item that has not yet been re-hashed.
while (texWords[i].name != NULL) texWords[i++].type |= TeXFlag;
#if 0
// I want my hash-table of LaTeX keywords to have a comfortable loading
// to balance lookup cost against the amount of waste space. The
// current state uses a little under 300 of the 512 entries, which
// seems satisfactory to me at present.
printf("%d out of %d loading in hash table\n", i, hashTableSize);
#endif
while (i < hashTableSize)
{ texWords[i].name = NULL;
texWords[i++].type = 0;
}
Keyword insert, pend;
for (i=0; i<hashTableSize; i++)
{ if ((texWords[i].type & TeXFlag) == 0) continue;
pend = texWords[i];
texWords[i].name = NULL;
texWords[i].type = 0;
while ((pend.type & TeXFlag) != 0)
{ pend.type &= ~TeXFlag;
insert = pend;
unsigned int h = texHash(insert.name);
for (;;)
{ if (texWords[h].name == NULL) // empty space to insert into
{ texWords[h] = insert;
break;
}
// I might come across a place where some not-yet-relocated entry lives. As
// far as the NEW hash table is concerned this represents empty space, so I
// insert there, moving the displaced data to a "pending" status.
else if ((texWords[h].type & TeXFlag) != 0)
{ pend = texWords[h];
texWords[h] = insert;
break;
}
// If I find a hash table position that is already in use it can never be
// a match, so I just view it as a clash.
// I use a simple linear scan after clashes. This can lead to clustering
// but I am not that worried!
h = (h + 1) % hashTableSize;
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// I want to be able to parse LaTeX-like stuff so that I can display it.
// I will use a pretty naive parser! It will look for the special
// characters
// # $ % & ~ _ ^ \ { and }
// It also has to treat some other individual characters individually
// + = | < > ` '
// and any item of the form "\" followed either by a single punctuation
// mark or by a (case-sensitive) word.
// There is a "current state" and nesting constructs save and restore it.
// Eg if there are any instances of
// { ... \em ... }
// then the "\em" sets a state for the materiual after it but until the
// end of the brace-enclosed block.
//
// Key bits of syntax are
// \begin{word} ... \end{word}
// \word-not-needing-args
// \word-needing-arg { ... }
// \word-needing-2-args { ... } { ... }
// \word[ ... , ... ] { ... } if cases with options are needed
// { ... }
// $ ... $
// \leftX ... \rightX
// item item item ...
//
// The stream of characters that I parse are returned by a procedure that
// is passed down to my code...
static getTeXchar *nextChar;
static int curChar;
// I had better make the size of my lexer buffer at last as wide as the
// longest item I ever expect to see on a line. Well I hope that any code
// that drives this splits lines well before 256 characters...
#define maxLexLength 256
static char lexerBuffer[maxLexLength+1];
static int lexType, lexLength;
static Keyword *lexKey;
#define lexError 0x00
#define lexEOF 0x01
#define lexSpecial 0x02
#define lexWord 0x03
#define lexNumber 0x04
#define lexBegin 0x05
#define lexEnd 0x06
#define lexSuper 0x07
#define lexSub 0x08
// More careful syntax, since this is what I will implement
//
// The category "P" is for complete items, either single words
// or things where their end is clear-cut because of bracketing rules.
// P ::= begin(word) E end(word)
// // I treat eg \begin{centre} s one lexical symbol
// // and need a bracketing rule for each such.
// | { E }
// | \( E \)
// | \[ E \]
// | $ E $
// | \leftX E \rightX
// | \begin{matrix} rows \end{matrix}
// | one-arg-word opts { E }
// | two-arg-word opts { E } { E }
// | word
// | punctuation-mark
// ;
// The category "S" allows a "P" to be given optional sub- or super-scripts.
// It permits at most one of each. One can of course write something like
// "{a^b}^c"
// if you really want, but "a^b^c" will not be valid in proper TeX. However
// as a matter of generosity I will allow it here via the code going a bit
// beyond the syntax written out here
// S ::= P
// | P ^ P
// | P _ P
// | P ^ P _ P
// | P _ P ^ P
// The category "E" is for a possibly empty list of "S" forms. I will in fact
// parameterise my code to read an "E" so that in one variant it stops only
// at a close bracket or end-of-file and in the other it also stops at
// a comma.
// E ::=
// | S
// | E S
// ;
// "opts" and "ol" provide for optional parameters marked out using square
// brackets and with commas to separate off separate parts. Note that I have
// a formal ambiguity here over the syntax "a,b" where the comma could be a
// punctuation mark as a P or separator between arguments. Actually inspection
// of the code that generated TeX for me shows that \sqrt is the only thing
// where an optional argument gets used, so I will handle the case specially
// for there and NOT use this general rule!
// opts ::=
// | []
// | [ ol ]
// ;
// ol ::= E
// | ol , E
// ;
// I deal specially with matrices by recognising & and \\ as delimiters.
// I view an empty matrix as an error and have an extra constraint that things
// should be rectangular, ie each row should have the same number of items
// in it.
// rows ::= matrow
// | rows \\ matrow
// ;
// matrow ::= E
// | matrow & E
// ;
// QUESTION: Do I need to take special account of things that are to be
// thought of as binary operators, such as +, - etc?
// I may want to if I want the tree that I produce to be a
// semantic model of the material being displayed and if I then
// want selection to respect this structure. But to start with
// I intend to try without.
// To do line-breaking here I will need to extend the syntax for E to be
// more like:
//
// E ::=
// | S1
// | E S1
// ;
// QUERY: especially in the light of REDLOG I may need precedence-related
// rules here for AND and OR and EQUALS. How pressing is that?
// S1 := S2
// | S1 + S2
// | S1 - S2
// | + S1
// | - S1
// ;
// S2 ::= S3
// | S2 \times S3
// | \frac{E}{E}
// ;
// S3 ::= item LPAR ARGLIST RPAR
// etc.
// I am not yet really certain just how much I need to do here but my current
// thoughts are that for line-breaking I will need to be able to notice the
// cases for
// A + B + C + D +
// E + F + G break at a + or - rather than within a term
// and similarly for lists {A,B,C} and (A,B,C)
//
// (... ... ... vs ... ... ...
// ... ...) / (...) ----------- for displayed fractions
// ... ... ...
//
// matrix display where possible
//
// Some uncomfortable fall-back for cases with huge expressions as
// exponents, subscripts, single args for functions etc
//
// Treatement of numbers that have huge numbers of digits (and I guess
// words that are very very long too)
//
// sqrt(... ... ... vs /---------------
// ... ... ...) v ... ... ... ...
//
// Groupings implied by all sorts of brackets, also by the prefix
// items \int, \sum (etc). This last one is NASTY since it is not clear
// that there is any way to tell when the guarded expression ends!
static void nextSymbol();
static Box *readE(int stopAt);
#define stopComma 0x01
#define stopTab 0x02
#define stopNL 0x04
#define stopKet 0x08
static Box *readS();
// Some systems have problems with characters that have a numeric code
// less than (or possibly even equal to) 0x20. Although many modern systems
// can cope the Postscript fonts that I use provide re-mapped glyphs with
// code values in the 0x80 to 0xff range that may be safer to use.
// With some X servers I still have trouble with character 0xa1, which is
// where 0x00 gets mapped to. Actually it seems worse than that: I can
// re-map the font to change the code to yet something different and still
// sometimes the character does not appear. I reserve code 0xc5 for such
// and extra remapping, but the tables here do not touch it at present!
static char remapTable[33] =
{
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
0xa9, 0xaa, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2,
0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2,
0xa0
};
static int remap(int ch)
{
ch &= 0xff; // in case chars are signed.
#ifndef WIN32
// If I use Xft to render things I can afford to (and indeed had better)
// use glyph positions in ther original locations. Also if I am printing
// I will avoid remapping.
if (fwin_use_xft) return (char)ch;
#endif
if (ch < 0x20) return remapTable[ch];
// There is SOME possibility that I should remap character 0x20 to 0xa0,
// but right now my belief is that I should not.
else if (ch == 0x7f) return (char)0xc4;
else return (char)ch;
}
#ifdef HAVE_XFT
// I will also set myself up so that I can restore the original encoding.
static char unmapTable[38] =
{
0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0xab, 0xac, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
0x1d, 0x1e, 0x1f, 0x20, 0x7f, 0xc5
};
static int unmap(int ch)
{
ch &= 0xff;
if (ch < 0xa0 || ch > 0xc5) return (char)ch;
return unmapTable[ch - 0xa0];
}
#endif
static Box *readRow()
{
Box *result = makeSymBox(SymNothing);
Box *n = readE(stopTab + stopNL);
if (n != NULL) result = makeNestBox(BoxBeside, result, n);
while (lexType == lexSpecial &&
strcmp(lexKey->name, "&") == 0)
{ nextSymbol();
n = readE(stopTab + stopNL);
if (n != NULL) result = makeNestBox(BoxBeside, result, n);
}
return result;
}
static Box *readRows()
{
Box *result = makeSymBox(SymNothing);
Box *n = readRow();
if (n != NULL) result = makeNestBox(BoxTower, result, n);
while (lexType == lexSpecial &&
strcmp(lexKey->name, "\\\\") == 0)
{ nextSymbol();
Box *n = readRow();
if (n != NULL) result = makeNestBox(BoxTower, result, n);
}
return result;
}
static Box *readMatrix()
{
Box *contents = NULL;
TeXState saveState = currentState;
nextSymbol();
contents = readRows();
if (lexType == lexError) return NULL;
else if (lexType != lexEnd || matchMatrix != lexKey->font)
{ printf("end does not match begin properly\n");
printf("Want \\end{matrix} got %d/%d\n", lexType, lexKey->font);
lexType = lexError;
return NULL;
}
currentState = saveState;
contents = makeMatrixBox(contents);
nextSymbol();
return contents;
}
static void skipUnit()
{
if (lexType != lexWord) return;
if (strcmp(lexerBuffer, "pt") == 0) nextSymbol();
}
static int readNumber()
{
int r;
if (lexType == lexNumber)
{ sscanf(lexerBuffer, "%d", &r);
nextSymbol();
skipUnit();
return r;
}
else if (lexType == lexSpecial && strcmp(lexerBuffer, "-") == 0)
{ nextSymbol();
if (lexType == lexNumber)
{ sscanf(lexerBuffer, "%d", &r);
nextSymbol();
skipUnit();
return -r;
}
}
return 0;
}
static Box *readP()
{ switch (lexType)
{
case lexBegin:
{ int w = lexKey->font; // used to say which sort of "begin"
if (w == matchMatrix) return readMatrix();
int chL = lexKey->charCode;
Box *contents = NULL;
TeXState saveState = currentState;
blockHandlerFunction *fn = (blockHandlerFunction *)lexKey->ptr;
if (fn != NULL) fn(chL, ' ', NULL);
nextSymbol();
contents = readE(0);
if (lexType == lexError) return NULL;
else if (lexType == lexEOF)
{
// Here if I find an EOF when I was expecting a close bracket of some sort
// I just treat it as if the close bracket had been found, an in the case
// of \left..\right I treat it as if I had seen "\right .".
if (fn != NULL) contents = fn(chL, '.', contents);
return contents;
}
else if (lexType != lexEnd || w != lexKey->font)
{ printf("end does not match begin properly\n");
printf("Want %d/%d got %d/%d\n",
lexEnd, w, lexType, lexKey->font);
lexType = lexError;
// In this case I will not perform any side-effects that calling the handler
// function normally would at the end of a block. That certainly means,
// for instance, that I lose track of whether I am inside or outside "$"
// markers. But since I have reported an error and the parse is getting
// nowhere this does not worry me.
return NULL;
}
currentState = saveState;
if (fn != NULL) contents = fn(chL, lexKey->charCode, contents);
nextSymbol();
return contents;
}
case lexWord:
{ Box *b = makeTextBox(lexerBuffer, lexLength,
currentState.currentFont); // includes currentSize
nextSymbol();
return b;
}
case lexNumber:
{ Box *b = makeTextBox(lexerBuffer, lexLength,
FntRoman + currentSize);
nextSymbol();
return b;
}
case lexSpecial: // in this case lexKey tells me which keyword it is.
switch (lexKey->type)
{
case TeXSymbol:
{ char s[2];
s[0] = remap(lexKey->charCode);
s[1] = 0;
Box *b = makeTextBox(s, 1, lexKey->font + currentSize);
nextSymbol();
return b;
}
case TeXWSymbol:
{ char s[2];
s[0] = remap(lexKey->charCode);
s[1] = 0;
Box *b = makeTextBox(s, 1, lexKey->font + currentSize);
nextSymbol();
return makeNestBox(BoxPadSpace, b, NULL);
}
case TeXVSymbol:
// This case will need MORE WORK in a couple of ways:
// (a) the symbol used is from the cmex font and so is positioned as
// a descender. It will need moving to put it where it wants to go!
// (b) subscripts and superscripts for these objects get put in special
// places. Eg consider \sum{i=0}^{n}.
// I mark for these by setting the "FntBig" bit in the byte that gives font
// and size. That then mostly has to adjust the code that lays out subscripts.
{ char s[2];
s[0] = remap(lexKey->charCode);
s[1] = 0;
Box *b = makeTextBox(s, 1, lexKey->font + FntBig + currentSize);
nextSymbol();
return b;
}
case TeXRoman:
{ Box *b = makeTextBox((const char *)lexKey->ptr,
strlen((const char *)lexKey->ptr),
FntRoman + currentSize);
nextSymbol();
return b;
}
case TeXScript:
printf("TeX script-marker found but not handled (%s)\n", lexKey->name);
nextSymbol();
return NULL;
case TeX0Arg:
if (lexKey->ptr != NULL)
{ Box *b = ((keywordHandlerFunction *)(lexKey->ptr))(lexKey->charCode);
nextSymbol();
return b;
}
printf("TeX keyword 0 found but not handled (%s)\n", lexKey->name);
nextSymbol();
return NULL;
case TeX1Arg:
// One-arg things can include cases such as "\sqrt" which need to support
// optional args as well as the mandatory one. As best I can see in the
// current tmprint code "\sqrt" is the only such case used! Furthermore
// I think it only ever puts ONE item within the "[]", as in
// \sqrt[3]{1+x} or \sqrt[p]{A}
// but it does put a "\," within the square brackets to adjust spacing.
{ Keyword *key = lexKey; // remember what introduced this
Box *contents = NULL, *optarg = NULL;
TeXState saveState = currentState;
oneArgHandlerFunction *fn = (oneArgHandlerFunction *)key->ptr;
nextSymbol();
// Check here for a "["
if (lexType == lexSpecial &&
strcmp(lexKey->name, "[") == 0)
{ nextSymbol();
if (currentSize < FntScrScr) currentState.currentFont+=FntScript;
optarg = readE(stopKet);
if (lexType != lexSpecial ||
strcmp(lexKey->name, "]") != 0)
{ printf("\"]\" not found\n");
lexType = lexError;
currentState = saveState;
return NULL;
}
currentState = saveState;
nextSymbol();
}
// The state I use when parsing the argument may be changed by this where I
// call the handler with NULL arguments.
if (fn != NULL) fn(NULL, NULL);
if (lexType != lexBegin ||
lexKey->font != matchBrace)
{ printf("\"{\" expected after keyword \"\\%s\"\n",
lexKey->name);
lexType = lexError;
currentState = saveState;
return NULL;
}
else nextSymbol();
contents = readE(0);
if (lexType == lexError)
{ currentState = saveState;
return NULL;
}
else if (lexType != lexEnd || lexKey->font != matchBrace)
{
// While I am lenient about SOME forms of bracketing, the "{}" surrounding
// arguments must be complete in one section of TeX input.
printf("\"}\" not found\n");
lexType = lexError;
currentState = saveState;
return NULL;
}
nextSymbol();
currentState = saveState;
if (fn != NULL) contents = fn(contents, optarg);
return contents;
}
case TeX2Arg:
{ Keyword *key = lexKey;
Box *b1, *b2;
TeXState saveState = currentState;
twoArgsHandlerFunction *fn = (twoArgsHandlerFunction *)key->ptr;
if (fn == NULL)
{ printf("incomplete implementation for \"\\%s\"\n", lexKey->name);
return NULL;
}
nextSymbol();
if (lexType != lexBegin ||
lexKey->font != matchBrace)
{ printf("\"{\" expected after keyword \"\\%s\"\n",
lexKey->name);
lexType = lexError;
return NULL;
}
else nextSymbol();
// The SECOND (and deeper) depth of \frac causes its contents
// to be set in a smaller font, as for subscripts.
if (key->font == '/')
{ if (currentState.insideFrac)
{ if (currentSize < FntScrScr)
currentState.currentFont+=FntScript;
}
else currentState.insideFrac = 1;
}
if (key->font == 'S' &&
currentSize < FntScrScr)
currentState.currentFont+=FntScript;
b1 = readE(0);
if (lexType == lexError)
{ currentState = saveState;
return NULL;
}
else if (lexType != lexEnd || lexKey->font != matchBrace)
{ printf("\"}\" not found\n");
lexType = lexError;
currentState = saveState;
return NULL;
}
nextSymbol();
currentState = saveState;
if (key->font == '/')
{ if (currentState.insideFrac)
{ if (currentSize < FntScrScr)
currentState.currentFont+=FntScript;
}
else currentState.insideFrac = 1;
}
// The second arg can be any sort of block, but it could
// also be a simple atom. Thus "\frac{1}2" will be accepted
b2 = readP();
currentState = saveState;
return fn(b1, b2);
}
case TeXRule: // I want to support \rule[offset]{height}{width}
{ Box *b1;
int depth=0, height=0, width=0;
TeXState saveState = currentState;
nextSymbol();
if (lexType == lexSpecial &&
strcmp(lexKey->name, "[") == 0)
{ nextSymbol();
depth = readNumber();
if (lexType != lexSpecial ||
strcmp(lexKey->name, "]") != 0)
{ printf("\"]\" not found\n");
lexType = lexError;
currentState = saveState;
return NULL;
}
currentState = saveState;
nextSymbol();
}
currentState = saveState;
if (lexType != lexBegin ||
lexKey->font != matchBrace)
{ printf("\"{\" expected after keyword \"\\%s\"\n",
lexKey->name);
lexType = lexError;
currentState = saveState;
return NULL;
}
else nextSymbol();
width = readNumber();
if (lexType == lexError)
{ currentState = saveState;
return NULL;
}
else if (lexType != lexEnd || lexKey->font != matchBrace)
{ printf("\"}\" not found\n");
lexType = lexError;
currentState = saveState;
return NULL;
}
nextSymbol();
currentState = saveState;
// The second arg can be any sort of block, but it could
// also be a simple atom. Thus "\rule{1}2" will be accepted.
if (lexType == lexBegin &&
lexKey->font == matchBrace)
{ nextSymbol();
height = readNumber();
if (lexType == lexError) return NULL;
else if (lexType != lexEnd || lexKey->font != matchBrace)
{ printf("\"}\" not found\n");
lexType = lexError;
return NULL;
}
nextSymbol();
}
else height = readNumber();
currentState = saveState;
b1 = makeSymBox(SymRule);
b1->sym.height = height+depth;
b1->sym.depth = -depth;
b1->sym.width = width;
return b1;
}
default:
printf("TeX keyword found but not handled (%s)\n", lexKey->name);
nextSymbol();
return NULL;
}
default:
printf("TeX syntax problem\n");
lexType = lexError;
case lexError: // if the token is lexError a diagnostic has already
// been printed.
return NULL;
}
return NULL;
}
static Box *readS()
{
Box *base, *super=NULL, *sub=NULL;
base = readP();
for (;;)
{ if (lexType == lexSuper)
{ nextSymbol();
TeXState saveState = currentState;
if (currentSize < FntScrScr) currentState.currentFont+=FntScript;
super = readP();
currentState = saveState;
if (lexType == lexSub)
{ nextSymbol();
saveState = currentState;
if (currentSize < FntScrScr) currentState.currentFont+=FntScript;
sub = readP();
currentState = saveState;
}
}
else if (lexType == lexSub)
{ nextSymbol();
TeXState saveState = currentState;
if (currentSize < FntScrScr) currentState.currentFont+=FntScript;
sub = readP();
currentState = saveState;
if (lexType == lexSuper)
{ nextSymbol();
saveState = currentState;
if (currentSize < FntScrScr) currentState.currentFont+=FntScript;
super = readP();
currentState = saveState;
}
}
// Now I have parsed a bit: glue the boxes together...
if (sub == NULL)
{ if (super != NULL)
base = makeNestBox(BoxSuperscript, base, super);
}
else if (super == NULL) base = makeNestBox(BoxSubscript, base, sub);
else base = makeNest3Box(BoxBothScripts, base, sub, super);
// Going beyond what TeX supports I will permit things like
// x^y^z and x_y_z. I group sub- and superscripts so that at each level
// I support one of each in either order. The resulting layout may be
// less then perfect but should be what you would have got with
// {x^y}^z etc so is not outrageous.
if (lexType != lexSuper &&
lexType != lexSub) break;
}
return base;
}
static Box *readE(int stopAt)
{
// I ALWAYS stop if I see a "}" or an "\end{xxx}". I can optionally
// stop on ",", "&" or "\\" (the first of these is used in optional argument
// lists and the second two in matrixes.
Box *result = NULL;
// This ALWAYS stops as a lexEnd (or error or eof). Depending on the
// bitmask given as an argument it may stop at "\\", "&", "," or "]" too
while (lexType != lexEnd &&
lexType != lexError &&
lexType != lexEOF &&
(!(stopAt & stopTab) ||
lexType != lexSpecial ||
strcmp(lexKey->name, "&") != 0) &&
(!(stopAt & stopNL) ||
lexType != lexSpecial ||
strcmp(lexKey->name, "\\\\") != 0) &&
(!(stopAt & stopKet) ||
lexType != lexSpecial ||
strcmp(lexKey->name, "]") != 0) &&
(!(stopAt & stopComma) ||
lexType != lexSpecial ||
strcmp(lexKey->name, ",") != 0))
{ Box *n = readS();
if (result == NULL) result = n;
else if (n != NULL)
result = makeNestBox(BoxBeside, result, n);
}
return result;
}
static Box *readE1()
{
// Read stuff until EOF.
Box *result = NULL;
while (lexType != lexError &&
lexType != lexEOF)
{
// Here if I get an "unexpected" close bracket I will in effect insert a
// "\left ." at the start of my input to match it.
if (lexType == lexEnd)
{ blockHandlerFunction *fn = (blockHandlerFunction *)lexKey->ptr;
if (result == NULL) result = makeSymBox(SymNothing);
if (fn != NULL) result = fn('.', lexKey->charCode, result);
nextSymbol();
continue;
}
Box *n = readS();
if (result == NULL) result = n;
else if (n != NULL)
result = makeNestBox(BoxBeside, result, n);
}
return result;
}
static void nextSymbol()
{
// Discard whitespace
while (curChar != 0 && isspace(curChar)) curChar = (*nextChar)();
// Detect end of input
if (curChar == 0)
{ lexType = lexEOF;
return;
}
// OK, now we have the start of a new symbol.
switch (curChar)
{
case '%': // discard comments
while ((curChar = (*nextChar)()) != 0 && curChar != '\n');
nextSymbol();
return;
case '^':
lexType = lexSuper;
curChar = (*nextChar)();
return;
case '_':
lexType = lexSub;
curChar = (*nextChar)();
return;
case '{':
case '}':
case '&':
lexerBuffer[0] = curChar;
lexerBuffer[1] = 0;
lexLength = 1;
lexKey = lookupHash(lexerBuffer);
if (curChar == '{') lexType = lexBegin;
else if (curChar == '}') lexType = lexEnd;
else lexType = lexSpecial;
curChar = (*nextChar)();
return;
case '$':
lexerBuffer[0] = curChar;
lexerBuffer[1] = 0;
lexLength = 1;
lexKey = lookupHash(lexerBuffer);
// "$" will be a BEGIN if you are not in math mode, or an END if you are!
// This is yet another curious hack to cope with the syntax of LaTeX, which
// appears somewhat curious in places.
lexType = insideDollars ? lexEnd : lexBegin;
curChar = (*nextChar)();
return;
// A bunch of characters are handled in a magic way here because they
// might map onto some specific font or character coding.
case '+': case '-': case '*': case '/':
case ':': case ';': case '@': case ',':
case '.': case '?': case '!': case '|':
case '`': case '#': case '~': case '=':
case '(': case ')': case '<': case '>':
case '[': case ']':
// "NOTSIGN" ??
lexerBuffer[0] = curChar;
lexerBuffer[1] = 0;
lexLength = 1;
if (DEBUGFONT & 8) printf("lexer special char %s\n", lexerBuffer);
lexKey = lookupHash(lexerBuffer);
lexType = lexSpecial;
curChar = (*nextChar)();
return;
case '\\':
lexLength = 0;
curChar = (*nextChar)();
if (curChar == 0) // Treat "\<eof>" as just <eof>
{ lexType = lexEOF;
return;
}
while (isalnum(curChar))
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
// I assemble EITHER "\word" or "\delim"
if (lexLength == 0)
{ lexerBuffer[lexLength++] = '\\'; // put the "\" explicitly there
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
lexerBuffer[lexLength] = 0;
// There is an UTTERLY odd bit of lexical treatment here that
// makes "\left(" one token. What I do is to take "\left" and "\right"
// and treat the token after it as part of a single token.
// Thus I have in effect "\left (" as just one token.
// The item following one of the words \left or \right must be a TeX
// "delimiter" and they can be in one of 3 forms:
// character (,),[,],/,|
// escaped character \{, \}, \|
// escaped word \lfloor, \Uparrow etc etc
// I will pack things in being a bit generous in what I allow, but bad cases
// will then get spotted because they will count as unknown TeX keywords.
if ((strcmp(lexerBuffer, "left") == 0 ||
strcmp(lexerBuffer, "right") == 0) &&
curChar != 0)
{ while (curChar != 0 && isspace(curChar)) curChar = (*nextChar)();
lexerBuffer[lexLength++] = ':';
if (curChar == '\\')
{ lexerBuffer[lexLength++] = curChar; // insert the backslash
curChar = (*nextChar)();
if (curChar != 0 && isalnum(curChar))
{ while (isalnum(curChar)) // \word
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
}
else if (curChar != 0) // \delim
{ lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
}
else if (curChar != 0) // just a delim
{ lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
lexerBuffer[lexLength] = 0;
}
lexKey = lookupHash(lexerBuffer);
if (lexKey == NULL)
{ printf("Unrecognised keyword \"\\%s\"\n", lexerBuffer);
lexType = lexError;
return;
}
if (lexKey->type == TeXBeginWord) // "\begin" or "\end"
{ int beginFlag = lexKey->font;
while (curChar != 0 && isspace(curChar)) curChar = (*nextChar)();
if (curChar != '{')
{ printf("Brace expected after \\begin or \\end\n");
lexType = lexError;
return;
}
else curChar = (*nextChar)();
while (curChar != 0 && isspace(curChar)) curChar = (*nextChar)();
lexLength = 0;
lexerBuffer[lexLength++] = beginFlag ? '(' : ')';
while (isalnum(curChar))
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
while (curChar != 0 && isspace(curChar)) curChar = (*nextChar)();
if (curChar != '}')
{ printf("Closing brace expected after \\begin or \\end\n");
lexType = lexError;
return;
}
else curChar = (*nextChar)();
lexerBuffer[lexLength] = 0;
lexKey = lookupHash(lexerBuffer);
if (lexKey == NULL)
{ printf("Unrecognised \\begin or \\end \"\\%s\"\n",
&lexerBuffer[1]);
lexType = lexError;
return;
}
lexType = beginFlag ? lexBegin : lexEnd;
return;
}
// I now need to cope with "\left(", "\(" and the like which are "begin/end"
// style items.
if (lexKey->type == TeXBegin) lexType = lexBegin;
else if (lexKey->type == TeXEnd) lexType = lexEnd;
else lexType = lexSpecial;
return;
default:
lexLength = 0;
// Anything that starts with a digit will be taken to be a number. I permit
// digits then possibly "." followewd by more digits and possibly an exponent
// introduced by "e" or "E".
if (isdigit(curChar)) // handle as a number
{ while (isdigit(curChar))
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
if (curChar == '.')
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
while (isdigit(curChar))
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
}
if (curChar == 'e' || curChar == 'E')
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
if (curChar == '+' || curChar == '-')
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
while (isdigit(curChar))
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
}
lexerBuffer[lexLength] = 0;
lexType = lexNumber;
return;
}
while (isalnum(curChar))
{ if (lexLength < maxLexLength)
lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
if (lexLength == 0)
{ lexerBuffer[lexLength++] = curChar;
curChar = (*nextChar)();
}
lexerBuffer[lexLength] = 0;
lexType = lexWord;
return;
}
}
Box *parseTeX(getTeXchar *fn, int owner)
{
nextChar = fn;
poolStartChunk(owner);
curChar = (*nextChar)();
nextSymbol();
if (DEBUGFONT & 8) printf("lexType = %d\n", lexType);
// That has got the lexer ready - now I can do the REAL parsing!
// I do my parsing here in \displaymath mode, so the font I will use
// by default will be an italic one.
insideDollars = 0;
currentState.currentFont = FntItalic + FntRegular;
currentState.insideFrac = 0;
// now the paser is initialised...
Box *r = readE1();
if (r != NULL) r = makeTopBox(r); // hook on a handle to the chunk start.
poolEndChunk();
return r;
}
void updateOwner(Box *b, int p)
{
IntVoidStar *chunk = (IntVoidStar *)(b->top.chunk);
chunk->i = p;
}
///////////////////////////////////////////////////////////////////////////
#ifdef WIN32
#define setFont1(dc, ff) dc->setFont((FXFont *)ff)
#define drawText1(dc, x, y, ss, l) dc->drawText(x, y, ss, l)
#else
#ifdef HAVE_XFT
// Try to set up to use Xft and client side fonts. If this is not
// possible (eg because the fonts that I want can not be found) set
// fwin_use_xft to 0 so that later code does not stumble.
static void setupXft(FXApp *app)
{
ftDraw = NULL; // I do this first so it is established even if Xft
// is not going to be used.
if (!fwin_use_xft) return;
#ifdef USE_FCINIT
if (!FcInit())
{ fwin_use_xft = 0;
return;
}
#else
{ FcConfig *w = FcConfigCreate();
FcConfigSetCurrent(w);
}
#endif
XftInit("");
dpy = (Display *)app->getDisplay();
screen = DefaultScreen(dpy);
// NOTE: I probably want to be able to save the existing font configuration
// so that I restore if after opening the maths fonts in case I need to change
// the size or style of the main non-maths font that I use.
// Establish my own private supply of fonts
// I will add in the four font files that I want to use here. Note that
// with Xft I will force use of the ".pfb" versions. These are close to
// BUT NOT IDENTICAL TO the original AMS Type1 Computer Modern fonts.
// It is important to use MY versions of the fonts here.
int someAdded = 0;
for (int i=0; i<4; i++)
{ char *ff = (char *)
malloc(20+strlen(programDir)+strlen(fontNames[i].name));
char ff1[LONGEST_LEGAL_FILENAME];
if (ff == NULL)
{ fwin_use_xft = 0;
return;
}
sprintf(ff, "%s/r38.fonts/%s.pfb", programDir, fontNames[i].name);
// For Xft I will insist that ALL the fonts that I try to add have
// readable font file and are accepted by AppFontAdd, otherwise I will
// suppose that Xft is not usable.
// The "0" arg to FcConfigAppFontAddFile says "add to default configuration"
if (!file_readable(ff1, ff, strlen(ff)) ||
!FcConfigAppFontAddFile(0, (const FcChar8 *)ff))
{ fwin_use_xft = 0;
return;
}
else someAdded = 1;
}
// ensure that fonts are really available. If I do not actually add any
// I will not fuss about this!
if (someAdded && !FcConfigBuildFonts(FcConfigGetCurrent()))
{ fwin_use_xft = 0;
return;
}
// I now have some confidence that the fonts that I want will be available
// to me. Well at least if the local font files existed!
#ifdef DEBUG_LIST_AVAILABLE_FONTS
for (int i=0; i<4; i++)
{ XftFontSet *fs = XftListFonts(dpy, screen,
XFT_FAMILY, XftTypeString, fontNames[i].name,
(const char *)0,
XFT_STYLE, XFT_FILE, XFT_ENCODING,
(const char *)0);
if (fs->nfont == 0)
{ XftFontSetDestroy(fs);
// Here at least one of the fonts that I ought to have in the r38.fonts
// directory does not seem to be available. I will thus disable use of
// Xft and fall back to the earlier style of X use via FOX's FXFont.
fwin_use_xft = 0;
return;
}
for (int j=0; j<fs->nfont; j++)
FcPatternPrint(fs->fonts[j]); // To help me debug
XftFontSetDestroy(fs);
}
#endif // DEBUG
ftVisual = DefaultVisual(dpy, screen);
ftColormap = DefaultColormap(dpy, screen);
// I really want the id() of the things that actually gets drawn on...
Drawable draw = (Drawable)((FXText *)text)->id();
ftDraw = XftDrawCreate(dpy, draw, ftVisual, ftColormap);
XftColorAllocValue(dpy, ftVisual, ftColormap, &ftRenderBlack, &ftBlack);
XftColorAllocValue(dpy, ftVisual, ftColormap, &ftRenderWhite, &ftWhite);
}
#endif
static void setFont1(FXDC *dc, void *ff)
{
#ifdef HAVE_XFT
if (fwin_use_xft)
{ ftFont = (XftFont *)ff;
return;
}
#endif
dc->setFont((FXFont *)ff);
}
static void drawText1(FXDC *dc, int x, int y, const char *ss, int l)
{
#ifdef HAVE_XFT
if (fwin_use_xft)
{ l = convertGlyphs(ss, l);
XftDrawGlyphs(ftDraw, &ftBlack, ftFont,
x, y, glyphs, l);
return;
}
#endif
dc->drawText(x, y, ss, l);
}
#endif
///////////////////////////////////////////////////////////////////////////
static void paintBracket(FXDC *dc, int type, int flags,
int x, int y, int height, int depth)
{
// See the LaTeX manual for a list of "delimiters", and note that I encode
// each as a single character here, with a maybe curious encoding. Also I
// draw square root signs here since they are also variable height items
// manufactured (sometimes) out of multiple glyphs in a tower.
//
// Maybe the case that concerns me most here will be \langle and \rangle since
// they need horizontal offsets for glyphs. At least to start with I will not
// support them!!
int size = flags & FontSizeMask;
int n = bracketCount(flags, height, depth);
// n is the size of bracket needed, measured in units of 0.5 times the
// normal cmex font height
if (n == 1)
{ int fnt = FntSymbol, ch = 0x00;
switch(type)
{
case '.':
return;
case '(':
fnt = FntRoman;
ch = 0x28;
break;
case ')':
fnt = FntRoman;
ch = 0x29;
break;
case '[':
fnt = FntRoman;
ch = 0x5b;
break;
case ']':
fnt = FntRoman;
ch = 0x5d;
break;
case '{':
ch = 0x64;
break;
case '}':
ch = 0x65;
break;
case '|': // LEFT vertical bar
ch = 0x6a;
break;
case '!': // RIGHT vertical bar
ch = 0x6a;
break;
case 'Q': // square root marker
ch = 0x70; // NB this is a descender
y -= mathFontHeight[size];
break;
case '/': // slash
ch = 0x36;
break;
case '\\': // backslash
ch = 0x6e;
break;
case 'L': // lfloor
ch = 0x62;
break;
case 'G': // lceil (like Gamma)
ch = 0x64;
break;
case 'J': // rfloor
ch = 0x63;
break;
case '7': // rceil
ch = 0x65;
break;
case '<': // langle
ch = 0x68;
break;
case '>': // rangle
ch = 0x69;
break;
case '#': // \| (double vertical bar)
ch = 0x6b;
break;
case '^': // uparrow
ch = 0x22;
break;
case 'v': // downarrow
ch = 0x23;
break;
case 'A': // Uparrow
ch = 0x2a;
break;
case 'V': // Downarrow
ch = 0x2b;
break;
case 'b': // updownarrow
ch = 0x6c;
break;
case 'B': // Updownarrow
ch = 0x6d;
break;
default:
printf("Attempt to paint unknown delimiter \"%c\"\n", type);
return;
}
char cc[1];
cc[0] = remap(ch);
setFont1(dc, mathFont[fnt + size]);
drawText1(dc, x, y, cc, 1);
return;
}
void *ff = mathFont[FntExtension + size];
int h = mathFontHeight[size], d = mathFontDepth[size];
int height1 = height + depth;
int h1 = h + d;
int height2 =(9*n*(h + d) + 8)/16;
// height2 will be the actual total height of the bracket that I draw.
if (height2 < height1) height2 = height1; // top align always
y = y - height - (height2 - height1 + 1)/2;
// The big delimiters are present in the font as "descenders" and so the
// place I must paint them is referenced to their top. y is now the
// coordinate to paint the top glyph.
int n1 = n;
if (n1 > 6) n1 = 6;
const char *topp;
int top, tower, mid = 0x00, bottom;
int toph = 0;
int ntower = -1;
switch (type)
{
case '.':
return;
case '(':
topp = "\x00\x10\x12\x20\x30";
toph = 3;
tower = 0x42;
if (n >= 6) ntower = n - 6;
bottom = 0x40;
break;
case ')':
topp = "\x01\x11\x13\x21\x31";
toph = 3;
tower = 0x43;
if (n >= 6) ntower = n - 6;
bottom = 0x41;
break;
case '[':
topp = "\x02\x68\x15\x22\x32";
toph = 3;
tower = 0x36;
if (n >= 6) ntower = n - 6;
bottom = 0x34;
break;
case ']':
topp = "\x03\x69\x16\x23\x33";
toph = 3;
tower = 0x37;
if (n >= 6) ntower = n - 6;
bottom = 0x35;
break;
case '{':
topp = "\x08\x6e\x1a\x28\x38";
toph = 3;
tower = 0x3e;
if (ntower >= 6) ntower = n - 6;
mid = 0x3c;
bottom = 0x3a;
break;
case '}':
topp = "\x09\x6f\x1b\x29\x39";
toph = 3;
tower = 0x3e;
if (n >= 6) ntower = n - 6;
mid = 0x3d;
bottom = 0x3b;
break;
case '|': // LEFT vertical bar
topp = "\x0c\x0c\x42\x42\x42";
// Note that vertical bars have to use a tower of glyphs right from
// the very start, while all the other cases can use a single glyph for
// heights 2,3,4 and 5.
toph = 1;
tower = 0x0c;
if (n >= 2) ntower = n - 2;
bottom = 0x0a;
break;
case '!': // RIGHT vertical bar
topp = "\x0c\x0c\x43\x43\x43";
toph = 1;
tower = 0x0c;
if (n >= 2) ntower = n - 2;
bottom = 0x0a;
break;
case 'Q':
topp = "\x70\x71\x72\x73\x76";
toph = 1;
tower = 0x75;
if (n > 5) ntower = n - 4;
bottom = 0x74;
break;
case 'L': // lfloor
topp = "\x04\x6a\x16\x24\x36";
toph = 1;
tower = 0x36;
if (n > 5) ntower = n - 4;
bottom = 0x34;
break;
case 'G': // lceil
topp = "\x06\x6c\x18\x22\x32";
toph = 3;
tower = 0x36;
if (n > 5) ntower = n - 4;
bottom = 0x36;
break;
case 'J': // rfloor
topp = "\x05\x6b\x17\x25\x37";
toph = 1;
tower = 0x37;
if (n > 5) ntower = n - 4;
bottom = 0x35;
break;
case '7': // rceil
topp = "\x07\x6d\x19\x23\x33";
toph = 3;
tower = 0x37;
if (n > 5) ntower = n - 4;
bottom = 0x37;
break;
case '^': // uparrow
// ALL cases are built up as towers, not just especially big ones
topp = "\xff\xff\xff\xff\x78";
toph = 1;
tower = 0x3f;
ntower = n - 2;
bottom = 0x3f;
break;
case 'v': // downarrow
topp = "\xff\xff\xff\xff\x3f";
toph = 1;
tower = 0x3f;
ntower = n - 2;
bottom = 0x79;
break;
case 'b': // updownarrow
topp = "\xff\xff\xff\xff\x78";
toph = 1;
tower = 0x3f;
ntower = n - 2;
bottom = 0x79;
break;
case 'A': // Uparrow
topp = "\xff\xff\xff\xff\x7e";
toph = 1;
tower = 0x77;
ntower = n - 2;
bottom = 0x77;
break;
case 'V': // Downarrow
topp = "\xff\xff\xff\xff\x77";
toph = 1;
tower = 0x77;
ntower = n - 2;
bottom = 0x7f;
break;
case 'B': // Updownarrow
topp = "\xff\xff\xff\xff\x7e";
toph = 1;
tower = 0x77;
ntower = n - 2;
bottom = 0x7f;
break;
case '#': // \| double vertical line
topp = "\xff\xff\xff\xff\x77";
toph = 1;
tower = 0x77;
ntower = n - 2;
bottom = 0x77;
break;
case '<': // langle
case '>': // rangle
case '\\': // backslash
case '/': // slash
default:
printf("Attempt to paint unknown delimiter \"%c\"\n", type);
return;
}
top = topp[n1-2];
if ((top & 0xff) == 0xff) top = topp[4];
// I general here I need to display in one of several patterns based on
// midflag and ntower. I use ntower=-1 for the case where a single
// glyph pains the entire bracket, otherwise ntower is a repeat
// count. mid is non-zero only in the "{}" case where everything is
// messier... In the easy case I go:
//
// top height is toph
// tower done ntower times height is 1
// bottom height is toph (except sqrt!)
//
// If mid is non-zero I use:
//
// top height is 1.5
// tower done ntower times height is 0.5
// mid height is 3
// tower done ntower times height is 0.5
// bottom height is 1.5
//
char ss[1];
setFont1(dc, ff);
// Draw the top glyph
ss[0] = remap(top);
drawText1(dc, x, y, ss, 1);
if (ntower < 0) return; // all done in just one glyph
if (mid == 0x00)
{ int delta2y = toph*h1; // twice the y delta needed for next glyph
while (ntower > 1)
{ ss[0] = remap(tower);
drawText1(dc, x, y+(9*delta2y)/16, ss, 1);
delta2y += h1;
ntower--;
}
ss[0] = remap(bottom);
drawText1(dc, x, y+(9*delta2y)/16, ss, 1);
return;
}
// Now I have a continuation of a { or }.
int delta4y = toph*h1;
for (n=1; n<ntower; n++)
{ ss[0] = remap(tower);
drawText1(dc, x, y+(9*delta4y)/32, ss, 1);
delta4y += h1;
}
ss[0] = remap(mid);
drawText1(dc, x, y+(9*delta4y)/32, ss, 1);
delta4y += 6*h1;
for (n=1; n<ntower; n++)
{ ss[0] = remap(tower);
drawText1(dc, x, y+(9*delta4y)/32, ss, 1);
delta4y += h1;
}
ss[0] = remap(bottom);
drawText1(dc, x, y+(9*delta4y)/32, ss, 1);
return;
}
void paintBox(FXDC *dc, Box *b, int x, int y)
{
void *ff;
TextBox *t;
SymBox *s;
BracketBox *bb;
int h1, d1;
switch (b->text.type)
{
case BoxSym:
s = &(b->sym);
switch (s->flags & SymTypeMask)
{
case SymStar:
case SymComma:
case SymExclam:
case SymColon:
case SymSemiColon:
case SymBlank:
case SymNothing:
case SymRule: // this is a rule with either height or width zero
return;
case SymUnderscore:
{ int h = (mathFontHeight[b->nest.flags & FontSizeMask]+12)/25;
if (h <= 0) h = 1; // ensure bar is at least 1 pixel tall
dc->fillRectangle(x, y, s->width, h);
return;
}
default:
printf("SymBox type %.2x not handled in paintBox\n", s->flags);
return;
}
case BoxText:
// Here (and in measureBox1) I have a WORRY. The material that ends up in a
// text box can be pretty-well arbitrary strings of ascii characters, but the
// font I am using may very well not cope with things beyond letters and
// numbers. And some glyphs that I might like (such as space and underline)
// are not even present in the Computer Modern fonts that I have available.
// so if I want to be REALLY picky I will need to scan character by character
// and take special action on characters that need it. While I am doing that
// I might take the view that in the Roman font I should convert things to
// use ligatures (eg ff, fi, fl, ffi, ffl) when relevant, and do kerning
// for myself...
//
// It may well be that via tmprint the only things that get into a BoxText
// directly involve simple alphanumerics. So otherwise I get BoxTexts that I
// myself have created in the clear knowledge of font-related complexity. So
// perhaps this is less of a worry than I had at one time feared! But I should
// try to ensure that I do not put blanks etc in these boxes.
//
// If the "FntBig" flag is set I will shift the material printed by
// my nominal character-cell height... This shift seems a little delicate to
// me in that I ought to position the "big" symbol (eg \sum, \int, \bigvee)
// with regard to the base-line of normal symbols.
t = &(b->text);
if (t->flags & FntBig)
{ int h = mathFontHeight[t->flags & FontSizeMask];
int d = mathFontDepth[t->flags & FontSizeMask];
// The calculations here had better match those performed when I measured
// the symbol.
int bigH = 0, hd = h+d;
if (t->text[0] == 0x5a) // \int
bigH = (2222*hd + 450)/900;
else bigH = (1400*hd + 450)/900;
hd = h - d;
y -= (bigH + hd)/2;;
}
ff = mathFont[t->flags & FontMask];
setFont1(dc, ff);
drawText1(dc, x, y, t->text, t->n);
if (DEBUGFONT & 1)
{ dc->drawRectangle(x, y, t->width, t->depth);
dc->drawRectangle(x, y - t->height, t->width, t->height);
}
return;
case BoxBracket:
bb = (BracketBox *)b;
h1 = bb->sub->text.height;
d1 = bb->sub->text.depth;
if (bb->leftBracket == 'Q')
{ int f = bb->flags & FontSizeMask;
int h = mathFontHeight[f];
int d = mathFontDepth[f];
h1 += (h + d + 12)/25; // make contents seem taller
// to leave gap before overbar
}
if (DEBUGFONT & 1)
{ dc->drawRectangle(x, y, bb->width, bb->depth);
dc->drawRectangle(x, y - bb->height, bb->width, bb->height);
}
paintBox(dc, bb->sub, x + bb->dx, y);
paintBracket(dc, bb->leftBracket, bb->flags,
x, y, h1, d1);
paintBracket(dc, bb->rightBracket, bb->flags,
x + bb->dx + bb->sub->text.width, y, h1, d1);
if (bb->leftBracket == 'Q') // need rule above the contents
{ int h = (mathFontHeight[bb->flags & FontSizeMask] +
mathFontDepth[bb->flags & FontSizeMask] + 12) /25;
// I want the rule here to be the same width as the stem that the "cmex" font
// gives the top-part of a huge sqrt sign. Measuring that seems to reveal that
// the rule thickness used is 40 units when the height of the whole font is
// 1000 - ie it is 1/25th of the whole height. So that is what I will use
// here.
if (h <= 0) h = 1; // ensure bar is at least 1 pixel tall
dc->fillRectangle(x+bb->dx, y-bb->height-h, bb->sub->text.width, h);
}
if (bb->sub1 != NULL)
paintBox(dc, bb->sub1, x+bb->dx1, y+bb->dy1);
return;
case BoxMatrix:
if (DEBUGFONT & 1)
{ dc->drawRectangle(x,
y-b->matrix.height,
b->matrix.width,
b->matrix.depth + b->matrix.height);
}
paintBox(dc, b->matrix.sub, x, y + b->matrix.dy);
return;
case BoxNest:
if (DEBUGFONT & 1)
{ dc->drawRectangle(x,
y-b->nest.height,
b->nest.width,
b->nest.depth + b->nest.height);
}
paintBox(dc, b->nest.sub1, x + b->nest.dx1, y + b->nest.dy1);
if (b->nest.sub2 != NULL) // in case of PadSpace
paintBox(dc, b->nest.sub2, x + b->nest.dx2, y + b->nest.dy2);
// I may need to tune the thickness of the rule that I use here
if ((b->nest.flags & NestMask) == BoxFraction)
{ int h = (mathFontHeight[b->nest.flags & FontSizeMask] +
mathFontDepth[b->nest.flags & FontSizeMask] + 12)/25;
int dy = (mathFontHeight[b->nest.flags & FontSizeMask] -
mathFontDepth[b->nest.flags & FontSizeMask])/2;
if (h <= 0) h = 1; // ensure bar is at least 1 pixel tall
dc->fillRectangle(x, y-h/2-dy, b->nest.width, h);
}
return;
case BoxNest3:
paintBox(dc, b->nest3.sub1, x + b->nest3.dx1, y + b->nest3.dy1);
paintBox(dc, b->nest3.sub2, x + b->nest3.dx2, y + b->nest3.dy2);
paintBox(dc, b->nest3.sub3, x + b->nest3.dx3, y + b->nest3.dy3);
return;
case BoxFrame:
paintBox(dc, b->frame.sub, x, y);
dc->drawRectangle(x,
y-b->frame.height,
b->frame.width,
b->frame.depth + b->frame.height);
return;
case BoxTop:
paintBox(dc, b->top.sub, x, y);
return;
default:
printf("Paint unknown box type %d: failing\n", b->text.type);
fflush(stdout);
return;
}
}
///////////////////////////////////////////////////////////////////////////
int setupShowMath(FXApp *app, int mainSize)
{
setupMemoryPool();
rehashKeywordTable(); // names in the table of TeX keywords
#ifdef HAVE_XFT
// Calling setupXft not only initialises Xft but it also tries to load the
// custom fonts that I need. If either of these steps fails I set
// fwin_use_xft to zero so that I do not try to use it at all further.
// The result is a fall-back to simple X11 protocols.
setupXft(app);
#endif
// loadPrivateFonts does nothing if Xft was available, but otherwise tries
// to add cmr10 etc fonts for me. If it fails it does not complain - after
// all those fonts may be available already!
loadPrivateFonts(app);
return changeMathFontSize(app, mainSize);
}
void closeShowMath(FXApp *app)
{
unloadFonts(app);
}
///////////////////////////////////////////////////////////////////////////
#endif // HAVE_LIBFOX
// End of FXShowMath.cpp