/* * 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 /* * The external declarations: */ #include "protos.h" /* * 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). */ static fontdesctype * ifontdef(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. */ static void setfamily(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. */ static char* getname(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. */ static void includechars(fontdesctype *f, char *s) { int b, c, d; int l = strlen(s); while (l > 0 && (s[l-1] == '\n' || s[l-1] == '\r')) s[--l] = 0; if (!ISXDIGIT(s[0]) || !ISXDIGIT(s[1]) || s[2]!=':' || strspn(s+3,"0123456789ABCDEFabcdef") < l-3) { fprintf_str(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. */ static void scan1fontcomment(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_str(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_str(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. */ static integer scanvm(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. */ static void scan_fontnames(char *str, const char *psfile) { char *p,*pe; struct resfont *re; /* 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'; add_name(p,&ps_fonts_used); if (1) { #ifdef DEBUG if (dd(D_FONTS)) fprintf_str(stderr, "Adding font '%s' from included postscript file '%s'.\n", p,psfile); #endif /* DEBUG */ re = findPSname(p); if(re != NULL) { if (re->sent != 2) { 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; } re->sent = 2; if (unused_top_of_psnames < DOWNLOADEDPSSIZE) { downloadedpsnames[unused_top_of_psnames] = xstrdup (re->PSname); unused_top_of_psnames++; } } else { char eb[1000]; snprintf(eb, sizeof(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; static void scanfontusage(char *p, const 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(const char *filename) { char p[500]; char *r; FILE *f; integer truecost = pagecost; Boolean trueknown = 0; fontdesctype *oldcf = curfnt; #ifdef DEBUG if (dd(D_FONTS)) fprintf_str(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, "r"); SET_BINARY(fileno(f)); to_close = USE_PCLOSE; } else { f = search(figpath, filename, READ); } if (f) { SET_BINARY(fileno(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)) fprintf_str(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)) fprintf_str(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. */ static Boolean okascmd(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 */ static void nameout(char *area, char *name) { char buf[500]; char *s; if (*area==0 && okascmd(name)) { snprintf(buf, sizeof(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(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); k++; } f = f->nextsize; } while (f!=NULL); numout((integer)k); cmdout("fstore"); } } }