Chapter 5
Character Maps

   5.1 Requests
   5.2 Memory
      Initialization
   5.3 Fill Map
      With Characters
      With Rulers
   5.4 Dump Map

5.1 Requests

<..handle char map mode..>
 ch_map_flag = !ch_map_flag;
 if( ch_map_flag ){ <.enter char map.> }
 else             { <.exit char map.>  }
 -_-_-

5.2 Memory

The character map is a sequence of lines that are dynamically allocated. Each character is represented by a pair (str,design), where str is a possible empty string and design is a character holding a value 0, 1, 2, or 3.

<..types..>+
 struct ch_map_rec{
   char* line;
   int max, chars;
 };
 -_-_-

<..vars..>+
 static struct ch_map_rec  ch_map[HEIGHT];
 static   int max_map_line, min_map_line;
 -_-_-

The following might be too high of a bound. The agreed def of html allows only 1024 characters in a map. However, with the permissible def of html, extra characters hopefully are ignored without causing problems. Also note that we probably going to have leading and trailing row that are not in use, and 120 line for a full page figure is not relly that big of a bound on the numer of lines per page.

<..defines..>+
 #define HEIGHT 120
 -_-_-

<..h ch..>
 1-_-_-

<..v ch..>
 2-_-_-

<..black ch..>
 3-_-_-

<..design bound..>
 4-_-_-

The str is a string holding the text that belongs to the corresponding position: representation for the character, specials, etc. The design hold a drawing content corresponding to space, horizontal line, vertical line, and filled area, respectively. If the str is empty, the design is assumed to be the content of the character. If the string is not empty, it is assumed to be the content of the character.

The string holds character codes in the range of 32–255. Hence, we still have a room to get more sophisticated designs.

<..vars..>+
 static BOOL ch_map_flag = FALSE;
 -_-_-

<..defines..>+
 #define NULL_MAP (struct map_line_type*) 0
 -_-_-

Initialization

<..enter char map..>
 init_ch_map();
 xresolution = yresolution = 0;
 while( special_n-- > 0 ){
   ch = get_char();
   if( (ch >= ’0’) && (ch <= ’9’) )
      { yresolution = yresolution * 10 + ch - ’0’; }
   else if( (ch == ’,’) && !xresolution && yresolution )
      { xresolution = yresolution;  yresolution = 0; }
   else { <.resolve boundary ch in maps.> }
 }
 if( !xresolution )  xresolution = yresolution;
 if( !xresolution ){ xresolution = XRESOLUTION;
                     yresolution = YRESOLUTION; }
 else { xresolution = xresolution * (INTEGER) (XRESOLUTION / 100);
        yresolution = yresolution * (INTEGER) (YRESOLUTION / 100);  }
 -_-_-

Resolution can be an integer number or a pair of integer numbers separated by a comma. The first for x-resolution, the second for y-resolution. Then we can have a map for boundary characters made up of pairs: character to be replaced followed by replcment.

<..resolve boundary ch in maps..>
 xresolution = yresolution = 0;
 <.special resolution err.>
 -_-_-

<..vars..>+
 static INTEGER xresolution, yresolution;
 -_-_-

<..defines..>+
 #define XRESOLUTION MARGINSP
 #ifdef LONG
 #define YRESOLUTION 786432L
 #else
 #define YRESOLUTION 786432
 #endif
 -_-_-

<..header functions..>+
 static void init_ch_map( ARG_I(void) );
 -_-_-

<..functions..>+
 static void init_ch_map(MYVOID)
 {   int i;
   for( i=0; i<HEIGHT; i++ ){
     ch_map[i].max = 0;  ch_map[i].chars = 0;  ch_map[i].line = NULL;  }
   max_map_line = -1;
   min_map_line = HEIGHT;
 }
 -_-_-

5.3 Fill Map

With Characters

