Subversion Repositories planix.SVN

Rev

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

/*
 * word2text.c
 * Copyright (C) 1998-2005 A.J. van Os; Released under GNU GPL
 *
 * Description:
 * MS Word to "text" functions
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#if defined(__riscos)
#include "DeskLib:Hourglass.h"
#include "drawfile.h"
#endif /* __riscos */
#include "antiword.h"


#define INITIAL_SIZE            40
#define EXTENTION_SIZE          20


/* Macros to make sure all such statements will be identical */
#define OUTPUT_LINE()           \
        do {\
                vAlign2Window(pDiag, pAnchor, lWidthMax, ucAlignment);\
                TRACE_MSG("after vAlign2Window");\
                pAnchor = pStartNewOutput(pAnchor, NULL);\
                pOutput = pAnchor;\
        } while(0)

#define RESET_LINE()            \
        do {\
                pAnchor = pStartNewOutput(pAnchor, NULL);\
                pOutput = pAnchor;\
        } while(0)

#if defined(__riscos)
/* Length of the document in characters */
static ULONG    ulDocumentLength;
/* Number of characters processed so far */
static ULONG    ulCharCounter;
static int      iCurrPct, iPrevPct;
#endif /* __riscos */
/* The document is in the format belonging to this version of Word */
static int      iWordVersion = -1;
/* Special treatment for files from Word 4/5/6 on an Apple Macintosh */
static BOOL     bOldMacFile = FALSE;
/* Section Information */
static const section_block_type *pSection = NULL;
static const section_block_type *pSectionNext = NULL;
/* All the (command line) options */
static options_type     tOptions;
/* Needed for reading a complete table row */
static const row_block_type     *pRowInfo = NULL;
static BOOL     bStartRow = FALSE;
static BOOL     bEndRowNorm = FALSE;
static BOOL     bEndRowFast = FALSE;
static BOOL     bIsTableRow = FALSE;
/* Index of the next style and font information */
static USHORT   usIstdNext = ISTD_NORMAL;
/* Needed for finding the start of a style */
static const style_block_type   *pStyleInfo = NULL;
static style_block_type         tStyleNext;
static BOOL     bStartStyle = FALSE;
static BOOL     bStartStyleNext = FALSE;
/* Needed for finding the start of a font */
static const font_block_type    *pFontInfo = NULL;
static font_block_type          tFontNext;
static BOOL     bStartFont = FALSE;
static BOOL     bStartFontNext = FALSE;
/* Needed for finding an image */
static ULONG    ulFileOffsetImage = FC_INVALID;


/*
 * vUpdateCounters - Update the counters for the hourglass
 */
static void
vUpdateCounters(void)
{
#if defined(__riscos)
        ulCharCounter++;
        iCurrPct = (int)((ulCharCounter * 100) / ulDocumentLength);
        if (iCurrPct != iPrevPct) {
                Hourglass_Percentage(iCurrPct);
                iPrevPct = iCurrPct;
        }
#endif /* __riscos */
} /* end of vUpdateCounters */

/*
 * bOutputContainsText - see if the output contains more than white space
 */
BOOL
bOutputContainsText(const output_type *pAnchor)
{
        const output_type       *pCurr;
        size_t  tIndex;

        fail(pAnchor == NULL);

        for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
                fail(pCurr->lStringWidth < 0);
                for (tIndex = 0; tIndex < pCurr->tNextFree; tIndex++) {
                        if (isspace((int)(UCHAR)pCurr->szStorage[tIndex])) {
                                continue;
                        }
#if defined(DEBUG)
                        if (pCurr->szStorage[tIndex] == FILLER_CHAR) {
                                continue;
                        }
#endif /* DEBUG */
                        return TRUE;
                }
        }
        return FALSE;
} /* end of bOutputContainsText */

/*
 * lTotalStringWidth - compute the total width of the output string
 */
static long
lTotalStringWidth(const output_type *pAnchor)
{
        const output_type       *pCurr;
        long            lTotal;

        lTotal = 0;
        for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
                DBG_DEC_C(pCurr->lStringWidth < 0, pCurr->lStringWidth);
                fail(pCurr->lStringWidth < 0);
                lTotal += pCurr->lStringWidth;
        }
        return lTotal;
} /* end of lTotalStringWidth */

/*
 * vStoreByte - store one byte
 */
static void
vStoreByte(UCHAR ucChar, output_type *pOutput)
{
        fail(pOutput == NULL);

        if (ucChar == 0) {
                pOutput->szStorage[pOutput->tNextFree] = '\0';
                return;
        }

        while (pOutput->tNextFree + 2 > pOutput->tStorageSize) {
                pOutput->tStorageSize += EXTENTION_SIZE;
                pOutput->szStorage = xrealloc(pOutput->szStorage,
                                        pOutput->tStorageSize);
        }
        pOutput->szStorage[pOutput->tNextFree] = (char)ucChar;
        pOutput->szStorage[pOutput->tNextFree + 1] = '\0';
        pOutput->tNextFree++;
} /* end of vStoreByte */

/*
 * vStoreChar - store a character as one or more bytes
 */
static void
vStoreChar(ULONG ulChar, BOOL bChangeAllowed, output_type *pOutput)
{
        char    szResult[4];
        size_t  tIndex, tLen;

        fail(pOutput == NULL);

        if (tOptions.eEncoding == encoding_utf_8 && bChangeAllowed) {
                DBG_HEX_C(ulChar > 0xffff, ulChar);
                fail(ulChar > 0xffff);
                tLen = tUcs2Utf8(ulChar, szResult, sizeof(szResult));
                for (tIndex = 0; tIndex < tLen; tIndex++) {
                        vStoreByte((UCHAR)szResult[tIndex], pOutput);
                }
        } else {
                DBG_HEX_C(ulChar > 0xff, ulChar);
                fail(ulChar > 0xff);
                vStoreByte((UCHAR)ulChar, pOutput);
                tLen = 1;
        }
        pOutput->lStringWidth += lComputeStringWidth(
                                pOutput->szStorage + pOutput->tNextFree - tLen,
                                tLen,
                                pOutput->tFontRef,
                                pOutput->usFontSize);
} /* end of vStoreChar */

/*
 * vStoreCharacter - store one character
 */
static void
vStoreCharacter(ULONG ulChar, output_type *pOutput)
{
        vStoreChar(ulChar, TRUE, pOutput);
} /* end of vStoreCharacter */

/*
 * vStoreString - store a string
 */
static void
vStoreString(const char *szString, size_t tStringLength, output_type *pOutput)
{
        size_t  tIndex;

        fail(szString == NULL || pOutput == NULL);

        for (tIndex = 0; tIndex < tStringLength; tIndex++) {
                vStoreCharacter((ULONG)(UCHAR)szString[tIndex], pOutput);
        }
} /* end of vStoreString */

/*
 * vStoreNumberAsDecimal - store a number as a decimal number
 */
