Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * prop2.c
 * Copyright (C) 2002-2005 A.J. van Os; Released under GPL
 *
 * Description:
 * Read the property information from a WinWord 1 or 2 file
 */

#include <string.h>
#include "antiword.h"


#define MAX_FILESIZE            0x2000000UL     /* 32 Mb */

/*
 * iGet2InfoLength - the length of the information for WinWord 1/2 files
 */
static int
iGet2InfoLength(int iByteNbr, const UCHAR *aucGrpprl)
{
        int     iTmp, iDel, iAdd;

        switch (ucGetByte(iByteNbr, aucGrpprl)) {
        case   3: case  15: case  78: case 152: case 154: case 155:
                return 2 + (int)ucGetByte(iByteNbr + 1, aucGrpprl);
        case  16: case  17: case  18: case  19: case  21: case  22: case  26:
        case  27: case  28: case  30: case  31: case  32: case  33: case  34:
        case  35: case  36: case  38: case  39: case  40: case  41: case  42:
        case  43: case  45: case  46: case  47: case  48: case  49: case  68:
        case  71: case  72: case  82: case  83: case  96: case  97: case  98:
        case  99: case 115: case 116: case 119: case 120: case 123: case 124:
        case 129: case 130: case 131: case 132: case 135: case 136: case 139:
        case 140: case 141: case 142: case 143: case 144: case 145: case 146:
        case 147: case 148: case 153: case 159: case 161: case 162:
                return 1 + 2;
        case  23:
                iTmp = (int)ucGetByte(iByteNbr + 1, aucGrpprl);
                if (iTmp == 255) {
                        iDel = (int)ucGetByte(iByteNbr + 2, aucGrpprl);
                        iAdd = (int)ucGetByte(
                                        iByteNbr + 3 + iDel * 4, aucGrpprl);
                        iTmp = 2 + iDel * 4 + iAdd * 3;
                }
                return 2 + iTmp;
        case  70:
                return 1 + 3;
        case  95:
                return 1 + 13;
        case 157: case 163:
                return 1 + 5;
        case 158: case 160: case 164:
                return 1 + 4;
        default:
                return 1 + 1;
        }
} /* end of iGet2InfoLength */

/*
 * Build the lists with Document Property Information for WinWord 1/2 files
 */
void
vGet2DopInfo(FILE *pFile, const UCHAR *aucHeader)
{
        document_block_type     tDocument;
        UCHAR   *aucBuffer;
        ULONG   ulBeginDocpInfo, ulTmp;
        size_t  tDocpInfoLen;
        USHORT  usTmp;

        ulBeginDocpInfo = ulGetLong(0x112, aucHeader); /* fcDop */
        DBG_HEX(ulBeginDocpInfo);
        tDocpInfoLen = (size_t)usGetWord(0x116, aucHeader); /* cbDop */
        DBG_DEC(tDocpInfoLen);
        if (tDocpInfoLen < 28) {
                DBG_MSG("No Document information");
                return;
        }

        aucBuffer = xmalloc(tDocpInfoLen);
        if (!bReadBytes(aucBuffer, tDocpInfoLen, ulBeginDocpInfo, pFile)) {
                aucBuffer = xfree(aucBuffer);
                return;
        }

        usTmp = usGetWord(0x00, aucBuffer);
        tDocument.ucHdrFtrSpecification = (UCHAR)(usTmp >> 8); /* grpfIhdt */
        tDocument.usDefaultTabWidth = usGetWord(0x0a, aucBuffer); /* dxaTab */
        ulTmp = ulGetLong(0x14, aucBuffer); /* dttmCreated */
        tDocument.tCreateDate = tConvertDTTM(ulTmp);
        ulTmp = ulGetLong(0x18, aucBuffer); /* dttmRevised */
        tDocument.tRevisedDate = tConvertDTTM(ulTmp);
        vCreateDocumentInfoList(&tDocument);

        aucBuffer = xfree(aucBuffer);
} /* end of vGet2DopInfo */

/*
 * Fill the section information block with information
 * from a WinWord 1/2 file.
 */
static void
vGet2SectionInfo(const UCHAR *aucGrpprl, size_t tBytes,
                section_block_type *pSection)
{
        int     iFodoOff, iInfoLen;
        USHORT  usCcol;
        UCHAR   ucTmp;

        fail(aucGrpprl == NULL || pSection == NULL);

        iFodoOff = 0;
        while (tBytes >= (size_t)iFodoOff + 1) {
                switch (ucGetByte(iFodoOff, aucGrpprl)) {
                case 117:       /* bkc */
                        ucTmp = ucGetByte(iFodoOff + 1, aucGrpprl);
                        DBG_DEC(ucTmp);
                        pSection->bNewPage = ucTmp != 0 && ucTmp != 1;
                        break;
                case 119:       /* ccolM1 */
                        usCcol = 1 + usGetWord(iFodoOff + 1, aucGrpprl);
                        DBG_DEC(usCcol);
                        break;
                case 128:       /* grpfIhdt */
                        pSection->ucHdrFtrSpecification =
                                        ucGetByte(iFodoOff + 1, aucGrpprl);
                        break;
                default:
                        break;
                }
                iInfoLen = iGet2InfoLength(iFodoOff, aucGrpprl);
                fail(iInfoLen <= 0);
                iFodoOff += iInfoLen;
        }
} /* end of vGet2SectionInfo */

