/* * Code for allowing fonts to be used in PostScript files given via * `psfile=...'. * * Modified to check for font usage (DocumentFonts and DocumentNeededResources) * in all included postscript files. This information is added to the * global list of postscript fonts used (ps_fonts_used). Each font is * also looked up (from psfonts.map) and if a file needs to be downloaded * for that font, it is added (with add_header). * 11/1996 Aaron Sawdey */ #include "dvips.h" /* The copyright notice in that file is included too! */ #ifdef KPATHSEA #include #else #include #if !defined(SYSV) && !defined(WIN32) extern char *strtok() ; /* some systems don't have this in strings.h */ #endif #define ISXGIGIT isxdigit #endif #ifdef VMS #define getname vms_getname #endif #ifndef STDC_HEADERS double atof(); #endif /* * These are the external routines we call. */ #include "protos.h" /* * These are the external variables we access. */ extern struct header_list *ps_fonts_used; extern char *infont ; extern fontdesctype *curfnt ; extern fontdesctype *fonthead ; extern integer fontmem ; extern fontdesctype *fonthd[MAXFONTHD] ; extern int nextfonthd ; extern char *nextstring ; extern char xdig[256] ; extern real conv ; extern integer pagecost ; extern int actualdpi ; extern double mag ; extern Boolean includesfonts ; #ifndef KPATHSEA extern char *figpath ; #endif extern int to_close ; #ifdef DEBUG extern integer debug_flag; #endif /* DEBUG */ /* * Create a font descriptor for a font included in a psfile. There will be * no fontmaptype node for the resulting font descriptor until this font is * encountered by fontdef() (if that ever happens). */ fontdesctype * ifontdef P5C(char *, name, char *, area, int, scsize, int, dssize, char *, scname) { fontdesctype *fp; fp = newfontdesc((integer)0, scsize, dssize, name, area); fp->scalename = scname; fp->next = fonthead ; fonthead = fp ; return fp; } /* * When a font appears in an included psfile for the first time, this routine * links it into the fonthd[] array. */ void setfamily P1C(fontdesctype *, f) { int i ; fontmem -= DICTITEMCOST; for (i=0; iname, fonthd[i]->name)==0 && strcmp(f->area, fonthd[i]->area)==0) { f->nextsize = fonthd[i]; fonthd[i] = f; return; } if (nextfonthd==MAXFONTHD) error("! Too many fonts in included psfiles") ; fontmem -= NAMECOST + strlen(f->name) + strlen(f->area) ; fonthd[nextfonthd++] = f ; f->nextsize = NULL ; } /* * Convert file name s to a pair of new strings in the string pool. * The first string is the original value of nextstring; the second * string is the return value. */ char* getname P1C(char *, s) { char *a, *p, sav; a = NULL; for (p=s; *p!=0; p++) if (*p=='/') a = p+1 ; if (a==NULL) *nextstring++ = 0 ; else { sav = *a ; *a = 0 ; (void) newstring(s) ; *a = sav ; s = a ; } return newstring(s); } /* * Mark character usage in *f based on the hexadecimal bitmap found in * string s. A two-digit offset separated by a colon gives the initial * character code. We have no way of knowing how many times each character * is used or how many strings get created when showing the characters so * we just estimate two usages per character and one string per pair of * usages. */ void includechars P2C(fontdesctype *, f, char *, s) { int b, c, d ; int l = strlen(s) ; if (l>0 && s[l-1]=='\n') s[--l] = 0 ; if (!ISXDIGIT(s[0]) || !ISXDIGIT(s[1]) || s[2]!=':' || strspn(s+3,"0123456789ABCDEFabcdef") < l-3) { fprintf(stderr, "%s\n", s) ; error("Bad syntax in included font usage table") ; return ; } c = (xdig[(int)(s[0])] << 4) + xdig[(int)(s[1])] ; s += 2 ; while (*++s) { d = xdig[(int)*s] ; for (b=8; b!=0; b>>=1) { if ((d&b)!=0) { pagecost ++ ; (void) prescanchar(&f->chardesc[c]) ; } if (++c==256) return ; } } } /* * String p should be start after the ":" in a font declaration of the form %*FONT: <2-hex-digits>: * where the sizes are floating-point numbers in units of PostScript points * (TeX's "bp"). We update the data structures for the included font, * charge fontmem for the VM used, and add to delchar if necessary. * Note that the scaled size and the design size are multiplied by mag/1000. * This is needed for the design size to undo the similar factor in conv since * design sizes are not supposed to be affected by magnification. Applying * the magnification factor to the scaled size selects magnified fonts as is * appropriate in the normal case where the included PostScript is scaled by * mag/1000. The definition of `fshow' in finclude.lpro unscales by `DVImag' * to account for this. We cannot change the font scaled size to account for * options like `hscale=' because then the definition of `fshow' would have * to change. */ void scan1fontcomment P1C(char *, p) { char *q, *name, *area; char *scname; /* location in buffer where we got scsize */ integer scsize, dssize; fontdesctype *fptr; real DVIperBP; DVIperBP = actualdpi/(72.0*conv) * (mag/1000.0); p = strtok(p, " "); if (p==NULL) return; area = nextstring ; /* tentatively in the string pool */ name = getname(p); q = strtok((char *)0, " "); if (p==NULL || (scsize=(integer)(atof(q)*DVIperBP))==0) { fprintf(stderr, "%s\n",p); error("No scaled size for included font"); nextstring = area ; /* remove from string pool */ return; } scname = q; q = strtok((char *)0, " "); if (p==NULL || (dssize=(integer)(atof(q)*DVIperBP))==0) { fprintf(stderr, "%s\n",p); error("No design size for included font"); nextstring = area ; return; } q = strtok((char *)0, " "); fptr = matchfont(name, area, scsize, scname); if (!fptr) { fptr = ifontdef(name, area, scsize, dssize, newstring(scname)); (void) preselectfont(fptr); setfamily(fptr); } else { nextstring = area; /* remove from string pool */ (void) preselectfont(fptr); if (fptr->scalename==NULL) { fptr->scalename=newstring(scname); setfamily(fptr); } } includesfonts = 1; fptr->psflag |= THISPAGE; includechars(fptr, q); } /* * Parse the arguments to a "%%VMusage" comment. The Adobe Type 1 Font Format * book specifies two arguments. This routine will accept one or two arguments; * if there are two arguments we take the maximum. */ integer scanvm P1C(char *, p) { char* q; integer vm, vmmax; q = strtok(p, " "); if (q==NULL) { error("Missing data in VMusage comment"); return 0; } vmmax = atol(q); q = strtok((char *)0, " "); if (q!=NULL && (vm=atol(q))>vmmax) vmmax = vm; return vmmax; } /* * Scan a list of font names. * Each name is added to the list ps_fonts_used, and if it has * an associated header file (from psfonts.map), the header file * is added with add_header. */ void scan_fontnames P2C(char *, str, char *, psfile) { char *p,*pe; struct resfont *re; int i; /* Turn all newlines, CRs, and tabs into spaces. */ p = str; while(*p) { if(*p == '\r' || *p == '\n' || *p == '\t') *p = ' '; p++; } /* Remove trailing spaces. */ p = str+strlen(str)-1; while(p > str && *p == ' ') { *p = '\0'; p--; } p = str; while(*p == ' ') p++; /* skip leading whitespace */ while(p && *p) { pe = strchr(p,' '); if(pe != NULL) *pe = '\0'; i = add_name(p,&ps_fonts_used); if(i) { #ifdef DEBUG if (dd(D_FONTS)) (void)fprintf(stderr, "Adding font '%s' from included postscript file '%s'.\n", p,psfile); #endif /* DEBUG */ re = findPSname(p); if(re != NULL) { if (re->Fontfile) { add_header(re->Fontfile) ; } else if (re->downloadheader) { /* this code borrowed from residentfont() in resident.c */ char *cp = re->downloadheader ; char *q ; infont = re->PSname ; while (1) { q = cp ; while (*cp && *cp != ' ') cp++ ; if (*cp) { *cp = 0 ; add_header(q) ; *cp++ = ' ' ; } else { add_header(q) ; break ; } infont = 0 ; } } infont = 0 ; } else { char eb[1000]; sprintf(eb,"Font %s used in file %s is not in the mapping file.", p,psfile); error(eb); } } p = pe; if(p != NULL) { p++; while(*p == ' ') p++; /* skip leading whitespace */ } } return; } /* * fc_state == 0: normal state, looking for interesting comments * fc_state == 1: looking for %%+ following %%DocumentFonts * fc_state == 2: looking for "%%+ font" following %%DocumentNeededResources */ static int fc_state = 0; /* * Do we need to check for information at the end of the postscript file? */ static int check_atend = 0; void scanfontusage P2C(char *, p, char *, psfile) { if (strncmp(p, "%%DocumentFonts: ",17) == 0) { p += 17 ; while (*p && *p <= ' ') p++ ; if(!strncmp(p,"(atend)",7)) { check_atend = 1; } else { scan_fontnames(p,psfile); fc_state = 1; } } else if (strncmp(p, "%%DocumentNeededFonts: ",23)==0) { p += 23 ; while (*p && *p <= ' ') p++ ; if(!strncmp(p,"(atend)",7)) { check_atend = 1; } else { scan_fontnames(p,psfile); fc_state = 1; } } else if (fc_state == 1 && strncmp(p,"%%+",3) == 0) { scan_fontnames(p+3,psfile); fc_state = 1; } else if (strncmp(p, "%%DocumentNeededResources: ",27) == 0) { p += 27 ; while (*p && *p <= ' ') p++ ; if(!strncmp(p,"(atend)",7)) { check_atend = 1; } else { if(!strncmp(p,"font ",5)) scan_fontnames(p+5,psfile); fc_state = 2; } } else if (fc_state == 2 && strncmp(p,"%%+",3) == 0) { p += 3 ; while (*p && *p <= ' ') p++ ; if(!strncmp(p,"font ",5)) scan_fontnames(p+5,psfile); fc_state = 2; } else { fc_state = 0; } return; } /* * Scan an initial sequence of comment lines looking for font and memory * usage specifications. This does not handle the "atend" construction. */ void scanfontcomments P1C(char *, filename) { char p[500]; char *r; FILE *f; integer truecost = pagecost ; Boolean trueknown = 0 ; fontdesctype *oldcf = curfnt; #ifdef DEBUG if (dd(D_FONTS)) (void)fprintf(stderr, "Checking for fonts in '%s'\n",filename); #endif /* DEBUG */ if (*filename == '`') { /* * Allow scanning of ` commands. Better return same results both times. */ f = popen(filename+1, FOPEN_RBIN_MODE) ; to_close = USE_PCLOSE ; } else { f = search(figpath, filename, READ) ; } if (f) { fc_state = 0; check_atend = 0; while (fgets(p,500,f) && p[0]=='%' && (p[1]=='!' || p[1]=='%' || p[1]=='*')) { if (strncmp(p, "%*Font:", 7) == 0) { scan1fontcomment(p+7); } else if (strncmp(p, "%%VMusage:", 9) == 0) { truecost += scanvm(p+10) ; trueknown = 1 ; } scanfontusage(p,filename); } if (trueknown) pagecost = truecost ; if(check_atend) { #ifdef DEBUG if (dd(D_FONTS)) (void)fprintf(stderr, "Checking for (atend) fonts in '%s'\n",filename); #endif /* DEBUG */ fc_state = 0; fseek(f,-4096,2); /* seek to 4096 bytes before EOF. */ fgets(p,500,f); /* throw away a partial line. */ /* find %%Trailer */ while((r=fgets(p,500,f)) && strncmp(p,"%%Trailer",9)) ; /* look for specs that were deferred to the trailer. */ if(r != NULL) { while(fgets(p,500,f)) { if(p[0]=='%' && p[1]=='%') scanfontusage(p,filename); } } #ifdef DEBUG else { /* r == NULL */ if (dd(D_FONTS)) (void)fprintf(stderr, "Did not find %%%%Trailer in included file '%s'.\n", filename); } #endif /* DEBUG */ } close_file(f) ; } curfnt = oldcf; } /* * Is string s less than 30 characters long with no special characters * that are not allowed in PostScript commands. */ Boolean okascmd P1C(char *, ss) { register int c = 0 ; register char *s = ss ; while (*s) if (*s<' ' || *s>126 || ++c==30) return(0) ; return(strcspn(ss,"()<>[]{}%/") == c) ; } /* * Output font area and font name strings as a literal string */ void nameout P2C(char *, area, char *, name) { char buf[30] ; char *s ; if (*area==0 && okascmd(name)) { (void)sprintf(buf, "/%s", name) ; cmdout(name); } else { for (s=area; *s; s++) scout(*s) ; for (s=name; *s; s++) scout(*s) ; stringend(); cmdout("cvn") ; } } /* * Output commands for defining a table of PostScript font identifiers for * fonts used in included psfiles in the current section. */ void fonttableout P1H(void) { int i, k; fontdesctype *f; for (i=0; inextsize) if (f->psflag==EXISTS) break; if (f!=NULL) { nameout(f->area, f->name); k = 0; do { if (f->psflag==EXISTS) { cmdout(f->scalename); lfontout((int)f->psname); } f = f->nextsize; k++; } while (f!=NULL); numout((integer)k); cmdout("fstore"); } } }