Subversion Repositories planix.SVN

Rev

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

/*
 * out2window.c
 * Copyright (C) 1998-2005 A.J. van Os; Released under GPL
 *
 * Description:
 * Output to a text window
 */

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

/* Used for numbering the chapters */
static unsigned int     auiHdrCounter[9];


/*
 * vString2Diagram - put a string into a diagram
 */
static void
vString2Diagram(diagram_type *pDiag, output_type *pAnchor)
{
        output_type     *pOutput;
        long            lWidth;
        USHORT          usMaxFontSize;

        TRACE_MSG("vString2Diagram");

        fail(pDiag == NULL);
        fail(pAnchor == NULL);

        /* Compute the maximum fontsize in this string */
        usMaxFontSize = MIN_FONT_SIZE;
        for (pOutput = pAnchor; pOutput != NULL; pOutput = pOutput->pNext) {
                if (pOutput->usFontSize > usMaxFontSize) {
                        usMaxFontSize = pOutput->usFontSize;
                }
        }

        /* Goto the next line */
        vMove2NextLine(pDiag, pAnchor->tFontRef, usMaxFontSize);

        /* Output all substrings */
        for (pOutput = pAnchor; pOutput != NULL; pOutput = pOutput->pNext) {
                lWidth = lMilliPoints2DrawUnits(pOutput->lStringWidth);
                vSubstring2Diagram(pDiag, pOutput->szStorage,
                        pOutput->tNextFree, lWidth, pOutput->ucFontColor,
                        pOutput->usFontStyle, pOutput->tFontRef,
                        pOutput->usFontSize, usMaxFontSize);
        }

        /* Goto the start of the line */
        pDiag->lXleft = 0;
        TRACE_MSG("leaving vString2Diagram");
} /* end of vString2Diagram */

/*
 * vSetLeftIndentation - set the left indentation of the specified diagram
 */
void
vSetLeftIndentation(diagram_type *pDiag, long lLeftIndentation)
{
        long    lX;

        TRACE_MSG("vSetLeftIndentation");

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

        lX = lMilliPoints2DrawUnits(lLeftIndentation);
        if (lX > 0) {
                pDiag->lXleft = lX;
        } else {
                pDiag->lXleft = 0;
        }
} /* end of vSetLeftIndentation */

/*
 * lComputeNetWidth - compute the net string width
 */
static long
lComputeNetWidth(output_type *pAnchor)
{
        output_type     *pTmp;
        long            lNetWidth;

        TRACE_MSG("lComputeNetWidth");

        fail(pAnchor == NULL);

        /* Step 1: Count all but the last sub-string */
        lNetWidth = 0;
        for (pTmp = pAnchor; pTmp->pNext != NULL; pTmp = pTmp->pNext) {
                fail(pTmp->lStringWidth < 0);
                lNetWidth += pTmp->lStringWidth;
        }
        fail(pTmp == NULL);
        fail(pTmp->pNext != NULL);

        /* Step 2: remove the white-space from the end of the string */
        while (pTmp->tNextFree != 0 &&
               isspace((int)(UCHAR)pTmp->szStorage[pTmp->tNextFree - 1])) {
                pTmp->szStorage[pTmp->tNextFree - 1] = '\0';
                pTmp->tNextFree--;
                NO_DBG_DEC(pTmp->lStringWidth);
                pTmp->lStringWidth = lComputeStringWidth(
                                                pTmp->szStorage,
                                                pTmp->tNextFree,
                                                pTmp->tFontRef,
                                                pTmp->usFontSize);
                NO_DBG_DEC(pTmp->lStringWidth);
        }

        /* Step 3: Count the last sub-string */
        lNetWidth += pTmp->lStringWidth;
        return lNetWidth;
} /* end of lComputeNetWidth */

/*
 * iComputeHoles - compute number of holes
 * (A hole is a number of whitespace characters followed by a
 *  non-whitespace character)
 */
