/* char_routines.c: Data structures for character information This file is part of Omega, which is based on the web2c distribution of TeX, Copyright (c) 1994--2001 John Plaice and Yannis Haralambous Omega 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 of the License, or (at your option) any later version. Omega 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 Omega; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include "cpascal.h" #include "manifests.h" #include "list_routines.h" #include "char_routines.h" #include "print_routines.h" #include "out_routines.h" #include "error_routines.h" #include "ligkern_routines.h" #include "dimen_routines.h" #include "header_routines.h" #include "font_routines.h" #include "out_ofm.h" #include "omfonts.h" #include "dvi.h" #define PLANE 0x10000 #define HALFPLANE 0x08000 #define MAX_START_OFM 65535 #define MAX_START_TFM 255 /* * Characters can range from 0x0 to 0x7fffffff (31 bits unsigned), * which is a lot of characters. We would expect characters to either * be bunched up in a given region, or else sparsely defined throughout * the range. The data structure to hold them is an array of HALFPLANE * arrays, each of which holds a full PLANE (0x10000) of characters. * * At all times, init_character ensures that planes[0] to planes[plane_max] * are either NULL or allocated arrays of PLANE characters. * * For allocated array p, init_character also ensures that planes[p][0] * to planes[p][char_max[p]] are either NULL or pointers to allocated * char_entry values. * * init_character can be called when actually reading a CHARACTER definition * or when the character is referenced in a charlist cycle, an extensible * piece or in a ligature/kerning table. In the latter case, ensure_existence * is called, and it sets the defined field of the character to be FALSE; * * init_character also sets current_character to the character entry; * */ unsigned bc=0x7fffffff; unsigned ec=0x0; unsigned ne=0; char_entry **planes[HALFPLANE]; unsigned char_max[HALFPLANE]; unsigned plane_max = 0; char_entry *current_character = NULL; char_entry *current_secondary_character = NULL; queue exten_queue; four_pieces **exten_table = NULL; unsigned no_labels = 0; label_entry *label_table; int label_ptr, sort_ptr; int lk_offset; boolean extra_loc_needed; extern unsigned bchar; void init_planes(void) { plane_max = 0; planes[plane_max] = NULL; char_max[plane_max] = 0; } void init_character(unsigned c, char_entry *ready_made) { register unsigned i, index, plane; if ((cCHAR_MAXIMUM)) { warning_1("CHARACTER index (H %X) not 31-bit unsigned integer; " "ignored", c); current_character = NULL; return; } plane = c / PLANE; index = c % PLANE; for (i=plane_max+1; i<=plane; i++) { planes[plane] = NULL; } if (planes[plane]==NULL) { planes[plane] = (char_entry **)xmalloc(PLANE * sizeof(char_entry *)); char_max[plane] = 0; planes[plane][0] = NULL; } for (i=char_max[plane]+1; i<=index; i++) { planes[plane][i] = NULL; } if (plane>plane_max) plane_max = plane; if (index>char_max[plane]) char_max[plane] = index; if (planes[plane][index] != NULL) { if (planes[plane][index]->defined == FALSE) { current_character = planes[plane][index]; current_character->defined = TRUE; return; } warning_1("CHARACTER index (H %X) previously defined; " "old definition ignored", c); free(current_character); current_character = NULL; } if (ready_made != NULL) { current_character = ready_made; planes[plane][index] = current_character; } else { current_character = (char_entry *)xmalloc(sizeof(char_entry)); planes[plane][index] = current_character; for (i=C_MIN; i<=C_MAX; i++) { current_character->indices[i] = NULL; current_character->index_indices[i] = 0; } current_character->c = c; current_character->copies = 0; current_character->remainder = 0; current_character->tag = TAG_NONE; current_character->defined = TRUE; current_character->accent = ACC_NONE; current_character->ovf_packet_length = 0; current_character->ovf_packet = NULL; cur_packet = NULL; } } void copy_characters(unsigned c, unsigned copies) { unsigned i=0; unsigned plane, index; char_entry *the_entry; if ((cCHAR_MAXIMUM)) { warning_1("CHARACTER index (H %X) not 31-bit unsigned integer; " "ignored", c); current_character = NULL; return; } plane = c / PLANE; index = c % PLANE; if (planes[plane]==NULL) internal_error_1("copy_characters (plane %d)", plane); the_entry = planes[plane][index]; if (the_entry==NULL) internal_error_1("copy_characters (index %d)", index); the_entry->copies = copies; for (i=(c+1); i<=(c+copies); i++) init_character(i, the_entry); } void ensure_existence(unsigned c) { register unsigned index, plane; plane = c / PLANE; index = c % PLANE; if ((planes[plane]==NULL) || (planes[plane][index]==NULL)) { init_character(c, NULL); planes[plane][index]->defined = FALSE; } current_secondary_character = planes[plane][index]; } #define FOR_ALL_CHARACTERS(FOR_ALL_CHARACTERS_ACTION) \ for (plane = 0; plane <=plane_max; plane++) { \ if (planes[plane] != NULL) { \ for (index = 0; index <=char_max[plane]; index++) { \ entry = planes[plane][index]; \ c = plane*PLANE + index; \ if (entry != NULL) { \ FOR_ALL_CHARACTERS_ACTION \ } \ } \ } \ } #define FOR_ALL_EXISTING_CHARACTERS(FOR_ALL_EXISTING_CHARACTERS_ACTION) \ FOR_ALL_CHARACTERS( \ if (entry->defined == TRUE) { \ FOR_ALL_EXISTING_CHARACTERS_ACTION \ } \ ) void output_ovf_chars(void) { register unsigned index, plane, c, k; char_entry *entry; fix wd; FOR_ALL_EXISTING_CHARACTERS( wd = lval(entry->indices[C_WD]); if (design_units != UNITY) wd = zround(((double)wd) / ((double)design_units) * 1048576.0); if ((entry->ovf_packet_length>241) || (wd < 0) || (wd >= 0x1000000) || (c < 0) || (c >255)) { out_ovf(242); out_ovf_4(entry->ovf_packet_length); out_ovf_4(c); out_ovf_4(wd); } else { out_ovf(entry->ovf_packet_length); out_ovf(c); out_ovf((wd >>16) & 0xff); out_ovf((wd >>8) & 0xff); out_ovf(wd & 0xff); } for (k=0; kovf_packet_length; k++) out_ovf(entry->ovf_packet[k]); ) } void check_existence_all_character_fields(void) { register unsigned index, plane, c; char_entry *entry; unsigned *exten; unsigned j; FOR_ALL_EXISTING_CHARACTERS( switch(entry->tag) { case TAG_NONE: { break; } case TAG_LIG: { check_ligature_program(c, entry->remainder); break; } case TAG_LIST:{ check_existence_and_safety(c, entry->remainder, NULL, "%sCharacter (H %X) NEXTLARGER than (H %X) " "has no CHARACTER spec"); break; } case TAG_EXT:{ exten = entry->extens; for (j=E_MIN; j<=E_MAX; j++) { if (exten[j]!=0) check_existence_and_safety(c, exten[j], extensible_pieces[j], "%s piece (H %X) of character (H %X) " "has no CHARACTER spec"); } break; } } ) } void clear_ligature_entries(void) { register unsigned index, plane, c; char_entry *entry; FOR_ALL_EXISTING_CHARACTERS( if (entry->tag == TAG_LIG) { entry->tag = TAG_NONE; entry->remainder = 0; } ) } void check_existence_and_safety(unsigned c, unsigned g, const_string extra, const_string fmt) { char_entry *gentry = planes[g/PLANE][g%PLANE]; if ((gCHAR_MAXIMUM)) internal_error_1("check_existence_and_safety (g=%d)", g); gentry = planes[g/PLANE][g%PLANE]; if ((g>=128) && (c<128)) seven_bit_calculated = 0; if ((gentry==NULL) || (gentry->defined == FALSE)) { warning_s_2(fmt, extra, g, c); current_character = gentry; set_character_measure(C_WD, 0); } } void doublecheck_existence(unsigned g, const_string extra, const_string fmt) { char_entry *gentry = planes[g/PLANE][g%PLANE]; if ((gCHAR_MAXIMUM)) internal_error_1("doublecheck_existence (g=%d)", g); gentry = planes[g/PLANE][g%PLANE]; if ((gentry==NULL) || (gentry->defined == FALSE)) { warning_s_1(fmt, extra, g); current_character = gentry; /* set_character_measure(C_WD, 0); */ } } extern string character_measures[]; void print_characters(boolean read_ovf) { register unsigned index, plane, c; char_entry *entry; four_pieces *exten; four_entries *lentry; unsigned j,k; FOR_ALL_CHARACTERS( if (entry->index_indices[C_WD] != 0) { print_character(c); for (k=C_MIN; kindex_indices[k] != 0) { print_character_measure(k, dimen_tables[k][entry->index_indices[k]]); } } fflush(file_output); switch (entry->tag) { case TAG_NONE: { break; } case TAG_LIG: { left(); out("COMMENT"); out_ln(); lentry = lig_kern_table + entry->remainder; if (lentry->entries[0] > STOP_FLAG) { lentry = lig_kern_table + (256*lentry->entries[2]+lentry->entries[3]); } do { print_one_lig_kern_entry(lentry, FALSE); if (lentry->entries[0] >= STOP_FLAG) { lentry = lig_kern_table + nl; } else { lentry = lentry + 1 + lentry->entries[0]; } } while (lentry < (lig_kern_table + nl)); right(); break; } case TAG_LIST: { print_next_larger(entry->remainder); break; } case TAG_EXT: { print_var_character(); exten = exten_table[entry->remainder]; for (j=E_MIN; j<=E_MAX; j++) { if (exten->pieces[j]!=0) print_extensible_piece(j,exten->pieces[j]); } right(); break; } } if ((read_ovf==TRUE) && (entry->ovf_packet_length>0)) { print_map(); print_packet(entry->ovf_packet, entry->ovf_packet_length); right(); } right(); fflush(file_output); } ) } unsigned stack_top = 0; int wstack[1000]; int xstack[1000]; int ystack[1000]; int zstack[1000]; static int string_balance(unsigned char *special, int k) { unsigned paren_level = 0; if (k == 0) return 1; if (*special == ' ') return 0; for (; k > 0; k--, special++) { if ((*special < ' ') || (*special > '~')) return 0; if (*special == '(') paren_level++; if (*special == ')') { if (paren_level-- == 0) return 0; } } return (paren_level == 0); } void print_packet(unsigned char *packet_start, unsigned packet_length) { unsigned cmd, arg; fix fix_arg, fix_arg1; unsigned char *packet = packet_start; unsigned char *max_packet = packet+packet_length; stack_top = 0; wstack[stack_top] = xstack[stack_top] = ystack[stack_top] = zstack[stack_top] = 0; while (packet = DVI_FNT_NUM_0) && (*packet <= DVI_FNT_NUM_63)) { arg = *packet - DVI_FNT_NUM_0; packet++; print_select_font(arg); } else switch(*packet) { case DVI_SET_1: case DVI_SET_2: case DVI_SET_3: case DVI_SET_4: cmd = *packet; packet++; arg = ovf_get_arg(&packet, cmd - DVI_SET_1 + 1, FALSE); print_set_char(arg); break; case DVI_NOP: packet++; break; case DVI_PUSH: cmd = DVI_PUSH; packet++; stack_top++; wstack[stack_top] = wstack[stack_top-1]; xstack[stack_top] = xstack[stack_top-1]; ystack[stack_top] = ystack[stack_top-1]; zstack[stack_top] = zstack[stack_top-1]; print_push(); break; case DVI_POP: cmd = DVI_PUSH; packet++; stack_top--; print_pop(); break; case DVI_SET_RULE: cmd = DVI_SET_RULE; packet++; fix_arg = ovf_get_arg(&packet, 4, TRUE); fix_arg1 = ovf_get_arg(&packet, 4, TRUE); print_set_rule(fix_arg, fix_arg1); break; case DVI_PUT_RULE: cmd = DVI_PUT_RULE; packet++; fix_arg = ovf_get_arg(&packet, 4, TRUE); fix_arg1 = ovf_get_arg(&packet, 4, TRUE); print_put_rule(fix_arg, fix_arg1); break; case DVI_RIGHT_1: case DVI_RIGHT_2: case DVI_RIGHT_3: case DVI_RIGHT_4: cmd = *packet; packet++; fix_arg = ovf_get_arg(&packet, cmd - DVI_RIGHT_1 + 1, TRUE); print_move(M_RIGHT, fix_arg); break; case DVI_DOWN_1: case DVI_DOWN_2: case DVI_DOWN_3: case DVI_DOWN_4: cmd = *packet; packet++; fix_arg = ovf_get_arg(&packet, cmd - DVI_DOWN_1 + 1, TRUE); print_move(M_DOWN, fix_arg); break; case DVI_W_0: cmd = DVI_W_0; packet++; print_move(M_RIGHT, wstack[stack_top]); break; case DVI_W_1: case DVI_W_2: case DVI_W_3: case DVI_W_4: cmd = *packet; packet++; wstack[stack_top] = ovf_get_arg(&packet, cmd - DVI_W_1 + 1, TRUE); print_move(M_RIGHT, wstack[stack_top]); break; case DVI_X_0: cmd = DVI_X_0; packet++; print_move(M_RIGHT, xstack[stack_top]); break; case DVI_X_1: case DVI_X_2: case DVI_X_3: case DVI_X_4: cmd = *packet; packet++; xstack[stack_top] = ovf_get_arg(&packet, cmd - DVI_X_1 + 1, TRUE); print_move(M_RIGHT, xstack[stack_top]); break; case DVI_Y_0: cmd = DVI_Y_0; packet++; print_move(M_DOWN, ystack[stack_top]); break; case DVI_Y_1: case DVI_Y_2: case DVI_Y_3: case DVI_Y_4: cmd = *packet; packet++; ystack[stack_top] = ovf_get_arg(&packet, cmd - DVI_Y_1 + 1, TRUE); print_move(M_DOWN, ystack[stack_top]); break; case DVI_Z_0: cmd = DVI_Z_0; packet++; print_move(M_DOWN, zstack[stack_top]); break; case DVI_Z_1: case DVI_Z_2: case DVI_Z_3: case DVI_Z_4: cmd = *packet; packet++; zstack[stack_top] = ovf_get_arg(&packet, cmd - DVI_Z_1 + 1, TRUE); print_move(M_DOWN, zstack[stack_top]); break; case DVI_PUT_1: case DVI_PUT_2: case DVI_PUT_3: case DVI_PUT_4: cmd = *packet; packet++; fix_arg = ovf_get_arg(&packet, cmd - DVI_PUT_1 + 1, FALSE); print_put_char(fix_arg); break; case DVI_XXX_1: case DVI_XXX_2: case DVI_XXX_3: case DVI_XXX_4: cmd = *packet; packet++; fix_arg = ovf_get_arg(&packet, cmd - DVI_XXX_1 + 1, FALSE); if (fix_arg < 0) { internal_error_1("Special string with negative length (%d)\n", fix_arg); } if ((fix_arg <= 64) && string_balance(packet, fix_arg)) { char *special = (char *) xmalloc(fix_arg+1); strncpy(special, (const char *)packet, fix_arg); special[fix_arg] = '\0'; print_special(special); free(special); } else { print_special_hex(packet, fix_arg); } packet += fix_arg; break; case DVI_FNT_1: case DVI_FNT_2: case DVI_FNT_3: case DVI_FNT_4: cmd = *packet; packet++; fix_arg = ovf_get_arg(&packet, cmd - DVI_FNT_1 + 1, FALSE); print_select_font(fix_arg); break; default: internal_error_1("Unrecognized DVI packet (%d)\n", *packet); } } fflush(file_output); fflush(stderr); } void check_char_tag(unsigned c) { ensure_existence(c); } void set_char_tag(unsigned c, unsigned tag) { ensure_existence(c); planes[c/PLANE][c%PLANE]->tag = tag; } void set_char_remainder(unsigned c, unsigned remainder) { ensure_existence(c); planes[c/PLANE][c%PLANE]->remainder = remainder; } static int get_char_remainder(unsigned c) { ensure_existence(c); return planes[c/PLANE][c%PLANE]->remainder; } void set_next_larger(unsigned larger) { check_char_tag(current_character->c); set_char_tag(current_character->c, TAG_LIST); set_char_remainder(current_character->c, larger); } void init_var_character(void) { four_pieces *entry = (four_pieces *) xmalloc(sizeof(four_pieces)); unsigned j; check_char_tag(current_character->c); set_char_tag(current_character->c, TAG_EXT); append_to_queue(&exten_queue, entry); for (j=E_MIN; j<=E_MAX; j++) { entry->pieces[j] = 0; } set_char_remainder(current_character->c, ne); current_character->extens = (unsigned int *)entry->pieces; ne++; } void set_extensible_piece(unsigned piece, unsigned val) { unsigned *exten = current_character->extens; if ((piece < E_MIN) || (piece > E_MAX)) internal_error_1("set_extensible_piece (piece=%d)", piece); if (exten[piece]!=0) warning_0("value already defined"); exten[piece] = val; } void adjust_labels(boolean play_with_starts) { unsigned plane, index; unsigned c; char_entry *entry; int max_start = (ofm_level==OFM_TFM) ? MAX_START_TFM : MAX_START_OFM; label_table = (label_entry *)xmalloc((no_labels+2)*sizeof(label_entry)); label_ptr = 0; label_table[0].rr = -1; /* sentinel */ FOR_ALL_CHARACTERS( if ((c>=bc) && (c<=ec) && (entry->tag == TAG_LIG)) { sort_ptr = label_ptr; /* hole at position sort_ptr+1 */ while (label_table[sort_ptr].rr > (int)(entry->remainder)) { label_table[sort_ptr+1] = label_table[sort_ptr]; sort_ptr--; /* move the hole */ } label_table[sort_ptr+1].cc = c; label_table[sort_ptr+1].rr = entry->remainder; label_ptr++; } ) if (play_with_starts) { if (bchar != CHAR_BOUNDARY) { extra_loc_needed = TRUE; lk_offset = 1; } else { extra_loc_needed = FALSE; lk_offset = 0; } sort_ptr = label_ptr; /* the largest unallocated label */ if ((label_table[sort_ptr].rr + lk_offset) > max_start) { lk_offset=0; extra_loc_needed=FALSE; /* location 0 can do double duty */ do { set_char_remainder(label_table[sort_ptr].cc, lk_offset); while (label_table[sort_ptr-1].rr == label_table[sort_ptr].rr) { sort_ptr--; set_char_remainder(label_table[sort_ptr].cc, lk_offset); } lk_offset++; sort_ptr--; } while ((lk_offset+label_table[sort_ptr].rr) > max_start); /* N.B. lk_offset=MAX_START+1 satisfies this when sort_ptr=0 */ } if (lk_offset>0) { while (sort_ptr>0) { set_char_remainder(label_table[sort_ptr].cc, get_char_remainder(label_table[sort_ptr].cc) +lk_offset); sort_ptr--; } } } } void print_labels(void) { unsigned i; if (label_ptr>0) { left(); out("COMMENT"); out_ln(); for (i=1; i<=label_ptr; i++) { left(); out("LABEL_ENTRY"); out(" "); out_int(i,10); out(" "); out_char(label_table[i].cc); out(" "); out_int(label_table[i].rr, 10); right(); } right(); } } void check_and_correct(void) { build_kern_table(); build_dimen_tables(); build_exten_table(); check_ligature_ends_properly(); compute_ofm_character_info(); adjust_labels(TRUE); check_existence_all_character_fields(); calculate_seven_bit_safe_flag(); check_ligature_infinite_loops(); check_charlist_infinite_loops(); doublecheck_ligatures(); doublecheck_extens(); } void check_charlist_infinite_loops(void) { unsigned plane, index; unsigned c; char_entry *entry; unsigned g; FOR_ALL_CHARACTERS( if (entry->tag == TAG_LIST) { g = entry->remainder; while ((g < c) && (planes[g/PLANE][g%PLANE]->tag == TAG_LIST)) { g = planes[g/PLANE][g%PLANE]->remainder; } if (g == c) { entry->tag = TAG_NONE; entry->remainder = 0; warning_1("Cycle of NEXTLARGER characters " "has been broken at ",c); } } ) } void build_exten_table(void) { list L1 = exten_queue.front, L2; unsigned i = 0; exten_table = (four_pieces **) xmalloc(ne*sizeof(four_pieces *)); while (L1 != NULL) { exten_table[i] = (four_pieces *)L1->contents; L2 = L1->ptr; free(L1); L1 = L2; i++; } } void retrieve_exten_table(unsigned char *table) { unsigned i = 0, j; four_pieces *entry; exten_table = (four_pieces **) xmalloc(ne*sizeof(four_pieces *)); for (i=0; ipieces[j] = table[4*i+j] & 0xff; } else { entry->pieces[j] = ((table[8*i+j*2] & 0xff) << 8) | (table[8*i+j*2+1] & 0xff); } } } } void print_extens(void) { unsigned i,j; if (ne>0) { left(); out("COMMENT"); out_ln(); for (i=0; ipieces[j] != 0) print_extensible_piece(j,exten_table[i]->pieces[j]); } right(); } right(); } } void doublecheck_extens(void) { unsigned i,j; if (ne>0) { for (i=0; ipieces[j] != 0) doublecheck_existence( exten_table[i]->pieces[j], extensible_pieces[j], "Unused %s piece (H %X) refers to " "nonexistent character"); } } } } void compute_ligkern_offset(void) { } void compute_character_info_size(void) { } void output_ofm_extensible(void) { unsigned i,j; for (i=0; ipieces[j] != 0) out_ofm_char(exten_table[i]->pieces[j]); else out_ofm_char(0); } } } void compute_ofm_character_info(void) { unsigned plane, index; unsigned c; char_entry *entry; bc = 0x7fffffff; ec=0; switch (ofm_level) { case OFM_TFM: { FOR_ALL_EXISTING_CHARACTERS( if (c < bc) bc = c; if (c > ec) ec = c; ) break; } case OFM_LEVEL0: { FOR_ALL_EXISTING_CHARACTERS( if (c < bc) bc = c; if (c > ec) ec = c; ) break; } /* Level 1 only uses plane 0. Take advantage of this fact. */ case OFM_LEVEL1: { FOR_ALL_CHARACTERS( if (c < bc) bc = c; if (c > ec) ec = c; ) break; } default: { internal_error_0("compute_ofm_character_info"); } } } void output_ofm_character_info(void) { unsigned plane, index; unsigned c; char_entry *entry; unsigned wd, ht, dp, ic; switch (ofm_level) { case OFM_TFM: { plane=0; for (index = bc; index <=ec; index++) { entry = planes[plane][index]; c = plane*PLANE + index; if (entry == NULL) { out_ofm_4(0); } else { if (entry->indices[C_WD] != NULL) wd = entry->indices[C_WD]->index; else wd = 0; if (entry->indices[C_HT] != NULL) ht = entry->indices[C_HT]->index; else ht = 0; if (entry->indices[C_DP] != NULL) dp = entry->indices[C_DP]->index; else dp = 0; if (entry->indices[C_IC] != NULL) ic = entry->indices[C_IC]->index; else ic = 0; out_ofm(wd); out_ofm(ht*16 + dp); out_ofm(ic*4 + entry->tag); out_ofm(entry->remainder); } } break; } case OFM_LEVEL0: { plane=0; for (index = bc; index <=ec; index++) { entry = planes[plane][index]; c = plane*PLANE + index; if (entry == NULL) { out_ofm_4(0); out_ofm_4(0); } else { if (entry->indices[C_WD] != NULL) wd = entry->indices[C_WD]->index; else wd = 0; if (entry->indices[C_HT] != NULL) ht = entry->indices[C_HT]->index; else ht = 0; if (entry->indices[C_DP] != NULL) dp = entry->indices[C_DP]->index; else dp = 0; if (entry->indices[C_IC] != NULL) ic = entry->indices[C_IC]->index; else ic = 0; out_ofm_2(wd); out_ofm(ht); out_ofm(dp); out_ofm(ic); out_ofm(entry->tag); out_ofm_2(entry->remainder); } } break; } case OFM_LEVEL1: { internal_error_0("OFM level 1 not currently supported"); break; } default: { internal_error_0("compute_ofm_character_info"); } } }