ftp.nice.ch/pub/next/unix/fonts/installfont.N.bs.tar.gz#/unadobe.c

This is unadobe.c in view mode; [Download] [Up]

/*
** U N A D O B E
**
** Extract font PostScript code from a Macintosh font file resource fork
** (a procedure somewhat akin to extracting water out of rocks), basically
** the same function as the oldie unAdobe program on the Macintosh.
** In addition to this it does the job of the unfont (or pfbtopfa)
** program, that is, reads PC pfb format packed font files.
**
** Information used in this program was acquired from:
** - Inside Macintosh I p.128: Format of a Resource File
** - Adobe TechNote #5040: Supporting Downloadable PostScript Language Fonts
**
** BUGS: this code is specifically written for Motorola-endian machines (like
** Macintosh resource files also are); there is no provision for reading
** MacBinary-encoded Macintosh files (solution: get rid of 128 first bytes,
** for example with "dd", then feed file on stdin).
**
** A million thanks to Rob Elliott <relliott@b11.b11.ingr.com> for pointing
** me in the correct direction on where to find the unpacking information and
** Mark Adler <madler@cco.caltech.edu> for telling me about the NeXT method
** of making the Macintosh file system resource fork available as .#rsrc#
**
** Written by Otto J. Makela <otto@jyu.fi>
** Distributed under the GNU Public Licence -- see file COPYING for details
*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>

int verbose=0;

main(int argc, char *argv[])	{
  int loop,i,files=0,type=0;
  FILE *fi,*fo;

  /* Loop thru all files given on command line */
 loop:
  for(loop=1; loop<argc; loop++)	{
    /* Possible argument */
    if(*argv[loop]=='-')
      if(argv[loop][1])	{
	while(i=*++argv[loop])
	  switch(i)	{
	  case 'm': type=1; break;
	  case 'p': type=2; break;
	  case 'v': verbose++; break;
	  default: goto usage;
	  }
	continue;
      } else	{
	/* Bare dashes mean stdin/out */
	fi=stdin;
	fo=stdout;
      }
    else if(!(fi=fopen(argv[loop],"rb")))	{
      fprintf(stderr,"%s: can't open file \"%s\" for input\n",
	      *argv,argv[loop]);
      exit(1);
    } else	{
      char *p,filename[BUFSIZ];

      strcpy(filename,argv[loop]);
      /* On a NeXT you see Macintosh resource forks as .#rsrc# files */
      if(p=strstr(filename,".#rsrc#"))	{
	*p='\0';
	if(!type) type=1;
      } else if(p=strstr(filename,".pfb"))	{
	p[3]='a';
	if(!type) type=2;
      } else
	strcat(filename,".ps");

      if(!(fo=fopen(filename,"wt")))	{
	fprintf(stderr,"%s: can't open file \"%s\" for output\n",
		*argv,filename);
	exit(1);
      }
    }

    if(type==1)	{
      if(verbose) fprintf(stderr,"Macintosh resource file %s:\n",argv[loop]);
      if(macfont(fi,fo))	{
	fprintf(stderr,"%s: EOF or data format errors reading file \"%s\"\n",
		*argv,argv[loop]);
	exit(1);
      }
    } else if(type==2)	{
      if(verbose) fprintf(stderr,"PC pfb file %s:\n",argv[loop]);
      if(pcfont(fi,fo))	{
	fprintf(stderr,"%s: EOF or data format errors reading file \"%s\"\n",
		*argv,argv[loop]);
	exit(1);
      }
    } else	{
      fprintf(stderr,"%s: could not determine file \"%s\" type\n",
	      *argv,argv[loop]);
    usage:
      fprintf(stderr,"usage: %s [-mpv] fontfile...\n"
	      "where the font file type is guessed from file extension or:\n"
	      "-m\tSet Macintosh resource file format for next file\n"
	      "-p\tSet PC pfb file format for next file\n"
	      "-v\tIncrease verboseness (repeat for more)\n",*argv);
      exit(1);
    }

    if(fi!=stdin) fclose(fi);
    if(fo!=stdout) fclose(fo);
    type=0; files++;
  }

  /* If no arguments given, read stdin and output to stdout */
  if(!files)	{
    argv[1]="-";
    argc=2;
    goto loop;
  }

  exit(0);
}


