|
|
#include "precomp.h"
//
// FH.CPP
// Font Handling
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_CORE
//
// Table of problem fonts
//
const TCHAR c_szIBMHex[] = "r_ibmhex";
const LPCTSTR c_aszProblemFonts[] = { c_szIBMHex, };
#define CFONTS_PROBLEM (sizeof(c_aszProblemFonts) / sizeof(c_aszProblemFonts[0]))
//
// 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; UINT iFont; 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++) { TRACE_OUT(("ASSesion::FHConsiderAllLocalFonts - EnumFontFamilies %s", lpFamilies->afhFamilies[i].szFontName));
for (iFont = 0; iFont < CFONTS_PROBLEM; iFont++) { if (!lstrcmpi(lpFamilies->afhFamilies[i].szFontName, c_aszProblemFonts[iFont])) { WARNING_OUT(("Found problem font %s", lpFamilies->afhFamilies[i].szFontName)); break; } }
if (iFont == CFONTS_PROBLEM) { //
// Not in the problem list, go ahead.
//
EnumFontFamilies(hdcDesktop, lpFamilies->afhFamilies[i].szFontName, (FONTENUMPROC)FHEachFont, (LPARAM)hdcDesktop); } else { WARNING_OUT(("Skipping problem font %s", lpFamilies->afhFamilies[i].szFontName)); } }
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; } }
|