/*
 * Build the lists with Section Property Information for WinWord 1/2 files
 */
void
vGet2SepInfo(FILE *pFile, const UCHAR *aucHeader)
{
        section_block_type      tSection;
        ULONG   *aulSectPage, *aulCharPos;
        UCHAR   *aucBuffer, *aucFpage;
        ULONG   ulBeginOfText, ulTextOffset, ulBeginSectInfo;
        size_t  tSectInfoLen, tIndex, tOffset, tLen, tBytes;
        UCHAR   aucTmp[1];

        fail(pFile == NULL || aucHeader == NULL);

        ulBeginOfText = ulGetLong(0x18, aucHeader); /* fcMin */
        NO_DBG_HEX(ulBeginOfText);
        ulBeginSectInfo = ulGetLong(0x7c, aucHeader); /* fcPlcfsed */
        DBG_HEX(ulBeginSectInfo);
        tSectInfoLen = (size_t)usGetWord(0x80, aucHeader); /* cbPlcfsed */
        DBG_DEC(tSectInfoLen);
        if (tSectInfoLen < 4) {
                DBG_DEC(tSectInfoLen);
                return;
        }

        aucBuffer = xmalloc(tSectInfoLen);
        if (!bReadBytes(aucBuffer, tSectInfoLen, ulBeginSectInfo, pFile)) {
                aucBuffer = xfree(aucBuffer);
                return;
        }
        NO_DBG_PRINT_BLOCK(aucBuffer, tSectInfoLen);

        /* Read the Section Descriptors */
        tLen = (tSectInfoLen - 4) / 10;
        /* Save the section offsets */
        aulCharPos = xcalloc(tLen, sizeof(ULONG));
        for (tIndex = 0, tOffset = 0;
             tIndex < tLen;
             tIndex++, tOffset += 4) {
                ulTextOffset = ulGetLong(tOffset, aucBuffer);
                NO_DBG_HEX(ulTextOffset);
                aulCharPos[tIndex] = ulBeginOfText + ulTextOffset;
                NO_DBG_HEX(aulCharPos[tIndex]);
        }
        /* Save the Sepx offsets */
        aulSectPage = xcalloc(tLen, sizeof(ULONG));
        for (tIndex = 0, tOffset = (tLen + 1) * 4;
             tIndex < tLen;
             tIndex++, tOffset += 6) {
                aulSectPage[tIndex] = ulGetLong(tOffset + 2, aucBuffer);
                NO_DBG_HEX(aulSectPage[tIndex]); /* fcSepx */
        }
        aucBuffer = xfree(aucBuffer);

        /* Read the Section Properties */
        for (tIndex = 0; tIndex < tLen; tIndex++) {
                if (aulSectPage[tIndex] == FC_INVALID) {
                        vDefault2SectionInfoList(aulCharPos[tIndex]);
                        continue;
                }
                /* Get the number of bytes to read */
                if (!bReadBytes(aucTmp, 1, aulSectPage[tIndex], pFile)) {
                        continue;
                }
                tBytes = 1 + (size_t)ucGetByte(0, aucTmp);
                NO_DBG_DEC(tBytes);
                /* Read the bytes */
                aucFpage = xmalloc(tBytes);
                if (!bReadBytes(aucFpage, tBytes, aulSectPage[tIndex], pFile)) {
                        aucFpage = xfree(aucFpage);
                        continue;
                }
                NO_DBG_PRINT_BLOCK(aucFpage, tBytes);
                /* Process the bytes */
                vGetDefaultSection(&tSection);
                vGet2SectionInfo(aucFpage + 1, tBytes - 1, &tSection);
                vAdd2SectionInfoList(&tSection, aulCharPos[tIndex]);
                aucFpage = xfree(aucFpage);
        }
        aulCharPos = xfree(aulCharPos);
        aulSectPage = xfree(aulSectPage);
} /* end of vGet2SepInfo */

/*
 * Build the list with Header/Footer Information for WinWord 1/2 files
 */
void
vGet2HdrFtrInfo(FILE *pFile, const UCHAR *aucHeader)
{
        ULONG   *aulCharPos;
        UCHAR   *aucBuffer;
        ULONG   ulHdrFtrOffset, ulBeginHdrFtrInfo;
        size_t  tHdrFtrInfoLen, tIndex, tOffset, tLen;

        fail(pFile == NULL || aucHeader == NULL);

        ulBeginHdrFtrInfo = ulGetLong(0x9a, aucHeader); /* fcPlcfhdd */
        NO_DBG_HEX(ulBeginHdrFtrInfo);
        tHdrFtrInfoLen = (size_t)usGetWord(0x9e, aucHeader); /* cbPlcfhdd */
        NO_DBG_DEC(tHdrFtrInfoLen);
        if (tHdrFtrInfoLen < 8) {
                DBG_DEC_C(tHdrFtrInfoLen != 0, tHdrFtrInfoLen);
                return;
        }

        aucBuffer = xmalloc(tHdrFtrInfoLen);
        if (!bReadBytes(aucBuffer, tHdrFtrInfoLen, ulBeginHdrFtrInfo, pFile)) {
                aucBuffer = xfree(aucBuffer);
                return;
        }
        NO_DBG_PRINT_BLOCK(aucBuffer, tHdrFtrInfoLen);

        tLen = tHdrFtrInfoLen / 4 - 1;
        /* Save the header/footer offsets */
        aulCharPos = xcalloc(tLen, sizeof(ULONG));
        for (tIndex = 0, tOffset = 0;
             tIndex < tLen;
             tIndex++, tOffset += 4) {
                ulHdrFtrOffset = ulGetLong(tOffset, aucBuffer);
                NO_DBG_HEX(ulHdrFtrOffset);
                aulCharPos[tIndex] = ulHdrFtrOffset2CharPos(ulHdrFtrOffset);
                NO_DBG_HEX(aulCharPos[tIndex]);
        }
        vCreat2HdrFtrInfoList(aulCharPos, tLen);
        aulCharPos = xfree(aulCharPos);
        aucBuffer = xfree(aucBuffer);
} /* end of vGet2HdrFtrInfo */