static int
iComputeHoles(output_type *pAnchor)
{
        output_type     *pTmp;
        size_t  tIndex;
        int     iCounter;
        BOOL    bWasSpace, bIsSpace;

        TRACE_MSG("iComputeHoles");

        fail(pAnchor == NULL);

        iCounter = 0;
        bIsSpace = FALSE;
        /* Count the holes */
        for (pTmp = pAnchor; pTmp != NULL; pTmp = pTmp->pNext) {
                fail(pTmp->tNextFree != strlen(pTmp->szStorage));
                for (tIndex = 0; tIndex <= pTmp->tNextFree; tIndex++) {
                        bWasSpace = bIsSpace;
                        bIsSpace = isspace((int)(UCHAR)pTmp->szStorage[tIndex]);
                        if (bWasSpace && !bIsSpace) {
                                iCounter++;
                        }
                }
        }
        return iCounter;
} /* end of iComputeHoles */

/*
 * vAlign2Window - Align a string and insert it into the text
 */
void
vAlign2Window(diagram_type *pDiag, output_type *pAnchor,
        long lScreenWidth, UCHAR ucAlignment)
{
        long    lNetWidth, lLeftIndentation;

        TRACE_MSG("vAlign2Window");

        fail(pDiag == NULL || pAnchor == NULL);
        fail(lScreenWidth < lChar2MilliPoints(MIN_SCREEN_WIDTH));

        lNetWidth = lComputeNetWidth(pAnchor);

        if (lScreenWidth > lChar2MilliPoints(MAX_SCREEN_WIDTH) ||
            lNetWidth <= 0) {
                /*
                 * Screenwidth is "infinite", so no alignment is possible
                 * Don't bother to align an empty line
                 */
                vString2Diagram(pDiag, pAnchor);
                TRACE_MSG("leaving vAlign2Window #1");
                return;
        }

        switch (ucAlignment) {
        case ALIGNMENT_CENTER:
                lLeftIndentation = (lScreenWidth - lNetWidth) / 2;
                DBG_DEC_C(lLeftIndentation < 0, lLeftIndentation);
                if (lLeftIndentation > 0) {
                        vSetLeftIndentation(pDiag, lLeftIndentation);
                }
                break;
        case ALIGNMENT_RIGHT:
                lLeftIndentation = lScreenWidth - lNetWidth;
                DBG_DEC_C(lLeftIndentation < 0, lLeftIndentation);
                if (lLeftIndentation > 0) {
                        vSetLeftIndentation(pDiag, lLeftIndentation);
                }
                break;
        case ALIGNMENT_JUSTIFY:
        case ALIGNMENT_LEFT:
        default:
                break;
        }
        vString2Diagram(pDiag, pAnchor);
        TRACE_MSG("leaving vAlign2Window #2");
} /* end of vAlign2Window */

/*
 * vJustify2Window - Justify a string and insert it into the text
 */
