Annotation of ntddk/src/print/pscript/fonttree.c, revision 1.1.1.1

1.1       root        1: //--------------------------------------------------------------------------
                      2: //
                      3: // Module Name:  FONTTREE
                      4: //
                      5: // Brief Description:  Device font tree querying routines
                      6: //
                      7: // Author:  Kent Settle (kentse)
                      8: // Created: 18-Apr-1991
                      9: //
                     10: // Copyright (C) 1991 - 1992 Microsoft Corporation.
                     11: //
                     12: // This module contains DrvQueryFontTree.
                     13: //
                     14: // History:
                     15: //   18-Apr-1991    -by-    Kent Settle       (kentse)
                     16: // Created.
                     17: //--------------------------------------------------------------------------
                     18: 
                     19: #include "pscript.h"
                     20: #include "enable.h"
                     21: #include "mapping.h"
                     22: #include <string.h>
                     23: 
                     24: #define MAX_MAPSUBTABLES    256
                     25: #define MAX_GLYPHTABLES     16
                     26: #define MAX_GLYPHS          16
                     27: 
                     28: PVOID BuildGLYPHSET(PDEVDATA, ULONG);
                     29: PVOID BuildKernPairs(PDEVDATA, ULONG);
                     30: 
                     31: //--------------------------------------------------------------------------
                     32: // PVOID DrvQueryFontTree (dhpdev,iFile,iFace,iMode)
                     33: // DHPDEV    dhpdev;
                     34: // ULONG     iFile;
                     35: // ULONG     iFace;
                     36: // ULONG     iMode;
                     37: // ULONG     *pid;
                     38: // 
                     39: // This function is used by GDI to obtain pointers to tree structures that
                     40: // define one of the following.
                     41: // 
                     42: //     1    The mapping from Unicode to glyph handles, including glyph 
                     43: //          variants; 
                     44: //     2    The mapping of ligatures and their variants to glyph handles; 
                     45: //     3    The mapping of kerning pairs to kerning handles.
                     46: // 
                     47: // Parameters:
                     48: //   dhpdev    
                     49: //     This is a PDEV handle returned from an earlier call to DrvEnablePDEV.
                     50: //   iFile
                     51: //     This identifies the font file (if device supports loading font files
                     52: //     using the DDI).
                     53: //   iFace
                     54: //     This is the index of the driver font.  The index of the first font
                     55: //     is one.  
                     56: //   iMode
                     57: //     This defines the type of information to be returned.  This may take
                     58: //     one of the following values:
                     59: // 
                     60: //       QFT_UNICODE GDI requests a pointer to a TREE_UNICODE structure 
                     61: //                   that defines the mappings from single Unicode characters
                     62: //                   to glyph handles.  This includes all glyph variants.
                     63: // 
                     64: //       QFT_LIGATURES GDI requests a pointer to a TREE_LIGATURES 
                     65: //                     structure that defines the mapping from strings 
                     66: //                     of Unicode characters to glyph handles.  This 
                     67: //                     includes all ligature variants.
                     68: // 
                     69: //       QFT_KERNPAIRS GDI requests a pointer to a TREE_KERNPAIRS 
                     70: //                     structure that define the mappings from pairs 
                     71: //                     of Unicode characters to kerning pair handles.  
                     72: //                     These kerning handles are later passed to
                     73: //                     DrvQueryFontData to obtain kerning corrections.
                     74: // 
                     75: // Returns:
                     76: //   The return value is a pointer to the requested structure. This 
                     77: //   structure is to remain unchanged during the lifetime of the 
                     78: //   associated PDEV.
                     79: //
                     80: // Note:  The following explanation was "borrowed" from lindsayh and the
                     81: //        Generic driver.
                     82: //
                     83: //   COMMENTS ON THE UNICODE TREE STRUCTURES:
                     84: //     The following is intended to help understand the strange operations
                     85: //  being done in the following function.  UNICODE is a sparse encoding,
                     86: //  so a tree structure is used to store the data.  The following function
                     87: //  produces a tree,  the leaves of which are the GLYPH HANDLES that the
                     88: //  engine passes to us to identify perticular glyphs.  These handles are
                     89: //  32 bits long,  contents up to the driver.  We need to allocate storage
                     90: //  for these and fill it in.  The first step is to determine how much
                     91: //  storage is needed.  This requires understanding how the tree is built.
                     92: //     The tree has 3 levels.  At the top level there is a MAPTABLE
                     93: //  structure.  This has as many as 256 sub trees.  These correspond to
                     94: //  the 8 MSBs of the unicode value.  This structure is folllowed by up to
                     95: //  256 PTRDIFFs (each a long).  The structure itself contains a 256 byte
                     96: //  array:  this is an index into the array of PTRDIFFs following.  Hence,
                     97: //  there only needs to be as many PTRDIFFs as there are subtrees (plus 1,
                     98: //  for the "no entry" value).  For example,  if there is only subtree,
                     99: //  there need be only 2 PTRDIFFs - the active one,  and a 0 entry.  The
                    100: //  byte array will mostly contain the index to the 0 entry,  but the
                    101: //  active entry will point at the other PTRDIFF.
                    102: //
                    103: //     Each of the above active PTRDIFFs points to a MAPSUBTABLE.  Each of
                    104: //  these can handle 16 sub trees.  It uses the same method as the MAPTABLE
                    105: //  to handle sparseness - i.e. there is a 16 byte array containing
                    106: //  indices into the array of PTRDIFFs following.  The 16 byte array
                    107: //  is indexed by the next 4 bits in the UNICODE value (i.e. the high
                    108: //  nibble of the low byte).  These PTRDIFFs point to GLYPHTABLEs,
                    109: //  the next lower level in the tree.
                    110: //
                    111: //     The final layer is the GLYPHTABLE data.  Each of these contains
                    112: //  up to 16 GLYPH HANDLEs,  and is indexed by the 4 LSBs of the UNICODE
                    113: //  value.  Like the MAPSUBTABLE,  there is a byte array containing
                    114: //  index values of the glyph handles following.
                    115: //
                    116: //   HOW MUCH MEMORY IS NEEDED??
                    117: //  The tricky part is now determining how much memory to allocate for
                    118: //  these structures.  The answer is:  a DWORD for each GLYPH HANDLE,
                    119: //  as many GLYPHTABLEs as there are groups defined by the high order
                    120: //  12 bits of the UNICODE value (i.e., up to 4096),  as many MAPSUBTABLES
                    121: //  as there are groups of 16 GLYPHTABLES (up to 256), plus a MAPTABLE
                    122: //  to amalgamate the top level.  As well,  the MAPTABLE and MAPSUBTABLE
                    123: //  require PTRDIFFs to the lower level - this is as many as there are
                    124: //  lower level entities.
                    125: //
                    126: //     Determining this is done as follows:  allocate a 64k BIT array,
                    127: //  considered as an array of 4096 WORDS (16 bits each).  For every
                    128: //  available glyph,  set the corresponding bit in the array,  and
                    129: //  increment a counter.  At the end of this stage,  we have a count of
                    130: //  the number of (non-zero) GLYPH HANDLES needed.  Now,  by scanning
                    131: //  the bit array by WORD,  count each non-zero word.  This tells us
                    132: //  how many GLYPHTABLEs are required.   A second bit array is also
                    133: //  updated at this time.  This array consists of 256 bits,  one for
                    134: //  each of the possible entries in the MAPTABLE.  Passing through the
                    135: //  WORD array,  we can also set bits in the 256 bit array corresponding
                    136: //  to the number of MAPTABLES required.  Finally,  counting the number
                    137: //  of set bits in the 256 bit array gives us the number of MAPTABLES
                    138: //  required.  Given this information,  it is possible to determine
                    139: //  the amount of storage required.  Note that it is necessary to allow
                    140: //  for a 0 entry in the PTRDIFF and HGLYPH arrays.
                    141: // 
                    142: //   If an error occurs, NULL should be returned and an error code should 
                    143: //   be logged.
                    144: //
                    145: // History:
                    146: //   18-Apr-1991    -by-    Kent Settle       (kentse)
                    147: // Wrote it.
                    148: //--------------------------------------------------------------------------
                    149: 
                    150: PVOID DrvQueryFontTree (dhpdev,iFile,iFace,iMode,pid)
                    151: DHPDEV    dhpdev;
                    152: ULONG     iFile;
                    153: ULONG     iFace;
                    154: ULONG     iMode;
                    155: ULONG    *pid;
                    156: {
                    157:     PDEVDATA            pdev;
                    158: 
                    159:     UNREFERENCED_PARAMETER(iFile);
                    160: 
                    161:     // This can be used by the driver to flag or id the data returned.
                    162:     // May be useful for deletion of the data later by DrvFree().
                    163: 
                    164:     *pid = 0;           // don't need to use for this driver
                    165: 
                    166:     // get a pointer to our PDEV.
                    167: 
                    168:     pdev = (PDEVDATA)dhpdev;
                    169: 
                    170:     if (bValidatePDEV(pdev) == FALSE)
                    171:     {
                    172:        RIP("PSCRIPT!DrvQueryFontTree: invalid PDEV.");
                    173:        SetLastError(ERROR_INVALID_PARAMETER);
                    174:         return((PVOID)NULL);
                    175:     }
                    176: 
                    177:     // validate the iFace.  we should only be called for device fonts,
                    178:     // ie iFace >= 1.
                    179: 
                    180:     if ((iFace < 1) || (iFace > (pdev->cDeviceFonts + pdev->cSoftFonts)))
                    181:     {
                    182:        RIP("PSCRPT!BuildGLYPHSET: invalid iFace.\n");
                    183:        SetLastError(ERROR_INVALID_PARAMETER);
                    184:        return((PVOID)NULL);
                    185:     }
                    186: 
                    187:     switch (iMode)
                    188:     {
                    189:        case QFT_GLYPHSET:
                    190:            return(BuildGLYPHSET(pdev, iFace));
                    191:            break;
                    192: 
                    193:        case QFT_KERNPAIRS:
                    194:             return(BuildKernPairs(pdev, iFace));
                    195:            break;
                    196: 
                    197:        default:
                    198:            RIP("PSCRIPT!DrvQueryFontTree: invalid iMode.\n");
                    199:            SetLastError(ERROR_INVALID_PARAMETER);
                    200:            return((PVOID)NULL);
                    201:     }
                    202: }
                    203: 
                    204: //--------------------------------------------------------------------------
                    205: // PVOID BuildGLYPHSET(pdev, iFace)
                    206: // PDEVDATA    pdev;
                    207: // ULONG       iFace;
                    208: //
                    209: //   If an error occurs, NULL should be returned and an error code should 
                    210: //   be logged.
                    211: //
                    212: // History:
                    213: //   10-Feb-1992         -by-    Kent Settle        (kentse)
                    214: // Borrowed some of if from RASDD, wrote the rest.
                    215: //--------------------------------------------------------------------------
                    216: 
                    217: PVOID BuildGLYPHSET(pdev, iFace)
                    218: PDEVDATA    pdev;
                    219: ULONG      iFace;
                    220: {
                    221:     PNTFM      pntfm;          // pointer to font metrics.
                    222:     PUCMap     pmap;           // pointer to UNICODE<->PSCRIPT mapping table.
                    223:     PUCMap     pmapReset;      // place to save pointer.
                    224:     DWORD       i;              // loop counter.
                    225:     PBYTE      pCharCode;      // pointer to character code.
                    226:     PBYTE       pCharReset;     // place to save pointer.
                    227:     WCHAR      wchCurrent;     // current UNICODE value.
                    228:     WCHAR      wchPrevious;    // previous UNICODE value.
                    229:     DWORD       cGlyphs;        // count of glyph handles.
                    230:     DWORD       cRuns;          // count of runs within FD_GLYPHSET.
                    231:     DWORD      cbTotalMem;     // count of bytes needed for FD_GLYPHSET.
                    232:     HGLYPH     *phg;           // pointer to HGLYPH's.
                    233:     FD_GLYPHSET *pGLYPHSET;    // pointer to FD_GLYPHSET.
                    234:     WCRUN      *pWCRUN;        // pointer to WCRUN.
                    235:     PULONG      pulUCTree;      // pointer to UNICODE tree.
                    236: 
                    237:     // get the font information for the given font.
                    238: 
                    239:     pntfm = pdev->pfmtable[iFace - 1].pntfm;
                    240: 
                    241:     // point to the appropriate mapping table in mapping.h.
                    242: 
                    243:     if (!strcmp((char *)pntfm + pntfm->loszFontName, "Symbol"))
                    244:         pmap = SymbolMap;
                    245: 
                    246:     else if (!strcmp((char *)pntfm + pntfm->loszFontName, "ZapfDingbats"))
                    247:         pmap = DingbatsMap;
                    248:     else
                    249:         pmap = LatinMap;
                    250: 
                    251:     pmapReset = pmap;
                    252: 
                    253:     // for every character in the mapping table, do a lookup in the NTFM
                    254:     // structure to find a matching PostScript character code.  For each
                    255:     // character found, we can extract the corresponding UNICODE value.
                    256:     // remember that the character codes are sitting in a BYTE array
                    257:     // which is sitting directly after a (USHORT) array of character
                    258:     // widths.
                    259: 
                    260:     pCharCode = ((PBYTE)pntfm + pntfm->loCharMetrics +
                    261:                        DWORDALIGN(pntfm->cCharacters * sizeof(USHORT)));
                    262: 
                    263:     // save the original pointer.
                    264: 
                    265:     pCharReset = pCharCode;
                    266: 
                    267:     cRuns = 1;  // there must be at least one run!
                    268:     cGlyphs = 0;
                    269: 
                    270:     // set wchPrevious equal to first UNICODE character code.
                    271: 
                    272:     wchPrevious = (WCHAR)pmap->usUCValue;
                    273: 
                    274:     while (pmap->szChar)
                    275:     {
                    276:         for (i = 0; i < (DWORD)pntfm->cCharacters; i++)
                    277:         {
                    278:             // search through all the characters in the NTFM structure,
                    279:             // looking for a matching PostScript character code. remember
                    280:             // that the high bit is used to indicate the font needs
                    281:             // to be remapped.  so ignore the high bit while checking
                    282:             // for a character match.
                    283: 
                    284:             if ((CHAR)*pCharCode == (CHAR)pmap->usPSValue)
                    285:             {
                    286:                 // we have found the character, now get its UNICODE value
                    287:                 // and set its BIT in the BIT array.  also, increment the
                    288:                 // glyph count, if this UNICODE value has not yet been used.
                    289:                 // since the mapping tables in mapping.h are sorted by
                    290:                 // UNICODE values, we can simply check this value against
                    291:                 // the one we found last time.  if it is the same, ignore 
                    292:                 // it, if it is different, use it.
                    293: 
                    294:                wchCurrent = (WCHAR)pmap->usUCValue;
                    295: 
                    296:                if (wchCurrent != wchPrevious)
                    297:                 {
                    298:                    cGlyphs++;
                    299: 
                    300:                    // see if we are starting new run.
                    301: 
                    302:                     if ((wchCurrent - wchPrevious) != 1)
                    303:                         cRuns++;
                    304:                 }
                    305:                 
                    306:                wchPrevious = wchCurrent;
                    307:                 break;
                    308:             }
                    309: 
                    310:             pCharCode++;
                    311:         }
                    312:         
                    313:         // point back to start of character list in NTFM structure.
                    314: 
                    315:         pCharCode = pCharReset;
                    316: 
                    317:         // point to next character in mapping table.
                    318: 
                    319:         pmap++;
                    320:     }
                    321: 
                    322:     // allocate memory to build the FD_GLYPHSET structure in.  this
                    323:     // include space for the FD_GLYPHSET structure itself, as well
                    324:     // as space for all the glyph handles.
                    325: 
                    326:     cbTotalMem = (cGlyphs * sizeof(HGLYPH)) + (cRuns * sizeof(WCRUN)) +
                    327:                 sizeof(FD_GLYPHSET) - sizeof(WCRUN);
                    328: 
                    329:     // DWORD bound it.
                    330: 
                    331:     cbTotalMem = (cbTotalMem + 3) & ~3;
                    332: 
                    333:     // this memory is to be kept around until the pdev is destroyed, so
                    334:     // we don't have to free this memory until the heap is destroyed.
                    335: 
                    336:     phg = pulUCTree = (PVOID)HeapAlloc(pdev->hheap, 0, cbTotalMem);
                    337: 
                    338:     if (phg == NULL)
                    339:     {
                    340:        RIP("PSCRIPT!BuildGLYPHSET: HeapAlloc for phg failed.\n");
                    341:        return((PVOID)NULL);
                    342:     }
                    343: 
                    344:     // fill in the FD_GLYPHSET structure.
                    345: 
                    346:     pGLYPHSET = (FD_GLYPHSET *)phg;
                    347: 
                    348:     pGLYPHSET->cjThis = sizeof(FD_GLYPHSET) + (cRuns - 1) * sizeof(WCRUN);
                    349:     pGLYPHSET->flAccel = 0;            // no accelerators for us.
                    350:     pGLYPHSET->cGlyphsSupported = cGlyphs;
                    351:     pGLYPHSET->cRuns = cRuns;
                    352: 
                    353:     // now set the phg pointer to the first WCRUN structure.
                    354: 
                    355:     (BYTE *)phg += sizeof(FD_GLYPHSET) - sizeof(WCRUN);
                    356: 
                    357:     // point to first WCRUN in array.
                    358: 
                    359:     pWCRUN = (WCRUN *)phg;
                    360: 
                    361:     // point to data area for glyph handles.
                    362: 
                    363:     (BYTE *)phg += sizeof(WCRUN) * cRuns;
                    364: 
                    365:     // for every character in the font, do a lookup in the mapping
                    366:     // table in mapping.h to find the corresponding UNICODE value.
                    367:     // first point to the character codes in the NTFM structure.
                    368:     // remember that the character codes are sitting in a BYTE array
                    369:     // which is sitting directly after a (USHORT) array of character
                    370:     // widths.
                    371: 
                    372:     pCharCode = pCharReset;
                    373:     pmap = pmapReset;
                    374: 
                    375:     // locate the first glyph, and initialize the first WCRUN.
                    376: 
                    377:     for (i = 0; i < (DWORD)pntfm->cCharacters; i++)
                    378:     {
                    379:        // search for the matching code in mapping.h.   remember
                    380:        // that the high bit is used to indicate the font needs
                    381:        // to be remapped.  so ignore the high bit while checking
                    382:        // for a character match.
                    383: 
                    384:        if ((CHAR)*pCharCode == (CHAR)pmap->usPSValue)
                    385:        {
                    386:            // we have found the character, now get its UNICODE value
                    387:            // and set its BIT in the BIT array.  also, increment the
                    388:            // glyph count, if this UNICODE value has not yet been used.
                    389:            // since the mapping tables in mapping.h are sorted by
                    390:            // UNICODE values, we can simply check this value against
                    391:            // the one we found last time.  if it is the same, ignore
                    392:            // it, if it is different, use it.
                    393: 
                    394:            wchPrevious = (WCHAR)pmap->usUCValue;
                    395: 
                    396:            pWCRUN->wcLow = wchPrevious;
                    397:            pWCRUN->cGlyphs = 1;
                    398:            pWCRUN->phg = phg;
                    399: 
                    400:            // store the glyph handle, which we will store as the
                    401:            // PostScript character code.
                    402: 
                    403:            *phg++ = (HGLYPH)*pCharCode;
                    404: 
                    405:             break;
                    406:        }
                    407: 
                    408:         // point to the next character in the NTFM structure.
                    409: 
                    410:         pCharCode++;
                    411:     }
                    412: 
                    413:     // move to the second character in the mapping table.
                    414: 
                    415:     pmap++;
                    416: 
                    417:     while (pmap->szChar)
                    418:     {
                    419:         // reset pointer to start of character list in NTFM structure.
                    420:         
                    421:         pCharCode = pCharReset;
                    422: 
                    423:         for (i = 0; i < ((DWORD)pntfm->cCharacters - 1); i++)
                    424:         {
                    425:             // search for the matching code in mapping.h.  remember
                    426:             // that the high bit is used to indicate the font needs
                    427:             // to be remapped.  so ignore the high bit while checking
                    428:             // for a character match.
                    429: 
                    430:             if ((CHAR)*pCharCode == (CHAR)pmap->usPSValue)
                    431:             {
                    432:                 // we have found the character, now get its UNICODE value
                    433:                 // and set its BIT in the BIT array.  also, increment the
                    434:                 // glyph count, if this UNICODE value has not yet been used.
                    435:                 // since the mapping tables in mapping.h are sorted by
                    436:                 // UNICODE values, we can simply check this value against
                    437:                 // the one we found last time.  if it is the same, ignore 
                    438:                 // it, if it is different, use it.
                    439: 
                    440:                wchCurrent = (WCHAR)pmap->usUCValue;
                    441: 
                    442:                if (wchCurrent != wchPrevious)
                    443:                {
                    444:                    // see if we are starting a new run.
                    445: 
                    446:                    if ((wchCurrent - wchPrevious) != 1)
                    447:                    {
                    448:                        // start the new WCRUN.
                    449: 
                    450:                        pWCRUN++;
                    451:                        pWCRUN->wcLow = wchCurrent;
                    452:                         pWCRUN->cGlyphs = 0;
                    453:                         pWCRUN->phg = phg;
                    454:                     }
                    455: 
                    456:                    // fill in the glyph handle.
                    457: 
                    458:                    pWCRUN->cGlyphs++;
                    459:                     *phg++ = (HGLYPH)*pCharCode;
                    460:                 }
                    461:                 
                    462:                wchPrevious = wchCurrent;
                    463:                 break;
                    464:             }
                    465: 
                    466:             pCharCode++;
                    467:         }
                    468: 
                    469:         // point to next character in mapping table.
                    470: 
                    471:         pmap++;
                    472:     }
                    473: 
                    474:     return(pulUCTree);
                    475: }
                    476: 
                    477: 
                    478: //--------------------------------------------------------------------------
                    479: // PVOID BuildKernPairs(pdev, iFace)
                    480: // PDEVDATA    pdev;
                    481: // ULONG       iFace;
                    482: //
                    483: //  This routine returns a pointer to a zero terminated array of
                    484: //  FD_KERNINGPAIR structures.  They should be sorted in order such that
                    485: //  the first character is the most significant, and the second character
                    486: //  is the least.
                    487: //
                    488: //   If an error occurs, NULL should be returned and an error code should 
                    489: //   be logged.
                    490: //
                    491: // History:
                    492: //   12-Mar-1992         -by-    Kent Settle        (kentse)
                    493: // Wrote it.
                    494: //--------------------------------------------------------------------------
                    495: 
                    496: PVOID BuildKernPairs(pdev, iFace)
                    497: PDEVDATA    pdev;
                    498: ULONG      iFace;
                    499: {
                    500:     PNTFM          pntfm;          // pointer to font metrics.
                    501:     FD_KERNINGPAIR *pKernPairs;     // pointer to FD_KERNINGPAIRs.
                    502:     FD_KERNINGPAIR *pKernPairsSave; // pointer to FD_KERNINGPAIRs.
                    503:     DWORD           cjMem;          // count of bytes.
                    504:     DWORD           cKernPairs;     // cound of kernpairs.
                    505:     FD_KERNINGPAIR *pkp;            // pointer to kernpair in ntfm struct.
                    506: 
                    507:     // get the font information for the given font.
                    508: 
                    509:     pntfm = pdev->pfmtable[iFace - 1].pntfm;
                    510: 
                    511:     // allocate enough memory to hold all the FD_KERNINGPAIR strutures.
                    512:     // remember to allocate room for the zero terminated structure.
                    513:     // NOTE!  the memory allocated here is never explicitly freed.  this
                    514:     // will happen when the pdev, and therefore the heap, is destroyed.
                    515:     // this is done, since this memory is supposed to remain here for
                    516:     // the engine until the pdev is destroyed.
                    517: 
                    518:     cKernPairs = (DWORD)pntfm->cKernPairs;
                    519:     cjMem = ((cKernPairs + 1) * sizeof(FD_KERNINGPAIR));
                    520: 
                    521:     if (!(pKernPairs = (FD_KERNINGPAIR *)HeapAlloc(pdev->hheap, 0, cjMem)))
                    522:     {
                    523:         RIP("PSCRIPT!BuildKernPairs:  HeapAlloc for pKernPairs failed.\n");
                    524:         return((PVOID)NULL);
                    525:     }
                    526: 
                    527:     // save a copy of the original pointer.
                    528: 
                    529:     pKernPairsSave = pKernPairs;
                    530: 
                    531:     // fill in the FD_KERNINGPAIR structure for each kerning pair.
                    532: 
                    533:     pkp = (FD_KERNINGPAIR *)((BYTE *)pntfm + pntfm->loKernPairs);
                    534: 
                    535:     while (cKernPairs--)
                    536:         *pKernPairs++ = *pkp++;
                    537: 
                    538:     // fill in the zero terminating FD_KERNINGPAIR structure.
                    539: 
                    540:     pKernPairs->wcFirst = (WCHAR)'\0';
                    541:     pKernPairs->wcSecond = (WCHAR)'\0';
                    542:     pKernPairs->fwdKern = (FWORD)0;
                    543: 
                    544:     return(pKernPairsSave);
                    545: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.