/*
 * Translate the rowinfo to a member of the row_info enumeration
 */
row_info_enum
eGet2RowInfo(int iFodo,
        const UCHAR *aucGrpprl, int iBytes, row_block_type *pRow)
{
        int     iFodoOff, iInfoLen;
        int     iIndex, iSize, iCol;
        int     iPosCurr, iPosPrev;
        USHORT  usTmp;
        BOOL    bFound24_0, bFound24_1, bFound25_0, bFound25_1, bFound154;

        fail(iFodo < 0 || aucGrpprl == NULL || pRow == NULL);

        iFodoOff = 0;
        bFound24_0 = FALSE;
        bFound24_1 = FALSE;
        bFound25_0 = FALSE;
        bFound25_1 = FALSE;
        bFound154 = FALSE;
        while (iBytes >= iFodoOff + 1) {
                iInfoLen = 0;
                switch (ucGetByte(iFodo + iFodoOff, aucGrpprl)) {
                case  24:       /* fIntable */
                        if (odd(ucGetByte(iFodo + iFodoOff + 1, aucGrpprl))) {
                                bFound24_1 = TRUE;
                        } else {
                                bFound24_0 = TRUE;
                        }
                        break;
                case  25:       /* fTtp */
                        if (odd(ucGetByte(iFodo + iFodoOff + 1, aucGrpprl))) {
                                bFound25_1 = TRUE;
                        } else {
                                bFound25_0 = TRUE;
                        }
                        break;
                case 30:        /* brcTop10 */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x01ff;
                        NO_DBG_DEC(usTmp >> 6);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_TOP;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_TOP;
                        }
                        break;
                case 31:        /* brcLeft10 */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x01ff;
                        NO_DBG_DEC(usTmp >> 6);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_LEFT;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_LEFT;
                        }
                        break;
                case 32:        /* brcBottom10 */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x01ff;
                        NO_DBG_DEC(usTmp >> 6);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_BOTTOM;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_BOTTOM;
                        }
                        break;
                case 33:        /* brcRight10 */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x01ff;
                        NO_DBG_DEC(usTmp >> 6);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_RIGHT;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_RIGHT;
                        }
                        break;
                case 38:        /* brcTop */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x0018;
                        NO_DBG_DEC(usTmp >> 3);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_TOP;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_TOP;
                        }
                        break;
                case 39:        /* brcLeft */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x0018;
                        NO_DBG_DEC(usTmp >> 3);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_LEFT;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_LEFT;
                        }
                        break;
                case 40:        /* brcBottom */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x0018;
                        NO_DBG_DEC(usTmp >> 3);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_BOTTOM;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_BOTTOM;
                        }
                        break;
                case 41:        /* brcRight */
                        usTmp = usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        usTmp &= 0x0018;
                        NO_DBG_DEC(usTmp >> 3);
                        if (usTmp == 0) {
                                pRow->ucBorderInfo &= ~TABLE_BORDER_RIGHT;
                        } else {
                                pRow->ucBorderInfo |= TABLE_BORDER_RIGHT;
                        }
                        break;
                case 152:       /* cDefTable10 */
                case 154:       /* cDefTable */
                        iSize = (int)usGetWord(iFodo + iFodoOff + 1, aucGrpprl);
                        if (iSize < 6 || iBytes < iFodoOff + 7) {
                                DBG_DEC(iSize);
                                DBG_DEC(iBytes);
                                DBG_DEC(iFodoOff);
                                iInfoLen = 1;
                                break;
                        }
                        iCol = (int)ucGetByte(iFodo + iFodoOff + 3, aucGrpprl);
                        if (iCol < 1 ||
                            iBytes < iFodoOff + 3 + (iCol + 1) * 2) {
                                DBG_DEC(iCol);
                                DBG_DEC(iBytes);
                                DBG_DEC(iFodoOff);
                                DBG_DEC(ucGetByte(iFodo + iFodoOff, aucGrpprl));
                                iInfoLen = 1;
                                break;
                        }
                        if (iCol >= (int)elementsof(pRow->asColumnWidth)) {
                                DBG_DEC(iCol);
                                werr(1, "The number of columns is corrupt");
                        }
                        pRow->ucNumberOfColumns = (UCHAR)iCol;
                        iPosPrev = (int)(short)usGetWord(
                                        iFodo + iFodoOff + 4,
                                        aucGrpprl);
                        for (iIndex = 0; iIndex < iCol; iIndex++) {
                                iPosCurr = (int)(short)usGetWord(
                                        iFodo + iFodoOff + 6 + iIndex * 2,
                                        aucGrpprl);
                                pRow->asColumnWidth[iIndex] =
                                                (short)(iPosCurr - iPosPrev);
                                iPosPrev = iPosCurr;
                        }
                        bFound154 = TRUE;
                        break;
                default:
                        break;
                }
                if (iInfoLen <= 0) {
                        iInfoLen =
                                iGet2InfoLength(iFodo + iFodoOff, aucGrpprl);
                        fail(iInfoLen <= 0);
                }
                iFodoOff += iInfoLen;
        }
        if (bFound24_1 && bFound25_1 && bFound154) {
                return found_end_of_row;
        }
        if (bFound24_0 && bFound25_0 && !bFound154) {
                return found_not_end_of_row;
        }
        if (bFound24_1) {
                return found_a_cell;
        }
        if (bFound24_0) {
                return found_not_a_cell;
        }
        return found_nothing;
} /* end of eGet2RowInfo */