static void
vStoreNumberAsDecimal(UINT uiNumber, output_type *pOutput)
{
        size_t  tLen;
        char    szString[3 * sizeof(UINT) + 1];

        fail(uiNumber == 0);
        fail(pOutput == NULL);

        tLen = (size_t)sprintf(szString, "%u", uiNumber);
        vStoreString(szString, tLen, pOutput);
} /* end of vStoreNumberAsDecimal */

/*
 * vStoreNumberAsRoman - store a number as a roman numerical
 */
static void
vStoreNumberAsRoman(UINT uiNumber, output_type *pOutput)
{
        size_t  tLen;
        char    szString[15];

        fail(uiNumber == 0);
        fail(pOutput == NULL);

        tLen = tNumber2Roman(uiNumber, FALSE, szString);
        vStoreString(szString, tLen, pOutput);
} /* end of vStoreNumberAsRoman */

/*
 * vStoreStyle - store a style
 */
static void
vStoreStyle(diagram_type *pDiag, output_type *pOutput,
        const style_block_type *pStyle)
{
        size_t  tLen;
        char    szString[120];

        fail(pDiag == NULL);
        fail(pOutput == NULL);
        fail(pStyle == NULL);

        if (tOptions.eConversionType == conversion_xml) {
                vSetHeaders(pDiag, pStyle->usIstd);
        } else {
                tLen = tStyle2Window(szString, sizeof(szString),
                                        pStyle, pSection);
                vStoreString(szString, tLen, pOutput);
        }
} /* end of vStoreStyle */

/*
 * vPutIndentation - output the specified amount of indentation
 */
static void
vPutIndentation(diagram_type *pDiag, output_type *pOutput,
        BOOL bNoMarks, BOOL bFirstLine,
        UINT uiListNumber, UCHAR ucNFC, const char *szListChar,
        long lLeftIndentation, long lLeftIndentation1)
{
        long    lWidth;
        size_t  tIndex, tNextFree;
        char    szLine[30];

        fail(pDiag == NULL);
        fail(pOutput == NULL);
        fail(szListChar == NULL);
        fail(lLeftIndentation < 0);

        if (tOptions.eConversionType == conversion_xml) {
                /* XML does its own indentation at rendering time */
                return;
        }

        if (bNoMarks) {
                if (bFirstLine) {
                        lLeftIndentation += lLeftIndentation1;
                }
                if (lLeftIndentation < 0) {
                        lLeftIndentation = 0;
                }
                vSetLeftIndentation(pDiag, lLeftIndentation);
                return;
        }
        if (lLeftIndentation <= 0) {
                DBG_HEX_C(ucNFC != 0x00, ucNFC);
                vSetLeftIndentation(pDiag, 0);
                return;
        }

#if defined(DEBUG)
        if (tOptions.eEncoding == encoding_utf_8) {
                fail(strlen(szListChar) > 3);
        } else {
                DBG_HEX_C(iscntrl((int)szListChar[0]), szListChar[0]);
                fail(iscntrl((int)szListChar[0]));
                fail(szListChar[1] != '\0');
        }
#endif /* DEBUG */

        switch (ucNFC) {
        case LIST_ARABIC_NUM:
        case LIST_NUMBER_TXT:
                tNextFree = (size_t)sprintf(szLine, "%u", uiListNumber);
                break;
        case LIST_UPPER_ROMAN:
        case LIST_LOWER_ROMAN:
                tNextFree = tNumber2Roman(uiListNumber,
                                ucNFC == LIST_UPPER_ROMAN, szLine);
                break;
        case LIST_UPPER_ALPHA:
        case LIST_LOWER_ALPHA:
                tNextFree = tNumber2Alpha(uiListNumber,
                                ucNFC == LIST_UPPER_ALPHA, szLine);
                break;
        case LIST_ORDINAL_NUM:
        case LIST_ORDINAL_TXT:
                if (uiListNumber % 10 == 1 && uiListNumber != 11) {
                        tNextFree =
                                (size_t)sprintf(szLine, "%ust", uiListNumber);
                } else if (uiListNumber % 10 == 2 && uiListNumber != 12) {
                        tNextFree =
                                (size_t)sprintf(szLine, "%und", uiListNumber);
                } else if (uiListNumber % 10 == 3 && uiListNumber != 13) {
                        tNextFree =
                                (size_t)sprintf(szLine, "%urd", uiListNumber);
                } else {
                        tNextFree =
                                (size_t)sprintf(szLine, "%uth", uiListNumber);
                }
                break;
        case LIST_OUTLINE_NUM:
                tNextFree = (size_t)sprintf(szLine, "%02u", uiListNumber);
                break;
        case LIST_SPECIAL:
        case LIST_SPECIAL2:
        case LIST_BULLETS:
                tNextFree = 0;
                break;
        default:
                DBG_HEX(ucNFC);
                DBG_FIXME();
                tNextFree = (size_t)sprintf(szLine, "%u", uiListNumber);
                break;
        }
        tNextFree += (size_t)sprintf(szLine + tNextFree, "%.3s", szListChar);
        szLine[tNextFree++] = ' ';
        szLine[tNextFree] = '\0';
        lWidth = lComputeStringWidth(szLine, tNextFree,
                                pOutput->tFontRef, pOutput->usFontSize);
        lLeftIndentation -= lWidth;
        if (lLeftIndentation < 0) {
                lLeftIndentation = 0;
        }
        vSetLeftIndentation(pDiag, lLeftIndentation);
        for (tIndex = 0; tIndex < tNextFree; tIndex++) {
                vStoreChar((ULONG)(UCHAR)szLine[tIndex], FALSE, pOutput);
        }
} /* end of vPutIndentation */

/*
 * vPutSeparatorLine - output a separator line
 *
 * A separator line is a horizontal line two inches long.
 * Two inches equals 144000 millipoints.
 */
static void
vPutSeparatorLine(output_type *pOutput)
{
        long    lCharWidth;
        int     iCounter, iChars;
        char    szOne[2];

        fail(pOutput == NULL);

        szOne[0] = OUR_EM_DASH;
        szOne[1] = '\0';
        lCharWidth = lComputeStringWidth(szOne, 1,
                                pOutput->tFontRef, pOutput->usFontSize);
        NO_DBG_DEC(lCharWidth);
        iChars = (int)((144000 + lCharWidth / 2) / lCharWidth);
        NO_DBG_DEC(iChars);
        for (iCounter = 0; iCounter < iChars; iCounter++) {
                vStoreCharacter((ULONG)(UCHAR)OUR_EM_DASH, pOutput);
        }
} /* end of vPutSeparatorLine */

/*
 * pStartNextOutput - start the next output record
 *
 * returns a pointer to the next record
 */