void
vJustify2Window(diagram_type *pDiag, output_type *pAnchor,
        long lScreenWidth, long lRightIndentation, UCHAR ucAlignment)
{
        output_type     *pTmp;
        char    *pcNew, *pcOld, *szStorage;
        long    lNetWidth, lSpaceWidth, lToAdd;
        int     iFillerLen, iHoles;

        TRACE_MSG("vJustify2Window");

        fail(pDiag == NULL || pAnchor == NULL);
        fail(lScreenWidth < MIN_SCREEN_WIDTH);
        fail(lRightIndentation > 0);

        if (ucAlignment != ALIGNMENT_JUSTIFY) {
                vAlign2Window(pDiag, pAnchor, lScreenWidth, ucAlignment);
                return;
        }

        lNetWidth = lComputeNetWidth(pAnchor);

        if (lScreenWidth > lChar2MilliPoints(MAX_SCREEN_WIDTH) ||
            lNetWidth <= 0) {
                /*
                 * Screenwidth is "infinite", so justify is not possible
                 * Don't bother to justify an empty line
                 */
                vString2Diagram(pDiag, pAnchor);
                TRACE_MSG("leaving vJustify2Window #1");
                return;
        }

        /* Justify */
        fail(ucAlignment != ALIGNMENT_JUSTIFY);
        lSpaceWidth = lComputeStringWidth(" ", 1,
                                pAnchor->tFontRef, pAnchor->usFontSize);
        lToAdd = lScreenWidth -
                        lNetWidth -
                        lDrawUnits2MilliPoints(pDiag->lXleft) +
                        lRightIndentation;
#if defined(DEBUG)
        if (lToAdd / lSpaceWidth < -1) {
                DBG_DEC(lSpaceWidth);
                DBG_DEC(lToAdd);
                DBG_DEC(lScreenWidth);
                DBG_DEC(lNetWidth);
                DBG_DEC(lDrawUnits2MilliPoints(pDiag->lXleft));
                DBG_DEC(pDiag->lXleft);
                DBG_DEC(lRightIndentation);
        }
#endif /* DEBUG */
        lToAdd /= lSpaceWidth;
        DBG_DEC_C(lToAdd < 0, lToAdd);
        if (lToAdd <= 0) {
                vString2Diagram(pDiag, pAnchor);
                TRACE_MSG("leaving vJustify2Window #2");
                return;
        }

        /* Justify by adding spaces */
        iHoles = iComputeHoles(pAnchor);
        for (pTmp = pAnchor; pTmp != NULL; pTmp = pTmp->pNext) {
                fail(pTmp->tNextFree != strlen(pTmp->szStorage));
                fail(lToAdd < 0);
                szStorage = xmalloc(pTmp->tNextFree + (size_t)lToAdd + 1);
                pcNew = szStorage;
                for (pcOld = pTmp->szStorage; *pcOld != '\0'; pcOld++) {
                        *pcNew++ = *pcOld;
                        if (*pcOld == ' ' &&
                            *(pcOld + 1) != ' ' &&
                            iHoles > 0) {
                                iFillerLen = (int)(lToAdd / iHoles);
                                lToAdd -= iFillerLen;
                                iHoles--;
                                for (; iFillerLen > 0; iFillerLen--) {
                                        *pcNew++ = ' ';
                                }
                        }
                }
                *pcNew = '\0';
                pTmp->szStorage = xfree(pTmp->szStorage);
                pTmp->szStorage = szStorage;
                pTmp->tStorageSize = pTmp->tNextFree + (size_t)lToAdd + 1;
                pTmp->lStringWidth +=
                        (pcNew - szStorage - (long)pTmp->tNextFree) *
                        lSpaceWidth;
                fail(pcNew < szStorage);
                pTmp->tNextFree = (size_t)(pcNew - szStorage);
                fail(pTmp->tNextFree != strlen(pTmp->szStorage));
        }
        DBG_DEC_C(lToAdd != 0, lToAdd);
        vString2Diagram(pDiag, pAnchor);
        TRACE_MSG("leaving vJustify2Window #3");
} /* end of vJustify2Window */

/*
 * vResetStyles - reset the style information variables
 */
void
vResetStyles(void)
{
        TRACE_MSG("vResetStyles");

        (void)memset(auiHdrCounter, 0, sizeof(auiHdrCounter));
} /* end of vResetStyles */

/*
 * tStyle2Window - Add the style characters to the line
 *
 * Returns the length of the resulting string
 */
