/*-------------------------------------------------------------------------- ----- File: t1subset.c ----- Author: Rainer Menzner (Rainer.Menzner@web.de) ----- Date: 2002-12-08 ----- Description: This file is part of the t1-library. It contains functions for subsetting type 1 fonts. ----- Copyright: t1lib is copyrighted (c) Rainer Menzner, 1996-2002. As of version 0.5, t1lib is distributed under the GNU General Public Library Lincense. The conditions can be found in the files LICENSE and LGPL, which should reside in the toplevel directory of the distribution. Please note that there are parts of t1lib that are subject to other licenses: The parseAFM-package is copyrighted by Adobe Systems Inc. The type1 rasterizer is copyrighted by IBM and the X11-consortium. ----- Warranties: Of course, there's NO WARRANTY OF ANY KIND :-) ----- Credits: I want to thank IBM and the X11-consortium for making their rasterizer freely available. Also thanks to Piet Tutelaers for his ps2pk, from which I took the rasterizer sources in a format independent from X11. Thanks to all people who make free software living! --------------------------------------------------------------------------*/ #define T1SUBSET_C #include #include #include #include #if defined(_MSC_VER) # include # include # include #else # include #endif #include #include #include #include "../type1/ffilest.h" #include "../type1/types.h" #include "parseAFM.h" #include "../type1/objects.h" #include "../type1/spaces.h" #include "../type1/util.h" #include "../type1/fontfcn.h" #include "../type1/regions.h" #include "t1types.h" #include "t1extern.h" #include "t1finfo.h" #include "t1misc.h" #include "t1base.h" #include "t1delete.h" #include "t1subset.h" /* Segment header for pfb-files (reminder): Segment-header: Byte 1: always 128 (0x80) Byte 2: 1 = ASCII text 2 = Binary text 3 = End of file indicator (EOF) Byte 3: least significant byte \ Byte 4: \ Length of Byte 5: / data segment in bytes Byte 6: most significant byte / */ #define SEGMENT_ASCII 1 #define SEGMENT_BINARY 2 #define SEGMENT_EOF 3 static char *charstringP; static int charstringL; /* We define two lookup tables to make a fast conversion from binary bytes to ASCII-hex bytes. */ static unsigned char highHexByte[256]={ 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43, 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45, 0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46 }; static unsigned char lowHexByte[256]={ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46 }; /* parameters for eexec encryption */ static unsigned short int eexec_r; static unsigned short int eexec_c1=52845; static unsigned short int eexec_c2=22719; /* Eexec-encrption according to the Adobe Black Book */ static unsigned char EexecEncrypt( unsigned char plain) { unsigned char cipher; cipher = (plain ^ (eexec_r>>8)); eexec_r = (cipher + eexec_r) * eexec_c1 + eexec_c2; return( cipher); } static int locateCharString( int FontID, char *charname) { int namelen; int dictlen; int i; psdict *CharStringsP; namelen=strlen( charname); CharStringsP=pFontBase->pFontArray[FontID].pType1Data->CharStringsP; dictlen=CharStringsP[0].key.len; for ( i=1; i<=dictlen; i++) { if (namelen==CharStringsP[i].key.len) { /* This could be the charstring in question */ if (strncmp( charname, CharStringsP[i].key.data.nameP, namelen)==0) { /* It is the the desired charstring */ charstringP=CharStringsP[i].value.data.valueP; charstringL=CharStringsP[i].value.len; return( i); } } } /* charstring not found ??? */ return( 0); } static int make_pfb_segment_header( char *ptr, int type, unsigned long size) { ptr[0]=(char) 0x80; ptr[1]=type; if (type==3) return( 2); ptr[2]=(char)(size & 0xFF); ptr[3]=(char)((size>>8) & 0xFF); ptr[4]=(char)((size>>16) & 0xFF); ptr[5]=(char)((size>>24) & 0xFF); return( 6); } /* A function for reading a Type 1 font file. eexec-decryption is done on the fly. */ char *T1_SubsetFont( int FontID, char *mask, unsigned int flags, int linewidth, unsigned long maxblocksize, unsigned long *bufsize) { FILE *ifp; static char *filebuf=NULL; unsigned long filesize=0; int filesegs=0; char *outbuf=NULL; unsigned long outsize=0; char *encryptbuf=NULL; unsigned long encryptsize=0; int encryptsegs=0; char *trailerbuf=NULL; unsigned long trailersize=0; int trailersegs=0; char linebuf[1025]; char *csdone; /* stores which charstrings already have been written */ int currstring_no=0; char *charnameP; unsigned char cipher; char rdstring[3]; char ndstring[3]; int retval=1; /* Indices. Could save a few but debugging becomes easier this way */ int i=0; int j=0; int k=0; int l=0; int m=0; int n=0; int o=0; int p=0; int notdefencoded=0; int stdenc=0; int reencode=0; int colcount=0; int tr_len=-1; int encrypt=1; /* 1=ASCII-hex, 2=Binary, 0=None (for debugging) */ int dindex=0; int nocharstrings=0; char encmask[256]; /* Mask after resolving composite characters */ T1_COMP_CHAR_INFO* cci = NULL; /* Otherwise we would get invalid accesses later */ if (T1_CheckForFontID(FontID)!=1) { T1_errno=T1ERR_INVALID_FONTID; return(NULL); } /* Check parameters */ if ( (mask==NULL) || (bufsize==NULL) || (linewidth < 8) || (linewidth > 1024) || (maxblocksize < 4) ) { T1_errno=T1ERR_INVALID_PARAMETER; return(NULL); } /* First, we check how many characters have to find their way into the subsetted file. We take possibly existing composite character definitions by the user into account. */ for ( j=0; j<256; j++) { if ( mask[j] != 0 ) { /* check whether addressed character is an internal character of the font */ if ( cci != NULL ) T1_FreeCompCharData( cci); cci = T1_GetCompCharData( FontID, j); if ( cci != NULL ) { nocharstrings += cci->numPieces; } else { ++nocharstrings; } } } /* adjust encrypting type according to flags. Default is ASCII-hex encryption because the output may be verbatim inserted into a PostScript-file. */ if (flags & T1_SUBSET_ENCRYPT_BINARY) encrypt=2; else if (flags & T1_SUBSET_ENCRYPT_NONE) encrypt=0; /* Open and initialize scanning machinery */ T1io_reset(); if ((ifp = (FILE *)T1Open(T1_GetFontFilePath( FontID), "rb"))==NULL) { T1_errno=T1ERR_FILE_OPEN_ERR; return(NULL); } if (encrypt==0) sprintf( err_warn_msg_buf, "Subsetting Font %d, flags=0x%X, non-encrypted debug output", FontID, flags); else if (encrypt==1) sprintf( err_warn_msg_buf, "Subsetting Font %d, flags=0x%X, ASCII-hex output with linelength %d", FontID, flags, linewidth); else if (encrypt==2) sprintf( err_warn_msg_buf, "Subsetting Font %d, flags=0x%X, Binary output with maximum blocksize %lu", FontID, flags, maxblocksize); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); /* Get size of file */ filesize=T1GetFileSize( ifp); sprintf( err_warn_msg_buf, "Source file %s is %lu bytes long", T1_GetFontFilePath( FontID), filesize); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); if ((filebuf=(char *)calloc( filesize, sizeof(char)))==NULL) { T1_errno=T1ERR_ALLOC_MEM; return( NULL); } while (retval>0) { /* get a line from the file. We have to read enough characters to ensure that "eexec" does not get split between two reads. Otherwise, decryption would not be started. */ retval=T1Gets(&(filebuf[i]), 1025, ifp); i+=retval; if ( (dindex==0) && (T1GetDecrypt()>0) ) { dindex=i-retval; /* from this point on we have decrypted bytes */ sprintf( err_warn_msg_buf, "Human-readable header finished (%d data bytes)", dindex); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); } /* Encoding handling */ if (strstr( &(filebuf[i-retval]), "/Encoding")!=NULL) { if (strstr( &(filebuf[i-retval]), "StandardEncoding")!=NULL) { stdenc=1; if ((flags & T1_SUBSET_FORCE_REENCODE)!=0) { /* we ignore the current line ... */ i-=retval; /* and put the encoding header into the buffer */ i+=sprintf( &(filebuf[i]), "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n" ); reencode=1; } else { T1_PrintLog( "T1_SubsetFont()", "Leaving StandardEncoding untouched", T1LOG_DEBUG); reencode=0; } } else { /* The encoding is explicitly defined in the font file. We skip the whole definition unless reencoding should be skipped. */ stdenc=0; retval=T1Gets(&(filebuf[i]), 1025, ifp); i+=retval; while (retval>0) { /* get a line and check for end of encoding definition. */ retval=T1Gets(&(filebuf[i]), 1025, ifp); if ((flags & T1_SUBSET_SKIP_REENCODE)!=0) { /* we store the encoding defined here */ i+=retval; reencode=0; } else { reencode=1; } if ( (dindex==0) && (T1GetDecrypt()>0) ) { dindex=i; /* from this point we have decrypted bytes */ } if (strstr( &(filebuf[i-retval]), "readonly def")!=NULL) { break; } } if (reencode==0) T1_PrintLog( "T1_SubsetFont()", "Preserving FontSpecific Encoding", T1LOG_DEBUG); } /* At this point, if required, the actual encoding definition follows. */ if ( reencode!=0) { k=0; /* Reset resulting encoding mask */ for ( j=0; j<256; j++) { encmask[j] = 0; } /* Resolve composite characters and their components */ for ( j=0; j<256; j++) { if (mask[j]!=0) { if ( cci != NULL) T1_FreeCompCharData( cci); cci = T1_GetCompCharData( FontID, j); if ( (cci != NULL) && (cci->numPieces > 1) ) { /* Tag all components that are required to construct the composite character j. */ for ( p=0; pnumPieces; p++) { encmask[cci->pieces[p].piece] = 1; } } /* Tag character j in encoding definition */ encmask[j] = 1; } } /* Write actually required encoding slots */ for ( j=0; j<256; j++) { if (encmask[j]!=0) { charnameP=T1_GetCharName( FontID, j); i+=sprintf( &(filebuf[i]), "dup %d /%s put\n", j, charnameP); k++; if (strcmp(charnameP, ".notdef")==0) notdefencoded=1; } } /* finish encoding definition */ i+=sprintf( &(filebuf[i]), "readonly def\n"); sprintf( err_warn_msg_buf, "Encoded %d characters", k); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); k=0; } } /* end of if (...encoding handling...) */ /* Extract the names are used for the charstring definitions. We will later need them! */ if (strstr( &(filebuf[i-retval]), "/RD")!=NULL) { sprintf( rdstring, "RD"); } if (strstr( &(filebuf[i-retval]), "/ND")!=NULL) { sprintf( ndstring, "ND"); } if (strstr( &(filebuf[i-retval]), "/-|")!=NULL) { sprintf( rdstring, "-|"); } if (strstr( &(filebuf[i-retval]), "/|-")!=NULL) { sprintf( ndstring, "|-"); } if (strstr( &(filebuf[i-retval]), "/CharStrings")!=NULL) { /* replace dictionary with one of an appropriate size: */ i -=retval; sprintf( err_warn_msg_buf, "Private dictionary finished (%u data bytes)", i-dindex); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); /* if .notdef is not in the encoding mask, we have to reserve room for the additional charstring .notdef. Note that still nocharstrings is an upper bound estimation, which is reached in cases where no characters are encoded more than one time. */ if (notdefencoded==0) nocharstrings++; i+=sprintf( &(filebuf[i]), "2 index /CharStrings %d dict dup begin\n", nocharstrings); break; } } /* the portion until Charstrings-dict is now read in */ /* We now have to write the CharStrings. Each charstring must be written once, even if the respective character appears more than once in the encoding. So we set up table to remember which charstrings already have been written. Note: The indices returned by locateCharString() range from 1 to n, so that we have to decrement the index when filling the csdone array! */ if (( csdone=(char *)calloc( pFontBase->pFontArray[FontID].pType1Data->CharStringsP[0].key.len, sizeof(char)))==NULL) { T1_errno=T1ERR_ALLOC_MEM; free( filebuf); T1Close( ifp); return( NULL); } /* The .notdef character is in force! */ if ((currstring_no=locateCharString( FontID, ".notdef"))==0) { T1_errno=T1ERR_UNSPECIFIED; free( csdone); T1Close( ifp); return( NULL); } i+=sprintf( &(filebuf[i]), "/.notdef %d %s ", charstringL, rdstring); memcpy(&(filebuf[i]), charstringP, charstringL); i+=charstringL; i+=sprintf( &(filebuf[i]), " %s\n", ndstring); csdone[currstring_no-1]=1; /* Now, step through the specifier matrix and write only the necessary charstrings. */ for ( j=0; j<256; j++) { if (mask[j]!=0) { charnameP=T1_GetCharName( FontID, j); if ((currstring_no=locateCharString( FontID, charnameP))==0) { /* This might be a composite character, check and handle this case ... */ if ( cci != NULL ) T1_FreeCompCharData( cci); cci = T1_GetCompCharData( FontID, j); if ( (cci != NULL) && (cci->numPieces > 1) ) { /* composite character, loop through pieces and look for the components */ for ( p=0; pnumPieces; p++) { charnameP=T1_GetCharName( FontID, cci->pieces[p].piece); if ((currstring_no=locateCharString( FontID, charnameP))==0) { /* Character component not found. Try next ... */ T1_PrintLog( "T1_SubsetFont()", "Could not locate component CS ""%s"" for index %d", T1LOG_WARNING, charnameP, cci->pieces[p].piece); } else { /* Add component to subset and process charstring only if it has not already been done */ if (csdone[currstring_no-1]==0) { k=i; i+=sprintf( &(filebuf[i]), "/%s %d %s ", charnameP, charstringL, rdstring); memcpy(&(filebuf[i]), charstringP, charstringL); i+=charstringL; i+=sprintf( &(filebuf[i]), " %s\n", ndstring); csdone[currstring_no-1]=1; T1_PrintLog( "T1_SubsetFont()", "Processing of component CS ""%s"" for index %d successful (len=%d bytes, line=%d bytes)", T1LOG_DEBUG, charnameP, cci->pieces[p].piece, charstringL, i-k); } else { sprintf( err_warn_msg_buf, "Skipped multiple processing of component CS ""%s"" (index %d)", charnameP, cci->pieces[p].piece); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); } } } continue; } else { /* Character not found and no composite. This is mysterious, but causes no harm because .notdef will be substituted */ sprintf( err_warn_msg_buf, "Could not locate CS ""%s"" for index %d", charnameP, j); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_WARNING); continue; } } /* Process charstring only if it has not already been done */ if (csdone[currstring_no-1]==0) { k=i; i+=sprintf( &(filebuf[i]), "/%s %d %s ", charnameP, charstringL, rdstring); memcpy(&(filebuf[i]), charstringP, charstringL); i+=charstringL; i+=sprintf( &(filebuf[i]), " %s\n", ndstring); csdone[currstring_no-1]=1; sprintf( err_warn_msg_buf, "Processing of CS ""%s"" for index %d successful (len=%d bytes, line=%d bytes)", charnameP, j, charstringL, i-k); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); } else { sprintf( err_warn_msg_buf, "Skipped multiple processing of CS ""%s"" (index %d)", charnameP, j); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); } } } /* Get rid of temporary data */ if (csdone!=NULL) { free( csdone); csdone = NULL; } if ( cci != NULL ) { free( cci); cci = NULL; } /* All charstrings are written. Some PostScript code follows */ i+=sprintf( &(filebuf[i]), "end\nend\nreadonly put\nnoaccess put\ndup /FontName get exch definefont pop\nmark currentfile closefile\n"); sprintf( err_warn_msg_buf, "Charstrings and Font definition finished (%d data bytes, from which %d bytes will be encrypted)", i, i-dindex); T1_PrintLog( "T1_SubsetFont()", err_warn_msg_buf, T1LOG_DEBUG); /* we compute the size of the encrypted area. */ if (encrypt==1) { encryptsize=i-dindex; encryptsize+=4; /* the four random bytes */ encryptsize*=2; /* since we use ASCII-hex output */ encryptsize+=(int)ceil((double)encryptsize/linewidth); /* we need a few newline characters to adjust the output format */ } else if (encrypt==2) { /* binary encryption. The whole data is contiguous and only at the end of the data a newline is added. */ encryptsize=i-dindex+1; encryptsize+=4; /* the four random bytes */ } else { /*The case of no encryption does not produce valid fonts, it is for debugging purposes only */ encryptsize=i-dindex+1; } /* we add the size of the 512 zeros and the cleartomark */ trailersize+=ZEROS; trailersize+=(int)ceil((double)ZEROS/linewidth); trailersize+=12; /* cleartomark */ /* Search for remaining PostScript code in the last 1024 bytes. This should suffice for any font. */ tr_len=T1GetTrailer( linebuf, 1025, ifp); T1Close(ifp); /* we do not need the file any longer */ /* And we add the size of the trailer. A trailer only consisting of a newline is ignored because the newline has already been included in the cleartomark-string. */ if ( !((tr_len==1) && ((linebuf[0]=='\n') || (linebuf[0]=='\r')) ) ) { trailersize+=tr_len; } if ((encryptbuf=(char *)calloc( encryptsize, 1))==NULL) { T1_errno=T1ERR_ALLOC_MEM; free( filebuf); return( NULL); } /* Allocate one byte in orer to be able to use sprintf() (which appends an ASCII-\0).*/ if ((trailerbuf=(char *)calloc( trailersize+1, 1))==NULL) { T1_errno=T1ERR_ALLOC_MEM; free( filebuf); free( encryptbuf); return( NULL); } k=0; colcount=0; /* perform eexec-encryption */ eexec_r=55665; if (encrypt==0) { for (j=dindex; jpFontArray[FontID].pType1Data->Private[LENIV].value.data.integer); }