static output_type *
pStartNextOutput(output_type *pCurrent)
{
        output_type     *pNew;

        TRACE_MSG("pStartNextOutput");

        if (pCurrent->tNextFree == 0) {
                /* The current record is empty, re-use */
                fail(pCurrent->szStorage[0] != '\0');
                fail(pCurrent->lStringWidth != 0);
                return pCurrent;
        }
        /* The current record is in use, make a new one */
        pNew = xmalloc(sizeof(*pNew));
        pCurrent->pNext = pNew;
        pNew->tStorageSize = INITIAL_SIZE;
        pNew->szStorage = xmalloc(pNew->tStorageSize);
        pNew->szStorage[0] = '\0';
        pNew->tNextFree = 0;
        pNew->lStringWidth = 0;
        pNew->ucFontColor = FONT_COLOR_DEFAULT;
        pNew->usFontStyle = FONT_REGULAR;
        pNew->tFontRef = (drawfile_fontref)0;
        pNew->usFontSize = DEFAULT_FONT_SIZE;
        pNew->pPrev = pCurrent;
        pNew->pNext = NULL;
        return pNew;
} /* end of pStartNextOutput */

/*
 * pStartNewOutput
 */
static output_type *
pStartNewOutput(output_type *pAnchor, output_type *pLeftOver)
{
        output_type     *pCurr, *pNext;
        USHORT          usFontStyle, usFontSize;
        drawfile_fontref        tFontRef;
        UCHAR           ucFontColor;

        TRACE_MSG("pStartNewOutput");

        ucFontColor = FONT_COLOR_DEFAULT;
        usFontStyle = FONT_REGULAR;
        tFontRef = (drawfile_fontref)0;
        usFontSize = DEFAULT_FONT_SIZE;
        /* Free the old output space */
        pCurr = pAnchor;
        while (pCurr != NULL) {
                TRACE_MSG("Free the old output space");
                pNext = pCurr->pNext;
                pCurr->szStorage = xfree(pCurr->szStorage);
                if (pCurr->pNext == NULL) {
                        ucFontColor = pCurr->ucFontColor;
                        usFontStyle = pCurr->usFontStyle;
                        tFontRef = pCurr->tFontRef;
                        usFontSize = pCurr->usFontSize;
                }
                pCurr = xfree(pCurr);
                pCurr = pNext;
        }
        if (pLeftOver == NULL) {
                /* Create new output space */
                TRACE_MSG("Create new output space");
                pLeftOver = xmalloc(sizeof(*pLeftOver));
                pLeftOver->tStorageSize = INITIAL_SIZE;
                NO_DBG_DEC(pLeftOver->tStorageSize);
                TRACE_MSG("before 2nd xmalloc");
                pLeftOver->szStorage = xmalloc(pLeftOver->tStorageSize);
                TRACE_MSG("after 2nd xmalloc");
                pLeftOver->szStorage[0] = '\0';
                pLeftOver->tNextFree = 0;
                pLeftOver->lStringWidth = 0;
                pLeftOver->ucFontColor = ucFontColor;
                pLeftOver->usFontStyle = usFontStyle;
                pLeftOver->tFontRef = tFontRef;
                pLeftOver->usFontSize = usFontSize;
                pLeftOver->pPrev = NULL;
                pLeftOver->pNext = NULL;
        }
        fail(!bCheckDoubleLinkedList(pLeftOver));
        return pLeftOver;
} /* end of pStartNewOutput */

/*
 * ulGetChar - get the next character from the specified list
 *
 * returns the next character of EOF
 */
static ULONG
ulGetChar(FILE *pFile, list_id_enum eListID)
{
        const font_block_type   *pCurr;
        ULONG           ulChar, ulFileOffset, ulCharPos;
        row_info_enum   eRowInfo;
        USHORT          usChar, usPropMod;
        BOOL            bSkip;

        fail(pFile == NULL);

        pCurr = pFontInfo;
        bSkip = FALSE;
        for (;;) {
                usChar = usNextChar(pFile, eListID,
                                &ulFileOffset, &ulCharPos, &usPropMod);
                if (usChar == (USHORT)EOF) {
                        return (ULONG)EOF;
                }

                vUpdateCounters();

                eRowInfo = ePropMod2RowInfo(usPropMod, iWordVersion);
                if (!bStartRow) {
#if 0
                        bStartRow = eRowInfo == found_a_cell ||
                                (pRowInfo != NULL &&
                                 ulFileOffset == pRowInfo->ulFileOffsetStart &&
                                 eRowInfo != found_not_a_cell);
#else
                        bStartRow = pRowInfo != NULL &&
                                ulFileOffset == pRowInfo->ulFileOffsetStart;
#endif
                        NO_DBG_HEX_C(bStartRow, pRowInfo->ulFileOffsetStart);
                }
                if (!bEndRowNorm) {
#if 0
                        bEndRow = eRowInfo == found_end_of_row ||
                                (pRowInfo != NULL &&
                                 ulFileOffset == pRowInfo->ulFileOffsetEnd &&
                                 eRowInfo != found_not_end_of_row);
#else
                        bEndRowNorm = pRowInfo != NULL &&
                                ulFileOffset == pRowInfo->ulFileOffsetEnd;
#endif
                        NO_DBG_HEX_C(bEndRowNorm, pRowInfo->ulFileOffsetEnd);
                }
                if (!bEndRowFast) {
                        bEndRowFast = eRowInfo == found_end_of_row;
                        NO_DBG_HEX_C(bEndRowFast, pRowInfo->ulFileOffsetEnd);
                }

                if (!bStartStyle) {
                        bStartStyle = pStyleInfo != NULL &&
                                ulFileOffset == pStyleInfo->ulFileOffset;
                        NO_DBG_HEX_C(bStartStyle, ulFileOffset);
                }
                if (pCurr != NULL && ulFileOffset == pCurr->ulFileOffset) {
                        bStartFont = TRUE;
                        NO_DBG_HEX(ulFileOffset);
                        pFontInfo = pCurr;
                        pCurr = pGetNextFontInfoListItem(pCurr);
                }

                /* Skip embedded characters */
                if (usChar == START_EMBEDDED) {
                        bSkip = TRUE;
                        continue;
                }
                if (usChar == END_IGNORE || usChar == END_EMBEDDED) {
                        bSkip = FALSE;
                        continue;
                }
                if (bSkip) {
                        continue;
                }
                ulChar = ulTranslateCharacters(usChar,
                                        ulFileOffset,
                                        iWordVersion,
                                        tOptions.eConversionType,
                                        tOptions.eEncoding,
                                        bOldMacFile);
                if (ulChar == IGNORE_CHARACTER) {
                        continue;
                }
                if (ulChar == PICTURE) {
                        ulFileOffsetImage = ulGetPictInfoListItem(ulFileOffset);
                } else {
                        ulFileOffsetImage = FC_INVALID;
                }
                if (ulChar == PAR_END) {
                        /* End of paragraph seen, prepare for the next */
                        vFillStyleFromStylesheet(usIstdNext, &tStyleNext);
                        vCorrectStyleValues(&tStyleNext);
                        bStartStyleNext = TRUE;
                        vFillFontFromStylesheet(usIstdNext, &tFontNext);
                        vCorrectFontValues(&tFontNext);
                        bStartFontNext = TRUE;
                }
                if (ulChar == PAGE_BREAK) {
                        /* Might be the start of a new section */
                        pSectionNext = pGetSectionInfo(pSection, ulCharPos);
                }
                return ulChar;
        }
} /* end of ulGetChar */