size_t
tStyle2Window(char *szLine, size_t tLineSize, const style_block_type *pStyle,
        const section_block_type *pSection)
{
        char    *pcTxt;
        size_t  tIndex, tStyleIndex;
        BOOL    bNeedPrevLvl;
        level_type_enum eNumType;
        UCHAR   ucNFC;

        TRACE_MSG("tStyle2Window");

        fail(szLine == NULL || pStyle == NULL || pSection == NULL);

        if (pStyle->usIstd == 0 || pStyle->usIstd > 9) {
                szLine[0] = '\0';
                return 0;
        }

        /* Set the numbers */
        tStyleIndex = (size_t)pStyle->usIstd - 1;
        for (tIndex = 0; tIndex < 9; tIndex++) {
                if (tIndex == tStyleIndex) {
                        auiHdrCounter[tIndex]++;
                } else if (tIndex > tStyleIndex) {
                        auiHdrCounter[tIndex] = 0;
                } else if (auiHdrCounter[tIndex] == 0) {
                        auiHdrCounter[tIndex] = 1;
                }
        }

        eNumType = eGetNumType(pStyle->ucNumLevel);
        if (eNumType != level_type_outline) {
                szLine[0] = '\0';
                return 0;
        }

        /* Print the numbers */
        pcTxt = szLine;
        bNeedPrevLvl = (pSection->usNeedPrevLvl & BIT(tStyleIndex)) != 0;
        for (tIndex = 0; tIndex <= tStyleIndex; tIndex++) {
                if (tIndex == tStyleIndex ||
                    (bNeedPrevLvl && tIndex < tStyleIndex)) {
                        if (pcTxt - szLine >= tLineSize - 25) {
                                /* Prevent a possible buffer overflow */
                                DBG_DEC(pcTxt - szLine);
                                DBG_DEC(tLineSize - 25);
                                DBG_FIXME();
                                szLine[0] = '\0';
                                return 0;
                        }
                        ucNFC = pSection->aucNFC[tIndex];
                        switch(ucNFC) {
                        case LIST_ARABIC_NUM:
                        case LIST_NUMBER_TXT:
                        case LIST_ORDINAL_TXT:
                                pcTxt += sprintf(pcTxt, "%u",
                                        auiHdrCounter[tIndex]);
                                break;
                        case LIST_UPPER_ROMAN:
                        case LIST_LOWER_ROMAN:
                                pcTxt += tNumber2Roman(
                                        auiHdrCounter[tIndex],
                                        ucNFC == LIST_UPPER_ROMAN,
                                        pcTxt);
                                break;
                        case LIST_UPPER_ALPHA:
                        case LIST_LOWER_ALPHA:
                                pcTxt += tNumber2Alpha(
                                        auiHdrCounter[tIndex],
                                        ucNFC == LIST_UPPER_ALPHA,
                                        pcTxt);
                                break;
                        case LIST_OUTLINE_NUM:
                                pcTxt += sprintf(pcTxt, "%02u",
                                        auiHdrCounter[tIndex]);
                                break;
                        default:
                                DBG_DEC(ucNFC);
                                DBG_FIXME();
                                pcTxt += sprintf(pcTxt, "%u",
                                        auiHdrCounter[tIndex]);
                                break;
                        }
                        if (tIndex < tStyleIndex) {
                                *pcTxt++ = '.';
                        } else if (tIndex == tStyleIndex) {
                                *pcTxt++ = ' ';
                        }
                }
        }
        *pcTxt = '\0';
        NO_DBG_MSG_C((int)pStyle->usIstd >= 1 &&
                (int)pStyle->usIstd <= 9 &&
                eNumType != level_type_none &&
                eNumType != level_type_outline, szLine);
        NO_DBG_MSG_C(szLine[0] != '\0', szLine);
        fail(pcTxt < szLine);
        return (size_t)(pcTxt - szLine);
} /* end of tStyle2Window */

/*
 * vRemoveRowEnd - remove the end of table row indicator
 *
 * Remove the double TABLE_SEPARATOR characters from the end of the string.
 * Special: remove the TABLE_SEPARATOR, 0x0a sequence
 */
static void
vRemoveRowEnd(char *szRowTxt)
{
        int     iLastIndex;

        TRACE_MSG("vRemoveRowEnd");

        fail(szRowTxt == NULL || szRowTxt[0] == '\0');

        iLastIndex = (int)strlen(szRowTxt) - 1;

        if (szRowTxt[iLastIndex] == TABLE_SEPARATOR ||
            szRowTxt[iLastIndex] == (char)0x0a) {
                szRowTxt[iLastIndex] = '\0';
                iLastIndex--;
        } else {
                DBG_HEX(szRowTxt[iLastIndex]);
        }

        if (iLastIndex >= 0 && szRowTxt[iLastIndex] == (char)0x0a) {
                szRowTxt[iLastIndex] = '\0';
                iLastIndex--;
        }

        if (iLastIndex >= 0 && szRowTxt[iLastIndex] == TABLE_SEPARATOR) {
                szRowTxt[iLastIndex] = '\0';
                return;
        }

        DBG_DEC(iLastIndex);
        DBG_HEX(szRowTxt[iLastIndex]);
        DBG_MSG(szRowTxt);
} /* end of vRemoveRowEnd */

