aboutsummaryrefslogtreecommitdiffstats
path: root/z38.c
diff options
context:
space:
mode:
authorJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
committerJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
commit71bdb35d52747e6d7d9f55df4524d57c2966be94 (patch)
tree480ee5eefccc40d5f3331cc52d66f722fd19bfb9 /z38.c
parentb41263ea7578fa9742486135c762803b52794105 (diff)
downloadlout-71bdb35d52747e6d7d9f55df4524d57c2966be94.tar.gz
Lout 3.17.
git-svn-id: http://svn.savannah.nongnu.org/svn/lout/trunk@2 9365b830-b601-4143-9ba8-b4a8e2c3339c
Diffstat (limited to 'z38.c')
-rw-r--r--z38.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/z38.c b/z38.c
new file mode 100644
index 0000000..4a7290f
--- /dev/null
+++ b/z38.c
@@ -0,0 +1,645 @@
+/*@z38.c:Character Mappings:Declarations@*************************************/
+/* */
+/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */
+/* COPYRIGHT (C) 1991, 1999 Jeffrey H. Kingston */
+/* */
+/* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */
+/* Basser Department of Computer Science */
+/* The University of Sydney 2006 */
+/* AUSTRALIA */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either Version 2, or (at your option) */
+/* any later version. */
+/* */
+/* This program 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
+/* */
+/* FILE: z38.c */
+/* MODULE: Character Mappings */
+/* EXTERNS: MapLoad(), MapCharEncoding(), MapEncodingName(), */
+/* MapPrintEncodings(), MapPrintResources(), MapSmallCaps() */
+/* */
+/*****************************************************************************/
+#include "externs.h"
+#define MAX_MAP 20 /* max number of lcm files */
+
+/*****************************************************************************/
+/* */
+/* Should really be private but have been placed in externs because for */
+/* efficiency they are used by z37.c and z34.c */
+/* */
+/* #define MAX_CHASH 353 */
+/* #define MAP_UPPERCASE 0 */
+/* #define MAP_LOWERCASE 1 */
+/* #define MAP_UNACCENTED 2 */
+/* #define MAP_ACCENT 3 */
+/* #define MAPS 4 */
+/* */
+/* typedef struct mapvec { */
+/* OBJECT file_name; */
+/* FILE_NUM fnum; */
+/* BOOLEAN must_print; */
+/* OBJECT name; */
+/* OBJECT vector[MAX_CHARS]; */
+/* FULL_CHAR hash_table[MAX_CHASH]; */
+/* FULL_CHAR map[MAPS][MAX_CHARS]; */
+/* } *MAP_VEC; */
+/* */
+/*****************************************************************************/
+
+MAP_VEC MapTable[MAX_MAP]; /* the mappings */
+
+static OBJECT notdef_word = nilobj; /* notdef word */
+static int maptop = 1; /* first free slot in MapTable[] */
+ /* save 0 for "no mapping" */
+
+/*****************************************************************************/
+/* */
+/* static int NameInsert(cname) */
+/* static FULL_CHAR NameRetrieve(cname) */
+/* */
+/*****************************************************************************/
+
+#define hash(str, pos) \
+{ FULL_CHAR *p = str; \
+ for( pos = 2 * *p++; *p; pos += *p++); \
+ pos = pos % MAX_CHASH; \
+}
+
+static void NameInsert(FULL_CHAR *cname, int ccode, MAP_VEC map)
+{ int pos;
+ hash(cname, pos);
+ while( map->hash_table[pos] != (FULL_CHAR) '\0' )
+ pos = (pos + 1) % MAX_CHASH;
+ map->vector[ccode] = MakeWord(WORD, cname, no_fpos);
+ map->hash_table[pos] = ccode;
+} /* end NameInsert */
+
+static FULL_CHAR NameRetrieve(FULL_CHAR *cname, MAP_VEC map)
+{ int pos; FULL_CHAR ch;
+ hash(cname, pos);
+ while( (ch = map->hash_table[pos]) != (FULL_CHAR) '\0' )
+ {
+ if( StringEqual(string(map->vector[ch]), cname) )
+ return ch;
+ pos = (pos + 1) % MAX_CHASH;
+ }
+ return ch;
+} /* end NameRetrieve */
+
+
+/*@::MapLoad()@***************************************************************/
+/* */
+/* MAPPING MapLoad(file_name, must_print) */
+/* */
+/* Declare file_name to be a character mapping (LCM) file. A file may be */
+/* so declared more than once. If must_print is true, the encoding vector */
+/* given by this file must be printed in the PostScript output. */
+/* */
+/*****************************************************************************/
+
+MAPPING MapLoad(OBJECT file_name, BOOLEAN must_print)
+{ FILE *fp; MAP_VEC map; MAPPING res;
+ int i, m, curr_line_num, line_pos, prev_code, dc, oc, count;
+ FULL_CHAR buff[MAX_BUFF], cn[MAX_BUFF], ch, mapname[MAX_BUFF],
+ mapval[MAX_BUFF];
+ debug2(DCM,D, "MapLoad(%s, %s)", EchoObject(file_name),bool(must_print));
+
+ /* if the file name is "-", it means no mapping file is supplied */
+ if( StringEqual(string(file_name), AsciiToFull("-")) )
+ { debug1(DCM, D, "MapLoad returning 0 (file name is %s)",
+ string(file_name));
+ return (MAPPING) 0;
+ }
+
+ /* if seen this file name before, just update must_print and return prev */
+ for( res = 1; res < maptop; res++ )
+ {
+ if( StringEqual(string(MapTable[res]->file_name), string(file_name)) )
+ { Dispose(file_name);
+ MapTable[res]->must_print = MapTable[res]->must_print || must_print;
+ debug1(DCM, D, "MapLoad returning %d (not new)", res);
+ return res;
+ }
+ }
+
+ /* initialize PostScript name of all undefined characters */
+ if( notdef_word == nilobj )
+ notdef_word = MakeWord(WORD, AsciiToFull(".notdef"), no_fpos);
+
+ /* new, so allocate a new slot in MapTable for this new mapping */
+ if( maptop == MAX_MAP )
+ Error(38, 1, "too many character mappings", FATAL, &fpos(file_name));
+ ifdebug(DMA, D, DebugRegisterUsage(MEM_CMAPS, 1, sizeof(struct mapvec)));
+ MapTable[res = maptop++] = map = (MAP_VEC) malloc( sizeof(struct mapvec) );
+ if( map == (MAP_VEC) NULL )
+ Error(38, 2, "run out of memory when loading character mapping",
+ FATAL, &fpos(file_name));
+
+ /* initialize all the fields */
+ map->file_name = file_name;
+ debug0(DFS, D, " calling DefineFile from MapLoad");
+ map->fnum = DefineFile(string(file_name), STR_EMPTY, &fpos(file_name),
+ MAPPING_FILE, MAPPING_PATH);
+ fp = OpenFile(map->fnum, FALSE, FALSE);
+ if( fp == NULL ) Error(38, 3, "cannot open character mapping file %s",
+ FATAL, PosOfFile(map->fnum), FileName(map->fnum));
+ map->must_print = must_print;
+ StringCopy(buff, AsciiToFull("vec"));
+ StringCat(buff, StringInt(maptop));
+ map->name = MakeWord(WORD, buff, no_fpos);
+ for( m = 0; m < MAPS; m++ )
+ { for( i = 0; i < MAX_CHARS; i++ )
+ map->map[m][i] = '\0';
+ }
+
+ /* unaccented map is defined to be self as default */
+ for( i = 0; i < MAX_CHARS; i++ )
+ map->map[MAP_UNACCENTED][i] = i;
+
+ for( i = 0; i < MAX_CHARS; i++ ) map->vector[i] = notdef_word;
+ for( i = 0; i < MAX_CHASH; i++ ) map->hash_table[i] = 0;
+
+ /* first pass through the file; read character codes and names only */
+ prev_code = -1; curr_line_num = 0;
+ while( fgets( (char *) buff, MAX_BUFF, fp) == (char *) buff )
+ {
+ /* skip comment lines and blank lines */
+ curr_line_num++;
+ for( i = 0; buff[i] == ' ' || buff[i] == '\t'; i++ );
+ if( buff[i] == '#' || buff[i] == '\n' || buff[i] == '\0' ) continue;
+
+ /* parse line and check validity of decimal and octal character codes */
+ count = sscanf( (char *) buff, "%d %o %s", &dc, &oc, cn);
+ if( count < 2 )
+ Error(38, 4, "character code(s) missing in mapping file (line %d)",
+ FATAL, &fpos(file_name));
+ if( dc != oc )
+ Error(38, 5, "decimal and octal codes disagree in mapping file (line %d)",
+ FATAL, &fpos(file_name));
+ if( dc < 1 && !StringEqual(cn, STR_NOCHAR) )
+ Error(38, 6, "code %d too small (min is 1) in mapping file (line %d)",
+ FATAL, &fpos(file_name), dc, curr_line_num);
+ if( dc < prev_code )
+ Error(38, 7, "code %d out of order in mapping file (line %d)",
+ FATAL, &fpos(file_name), dc, curr_line_num);
+ if( dc == prev_code )
+ Error(38, 8, "code %d repeated in mapping file (line %d)",
+ FATAL, &fpos(file_name), dc, curr_line_num);
+ if( dc > MAX_CHARS )
+ Error(38, 9, "code %d too large (max is %d) in mapping file (line %d)",
+ FATAL, &fpos(file_name), dc, MAX_CHARS, curr_line_num);
+ prev_code = dc;
+
+ /* insert character name, if any */
+ debug2(DCM, DD, " line %d: %s", curr_line_num, cn);
+ if( count >= 3 && !StringEqual(cn, STR_NOCHAR) )
+ {
+ /* insert (cn, dc) pair into hash table; name may be repeated */
+ if( (ch = NameRetrieve(cn, map)) != 0 )
+ map->vector[dc] = map->vector[ch];
+ else
+ NameInsert(cn, dc, map);
+ }
+ }
+
+ /* second pass through the file: read mappings */
+ rewind(fp);
+ curr_line_num = 0;
+ while( fgets( (char *) buff, MAX_BUFF, fp) == (char *) buff )
+ {
+ /* skip comment lines and blank lines */
+ curr_line_num++;
+ for( i = 0; buff[i] == ' ' || buff[i] == '\t'; i++ );
+ if( buff[i] == '#' || buff[i] == '\n' || buff[i] == '\0' ) continue;
+
+ /* parse line */
+ count = sscanf( (char *) buff, "%d %o %s%n",
+ &dc, &oc, cn, &line_pos);
+
+ /* find and insert the maps */
+ while( sscanf( (char *) &buff[line_pos], "%s %[^;];%n",
+ mapname, mapval, &i) == 2 )
+ {
+ debug3(DCM, DD, " line %d: %s %s", curr_line_num, mapname, mapval);
+ line_pos += i;
+ if( StringEqual(mapname, AsciiToFull("UC")) )
+ m = MAP_UPPERCASE;
+ else if( StringEqual(mapname, AsciiToFull("LC")) )
+ m = MAP_LOWERCASE;
+ else if( StringEqual(mapname, AsciiToFull("UA")) )
+ m = MAP_UNACCENTED;
+ else if( StringEqual(mapname, AsciiToFull("AC")) )
+ m = MAP_ACCENT;
+ else
+ Error(38, 10, "unknown mapping name %s in mapping file %s (line %d)",
+ FATAL, &fpos(file_name), mapname, FileName(map->fnum), curr_line_num);
+ ch = NameRetrieve(mapval, map);
+ if( ch == (FULL_CHAR) '\0' )
+ Error(38, 11, "unknown character %s in mapping file %s (line %d)",
+ FATAL, &fpos(file_name), mapval, FileName(map->fnum), curr_line_num);
+ map->map[m][dc] = ch;
+ }
+ }
+ fclose(fp);
+ debug1(DCM, D, "MapLoad returning %d (new mapping)", res);
+ return res;
+} /* end MapLoad */
+
+
+/*@::MapCharEncoding(), MapEncodingName(), MapPrintEncodings()@***************/
+/* */
+/* FULL_CHAR MapCharEncoding(str, map) */
+/* */
+/* Returns the character code corresponding to character name str in */
+/* MAPPING enc, or 0 if not found. */
+/* */
+/*****************************************************************************/
+
+FULL_CHAR MapCharEncoding(FULL_CHAR *str, MAPPING m)
+{ MAP_VEC map;
+ map = MapTable[m];
+ return (FULL_CHAR) NameRetrieve(str, map);
+} /* end MapCharEncoding */
+
+
+/*****************************************************************************/
+/* */
+/* FULL_CHAR *MapEncodingName(m) */
+/* */
+/* Returns the PostScript name of the encoding vector of mapping m */
+/* */
+/*****************************************************************************/
+
+FULL_CHAR *MapEncodingName(MAPPING m)
+{ assert( m < maptop, "MapEncodingName: m out of range!" );
+ return string(MapTable[m]->name);
+} /* end MapEncodingName */
+
+
+/*****************************************************************************/
+/* */
+/* MapPrintEncodings(fp) */
+/* */
+/* Print all encoding vectors in PostScript form on file fp. */
+/* */
+/*****************************************************************************/
+
+void MapPrintEncodings(FILE *fp)
+{ MAPPING m; MAP_VEC map; int i;
+ for( m = 1; m < maptop; m++ ) if( MapTable[m]->must_print )
+ { map = MapTable[m];
+ switch( BackEnd )
+ {
+ case POSTSCRIPT:
+
+ fprintf(fp, "%%%%BeginResource encoding %s\n", string(map->name));
+ fprintf(fp, "/%s [\n", string(map->name));
+ for( i = 0; i < MAX_CHARS; i++ )
+ fprintf(fp, "/%s%c", string(map->vector[i]), (i+1) % 8 != 0 ? ' ' : '\n');
+ fprintf(fp, "] def\n");
+ fprintf(fp, "%%%%EndResource\n\n");
+ break;
+
+
+ case PDF:
+
+ PDFFile_BeginFontEncoding(fp, (char*) string(map->name));
+ for( i = 0; i < MAX_CHARS; i++ )
+ fprintf(fp, "/%s%c", string(map->vector[i]),
+ (i+1) % 8 != 0 ? ' ' : '\n');
+ PDFFile_EndFontEncoding(fp);
+ break;
+ }
+ }
+} /* end MapPrintEncodings */
+
+
+/*****************************************************************************/
+/* */
+/* MapPrintResources(fp) */
+/* */
+/* Print resource entries for all encoding vectors on file fp. */
+/* */
+/*****************************************************************************/
+
+void MapPrintResources(FILE *fp)
+{ MAPPING m; MAP_VEC map;
+ for( m = 1; m < maptop; m++ ) if( MapTable[m]->must_print )
+ { map = MapTable[m];
+ fprintf(fp, "%%%%+ encoding %s\n", string(map->name));
+ }
+} /* end MapPrintResources */
+
+/*@@**************************************************************************/
+/* */
+/* OBJECT DoWord(buff, q, x, fnum) */
+/* */
+/* Replace WORD or QWORD x by a small caps version, based on word_font(x). */
+/* */
+/*****************************************************************************/
+
+static OBJECT DoWord(FULL_CHAR *buff, FULL_CHAR *q, OBJECT x, FONT_NUM fnum)
+{ OBJECT res;
+ *q++ = '\0';
+ res = MakeWord(type(x), buff, &fpos(x));
+ word_font(res) = fnum;
+ word_colour(res) = word_colour(x);
+ word_language(res) = word_language(x);
+ word_hyph(res) = word_hyph(x);
+ underline(res) = UNDER_OFF;
+ return res;
+} /* end DoWord */
+
+
+/*****************************************************************************/
+/* */
+/* OBJECT DoVShift(x, vshift, chld) */
+/* */
+/* Make an new VSHIFT object with the given shift and child. */
+/* */
+/*****************************************************************************/
+
+static OBJECT DoVShift(OBJECT x, FULL_LENGTH vshift, OBJECT chld)
+{ OBJECT res;
+ New(res, VSHIFT);
+ FposCopy(fpos(res), fpos(x));
+ shift_type(res) = GAP_DEC;
+ units(shift_gap(res)) = FIXED_UNIT;
+ mode(shift_gap(res)) = EDGE_MODE;
+ width(shift_gap(res)) = vshift;
+ underline(res) = UNDER_OFF;
+ Link(res, chld);
+ return res;
+}
+
+/*****************************************************************************/
+/* */
+/* void DoAddGap(new_acat) */
+/* */
+/* Add a new 0i gap object to new_acat. */
+/* */
+/*****************************************************************************/
+
+static void DoAddGap(OBJECT new_acat)
+{ OBJECT new_g;
+ New(new_g, GAP_OBJ);
+ FposCopy(fpos(new_g), fpos(new_acat));
+ hspace(new_g) = vspace(new_g) = 0;
+ SetGap(gap(new_g), TRUE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 0*IN);
+ underline(new_g) = UNDER_OFF;
+ Link(new_acat, new_g);
+}
+
+/*@::MapSmallCaps()@**********************************************************/
+/* */
+/* OBJECT MapSmallCaps(x, style) */
+/* */
+/* Replace WORD or QWORD x by a small caps version, based on word_font(x). */
+/* */
+/*****************************************************************************/
+#define INIT 0
+#define ALL_NON 1
+#define ALL_TRANS 2
+#define MIXED_NON 3
+#define MIXED_TRANS 4
+#define transformable(ch) (uc[ch] != '\0')
+
+OBJECT MapSmallCaps(OBJECT x, STYLE *style)
+{ MAPPING m; int i; OBJECT new_y, new_x, new_acat, tmp;
+ FULL_CHAR buff[MAX_BUFF], *uc, *p, *q;
+ FONT_NUM small_font; FULL_LENGTH vshift; int state; STYLE new_style;
+ static OBJECT font_change_word = nilobj;
+ assert( is_word(type(x)), "MapSmallCaps: !is_word(type(x))" );
+ debug2(DCM, D, "MapSmallCaps(%s %s)", Image(type(x)), string(x));
+
+ /* get the mapping and return if there isn't one for this font */
+ m = FontMapping(font_num(x), &fpos(x));
+ if( m == 0 )
+ { debug0(DCM, D, "MapSmallCaps returning unchanged (mapping is 0)");
+ return x;
+ }
+ assert( 1 <= m && m < maptop, "MapSmallCaps: mapping out of range!" );
+ uc = MapTable[m]->map[MAP_UPPERCASE];
+
+ /* if plain text, apply the mapping and exit */
+ if( BackEnd == PLAINTEXT )
+ {
+ for( i = 0; string(x)[i] != '\0'; i++ )
+ if( uc[string(x)[i]] != '\0' )
+ string(x)[i] = uc[string(x)[i]];
+ debug1(DCM, D, "MapSmallCaps returning (plain text) %s", EchoObject(x));
+ return x;
+ }
+
+ /* set up the font change word if not already done */
+ if( font_change_word == nilobj )
+ { font_change_word = MakeWord(WORD, AsciiToFull("0.7f"), no_fpos);
+ }
+
+ state = INIT; q = buff;
+ for( p = string(x); *p != '\0'; p++ )
+ {
+ debug2(DCM, DD, " examining %c (%s)", *p,
+ transformable(*p) ? "transformable" : "not transformable");
+ switch( state )
+ {
+ case INIT:
+
+ /* this state is for when we are at the first character */
+ if( transformable(*p) )
+ { *q++ = uc[*p];
+
+ /* work out what the smaller font is going to be, and the vshift */
+ StyleCopy(new_style, *style);
+ FontChange(&new_style, font_change_word);
+ small_font = font(new_style);
+ vshift = FontHalfXHeight(word_font(x)) - FontHalfXHeight(small_font);
+
+ state = ALL_TRANS;
+ }
+ else
+ { *q++ = *p;
+ state = ALL_NON;
+ }
+ break;
+
+
+ case ALL_NON:
+
+ /* in this state, all characters so far are non-transformable */
+ if( transformable(*p) )
+ {
+ /* work out what the smaller font is going to be */
+ StyleCopy(new_style, *style);
+ FontChange(&new_style, font_change_word);
+ small_font = font(new_style);
+ vshift = FontHalfXHeight(word_font(x)) - FontHalfXHeight(small_font);
+
+ /* make a new WORD out of the current contents of buff */
+ new_y = DoWord(buff, q, x, word_font(x));
+
+ /* construct the skeleton of the result to replace x */
+ New(new_x, ONE_COL);
+ FposCopy(fpos(new_x), fpos(x));
+ New(new_acat, ACAT);
+ FposCopy(fpos(new_acat), fpos(x));
+ Link(new_x, new_acat);
+ Link(new_acat, new_y);
+ DoAddGap(new_acat);
+
+ /* start off a new buffer with *p */
+ q = buff;
+ *q++ = uc[*p];
+ state = MIXED_TRANS;
+ }
+ else *q++ = *p;
+ break;
+
+
+ case ALL_TRANS:
+
+ /* in this state, all characters so far are transformable */
+ if( transformable(*p) ) *q++ = uc[*p];
+ else
+ {
+ /* make a new @VShift WORD out of the current contents of buff */
+ tmp = DoWord(buff, q, x, small_font);
+ new_y = DoVShift(x, vshift, tmp);
+
+ /* construct the skeleton of the result to replace x */
+ New(new_x, ONE_COL);
+ FposCopy(fpos(new_x), fpos(x));
+ New(new_acat, ACAT);
+ FposCopy(fpos(new_acat), fpos(x));
+ Link(new_x, new_acat);
+ Link(new_acat, new_y);
+ DoAddGap(new_acat);
+
+ /* start off a new buffer with *p */
+ q = buff;
+ *q++ = *p;
+ state = MIXED_NON;
+ }
+ break;
+
+
+ case MIXED_NON:
+
+ /* in this state the previous char was non-transformable, but */
+ /* there have been characters before that that were transformable */
+ if( transformable(*p) )
+ {
+ /* make a new WORD out of the current contents of buff */
+ new_y = DoWord(buff, q, x, word_font(x));
+
+ /* link the new word into the growing structure that replaces x */
+ Link(new_acat, new_y);
+ DoAddGap(new_acat);
+
+ /* start off a new buffer with *p */
+ q = buff;
+ *q++ = uc[*p];
+ state = MIXED_TRANS;
+ }
+ else *q++ = *p;
+ break;
+
+
+ case MIXED_TRANS:
+
+ /* in this state the previous char was transformable, but there */
+ /* have been characters before that that were non-transformable */
+ if( transformable(*p) ) *q++ = uc[*p];
+ else
+ {
+ /* make a new @VShift WORD out of the current contents of buff */
+ tmp = DoWord(buff, q, x, small_font);
+ new_y = DoVShift(x, vshift, tmp);
+
+ /* link the new word into the growing structure that replaces x */
+ Link(new_acat, new_y);
+ DoAddGap(new_acat);
+
+ /* start off a new buffer with *p */
+ q = buff;
+ *q++ = *p;
+ state = MIXED_NON;
+ }
+ break;
+
+ }
+ }
+
+ /* now at termination, clean up the structure */
+ switch( state )
+ {
+ case INIT:
+ case ALL_NON:
+
+ /* original x is OK as is: either empty or all non-transformable */
+ break;
+
+
+ case ALL_TRANS:
+
+ /* make a new @VShift WORD and replace x with it */
+ tmp = DoWord(buff, q, x, small_font);
+ new_x = DoVShift(x, vshift, tmp);
+ ReplaceNode(new_x, x);
+ Dispose(x);
+ x = new_x;
+ break;
+
+
+ case MIXED_NON:
+
+ /* make a new WORD, add to new_acat, and replace x */
+ new_y = DoWord(buff, q, x, word_font(x));
+ Link(new_acat, new_y);
+ ReplaceNode(new_x, x);
+ Dispose(x);
+ x = new_x;
+ break;
+
+
+ case MIXED_TRANS:
+
+ /* make a new @VShift WORD, add to new_acat, and replace x */
+ tmp = DoWord(buff, q, x, small_font);
+ new_y = DoVShift(x, vshift, tmp);
+ Link(new_acat, new_y);
+ ReplaceNode(new_x, x);
+ Dispose(x);
+ x = new_x;
+ break;
+ }
+ debug1(DCM, D, "MapSmallCaps returning %s", EchoObject(x));
+ return x;
+} /* end MapSmallCaps */
+
+
+/*****************************************************************************/
+/* */
+/* BOOLEAN MapIsLowerCase(FULL_CHAR ch, MAPPING m) */
+/* */
+/* Returns TRUE if ch is a lower-case character in mapping m; i.e. if it */
+/* has a corresponding upper-case character. */
+/* */
+/*****************************************************************************/
+
+BOOLEAN MapIsLowerCase(FULL_CHAR ch, MAPPING m)
+{ BOOLEAN res;
+ debug2(DCM, D, "MapIsLowerCase(%c, %d)", ch, m);
+ res = (MapTable[m]->map[MAP_UPPERCASE][ch] != '\0');
+ debug1(DCM, D, "MapIsLowerCase returning %s", bool(res));
+ return res;
+} /* end MapIsLowerCase */