mirror of
https://github.com/simon987/antiword.git
synced 2025-04-10 13:06:41 +00:00
769 lines
19 KiB
C
769 lines
19 KiB
C
/*
|
|
* 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 */
|