/*************************************************************************** * ELSEPAN.C - ElseWare PANOSE(tm) 1.0 Font Mapper routines. * * $keywords: elsepan.c 1.5 19-Jul-93 11:15:09 AM$ * * Copyright (C) 1991-93 ElseWare Corporation. All rights reserved. ***************************************************************************/ #define ELSE_MAPPER_CORE #include #include "elsepan.h" /* Sanity check: this is the poor man's way to make sure the mapstate * we get is valid. We init this value during startup and check it upon * entry to every API routine. * * Note it is a good idea to modify SANITY_VALUE every time you make a * significant change to the software just in case the mapper ends up * in an environment where there may be multiple copies of it running, * or if client software tries to save the mapstate (which is a no-no: it * should use the API to get and set the exposed mapstate variables). */ #ifndef NOELSEWEIGHTS #define SANITY_VALUE 0xD0CACA12L #else #define SANITY_VALUE 0xD0CACA13L #endif #define M_SANE(lpMapState) \ (((lpMapState) != NULL) && ((lpMapState)->ulSanity == SANITY_VALUE)) #define M_lpjOFFS(lpDB, lOffs) (((EW_LPBYTE)(lpDB)) + (lOffs)) #ifndef M_ELSEMEMCPY #define ELSELOCALMEMCPY #define M_ELSEMEMCPY(dst, src, len) s_lpPANMemCpy((dst), (src), (len)) LOCAL EW_LPBYTE EW_NEAR EW_PASCAL s_lpPANMemCpy ELSEARGS (( EW_LPBYTE lpDst, EW_LPBYTE lpSrc, EW_USHORT unLen )); #endif LOCAL EW_LPPIND_MEM EW_NEAR EW_PASCAL s_lpPANGetIndRec ELSEARGS (( EW_LPPDICT_MEM lpPDB, EW_LPBYTE EW_FAR *lplpPanWant, EW_LPBYTE EW_FAR *lplpPanThis )); LOCAL EW_BOOL EW_NEAR EW_PASCAL s_bPANGetPenaltyC0 ELSEARGS (( EW_LPPIND_MEM lpPanIndRec, EW_LPPTBL_C0_MEM lpPC0, EW_LPUSHORT lpunMatch, EW_USHORT unTblSize, EW_USHORT unAttrA, EW_USHORT unAttrB )); LOCAL EW_USHORT EW_NEAR EW_PASCAL s_unPANGetPenaltyC1 ELSEARGS (( EW_USHORT unAttrA, EW_USHORT unAttrB )); LOCAL EW_BOOL EW_NEAR EW_PASCAL s_bPANGetPenaltyC2 ELSEARGS (( EW_LPPIND_MEM lpPanIndRec, EW_LPBYTE lpPTbl, EW_LPUSHORT lpunMatch, EW_USHORT unTblSize, EW_USHORT unAttrA, EW_USHORT unAttrB )); LOCAL EW_USHORT EW_NEAR EW_PASCAL s_unPANGetPenaltyC4 ELSEARGS (( EW_LPPTBL_C4_MEM lpPC4, EW_USHORT unAttrA, EW_USHORT unAttrB )); LOCAL EW_LPBYTE EW_NEAR EW_PASCAL s_lpPANGetWeights ELSEARGS (( EW_LPMAPSTATE lpMapState, EW_LPPDICT_MEM lpPDB, EW_LPPIND_MEM lpPanIndRec )); LOCAL EW_BOOL EW_NEAR EW_PASCAL s_bPANMatchDigits ELSEARGS (( EW_LPPDICT_MEM lpPDB, EW_LPUSHORT lpunMatchTotal, EW_LPPIND_MEM lpPanIndRec, EW_LPPTBL_MEM lpPTblRec, EW_USHORT unWt, EW_USHORT unAttrA, EW_USHORT unAttrB )); /*************************************************************************** * FUNCTION: nPANMapInit * * PURPOSE: Initialize the font mapper. Fill in the default settings. * * RETURNS: Return the size of the map-state struct if successful, or * the negative size if the passed in struct was too small. * The function returns zero if it failed to initialize. ***************************************************************************/ EW_SHORT EW_FAR EW_PASCAL nPANMapInit( EW_LPMAPSTATE lpMapState, EW_USHORT unSizeMapState) { EW_USHORT i; EW_LPPDICT_MEM lpPDB; EW_LPBYTE lpPanDef; EW_LPBYTE lpjWtA; EW_LPBYTE lpjWtB; // // Simple version check: make sure we got the right size struct. // if( unSizeMapState < sizeof( EW_MAPSTATE ) ) { if( unSizeMapState >= sizeof( EW_ULONG ) ) { lpMapState->ulSanity = 0L; } return( -(EW_SHORT) sizeof( EW_MAPSTATE ) ); } lpMapState->ulSanity = 0L; // // Attempt to allocate the penalty database. We keep the handle // until the mapper is disabled. // if( !( lpMapState->ulhPan1Data = M_lAllocPAN1DATA( ) ) ) { goto errout0; } // // Make sure the penalty database is the right version and // in the right byte ordering. // if( !( lpPDB = M_lLockPAN1DATA( lpMapState->ulhPan1Data ) ) ) { goto errout1; } if( ( lpPDB->unVersion != PANOSE_PENALTY_VERS ) || ( lpPDB->unByteOrder != PTBL_BYTE_ORDER ) ) { goto errout2; } M_bUnlockPAN1DATA( lpMapState->ulhPan1Data ); // // Fill in defaults. // lpMapState->unThreshold = ELSEDEFTHRESHOLD; lpMapState->unRelaxThresholdCount = 0; lpMapState->bUseDef = TRUE; // // Initial default font is the PANOSE number for Courier. // lpPanDef = lpMapState->ajPanDef; lpPanDef[PAN_IND_FAMILY] = FAMILY_LATTEXT; lpPanDef[PAN_IND_SERIF] = SERIF_THIN; lpPanDef[PAN_IND_WEIGHT] = WEIGHT_THIN; lpPanDef[PAN_IND_PROPORTION] = PROPORTION_MONOSPACE; lpPanDef[PAN_IND_CONTRAST] = CONTRAST_NONE; lpPanDef[PAN_IND_STROKE] = STROKE_GRADVERT; lpPanDef[PAN_IND_ARMSTYLE] = ARM_STRAIGHTSGLSERIF; lpPanDef[PAN_IND_LTRFORM] = LTRFORM_NORMCONTACT; lpPanDef[PAN_IND_MIDLINE] = MIDLINE_STDSERIFED; lpPanDef[PAN_IND_XHEIGHT] = XHEIGHT_CONSTLARGE; #ifndef NOELSEWEIGHTS // // Initialize the custom weights array. // for( i = 0, lpjWtA = lpMapState->ajWtRefA, lpjWtB = lpMapState->ajWtRefB; i < MAX_CUSTOM_WEIGHTS; ++i, *lpjWtA++ = PANOSE_ANY, *lpjWtB++ = PANOSE_ANY) ; #endif // // This value is checked by all other functions, in an attempt // to safeguard against a mapstate that we didn't initialize. // lpMapState->ulSanity = SANITY_VALUE; // // Normal return. // return( sizeof( EW_MAPSTATE ) ); errout2: M_bUnlockPAN1DATA(lpMapState->ulhPan1Data); errout1: M_bFreePAN1DATA(lpMapState->ulhPan1Data); errout0: return( 0 ); } /*************************************************************************** * FUNCTION: bPANMapClose * * PURPOSE: Free the penalty database and close the font mapper. Also * clear the sanity value so we will not service any more calls * on this mapstate. * * RETURNS: The function returns TRUE if the penalty database is * successfully freed. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANMapClose( EW_LPMAPSTATE lpMapState ) { if( M_SANE( lpMapState ) ) { lpMapState->ulSanity = 0L; return( M_bFreePAN1DATA( lpMapState->ulhPan1Data ) ); } return( FALSE ); } #ifndef NOELSEPICKFONTS /*************************************************************************** * FUNCTION: nPANGetMapDefault * * PURPOSE: Fill in the passed-in PANOSE number structure with the * default font. * * RETURNS: Return 0 if the default number was not copied (passed-in * structure too small), or NUM_PAN_DIGITS if it was. ***************************************************************************/ EW_SHORT EW_FAR EW_PASCAL nPANGetMapDefault( EW_LPMAPSTATE lpMapState, EW_LPBYTE lpPanDef, EW_USHORT unSizePanDef) { // // Sanity checks. // if( !M_SANE( lpMapState ) || ( unSizePanDef < SIZE_PAN1_NUM ) ) { return( 0 ); } // // Copy the number. // M_ELSEMEMCPY( lpPanDef, lpMapState->ajPanDef, SIZE_PAN1_NUM ); return( NUM_PAN_DIGITS ); } /*************************************************************************** * FUNCTION: nPANSetMapDefault * * PURPOSE: Make the passed-in PANOSE number the new default font. There * is no sanity checking on the number. * * RETURNS: Return 0 if the default number was not copied (passed-in * structure too small), or NUM_PAN_DIGITS if it was. ***************************************************************************/ EW_SHORT EW_FAR EW_PASCAL nPANSetMapDefault( EW_LPMAPSTATE lpMapState, EW_LPBYTE lpPanDef, EW_USHORT unSizePanDef) { // // Sanity checks. // if( !M_SANE( lpMapState ) || ( unSizePanDef < SIZE_PAN1_NUM ) ) { return( 0 ); } // // Copy the number. // M_ELSEMEMCPY( lpMapState->ajPanDef, lpPanDef, SIZE_PAN1_NUM ); return( NUM_PAN_DIGITS ); } /*************************************************************************** * FUNCTION: bPANEnableMapDefault * * PURPOSE: Enable/disable usage of the default font. * * RETURNS: Return the previous usage state, or FALSE in the event of * an error. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANEnableMapDefault( EW_LPMAPSTATE lpMapState, EW_BOOL bEnable) { if( M_SANE( lpMapState ) ) { EW_BOOL bPrev = lpMapState->bUseDef; lpMapState->bUseDef = bEnable; return( bPrev ); } else { return( FALSE ); } } /*************************************************************************** * FUNCTION: bPANIsDefaultEnabled * * PURPOSE: This function gets the state of using the default font. * * RETURNS: Return TRUE if usage of the default font is enabled, and * FALSE if it is not or an error occurred. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANIsDefaultEnabled( EW_LPMAPSTATE lpMapState ) { return( M_SANE( lpMapState ) && lpMapState->bUseDef ); } #endif /* ifndef NOELSEPICKFONTS */ #ifndef NOELSETHRESHOLD /*************************************************************************** * FUNCTION: unPANGetMapThreshold * * PURPOSE: This function gets the state of using threshold checking * in the mapper. * * RETURNS: Return the match threshold, or zero if an error occurred. ***************************************************************************/ EW_USHORT EW_FAR EW_PASCAL unPANGetMapThreshold( EW_LPMAPSTATE lpMapState ) { return( M_SANE( lpMapState ) ? lpMapState->unThreshold : 0 ); } /*************************************************************************** * FUNCTION: bPANSetMapThreshold * * PURPOSE: Change the match threshold. * * RETURNS: Return TRUE if the threshold is changed, FALSE if it is * equal to the match error value and therefore rejected, or * an error occurred. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANSetMapThreshold( EW_LPMAPSTATE lpMapState, EW_USHORT unThreshold) { // // Cannot set a threshold equal to the error value. // if( !M_SANE( lpMapState ) || ( unThreshold == PAN_MATCH_ERROR ) ) { return( FALSE ); } // // Set new threshold. // lpMapState->unThreshold = unThreshold; return( TRUE ); } /*************************************************************************** * FUNCTION: bPANIsThresholdRelaxed * * PURPOSE: This function gets the state of using the threshold in * mapping. * * RETURNS: Return TRUE if the match threshold is relaxed, or FALSE if * it is not or an error occurred. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANIsThresholdRelaxed( EW_LPMAPSTATE lpMapState ) { return( M_SANE( lpMapState ) &&( lpMapState->unRelaxThresholdCount > 0 ) ); } /*************************************************************************** * FUNCTION: vPANRelaxThreshold * * PURPOSE: Temporarily relax the threshold variable so every font * except the erroneous ones will return a match value. * * RETURNS: Nothing. ***************************************************************************/ EW_VOID EW_FAR EW_PASCAL vPANRelaxThreshold( EW_LPMAPSTATE lpMapState ) { if( M_SANE( lpMapState ) ) { ++lpMapState->unRelaxThresholdCount; } } /*************************************************************************** * FUNCTION: bPANRestoreThreshold * * PURPOSE: Restore mapping within a threshold. * * RETURNS: Return TRUE if the threshold is back in effect or an error * occurred, FALSE if someone else has relaxed it too so it * still is relaxed. We return TRUE on error in the event someone * rights a 'for' loop restoring until TRUE is returned. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANRestoreThreshold( EW_LPMAPSTATE lpMapState ) { if( M_SANE( lpMapState ) &&( lpMapState->unRelaxThresholdCount > 0 ) ) { return( --lpMapState->unRelaxThresholdCount == 0 ); } else { return( TRUE ); } } #endif /* ifndef NOELSETHRESHOLD */ #ifndef NOELSEWEIGHTS /*************************************************************************** * FUNCTION: bPANGetMapWeights * * PURPOSE: Retrieve the mapper weight values for the passed-in family * digits pair. The variable *lpbIsCustom is set if custom * mapper weights have been set by the caller. * * The weights array is an array of 10 bytes corresponding to * the 10 PANOSE digits. The first weight is ignored (and usually * set to zero) because we never actually assess a weighted * penalty on the family digit. We include it so the index * constants may be used to access the values in the weights * array. * * RETURNS: Return TRUE if mapper weights were retrieved/available (it is * legal for the caller to pass in NULL for lpjWts), or FALSE * if none exist. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANGetMapWeights( EW_LPMAPSTATE lpMapState, EW_BYTE jFamilyA, EW_BYTE jFamilyB, EW_LPBYTE lpjWts, EW_LPBOOL lpbIsCustom) { EW_USHORT i; EW_BOOL bFound = FALSE; EW_LPPDICT_MEM lpPDB; EW_LPPIND_MEM lpPanIndRec; EW_LPBYTE lpjWtA; EW_LPBYTE lpjWtB; // // Sanity test on the family digits. // if( !M_SANE( lpMapState ) || ( jFamilyA <= PANOSE_NOFIT ) ||( jFamilyA > MAX_PAN1_FAMILY ) || ( jFamilyB <= PANOSE_NOFIT ) ||( jFamilyB > MAX_PAN1_FAMILY ) ) { return( FALSE ); } // // Search for custom weights. // for( i = 0, lpjWtA = lpMapState->ajWtRefA, lpjWtB = lpMapState->ajWtRefB; !bFound && ( i < MAX_CUSTOM_WEIGHTS ) && *lpjWtA; ++i, ++lpjWtA, ++lpjWtB) { // // If custom weights are found then set *lpbIsCustom to // TRUE, copy the weights, and return success. // if( ( (*lpjWtA == jFamilyA ) &&( *lpjWtB == jFamilyB ) ) || ( (*lpjWtA == jFamilyB ) &&( *lpjWtB == jFamilyA ) ) ) { if( lpjWts ) { M_ELSEMEMCPY( lpjWts, &lpMapState->ajCustomWt[SIZE_PAN1_NUM * i], SIZE_PAN1_NUM ); } if( lpbIsCustom ) { *lpbIsCustom = TRUE; } bFound = TRUE; } } // // No custom weights available. Search the penalty database // for default weights. // if( !bFound && ( lpPDB = M_lLockPAN1DATA( lpMapState->ulhPan1Data ) ) ) { for( i = 0, lpPanIndRec = lpPDB->pind; !bFound && ( i < lpPDB->unNumDicts ); ++i, ++lpPanIndRec ) { if( ( (lpPanIndRec->jFamilyA == jFamilyA ) && ( lpPanIndRec->jFamilyB == jFamilyB ) ) || ( (lpPanIndRec->jFamilyA == jFamilyB ) && ( lpPanIndRec->jFamilyB == jFamilyA ) ) ) { if( lpPanIndRec->unOffsWts ) { if( lpjWts ) { M_ELSEMEMCPY( lpjWts, M_lpjOFFS( lpPDB, lpPanIndRec->unOffsWts ), SIZE_PAN1_NUM ); } if( lpbIsCustom ) { *lpbIsCustom = FALSE; } bFound = TRUE; } } } M_bUnlockPAN1DATA( lpMapState->ulhPan1Data ); } // // Return the result of the search. // return( bFound ); } /*************************************************************************** * FUNCTION: bPANSetMapWeights * * PURPOSE: Set the mapper weight values for the passed-in family * digits pair. * * The weights array is an array of 10 bytes corresponding to * the 10 PANOSE digits. The first weight is ignored (and usually * set to zero) because we never actually assess a weighted * penalty on the family digit. We include it so the index * constants may be used to access the values in the weights * array. * * RETURNS: Return TRUE if mapper weights were set, or FALSE if this * family pair is not supported by the mapper or there is no * more room for custom mapper weights. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANSetMapWeights( EW_LPMAPSTATE lpMapState, EW_BYTE jFamilyA, EW_BYTE jFamilyB, EW_LPBYTE lpjWts ) { EW_USHORT i; EW_BOOL bFound; EW_LPPDICT_MEM lpPDB; EW_LPPIND_MEM lpPanIndRec; EW_LPBYTE lpjWtA; EW_LPBYTE lpjWtB; EW_LPBYTE lpjWtFam; // // Sanity test on the family digits. // if( !M_SANE( lpMapState ) || !lpjWts || ( jFamilyA <= PANOSE_NOFIT ) ||( jFamilyA > MAX_PAN1_FAMILY ) || ( jFamilyB <= PANOSE_NOFIT ) ||( jFamilyB > MAX_PAN1_FAMILY ) ) { return( FALSE ); } // // First make sure this family pair exists in the penalty // database (it does not make sense to store penalties for // a family pair we'll never map against). // if( lpPDB = M_lLockPAN1DATA( lpMapState->ulhPan1Data ) ) { for( i = 0, bFound = FALSE, lpPanIndRec = lpPDB->pind; i < lpPDB->unNumDicts; ++i, ++lpPanIndRec) { if( ( (lpPanIndRec->jFamilyA == jFamilyA ) && ( lpPanIndRec->jFamilyB == jFamilyB ) ) || ( (lpPanIndRec->jFamilyA == jFamilyB ) && ( lpPanIndRec->jFamilyB == jFamilyA ) ) ) { bFound = TRUE; break; } } M_bUnlockPAN1DATA( lpMapState->ulhPan1Data ); if( !bFound ) { return( FALSE ); } } else { return( FALSE ); } // // Search for an existing entry. // for( i = 0, lpjWtA = lpMapState->ajWtRefA, lpjWtB = lpMapState->ajWtRefB; ( i < MAX_CUSTOM_WEIGHTS ) && *lpjWtA; ++i, ++lpjWtA, ++lpjWtB) { if( ( (*lpjWtA == jFamilyA ) &&( *lpjWtB == jFamilyB ) ) || ( (*lpjWtA == jFamilyB ) &&( *lpjWtB == jFamilyA ) ) ) { break; } } // // Abort if the weights were not found and there are no free slots. // if( i >= MAX_CUSTOM_WEIGHTS ) { return( FALSE ); } // // We either found the previous weights or have a free slot, // in both cases copy the passed-in weights. For aesthetics, // preserve zero for the family weight( it is not used ). // *lpjWtA = jFamilyA; *lpjWtB = jFamilyB; M_ELSEMEMCPY( lpjWtFam = &lpMapState->ajCustomWt[SIZE_PAN1_NUM * i], lpjWts, SIZE_PAN1_NUM); *lpjWtFam = 0; // // Return success. // return( TRUE ); } /*************************************************************************** * FUNCTION: bPANClearMapWeights * * PURPOSE: Locate the custom mapper weights for the passed-in family * digit pair and clear them, thus causing the mapper to revert * back to using the default weights. * * RETURNS: Return TRUE if custom mapper weights were located and cleared, * FALSE if there are no custom weights for the passed-in family * digit pair. ***************************************************************************/ EW_BOOL EW_FAR EW_PASCAL bPANClearMapWeights( EW_LPMAPSTATE lpMapState, EW_BYTE jFamilyA, EW_BYTE jFamilyB ) { EW_USHORT i; EW_USHORT j; EW_LPBYTE lpjWtA; EW_LPBYTE lpjWtB; // // Sanity test on the family digits. // if( !M_SANE( lpMapState ) || ( jFamilyA <= PANOSE_NOFIT ) ||( jFamilyA > MAX_PAN1_FAMILY ) || ( jFamilyB <= PANOSE_NOFIT ) ||( jFamilyB > MAX_PAN1_FAMILY ) ) { return( FALSE ); } // // Search for custom weights. // for( i = 0, lpjWtA = lpMapState->ajWtRefA, lpjWtB = lpMapState->ajWtRefB; ( i < MAX_CUSTOM_WEIGHTS ) && *lpjWtA; ++i, ++lpjWtA, ++lpjWtB) { // // If custom weights are found then overwrite them by // shifting other weights forward in the array. // if( ( (*lpjWtA == jFamilyA ) &&( *lpjWtB == jFamilyB ) ) || ( (*lpjWtA == jFamilyB ) &&( *lpjWtB == jFamilyA ) ) ) { for( j = i + 1, ++lpjWtA, ++lpjWtB; ( j < MAX_CUSTOM_WEIGHTS ) && *lpjWtA; ++j, ++lpjWtA, ++lpjWtB) { lpjWtA[-1] = *lpjWtA; lpjWtB[-1] = *lpjWtB; } lpjWtA[-1] = PANOSE_ANY; lpjWtB[-1] = PANOSE_ANY; if( i < ( j - 1 ) ) { M_ELSEMEMCPY( &lpMapState->ajCustomWt[SIZE_PAN1_NUM * i], &lpMapState->ajCustomWt[SIZE_PAN1_NUM * (i + 1)], ( SIZE_PAN1_NUM * (j - i - 1 ) ) ); } return( TRUE ); } } // // Custom weights matching this family digit pair were not // found, return failure. // return( FALSE ); } #endif /* ifndef NOELSEWEIGHTS */ /*************************************************************************** * FUNCTION: unPANMatchFonts * * PURPOSE: Match two PANOSE numbers. * * RETURNS: Return a match value if the fonts are successfully compared * and are within range of the threshold, otherwise return * PAN_MATCH_ERROR if there is an error or the fonts are out * of range. ***************************************************************************/ EW_USHORT EW_FAR EW_PASCAL unPANMatchFonts( EW_LPMAPSTATE lpMapState, EW_LPBYTE lpPanWant, EW_ULONG ulSizeWant, EW_LPBYTE lpPanThis, EW_ULONG ulSizeThis, EW_BYTE jMapToFamily ) { EW_USHORT unMatch = PAN_MATCH_ERROR; EW_USHORT unThreshold; EW_USHORT i; EW_USHORT j; EW_LPPDICT_MEM lpPDB; EW_LPPIND_MEM lpPanIndRec; EW_LPPTBL_MEM lpPTblRec; EW_LPBYTE lpjWts; EW_LPATOB_MEM lpAtoBHead; EW_LPATOB_ITEM_MEM lpAtoB; // // Sanity check on the PANOSE numbers. Both numbers must be // valid PANOSE 1.0 numbers, and the 'this'( compared-to ) // number must match the map-to family. // if( !M_SANE( lpMapState ) || ( ulSizeWant != SIZE_PAN1_NUM ) || ( ulSizeThis != SIZE_PAN1_NUM ) || ( lpPanWant[PAN_IND_FAMILY] <= PANOSE_NOFIT ) || ( lpPanWant[PAN_IND_FAMILY] > MAX_PAN1_FAMILY ) || ( lpPanThis[PAN_IND_FAMILY] <= PANOSE_NOFIT ) || ( lpPanThis[PAN_IND_FAMILY] > MAX_PAN1_FAMILY ) || ( lpPanThis[PAN_IND_FAMILY] != jMapToFamily ) ) { goto backout0; } // // Lock the penalty database. // if( !(lpPDB = M_lLockPAN1DATA( lpMapState->ulhPan1Data ) ) ) { goto backout0; } // // Locate the index entry that points to the dictionary containing // the penalty tables for this PANOSE number. // This routine may flip what lpPanWant and lpPanThis point to so // we can guarantee the 'FamilyA' from the penalty tables is always // associated with lpPanWant and 'FamilyB' is associated with // lpPanThis. // // Optimization for unsupported families: If we do not support this // family, but the numbers are identical, then return an 'exact match' // value of zero. Otherwise return the usual match error value. // if( !(lpPanIndRec = s_lpPANGetIndRec(lpPDB, &lpPanWant, &lpPanThis ) ) ) { for( i = 0; ( i < NUM_PAN_DIGITS ) && ( *lpPanWant == *lpPanThis ) && ( *lpPanWant != PANOSE_NOFIT ); ++i, ++lpPanWant, ++lpPanThis) ; if( i >= NUM_PAN_DIGITS ) { unMatch = 0; } goto backout1; } // // Get the array of mapper weights -- this could be a custom array // supplied by the user, or the default array from the penalty // database. // if( !( lpjWts = s_lpPANGetWeights( lpMapState, lpPDB, lpPanIndRec ) ) ) { goto backout1; } // // If we are NOT supposed to do threshold testing then just set // it to the maximum integer. // if( lpMapState->unRelaxThresholdCount > 0 ) { unThreshold = ELSEMAXSHORT; } else { unThreshold = lpMapState->unThreshold; } // // Index the penalty table array. // lpPTblRec = (EW_LPPTBL_MEM) M_lpjOFFS( lpPDB, lpPanIndRec->unOffsPTbl ); // // There are two flavors of walking the digits: // // 1. For cross-family matching, we walk an array of indices mapping // digits from one family to the digits of another. // 2. For normal( same family ) matching, we directly walk the digits. // // Test for an a-to-b array( cross-family matching ). // if( lpPanIndRec->unOffsAtoB ) { // // This is a cross-family mapping, get the a-to-b array head. // lpAtoBHead = (EW_LPATOB_MEM) M_lpjOFFS( lpPDB, lpPanIndRec->unOffsAtoB ); // // Walk the a-to-b array. // for( i = unMatch = 0, j = lpAtoBHead->unNumAtoB, lpAtoB = lpAtoBHead->AtoBItem; i < j; ++i, ++lpPTblRec, ++lpjWts, ++lpAtoB) { // // Compare the two digits. Abort if the test fails or the // accumulated match value is greater than the threshold. // if( !s_bPANMatchDigits( lpPDB, &unMatch, lpPanIndRec, lpPTblRec, *lpjWts, lpPanWant[lpAtoB->jAttrA], lpPanThis[lpAtoB->jAttrB]) || ( unMatch > unThreshold ) ) { unMatch = PAN_MATCH_ERROR; goto backout1; } } } else { // // Normal match: comparing PANOSE numbers from the same // families. Walk the digits accumulating the match result. // for( i = unMatch = 0, ++lpPanWant, ++lpPanThis; i <( NUM_PAN_DIGITS - 1 ); ++i, ++lpPTblRec, ++lpjWts, ++lpPanWant, ++lpPanThis ) { // // Compare the two digits. Abort if the test fails or the // accumulated match value is greater than the threshold. // if( !s_bPANMatchDigits( lpPDB, &unMatch, lpPanIndRec, lpPTblRec, *lpjWts, *lpPanWant, *lpPanThis) || ( unMatch > unThreshold ) ) { unMatch = PAN_MATCH_ERROR; goto backout1; } } } // // Return the match value. If it was out of range or an error // occurred, then it will equal PAN_MATCH_ERROR. // backout1: M_bUnlockPAN1DATA( lpMapState->ulhPan1Data ); backout0: return( unMatch ); } #ifndef NOELSEPICKFONTS /*************************************************************************** * FUNCTION: unPANPickFonts * * PURPOSE: Walk an array of fonts ordering them by the closest to the * requested font. If no font is within range of the threshold * then look for the closest to the default font. If still * no font is found then just pick the first font in the list. * * Implementation note: This proc assumes PANOSE 1.0 numbers. * A future version of this proc will accept intermixed PANOSE * 1.0 and 2.0 numbers, and will call a callback routine to * supply each record, instead of presuming it can walk an * array of fixed-length records. * * RETURNS: Return the number of fonts found to match the requested * font, or zero if unNumInds == 0 or an error ocurred. * * If no close match was found but the default font is enabled, * then one is returned and *lpMatchValues == PAN_MATCH_ERROR. * * If no suitable match was found and the default font is * disabled, then zero is returned. ***************************************************************************/ EW_USHORT EW_FAR EW_PASCAL unPANPickFonts( EW_LPMAPSTATE lpMapState, EW_LPUSHORT lpIndsBest, EW_LPUSHORT lpMatchValues, EW_LPBYTE lpPanWant, EW_USHORT unNumInds, EW_LPBYTE lpPanFirst, EW_USHORT unNumAvail, EW_SHORT nRecSize, EW_BYTE jMapToFamily ) { EW_USHORT i; EW_USHORT j; EW_USHORT k; EW_USHORT unNumFound = 0; EW_USHORT unMatchValue; EW_USHORT unSavedThreshold; EW_LPUSHORT lpMatches; EW_LPUSHORT lpInds; EW_LPBYTE lpPanThis; // // Sanity check. // if( !M_SANE( lpMapState ) || ( unNumInds == 0 ) || ( unNumAvail == 0 ) || ( (nRecSize < 0 ) &&( nRecSize > -(EW_SHORT )SIZE_PAN1_NUM) ) || ( (nRecSize > 0 ) &&( nRecSize < (EW_SHORT )SIZE_PAN1_NUM) ) ) { return( 0 ); } // // This routine implements a 'quit early' algorithm by modifying // the threshold to the worst acceptable value in the list (once // the list is full). This has the effect of causing matchfonts // to abort & return PAN_MATCH_ERROR whenever a penalty exceeds // the threshold. // unSavedThreshold = lpMapState->unThreshold; // // Walk the PANOSE numbers ordering them from best to worst // match. Walk the array with a byte pointer, advancing by // the passed-in record size. // for( i = 0, lpPanThis = lpPanFirst; i < unNumAvail; ++i, lpPanThis += nRecSize) { // // Get the match value. // if( ( unMatchValue = unPANMatchFonts( lpMapState, lpPanWant, SIZE_PAN1_NUM, lpPanThis, SIZE_PAN1_NUM, jMapToFamily ) ) != PAN_MATCH_ERROR ) { // // Find the slot in the array where this match value // should reside. // for( j = 0, lpMatches = lpMatchValues; ( j < unNumFound ) &&( *lpMatches < unMatchValue ); ++j, ++lpMatches) ; // // If this match value is better than one of the matches // already in the array, then insert it. Notice that // until the array is full everything goes in it. After // that, we shuffle less close matches off the end. // if( j < unNumInds ) { if( unNumFound < unNumInds ) { ++unNumFound; } for( lpInds = &lpIndsBest[k = unNumFound - 1], lpMatches = &lpMatchValues[k]; k > j; lpInds[0] = lpInds[-1], lpMatches[0] = lpMatches[-1], --k, --lpInds, --lpMatches) ; *lpInds = i; *lpMatches = unMatchValue; // // If the list is full, then set the threshold equal // to the last match value in the list. The matchfonts // routine will abort & return PAN_MATCH_ERROR on any // match greater than this value. // // Also, if the last value in the list is zero (exact // match), then exit the loop because the list will // not change. // if( unNumFound == unNumInds ) { if( (k = lpMatchValues[unNumFound - 1] ) == 0) { break; } lpMapState->unThreshold = k; } } } } // // If no acceptable match was found, then attempt to find a match // for the default font. We temporarily step off the threshold // so we will definitely find something. At this point, we do // not care if the default is not within the threshold, we just // want to find it. // if( !unNumFound && lpMapState->bUseDef ) { lpMapState->unThreshold = ELSEMAXSHORT; for( i = 0, lpPanThis = lpPanFirst; i < unNumAvail; ++i, lpPanThis += nRecSize) { if( ( unMatchValue = unPANMatchFonts( lpMapState, lpMapState->ajPanDef, SIZE_PAN1_NUM, lpPanThis, SIZE_PAN1_NUM, lpMapState->ajPanDef[PAN_IND_FAMILY] ) ) != PAN_MATCH_ERROR ) { if( unNumFound == 0 ) { *lpIndsBest = i; lpMapState->unThreshold = *lpMatchValues = unMatchValue; ++unNumFound; } else if( unMatchValue < *lpMatchValues ) { *lpIndsBest = i; lpMapState->unThreshold = *lpMatchValues = unMatchValue; } } } // // We flag this match with the error so the caller can // determine that the default font was substituted. // if( unNumFound > 0 ) { *lpMatchValues = PAN_MATCH_ERROR; } } // // Restore the threshold. // lpMapState->unThreshold = unSavedThreshold; // // If still no match is found then just pick the first font. // if( !unNumFound ) { *lpIndsBest = 0; *lpMatchValues = PAN_MATCH_ERROR; ++unNumFound; } // // Return the number of fonts found. It will be zero if we // encountered an error or couldn't find a suitable match. // return( unNumFound ); } #endif /* ifndef NOELSEPICKFONTS */ /*************************************************************************** * FUNCTION: vPANMakeDummy * * PURPOSE: Build a dummy PANOSE number with all attributes set to * PANOSE_NOFIT. * * RETURNS: Nothing. ***************************************************************************/ EW_VOID EW_FAR EW_PASCAL vPANMakeDummy( EW_LPBYTE lpPanThis, EW_USHORT unSize ) { EW_USHORT i; EW_USHORT j; unSize /= sizeof( EW_BYTE ); for( i = j = 0; (i < NUM_PAN_DIGITS ) &&( j < unSize ); ++i, j += sizeof( EW_BYTE ), *lpPanThis++ = PANOSE_NOFIT) ; } /***************************************************************************/ /************************** LOCAL SERVICE ROUTINES *************************/ /***************************************************************************/ /*************************************************************************** * FUNCTION: s_lpPANGetIndRec * * PURPOSE: Search the header of the database looking for a dictionary * of penalty tables designed for this family pair. * * There is a similar search for the index rec in the routine * bPANGetMapWeights. If you make a change here, also check in * that routine. * * RETURNS: Return the pointer to the index record if a match is found, * or NULL if one is not. ***************************************************************************/ LOCAL EW_LPPIND_MEM EW_NEAR EW_PASCAL s_lpPANGetIndRec( EW_LPPDICT_MEM lpPDB, EW_LPBYTE EW_FAR *lplpPanWant, EW_LPBYTE EW_FAR *lplpPanThis ) { EW_USHORT i; EW_BYTE jFamilyA =( *lplpPanWant )[PAN_IND_FAMILY]; EW_BYTE jFamilyB =( *lplpPanThis )[PAN_IND_FAMILY]; EW_LPBYTE lpPanSwitch; EW_LPPIND_MEM lpPanIndRec; // // Walk the index array in the penalty database looking for // a matching family pair. // for( i = 0, lpPanIndRec = lpPDB->pind; i < lpPDB->unNumDicts; ++i, ++lpPanIndRec) { if( ( lpPanIndRec->jFamilyA == jFamilyA ) && ( lpPanIndRec->jFamilyB == jFamilyB ) ) { // // Straight match. Return the index. // return( lpPanIndRec ); } else if( ( lpPanIndRec->jFamilyA == jFamilyB ) && ( lpPanIndRec->jFamilyB == jFamilyA ) ) { // // There is a match but the families are swapped. Swap // the PANOSE numbers to match the order in the penalty // database in the event it contains tables that are // order-dependent (this can happen with cross-family // mapping, C0-style/uncompressed/non-symmetric tables). // lpPanSwitch = *lplpPanWant; *lplpPanWant = *lplpPanThis; *lplpPanThis = lpPanSwitch; return( lpPanIndRec ); } } // // No match found, return an error. // return( NULL ); } /*************************************************************************** * FUNCTION: s_bPANGetPenaltyC0 * * PURPOSE: Compute the penalty between two PANOSE digits using 'C0' * compression, where the entire table is provided (except * the any and no-fit rows and columns). * * RETURNS: Return TRUE if the computed index is within range, and * *lpunMatch is filled in with the penalty value, FALSE if * it is out of range. ***************************************************************************/ LOCAL EW_BOOL EW_NEAR EW_PASCAL s_bPANGetPenaltyC0( EW_LPPIND_MEM lpPanIndRec, EW_LPPTBL_C0_MEM lpPC0, EW_LPUSHORT lpunMatch, EW_USHORT unTblSize, EW_USHORT unAttrA, EW_USHORT unAttrB ) { EW_USHORT unInd; // // Make sure each value is within range. Notice this may // be a non-square table. // if( ( unAttrA > lpPC0->jARangeLast ) ||( unAttrB > lpPC0->jBRangeLast ) ) { *lpunMatch = lpPanIndRec->jDefNoFitPenalty; return( FALSE ); } // // Compute the table index. // if( ( unInd = ( (unAttrA - 2 ) *(lpPC0->jBRangeLast - 1 ) ) + unAttrB - 2) >= unTblSize ) { *lpunMatch = lpPanIndRec->jDefNoFitPenalty; return( FALSE ); } // // Get the penalty. // *lpunMatch = lpPC0->jPenalties[unInd]; return( TRUE ); } /*************************************************************************** * FUNCTION: s_unPANGetPenaltyC1 * * PURPOSE: Compute the penalty between two PANOSE digits using 'C1' * compression, which is a perfectly symmetrical table around * the diagonal. Two digits on the diagonal are an exact match. * A difference of 1 yields a penalty of 1, a difference of 2 * yields a penalty of 2, and so on. * * It is assumed the caller handled any, no-fit, and exact * matches. * * RETURNS: Return the penalty from the table, the function cannot fail. ***************************************************************************/ LOCAL EW_USHORT EW_NEAR EW_PASCAL s_unPANGetPenaltyC1( EW_USHORT unAttrA, EW_USHORT unAttrB ) { EW_SHORT nDiff; // // Compute the penalty, which is simply the absolute value // of the difference between the two numbers. // if( ( nDiff = (EW_SHORT) unAttrA - (EW_SHORT) unAttrB ) < 0 ) { nDiff = -nDiff; } return( nDiff ); } /*************************************************************************** * FUNCTION: s_bPANGetPenaltyC2 * * PURPOSE: Compute the penalty between two PANOSE digits using 'C2' * compression, which is a table symmetrical about the * diagonal, but not a smooth range from low to high, so the * lower left corner of the table is provided. The unAttrA * digit references the row and unAttrB references the column. * * It is assumed the caller handled any, no-fit, and exact * matches. * * RETURNS: Return TRUE if the computed index is within range, and * *lpunMatch is filled in with the penalty value, FALSE if * it is out of range. ***************************************************************************/ LOCAL EW_BOOL EW_NEAR EW_PASCAL s_bPANGetPenaltyC2( EW_LPPIND_MEM lpPanIndRec, EW_LPBYTE lpPTbl, EW_LPUSHORT lpunMatch, EW_USHORT unTblSize, EW_USHORT unAttrA, EW_USHORT unAttrB ) { EW_USHORT unSwap; EW_SHORT nInd; // // The formula we use assumes the lower left half of the // penalty table, which means row > column. The table is // symmetric about the diagonal, so if row < column we can // just switch their values. // if( unAttrA < unAttrB ) { unSwap = unAttrA; unAttrA = unAttrB; unAttrB = unSwap; } // // The table is missing the any, no-fit, and exact match // penalties as those are handled separately. Since the // table is triangular shaped, we use the additive series // to compute the row: // // n + ... + 3 + 2 + 1 == 1/2 * n *( n + 1 ) // // Substituting n for row - 3, the first possible row, and // adding the column offset, we get the following formula: // // ( 1/2 * (row - 3 ) *( row - 2 ) ) +( col - 2 ) // // We know that row >= 3 and col >= 2 as we catch the other // cases above. // if( ( nInd = M_ELSEMULDIV( unAttrA - 3, unAttrA - 2, 2 ) + (EW_SHORT) unAttrB - 2) >= (EW_SHORT) unTblSize ) { *lpunMatch = lpPanIndRec->jDefNoFitPenalty; return( FALSE ); } *lpunMatch = lpPTbl[nInd]; return( TRUE ); } /*************************************************************************** * FUNCTION: s_unPANGetPenaltyC4 * * PURPOSE: Compute the penalty between two PANOSE digits using 'C4' * compression, which is almost identical to 'C1' compression * except a start and increment value are supplied. * * It is assumed the caller handled any, no-fit, and exact * matches. * * RETURNS: Return the penalty from the table, the function cannot fail. ***************************************************************************/ LOCAL EW_USHORT EW_NEAR EW_PASCAL s_unPANGetPenaltyC4( EW_LPPTBL_C4_MEM lpPC4, EW_USHORT unAttrA, EW_USHORT unAttrB ) { EW_SHORT nDiff; // // First compute the absolute value of the difference // between the two numbers. // if( (nDiff = (EW_SHORT )unAttrA -( EW_SHORT )unAttrB) < 0) { nDiff = -nDiff; } // // Then scale by the increment and start values. // if( nDiff > 0 ) { nDiff = ( ( nDiff - 1 ) *(EW_SHORT) lpPC4->jIncrement ) + (EW_SHORT) lpPC4->jStart; } return( nDiff ); } /*************************************************************************** * FUNCTION: s_lpPANGetWeights * * PURPOSE: Check the mapstate record for a set of user-supplied custom * weights. If none are present, then use the default weights * from the mapping table. * * RETURNS: Return the pointer to the array of weight values. ***************************************************************************/ LOCAL EW_LPBYTE EW_NEAR EW_PASCAL s_lpPANGetWeights( EW_LPMAPSTATE lpMapState, EW_LPPDICT_MEM lpPDB, EW_LPPIND_MEM lpPanIndRec ) { EW_USHORT i; EW_LPBYTE lpjWtA; EW_LPBYTE lpjWtB; EW_BYTE jFamilyA = lpPanIndRec->jFamilyA; EW_BYTE jFamilyB = lpPanIndRec->jFamilyB; #ifndef NOELSEWEIGHTS // // Search for custom weights. // for( i = 0, lpjWtA = lpMapState->ajWtRefA, lpjWtB = lpMapState->ajWtRefB; ( i < MAX_CUSTOM_WEIGHTS ) && *lpjWtA; ++i, ++lpjWtA, ++lpjWtB ) { // // If custom weights are found then return a pointer into // the mapstate struct. We store a weight value for the family // digit but do not use it. The pointer points to the first // digit after the family digit. // if( ( (*lpjWtA == jFamilyA ) &&( *lpjWtB == jFamilyB ) ) || ( (*lpjWtA == jFamilyB ) &&( *lpjWtB == jFamilyA ) ) ) { return( &lpMapState->ajCustomWt[ ( SIZE_PAN1_NUM * i ) + 1] ); } } #endif // // If no custom weights were found then return the default // weight from the penalty database. // if( lpPanIndRec->unOffsWts ) { return( M_lpjOFFS(lpPDB, lpPanIndRec->unOffsWts + 1 ) ); } else { return( NULL ); } } /*************************************************************************** * FUNCTION: s_bPANMatchDigits * * PURPOSE: Compute the match value between two PANOSE digits and add * it to the passed in match total. * * RETURNS: Return TRUE if the match value is computed and added to * *lpunMatchTotal. If an error occurs, return FALSE and * set *lpunMatchTotal to the value PAN_MATCH_ERROR. ***************************************************************************/ LOCAL EW_BOOL EW_NEAR EW_PASCAL s_bPANMatchDigits( EW_LPPDICT_MEM lpPDB, EW_LPUSHORT lpunMatchTotal, EW_LPPIND_MEM lpPanIndRec, EW_LPPTBL_MEM lpPTblRec, EW_USHORT unWt, EW_USHORT unAttrA, EW_USHORT unAttrB ) { EW_USHORT unLast = lpPTblRec->jRangeLast; EW_USHORT unMatch; // // First make sure the digit values are not out of range. // if( (unAttrA > unLast ) ||( unAttrB > unLast ) ) { goto errout; } // // Special case no-fit, any, or exact matches. // if( ( unAttrA == PANOSE_NOFIT ) || ( unAttrB == PANOSE_NOFIT ) ) { if( lpPTblRec->jCompress != PAN_COMPRESS_C3 ) { *lpunMatchTotal += lpPanIndRec->jDefNoFitPenalty * unWt; return( TRUE ); } } else if( ( unAttrA == PANOSE_ANY ) || ( unAttrB == PANOSE_ANY ) ) { *lpunMatchTotal += lpPanIndRec->jDefAnyPenalty * unWt; return( TRUE ); } else if( (unAttrA == unAttrB ) && ( lpPTblRec->jCompress != PAN_COMPRESS_C0 ) ) { *lpunMatchTotal += lpPanIndRec->jDefMatchPenalty * unWt; return( TRUE ); } // // Compute the penalty depending on the kind of compression // used for the table. // switch( lpPTblRec->jCompress ) { case PAN_COMPRESS_C0: if( !lpPTblRec->unOffsTbl || !lpPTblRec->unTblSize || !s_bPANGetPenaltyC0( lpPanIndRec, (EW_LPPTBL_C0_MEM) M_lpjOFFS( lpPDB, lpPTblRec->unOffsTbl ), &unMatch, lpPTblRec->unTblSize, unAttrA, unAttrB ) ) { goto errout; } *lpunMatchTotal += unMatch * unWt; break; case PAN_COMPRESS_C1: *lpunMatchTotal += s_unPANGetPenaltyC1( unAttrA, unAttrB ) * unWt; break; case PAN_COMPRESS_C2: if( !lpPTblRec->unOffsTbl || !lpPTblRec->unTblSize || !s_bPANGetPenaltyC2( lpPanIndRec, M_lpjOFFS( lpPDB, lpPTblRec->unOffsTbl ), &unMatch, lpPTblRec->unTblSize, unAttrA, unAttrB ) ) { goto errout; } *lpunMatchTotal += unMatch * unWt; break; case PAN_COMPRESS_C3: if( !lpPTblRec->unOffsTbl || !lpPTblRec->unTblSize ) { goto errout; } if( ( unAttrA == PANOSE_NOFIT ) || ( unAttrB == PANOSE_NOFIT ) ) { unMatch = *M_lpjOFFS( lpPDB, lpPTblRec->unOffsTbl ); } else if( !s_bPANGetPenaltyC2( lpPanIndRec, M_lpjOFFS( lpPDB, lpPTblRec->unOffsTbl + 1 ), &unMatch, (EW_USHORT) ( lpPTblRec->unTblSize - 1 ), unAttrA, unAttrB ) ) { goto errout; } *lpunMatchTotal += unMatch * unWt; break; case PAN_COMPRESS_C4: if( !lpPTblRec->unOffsTbl || !lpPTblRec->unTblSize ) { goto errout; } *lpunMatchTotal += s_unPANGetPenaltyC4( (EW_LPPTBL_C4_MEM) M_lpjOFFS( lpPDB, lpPTblRec->unOffsTbl ), unAttrA, unAttrB) * unWt; break; } // // Match computed, successful return. // return( TRUE ); // // An error occurred, return FALSE. // errout: *lpunMatchTotal = PAN_MATCH_ERROR; return( FALSE ); } /*************************************************************************** * FUNCTION: s_lpPANMemCpy * * PURPOSE: Perform a memcpy operation. * * RETURNS: The function returns lpDst. ***************************************************************************/ #ifdef ELSELOCALMEMCPY LOCAL EW_LPBYTE EW_NEAR EW_PASCAL s_lpPANMemCpy( EW_LPBYTE lpDst, EW_LPBYTE lpSrc, EW_USHORT unLen) { EW_LPBYTE lpRet = lpDst; EW_USHORT i; for( i = 0; i < unLen; ++i, *lpDst++ = *lpSrc++ ) ; return( lpRet ); } #endif /*************************************************************************** * Revision log: * * 31-Jan-93 msd PANOSE 1.0 mapper: 10-digit PANOSE. * 2-Feb-93 msd Removed huge pointer stuff. * 3-Feb-93 msd Removed ctrl-z at EOF. Added 'unused' pragmas. * 3-Feb-93 msd Fixed bug caused by vcs check-in. * 14-Feb-93 msd Removed extra restore-threshold call in pickfonts. * 15-Feb-93 msd For extra security, bumped the sanity value from * word to a long. ***************************************************************************/ /* * $lgb$ * 1.0 17-Feb-93 msd New module created because of vcs problems. * 1.1 17-Feb-93 msd Small doc change. * 1.2 18-Feb-93 msd Added penalty table byte-ordering check, and C4 ptbl compression( new version of ptbl ). Modified internal routines so 'unused' pragmas are not necessary. Use EW_FAR. * 1.3 23-Feb-93 msd On close session, kill the sanity value so subsequent mapper calls will fail. * 1.4 25-Feb-93 msd Modified the default font search logic in pickfonts -- search by the default font's family, not by the requested family. Also use M_ELSEMEMCPY() in a few more places. * 1.5 19-Jul-93 msd Added compilation flags to selectively disable mapper routines. * $lge$ */