/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab: * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Golden Hills Computer Services code. * * The Initial Developer of the Original Code is * Brian Stell . * * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brian Stell . * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // // This code converts TrueType to Postscript Type 8 fonts. (Note 1) // // The main entry point is FT2SubsetToType8() // // Note 1: actually any FreeType2 font that support FT_Outline_Decompose // should work // #include #include #include "nsCidMap.h" #include "nsType1.h" #include "nsType8.h" #include "nsIFreeType2.h" #include "nsIServiceManager.h" #include "nsISignatureVerifier.h" #include "plbase64.h" #include "nsCRT.h" #define DEFAULT_CMAP_SIZE 10240 #define HEXASCII_LINE_LEN 64 static void hex_out(unsigned char *buf, PRUint32 n, FILE *f, PRUint32 *pos); static void flatten_name(char *aToName); static int FT2SubsetToCidKeyedType1(nsIFreeType2 *aFt2, FT_Face aFace, const PRUnichar *aCharIDs, int aLen, const char *aFontName, const char *aRegistry, const char *aEncoding, int aSupplement, int aWmode, int aLenIV, FILE *aFile); void AddCIDCheckCode(FILE *aFile) { fprintf(aFile, "%%\n"); fprintf(aFile, "%% Check if CID is supported\n"); fprintf(aFile, "%%\n"); fprintf(aFile, "/CanUseCIDFont false def\n"); fprintf(aFile, "%% If level at least level 2 check if CID is supported\n"); fprintf(aFile, "%% or it has been convert to (base) level 2\n"); fprintf(aFile, "version cvi 2000 ge {\n"); fprintf(aFile, " /CIDInit /ProcSet resourcestatus\n"); fprintf(aFile, " { pop pop /CanUseCIDFont true def }\n"); fprintf(aFile, " { /MozConvertedToLevel2 where \n"); fprintf(aFile, " { pop MozConvertedToLevel2 { /CanUseCIDFont true def" " } if } if\n"); fprintf(aFile, " }\n"); fprintf(aFile, " ifelse\n"); fprintf(aFile, "} if\n"); fprintf(aFile, "%% If it has been converted to level 1 it is very big but\n"); fprintf(aFile, "%% guaranteed to be compatible\n"); fprintf(aFile, "/MozConvertedToLevel1 where {\n"); fprintf(aFile, " pop MozConvertedToLevel1 { /CanUseCIDFont true def } if\n"); fprintf(aFile, "} if\n"); fprintf(aFile, "CanUseCIDFont not {\n"); fprintf(aFile, " %%\n"); fprintf(aFile, " %% Cannot print this so tell the user how to convert it\n"); fprintf(aFile, " %%\n"); // IMPROVMENT: add the document title and/or user id fprintf(aFile, " /Times-Roman findfont 12 scalefont setfont\n"); fprintf(aFile, " 50 700 moveto currentpoint\n"); fprintf(aFile, " (The Postscript interpreter in your printer is ) show\n"); fprintf(aFile, " version show\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint\n"); fprintf(aFile, " (This printout requires at least version 2015 or " "greater) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint\n"); fprintf(aFile, " (To make a Unix/Linux Gecko browser (eg: Netscape or " "Mozilla) produce ) show\n"); fprintf(aFile, " (output that will work ) show\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint\n"); fprintf(aFile, " (on any level 2 interpreter change the \"Print Command\" " "to use ) show\n"); fprintf(aFile, " (Ghostscript to convert the output) show\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint\n"); fprintf(aFile, " (down to basic level 2; eg: change the print command " "from) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint 20 0 rmoveto\n"); fprintf(aFile, " (lpr [OPTIONS]) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint\n"); fprintf(aFile, " (to \\(all on one line\\)) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint 20 0 rmoveto\n"); fprintf(aFile, " (gs -q -sDEVICE=pswrite -sOutputFile=- -dNOPAUSE " "-dBATCH ) show\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint 60 0 rmoveto\n"); fprintf(aFile, " (-dMozConvertedToLevel2=true - | lpr [OPTIONS]) show\n"); fprintf(aFile, " /Times-Roman findfont 8 scalefont setfont\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint\n"); fprintf(aFile, " (for level 1 use: ) show\n"); fprintf(aFile, " moveto 0 -15 rmoveto currentpoint 20 0 rmoveto\n"); fprintf(aFile, " (gs -q -sDEVICE=pswrite -sOutputFile=- -dNOPAUSE ) show\n"); fprintf(aFile, " ( -dBATCH -dLanguageLevel=1 ) show\n"); fprintf(aFile, " (-dMozConvertedToLevel1=true - | lpr [OPTIONS]) show\n"); fprintf(aFile, " /Times-Roman findfont 12 scalefont setfont\n"); fprintf(aFile, " moveto 0 -50 rmoveto currentpoint\n"); fprintf(aFile, " (If you are printing this from a file use ) show\n"); fprintf(aFile, " (Ghostscript to convert it to basic level 2;) show\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint\n"); fprintf(aFile, " (eg: change the print command from) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint 20 0 rmoveto\n"); fprintf(aFile, " (lpr [OPTIONS] ) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint\n"); fprintf(aFile, " (to) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint 20 0 rmoveto\n"); fprintf(aFile, " (gs -q -sDEVICE=pswrite -sOutputFile=- -dNOPAUSE " "-dBATCH ) show\n"); fprintf(aFile, " moveto 0 -30 rmoveto currentpoint 40 0 rmoveto\n"); fprintf(aFile, " (-dMozConvertedToLevel2=true | lpr [OPTIONS]" ") show\n"); fprintf(aFile, " /Times-Roman findfont 8 scalefont setfont\n"); fprintf(aFile, " moveto 0 -20 rmoveto currentpoint\n"); fprintf(aFile, " (for level 1 use: ) show\n"); fprintf(aFile, " moveto 0 -15 rmoveto currentpoint 20 0 rmoveto\n"); fprintf(aFile, " (gs -q -sDEVICE=pswrite -sOutputFile=- -dNOPAUSE -dBATCH " ") show\n"); fprintf(aFile, " (-dLanguageLevel=1 -dMozConvertedToLevel1=true ) show\n"); fprintf(aFile, " ( | lpr [OPTIONS]) show\n"); fprintf(aFile, " /Times-Roman findfont 12 scalefont setfont\n"); fprintf(aFile, " showpage\n"); fprintf(aFile, " quit\n"); fprintf(aFile, "} if\n"); fprintf(aFile, "\n"); } static char * FT2ToType1FontName(FT_Face aFace, int aWmode) { char *fontname; int name_len; name_len = strlen(aFace->family_name); // family name name_len += 1 + strlen(aFace->style_name); // '.' + style name name_len += 11; // '.' + face_index name_len += 2; // '.' + wmode name_len += 1; // string terminator fontname = (char *)PR_Malloc(name_len); if (!fontname) { return nsnull; } sprintf(fontname, "%s.%s.%ld.%d", aFace->family_name, aFace->style_name, aFace->face_index, aWmode?1:0); flatten_name(fontname); return fontname; } static char * FontNameToType8CmapName(char *aFontName) { char *cmapname; /* get cmap name */ cmapname = (char *)PR_Malloc(strlen(aFontName)+1+5); if (!cmapname) { return nsnull; } sprintf((char*)cmapname, "%s_cmap", aFontName); return cmapname; } char * FT2ToType8CidFontName(FT_Face aFace, int aWmode) { int cidfontname_len; char *cmapname = NULL; char *fontname = NULL; char *cidfontname = NULL; fontname = FT2ToType1FontName(aFace, aWmode); if (!fontname) { goto done; } /* get cmap name */ cmapname = FontNameToType8CmapName(fontname); if (!cmapname) { goto done; } /* generate the font name */ cidfontname_len = strlen(fontname) + 2 + strlen(cmapname) + 1; cidfontname = (char *)PR_Malloc(cidfontname_len); if (!cidfontname) { NS_ERROR("Failed to alloc space for font name"); goto done; } sprintf((char*)cidfontname, "%s--%s", fontname, cmapname); done: if (fontname) PR_Free((void*)fontname); if (cmapname) PR_Free((void*)cmapname); return (char *)cidfontname; } char * FT2SubsetToEncoding(const PRUnichar *aCharIDs, PRUint32 aNumChars) { unsigned char* rawDigest; char *encoding = NULL; nsresult rv; PRUint32 len; HASHContextStr* id; // // generate a cryptographic digest of the charIDs to use as the "encoding" // nsCOMPtr verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { rv = verifier->HashBegin(nsISignatureVerifier::SHA1, &id); if (NS_SUCCEEDED(rv)) { rv = verifier->HashUpdate(id, (const char*)aCharIDs, aNumChars*sizeof(PRUnichar)); if (NS_SUCCEEDED(rv)) { rawDigest = (unsigned char*)PR_Malloc( nsISignatureVerifier::SHA1_LENGTH); if (rawDigest) { rv = verifier->HashEnd(id, &rawDigest, &len, nsISignatureVerifier::SHA1_LENGTH); if (NS_SUCCEEDED(rv)) { encoding = PL_Base64Encode((char*)rawDigest, len, encoding); } PR_Free(rawDigest); if (encoding) return encoding; } } } } // if psm is not available then just make a string based // on the time and a simple hash of the chars PRUint32 hash = nsCRT::HashCode(aCharIDs, &aNumChars); encoding = (char*)PR_Malloc(10+1+10+1+10+1); if (!encoding) return nsnull; PRTime tmp_now, now = PR_Now(); PRTime usec_per_sec; PRUint32 t_sec, t_usec; LL_I2L(usec_per_sec, PR_USEC_PER_SEC); LL_DIV(tmp_now, now, usec_per_sec); LL_L2I(t_sec, tmp_now); LL_MOD(tmp_now, now, usec_per_sec); LL_L2I(t_usec, tmp_now); sprintf(encoding, "%u.%u.%u", hash, t_sec, t_usec); return encoding; } PRBool FT2SubsetToType8(FT_Face aFace, const PRUnichar *aCharIDs, PRUint32 aNumChars, int aWmode, FILE *aFile) { PRUint32 i; char *fontname = NULL; char *cmapname = NULL; char *cidfontname = NULL; char *registry; char *encoding = NULL; int supplement, lenIV; PRUint32 CIDs_buf[5000]; PRUint32 *CIDs = CIDs_buf; int cmap_type = 0; PRBool status = PR_FALSE; nsresult rv; nsCOMPtr ft2 = do_GetService(NS_FREETYPE2_CONTRACTID, &rv); if (NS_FAILED(rv)) { NS_ERROR("Failed to get nsIFreeType2 service"); goto done; } if ((aNumChars+1) > sizeof(CIDs_buf)/sizeof(CIDs_buf[0])) CIDs = (PRUint32 *)PR_Malloc((aNumChars+1)*sizeof(CIDs_buf[0])); if (!CIDs) { NS_ERROR("Failed to alloc space for CIDs"); goto done; } fontname = FT2ToType1FontName(aFace, aWmode); if (!fontname) goto done; cmapname = FontNameToType8CmapName(fontname); if (!cmapname) goto done; cidfontname = FT2ToType8CidFontName(aFace, aWmode); if (!cidfontname) goto done; registry = "mozilla_printout"; encoding = FT2SubsetToEncoding(aCharIDs, aNumChars); if (!encoding) goto done; supplement = 0; lenIV = 0; // // Build the Char to CID map // // reserve one space for the notdef glyph // then map the the chars sequentially to glyphs // for (i=0; iunits_per_EM; if (aLen+2 > DEFAULT_CMAP_SIZE) { cmapinfo = (unsigned int*)PR_Calloc(aLen+2, sizeof(unsigned int)); if (!cmapinfo) return PR_FALSE; } fprintf(aFile, "%%%%DocumentNeededResources: procset CIDInit\n"); fprintf(aFile, "%%%%IncludeResource: procset CIDInit\n"); fprintf(aFile, "%%%%BeginResource: CIDFont %s\n", aFontName); fprintf(aFile, "%%%%Title: (%s %s %s %d)\n", aFontName, aRegistry, aEncoding, aSupplement); fprintf(aFile, "%%%%Version: 1\n"); fprintf(aFile, "\n"); fprintf(aFile, "/CIDInit /ProcSet findresource begin\n"); fprintf(aFile, "\n"); fprintf(aFile, "20 dict begin\n"); fprintf(aFile, "\n"); fprintf(aFile, "/CIDFontName /%s def\n", aFontName); fprintf(aFile, "/CIDFontVersion 1 def\n"); fprintf(aFile, "/CIDFontType 0 def\n"); fprintf(aFile, "\n"); fprintf(aFile, "/CIDSystemInfo 3 dict dup begin\n"); fprintf(aFile, " /Registry (%s) def \n", aRegistry); fprintf(aFile, " /Ordering (%s) def \n", aEncoding); fprintf(aFile, " /Supplement 0 def \n"); fprintf(aFile, "end def\n"); fprintf(aFile, "\n"); fprintf(aFile, "/FontBBox [%d %d %d %d] def\n", toCS(upm, aFace->bbox.xMin), toCS(upm, aFace->bbox.yMin), toCS(upm, aFace->bbox.xMax), toCS(upm, aFace->bbox.yMax)); fprintf(aFile, "\n"); /* measure the notdef glyph length */ cmapinfo[0] = FT2GlyphToType1CharString(aFt2, aFace, 0, aWmode, aLenIV, NULL); num_charstrings = 1; charstrings_len = cmapinfo[0]; /* get charstring lengths */ max_charstring = cmapinfo[0]; for (i=0; iGetCharIndex(aFace, aCharIDs[i], &glyphID); cmapinfo[i+1] = FT2GlyphToType1CharString(aFt2, aFace, glyphID, aWmode, aLenIV, NULL); charstrings_len += cmapinfo[i+1]; if (cmapinfo[i+1]) num_charstrings++; if (cmapinfo[i+1] > max_charstring) max_charstring = cmapinfo[i+1]; } cmapinfo[i+1] = 0; if (max_charstring > sizeof(charstring_buf)) charstring = (unsigned char *)PR_Malloc(max_charstring); if (!charstring) { NS_ERROR("Failed to alloc bytes for charstring"); return PR_FALSE; } line_pos = 0; fd_len = 0; gd_len = 3; cmap_array_len = 1 + aLen + 1; cidmap_len = (fd_len+gd_len) * cmap_array_len; fprintf(aFile, "/CIDMapOffset %d def\n", 0); fprintf(aFile, "/FDBytes %d def\n", fd_len); fprintf(aFile, "/GDBytes %d def\n", gd_len); fprintf(aFile, "/CIDCount %d def\n", cmap_array_len-1); fprintf(aFile, "\n"); fprintf(aFile, "/FDArray 1 array\n"); fprintf(aFile, "\n"); fprintf(aFile, "dup 0\n"); fprintf(aFile, " %%ADOBeginFontDict\n"); fprintf(aFile, " 14 dict begin\n"); fprintf(aFile, " \n"); fprintf(aFile, " /FontName /%s-Proportional def\n", aFontName); fprintf(aFile, " /FontType 1 def\n"); fprintf(aFile, " /FontMatrix [ 0.001 0 0 0.001 0 0 ] def\n"); fprintf(aFile, " /PaintType 0 def\n"); fprintf(aFile, " \n"); fprintf(aFile, " %%ADOBeginPrivateDict\n"); fprintf(aFile, " /Private 25 dict dup begin\n"); fprintf(aFile, " /lenIV %d def\n", aLenIV); fprintf(aFile, " /SubrCount 0 def\n"); fprintf(aFile, "\n"); fprintf(aFile, " end def\n"); fprintf(aFile, " %%ADOEndPrivateDict\n"); fprintf(aFile, "currentdict end\n"); fprintf(aFile, "%%ADOEndFontDict\n"); fprintf(aFile, "put\n"); fprintf(aFile, "\n"); fprintf(aFile, "def\n"); fprintf(aFile, "\n"); data_len = cidmap_len + charstrings_len; // // output the BeginData tag // // binary data is 1/2 the size of hexascii data but printers // get confused by it so we don't use it // data_len2 = 27; // "(Hex) %10d StartData\n" data_len2 += (data_len+1)*2; // the data (+pad byte) in hexascii // new-lines at the end of the hexascii lines data_len2 += ((data_len+1)*2)/HEXASCII_LINE_LEN; data_len2 += 2; // EOD + new-line fprintf(aFile, "%%%%BeginData: %d Binary Bytes\n", data_len2); fprintf(aFile, "(Hex) %10d StartData\n", data_len); // // output the CIDMap // data_offset = cidmap_len; for (i=0; i> 16) & 0xFF; buf[l++] = (data_offset >> 8) & 0xFF; buf[l++] = data_offset & 0xFF; } else { NS_ASSERTION(gd_len==3, "only support gd_len == 3"); return PR_FALSE; } hex_out(buf, fd_len + gd_len, aFile, &line_pos); data_offset += cmapinfo[i]; } // // output the charStrings // // output the notdef glyph charstring_len = FT2GlyphToType1CharString(aFt2, aFace, 0, aWmode, aLenIV, charstring); hex_out(charstring, charstring_len, aFile, &line_pos); /* output the charstrings for the glyphs */ for (i=0; iGetCharIndex(aFace, aCharIDs[i], &glyphID); charstring_len = FT2GlyphToType1CharString(aFt2, aFace, glyphID, aWmode, aLenIV, charstring); NS_ASSERTION(charstring_len==cmapinfo[i+1], "glyph data changed"); hex_out(charstring, charstring_len, aFile, &line_pos); } // add one byte padding so the interperter can always do 2 byte reads charstring[0] = 0; hex_out(charstring, 1, aFile, &line_pos); // Output the EOD fprintf(aFile, ">\n"); // EOD /* StartData does an implicit "end end" */ fprintf(aFile, "%%%%EndData\n"); fprintf(aFile, "%%%%EndResource\n"); if (cmapinfo != cmapinfo_buf) { PR_Free(cmapinfo); } if (charstring != charstring_buf) { PR_Free(charstring); } return PR_TRUE; } static void flatten_name(char *aString) { for (; *aString; aString++) { if (*aString == ' ') *aString = '_'; else if (*aString == '(') *aString = '_'; else if (*aString == ')') *aString = '_'; } } static void hex_out(unsigned char *aBuf, PRUint32 aLen, FILE *f, PRUint32 *line_pos) { PRUint32 i, pos = *line_pos; for (i=0; i= HEXASCII_LINE_LEN) { fprintf(f, "\n"); pos = 0; } } *line_pos = pos; }