Artifact 6e73a3aa9a8b965c1e5ccadb155cc4d10d33368ecdf77408e2d02f795e84129f:
- Executable file
r38/lisp/csl/cslbase/FXShowMath.cpp
— part of check-in
[f2fda60abd]
at
2011-09-02 18:13:33
on branch master
— Some historical releases purely for archival purposes
git-svn-id: https://svn.code.sf.net/p/reduce-algebra/code/trunk/historical@1375 2bfe0521-f11c-4a00-b80e-6202646ff360 (user: arthurcnorman@users.sourceforge.net, size: 170157) [annotate] [blame] [check-ins using] [more...]
// // "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