/*
 * tComputeStringLengthMax - max string length in relation to max column width
 *
 * Return the maximum string length
 */
static size_t
tComputeStringLengthMax(const char *szString, size_t tColumnWidthMax)
{
        const char      *pcTmp;
        size_t  tLengthMax, tLenPrev, tLen, tWidth;

        TRACE_MSG("tComputeStringLengthMax");

        fail(szString == NULL);
        fail(tColumnWidthMax == 0);

        pcTmp = strchr(szString, '\n');
        if (pcTmp != NULL) {
                tLengthMax = (size_t)(pcTmp - szString + 1);
        } else {
                tLengthMax = strlen(szString);
        }
        if (tLengthMax == 0) {
                return 0;
        }

        tLen = 0;
        tWidth = 0;
        for (;;) {
                tLenPrev = tLen;
                tLen += tGetCharacterLength(szString + tLen);
                DBG_DEC_C(tLen > tLengthMax, tLen);
                DBG_DEC_C(tLen > tLengthMax, tLengthMax);
                fail(tLen > tLengthMax);
                tWidth = tCountColumns(szString, tLen);
                if (tWidth > tColumnWidthMax) {
                        return tLenPrev;
                }
                if (tLen >= tLengthMax) {
                        return tLengthMax;
                }
        }
} /* end of tComputeStringLengthMax */

/*
 * tGetBreakingPoint - get the number of bytes that fit the column
 *
 * Returns the number of bytes that fit the column
 */
static size_t
tGetBreakingPoint(const char *szString,
        size_t tLen, size_t tWidth, size_t tColumnWidthMax)
{
        int     iIndex;

        TRACE_MSG("tGetBreakingPoint");

        fail(szString == NULL);
        fail(tLen > strlen(szString));
        fail(tWidth > tColumnWidthMax);

        if (tWidth < tColumnWidthMax ||
            (tWidth == tColumnWidthMax &&
             (szString[tLen] == ' ' ||
              szString[tLen] == '\n' ||
              szString[tLen] == '\0'))) {
                /* The string already fits, do nothing */
                return tLen;
        }
        /* Search for a breaking point */
        for (iIndex = (int)tLen - 1; iIndex >= 0; iIndex--) {
                if (szString[iIndex] == ' ') {
                        return (size_t)iIndex;
                }
        }
        /* No breaking point found, just fill the column */
        return tLen;
} /* end of tGetBreakingPoint */

/*
 * tComputeColumnWidthMax - compute the maximum column width
 */
static size_t
tComputeColumnWidthMax(short sWidth, long lCharWidth, double dFactor)
{
        size_t  tColumnWidthMax;

        TRACE_MSG("tComputeColumnWidthMax");

        fail(sWidth < 0);
        fail(lCharWidth <= 0);
        fail(dFactor <= 0.0);

        tColumnWidthMax = (size_t)(
                (lTwips2MilliPoints(sWidth) * dFactor + lCharWidth / 2.0) /
                 lCharWidth);
        if (tColumnWidthMax == 0) {
                /* Minimum column width */
                return 1;
        }
        if (tColumnWidthMax > 1) {
                /* Make room for the TABLE_SEPARATOR_CHAR */
                tColumnWidthMax--;
        }
        NO_DBG_DEC(tColumnWidthMax);
        return tColumnWidthMax;
} /* end of tComputeColumnWidthMax */

/*
 * vTableRow2Window - put a table row into a diagram
 */