/*
 * Fill the style information block with information
 * from a WinWord 1/2 file.
 */
void
vGet2StyleInfo(int iFodo,
        const UCHAR *aucGrpprl, int iBytes, style_block_type *pStyle)
{
        int     iFodoOff, iInfoLen;
        int     iTmp, iDel, iAdd;
        short   sTmp;
        UCHAR   ucTmp;

        fail(iFodo < 0 || aucGrpprl == NULL || pStyle == NULL);

        NO_DBG_DEC(pStyle->usIstd);

        iFodoOff = 0;
        while (iBytes >= iFodoOff + 1) {
                iInfoLen = 0;
                switch (ucGetByte(iFodo + iFodoOff, aucGrpprl)) {
                case   2:       /* istd */
                        sTmp = (short)ucGetByte(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        NO_DBG_DEC(sTmp);
                        break;
                case   5:       /* jc */
                        pStyle->ucAlignment = ucGetByte(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        break;
                case  12:       /* nfcSeqNumb */
                        pStyle->ucNFC = ucGetByte(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        break;
                case  13:       /* nLvlAnm */
                        ucTmp = ucGetByte(iFodo + iFodoOff + 1, aucGrpprl);
                        pStyle->ucNumLevel = ucTmp;
                        pStyle->bNumPause =
                                eGetNumType(ucTmp) == level_type_pause;
                        break;
                case  15:       /* ChgTabsPapx */
                case  23:       /* ChgTabs */
                        iTmp = (int)ucGetByte(iFodo + iFodoOff + 1, aucGrpprl);
                        if (iTmp < 2) {
                                iInfoLen = 1;
                                break;
                        }
                        NO_DBG_DEC(iTmp);
                        iDel = (int)ucGetByte(iFodo + iFodoOff + 2, aucGrpprl);
                        if (iTmp < 2 + 2 * iDel) {
                                iInfoLen = 1;
                                break;
                        }
                        NO_DBG_DEC(iDel);
                        iAdd = (int)ucGetByte(
                                iFodo + iFodoOff + 3 + 2 * iDel, aucGrpprl);
                        if (iTmp < 2 + 2 * iDel + 2 * iAdd) {
                                iInfoLen = 1;
                                break;
                        }
                        NO_DBG_DEC(iAdd);
                        break;
                case  16:       /* dxaRight */
                        pStyle->sRightIndent = (short)usGetWord(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        NO_DBG_DEC(pStyle->sRightIndent);
                        break;
                case  17:       /* dxaLeft */
                        pStyle->sLeftIndent = (short)usGetWord(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        NO_DBG_DEC(pStyle->sLeftIndent);
                        break;
                case  18:       /* Nest dxaLeft */
                        sTmp = (short)usGetWord(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        pStyle->sLeftIndent += sTmp;
                        if (pStyle->sLeftIndent < 0) {
                                pStyle->sLeftIndent = 0;
                        }
                        NO_DBG_DEC(sTmp);
                        NO_DBG_DEC(pStyle->sLeftIndent);
                        break;
                case  19:       /* dxaLeft1 */
                        pStyle->sLeftIndent1 = (short)usGetWord(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        NO_DBG_DEC(pStyle->sLeftIndent1);
                        break;
                case  21:       /* dyaBefore */
                        pStyle->usBeforeIndent = usGetWord(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        NO_DBG_DEC(pStyle->usBeforeIndent);
                        break;
                case  22:       /* dyaAfter */
                        pStyle->usAfterIndent = usGetWord(
                                        iFodo + iFodoOff + 1, aucGrpprl);
                        NO_DBG_DEC(pStyle->usAfterIndent);
                        break;
                default:
                        break;
                }
                if (iInfoLen <= 0) {
                        iInfoLen =
                                iGet2InfoLength(iFodo + iFodoOff, aucGrpprl);
                        fail(iInfoLen <= 0);
                }
                iFodoOff += iInfoLen;
        }
} /* end of vGet2StyleInfo */

/*
 * Build the lists with Paragraph Information for WinWord 1/2 files
 */
void
vGet2PapInfo(FILE *pFile, const UCHAR *aucHeader)
{
        row_block_type          tRow;
        style_block_type        tStyle;
        USHORT  *ausParfPage;
        UCHAR   *aucBuffer;
        ULONG   ulCharPos, ulCharPosFirst, ulCharPosLast;
        ULONG   ulBeginParfInfo;
        size_t  tParfInfoLen, tParfPageNum, tOffset, tSize, tLenOld, tLen;
        int     iIndex, iIndex2, iRun, iFodo, iLen;
        row_info_enum   eRowInfo;
        USHORT  usParfFirstPage, usCount, usIstd;
        UCHAR   ucStc;
        UCHAR   aucFpage[BIG_BLOCK_SIZE];

        fail(pFile == NULL || aucHeader == NULL);

        ulBeginParfInfo = ulGetLong(0xa6, aucHeader); /* fcPlcfbtePapx */
        NO_DBG_HEX(ulBeginParfInfo);
        tParfInfoLen = (size_t)usGetWord(0xaa, aucHeader); /* cbPlcfbtePapx */
        NO_DBG_DEC(tParfInfoLen);
        if (tParfInfoLen < 4) {
                DBG_DEC(tParfInfoLen);
                return;
        }

        aucBuffer = xmalloc(tParfInfoLen);
        if (!bReadBytes(aucBuffer, tParfInfoLen, ulBeginParfInfo, pFile)) {
                aucBuffer = xfree(aucBuffer);
                return;
        }
        NO_DBG_PRINT_BLOCK(aucBuffer, tParfInfoLen);

        tLen = (tParfInfoLen - 4) / 6;
        ausParfPage = xcalloc(tLen, sizeof(USHORT));
        for (iIndex = 0, tOffset = (tLen + 1) * 4;
             iIndex < (int)tLen;
             iIndex++, tOffset += 2) {
                ausParfPage[iIndex] = usGetWord(tOffset, aucBuffer);
                NO_DBG_DEC(ausParfPage[iIndex]);
        }
        DBG_HEX(ulGetLong(0, aucBuffer));
        aucBuffer = xfree(aucBuffer);
        tParfPageNum = (size_t)usGetWord(0x144, aucHeader); /* cpnBtePap */
        DBG_DEC(tParfPageNum);
        if (tLen < tParfPageNum) {
                /* Replace ParfPage by a longer version */
                tLenOld = tLen;
                usParfFirstPage = usGetWord(0x140, aucHeader); /* pnPapFirst */
                DBG_DEC(usParfFirstPage);
                tLen += tParfPageNum - 1;
                tSize = tLen * sizeof(USHORT);
                ausParfPage = xrealloc(ausParfPage, tSize);
                /* Add new values */
                usCount = usParfFirstPage + 1;
                for (iIndex = (int)tLenOld; iIndex < (int)tLen; iIndex++) {
                        ausParfPage[iIndex] = usCount;
                        NO_DBG_DEC(ausParfPage[iIndex]);
                        usCount++;
                }
        }

        (void)memset(&tRow, 0, sizeof(tRow));
        ulCharPosFirst = CP_INVALID;
        for (iIndex = 0; iIndex < (int)tLen; iIndex++) {
                if (!bReadBytes(aucFpage, BIG_BLOCK_SIZE,
                                (ULONG)ausParfPage[iIndex] * BIG_BLOCK_SIZE,
                                pFile)) {
                        break;
                }
                NO_DBG_PRINT_BLOCK(aucFpage, BIG_BLOCK_SIZE);
                iRun = (int)ucGetByte(0x1ff, aucFpage);
                NO_DBG_DEC(iRun);
                for (iIndex2 = 0; iIndex2 < iRun; iIndex2++) {
                        if ((iRun + 1) * 4 + iIndex2 * 1 >= BIG_BLOCK_SIZE) {
                                break;
                        }
                        NO_DBG_HEX(ulGetLong(iIndex2 * 4, aucFpage));
                        iFodo = 2 * (int)ucGetByte(
                                (iRun + 1) * 4 + iIndex2 * 1, aucFpage);
                        if (iFodo <= 0) {
                                continue;
                        }

                        iLen = 2 * (int)ucGetByte(iFodo, aucFpage);

                        ucStc = ucGetByte(iFodo + 1, aucFpage);
                        usIstd = usStc2istd(ucStc);

                        vFillStyleFromStylesheet(usIstd, &tStyle);
                        vGet2StyleInfo(iFodo, aucFpage + 8, iLen - 8, &tStyle);
                        ulCharPos = ulGetLong(iIndex2 * 4, aucFpage);
                        NO_DBG_HEX(ulCharPos);
                        tStyle.ulFileOffset = ulCharPos;
                        vAdd2StyleInfoList(&tStyle);

                        eRowInfo = eGet2RowInfo(iFodo,
                                        aucFpage + 8, iLen - 8, &tRow);

                        switch(eRowInfo) {
                        case found_a_cell:
                                if (ulCharPosFirst != CP_INVALID) {
                                        break;
                                }
                                ulCharPosFirst = ulGetLong(
                                                iIndex2 * 4, aucFpage);
                                NO_DBG_HEX(ulCharPosFirst);
                                tRow.ulCharPosStart = ulCharPosFirst;
                                tRow.ulFileOffsetStart = ulCharPosFirst;
                                break;
                        case found_end_of_row:
                                ulCharPosLast = ulGetLong(
                                                iIndex2 * 4, aucFpage);
                                NO_DBG_HEX(ulCharPosLast);
                                tRow.ulCharPosEnd = ulCharPosLast;
                                /* Add 1 for compatiblity with Word 6 and up */
                                tRow.ulFileOffsetEnd = ulCharPosLast + 1;
                                vAdd2RowInfoList(&tRow);
                                (void)memset(&tRow, 0, sizeof(tRow));
                                ulCharPosFirst = CP_INVALID;
                                break;
                        case found_nothing:
                                break;
                        default:
                                DBG_DEC(eRowInfo);
                                break;
                        }
                }
        }
        ausParfPage = xfree(ausParfPage);
} /* end of vGet2PapInfo */

/*
 * Fill the font information block with information
 * from a WinWord 1 file.
 */
void
vGet1FontInfo(int iFodo,
        const UCHAR *aucGrpprl, size_t tBytes, font_block_type *pFont)
{
        BOOL    bIcoChange, bFtcChange, bHpsChange, bKulChange;
        USHORT  usTmp;
        UCHAR   ucTmp;
        UCHAR   aucChpx[12];

        fail(iFodo < 0 || aucGrpprl == NULL || pFont == NULL);

        if (tBytes > sizeof(aucChpx)) {
                NO_DBG_PRINT_BLOCK(aucGrpprl + iFodo, tBytes);
                return;
        }

        /* Build the CHPX structure */
        (void)memset(aucChpx, 0, sizeof(aucChpx));
        (void)memcpy(aucChpx, aucGrpprl + iFodo, min(tBytes, sizeof(aucChpx)));

        usTmp = usGetWord(0, aucChpx);
        if ((usTmp & BIT(0)) != 0) {
                pFont->usFontStyle ^= FONT_BOLD;
        }
        if ((usTmp & BIT(1)) != 0) {
                pFont->usFontStyle ^= FONT_ITALIC;
        }
        if ((usTmp & BIT(2)) != 0) {
                pFont->usFontStyle ^= FONT_STRIKE;
        }
        if ((usTmp & BIT(5)) != 0) {
                pFont->usFontStyle ^= FONT_SMALL_CAPITALS;
        }
        if ((usTmp & BIT(6)) != 0) {
                pFont->usFontStyle ^= FONT_CAPITALS;
        }
        if ((usTmp & BIT(7)) != 0) {
                pFont->usFontStyle ^= FONT_HIDDEN;
        }

        ucTmp = ucGetByte(5, aucChpx);
        if (ucTmp != 0) {
                if (ucTmp < 128) {
                        pFont->usFontStyle |= FONT_SUPERSCRIPT;
                        DBG_MSG("Superscript");
                } else {
                        pFont->usFontStyle |= FONT_SUBSCRIPT;
                        DBG_MSG("Subscript");
                }
        }

        bIcoChange = (usTmp & BIT(10)) != 0;
        bFtcChange = (usTmp & BIT(11)) != 0;
        bHpsChange = (usTmp & BIT(12)) != 0;
        bKulChange = (usTmp & BIT(13)) != 0;

        if (bFtcChange) {
                usTmp = usGetWord(2, aucChpx);
                if (usTmp <= (USHORT)UCHAR_MAX) {
                        pFont->ucFontNumber = (UCHAR)usTmp;
                } else {
                        pFont->ucFontNumber = 0;
                }
        }

        if (bHpsChange) {
                pFont->usFontSize = (USHORT)ucGetByte(4, aucChpx);
        }

        if (bIcoChange || bKulChange) {
                usTmp = usGetWord(6, aucChpx);
                if (bIcoChange) {
                        pFont->ucFontColor = (UCHAR)((usTmp & 0x0f00) >> 8);
                        if (pFont->ucFontColor <= 7) {
                                /* Add 1 for compatibility with Word 2 and up */
                                pFont->ucFontColor++;
                        } else {
                                DBG_DEC(pFont->ucFontColor);
                                pFont->ucFontColor = 0;
                        }
                }
                if (bKulChange) {
                        usTmp = (usTmp & 0x7000) >> 12;
                        DBG_DEC_C(usTmp > 4, usTmp);
                        if (usTmp == 0) {
                                pFont->usFontStyle &= ~FONT_UNDERLINE;
                        } else {
                                pFont->usFontStyle |= FONT_UNDERLINE;
                        }
                }
        }
} /* end of vGet1FontInfo */

/*
 * Fill the font information block with information
 * from a WinWord 1/2 file.
 */
void
vGet2FontInfo(int iFodo,
        const UCHAR *aucGrpprl, size_t tBytes, font_block_type *pFont)
{
        BOOL    bIcoChange, bFtcChange, bHpsChange, bKulChange;
        USHORT  usTmp;
        UCHAR   ucTmp;
        UCHAR   aucChpx[18];

        fail(iFodo < 0 || aucGrpprl == NULL || pFont == NULL);

        if (tBytes > sizeof(aucChpx)) {
                NO_DBG_PRINT_BLOCK(aucGrpprl + iFodo, tBytes);
                return;
        }

        /* Build the CHPX structure */
        (void)memset(aucChpx, 0, sizeof(aucChpx));
        (void)memcpy(aucChpx, aucGrpprl + iFodo, min(tBytes, sizeof(aucChpx)));

        usTmp = usGetWord(0, aucChpx);
        if ((usTmp & BIT(0)) != 0) {
                pFont->usFontStyle ^= FONT_BOLD;
        }
        if ((usTmp & BIT(1)) != 0) {
                pFont->usFontStyle ^= FONT_ITALIC;
        }
        if (usTmp & BIT(3)) {
                pFont->usFontStyle ^= FONT_MARKDEL;
        }
        if ((usTmp & BIT(5)) != 0) {
                pFont->usFontStyle ^= FONT_SMALL_CAPITALS;
        }
        if ((usTmp & BIT(6)) != 0) {
                pFont->usFontStyle ^= FONT_CAPITALS;
        }
        if ((usTmp & BIT(7)) != 0) {
                pFont->usFontStyle ^= FONT_HIDDEN;
        }
        if (usTmp & BIT(10)) {
                pFont->usFontStyle ^= FONT_STRIKE;
        }

        ucTmp = ucGetByte(10, aucChpx);
        DBG_MSG_C(ucTmp != 0 && ucTmp < 128, "Superscript");
        DBG_MSG_C(ucTmp >= 128, "Subscript");

        usTmp = usGetWord(2, aucChpx);
        if (usTmp == 0) {
                /* No changes, nothing to do */
                return;
        }

        bIcoChange = (usTmp & BIT(0)) != 0;
        bFtcChange = (usTmp & BIT(1)) != 0;
        bHpsChange = (usTmp & BIT(2)) != 0;
        bKulChange = (usTmp & BIT(3)) != 0;

        if (bFtcChange) {
                usTmp = usGetWord(4, aucChpx);
                if (usTmp <= (USHORT)UCHAR_MAX) {
                        pFont->ucFontNumber = (UCHAR)usTmp;
                } else {
                        pFont->ucFontNumber = 0;
                }
        }

        if (bHpsChange) {
                pFont->usFontSize = usGetWord(6, aucChpx);
        }

        if (bIcoChange || bKulChange) {
                ucTmp = ucGetByte(9, aucChpx);
                if (bIcoChange) {
                        pFont->ucFontColor = ucTmp & 0x1f;
                        if (pFont->ucFontColor > 16) {
                                DBG_DEC(pFont->ucFontColor);
                                pFont->ucFontColor = 0;
                        }
                }
                if (bKulChange) {
                        ucTmp = (ucTmp & 0xe0) >> 5;
                        DBG_DEC_C(ucTmp > 4, ucTmp);
                        if (ucTmp == 0) {
                                pFont->usFontStyle &= ~FONT_UNDERLINE;
                        } else {
                                pFont->usFontStyle |= FONT_UNDERLINE;
                        }
                }
        }
} /* end of vGet2FontInfo */

/*
 * Fill the picture information block with information from a WinWord 1 file.
 * Returns TRUE when successful, otherwise FALSE
 */
static BOOL
bGet1PicInfo(int iFodo,
        const UCHAR *aucGrpprl, size_t tBytes, picture_block_type *pPicture)
{
        ULONG   ulTmp;
        UCHAR   aucChpx[12];

        fail(iFodo < 0 || aucGrpprl == NULL || pPicture == NULL);

        if (tBytes > sizeof(aucChpx)) {
                NO_DBG_PRINT_BLOCK(aucGrpprl + iFodo, tBytes);
                tBytes = sizeof(aucChpx);
        }

        /* Build the CHPX structure */
        (void)memset(aucChpx, 0, sizeof(aucChpx));
        (void)memcpy(aucChpx, aucGrpprl + iFodo, min(tBytes, sizeof(aucChpx)));

        ulTmp = ulGetLong(8, aucChpx);
        if (ulTmp != 0 && ulTmp < MAX_FILESIZE) {
                pPicture->ulPictureOffset = ulTmp;
                DBG_HEX(pPicture->ulPictureOffset);
                return TRUE;
        }
        return FALSE;
} /* end of bGet1PicInfo */

/*
 * Fill the picture information block with information from a WinWord 2 file.
 * Returns TRUE when successful, otherwise FALSE
 */
static BOOL
bGet2PicInfo(int iFodo,
        const UCHAR *aucGrpprl, size_t tBytes, picture_block_type *pPicture)
{
        ULONG   ulTmp;
        UCHAR   aucChpx[18];

        fail(iFodo < 0 || aucGrpprl == NULL || pPicture == NULL);

        if (tBytes > sizeof(aucChpx)) {
                NO_DBG_PRINT_BLOCK(aucGrpprl + iFodo, tBytes);
                tBytes = sizeof(aucChpx);
        }

        /* Build the CHPX structure */
        (void)memset(aucChpx, 0, sizeof(aucChpx));
        (void)memcpy(aucChpx, aucGrpprl + iFodo, min(tBytes, sizeof(aucChpx)));

        ulTmp = ulGetLong(14, aucChpx);
        if (ulTmp != 0 && ulTmp < MAX_FILESIZE) {
                pPicture->ulPictureOffset = ulTmp;
                DBG_HEX(pPicture->ulPictureOffset);
                DBG_DEC(tBytes);
                return TRUE;
        }
        return FALSE;
} /* end of bGet2PicInfo */

/*
 * Build the lists with Character Information for WinWord 1/2 files
 */
void
vGet2ChrInfo(FILE *pFile, int iWordVersion, const UCHAR *aucHeader)
{
        font_block_type         tFont;
        picture_block_type      tPicture;
        USHORT  *ausCharPage;
        UCHAR   *aucBuffer;
        ULONG   ulFileOffset, ulCharPos, ulBeginCharInfo;
        size_t  tCharInfoLen, tOffset, tSize, tChrLen, tCharPageNum;
        size_t  tLenOld, tLen;
        int     iIndex, iIndex2, iRun, iFodo;
        BOOL    bSuccess1, bSuccess2;
        USHORT  usCharFirstPage, usCount, usIstd;
        UCHAR   aucFpage[BIG_BLOCK_SIZE];

        fail(pFile == NULL || aucHeader == NULL);
        fail(iWordVersion != 1 && iWordVersion != 2);

        ulBeginCharInfo = ulGetLong(0xa0, aucHeader); /* fcPlcfbteChpx */
        DBG_HEX(ulBeginCharInfo);
        tCharInfoLen = (size_t)usGetWord(0xa4, aucHeader); /* cbPlcfbteChpx */
        DBG_DEC(tCharInfoLen);
        if (tCharInfoLen < 4) {
                DBG_DEC(tCharInfoLen);
                return;
        }

        aucBuffer = xmalloc(tCharInfoLen);
        if (!bReadBytes(aucBuffer, tCharInfoLen, ulBeginCharInfo, pFile)) {
                aucBuffer = xfree(aucBuffer);
                return;
        }
        NO_DBG_PRINT_BLOCK(aucBuffer, tCharInfoLen);

        tLen = (tCharInfoLen - 4) / 6;
        ausCharPage = xcalloc(tLen, sizeof(USHORT));
        for (iIndex = 0, tOffset = (tLen + 1) * 4;
             iIndex < (int)tLen;
             iIndex++, tOffset += 2) {
                ausCharPage[iIndex] = usGetWord(tOffset, aucBuffer);
                NO_DBG_DEC(ausCharPage[iIndex]);
        }
        DBG_HEX(ulGetLong(0, aucBuffer));
        aucBuffer = xfree(aucBuffer);
        tCharPageNum = (size_t)usGetWord(0x142, aucHeader); /* cpnBteChp */
        DBG_DEC(tCharPageNum);
        if (tLen < tCharPageNum) {
                /* Replace CharPage by a longer version */
                tLenOld = tLen;
                usCharFirstPage = usGetWord(0x13e, aucHeader); /* pnChrFirst */
                NO_DBG_DEC(usCharFirstPage);
                tLen += tCharPageNum - 1;
                tSize = tLen * sizeof(USHORT);
                ausCharPage = xrealloc(ausCharPage, tSize);
                /* Add new values */
                usCount = usCharFirstPage + 1;
                for (iIndex = (int)tLenOld; iIndex < (int)tLen; iIndex++) {
                        ausCharPage[iIndex] = usCount;
                        NO_DBG_DEC(ausCharPage[iIndex]);
                        usCount++;
                }
        }

        for (iIndex = 0; iIndex < (int)tLen; iIndex++) {
                if (!bReadBytes(aucFpage, BIG_BLOCK_SIZE,
                                (ULONG)ausCharPage[iIndex] * BIG_BLOCK_SIZE,
                                pFile)) {
                        break;
                }
                NO_DBG_PRINT_BLOCK(aucFpage, BIG_BLOCK_SIZE);
                iRun = (int)ucGetByte(0x1ff, aucFpage);
                NO_DBG_DEC(iRun);
                for (iIndex2 = 0; iIndex2 < iRun; iIndex2++) {
                        if ((iRun + 1) * 4 + iIndex2 >= BIG_BLOCK_SIZE) {
                                break;
                        }
                        ulCharPos = ulGetLong(iIndex2 * 4, aucFpage);
                        ulFileOffset = ulCharPos;
                        iFodo = 2 * (int)ucGetByte(
                                (iRun + 1) * 4 + iIndex2, aucFpage);

                        tChrLen = (size_t)ucGetByte(iFodo, aucFpage);

                        usIstd = usGetIstd(ulFileOffset);
                        vFillFontFromStylesheet(usIstd, &tFont);
                        if (iFodo != 0) {
                                if (iWordVersion == 1) {
                                        vGet1FontInfo(iFodo,
                                                aucFpage + 1, tChrLen, &tFont);
                                } else if (iWordVersion == 2) {
                                        vGet2FontInfo(iFodo,
                                                aucFpage + 1, tChrLen, &tFont);
                                }
                        }
                        tFont.ulFileOffset = ulFileOffset;
                        vAdd2FontInfoList(&tFont);

                        if (iFodo <= 0) {
                                continue;
                        }

                        (void)memset(&tPicture, 0, sizeof(tPicture));
                        bSuccess1 = iWordVersion == 1 &&
                                        bGet1PicInfo(iFodo, aucFpage + 1,
                                                tChrLen, &tPicture);
                        bSuccess2 = iWordVersion == 2 &&
                                        bGet2PicInfo(iFodo, aucFpage + 1,
                                                tChrLen, &tPicture);
                        if (bSuccess1 || bSuccess2) {
                                tPicture.ulFileOffset = ulFileOffset;
                                tPicture.ulFileOffsetPicture =
                                                tPicture.ulPictureOffset;
                                vAdd2PictInfoList(&tPicture);
                        }
                }
        }
        ausCharPage = xfree(ausCharPage);
} /* end of vGet2ChrInfo */