#include #include /* * This program is covered by the Berkeley copyright. Feel free to * use it for anything, including creation of a commercial product. * But it is intended as a proof of concept, not as a production- * quality program. Thus I disclaim all responsibility for what it * may do. * * Charles Hedrick, Rutgers University, March 31, 2000 * hedrick@nbcs.rutgers.edu */ /* chcmap - modify character map of opentype font, to allow * applications that don't know about opentype to access text figures * and small caps. See chcmap.doc for instructions. * * based on format description from * http://partners.adobe.com/asn/developer/opentype/otff.htm * you'll want that in front of you if you're to have any hope * of understanding this code * * if you just want to modify the character arrangement, it should * be fairly easy, and should not require you to understand the * format of the opentype file. Look in dohackcmap for the sequences of calls * to doonehack. * doonehack(0x30, 0xf730, 10); * moves the 10 characters at Unicode f730 to 30. * You can either modify these calls, or add a new option. To do that, * simply modify "main" to understand a new option, set "what" to a * new letter code for that option, and add a new set of doonehack calls * to dohackcmap. A reasonable project would be to modify the program * to read the possible rearrangements from a file. (It's probably too * complex to pass as an argument.) * * WARNING: doonehack will typically increase the number of entries * in the mapping table, probably by two entries per call. readstr * leaves 140 entries available for such additions. If you have more * than 70 calls to doonehack for any option, increase the 140 in readstr. * Or do something more elegant... * * You'll note that I make no attempt to free malloc'ed memory. * I don't believe doing so would significantly decrease memory * usage. * * I worked with a very old version of Microsoft Visual C++, from about * 1994. However this program should use nothing beyond very basic C. * So I'd assume it would work under any reasonable C compiler. * About the only thing that is an extension is the "b" option to fopen. * That should be present in all PC-based C implementations. If you * compile under Unix, remove the b. * * Caveat: this code only modifies the Windows entries. Since I don't * use a Mac, I have no way to test changes to the Mac sections. I'd * rather not do something than do something that probably won't work. */ struct maintable { unsigned long sfntversion; unsigned short numtables; unsigned short searchrange; unsigned short entryselector; unsigned short rangeshift; } maintable; struct tabledesc { char ident[4]; unsigned long checksum; unsigned long offset; unsigned long length; } *tabledescs; char **tabledata; unsigned long *tablelen; struct cmaphead { unsigned short version; unsigned short nsubtables; } cmaphead; struct cmapsubhead { unsigned short platid; unsigned short encid; unsigned long offset; } *cmapsubheads; struct deltahead { unsigned short format; unsigned short length; unsigned short version; unsigned short segcountx2; unsigned short searchrange; unsigned short entryselector; unsigned short rangeshift; unsigned short *endchar; unsigned short pad; unsigned short *startchar; short *delta; unsigned short *rangeoffset; unsigned short *glyphid; } deltahead; struct namehead { unsigned short formatselector; unsigned short numnames; unsigned short stringstart; } namehead; struct namerec{ unsigned short platformid; unsigned short encodingid; unsigned short languageid; unsigned short nameid; unsigned short stringlen; unsigned short stringoffset; } *namerecs; unsigned char **strings; /* strings from the name section */ long *stringlens; /* lengths of strings above */ char *int1, *int2, *int3, *work; long sizeint1, sizeint2, sizeint3, sizework; long segcount, oldsegcount; int what; /* what type of transformation the user wants, o for osf, etc */ char *suffix; /* suffix for font family name */ FILE *infile, *outfile; void * safemalloc(l) long l; { void *p; p = malloc(l); if (!p) { fprintf(stderr, "malloc failed\n"); exit(1); } return p; } /* wsearch(t,l,s,m) * look for string s in t, assuming 16-bit characters * t is length l in bytes * s is length m in bytes * return pointer to string found or null */ char *wsearch(t, l, s, m) unsigned char *t,*s; long l, m; { unsigned char *t1, *s1; unsigned short ct, cs; long i; l -= (m-2); while (l > 0) { for (s1 = s, t1 = t, i = m; i>0;) { ct = (*t1 << 8)|*(t1+1); cs = (*s1 << 8)|*(s1+1); if (ct != cs) { break; } s1 += 2; i -= 2; t1 += 2; } if (i == 0) { return t; } l -= 2; t += 2; } return NULL; } /* strcpyw, copy an 8-bit string to a 16-bit destination. * source string is null-terminated */ strcpyw(unsigned char *s, unsigned char *t) { while (*t) { *s = 0; *(s+1) = *t; s += 2; t++; } } /* copy a 16-bit substring into malloced memory * s is the source * length is the length to copy, in bytes * loclen is the location of a variable which is set to the number of * bytes copied. * noblanks is one if we are to remove all blanks while copying the string * * returns the newly malloced string */ char * wgetstr(unsigned char *s, long length, long *loclen, int noblanks) { unsigned char *out, *os; unsigned short c; long newlen = 0; out = safemalloc(length+1); os = out; while (length > 0) { c = (*s << 8)|*(s+1); if (noblanks && c == ' ') { s += 2; length -= 2; continue; } *os++ = *s++; *os++ = *s++; newlen += 2; length -= 2; } *loclen = newlen; return out; } /* * wprintstr prints a 16-bit string */ wprintstr(unsigned char *s, long length) { s++; while (length > 0) { printf("%c", *s); length -= 2; s += 2; } } /* * usual byte swapping, for going from intel to motorola order * OpenType uses Motorola order. * define these as returning their arguments if you're on a machine * that uses Motorola order (e.g. Sparc) */ unsigned short ntohs(a) unsigned short a; { return (a>>8)|((a&0xff)<<8); } #define htons(a) ntohs(a) unsigned long ntohl(a) unsigned long a; { return (a>>24)|((a&0xff0000)>>8)|((a&0xff00)<<8)|((a&0xff)<<24); } #define htonl(a) ntohl(a) /* * OpenType checksum */ unsigned long checksum(char *s, unsigned long Length) { unsigned long *Table = (unsigned long *)s; unsigned long Sum = 0l; unsigned long word; unsigned long *EndPtr = Table+((Length+3) & ~3) / sizeof(unsigned long); while (Table < EndPtr) { word = *Table++; Sum += ntohl(word); } return Sum; } /* read the opentype file. results in headers in tabledescs, * contents of tables in tabledata. * tablelen contains length of the items in tabledata. */ readmain() { int i; fread(&maintable,sizeof(maintable) , 1, infile); maintable.numtables = ntohs( maintable.numtables); #ifdef DEBUG printf("%d tables\n", maintable.numtables); #endif tabledescs = safemalloc (maintable.numtables * sizeof (struct tabledesc)); tabledata = safemalloc (maintable.numtables * sizeof(char *)); tablelen = safemalloc (maintable.numtables * sizeof(unsigned long *)); for (i = 0; i < maintable.numtables; i++) { fread(&tabledescs[i], sizeof(struct tabledesc), 1, infile); tabledescs[i].checksum = ntohl(tabledescs[i].checksum); tabledescs[i].offset = ntohl(tabledescs[i].offset); tabledescs[i].length = ntohl(tabledescs[i].length); #ifdef DEBUG printf("%c%c%c%c offset %d length %d checksum %x\n", tabledescs[i].ident[0], tabledescs[i].ident[1], tabledescs[i].ident[2], tabledescs[i].ident[3], tabledescs[i].offset, tabledescs[i].length, tabledescs[i].checksum); #endif } for (i = 0; i < maintable.numtables; i++) { tabledata[i] = safemalloc((tabledescs[i].length + 3) & ~3); tablelen[i] = tabledescs[i].length; fseek(infile, tabledescs[i].offset, SEEK_SET); fread(tabledata[i], (tabledescs[i].length + 3) & ~3, 1, infile); } } /* read a new opentype file. * remove digital signature, since we can't regenerate it. * change font name as requested by user * recompute checksums */ writemain() { long offset, numtables, i, c, n; unsigned long cksum; char *cp; numtables = maintable.numtables; /* kill the digital signature, because I can't recompute it */ for (i = 0; i < numtables; i++) { if (toupper(tabledescs[i].ident[0]) == 'D' && toupper(tabledescs[i].ident[1]) == 'S' && toupper(tabledescs[i].ident[2]) == 'I' && toupper(tabledescs[i].ident[3]) == 'G') { if (i < (numtables - 1)) { memmove(&tabledescs[i], &tabledescs[i+1], sizeof(struct tabledesc) * (numtables - i - 1)); memmove(&tabledata[i], &tabledata[i+1], sizeof(char **) * (numtables - i - 1)); memmove(&tablelen[i], &tablelen[i+1], sizeof(long *) * (numtables - i - 1)); } numtables--; break; } } offset = sizeof(maintable) + sizeof(struct tabledesc) * numtables; maintable.numtables = htons(numtables); i = 2; c = 1; n = 0; while (i <= numtables) { c = i; n++; i = i*2; } maintable.searchrange = htons(c * 16); maintable.entryselector = htons(n); maintable.rangeshift = htons(numtables * 16 - c * 16); /* zero the checksum offset for recomputation */ for (i = 0; i < numtables; i++) { if (toupper(tabledescs[i].ident[0]) == 'H' && toupper(tabledescs[i].ident[1]) == 'E' && toupper(tabledescs[i].ident[2]) == 'A' && toupper(tabledescs[i].ident[3]) == 'D') { ((long *)tabledata[i])[2] = 0; } } for (i = 0; i < numtables; i++) { tabledescs[i].checksum = htonl(checksum(tabledata[i],tablelen[i])); tabledescs[i].offset = htonl(offset); offset += (tablelen[i] + 3) & ~3; tabledescs[i].length = htonl(tablelen[i]); } /* now master checksum. The data is now all in network order */ cksum = checksum((char *)&maintable,sizeof(maintable)); for (i = 0; i < numtables; i++) { cksum += checksum((char *)&tabledescs[i], sizeof(struct tabledesc)); cksum += checksum((char *)tabledata[i],tablelen[i]); } /* now put the checksum in */ for (i = 0; i < numtables; i++) { if (toupper(tabledescs[i].ident[0]) == 'H' && toupper(tabledescs[i].ident[1]) == 'E' && toupper(tabledescs[i].ident[2]) == 'A' && toupper(tabledescs[i].ident[3]) == 'D') { ((long *)tabledata[i])[2] = htonl(0xb1b0afba - cksum); } } /* * note that the master checksum calc hasn't been tested, because * ATM doesn't seem to care */ fwrite(&maintable,sizeof(maintable) , 1, outfile); for (i = 0; i < numtables; i++) { fwrite(&tabledescs[i], sizeof(struct tabledesc), 1, outfile); } for (i = 0; i < numtables; i++) { fwrite(tabledata[i], (tablelen[i] + 3) & ~3, 1, outfile); } } /* reads one of the arrays in a cmap type 4 structure * reads into a malloced string, which it returns in loc * data is the input string. return value is first byte of data that * hasn't been read */ char * readstr(unsigned short **loc, long segc, char *data) { unsigned short *mem; long i; mem = safemalloc((segc+140) * sizeof(unsigned short)); memcpy(mem,data, sizeof(unsigned short) * segc); for (i = 0; i < segc; i++) { mem[i] = ntohs(mem[i]); } *loc = mem; return data + sizeof(unsigned short) * segc; } /* write out one of the arrays */ char * writestr(loc, segc, data) unsigned short *loc; long segc; char *data; { long i; for (i = 0; i < segc; i++) { loc[i] = htons(loc[i]); } memcpy(data, loc, sizeof(unsigned short) * segc); return data + sizeof(unsigned short) * segc; } /* do one modification to the cmap table * the complexity here is that normally we have to split entries in * the table. */ void doonehack(d, s, c) long d; long s; long c; { long i, n; for (i = 0; i < segcount; i++) { if (deltahead.endchar[i] >= (d + c - 1) && deltahead.startchar[i] <= d) break; } if (i >= segcount) { fprintf(stderr, "Couldn't find target %x\n", d); exit(1); } #ifdef DEBUG printf("found target %x\n", d); #endif /* see if source is there before making any changes */ for (n = 0; n < segcount; n++) { if (deltahead.endchar[n] >= s && deltahead.startchar[n] <= (s + c -1)) break; } if (n >= segcount) { fprintf(stderr, "Couldn't find source %x -- ignoring\n", s); return; } /* create a new range * This normally requires us to create new entries, since normally * the range we want doesn't exactly match any of the existing ones. */ /* if the existing range ends later than the characters we are * changing, we need to create a new entry. i will be the * range we are changing. i+1 will be a new entry that contains * the character left over from the original range above the * ones we are modifying. Much of this code is clear: the memmoves * move up the data to create a new entry. The changes to startchar * and endchar define the boundary between the i and i+1. The * delicate code is the updating of rangeoffset. Rangeoffset specifies * a location relative to the memory location of the specification. * So when we add a new entry to the table, suddenly all entries before * the new one have to be updated. */ if (deltahead.endchar[i] > (d + c - 1)) { memmove(&deltahead.endchar[i+1], &deltahead.endchar[i], (segcount-i) * sizeof(short)); memmove(&deltahead.startchar[i+1], &deltahead.startchar[i], (segcount-i) * sizeof(short)); memmove(&deltahead.delta[i+1], &deltahead.delta[i], (segcount-i) * sizeof(short)); memmove(&deltahead.rangeoffset[i+1], &deltahead.rangeoffset[i], (segcount-i) * sizeof(short)); if (deltahead.rangeoffset[i+1]) deltahead.rangeoffset[i+1] += ((d + c) - deltahead.startchar[i]) * 2; deltahead.startchar[i+1] = d + c; deltahead.endchar[i] = d + c - 1; segcount++; /* have to adjust rangeoffset because we've added more stuff after them */ for (n = 0; n <= i; n++) if (deltahead.rangeoffset[n]) deltahead.rangeoffset[n] += 2; } /* similarly, if the existing range starts before the set of * characters we're modifying, we have to add another entry. * i ends up containing the leftover characters below our changes. * i+1 is the range containing the changes. */ if (deltahead.startchar[i] < d) { memmove(&deltahead.endchar[i+1], &deltahead.endchar[i], (segcount-i) * sizeof(short)); memmove(&deltahead.startchar[i+1], &deltahead.startchar[i], (segcount-i) * sizeof(short)); memmove(&deltahead.delta[i+1], &deltahead.delta[i], (segcount-i) * sizeof(short)); memmove(&deltahead.rangeoffset[i+1], &deltahead.rangeoffset[i], (segcount-i) * sizeof(short)); deltahead.startchar[i+1] = d; deltahead.endchar[i] = d-1; segcount++; i++; /* have to adjust rangeoffset because we've added more stuff after them */ for (n = 0; n < i; n++) if (deltahead.rangeoffset[n]) deltahead.rangeoffset[n] += 2; } /* i is now the range we are going to change */ for (n = 0; n < segcount; n++) { if (deltahead.endchar[n] >= s && deltahead.startchar[n] <= (s + c -1)) break; } if (n >= segcount) { fprintf(stderr, "Couldn't find source %x\n", s); exit(1); } #ifdef DEBUG printf("found source %x delta %x rangeoff %x\n", s, deltahead.delta[n], deltahead.rangeoffset[n]); #endif /* update the description of this range. How we do that depends upon * whether the description is a delta or a rangeoffset. */ if (deltahead.delta[n]) { #ifdef DEBUG printf("set delta to %d\n", deltahead.delta[n] + (s - d)); #endif deltahead.delta[i] = deltahead.delta[n] + (s - d); } else { #ifdef DEBUG printf("set delta to 0\n"); #endif deltahead.delta[i] = 0; } if (deltahead.rangeoffset[n]) deltahead.rangeoffset[i] = deltahead.rangeoffset[n] + (s - deltahead.startchar[n]) *2 + (n - i) * 2; else deltahead.rangeoffset[i] = 0; } /* modify the cmap table. Parse the table, call hackone as needed, * then write out the changed table. */ dohackcmap(index) int index; { long curpos = 0, unicodepos, i, nsubtables, added, c, n; char *data = tabledata[index]; char *newdata; memcpy(&cmaphead, data, sizeof(cmaphead)); data += sizeof(cmaphead); cmaphead.nsubtables = ntohs(cmaphead.nsubtables); #ifdef DEBUG printf("cmap subtables %d\n", cmaphead.nsubtables); #endif cmapsubheads = safemalloc(cmaphead.nsubtables * sizeof(struct cmapsubhead)); for (i = 0; i < cmaphead.nsubtables; i++) { memcpy(&cmapsubheads[i], data, sizeof(struct cmapsubhead)); data += sizeof(struct cmapsubhead); cmapsubheads[i].platid = ntohs(cmapsubheads[i].platid); cmapsubheads[i].encid = ntohs(cmapsubheads[i].encid); cmapsubheads[i].offset = ntohl(cmapsubheads[i].offset); #ifdef DEBUG printf(" platform %d encoding %d offset %d\n", cmapsubheads[i].platid, cmapsubheads[i].encid, cmapsubheads[i].offset); #endif } /* find the microsoft unicode description */ for (i = 0; i < cmaphead.nsubtables; i++) { if (cmapsubheads[i].platid == 3 && cmapsubheads[i].encid == 1) break; } if (i >= cmaphead.nsubtables) { fprintf(stderr, "Couldn't find Microsoft Unicode in cmap\n"); exit(1); } /* now get to unicode description */ unicodepos = cmapsubheads[i].offset; data = tabledata[index] + unicodepos; memcpy(&deltahead, data, (char *)&(deltahead.rangeshift) - (char *)&deltahead + sizeof(deltahead.rangeshift)); data += (char *)&(deltahead.rangeshift) - (char *)&deltahead + sizeof(deltahead.rangeshift); deltahead.format = ntohs(deltahead.format); deltahead.length = ntohs(deltahead.length); deltahead.version = ntohs(deltahead.version); deltahead.segcountx2 = ntohs(deltahead.segcountx2); deltahead.searchrange = ntohs(deltahead.searchrange); deltahead.entryselector = ntohs(deltahead.entryselector); deltahead.rangeshift = ntohs(deltahead.rangeshift); segcount = deltahead.segcountx2 / 2; data = readstr(&deltahead.endchar, segcount, data); #ifdef DEBUG printf ("endchar %x\n", *deltahead.endchar); #endif data = readstr(&deltahead.startchar, segcount, data + sizeof(short)); #ifdef DEBUG printf ("startchar %x\n", *deltahead.startchar); #endif data = readstr(&deltahead.delta, segcount, data); #ifdef DEBUG printf ("delta %x\n", *deltahead.delta); #endif data = readstr(&deltahead.rangeoffset, segcount, data); #ifdef DEBUG printf ("rangeoffset %x\n", *deltahead.rangeoffset); printf("segcount %d\n", segcount); #endif /* hack on the table */ oldsegcount = segcount; if (what == 's') { doonehack(0x30, 0xf730, 10); /* var width oldstyle figures */ doonehack(0xa4, 0x2212, 1); /* minus onto currency sign */ doonehack(0x61, 0xf761, 26); /* sc into lowercase */ doonehack(0xe0, 0xf7e0, 23); /* accented sc into lowercase accented */ doonehack(0xf8, 0xf7f8, 8); /* more " */ doonehack(0x142, 0xf6f9, 1); /* l bar */ doonehack(0x153, 0xf6fa, 1); /* oe ligature */ doonehack(0x161, 0xf6fd, 1); /* s caron */ doonehack(0x17e, 0xf6ff, 1); /* z caron */ doonehack(0x24, 0xf653, 1); /* dollar */ doonehack(0x26, 0xf726, 1); /* ampersand */ } else if (what == 'o') { doonehack(0x30, 0xf730, 10); /* variable width text figures */ doonehack(0xa4, 0x2212, 1); /* minus onto currency sign */ } else if (what == 'x') { doonehack(0x21, 0xf721, 1); /* small exclamation */ doonehack(0x22, 0xf6f8, 1); /* small hungarian umlaut onto " */ doonehack(0x23, 0xf650, 1); /* small # # */ doonehack(0x24, 0xf653, 1); /* small dollar */ doonehack(0x25, 0xf642, 1); /* small % # */ doonehack(0x26, 0xf726, 1); /* small ampersand */ doonehack(0x27, 0xf7b4, 1); /* small acute */ doonehack(0x28 ,0xf631, 2); /* raised () onto () # */ doonehack(0x2a ,0x2025, 1); /* twodotleader onto * # */ doonehack(0x2b ,0x2024, 1); /* onedotleader onto + # */ doonehack(0x2d, 0xf6ba, 1); /* centered hyphen # */ doonehack(0x2f, 0x2044, 1); /* fraction slash onto slash */ doonehack(0x30, 0xf643, 10); /* fixed width oldstyle figures */ doonehack(0x3c, 0xf66b, 1); /* raised , onto < # */ doonehack(0x3e, 0xf66c, 1); /* raised . onto > # */ doonehack(0x3f, 0xf73f, 1); /* small question mark */ doonehack(0x41, 0xf6e9, 2); /* superior a,b # */ doonehack(0x43 ,0xf62e, 1); /* raised cent onto C # */ doonehack(0x44, 0xf6eb, 2); /* superior d,e # */ doonehack(0x46 ,0xf62f, 1); /* raised dollar onto F # */ doonehack(0x49, 0xf6ed, 1); /* superior i # */ doonehack(0x4c, 0xf6ee, 2); /* superior l,m # */ doonehack(0x4f, 0xf6f0, 1); /* superior o # */ doonehack(0x52, 0xf6f1, 3); /* superior r,s,t # */ doonehack(0x56, 0xfb00, 5); /* ligatures */ doonehack(0x5b, 0xf62c, 1); /* lowered ( onto [ # */ doonehack(0x5d, 0xf62d, 1); /* lowered ) onto ] # */ doonehack(0x5e, 0xf6f6, 1); /* small circumflex onto ^ # */ doonehack(0x5f ,0xf630, 1); /* raised hyphen onto _ # */ doonehack(0x60, 0xf760, 1); /* small grave */ doonehack(0x61, 0xf761, 26); /* sc into lowercase */ doonehack(0x7b, 0x20a1, 1); /* monetary colon onto { # */ doonehack(0x7c, 0xf6dc, 2); /* fitted 1, rupee onto | } # */ doonehack(0x7e, 0xf6fe, 1); /* small tilde onto tilde # */ doonehack(0xa1, 0xf7a1, 1); /* small inverted exclamation point */ doonehack(0xa2, 0xf654, 1); /* small cent # */ doonehack(0xa3, 0xf651, 1); /* small pounds # */ doonehack(0xa4, 0x2212, 1); /* minus onto currency sign */ doonehack(0xa5, 0xf652, 1); /* small yen # */ doonehack(0xa8, 0xf7a8, 1); /* small dieresis */ doonehack(0xa9, 0xf6f4, 1); /* small breve onto copyright # */ doonehack(0xaa, 0xf6f5, 1); /* small caron onto superior a # */ doonehack(0xab, 0xf6b6, 1); /* centered open dbl angles # */ doonehack(0xac, 0xf6f7, 1); /* small single dot accent onto not a # */ doonehack(0xaf, 0xf7af, 1); /* small macron */ doonehack(0xb0, 0xf6fc, 1); /* small ring onto degree # */ doonehack(0xb2, 0x2012, 1); /* figure dash onto 2 superior # */ doonehack(0xb3, 0xf62b, 1); /* lowered hyphen onto 3 superior # */ doonehack(0xb6, 0xf6fb, 1); /* small agonek onto para # */ doonehack(0xb7, 0xf6bd, 1); /* centered centered dot # */ doonehack(0xb8, 0xf7b8, 1); /* small cedilla */ doonehack(0xbb, 0xf6b7, 1); /* centered close dbl angles # */ doonehack(0xbf, 0xf7bf, 1); /* small inverted question mark */ doonehack(0xc0, 0x215b, 4); /* 1/8, 3/8, 5/8, 7/8 */ doonehack(0xc4, 0x2153, 2); /* 1/3, 2/3 */ doonehack(0xc8, 0xf661, 10); /* raised fraction figures */ doonehack(0xd2, 0xf655, 10); /* lowered fraction figures */ doonehack(0xdc, 0xf629, 2); /* lowered cent,dollar to U dier, Y acute # */ doonehack(0xde, 0xf660, 1); /* lowered . uppercase thorn # */ doonehack(0xdf, 0xf65f, 1); /* lowered , onto SS# */ doonehack(0xe0, 0xf7e0, 23); /* accented sc into lowercase accented */ doonehack(0xf8, 0xf7f8, 8); /* more " */ doonehack(0x142, 0xf6f9, 1); /* l bar */ doonehack(0x153, 0xf6fa, 1); /* oe ligature */ doonehack(0x161, 0xf6fd, 1); /* s caron */ doonehack(0x17e, 0xf6ff, 1); /* z caron */ doonehack(0x2013, 0xf6bb, 2); /* centered en, em dash # */ doonehack(0x2030, 0xf628, 1); /* small per mille # */ doonehack(0x2039, 0xf6b8, 2); /* centered single angles # */ } /* write back */ /* we added segcount - oldsegcount entries to 4 tables of shorts */ added = (segcount - oldsegcount) * 4 * sizeof(short); #ifdef DEBUG printf("added %d\n", added); #endif newdata = safemalloc(tablelen[index] + added); data = newdata; nsubtables = cmaphead.nsubtables; /* save number in host format */ cmaphead.nsubtables = htons(cmaphead.nsubtables); memcpy(data, &cmaphead, sizeof(cmaphead)); data += sizeof(cmaphead); #ifdef DEBUG printf("cmap subtables %d\n", cmaphead.nsubtables); #endif for (i = 0; i < nsubtables; i++) { cmapsubheads[i].platid = htons(cmapsubheads[i].platid); cmapsubheads[i].encid = htons(cmapsubheads[i].encid); if (cmapsubheads[i].offset > unicodepos) cmapsubheads[i].offset += added; #ifdef DEBUG printf("offset %d", cmapsubheads[i].offset); #endif cmapsubheads[i].offset = htonl(cmapsubheads[i].offset); memcpy(data, &cmapsubheads[i], sizeof(struct cmapsubhead)); data += sizeof(struct cmapsubhead); } if (data < (newdata + unicodepos)) { #ifdef DEBUG printf(" memcpy %d %d\n", data -newdata, newdata + unicodepos - data); #endif memcpy(data, tabledata[index] + (data - newdata), newdata + unicodepos - data); } data = newdata + unicodepos; deltahead.format = htons(deltahead.format); deltahead.length = htons(deltahead.length+added); deltahead.version = htons(deltahead.version); deltahead.segcountx2 = htons(segcount * 2); i = 2; c = 1; n = 0; while (i <= segcount) { c = i; n++; i = i*2; } deltahead.searchrange = htons(c * 2); deltahead.entryselector = htons(n); deltahead.rangeshift = htons(segcount * 2 - c * 2); memcpy(data,&deltahead, (char *)&(deltahead.rangeshift) - (char *)&deltahead + sizeof(deltahead.rangeshift)); data += (char *)&(deltahead.rangeshift) - (char *)&deltahead + sizeof(deltahead.rangeshift); data = writestr(deltahead.endchar, segcount, data); memcpy(data,&deltahead.pad,sizeof(deltahead.pad)); data += sizeof(deltahead.pad); data = writestr(deltahead.startchar, segcount, data); data = writestr(deltahead.delta, segcount, data); data = writestr(deltahead.rangeoffset, segcount, data); tablelen[index] += added; #ifdef DEBUG printf("memcpy %d %d\n", data-newdata-added, tablelen[index]-(data-newdata)); #endif memcpy(data, tabledata[index] + (data-newdata-added), tablelen[index] - (data-newdata)); tabledata[index] = newdata; } /* find the cmap table and modify it */ hackcmap() { int i, numtables; numtables = maintable.numtables; for (i = 0; i < numtables; i++) { if (toupper(tabledescs[i].ident[0]) == 'C' && toupper(tabledescs[i].ident[1]) == 'M' && toupper(tabledescs[i].ident[2]) == 'A' && toupper(tabledescs[i].ident[3]) == 'P') { dohackcmap(i); } } } /* * worker routine for modifying the font names */ hacknameint(i) int i; { char *data, *stringspace, *oldname, *newname = NULL, *cp, *cp2, *cp3; char *woldname, *wnewname = NULL, *woldnamec, *wnewnamec; long woldnamelen, wnewnamelen, woldnameclen, wnewnameclen, after, numnames, stringoff; int n; /* read in the name table */ data = tabledata[i]; memcpy(&namehead, data, sizeof(namehead)); namehead.formatselector = ntohs(namehead.formatselector); namehead.numnames = ntohs(namehead.numnames); namehead.stringstart = ntohs(namehead.stringstart); numnames = namehead.numnames; if (namehead.formatselector != 0) { fprintf(stderr, "format selector for name table non-zero\n"); exit(1); } strings = safemalloc(numnames * sizeof (unsigned char *)); stringlens = safemalloc(numnames * sizeof (long)); namerecs = safemalloc(numnames * sizeof (struct namerec)); stringspace = data + namehead.stringstart; data += sizeof(namehead); /* now at list of name records */ for (n = 0; n < numnames; n++) { memcpy(&namerecs[n], data, sizeof(struct namerec)); data += sizeof(struct namerec); namerecs[n].platformid = ntohs(namerecs[n].platformid); namerecs[n].encodingid = ntohs(namerecs[n].encodingid); namerecs[n].languageid = ntohs(namerecs[n].languageid); namerecs[n].nameid = ntohs(namerecs[n].nameid); namerecs[n].stringlen = ntohs(namerecs[n].stringlen); namerecs[n].stringoffset = ntohs(namerecs[n].stringoffset); strings[n] = stringspace + namerecs[n].stringoffset; stringlens[n] = namerecs[n].stringlen; /* remember the family name (nameid 1), override with * preferred family name (16) if it is specified * Remember normal format (woldname) and format with * blanks removed (woldnamec) * Compute the new names, which are the old names with the * suffix added. */ if (namerecs[n].nameid == 1 && newname == NULL || namerecs[n].nameid == 16) { if (namerecs[n].platformid == 3) { woldname = wgetstr(strings[n], stringlens[n], &woldnamelen, 0); woldnamec = wgetstr(strings[n], stringlens[n], &woldnameclen, 1); wnewname = safemalloc(woldnamelen + 2*strlen(suffix) + 2); wnewnamelen = woldnamelen + 2*strlen(suffix) + 2; memcpy(wnewname, woldname, woldnamelen); wnewname[woldnamelen] = 0; wnewname[woldnamelen + 1] = ' '; strcpyw(wnewname + woldnamelen + 2, suffix); wnewnamec = safemalloc(woldnameclen + 2*strlen(suffix)); wnewnameclen = woldnameclen + 2*strlen(suffix); memcpy(wnewnamec, woldnamec, woldnameclen); strcpyw(wnewnamec + woldnameclen, suffix); } } } /* now update the names. Have to do different modifications * for different types of name */ for (n = 0; n < numnames; n++) { if (namerecs[n].platformid == 3) { if (namerecs[n].nameid == 1) { /* family name */ if (wsearch(strings[n], stringlens[n], woldname, woldnamelen) != strings[n]) { fprintf (stderr, "Font family name is not consistent with preferred family name\n"); exit(1); } /* family name should start start with woldname. Indeed it * is often the same thing, but woldname may be the "preferred * family name." Replace woldname with the new name, which * has the suffix */ cp = safemalloc(stringlens[n] - woldnamelen + wnewnamelen); memcpy(cp, wnewname, wnewnamelen); memcpy(cp + wnewnamelen, strings[n] + woldnamelen, stringlens[n] - woldnamelen); strings[n] = cp; stringlens[n] = stringlens[n] - woldnamelen + wnewnamelen; } else if (namerecs[n].nameid == 3) { /* unique font identifier. This could be anything. It just has * to be unique. If I can find the family name, insert the * suffix after it. Else tack it on the end */ if (cp2 = wsearch(strings[n], stringlens[n], woldname, woldnamelen)) { /* found the old name in there somewhere. Add the suffix to it */ cp = safemalloc(stringlens[n] - woldnamelen + wnewnamelen); cp3 = cp; if (cp2 > strings[n]) { memcpy(cp3, strings[n], cp2 - strings[n]); cp3 += cp2 - strings[n]; } memcpy(cp3, wnewname, wnewnamelen); cp3 += wnewnamelen; after = strings[n] + stringlens[n] - (cp2 + woldnamelen); if (after > 0) memcpy(cp3, cp2 + woldnamelen, after); strings[n] = cp; stringlens[n] = stringlens[n] - woldnamelen + wnewnamelen; } else if (cp2 = wsearch(strings[n], stringlens[n], woldnamec, woldnameclen)) { /* found the oldname with blanks removed. Add the suffix */ cp = safemalloc(stringlens[n] - woldnameclen + wnewnameclen); cp3 = cp; if (cp2 > strings[n]) { memcpy(cp3, strings[n], cp2 - strings[n]); cp3 += cp2 - strings[n]; } memcpy(cp3, wnewnamec, wnewnameclen); cp3 += wnewnameclen; after = strings[n] + stringlens[n] - (cp2 + woldnameclen); if (after > 0) memcpy(cp3, cp2 + woldnameclen, after); strings[n] = cp; stringlens[n] = stringlens[n] - woldnameclen + wnewnameclen; } else { /* didn't find the old name. Add the suffix to the end */ cp = safemalloc(stringlens[n] + strlen(suffix) + 2); memcpy(cp, strings[n], stringlens[n]); cp[stringlens[n]] = 0; cp[stringlens[n]+1] = ';'; strcpyw(cp + stringlens[n] + 2, suffix); strings[n] = cp; stringlens[n] = stringlens[n] + 2*strlen(suffix) + 2; } } else if (namerecs[n].nameid == 4) { /* full name. You'd think this would be the full form, with blanks. * That's what the docs imply. But for Adobe it seems to be the * same as the Postscript name. To be safe, I handle either format. */ if (wsearch(strings[n], stringlens[n], woldname, woldnamelen) == strings[n]) { /* starts with old name */ cp = safemalloc(stringlens[n] - woldnamelen + wnewnamelen); memcpy(cp, wnewname, wnewnamelen); memcpy(cp + wnewnamelen, strings[n] + woldnamelen, stringlens[n] - woldnamelen); strings[n] = cp; stringlens[n] = stringlens[n] - woldnamelen + wnewnamelen; } else if (wsearch(strings[n], stringlens[n], woldnamec, woldnameclen) == strings[n]) { /* starts with old name with blanks removed */ cp = safemalloc(stringlens[n] - woldnameclen + wnewnameclen); memcpy(cp, wnewnamec, wnewnameclen); memcpy(cp + wnewnameclen, strings[n] + woldnameclen, stringlens[n] - woldnameclen); strings[n] = cp; stringlens[n] = stringlens[n] - woldnameclen + wnewnameclen; } else { printf("Full font name "); wprintstr(strings[n], stringlens[n]); printf(" does not begin with family name "); wprintstr(woldname, woldnamelen); printf("\n"); exit(1); } } else if (namerecs[n].nameid == 6) { /* Postscript name. Should begin with family name without blanks. * add suffix to the end of family name */ if (wsearch(strings[n], stringlens[n], woldnamec, woldnameclen) != strings[n]) { fprintf (stderr, "Postscript name does not begin with family name\n"); exit(1); } cp = safemalloc(stringlens[n] - woldnameclen + wnewnameclen); memcpy(cp, wnewnamec, wnewnameclen); memcpy(cp + wnewnameclen, strings[n] + woldnameclen, stringlens[n] - woldnameclen); strings[n] = cp; stringlens[n] = stringlens[n] - woldnameclen + wnewnameclen; } else if (namerecs[n].nameid == 16) { /* preferred family name. If it exists, it is woldname, so just * update it to wnewname */ strings[n] = wnewname; stringlens[n] = wnewnamelen; } } } /* now write out new table. Have to recompute offsets for strings, * since many have changed. (That's the whole point, after all.) */ tablelen[i] = sizeof(namehead) + numnames * sizeof(struct namerec); stringoff = 0; for (n = 0; n < numnames; n++) { namerecs[n].stringoffset = stringoff; namerecs[n].stringlen = stringlens[n]; tablelen[i] += stringlens[n]; stringoff += stringlens[n]; } tabledata[i] = safemalloc(tablelen[i]); data = tabledata[i]; namehead.formatselector = htons(namehead.formatselector); namehead.numnames = htons(namehead.numnames); namehead.stringstart = htons(namehead.stringstart); memcpy(data, &namehead, sizeof(namehead)); data += sizeof(namehead); for (n = 0; n < numnames; n++) { namerecs[n].platformid = htons(namerecs[n].platformid); namerecs[n].encodingid = htons(namerecs[n].encodingid); namerecs[n].languageid = htons(namerecs[n].languageid); namerecs[n].nameid = htons(namerecs[n].nameid); namerecs[n].stringlen = htons(namerecs[n].stringlen); namerecs[n].stringoffset = htons(namerecs[n].stringoffset); memcpy(data, &namerecs[n], sizeof(struct namerec)); data += sizeof(struct namerec); } for (n = 0; n < numnames; n++) { memcpy(data, strings[n], stringlens[n]); data += stringlens[n]; } tablelen[i] = data - tabledata[i]; } /* locate name table and update it */ hacknames() { int i, numtables; numtables = maintable.numtables; for (i = 0; i < numtables; i++) { if (toupper(tabledescs[i].ident[0]) == 'N' && toupper(tabledescs[i].ident[1]) == 'A' && toupper(tabledescs[i].ident[2]) == 'M' && toupper(tabledescs[i].ident[3]) == 'E') { hacknameint(i); } } } main (argc, argv) int argc; char **argv; { int i, c, n, cmapindex; long curpos, cmappos, unicodepos, segcount; if (argc != 5) { fprintf(stderr, "Usage: chcmap -sc|-osf|-expert suffix infile outfile\n"); exit(1); } if (strcmp(argv[1], "-sc") == 0) what = 's'; else if (strcmp(argv[1], "-osf") == 0) what = 'o'; else if (strcmp(argv[1], "-expert") == 0) what = 'x'; else { fprintf(stderr, "unrecognized option %s", argv[1]); exit(1); } suffix = argv[2]; infile = fopen(argv[3], "rb"); if (!infile) { fprintf(stderr, "Can't read file %s\n", argv[3]); exit(1); } outfile = fopen(argv[4], "wb"); if (!outfile) { fprintf(stderr, "Can't write file %s\n", argv[4]); exit(1); } /* read table descriptions */ readmain(); hackcmap(); hacknames(); writemain(); }