/*
 * lGetWidthMax - get the maximum line width from the paragraph break value
 *
 * Returns the maximum line width in millipoints
 */
static long
lGetWidthMax(int iParagraphBreak)
{
        fail(iParagraphBreak < 0);

        if (iParagraphBreak == 0) {
                return LONG_MAX;
        }
        if (iParagraphBreak < MIN_SCREEN_WIDTH) {
                return lChar2MilliPoints(MIN_SCREEN_WIDTH);
        }
        if (iParagraphBreak > MAX_SCREEN_WIDTH) {
                return lChar2MilliPoints(MAX_SCREEN_WIDTH);
        }
        return lChar2MilliPoints(iParagraphBreak);
} /* end of lGetWidthMax */

/*
 * bWordDecryptor - turn Word to something more useful
 *
 * returns TRUE when succesful, otherwise FALSE
 */
BOOL
bWordDecryptor(FILE *pFile, long lFilesize, diagram_type *pDiag)
{
        imagedata_type  tImage;
        const style_block_type  *pStyleTmp;
        const font_block_type   *pFontTmp;
        const char      *szListChar;
        output_type     *pAnchor, *pOutput, *pLeftOver;
        ULONG   ulChar;
        long    lBeforeIndentation, lAfterIndentation;
        long    lLeftIndentation, lLeftIndentation1, lRightIndentation;
        long    lWidthCurr, lWidthMax, lDefaultTabWidth, lHalfSpaceWidth, lTmp;
        list_id_enum    eListID;
        image_info_enum eRes;
        UINT    uiFootnoteNumber, uiEndnoteNumber, uiTmp;
        int     iListSeqNumber;
        BOOL    bWasTableRow, bTableFontClosed, bWasEndOfParagraph;
        BOOL    bInList, bWasInList, bNoMarks, bFirstLine;
        BOOL    bAllCapitals, bHiddenText, bMarkDelText, bSuccess;
        USHORT  usListNumber;
        USHORT  usFontStyle, usFontStyleMinimal, usFontSize, usTmp;
        UCHAR   ucFontNumber, ucFontColor;
        UCHAR   ucNFC, ucAlignment;

        fail(pFile == NULL || lFilesize <= 0 || pDiag == NULL);

        TRACE_MSG("bWordDecryptor");

        iWordVersion = iInitDocument(pFile, lFilesize);
        if (iWordVersion < 0) {
                DBG_DEC(iWordVersion);
                return FALSE;
        }

        vGetOptions(&tOptions);
        bOldMacFile = bIsOldMacFile();
        vPrepareHdrFtrText(pFile);
        vPrepareFootnoteText(pFile);

        vPrologue2(pDiag, iWordVersion);

        /* Initialisation */
#if defined(__riscos)
        ulCharCounter = 0;
        iCurrPct = 0;
        iPrevPct = -1;
        ulDocumentLength = ulGetDocumentLength();
#endif /* __riscos */
        pSection = pGetSectionInfo(NULL, 0);
        pSectionNext = pSection;
        lDefaultTabWidth = lGetDefaultTabWidth();
        DBG_DEC_C(lDefaultTabWidth != 36000, lDefaultTabWidth);
        pRowInfo = pGetNextRowInfoListItem();
        DBG_HEX_C(pRowInfo != NULL, pRowInfo->ulFileOffsetStart);
        DBG_HEX_C(pRowInfo != NULL, pRowInfo->ulFileOffsetEnd);
        DBG_MSG_C(pRowInfo == NULL, "No rows at all");
        bStartRow = FALSE;
        bEndRowNorm = FALSE;
        bEndRowFast = FALSE;
        bIsTableRow = FALSE;
        bWasTableRow = FALSE;
        vResetStyles();
        pStyleInfo = pGetNextTextStyle(NULL);
        bStartStyle = FALSE;
        bInList = FALSE;
        bWasInList = FALSE;
        iListSeqNumber = 0;
        usIstdNext = ISTD_NORMAL;
        pAnchor = NULL;
        pFontInfo = pGetNextFontInfoListItem(NULL);
        DBG_HEX_C(pFontInfo != NULL, pFontInfo->ulFileOffset);
        DBG_MSG_C(pFontInfo == NULL, "No fonts at all");
        bStartFont = FALSE;
        ucFontNumber = 0;
        usFontStyleMinimal = FONT_REGULAR;
        usFontStyle = FONT_REGULAR;
        usFontSize = DEFAULT_FONT_SIZE;
        ucFontColor = FONT_COLOR_DEFAULT;
        pAnchor = pStartNewOutput(pAnchor, NULL);
        pOutput = pAnchor;
        pOutput->ucFontColor = ucFontColor;
        pOutput->usFontStyle = usFontStyle;
        pOutput->tFontRef = tOpenFont(ucFontNumber, usFontStyle, usFontSize);
        pOutput->usFontSize = usFontSize;
        bTableFontClosed = TRUE;
        lBeforeIndentation = 0;
        lAfterIndentation = 0;
        lLeftIndentation = 0;
        lLeftIndentation1 = 0;
        lRightIndentation = 0;
        bWasEndOfParagraph = TRUE;
        bNoMarks = TRUE;
        bFirstLine = TRUE;
        ucNFC = LIST_BULLETS;
        if (pStyleInfo != NULL) {
                szListChar = pStyleInfo->szListChar;
                pStyleTmp = pStyleInfo;
        } else {
                if (tStyleNext.szListChar[0] == '\0') {
                        vGetBulletValue(tOptions.eConversionType,
                                tOptions.eEncoding, tStyleNext.szListChar, 4);
                }
                szListChar = tStyleNext.szListChar;
                pStyleTmp = &tStyleNext;
        }
        usListNumber = 0;
        ucAlignment = ALIGNMENT_LEFT;
        bAllCapitals = FALSE;
        bHiddenText = FALSE;
        bMarkDelText = FALSE;
        lWidthMax = lGetWidthMax(tOptions.iParagraphBreak);
        NO_DBG_DEC(lWidthMax);

        Hourglass_On();

        uiFootnoteNumber = 0;
        uiEndnoteNumber = 0;
        eListID = text_list;
        for(;;) {
                ulChar = ulGetChar(pFile, eListID);
                if (ulChar == (ULONG)EOF) {
                        if (bOutputContainsText(pAnchor)) {
                                OUTPUT_LINE();
                        } else {
                                RESET_LINE();
                        }
                        switch (eListID) {
                        case text_list:
                                if (tOptions.eConversionType !=
                                                        conversion_xml) {
                                        eListID = footnote_list;
                                        if (uiFootnoteNumber != 0) {
                                                vPutSeparatorLine(pAnchor);
                                                OUTPUT_LINE();
                                                uiFootnoteNumber = 0;
                                        }
                                        break;
                                }
                                /* No break or return */
                        case footnote_list:
                                eListID = endnote_list;
                                if (uiEndnoteNumber != 0) {
                                        vPutSeparatorLine(pAnchor);
                                        OUTPUT_LINE();
                                        uiEndnoteNumber = 0;
                                }
                                break;
                        case endnote_list:
                                eListID = textbox_list;
                                if (bExistsTextBox()) {
                                        vPutSeparatorLine(pAnchor);
                                        OUTPUT_LINE();
                                }
                                break;
                        case textbox_list:
                                eListID = hdrtextbox_list;
                                if (bExistsHdrTextBox()) {
                                        vPutSeparatorLine(pAnchor);
                                        OUTPUT_LINE();
                                }
                                break;
                        case hdrtextbox_list:
                        default:
                                eListID = end_of_lists;
                                break;
                        }
                        if (eListID == end_of_lists) {
                                break;
                        }
                        continue;
                }

                if (ulChar == UNKNOWN_NOTE_CHAR) {
                        switch (eListID) {
                        case footnote_list:
                                ulChar = FOOTNOTE_CHAR;
                                break;
                        case endnote_list:
                                ulChar = ENDNOTE_CHAR;
                                break;
                        default:
                                break;
                        }
                }

                if (bStartRow) {
                        /* Begin of a tablerow found */
                        if (bOutputContainsText(pAnchor)) {
                                OUTPUT_LINE();
                        } else {
                                RESET_LINE();
                        }
                        fail(pAnchor != pOutput);
                        if (bTableFontClosed) {
                                /* Start special table font */
                                vCloseFont();
                                /*
                                 * Compensate for the fact that Word uses
                                 * proportional fonts for its tables and we
                                 * only one fixed-width font
                                 */
                                uiTmp = ((UINT)usFontSize * 5 + 3) / 6;
                                if (uiTmp < MIN_TABLEFONT_SIZE) {
                                        uiTmp = MIN_TABLEFONT_SIZE;
                                } else if (uiTmp > MAX_TABLEFONT_SIZE) {
                                        uiTmp = MAX_TABLEFONT_SIZE;
                                }
                                pOutput->usFontSize = (USHORT)uiTmp;
                                pOutput->tFontRef =
                                        tOpenTableFont(pOutput->usFontSize);
                                pOutput->usFontStyle = FONT_REGULAR;
                                pOutput->ucFontColor = FONT_COLOR_BLACK;
                                bTableFontClosed = FALSE;
                        }
                        bIsTableRow = TRUE;
                        bStartRow = FALSE;
                }

                if (bWasTableRow &&
                    !bIsTableRow &&
                    ulChar != PAR_END &&
                    ulChar != HARD_RETURN &&
                    ulChar != PAGE_BREAK &&
                    ulChar != COLUMN_FEED) {
                        /*
                         * The end of a table should be followed by an
                         * empty line, like the end of a paragraph
                         */
                        OUTPUT_LINE();
                        vEndOfParagraph(pDiag,
                                        pOutput->tFontRef,
                                        pOutput->usFontSize,
                                        (long)pOutput->usFontSize * 600);
                }

                switch (ulChar) {
                case PAGE_BREAK:
                case COLUMN_FEED:
                        if (bIsTableRow) {
                                /* Ignore when in a table */
                                break;
                        }
                        if (bOutputContainsText(pAnchor)) {
                                OUTPUT_LINE();
                        } else {
                                RESET_LINE();
                        }
                        if (ulChar == PAGE_BREAK) {
                                vEndOfPage(pDiag, lAfterIndentation,
                                                pSection != pSectionNext);
                        } else {
                                vEndOfParagraph(pDiag,
                                        pOutput->tFontRef,
                                        pOutput->usFontSize,
                                        lAfterIndentation);
                        }
                        break;
                default:
                        break;
                }

                if (bStartFont || (bStartFontNext && ulChar != PAR_END)) {
                        /* Begin of a font found */
                        if (bStartFont) {
                                /* bStartFont takes priority */
                                fail(pFontInfo == NULL);
                                pFontTmp = pFontInfo;
                        } else {
                                pFontTmp = &tFontNext;
                        }
                        bAllCapitals = bIsCapitals(pFontTmp->usFontStyle);
                        bHiddenText = bIsHidden(pFontTmp->usFontStyle);
                        bMarkDelText = bIsMarkDel(pFontTmp->usFontStyle);
                        usTmp = pFontTmp->usFontStyle &
                                (FONT_BOLD|FONT_ITALIC|FONT_UNDERLINE|
                                 FONT_STRIKE|FONT_MARKDEL|
                                 FONT_SUPERSCRIPT|FONT_SUBSCRIPT);
                        if (!bIsTableRow &&
                            (usFontSize != pFontTmp->usFontSize ||
                             ucFontNumber != pFontTmp->ucFontNumber ||
                             usFontStyleMinimal != usTmp ||
                             ucFontColor != pFontTmp->ucFontColor)) {
                                pOutput = pStartNextOutput(pOutput);
                                vCloseFont();
                                pOutput->ucFontColor = pFontTmp->ucFontColor;
                                pOutput->usFontStyle = pFontTmp->usFontStyle;
                                pOutput->usFontSize = pFontTmp->usFontSize;
                                pOutput->tFontRef = tOpenFont(
                                                pFontTmp->ucFontNumber,
                                                pFontTmp->usFontStyle,
                                                pFontTmp->usFontSize);
                                fail(!bCheckDoubleLinkedList(pAnchor));
                        }
                        ucFontNumber = pFontTmp->ucFontNumber;
                        usFontSize = pFontTmp->usFontSize;
                        ucFontColor = pFontTmp->ucFontColor;
                        usFontStyle = pFontTmp->usFontStyle;
                        usFontStyleMinimal = usTmp;
                        if (bStartFont) {
                                /* Get the next font info */
                                pFontInfo = pGetNextFontInfoListItem(pFontInfo);
                                NO_DBG_HEX_C(pFontInfo != NULL,
                                                pFontInfo->ulFileOffset);
                                DBG_MSG_C(pFontInfo == NULL, "No more fonts");
                        }
                        bStartFont = FALSE;
                        bStartFontNext = FALSE;
                }

                if (bStartStyle || (bStartStyleNext && ulChar != PAR_END)) {
                        bFirstLine = TRUE;
                        /* Begin of a style found */
                        if (bStartStyle) {
                                /* bStartStyle takes priority */
                                fail(pStyleInfo == NULL);
                                pStyleTmp = pStyleInfo;
                        } else {
                                pStyleTmp = &tStyleNext;
                        }
                        if (!bIsTableRow) {
                                vStoreStyle(pDiag, pOutput, pStyleTmp);
                        }
                        usIstdNext = pStyleTmp->usIstdNext;
                        lBeforeIndentation =
                                lTwips2MilliPoints(pStyleTmp->usBeforeIndent);
                        lAfterIndentation =
                                lTwips2MilliPoints(pStyleTmp->usAfterIndent);
                        lLeftIndentation =
                                lTwips2MilliPoints(pStyleTmp->sLeftIndent);
                        lLeftIndentation1 =
                                lTwips2MilliPoints(pStyleTmp->sLeftIndent1);
                        lRightIndentation =
                                lTwips2MilliPoints(pStyleTmp->sRightIndent);
                        bInList = bStyleImpliesList(pStyleTmp, iWordVersion);
                        bNoMarks = !bInList || pStyleTmp->bNumPause;
                        ucNFC = pStyleTmp->ucNFC;
                        szListChar = pStyleTmp->szListChar;
                        ucAlignment = pStyleTmp->ucAlignment;
                        if (bInList && !bWasInList) {
                                /* Start of a list */
                                iListSeqNumber++;
                                vStartOfList(pDiag, ucNFC,
                                                bWasTableRow && !bIsTableRow);
                        }
                        if (!bInList && bWasInList) {
                                /* End of a list */
                                vEndOfList(pDiag);
                        }
                        bWasInList = bInList;
                        if (bStartStyle) {
                                pStyleInfo = pGetNextTextStyle(pStyleInfo);
                                NO_DBG_HEX_C(pStyleInfo != NULL,
                                                pStyleInfo->ulFileOffset);
                                DBG_MSG_C(pStyleInfo == NULL,
                                                "No more styles");
                        }
                        bStartStyle = FALSE;
                        bStartStyleNext = FALSE;
                }

                if (bWasEndOfParagraph) {
                        vStartOfParagraph1(pDiag, lBeforeIndentation);
                }

                if (!bIsTableRow &&
                    lTotalStringWidth(pAnchor) == 0) {
                        if (!bNoMarks) {
                                usListNumber = usGetListValue(iListSeqNumber,
                                                        iWordVersion,
                                                        pStyleTmp);
                        }
                        if (bInList && bFirstLine) {
                                vStartOfListItem(pDiag, bNoMarks);
                        }
                        vPutIndentation(pDiag, pAnchor, bNoMarks, bFirstLine,
                                        usListNumber, ucNFC, szListChar,
                                        lLeftIndentation, lLeftIndentation1);
                        bFirstLine = FALSE;
                        /* One number or mark per paragraph will do */
                        bNoMarks = TRUE;
                }

                if (bWasEndOfParagraph) {
                        vStartOfParagraph2(pDiag);
                        bWasEndOfParagraph = FALSE;
                }

                switch (ulChar) {
                case PICTURE:
                        (void)memset(&tImage, 0, sizeof(tImage));
                        eRes = eExamineImage(pFile, ulFileOffsetImage, &tImage);
                        switch (eRes) {
                        case image_no_information:
                                bSuccess = FALSE;
                                break;
                        case image_minimal_information:
                        case image_full_information:
#if 0
                                if (bOutputContainsText(pAnchor)) {
                                        OUTPUT_LINE();
                                } else {
                                        RESET_LINE();
                                }
#endif
                                bSuccess = bTranslateImage(pDiag, pFile,
                                        eRes == image_minimal_information,
                                        ulFileOffsetImage, &tImage);
                                break;
                        default:
                                DBG_DEC(eRes);
                                bSuccess = FALSE;
                                break;
                        }
                        if (!bSuccess) {
                                vStoreString("[pic]", 5, pOutput);
                        }
                        break;
                case FOOTNOTE_CHAR:
                        uiFootnoteNumber++;
                        if (tOptions.eConversionType == conversion_xml) {
                                vStoreCharacter((ULONG)FOOTNOTE_OR_ENDNOTE,
                                                                pOutput);
                                break;
                        }
                        vStoreCharacter((ULONG)'[', pOutput);
                        vStoreNumberAsDecimal(uiFootnoteNumber, pOutput);
                        vStoreCharacter((ULONG)']', pOutput);
                        break;
                case ENDNOTE_CHAR:
                        uiEndnoteNumber++;
                        vStoreCharacter((ULONG)'[', pOutput);
                        vStoreNumberAsRoman(uiEndnoteNumber, pOutput);
                        vStoreCharacter((ULONG)']', pOutput);
                        break;
                case UNKNOWN_NOTE_CHAR:
                        vStoreString("[?]", 3, pOutput);
                        break;
                case PAR_END:
                        if (bIsTableRow) {
                                vStoreCharacter((ULONG)'\n', pOutput);
                                break;
                        }
                        if (bOutputContainsText(pAnchor)) {
                                OUTPUT_LINE();
                        } else {
                                vMove2NextLine(pDiag,
                                        pOutput->tFontRef, pOutput->usFontSize);
                                RESET_LINE();
                        }
                        vEndOfParagraph(pDiag,
                                        pOutput->tFontRef,
                                        pOutput->usFontSize,
                                        lAfterIndentation);
                        bWasEndOfParagraph = TRUE;
                        break;
                case HARD_RETURN:
                        if (bIsTableRow) {
                                vStoreCharacter((ULONG)'\n', pOutput);
                                break;
                        }
                        if (bOutputContainsText(pAnchor)) {
                                OUTPUT_LINE();
                        } else {
                                vMove2NextLine(pDiag,
                                        pOutput->tFontRef, pOutput->usFontSize);
                                RESET_LINE();
                        }
                        break;
                case PAGE_BREAK:
                case COLUMN_FEED:
                        pSection = pSectionNext;
                        break;
                case TABLE_SEPARATOR:
                        if (bIsTableRow) {
                                vStoreCharacter(ulChar, pOutput);
                                break;
                        }
                        vStoreCharacter((ULONG)' ', pOutput);
                        vStoreCharacter((ULONG)TABLE_SEPARATOR_CHAR, pOutput);
                        break;
                case TAB:
                        if (bIsTableRow ||
                            tOptions.eConversionType == conversion_xml) {
                                vStoreCharacter((ULONG)' ', pOutput);
                                break;
                        }
                        if (tOptions.iParagraphBreak == 0 &&
                            (tOptions.eConversionType == conversion_text ||
                             tOptions.eConversionType == conversion_fmt_text)) {
                                /* No logical lines, so no tab expansion */
                                vStoreCharacter(TAB, pOutput);
                                break;
                        }
                        lHalfSpaceWidth = (lComputeSpaceWidth(
                                        pOutput->tFontRef,
                                        pOutput->usFontSize) + 1) / 2;
                        lTmp = lTotalStringWidth(pAnchor);
                        lTmp += lDrawUnits2MilliPoints(pDiag->lXleft);
                        lTmp /= lDefaultTabWidth;
                        do {
                                vStoreCharacter((ULONG)FILLER_CHAR, pOutput);
                                lWidthCurr = lTotalStringWidth(pAnchor);
                                lWidthCurr +=
                                        lDrawUnits2MilliPoints(pDiag->lXleft);
                        } while (lTmp == lWidthCurr / lDefaultTabWidth &&
                                 lWidthCurr < lWidthMax + lRightIndentation);
                        break;
                default:
                        if (bHiddenText && tOptions.bHideHiddenText) {
                                continue;
                        }
                        if (bMarkDelText && tOptions.bRemoveRemovedText) {
                                continue;
                        }
                        if (ulChar == UNICODE_ELLIPSIS &&
                            tOptions.eEncoding != encoding_utf_8) {
                                vStoreString("...", 3, pOutput);
                        } else {
                                if (bAllCapitals) {
                                        ulChar = ulToUpper(ulChar);
                                }
                                vStoreCharacter(ulChar, pOutput);
                        }
                        break;
                }

                if (bWasTableRow && !bIsTableRow) {
                        /* End of a table */
                        vEndOfTable(pDiag);
                        /* Resume normal font */
                        NO_DBG_MSG("End of table font");
                        vCloseFont();
                        bTableFontClosed = TRUE;
                        pOutput->ucFontColor = ucFontColor;
                        pOutput->usFontStyle = usFontStyle;
                        pOutput->usFontSize = usFontSize;
                        pOutput->tFontRef = tOpenFont(
                                        ucFontNumber, usFontStyle, usFontSize);
                }
                bWasTableRow = bIsTableRow;

                if (bIsTableRow) {
                        fail(pAnchor != pOutput);
                        if (!bEndRowNorm && !bEndRowFast) {
                                continue;
                        }
                        /* End of a table row */
                        if (bEndRowNorm) {
                                fail(pRowInfo == NULL);
                                vTableRow2Window(pDiag, pAnchor, pRowInfo,
                                                tOptions.eConversionType,
                                                tOptions.iParagraphBreak);
                        } else {
                                fail(!bEndRowFast);
                        }
                        /* Reset */
                        pAnchor = pStartNewOutput(pAnchor, NULL);
                        pOutput = pAnchor;
                        if (bEndRowNorm) {
                                pRowInfo = pGetNextRowInfoListItem();
                        }
                        bIsTableRow = FALSE;
                        bEndRowNorm = FALSE;
                        bEndRowFast = FALSE;
                        NO_DBG_HEX_C(pRowInfo != NULL,
                                                pRowInfo->ulFileOffsetStart);
                        NO_DBG_HEX_C(pRowInfo != NULL,
                                                pRowInfo->ulFileOffsetEnd);
                        continue;
                }
                lWidthCurr = lTotalStringWidth(pAnchor);
                lWidthCurr += lDrawUnits2MilliPoints(pDiag->lXleft);
                if (lWidthCurr < lWidthMax + lRightIndentation) {
                        continue;
                }
                pLeftOver = pSplitList(pAnchor);
                vJustify2Window(pDiag, pAnchor,
                                lWidthMax, lRightIndentation, ucAlignment);
                pAnchor = pStartNewOutput(pAnchor, pLeftOver);
                for (pOutput = pAnchor;
                     pOutput->pNext != NULL;
                     pOutput = pOutput->pNext)
                        ;       /* EMPTY */
                fail(pOutput == NULL);
                if (lTotalStringWidth(pAnchor) > 0) {
                        vSetLeftIndentation(pDiag, lLeftIndentation);
                }
        }

        pAnchor = pStartNewOutput(pAnchor, NULL);
        pAnchor->szStorage = xfree(pAnchor->szStorage);
        pAnchor = xfree(pAnchor);
        vCloseFont();
        vFreeDocument();
        Hourglass_Off();
        return TRUE;
} /* end of bWordDecryptor */