void
vTableRow2Window(diagram_type *pDiag, output_type *pOutput,
        const row_block_type *pRowInfo,
        conversion_type eConversionType, int iParagraphBreak)
{
        output_type     tRow;
        char    *aszColTxt[TABLE_COLUMN_MAX];
        char    *szLine, *pcTxt;
        double  dMagnify;
        long    lCharWidthLarge, lCharWidthSmall;
        size_t  tColumnWidthTotal, atColumnWidthMax[TABLE_COLUMN_MAX];
        size_t  tSize, tColumnWidthMax, tWidth, tLen;
        int     iIndex, iNbrOfColumns, iTmp;
        BOOL    bNotReady;

        TRACE_MSG("vTableRow2Window");

        fail(pDiag == NULL || pOutput == NULL || pRowInfo == NULL);
        fail(pOutput->szStorage == NULL);
        fail(pOutput->pNext != NULL);
        fail(iParagraphBreak < 0);

        /* Character sizes */
        lCharWidthLarge = lComputeStringWidth("W", 1,
                                pOutput->tFontRef, pOutput->usFontSize);
        NO_DBG_DEC(lCharWidthLarge);
        lCharWidthSmall = lComputeStringWidth("i", 1,
                                pOutput->tFontRef, pOutput->usFontSize);
        NO_DBG_DEC(lCharWidthSmall);
        /* For the time being: use a fixed width font */
        fail(lCharWidthLarge != lCharWidthSmall);

        vRemoveRowEnd(pOutput->szStorage);

        /* Split the row text into a set of column texts */
        aszColTxt[0] = pOutput->szStorage;
        for (iNbrOfColumns = 1;
             iNbrOfColumns < TABLE_COLUMN_MAX;
             iNbrOfColumns++) {
                aszColTxt[iNbrOfColumns] =
                                strchr(aszColTxt[iNbrOfColumns - 1],
                                        TABLE_SEPARATOR);
                if (aszColTxt[iNbrOfColumns] == NULL) {
                        break;
                }
                *aszColTxt[iNbrOfColumns] = '\0';
                aszColTxt[iNbrOfColumns]++;
                NO_DBG_DEC(iNbrOfColumns);
                NO_DBG_MSG(aszColTxt[iNbrOfColumns]);
        }

        /* Work around a bug in Word */
        while (iNbrOfColumns > (int)pRowInfo->ucNumberOfColumns &&
               pRowInfo->asColumnWidth[iNbrOfColumns] == 0) {
                iNbrOfColumns--;
        }

        DBG_DEC_C(iNbrOfColumns != (int)pRowInfo->ucNumberOfColumns,
                iNbrOfColumns);
        DBG_DEC_C(iNbrOfColumns != (int)pRowInfo->ucNumberOfColumns,
                pRowInfo->ucNumberOfColumns);
        if (iNbrOfColumns != (int)pRowInfo->ucNumberOfColumns) {
                werr(0, "Skipping an unmatched table row");
                return;
        }

#if defined(__FULL_TEXT_SEARCH)
        /* No table formatting: use for full-text search (untested) */
        for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
                fprintf(pDiag->pOutFile, "%s\n" , aszColTxt[iIndex]);
        }
