windows-nt/Source/XPSP1/NT/shell/osshell/fontfldr/ole2map/pan1ptbl.c
2020-09-26 16:20:57 +08:00

1383 lines
43 KiB
C

/***************************************************************************
* PAN1PTBL.C - ElseWare PANOSE(tm) Font Mapper penalty database.
*
* $keywords: pan1ptbl.c 1.9 15-Apr-94 3:53:54 PM$
*
* This is a stand-alone program that writes a 'C' style header file
* containing the penalty tables for the font mapper. The tables are
* in MEMORY format, e.g., they use Intel or Motorola byte ordering
* depending on the platform upon which the program is compiled.
*
* This program should accompany ELSEPAN.C, the ElseWare PANOSE Mapper
* source code, which assumes the output from this program is in a file
* named PAN1PTBL.H.
*
* The command-line is as follows:
*
* pan1ptbl [-b] [-v] [-l] [-d] <in-filename> <out-filename>
*
* where:
*
* -b means produce a binary file (instead of a 'C' struct)
* -v means get from and put to version control
* -l means precede the struct with the keyword FAR
* -d means dump the database to the screen
* <in-filename> is the name of the text file containing the
* penalty database (PAN1PTBL.TXT)
* <out-filename> is the name of the output file (PAN1PTBL.H)
*
* The '-b' switch causes the MEMORY representation of the struct to be
* dumped to file (in binary form). The normal behavior (no '-b' switch)
* is to produce a text file containing a C-style declaration of the
* struct.
*
* The '-v' switch presumes the user has Sorcerer's Apprentice, and causes
* a 'vout' of pan1ptbl.h followed by a 'vin' of the newly generated file.
* Notice that Sorcerer's Apprentice ignores the transaction if the file
* is not different from the previous revision.
*
* The '-l' switch causes the database to be declared 'FAR,' which in
* DOS and Windows large model programs causes the data structure to be
* placed in its own data segment.
*
* The '-d' switch causes a textual description of the database to be
* dumped to stdout for debugging purposes.
*
* Copyright (C) 1991-93 ElseWare Corporation. All rights reserved.
***************************************************************************/
#define ELSE_MAPPER_CORE
#define NOELSEPANDATA
#include "elsepan.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef MACINTOSH
#include "osutils.h"
#endif /* MACINTOSH */
#ifdef MACINTOSH
# define SZ_DATABASE "DATABASE"
# define SZ_TABLE "TABLE"
# define PT_strupr(p) uprstring((p), FALSE)
BOOL PT_strcmpi(char *s, char *t) {
uprstring(s, FALSE);
return strcmp(s, t);
}
#else /* WINDOWS */
# define SZ_DATABASE TEXT("Database")
# define SZ_TABLE TEXT("Table")
# define PT_strupr(p) _strupr(p)
# define PT_strcmpi(s, t) _strcmpi(s, t)
#endif
static EW_BOOL bReadDB(FILE *fpin, EW_BOOL bBuildIt);
static EW_BOOL bReadPTbl(FILE *fpin, EW_BOOL bBuildIt,
EW_BOOL bExpectSquare, char *pLnBuf, int iBufSize,
EW_PTBL_MEM *pPTbl, EW_BYTE *pDataBuf);
static int iGetCompressKind(EW_BYTE *pDataBuf,
int iRow, int iCol, int iNoFit);
static EW_BOOL bAddUniqueTable(EW_PTBL_MEM *pPTbl, int iSize);
static int iReadData(FILE *fpin, char *pLnBuf, int iBufSize,
EW_BYTE *pDataBuf);
static EW_BOOL bGetLine(char *pBuf, int iLen, FILE *fpin);
static void vShowDB(void);
static EW_BOOL bShowDupTbl(EW_PTBL_MEM *pPTbl);
static EW_BYTE s_buf[8192];
static EW_USHORT s_unOffs;
static int s_iLnPos;
/***************************************************************************
* FUNCTION: main
*
* PURPOSE: Write the file PAN1PTBL.H, which is a 'C'-style .H file
* containing the declaration of the memory copy of the
* penalty database.
*
* RETURNS: The program returns 0 if it succeeds, 1 if there is a failure.
***************************************************************************/
int main (
int argc,
char *argv[])
{
int i;
int j;
int iRet = 1;
EW_BOOL bDumpBin = FALSE;
EW_BOOL bDoVCS = FALSE;
EW_BOOL bMakeFar = FALSE;
EW_BOOL bShowDB = FALSE;
time_t ltime;
EW_PDICT_MEM *pHead = (EW_PDICT_MEM *)s_buf;
FILE *fpin;
FILE *fpout;
char *p;
char *pszInFileNm;
char *pszOutFileNm;
char szIncNm[64];
char szTime[64];
printf("ElseWare Penalty Table Maker v1.2\n");
/* Display 'usage' message if the minimum number of parameters
* was not recieved.
*/
if( argc < 3 ) {
printf("Usage: pan1ptbl [-b] [-v] [-l] [-d] <infile> <outfile>\n");
printf(" -b means dump in binary form (not C-struct)\n");
printf(" -v means get from and put to version control\n");
printf(" -l means precede the struct with the keyword FAR\n");
printf(" -d dump the database to the screen\n");
printf(" <infile> is the text penalty database (PAN1PTBL.TXT)\n");
printf(" <outfile> is the output file (PAN1PTBL.H)\n");
goto backout0;
}
/* Set flags.
*/
for( p = argv[i = 1]; i < (argc - 2 ); p = argv[++i]) {
if( p[0] != '-' ) {
printf("Expected command line flag at '%s,' pan1ptbl aborted.\n", p);
goto backout0;
}
switch( p[1] ) {
case 'b':
case 'B':
bDumpBin = TRUE;
break;
case 'v':
case 'V':
bDoVCS = TRUE;
break;
case 'l':
case 'L':
bMakeFar = TRUE;
break;
case 'd':
case 'D':
bShowDB = TRUE;
break;
default:
printf("Unrecognized command line flag '%s' ignored.\n", p);
break;
}
}
/* Make sure the last few params are not flags (should be
* file names).
*/
if( *p == '-' ) {
printf("Expected input file name at '%s,' pan1ptbl ignored.\n", p);
goto backout0;
}
PT_strupr(pszInFileNm = p);
p = argv[argc - 1];
if( *p == '-' ) {
printf("Expected output file name at '%s,' pan1ptbl ignored.\n", p);
goto backout0;
}
PT_strupr(pszOutFileNm = p);
/* Extract the file from version control if we're supposed to
* do that (Sorcerer's Apprentice is assumed).
*/
if( bDoVCS ) {
#ifdef WINDOWS
char cmd[128];
sprintf(cmd, "vout %s", pszOutFileNm);
if( system(cmd ) != 0) {
printf("Failed to get %s from version control, pan1ptbl aborted.\n",
pszOutFileNm);
goto backout0;
}
#endif /* WINDOWS */
#ifdef MACINTOSH
printf("VCS mode not valid on the Macintosh. Continuing anyway...\n");
#endif /* MACINTOSH */
}
/* Open the input file.
*/
if( !(fpin = fopen(pszInFileNm, "rt" ))) {
printf("Could not open input file %s, pan1ptbl aborted.\n",
pszInFileNm);
goto backout0;
}
/* Create the output file.
*/
if( bDumpBin ) {
fpout = fopen(pszOutFileNm, "wb");
} else {
fpout = fopen(pszOutFileNm, "wt");
}
if( !fpout ) {
printf("Could not open output file %s, pan1ptbl aborted.\n",
pszOutFileNm);
if( bDoVCS ) {
#ifdef WINDOWS
char cmd[128];
sprintf(cmd, "vadmin u %s", pszOutFileNm);
if( system(cmd ) != 0) {
printf("Failed to unlock %s from version control.\n",
pszOutFileNm);
}
#endif /* WINDOWS */
}
goto backout1;
}
/* Build the database.
*/
s_iLnPos = 0;
if( !bReadDB(fpin, FALSE )) {
goto backout2;
}
rewind(fpin);
s_iLnPos = 0;
if( !bReadDB(fpin, TRUE )) {
goto backout2;
}
printf("%s: %u bytes in database.\n", pszOutFileNm, pHead->unSizeDB);
if( bShowDB ) {
vShowDB();
}
/* If we're writing binary, then dump it now and exit.
*/
if( bDumpBin ) {
fwrite(s_buf, pHead->unSizeDB, 1, fpout);
goto webedun;
}
/* 'C' header write.
*
* Write the file containing the 'C' structure that defines
* the penalty database.
*/
if( time(&ltime )) {
strcpy(szTime, asctime(localtime(&ltime)));
szTime[strlen(szTime) - 1] = '\0';
} else {
szTime[0] = '\0';
}
strcpy(szIncNm, "__");
if( (p = strrchr(pszOutFileNm, '\\' )) ||
( p = strrchr(pszOutFileNm, ':' ))) {
strcat(szIncNm, &p[1]);
} else {
strcat(szIncNm, pszOutFileNm);
}
if( p = strchr(szIncNm, '.' )) {
*p = '_';
}
strcat(szIncNm, "__");
if( bDoVCS ) {
fprintf(fpout,
"/*\044change:Updated by PAN1PTBL.EXE on %s.\044*/\n", szTime);
}
fprintf(fpout,
"/***************************************************************************\n");
fprintf(fpout,
" * %s - ElseWare PANOSE(tm) default penalty tables.\n", pszOutFileNm);
fprintf(fpout,
" *\n");
if( bDoVCS ) {
fprintf(fpout,
" * $keywords: pan1ptbl.c 1.9 15-Apr-94 3:53:54 PM$\n");
fprintf(fpout,
" *\n");
}
fprintf(fpout,
" * This file was generated by PAN1PTBL.EXE.\n");
fprintf(fpout,
" *\n");
fprintf(fpout,
" * This file contains the penalty tables for the PANOSE 1.0 font\n");
fprintf(fpout,
" * mapper. It was generated from the file %s.\n", pszInFileNm);
fprintf(fpout,
" *\n");
fprintf(fpout,
" * Penalty database structure version %x.%02x.\n",
PANOSE_PENALTY_VERS / 0x100,
PANOSE_PENALTY_VERS % 0x100);
fprintf(fpout,
" *\n");
if( !bDoVCS ) {
fprintf(fpout,
" * File created %s.\n", szTime);
fprintf(fpout,
" *\n");
}
fprintf(fpout,
" * Copyright( C ) 1992-93 ElseWare Corporation. All rights reserved.\n");
fprintf(fpout,
" ***************************************************************************/\n");
fprintf(fpout,
"\n");
fprintf(fpout,
"#ifndef %s\n", szIncNm);
fprintf(fpout,
"#define %s\n", szIncNm);
fprintf(fpout,
"\n");
fprintf(fpout,
"/***************************************************************************\n");
fprintf(fpout,
" * PENALTY DATABASE\n");
fprintf(fpout,
" *\n");
fprintf(fpout,
" * Below is the default penalty database for the PANOSE font\n");
fprintf(fpout,
" * mapper. It is in the MEMORY format.\n");
fprintf(fpout,
" *\n");
fprintf(fpout,
" * Look at PAN1PTBL.C to see how this is created.\n");
fprintf(fpout,
" ***************************************************************************/\n");
if( bMakeFar ) {
fprintf(fpout,
"EW_BYTE FAR s_panDB[] = {");
} else {
fprintf(fpout,
"EW_BYTE s_panDB[] = {");
}
for( i = 1, p = s_buf; i < (int )pHead->unSizeDB; ) {
fprintf(fpout, "\n ");
for( j = 0; (i < (int )pHead->unSizeDB) &&( j < 10 );
++i, ++j, ++p) {
fprintf(fpout, " 0x%02x,",( EW_BYTE )*p);
}
}
if( j == 10 ) {
fprintf(fpout, "\n ");
}
fprintf(fpout,
" 0x%02x\n};\n",( EW_BYTE )*p);
fprintf(fpout,
"\n");
fprintf(fpout,
"#endif /* ifndef %s */\n", szIncNm);
if( bDoVCS ) {
fprintf(fpout,
"\n");
fprintf(fpout,
"/***************************************************************************\n");
fprintf(fpout,
" * Revision log:\n");
fprintf(fpout,
" ***************************************************************************/\n");
fprintf(fpout,
"/*\n");
fprintf(fpout,
" * \044log\044\n");
fprintf(fpout,
" */\n");
}
webedun:
fclose(fpout);
fpout = NULL;
/* Return the file to version control.
*/
if( bDoVCS ) {
#ifdef WINDOWS
char cmd[128];
sprintf(cmd, "vin -c\"File updated by PAN1PTBL.EXE %s\" %s",
szTime, pszOutFileNm);
if( system(cmd ) != 0) {
printf("Failed to put %s into version control.\n", pszOutFileNm);
}
#endif /* WINDOWS */
}
iRet = 0;
goto backout1;
backout2:
fclose(fpout);
backout1:
fclose(fpin);
backout0:
return( iRet );
}
/***************************************************************************
* FUNCTION: bReadDB
*
* PURPOSE: Read the text penalty database. This routine is called twice:
* once to gather statistics, and then a second time to fill in
* everything else.
*
* RETURNS: The function returns TRUE if parses the whole file without
* detecting errors.
***************************************************************************/
static EW_BOOL bReadDB(
FILE *fpin,
EW_BOOL bBuildIt)
{
int i;
int j;
int w;
int iNumTbl;
EW_BOOL bUseAtoB;
EW_PDICT_MEM *pHead =( EW_PDICT_MEM * )s_buf;
EW_PIND_MEM *pInd = pHead->pind;
EW_BYTE *pWt = NULL;
EW_ATOB_MEM *pAtoB = NULL;
EW_ATOB_ITEM_MEM *pAtoBItem = NULL;
EW_PTBL_MEM *pPTbl = NULL;
char szBuf[256];
char szKey[128];
/* First pass: init header. On the second pass it contains
* the count of dictionaries in the database.
*/
if( bBuildIt ) {
s_unOffs = sizeof(EW_PDICT_MEM) +
( sizeof(EW_PIND_MEM ) *( pHead->unNumDicts - 1 ));
} else {
pHead->unVersion = PANOSE_PENALTY_VERS;
pHead->unByteOrder = PTBL_BYTE_ORDER;
pHead->unNumDicts = 0;
pHead->unSizeDB = 0;
s_unOffs = sizeof(EW_PDICT_MEM);
}
/* Get the first line.
*/
if( !bGetLine(szBuf, sizeof(szBuf ), fpin)) {
printf("No data found in the input file, pan1ptbl aborted.\n");
return( FALSE );
}
/* For each database.
*
* Look for [ Database ]
*/
while( (sscanf(szBuf, "[ %s ]", szKey ) == 1) &&
( PT_strcmpi(szKey, "Database" ) == 0)) {
/* Init vars.
*/
iNumTbl = 0;
bUseAtoB = FALSE;
pWt = NULL;
pAtoB = NULL;
pAtoBItem = NULL;
pPTbl = NULL;
/* If we're really building then set up the vars
* to recieve the tables.
*/
if( bBuildIt ) {
/* The weight array is always exactly 10 bytes long,
* initialized to all zeroes. The first byte contains
* the weight for the family digit, which does not
* have a penalty table associated with it.
*/
pWt = &s_buf[s_unOffs];
pInd->unOffsWts = s_unOffs;
memset(pWt, 0, j =( sizeof(EW_BYTE ) * NUM_PAN_DIGITS));
++pWt;
s_unOffs += j;
/* For the A-to-B remapping array, the offset
* variable in the index contains the count of
* items from the first pass. Use that to set
* up the array.
*/
if( pInd->unOffsAtoB > 0 ) {
pAtoB =( EW_ATOB_MEM * )&s_buf[s_unOffs];
pAtoBItem = pAtoB->AtoBItem;
i = pInd->unOffsAtoB;
pInd->unOffsAtoB = s_unOffs;
memset((char *)pAtoB, 0, j =( sizeof(EW_ATOB_MEM ) +
( sizeof(EW_ATOB_ITEM_MEM ) *( i - 1 ))));
pAtoB->unNumAtoB = i;
s_unOffs += j;
} else {
i = NUM_PAN_DIGITS - 1;
}
/* Set up penalty table pointer.
*/
pPTbl =( EW_PTBL_MEM * )&s_buf[s_unOffs];
pInd->unOffsPTbl = s_unOffs;
memset((char *)pPTbl, 0, j =( sizeof(EW_PTBL_MEM ) * i));
s_unOffs += j;
} else {
pInd->jFamilyA = 0;
pInd->jFamilyB = 0;
pInd->jDefAnyPenalty = 0;
pInd->jDefNoFitPenalty = 10;
pInd->jDefMatchPenalty = 0;
pInd->jReserved = 0;
pInd->unOffsWts = 0;
pInd->unOffsAtoB = 0;
pInd->unOffsPTbl = 0;
}
/* Look for row = <PANOSE-family-digit-for-row>
*/
if( !bGetLine(szBuf, sizeof(szBuf ), fpin) ||
( sscanf(szBuf, "row = %d", &i ) != 1)) {
printf("%d: Expected 'row' statement, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
if( (i < PANOSE_NOFIT ) ||( i > MAX_PAN1_FAMILY )) {
printf("%d: Row PANOSE digit out of range, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
pInd->jFamilyA = i;
/* Look for col = <PANOSE-family-digit-for-column>
*/
if( !bGetLine(szBuf, sizeof(szBuf ), fpin) ||
( sscanf(szBuf, "col = %d", &i ) != 1)) {
printf("%d: Expected 'col' statement, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
if( (i < PANOSE_NOFIT ) ||( i > MAX_PAN1_FAMILY )) {
printf("%d: Column PANOSE digit out of range, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
pInd->jFamilyB = i;
/* Read in a table heading:
*
* [ Table : <ind-row> : <ind-col> : <default-weight> ]
*/
if( !bGetLine(szBuf, sizeof(szBuf ), fpin)) {
printf("%d: Expected penalty table after database header, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
/* For each table.
*/
while ((sscanf(szBuf, "[ %s : %d : %d : %d ]",
szKey, &i, &j, &w) == 4) &&( PT_strcmpi(szKey, "Table" ) == 0)) {
/* Make sure indices in range.
*/
if( (i <= 0 ) ||( j <= 0 ) ||
( i >= NUM_PAN_DIGITS ) ||( j >= NUM_PAN_DIGITS )) {
printf("%d: Digit index out of range, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
/* Test non-zero weight.
*/
if( !w && !bBuildIt ) {
printf("%d: Warning: Weight value of zero.\n", s_iLnPos);
}
/* Detect need for A-to-B remapping array. This happens
* when the row-ind != col-ind or when the inds do not
* count from 1 to 9.
*/
if( (i != j ) ||( i != (iNumTbl + 1 ))) {
bUseAtoB = TRUE;
}
/* Attempt to read the table.
*
* This routine bumps s_unOffs.
*/
if (!bReadPTbl(fpin, bBuildIt,
( EW_BOOL )((i == j) &&( pInd->jFamilyA == pInd->jFamilyB )),
szBuf, sizeof(szBuf), pPTbl, &s_buf[s_unOffs])) {
return( FALSE );
}
/* Pick up default weight value and A-to-B settings.
*/
if( bBuildIt ) {
*pWt++ = w;
if( pAtoBItem ) {
pAtoBItem->jAttrA = i;
pAtoBItem->jAttrB = j;
{
EW_ATOB_ITEM_MEM *pAtoBCmp = pAtoB->AtoBItem;
for( ; pAtoBCmp < pAtoBItem; ++pAtoBCmp ) {
if( (pAtoBCmp->jAttrA == pAtoBItem->jAttrA ) &&
( pAtoBCmp->jAttrB == pAtoBItem->jAttrB )) {
printf("%u: Row & col pair used in a previous table, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
}
}
++pAtoBItem;
}
++pPTbl;
}
++iNumTbl;
}
/* End of dictionary handling.
*/
if( !iNumTbl ) {
printf("%d: Expected penalty table header, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
/* Verify count of tables: there should be exactly 9 without
* an A-to-B remapping array.
*/
if( bUseAtoB ) {
if( bBuildIt ) {
if( iNumTbl != (int )pAtoB->unNumAtoB) {
printf("%d: Second-pass: Error in AtoB count.\n", s_iLnPos);
return( FALSE );
}
} else if( iNumTbl > (NUM_PAN_DIGITS - 1 )) {
printf("%d: Cannot have more that %u tables, pan1ptbl aborted.\n",
s_iLnPos,( int )(NUM_PAN_DIGITS - 1));
return( FALSE );
} else {
/* Save the count of tables here for second pass.
*/
pInd->unOffsAtoB = iNumTbl;
}
} else if( iNumTbl != (NUM_PAN_DIGITS - 1 )) {
printf("%d: Expected exactly %d tables, pan1ptbl aborted.\n",
s_iLnPos,( int )(NUM_PAN_DIGITS - 1));
return( FALSE );
}
/* Bump count of dictionaries, as we successfully read one.
*/
if( !bBuildIt ) {
++pHead->unNumDicts;
s_unOffs += sizeof(EW_PIND_MEM);
}
++pInd;
}
if( !pHead->unNumDicts ) {
printf("No penalty dictionaries found, pan1ptbl aborted.\n");
return( FALSE );
}
if( bBuildIt ) {
pHead->unSizeDB = s_unOffs;
}
return( TRUE );
}
/***************************************************************************
* FUNCTION: bReadPTbl
*
* PURPOSE: Read, validate, and compress a table.
*
* RETURNS: The function returns TRUE if it succesfully processes the
* table, FALSE if it does not.
***************************************************************************/
static EW_BOOL bReadPTbl(
FILE *fpin,
EW_BOOL bBuildIt,
EW_BOOL bExpectSquare,
char *pLnBuf,
int iBufSize,
EW_PTBL_MEM *pPTbl,
EW_BYTE *pDataBuf)
{
int i;
int j;
int k;
int iNoFit;
int iCol;
int iRow;
*pDataBuf = 0;
/* Read in the data table.
*
* First line is the column headings.
*/
if( !(iCol = iReadData(fpin, pLnBuf, iBufSize, &pDataBuf[1] ))) {
printf("%d: Expected table data, pan1ptbl aborted.\n", s_iLnPos);
return( FALSE );
}
++iCol;
/* Read the remaining rows, watching for the same number of columns.
*/
for (iRow = 1; (i = iReadData(fpin, pLnBuf, iBufSize,
&pDataBuf[iRow * iCol]));
++iRow) {
if( i != iCol ) {
printf("%d: Inconsistent number of columns, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
}
/* Test for table too small.
*/
if( (iRow < 5 ) ||( iCol < 5 )) {
printf("%d: The table is too small, pan1ptbl aborted.\n", s_iLnPos);
return( FALSE );
}
/* Look for a square table.
*/
if( bExpectSquare && (iRow != iCol )) {
printf("%d: Expected a square table( #row = #col ), pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
/* Test 'any' penalties. They should all equal zero.
*/
for (i = 1, j = iCol;
( i < j ) &&( (i == 2 ) ||( pDataBuf[iCol + i] == 0 ));
++i)
;
if( i >= j ) {
for (i = 1, j = iRow;
( i < j ) &&( (i == 2 ) ||( pDataBuf[(iCol * i ) + 1] == 0));
++i)
;
}
if( i < j ) {
printf("%d: All 'any' penalties should be zero, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
/* Test 'no-fit' penalties. They should all have the same value.
*/
iNoFit = pDataBuf[iCol + 2];
for (i = 1, j = iCol;
( i < j ) &&( pDataBuf[(iCol * 2 ) + i] ==( EW_BYTE )iNoFit);
++i)
;
if( i >= j ) {
for (i = 1, j = iRow;
( i < j ) &&( pDataBuf[(iCol * i ) + 2] ==( EW_BYTE )iNoFit);
++i)
;
}
if( i < j ) {
printf("%d: All 'no-fit' penalties should match, pan1ptbl aborted.\n",
s_iLnPos);
return( FALSE );
}
/* If we're just scanning, then we're done collecting info.
*/
if( !bBuildIt || !pPTbl ) {
return( TRUE );
}
/* Init table.
*/
pPTbl->jRangeLast =( (iRow > iCol ) ? iRow : iCol) - 2;
pPTbl->unOffsTbl = 0;
pPTbl->unTblSize = 0;
k = 0;
/* Determine the compression type, and then copy the table
* based upon the type.
*/
switch (pPTbl->jCompress =
( EW_BYTE )iGetCompressKind(pDataBuf, iRow, iCol, iNoFit)) {
case PAN_COMPRESS_C0:
/* C0 compression: suck in the whole table except
* the any and no-fit values. This table is preceded
* by a header containing the row and column dimensions.
*/
{
EW_PTBL_C0_MEM *pPC0 =( EW_PTBL_C0_MEM * )&pDataBuf[k];
pPC0->jARangeLast =( EW_BYTE )iRow - 2;
pPC0->jBRangeLast =( EW_BYTE )iCol - 2;
pPC0->jReserved = 0;
k +=( sizeof(EW_PTBL_C0_MEM ) - sizeof(EW_BYTE));
}
for( i = 3; i < iRow; ++i ) {
for( j = 3; j < iCol; ++j, ++k ) {
pDataBuf[k] = pDataBuf[(i * iCol) + j];
}
}
bAddUniqueTable(pPTbl, k);
break;
case PAN_COMPRESS_C1:
/* C1 compression: perfectly symmetrical table with
* penalties increasing the further they are away
* from the diagonal. No additional data stored.
*/
break;
case PAN_COMPRESS_C3:
/* C3 compression: same as C2 compression except the
* first byte is the no-fit penalty.
*/
pDataBuf[k++] = iNoFit;
case PAN_COMPRESS_C2:
/* C2 compression: symmetrical about the diagonal,
* suck in the lower left corner.
*/
for( i = 4; i < iRow; ++i ) {
for( j = 3; j < i; ++j, ++k ) {
pDataBuf[k] = pDataBuf[(i * iCol) + j];
}
}
bAddUniqueTable(pPTbl, k);
break;
case PAN_COMPRESS_C4:
/* C4 compression: similar to C1, except the start
* and increment values are specified.
*/
{
EW_PTBL_C4_MEM *pPC4 =( EW_PTBL_C4_MEM * )&pDataBuf[k];
pPC4->jStart = pDataBuf[(iCol * 4) + 3];
pPC4->jIncrement = pDataBuf[(iCol * 5) + 3] - pPC4->jStart;
k += sizeof(EW_PTBL_C4_MEM);
}
bAddUniqueTable(pPTbl, k);
break;
}
return( TRUE );
}
/***************************************************************************
* FUNCTION: iGetCompressKind
*
* PURPOSE: Examine the data table and determine what kind, if any, of
* data compression can be used.
*
* Notice the tables have an extra row and column for the
* headings. The minimum table size should be 5 x 5, which
* is really a 4 x 4 table (the parse loop catches anything
* smaller and aborts).
*
* RETURNS: The function returns the compression id.
***************************************************************************/
static int iGetCompressKind(
EW_BYTE *pDataBuf,
int iRow,
int iCol,
int iNoFit)
{
int i;
int j;
/* All compression mechanisms require a square table.
*/
if( (iRow != iCol ) ||( iCol < 5 )) {
return( PAN_COMPRESS_C0 );
}
/* Test for symmetry around the diagonal.
*/
for( i = 3; i < iCol; ++i ) {
/* The diagonal( exact match value ) must be zero.
*/
if( pDataBuf[(iCol * i ) + i] != 0) {
return( PAN_COMPRESS_C0 );
}
/* Test for value at( i,j ) ==( j,i ).
*/
for( j = i + 1; (j < iCol ); ++j) {
if( pDataBuf[(iCol * i ) + j] != pDataBuf[(iCol * j) + i]) {
return( PAN_COMPRESS_C0 );
}
}
}
/* The table is symmetrical, now walk it looking for a special
* pattern: it starts at a given value and increments by a given
* value. If we find this, then we just store the start and
* increment values.
*
* The table must be atleast 4 x 4 to try this test. Note if
* it is exactly 4 x 4 and the one value in the table is a 1,
* then C1 is the most compact way to store it (the 'else'
* statement below checks for that). Otherwise C2 or C3 is
* the best.
*/
if( (iCol > 5 ) &&( iNoFit == 10 )) {
EW_BOOL bFits = TRUE;
EW_BYTE jStart = pDataBuf[(iCol * 4) + 3];
EW_BYTE jInc = pDataBuf[(iCol * 5) + 3] - jStart;
for( i = 5; (i < iCol ) && bFits; ++i) {
for( j = 3; (j < i ) && bFits; ++j) {
bFits =( pDataBuf[(iCol * i ) + j] ==
( EW_BYTE )(((EW_BYTE)(i - j - 1) * jInc) + jStart));
}
}
/* C1 compression implies( start, inc ) ==( 1, 1 ),
* C4 stores the start and inc values.
*/
if( bFits ) {
return( ((jStart == 1 ) &&( jInc == 1 )) ?
PAN_COMPRESS_C1 : PAN_COMPRESS_C4);
}
} else if( (pDataBuf[(iCol * 4 ) + 3] == 1) &&( iNoFit == 10 )) {
return( PAN_COMPRESS_C1 );
}
/* The table is symmetrical about the diagonal, but there
* is no recognized pattern within it, so return C2 or C3
* compression (C3 is C2 compression with a specifier for
* the 'no-fit' value).
*/
return( (iNoFit == 10 ) ? PAN_COMPRESS_C2 : PAN_COMPRESS_C3);
}
/***************************************************************************
* FUNCTION: bAddUniqueTable
*
* PURPOSE: Add a unique table to the accumulated penalty database, if
* a table matching this table's data already exists, then re-use
* that data.
*
* Upon entry to this routine, s_unOffs should point to the
* data that is about to be added. This func walks all the
* tables that have already been added looking for duplicate
* data.
*
* This function should be called when we're building the
* database( second pass ).
*
* RETURNS: The function returns TRUE if a new table was added, or FALSE
* if an existing table was re-used.
***************************************************************************/
static EW_BOOL bAddUniqueTable(
EW_PTBL_MEM *pPTbl,
int iSize)
{
int i;
int j;
int iNumTbl;
EW_PDICT_MEM *pHead =( EW_PDICT_MEM * )s_buf;
EW_PIND_MEM *pInd = pHead->pind;
EW_PTBL_MEM *pPTblTst;
/* Shortcut for C1 compression: it has no data.
*/
if( !iSize ) {
return( FALSE );
}
/* This walk is kind of tricky: we are walking a penalty
* database that is in the process of being created. Some
* fields are valid, some are not.
*
* For each dictionary.
*/
for( i = 0; i < (int )pHead->unNumDicts; ++i, ++pInd) {
/* If the ptbl offset is NULL, then we've reached the
* end of the filled-in dictionaries.
*/
if( !pInd->unOffsPTbl ) {
break;
}
/* Get the count of tables.
*/
if( pInd->unOffsAtoB ) {
iNumTbl =( int )((EW_ATOB_MEM *)&s_buf[pInd->unOffsAtoB])->unNumAtoB;
} else {
iNumTbl = NUM_PAN_DIGITS - 1;
}
/* For each penalty table.
*/
for( j = 0, pPTblTst = (EW_PTBL_MEM * )&s_buf[pInd->unOffsPTbl];
j < iNumTbl;
++j, ++pPTblTst) {
/* The last filled-in table should be the one that
* was passed in.
*/
if( (pPTblTst >= pPTbl ) || !pPTblTst->jRangeLast) {
break;
}
/* Look for matching data, and return if it is found.
*/
if( (pPTblTst->unOffsTbl > 0 ) &&
( pPTbl->jCompress == pPTblTst->jCompress ) &&
( iSize == (int )pPTblTst->unTblSize) &&
(memcmp(&s_buf[s_unOffs],
&s_buf[pPTblTst->unOffsTbl], iSize) == 0)) {
pPTbl->unTblSize = iSize;
pPTbl->unOffsTbl = pPTblTst->unOffsTbl;
return( FALSE );
}
}
}
/* No duplicate was found, keep the structure that is currently
* in the buffer.
*/
pPTbl->unOffsTbl = s_unOffs;
s_unOffs += pPTbl->unTblSize = iSize;
return( TRUE );
}
/***************************************************************************
* FUNCTION: iReadData
*
* PURPOSE: Read an array of numbers from file.
*
* RETURNS: The function returns the count of numbers it read from the
* line.
***************************************************************************/
static int iReadData(
FILE *fpin,
char *pLnBuf,
int iBufSize,
EW_BYTE *pDataBuf)
{
int i;
int j = 0;
/* Get the test line.
*/
if( !bGetLine(pLnBuf, iBufSize, fpin )) {
return( 0 );
}
/* For each number.
*/
while( TRUE ) {
/* Walk white space.
*/
for( ; *pLnBuf && ((*pLnBuf == ' ' ) ||( *pLnBuf == '\t' )); ++pLnBuf)
;
/* Stop if not at a number.
*/
if( !*pLnBuf || (*pLnBuf < '0' ) ||( *pLnBuf > '9' )) {
break;
}
/* Read the number.
*/
for( i = 0; *pLnBuf && (*pLnBuf >= '0' ) &&( *pLnBuf <= '9' );
++pLnBuf) {
i =( i * 10 ) +( *pLnBuf - '0' );
}
*pDataBuf++ = i;
++j;
}
/* Sanity check. At this point the buffer should be empty,
* the end of the line, or the start of a comment.
*/
if( *pLnBuf && (*pLnBuf >= ' ' ) &&( *pLnBuf != ';' )) {
return( 0 );
}
return( j );
}
/***************************************************************************
* FUNCTION: bGetLine
*
* PURPOSE: Read a line from file. Skip blank and comment lines.
*
* RETURNS: The function returns TRUE if it finds a line, FALSE if it
* does not.
***************************************************************************/
static EW_BOOL bGetLine(
char *pBuf,
int iLen,
FILE *fpin)
{
int i;
char ch;
char *p;
#define M_EOL(ch)( ((ch ) == '\r') ||( (ch ) == '\n'))
do {
/* First read through end of line characters.
*/
while( ((ch = fgetc(fpin )) != EOF) && M_EOL(ch)) {
if( ch == '\n' ) {
++s_iLnPos;
}
}
if( ch == EOF ) {
return( FALSE );
}
/* Read the line.
*/
for (i = 1, p = pBuf, *p++ = ch;
( i < iLen ) &&( (*p = fgetc(fpin )) != EOF) && !M_EOL(*p);
++i, ++p)
;
*p = '\0';
/* Bump the line counter.
*/
++s_iLnPos;
/* Skip this line if it begins with a comment character.
*/
} while( pBuf[0] == ';' );
return( TRUE );
}
/***************************************************************************
* FUNCTION: vShowDB
*
* PURPOSE: Dump the database to stdout.
*
* RETURNS: Nothing.
***************************************************************************/
static void vShowDB()
{
int i;
int j;
int k;
int m;
int n;
int iNumPTbl;
EW_PDICT_MEM *pHead =( EW_PDICT_MEM * )s_buf;
EW_PIND_MEM *pInd = pHead->pind;
EW_BYTE *pWt;
EW_ATOB_MEM *pAtoB;
EW_ATOB_ITEM_MEM *pAtoBItem;
EW_PTBL_MEM *pPTbl;
EW_BYTE *pData;
printf("\n\nDATABASE DUMP\n");
printf("~~~~~~~~~~~~~\n");
printf("vers = 0x%04x\n", pHead->unVersion);
printf("num dicts = %u\n", pHead->unNumDicts);
printf("size db = %u\n", pHead->unSizeDB);
for( i = 0; i < (int )pHead->unNumDicts; ++i, ++pInd) {
printf("\n=======================================================\n");
printf("0x%04x:Index entry:\n",
( (unsigned )(char *)pInd -( unsigned )(char *)pHead));
printf("family row = %u\n",( EW_USHORT )pInd->jFamilyA);
printf("family col = %u\n",( EW_USHORT )pInd->jFamilyB);
printf("any = %u\n",( EW_USHORT )pInd->jDefAnyPenalty);
printf("no-fit = %u\n",( EW_USHORT )pInd->jDefNoFitPenalty);
printf("match = %u\n",( EW_USHORT )pInd->jDefMatchPenalty);
printf("reserved = %u\n",( EW_USHORT )pInd->jReserved);
printf("offs wt = 0x%04x\n", pInd->unOffsWts);
printf("offs atob = 0x%04x\n", pInd->unOffsAtoB);
printf("offs ptbl = 0x%04x\n", pInd->unOffsPTbl);
if( pInd->unOffsWts ) {
pWt =( EW_BYTE * )&s_buf[pInd->unOffsWts];
printf("\n0x%04x:weights:", pInd->unOffsWts);
for( j = 0; j < NUM_PAN_DIGITS; ++j, ++pWt ) {
printf(" %u",( EW_USHORT )*pWt);
}
printf("\n");
} else {
printf("\nERROR: NO WEIGHTS\n");
}
if( pInd->unOffsAtoB ) {
pAtoB =( EW_ATOB_MEM * )&s_buf[pInd->unOffsAtoB];
pAtoBItem = pAtoB->AtoBItem;
printf("\n0x%04x:AtoB:\n", pInd->unOffsAtoB);
printf("count = %u\n", pAtoB->unNumAtoB);
for( j = 0; j < (int )pAtoB->unNumAtoB; ++j, ++pAtoBItem) {
printf("pair = %u, %u\n",( EW_USHORT )pAtoBItem->jAttrA,
( EW_USHORT )pAtoBItem->jAttrB);
}
iNumPTbl = pAtoB->unNumAtoB;
} else {
iNumPTbl = NUM_PAN_DIGITS - 1;
}
if( pInd->unOffsPTbl && (iNumPTbl > 0 )) {
pPTbl =( EW_PTBL_MEM * )&s_buf[pInd->unOffsPTbl];
for( j = 0; j < iNumPTbl; ++j, ++pPTbl ) {
printf("\n0x%04x:penalty table:\n",
( (unsigned )(char *)pPTbl -( unsigned )(char *)pHead));
printf("rng last = %u\n",( EW_USHORT )pPTbl->jRangeLast);
printf("compress = %u\n",( EW_USHORT )pPTbl->jCompress);
printf("offs tbl = 0x%04x\n", pPTbl->unOffsTbl);
printf("tbl size = %u\n", pPTbl->unTblSize);
if( pPTbl->unOffsTbl && !bShowDupTbl(pPTbl )) {
k = 0;
pData =( EW_BYTE * )&s_buf[pPTbl->unOffsTbl];
switch( pPTbl->jCompress ) {
case PAN_COMPRESS_C0:
{
EW_PTBL_C0_MEM *pPC0 =( EW_PTBL_C0_MEM * )&pData[k];
pData += k =( sizeof(EW_PTBL_C0_MEM ) - sizeof(EW_BYTE));
printf("Non-symmetrical table, row = %u, col = %u, reserved = %u\n",
( EW_USHORT )pPC0->jARangeLast,
( EW_USHORT )pPC0->jBRangeLast,
( EW_USHORT )pPC0->jReserved);
printf(" ");
for( m = 0; m <= (int )pPC0->jBRangeLast; ++m) {
printf(" %2u", m);
}
printf("\n");
for( m = 0; m <= (int )pPC0->jARangeLast; ++m) {
printf("%2u", m);
if( m > 1 ) {
printf(" ");
for( n = 2; (n <= (int )pPC0->jBRangeLast) &&
( k < (int )pPTbl->unTblSize);
++n, ++k, ++pData) {
printf(" %2u",( EW_USHORT )*pData);
}
}
printf("\n");
}
}
break;
case PAN_COMPRESS_C1:
printf("ERROR: TABLE SHOULD BE EMPTY\n");
break;
case PAN_COMPRESS_C3:
printf("no-fit value = %u\n",( EW_USHORT )*pData);
++pData, ++k;
case PAN_COMPRESS_C2:
printf(" ");
for( m = 0; m <= (int )pPTbl->jRangeLast; ++m) {
printf(" %2u", m);
}
printf("\n");
for( m = 0; m <= (int )pPTbl->jRangeLast; ++m) {
printf("%2u", m);
if( m > 2 ) {
printf(" ");
for( n = 2; (n < m ) &&( k < (int )pPTbl->unTblSize);
++n, ++k, ++pData) {
printf(" %2u",( EW_USHORT )*pData);
}
}
printf("\n");
}
break;
case PAN_COMPRESS_C4:
{
EW_PTBL_C4_MEM *pPC4 =( EW_PTBL_C4_MEM * )&pData[k];
pData += k = sizeof(EW_PTBL_C4_MEM);
printf("start = %u, increment = %u\n",
( EW_USHORT )pPC4->jStart,
( EW_USHORT )pPC4->jIncrement);
}
break;
default:
printf("ERROR: INVALID COMPRESSION TYPE\n");
break;
}
} else if (!pPTbl->unOffsTbl &&
( pPTbl->jCompress != PAN_COMPRESS_C1 )) {
printf("ERROR: NO DATA\n");
}
}
} else {
printf("\nERROR: NO PENALTIES\n");
}
}
printf("\n=======================================================\n");
printf("Raw data dump:\n");
pData =( EW_BYTE * )s_buf;
printf(" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
for( k = 0; k < (int )pHead->unSizeDB; ) {
printf("0x%04x:", k);
for( i = 0; (i < 16 ) &&( k < (int )pHead->unSizeDB);
++i, ++k, ++pData) {
printf(" %02x",( EW_USHORT )*pData);
}
printf("\n");
}
}
/***************************************************************************
* FUNCTION: bShowDupTbl
*
* PURPOSE: Search for a duplicate table and don't show it twice, instead
* indicate it is a duplicate table.
*
* RETURNS: The function returns TRUE if this is, indeed, a duplicate
* table, or FALSE if it is not.
***************************************************************************/
static EW_BOOL bShowDupTbl(
EW_PTBL_MEM *pPTbl)
{
int i;
int j;
int iNumTbl;
EW_PDICT_MEM *pHead =( EW_PDICT_MEM * )s_buf;
EW_PIND_MEM *pInd = pHead->pind;
EW_PTBL_MEM *pPTblTst;
/* Quick test for C1 compression.
*/
if( !pPTbl->unTblSize || !pPTbl->unOffsTbl ) {
return( FALSE );
}
/* For each dictionary.
*/
for( i = 0; i < (int )pHead->unNumDicts; ++i, ++pInd) {
/* Get the count of tables.
*/
if( pInd->unOffsAtoB ) {
iNumTbl =( int )((EW_ATOB_MEM *)&s_buf[pInd->unOffsAtoB])->unNumAtoB;
} else {
iNumTbl = NUM_PAN_DIGITS - 1;
}
/* For each penalty table.
*/
for( j = 0, pPTblTst = (EW_PTBL_MEM * )&s_buf[pInd->unOffsPTbl];
j < iNumTbl;
++j, ++pPTblTst) {
/* Walk up to the current table.
*/
if( pPTblTst >= pPTbl ) {
break;
}
/* Look for matching data, and return if it is found.
*/
if( (pPTblTst->unOffsTbl > 0 ) &&
( pPTbl->jRangeLast == pPTblTst->jRangeLast ) &&
( pPTbl->jCompress == pPTblTst->jCompress ) &&
( pPTbl->unTblSize == pPTblTst->unTblSize ) &&
( pPTbl->unOffsTbl == pPTblTst->unOffsTbl )) {
printf("The data has already dumped( duplicate table ).\n");
return( TRUE );
}
}
}
/* No duplicate found.
*/
return( FALSE );
}
/***************************************************************************
* Revision log:
***************************************************************************/
/*
* $lgb$
* 1.0 31-Jan-93 msd PANOSE 1.0 penalties database, textual version.
* 1.1 31-Jan-93 msd Modified the way a file is written if we know we're checking it in and out of vcs.
* 1.2 1-Feb-93 msd Fixed a bug with the vcs handling stuff.
* 1.3 3-Feb-93 msd Removed ctrl-z at EOF. Ifdef'd in Mac code to get this to build as an MPW tool. Ifdef'd out system calls on the Mac.
* 1.4 3-Feb-93 msd Added generic line-read routine that can handle both mac- and pc-format test files.
* 1.5 6-Feb-93 msd Init reserved bye in C0 penalty header.
* 1.6 18-Feb-93 msd Implemented binary file writing, init penalty table byte-ordering variable, and C4 ptbl compression( new version of ptbl ). Identical tables are not repeated.
* 1.7 26-Feb-93 msd Don't abort on weight values of zero( just warn ). Also repaired line counter.
* 1.8 1-Apr-93 msd Added _cdecl keyword to main().
* 1.9 15-Apr-94 jasons Removed cdecl on main for Mac.
* $lge$
*/