/*
 * lLastStringWidth - compute the width of the last part of the output string
 */
static long
lLastStringWidth(const output_type *pAnchor)
{
        const output_type       *pCurr, *pStart;

        pStart = NULL;
        for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
                if (pCurr->tNextFree == 1 &&
                    (pCurr->szStorage[0] == PAR_END ||
                     pCurr->szStorage[0] == HARD_RETURN)) {
                        /* Found a separator. Start after the separator */
                        pStart = pCurr->pNext;
                }
        }
        if (pStart == NULL) {
                /* No separators. Use the whole output string */
                pStart = pAnchor;
        }
        return lTotalStringWidth(pStart);
} /* end of lLastStringWidth */

/*
 * pHdrFtrDecryptor - turn a header/footer list element to something useful
 */
output_type *
pHdrFtrDecryptor(FILE *pFile, ULONG ulCharPosStart, ULONG ulCharPosNext)
{
        output_type     *pAnchor, *pOutput, *pLeftOver;
        ULONG   ulChar, ulFileOffset, ulCharPos;
        long    lWidthCurr, lWidthMax;
        long    lRightIndentation;
        USHORT  usChar;
        UCHAR   ucAlignment;
        BOOL    bSkip;

        fail(iWordVersion < 0);
        fail(tOptions.eConversionType == conversion_unknown);
        fail(tOptions.eEncoding == 0);

        if (ulCharPosStart == ulCharPosNext) {
                /* There are no bytes to decrypt */
                return NULL;
        }

        lRightIndentation = 0;
        ucAlignment = ALIGNMENT_LEFT;
        bSkip = FALSE;
        lWidthMax = lGetWidthMax(tOptions.iParagraphBreak);
        pAnchor = pStartNewOutput(NULL, NULL);
        pOutput = pAnchor;
        pOutput->tFontRef = tOpenFont(0, FONT_REGULAR, DEFAULT_FONT_SIZE);
        usChar = usToHdrFtrPosition(pFile, ulCharPosStart);
        ulCharPos = ulCharPosStart;
        ulFileOffset = ulCharPos2FileOffset(ulCharPos);
        while (usChar != (USHORT)EOF && ulCharPos != ulCharPosNext) {
                /* Skip embedded characters */
                if (usChar == START_EMBEDDED) {
                        bSkip = TRUE;
                } else if (usChar == END_IGNORE || usChar == END_EMBEDDED) {
                        bSkip = FALSE;
                }
                /* Translate character */
                if (bSkip || usChar == END_IGNORE || usChar == END_EMBEDDED) {
                        ulChar = IGNORE_CHARACTER;
                } else {
                        ulChar = ulTranslateCharacters(usChar,
                                        ulFileOffset,
                                        iWordVersion,
                                        tOptions.eConversionType,
                                        tOptions.eEncoding,
                                        bOldMacFile);
                }
                /* Process character */
                if (ulChar != IGNORE_CHARACTER) {
                        switch (ulChar) {
                        case PICTURE:
                                vStoreString("[pic]", 5, pOutput);
                                break;
                        case PAR_END:
                        case HARD_RETURN:
                        case PAGE_BREAK:
                        case COLUMN_FEED:
                                /* To the next substring */
                                pOutput = pStartNextOutput(pOutput);
                                vCloseFont();
                                pOutput->tFontRef = tOpenFont(0,
                                        FONT_REGULAR, DEFAULT_FONT_SIZE);
                                /* A substring with just one character */
                                if (ulChar == HARD_RETURN) {
                                        vStoreCharacter(HARD_RETURN, pOutput);
                                } else {
                                        vStoreCharacter(PAR_END, pOutput);
                                }
                                /* To the next substring */
                                pOutput = pStartNextOutput(pOutput);
                                vCloseFont();
                                pOutput->tFontRef = tOpenFont(0,
                                        FONT_REGULAR, DEFAULT_FONT_SIZE);
                                fail(!bCheckDoubleLinkedList(pAnchor));
                                break;
                        case TABLE_SEPARATOR:
                                vStoreCharacter((ULONG)' ', pOutput);
                                vStoreCharacter((ULONG)TABLE_SEPARATOR_CHAR,
                                                        pOutput);
                                break;
                        case TAB:
                                vStoreCharacter((ULONG)FILLER_CHAR, pOutput);
                                break;
                        default:
                                vStoreCharacter(ulChar, pOutput);
                                break;
                        }
                }
                lWidthCurr = lLastStringWidth(pAnchor);
                if (lWidthCurr >= lWidthMax + lRightIndentation) {
                        pLeftOver = pSplitList(pAnchor);
                        for (pOutput = pAnchor;
                             pOutput->pNext != NULL;
                             pOutput = pOutput->pNext)
                                ;       /* EMPTY */
                        fail(pOutput == NULL);
                        /* To the next substring */
                        pOutput = pStartNextOutput(pOutput);
                        /* A substring with just one HARD_RETURN */
                        vStoreCharacter(HARD_RETURN, pOutput);
                        /* Put the leftover piece(s) at the end */
                        pOutput->pNext = pLeftOver;
                        if (pLeftOver != NULL) {
                                pLeftOver->pPrev = pOutput;
                        }
                        fail(!bCheckDoubleLinkedList(pAnchor));
                        for (pOutput = pAnchor;
                             pOutput->pNext != NULL;
                             pOutput = pOutput->pNext)
                                ;       /* EMPTY */
                        fail(pOutput == NULL);
                }
                usChar = usNextChar(pFile, hdrftr_list,
                                        &ulFileOffset, &ulCharPos, NULL);
        }
        vCloseFont();
        if (bOutputContainsText(pAnchor)) {
                return pAnchor;
        }
        pAnchor = pStartNewOutput(pAnchor, NULL);
        pAnchor->szStorage = xfree(pAnchor->szStorage);
        pAnchor = xfree(pAnchor);
        return NULL;
} /* end of pHdrFtrDecryptor */