#else
        if (bAddTableRow(pDiag, aszColTxt, iNbrOfColumns,
                        pRowInfo->asColumnWidth, pRowInfo->ucBorderInfo)) {
                /* All work has been done */
                return;
        }

        /* Fill the table with maximum column widths */
        if (eConversionType == conversion_text ||
            eConversionType == conversion_fmt_text) {
                if (iParagraphBreak == 0 ||
                    iParagraphBreak >= MAX_SCREEN_WIDTH) {
                        dMagnify = (double)MAX_SCREEN_WIDTH;
                } else if (iParagraphBreak <= MIN_SCREEN_WIDTH) {
                        dMagnify = (double)MIN_SCREEN_WIDTH;
                } else {
                        dMagnify = (double)iParagraphBreak;
                }
                dMagnify /= (double)DEFAULT_SCREEN_WIDTH;
                DBG_FLT_C(dMagnify < 0.99 || dMagnify > 1.01, dMagnify);
        } else {
                dMagnify = 1.0;
        }
        tColumnWidthTotal = 0;
        for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
                atColumnWidthMax[iIndex] = tComputeColumnWidthMax(
                                        pRowInfo->asColumnWidth[iIndex],
                                        lCharWidthLarge,
                                        dMagnify);
                tColumnWidthTotal += atColumnWidthMax[iIndex];
        }

        /*
         * Get enough space for the row.
         * Worst case: three bytes per UTF-8 character
         */
        tSize = 3 * (1 + tColumnWidthTotal + (size_t)iNbrOfColumns + 3);
        szLine = xmalloc(tSize);

        do {
                /* Print one line of a table row */
                bNotReady = FALSE;
                pcTxt = szLine;
                *pcTxt++ = TABLE_SEPARATOR_CHAR;
                for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
                        tColumnWidthMax = atColumnWidthMax[iIndex];
                        if (aszColTxt[iIndex] == NULL) {
                                /* Add an empty column */
                                for (iTmp = 0;
                                     iTmp < (int)tColumnWidthMax;
                                     iTmp++) {
                                        *pcTxt++ = (char)FILLER_CHAR;
                                }
                                *pcTxt++ = TABLE_SEPARATOR_CHAR;
                                *pcTxt = '\0';
                                continue;
                        }
                        /* Compute the length and width of the column text */
                        tLen = tComputeStringLengthMax(
                                        aszColTxt[iIndex], tColumnWidthMax);
                        NO_DBG_DEC(tLen);
                        while (tLen != 0 &&
                                        (aszColTxt[iIndex][tLen - 1] == '\n' ||
                                         aszColTxt[iIndex][tLen - 1] == ' ')) {
                                aszColTxt[iIndex][tLen - 1] = ' ';
                                tLen--;
                        }
                        tWidth = tCountColumns(aszColTxt[iIndex], tLen);
                        fail(tWidth > tColumnWidthMax);
                        tLen = tGetBreakingPoint(aszColTxt[iIndex],
                                        tLen, tWidth, tColumnWidthMax);
                        tWidth = tCountColumns(aszColTxt[iIndex], tLen);
                        if (tLen == 0 && *aszColTxt[iIndex] == '\0') {
                                /* No text at all */
                                aszColTxt[iIndex] = NULL;
                        } else {
                                /* Add the text */
                                pcTxt += sprintf(pcTxt,
                                        "%.*s", (int)tLen, aszColTxt[iIndex]);
                                if (tLen == 0 && *aszColTxt[iIndex] != ' ') {
                                        tLen = tGetCharacterLength(
                                                        aszColTxt[iIndex]);
                                        DBG_CHR(*aszColTxt[iIndex]);
                                        DBG_FIXME();
                                        fail(tLen == 0);
                                }
                                aszColTxt[iIndex] += tLen;
                                while (*aszColTxt[iIndex] == ' ') {
                                        aszColTxt[iIndex]++;
                                }
                                if (*aszColTxt[iIndex] == '\0') {
                                        /* This row is now complete */
                                        aszColTxt[iIndex] = NULL;
                                } else {
                                        /* This row needs more lines */
                                        bNotReady = TRUE;
                                }
                        }
                        /* Fill up the rest */
                        for (iTmp = 0;
                             iTmp < (int)tColumnWidthMax - (int)tWidth;
                             iTmp++) {
                                *pcTxt++ = (char)FILLER_CHAR;
                        }
                        /* End of column */
                        *pcTxt++ = TABLE_SEPARATOR_CHAR;
                        *pcTxt = '\0';
                }
                /* Output the table row line */
                *pcTxt = '\0';
                tRow = *pOutput;
                tRow.szStorage = szLine;
                fail(pcTxt < szLine);
                tRow.tNextFree = (size_t)(pcTxt - szLine);
                tRow.lStringWidth = lComputeStringWidth(
                                        tRow.szStorage,
                                        tRow.tNextFree,
                                        tRow.tFontRef,
                                        tRow.usFontSize);
                vString2Diagram(pDiag, &tRow);
                TRACE_MSG("after vString2Diagram in vTableRow2Window");
        } while (bNotReady);
        /* Clean up before you leave */
        szLine = xfree(szLine);
        TRACE_MSG("leaving vTableRow2Window");
#endif /* __FULL_TEXT_SEARCH */
} /* end of vTableRow2Window */