The 0.75 constant has been derived by trial and error by centering the nodes of ‘\TreeSpec(\SRectNode)()() \Tree()( 3,dog// 0,boxer & 0,cocker & 3, schnauzer // 0,miniature~~schauzer & 0,standard~~schnauzer & 0,giant~~schnauzer// )’.

<..insert ch to ch-map..>
 insert_ch_map((char) ch, TRUE);
 -_-_-

<..ch: 1, ruler: 0..>
 tag-_-_-

<..header functions..>+
 static void insert_ch_map( ARG_II(char,BOOL) );
 -_-_-

<..functions..>+
 
 static void insert_ch_map( ch, <.ch: 1, ruler: 0.> )
        U_CHAR ch;
        BOOL <.ch: 1, ruler: 0.>
 
 ;{     int row, col;
    <.get row and col for ch.>
    if(ch != 10){
       if( (ch_map[row].max > MAX_MAP_LINE) || (col > MAX_MAP_LINE) ){
         if( ok_map ){ warn_i_int_2( 25, MAX_MAP_LINE, ch);
                       ok_map = FALSE; }
       }else{  <.adjust boundary ch.>
          if( row < min_map_line ) min_map_line = row;
          if( row > max_map_line ) max_map_line = row;
          if( ch_map[row].max ){ <.insert char to nonempty map line.> }
          else { <.insert char to empty map line.> }
 }  }   }
 -_-_-

Leave the following to the user responsibility of getting the char mappings rightly in the ‘.htf’ fonts.

\<adjust boundary ch NO\><<< 
switch( ch ){ 
  case ’>’: { ch = ’x’; break; } 
  case ’&’: { ch = ’’; break; } 
  case ’"’: { ch = ’’; break; } 
} 
>>> 

<..vars..>+
 static U_CHAR ok_map = TRUE;
 -_-_-

<..get row and col for ch..>
 {          double x;
    row = (int) ( (y_val>0? y_val : 0.0) / (double) yresolution + 0.5);
    if( row >= HEIGHT ){
      if( ok_map ){ warn_i_int_2( 34, row, ch); ok_map = FALSE; }
      return; }
    x = (x_val>0? x_val : 0.0 ) / (double) xresolution + 0.75;
    col = (int) x;
    if( (ch >  ’) && (ch != ’-’) && (ch != ’|’) ){
       if( row == prevrow ){
          if( (col == prevcol + 1) && (x > prev_x + 0.5) )
             insert_ch_map(’ ’, TRUE);
          else if( (col > prevcol + 1) && (x < prev_x+0.2)
                                       && ( ch != ’&’ ))
             col = prevcol + 1;
       }else  prevrow = -1;
       prev_x = x
              + (<.(double) char_width( design_ch? design_ch : ch ).>)
              / (double) xresolution;
       prevcol = col;
    }else  prevrow = -1;
    prevrow = row;
 }
 -_-_-

The ‘( ch != ’&’ )’ above is to avoid breaking indirect unicode characters ‘&...;’ in character maps.

<..vars..>+
 static int prevcol = -1, prevrow;
 static double prev_x;
 -_-_-

<..defines..>+
 #define MAX_MAP_LINE 500
 -_-_-

<..insert char to empty map line..>
    int   n;
    char* p;
 ch_map[row].chars = (n = (col + 2 + 5) / 5 * 5) - <.ch: 1, ruler: 0.>;
 ch_map[row].max =  n - 1;
 ch_map[row].line = p = m_alloc(char, n);
 while( n-- ){ *(p++) = 0; }
 *(ch_map[row].line + col) = ch;
 -_-_-

\<insert char to empty map lineNO\><<< 
                                               int   n; 
                                               char* p; 
ch_map[row].chars =  (n = (col + 2 + 5) / 5 * 5) 
                                 - ‘<ch: 1, ruler: 0‘>; 
ch_map[row].max =  n - 1; 
ch_map[row].line = p = m_alloc(char, n); 
while( n-- ){ *(p++) = 0; } 
*(ch_map[row].line + col) = filter_bound_ch(ch); 
>>> 
 

<..insert char to nonempty map line..>
     int   n;
     char* p;
 if( ch_map[row].chars > col ){
       <.insert char within map line.> }
 else{ <.insert char beyond end of map line.> }
 -_-_-

Below: 8 = 1 (col starts at 0) + 2 (new ch=ch+bound) + 5 (rounding)

The rounding to 5 to (hopefully) help garbage collection.

<..insert char beyond end of map line..>
 n = (col - ch_map[row].chars + 8) / 5 * 5;
 ch_map[row].chars += n - <.ch: 1, ruler: 0.>;
 ch_map[row].max += n;
 ch_map[row].line = (char *)
     r_alloc((void *) ch_map[row].line,
             (size_t) ch_map[row].max + 1);
 while( n-- )  *(ch_map[row].line + ch_map[row].max - n) = 0;
 *(ch_map[row].line + ch_map[row].max
          - (ch_map[row].chars - col) + !<.ch: 1, ruler: 0.> ) = ch;
 -_-_-

\<insert char beyond end of map lineNO\><<< 
n = (col - ch_map[row].chars + 8) / 5 * 5; 
ch_map[row].chars += n - ‘<ch: 1, ruler: 0‘>; 
ch_map[row].max += n; 
ch_map[row].line = (char *) 
      r_alloc((void *) ch_map[row].line, 
              (size_t) ch_map[row].max + 1); 
while( n-- )  *(ch_map[row].line + ch_map[row].max - n) = 0; 
*(ch_map[row].line + ch_map[row].max 
    - (ch_map[row].chars - col) + �<ch: 1, ruler: 0‘> ) = 
                                 filter_bound_ch(ch); 
>>> 
 

<..insert char within map line..>
 if( <.ch: 1, ruler: 0.> ){
    if(   *(ch_map[row].line + ch_map[row].max - 1)
       ||  (ch_map[row].chars - col == 1)  ){ <.get room for ch.> }
    col = (ch_map[row].chars--) - col;
    p = ch_map[row].line + ch_map[row].max;
    while( col ){                     unsigned char temp_ch;
      if( ((unsigned char) (*p)) < <.design bound.> ) col--;
        temp_ch = *(--p);  *(p+1) = temp_ch;  }
 } else {
    col = ch_map[row].chars - col;
    p = ch_map[row].line + ch_map[row].max;
    while( col ){
      if( ((unsigned char) (*p)) < <.design bound.> ) col--;  p--;  }
 }
 *(++p) = ch;
 -_-_-

\<insert char within map lineNO\><<< 
if( ‘<ch: 1, ruler: 0‘> ){ 
   if(   *(ch_map[row].line + ch_map[row].max - 1) 
      ||  (ch_map[row].chars - col == 1)  ){ ‘<get room for ch‘> } 
   col = (ch_map[row].chars--) - col; 
   p = ch_map[row].line + ch_map[row].max; 
   while( col ){  if( ((unsigned char) (*p)) < ‘<design bound‘> ) col--; 
                  *(p+1) = *(--p);  } 
} else { 
   col = ch_map[row].chars - col; 
   p = ch_map[row].line + ch_map[row].max; 
   while( col ){ 
     if( ((unsigned char) (*p)) < ‘<design bound‘> ) col--;  p--;  } 
} 
*(++p) = filter_bound_ch(ch); 
>>> 
 

<..get room for ch..>
 ch_map[row].max += 5;
 ch_map[row].line = (char *)
    r_alloc((void *) ch_map[row].line,
            (size_t) ch_map[row].max + 1 );
 for( n = 0; n<5; n++ )
    *(ch_map[row].line + ch_map[row].max - n) = 0;
 ch_map[row].chars += 5;
 -_-_-

REMOVE FILTER!!!!!!

\<header functionsNO\><<< 
INTEGER filter_bound_ch( ARG_I(INTEGER) ); 
>>> 
 
 

With Rulers

<..ruler into ch map..>
    long int  sv_x_val, sv_y_val, sv_right, sv;
    int  ch;
 sv_x_val = x_val;
 sv_y_val = y_val;
 sv_right = right;
 y_val-=up;
 if( right < 0 ){ x_val += right;  right = -right; }
 if( up < 0 ){ y_val += up;  up = -up; }
 ch = ( (right > xresolution) &&  (up > yresolution) ) ?
        <.black ch.> :  ( ( right > up )? <.h ch.> : <.v ch.> );
 right += x_val;
 up    += sv = y_val;
 for( ; x_val < right; x_val += xresolution )
   for( y_val = sv ; y_val < up;  y_val += yresolution )
      insert_ch_map((char) ch, FALSE);
 x_val = sv_x_val;
 y_val = sv_y_val;
 if( sv_x_val + sv_right > max_x_val ) max_x_val = sv_x_val + sv_right;
 if( <.ch: 1, ruler: 0.> ) x_val += sv_right;
 -_-_-

5.4 Dump Map

<..exit char map..>
 dump_ch_map();
 -_-_-

We need memory of order 2 times the number of characters.

<..dump ch-map at end of page..>
 if( ch_map_flag ){
    warn_i(27);   dump_ch_map();  init_ch_map(); }
 -_-_-

dump_ch_map();’ creates overflow problems here.

<..header functions..>+
 static void dump_ch_map( ARG_I(void) );
 -_-_-

<..functions..>+
 static void dump_ch_map(MYVOID)
 {     int   n, i, min, k, extra_sp;
       U_CHAR  *p;
         struct map_line_type  *q; 
   <.min := start of bounding box.>
   for( i=min_map_line; i<=max_map_line; i++ ){
     if( ( n = ch_map[i].max) > 0 ){
       p = ch_map[i].line;  k = min;    extra_sp = 0;
       <.ignore trailing spaces.>
       while( 1 + n-- ){
         if( --k < 0 ){
           if( extra_sp && (((unsigned char) *p)     < <.design bound.>)
                        && (((unsigned char) *(p+1)) < <.design bound.>) )
           {  extra_sp--;
           } else { switch( *p ){ <.dump ch.> } }
         }
         p++;
       }
       free((void *)  ch_map[i].line );
     }
     if( i<max_map_line )  put_char(’\n’);
   }
   nomargin = FALSE;
 }
 -_-_-

<..min := start of bounding box..>
 {    int   max;
    min = 100; max = 0;
    for( i=min_map_line; i<=max_map_line; i++ ){
      p = ch_map[i].line;
      n = ch_map[i].max;  if( max < n )  max = n;
      k = 0;  while( n-- ){ if(*(p++)) break;  k++; }
      if( ch_map[i].max && (k < min) ) min = k;        }
    if( (max < 78) && !nomargin ) min = 0;
 }
 -_-_-

<..ignore trailing spaces..>
 {     U_CHAR  *s;
    s = p + n;
    while( n && !(*s) && !(*(s-1)) ){  n--; s--; }
    if( n && !(*s) && (((unsigned char) *(s-1)) < <.design bound.>) ) n--;
 }
 -_-_-

<..dump ch..>
            case 0: { put_char(’ ’); break; }
     case <.h ch.>: { put_char(’-’); break; }
     case <.v ch.>: { put_char(’|’); break; }
 case <.black ch.>: { put_char(’#’); break; }
          case  ’: { extra_sp++; }
           default: { <.insert ch from map into file.>  break; }
 -_-_-

<..insert ch from map into file..>
     BOOL    tag;
     INTEGER count;
 tag = TRUE;   count = 0;
 do{   if( *p == ’<’ )       tag   = FALSE;
       else  if( *p == ’>’ ) tag   = TRUE;
             else           count += tag;
       put_char( *p ); n--;
 }while( ((unsigned char) *(++p)) >= <.design bound.> );
 if( !count ){ n++; p--; }
 -_-_-

COUNT the number of characters and issue a warning if over limit (1024 default).