/*
 * pFootnoteDecryptor - turn a footnote text list element into text
 */
char *
szFootnoteDecryptor(FILE *pFile, ULONG ulCharPosStart, ULONG ulCharPosNext)
{
        char    *szText;
        ULONG   ulChar, ulFileOffset, ulCharPos;
        USHORT  usChar;
        size_t  tLen, tIndex, tNextFree, tStorageSize;
        char    szResult[6];
        BOOL    bSkip;

        fail(iWordVersion < 0);
        fail(tOptions.eConversionType == conversion_unknown);
        fail(tOptions.eEncoding == 0);

        if (ulCharPosStart == ulCharPosNext) {
                /* There are no bytes to decrypt */
                return NULL;
        }

        if (tOptions.eConversionType != conversion_xml) {
                /* Only implemented for XML output */
                return NULL;
        }

        bSkip = FALSE;

        /* Initialise the text buffer */
        tStorageSize = INITIAL_SIZE;
        szText = xmalloc(tStorageSize);
        tNextFree = 0;
        szText[tNextFree] = '\0';

        /* Goto the start */
        usChar = usToFootnotePosition(pFile, ulCharPosStart);
        ulCharPos = ulCharPosStart;
        ulFileOffset = ulCharPos2FileOffset(ulCharPos);
        /* Skip the unwanted starting characters */
        while (usChar != (USHORT)EOF && ulCharPos != ulCharPosNext &&
               (usChar == FOOTNOTE_OR_ENDNOTE ||
                usChar == PAR_END ||
                usChar == TAB ||
                usChar == (USHORT)' ')) {
                usChar = usNextChar(pFile, footnote_list,
                                        &ulFileOffset, &ulCharPos, NULL);
        }
        /* Process the footnote text */
        while (usChar != (USHORT)EOF && ulCharPos != ulCharPosNext) {
                /* Skip embedded characters */
                if (usChar == START_EMBEDDED) {
                        bSkip = TRUE;
                } else if (usChar == END_IGNORE || usChar == END_EMBEDDED) {
                        bSkip = FALSE;
                }
                /* Translate character */
                if (bSkip ||
                    usChar == END_IGNORE ||
                    usChar == END_EMBEDDED ||
                    usChar == FOOTNOTE_OR_ENDNOTE) {
                        ulChar = IGNORE_CHARACTER;
                } else {
                        ulChar = ulTranslateCharacters(usChar,
                                        ulFileOffset,
                                        iWordVersion,
                                        tOptions.eConversionType,
                                        tOptions.eEncoding,
                                        bOldMacFile);
                }
                /* Process character */
                if (ulChar == PICTURE) {
                        tLen = 5;
                        strcpy(szResult, "[pic]");
                } else if (ulChar == IGNORE_CHARACTER) {
                        tLen = 0;
                        szResult[0] = '\0';
                } else {
                        switch (ulChar) {
                        case PAR_END:
                        case HARD_RETURN:
                        case PAGE_BREAK:
                        case COLUMN_FEED:
                                ulChar = (ULONG)PAR_END;
                                break;
                        case TAB:
                                ulChar = (ULONG)' ';
                                break;
                        default:
                                break;
                        }
                        tLen = tUcs2Utf8(ulChar, szResult, sizeof(szResult));
                }
                /* Add the results to the text */
                if (tNextFree + tLen + 1 > tStorageSize) {
                        tStorageSize += EXTENTION_SIZE;
                        szText = xrealloc(szText, tStorageSize);
                }
                for (tIndex = 0; tIndex < tLen; tIndex++) {
                        szText[tNextFree++] = szResult[tIndex];
                }
                szText[tNextFree] = '\0';
                /* Next character */
                usChar = usNextChar(pFile, footnote_list,
                                        &ulFileOffset, &ulCharPos, NULL);
        }
        /* Remove redundant spaces */
        while (tNextFree != 0 && szText[tNextFree - 1] == ' ') {
                szText[tNextFree - 1] = '\0';
                tNextFree--;
        }
        if (tNextFree == 0) {
                /* No text */
                szText = xfree(szText);
                return NULL;
        }
        return szText;
} /* end of szFootnoteDecryptor */