/*
** Read a Macintosh resource fork file
*/
macfont(FILE *fi,FILE *fo)	{
  int i;

  /* The following data represents a Macintosh resource fork structure */
  struct	{
    unsigned long int data_offset;
    unsigned long int map_offset;
    unsigned long int data_length;
    unsigned long int map_length;
  } resourceheader;
  struct	{
    unsigned char dummy0[16];
    unsigned long int dummy1;
    unsigned short int dummy2;
    unsigned short int resourcefile_attribute;
    unsigned short int typelist_offset;
    unsigned short int namelist_offset;
  } resourcemap;
  unsigned short type_count;
  struct	{
    char resource_type[4];
    unsigned short int reference_count;
    unsigned short int referencelist_offset;
  } typelist;
  struct	{
    unsigned short int resource_id;
    unsigned short int referencelist_offset;
    union	{
      unsigned char resource_attribute;
      unsigned long data_offset;
    } reference;
    unsigned long int dummy3;
  } referencelist;


  /* First read main resource file header */
  if(fread(&resourceheader,1,sizeof(resourceheader),fi)!=
     sizeof(resourceheader))
    return(1);
  
  if(verbose) fprintf(stderr,
		      "resource data\toffset = %lu\n"
		      "\t\tlength = %lu\n"
		      "resource map\toffset = %lu\n"
		      "\t\tlength = %lu\n",
		      resourceheader.data_offset,resourceheader.data_length,
		      resourceheader.map_offset,resourceheader.map_length);

  fseek(fi,resourceheader.map_offset,0);
  if(fread(&resourcemap,1,sizeof(resourcemap),fi)!=sizeof(resourcemap))
    return(1);

  if(verbose) fprintf(stderr,
		      "resourcefile attribute = %04x\n"
		      "type list offset = %lu (absolute %lu)\n"
		      "name list offset = %lu (absolute %lu)\n",
		      resourcemap.resourcefile_attribute,
		      resourcemap.typelist_offset,
		      resourceheader.map_offset+resourcemap.typelist_offset,
		      resourcemap.namelist_offset,
		      resourceheader.map_offset+resourcemap.namelist_offset);

  fseek(fi,resourceheader.map_offset+resourcemap.typelist_offset,0);
  if(fread(&type_count,1,sizeof(type_count),fi)!=sizeof(type_count))
    return(1);

  /* Search for a POST resource containing the PostScript code */
  for(i=0; i<=type_count; i++)	{
    if(fread(&typelist,1,sizeof(typelist),fi)!=sizeof(typelist))
      return(1);
    if(verbose)
      fprintf(stderr,"resource_type=\"%.4s\"\n",typelist.resource_type);
    if(!strncmp(typelist.resource_type,"POST",4)) break;
  }

  /* No POST resource was found */
  if(i>type_count) return(1);

  if(verbose) fprintf(stderr,
		      "%.4s resources = %u\n"
		      "%.4s reference list offset = %lu (absolute %lu)\n",
		      typelist.resource_type,typelist.reference_count+1,
		      typelist.resource_type,typelist.referencelist_offset,
		      resourceheader.map_offset+resourcemap.typelist_offset+
		      typelist.referencelist_offset);

  /* Loop thru the resources in numerical order */
  for(i=0; i<=typelist.reference_count; i++)	{
    fseek(fi,resourceheader.map_offset+resourcemap.typelist_offset+
	  typelist.referencelist_offset+i*sizeof(referencelist),0);
    if(fread(&referencelist,1,sizeof(referencelist),fi)!=
       sizeof(referencelist))
      return(1);

    if(verbose) fprintf(stderr,
			"resource_id = %u, attribute %02x\n",
			referencelist.resource_id,
			referencelist.reference.resource_attribute);

    referencelist.reference.resource_attribute=0;

    if(verbose) fprintf(stderr,
			"data offset = %lu (absolute %lu)\n",
			referencelist.reference.data_offset,
			resourceheader.data_offset+
			referencelist.reference.data_offset);

    if(read_resource(fi,resourceheader.data_offset+
		     referencelist.reference.data_offset,fo))
      return(1);
  }

  return(0);
}


/*
** Read resource data from given offset of file, decode Adobe coding
*/
read_resource(FILE *fi,unsigned long offset,FILE *fo)	{
  unsigned long size;
  int c;

  fseek(fi,offset,0);
  if(fread(&size,1,sizeof(size),fi)!=sizeof(size)) return(1);
  if(!size) return(0);
  if(verbose)
    fprintf(stderr,"\tlength = %lu\n",size);

  c=fgetc(fi);

  /* This bit is a wart.  I really don't know why it's needed! */
  /* Unfortunately, the results will be incorrect without it */
  fgetc(fi); size--;

  switch(c)	{
    /* Comment resource, just ignore */
  case 0:
    break;
    /* ASCII text, copy directly to output */
  case 1:
    while(--size)
      if((c=fgetc(fi))==-1)
	return(1);
      else if(c=='\r')
	fputc('\n',fo);
      else
	fputc(c,fo);
    break;
    /* Binary data, convert to hexadecimal */
  case 2:
    for(c=1; --size; c++)
      fprintf(fo,(c%32)?"%02X":"%02X\n",fgetc(fi));
    fputc('\n',fo);
    break;
    /* End of file marker -- we actually should send ^D at this point? */
  case 3:
    break;
    /* Font program is in data fork -- what the hell do we do to this? */
    /* (we'll assume calling script has handled this bit) */
  case 4:
    fprintf(stderr,"Font program is in data fork\n");
    /* Flow thru: Hit EOF -- a error */
  case -1:
  default:
    return(1);
    /* End-of-font marker resource */
  case 5:
    break;
  }
  return(0);
}


/*
** Read a PC pfb format file
*/
pcfont(FILE *fi,FILE *fo)	{
  int c;
  unsigned long size;

  while((c=fgetc(fi))==128)	{
    c=fgetc(fi);
    size=fgetc(fi);
    size+=fgetc(fi)<<(8);
    size+=fgetc(fi)<<(8+8);
    size+=fgetc(fi)<<(8+8+8);

    if(verbose) fprintf(stderr,"segment type = %u, length = %lu\n",c,size);
    switch(c)	{
      /* ASCII text, copy directly to output */
    case 1:
      while(size--)
	if((c=fgetc(fi))==-1)
	  return(1);
	else if(c=='\r')
	  fputc('\n',fo);
	else
	  fputc(c,fo);
      break;
      /* Binary data, convert to hexadecimal */
    case 2:
      for(c=1; size--; c++)
	fprintf(fo,(c%32)?"%02X":"%02X\n",fgetc(fi));
      fputc('\n',fo);
      break;
      /* End of file marker or real EOF */
    case 3:
      return(0);
      /* Hit EOF or anything else -- a error */
    default:
      return(1);
    }
  }

  if(verbose && c!=-1) fprintf(stderr,"read illegal byte = 0x%02x\n",c);
  return(c!=-1);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.