1 /*@z38.c:Character Mappings:Declarations@*************************************/
2 /* */
3 /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39) */
4 /* COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston */
5 /* */
6 /* Jeffrey H. Kingston (jeff@it.usyd.edu.au) */
7 /* School of Information Technologies */
8 /* The University of Sydney 2006 */
9 /* AUSTRALIA */
10 /* */
11 /* This program is free software; you can redistribute it and/or modify */
12 /* it under the terms of the GNU General Public License as published by */
13 /* the Free Software Foundation; either Version 3, or (at your option) */
14 /* any later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
19 /* GNU General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU General Public License */
22 /* along with this program; if not, write to the Free Software */
23 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
24 /* */
25 /* FILE: z38.c */
26 /* MODULE: Character Mappings */
27 /* EXTERNS: MapLoad(), MapCharEncoding(), MapEncodingName(), */
28 /* MapPrintEncodings(), MapPrintResources(), MapSmallCaps() */
29 /* */
30 /*****************************************************************************/
31 #include "externs.h"
32 #define MAX_MAP 20 /* max number of lcm files */
33
34 /*****************************************************************************/
35 /* */
36 /* Should really be private but have been placed in externs because for */
37 /* efficiency they are used by z37.c and z34.c */
38 /* */
39 /* #define MAX_CHASH 353 */
40 /* #define MAP_UPPERCASE 0 */
41 /* #define MAP_LOWERCASE 1 */
42 /* #define MAP_UNACCENTED 2 */
43 /* #define MAP_ACCENT 3 */
44 /* #define MAPS 4 */
45 /* */
46 /* typedef struct mapvec { */
47 /* OBJECT file_name; */
48 /* FILE_NUM fnum; */
49 /* BOOLEAN seen_recoded; */
50 /* int last_page_printed; */
51 /* OBJECT name; */
52 /* OBJECT vector[MAX_CHARS]; */
53 /* FULL_CHAR hash_table[MAX_CHASH]; */
54 /* FULL_CHAR map[MAPS][MAX_CHARS]; */
55 /* } *MAP_VEC; */
56 /* */
57 /*****************************************************************************/
58
59 MAP_VEC MapTable[MAX_MAP]; /* the mappings */
60
61 static OBJECT notdef_word; /* notdef word */
62 static int maptop; /* first free slot in MapTable[] */
63 /* save 0 for "no mapping" */
64
65
66 /*****************************************************************************/
67 /* */
68 /* void MapInit(void) */
69 /* */
70 /* Initialize this module. */
71 /* */
72 /*****************************************************************************/
73
MapInit(void)74 void MapInit(void)
75 {
76 notdef_word = nilobj;
77 maptop = 1;
78 }
79
80
81 /*****************************************************************************/
82 /* */
83 /* static int NameInsert(cname) */
84 /* static FULL_CHAR NameRetrieve(cname) */
85 /* */
86 /*****************************************************************************/
87
88 #define hash(str, pos) \
89 { FULL_CHAR *p = str; \
90 for( pos = 2 * *p++; *p; pos += *p++); \
91 pos = pos % MAX_CHASH; \
92 }
93
NameInsert(FULL_CHAR * cname,int ccode,MAP_VEC map)94 static void NameInsert(FULL_CHAR *cname, int ccode, MAP_VEC map)
95 { int pos;
96 hash(cname, pos);
97 while( map->hash_table[pos] != (FULL_CHAR) '\0' )
98 pos = (pos + 1) % MAX_CHASH;
99 map->vector[ccode] = MakeWord(WORD, cname, no_fpos);
100 map->hash_table[pos] = ccode;
101 } /* end NameInsert */
102
NameRetrieve(FULL_CHAR * cname,MAP_VEC map)103 static FULL_CHAR NameRetrieve(FULL_CHAR *cname, MAP_VEC map)
104 { int pos; FULL_CHAR ch;
105 hash(cname, pos);
106 while( (ch = map->hash_table[pos]) != (FULL_CHAR) '\0' )
107 {
108 if( StringEqual(string(map->vector[ch]), cname) )
109 return ch;
110 pos = (pos + 1) % MAX_CHASH;
111 }
112 return ch;
113 } /* end NameRetrieve */
114
115
116 /*@::MapLoad()@***************************************************************/
117 /* */
118 /* MAPPING MapLoad(file_name, recoded) */
119 /* */
120 /* Declare file_name to be a character mapping (LCM) file. A file may be */
121 /* so declared more than once. Parameter recoded is true if the font that */
122 /* uses this mapping declares that it needs to be recoded, which in turn */
123 /* means that this mapping might have to be printed out. Whether or not it */
124 /* is actually printed depends upon whether we print a font that uses it */
125 /* and that requires recoding. */
126 /* */
127 /*****************************************************************************/
128
MapLoad(OBJECT file_name,BOOLEAN recoded)129 MAPPING MapLoad(OBJECT file_name, BOOLEAN recoded)
130 { FILE *fp; MAP_VEC map; MAPPING res;
131 int i, m, curr_line_num, line_pos, prev_code, dc, count;
132 unsigned int oc;
133 int status;
134 FULL_CHAR buff[MAX_BUFF], cn[MAX_BUFF], ch, mapname[MAX_BUFF],
135 mapval[MAX_BUFF];
136 debug2(DCM,D, "MapLoad(%s, %s)", EchoObject(file_name), bool(recoded));
137
138 /* if the file name is "-", it means no mapping file is supplied */
139 if( StringEqual(string(file_name), AsciiToFull("-")) )
140 { debug1(DCM, D, "MapLoad returning 0 (file name is %s)",
141 string(file_name));
142 return (MAPPING) 0;
143 }
144
145 /* if seen this file name before, just update seen_recoded and return prev */
146 for( res = 1; res < maptop; res++ )
147 {
148 if( StringEqual(string(MapTable[res]->file_name), string(file_name)) )
149 { Dispose(file_name);
150 MapTable[res]->seen_recoded = MapTable[res]->seen_recoded || recoded;
151 debug1(DCM, D, "MapLoad returning %d (not new)", res);
152 return res;
153 }
154 }
155
156 /* initialize PostScript name of all undefined characters */
157 if( notdef_word == nilobj )
158 notdef_word = MakeWord(WORD, AsciiToFull(".notdef"), no_fpos);
159
160 /* new, so allocate a new slot in MapTable for this new mapping */
161 if( maptop == MAX_MAP )
162 Error(38, 1, "too many character mappings", FATAL, &fpos(file_name));
163 ifdebug(DMA, D, DebugRegisterUsage(MEM_CMAPS, 1, sizeof(struct mapvec)));
164 MapTable[res = maptop++] = map = (MAP_VEC) malloc( sizeof(struct mapvec) );
165 if( map == (MAP_VEC) NULL )
166 Error(38, 2, "run out of memory when loading character mapping",
167 FATAL, &fpos(file_name));
168
169 /* initialize all the fields */
170 map->file_name = file_name;
171 debug0(DFS, D, " calling DefineFile from MapLoad");
172 map->fnum = DefineFile(string(file_name), STR_EMPTY, &fpos(file_name),
173 MAPPING_FILE, MAPPING_PATH);
174 fp = OpenFile(map->fnum, FALSE, FALSE);
175 if( fp == NULL ) Error(38, 3, "cannot open character mapping file %s",
176 FATAL, PosOfFile(map->fnum), FileName(map->fnum));
177 map->seen_recoded = recoded;
178 map->last_page_printed = 0;
179 StringCopy(buff, AsciiToFull("vec"));
180 StringCat(buff, StringInt(maptop));
181 map->name = MakeWord(WORD, buff, no_fpos);
182 for( m = 0; m < MAPS; m++ )
183 { for( i = 0; i < MAX_CHARS; i++ )
184 map->map[m][i] = '\0';
185 }
186
187 /* unaccented map is defined to be self as default */
188 for( i = 0; i < MAX_CHARS; i++ )
189 map->map[MAP_UNACCENTED][i] = i;
190
191 for( i = 0; i < MAX_CHARS; i++ ) map->vector[i] = notdef_word;
192 for( i = 0; i < MAX_CHASH; i++ ) map->hash_table[i] = 0;
193
194 /* first pass through the file; read character codes and names only */
195 prev_code = -1; curr_line_num = 0;
196 while( (status = ReadOneLine(fp, buff, MAX_BUFF)) != 0 )
197 {
198 /* skip comment lines and blank lines */
199 curr_line_num++;
200 for( i = 0; buff[i] == ' ' || buff[i] == '\t'; i++ );
201 if( buff[i] == '#' || buff[i] == '\0' ) continue;
202
203 /* parse line and check validity of decimal and octal character codes */
204 count = sscanf( (char *) buff, "%d %o %s", &dc, &oc, cn);
205 if( count < 2 )
206 Error(38, 4, "character code(s) missing in mapping file (line %d)",
207 FATAL, &fpos(file_name), curr_line_num);
208 if( dc != oc )
209 Error(38, 5, "decimal and octal codes disagree in mapping file (line %d)",
210 FATAL, &fpos(file_name), curr_line_num);
211 if( dc < 1 && !StringEqual(cn, STR_NOCHAR) )
212 Error(38, 6, "code %d too small (min is 1) in mapping file (line %d)",
213 FATAL, &fpos(file_name), dc, curr_line_num);
214 if( dc < prev_code )
215 Error(38, 7, "code %d out of order in mapping file (line %d)",
216 FATAL, &fpos(file_name), dc, curr_line_num);
217 if( dc == prev_code )
218 Error(38, 8, "code %d repeated in mapping file (line %d)",
219 FATAL, &fpos(file_name), dc, curr_line_num);
220 if( dc > MAX_CHARS )
221 Error(38, 9, "code %d too large (max is %d) in mapping file (line %d)",
222 FATAL, &fpos(file_name), dc, MAX_CHARS, curr_line_num);
223 prev_code = dc;
224
225 /* insert character name, if any */
226 debug2(DCM, DD, " line %d: %s", curr_line_num, cn);
227 if( count >= 3 && !StringEqual(cn, STR_NOCHAR) )
228 {
229 /* insert (cn, dc) pair into hash table; name may be repeated */
230 if( (ch = NameRetrieve(cn, map)) != 0 )
231 map->vector[dc] = map->vector[ch];
232 else
233 NameInsert(cn, dc, map);
234 }
235 }
236
237 /* second pass through the file: read mappings */
238 rewind(fp);
239 curr_line_num = 0;
240 while( (status = ReadOneLine(fp, buff, MAX_BUFF)) != 0 )
241 {
242 /* skip comment lines and blank lines */
243 curr_line_num++;
244 for( i = 0; buff[i] == ' ' || buff[i] == '\t'; i++ );
245 if( buff[i] == '#' || buff[i] == '\0' ) continue;
246
247 /* parse line */
248 count = sscanf( (char *) buff, "%d %o %s%n", &dc, &oc, cn, &line_pos);
249
250 /* find and insert the maps */
251 while( sscanf( (char *) &buff[line_pos], "%s %[^;];%n",
252 mapname, mapval, &i) == 2 )
253 {
254 debug3(DCM, DD, " line %d: %s %s", curr_line_num, mapname, mapval);
255 line_pos += i;
256 if( StringEqual(mapname, AsciiToFull("UC")) )
257 m = MAP_UPPERCASE;
258 else if( StringEqual(mapname, AsciiToFull("LC")) )
259 m = MAP_LOWERCASE;
260 else if( StringEqual(mapname, AsciiToFull("UA")) )
261 m = MAP_UNACCENTED;
262 else if( StringEqual(mapname, AsciiToFull("AC")) )
263 m = MAP_ACCENT;
264 else
265 Error(38, 10, "unknown mapping name %s in mapping file %s (line %d)",
266 FATAL, &fpos(file_name), mapname, FileName(map->fnum), curr_line_num);
267 ch = NameRetrieve(mapval, map);
268 if( ch == (FULL_CHAR) '\0' )
269 Error(38, 11, "unknown character %s in mapping file %s (line %d)",
270 FATAL, &fpos(file_name), mapval, FileName(map->fnum), curr_line_num);
271 map->map[m][dc] = ch;
272 }
273 }
274 fclose(fp);
275 debug1(DCM, D, "MapLoad returning %d (new mapping)", res);
276 return res;
277 } /* end MapLoad */
278
279
280 /*@::MapCharEncoding(), MapEncodingName(), MapPrintEncodings()@***************/
281 /* */
282 /* FULL_CHAR MapCharEncoding(str, map) */
283 /* */
284 /* Returns the character code corresponding to character name str in */
285 /* MAPPING enc, or 0 if not found. */
286 /* */
287 /*****************************************************************************/
288
MapCharEncoding(FULL_CHAR * str,MAPPING m)289 FULL_CHAR MapCharEncoding(FULL_CHAR *str, MAPPING m)
290 { MAP_VEC map;
291 map = MapTable[m];
292 return (FULL_CHAR) NameRetrieve(str, map);
293 } /* end MapCharEncoding */
294
295
296 /*****************************************************************************/
297 /* */
298 /* FULL_CHAR *MapEncodingName(m) */
299 /* */
300 /* Returns the PostScript name of the encoding vector of mapping m */
301 /* */
302 /*****************************************************************************/
303
MapEncodingName(MAPPING m)304 FULL_CHAR *MapEncodingName(MAPPING m)
305 { assert( m < maptop, "MapEncodingName: m out of range!" );
306 return string(MapTable[m]->name);
307 } /* end MapEncodingName */
308
309
310 /*****************************************************************************/
311 /* */
312 /* void MapEnsurePrinted(MAPPING m, int curr_page) */
313 /* */
314 /* Ensure that MAPPING m is printed on page curr_page, if required. */
315 /* It's required if it has neither been printed on the current page */
316 /* already, nor on page 1 (page 1 is really the entire document setup). */
317 /* */
318 /*****************************************************************************/
319
MapEnsurePrinted(MAPPING m,int curr_page)320 void MapEnsurePrinted(MAPPING m, int curr_page)
321 { MAP_VEC map = MapTable[m];
322 assert( map->seen_recoded, "MapEnsurePrinted: not seen_recoded!" );
323 if( map->last_page_printed < curr_page && map->last_page_printed != 1 )
324 { map->last_page_printed = curr_page;
325 BackEnd->PrintMapping(m);
326 }
327 }
328
329
330 /*****************************************************************************/
331 /* */
332 /* MapPrintEncodings() */
333 /* */
334 /* Print all encoding vectors in existence so far; this counts as printing */
335 /* them on "page 1", but in fact they will appear in the document setup */
336 /* section. */
337 /* */
338 /*****************************************************************************/
339
MapPrintEncodings()340 void MapPrintEncodings()
341 { MAPPING m; MAP_VEC map;
342 for( m = 1; m < maptop; m++ )
343 { if( MapTable[m]->seen_recoded )
344 { BackEnd->PrintMapping(m);
345 map = MapTable[m];
346 map->last_page_printed = 1;
347 }
348 }
349 } /* end MapPrintEncodings */
350
351
352 /*****************************************************************************/
353 /* */
354 /* MapPrintPSResources(fp) */
355 /* */
356 /* Print PostScript resource entries for all encoding vectors on file fp. */
357 /* */
358 /*****************************************************************************/
359
MapPrintPSResources(FILE * fp)360 void MapPrintPSResources(FILE *fp)
361 { MAPPING m; MAP_VEC map;
362 for( m = 1; m < maptop; m++ ) if( MapTable[m]->seen_recoded )
363 { map = MapTable[m];
364 fprintf(fp, "%%%%+ encoding %s%s", string(map->name), (char *) STR_NEWLINE);
365 }
366 } /* end MapPrintPSResources */
367
368
369 /*@@**************************************************************************/
370 /* */
371 /* OBJECT DoWord(buff, q, x, fnum) */
372 /* */
373 /* Replace WORD or QWORD x by a small caps version, based on word_font(x). */
374 /* */
375 /*****************************************************************************/
376
DoWord(FULL_CHAR * buff,FULL_CHAR * q,OBJECT x,FONT_NUM fnum)377 static OBJECT DoWord(FULL_CHAR *buff, FULL_CHAR *q, OBJECT x, FONT_NUM fnum)
378 { OBJECT res;
379 *q++ = '\0';
380 res = MakeWord(type(x), buff, &fpos(x));
381 word_font(res) = fnum;
382 word_colour(res) = word_colour(x);
383 word_underline_colour(res) = word_underline_colour(x);
384 word_texture(res) = word_texture(x);
385 word_outline(res) = word_outline(x);
386 word_language(res) = word_language(x);
387 word_baselinemark(res) = word_baselinemark(x);
388 word_strut(res) = word_strut(x);
389 word_ligatures(res) = word_ligatures(x);
390 word_hyph(res) = word_hyph(x);
391 underline(res) = UNDER_OFF;
392 return res;
393 } /* end DoWord */
394
395
396 /*****************************************************************************/
397 /* */
398 /* OBJECT DoVShift(x, vshift, chld) */
399 /* */
400 /* Make an new VSHIFT object with the given shift and child. */
401 /* */
402 /*****************************************************************************/
403
DoVShift(OBJECT x,FULL_LENGTH vshift,OBJECT chld)404 static OBJECT DoVShift(OBJECT x, FULL_LENGTH vshift, OBJECT chld)
405 { OBJECT res;
406 New(res, VSHIFT);
407 FposCopy(fpos(res), fpos(x));
408 shift_type(res) = GAP_DEC;
409 units(shift_gap(res)) = FIXED_UNIT;
410 mode(shift_gap(res)) = EDGE_MODE;
411 width(shift_gap(res)) = vshift;
412 underline(res) = UNDER_OFF;
413 Link(res, chld);
414 return res;
415 }
416
417 /*****************************************************************************/
418 /* */
419 /* void DoAddGap(new_acat) */
420 /* */
421 /* Add a new 0i gap object to new_acat. */
422 /* */
423 /*****************************************************************************/
424
DoAddGap(OBJECT new_acat)425 static void DoAddGap(OBJECT new_acat)
426 { OBJECT new_g;
427 New(new_g, GAP_OBJ);
428 FposCopy(fpos(new_g), fpos(new_acat));
429 hspace(new_g) = vspace(new_g) = 0;
430 SetGap(gap(new_g), TRUE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 0*IN);
431 underline(new_g) = UNDER_OFF;
432 Link(new_acat, new_g);
433 }
434
435 /*@::MapSmallCaps()@**********************************************************/
436 /* */
437 /* OBJECT MapSmallCaps(x, style) */
438 /* */
439 /* Replace WORD or QWORD x by a small caps version, based on word_font(x). */
440 /* */
441 /*****************************************************************************/
442 #define INIT 0
443 #define ALL_NON 1
444 #define ALL_TRANS 2
445 #define MIXED_NON 3
446 #define MIXED_TRANS 4
447 #define transformable(ch) (uc[ch] != '\0')
448
449 /* basically temporaries but remembered from call to call for recycling */
450 static OBJECT font_change_word = nilobj;
451 static FULL_LENGTH font_change_length = 0;
452
MapSmallCaps(OBJECT x,STYLE * style)453 OBJECT MapSmallCaps(OBJECT x, STYLE *style)
454 { MAPPING m; int i; OBJECT new_y, new_x = nilobj, new_acat = nilobj, tmp;
455 FULL_CHAR buff[MAX_BUFF], *uc, *p, *q;
456 FONT_NUM small_font = 0; FULL_LENGTH vshift = 0; int state; STYLE new_style;
457 assert( is_word(type(x)), "MapSmallCaps: !is_word(type(x))" );
458 debug2(DCM, D, "MapSmallCaps(%s %s)", Image(type(x)), string(x));
459
460 /* get the mapping and return if there isn't one for this font */
461 m = FontMapping(word_font(x), &fpos(x));
462 if( m == 0 )
463 { debug0(DCM, D, "MapSmallCaps returning unchanged (mapping is 0)");
464 return x;
465 }
466 assert( 1 <= m && m < maptop, "MapSmallCaps: mapping out of range!" );
467 uc = MapTable[m]->map[MAP_UPPERCASE];
468
469 /* if plain text, apply the mapping and exit */
470 if( !(BackEnd->scale_avail) )
471 {
472 for( i = 0; string(x)[i] != '\0'; i++ )
473 if( uc[string(x)[i]] != '\0' )
474 string(x)[i] = uc[string(x)[i]];
475 debug1(DCM, D, "MapSmallCaps returning (plain text) %s", EchoObject(x));
476 return x;
477 }
478
479 /* make sure the small caps size is a reasonable one */
480 if( smallcaps_len(*style) <= 0 )
481 Error(38, 12, "small caps size is zero or negative", FATAL, &fpos(x));
482
483 /* set up the font change word if not already done */
484 if( font_change_length != smallcaps_len(*style) )
485 { char tmp[100];
486 font_change_length = smallcaps_len(*style);
487 sprintf(tmp, "%.2ff", (float) font_change_length / FR);
488 font_change_word = MakeWord(WORD, AsciiToFull(tmp), no_fpos);
489 }
490
491 state = INIT; q = buff;
492 for( p = string(x); *p != '\0'; p++ )
493 {
494 debug2(DCM, DD, " examining %c (%s)", *p,
495 transformable(*p) ? "transformable" : "not transformable");
496 switch( state )
497 {
498 case INIT:
499
500 /* this state is for when we are at the first character */
501 if( transformable(*p) )
502 { *q++ = uc[*p];
503
504 /* work out what the smaller font is going to be, and the vshift */
505 StyleCopy(new_style, *style);
506 FontChange(&new_style, font_change_word);
507 small_font = font(new_style);
508 vshift = word_baselinemark(x) ? 0 :
509 (FontHalfXHeight(word_font(x)) - FontHalfXHeight(small_font));
510
511 state = ALL_TRANS;
512 }
513 else
514 { *q++ = *p;
515 state = ALL_NON;
516 }
517 break;
518
519
520 case ALL_NON:
521
522 /* in this state, all characters so far are non-transformable */
523 if( transformable(*p) )
524 {
525 /* work out what the smaller font is going to be */
526 StyleCopy(new_style, *style);
527 FontChange(&new_style, font_change_word);
528 small_font = font(new_style);
529 vshift = word_baselinemark(x) ? 0 :
530 (FontHalfXHeight(word_font(x)) - FontHalfXHeight(small_font));
531
532 /* make a new WORD out of the current contents of buff */
533 new_y = DoWord(buff, q, x, word_font(x));
534
535 /* construct the skeleton of the result to replace x */
536 New(new_x, ONE_COL);
537 FposCopy(fpos(new_x), fpos(x));
538 New(new_acat, ACAT);
539 FposCopy(fpos(new_acat), fpos(x));
540 Link(new_x, new_acat);
541 Link(new_acat, new_y);
542 DoAddGap(new_acat);
543
544 /* start off a new buffer with *p */
545 q = buff;
546 *q++ = uc[*p];
547 state = MIXED_TRANS;
548 }
549 else *q++ = *p;
550 break;
551
552
553 case ALL_TRANS:
554
555 /* in this state, all characters so far are transformable */
556 if( transformable(*p) ) *q++ = uc[*p];
557 else
558 {
559 /* make a new @VShift WORD out of the current contents of buff */
560 tmp = DoWord(buff, q, x, small_font);
561 new_y = DoVShift(x, vshift, tmp);
562
563 /* construct the skeleton of the result to replace x */
564 New(new_x, ONE_COL);
565 FposCopy(fpos(new_x), fpos(x));
566 New(new_acat, ACAT);
567 FposCopy(fpos(new_acat), fpos(x));
568 Link(new_x, new_acat);
569 Link(new_acat, new_y);
570 DoAddGap(new_acat);
571
572 /* start off a new buffer with *p */
573 q = buff;
574 *q++ = *p;
575 state = MIXED_NON;
576 }
577 break;
578
579
580 case MIXED_NON:
581
582 /* in this state the previous char was non-transformable, but */
583 /* there have been characters before that that were transformable */
584 if( transformable(*p) )
585 {
586 /* make a new WORD out of the current contents of buff */
587 new_y = DoWord(buff, q, x, word_font(x));
588
589 /* link the new word into the growing structure that replaces x */
590 Link(new_acat, new_y);
591 DoAddGap(new_acat);
592
593 /* start off a new buffer with *p */
594 q = buff;
595 *q++ = uc[*p];
596 state = MIXED_TRANS;
597 }
598 else *q++ = *p;
599 break;
600
601
602 case MIXED_TRANS:
603
604 /* in this state the previous char was transformable, but there */
605 /* have been characters before that that were non-transformable */
606 if( transformable(*p) ) *q++ = uc[*p];
607 else
608 {
609 /* make a new @VShift WORD out of the current contents of buff */
610 tmp = DoWord(buff, q, x, small_font);
611 new_y = DoVShift(x, vshift, tmp);
612
613 /* link the new word into the growing structure that replaces x */
614 Link(new_acat, new_y);
615 DoAddGap(new_acat);
616
617 /* start off a new buffer with *p */
618 q = buff;
619 *q++ = *p;
620 state = MIXED_NON;
621 }
622 break;
623
624 }
625 }
626
627 /* now at termination, clean up the structure */
628 switch( state )
629 {
630 case INIT:
631 case ALL_NON:
632
633 /* original x is OK as is: either empty or all non-transformable */
634 break;
635
636
637 case ALL_TRANS:
638
639 /* make a new @VShift WORD and replace x with it */
640 tmp = DoWord(buff, q, x, small_font);
641 new_x = DoVShift(x, vshift, tmp);
642 ReplaceNode(new_x, x);
643 Dispose(x);
644 x = new_x;
645 break;
646
647
648 case MIXED_NON:
649
650 /* make a new WORD, add to new_acat, and replace x */
651 new_y = DoWord(buff, q, x, word_font(x));
652 Link(new_acat, new_y);
653 ReplaceNode(new_x, x);
654 Dispose(x);
655 x = new_x;
656 break;
657
658
659 case MIXED_TRANS:
660
661 /* make a new @VShift WORD, add to new_acat, and replace x */
662 tmp = DoWord(buff, q, x, small_font);
663 new_y = DoVShift(x, vshift, tmp);
664 Link(new_acat, new_y);
665 ReplaceNode(new_x, x);
666 Dispose(x);
667 x = new_x;
668 break;
669 }
670 debug1(DCM, D, "MapSmallCaps returning %s", EchoObject(x));
671 return x;
672 } /* end MapSmallCaps */
673
674
675 /*****************************************************************************/
676 /* */
677 /* BOOLEAN MapIsLowerCase(FULL_CHAR ch, MAPPING m) */
678 /* */
679 /* Returns TRUE if ch is a lower-case character in mapping m; i.e. if it */
680 /* has a corresponding upper-case character. */
681 /* */
682 /*****************************************************************************/
683
MapIsLowerCase(FULL_CHAR ch,MAPPING m)684 BOOLEAN MapIsLowerCase(FULL_CHAR ch, MAPPING m)
685 { BOOLEAN res;
686 debug2(DCM, D, "MapIsLowerCase(%c, %d)", ch, m);
687 res = (MapTable[m]->map[MAP_UPPERCASE][ch] != '\0');
688 debug1(DCM, D, "MapIsLowerCase returning %s", bool(res));
689 return res;
690 } /* end MapIsLowerCase */
691