windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/fh.cpp
2020-09-26 16:20:57 +08:00

2996 lines
94 KiB
C++

#include "precomp.h"
//
// FH.CPP
// Font Handling
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_CORE
//
// FH_Init()
//
// This routine allocates a structure for the local font list, then fills
// it in. It returns the number of local fonts in the list, or zero if
// something went wrong
//
UINT FH_Init(void)
{
UINT cFonts = 0;
DebugEntry(FH_Init);
//
// Create the font array and the font index array
//
g_fhFonts = new FHLOCALFONTS;
if (!g_fhFonts)
{
ERROR_OUT(("FH_Init: couldn't allocate g_fhFonts local list"));
DC_QUIT;
}
ZeroMemory(g_fhFonts, sizeof(FHLOCALFONTS));
SET_STAMP(g_fhFonts, FHLOCALFONTS);
//
// Now we consider the fonts individually, and store all acceptable
// ones in the local font list.
//
FHConsiderAllLocalFonts();
cFonts = g_fhFonts->fhNumFonts;
if (cFonts)
{
FHSortAndIndexLocalFonts();
}
else
{
WARNING_OUT(( "No fonts found - this seems unlikely"));
}
DC_EXIT_POINT:
DebugExitDWORD(FH_Init, cFonts);
return(cFonts);
}
//
// FH_Term()
//
void FH_Term(void)
{
DebugEntry(FH_Term);
if (g_fhFonts)
{
delete g_fhFonts;
g_fhFonts = NULL;
}
DebugExitVOID(FH_Term);
}
//
// FH_ReceivedPacket - see fh.h
//
void ASShare::FH_ReceivedPacket
(
ASPerson * pasPerson,
PS20DATAPACKET pPacket
)
{
PFHPACKET pFontsPacket;
UINT iLocal;
UINT iRemote;
LPNETWORKFONT pRemoteFont;
POEREMOTEFONT pLocalFont;
UINT cbSize;
DebugEntry(ASShare::FH_ReceivedPacket);
ValidatePerson(pasPerson);
pFontsPacket = (PFHPACKET)pPacket;
//
// If the number we received isn't the same as before, we need to
// possibly free the previous font block, and then allocate a new one.
//
// Once we're in a share with this person, every new joiner will cause
// existing members to resend their local fonts, usually the same size.
// So we can optimize and not realloc in that case.
//
if (pFontsPacket->cFonts != pasPerson->oecFonts)
{
if (pasPerson->poeFontInfo)
{
delete[] pasPerson->poeFontInfo;
pasPerson->poeFontInfo = NULL;
pasPerson->oecFonts = 0;
}
else
{
ASSERT(!pasPerson->oecFonts);
}
//
// Allocate a new block
//
pasPerson->poeFontInfo = new OEREMOTEFONT[pFontsPacket->cFonts];
if (!pasPerson->poeFontInfo)
{
ERROR_OUT(("Couldn't allocate %d fonts for FH packet from [%d]",
pasPerson->mcsID));
DC_QUIT;
}
ZeroMemory(pasPerson->poeFontInfo, pFontsPacket->cFonts * sizeof(OEREMOTEFONT));
pasPerson->oecFonts = pFontsPacket->cFonts;
}
TRACE_OUT(("Received %d remote fonts in packet from person [%d]",
pasPerson->oecFonts, pasPerson->mcsID));
//
// Consider each remote font. The multibyte fields of the NETWORKFONT
// structure are flipped as they are read; otherwise we would have to
// duplicate the logic about which fields are present in which version.
//
//
// The size of each font is in the packet.
//
cbSize = pFontsPacket->cbFontSize;
pRemoteFont = pFontsPacket->aFonts;
pLocalFont = pasPerson->poeFontInfo;
for (iRemote = 0; iRemote < pasPerson->oecFonts; iRemote++, pLocalFont++)
{
//
// Copy the fields we store directly.
//
pLocalFont->rfFontFlags = pRemoteFont->nfFontFlags;
pLocalFont->rfAveWidth = pRemoteFont->nfAveWidth;
pLocalFont->rfAveHeight = pRemoteFont->nfAveHeight;
pLocalFont->rfAspectX = pRemoteFont->nfAspectX;
pLocalFont->rfAspectY = pRemoteFont->nfAspectY;
//
// And the R2.0 field(s)...
//
if (m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE)
{
pLocalFont->rfCodePage = pRemoteFont->nfCodePage;
}
//
// And the other R2.0 field(s)...
//
if (m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_R20_SIGNATURE)
{
pLocalFont->rfSigFats = pRemoteFont->nfSigFats;
pLocalFont->rfSigThins = pRemoteFont->nfSigThins;
pLocalFont->rfSigSymbol = pRemoteFont->nfSigSymbol;
}
if (m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_EM_HEIGHT)
{
pLocalFont->rfMaxAscent = pRemoteFont->nfMaxAscent;
TRACE_OUT(( "maxAscent %hd", pLocalFont->rfMaxAscent));
}
//
// Set up an initial remote to local handle mapping, by scanning
// for the first local font with the remote font's facename.
//
// We first set a default match value, in case we dont find a true
// match - this value should never be referenced since we never get
// sent fonts that we can't match (because we sent details of our
// fonts to remote systems, and they should be using the same, or a
// compatible, font matching algorithm.
//
// The mapping we obtain here is to the first local font that has
// the remote font's facename, which is probably not the correct
// font (ie there are probably multiple fonts with the same
// facename). This initial mapping will be updated when we do the
// full matching for all remote fonts. (See FHConsiderRemoteFonts
// for details), but is sufficient, as all we will use it for until
// then, is to obtain the facename.
//
// This approach means that we do not have to store the remote
// facename, which is a useful saving on remote font details space.
//
// SFR5279: cannot default to zero because that means we give a
// name to fonts that do not in fact match at all, causing us to
// always waste effort in FHConsiderRemoteFonts and sometimes to
// wrongly match two fonts that do not really match at all.
//
pLocalFont->rfLocalHandle= NO_FONT_MATCH;
for (iLocal = 0; iLocal < g_fhFonts->fhNumFonts; iLocal++)
{
if (!lstrcmp(g_fhFonts->afhFonts[iLocal].Details.nfFaceName,
pRemoteFont->nfFaceName))
{
pLocalFont->rfLocalHandle = (TSHR_UINT16)iLocal;
break;
}
}
//
// Advance to the next remote font.
//
pRemoteFont = (LPNETWORKFONT)((LPBYTE)pRemoteFont + cbSize);
}
DC_EXIT_POINT:
//
// We have a new set of fonts, so determine the common list.
//
FH_DetermineFontSupport();
DebugExitVOID(ASShare::FH_ReceivedPacket);
}
//
// FH_SendLocalFontInfo()
//
void ASShare::FH_SendLocalFontInfo(void)
{
PFHPACKET pFontPacket = NULL;
LPBYTE pNetworkFonts;
UINT pktSize;
UINT iFont;
BOOL fSendFont;
UINT cDummyFonts = 0;
#ifdef _DEBUG
UINT sentSize;
#endif // _DEBUG
DebugEntry(ASShare::FH_SendLocalFontInfo);
ASSERT(!m_fhLocalInfoSent);
//
//
// Look at the combined capability flags to see whether the remote(s)
// can cope with our preferred font structure (R20) or a slightly
// older one (R11) or only the original flavor (pre R11).
//
//
if (!(m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_R20_TEST_FLAGS))
{
WARNING_OUT(("Remotes in share don't support CAPS_FONT_R20"));
m_fhLocalInfoSent = TRUE;
DC_QUIT;
}
pktSize = sizeof(FHPACKET) + (g_fhFonts->fhNumFonts - 1) * sizeof(NETWORKFONT);
pFontPacket = (PFHPACKET)SC_AllocPkt(PROT_STR_MISC, g_s20BroadcastID, pktSize);
if (!pFontPacket)
{
WARNING_OUT(("Failed to alloc FH packet, size %u", pktSize));
DC_QUIT;
}
//
// Packet successfully allocated. Fill in the data and send it.
//
pFontPacket->header.data.dataType = DT_FH;
pFontPacket->cbFontSize = sizeof(NETWORKFONT);
//
// Copy the fonts we want to send into the network packet.
//
pNetworkFonts = (LPBYTE)pFontPacket->aFonts;
cDummyFonts = 0;
for (iFont = 0 ; iFont < g_fhFonts->fhNumFonts ; iFont++)
{
//
// Assume we will send this font.
//
fSendFont = TRUE;
//
// Check whether font is ANSI charset or font CodePage capability
// is supported. If neither, skip on to next local font.
//
TRACE_OUT(( "TEST CP set OK: font[%u] CodePage[%hu]", iFont,
g_fhFonts->afhFonts[iFont].Details.nfCodePage));
if ((g_fhFonts->afhFonts[iFont].Details.nfCodePage != ANSI_CHARSET) &&
(!(m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE)) )
{
TRACE_OUT(( "Dont send font[%u] CodePage[%hu]", iFont,
g_fhFonts->afhFonts[iFont].Details.nfCodePage));
fSendFont = FALSE;
}
if (fSendFont)
{
//
// We want to send this entry so copy across as much of the
// stored details as the protocol level requires.
// We then mask the flags and advance to the next location in
// the packet.
//
memcpy(pNetworkFonts,
&g_fhFonts->afhFonts[iFont].Details,
sizeof(NETWORKFONT));
((LPNETWORKFONT)pNetworkFonts)->nfFontFlags &= ~NF_LOCAL;
}
else
{
//
// If we determine that we do not want to send the current
// font then we fill the corresponding entry in the network
// packet with zeros. This ensures that an index into our
// local font table is also an index into the network packet,
// so no conversion is required. Setting the whole entry to
// zero gives the font a NULL facename and zero size, which
// will never match a real font.
//
ZeroMemory(pNetworkFonts, sizeof(NETWORKFONT));
cDummyFonts++;
}
//
// Move to the next entry in the font packet.
//
pNetworkFonts += sizeof(NETWORKFONT);
}
//
// Note that at the end of this loop, we may not have sent any fonts,
// eg where the remote system does not support the font CodePage
// capability and we do not have any true ANSI fonts. We send the
// packet anyway, so the remote system sees that we have no fonts to
// match.
//
//
// Only now do we know the number of fonts we actually put in the
// packet.
//
pFontPacket->cFonts = (TSHR_UINT16)g_fhFonts->fhNumFonts;
//
// Send the fonts packet on the MISC stream. It has no dependency on
// any updates and we want it to get across quickly.
//
if (m_scfViewSelf)
FH_ReceivedPacket(m_pasLocal, &(pFontPacket->header));
#ifdef _DEBUG
sentSize =
#endif // _DEBUG
DCS_CompressAndSendPacket(PROT_STR_MISC, g_s20BroadcastID,
&(pFontPacket->header), pktSize);
TRACE_OUT(("FH packet size: %08d, sent %08d", pktSize, sentSize));
TRACE_OUT(( "Sent font packet with %u fonts (inc %u dummies)",
g_fhFonts->fhNumFonts,
cDummyFonts));
//
// Set the flag that indicates that we have successfully sent the
// font info.
//
m_fhLocalInfoSent = TRUE;
//
// The font info has been sent, so this may mean we can enable text
// orders.
//
FHMaybeEnableText();
DC_EXIT_POINT:
DebugExitVOID(ASShare::FH_SendLocalFontInfo);
}
//
// FUNCTION: FH_GetMaxHeightFromLocalHandle
//
// DESCRIPTION:
//
// Given an FH font handle (ie a handle originating from the locally
// supported font structure which was sent to the remote machine at the
// start of the call) this function returns the MaxBaseLineExt value stored
// with the LOCALFONT details
//
// PARAMETERS:
//
// fontHandle - font handle being queried.
//
// RETURNS: max font height
//
//
UINT FH_GetMaxHeightFromLocalHandle(UINT fontHandle)
{
UINT rc;
DebugEntry(FH_GetMaxHeightFromLocalHandle);
//
// First check that the font handle is valid.
//
if (fontHandle >= g_fhFonts->fhNumFonts)
{
ERROR_OUT(( "Invalid font handle %u", fontHandle));
fontHandle = 0;
}
//
// Return the max font height
//
rc = g_fhFonts->afhFonts[fontHandle].lMaxBaselineExt;
DebugExitDWORD(FH_GetMaxHeightFromLocalHandle, rc);
return(rc);
}
//
// FUNCTION: FH_GetFontFlagsFromLocalHandle
//
// DESCRIPTION:
//
// Given an FH font handle (ie a handle originating from the locally
// supported font structure which was sent to the remote machine at the
// start of the call) this function returns the FontFlags value stored with
// the LOCALFONT details
//
// PARAMETERS:
//
// fontHandle - font handle being queried.
//
// RETURNS: font flags
//
//
UINT FH_GetFontFlagsFromLocalHandle(UINT fontHandle)
{
UINT rc;
DebugEntry(FH_GetFontFlagsFromLocalHandle);
//
// First check that the font handle is valid.
//
if (fontHandle >= g_fhFonts->fhNumFonts)
{
ERROR_OUT(( "Invalid font handle %u", fontHandle));
fontHandle = 0;
}
//
// Return the font flags.
//
rc = g_fhFonts->afhFonts[fontHandle].Details.nfFontFlags;
DebugExitDWORD(FH_GetFontFlagsFromLocalHandle, rc);
return(rc);
}
//
// FUNCTION: FH_GetCodePageFromLocalHandle
//
// DESCRIPTION:
//
// Given an FH font handle (ie a handle originating from the locally
// supported font structure which was sent to the remote machine at the
// start of the call) this function returns the CodePage value stored with
// the LOCALFONT details
//
// PARAMETERS:
//
// fontHandle - font handle being queried.
//
// RETURNS: char set
//
//
UINT FH_GetCodePageFromLocalHandle(UINT fontHandle)
{
UINT rc = 0;
DebugEntry(FH_GetCodePageFromLocalHandle);
//
// First check that the font handle is valid.
//
if (fontHandle >= g_fhFonts->fhNumFonts)
{
ERROR_OUT(( "Invalid font handle %u", fontHandle));
fontHandle = 0;
}
//
// Return the char set.
//
rc = g_fhFonts->afhFonts[fontHandle].Details.nfCodePage;
DebugExitDWORD(FH_GetCodePageFromLocalHandle, rc);
return(rc);
}
//
// FH_ConvertAnyFontIDToLocal()
//
// DESCRIPTION:
// Converts any font name ID fields in the passed order from remote font
// face name IDs to local font facename IDs.
//
void ASShare::FH_ConvertAnyFontIDToLocal
(
LPCOM_ORDER pOrder,
ASPerson * pasPerson
)
{
LPCOMMON_TEXTORDER pCommon = NULL;
DebugEntry(ASShare::FH_ConvertAnyFontIDToLocal);
ValidatePerson(pasPerson);
//
// Get a pointer to the structure which is common to both TextOut and
// ExtTextOut
//
if (TEXTFIELD(pOrder)->type == LOWORD(ORD_TEXTOUT))
{
pCommon = &TEXTFIELD(pOrder)->common;
}
else if (EXTTEXTFIELD(pOrder)->type == LOWORD(ORD_EXTTEXTOUT))
{
pCommon = &EXTTEXTFIELD(pOrder)->common;
}
else
{
ERROR_OUT(( "Order type not TextOut or ExtTextOut."));
DC_QUIT;
}
TRACE_OUT(( "fonthandle IN %lu", pCommon->FontIndex));
pCommon->FontIndex = FHGetLocalFontHandle(pCommon->FontIndex, pasPerson);
TRACE_OUT(( "fonthandle OUT %lu", pCommon->FontIndex));
DC_EXIT_POINT:
DebugExitVOID(ASShare::FH_ConvertAnyFontIDToLocal);
}
//
// FH_GetFaceNameFromLocalHandle - see fh.h
//
LPSTR FH_GetFaceNameFromLocalHandle(UINT fontHandle, LPUINT pFaceNameLength)
{
LPSTR pFontName = NULL;
DebugEntry(FH_GetFaceNameFromLocalHandle);
//
// First check that the font handle is valid.
//
if (fontHandle >= g_fhFonts->fhNumFonts)
{
ERROR_OUT(( "Invalid font handle %u", fontHandle));
fontHandle = 0;
}
//
// Now get the facename
//
*pFaceNameLength = lstrlen(g_fhFonts->afhFonts[fontHandle].RealName);
pFontName = g_fhFonts->afhFonts[fontHandle].RealName;
DebugExitVOID(FH_GetFaceNameFromLocalHandle);
return(pFontName);
}
//
// FH_DetermineFontSupport()
//
void ASShare::FH_DetermineFontSupport(void)
{
UINT cCommonFonts;
UINT iLocal;
ASPerson * pasPerson;
DebugEntry(ASShare::FH_DetermineFontSupport);
//
// First mark all local fonts as supported.
//
cCommonFonts = g_fhFonts->fhNumFonts;
for (iLocal = 0; iLocal < g_fhFonts->fhNumFonts; iLocal++)
{
g_fhFonts->afhFonts[iLocal].SupportCode = FH_SC_EXACT_MATCH;
}
//
// Work through all remote people (but not us)
//
ValidatePerson(m_pasLocal);
for (pasPerson = m_pasLocal->pasNext;
(cCommonFonts > 0) && (pasPerson != NULL);
pasPerson = pasPerson->pasNext)
{
ValidatePerson(pasPerson);
if (pasPerson->oecFonts)
{
cCommonFonts = FHConsiderRemoteFonts(cCommonFonts, pasPerson);
}
else
{
//
// We do not have valid fonts for this person, so must not
// send any text orders at all.
//
TRACE_OUT(( "Pending FONT INFO from person [%d]", pasPerson->mcsID));
cCommonFonts = 0;
}
}
//
// We have determined the common supported fonts, and may be able to
// enable text orders now.
//
FHMaybeEnableText();
DebugExitVOID(ASShare::FH_DetermineFontSupport);
}
//
// FH_CreateAndSelectFont()
//
BOOL FH_CreateAndSelectFont(HDC surface,
HFONT* pHNewFont,
HFONT* pHOldFont,
LPSTR fontName,
UINT codepage,
UINT fontMaxHeight,
UINT fontHeight,
UINT fontWidth,
UINT fontWeight,
UINT fontFlags)
{
BOOL rc;
BYTE italic;
BYTE underline;
BYTE strikeout;
BYTE pitch;
BYTE charset;
BYTE precis;
DebugEntry(FH_CreateAndSelectFont);
//
// Set the return code to indicate failure (FALSE). We will change this
// later if we successfully create the font.
//
rc = FALSE;
//
// Massage the data passed which describes the font into the correct
// arrangement to pass on a create font call. Then create a font.
//
//
// If a facename passed is the null string then we are supposed to use
// the system font.
//
if (fontName[0] == 0)
{
WARNING_OUT(( "Using system font"));
*pHNewFont = GetStockFont(SYSTEM_FONT);
}
else
{
//
// Determine the italic, underline, strikeout and pitch values from
// the packed flags.
//
italic = (BYTE)(fontFlags & NF_ITALIC);
underline = (BYTE)(fontFlags & NF_UNDERLINE);
strikeout = (BYTE)(fontFlags & NF_STRIKEOUT);
if (fontFlags & NF_FIXED_PITCH)
{
pitch = FF_DONTCARE | FIXED_PITCH;
}
else
{
pitch = FF_DONTCARE | VARIABLE_PITCH;
}
//
// Check whether this is a TrueType font. This is important, as
// the Windows Font mapper is biased towards non-TrueType, and it
// is easy to do the subsequent decoding with a non-TrueType font.
//
// Note that the Windows headers do not define a name for the
// required value (which is 0x04 in the manuals), so we use the
// value used in the TextMetrics (which has the same value).
//
if (fontFlags & NF_TRUE_TYPE)
{
pitch |= TMPF_TRUETYPE;
precis = OUT_TT_ONLY_PRECIS;
}
else
{
precis = OUT_RASTER_PRECIS;
}
//
// The height we are passed is the character height, not the cell
// height. To indicate this to Windows we need to pass it in as a
// negative value.
//
TRACE_OUT(( "CreateFont cx(%u) cy(%u) wt(%u) pitch(%u) name:%s",
fontWidth,
fontHeight,
fontWeight,
pitch,
fontName ));
//
// Use the misleadingly named codepage value to calculate what
// charset to ask Windows for.
//
if (codepage == NF_CP_WIN_ANSI)
{
charset = ANSI_CHARSET;
}
else if (codepage == NF_CP_WIN_OEM)
{
charset = OEM_CHARSET;
}
else if (codepage == NF_CP_WIN_SYMBOL)
{
charset = SYMBOL_CHARSET;
}
else
{
//
// We have to trust our luck to Windows by specifying default
// (meaning don't care).
//
charset = DEFAULT_CHARSET;
}
*pHNewFont = CreateFont(-(int)fontHeight,
fontWidth,
0, // escapement
0, // orientation
fontWeight,
italic,
underline,
strikeout,
charset,
precis,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
pitch,
fontName);
if (*pHNewFont == NULL)
{
WARNING_OUT(( "Failed to create font %s", fontName));
DC_QUIT;
}
}
//
// Now we have created the font we need to select it into the HDC
// which was passed to us.
//
*pHOldFont = SelectFont(surface, *pHNewFont);
if (*pHOldFont == NULL)
{
ERROR_OUT(( "Failed to select font %s", fontName));
DeleteFont(*pHNewFont);
*pHNewFont = NULL;
DC_QUIT;
}
TRACE_OUT(( "Select new font: %p Old font: %", *pHNewFont,
*pHOldFont));
//
// We have successfully created and selected the font.
//
rc = TRUE;
DC_EXIT_POINT:
DebugExitDWORD(FH_CreateAndSelectFont, rc);
return(rc);
}
//
// FHAddFontToLocalTable
//
// Adds the given font into the local font table, along with any renaming
// and approximate matches.
//
//
void FHAddFontToLocalTable( LPSTR faceName,
TSHR_UINT16 fontFlags,
TSHR_UINT16 codePage,
TSHR_UINT16 maxHeight,
TSHR_UINT16 aveHeight,
TSHR_UINT16 aveWidth,
TSHR_UINT16 aspectX,
TSHR_UINT16 aspectY,
TSHR_UINT16 maxAscent)
{
TSHR_INT16 fatSig;
TSHR_INT16 thinSig;
TSHR_INT16 symbolSig;
FHWIDTHTABLE wTable;
TSHR_UINT16 height;
TSHR_UINT16 width;
TSHR_UINT16 weight;
LOCALFONT thisFont;
TSHR_UINT16 fIndex;
//
// SFRFONT: place marker.
// Here would be the best place to adjust codepage; for example suppose
// we find that CodePage 950 (Chinese) is so different on all platforms
// that we just should not send text orders in this codepage, we can
// set codePage=NF_CP_UNKNOWN and it will be discarded.
//
//
// SFRFONT: no point hanging on to details of fonts with unknown
// code pages; we cannot risk matching them.
//
if (codePage == NF_CP_UNKNOWN)
{
TRACE_OUT(( "unknown CP: discard"));
DC_QUIT;
}
//
// Check we still have room for more fonts.
//
if (g_fhFonts->fhNumFonts >= FH_MAX_FONTS)
{
//
// We are already at our maximum number of fonts.
//
DC_QUIT;
}
//
// Zero the fields where we store facenames to allow bytewise matches.
//
ZeroMemory(thisFont.Details.nfFaceName, FH_FACESIZE);
ZeroMemory(thisFont.RealName, FH_FACESIZE);
//
// Store the easy bits!
//
thisFont.Details.nfFontFlags = fontFlags;
thisFont.Details.nfAveWidth = aveWidth;
thisFont.Details.nfAveHeight = aveHeight;
thisFont.Details.nfAspectX = aspectX;
thisFont.Details.nfAspectY = aspectY;
thisFont.Details.nfCodePage = codePage;
thisFont.lMaxBaselineExt = maxHeight;
//
// Store the real name, for use when we want to create an instance of
// this font.
//
lstrcpy (thisFont.RealName, faceName);
//
// Fill in the wire-format facename.
//
// NB - This has a machine-specific prefix, but for NT the prefix is an
// empty string, so we can just use a strcpy without worrying about the
// issues of adding a prefix.
//
lstrcpy (thisFont.Details.nfFaceName, faceName);
//
// Make sure the signatures are zero for now.
//
thisFont.Details.nfSigFats = 0;
thisFont.Details.nfSigThins = 0;
thisFont.Details.nfSigSymbol = 0;
//
// Now calculate the signature and maxAscent for this font
//
weight = 0; // use default weight
if ((fontFlags & NF_FIXED_SIZE) != 0)
{
//
// Fixed size font: use actual font size for signatures/maxAscent
//
height = thisFont.lMaxBaselineExt;
width = thisFont.Details.nfAveWidth;
thisFont.Details.nfMaxAscent = maxAscent;
}
else
{
//
// Scalable font: use default height/width for signatures/maxAscent
//
height = NF_METRICS_HEIGHT;
width = NF_METRICS_WIDTH;
thisFont.Details.nfMaxAscent = NF_METRICS_HEIGHT;
}
//
// Initialise signature fields to zero (== NF_NO_SIGNATURE). They will
// be overwritten assuming we get a font width table OK.
//
fatSig = 0;
thinSig = 0;
symbolSig = 0;
//
// FHGenerateFontWidthTable also gives us a proper maxAscent value for
// scalable fonts (i.e. based on its own rendition of the font)
//
if (FHGenerateFontWidthTable(&wTable,
&thisFont,
height,
width,
weight,
thisFont.Details.nfFontFlags,
&maxAscent))
{
//
// If this is a scalable font, use the updated maxAscent value that
// FHGenerateFontWidthTable has given us.
//
if (0 == (thisFont.Details.nfFontFlags & NF_FIXED_SIZE))
{
thisFont.Details.nfMaxAscent = maxAscent;
TRACE_OUT(( "Set maxAscent = %d", thisFont.Details.nfMaxAscent));
}
//
// We have all the raw data we need. Calculate the signatures.
//
FHCalculateSignatures(&wTable, &fatSig, &thinSig, &symbolSig);
}
//
// Store the signatures. If the call to FHGenerateFontWidthTable
// fails, the signatures are zero.
//
thisFont.Details.nfSigFats = (BYTE)fatSig;
thisFont.Details.nfSigThins = (BYTE)thinSig;
thisFont.Details.nfSigSymbol = (TSHR_UINT16)symbolSig;
TRACE_OUT(( "Font %hu signatures: (x%.4hx%.2hx%.2hx)",
g_fhFonts->fhNumFonts,
thisFont.Details.nfSigSymbol,
(TSHR_UINT16)(thisFont.Details.nfSigThins),
(TSHR_UINT16)(thisFont.Details.nfSigFats)));
//
// We can now copy the details to the end of the local table.
//
memcpy((void *)&g_fhFonts->afhFonts[g_fhFonts->fhNumFonts],
(void *)&thisFont,
sizeof(LOCALFONT));
//
// Count this font.
//
TRACE_OUT(( "Added record %s",
g_fhFonts->afhFonts[g_fhFonts->fhNumFonts].Details.nfFaceName));
g_fhFonts->fhNumFonts++;
TRACE_OUT(( "g_fhFonts->fhNumFonts now %u", g_fhFonts->fhNumFonts));
DC_EXIT_POINT:
DebugExitVOID(FHAddFontToLocalTable);
}
//
// FHConsiderRemoteFonts
//
// Considers the remote fonts for a single remote person.
//
// Takes the existing number of supported fonts, and returns the number
// that are still common after considering this person.
//
UINT ASShare::FHConsiderRemoteFonts
(
UINT cCanSend,
ASPerson * pasPerson
)
{
UINT iLocal;
UINT iRemote;
UINT cCanReceive=0;
BOOL fCanReceive, fOnlyAscii;
UINT sendSupportCode;
UINT bestSupportSoFar;
DebugEntry(ASShare::FHConsiderRemoteFonts);
ValidatePerson(pasPerson);
//
// Consider each of the still valid local fonts, and see if the remote
// person also supports them.
//
//
// SFR5396: LOOP ONE
//
// Look through all the LOCAL fonts, for ones where we find a match in
// the remote font table. These are fonts we can SEND, and for which
// we must set g_fhFonts->afhFonts[].Supported.
//
// We also set the rfLocalHandle for remote fonts that we can receive
// if we encounter them in this search. We complete the search for
// remote fonts that we can receive in LOOP TWO.
//
// Things we check in this loop: - we may already know there is no
// match for this local name
// so drop out quickly. - otherwise check through EVERY REMOTE
// font looking for the
// best possible match. (If we find an EXACT match, leave the
// inner loop early)
//
//
for (iLocal=0;
(cCanSend > 0) && (iLocal < g_fhFonts->fhNumFonts);
iLocal++)
{
if (g_fhFonts->afhFonts[iLocal].SupportCode != FH_SC_NO_MATCH)
{
//
//
// This font is still valid so check it with all the remote
// fonts for this person.
//
// Things we check in this loop:
// - do the face names match? if no - try next remote font.
// - the pitch: if one is FIXED pitch and one isn't try next
// - the codepages: are the local/remote the same? This
// determines whether we send only ASCII chars.
// - scalability: possible combinations are:
// local fixed/remote scalable (can send/not rcv)
// local scalable/remote scalable (can send and rcv)
// local fixed/remote fixed, sizes match (send & rcv)
// local scalable/remote fixed (cannot send/can rcv)
// for this last case, keep trying the remote fonts.
//
// In "back level" calls to Pre-R11 boxes we stop here but
// force the matches to be approximate. Otherwise check
//
// - aspect ratios (if present): must match or try the
// next remote font.
// - signatures: these are used to finally decide whether
// the fonts are exact matches; good enough to treat as
// approximate matches or such poor matches that the
// font is not supported (cannot be sent).
//
//
//
// Handy SHORTHAND macroes.
//
#define REMOTEFONT pasPerson->poeFontInfo[iRemote]
#define LOCALFT g_fhFonts->afhFonts[iLocal]
#define LOCALDETS LOCALFT.Details
//
// Initially assume that the fonts do not match, but that
// if they do they will match across the whole codepage
// (not just the ascii set).
//
sendSupportCode = FH_SC_NO_MATCH;
bestSupportSoFar = FH_SC_NO_MATCH;
fOnlyAscii = FALSE;
//
//
// Loop through all the remote fonts looking to see which, if
// any, offers the best possible match. Initially,
// sendSupportCode is set to NO_MATCH; as we go through each
// iteration we see if we can improve on the current setting
// of sendSupportCode. We leave the loop as soon as we find
// an EXACT_MATCH ('cos we are not going to do any better than
// that!) or when we run out of remote fonts. The best match
// found so far is kept in bestSupportSoFar.
//
//
for (iRemote = 0;
(iRemote < pasPerson->oecFonts)
&& (sendSupportCode != FH_SC_EXACT_MATCH);
iRemote++)
{
//
// If the remote font is already flagged as having no
// possible match then skip out now. (We set this field
// during the initial processing of the remote font).
//
if (REMOTEFONT.rfLocalHandle==NO_FONT_MATCH)
{
continue; // SFR5279
}
//
// Check the face names...
//
if (lstrcmp(LOCALDETS.nfFaceName,
g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle].Details.nfFaceName))
{
continue;
}
TRACE_OUT(( "Matched Remote Face Name %s",
g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle]
.Details.nfFaceName));
//
// Check the pitch...
//
if( (LOCALDETS.nfFontFlags & NF_FIXED_PITCH)!=
(REMOTEFONT.rfFontFlags & NF_FIXED_PITCH) )
{
TRACE_OUT(( "Different Pitch %x %x",
LOCALDETS.nfFontFlags,
REMOTEFONT.rfFontFlags));
continue;
}
//
//
// If both systems support the font CodePage capability
// (indicated by the remote capability flags - which are
// the union of remote and local by now), check that the
// CodePages and CodePage flags match, and if not,
// restrict ourselves to sending the ASCII subset.
//
// If we support the font CodePage capability but remote
// system does not, then restrict ourselves to sending the
// ASCII subset.
//
// If we do not support the font CodePage capability, then
// we assume that the remote is only sending ANSI CodePage,
// either because it doesn't know about the font CodePage
// capability or because it can see that we don't support
// it. Therefore, we do not need to check the CodePage.
// BUT: restrict ourselves to ASCII only.
//
//
if (!(m_pasLocal->cpcCaps.orders.capsfFonts & CAPS_FONT_CODEPAGE))
{
//
// We do not support codepage checking.
//
TRACE_OUT(( "not checking CP"));
fOnlyAscii = TRUE;
}
if ((m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE)
&& (LOCALDETS.nfCodePage != REMOTEFONT.rfCodePage) )
{
TRACE_OUT(( "Different CPs %hu %hu",
LOCALDETS.nfCodePage,
REMOTEFONT.rfCodePage));
//
//
// Assume that all codepages include ASCII.
//
//
fOnlyAscii = TRUE;
}
//
//
// If we support codepage, but the remote does not then
// the remote will only be sending us ANSI chars. Make sure
// that we send only ASCII subset.
//
//
if ((m_pasLocal->cpcCaps.orders.capsfFonts & CAPS_FONT_CODEPAGE) &&
!(m_oeCombinedOrderCaps.capsfFonts & CAPS_FONT_CODEPAGE))
{
TRACE_OUT(( "Only ASCII"));
fOnlyAscii = TRUE;
}
//
//
// The face names and CodePages match and the fonts are of
// the same type of pitch (ie both are fixed pitch or both
// are variable pitch).
//
//
if ((REMOTEFONT.rfFontFlags & NF_FIXED_SIZE) == 0)
{
//
//
// The remote font is scalable, so we can send any font
// (with this facename) to the remote system, even if
// the local font is fixed sized. Set sendSupportCode
// to FH_SC_EXACT_MATCH now - we will change this to
// FH_SC_APPROX_MATCH later if other fields differ.
//
//
TRACE_OUT((
"Person [%d] Can SEND: remote SCALABLE %s (remote)%u to (local)%u",
pasPerson->mcsID,
LOCALDETS.nfFaceName,
iRemote, iLocal));
sendSupportCode = FH_SC_EXACT_MATCH;
//
//
// SFR5396: it is true that we can SEND this font
// because the remote version of the font is scalable.
// That does not mean that we can necessarily receive
// the font... unless ours is scalable too.
//
//
if ((LOCALDETS.nfFontFlags & NF_FIXED_SIZE)==0)
{
TRACE_OUT((
"Person [%d] Can RECEIVE remote font %u as local %u",
pasPerson->mcsID, iRemote, iLocal));
REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
}
}
else if (LOCALDETS.nfFontFlags & NF_FIXED_SIZE)
{
//
//
// The remote font is fixed size and so is the local
// one, so check if the sizes match exactly.
//
//
if ((LOCALDETS.nfAveWidth == REMOTEFONT.rfAveWidth) &&
(LOCALDETS.nfAveHeight == REMOTEFONT.rfAveHeight))
{
//
//
// Our fixed size local font is the same as the
// fixed size font at the remote. We set
// sendSupportCode to FH_SC_EXACT_MATCH now - we
// will change this to FH_SC_APPROX_MATCH later if
// other fields differ.
//
//
TRACE_OUT(("Person [%d] Matched remote fixed font %s %u to %u",
pasPerson->mcsID,
LOCALDETS.nfFaceName,
iRemote, iLocal));
sendSupportCode = FH_SC_EXACT_MATCH;
REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
}
else
{
TRACE_OUT(( "rejected %s ave width/heights "
"local/remote width %d/%d height %d/%d",
LOCALDETS.nfFaceName,
LOCALDETS.nfAveWidth,
REMOTEFONT.rfAveWidth,
LOCALDETS.nfAveHeight,
REMOTEFONT.rfAveHeight));
}
}
else
{
TRACE_OUT((
"Can only RECEIVE %s %u Remote is fixed, but local %u not",
LOCALDETS.nfFaceName,
iRemote,
iLocal));
//
//
// SFR5396: while we cannot SEND this font because our
// local version is scalable, but the remote's is
// fixed - we can still receive the font in an order.
//
//
REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
}
//
//
// If we have have set the send support code to indicate
// that we have matched we now consider any R1.1 info if it
// is present. As a result of this we may adjust the send
// support code (from indicating an exact match) to
// indicate either an approximate match or no match at all.
//
//
if (!pasPerson->oecFonts)
{
//
//
// The remote system did not send us any R11 font
// info. In this case we assume all font matches are
// approximate and restrict ourselves to the ascii
// subset.
//
//
if (sendSupportCode != FH_SC_NO_MATCH)
{
TRACE_OUT(( "No R11 so approx match only"));
sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
}
}
else if (sendSupportCode != FH_SC_NO_MATCH)
{
//
//
// The remote system did send us R11 font info and
// the font is flagged as matching.
//
//
if ((m_oeCombinedOrderCaps.capsfFonts
& CAPS_FONT_R20_SIGNATURE)!=0)
{
//
//
// Check the signatures.
//
//
TRACE_OUT((
"Person [%d] local %d (remote %d) signatures (x%.4hx%.2hx%.2hx v x%.4hx%.2hx%.2hx)",
pasPerson->mcsID,
iLocal,
iRemote,
LOCALDETS.nfSigSymbol,
(TSHR_UINT16)(LOCALDETS.nfSigThins),
(TSHR_UINT16)(LOCALDETS.nfSigFats),
REMOTEFONT.rfSigSymbol,
(TSHR_UINT16)(REMOTEFONT.rfSigThins),
(TSHR_UINT16)(REMOTEFONT.rfSigFats)));
if ((LOCALDETS.nfSigFats != REMOTEFONT.rfSigFats) ||
(LOCALDETS.nfSigThins != REMOTEFONT.rfSigThins) ||
(LOCALDETS.nfSigSymbol != REMOTEFONT.rfSigSymbol) ||
(REMOTEFONT.rfSigSymbol == NF_NO_SIGNATURE))
{
//
// Decide what to do from the signatures.
//
if (REMOTEFONT.rfSigSymbol == NF_NO_SIGNATURE)
{
TRACE_OUT(("NO match: remote no signature"));
sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
}
else if ((LOCALDETS.nfSigFats == REMOTEFONT.rfSigFats)
&& (LOCALDETS.nfSigThins == REMOTEFONT.rfSigThins))
{
TRACE_OUT(( "our ASCII sigs match"));
sendSupportCode = FH_SC_EXACT_ASCII_MATCH;
}
else
{
//
// NOTE:
// We could use the "closeness" of the fat
// and thin signatures to help us decide
// whether to use approximate matching or
// not. But currently we don't.
//
TRACE_OUT(( "Sig mismatch: APPROX_ASC"));
sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
}
}
else
{
//
//
// All signatures match exactly.
// Leave SendSupportCode as FH_SC_EXACT_MATCH
//
//
TRACE_OUT(("EXACT MATCH: Signatures match exactly"));
}
}
else
{
//
// Not using signatures. Do we care?
//
sendSupportCode = FH_SC_APPROX_MATCH;
TRACE_OUT(( "APPROX MATCH: no sigs"));
}
//
//
// Check the aspect ratio - but only if we do not
// already know that this font does not match.
//
//
if ( (sendSupportCode!=FH_SC_NO_MATCH) &&
( (!(m_oeCombinedOrderCaps.capsfFonts
& CAPS_FONT_ASPECT))
|| (LOCALDETS.nfAspectX != REMOTEFONT.rfAspectX)
|| (LOCALDETS.nfAspectY != REMOTEFONT.rfAspectY) ))
{
//
//
// Either no aspect ratio was supplied or the
// aspect ratio differed.
//
//
if (sendSupportCode == FH_SC_EXACT_MATCH)
{
//
// Force delta-X text orders for mismatched
// aspect ratio. Note we tested above to
// see whether supportCode == EXACT because if
// we have already "downgraded" support then
// we do not need to change it here
//
sendSupportCode = FH_SC_APPROX_MATCH;
TRACE_OUT(( "AR mismatch: APPROX_MATCH"));
}
else if (sendSupportCode == FH_SC_EXACT_ASCII_MATCH)
{
//
// Same again but for ASCII only.
//
sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
TRACE_OUT(( "AR mismatch: APPROX_ASCII_MATCH"));
}
}
}
if (sendSupportCode != FH_SC_NO_MATCH)
{
//
//
// Is this a better match than any we have seen
// before?
//
//
switch (sendSupportCode)
{
case FH_SC_EXACT_MATCH:
case FH_SC_APPROX_MATCH:
//
//
// Note that we do not have to worry about
// overwriting a bestSupportSoFar of EXACT
// with APPROX because we leave the loop when
// we get an exact match.
//
//
bestSupportSoFar = sendSupportCode;
break;
case FH_SC_EXACT_ASCII_MATCH:
//
//
// An approximate match over the whole 255
// code points is better than an exact one
// over just the ascii-s. Debatable, but that
// is what I have decided.
//
//
if (bestSupportSoFar != FH_SC_APPROX_MATCH)
{
bestSupportSoFar = FH_SC_EXACT_ASCII_MATCH;
}
break;
case FH_SC_APPROX_ASCII_MATCH:
//
//
// An approximate match over just the ascii-s
// is better than nothing at all!
//
//
if (bestSupportSoFar == FH_SC_NO_MATCH)
{
bestSupportSoFar = FH_SC_APPROX_ASCII_MATCH;
}
break;
default:
ERROR_OUT(("invalid support code"));
break;
}
}
}
sendSupportCode = bestSupportSoFar;
//
// If we matched the remote font, we have already updated
// its local handle to
// the matched local font. While the local handle was already
// set up, it was only set up to the first local font with the
// same facename, rather than the correct font.
//
// If we did not match the remote font, mark it as not
// supported, and decrement the common font count.
//
if (sendSupportCode != FH_SC_NO_MATCH)
{
TRACE_OUT(( "Local font %d/%s can be SENT (code=%u)",
iLocal,
LOCALDETS.nfFaceName,
sendSupportCode));
if (fOnlyAscii)
{
if (sendSupportCode == FH_SC_EXACT_MATCH)
{
sendSupportCode = FH_SC_EXACT_ASCII_MATCH;
TRACE_OUT(( "Adjust %d/%s to EXACT_ASC (code=%u)",
iLocal,
LOCALDETS.nfFaceName,
sendSupportCode));
}
else
{
TRACE_OUT(( "Adjust %d/%s to APPROX_ASC (code=%u)",
iLocal,
LOCALDETS.nfFaceName,
sendSupportCode));
sendSupportCode = FH_SC_APPROX_ASCII_MATCH;
}
}
}
else
{
TRACE_OUT(( "Local font %d/%s cannot be SENT",
iLocal,LOCALDETS.nfFaceName));
cCanSend--;
}
LOCALFT.SupportCode &= sendSupportCode;
}
else
{
TRACE_OUT(( "Cannot SEND %d/%s",iLocal,LOCALDETS.nfFaceName));
}
}
//
//
// SFR5396: LOOP TWO
//
// Loop through all the remote fonts, looking for ones where we have
// a locally matching font. These are fonts that we can RECEIVE in
// orders, and for which we need to map the remote font handle to the
// local font handle. This means setting REMOTEFONT.rfLocalHandle.
//
// By the time we reach here, REMOTEFONT.rfLocalHandle is already set
// to:
// - NO_FONT_MATCH (in FH_ProcessRemoteFonts)
// or the index in the local table of a definite match found in LOOP1
// or the index of the first entry in the local table with the
// same face name as the remote font (set in FH_ProcessRemoteFonts)
//
// so - we can begin our search in the local table from
// REMOTEFONT.rfLocalHandle.
//
//
for (iRemote = 0;
(iRemote < pasPerson->oecFonts);
iRemote++)
{
iLocal = REMOTEFONT.rfLocalHandle;
if (iLocal == NO_FONT_MATCH)
{
//
// We have no fonts whatsoever that match this font name
// Go round again... try the next REMOTE font.
//
continue;
}
TRACE_OUT(( "Can we receive %s?",
g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle].Details.nfFaceName));
for (fCanReceive = FALSE;
(iLocal < g_fhFonts->fhNumFonts) && (!fCanReceive);
iLocal++)
{
//
// Check the face names...
//
if (lstrcmp(LOCALDETS.nfFaceName,
g_fhFonts->afhFonts[REMOTEFONT.rfLocalHandle].Details.nfFaceName))
{
//
// Try the next LOCAL font.
//
continue;
}
//
// Check the pitch...
//
if((LOCALDETS.nfFontFlags & NF_FIXED_PITCH)!=
(REMOTEFONT.rfFontFlags & NF_FIXED_PITCH))
{
//
// Different pitches, try the next local font.
//
TRACE_OUT(( "Pitch mismatch"));
continue;
}
//
//
// The face names match and the fonts are of
// the same type of pitch (ie both are fixed pitch or both
// are variable pitch).
//
//
if ((REMOTEFONT.rfFontFlags & NF_FIXED_SIZE) == 0)
{
if ((LOCALDETS.nfFontFlags & NF_FIXED_SIZE)==0)
{
//
//
// The remote font is scalable. Ours is also
// scalable then we can receive the font.
//
// We do not need to look at any more LOCAL fonts.
//
//
fCanReceive = TRUE;
}
}
else if (LOCALDETS.nfFontFlags & NF_FIXED_SIZE)
{
//
//
// The remote font is fixed size and so is the local
// one, so check if the sizes match exactly.
//
//
if ((LOCALDETS.nfAveWidth == REMOTEFONT.rfAveWidth) &&
(LOCALDETS.nfAveHeight == REMOTEFONT.rfAveHeight))
{
//
//
// Our fixed size local font is the same as the
// fixed size font at the remote.
//
// We do not need to look at any more LOCAL fonts.
//
//
fCanReceive = TRUE;
}
else
{
TRACE_OUT(( "different fixed sizes"));
}
}
else
{
//
//
// The remote is FIXED but the LOCAL is scalable. We
// can receive orders for text of this type (but not send)
//
// We do not need to look at any more LOCAL fonts.
//
//
fCanReceive = TRUE;
}
if (fCanReceive)
{
TRACE_OUT(("Person [%d] Can RECEIVE remote font %s %u as %u",
pasPerson->mcsID,
LOCALDETS.nfFaceName,
iRemote, iLocal));
REMOTEFONT.rfLocalHandle = (TSHR_UINT16)iLocal;
cCanReceive++;
}
}
}
TRACE_OUT(("Person [%d] Can SEND %d fonts", pasPerson->mcsID, cCanSend));
TRACE_OUT(("Person [%d] Can RECEIVE %d fonts", pasPerson->mcsID, cCanReceive));
DebugExitDWORD(ASShare::FHConsiderRemoteFonts, cCanSend);
return(cCanSend);
}
//
// FHMaybeEnableText
//
// Enables or disables sending of text orders
//
void ASShare::FHMaybeEnableText(void)
{
BOOL fEnableText = FALSE;
ASPerson * pasPerson;
DebugEntry(ASShare::FHMaybeEnableText);
//
// To enable sending text orders we must have sent out our own packet
// of fonts, and there must be no outstanding remote packets required.
//
if (m_fhLocalInfoSent)
{
//
// Assume we can enable text orders.
//
fEnableText = TRUE;
//
// The local info was sent, so check remote dudes (not us)
//
ValidatePerson(m_pasLocal);
for (pasPerson = m_pasLocal->pasNext; pasPerson != NULL; pasPerson = pasPerson->pasNext)
{
ValidatePerson(pasPerson);
if (!pasPerson->oecFonts)
{
//
// We have found a font packet that we have not yet
// received, so must disable sending text, and can break
// out of the search.
//
TRACE_OUT(( "No font packet yet from person [%d]", pasPerson->mcsID));
fEnableText = FALSE;
break;
}
}
}
else
{
TRACE_OUT(( "Local font info not yet sent"));
}
OE_EnableText(fEnableText);
if (g_asCanHost)
{
//
// Pass on new font data to the other tasks.
//
if (fEnableText)
{
OE_NEW_FONTS newFontData;
//
// Copy the data from the Share Core.
//
newFontData.fontCaps = m_oeCombinedOrderCaps.capsfFonts;
newFontData.countFonts = (WORD)g_fhFonts->fhNumFonts;
newFontData.fontData = g_fhFonts->afhFonts;
newFontData.fontIndex = g_fhFonts->afhFontIndex;
TRACE_OUT(( "Sending %d Fonts", g_fhFonts->fhNumFonts));
//
// Notify display driver of new fonts
//
OSI_FunctionRequest(OE_ESC_NEW_FONTS,
(LPOSI_ESCAPE_HEADER)&newFontData,
sizeof(newFontData));
}
}
DebugExitVOID(ASShare::FHMaybeEnableText);
}
//
// FHGetLocalFontHandle
//
// Translate a remote font handle/local ID pair to a local font handle.
//
UINT ASShare::FHGetLocalFontHandle
(
UINT remotefont,
ASPerson * pasPerson
)
{
DebugEntry(ASShare::FHGetLocalFontHandle);
ValidatePerson(pasPerson);
if (!pasPerson->oecFonts)
{
WARNING_OUT(("Order packet from [%d] but no fonts", pasPerson->mcsID));
}
if (remotefont == DUMMY_FONT_ID)
{
//
// The dummy font ID has been supplied for the remote font Id.
// Substitute the first valid local font Id.
//
for (remotefont = 0;
remotefont < pasPerson->oecFonts;
remotefont++)
{
if (pasPerson->poeFontInfo[remotefont].rfLocalHandle !=
NO_FONT_MATCH)
{
break;
}
}
}
if (remotefont >= pasPerson->oecFonts)
{
//
// The remote font is invalid.
// There is no error value, we simply return the valid but
// incorrect value 0.
//
TRACE_OUT(("Person [%d] Invalid font handle %u",
pasPerson->mcsID, remotefont));
return(0);
}
DebugExitVOID(ASShare::FHGetLocalFontHandle);
return(pasPerson->poeFontInfo[remotefont].rfLocalHandle);
}
//
//
// FUNCTION: FHCalculateSignatures
//
// DESCRIPTION:
//
// Given a width table, calculates the three font signatures that are
// included in the R2.0 NETWORKFONT structure.
//
// PARAMETERS:
//
// pTable - pointer to width table
// pSigFats, pSigThins, pSigSymbol - return the three signatures
//
// RETURNS:
//
// None
//
//
void FHCalculateSignatures(PFHWIDTHTABLE pTable,
LPTSHR_INT16 pSigFats,
LPTSHR_INT16 pSigThins,
LPTSHR_INT16 pSigSymbol)
{
UINT charI = 0;
UINT fatSig = 0;
UINT thinSig = 0;
UINT symbolSig = 0;
DebugEntry(FHCalculateSignatures);
ASSERT((pTable != NULL));
ASSERT((pSigFats != NULL));
ASSERT((pSigThins != NULL));
ASSERT((pSigSymbol != NULL));
//
// nfSigFats the sum of the widths (in pels) of the chars
// 0-9,@-Z,$,%,&. divided by two: the fat chars
// nfSigThins the sum of the widths (in pels) of the chars
// 0x20->0x7F EXCLUDING those summed in nfSigFats.
// Again - divided by two. The thin chars.
// nfSigSymbol The sum of the widths (in pels) of the chars
// x80->xFF.
//
//
// Loop for 0-9, some punctuation, A-Z. Then add $,% and &. i.e. mainly
// fat characters.
//
for (charI= NF_ASCII_ZERO; charI<NF_ASCII_Z ; charI++ )
{
fatSig += pTable->charWidths[charI];
}
fatSig += pTable->charWidths[NF_ASCII_DOLLAR] +
pTable->charWidths[NF_ASCII_PERCENT] +
pTable->charWidths[NF_ASCII_AMPERSAND];
//
// thin sig covers the rest of the "ascii" characters (x20->7F) not
// already included in fatSig.
//
for (charI= NF_ASCII_FIRST; charI<NF_ASCII_LAST ; charI++ )
{
thinSig += pTable->charWidths[charI];
}
thinSig -= fatSig;
//
// symbolSig covers the "non-ascii" characters (x0->1F, 80->FF)
//
for (charI= 0x00; charI<(NF_ASCII_FIRST-1) ; charI++ )
{
symbolSig += pTable->charWidths[charI];
}
for (charI= NF_ASCII_LAST+1; charI<0xFF ; charI++ )
{
symbolSig += pTable->charWidths[charI];
}
TRACE_OUT(( "Signatures: symbol %#lx thin %#lx fat %#lx",
symbolSig, thinSig, fatSig));
//
// Halve the fat and thin sigs so that they fit into one byte each.
//
fatSig /= 2;
thinSig /= 2;
if ( (((TSHR_UINT16)symbolSig)==0)
&& (((BYTE)fatSig)==0) && (((BYTE)thinSig)==0))
{
//
// Worry about the faint possibility that all three sums could add
// up to a value of zero when truncated.
//
symbolSig=1;
}
//
// Fill in return pointers.
//
*pSigFats = (TSHR_INT16)fatSig;
*pSigThins = (TSHR_INT16)thinSig;
*pSigSymbol = (TSHR_INT16)symbolSig;
DebugExitVOID(FHCalculateSignatures);
}
//
// FHEachFontFamily
//
// This callback is called for each font family. We use it to build up a
// list of all the family names.
//
//
// Although wingdi.h defines the first two parameters for an ENUMFONTPROC
// as LOGFONT and TEXTMETRIC (thereby disagreeing with MSDN), tests show
// that the structures are actually as defined in MSDN (i.e. we get valid
// information when accessing the extended fields)
//
int CALLBACK FHEachFontFamily
(
const ENUMLOGFONT FAR * enumlogFont,
const NEWTEXTMETRIC FAR * TextMetric,
int FontType,
LPARAM lParam
)
{
LPFHFAMILIES lpFamilies = (LPFHFAMILIES)lParam;
DebugEntry(FHEachFontFamily);
ASSERT(!IsBadWritePtr(lpFamilies, sizeof(*lpFamilies)));
if (lpFamilies->fhcFamilies == FH_MAX_FONTS)
{
//
// We cannot support any more font families so stop enumerating.
//
WARNING_OUT(( "Can only handle %u families", FH_MAX_FONTS));
return(FALSE); // Stop the enumeration
}
TRACE_OUT(("FHEachFontFamily: %s", enumlogFont->elfLogFont.lfFaceName));
ASSERT(lstrlen(enumlogFont->elfLogFont.lfFaceName) < FH_FACESIZE);
lstrcpy(lpFamilies->afhFamilies[lpFamilies->fhcFamilies].szFontName,
enumlogFont->elfLogFont.lfFaceName);
lpFamilies->fhcFamilies++;
DebugExitBOOL(FHEachFontFamily, TRUE);
return(TRUE); // Continue enumerating
}
//
// FHEachFont
//
// This callback is called for each font. It gathers and stores the font
// details.
//
//
//
// Although wingdi.h defines the first two parameters for an ENUMFONTPROC
// as LOGFONT and TEXTMETRIC (thereby disagreeing with MSDN), tests show
// that the structures are actually as defined in MSDN (i.e. we get valid
// information when accessing the extended fields)
//
int CALLBACK FHEachFont(const ENUMLOGFONT FAR * enumlogFont,
const NEWTEXTMETRIC FAR * TextMetric,
int FontType,
LPARAM lParam)
{
HDC hdc = (HDC)lParam;
TSHR_UINT16 fontflags = 0;
TSHR_UINT16 CodePage = 0;
HFONT hfont;
HFONT holdfont = NULL;
TEXTMETRIC tm;
BOOL fAcceptFont;
int rc;
DebugEntry(FHEachFont);
TRACE_OUT(( "Family name: %s", enumlogFont->elfLogFont.lfFaceName));
TRACE_OUT(( "Full name: %s", enumlogFont->elfFullName));
if (g_fhFonts->fhNumFonts >= FH_MAX_FONTS)
{
//
// We cannot support any more fonts so stop enumerating.
//
WARNING_OUT(( "Can only handle %u fonts", FH_MAX_FONTS));
rc = 0;
DC_QUIT; // Stop the enumeration
}
//
// We want to continue...
//
rc = 1;
//
// Don't bother with this if it's a bold/italic variant.
//
// NOTE:
// The elfFullName field is only valid for TrueType fonts on Win95. For
// non TrueType fonts, assume that the full name and face name are the
// same.
//
if (!g_asWin95 || (FontType & TRUETYPE_FONTTYPE))
{
if (lstrcmp(enumlogFont->elfLogFont.lfFaceName, (LPCSTR)enumlogFont->elfFullName))
{
TRACE_OUT(( "Discarding variant: %s", enumlogFont->elfFullName));
DC_QUIT; // Jump out, but don't stop enumerating!
}
}
//
// We now accumulate information on all local fonts in all CodePages.
// This relies on the subsequent sending of local fonts and matching of
// remote fonts taking into account the CodePage capabilities of the
// systems.
//
//
// On this pass we copy the details into our structure.
//
if (FontType & TRUETYPE_FONTTYPE)
{
//
// This is a truetype font, which we simply accept without double
// checking its metrics. (Metric double checking to exclude
// duplicates is of most relevance to fixed size fonts, which are
// explicitly optimised for one screen size)
//
fAcceptFont = TRUE;
//
// Indicate TrueType (this will go in the NETWORKFONT structure
// (i.e. over the wire)
//
fontflags |= NF_TRUE_TYPE;
//
// Signal that we did not call CreateFont for this font.
//
hfont = NULL;
}
else
{
//
// We create a font from the logical description, and select it so
// that we can query its metrics.
//
// The point of this is that it allows us to identify fonts where
// the logical font description is not a unique description of this
// font, and hence if we cannot get to this font via a logical font
// description, we cannot get to it at all.
//
// If we cannot get to it, then we cannot claim to support it.
//
// This selection operation is SLOW - of the order of a couple of
// seconds in some extreme cases (for example where the font is
// stored on a network drive, and pageing has to take place) and
// when you can have hundreds of fonts this can add up to a
// considerable time.
//
// Hence we only do the selection for non truetype fonts because
// these are the fonts where it is easy to get multiple fonts of
// the same logical description, though designed for different
// display drivers.
//
//
// Create a font from the logical font, so we can see what font
// Windows actually choses.
//
hfont = CreateFontIndirect(&enumlogFont->elfLogFont);
holdfont = SelectFont(hdc, hfont);
//
// Find the metrics of the font that Windows has actually selected.
//
GetTextMetrics(hdc, &tm);
//
// Double check the aspect ratios - enumerate returns all fonts,
// but it is possible to have fonts that are never matched by
// Windows due to duplications.
//
fAcceptFont = ((tm.tmDigitizedAspectX == TextMetric->tmDigitizedAspectX)
&& (tm.tmDigitizedAspectY == TextMetric->tmDigitizedAspectY));
}
//
// Trace out the full text metrics for debugging.
//
if (fAcceptFont)
{
//
// This font is accepted.
//
//
// Determine the font flags settings.
//
if ((TextMetric->tmPitchAndFamily & TMPF_FIXED_PITCH) == 0)
{
//
// Setting the TMPF_FIXED_PITCH bit in the text metrics is used
// to indicate that the font is NOT fixed pitch. What a
// wonderfully named bit (see Microsoft CD for explanation).
//
fontflags |= NF_FIXED_PITCH;
}
if ((FontType & RASTER_FONTTYPE) ||
(FontType & TRUETYPE_FONTTYPE) == 0)
{
//
// This is a raster font, but not a truetype font so it must be
// of fixed size.
//
fontflags |= NF_FIXED_SIZE;
}
//
// Get the font CodePage. SFRFONT: must map from CharSet to
// Codepage. For now we only support ANSI and OEM charsets. This
// will need to change to support e.g BiDi/Arabic
//
CodePage = TextMetric->tmCharSet;
if (CodePage == ANSI_CHARSET)
{
TRACE_OUT(( "ANSI codepage"));
CodePage = NF_CP_WIN_ANSI;
}
else if (CodePage == OEM_CHARSET)
{
TRACE_OUT(( "OEM codepage"));
CodePage = NF_CP_WIN_OEM;
}
else if (CodePage == SYMBOL_CHARSET)
{
TRACE_OUT(("Symbol codepage"));
CodePage = NF_CP_WIN_SYMBOL;
}
else
{
TRACE_OUT(( "Charset %hu, unknown codepage", CodePage));
CodePage = NF_CP_UNKNOWN;
}
//
//
// SFRFONT: We have replaced the "old" checksum which was based on
// the actual bits making up the font to one based on the widths of
// characters in the font. The intention is that we use this to
// ensure that the actual characters in the local font and in the
// remote font which matches it are all the same width as each
// other.
//
// We calculate this sum for all fonts (not just non-truetype as
// before) because in cross platform calls with approximate font
// matching it applies to fonts of all types.
//
//
//
//
// There is considerable confusion caused by the terminology for
// fonts characteristics. The protocol uses two values MAXHEIGHT
// and AVEHEIGHT. In fact neither of these names is accurate
// (MAXHEIGHT is not the maximum height of a char; and AVEHEIGHT is
// not the average height of all chars).
//
// SFRFONT: we have added maxAscent to the protocol. This is the
// height of a capital letter (such as eM!) PLUS any internal
// leading. This value allows remote boxes to find the baseline -
// the point at which the bottommost pel of a letter with no
// descenders (e.g. capital M) is to be drawn. This is needed
// because not all boxes in the call follow the windows convention
// of specifying the start of text as being the top-left corner of
// the first character cell. maxAscent == tmAscent in the
// TextMetric.
//
//
FHAddFontToLocalTable((LPSTR)enumlogFont->elfLogFont.lfFaceName,
(TSHR_UINT16)fontflags,
(TSHR_UINT16)CodePage,
(TSHR_UINT16)TextMetric->tmHeight,
(TSHR_UINT16)(TextMetric->tmHeight -
TextMetric->tmInternalLeading),
(TSHR_UINT16)TextMetric->tmAveCharWidth,
(TSHR_UINT16)TextMetric->tmDigitizedAspectX,
(TSHR_UINT16)TextMetric->tmDigitizedAspectY,
(TSHR_UINT16)TextMetric->tmAscent);
}
else
{
//
// Windows returns a different font when we use this logical font
// description - presumably because of duplicate fonts. We
// therfore must not claim to support this particular font.
//
TRACE_OUT(( "Discarding hidden font %s",
enumlogFont->elfLogFont.lfFaceName));
}
if (hfont)
{
//
// We called CreateFont in processing this font, so now delete it
// to clean up.
//
SelectFont(hdc, holdfont);
//
// We have finished with the font so delete it.
//
DeleteFont(hfont);
}
DC_EXIT_POINT:
DebugExitDWORD(FHEachFont, rc);
return(rc);
}
//
// FHConsiderAllLocalFonts
//
// Considers the details of each of the fonts on the local system, and if
// acceptable adds them to the local font list.
//
//
void FHConsiderAllLocalFonts(void)
{
HDC hdcDesktop;
UINT i;
LPFONTNAME newFontList;
LPFHFAMILIES lpFamilies = NULL;
DebugEntry(FHConsiderAllLocalFonts);
g_fhFonts->fhNumFonts = 0;
//
// We can't enumerate all the fonts directly; we have to enumerate the
// family names, then the fonts within each family.
//
// This alloc assumes the worst case memory-wise (i.e. each
// family contains a single font) and therefore we will usually
// allocate more memory than we need. We use LocalReAlloc later to fix
// this.
//
lpFamilies = new FHFAMILIES;
if (!lpFamilies)
{
ERROR_OUT(("Failed to alloc FHFAMILIES"));
DC_QUIT;
}
SET_STAMP(lpFamilies, FHFAMILIES);
hdcDesktop = GetWindowDC(HWND_DESKTOP);
//
// Find all the font family names.
//
lpFamilies->fhcFamilies = 0;
EnumFontFamilies(hdcDesktop, NULL,(FONTENUMPROC)FHEachFontFamily,
(LPARAM)lpFamilies);
TRACE_OUT(("Found %d font families ", lpFamilies->fhcFamilies));
//
// Now enumerate each font for each family
//
for (i = 0; i < lpFamilies->fhcFamilies; i++)
{
EnumFontFamilies(hdcDesktop, lpFamilies->afhFamilies[i].szFontName,
(FONTENUMPROC)FHEachFont,
(LPARAM)hdcDesktop);
}
ReleaseDC(HWND_DESKTOP, hdcDesktop);
DC_EXIT_POINT:
//
// Having considered all the fonts, we can now free the list of family
// names.
//
if (lpFamilies)
{
delete lpFamilies;
}
DebugExitVOID(FHConsiderAllLocalFonts);
}
//
// FHGenerateFontWidthTable
//
BOOL FHGenerateFontWidthTable(PFHWIDTHTABLE pTable,
LPLOCALFONT pFontInfo,
UINT fontHeight,
UINT fontWidth,
UINT fontWeight,
UINT fontFlags,
LPTSHR_UINT16 pMaxAscent)
{
HFONT hNewFont;
HFONT hOldFont;
BOOL gdiRC;
UINT i;
HDC cachedDC;
BOOL localRC;
BOOL functionRC;
TEXTMETRIC textmetrics;
int width;
UINT aFontSizes[256];
DebugEntry(FHGenerateFontWidthTable);
//
// Set the return value to FALSE (unsuccessful). We will set it to
// TRUE later if the function succeeds.
//
functionRC = FALSE;
//
// Set the old font handle to NULL. If this is not NULL at the exit
// point of this function then we will select it back into the cachedDC
// device context.
//
hOldFont = NULL;
//
// Set the new font handle to NULL. If this is not NULL at the exit
// point of this function then the new font will be deleted.
//
hNewFont = NULL;
//
// Get a cached DC with which to do the query.
//
cachedDC = GetDC(HWND_DESKTOP);
if (cachedDC == NULL)
{
WARNING_OUT(( "Failed to get DC"));
DC_QUIT;
}
//
// Get all the info we need from the local font table.
//
localRC = FH_CreateAndSelectFont(cachedDC,
&hNewFont,
&hOldFont,
pFontInfo->RealName,
pFontInfo->Details.nfCodePage,
pFontInfo->lMaxBaselineExt,
fontHeight,
fontWidth,
fontWeight,
fontFlags);
if (localRC == FALSE)
{
ERROR_OUT(( "Failed to create/select font %s, %u, %u",
pFontInfo->RealName,
fontHeight,
fontWidth));
DC_QUIT;
}
//
// Determine if the current font is a truetype font.
//
GetTextMetrics(cachedDC, &textmetrics);
if (textmetrics.tmPitchAndFamily & TMPF_TRUETYPE)
{
//
// Truetype fonts are ABC spaced.
//
ABC abc[256];
TRACE_OUT(("TrueType font %s, first char %d last char %d",
pFontInfo->RealName, (UINT)(WORD)textmetrics.tmFirstChar,
(UINT)(WORD)textmetrics.tmLastChar));
//
// Get all widths in one call - faster than getting them separately
//
GetCharABCWidths(cachedDC, 0, 255, abc);
for (i = 0; i < 256; i++)
{
width = abc[i].abcA + abc[i].abcB + abc[i].abcC;
if ((width < 0) || (width > 255))
{
//
// Width is outside the range we can cope with, so quit.
//
TRACE_OUT(( "Width %d is outside range", width));
DC_QUIT;
}
pTable->charWidths[i] = (BYTE)width;
}
}
else
{
TRACE_OUT(( "Non-truetype font"));
//
// Check if the font is fixed or variable pitch - note that a clear
// bit indicates FIXED, not the reverse which you might expect!
//
if ((textmetrics.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0)
{
//
// No need to call GetCharWidth for a fixed width font (and
// more to the point it can return us bad values if we do)
//
for (i = 0; i < 256; i++)
{
aFontSizes[i] = textmetrics.tmAveCharWidth;
}
}
else
{
//
// Query the width of each character in the font.
//
ZeroMemory(aFontSizes, sizeof(aFontSizes));
gdiRC = GetCharWidth(cachedDC,
0,
255,
(LPINT)aFontSizes);
if (gdiRC == FALSE)
{
ERROR_OUT(( "Failed to get char widths for %s, %u, %u",
pFontInfo->RealName,
fontHeight,
fontWidth));
DC_QUIT;
}
}
//
// Now copy the widths into the width table.
// We must adjust the widths to take account of any overhang
// between characters.
//
for (i = 0; i < 256; i++)
{
width = aFontSizes[i] - textmetrics.tmOverhang;
if ((width < 0) || (width > 255))
{
TRACE_OUT(( "Width %d is outside range", width));
DC_QUIT;
}
pTable->charWidths[i] = (BYTE)width;
}
}
//
// The font table has been successfully generated.
//
functionRC = TRUE;
TRACE_OUT(( "Generated font table for: %s", pFontInfo->RealName));
//
// Return the maxAscent value, as we have easy access to it here. This
// saves us having to create the font again later to find it.
//
TRACE_OUT(( "Updating maxAscent %hu -> %hu",
*pMaxAscent,
(TSHR_UINT16)textmetrics.tmAscent));
*pMaxAscent = (TSHR_UINT16)textmetrics.tmAscent;
DC_EXIT_POINT:
if (hOldFont != NULL)
{
SelectFont(cachedDC, hOldFont);
}
if (hNewFont != NULL)
{
DeleteFont(hNewFont);
}
if (cachedDC != NULL)
{
ReleaseDC(HWND_DESKTOP, cachedDC);
}
DebugExitDWORD(FHGenerateFontWidthTable, functionRC);
return(functionRC);
}
//
// Define a macro to simplify the following code. This returns the first
// character in the name of the font at position i in the local table.
//
//
// nfFaceName is an array of CHARs, which are SIGNED. We need to treat them
// as UNSIGNED values, they are indeces from 0 to 255 into the font hash
// table.
//
#define LF_FIRSTCHAR(i) (BYTE)g_fhFonts->afhFonts[i].Details.nfFaceName[0]
//
// Name: FHSortAndIndexLocalFonts
//
// Purpose: Sorts local font table by font name and generates an index for
// quicker searching in the display driver.
//
// Returns: None.
//
// Params: None.
//
//
void FHSortAndIndexLocalFonts(void)
{
TSHR_UINT16 thisIndexEntry;
TSHR_UINT16 fontTablePos;
DebugEntry(FHSortAndIndexLocalFonts);
//
// Check there are actually some fonts to sort/index
//
if (0 == g_fhFonts->fhNumFonts)
{
WARNING_OUT(( "No fonts to sort/index"));
DC_QUIT;
}
//
// Use qsort to do the sort. We sort on the font name, ascending.
// Therefore we must use STRCMP and not lstrcmp. The latter sorts
// by 'word' method, where upper case sorts before lower case. But
// our NT driver has no access to a similar routine. And this code +
// driver code must be in ssync for the driver to successfully search
// the sorted font table.
//
FH_qsort(g_fhFonts->afhFonts, g_fhFonts->fhNumFonts, sizeof(LOCALFONT));
TRACE_OUT(( "Sorted local font list"));
//
// Now generate the index. Each element i in the g_fhFonts->afhFontIndex
// array must indicate the first entry in the local font table
// beginning with character i. If there are no fonts beginning with
// character i, then the element is set to USHRT_MAX (i.e. a large
// value).
//
//
// First clear the index table to unused entries.
//
for (thisIndexEntry = 0;
thisIndexEntry < FH_LOCAL_INDEX_SIZE;
thisIndexEntry++)
{
g_fhFonts->afhFontIndex[thisIndexEntry] = USHRT_MAX;
}
//
// Now fill in the useful information.
//
// This for loop steps through the index array, using the first
// character of the first font in the local table as its start point.
// Since the font table is alphabetically sorted, this will correspond
// to the first index entry that needs filling in.
//
// The terminating condition for this loop may seem a little odd, but
// works because fontTablePos will always reach a value of g_fhFonts->fhNumFonts
// before thisIndexEntry gets to the last index element.
//
fontTablePos = 0;
for (thisIndexEntry = LF_FIRSTCHAR(0);
fontTablePos < g_fhFonts->fhNumFonts;
thisIndexEntry++)
{
//
// Don't do anything until we get to the index element
// corresponding to the first character in the font pointed to by
// fontTablePos. (We'll be there straight away on the first pass)
//
if (thisIndexEntry == LF_FIRSTCHAR(fontTablePos))
{
//
// We've found the first font table entry starting with
// character thisIndexEntry, so enter it in the index.
//
g_fhFonts->afhFontIndex[thisIndexEntry] = fontTablePos;
//
// Now zip past the rest of the local font table entries that
// start with this character, also checking that we haven't got
// to the end of the font table.
//
// If the latter happens, it means we've finished and the check
// in the for statement will ensure that we exit the loop.
//
while ((LF_FIRSTCHAR(fontTablePos) == thisIndexEntry) &&
(fontTablePos < g_fhFonts->fhNumFonts))
{
fontTablePos++;
}
}
}
TRACE_OUT(( "Built local font table index"));
DC_EXIT_POINT:
DebugExitVOID(FHSortAndIndexLocalFonts);
}
//
// FHComp()
// This is a wrapper around strcmp(), which becomes an inline function in
// retail. It also handles the casting of the LPVOIDs.
//
//
// Compare item 1, item 2
//
int FHComp
(
LPVOID lpFont1,
LPVOID lpFont2
)
{
return(strcmp(((LPLOCALFONT)lpFont1)->Details.nfFaceName,
((LPLOCALFONT)lpFont2)->Details.nfFaceName));
}
//
// FH_qsort(base, num, wid) - quicksort function for sorting arrays
//
// Purpose:
// quicksort the array of elements
// side effects: sorts in place
//
// Entry:
// char *base = pointer to base of array
// unsigned num = number of elements in the array
// unsigned width = width in bytes of each array element
//
// Exit:
// returns void
//
// Exceptions:
//
// sort the array between lo and hi (inclusive)
void FH_qsort
(
LPVOID base,
UINT num,
UINT width
)
{
LPSTR lo;
LPSTR hi;
LPSTR mid;
LPSTR loguy;
LPSTR higuy;
UINT size;
char *lostk[30], *histk[30];
int stkptr; // stack for saving sub-array to be processed
// Note: the number of stack entries required is no more than
// 1 + log2(size), so 30 is sufficient for any array
ASSERT(width);
if (num < 2)
return; // nothing to do
stkptr = 0; // initialize stack
lo = (LPSTR)base;
hi = (LPSTR)base + width * (num-1); // initialize limits
// this entry point is for pseudo-recursion calling: setting
// lo and hi and jumping to here is like recursion, but stkptr is
// prserved, locals aren't, so we preserve stuff on the stack
recurse:
size = (UINT)(hi - lo) / width + 1; // number of el's to sort
// below a certain size, it is faster to use a O(n^2) sorting method
if (size <= CUTOFF)
{
shortsort(lo, hi, width);
}
else
{
// First we pick a partititioning element. The efficiency of the
// algorithm demands that we find one that is approximately the
// median of the values, but also that we select one fast. Using
// the first one produces bad performace if the array is already
// sorted, so we use the middle one, which would require a very
// weirdly arranged array for worst case performance. Testing shows
// that a median-of-three algorithm does not, in general, increase
// performance.
mid = lo + (size / 2) * width; // find middle element
swap(mid, lo, width); // swap it to beginning of array
// We now wish to partition the array into three pieces, one
// consisiting of elements <= partition element, one of elements
// equal to the parition element, and one of element >= to it. This
// is done below; comments indicate conditions established at every
// step.
loguy = lo;
higuy = hi + width;
// Note that higuy decreases and loguy increases on every iteration,
// so loop must terminate.
for (;;) {
// lo <= loguy < hi, lo < higuy <= hi + 1,
// A[i] <= A[lo] for lo <= i <= loguy,
// A[i] >= A[lo] for higuy <= i <= hi
do
{
loguy += width;
}
while ((loguy <= hi) && (FHComp(loguy, lo) <= 0));
// lo < loguy <= hi+1, A[i] <= A[lo] for lo <= i < loguy,
// either loguy > hi or A[loguy] > A[lo]
do
{
higuy -= width;
}
while ((higuy > lo) && (FHComp(higuy, lo) >= 0));
// lo-1 <= higuy <= hi, A[i] >= A[lo] for higuy < i <= hi,
// either higuy <= lo or A[higuy] < A[lo]
if (higuy < loguy)
break;
// if loguy > hi or higuy <= lo, then we would have exited, so
// A[loguy] > A[lo], A[higuy] < A[lo],
// loguy < hi, highy > lo
swap(loguy, higuy, width);
// A[loguy] < A[lo], A[higuy] > A[lo]; so condition at top
// of loop is re-established
}
// A[i] >= A[lo] for higuy < i <= hi,
// A[i] <= A[lo] for lo <= i < loguy,
// higuy < loguy, lo <= higuy <= hi
// implying:
// A[i] >= A[lo] for loguy <= i <= hi,
// A[i] <= A[lo] for lo <= i <= higuy,
// A[i] = A[lo] for higuy < i < loguy
swap(lo, higuy, width); // put partition element in place
// OK, now we have the following:
// A[i] >= A[higuy] for loguy <= i <= hi,
// A[i] <= A[higuy] for lo <= i < higuy
// A[i] = A[lo] for higuy <= i < loguy
// We've finished the partition, now we want to sort the subarrays
// [lo, higuy-1] and [loguy, hi].
// We do the smaller one first to minimize stack usage.
// We only sort arrays of length 2 or more.
if ( higuy - 1 - lo >= hi - loguy ) {
if (lo + width < higuy) {
lostk[stkptr] = lo;
histk[stkptr] = higuy - width;
++stkptr;
} // save big recursion for later
if (loguy < hi) {
lo = loguy;
goto recurse; // do small recursion
}
}
else {
if (loguy < hi) {
lostk[stkptr] = loguy;
histk[stkptr] = hi;
++stkptr; // save big recursion for later
}
if (lo + width < higuy) {
hi = higuy - width;
goto recurse; // do small recursion
}
}
}
// We have sorted the array, except for any pending sorts on the stack.
// Check if there are any, and do them.
--stkptr;
if (stkptr >= 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse; // pop subarray from stack
}
else
return; // all subarrays done
}
//
// shortsort(hi, lo, width) - insertion sort for sorting short arrays
//
// Purpose:
// sorts the sub-array of elements between lo and hi (inclusive)
// side effects: sorts in place
// assumes that lo < hi
//
// Entry:
// char *lo = pointer to low element to sort
// char *hi = pointer to high element to sort
// unsigned width = width in bytes of each array element
//
// Exit:
// returns void
//
// Exceptions:
//
void shortsort
(
char *lo,
char *hi,
unsigned int width
)
{
char *p, *max;
// Note: in assertions below, i and j are alway inside original bound of
// array to sort.
while (hi > lo) {
// A[i] <= A[j] for i <= j, j > hi
max = lo;
for (p = lo+width; p <= hi; p += width) {
// A[i] <= A[max] for lo <= i < p
if (FHComp(p, max) > 0)
{
max = p;
}
// A[i] <= A[max] for lo <= i <= p
}
// A[i] <= A[max] for lo <= i <= hi
swap(max, hi, width);
// A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi
hi -= width;
// A[i] <= A[j] for i <= j, j > hi, loop top condition established
}
// A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
// so array is sorted
}
//
// swap(a, b, width) - swap two elements
//
// Purpose:
// swaps the two array elements of size width
//
// Entry:
// char *a, *b = pointer to two elements to swap
// unsigned width = width in bytes of each array element
//
// Exit:
// returns void
//
// Exceptions:
//
void swap (
char *a,
char *b,
unsigned int width
)
{
char tmp;
if ( a != b )
// Do the swap one character at a time to avoid potential alignment
// problems.
while ( width-- ) {
tmp = *a;
*a++ = *b;
*b++ = tmp;
}
}