Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4987 lines
146 KiB

/*++
Copyright (c) 1996 Adobe Systems Incorporated
Copyright (c) 1996 Microsoft Corporation
Module Name:
afm2ntm.c
Abstract:
Convert AFM to NTM.
Environment:
Windows NT PostScript driver: makentf utility.
Revision History:
09/11/97 -ksuzuki-
Added code to support localized menu name, fixed pitch CJK font, and
two IFIMETRICS.
12/11/96 -rkiesler-
Wrote functions.
09/16/96 -slam-
Created initial framework.
--*/
#include "lib.h"
#include "ppd.h"
#include "pslib.h"
#include "psglyph.h"
#include "afm2ntm.h"
#include "cjkfonts.h"
#include "winfont.h"
//
// Extarnals
//
extern PSTR pAFMFileName;
extern BOOL bVerbose;
extern BOOL bOptimize;
VOID
SortWinCPT(
IN OUT WINCPT *pSortedWinCpts,
IN WINCPTOPS *pCPtoPS
);
//
// Globals.
//
#define NUM_DPCHARSET 16
PUPSCODEPT pPsNames;
BOOL isSymbolCharSet=FALSE;
//
// The purpose of the NTMSIZEINFO and IFIMETRICSSIZEINFO structures is
// to hold aligned size of each structure element.
//
typedef struct _NTMSIZEINFO
{
int nSize;
int nFontNameSize;
int nDisplayNameSize;
int nGlyphSetNameSize;
int nCharWidthSize;
int nKernPairSize;
int nCharDefSize;
int nTotalSize;
}
NTMSIZEINFO;
typedef struct _IFIMETRICSSIZEINFO
{
int nSize;
int nIfiExtraSize;
int nFamilyNameSize;
int nStyleNameSize;
int nFaceNameSize;
int nUniqueNameSize;
int nFontSimSize;
int nBoldSize;
int nItalicSize;
int nBoldItalicSize;
int nCharSetSize;
int nTotalSize;
}
IFIMETRICSSIZEINFO;
#define INIT_NTMSIZEINFO(nsi) \
{\
(nsi).nSize = -1;\
(nsi).nFontNameSize = -1;\
(nsi).nDisplayNameSize = -1;\
(nsi).nGlyphSetNameSize = -1;\
(nsi).nCharWidthSize = -1;\
(nsi).nKernPairSize = -1;\
(nsi).nCharDefSize = -1;\
(nsi).nTotalSize = -1;\
};
#define INIT_IFIMETRICSSIZEINFO(isi) \
{\
(isi).nSize = -1;\
(isi).nIfiExtraSize = -1;\
(isi).nFamilyNameSize = -1;\
(isi).nStyleNameSize = -1;\
(isi).nFaceNameSize = -1;\
(isi).nUniqueNameSize = -1;\
(isi).nFontSimSize = -1;\
(isi).nBoldSize = -1;\
(isi).nItalicSize = -1;\
(isi).nBoldItalicSize = -1;\
(isi).nCharSetSize = -1;\
(isi).nTotalSize = -1;\
};
#define GET_NTMTOTALSIZE(nsi) \
{\
if ((nsi).nSize == -1 \
|| (nsi).nFontNameSize == -1 \
|| (nsi).nDisplayNameSize == -1 \
|| (nsi).nGlyphSetNameSize == -1 \
|| (nsi).nCharWidthSize == -1 \
|| (nsi).nKernPairSize == -1 \
|| (nsi).nCharDefSize == -1)\
{\
ERR(("makentf - afm2ntm: GET_NTMTOTALSIZE\n"));\
}\
(nsi).nTotalSize = (nsi).nSize \
+ (nsi).nFontNameSize \
+ (nsi).nDisplayNameSize \
+ (nsi).nGlyphSetNameSize \
+ (nsi).nCharWidthSize \
+ (nsi).nKernPairSize \
+ (nsi).nCharDefSize;\
};
#define GET_IFIMETRICSTOTALSIZE(isi) \
{\
if ((isi).nSize == -1 \
|| (isi).nIfiExtraSize == -1 \
|| (isi).nFamilyNameSize == -1 \
|| (isi).nStyleNameSize == -1 \
|| (isi).nFaceNameSize == -1 \
|| (isi).nUniqueNameSize == -1 \
|| (isi).nFontSimSize == -1 \
|| (isi).nBoldSize == -1 \
|| (isi).nItalicSize == -1 \
|| (isi).nBoldItalicSize == -1 \
|| (isi).nCharSetSize == -1)\
{\
ERR(("makentf - afm2ntm: GET_IFIMETRICSTOTALSIZE\n"));\
}\
(isi).nTotalSize = (isi).nSize \
+ (isi).nIfiExtraSize \
+ (isi).nFamilyNameSize \
+ (isi).nStyleNameSize \
+ (isi).nFaceNameSize \
+ (isi).nUniqueNameSize \
+ (isi).nFontSimSize \
+ (isi).nBoldSize \
+ (isi).nItalicSize \
+ (isi).nBoldItalicSize \
+ (isi).nCharSetSize;\
};
#define FREE_AFMTONTM_MEMORY \
{\
if (pNameStr2 != pNameStr) MemFree(pNameStr2);\
MemFree(pNameStr);\
MemFree(pFontChars);\
MemFree(pCharDefTbl);\
};
PNTM
AFMToNTM(
PBYTE pAFM,
PGLYPHSETDATA pGlyphSetData,
PULONG pUniPs,
CHSETSUPPORT *pCharSet,
BOOL bIsCJKFont,
BOOL bIsPitchChanged
)
/*++
Routine Description:
Convert AFM to NTM.
Arguments:
pAFM - pointer to memory mapped AFM file.
pGlyphSetData - pointer to the GLYPHSETDATA struct which represents
this font's preferred charset.
pUniPs - Points to a table which maps 0-based Glyph Indices of chars
in the GLYPHRUNS of the GLYPHSETDATA struct for this font to indices
into the UnicodetoPs structure which maps Unicode points to PS char
information. This mapping array is created by the CreateGlyphSets
function defined in this module.
Return Value:
NULL => error
otherwise => ptr to a NTM.
--*/
{
USHORT multiCharSet=0;
IFIEXTRA *pifiEx;
PBYTE pChMetrics, pToken, pChWidthTok, pCharDefTbl, pby;
CHSETSUPPORT chSets;
PSZ pszGlyphSetName, pszFontName, pszFamilyName, pszEngFamilyName;
int cGlyphSetNameLen, cFontNameLen, cFamilyNameLen, cEngFamilyNameLen;
int cNameStrLen, wcNameStrLen, cNameStr2Len, wcNameStr2Len, chCnt;
PNTM pNTM;
PIFIMETRICS pifi, pifi2;
PWIDTHRUN pWidthRuns;
SHORT sIntLeading, sAscent, sExternalLeading;
ULONG ulNTMSize, ulIFISize, ulIFISize2;
ULONG ulChCnt, ulCharDefTbl, ulKernPairs, ulAliases;
ETMINFO EtmInfo;
PPSFAMILYINFO pFamilyInfo;
RECT rcBBox;
LPPANOSE ppanose;
PGLYPHRUN pGlyphRun;
PSTR pNameStr, pNameStr2;
FD_KERNINGPAIR *pKernPairs;
PPSCHARMETRICS pFontChars;
BOOLEAN bIsVGlyphSet, bIsMSFaceName, bIsFixedPitch, bIsItalic, bIsBold;
USHORT jWinCharSet;
CHARSETINFO csi;
char szStyleName[64], szUniqueID[32];
int i, j;
BOOL bRightFamilyInfo = TRUE;
NTMSIZEINFO nsi;
IFIMETRICSSIZEINFO isi;
VERBOSE(("Entering AFMToNTM...\n"));
INIT_NTMSIZEINFO(nsi);
INIT_IFIMETRICSSIZEINFO(isi);
pNTM = NULL;
pFontChars = NULL;
pCharDefTbl = NULL;
pNameStr = pNameStr2 = NULL;
szStyleName[0] = szUniqueID[0] = '\0';
if (bVerbose) printf("AFM file name:%s\n", pAFMFileName);
//////////////////////////////////////////////////////////////////////////
//
// We support two kinds of CJK AFM files: Adobe CID font AFM or
// clone PS font AFM. Adobe CID font AFM always has the following
// key-value pairs.
//
// FontName Ryumin-Light
// CharacterSet Adobe-Japan1-1 (or others except 'Adobe-Japan1-0')
// IsCIDFont true (must be there)
//
// Note that the FontName value does not include encoding name.
// Just font family name only.
//
// Clone PS font AFM has the following special key-value pairs.
//
// FontName RicohKaisho
// CharacterSet Adobe-Japan1-0
// IsCIDFont false (or must not be there)
//
// The FontName value of clone PS font AFM shouldn't include encoding
// name neither, the CharacterSet value is 'Adobe-Japan1-0' for J, and
// IsCIDFont key should not be specified or must be 'false'.
//
//////////////////////////////////////////////////////////////////////////
//
// Find which glyphset we are dealing with.
//
bIsVGlyphSet = IsVGlyphSet(pGlyphSetData);
pszGlyphSetName = (PSTR)MK_PTR(pGlyphSetData, dwGlyphSetNameOffset);
cGlyphSetNameLen = strlen(pszGlyphSetName);
if (bOptimize)
{
//
// Set referense mark if optimization option is specified. This mark is
// checked later by the WriteNTF function to exclude unreferenced
// glyphset data when writing to an NTF file.
//
pGlyphSetData->dwReserved[0] = 1;
}
//
// Save number of chars defined in the font.
// Get ptr to AFM char metrics, then the current
// pos should be the character count field.
//
pToken = FindAFMToken(pAFM, PS_CH_METRICS_TOK);
if (pToken == NULL) // Fixed bug 354007
{
ERR(("makentf - afm2ntm: CH Metrics missing\n"));
return NULL;
}
for (i = 0; i < (int) StrLen(pToken); i++)
{
if (!IS_NUM(&pToken[i]))
{
ERR(("makentf - afm2ntm: CH Metrics is not a number\n"));
return NULL;
}
}
chCnt = atoi(pToken);
//
// Get number of GLYPHs from GlyphsetData. We'll need to use define
// char width table entries and char defined entries for all
// possible glyphs in the glyphset, even those that are not defined
// in this particular font.
//
ulChCnt = pGlyphSetData->dwGlyphCount;
//
// Alloc memory for an array of PSCHARMETRICS structs, one for each char
// in the font, and build the table of char metrics.
//
if ((pFontChars =
(PPSCHARMETRICS)
MemAllocZ((size_t) chCnt * sizeof(PSCHARMETRICS))) == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
return NULL;
}
//
// Alloc memory for the IsCharDefined table, an array of bits which
// indicate which chars in the GLYPHSET are defined in this font.
//
ulCharDefTbl = ((ulChCnt + 7) / 8) * sizeof (BYTE);
if ((pCharDefTbl = (PBYTE)MemAllocZ((size_t)ulCharDefTbl)) == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
MemFree(pFontChars);
return NULL;
}
//
// Build table of PSCHARMETRICS info.
//
if (!BuildPSCharMetrics(pAFM, pUniPs, pFontChars, pCharDefTbl, ulChCnt))
{
ERR(("makentf - afm2ntm: BuildPSCharMetrics\n"));
MemFree(pFontChars);
MemFree(pCharDefTbl);
return NULL;
}
//
// Get font name from AFM and use it to obtain the MS family name
// from the table in memory.
//
pszEngFamilyName = NULL;
cEngFamilyNameLen = 0;
pFamilyInfo = NULL;
pszFontName = FindAFMToken(pAFM, PS_FONT_NAME_TOK);
if (pszFontName == NULL) // Fixed bug 354007
{
ERR(("makentf - afm2ntm: Font Name Missing\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
for (cFontNameLen = 0; !EOL(&pszFontName[cFontNameLen]); cFontNameLen++);
pFamilyInfo = (PPSFAMILYINFO) bsearch(pszFontName,
(PBYTE) (((PPSFAMILYINFO) (pFamilyTbl->pTbl))[0].pFontName),
pFamilyTbl->usNumEntries,
sizeof(PSFAMILYINFO),
StrCmp);
if (bIsMSFaceName = (pFamilyInfo != NULL))
{
bRightFamilyInfo = TRUE;
if (bIsPitchChanged && (pFamilyInfo->usPitch == DEFAULT_PITCH))
{
bRightFamilyInfo = FALSE;
if (pFamilyInfo > ((PPSFAMILYINFO) (pFamilyTbl->pTbl)))
{
pFamilyInfo = pFamilyInfo - 1;
if (!StrCmp(pFamilyInfo->pFontName, pszFontName) &&
(pFamilyInfo->usPitch != DEFAULT_PITCH ))
bRightFamilyInfo = TRUE;
}
if (bRightFamilyInfo == FALSE)
{
pFamilyInfo = pFamilyInfo + 1;
if (pFamilyInfo <
(((PPSFAMILYINFO) (pFamilyTbl->pTbl)) + pFamilyTbl->usNumEntries))
{
pFamilyInfo = pFamilyInfo + 1;
if (!StrCmp(pFamilyInfo->pFontName, pszFontName) &&
(pFamilyInfo->usPitch != DEFAULT_PITCH))
bRightFamilyInfo = TRUE;
}
}
}
else if (!bIsPitchChanged && (pFamilyInfo->usPitch != DEFAULT_PITCH))
{
bRightFamilyInfo = FALSE;
if (pFamilyInfo > ((PPSFAMILYINFO) (pFamilyTbl->pTbl)))
{
pFamilyInfo = pFamilyInfo - 1;
if (!StrCmp(pFamilyInfo->pFontName, pszFontName) &&
(pFamilyInfo->usPitch == DEFAULT_PITCH))
bRightFamilyInfo = TRUE;
}
if (bRightFamilyInfo == FALSE)
{
pFamilyInfo = pFamilyInfo + 1;
if (pFamilyInfo <
(((PPSFAMILYINFO) (pFamilyTbl->pTbl)) + pFamilyTbl->usNumEntries))
{
pFamilyInfo = pFamilyInfo + 1;
if (!StrCmp(pFamilyInfo->pFontName, pszFontName) &&
(pFamilyInfo->usPitch == DEFAULT_PITCH))
bRightFamilyInfo = TRUE;
}
}
}
}
if (bIsMSFaceName && (bRightFamilyInfo == TRUE))
{
pszFamilyName = pFamilyInfo->FamilyKey.pName;
cFamilyNameLen = strlen(pszFamilyName);
pszEngFamilyName = pFamilyInfo->pEngFamilyName;
cEngFamilyNameLen = strlen(pszEngFamilyName);
if (!cEngFamilyNameLen) pszEngFamilyName = NULL;
}
else if ((pszFamilyName =
FindAFMToken(pAFM, PS_FONT_FAMILY_NAME_TOK)) != NULL)
{
for (cFamilyNameLen = 0; !EOL(&pszFamilyName[cFamilyNameLen]); cFamilyNameLen++);
}
else
{
pszFamilyName = pszFontName;
cFamilyNameLen = cFontNameLen;
}
if (bVerbose)
{
printf("MSFaceName%sfound:", bIsMSFaceName ? " " : " not ");
printf("%s\n", bIsMSFaceName ? pszFamilyName : "n/a");
printf("MSFaceName length:%d\n", bIsMSFaceName ? cFamilyNameLen : -1);
printf("This is a %s font.\n", bIsVGlyphSet ? "vertical" : "horizontal");
}
//
// Predetermine if this font supports multiple charsets.
//
if (pCharSet)
{
if (CSET_SUPPORT(*pCharSet, CS_ANSI))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_EASTEUROPE))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_RUSSIAN))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_GREEK))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_TURKISH))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_HEBREW))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_ARABIC))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_BALTIC))
multiCharSet++;
if (CSET_SUPPORT(*pCharSet, CS_SYMBOL))
multiCharSet++;
//
// Save Windows Codepage id. Just use the id stored in the first
// CODEPAGEINFO in the GLYPHSETDATA for this font.
//
// The order of this check is important since jWinCharSet
// should match the first dpCharSets array if it exists.
//
if (CSET_SUPPORT(*pCharSet, CS_ANSI))
jWinCharSet = ANSI_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_EASTEUROPE))
jWinCharSet = EASTEUROPE_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_RUSSIAN))
jWinCharSet = RUSSIAN_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_GREEK))
jWinCharSet = GREEK_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_TURKISH))
jWinCharSet = TURKISH_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_HEBREW))
jWinCharSet = HEBREW_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_ARABIC))
jWinCharSet = ARABIC_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_BALTIC))
jWinCharSet = BALTIC_CHARSET;
else if (CSET_SUPPORT(*pCharSet, CS_SYMBOL))
jWinCharSet = SYMBOL_CHARSET;
}
else
{
PCODEPAGEINFO cpi = (PCODEPAGEINFO)MK_PTR(pGlyphSetData, dwCodePageOffset);
jWinCharSet = (USHORT)(cpi->dwWinCharset & 0xffff);
}
//
// Get codepage info for the MultiByteToWideChar function calls.
//
// We want to translate a string into a readable one, not into a symbolic
// one, so that we use ANSI charset when dealing with SYMBOL charset.
//
if (jWinCharSet == SYMBOL_CHARSET)
{
DWORD dwTmp = ANSI_CHARSET;
if (!TranslateCharsetInfo(&dwTmp, &csi, TCI_SRCCHARSET))
csi.ciACP = CP_ACP;
}
else if (!TranslateCharsetInfo((DWORD FAR*)jWinCharSet, &csi, TCI_SRCCHARSET))
csi.ciACP = CP_ACP;
//////////////////////////////////////////////////////////////////////////
//
// Get the size of each element of NTM structure
//
//////////////////////////////////////////////////////////////////////////
//
// Sizes known so far.
//
nsi.nSize = ALIGN4(sizeof (NTM));
nsi.nGlyphSetNameSize = ALIGN4(cGlyphSetNameLen + 1);
nsi.nCharDefSize = ALIGN4(ulCharDefTbl);
//
// Size of the font name. Glyphset name is required for CJK font.
//
nsi.nFontNameSize = ALIGN4(cFontNameLen + 1);
if (bIsCJKFont)
{
nsi.nFontNameSize += nsi.nGlyphSetNameSize;
}
//
// Size of the display name. Use pszFamilyName regardless of
// Roman, C, J, and K fonts. Add one for '@' if it's CJK
// vertical font.
//
i = cFamilyNameLen + 1;
if (bIsCJKFont && bIsVGlyphSet) i++;
nsi.nDisplayNameSize = ALIGN4(i);
//
// Determine if font is fixed pitch.
//
bIsFixedPitch = FALSE;
if (bIsCJKFont)
{
bIsFixedPitch = IsCJKFixedPitchEncoding(pGlyphSetData);
}
else if ((pToken = FindAFMToken(pAFM, PS_PITCH_TOK)) != NULL)
{
if (!StrCmp(pToken, "true"))
{
//
// This is a fixed pitch font.
//
bIsFixedPitch = !StrCmp(pToken, "true");
}
}
if (bIsFixedPitch)
{
nsi.nCharWidthSize = 0;
}
else
{
//
// Proportional font. Determine number of WIDTHRUNs for this font.
//
// Fix bug 240339, jjia, 8/3/98
nsi.nCharWidthSize =
GetAFMCharWidths(pAFM, NULL, pFontChars, pUniPs,
pGlyphSetData->dwGlyphCount, NULL, NULL);
}
//
// Determine if there is the pair kerning info for this font.
//
if (ulKernPairs = GetAFMKernPairs(pAFM, NULL, pGlyphSetData))
{
//
// Account for size of kern pairs.
//
nsi.nKernPairSize = ALIGN4((ulKernPairs + 1) * sizeof(FD_KERNINGPAIR));
}
else
{
nsi.nKernPairSize = 0;
}
//////////////////////////////////////////////////////////////////////////
//
// Get the size of each element of IFIMETRICS structure
//
//////////////////////////////////////////////////////////////////////////
isi.nSize = ALIGN4(sizeof (IFIMETRICS));
//
// From AdobePS5-NT4 5.1, make the size of NT4 IFIEXTRA same as the one
// of NT5 or later versions. NT4 IFIEXTRA size is 16 and NT5 IFIEXTRA
// size is 24.
//
if (sizeof (IFIEXTRA) <= 16)
isi.nIfiExtraSize = 24;
else
isi.nIfiExtraSize = ALIGN4(sizeof (IFIEXTRA));
//
// For Roman we provide single IFIMETRICS, but we provide two IFIMETRICS
// for CJK. Font family name element of the first IFIMETRICS begins with
// a menu name in English then localized one. Font family name element of
// the second IFIMETRICS begins with a localized menu name then English
// one. We use pNameStr and pNameStr2 for the English and localized menu
// names respectively.
//
// Prepare pNameStr. Account for encoding name if we are dealing with
// CJK font.
//
i = 0;
if (bIsCJKFont)
{
if (bIsVGlyphSet)
{
//
// V GS, account for preceding '@' char.
//
i++;
}
if (pszEngFamilyName)
{
//
// IFIMetrics English menu name = [@]fontname
//
if ((pNameStr = (PSTR) MemAllocZ(i + cEngFamilyNameLen + 1)) == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
if (i) pNameStr[0] = '@';
memcpy(&(pNameStr[i]), pszEngFamilyName, cEngFamilyNameLen);
i += cEngFamilyNameLen;
}
else
{
int cGsNameLen;
//
// IFIMetrics English menu name = [@]fontname + GS name string,
// but it does not end with '-H' or '-V'.
//
cGsNameLen = cGlyphSetNameLen - 2;
if ((pNameStr = (PSTR) MemAllocZ(i + cFontNameLen + cGsNameLen + 1)) == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
if (i) pNameStr[0] = '@';
memcpy(&(pNameStr[i]), pszFontName, cFontNameLen);
memcpy(&(pNameStr[i + cFontNameLen]), pszGlyphSetName, cGsNameLen);
i += cFontNameLen + cGsNameLen;
}
}
else
{
if ((pNameStr = (PSTR) MemAllocZ(cFamilyNameLen + 1)) == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
memcpy(pNameStr, pszFamilyName, cFamilyNameLen);
i += cFamilyNameLen;
}
pNameStr[i] = '\0';
cNameStrLen = strlen(pNameStr);
wcNameStrLen = MultiByteToWideChar(csi.ciACP, 0,
pNameStr, cNameStrLen, 0, 0);
if (!wcNameStrLen)
{
ERR(("makentf - afm2ntm: MultiByteToWideChar\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
//
// Prepair pNameStr2. This if for CJK font only. If MS face name
// is not available, use same name as pNameStr.
//
pNameStr2 = NULL;
cNameStr2Len = wcNameStr2Len = 0;
if (bIsCJKFont)
{
if (bIsMSFaceName)
{
//
// If we are dealing with V encoding, its MS menu name can not be
// found in psfamily.dat so that add '@' to make it a V menu name.
//
i = bIsVGlyphSet ? 1 : 0;
if ((pNameStr2 = (PSTR)MemAllocZ(i + cFamilyNameLen + 1)) == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
if (i) pNameStr2[0] = '@';
memcpy(&(pNameStr2[i]), pszFamilyName, cFamilyNameLen);
pNameStr2[i + cFamilyNameLen] = '\0';
}
else
{
pNameStr2 = pNameStr;
}
cNameStr2Len = strlen(pNameStr2);
wcNameStr2Len = MultiByteToWideChar(csi.ciACP, 0,
pNameStr2, cNameStr2Len, 0, 0);
if (!wcNameStr2Len)
{
ERR(("makentf - afm2ntm: MultiByteToWideChar\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
}
if (bVerbose)
{
printf("Font menu name in English:%s\n", pNameStr);
printf("Localized Font menu name%savailable:", pNameStr2 ? " " : " not ");
printf("%s\n", pNameStr2 ? pNameStr2 : "n/a");
}
//
// WIN31 COMPATABILITY! Check to see if this face name has aliases.
// if it does, then we need to set the FM_INFO_FAMILY_EQUIV bit of
// pTmpIFI->flInfo, and fill in an array of family aliases. Note that
// the cjGetFamilyAliases function gives us the number in Unicode size.
//
isi.nFamilyNameSize = ALIGN4(cjGetFamilyAliases(NULL, pNameStr, 0));
if (pNameStr2)
{
//
// We add one more face name. Thus, set FM_INFO_FAMILY_EQUIV bit
// (later) and add two, instead of one, for the two-null terminators.
//
isi.nFamilyNameSize += ALIGN4((wcNameStr2Len + 2) * sizeof (WCHAR));
}
//
// Account for size of Adobe PS font name. This is zero because it
// shares the face name for Win3.1 compatibility.
//
isi.nFaceNameSize = 0;
//
// Account for the sizes of the style and unique names in Unicode string.
//
// Style name: conbine Weight and ' Italic' if non-zero ItalicAngle values
// is present.
//
// Unique name: convert UniqueID value into Unicode string. If UniqueID
// is not found, leave the name blank.
//
pToken = FindAFMToken(pAFM, PS_WEIGHT_TOK);
if (pToken == NULL)
{
ERR(("makentf - afm2ntm: Weight value missing\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
AFM2NTMStrCpy(szStyleName, CCHOF(szStyleName), pToken);
pToken = FindAFMToken(pAFM, PS_ITALIC_TOK);
if (pToken)
{
if (atoi(pToken) > 0)
StringCchCatA(szStyleName, CCHOF(szStyleName), " Italic");
}
isi.nStyleNameSize = ALIGN4((strlen(szStyleName) + 1) * 2);
pToken = FindUniqueID(pAFM);
if (pToken)
{
AFM2NTMStrCpy(szUniqueID, CCHOF(szUniqueID), pToken);
isi.nUniqueNameSize = ALIGN4((strlen(szUniqueID) + 1) * 2);
}
else
isi.nUniqueNameSize = 0;
//
// If font doesn't support (Italics OR Bold), reserve additional memory
// at end of IFIMETRICS for structures required to do Italics simulation.
//
bIsItalic = FALSE;
bIsBold = FALSE;
j = bIsCJKFont ? 1 : 0;
if ((pToken = FindAFMToken(pAFM, PS_ITALIC_TOK)) != NULL)
{
if ( StrCmp(pToken, "0") && StrCmp(pToken, "0.0") )
bIsItalic = TRUE;
}
if ((pToken = FindAFMToken(pAFM, PS_WEIGHT_TOK)) != NULL)
{
for (i = 0; i < WeightKeyTbl[j].usNumEntries; i++)
{
if (!StrCmp(pToken, (PBYTE)(((PKEY) (WeightKeyTbl[j].pTbl))[i].pName)))
{
if ((((PKEY) (WeightKeyTbl[j].pTbl))[i].usValue) == FW_BOLD)
{
bIsBold = TRUE;
}
break;
}
}
}
// Reserve space for dpFontSim
if (!bIsBold || !bIsItalic)
isi.nFontSimSize = ALIGN4(sizeof(FONTSIM));
else
isi.nFontSimSize = 0;
// Reserve space for dpBold
if (!bIsBold)
isi.nBoldSize = ALIGN4(sizeof(FONTDIFF));
else
isi.nBoldSize = 0;
// Reserve space for dpItalic
if (!bIsItalic)
isi.nItalicSize = ALIGN4(sizeof(FONTDIFF));
else
isi.nItalicSize = 0;
// Reserve space for dpBoldItalic
if (!bIsBold || !bIsItalic)
isi.nBoldItalicSize = ALIGN4(sizeof(FONTDIFF));
else
isi.nBoldItalicSize = 0;
// Determine if this font supports multiple char sets.
if (pCharSet)
{
if (multiCharSet > 1)
isi.nCharSetSize = ALIGN4(NUM_DPCHARSET);
else
isi.nCharSetSize = 0;
}
else
{
isi.nCharSetSize = 0;
}
//////////////////////////////////////////////////////////////////////////
//
// Allocate memory for NTM, IFIMETRICS, and strings. We provide
// the secondary IFIMETRICS and strings if we are dealing with
// CJK font.
//
//////////////////////////////////////////////////////////////////////////
GET_NTMTOTALSIZE(nsi);
ulNTMSize = (ULONG)nsi.nTotalSize;
GET_IFIMETRICSTOTALSIZE(isi);
ulIFISize = (ULONG)isi.nTotalSize;
ulIFISize2 = bIsCJKFont ? ulIFISize * 2 : ulIFISize;
pNTM = (PNTM) MemAllocZ((size_t)(ulNTMSize + ulIFISize2));
if (pNTM == NULL)
{
ERR(("makentf - afm2ntm: malloc\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
//////////////////////////////////////////////////////////////////////////
//
// Construct NTM structure
//
//////////////////////////////////////////////////////////////////////////
pNTM->dwSize = ulNTMSize + ulIFISize2;
pNTM->dwVersion = NTM_VERSION;
pNTM->dwFlags = 0;
//
// Store the font name.
//
pNTM->dwFontNameOffset = ALIGN4(sizeof(NTM));
pby = (PBYTE)MK_PTR(pNTM, dwFontNameOffset);
memcpy(pby, pszFontName, cFontNameLen);
pby += cFontNameLen;
if (bIsCJKFont)
{
//
// Append glyphset name string to the font name.
//
memcpy(pby, pszGlyphSetName, cGlyphSetNameLen);
pby += cGlyphSetNameLen;
}
*pby = '\0';
//
// Store the display name.
//
pNTM->dwDisplayNameOffset = pNTM->dwFontNameOffset
+ (DWORD)nsi.nFontNameSize;
pby = (PBYTE)MK_PTR(pNTM, dwDisplayNameOffset);
if (bIsCJKFont && bIsVGlyphSet) *pby++ = '@';
memcpy(pby, pszFamilyName, cFamilyNameLen);
*(pby + cFamilyNameLen) = '\0';
//
// Get PS font version from AFM and store in NTM.
//
pToken = FindAFMToken(pAFM, PS_FONT_VERSION_TOK);
if (pToken == NULL) // Fixed bug 354007
{
ERR(("makentf - afm2ntm: Font Version value missing\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
pNTM->dwFontVersion = atoi(pToken) << 16 | atoi(&pToken[4]);
//
// Get the name string of the GLYPHSETDATA associated with this font
// and store it in NTM.
//
pNTM->dwGlyphSetNameOffset = pNTM->dwDisplayNameOffset
+ (DWORD)nsi.nDisplayNameSize;
StringCchCopyA((PBYTE)MK_PTR(pNTM, dwGlyphSetNameOffset), nsi.nGlyphSetNameSize, pszGlyphSetName);
//
// Store the count of Glyphs.
//
pNTM->dwGlyphCount = ulChCnt;
//
// Calculate offset, create ptr to IFIMETRICS.
//
pNTM->dwIFIMetricsOffset = ulNTMSize;
pifi = (PIFIMETRICS) MK_PTR(pNTM, dwIFIMetricsOffset);
//
// Calculate offset, create ptr to the secondly IFIMETRICS if necessary.
//
if (bIsCJKFont)
{
pNTM->dwIFIMetricsOffset2 = ulNTMSize + ulIFISize;
pifi2 = (PIFIMETRICS)MK_PTR(pNTM, dwIFIMetricsOffset2);
}
else
{
pNTM->dwIFIMetricsOffset2 = 0;
pifi2 = NULL;
}
//
// For both Fixed and Prop fonts, we need to get the ETMInfo.
// (Fix bug 211966, PPeng, 6-6-97)
//
GetAFMETM(pAFM, pFontChars, &EtmInfo);
//
// According to AFM spec, if a 'CharWidth' token is found in AFM, the
// font must be fixed pitch.
//
if (bIsFixedPitch)
{
//
// This is a fixed pitch font. Get the AvgWidth - which is anyone's width
//
pNTM->dwDefaultCharWidth = 0;
pNTM->dwCharWidthCount = 0;
pNTM->dwCharWidthOffset = 0;
//
// We just get a reasonable number from the AFM different from zero.
// This number is used in computing font transfroms.
//
if ((pToken = FindAFMToken(pAFM, PS_CH_METRICS_TOK)) != NULL)
{
//
// Get width of first char defined in AFM and use as
// average width.
//
NEXT_TOKEN(pToken);
pChWidthTok = FindAFMToken(pToken, PS_CH_WIDTH_TOK);
if (pChWidthTok == NULL)
{
pChWidthTok = FindAFMToken(pToken, PS_CH_WIDTH0_TOK);
}
if (pChWidthTok != NULL)
{
pToken = pChWidthTok;
pifi->fwdAveCharWidth =
pifi->fwdMaxCharInc = (FWORD)atoi(pToken);
}
}
pifi->fwdMaxCharInc = pifi->fwdAveCharWidth ;
if (bIsCJKFont)
{
// DCR: couldn't divide by 2 simply for C and K.
pifi->fwdAveCharWidth /= 2;
}
ASSERTMSG(pifi->fwdAveCharWidth, ("PSCRIPT: pifi->fwdAveCharWidth == 0\n"));
}
else
{
//
// Proportional font. Generate WIDTHRUNs.
//
pNTM->dwCharWidthOffset = pNTM->dwGlyphSetNameOffset
+ (DWORD)nsi.nGlyphSetNameSize;
pWidthRuns = (PWIDTHRUN) MK_PTR(pNTM, dwCharWidthOffset);
pNTM->dwCharWidthCount = GetAFMCharWidths(pAFM,
&pWidthRuns,
pFontChars,
pUniPs,
pGlyphSetData->dwGlyphCount,
&pifi->fwdAveCharWidth,
&pifi->fwdMaxCharInc);
// Fix bug 240339, jjia, 8/3/98
if (pWidthRuns[0].dwCharWidth == WIDTHRUN_COMPLEX)
{
pWidthRuns[0].dwCharWidth = WIDTHRUN_COMPLEX +
sizeof(WIDTHRUN);
}
}
//
// For both Prop and Fixed fonts
// (Fix bug 210314, PPeng, 6-10-97)
//
pNTM->dwDefaultCharWidth = pifi->fwdAveCharWidth;
//
// Construct kerning pairs.
//
if (ulKernPairs)
{
//
// Fill NTM with kern pair data.
//
pNTM->dwKernPairOffset = pNTM->dwGlyphSetNameOffset
+ (DWORD)nsi.nGlyphSetNameSize
+ (DWORD)nsi.nCharWidthSize;
pKernPairs = (FD_KERNINGPAIR *) MK_PTR(pNTM, dwKernPairOffset);
pNTM->dwKernPairCount = GetAFMKernPairs(pAFM, pKernPairs, pGlyphSetData);
}
else
{
//
// No pair kerning info for this font.
//
pNTM->dwKernPairCount = 0;
pNTM->dwKernPairOffset = 0;
}
//
// Store the CharDefined tbl.
//
pNTM->dwCharDefFlagOffset = pNTM->dwGlyphSetNameOffset
+ (DWORD)nsi.nGlyphSetNameSize
+ (DWORD)nsi.nCharWidthSize
+ (DWORD)nsi.nKernPairSize;
memcpy((PBYTE) MK_PTR(pNTM, dwCharDefFlagOffset), pCharDefTbl, ulCharDefTbl);
//
// Get font character set from AFM and store in NTM
//
pToken = pAFMCharacterSetString;
if (pToken != NULL)
{
if (StrCmp(pToken, PS_STANDARD_CHARSET_TOK) == 0)
pNTM->dwCharSet = CHARSET_STANDARD;
else if (StrCmp(pToken, PS_SPECIAL_CHARSET_TOK) == 0)
pNTM->dwCharSet = CHARSET_SPECIAL;
else if (StrCmp(pToken, PS_EXTENDED_CHARSET_TOK) == 0)
pNTM->dwCharSet = CHARSET_EXTENDED;
else
pNTM->dwCharSet = CHARSET_UNKNOWN;
}
//
// Save the codepage of the font if there is only one of it is used for
// the font.
//
if (pGlyphSetData->dwCodePageCount == 1)
pNTM->dwCodePage = ((PCODEPAGEINFO)MK_PTR(pGlyphSetData, dwCodePageOffset))->dwCodePage;
else
pNTM->dwCodePage = 0;
//
// Cleare the reserved area.
//
pNTM->dwReserved[0] =
pNTM->dwReserved[1] =
pNTM->dwReserved[2] = 0;
//////////////////////////////////////////////////////////////////////////
//
// Construct IFIMETRICS structure
//
//////////////////////////////////////////////////////////////////////////
pifi->cjThis = ulIFISize;
pifi->cjIfiExtra = isi.nIfiExtraSize;
pifi->lEmbedId = 0; // only useful for tt fonts
pifi->lCharBias = 0; // only useful for tt fonts
pifi->flInfo = FM_INFO_ARB_XFORMS |
FM_INFO_NOT_CONTIGUOUS |
FM_INFO_TECH_OUTLINE_NOT_TRUETYPE |
FM_INFO_1BPP |
FM_INFO_RIGHT_HANDED;
//
// Everything in IFIEXTRA is leave blank for now.
// Only the number of glyphs is filled in.
//
pifiEx = (PIFIEXTRA)((PBYTE)pifi + isi.nSize);
pifiEx->cig = pGlyphSetData->dwGlyphCount;
//
// Store font family name to IFIMETRICS. Copy font name aliases too if any.
// Note that this routine also converts the appropriate family name str to
// unicode prior to storing it in IFIMETRICS.
//
pifi->dpwszFamilyName = (PTRDIFF)(isi.nSize + isi.nIfiExtraSize);
ulAliases = cjGetFamilyAliases(pifi, pNameStr, csi.ciACP);
//
// Adjust ulAliases to the first null terminator if FM_INFO_FAMILY_EQUIV
// bit is set.
//
if (pifi->flInfo & FM_INFO_FAMILY_EQUIV)
ulAliases -= sizeof (WCHAR);
if (pNameStr2)
{
pifi->flInfo |= FM_INFO_FAMILY_EQUIV;
pby = (PBYTE)MK_PTR(pifi, dpwszFamilyName) + ulAliases;
MultiByteToWideChar(csi.ciACP, 0,
pNameStr2, cNameStr2Len,
(PWSTR)pby, wcNameStr2Len);
pby += wcNameStr2Len * sizeof (WCHAR);
//
// Terminate with two WCHAR nulls.
//
*((PWSTR)pby) = (WCHAR)'\0';
pby += sizeof (WCHAR);
*((PWSTR)pby) = (WCHAR)'\0';
pby += sizeof (WCHAR);
}
//
// Face name shares the family name/aliases for Win3.1 compatibility.
//
pifi->dpwszFaceName = pifi->dpwszFamilyName;
//
// Store style and unique names. Style name has to be available but
// unique name may not be available.
//
pifi->dpwszStyleName = pifi->dpwszFamilyName + (PTRDIFF)isi.nFamilyNameSize;
pby = (PBYTE)MK_PTR(pifi, dpwszStyleName);
MULTIBYTETOUNICODE((LPWSTR)pby, isi.nStyleNameSize, NULL, szStyleName, strlen(szStyleName));
if (isi.nUniqueNameSize)
{
pifi->dpwszUniqueName = pifi->dpwszStyleName + (PTRDIFF)isi.nStyleNameSize;
pby = (PBYTE)MK_PTR(pifi, dpwszUniqueName);
MULTIBYTETOUNICODE((LPWSTR)pby, isi.nUniqueNameSize, NULL, szUniqueID, strlen(szUniqueID));
}
else
{
pifi->dpwszUniqueName = pifi->dpwszStyleName + isi.nStyleNameSize - sizeof (WCHAR);
}
//
// Save Windows characterset.
//
pifi->jWinCharSet = (BYTE)jWinCharSet;
//
// Store the font's family type flags.
//
if (pFamilyInfo != NULL)
{
pifi->jWinPitchAndFamily = (BYTE) pFamilyInfo->FamilyKey.usValue & 0xff;
}
else
{
pifi->jWinPitchAndFamily = FF_SWISS;
}
//
// Set pitch flags.
//
if (bIsFixedPitch)
{
pifi->jWinPitchAndFamily |= FIXED_PITCH;
pifi->flInfo |= FM_INFO_OPTICALLY_FIXED_PITCH;
if (!bIsCJKFont)
pifi->flInfo |= FM_INFO_CONSTANT_WIDTH;
else
pifi->flInfo |= FM_INFO_DBCS_FIXED_PITCH;
}
else
{
pifi->jWinPitchAndFamily |= VARIABLE_PITCH;
}
//
// Get weight from AFM key.
//
pifi->usWinWeight = FW_NORMAL;
pifi->fsSelection = 0;
j = bIsCJKFont ? 1 : 0;
if ((pToken = FindAFMToken(pAFM, PS_WEIGHT_TOK)) != NULL)
for (i = 0; i < WeightKeyTbl[j].usNumEntries; i++)
{
if (!StrCmp(pToken, (PBYTE) (((PKEY) (WeightKeyTbl[j].pTbl))[i].pName)))
{
pifi->usWinWeight = (((PKEY) (WeightKeyTbl[j].pTbl))[i].usValue);
if (pifi->usWinWeight == FW_BOLD)
{
pifi->fsSelection = FM_SEL_BOLD;
}
break;
}
}
//
// Is this really how to set font selection flags?
// AFMtoPFM converter treats angle as a float, but etm.etmslant
// field is a short.
//
//
// Set Italic sel flag if necessary.
//
if ((pToken = FindAFMToken(pAFM, PS_ITALIC_TOK)) != NULL)
pNTM->etm.etmSlant = (SHORT)atoi(pToken);
if (pNTM->etm.etmSlant)
{
pifi->fsSelection |= FM_SEL_ITALIC;
}
#if 0
//
// DCR: so, what are we gonna do with this?
//
FSHORT fsSelection = 0;
//
// Excerpts from bodind's code. Not sure if we need this
// useful.
//
if (pjPFM[OFF_Underline])
fsSelection |= FM_SEL_UNDERSCORE;
if (pjPFM[OFF_StrikeOut])
fsSelection |= FM_SEL_STRIKEOUT;
if (READ_WORD(&pjPFM[OFF_Weight]) > FW_NORMAL)
fsSelection |= FM_SEL_BOLD;
#endif
pifi->fsType = FM_NO_EMBEDDING;
pifi->fwdUnitsPerEm = EM; // hardcoded for type 1 fonts
//
// Use FontBBox2 if found. Otherwise, use FontBBox. FontBBox2 is
// the bounding box values of the characters not the union of all
// the characters described in the AFM file but the characters
// actually used in a specific character set such as 90ms.
//
if (((pToken = FindAFMToken(pAFM, PS_FONT_BBOX2_TOK)) == NULL) &&
((pToken = FindAFMToken(pAFM, PS_FONT_BBOX_TOK)) == NULL))
{
ERR(("makentf - afm2ntm: FontBBox not found\n"));
FREE_AFMTONTM_MEMORY;
return NULL;
}
//
// Save font bounding box.
//
PARSE_RECT(pToken, rcBBox);
sIntLeading = (SHORT) (rcBBox.top - rcBBox.bottom) - EM;
if (sIntLeading < 0)
sIntLeading = 0;
sAscent = (USHORT) rcBBox.top & 0xffff;
pifi->fwdWinAscender = sAscent;
//
// Poof! Magic Metrics...
//
sExternalLeading = 196;
// see pfm.c, win31 sources, this computation
// produces quantity that is >= |rcBBox.bottom|
pifi->fwdWinDescender = EM - sAscent + sIntLeading;
pifi->fwdMacAscender = sAscent;
pifi->fwdMacDescender = -pifi->fwdWinDescender;
pifi->fwdMacLineGap = (FWORD) sExternalLeading - sIntLeading;
if (pifi->fwdMacLineGap < 0)
pifi->fwdMacLineGap = 0;
pifi->fwdTypoAscender = pifi->fwdMacAscender;
pifi->fwdTypoDescender = pifi->fwdMacDescender;
pifi->fwdTypoLineGap = pifi->fwdMacLineGap;
if (pifi->fwdAveCharWidth > pifi->fwdMaxCharInc)
{
//
// fix the bug in the header if there is one,
// We do not want to change AveCharWidht, it is used for
// computing font xforms, Max is used for nothing as fas as I know.
//
pifi->fwdMaxCharInc = pifi->fwdAveCharWidth;
}
//
// Create EXTTEXTMETRICs. Poof! More magic.
//
pNTM->etm.etmSize = sizeof(EXTTEXTMETRIC);
pNTM->etm.etmCapHeight = EtmInfo.etmCapHeight;
pNTM->etm.etmXHeight = EtmInfo.etmXHeight;
pNTM->etm.etmLowerCaseAscent = EtmInfo.etmLowerCaseAscent;
pNTM->etm.etmLowerCaseDescent = EtmInfo.etmLowerCaseDescent;
pNTM->etm.etmPointSize = 12 * 20; /* Nominal point size = 12 */
pNTM->etm.etmOrientation = 0;
pNTM->etm.etmMasterHeight = 1000;
pNTM->etm.etmMinScale = 3;
pNTM->etm.etmMaxScale = 1000;
pNTM->etm.etmMasterUnits = 1000;
if ((pToken = FindAFMToken(pAFM, PS_UNDERLINE_POS_TOK)) != NULL)
pNTM->etm.etmUnderlineOffset = (SHORT)atoi(pToken);
if ((pToken = FindAFMToken(pAFM, PS_UNDERLINE_THICK_TOK)) != NULL)
pNTM->etm.etmUnderlineWidth = (SHORT)atoi(pToken);
pNTM->etm.etmSuperScript = -500;
pNTM->etm.etmSubScript = 250;
pNTM->etm.etmSuperScriptSize = 500;
pNTM->etm.etmSubScriptSize = 500;
pNTM->etm.etmDoubleUpperUnderlineOffset = pNTM->etm.etmUnderlineOffset / 2;
pNTM->etm.etmDoubleLowerUnderlineOffset = pNTM->etm.etmUnderlineOffset;
pNTM->etm.etmDoubleUpperUnderlineWidth = // same as LowerUnderlineWidth
pNTM->etm.etmDoubleLowerUnderlineWidth = pNTM->etm.etmUnderlineWidth / 2;
pNTM->etm.etmStrikeOutOffset = 500;
pNTM->etm.etmStrikeOutWidth = 50; // ATM sets it to 50 (also all PFMs have 50)
pNTM->etm.etmNKernPairs = (USHORT) ulKernPairs & 0xffff;
//
// No track kerning. This mimics the behavior of old AFM->PFM utility.
//
pNTM->etm.etmNKernTracks = 0;
//
// SuperScripts and Subscripts come from etm:
//
pifi->fwdSubscriptXSize = // same as YSize
pifi->fwdSubscriptYSize = pNTM->etm.etmSubScriptSize;
pifi->fwdSubscriptXOffset = 0;
pifi->fwdSubscriptYOffset = pNTM->etm.etmSubScript;
pifi->fwdSuperscriptXSize = // same as YSize
pifi->fwdSuperscriptYSize = pNTM->etm.etmSuperScriptSize;
pifi->fwdSuperscriptXOffset = 0;
pifi->fwdSuperscriptYOffset = pNTM->etm.etmSuperScript;
pifi->fwdUnderscoreSize = pNTM->etm.etmUnderlineWidth;
//
// fwdUnderscorePosition is typically negative - AFM may have negative value already
//
if (pNTM->etm.etmUnderlineOffset <0)
pifi->fwdUnderscorePosition = -pNTM->etm.etmUnderlineOffset;
else
pifi->fwdUnderscorePosition = pNTM->etm.etmUnderlineOffset;
// Make it compatible with ATM. Fix bug Adobe #211202
pifi->fwdUnderscorePosition = -(pifi->fwdUnderscorePosition -
pifi->fwdUnderscoreSize / 2);
pifi->fwdStrikeoutSize = pNTM->etm.etmStrikeOutWidth;
//
// This is what KentSe was using to position strikeout and it looked good [bodind]
// Instead we could have used etmStrikeoutOffset (usually equal to 500) which
// was too big.
//
// Make it compatible with ATM. Fix bug Adobe #211202
// pifi->fwdStrikeoutPosition = ((LONG)pNTM->etm.etmLowerCaseAscent / 2);
if (pNTM->etm.etmCapHeight != 0)
pifi->fwdStrikeoutPosition = (pNTM->etm.etmCapHeight - pifi->fwdUnderscoreSize) / 2;
else
pifi->fwdStrikeoutPosition = (pNTM->etm.etmXHeight - pifi->fwdUnderscoreSize) / 2;
pifi->fwdLowestPPEm = pNTM->etm.etmMinScale;
//
// Per bodind, Win 3.1 values for first, last, break and default char can
// be hardcoded.
//
pifi->chFirstChar = 0x20;
pifi->chLastChar = 0xff;
if (!bIsCJKFont)
{
pifi->chBreakChar = 0x20;
// The following line of code should work, however, there seems to be
// a bug in afm -> pfm conversion utility which makes
// DefaultChar == 0x20 instead of 149 - 20 (for bullet).
// pifi->chDefaultChar = pjPFM[OFF_DefaultChar] + pjPFM[OFF_FirstChar];
// Therefore, instead, I will use 149 which seems to work for all fonts.
pifi->chDefaultChar = 149;
}
else
{
pifi->chBreakChar =
pifi->chDefaultChar = 0x00;
}
//
// Get Unicode values for first and last char from GLYPHSETDATA. We
// should do this on a per GLYPHSETDATA basis rather than a per font
// basis, but the calculations are so simple just do it on the fly,
// rather than dragging first and last char around with the
// GLYPHSETDATA.
//
pGlyphRun = (PGLYPHRUN) MK_PTR(pGlyphSetData, dwRunOffset);
pifi->wcFirstChar = pGlyphRun->wcLow;
(ULONG_PTR) pGlyphRun += (pGlyphSetData->dwRunCount - 1) * sizeof(GLYPHRUN);
pifi->wcLastChar = pGlyphRun->wcLow + pGlyphRun->wGlyphCount - 1;
MultiByteToWideChar(csi.ciACP, 0,
&pifi->chDefaultChar, 1,
&pifi->wcDefaultChar, sizeof(WCHAR));
MultiByteToWideChar(csi.ciACP, 0,
&pifi->chBreakChar, 1,
&pifi->wcBreakChar, sizeof(WCHAR));
pifi->fwdCapHeight = pNTM->etm.etmCapHeight;
pifi->fwdXHeight = pNTM->etm.etmXHeight;
// All the fonts that this font driver will see are to be rendered left
// to right
pifi->ptlBaseline.x = 1;
pifi->ptlBaseline.y = 0;
pifi->ptlAspect.y = 300;
pifi->ptlAspect.x = 300;
// italic angle from etm.
pifi->lItalicAngle = pNTM->etm.etmSlant;
if (pifi->lItalicAngle == 0)
{
// The base class of font is not italicized,
pifi->ptlCaret.x = 0;
pifi->ptlCaret.y = 1;
}
else
{
// ptlCaret.x = -sin(lItalicAngle);
// ptlCaret.y = cos(lItalicAngle);
//!!! until I figure out the fast way to get sin and cos I cheat: [bodind]
pifi->ptlCaret.x = 1;
pifi->ptlCaret.y = 3;
}
//!!! The font box; This is bogus, this info is not in .pfm file!!! [bodind]
//!!! but I suppose that this info is not too useful anyway, it is nowhere
//!!! used in the engine or elsewhere in the ps driver.
//!!! left and right are bogus, top and bottom make sense.
pifi->rclFontBox.left = 0; // bogus
pifi->rclFontBox.top = (LONG) pifi->fwdTypoAscender; // correct
pifi->rclFontBox.right = (LONG) pifi->fwdMaxCharInc; // bogus
pifi->rclFontBox.bottom = (LONG) pifi->fwdTypoDescender; // correct
// achVendorId, unknown, don't bother figure it out from copyright msg
pifi->achVendId[0] = 'U';
pifi->achVendId[1] = 'n';
pifi->achVendId[2] = 'k';
pifi->achVendId[3] = 'n';
pifi->cKerningPairs = ulKernPairs;
// Panose
pifi->ulPanoseCulture = FM_PANOSE_CULTURE_LATIN;
ppanose = &(pifi->panose);
ppanose->bFamilyType = PAN_ANY;
ppanose->bSerifStyle =
((pifi->jWinPitchAndFamily & 0xf0) == FF_SWISS) ?
PAN_SERIF_NORMAL_SANS : PAN_ANY;
ppanose->bWeight = (BYTE) WINWT_TO_PANWT(pifi->usWinWeight);
ppanose->bProportion = (pifi->jWinPitchAndFamily & FIXED_PITCH) ?
PAN_PROP_MONOSPACED : PAN_ANY;
ppanose->bContrast = PAN_ANY;
ppanose->bStrokeVariation = PAN_ANY;
ppanose->bArmStyle = PAN_ANY;
ppanose->bLetterform = PAN_ANY;
ppanose->bMidline = PAN_ANY;
ppanose->bXHeight = PAN_ANY;
// If the font is not italic or Not-Bold, the driver can simulate it
// Set the dpBold, dpItalic, and dpBoldItalic correctly, PPeng, 6-3-1997
// Depends on AFM, we need to set some of the sim structure:
// Normal - need dpBold, dpItalic, and dpBoldItalic
// Bold - need dpItalic
// Italic - need dpBoldItalic
// BoldItalic - Nothing
// Don't move code around !!
// At this point, bIsBold and bIsItalic should be set already
// Don't move code around !!
if (!bIsBold || !bIsItalic)
{
FONTSIM *pFontSim;
FONTDIFF *pFontDiff;
FONTDIFF FontDiff;
// Preset temporary FontDiff structure
FontDiff.jReserved1 = 0;
FontDiff.jReserved2 = 0;
FontDiff.jReserved3 = 0;
FontDiff.bWeight = pifi->panose.bWeight;
FontDiff.usWinWeight = pifi->usWinWeight;
FontDiff.fsSelection = pifi->fsSelection;
FontDiff.fwdAveCharWidth = pifi->fwdAveCharWidth;
FontDiff.fwdMaxCharInc = pifi->fwdMaxCharInc;
FontDiff.ptlCaret = pifi->ptlCaret;
// Initialize FONTSIM structure
pifi->dpFontSim = pifi->dpwszStyleName + (PTRDIFF)(isi.nStyleNameSize + isi.nUniqueNameSize);
pFontSim = (FONTSIM *) MK_PTR(pifi, dpFontSim);
pFontSim->dpBold = pFontSim->dpBoldItalic = pFontSim->dpItalic = 0;
// Notice the FontDiff data are arranged right after FontSim
// in the following order: dpBold, dpItalic, dpBoldItalic
if (!bIsBold)
{
// Right after FontSim.
pFontSim->dpBold = ALIGN4(sizeof(FONTSIM));
pFontDiff = (FONTDIFF *) MK_PTR(pFontSim, dpBold);
*pFontDiff = FontDiff;
pFontDiff->bWeight = PAN_WEIGHT_BOLD;
pFontDiff->fsSelection |= FM_SEL_BOLD;
pFontDiff->usWinWeight = FW_BOLD;
pFontDiff->fwdAveCharWidth += 1;
pFontDiff->fwdMaxCharInc += 1;
// if already Italic, CANNOT Un-italic it
if (bIsItalic)
{
pFontDiff->ptlCaret.x = 1;
pFontDiff->ptlCaret.y = 3;
}
else
{
pFontDiff->ptlCaret.x = 0;
pFontDiff->ptlCaret.y = 1;
}
}
if (!bIsItalic)
{
if (pFontSim->dpBold)
{
// Right after FontDiff for dpBold, or...
pFontSim->dpItalic = pFontSim->dpBold + ALIGN4(sizeof(FONTDIFF));
}
else
{
// ...right after FontSim.
pFontSim->dpItalic = ALIGN4(sizeof(FONTSIM));
}
pFontDiff = (FONTDIFF *) MK_PTR(pFontSim, dpItalic);
*pFontDiff = FontDiff;
pFontDiff->fsSelection |= FM_SEL_ITALIC;
// Italic angle is approximately 18 degree
pFontDiff->ptlCaret.x = 1;
pFontDiff->ptlCaret.y = 3;
}
// Make BoldItalic simulation if necessary - besides dpBold or dpItalic
if (!bIsItalic || !bIsBold)
{
if (pFontSim->dpItalic)
{
// Right after FontDiff for dpItalic, or...
pFontSim->dpBoldItalic = pFontSim->dpItalic + ALIGN4(sizeof(FONTDIFF));
}
else if (pFontSim->dpBold)
{
// ...right after FontDiff for dpBold if dpItalic is not set, or...
pFontSim->dpBoldItalic = pFontSim->dpBold + ALIGN4(sizeof(FONTDIFF));
}
else
{
// ...right after FontSim if none of other two is set.
pFontSim->dpBoldItalic = ALIGN4(sizeof(FONTSIM));
}
pFontDiff = (FONTDIFF *) MK_PTR(pFontSim, dpBoldItalic);
*pFontDiff = FontDiff;
pFontDiff->bWeight = PAN_WEIGHT_BOLD;
pFontDiff->fsSelection |= (FM_SEL_BOLD | FM_SEL_ITALIC);
pFontDiff->usWinWeight = FW_BOLD;
pFontDiff->fwdAveCharWidth += 1;
pFontDiff->fwdMaxCharInc += 1;
// Italic angle is approximately 18 degree
pFontDiff->ptlCaret.x = 1;
pFontDiff->ptlCaret.y = 3;
}
}
else
pifi->dpFontSim = 0;
if (multiCharSet > 1)
{
PBYTE pDpCharSet;
pifi->dpCharSets = ulIFISize - ALIGN4(NUM_DPCHARSET);
pDpCharSet = (BYTE *)MK_PTR(pifi, dpCharSets);
// The order of this check is important since jWinCharSet
// should match the first dpCharSets array if it exists.
i = 0;
if (CSET_SUPPORT(*pCharSet, CS_ANSI))
pDpCharSet[i++] = ANSI_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_EASTEUROPE))
pDpCharSet[i++] = EASTEUROPE_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_RUSSIAN))
pDpCharSet[i++] = RUSSIAN_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_GREEK))
pDpCharSet[i++] = GREEK_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_TURKISH))
pDpCharSet[i++] = TURKISH_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_HEBREW))
pDpCharSet[i++] = HEBREW_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_ARABIC))
pDpCharSet[i++] = ARABIC_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_BALTIC))
pDpCharSet[i++] = BALTIC_CHARSET;
if (CSET_SUPPORT(*pCharSet, CS_SYMBOL))
pDpCharSet[i++] = SYMBOL_CHARSET;
while (i < 16)
pDpCharSet[i++] = DEFAULT_CHARSET;
}
else
pifi->dpCharSets = 0; // no multiple charsets in ps fonts
//
// Copy the first IFIMETRICS to the secondly if necessary, and then
// switch English and localized font menu names.
//
if (bIsCJKFont)
{
ASSERT(pifi2 != NULL);
memcpy(pifi2, pifi, isi.nTotalSize);
pifi2->flInfo &= ~FM_INFO_FAMILY_EQUIV;
ulAliases = cjGetFamilyAliases(pifi2, pNameStr2, csi.ciACP);
if (pifi2->flInfo & FM_INFO_FAMILY_EQUIV)
ulAliases -= sizeof (WCHAR);
pifi2->flInfo |= FM_INFO_FAMILY_EQUIV;
pby = (PBYTE)MK_PTR(pifi2, dpwszFamilyName) + ulAliases;
MultiByteToWideChar(csi.ciACP, 0,
pNameStr, cNameStrLen,
(PWSTR)pby, wcNameStrLen);
pby += wcNameStrLen * sizeof (WCHAR);
//
// Terminate with two WCHAR nulls.
//
*((PWSTR)pby) = (WCHAR)'\0';
pby += sizeof (WCHAR);
*((PWSTR)pby) = (WCHAR)'\0';
pby += sizeof (WCHAR);
//
// Face name shares the family name/aliases for Win3.1 compatibility.
//
pifi2->dpwszFaceName = pifi2->dpwszFamilyName;
#if 1
//
// We now support style and unique names too.
//
pifi2->dpwszStyleName = pifi2->dpwszFamilyName + (PTRDIFF)isi.nFamilyNameSize;
pby = (PBYTE)MK_PTR(pifi2, dpwszStyleName);
MULTIBYTETOUNICODE((LPWSTR)pby, isi.nStyleNameSize, NULL, szStyleName, strlen(szStyleName));
if (isi.nUniqueNameSize)
{
pifi2->dpwszUniqueName = pifi2->dpwszStyleName + (PTRDIFF)isi.nStyleNameSize;
pby = (PBYTE)MK_PTR(pifi2, dpwszUniqueName);
MULTIBYTETOUNICODE((LPWSTR)pby, isi.nUniqueNameSize, NULL, szUniqueID, strlen(szUniqueID));
}
else
{
pifi2->dpwszUniqueName = pifi2->dpwszStyleName + isi.nStyleNameSize - sizeof (WCHAR);
}
#else
//
// These names don't exist, so point to the NULL char.
// This is too for Win3.1 compatibility.
//
pifi2->dpwszStyleName = pifi2->dpwszFamilyName + ulAliases - sizeof (WCHAR);
pifi2->dpwszUniqueName = pifi2->dpwszStyleName;
#endif
#ifdef FORCE_2NDIFIMETRICS_FIRST
{
DWORD dw = pNTM->dwIFIMetricsOffset;
pNTM->dwIFIMetricsOffset = pNTM->dwIFIMetricsOffset2;
pNTM->dwIFIMetricsOffset2 = dw;
}
#endif
}
if (bVerbose)
{
printf("NTM:dwFontNameOffset:%s\n", (PSZ)MK_PTR(pNTM, dwFontNameOffset));
printf("NTM:dwDisplayNameOffset:%s\n", (PSZ)MK_PTR(pNTM, dwDisplayNameOffset));
printf("NTM:dwGlyphSetNameOffset:%s\n", (PSZ)MK_PTR(pNTM, dwGlyphSetNameOffset));
printf("NTM:dwGlyphCount:%ld\n", pNTM->dwGlyphCount);
printf("NTM:dwCharWidthCount:%ld\n", pNTM->dwCharWidthCount);
printf("NTM:dwDefaultCharWidth:%ld\n", pNTM->dwDefaultCharWidth);
printf("NTM:dwCharSet:%ld\n", pNTM->dwCharSet);
printf("NTM:dwCodePage:%ld\n", pNTM->dwCodePage);
pifi = (PIFIMETRICS)MK_PTR(pNTM, dwIFIMetricsOffset);
printf("IFIMETRICS:dpwszFamilyName:%S\n", (LPWSTR)MK_PTR(pifi, dpwszFamilyName));
printf("IFIMETRICS:dpwszStyleName:%S\n", (LPWSTR)MK_PTR(pifi, dpwszStyleName));
printf("IFIMETRICS:dpwszFaceName:%S\n", (LPWSTR)MK_PTR(pifi, dpwszFaceName));
printf("IFIMETRICS:dpwszUniqueName:%S\n", (LPWSTR)MK_PTR(pifi, dpwszUniqueName));
printf("IFIMETRICS:jWinCharSet:%d\n", (WORD)pifi->jWinCharSet);
printf("IFIMETRICS:jWinPitchAndFamily:%02X\n", (WORD)pifi->jWinPitchAndFamily);
printf("IFIMETRICS:usWinWeight:%d\n", (int)pifi->usWinWeight);
printf("IFIMETRICS:flInfo:%08lX\n", pifi->flInfo);
printf("IFIMETRICS:fsSelection:%04X\n", (WORD)pifi->fsSelection);
printf("IFIMETRICS:fsType:%04X\n", (WORD)pifi->fsType);
printf("IFIMETRICS:fwdUnitsPerEm:%d\n", (int)pifi->fwdUnitsPerEm);
printf("IFIMETRICS:fwdLowestPPEm:%d\n", (int)pifi->fwdLowestPPEm);
printf("IFIMETRICS:fwdWinAscender:%d\n", (int)pifi->fwdWinAscender);
printf("IFIMETRICS:fwdWinDescender:%d\n", (int)pifi->fwdWinDescender);
printf("IFIMETRICS:fwdMacAscender:%d\n", (int)pifi->fwdMacAscender);
printf("IFIMETRICS:fwdMacDescender:%d\n", (int)pifi->fwdMacDescender);
printf("IFIMETRICS:fwdMacLineGap:%d\n", (int)pifi->fwdMacLineGap);
printf("IFIMETRICS:fwdTypoAscender:%d\n", (int)pifi->fwdTypoAscender);
printf("IFIMETRICS:fwdTypoDescender:%d\n", (int)pifi->fwdTypoDescender);
printf("IFIMETRICS:fwdTypoLineGap:%d\n", (int)pifi->fwdTypoLineGap);
printf("IFIMETRICS:fwdAveCharWidth:%d\n", (int)pifi->fwdAveCharWidth);
printf("IFIMETRICS:fwdMaxCharInc:%d\n", (int)pifi->fwdMaxCharInc);
printf("IFIMETRICS:fwdCapHeight:%d\n", (int)pifi->fwdCapHeight);
printf("IFIMETRICS:fwdXHeight:%d\n", (int)pifi->fwdXHeight);
printf("IFIMETRICS:fwdSubscriptXSize:%d\n", (int)pifi->fwdSubscriptXSize);
printf("IFIMETRICS:fwdSubscriptYSize:%d\n", (int)pifi->fwdSubscriptYSize);
printf("IFIMETRICS:fwdSubscriptXOffset:%d\n", (int)pifi->fwdSubscriptXOffset);
printf("IFIMETRICS:fwdSubscriptYOffset:%d\n", (int)pifi->fwdSubscriptYOffset);
printf("IFIMETRICS:fwdSuperscriptXSize:%d\n", (int)pifi->fwdSuperscriptXSize);
printf("IFIMETRICS:fwdSuperscriptYSize:%d\n", (int)pifi->fwdSuperscriptYSize);
printf("IFIMETRICS:fwdSuperscriptXOffset:%d\n", (int)pifi->fwdSuperscriptXOffset);
printf("IFIMETRICS:fwdSuperscriptYOffset:%d\n", (int)pifi->fwdSuperscriptYOffset);
printf("IFIMETRICS:fwdUnderscoreSize:%d\n", (int)pifi->fwdUnderscoreSize);
printf("IFIMETRICS:fwdUnderscorePosition:%d\n", (int)pifi->fwdUnderscorePosition);
printf("IFIMETRICS:fwdStrikeoutSize:%d\n", (int)pifi->fwdStrikeoutSize);
printf("IFIMETRICS:fwdStrikeoutPosition:%d\n", (int)pifi->fwdStrikeoutPosition);
printf("IFIMETRICS:chFirstChar:%02X\n", (WORD)pifi->chFirstChar);
printf("IFIMETRICS:chLastChar:%02X\n", (WORD)pifi->chLastChar);
printf("IFIMETRICS:chDefaultChar:%02X\n", (WORD)pifi->chDefaultChar);
printf("IFIMETRICS:chBreakChar:%02X\n", (WORD)pifi->chBreakChar);
printf("IFIMETRICS:ptlBaseline:(%ld, %ld)\n", pifi->ptlBaseline.x, pifi->ptlBaseline.y);
printf("IFIMETRICS:ptlAspect:(%ld, %ld)\n", pifi->ptlAspect.x, pifi->ptlAspect.y);
printf("IFIMETRICS:ptlCaret:(%ld, %ld)\n", pifi->ptlCaret.x, pifi->ptlCaret.y);
printf("IFIMETRICS:rclFontBox:(%ld, %ld, %ld, %ld)\n",
pifi->rclFontBox.left, pifi->rclFontBox.top,
pifi->rclFontBox.right, pifi->rclFontBox.bottom);
if (pifi->dpFontSim)
{
FONTSIM* pFontSim = (FONTSIM*)MK_PTR(pifi, dpFontSim);
if (pFontSim->dpBold)
printf("FONTSIM:Bold\n");
if (pFontSim->dpItalic)
printf("FONTSIM:Italic\n");
if (pFontSim->dpBoldItalic)
printf("FONTSIM:BoldItalic\n");
}
printf("GLYPHSETDATA:dwFlags:%08X\n", pGlyphSetData->dwFlags);
printf("GLYPHSETDATA:dwGlyphSetNameOffset:%s\n",
(PSZ)MK_PTR(pGlyphSetData, dwGlyphSetNameOffset));
printf("GLYPHSETDATA:dwGlyphCount:%ld\n", pGlyphSetData->dwGlyphCount);
printf("GLYPHSETDATA:dwRunCount:%ld\n", pGlyphSetData->dwRunCount);
printf("GLYPHSETDATA:dwCodePageCount:%ld\n", pGlyphSetData->dwCodePageCount);
{
DWORD dw;
PCODEPAGEINFO pcpi = (PCODEPAGEINFO)MK_PTR(pGlyphSetData, dwCodePageOffset);
for (dw = 1; dw <= pGlyphSetData->dwCodePageCount; dw++)
{
printf("CODEPAGEINFO#%ld:dwCodePage:%ld\n", dw, pcpi->dwCodePage);
printf("CODEPAGEINFO#%ld:dwWinCharset:%ld\n", dw, pcpi->dwWinCharset);
printf("CODEPAGEINFO#%ld:dwEncodingNameOffset:%s\n",
dw, (PSZ)MK_PTR(pcpi, dwEncodingNameOffset));
pcpi++;
}
}
printf("\n");
}
//////////////////////////////////////////////////////////////////
//
// Free strings and char metrics info.
//
//////////////////////////////////////////////////////////////////
FREE_AFMTONTM_MEMORY;
return(pNTM);
}
PBYTE
FindAFMToken(
PBYTE pAFM,
PSZ pszToken
)
/*++
Routine Description:
Finds an AFM token in a memory mapped AFM file stream.
Arguments:
pAFM - pointer to memory mapped AFM file.
pszToken - pointer to null-term string containing token to search for
Return Value:
NULL => error
otherwise => ptr to token's value. This is defined as the first non-blank
char after the token name. If EOL(FindAfmToken(pAFM, pszToken)) then
pszToken was found but it has no value (e.g. EndCharMetrics).
--*/
{
PBYTE pCurToken;
int i;
VERBOSE(("Entering FindAFMToken... %s\n", pszToken));
while (TRUE)
{
PARSE_TOKEN(pAFM, pCurToken);
if (!(StrCmp(pCurToken, PS_COMMENT_TOK)))
{
NEXT_LINE(pAFM);
}
else
{
for (i = 0; i < MAX_TOKENS; i++)
{
if (!(StrCmp(pCurToken, pszToken)))
{
return(pAFM);
}
else if (!(StrCmp(pCurToken, PS_EOF_TOK)))
{
return NULL;
}
}
NEXT_TOKEN(pAFM);
}
}
return NULL;
}
CHSETSUPPORT
GetAFMCharSetSupport(
PBYTE pAFM,
CHSETSUPPORT *pGlyphSet
)
/*++
Routine Description:
Given a ptr to a memory mapped AFM, determine which Windows charset(s)
it supports.
Arguments:
pAFMetrx - pointer to CharMetrics in a memory mapped AFM file.
Return Value:
Contains bit fields which indicate which csets are supported. Use
CS_SUP(CS_xxx) macro to determine if a particular cset is supported.
--*/
{
PBYTE pToken;
USHORT i, chCnt;
CHSETSUPPORT flCsetSupport;
PBYTE pAFMMetrx;
isSymbolCharSet = FALSE;
*pGlyphSet = CS_NOCHARSET;
//
// Check to see if this is a CJK font.
//
if ((flCsetSupport = IsCJKFont(pAFM)))
{
return(flCsetSupport);
}
pToken = pAFMCharacterSetString;
if (pToken != NULL)
{
if (StrCmp(pToken, PS_STANDARD_CHARSET_TOK) == 0)
*pGlyphSet = CS_228;
else
*pGlyphSet = CS_314;
}
else
*pGlyphSet = CS_228;
//
// Check to see if a EncodingScheme token in the AFM file. If so, check
// if this is a standard encoding font or a Pi (Symbol) font.
//
if ((pToken = FindAFMToken(pAFM, PS_ENCODING_TOK)) != NULL)
{
if (StrCmp(pToken, PS_STANDARD_ENCODING) == 0)
{
return(CSUP(CS_ANSI));
}
}
//
// Find the beginning of the char metrics.
//
pAFMMetrx = FindAFMToken(pAFM, PS_CH_METRICS_TOK);
if (pAFMMetrx == NULL) // Fixed bug 354007
{
*pGlyphSet = CS_NOCHARSET;
ERR(("makentf - invalid StartCharMetrics\n"));
return(CS_NOCHARSET);
}
//
// Current pos should be the character count field.
//
for (i = 0; i < StrLen(pAFMMetrx); i++)
{
if (!IS_NUM(&pAFMMetrx[i]))
{
*pGlyphSet = CS_NOCHARSET;
ERR(("makentf - invalid StartCharMetrics\n"));
return(CS_NOCHARSET);
}
}
chCnt = (USHORT)atoi(pAFMMetrx);
(ULONG_PTR) pAFMMetrx += i;
//
// Process each char.
//
flCsetSupport = 0;
i = 0;
do
{
PARSE_TOKEN(pAFMMetrx, pToken);
if (StrCmp(pToken, PS_CH_NAME_TOK) == 0)
{
if (StrCmp(pAFMMetrx, PS_CH_NAME_EASTEUROPE) == 0)
flCsetSupport |= CSUP(CS_EASTEUROPE);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_RUSSIAN) == 0)
flCsetSupport |= CSUP(CS_RUSSIAN);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_ANSI) == 0)
flCsetSupport |= CSUP(CS_ANSI);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_GREEK) == 0)
flCsetSupport |= CSUP(CS_GREEK);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_TURKISH) == 0)
flCsetSupport |= CSUP(CS_TURKISH);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_HEBREW) == 0)
flCsetSupport |= CSUP(CS_HEBREW);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_ARABIC) == 0)
flCsetSupport |= CSUP(CS_ARABIC);
else if (StrCmp(pAFMMetrx, PS_CH_NAME_BALTIC) == 0)
flCsetSupport |= CSUP(CS_BALTIC);
i++;
}
else if (StrCmp(pToken, PS_EOF_TOK) == 0)
{
break;
}
NEXT_TOKEN(pAFMMetrx);
} while (i < chCnt);
//
// Assume symbol if none of the other char set is supported.
//
if (flCsetSupport == 0)
{
*pGlyphSet = CS_NOCHARSET;
flCsetSupport = CSUP(CS_SYMBOL);
isSymbolCharSet = TRUE;
}
return flCsetSupport;
}
int __cdecl
StrCmp(
const VOID *str1,
const VOID *str2
)
/*++
Routine Description:
Compare two strings which are terminated by either a null char or a
space.
Arguments:
str1, str2 - Strings to compare.
Return Value:
-1 => str1 < str2
1 => str1 > str2
0 => str1 = str2
--*/
{
PBYTE s1 = (PBYTE) str1, s2 = (PBYTE) str2;
// Error case, just return less then.
if ((s1 == NULL) || (s2 == NULL))
return(-1);
while (!IS_WHTSPACE(s1) && !IS_WHTSPACE(s2))
{
if (*s1 < *s2)
{
return(-1);
}
else if (*s1 > *s2)
{
return(1);
}
s1++;
s2++;
}
//
// Strings must be same length to be an exact match.
//
if (IS_WHTSPACE(s1) && IS_WHTSPACE(s2))
{
return(0);
}
else if (IS_WHTSPACE(s1))
// else if ((*s1 == ' ') || (*s1 == '\0'))
{
//
// s1 is shorter, so is lower in collating sequence than s2.
//
return(-1);
}
else
//
// s2 is shorter, so is lower in collating sequence than s1.
//
return(1);
}
size_t
StrLen(
PBYTE pString
)
{
ULONG i;
//
// Scan for next space, ';' token seperator, or end of line.
//
for (i = 0; !EOL(&pString[i]); i++)
if(pString[i] == ';' || pString[i] == ' ')
break;
return(i);
}
int
AFM2NTMStrCpy(
const VOID *str1,
size_t cchDest,
const VOID *str2
)
/*++
Routine Description:
Copies str2 to str1. Strings may be terminated by either a null char or a
space.
Arguments:
str2 - source string
str1 - dest string
cchDest - size (in chars) of the dest buffer
Return Value:
Number of bychars copied
--*/
{
PBYTE s1 = (PBYTE) str1, s2 = (PBYTE) str2;
ULONG n = 0;
if (cchDest > 0)
{
while (!IS_WHTSPACE(&s2[n]) && (cchDest > 0))
{
s1[n] = s2[n++];
cchDest--;
}
if (cchDest == 0)
{
//
// In this case n must be the original cchDest
// value, so we have to truncate the dest string.
//
n--;
}
s1[n] = '\0';
}
return(n);
}
static int __cdecl
StrPos(
const PBYTE str1,
CHAR c
)
/*++
Routine Description:
Retuns index of char c in str1. String may be terminated by either a
CR/LF, or a ':'.
Arguments:
str1 - string to search
c - search char
Return Value:
Index of c in str1, or -1 if not found
--*/
{
ULONG i = 0;
while (!EOL(&str1[i]))
{
if (str1[i++] == c)
return(i - 1);
}
return(-1);
}
int __cdecl
CmpUniCodePts(
const VOID *p1,
const VOID *p2
)
/*++
Routine Description:
Compares the Unicode char code field of two UPSCODEPT structs.
Arguments:
p1, p2 - Strings to compare.
Return Value:
-1 => p1 < p2
1 => p1 > p2
0 => p1 = p2
--*/
{
PUPSCODEPT ptr1 = (PUPSCODEPT) p1, ptr2 = (PUPSCODEPT) p2;
//
// Compare Unicode code point fields.
//
if (ptr1->wcUnicodeid > ptr2->wcUnicodeid)
return(1);
else if (ptr1->wcUnicodeid < ptr2->wcUnicodeid)
return(-1);
else
return(0);
}
static int __cdecl
CmpUnicodePsNames(
const VOID *p1,
const VOID *p2
)
/*++
Routine Description:
Compares two strings. This routine is meant to be used only for looking
up a char name key in an array of UPSCODEPT structs.
Arguments:
p1 - a null or whitespace terminated string.
p2 - points to a UPSCODEPT struct.
Return Value:
-1 => p1 < p2
1 => p1 > p2
0 => p1 = p2
--*/
{
PBYTE ptr1 = (PBYTE) p1;
PUPSCODEPT ptr2 = (PUPSCODEPT) p2;
//
// Compare name fields.
//
return (StrCmp(ptr1, ptr2->pPsName));
}
static int __cdecl
CmpPsChars(
const VOID *p1,
const VOID *p2
)
/*++
Routine Description:
Compares a null or space terminated string to the pPsName string field
of a PSCHARMETRICS struct.
Arguments:
p1 - a null or whitespace terminated string.
p2 - points to a PSCHARMETRICS struct.
Return Value:
-1 => p1 < p2
1 => p1 > p2
0 => p1 = p2
--*/
{
PBYTE ptr1 = (PBYTE) p1;
PPSCHARMETRICS ptr2 = (PPSCHARMETRICS) p2;
//
// Compare name fields.
//
return (StrCmp(ptr1, ptr2->pPsName));
}
static int __cdecl
CmpPsNameWinCpt(
const VOID *p1,
const VOID *p2
)
/*++
Routine Description:
Compares a null or space terminated string to the pPsName string field
of a WINCPT struct.
Arguments:
p1 - a null or whitespace terminated string.
p2 - points to a WINCPT struct.
Return Value:
-1 => p1 < p2
1 => p1 > p2
0 => p1 = p2
--*/
{
PBYTE ptr1 = (PBYTE) p1;
PWINCPT ptr2 = (PWINCPT) p2;
//
// Compare name fields.
//
return(StrCmp(ptr1, ptr2->pPsName));
}
static int __cdecl
CmpKernPairs(
const VOID *p1,
const VOID *p2
)
/*++
Routine Description:
Compares 2 FD_KERNINGPAIR structs according to a key = wcSecond << 16 +
wcFirst.
Arguments:
p1, p2 - ptrs to FD_KERNINGPAIRS to compare.
Return Value:
-1 => p1 < p2
1 => p1 > p2
0 => p1 = p2
--*/
{
FD_KERNINGPAIR *ptr1 = (FD_KERNINGPAIR *) p1;
FD_KERNINGPAIR *ptr2 = (FD_KERNINGPAIR *) p2;
ULONG key1, key2;
//
// Compute key for each kern pair.
//
key1 = (ptr1->wcSecond << 16) + ptr1->wcFirst;
key2 = (ptr2->wcSecond << 16) + ptr2->wcFirst;
if (key1 > key2)
{
return(1);
}
else if (key2 > key1)
{
return(-1);
}
else
{
return(0);
}
}
int __cdecl
CmpGlyphRuns(
const VOID *p1,
const VOID *p2
)
/*++
Routine Description:
Compares the starting Unicode point of two GLYPHRUN structs.
Arguments:
p1, p2 - GLYPHRUNs to compare.
Return Value:
-1 => p1 < p2
1 => p1 > p2
0 => p1 = p2
--*/
{
PGLYPHRUN ptr1 = (PGLYPHRUN) p1, ptr2 = (PGLYPHRUN) p2;
//
// Compare Unicode code point fields.
//
if (ptr1->wcLow > ptr2->wcLow)
return(1);
else if (ptr1->wcLow < ptr2->wcLow)
return(-1);
else
return(0);
}
ULONG
CreateGlyphSets(
PGLYPHSETDATA *pGlyphSet,
PWINCODEPAGE pWinCodePage,
PULONG *pUniPs
)
/*++
Routine Description:
Create a GLYPHSETDATA data structure, which maps Unicode pts to Windows
codepage/codepoints.
Arguments:
pGlyphSet - A PGLYPHSETDATA pointer which upon successful
completion contains the address of the newly allocated GLYPHSETDATA
struct.
pWinCodePage - a pointer to a windows code page info struct
used to create the GLYPHSETDATA struct.
pUniPs - Upon successful completion, -> a table which maps 0-based Glyph
Indices of chars in the GLYPHRUNS of the GLYPHSETDATA struct for this
charset to indices into the UnicodetoPs structure which maps Unicode
points to PS char information.
Return Value:
NULL => error
Otherwise total size of all GLYPHSETDATAs and related structs which are
created.
--*/
{
int i, j;
ULONG c;
int cRuns;
int cChars;
int cCharRun;
WCHAR wcLast;
WCHAR wcRunStrt;
PGLYPHSETDATA pGlyphSetData;
PGLYPHRUN pGlyphRuns;
ULONG ulSize;
PVOID pMapTable;
PWINCPT pWinCpt;
PCODEPAGEINFO pCodePageInfo;
BOOLEAN bFound, bIsPiFont;
DWORD dwEncodingNameOffset;
DWORD dwGSNameSize, dwCodePageInfoSize, dwCPIGSNameSize, dwGlyphRunSize;
BOOL bSingleCodePage;
bSingleCodePage = (pWinCodePage->usNumBaseCsets == 1) ? TRUE : FALSE;
ulSize = 0;
cChars = cRuns = i = 0;
if ((bIsPiFont = pWinCodePage->pCsetList[0] == CS_SYMBOL))
{
//
// This is a symbol font. We takes care of PS char codes from 0x20 to
// 0xff. We also map PS char codes to a single run in the Unicode
// private range.
//
cChars = (256 - 32) + 256;
cRuns = 1 * 2;
bSingleCodePage = FALSE;
VERBOSE(("Pi Font"));
}
else
{
//
// Process all unicode code pts. to determine the number of Unicode
// point runs present in this windows codepage.
//
do
{
//
// Proceed until the starting codepoint of next run is found.
//
// for (j = 0; j < pWinCodePage->usNumBaseCsets &&
// i < NUM_PS_CHARS;
// j++)
// if (CSET_SUPPORT(UnicodetoPs[i].flCharSets, pWinCodePage->pCsetList[j]))
// break;
// else
// i++;
//
bFound = FALSE;
for (; i < NUM_PS_CHARS; i++)
{
for (j = 0; j < pWinCodePage->usNumBaseCsets; j++)
{
if (CSET_SUPPORT(UnicodetoPs[i].flCharSets, pWinCodePage->pCsetList[j]))
{
bFound = TRUE;
break;
}
}
if (bFound)
break;
}
//
// Check to see if we've scanned all Unicode points.
//
if (i == NUM_PS_CHARS)
break;
//
// Start a new run.
//
cCharRun = 0;
wcRunStrt = UnicodetoPs[i].wcUnicodeid;
//
// Chars are only part of the run if they are supported
// in the current charset.
//
while (i < NUM_PS_CHARS &&
UnicodetoPs[i].wcUnicodeid == wcRunStrt + cCharRun)
{
for (j = 0; j < pWinCodePage->usNumBaseCsets; j++)
{
if (CSET_SUPPORT(UnicodetoPs[i].flCharSets, pWinCodePage->pCsetList[j]))
{
cCharRun++;
break;
}
}
i++;
}
if (cCharRun)
{
cChars += cCharRun;
cRuns++;
}
} while (i < NUM_PS_CHARS);
}
//
// Compute the total amount of memory required for the GLYPHSETDATA array
// and all other related data. We need
// 1. one CODEPAGEINFO struct for each base charset supported by this font,
// 2. one GLYPHRUN struct for each run, and
// 3. four bytes per char to store codepage and codepoint or two bytes per
// char to store only codepoint for the mapping table.
//
dwGSNameSize = ALIGN4(strlen(pWinCodePage->pszCPname) + 1);
dwCodePageInfoSize = ALIGN4(pWinCodePage->usNumBaseCsets * sizeof (CODEPAGEINFO));
dwGlyphRunSize = ALIGN4(cRuns * sizeof (GLYPHRUN));
ulSize = ALIGN4(sizeof(GLYPHSETDATA))
+ dwGSNameSize
+ dwCodePageInfoSize
+ dwGlyphRunSize;
//
// Account for the size of the mapping table.
//
ulSize += bSingleCodePage ? ALIGN4((cChars * sizeof (WORD))) : (cChars * sizeof (DWORD));
//
// Account for the size of CODEPAGE name strings found in CODEPAGEINFO
// struct(s).
//
for (dwCPIGSNameSize = 0, j = 0; j < pWinCodePage->usNumBaseCsets; j++)
{
dwCPIGSNameSize += ALIGN4(strlen(aPStoCP[pWinCodePage->pCsetList[j]].pGSName) + 1);
}
ulSize += dwCPIGSNameSize;
//
// Allocate memory for the GLYPHSETDATA struct.
//
if ((pGlyphSetData = (PGLYPHSETDATA) MemAllocZ((size_t) ulSize)) == NULL)
{
ERR(("makentf - CreateGlyphSets: malloc\n"));
return(FALSE);
}
//
// Allocate an array of ULONGs to store the index of each char into
// the Unicode->Ps translation table.
//
if (!bIsPiFont)
{
if ((*pUniPs = (PULONG) MemAllocZ((size_t)(cChars * sizeof(ULONG)))) == NULL)
{
ERR(("makentf - CreateGlyphSets: malloc\n"));
return(FALSE);
}
}
//
// Init GLYPHSETDATA fields.
//
pGlyphSetData->dwSize = ulSize;
pGlyphSetData->dwVersion = GLYPHSETDATA_VERSION;
pGlyphSetData->dwFlags = 0;
pGlyphSetData->dwGlyphSetNameOffset = ALIGN4(sizeof(GLYPHSETDATA));
pGlyphSetData->dwGlyphCount = cChars;
pGlyphSetData->dwCodePageCount = pWinCodePage->usNumBaseCsets;
pGlyphSetData->dwCodePageOffset = pGlyphSetData->dwGlyphSetNameOffset + dwGSNameSize;
pGlyphSetData->dwRunCount = cRuns;
pGlyphSetData->dwRunOffset = pGlyphSetData->dwCodePageOffset + dwCodePageInfoSize + dwCPIGSNameSize;
pGlyphSetData->dwMappingTableOffset = pGlyphSetData->dwRunOffset + dwGlyphRunSize;
//
// Set the mapping table type flag to dwFlags field.
//
pGlyphSetData->dwFlags |= bSingleCodePage ? GSD_MTT_WCC : GSD_MTT_DWCPCC;
//
// Store code page name
//
StringCchCopyA((PSZ) MK_PTR(pGlyphSetData, dwGlyphSetNameOffset), dwGSNameSize, pWinCodePage->pszCPname);
//
// Initialize a CODEPAGEINFO struct for each base charset supported
// by this font.
//
pCodePageInfo = (PCODEPAGEINFO) MK_PTR(pGlyphSetData, dwCodePageOffset);
dwEncodingNameOffset = dwCodePageInfoSize;
for (j = 0; j < pWinCodePage->usNumBaseCsets; j++, pCodePageInfo++)
{
//
// Save CODEPAGEINFO. We don't use PS encoding vectors.
//
pCodePageInfo->dwCodePage = aPStoCP[pWinCodePage->pCsetList[j]].usACP;
pCodePageInfo->dwWinCharset = (DWORD)aPStoCP[pWinCodePage->pCsetList[j]].jWinCharset;
pCodePageInfo->dwEncodingNameOffset = dwEncodingNameOffset;
pCodePageInfo->dwEncodingVectorDataSize = 0;
pCodePageInfo->dwEncodingVectorDataOffset = 0;
//
// Copy codepage name string to end of array of CODEPAGEINFOs.
//
StringCchCopyA((PBYTE)MK_PTR(pCodePageInfo, dwEncodingNameOffset),
ALIGN4(strlen(aPStoCP[pWinCodePage->pCsetList[j]].pGSName) + 1),
aPStoCP[pWinCodePage->pCsetList[j]].pGSName);
//
// Adjust the offset to the codepage name for the next CODEPAGINFO structure
//
dwEncodingNameOffset -= ALIGN4(sizeof (CODEPAGEINFO));
dwEncodingNameOffset += ALIGN4(strlen((PBYTE)MK_PTR(pCodePageInfo, dwEncodingNameOffset)) + 1);
}
//
// Init ptr to the mapping table.
//
pGlyphRuns = GSD_GET_GLYPHRUN(pGlyphSetData);
pMapTable = GSD_GET_MAPPINGTABLE(pGlyphSetData);
//
// Make another pass through the Unicode points to initialize the Unicode
// runs and gi->codepage/codept mapping array for this codepage.
//
cRuns = 0;
if (bIsPiFont)
{
//
// Glyphset for Pi fonts has 1 run of 256 minus 0x20(it's 0x1f
// actually) chars over the Unicode private range.
//
pGlyphRuns[cRuns].wcLow = NOTDEF1F;
pGlyphRuns[cRuns].wGlyphCount = 256 - NOTDEF1F;
pGlyphRuns[cRuns + 1].wcLow = UNICODE_PRV_STRT;
pGlyphRuns[cRuns + 1].wGlyphCount = 256;
//
// We know that Pi fonts support only single encoding, but we also
// provide the mapping table for Unicode range f000...f0ff, which
// is mapped to PS code point 00...ff.
//
for (i = 0; i < 256 - NOTDEF1F; i++)
{
((DWORD*)pMapTable)[i] =
aPStoCP[pWinCodePage->pCsetList[0]].usACP << 16 | (i + NOTDEF1F);
}
for (i = 0; i < 256; i++)
{
((DWORD*)pMapTable)[i + 256 - NOTDEF1F] =
aPStoCP[pWinCodePage->pCsetList[0]].usACP << 16 | i;
}
}
else
{
cChars = i = 0;
do
{
//
// Proceed until the starting codepoint of next run is found.
//
// for (j = 0; j < pWinCodePage->usNumBaseCsets &&
// i < NUM_PS_CHARS;
// j++)
// if (CSET_SUPPORT(UnicodetoPs[i].flCharSets, pWinCodePage->pCsetList[j]))
// break;
// else
// i++;
//
bFound = FALSE;
for (; i < NUM_PS_CHARS; i++)
{
for (j = 0; j < pWinCodePage->usNumBaseCsets; j++)
{
if (CSET_SUPPORT(UnicodetoPs[i].flCharSets, pWinCodePage->pCsetList[j]))
{
bFound = TRUE;
break;
}
}
if (bFound)
break;
}
//
// Check to see if we've scanned all Unicode points.
//
if (i == NUM_PS_CHARS)
break;
//
// Start a new run.
//
cCharRun = 0;
wcRunStrt = UnicodetoPs[i].wcUnicodeid;
//
// Chars are only part of the run if they are supported
// in the current charset.
//
while (i < NUM_PS_CHARS &&
UnicodetoPs[i].wcUnicodeid == wcRunStrt + cCharRun)
{
for (j = 0, bFound = FALSE;
j < pWinCodePage->usNumBaseCsets && !bFound; j++)
{
if (CSET_SUPPORT(UnicodetoPs[i].flCharSets, pWinCodePage->pCsetList[j]))
{
if (((pWinCpt =
(PWINCPT) bsearch(UnicodetoPs[i].pPsName,
aPStoCP[pWinCodePage->pCsetList[j]].aWinCpts,
aPStoCP[pWinCodePage->pCsetList[j]].ulChCnt,
sizeof(WINCPT),
CmpPsNameWinCpt))
!= NULL))
{
//
// Found a corresponding PS char in the current
// windows codepage. Save it in the mapping table.
//
if (bSingleCodePage)
{
((WORD*)pMapTable)[cChars] = pWinCpt->usWinCpt;
}
else
{
((DWORD*)pMapTable)[cChars] =
aPStoCP[pWinCodePage->pCsetList[j]].usACP << 16 | pWinCpt->usWinCpt;
}
bFound = TRUE;
}
else if (j == (pWinCodePage->usNumBaseCsets - 1))
{
//
// Corresponding PS char was not found. Use Win
// codept 0 as .notdef char and base codepage.
//
if (bSingleCodePage)
((WORD*)pMapTable)[cChars] = 0;
else
((DWORD*)pMapTable)[cChars] =
aPStoCP[pWinCodePage->pCsetList[0]].usACP << 16;
bFound = TRUE;
}
//
// If char is present in this codepage, save index in
// Unicode->Ps table.
//
if (bFound)
{
(*pUniPs)[cChars] = i;
cChars++;
cCharRun++;
}
}
}
i++;
}
if (cCharRun)
{
pGlyphRuns[cRuns].wcLow = wcRunStrt;
pGlyphRuns[cRuns].wGlyphCount = (WORD)cCharRun;
cRuns++;
}
} while (i < NUM_PS_CHARS);
}
//
// Return success.
//
*pGlyphSet = pGlyphSetData;
if (bVerbose && !bOptimize)
{
printf("GLYPHSETDATA:dwFlags:%08X\n", pGlyphSetData->dwFlags);
printf("GLYPHSETDATA:dwGlyphSetNameOffset:%s\n",
(PSZ)MK_PTR(pGlyphSetData, dwGlyphSetNameOffset));
printf("GLYPHSETDATA:dwGlyphCount:%ld\n", pGlyphSetData->dwGlyphCount);
printf("GLYPHSETDATA:dwRunCount:%ld\n", pGlyphSetData->dwRunCount);
printf("GLYPHSETDATA:dwCodePageCount:%ld\n", pGlyphSetData->dwCodePageCount);
{
DWORD dw;
PCODEPAGEINFO pcpi = (PCODEPAGEINFO)MK_PTR(pGlyphSetData, dwCodePageOffset);
for (dw = 1; dw <= pGlyphSetData->dwCodePageCount; dw++)
{
printf("CODEPAGEINFO#%ld:dwCodePage:%ld\n", dw, pcpi->dwCodePage);
printf("CODEPAGEINFO#%ld:dwWinCharset:%ld\n", dw, pcpi->dwWinCharset);
printf("CODEPAGEINFO#%ld:dwEncodingNameOffset:%s\n",
dw, (PSZ)MK_PTR(pcpi, dwEncodingNameOffset));
pcpi++;
}
}
if (bIsPiFont)
{
printf("(Single codepage with dwFlags bit 0 cleared.)\n");
printf("(Special for Symbol glyphset)\n");
}
printf("\n");
}
return(ulSize);
}
LONG
FindClosestCodePage(
PWINCODEPAGE *pWinCodePages,
ULONG ulNumCodePages,
CHSETSUPPORT chSets,
PCHSETSUPPORT pchCsupMatch
)
/*++
Routine Description:
Given a list of ptrs to WINCODEPAGE structs, determine which WINCODEPAGE's
component charsets best match the charsets value in chSets.
Arguments:
pWinCodePages - List of PWINCODEPAGES.
ulNumCodePages - Number of entries in pWinCodePages
chSets - CHSETSUPPORT value which indicates which standard charsets
are supported by this font.
pchCsupMatch - Pointer to a CHSETSUPPORT variable which returns the
supported charsets of the code page which most closely matches the
chSets value. If no matching codepages are found, the value will be 0.
Return Value:
-1 => no matching Codepages were found.
Otherwise this is the index in pWinCodePages of the "best match" codepage.
--*/
{
ULONG c;
LONG j;
LONG cpMatch;
LONG nCurCsets, nLastCsets;
FLONG flCurCset;
cpMatch = -1;
//
// Scan the list of Windows codepages.
//
for (c = 0, nLastCsets = 0; c < ulNumCodePages; c++)
{
//
// Hack..Hack! If this is the Unicode codepage, ignore it as
// no NTMs should reference it!
//
if (strcmp(pWinCodePages[c]->pszCPname, UNICODE_GS_NAME))
{
nCurCsets = flCurCset = 0;
//
// Determine which charsets in the current codepage are
// a match for those supported by the current font.
//
for (j = 0; j < pWinCodePages[c]->usNumBaseCsets; j++)
{
if (CSET_SUPPORT(chSets, pWinCodePages[c]->pCsetList[j]))
{
nCurCsets++;
}
flCurCset |= CSUP(pWinCodePages[c]->pCsetList[j]);
}
if (flCurCset == (FLONG) chSets)
{
//
// Found a charset which supports ALL of the font's charsets.
//
cpMatch = (LONG) c;
*pchCsupMatch = flCurCset;
break;
}
else if (nCurCsets > nLastCsets)
{
//
// This Windows codepage is the maximal match so far.
//
nLastCsets = nCurCsets;
cpMatch = (LONG) c;
*pchCsupMatch = flCurCset;
}
}
}
return(cpMatch);
}
ULONG
GetAFMCharWidths(
PBYTE pAFM,
PWIDTHRUN *pWidthRuns,
PPSCHARMETRICS pFontChars,
PULONG pUniPs,
ULONG ulChCnt,
PUSHORT pusAvgCharWidth,
PUSHORT pusMaxCharWidth
)
/*++
Routine Description:
Given a memory mapped AFM file ptr and a ptr to a which maps glyph indices
to UPSCODEPT Unicode->Ps translation structs, fill memory with a list of
WIDTHRUN structs which provide char width information.
Arguments:
pAFM - Pointer to memory mapped AFM file.
pWidthRuns - If NULL, this is a size request and the function returns the
total size in bytes of all WIDTHRUN structs required for this font.
Otherwise the ptr is assumed to point to a buffer large enough to
hold the number of required WIDTHRUNs.
pFontChars - pointer a table of PS font char metrics info previously
created by calling the BuildPSCharMetrics function. This array contains
per char metric information.
pUniPs - Points to a table which maps 0-based Glyph Indices of chars
in the GLYPHRUNS of the GLYPHSETDATA struct for this font to indices
into the UnicodetoPs structure which maps Unicode points to PS char
information. This mapping array is created by the CreateGlyphSet function
defined in this module.
ulChCnt - Number of chars in the GLYPHSET for this font. This most likely
is not the same as the number of chars defined in the font's AFM.
pulAvgCharWidth - pts to a USHORT used to return the average char
width of the font. If NULL the average char width is not returned.
pulMaxCharWidth - pts to a USHORT used to return the max char
width of the font. If NULL the max char width is not returned.
Return Value:
0 => error.
Otherwise returns number of WIDTHRUN structs required for this font.
--*/
{
ULONG i, j, curChar;
int cRuns, cRealRuns;
int cChars;
int cCharRun;
ULONG firstCharWidth;
ULONG curCharWidth;
ULONG notdefwidth;
WCHAR wcRunStrt;
USHORT chCnt;
PBYTE pToken;
PBYTE pChMet;
PPSCHARMETRICS pCurChar;
BOOLEAN bIsPiFont, bIsCJKFont;
CHAR ch;
BYTE CharNameBuffer[32];
PBYTE pChName;
// fix bug 240339, jjia, 8/3/98
BOOLEAN bWidthRunComplex;
PWORD pWidthArray;
// Fixed bug Adobe #367195.
// In this program, when handling PiFont, we always assume the first character
// in the CharMetrics is a space (32) char. However, some special font such as
// HoeflerText-Ornaments does not follow this rule. the 1st char in the font is 9,
// the 2ed char is 32. Added this flag to handle this kind of fonts.
BOOLEAN bTwoSpace = FALSE;
//
// Determine if this is a Pi or CJK font.
//
bIsPiFont = IsPiFont(pAFM);
bIsCJKFont = (IsCJKFont(pAFM) != 0);
//
// Get ptr to AFM char metrics.
//
pChMet = FindAFMToken(pAFM, PS_CH_METRICS_TOK);
if (pChMet == NULL) // Fixed bug 354007
return (FALSE);
//
// Current pos should be the character count field.
//
for (i = 0; i < (int) StrLen(pChMet); i++)
{
if (!IS_NUM(&pChMet[i]))
{
return(FALSE);
}
}
chCnt = (USHORT)atoi(pChMet);
(ULONG_PTR) pChMet += i;
//
// If requested, make a pass through the PS Char Metrics to determine
// the max char width.
//
if (pusMaxCharWidth != NULL)
{
*pusMaxCharWidth = 0;
for (i = 0; i < chCnt; i++)
{
if (pFontChars[i].chWidth > *pusMaxCharWidth)
{
*pusMaxCharWidth = (USHORT) pFontChars[i].chWidth & 0xffff;
}
}
}
//
// Search for .notdef char in list of PS chars, get .notdef char width.
//
if (bIsPiFont)
{
notdefwidth = pFontChars[0].chWidth;
}
else if ((pCurChar = (PPSCHARMETRICS) bsearch("space",
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp)) != NULL)
notdefwidth = pCurChar->chWidth;
else
notdefwidth = 0;
//
// If average width was requested, process string of sample chars 1
// at a time to compute average char width.
// DCR --: Assume the sample is western alphabetic + space.
// Need to fix this for non-western fonts !!!.
//
if (pusAvgCharWidth != NULL)
{
LONG lWidth, count; // a long to prevent OverFlow
WINCPTOPS *pCPtoPS;
WINCPT sortedWinCpts[MAX_CSET_CHARS]; // maxiaml 255 chars
CHSETSUPPORT flCsupGlyphSet;
ULONG k;
BYTE *pSampleStr;
//
// Determine which charsets this font supports.
//
(VOID)GetAFMCharSetSupport(pAFM, &flCsupGlyphSet);
if (flCsupGlyphSet == CS_228 || flCsupGlyphSet == CS_314)
{
pCPtoPS = &aPStoCP[CS_228];
}
else
{
// default - use the ANSI code page table
pCPtoPS = &aPStoCP[CS_ANSI];
}
SortWinCPT(&(sortedWinCpts[0]), pCPtoPS);
lWidth = 0;
count = 0;
k = 0x20; // start from FirstChar !!
for (i = 0; i < pCPtoPS->ulChCnt && k <= 0xFF; i++, k++)
{
pCurChar = NULL;
if (bIsPiFont)
{
if (i<chCnt)
pCurChar = &(pFontChars[ i ]);
// We don't need Not-Encoded characters in a PiFont.
if (pCurChar && strcmp(pCurChar->pPsName, "-1") == 0 )
pCurChar = NULL;
}
else
{
// sortedWinCpts is sorted by usWinCpt, so skip UP to what we want
while (k > sortedWinCpts[i].usWinCpt && i < pCPtoPS->ulChCnt )
{
i++;
}
// Take notdef chars in the 0x20 to 0xff range - gaps
while (k < sortedWinCpts[i].usWinCpt && k <= 0xFF )
{
k++;
lWidth += notdefwidth;
count++;
}
pSampleStr = NULL;
if (k == sortedWinCpts[i].usWinCpt)
pSampleStr = sortedWinCpts[i].pPsName;
if (pSampleStr == NULL)
continue;
pCurChar = (PPSCHARMETRICS) bsearch(pSampleStr,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
if (pCurChar != NULL && pCurChar->pPsName && pCurChar->pPsName[0] != 0 &&
pCurChar->chWidth > 0)
{
lWidth += (LONG) pCurChar->chWidth;
count++;
}
else
{
lWidth += notdefwidth;
count++;
}
}
if (count)
lWidth = (lWidth + count/2)/count;
if (lWidth == 0)
{
lWidth = 0 ;
// This is a buggy font. Or CJK font!!!
// In this case we must come up with the reasonable number different from
// zero. This number is used in computing font trasfroms.
for (i = 0; i <= chCnt; i++)
lWidth += (LONG) (pFontChars[i].chWidth & 0xffff);
lWidth = (lWidth + chCnt / 2) / chCnt ;
// ASSERTMSG(*pusAvgCharWidth, ("PSCRIPT: pifi->fwdAveCharWidth == 0\n"));
}
// Now assign it to the original (short) width
*pusAvgCharWidth = (FWORD) lWidth;
if (*pusAvgCharWidth == 0 || (bIsCJKFont && *pusAvgCharWidth < EM))
{
*pusAvgCharWidth = EM;
}
if (bIsCJKFont)
{
// DCR: couldn't divide by 2 simply for C and K.
*pusAvgCharWidth = *pusAvgCharWidth / 2;
}
}
//
// Determine the amount of memory required for the WIDTHRUNS which cover
// all possible points in the font's charset.
//
i = cRuns = 0;
if (bIsPiFont)
{
curChar = 1;
if (atoi(pFontChars[i].pPsName) == (BYTE) ' ')
{
curCharWidth = pFontChars[i].chWidth;
}
else
{
// Fixed bug Adobe #367195
if (atoi(pFontChars[i + 1].pPsName) == (BYTE) ' ')
bTwoSpace = TRUE;
curCharWidth = notdefwidth;
}
}
else
{
//
// Setup ptr to "char name" based on whether this is a
// western or CJK font.
//
if (bIsCJKFont)
{
_ultoa(pUniPs[i], CharNameBuffer, 10);
pChName = CharNameBuffer;
}
else
{
pChName = UnicodetoPs[pUniPs[i]].pPsName;
}
if ((pCurChar = (PPSCHARMETRICS) bsearch(pChName,
pFontChars,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
CmpPsChars)) == NULL)
{
curCharWidth = notdefwidth;
}
else
{
curCharWidth = pCurChar->chWidth;
}
}
do
{
//
// Start new run.
//
cCharRun = 1;
wcRunStrt = (USHORT) (i & 0xffff);
for (firstCharWidth = curCharWidth, i++; i < ulChCnt; i++)
{
if (bIsPiFont)
{
if (curChar < chCnt)
{
// Fixed bug Adobe #367185
if ((bTwoSpace) &&
((ULONG) atoi(pFontChars[curChar].pPsName) == (i - 1 + (BYTE) ' ')))
{
curCharWidth = pFontChars[curChar].chWidth;
curChar++;
}
else if ((!bTwoSpace) &&
((ULONG) atoi(pFontChars[curChar].pPsName) == (i + (BYTE) ' ')))
{
curCharWidth = pFontChars[curChar].chWidth;
curChar++;
}
else
{
curCharWidth = notdefwidth;
}
}
else
{
curCharWidth = notdefwidth;
}
}
else
{
//
// Setup ptr to "char name" based on whether this is a
// western or CJK font.
//
if (bIsCJKFont)
{
_ultoa(pUniPs[i], CharNameBuffer, 10);
pChName = CharNameBuffer;
}
else
{
pChName = UnicodetoPs[pUniPs[i]].pPsName;
}
if((pCurChar = (PPSCHARMETRICS) bsearch(pChName,
pFontChars,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
CmpPsChars)) != NULL)
{
curCharWidth = pCurChar->chWidth;
}
else
{
curCharWidth = notdefwidth;
}
}
if ((curCharWidth == firstCharWidth) &&
((SHORT) i == (wcRunStrt + cCharRun)))
{
cCharRun++;
}
else
{
break;
}
}
cRuns++;
} while (i < ulChCnt);
// Fix bug 240339, jjia, 8/3/98
if ((cRuns * sizeof(WIDTHRUN)) >
(ulChCnt * sizeof(WORD) + sizeof(WIDTHRUN)))
bWidthRunComplex = TRUE;
else
bWidthRunComplex = FALSE;
if (pWidthRuns == NULL)
{
//
// Return number of WIDTHRUNs only.
//
if (!bIsPiFont)
{
// Fix bug 240339, jjia, 8/3/98
if (bWidthRunComplex)
return (ALIGN4(ulChCnt * sizeof(WORD) + sizeof(WIDTHRUN)));
else
return (ALIGN4(cRuns * sizeof(WIDTHRUN)));
}
else
{
//
// Hack to support 2 Unicode runs.
//
return (ALIGN4(cRuns * 2 * sizeof(WIDTHRUN)));
}
}
//
// Create the list of WIDTHRUNs.
//
cRealRuns = cRuns;
i = cRuns = 0;
// Fix bug 240339, jjia, 8/3/98
if (bWidthRunComplex && (!bIsPiFont))
{
(*pWidthRuns)[0].wStartGlyph = (WORD) (i & 0xffff);
(*pWidthRuns)[0].dwCharWidth = WIDTHRUN_COMPLEX;
(*pWidthRuns)[0].wGlyphCount = (WORD)ulChCnt;
cRuns = 1;
pWidthArray = (PWORD)&(*pWidthRuns)[1];
for (; i < ulChCnt; i++)
{
if (bIsCJKFont)
{
_ultoa(pUniPs[i], CharNameBuffer, 10);
pChName = CharNameBuffer;
}
else
{
pChName = UnicodetoPs[pUniPs[i]].pPsName;
}
if((pCurChar = (PPSCHARMETRICS) bsearch(pChName,
pFontChars,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
CmpPsChars)) == NULL)
{
//
// Char is not defined in this font.
//
pWidthArray[i] = (WORD)notdefwidth;
}
else
{
//
// Char is defined in this font.
//
pWidthArray[i] = (WORD)(pCurChar->chWidth);
}
}
return (cRuns);
}
if (bIsPiFont)
{
curChar = 1;
if (atoi(pFontChars[i].pPsName) == (BYTE) ' ')
{
curCharWidth = pFontChars[i].chWidth;
}
else
{
// Fixed bug Adobe #367195
if (atoi(pFontChars[i + 1].pPsName) == (BYTE) ' ')
bTwoSpace = TRUE;
curCharWidth = notdefwidth;
}
}
else
{
//
// Setup ptr to "char name" based on whether this is a
// western or CJK font.
//
if (bIsCJKFont)
{
_ultoa(pUniPs[i], CharNameBuffer, 10);
pChName = CharNameBuffer;
}
else
{
pChName = UnicodetoPs[pUniPs[i]].pPsName;
}
if ((pCurChar = (PPSCHARMETRICS) bsearch(pChName,
pFontChars,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
CmpPsChars)) != NULL)
{
curCharWidth = pCurChar->chWidth;
}
else
{
curCharWidth = notdefwidth;
}
}
do
{
//
// Start new run.
//
cCharRun = 1;
wcRunStrt = (USHORT) (i & 0xffff);
for (firstCharWidth = curCharWidth, i++; i < ulChCnt; i++)
{
if (bIsPiFont)
{
if (curChar < chCnt)
{
// Fixed bug Adobe #367185
if ((bTwoSpace) &&
((ULONG) atoi(pFontChars[curChar].pPsName) == (i - 1 + (BYTE) ' ')))
{
curCharWidth = pFontChars[curChar].chWidth;
curChar++;
}
else if ((!bTwoSpace) &&
((ULONG) atoi(pFontChars[curChar].pPsName) == (i + (BYTE) ' ')))
{
curCharWidth = pFontChars[curChar].chWidth;
curChar++;
}
else
{
curCharWidth = notdefwidth;
}
}
else
{
curCharWidth = notdefwidth;
}
}
else
{
//
// Setup ptr to "char name" based on whether this is a
// western or CJK font.
//
if (bIsCJKFont)
{
_ultoa(pUniPs[i], CharNameBuffer, 10);
pChName = CharNameBuffer;
}
else
{
pChName = UnicodetoPs[pUniPs[i]].pPsName;
}
if((pCurChar = (PPSCHARMETRICS) bsearch(pChName,
pFontChars,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
CmpPsChars)) == NULL)
{
//
// Char is not defined in this font.
//
curCharWidth = notdefwidth;
}
else
{
//
// Char is defined in this font.
//
curCharWidth = pCurChar->chWidth;
}
}
if ((curCharWidth == firstCharWidth) &&
((SHORT) i == (wcRunStrt + cCharRun)))
{
cCharRun++;
}
else
{
break;
}
}
(*pWidthRuns)[cRuns].wStartGlyph = wcRunStrt;
(*pWidthRuns)[cRuns].dwCharWidth = firstCharWidth;
(*pWidthRuns)[cRuns].wGlyphCount = (WORD)cCharRun;
if (bIsPiFont)
{
//
// Hack to support 2 unicode runs.
//
(*pWidthRuns)[cRuns + cRealRuns].wStartGlyph = wcRunStrt;
(*pWidthRuns)[cRuns + cRealRuns].dwCharWidth = firstCharWidth;
(*pWidthRuns)[cRuns + cRealRuns].wGlyphCount = (WORD)cCharRun;
}
cRuns++;
} while (cRuns < cRealRuns);
if (bIsPiFont)
{
return(cRuns * 2);
}
else
{
return(cRuns);
}
}
ULONG
GetAFMETM(
PBYTE pAFM,
PPSCHARMETRICS pFontChars,
PETMINFO pEtmInfo
)
/*++
Routine Description:
Given a memory mapped AFM file ptr and a ptr to a which maps glyph indices
to UPSCODEPT Unicode->Ps translation structs, fill memory with a list of
WIDTHRUN structs which provide char width information.
Arguments:
pAFM - Pointer to memory mapped AFM file.
pFontChars - pointer a table of PS font char metrics info previously
created by calling the BuildPSCharMetrics function. This array contains
per char metric information.
pulEtmInfo - pts to an ETMINFO struct used to return EXTEXTMETRIC
info which must be derived from the AFM char metrics. If NULL the
structure is not returned.
Return Value:
0 => error.
1 => success
--*/
{
ULONG i;
USHORT chCnt;
PPSCHARMETRICS pCurChar;
BOOLEAN bIsPiFont;
CHSETSUPPORT csIsCJKFont;
PBYTE pChMet;
PSTR pCJKCapH, pCJKx;
//
// Determine if this is a Pi or CJK font.
//
bIsPiFont = IsPiFont(pAFM);
csIsCJKFont = IsCJKFont(pAFM);
//
// Get ptr to AFM char metrics.
//
pChMet = FindAFMToken(pAFM, PS_CH_METRICS_TOK);
if (pChMet == NULL) // Fixed bug 354007
return (FALSE);
//
// Current pos should be the character count field.
//
for (i = 0; i < (int) StrLen(pChMet); i++)
{
if (!IS_NUM(&pChMet[i]))
{
return(FALSE);
}
}
chCnt = (USHORT)atoi(pChMet);
(ULONG_PTR) pChMet += i;
//
// Get EXTEXTMETRIC info if requested.
//
if (pEtmInfo != NULL)
{
if (bIsPiFont)
{
//
// For Pi Fonts, chars are indexed by char code.
//
if ((BYTE) CAP_HEIGHT_CH - (BYTE) ' ' < chCnt)
pCurChar = &(pFontChars[(BYTE) CAP_HEIGHT_CH - (BYTE) ' ']);
else
pCurChar = NULL; // default to 0 CapHeight
}
else
{
if (!csIsCJKFont)
{
pCurChar = (PPSCHARMETRICS) bsearch(CAP_HEIGHT_CHAR,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
else
{
// We need CID of "H" in CJK
if (csIsCJKFont & (CSUP(CS_CHINESEBIG5) | CSUP(CS_GB2312)))
pCJKCapH = "853";
else if (csIsCJKFont & (CSUP(CS_SHIFTJIS) | CSUP(CS_SHIFTJIS83)))
pCJKCapH = "271";
else if (csIsCJKFont & (CSUP(CS_HANGEUL) | CSUP(CS_JOHAB)))
pCJKCapH = "8134";
else
pCJKCapH = CAP_HEIGHT_CHAR;
pCurChar = (PPSCHARMETRICS) bsearch(pCJKCapH,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
}
if (pCurChar != NULL)
{
pEtmInfo->etmCapHeight = (USHORT) pCurChar->rcChBBox.top & 0xffff;
}
else
{
pEtmInfo->etmCapHeight = 0;
}
if (bIsPiFont)
{
//
// For Pi Fonts, chars are indexed by char code.
//
if ((BYTE) X_HEIGHT_CH - (BYTE) ' ' < chCnt)
pCurChar = &(pFontChars[(BYTE) X_HEIGHT_CH - (BYTE) ' ']);
else
pCurChar = NULL; // default to 0
}
else
{
if (!csIsCJKFont)
{
pCurChar = (PPSCHARMETRICS) bsearch(X_HEIGHT_CHAR,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
else
{
// We need CID of "x" in CJK
if (csIsCJKFont & (CSUP(CS_CHINESEBIG5) | CSUP(CS_GB2312)))
pCJKx = "901";
else if (csIsCJKFont & (CSUP(CS_SHIFTJIS) | CSUP(CS_SHIFTJIS83)))
pCJKx = "319";
else if (csIsCJKFont & (CSUP(CS_HANGEUL) | CSUP(CS_JOHAB)))
pCJKx = "8182";
else
pCJKx = X_HEIGHT_CHAR;
pCurChar = (PPSCHARMETRICS) bsearch(pCJKx,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
}
if (pCurChar != NULL)
{
pEtmInfo->etmXHeight = (USHORT) pCurChar->rcChBBox.top & 0xffff;
}
else
{
pEtmInfo->etmXHeight = 0;
}
if (bIsPiFont)
{
//
// For Pi Fonts, chars are indexed by char code.
//
if ((BYTE) LWR_ASCENT_CH - (BYTE) ' ' < chCnt)
pCurChar = &(pFontChars[(BYTE) LWR_ASCENT_CH - (BYTE) ' ']);
else
pCurChar = NULL; // default to 0
}
else
{
pCurChar = (PPSCHARMETRICS) bsearch(LWR_ASCENT_CHAR,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
if (pCurChar != NULL)
{
pEtmInfo->etmLowerCaseAscent = (USHORT) pCurChar->rcChBBox.top & 0xffff;
}
else
{
pEtmInfo->etmLowerCaseAscent = 0;
}
if (bIsPiFont)
{
//
// For Pi Fonts, chars are indexed by char code.
//
if ((BYTE) LWR_DESCENT_CH - (BYTE) ' ' < chCnt)
pCurChar = &(pFontChars[(BYTE) LWR_DESCENT_CH - (BYTE) ' ']);
else
pCurChar = NULL; // default to 0
}
else
{
pCurChar = (PPSCHARMETRICS) bsearch(LWR_DESCENT_CHAR,
pFontChars[0].pPsName,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
strcmp);
}
if (pCurChar != NULL)
{
pEtmInfo->etmLowerCaseDescent = (USHORT) pCurChar->rcChBBox.bottom & 0xffff;
}
else
{
pEtmInfo->etmLowerCaseDescent = 0;
}
}
return TRUE;
}
ULONG
GetAFMKernPairs(
PBYTE pAFM,
FD_KERNINGPAIR *pKernPairs,
PGLYPHSETDATA pGlyphSetData
)
/*++
Routine Description:
Given a memory mapped AFM file ptr and a ptr to a GLYPHSETDATA which
describes the supported charset for the font, fill memory with a list of
FD_KERNINGPAIR structs which provide pair kerning information.
Arguments:
pAFM - Pointer to memory mapped AFM file.
pKernPairs - If NULL, this is a size request and the function returns the
total size in bytes of all FD_KERNINGPAIR structs required for this font.
Otherwise the ptr is assumed to point to a buffer large enough to
hold the number of required FD_KERNINGPAIRs.
pGlyphSetData - Points to a GLYPHSETDATA structure which describes the
Unicode->code point mappings for the charset to be used with this font.
Return Value:
0 => no kerning.
Otherwise returns number of FD_KERNINGPAIR structs required for this font.
--*/
{
PBYTE pKernData;
PBYTE pToken;
PUPSCODEPT pKernStrtChar, pKernEndChar;
PGLYPHRUN pGlyphRuns;
ULONG i, cMaxKernPairs, cKernPairs;
BOOLEAN bFound;
//
// for the time being, no kerning for Pi or CJK fonts.
//
if (IsPiFont(pAFM) || IsCJKFont(pAFM))
{
return(FALSE);
}
//
// Is there kerning info for this font?
//
if ((pKernData = FindAFMToken(pAFM, PS_KERN_DATA_TOK)) == NULL)
{
//
// There is no kerning info for this font.
//
return(FALSE);
}
//
// Get ptr to AFM kerning data.
//
if ((pKernData = FindAFMToken(pAFM, PS_NUM_KERN_PAIRS_TOK)) == NULL)
{
//
// There is no kerning info for this font.
//
return(FALSE);
}
//
// Current pos should be the kern pair count field.
//
for (i = 0; i < (int) StrLen(pKernData); i++)
{
if (!IS_NUM(&pKernData[i]))
{
return(FALSE);
}
}
cMaxKernPairs = atoi(pKernData);
NEXT_LINE(pKernData);
cKernPairs = 0;
pGlyphRuns = (PGLYPHRUN) (MK_PTR(pGlyphSetData, dwRunOffset));
//
// Get the kern pairs from the AFM.
//
do
{
PARSE_TOKEN(pKernData, pToken);
if (!StrCmp(pToken, PS_KERN_PAIR_TOK))
{
//
// Kern pair token found. Get Unicode id for start and end
// chars. Determine if these chars are supported in the
// charset to be used with the current font.
//
if((pKernStrtChar = (PUPSCODEPT) bsearch(pKernData,
PstoUnicode,
(size_t) NUM_PS_CHARS,
sizeof(UPSCODEPT),
CmpUnicodePsNames)) == NULL)
{
//
// No Unicode code pt for this char.
//
break;
}
//
// Determine if the char is present in the Unicode runs for
// this glyphset.
//
bFound = FALSE;
for (i = 0; i < pGlyphSetData->dwRunCount &&
pKernStrtChar->wcUnicodeid >= pGlyphRuns[i].wcLow &&
!bFound;
i++)
{
bFound =
pKernStrtChar->wcUnicodeid <
pGlyphRuns[i].wcLow + pGlyphRuns[i].wGlyphCount;
}
if (!bFound)
{
//
// Char is not supported, so ignore this kern pair.
//
NEXT_LINE(pKernData);
break;
}
//
// Get the 2nd char in the kern pair.
//
PARSE_TOKEN(pKernData, pToken);
//
// Determine if the 2nd char is supported in this charset.
//
if((pKernEndChar = (PUPSCODEPT) bsearch(pKernData,
PstoUnicode,
(size_t) NUM_PS_CHARS,
sizeof(UPSCODEPT),
CmpUnicodePsNames)) == NULL)
{
//
// No Unicode code pt for this char.
//
break;
}
//
// Determine if the char is present in the Unicode runs for
// this glyphset.
//
bFound = FALSE;
for (i = 0; i < pGlyphSetData->dwRunCount &&
pKernEndChar->wcUnicodeid >= pGlyphRuns[i].wcLow &&
!bFound;
i++)
{
bFound =
pKernEndChar->wcUnicodeid <
pGlyphRuns[i].wcLow + pGlyphRuns[i].wGlyphCount;
}
if (!bFound)
{
//
// Char is not supported, so ignore this kern pair.
//
NEXT_LINE(pKernData);
break;
}
//
// Account for another kern pair.
//
if (pKernPairs != NULL)
{
pKernPairs[cKernPairs].wcFirst = pKernStrtChar->wcUnicodeid;
pKernPairs[cKernPairs].wcSecond = pKernEndChar->wcUnicodeid;
PARSE_TOKEN(pKernData, pToken);
pKernPairs[cKernPairs].fwdKern = (FWORD)atoi(pKernData);
}
cKernPairs++;
}
else if (!StrCmp(pToken, PS_EOF_TOK) ||
!StrCmp(pToken, PS_END_KERN_PAIRS_TOK))
{
break;
}
NEXT_TOKEN(pKernData);
} while (cKernPairs < cMaxKernPairs);
if (pKernPairs != NULL)
{
//
// Sort kerning pairs by key = wcSecond << 16 + wcFIrst.
//
qsort(pKernPairs, (size_t) cKernPairs, (size_t) sizeof(FD_KERNINGPAIR),
CmpKernPairs);
//
// Array of kerning pairs is terminated by a FD_KERNINGPAIR with
// all fields set to 0.
//
pKernPairs[cKernPairs].wcFirst = 0;
pKernPairs[cKernPairs].wcSecond = 0;
pKernPairs[cKernPairs].fwdKern = 0;
}
return(cKernPairs);
}
ULONG
BuildPSFamilyTable(
PBYTE pDatFile,
PTBL *pPsFamilyTbl,
ULONG ulFileSize
)
/*++
Routine Description:
Builds a table of PSFAMILYINFO structs from a text file of font info.
The table is sorted in family name sequence. See the file PSFAMILY.DAT
for info on the input file format.
Arguments:
pDatFile - Ptr to memory mapped file image of .DAT file.
pPsFamilyTbl - Ptr to memory to contain a ptr to a table of PSFAMILYINFO
structs, which will be sorted in sFamilyName order.
ulFileSize - size in bytes of memory mapped file stream.
Return Value:
Number of entries in newly created table pointed to by *pPsFamilyTbl.
0 => error
--*/
{
USHORT cFams;
ULONG i, j;
CHAR pFamilyType[CHAR_NAME_LEN];
CHAR pPitchType[CHAR_NAME_LEN];
CHAR *pStartLine;
ULONG cNameSize, cEngFamilyNameSize;
ULONG cFamilyTypeSize, cFamilyNameSize;
ULONG cDelimiters;
PPSFAMILYINFO pPsFontFamMap;
//
// Make a pass through the file to determine number of families.
//
i = 0;
cFams = 0;
do
{
cDelimiters = 0;
//
// Skip leading whitespace.
//
while (IS_WHTSPACE(&pDatFile[i]) && i < ulFileSize)
i++;
//
// We're at start of new line. If this is a comment, skip
// this line.
//
if (IS_COMMENT(&pDatFile[i]))
while (i <= ulFileSize && !EOL(&pDatFile[i]))
i++;
while (!EOL(&pDatFile[i]) && i < ulFileSize)
{
//
// Search for lines with 3 ':' delimiters.
//
if (pDatFile[i++] == ':')
{
cDelimiters++;
}
}
if (cDelimiters >= 3)
{
//
// Found another family name mapping.
//
cFams++;
}
} while (i < ulFileSize);
//
// Allocate memory for family info table.
//
if ((*pPsFamilyTbl =
(PTBL) MemAllocZ((size_t) (cFams * sizeof(PSFAMILYINFO)) + sizeof(TBL))) == NULL)
return(FALSE);
(*pPsFamilyTbl)->pTbl = (PVOID) ((ULONG_PTR) *pPsFamilyTbl + sizeof(TBL));
pPsFontFamMap = (PPSFAMILYINFO) ((*pPsFamilyTbl)->pTbl);
//
// Parse file again, building table of PSFAMILYINFOs.
//
i = 0;
cFams = 0;
do
{
//
// Skip leading whitespace.
//
while (IS_WHTSPACE(&pDatFile[i]) && i < ulFileSize)
i++;
//
// We're at start of new line. If this is a comment, skip
// this line.
//
if (IS_COMMENT(&pDatFile[i]))
while (i <= ulFileSize && !EOL(&pDatFile[i]))
i++;
else
pStartLine = &pDatFile[i];
while (!EOL(&pDatFile[i]) && i < ulFileSize)
//
// Search for lines with 3 ':' delimiters.
//
if (pDatFile[i++] == ':')
{
//
// Check for English family name mapping.
//
if (pDatFile[i] == ':')
{
cEngFamilyNameSize = 0;
}
else if ((cEngFamilyNameSize = StrPos(&pDatFile[i], ':')) == -1)
{
//
// No more delimeters on this line, skip it.
//
i += StrLen(&pDatFile[i]);
break;
}
i += cEngFamilyNameSize + 1;
//
// Check for another family name mapping. If present, build
// a FAMILYINFO struct for it.
//
if ((cFamilyNameSize = StrPos(&pDatFile[i], ':')) == -1)
{
//
// No more delimeters on this line, skip it.
//
i += StrLen(&pDatFile[i]);
break;
}
i += cFamilyNameSize + 1;
//
// Check for font family type name
//
if ((cFamilyTypeSize = StrPos(&pDatFile[i], ':')) != -1)
{
i += cFamilyTypeSize + 1;
}
else
{
cFamilyTypeSize = 0;
}
//
// Make sure there are still chars for Win family type name
// or pitch name.
//
if (EOL(&pDatFile[i]) || i >= ulFileSize)
{
//
// Just ran out of file buffer.
//
break;
}
//
// Get the font and family names.
//
cNameSize = StrPos(pStartLine, ':');
memcpy(pPsFontFamMap[cFams].pFontName, pStartLine, cNameSize);
pPsFontFamMap[cFams].pFontName[cNameSize] = '\0';
pStartLine += cNameSize + 1;
if (cEngFamilyNameSize)
{
memcpy(pPsFontFamMap[cFams].pEngFamilyName, pStartLine, cEngFamilyNameSize);
pPsFontFamMap[cFams].pEngFamilyName[cEngFamilyNameSize] = '\0';
}
pStartLine += cEngFamilyNameSize + 1;
memcpy(pPsFontFamMap[cFams].FamilyKey.pName, pStartLine, cFamilyNameSize);
pPsFontFamMap[cFams].FamilyKey.pName[cFamilyNameSize] = '\0';
// if cFamilyTypeSize != 0, means there must be a pitch name
if (cFamilyTypeSize)
{
pStartLine += cFamilyNameSize + 1;
memcpy(pFamilyType, pStartLine, cFamilyTypeSize);
pFamilyType[cFamilyTypeSize] = '\0';
AFM2NTMStrCpy(pPitchType, CCHOF(pPitchType), &pDatFile[i]);
i += strlen(pPitchType);
}
else
{
//
// Get Win family type name (e.g. Swiss, Roman, etc.). Store
// appropriate family type value in the FAMILYINFO.
//
AFM2NTMStrCpy(pFamilyType, CCHOF(pFamilyType), &pDatFile[i]);
i += strlen(pFamilyType);
}
//
// Search for family type in table. Default is FF_DONTCARE.
//
pPsFontFamMap[cFams].FamilyKey.usValue = FF_DONTCARE;
for (j = 0; j < FamilyKeyTbl.usNumEntries; j++)
{
if (!strcmp(pFamilyType, ((PKEY) (FamilyKeyTbl.pTbl))[j].pName))
{
pPsFontFamMap[cFams].FamilyKey.usValue = ((PKEY) (FamilyKeyTbl.pTbl))[j].usValue;
break;
}
}
//
// Search for family type in table. Default is FF_DONTCARE.
//
pPsFontFamMap[cFams].usPitch = DEFAULT_PITCH;
if (cFamilyTypeSize)
{
for (j = 0; j < PitchKeyTbl.usNumEntries; j++)
{
if (!strcmp(pPitchType, ((PKEY) (PitchKeyTbl.pTbl))[j].pName))
{
pPsFontFamMap[cFams].usPitch = ((PKEY) (PitchKeyTbl.pTbl))[j].usValue;
break;
}
}
}
cFams++;
}
} while (i < ulFileSize);
(*pPsFamilyTbl)->usNumEntries = cFams;
//
// Sort FAMILYINFO table in font name order.
//
qsort(&(pPsFontFamMap[0].pFontName), (size_t) cFams,
(size_t) sizeof(PSFAMILYINFO), strcmp);
return(cFams);
}
ULONG
BuildPSCharMetrics(
PBYTE pAFM,
PULONG pUniPs,
PPSCHARMETRICS pFontChars,
PBYTE pCharDefTbl,
ULONG cGlyphSetChars
)
/*++
Routine Description:
Builds a array of bit flags used to determine if a particular char is
defined for a given font.
Arguments:
pAFM - Ptr to memory mapped file image of .AFM file.
pUniPs - Points to a table which maps 0-based Glyph Indices of chars
in the GLYPHRUNS of the GLYPHSETDATA struct for this font to indices
into the UnicodetoPs structure which maps Unicode points to PS char
information. This mapping array is created by the CreateGlyphSet function
defined in this module.
pFontChars - Ptr to memory to contains an array of PSCHARMETRICS structs,
which contains PS char name, and char width info for each char defined
in the font's AFM. The amount of memory required in bytes is
sizeof(PSCHARMETRICS) * num of chars in the font.
pCharDefTbl - Ptr to memory of size ((cGlyphSetChars + 7) /8)) bytes,
will contain bit flags indicating if a char is supported in the given font.
cGlyphSetChars - Number of chars in the GLYPHSET for this font. This
most likely is NOT the same as the number of chars defined in the
font's AFM.
Return Value:
TRUE => success
FALSE => error
--*/
{
ULONG i, j;
PBYTE pChMet, pToken;
USHORT chCnt;
ULONG curCharWidth;
PBYTE pCharNameTok;
BOOLEAN bIsPiFont, bIsCJKFont;
BYTE CharNameBuffer[32];
PBYTE pChName;
//
// Is this is a symbol font, the char "names" will actually be the
// default char codes in the AFM.
//
if (bIsPiFont = IsPiFont(pAFM))
{
pCharNameTok = PS_CH_CODE_TOK;
}
else
{
pCharNameTok = PS_CH_NAME_TOK;
}
bIsCJKFont = (IsCJKFont(pAFM) != 0);
//
// Check validity of output pointers.
//
if (pFontChars == NULL || pCharDefTbl == NULL)
{
return(FALSE);
}
//
// Get ptr to AFM char metrics.
//
pChMet = FindAFMToken(pAFM, PS_CH_METRICS_TOK);
if (pChMet == NULL) // Fixed bug 354007
return (FALSE);
//
// Current pos should be the character count field.
//
for (i = 0; i < (int) StrLen(pChMet); i++)
{
if (!IS_NUM(&pChMet[i]))
{
return(FALSE);
}
}
chCnt = (USHORT)atoi(pChMet);
(ULONG_PTR) pChMet += i;
//
// Make a pass through the AFM Char Metrics, creating an array of
// PSCHARMETRICS structs.
//
i = 0;
do
{
PARSE_TOKEN(pChMet, pToken);
if (!StrCmp(pToken, PS_CH_WIDTH_TOK) ||
!StrCmp(pToken, PS_CH_WIDTH0_TOK))
{
pFontChars[i].chWidth = atoi(pChMet);
}
if (!StrCmp(pToken, pCharNameTok))
{
AFM2NTMStrCpy(pFontChars[i].pPsName, CCHOF(pFontChars[i].pPsName), pChMet);
}
if (!StrCmp(pToken, PS_CH_BBOX_TOK))
{
//
// Save char bounding box.
//
PARSE_RECT(pChMet, pFontChars[i].rcChBBox);
i++;
}
else if (!StrCmp(pToken, PS_EOF_TOK))
{
break;
}
NEXT_TOKEN(pChMet);
} while (i < chCnt);
//
// Sort the list of PSCHARMETRICSs in PS Name sequence. If this is
// a Pi font, chars are already sorted in CC order.
//
if (!bIsPiFont)
{
qsort(pFontChars, (size_t) chCnt, (size_t) sizeof(PSCHARMETRICS),
CmpPsChars);
}
//
// Build array of bit flags which indicate whether each char in the
// GLYPHSETDATA is actually defined in the AFM.
//
for (i = 0; i < ((cGlyphSetChars + 7) / 8); i++)
{
pCharDefTbl[i] = 0;
}
for (i = 0; i < cGlyphSetChars; i++)
{
if (bIsPiFont)
{
//
// Make the first char (0x1f:'.notdef1f') undefined.
//
if (i == 0)
continue;
//
// Char is defined unless there are < 256 chars in the font.
//
if (i < chCnt)
DEFINE_CHAR(i, pCharDefTbl);
else
break;
}
else
{
//
// Setup ptr to "char name" based on whether this is a
// western or CJK font.
//
if (bIsCJKFont)
{
// Make CID 0 undefined glyph.
if (pUniPs[i] == 0)
continue;
_ultoa(pUniPs[i], CharNameBuffer, 10);
pChName = CharNameBuffer;
}
else
{
pChName = UnicodetoPs[pUniPs[i]].pPsName;
}
if (((PPSCHARMETRICS) bsearch(pChName,
pFontChars,
(size_t) chCnt,
sizeof(PSCHARMETRICS),
CmpPsChars)) != NULL)
{
//
// Char is defined in this font.
//
DEFINE_CHAR(i, pCharDefTbl);
}
}
}
return(TRUE);
}
ULONG
cjGetFamilyAliases(
IFIMETRICS *pifi,
PSTR pstr,
UINT cp
)
/*++
Routine Description:
Fill in the family name of the IFIMETRICS structure.
Arguments:
pifi - Ptr to IFIMETRICS. If NULL, return size of family alias strings
only.
pstr - Ptr to null terminated Font Menu Name string.
cp - Codepage value.
Return Value:
?
--*/
{
PSTR *pTable;
PWSTR pwstr;
DWORD cWchars, cw;
ULONG ulLength;
// assume no alias table found.
pTable = (PSTR *)(NULL);
// This is a hardcoded Win31 Hack that we need to be compatible
// with since some apps have hardcoded font names.
if (!(strcmp(pstr, "Times")))
pTable = TimesAlias;
else if (!(strcmp(pstr, "Helvetica")))
pTable = HelveticaAlias;
#if 0
// Disabled due to bug #259664 fix
else if (!(strcmp(pstr, "Courier")))
pTable = CourierAlias;
#endif
else if (!(strcmp(pstr, "Helvetica Narrow")))
pTable = HelveticaNarrowAlias;
else if (!(strcmp(pstr, "Palatino")))
pTable = PalatinoAlias;
else if (!(strcmp(pstr, "Bookman")))
pTable = BookmanAlias;
else if (!(strcmp(pstr, "NewCenturySchlbk")))
pTable = NewCenturySBAlias;
else if (!(strcmp(pstr, "AvantGarde")))
pTable = AvantGardeAlias;
else if (!(strcmp(pstr, "ZapfChancery")))
pTable = ZapfChanceryAlias;
else if (!(strcmp(pstr, "ZapfDingbats")))
pTable = ZapfDingbatsAlias;
//
// If font name does not match any of the family alias names,
// use font name itself as IFIMETRICS family name.
//
if (pTable == NULL)
{
ulLength = strlen(pstr);
cWchars = MultiByteToWideChar(cp, 0, pstr, ulLength, 0, 0);
if (pifi != NULL)
{
pwstr = (PWSTR)MK_PTR(pifi, dpwszFamilyName);
MultiByteToWideChar(cp, 0, pstr, ulLength, pwstr, cWchars);
pwstr[cWchars]= (WCHAR)'\0';
}
return((cWchars + 1) * sizeof (WCHAR));
}
//
// A family alias name match was found.
//
if (pifi != NULL)
{
//
// This call is a request to actually copy the string table.
//
pwstr = (PWSTR)MK_PTR(pifi, dpwszFamilyName);
pifi->flInfo |= FM_INFO_FAMILY_EQUIV;
}
cWchars = 0;
while (*pTable)
{
ulLength = strlen(*pTable);
cw = MultiByteToWideChar(cp, 0, *pTable, ulLength, 0, 0);
if (pifi != NULL)
{
MultiByteToWideChar(cp, 0, *pTable, ulLength, &pwstr[cWchars], cw);
pwstr[cWchars + cw] = (WCHAR)'\0';
}
cWchars += cw + 1;
pTable++;
}
if (pifi != NULL)
{
//
// Add terminator to end of string array.
//
pwstr[cWchars] = (WCHAR)'\0';
}
return((cWchars + 1) * sizeof(WCHAR));
}
PBYTE
FindStringToken(
PBYTE pPSFile,
PBYTE pToken
)
/*++
Routine Description:
Find the first occurrence of pToken occurring in the stream pPSFile.
pToken is terminated by the first occurrence of a space or NULL char.
Arguments:
pPSFile - Ptr to memory mapped file stream to search.
pToken - Ptr to string token to search for.
Return Value:
!=NULL => ptr to first occurence of pToken
==NULL => token not found
--*/
{
while (TRUE)
{
while (IS_WHTSPACE(pPSFile) && !EOL(pPSFile))
{
pPSFile++;
}
if (!StrCmp(pPSFile, DSC_EOF_TOK))
{
break;
}
else if (!StrCmp(pPSFile, pToken))
{
return(pPSFile);
}
else
{
pPSFile += StrLen(pPSFile) + 1;
}
}
return(FALSE);
}
BOOLEAN
AsciiToHex(
PBYTE pStr,
PUSHORT pNum
)
/*++
Routine Description:
Treat the the space or null terminated input string as a series of hex
digits convert it to a USHORT.
Arguments:
pStr - Ptr to string to convert.
pNum - Ptr to variable which returns numeric value.
Return Value:
TRUE => String converted
FALSE => String could not be converted
--*/
{
USHORT usHexNum, ulDigit;
CHAR curChar;
usHexNum = 0;
while (!EOL(pStr) && !IS_HEX_DIGIT(pStr))
{
pStr++;
}
for( ; IS_HEX_DIGIT(pStr); pStr++);
ulDigit = 1;
for (pStr--; IS_HEX_DIGIT(pStr) && !EOL(pStr) && ulDigit; pStr--)
{
if (IS_NUM(pStr))
{
usHexNum += (*pStr - '0') * ulDigit;
}
else
{
curChar = (CHAR)toupper(*pStr);
usHexNum += ((curChar - 'A') + 10) * ulDigit;
}
ulDigit <<= 4;
}
if (usHexNum)
{
*pNum = usHexNum;
return(TRUE);
}
else
{
return(FALSE);
}
}
BOOLEAN
IsPiFont(
PBYTE pAFM
)
/*++
Routine Description:
Determine if the font represented by the passed AFM is a font which
uses the Symbol charset.
Arguments:
pAFM - Ptr to AFM.
Return Value:
TRUE => Font is PI font, uses "Symbol" Glyphset
FALSE => Font not PI font
--*/
{
// This routine used to do a lot more. Should change
// to a macro later.
return((BOOLEAN)isSymbolCharSet);
}
BOOLEAN
IsCJKFixedPitchEncoding(
PGLYPHSETDATA pGlyphSetData
)
/*++
Routine Description:
Determine if the encoding is the one for fixed pitch font.
Arguments:
pGlyphSetData - Ptr to GLYPHSETDATA
Return Value:
TRUE => Fixed pitch font's encoding
FALSE => Proportional font's encoding
--*/
{
BOOLEAN bResult;
char* pszGlyphSetName;
char** pszPropCjkGsName;
bResult = TRUE;
pszGlyphSetName = (char*)MK_PTR(pGlyphSetData, dwGlyphSetNameOffset);
for (pszPropCjkGsName = PropCjkGsNames; *pszPropCjkGsName; pszPropCjkGsName++)
{
if (!strcmp(pszGlyphSetName, *pszPropCjkGsName))
{
bResult = FALSE;
break;
}
}
return bResult;
}
PBYTE
FindUniqueID(
PBYTE pAFM
)
/*++
Routine Description:
Finds UniqueID token in a memory mapped AFM file stream.
UniqueID is assumed on 'Comment UniqueID' line.
Arguments:
pAFM - pointer to memory mapped AFM file.
Return Value:
NULL => error
otherwise => ptr to UniqueID value.
--*/
{
PBYTE pCurToken;
while (TRUE)
{
PARSE_TOKEN(pAFM, pCurToken);
if (!StrCmp(pCurToken, PS_COMMENT_TOK))
{
if (!StrCmp(pAFM, "UniqueID"))
{
pAFM += 8;
while (IS_WHTSPACE(pAFM)) pAFM++;
return pAFM;
}
}
else if (!StrCmp(pCurToken, PS_EOF_TOK))
{
return NULL;
}
NEXT_LINE(pAFM);
}
return NULL;
}