/* ========================================================================== * * CATABLE: generate TABLES that show the contents of Frame's CATALOGS * * This program reads a MIF document from standard input. It locates the * various "catalog" lists, for example the , , * and . It extracts selected data * from a catalog and formats that data as Frame Table. All the tables are * written as a MIF file to standard output. * * The whole process is driven by static arrays. You can modify these to * create different or additional tables. * * To use the program: * Create or select a template file that you want to document. * Save it as MIF. * Pipe the MIF to this program. * Save the output in a file of type ".mif" * Under Frame, import this program's output into the template. * * A number of tables will appear in the template. The contents of these * tables display the properties of the catalogs in the template file. You * see and compare the different paragraph tags, font tags, variables and * so forth, all at once. * * CATABLE is useful in itself. Its main point, though, is to show the use of * MIFFEd to manipulate and generate MIF. And you can extend the program * to generate new or different tables. * * ========================================================================= */ #include #include #include /* for nint() -- don't forget to put -lm *after* catable.o */ #include "miffed.h" /* ========================================================================== * * How the output tables are specified: * * Each output table is specified by the static definition of a TABLE_DEF * structure and some COLUMN_DEF structures. One TABLE_DEF describes one * table to be produced. It contains a COLUMN_DEF for each column. Each * COLUMN_DEF tells how to generate the cells of one column. * * Here is one of the tables included in the program now: * * Table 1: Principal Font Properties * -------------------------------------------------------------------- * Font Tag | Family | Size | Angle | Weight | Color | Spread * -------------------------------------------------------------------- * side_head | Helvetica | 14pt | Regular | Regular | Black | 0.0 % * emphasis | As-is | As-is| Italic | As-is | As-is | As-is * * This table is generated from the Frame list. Each * ROW in the table is generated from one of the lists within * the . Each CELL is produced from a specific list within * the , for example, a cell in the first column reflects the value * of one list within one list. * * The title text, number of columns, and other global table features are * established in the TABLE_DEF. The width of each column and the method * of generating the cell entries are specified in the COLUMN_DEFs. The * following data declarations are used to set this up. * * ==========================================================================*/ /* ========================================================================== * * Each cell in a table is based on the value of one MIFFEd list. That value * can be "treated" in different ways. The treatment for each column is * specified in its COLUMN_DEF. This enum specifies the available treatments. * See the "treat()" function later for comments on each treatment * You can feel free to add more! * * ==========================================================================*/ enum Treatments { copyText /* just copy the textOf() the item */ , copyInitial /* copy only the initial letter, like "Y"/"N" from Yes/No */ , boolXDash /* item is Yes/No, but copy "X" for Yes and hyphen for No. */ , keywordXlate /* look the value up in a table and copy the translation */ , roundPct /* special for "Spread" where Frame messes up -- see treat() */ }; /* ========================================================================== * * This static list is used to translate long, oddly-capitalized keywords that * appear in Frame catalogs. The list is searched when the "treatment" of a * cell value is keywordXlate. Feel free to add more pairs of words. * * ==========================================================================*/ struct wordPair { char * keyword; char * replacement; } lookupTable[] = { { "FDouble", "Double" }, { "FSingle", "Single" }, { "FNumeric", "Numeric" }, { "FNoUnderlining","None"}, { "FSubscript", "Sub" }, { "FSuperscript", "Super" }, { "FNormal", "normal"}, { "FLowercase", "Lowercase" }, { "FUppercase", "Uppercase" }, { "FSmallCaps", "Small cap" }, { "FAsTyped", "As typed" }, { NULL, NULL } /* list end delimiter */ }; /* ========================================================================== * * The characteristics of each column are specified in one of these structures: * bodyTag: the of the one paragraph in the * headText: text of the column heading paragraph * sourceTag: name of list that supplies values, like "PgfAlignment" or "FWidth" * defaultText: string to be used when no sourceTag is found, like "As-is" * colWidth: width of this column in points * headAngle: rotation angle of head, 0=normal, 270=reads UP, 90=reads DOWN * treatment: how to process a value * * ==========================================================================*/ typedef struct { /* Column definition */ char * bodyTag; /* paragraph tag, NULL==use TABLEDEF bodyTag */ char * headText; /* text of column heading */ char * sourceTag; /* name of list supplying values, e.g. "PgfTag" */ char * defaultText; /* value when sourceTag not found, NULL=none */ short colWidth; /* column width in points */ short headAngle; /* heading rotation, 0/90/180/270 */ enum Treatments treatment; /* how treat found value */ } COLUMN_DEF; /* ========================================================================== * * This structure describes the global properties of a table: * styleTag: name of table catalog style to use -- can be one you designed * titleText: text of table title paragraph if any * titleTag: of the table title paragraph * headTag: to use in all the heading cells * bodyTag: to use in body cells, when COLUMN_DEF does not specify * rowSourceTag: the list that supplies one row of data, e.g. "Pgf" or "Font" * numColumns: how many COLUMN_DEFs follow this TABLE_DEF -- MUST BE CORRECT! * columns: an array of numColumns COLUMN_DEF structures * * ==========================================================================*/ typedef struct { /* Table definition */ char * styleTag; /* name of table catalog tag, NULL=="Format A" */ char * titleText; /* text of table title, NULL==no title */ char * titleTag; /* para tag of title para, NULL=="TableTitle" */ char * headTag; /* para tag of head/footers, NULL="CellHeading" */ char * bodyTag; /* para tag of cells, NULL=="CellBody" */ char * rowSourceTag; /* name of list supplying rows, e.g. "Pgf" */ int numColumns; /* number of COLUMN_DEFs that follow */ COLUMN_DEF *columns; /* definition of columns */ } TABLE_DEF; /* ========================================================================== * * Here is the sample table described earlier, and others. * * ==========================================================================*/ static char szAsIs[]="As-is"; static char szasis[]="as-is"; /* As-is in Times 12 won't fit 36pt cols */ COLUMN_DEF Fonts1Cols[] = { /* columns for table Fonts1 which follows... */ { NULL, /* bodyTag */ "Font Tag", /* headText */ "FTag", /* sourceTag */ NULL, /* defaultText */ 84, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Family", /* headText */ "FFamily", /* sourceTag */ NULL, /* defaultText */ 84, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Size", /* headText */ "FSize", /* sourceTag */ szAsIs, /* defaultText */ 48, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Angle", /* headText */ "FAngle", /* sourceTag */ szAsIs, /* defaultText */ 65, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Weight", /* headText */ "FWeight", /* sourceTag */ szAsIs, /* defaultText */ 65, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Color", /* headText */ "FColor", /* sourceTag */ szAsIs, /* defaultText */ 65, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Spread", /* headText */ "FDW", /* sourceTag */ szAsIs, /* defaultText */ 48, /* colWidth */ 0, /* headAngle */ roundPct } /* treatment */ }; /* end of Fonts1Cols */ TABLE_DEF Fonts1 = { NULL, /* styleTag */ "Principal Character Style Properties", /* titleText */ NULL,NULL,NULL, /* titleTag, headTag, bodyTag default */ "Font", /* sourceTag */ 7, /* numColumns */ &Fonts1Cols[0] }; static COLUMN_DEF Fonts2Cols[] = { /* columns of table Fonts2 which follows */ { NULL, /* bodyTag */ "Font Tag", /* headText */ "FTag", /* sourceTag */ NULL, /* defaultText */ 84, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Underline", /* headText */ "FUnderlining", /* sourceTag */ szAsIs, /* defaultText */ 66, /* colWidth */ 0, /* headAngle */ keywordXlate } /* FDouble|FSingle|FNumeric */ , { NULL, /* bodyTag */ "Overline", /* headText */ "FOverline", /* sourceTag */ szasis, /* defaultText */ 36, /* colWidth */ 270, /* headAngle */ copyInitial } /* treatment */ , { NULL, /* bodyTag */ "Strikethrough", /* headText */ "FStrike", /* sourceTag */ szasis, /* defaultText */ 36, /* colWidth */ 270, /* headAngle */ copyInitial } /* treatment */ , { NULL, /* bodyTag */ "Change Bar", /* headText */ "FChangeBar", /* sourceTag */ szasis, /* defaultText */ 36, /* colWidth */ 270, /* headAngle */ copyInitial } /* treatment */ , { NULL, /* bodyTag */ "Outline", /* headText */ "FOutline", /* sourceTag */ szasis, /* defaultText */ 36, /* colWidth */ 270, /* headAngle */ copyInitial } /* treatment */ , { NULL, /* bodyTag */ "Shadow", /* headText */ "FShadow", /* sourceTag */ szasis, /* defaultText */ 36, /* colWidth */ 270, /* headAngle */ copyInitial } /* treatment */ , { NULL, /* bodyTag */ "Position", /* headText */ "FPosition", /* sourceTag */ szAsIs, /* defaultText */ 66, /* colWidth */ 0, /* headAngle */ keywordXlate } /* FSubscript|FSuperscript|FNormal */ , { NULL, /* bodyTag */ "Letter Case", /* headText */ "FCase", /* sourceTag */ szAsIs, /* defaultText */ 66, /* colWidth */ 0, /* headAngle */ keywordXlate } /* FLowercase|FSmallCaps|FUppercase */ , { NULL, /* bodyTag */ "Pair Kern", /* headText */ "FPairKern", /* sourceTag */ szasis, /* defaultText */ 36, /* colWidth */ 270, /* headAngle */ copyInitial } /* treatment */ }; /* end of Fonts2Cols */ TABLE_DEF Fonts2 = { NULL, /* styleTag */ "Character Style Features", /* titleText */ NULL,NULL,NULL, /* titleTag, headTag, bodyTag default */ "Font", /* row sourceTag */ 10, /* numColumns */ &Fonts2Cols[0] }; COLUMN_DEF VarColumns[] = { { NULL, /* bodyTag */ "Variable Name", /* headText */ "VariableName", /* sourceTag */ NULL, /* defaultText */ 144, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Variable Definition", /* headText */ "VariableDef", /* sourceTag */ NULL, /* defaultText */ 324, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ }; /* end of VarColumns */ TABLE_DEF Variables = { NULL, /* styleTag */ "Variable Definitions", /* titleText */ NULL,NULL,NULL, /* titleTag, headTag, bodyTag default */ "VariableFormat", /* row sourceTag */ 2, /* numColumns */ &VarColumns[0] }; COLUMN_DEF XRColumns[] = { { NULL, /* bodyTag */ "Cross-Reference Name", /* headText */ "XRefName", /* sourceTag */ NULL, /* defaultText */ 144, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ , { NULL, /* bodyTag */ "Cross-Reference Definition", /* headText */ "XRefDef", /* sourceTag */ NULL, /* defaultText */ 324, /* colWidth */ 0, /* headAngle */ copyText } /* treatment */ }; /* end of VarColumns */ TABLE_DEF XRefs = { NULL, /* styleTag */ "Cross-Reference Definitions", /* titleText */ NULL,NULL,NULL, /* titleTag, headTag, bodyTag default */ "XRefFormat", /* row sourceTag */ 2, /* numColumns */ &XRColumns[0] }; /* ========================================================================== * * The following static table drives the main() function. Each top-level * list in the input file is looked up in this table, and used to generate * in the output each time it appears. Each table defined in the * preceding section should appear here once. * * ==========================================================================*/ struct tableCatPair { char * catname; TABLE_DEF *tblToDo; } masterWorkList[] = { {"FontCatalog", &Fonts1}, {"FontCatalog", &Fonts2}, {"VariableFormats",&Variables}, {"XRefFormats",&XRefs}, { NULL, NULL } /* required end of list */ }; /* ========================================================================== * This function produces a one-string given a text string and a tag. * Note if the string is null, the Para contains > > * ==========================================================================*/ itemHandle oneLinePara(char *pgfTag, char *text) { if (!text) text = ""; /* don't pass null address to patToMIF */ return patToMIF(">",pgfTag,text); } /* ========================================================================== * This function takes a text string and a pgf tag and produces a * whose value is a one-string : * > > * ==========================================================================*/ itemHandle oneLineCell(char *pgfTag, char *text) { itemHandle hCell = newList("Cell"); itemHandle hCont = newList("CellContent"); append(hCont,oneLinePara(pgfTag,text)); append(hCell,hCont); return hCell; } /* ========================================================================== * This function initializes a from a TABLE_DEF and an ID number: * ...> * ==========================================================================*/ itemHandle tableStart(TABLE_DEF *TDef, int tblID) { int j; char *tbStyleTag; itemHandle hTbl = patToMIF(">",tblID); tbStyleTag = TDef->styleTag; if (NULL==tbStyleTag) tbStyleTag = "Format A"; append(hTbl,patToMIF("",tbStyleTag)); append(hTbl,patToMIF("",TDef->numColumns)); for(j = 0; j < TDef->numColumns; ++j) append(hTbl, patToMIF("",TDef->columns[j].colWidth) ); return hTbl; } /* ========================================================================== * This function generates the table title given the TABLE_DEF: * > * Uses oneLinePara(). Should not be called if titleText is null. * ==========================================================================*/ itemHandle tableTitle(TABLE_DEF *TDef) { itemHandle hTT = newList("TblTitleContent"); char * tTag = TDef->titleTag; if (NULL == tTag) tTag = "TableTitle"; /* default para tag */ append(hTT, oneLinePara(tTag, TDef->titleText) ); return hTT; } /* ========================================================================== * This function generates the heading row from the TABLE_DEF and COLUMN_DEFs: * ...> >. Uses oneLineCell(), but also has to insert * a when the heading is rotated. * ==========================================================================*/ itemHandle headRow(TABLE_DEF *TDef) { itemHandle hTH = newList("TblH"); itemHandle hRow = newList("Row"); char * cellTag = TDef->headTag; int j; if (NULL==cellTag) cellTag = "CellHeading"; /* default heading para tag */ for(j=0; jnumColumns; ++j) { itemHandle hCell = oneLineCell(cellTag, TDef->columns[j].headText); if (TDef->columns[j].headAngle) insert(hCell,patToMIF("",TDef->columns[j].headAngle)); append(hRow,hCell); } append(hTH,hRow); return hTH; } /* ========================================================================== * This function receives a one-item list -- for example , * , or -- and also receives one of the * enum Treatments values. * * It locates the text value of the first item of the list, "treats" it in the * requested way, and returns the address of the treated text. * * There are no doubt lots of other interesting "treatments" that are needed. * Feel free to add some. * ==========================================================================*/ static char treatedText[64]; char * treat(itemHandle hAttribute, enum Treatments code) { itemHandle hValue = nextItem(hAttribute); char * pText = textOf(hValue); switch(code) { case copyText: { return pText; } case copyInitial: { treatedText[0]=*pText; treatedText[1]=0; return treatedText; } case boolXDash: { treatedText[0]=('Y'==*pText)?'X':'-'; treatedText[1]=0; return treatedText; } case roundPct: { /* * When Frame 4 stores a "spread" percentage it makes some kind of * goof and stores a near-integer, like -1.9989 instead of -2. * Get the float value, round it to an integer, and print that. */ int iVal = nint(floatOf(hValue)); sprintf(treatedText,"%d%%",iVal); return treatedText; } case keywordXlate: { /* search for *pText in the keyword lookupTable, return replacement */ struct wordPair * pWP; for(pWP = lookupTable; (pWP->keyword); ++pWP) { if (0==strcmp(pText,pWP->keyword)) return pWP->replacement; } return "unknown keyword"; } default: return "?untreated"; } } /* ========================================================================== * Here's the beef! This is the function that takes a catalog entry and a set * of COLUMN_DEFs, and generates one of data. In pseudocode: * for each column: * Search the catalog entry for this column's source, e.g. look in * this for a . * If found, "treat" the first value in the source list as specified * to produce a string * Else select the default string if any * Use oneLineCell() to generate a cell and append it to the row * end for * ==========================================================================*/ itemHandle bodyRow(TABLE_DEF *TDef, itemHandle catItem) { int j; itemHandle hRow = newList("Row"); char *dfltTag = (TDef->bodyTag)?TDef->bodyTag:"CellBody"; char *pgfTag; char *treatedValue; itemHandle hSrc; COLUMN_DEF *pCDef; for(j = 0; j < TDef->numColumns; ++j) { pCDef = &TDef->columns[j]; pgfTag = (pCDef->bodyTag)?pCDef->bodyTag:dfltTag; hSrc = firstOfName(catItem,pCDef->sourceTag); if (hSrc) /* found it */ treatedValue = treat(hSrc, pCDef->treatment); else treatedValue = pCDef->defaultText; append(hRow,oneLineCell(pgfTag,treatedValue)); } return hRow; } /* ========================================================================== * This function takes a TABLE_DEF, a table ID number, and a Catalog list, * and produces an entire from it. It uses all the preceding funcs. * We are getting close to the main() action, now! * ==========================================================================*/ itemHandle oneTable(TABLE_DEF *TDef, int tblID, itemHandle catalog) { itemHandle catItem; itemHandle hBody; /* start with the basic */ itemHandle hTbl = tableStart(TDef,tblID); /* if a table title is wanted, generate it */ if (TDef->titleText) append(hTbl,tableTitle(TDef)); /* generate the > heading row */ append(hTbl,headRow(TDef)); /* generate the body consisting of one row for each rowSourceTag found */ hBody = newList("TblBody"); for(catItem = firstOfName(catalog,TDef->rowSourceTag); (catItem); catItem = nextSameName(catItem) ) { append(hBody, bodyRow(TDef, catItem) ); } /* add the body to the */ append(hTbl,hBody); return hTbl; } /* ========================================================================== * At last, main(). * create the list into which each will be appended. * create the into which each will be appended. * loop over each top-level list in standard input: * test the list-name against the masterWorkList. * wherever it appears, * generate a table and append to * generate a para referencing the table and append to * write a MIF file consisting of ,, * ==========================================================================*/ int main () { struct tableCatPair *tcp; /* scans elements of masterWorkList */ int tblID = 1; /* table ID number, increments each */ itemHandle oneList; /* current input list */ itemHandle hTbls = newList("Tbls"); /* */ itemHandle hTFlow = newList("TextFlow"); /* */ char *parapat = ">>"; for ( oneList = readOneItem(stdin,0); (oneList); oneList = readOneItem(stdin,0) ) { for(tcp = masterWorkList; (tcp->catname); ++tcp) { if (0==strcmp(tcp->catname,textOf(oneList))) { append(hTbls,oneTable(tcp->tblToDo,tblID,oneList)); append(hTFlow, patToMIF(parapat,tblID)); ++tblID; } } trashSequence(oneList); } printf("# from CATABLE\n"); writeMIF(stdout,hTbls,OUT_EOL+OUT_INDENT); writeMIF(stdout,hTFlow,OUT_EOL+OUT_INDENT); return 0; }