/* ** This is the central routine of this section. */ void ttfont_CharStrings(TTStreamWriter& stream, struct TTFONT *font, std::vector<int>& glyph_ids) { Fixed post_format; /* The 'post' table format number. */ post_format = getFixed( font->post_table ); if( post_format.whole != 2 || post_format.fraction != 0 ) throw TTException("TrueType fontdoes not have a format 2.0 'post' table"); /* Emmit the start of the PostScript code to define the dictionary. */ stream.printf("/CharStrings %d dict dup begin\n", glyph_ids.size()); /* Emmit one key-value pair for each glyph. */ for(std::vector<int>::const_iterator i = glyph_ids.begin(); i != glyph_ids.end(); ++i) { if(font->target_type == PS_TYPE_42) /* type 42 */ { stream.printf("/%s %d def\n",ttfont_CharStrings_getname(font, *i), *i); } else /* type 3 */ { stream.printf("/%s{",ttfont_CharStrings_getname(font, *i)); tt_type3_charproc(stream, font, *i); stream.putline("}_d"); /* "} bind def" */ } } stream.putline("end readonly def"); } /* end of ttfont_CharStrings() */
/*----------------------------------------------------------- ** Create the optional "FontInfo" sub-dictionary. -----------------------------------------------------------*/ void ttfont_FontInfo(TTStreamWriter& stream, struct TTFONT *font) { Fixed ItalicAngle; /* We create a sub dictionary named "FontInfo" where we */ /* store information which though it is not used by the */ /* interpreter, is useful to some programs which will */ /* be printing with the font. */ stream.putline("/FontInfo 10 dict dup begin"); /* These names come from the TrueType font's "name" table. */ stream.printf("/FamilyName (%s) def\n",font->FamilyName); stream.printf("/FullName (%s) def\n",font->FullName); if ( font->Copyright != (char*)NULL || font->Trademark != (char*)NULL ) { stream.printf("/Notice (%s", font->Copyright != (char*)NULL ? font->Copyright : ""); stream.printf("%s%s) def\n", font->Trademark != (char*)NULL ? " " : "", font->Trademark != (char*)NULL ? font->Trademark : ""); } /* This information is not quite correct. */ stream.printf("/Weight (%s) def\n",font->Style); /* Some fonts have this as "version". */ stream.printf("/Version (%s) def\n",font->Version); /* Some information from the "post" table. */ ItalicAngle = getFixed( font->post_table + 4 ); stream.printf("/ItalicAngle %d.%d def\n",ItalicAngle.whole,ItalicAngle.fraction); stream.printf("/isFixedPitch %s def\n", getULONG( font->post_table + 12 ) ? "true" : "false" ); stream.printf("/UnderlinePosition %d def\n", (int)getFWord( font->post_table + 8 ) ); stream.printf("/UnderlineThickness %d def\n", (int)getFWord( font->post_table + 10 ) ); stream.putline("end readonly def"); } /* end of ttfont_FontInfo() */
/*------------------------------------------------------------- ** Define the encoding array for this font. ** Since we don't really want to deal with converting all of ** the possible font encodings in the wild to a standard PS ** one, we just explicitly create one for each font. -------------------------------------------------------------*/ void ttfont_encoding(TTStreamWriter& stream, struct TTFONT *font, std::vector<int>& glyph_ids, font_type_enum target_type) { stream.putline("/Encoding StandardEncoding def"); // if (target_type == PS_TYPE_3) { // stream.printf("/Encoding [ "); // for (std::vector<int>::const_iterator i = glyph_ids.begin(); // i != glyph_ids.end(); ++i) { // const char* name = ttfont_CharStrings_getname(font, *i); // stream.printf("/%s ", name); // } // stream.printf("] def\n"); // } else { // stream.putline("/Encoding StandardEncoding def"); // } } /* end of ttfont_encoding() */
/* ** Here is the routine which ties it all together. ** ** Create the array called "sfnts" which ** holds the actual TrueType data. */ void ttfont_sfnts(TTStreamWriter& stream, struct TTFONT *font) { static const char *table_names[] = /* The names of all tables */ { /* which it is worth while */ "cvt ", /* to include in a Type 42 */ "fpgm", /* PostScript font. */ "glyf", "head", "hhea", "hmtx", "loca", "maxp", "prep" } ; struct /* The location of each of */ { ULONG oldoffset; /* the above tables. */ ULONG newoffset; ULONG length; ULONG checksum; } tables[9]; BYTE *ptr; /* A pointer into the origional table directory. */ ULONG x,y; /* General use loop countes. */ int c; /* Input character. */ int diff; ULONG nextoffset; int count; /* How many `important' tables did we find? */ ptr = font->offset_table + 12; nextoffset=0; count=0; /* ** Find the tables we want and store there vital ** statistics in tables[]. */ for (x=0; x < 9; x++ ) { do { diff = strncmp( (char*)ptr, table_names[x], 4 ); if ( diff > 0 ) /* If we are past it. */ { tables[x].length = 0; diff = 0; } else if ( diff < 0 ) /* If we haven't hit it yet. */ { ptr += 16; } else if ( diff == 0 ) /* Here it is! */ { tables[x].newoffset = nextoffset; tables[x].checksum = getULONG( ptr + 4 ); tables[x].oldoffset = getULONG( ptr + 8 ); tables[x].length = getULONG( ptr + 12 ); nextoffset += ( ((tables[x].length + 3) / 4) * 4 ); count++; ptr += 16; } } while (diff != 0); } /* end of for loop which passes over the table directory */ /* Begin the sfnts array. */ sfnts_start(stream); /* Generate the offset table header */ /* Start by copying the TrueType version number. */ ptr = font->offset_table; for (x=0; x < 4; x++) { sfnts_pputBYTE( stream, *(ptr++) ); } /* Now, generate those silly numTables numbers. */ sfnts_pputUSHORT(stream, count); /* number of tables */ if ( count == 9 ) { sfnts_pputUSHORT(stream, 7); /* searchRange */ sfnts_pputUSHORT(stream, 3); /* entrySelector */ sfnts_pputUSHORT(stream, 81); /* rangeShift */ } #ifdef DEBUG_TRUETYPE else { debug("only %d tables selected",count); } #endif /* Now, emmit the table directory. */ for (x=0; x < 9; x++) { if ( tables[x].length == 0 ) /* Skip missing tables */ { continue; } /* Name */ sfnts_pputBYTE( stream, table_names[x][0] ); sfnts_pputBYTE( stream, table_names[x][1] ); sfnts_pputBYTE( stream, table_names[x][2] ); sfnts_pputBYTE( stream, table_names[x][3] ); /* Checksum */ sfnts_pputULONG( stream, tables[x].checksum ); /* Offset */ sfnts_pputULONG( stream, tables[x].newoffset + 12 + (count * 16) ); /* Length */ sfnts_pputULONG( stream, tables[x].length ); } /* Now, send the tables */ for (x=0; x < 9; x++) { if ( tables[x].length == 0 ) /* skip tables that aren't there */ { continue; } #ifdef DEBUG_TRUETYPE debug("emmiting table '%s'",table_names[x]); #endif /* 'glyf' table gets special treatment */ if ( strcmp(table_names[x],"glyf")==0 ) { sfnts_glyf_table(stream,font,tables[x].oldoffset,tables[x].length); } else /* Other tables may not exceed */ { /* 65535 bytes in length. */ if ( tables[x].length > 65535 ) { throw TTException("TrueType font has a table which is too long"); } /* Start new string if necessary. */ sfnts_new_table(stream, tables[x].length); /* Seek to proper position in the file. */ fseek( font->file, tables[x].oldoffset, SEEK_SET ); /* Copy the bytes of the table. */ for ( y=0; y < tables[x].length; y++ ) { if ( (c = fgetc(font->file)) == EOF ) { throw TTException("TrueType font may be corrupt (reason 7)"); } sfnts_pputBYTE(stream, c); } } /* Padd it out to a four byte boundary. */ y=tables[x].length; while ( (y % 4) != 0 ) { sfnts_pputBYTE(stream, 0); y++; #ifdef DEBUG_TRUETYPE_INLINE puts("\n% pad byte:\n"); #endif } } /* End of loop for all tables */ /* Close the array. */ sfnts_end_string(stream); stream.putline("]def"); } /* end of ttfont_sfnts() */
/*--------------------------------------------------------------------- ** Write the header for a PostScript font. ---------------------------------------------------------------------*/ void ttfont_header(TTStreamWriter& stream, struct TTFONT *font) { int VMMin; int VMMax; /* ** To show that it is a TrueType font in PostScript format, ** we will begin the file with a specific string. ** This string also indicates the version of the TrueType ** specification on which the font is based and the ** font manufacturer's revision number for the font. */ if ( font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID) { stream.printf("%%!PS-TrueTypeFont-%d.%d-%d.%d\n", font->TTVersion.whole, font->TTVersion.fraction, font->MfrRevision.whole, font->MfrRevision.fraction); } /* If it is not a Type 42 font, we will use a different format. */ else { stream.putline("%!PS-Adobe-3.0 Resource-Font"); } /* See RBIIp 641 */ /* We will make the title the name of the font. */ stream.printf("%%%%Title: %s\n",font->FullName); /* If there is a Copyright notice, put it here too. */ if ( font->Copyright != (char*)NULL ) { stream.printf("%%%%Copyright: %s\n",font->Copyright); } /* We created this file. */ if ( font->target_type == PS_TYPE_42 ) { stream.putline("%%Creator: Converted from TrueType to type 42 by PPR"); } else if (font->target_type == PS_TYPE_42_3_HYBRID) { stream.putline("%%Creator: Converted from TypeType to type 42/type 3 hybrid by PPR"); } else { stream.putline("%%Creator: Converted from TrueType to type 3 by PPR"); } /* If VM usage information is available, print it. */ if ( font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID) { VMMin = (int)getULONG( font->post_table + 16 ); VMMax = (int)getULONG( font->post_table + 20 ); if ( VMMin > 0 && VMMax > 0 ) stream.printf("%%%%VMUsage: %d %d\n",VMMin,VMMax); } /* Start the dictionary which will eventually */ /* become the font. */ if (font->target_type == PS_TYPE_42) { stream.putline("15 dict begin"); } else { stream.putline("25 dict begin"); /* Type 3 fonts will need some subroutines here. */ stream.putline("/_d{bind def}bind def"); stream.putline("/_m{moveto}_d"); stream.putline("/_l{lineto}_d"); stream.putline("/_cl{closepath eofill}_d"); stream.putline("/_c{curveto}_d"); stream.putline("/_sc{7 -1 roll{setcachedevice}{pop pop pop pop pop pop}ifelse}_d"); stream.putline("/_e{exec}_d"); } stream.printf("/FontName /%s def\n",font->PostName); stream.putline("/PaintType 0 def"); if (font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID) { stream.putline("/FontMatrix[1 0 0 1 0 0]def"); } else { stream.putline("/FontMatrix[.001 0 0 .001 0 0]def"); } stream.printf("/FontBBox[%d %d %d %d]def\n",font->llx-1,font->lly-1,font->urx,font->ury); if (font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID) { stream.printf("/FontType 42 def\n", font->target_type ); } else { stream.printf("/FontType 3 def\n", font->target_type ); } } /* end of ttfont_header() */
/*---------------------------------------------------------------- ** Emmit the code to finish up the dictionary and turn ** it into a font. ----------------------------------------------------------------*/ void ttfont_trailer(TTStreamWriter& stream, struct TTFONT *font) { /* If we are generating a type 3 font, we need to provide */ /* a BuildGlyph and BuildChar proceedures. */ if (font->target_type == PS_TYPE_3 || font->target_type == PS_TYPE_42_3_HYBRID) { stream.put_char('\n'); stream.putline("/BuildGlyph"); stream.putline(" {exch begin"); /* start font dictionary */ stream.putline(" CharStrings exch"); stream.putline(" 2 copy known not{pop /.notdef}if"); stream.putline(" true 3 1 roll get exec"); stream.putline(" end}_d"); stream.put_char('\n'); /* This proceedure is for compatiblity with */ /* level 1 interpreters. */ stream.putline("/BuildChar {"); stream.putline(" 1 index /Encoding get exch get"); stream.putline(" 1 index /BuildGlyph get exec"); stream.putline("}_d"); stream.put_char('\n'); } /* If we are generating a type 42 font, we need to check to see */ /* if this PostScript interpreter understands type 42 fonts. If */ /* it doesn't, we will hope that the Apple TrueType rasterizer */ /* has been loaded and we will adjust the font accordingly. */ /* I found out how to do this by examining a TrueType font */ /* generated by a Macintosh. That is where the TrueType interpreter */ /* setup instructions and part of BuildGlyph came from. */ if (font->target_type == PS_TYPE_42 || font->target_type == PS_TYPE_42_3_HYBRID) { stream.put_char('\n'); /* If we have no "resourcestatus" command, or FontType 42 */ /* is unknown, leave "true" on the stack. */ stream.putline("systemdict/resourcestatus known"); stream.putline(" {42 /FontType resourcestatus"); stream.putline(" {pop pop false}{true}ifelse}"); stream.putline(" {true}ifelse"); /* If true, execute code to produce an error message if */ /* we can't find Apple's TrueDict in VM. */ stream.putline("{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse"); /* Since we are expected to use Apple's TrueDict TrueType */ /* reasterizer, change the font type to 3. */ stream.putline("/FontType 3 def"); /* Define a string to hold the state of the Apple */ /* TrueType interpreter. */ stream.putline(" /TrueState 271 string def"); /* It looks like we get information about the resolution */ /* of the printer and store it in the TrueState string. */ stream.putline(" TrueDict begin sfnts save"); stream.putline(" 72 0 matrix defaultmatrix dtransform dup"); stream.putline(" mul exch dup mul add sqrt cvi 0 72 matrix"); stream.putline(" defaultmatrix dtransform dup mul exch dup"); stream.putline(" mul add sqrt cvi 3 -1 roll restore"); stream.putline(" TrueState initer end"); /* This BuildGlyph procedure will look the name up in the */ /* CharStrings array, and then check to see if what it gets */ /* is a procedure. If it is, it executes it, otherwise, it */ /* lets the TrueType rasterizer loose on it. */ /* When this proceedure is executed the stack contains */ /* the font dictionary and the character name. We */ /* exchange arguments and move the dictionary to the */ /* dictionary stack. */ stream.putline(" /BuildGlyph{exch begin"); /* stack: charname */ /* Put two copies of CharStrings on the stack and consume */ /* one testing to see if the charname is defined in it, */ /* leave the answer on the stack. */ stream.putline(" CharStrings dup 2 index known"); /* stack: charname CharStrings bool */ /* Exchange the CharStrings dictionary and the charname, */ /* but if the answer was false, replace the character name */ /* with ".notdef". */ stream.putline(" {exch}{exch pop /.notdef}ifelse"); /* stack: CharStrings charname */ /* Get the value from the CharStrings dictionary and see */ /* if it is executable. */ stream.putline(" get dup xcheck"); /* stack: CharStrings_entry */ /* If is a proceedure. Execute according to RBIIp 277-278. */ stream.putline(" {currentdict systemdict begin begin exec end end}"); /* Is a TrueType character index, let the rasterizer at it. */ stream.putline(" {TrueDict begin /bander load cvlit exch TrueState render end}"); stream.putline(" ifelse"); /* Pop the font's dictionary off the stack. */ stream.putline(" end}bind def"); /* This is the level 1 compatibility BuildChar procedure. */ /* See RBIIp 281. */ stream.putline(" /BuildChar{"); stream.putline(" 1 index /Encoding get exch get"); stream.putline(" 1 index /BuildGlyph get exec"); stream.putline(" }bind def"); /* Here we close the condition which is true */ /* if the printer has no built-in TrueType */ /* rasterizer. */ stream.putline("}if"); stream.put_char('\n'); } /* end of if Type 42 not understood. */ stream.putline("FontName currentdict end definefont pop"); /* stream.putline("%%EOF"); */ } /* end of ttfont_trailer() */