/* This is the main file for hacking dvips to do HyperPostScript * Written by Mark D. Doyle 11/94. It is (C) Copyright 1994 by Mark D. Doyle * and the University of California. You may modify and use this program to * your heart's content. */ #include "dvips.h" /* * The external declarations: */ #include "protos.h" #ifdef HPS #include #ifdef KPATHSEA #include #else #define TOLOWER Tolower #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #ifndef Tolower #define Tolower tolower #endif #define NAME 0 #define HREF 1 #define skip_space(s) for(; *s == ' '; s++); Boolean inHTMLregion = 0; Boolean NO_ERROR = 1; Boolean HPS_ERROR = 0; integer HREF_COUNT; Boolean ISHREF = 0; Boolean POPPED = 0; Boolean PUSHED = 0; char *current_name; int current_type; int current_pushcount; #define GoTo 0 #define GoToR 1 #define Launch 2 typedef struct rectangle { double llx; /* lower left x coor */ double lly; /* lower left y coor */ double urx; /* upper right x coor */ double ury; /* upper right y coor */ } dvipsRectangle; typedef struct hps_link { int action; /* GoTo, GoToR, or Launch */ char *file; /* PDF-style system-independent filename - unused now */ dvipsRectangle rect; /* rectangle for location of link */ int border[5]; /* Border info --- with dashes*/ int srcpg; /* Page of location */ int page; /* Page of destination --- left out is same as srcpg */ int vert_dest; /* Vertical position of destination on page */ double color[3]; /* color: length of array gives colorspace (3 <=> RGB)*/ char *title; /* Title of link */ } Hps_link; struct nlist { /* hashtable entry */ struct nlist *next; /* next entry in chain */ char *name; /* defined name */ Hps_link *defn; /* Link associated with name */ }; typedef struct rect_list { /* linked list of rectangles */ struct rect_list *next; /* next rectangle in the chain */ dvipsRectangle rect; } Rect_list; Rect_list *current_rect_list; #define HASHSIZE 1223 static struct nlist *link_targets[HASHSIZE]; /* names .... */ static struct nlist *link_sources[HASHSIZE]; /* hrefs .... */ #ifdef KPATHSEA #define dup_str xstrdup #else char *dup_str(); /* duplicate a string */ #endif Boolean noprocset; /* Leave out BeginProc and EndProc comments */ /* This was hardcoded to letter size. The denominator converts scaled points to big points (65536*72.27/72). */ #undef PAGESIZE /* HP-UX 10 defines for itself. */ #define PAGESIZE ((int) (vpapersize/65781.76)) #define MARGIN 72 #define VERTICAL 0 #define HORIZONTAL 1 #define FUDGE 2.0 /* For later use * static char *dest_key[9] = {"Fit", "FitB", "FitW", "FitH", "FitBH" "FitR", "FitV", "FitBV", "XYZ"}; */ char *hs = NULL; /* html string to be handled */ char *url_name = NULL; /* url between double quotes */ static int href_or_name(void); static int parseref(void); static int get_string(void); static int do_link(char *s, int type); static struct nlist *lookup_link(char *s, int type); static struct nlist *install_link(char *name, struct hps_link *defn, int type); static Hps_link *link_dup(struct hps_link *s); static double dvi_to_hps_conv(int i, int dir); static int href_name_match(char *h, char *n); static void stamp_hps(struct hps_link *pl); static void stamp_external(char *s, struct hps_link *pl); static void print_rect_list(void); /* parse anchor into link */ void do_html(char *s) { Hps_link *nl; url_name = (char *)malloc(strlen(s)+1); hs = s; HREF_COUNT = 0; skip_space(hs); /* skip spaces */ if ( TOLOWER(*hs) == 'a') { /* valid start */ POPPED = FALSE; if (inHTMLregion) { error("previous html not closed with /A"); return; } else { inHTMLregion = TRUE; current_rect_list = NULL; } hs++; skip_space(hs); while(*hs != '\0') { /* parse for names and href */ if (!href_or_name()) { error("Bad HMTL:"); error(s); error("!"); break; } skip_space(hs); } } else if ( (*hs == '/') && (TOLOWER(hs[1]) == 'a') ) { if (!inHTMLregion) { error("In html, /A found without previous A"); return; } else { inHTMLregion = FALSE; } if (current_type == HREF && current_name[0] != '#') { if ((nl = lookup_link(current_name, current_type)->defn)) { nl->rect.urx = dvi_to_hps_conv(hh, HORIZONTAL); nl->rect.ury = dvi_to_hps_conv(vv, VERTICAL)-FUDGE+12.0; stamp_external(current_name,nl); /* Broken lines ? */ } else { error("!Null lookup"); } } else { if ((nl = lookup_link(current_name, current_type)->defn)) { nl->rect.urx = dvi_to_hps_conv(hh, HORIZONTAL); nl->rect.ury = dvi_to_hps_conv(vv, VERTICAL)-FUDGE+12.0; if (current_type) { stamp_hps(nl); /* Put link info right where special is */ print_rect_list(); /* print out rectangles */ } } else { error("!Null lookup"); } } } else { error( "No A in html special"); error(s); /*error("!");*/ } return; } static int href_or_name(void) { if ((strncmp(hs, "href", 4) == 0) || (strncmp(hs, "HREF", 4) == 0)) { ISHREF = TRUE; } else if ((strncmp(hs, "name", 4) == 0) || (strncmp(hs, "NAME", 4) == 0)) { ISHREF = FALSE; } else { error("Not valid href or name html reference"); return(HPS_ERROR); } if(!parseref()) return(HPS_ERROR); if (url_name) { if( ISHREF ) { do_link(url_name, HREF); } else do_link(url_name, NAME); } else { error("No string in qoutes "); return(HPS_ERROR); } return(NO_ERROR); } static int parseref(void) { int i = 0; for(i=0; i++ < 4; hs++); /* skip href or name in html string */ skip_space(hs); if (*hs == '=' ) { hs++; } else { error("No equal sign"); return(HPS_ERROR); } if(!get_string()) return(HPS_ERROR); return(NO_ERROR); /* extract stuff between double quotes */ } static int get_string(void) { char *v = url_name; skip_space(hs); /* hash_name(); */ if (*hs == '"') { for(hs++; *hs != '"' && *hs != '\0'; *v++ = *hs++); /* need to esc " */ if(*hs == '"') { hs++; *v++ = '\0'; return(NO_ERROR); } else { error("Improper href. Missing closing double quote"); return(HPS_ERROR); } } else { error("Improper href. Missing opening double quote"); return(HPS_ERROR); } } static int do_link(char *s, int type) { Hps_link *p; if (HREF_COUNT++ > 0) { error("!HTML string contains more than one href"); return(HPS_ERROR); } p = (Hps_link *)malloc(sizeof(*p)); p->action = GoTo; p->title = (char *)malloc(strlen(s)+1); p->title = s; p->srcpg = pagecounter; p->rect.llx = dvi_to_hps_conv(hh, HORIZONTAL); p->rect.lly = dvi_to_hps_conv(vv, VERTICAL)-FUDGE; p->rect.urx = -1.0; p->rect.ury = -1.0; p->vert_dest = -1; p->page = -1; p->color[0] = 0; p->color[1] = 0; /* Blue links */ p->color[2] = 1; p->border[0] = 1; /* horizontal corner radius */ p->border[1] = 1; /* vertical corner radius */ p->border[2] = 1; /* box line width */ p->border[3] = 3; /* dash on size */ p->border[4] = 3; /* dash off size */ current_name = (char *)malloc(strlen(s)+1); current_name = s; current_type = type; current_pushcount = pushcount; install_link(s, p, type); return(NO_ERROR); } static unsigned int hash_string(char *s) { unsigned hashval; for (hashval = 0; *s != '\0'; s++) hashval = *s + 31 * hashval; return hashval % HASHSIZE; } /* lookup a hashed name */ static struct nlist * lookup_link(char *s, int type) { struct nlist *np; for(np = type ? link_sources[hash_string(s)] : link_targets[hash_string(s)]; np != NULL; np = np -> next) if (strcmp(s, np->name) == 0) return np; /* found */ return NULL; /* not found */ } static struct nlist * install_link(char *name, Hps_link *defn, int type) { struct nlist *np; unsigned hashval; np = (struct nlist *)malloc(sizeof(*np)); if (np == NULL || (np->name = dup_str(name)) == NULL) return NULL; hashval = hash_string(name); np->next = type ? link_sources[hashval] : link_targets[hashval]; (type ? link_sources : link_targets)[hashval] = np; if ((np->defn = link_dup(defn)) == NULL) return NULL; return np; } #ifndef KPATHSEA char * dup_str(char *w) /* make a duplicate of s */ { char *p; p = (char *)malloc(strlen(w)+1); if (p != NULL) strcpy(p,w); return p; } #endif static Hps_link * link_dup(Hps_link *s) /* make a duplicate link */ { Hps_link *p; p = (Hps_link *) malloc(sizeof(*p)); if (p != NULL) p = s; return p; } static double dvi_to_hps_conv(int i, int dir) { double hps_coor; /* Convert dvi integers into proper hps coordinates Take into account magnification and resolution that dvi file was produced at */ hps_coor = dir ? (((i * 72.0) / vactualdpi) +MARGIN) : (PAGESIZE - ((i * 72.0) / (vactualdpi)) - MARGIN ); return(hps_coor); } static int vert_loc(int i) { int return_value; return_value = (int) (i + (PAGESIZE / 4) + FUDGE); if ( return_value > PAGESIZE) { return((int)PAGESIZE); } else if (return_value < (PAGESIZE / 4.0)) { return((PAGESIZE / 4)); } else return(return_value); } static Hps_link * dest_link(char *s) { /* Assume for now that only one entry with same NAME, i.e. Should be true for all legitimate papers. Also, assume prepending of # for names. */ struct nlist *np; s++; /* get rid of hashmark */ for(np = link_targets[hash_string(s)]; np != NULL; np = np -> next) { if ( href_name_match(s, np->name)) { return (np->defn); /* found */ } } error("Oh no, link not found in target hashtable"); error(s); error("!"); return NULL; /* not found */ } static int count_targets(void) { int count=0; int i; struct nlist *np; for (i = 0; i < HASHSIZE; i++) for(np = link_targets[i]; np != NULL; np = np -> next) count++; return count; } static void do_targets(void) { struct nlist *np; int i; Hps_link *dest; for (i = 0; i < HASHSIZE; i++) for(np = link_sources[i]; np != NULL; np = np -> next) { if (np->name[0] == '#') { dest = dest_link(np->name); np->defn->page = dest->srcpg; np->defn->vert_dest = vert_loc((int) dest->rect.lly); } } } static void do_target_dict(void) { struct nlist *np; int i; (void) fprintf(bitfile, "HPSdict begin\n"); fprintf(bitfile, "/TargetAnchors\n"); fprintf(bitfile, "%i dict dup begin\n",count_targets()); for (i = 0; i < HASHSIZE; i++) for(np = link_targets[i]; np != NULL; np = np -> next) fprintf(bitfile, "(%s) [%i [%.0f %.0f %.0f %.0f] %i] def\n", np->defn->title, np->defn->srcpg, np->defn->rect.llx, np->defn->rect.lly, np->defn->rect.urx, np->defn->rect.ury, vert_loc((int) np->defn->rect.lly)); fprintf(bitfile,"end targetdump-hook def end\n"); } static int href_name_match(char *h, char *n) { int count = 0; int name_length = strlen(n); while((*h == *n) & (*h != '\0') ) { count++; h++; n++; } if (name_length == count) { return 1; } else { /* printf(" count: %i \n", count); */ return 0; } } static void stamp_hps(Hps_link *pl) { char *tmpbuf; unsigned tmpsize; if (pl == NULL) { error("stamp_hps: null pl pointer, oh no!"); return; } if (pl->title == NULL) { error("stamp_hps: null pl->title pointer, oh no!"); return; } tmpsize = strlen(pl->title) + 500; tmpbuf = xmalloc(tmpsize); /* print out the proper pdfm with local page info only * target info will be in the target dictionary */ snprintf(tmpbuf, tmpsize, " (%s) [[%.0f %.0f %.0f %.0f] [%i %i %i [%i %i]] [%.0f %.0f %.0f]] pdfm ", pl->title, pl->rect.llx, pl->rect.lly, pl->rect.urx, pl->rect.ury, pl->border[0], pl->border[1], pl->border[2], pl->border[3],pl->border[4], pl->color[0], pl->color[1], pl->color[2]); cmdout(tmpbuf); free(tmpbuf); } /* For external URL's, we just pass them through as a string. The hyperps * interpreter can then do what is wants with them. */ static void stamp_external(char *s, Hps_link *pl) { char *tmpbuf; unsigned tmpsize; if (pl == NULL) { error("stamp_external: null pl pointer, oh no!"); return; } if (s == NULL) { error("stamp_external: null s pointer, oh no!"); return; } tmpsize = strlen(s) + 500; tmpbuf = xmalloc(tmpsize); /* print out the proper pdfm with local page info only * target info will be in the target dictionary */ snprintf(tmpbuf, tmpsize, " [[%.0f %.0f %.0f %.0f] [%i %i %i [%i %i]] [%.0f %.0f %.0f]] (%s) pdfm ", pl->rect.llx, pl->rect.lly, pl->rect.urx, pl->rect.ury, pl->border[0], pl->border[1], pl->border[2], pl->border[3],pl->border[4], pl->color[0], pl->color[1], pl->color[2], s); cmdout(tmpbuf); free(tmpbuf); } void finish_hps(void) { fclose(bitfile); set_bitfile("head.tmp",1); do_targets(); do_target_dict(); fprintf(bitfile, "TeXDict begin\n"); fprintf(bitfile, "%%%%EndSetup\n"); fclose(bitfile); open_output(); noprocset = 1; removecomments = 0; copyfile("head.tmp"); copyfile("body.tmp"); if (dvips_debug_flag == 0 && debug_flag == 0) { unlink("head.tmp"); unlink("body.tmp"); } } void set_bitfile(const char *s, int mode) { if ((bitfile=fopen(s, mode ? FOPEN_ABIN_MODE : FOPEN_WBIN_MODE))==NULL) { error(s); error("!couldn't open file"); } linepos = 0; } void vertical_in_hps(void) { Rect_list *rl; /*printf("in vertical_in_hps"); */ if (current_type == NAME) return; /* Handle this case later */ if (!current_rect_list) { current_rect_list = (Rect_list *)malloc(sizeof(Rect_list)); current_rect_list->next = NULL; } else { rl = (Rect_list *)malloc(sizeof(Rect_list)); rl->next = current_rect_list; current_rect_list = rl; } current_rect_list->rect.llx = dvi_to_hps_conv(hh, HORIZONTAL); current_rect_list->rect.lly = dvi_to_hps_conv(vv, VERTICAL)-FUDGE; current_rect_list->rect.urx = dvi_to_hps_conv(hhmem, HORIZONTAL); current_rect_list->rect.ury = dvi_to_hps_conv(vvmem, VERTICAL)-FUDGE; if (POPPED) start_new_box(); } static void print_rect_list(void) { Rect_list *rl, *rln; for(rl = current_rect_list; rl != NULL; rl = rln) { /* printf("Rectangle is %.0f, %.0f, %.0f, %.0f\n", rl->rect.llx, rl->rect.lly, rl->rect.urx, rl->rect.ury); */ rln = rl -> next; free(rl); } } void end_current_box(void) { Hps_link *nl; POPPED = TRUE; HREF_COUNT--; if (current_type == HREF && current_name[0] != '#') { if ((nl = lookup_link(current_name, current_type)->defn)) { nl->rect.urx = dvi_to_hps_conv(hhmem, HORIZONTAL); nl->rect.ury = dvi_to_hps_conv(vvmem, VERTICAL)-FUDGE+12.0; stamp_external(current_name,nl); /* Broken lines ? */ } else { error("!Null lookup"); } } else { if ((nl = lookup_link(current_name, current_type)->defn)) { nl->rect.urx = dvi_to_hps_conv(hhmem, HORIZONTAL); nl->rect.ury = dvi_to_hps_conv(vvmem, VERTICAL)-FUDGE+12.0; if (current_type) { stamp_hps(nl); /* Put link info right where special is */ } } else { error("!Null lookup"); } /* printf("Ended box %.0f, %.0f, %.0f, %.0f\n", nl->rect.llx, \ nl->rect.lly,nl->rect.urx, nl->rect.ury); */ } } void start_new_box(void) { POPPED = FALSE; do_link(current_name, current_type); } #else int no_hypertex; /* prevent an empty file */ #endif