#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; charIcharWidths[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; charIcharWidths[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; } }