xref: /plan9/sys/src/cmd/aux/antiword/out2window.c (revision 25b329d522281a8cdd35da0dcc08c3fc621059a9)
1 /*
2  * out2window.c
3  * Copyright (C) 1998-2005 A.J. van Os; Released under GPL
4  *
5  * Description:
6  * Output to a text window
7  */
8 
9 #include <string.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include "antiword.h"
13 
14 /* Used for numbering the chapters */
15 static unsigned int	auiHdrCounter[9];
16 
17 
18 /*
19  * vString2Diagram - put a string into a diagram
20  */
21 static void
vString2Diagram(diagram_type * pDiag,output_type * pAnchor)22 vString2Diagram(diagram_type *pDiag, output_type *pAnchor)
23 {
24 	output_type	*pOutput;
25 	long		lWidth;
26 	USHORT		usMaxFontSize;
27 
28 	TRACE_MSG("vString2Diagram");
29 
30 	fail(pDiag == NULL);
31 	fail(pAnchor == NULL);
32 
33 	/* Compute the maximum fontsize in this string */
34 	usMaxFontSize = MIN_FONT_SIZE;
35 	for (pOutput = pAnchor; pOutput != NULL; pOutput = pOutput->pNext) {
36 		if (pOutput->usFontSize > usMaxFontSize) {
37 			usMaxFontSize = pOutput->usFontSize;
38 		}
39 	}
40 
41 	/* Goto the next line */
42 	vMove2NextLine(pDiag, pAnchor->tFontRef, usMaxFontSize);
43 
44 	/* Output all substrings */
45 	for (pOutput = pAnchor; pOutput != NULL; pOutput = pOutput->pNext) {
46 		lWidth = lMilliPoints2DrawUnits(pOutput->lStringWidth);
47 		vSubstring2Diagram(pDiag, pOutput->szStorage,
48 			pOutput->tNextFree, lWidth, pOutput->ucFontColor,
49 			pOutput->usFontStyle, pOutput->tFontRef,
50 			pOutput->usFontSize, usMaxFontSize);
51 	}
52 
53 	/* Goto the start of the line */
54 	pDiag->lXleft = 0;
55 	TRACE_MSG("leaving vString2Diagram");
56 } /* end of vString2Diagram */
57 
58 /*
59  * vSetLeftIndentation - set the left indentation of the specified diagram
60  */
61 void
vSetLeftIndentation(diagram_type * pDiag,long lLeftIndentation)62 vSetLeftIndentation(diagram_type *pDiag, long lLeftIndentation)
63 {
64 	long	lX;
65 
66 	TRACE_MSG("vSetLeftIndentation");
67 
68 	fail(pDiag == NULL);
69 	fail(lLeftIndentation < 0);
70 
71 	lX = lMilliPoints2DrawUnits(lLeftIndentation);
72 	if (lX > 0) {
73 		pDiag->lXleft = lX;
74 	} else {
75 		pDiag->lXleft = 0;
76 	}
77 } /* end of vSetLeftIndentation */
78 
79 /*
80  * lComputeNetWidth - compute the net string width
81  */
82 static long
lComputeNetWidth(output_type * pAnchor)83 lComputeNetWidth(output_type *pAnchor)
84 {
85 	output_type	*pTmp;
86 	long		lNetWidth;
87 
88 	TRACE_MSG("lComputeNetWidth");
89 
90 	fail(pAnchor == NULL);
91 
92 	/* Step 1: Count all but the last sub-string */
93 	lNetWidth = 0;
94 	for (pTmp = pAnchor; pTmp->pNext != NULL; pTmp = pTmp->pNext) {
95 		fail(pTmp->lStringWidth < 0);
96 		lNetWidth += pTmp->lStringWidth;
97 	}
98 	fail(pTmp == NULL);
99 	fail(pTmp->pNext != NULL);
100 
101 	/* Step 2: remove the white-space from the end of the string */
102 	while (pTmp->tNextFree != 0 &&
103 	       isspace((int)(UCHAR)pTmp->szStorage[pTmp->tNextFree - 1])) {
104 		pTmp->szStorage[pTmp->tNextFree - 1] = '\0';
105 		pTmp->tNextFree--;
106 		NO_DBG_DEC(pTmp->lStringWidth);
107 		pTmp->lStringWidth = lComputeStringWidth(
108 						pTmp->szStorage,
109 						pTmp->tNextFree,
110 						pTmp->tFontRef,
111 						pTmp->usFontSize);
112 		NO_DBG_DEC(pTmp->lStringWidth);
113 	}
114 
115 	/* Step 3: Count the last sub-string */
116 	lNetWidth += pTmp->lStringWidth;
117 	return lNetWidth;
118 } /* end of lComputeNetWidth */
119 
120 /*
121  * iComputeHoles - compute number of holes
122  * (A hole is a number of whitespace characters followed by a
123  *  non-whitespace character)
124  */
125 static int
iComputeHoles(output_type * pAnchor)126 iComputeHoles(output_type *pAnchor)
127 {
128 	output_type	*pTmp;
129 	size_t	tIndex;
130 	int	iCounter;
131 	BOOL	bWasSpace, bIsSpace;
132 
133 	TRACE_MSG("iComputeHoles");
134 
135 	fail(pAnchor == NULL);
136 
137 	iCounter = 0;
138 	bIsSpace = FALSE;
139 	/* Count the holes */
140 	for (pTmp = pAnchor; pTmp != NULL; pTmp = pTmp->pNext) {
141 		fail(pTmp->tNextFree != strlen(pTmp->szStorage));
142 		for (tIndex = 0; tIndex <= pTmp->tNextFree; tIndex++) {
143 			bWasSpace = bIsSpace;
144 			bIsSpace = isspace((int)(UCHAR)pTmp->szStorage[tIndex]);
145 			if (bWasSpace && !bIsSpace) {
146 				iCounter++;
147 			}
148 		}
149 	}
150 	return iCounter;
151 } /* end of iComputeHoles */
152 
153 /*
154  * vAlign2Window - Align a string and insert it into the text
155  */
156 void
vAlign2Window(diagram_type * pDiag,output_type * pAnchor,long lScreenWidth,UCHAR ucAlignment)157 vAlign2Window(diagram_type *pDiag, output_type *pAnchor,
158 	long lScreenWidth, UCHAR ucAlignment)
159 {
160 	long	lNetWidth, lLeftIndentation;
161 
162 	TRACE_MSG("vAlign2Window");
163 
164 	fail(pDiag == NULL || pAnchor == NULL);
165 	fail(lScreenWidth < lChar2MilliPoints(MIN_SCREEN_WIDTH));
166 
167 	lNetWidth = lComputeNetWidth(pAnchor);
168 
169 	if (lScreenWidth > lChar2MilliPoints(MAX_SCREEN_WIDTH) ||
170 	    lNetWidth <= 0) {
171 		/*
172 		 * Screenwidth is "infinite", so no alignment is possible
173 		 * Don't bother to align an empty line
174 		 */
175 		vString2Diagram(pDiag, pAnchor);
176 		TRACE_MSG("leaving vAlign2Window #1");
177 		return;
178 	}
179 
180 	switch (ucAlignment) {
181 	case ALIGNMENT_CENTER:
182 		lLeftIndentation = (lScreenWidth - lNetWidth) / 2;
183 		DBG_DEC_C(lLeftIndentation < 0, lLeftIndentation);
184 		if (lLeftIndentation > 0) {
185 			vSetLeftIndentation(pDiag, lLeftIndentation);
186 		}
187 		break;
188 	case ALIGNMENT_RIGHT:
189 		lLeftIndentation = lScreenWidth - lNetWidth;
190 		DBG_DEC_C(lLeftIndentation < 0, lLeftIndentation);
191 		if (lLeftIndentation > 0) {
192 			vSetLeftIndentation(pDiag, lLeftIndentation);
193 		}
194 		break;
195 	case ALIGNMENT_JUSTIFY:
196 	case ALIGNMENT_LEFT:
197 	default:
198 		break;
199 	}
200 	vString2Diagram(pDiag, pAnchor);
201 	TRACE_MSG("leaving vAlign2Window #2");
202 } /* end of vAlign2Window */
203 
204 /*
205  * vJustify2Window - Justify a string and insert it into the text
206  */
207 void
vJustify2Window(diagram_type * pDiag,output_type * pAnchor,long lScreenWidth,long lRightIndentation,UCHAR ucAlignment)208 vJustify2Window(diagram_type *pDiag, output_type *pAnchor,
209 	long lScreenWidth, long lRightIndentation, UCHAR ucAlignment)
210 {
211 	output_type	*pTmp;
212 	char	*pcNew, *pcOld, *szStorage;
213 	long	lNetWidth, lSpaceWidth, lToAdd;
214 	int	iFillerLen, iHoles;
215 
216 	TRACE_MSG("vJustify2Window");
217 
218 	fail(pDiag == NULL || pAnchor == NULL);
219 	fail(lScreenWidth < MIN_SCREEN_WIDTH);
220 	fail(lRightIndentation > 0);
221 
222 	if (ucAlignment != ALIGNMENT_JUSTIFY) {
223 		vAlign2Window(pDiag, pAnchor, lScreenWidth, ucAlignment);
224 		return;
225 	}
226 
227 	lNetWidth = lComputeNetWidth(pAnchor);
228 
229 	if (lScreenWidth > lChar2MilliPoints(MAX_SCREEN_WIDTH) ||
230 	    lNetWidth <= 0) {
231 		/*
232 		 * Screenwidth is "infinite", so justify is not possible
233 		 * Don't bother to justify an empty line
234 		 */
235 		vString2Diagram(pDiag, pAnchor);
236 		TRACE_MSG("leaving vJustify2Window #1");
237 		return;
238 	}
239 
240 	/* Justify */
241 	fail(ucAlignment != ALIGNMENT_JUSTIFY);
242 	lSpaceWidth = lComputeStringWidth(" ", 1,
243 				pAnchor->tFontRef, pAnchor->usFontSize);
244 	lToAdd = lScreenWidth -
245 			lNetWidth -
246 			lDrawUnits2MilliPoints(pDiag->lXleft) +
247 			lRightIndentation;
248 #if defined(DEBUG)
249 	if (lToAdd / lSpaceWidth < -1) {
250 		DBG_DEC(lSpaceWidth);
251 		DBG_DEC(lToAdd);
252 		DBG_DEC(lScreenWidth);
253 		DBG_DEC(lNetWidth);
254 		DBG_DEC(lDrawUnits2MilliPoints(pDiag->lXleft));
255 		DBG_DEC(pDiag->lXleft);
256 		DBG_DEC(lRightIndentation);
257 	}
258 #endif /* DEBUG */
259 	lToAdd /= lSpaceWidth;
260 	DBG_DEC_C(lToAdd < 0, lToAdd);
261 	if (lToAdd <= 0) {
262 		vString2Diagram(pDiag, pAnchor);
263 		TRACE_MSG("leaving vJustify2Window #2");
264 		return;
265 	}
266 
267 	/* Justify by adding spaces */
268 	iHoles = iComputeHoles(pAnchor);
269 	for (pTmp = pAnchor; pTmp != NULL; pTmp = pTmp->pNext) {
270 		fail(pTmp->tNextFree != strlen(pTmp->szStorage));
271 		fail(lToAdd < 0);
272 		szStorage = xmalloc(pTmp->tNextFree + (size_t)lToAdd + 1);
273 		pcNew = szStorage;
274 		for (pcOld = pTmp->szStorage; *pcOld != '\0'; pcOld++) {
275 			*pcNew++ = *pcOld;
276 			if (*pcOld == ' ' &&
277 			    *(pcOld + 1) != ' ' &&
278 			    iHoles > 0) {
279 				iFillerLen = (int)(lToAdd / iHoles);
280 				lToAdd -= iFillerLen;
281 				iHoles--;
282 				for (; iFillerLen > 0; iFillerLen--) {
283 					*pcNew++ = ' ';
284 				}
285 			}
286 		}
287 		*pcNew = '\0';
288 		pTmp->szStorage = xfree(pTmp->szStorage);
289 		pTmp->szStorage = szStorage;
290 		pTmp->tStorageSize = pTmp->tNextFree + (size_t)lToAdd + 1;
291 		pTmp->lStringWidth +=
292 			(pcNew - szStorage - (long)pTmp->tNextFree) *
293 			lSpaceWidth;
294 		fail(pcNew < szStorage);
295 		pTmp->tNextFree = (size_t)(pcNew - szStorage);
296 		fail(pTmp->tNextFree != strlen(pTmp->szStorage));
297 	}
298 	DBG_DEC_C(lToAdd != 0, lToAdd);
299 	vString2Diagram(pDiag, pAnchor);
300 	TRACE_MSG("leaving vJustify2Window #3");
301 } /* end of vJustify2Window */
302 
303 /*
304  * vResetStyles - reset the style information variables
305  */
306 void
vResetStyles(void)307 vResetStyles(void)
308 {
309 	TRACE_MSG("vResetStyles");
310 
311 	(void)memset(auiHdrCounter, 0, sizeof(auiHdrCounter));
312 } /* end of vResetStyles */
313 
314 /*
315  * tStyle2Window - Add the style characters to the line
316  *
317  * Returns the length of the resulting string
318  */
319 size_t
tStyle2Window(char * szLine,size_t tLineSize,const style_block_type * pStyle,const section_block_type * pSection)320 tStyle2Window(char *szLine, size_t tLineSize, const style_block_type *pStyle,
321 	const section_block_type *pSection)
322 {
323 	char	*pcTxt;
324 	size_t	tIndex, tStyleIndex;
325 	BOOL	bNeedPrevLvl;
326 	level_type_enum	eNumType;
327 	UCHAR	ucNFC;
328 
329 	TRACE_MSG("tStyle2Window");
330 
331 	fail(szLine == NULL || pStyle == NULL || pSection == NULL);
332 
333 	if (pStyle->usIstd == 0 || pStyle->usIstd > 9) {
334 		szLine[0] = '\0';
335 		return 0;
336 	}
337 
338 	/* Set the numbers */
339 	tStyleIndex = (size_t)pStyle->usIstd - 1;
340 	for (tIndex = 0; tIndex < 9; tIndex++) {
341 		if (tIndex == tStyleIndex) {
342 			auiHdrCounter[tIndex]++;
343 		} else if (tIndex > tStyleIndex) {
344 			auiHdrCounter[tIndex] = 0;
345 		} else if (auiHdrCounter[tIndex] == 0) {
346 			auiHdrCounter[tIndex] = 1;
347 		}
348 	}
349 
350 	eNumType = eGetNumType(pStyle->ucNumLevel);
351 	if (eNumType != level_type_outline) {
352 		szLine[0] = '\0';
353 		return 0;
354 	}
355 
356 	/* Print the numbers */
357 	pcTxt = szLine;
358 	bNeedPrevLvl = (pSection->usNeedPrevLvl & BIT(tStyleIndex)) != 0;
359 	for (tIndex = 0; tIndex <= tStyleIndex; tIndex++) {
360 		if (tIndex == tStyleIndex ||
361 		    (bNeedPrevLvl && tIndex < tStyleIndex)) {
362 			if (pcTxt - szLine >= tLineSize - 25) {
363 				/* Prevent a possible buffer overflow */
364 				DBG_DEC(pcTxt - szLine);
365 				DBG_DEC(tLineSize - 25);
366 				DBG_FIXME();
367 				szLine[0] = '\0';
368 				return 0;
369 			}
370 			ucNFC = pSection->aucNFC[tIndex];
371 			switch(ucNFC) {
372 			case LIST_ARABIC_NUM:
373 			case LIST_NUMBER_TXT:
374 			case LIST_ORDINAL_TXT:
375 				pcTxt += sprintf(pcTxt, "%u",
376 					auiHdrCounter[tIndex]);
377 				break;
378 			case LIST_UPPER_ROMAN:
379 			case LIST_LOWER_ROMAN:
380 				pcTxt += tNumber2Roman(
381 					auiHdrCounter[tIndex],
382 					ucNFC == LIST_UPPER_ROMAN,
383 					pcTxt);
384 				break;
385 			case LIST_UPPER_ALPHA:
386 			case LIST_LOWER_ALPHA:
387 				pcTxt += tNumber2Alpha(
388 					auiHdrCounter[tIndex],
389 					ucNFC == LIST_UPPER_ALPHA,
390 					pcTxt);
391 				break;
392 			case LIST_OUTLINE_NUM:
393 				pcTxt += sprintf(pcTxt, "%02u",
394 					auiHdrCounter[tIndex]);
395 				break;
396 			default:
397 				DBG_DEC(ucNFC);
398 				DBG_FIXME();
399 				pcTxt += sprintf(pcTxt, "%u",
400 					auiHdrCounter[tIndex]);
401 				break;
402 			}
403 			if (tIndex < tStyleIndex) {
404 				*pcTxt++ = '.';
405 			} else if (tIndex == tStyleIndex) {
406 				*pcTxt++ = ' ';
407 			}
408 		}
409 	}
410 	*pcTxt = '\0';
411 	NO_DBG_MSG_C((int)pStyle->usIstd >= 1 &&
412 		(int)pStyle->usIstd <= 9 &&
413 		eNumType != level_type_none &&
414 		eNumType != level_type_outline, szLine);
415 	NO_DBG_MSG_C(szLine[0] != '\0', szLine);
416 	fail(pcTxt < szLine);
417 	return (size_t)(pcTxt - szLine);
418 } /* end of tStyle2Window */
419 
420 /*
421  * vRemoveRowEnd - remove the end of table row indicator
422  *
423  * Remove the double TABLE_SEPARATOR characters from the end of the string.
424  * Special: remove the TABLE_SEPARATOR, 0x0a sequence
425  */
426 static void
vRemoveRowEnd(char * szRowTxt)427 vRemoveRowEnd(char *szRowTxt)
428 {
429 	int	iLastIndex;
430 
431 	TRACE_MSG("vRemoveRowEnd");
432 
433 	fail(szRowTxt == NULL || szRowTxt[0] == '\0');
434 
435 	iLastIndex = (int)strlen(szRowTxt) - 1;
436 
437 	if (szRowTxt[iLastIndex] == TABLE_SEPARATOR ||
438 	    szRowTxt[iLastIndex] == (char)0x0a) {
439 		szRowTxt[iLastIndex] = '\0';
440 		iLastIndex--;
441 	} else {
442 		DBG_HEX(szRowTxt[iLastIndex]);
443 	}
444 
445 	if (iLastIndex >= 0 && szRowTxt[iLastIndex] == (char)0x0a) {
446 		szRowTxt[iLastIndex] = '\0';
447 		iLastIndex--;
448 	}
449 
450 	if (iLastIndex >= 0 && szRowTxt[iLastIndex] == TABLE_SEPARATOR) {
451 		szRowTxt[iLastIndex] = '\0';
452 		return;
453 	}
454 
455 	DBG_DEC(iLastIndex);
456 	DBG_HEX(szRowTxt[iLastIndex]);
457 	DBG_MSG(szRowTxt);
458 } /* end of vRemoveRowEnd */
459 
460 /*
461  * tComputeStringLengthMax - max string length in relation to max column width
462  *
463  * Return the maximum string length
464  */
465 static size_t
tComputeStringLengthMax(const char * szString,size_t tColumnWidthMax)466 tComputeStringLengthMax(const char *szString, size_t tColumnWidthMax)
467 {
468 	const char	*pcTmp;
469 	size_t	tLengthMax, tLenPrev, tLen, tWidth;
470 
471 	TRACE_MSG("tComputeStringLengthMax");
472 
473 	fail(szString == NULL);
474 	fail(tColumnWidthMax == 0);
475 
476 	pcTmp = strchr(szString, '\n');
477 	if (pcTmp != NULL) {
478 		tLengthMax = (size_t)(pcTmp - szString + 1);
479 	} else {
480 		tLengthMax = strlen(szString);
481 	}
482 	if (tLengthMax == 0) {
483 		return 0;
484 	}
485 
486 	tLen = 0;
487 	tWidth = 0;
488 	for (;;) {
489 		tLenPrev = tLen;
490 		tLen += tGetCharacterLength(szString + tLen);
491 		DBG_DEC_C(tLen > tLengthMax, tLen);
492 		DBG_DEC_C(tLen > tLengthMax, tLengthMax);
493 		fail(tLen > tLengthMax);
494 		tWidth = tCountColumns(szString, tLen);
495 		if (tWidth > tColumnWidthMax) {
496 			return tLenPrev;
497 		}
498 		if (tLen >= tLengthMax) {
499 			return tLengthMax;
500 		}
501 	}
502 } /* end of tComputeStringLengthMax */
503 
504 /*
505  * tGetBreakingPoint - get the number of bytes that fit the column
506  *
507  * Returns the number of bytes that fit the column
508  */
509 static size_t
tGetBreakingPoint(const char * szString,size_t tLen,size_t tWidth,size_t tColumnWidthMax)510 tGetBreakingPoint(const char *szString,
511 	size_t tLen, size_t tWidth, size_t tColumnWidthMax)
512 {
513 	int	iIndex;
514 
515 	TRACE_MSG("tGetBreakingPoint");
516 
517 	fail(szString == NULL);
518 	fail(tLen > strlen(szString));
519 	fail(tWidth > tColumnWidthMax);
520 
521 	if (tWidth < tColumnWidthMax ||
522 	    (tWidth == tColumnWidthMax &&
523 	     (szString[tLen] == ' ' ||
524 	      szString[tLen] == '\n' ||
525 	      szString[tLen] == '\0'))) {
526 		/* The string already fits, do nothing */
527 		return tLen;
528 	}
529 	/* Search for a breaking point */
530 	for (iIndex = (int)tLen - 1; iIndex >= 0; iIndex--) {
531 		if (szString[iIndex] == ' ') {
532 			return (size_t)iIndex;
533 		}
534 	}
535 	/* No breaking point found, just fill the column */
536 	return tLen;
537 } /* end of tGetBreakingPoint */
538 
539 /*
540  * tComputeColumnWidthMax - compute the maximum column width
541  */
542 static size_t
tComputeColumnWidthMax(short sWidth,long lCharWidth,double dFactor)543 tComputeColumnWidthMax(short sWidth, long lCharWidth, double dFactor)
544 {
545 	size_t	tColumnWidthMax;
546 
547 	TRACE_MSG("tComputeColumnWidthMax");
548 
549 	fail(sWidth < 0);
550 	fail(lCharWidth <= 0);
551 	fail(dFactor <= 0.0);
552 
553 	tColumnWidthMax = (size_t)(
554 		(lTwips2MilliPoints(sWidth) * dFactor + lCharWidth / 2.0) /
555 		 lCharWidth);
556 	if (tColumnWidthMax == 0) {
557 		/* Minimum column width */
558 		return 1;
559 	}
560 	if (tColumnWidthMax > 1) {
561 		/* Make room for the TABLE_SEPARATOR_CHAR */
562 		tColumnWidthMax--;
563 	}
564 	NO_DBG_DEC(tColumnWidthMax);
565 	return tColumnWidthMax;
566 } /* end of tComputeColumnWidthMax */
567 
568 /*
569  * vTableRow2Window - put a table row into a diagram
570  */
571 void
vTableRow2Window(diagram_type * pDiag,output_type * pOutput,const row_block_type * pRowInfo,conversion_type eConversionType,int iParagraphBreak)572 vTableRow2Window(diagram_type *pDiag, output_type *pOutput,
573 	const row_block_type *pRowInfo,
574 	conversion_type eConversionType, int iParagraphBreak)
575 {
576 	output_type	tRow;
577 	char	*aszColTxt[TABLE_COLUMN_MAX];
578 	char	*szLine, *pcTxt;
579 	double	dMagnify;
580 	long	lCharWidthLarge, lCharWidthSmall;
581 	size_t	tColumnWidthTotal, atColumnWidthMax[TABLE_COLUMN_MAX];
582 	size_t	tSize, tColumnWidthMax, tWidth, tLen;
583 	int	iIndex, iNbrOfColumns, iTmp;
584 	BOOL	bNotReady;
585 
586 	TRACE_MSG("vTableRow2Window");
587 
588 	fail(pDiag == NULL || pOutput == NULL || pRowInfo == NULL);
589 	fail(pOutput->szStorage == NULL);
590 	fail(pOutput->pNext != NULL);
591 	fail(iParagraphBreak < 0);
592 
593 	/* Character sizes */
594 	lCharWidthLarge = lComputeStringWidth("W", 1,
595 				pOutput->tFontRef, pOutput->usFontSize);
596 	NO_DBG_DEC(lCharWidthLarge);
597 	lCharWidthSmall = lComputeStringWidth("i", 1,
598 				pOutput->tFontRef, pOutput->usFontSize);
599 	NO_DBG_DEC(lCharWidthSmall);
600 	/* For the time being: use a fixed width font */
601 	fail(lCharWidthLarge != lCharWidthSmall);
602 
603 	vRemoveRowEnd(pOutput->szStorage);
604 
605 	/* Split the row text into a set of column texts */
606 	aszColTxt[0] = pOutput->szStorage;
607 	for (iNbrOfColumns = 1;
608 	     iNbrOfColumns < TABLE_COLUMN_MAX;
609 	     iNbrOfColumns++) {
610 		aszColTxt[iNbrOfColumns] =
611 				strchr(aszColTxt[iNbrOfColumns - 1],
612 					TABLE_SEPARATOR);
613 		if (aszColTxt[iNbrOfColumns] == NULL) {
614 			break;
615 		}
616 		*aszColTxt[iNbrOfColumns] = '\0';
617 		aszColTxt[iNbrOfColumns]++;
618 		NO_DBG_DEC(iNbrOfColumns);
619 		NO_DBG_MSG(aszColTxt[iNbrOfColumns]);
620 	}
621 
622 	/* Work around a bug in Word */
623 	while (iNbrOfColumns > (int)pRowInfo->ucNumberOfColumns &&
624 	       pRowInfo->asColumnWidth[iNbrOfColumns] == 0) {
625 		iNbrOfColumns--;
626 	}
627 
628 	DBG_DEC_C(iNbrOfColumns != (int)pRowInfo->ucNumberOfColumns,
629 		iNbrOfColumns);
630 	DBG_DEC_C(iNbrOfColumns != (int)pRowInfo->ucNumberOfColumns,
631 		pRowInfo->ucNumberOfColumns);
632 	if (iNbrOfColumns != (int)pRowInfo->ucNumberOfColumns) {
633 		werr(0, "Skipping an unmatched table row");
634 		return;
635 	}
636 
637 #if defined(__FULL_TEXT_SEARCH)
638 	/* No table formatting: use for full-text search (untested) */
639 	for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
640 		fprintf(pDiag->pOutFile, "%s\n" , aszColTxt[iIndex]);
641 	}
642 #else
643 	if (bAddTableRow(pDiag, aszColTxt, iNbrOfColumns,
644 			pRowInfo->asColumnWidth, pRowInfo->ucBorderInfo)) {
645 		/* All work has been done */
646 		return;
647 	}
648 
649 	/* Fill the table with maximum column widths */
650 	if (eConversionType == conversion_text ||
651 	    eConversionType == conversion_fmt_text) {
652 		if (iParagraphBreak == 0 ||
653 		    iParagraphBreak >= MAX_SCREEN_WIDTH) {
654 			dMagnify = (double)MAX_SCREEN_WIDTH;
655 		} else if (iParagraphBreak <= MIN_SCREEN_WIDTH) {
656 			dMagnify = (double)MIN_SCREEN_WIDTH;
657 		} else {
658 			dMagnify = (double)iParagraphBreak;
659 		}
660 		dMagnify /= (double)DEFAULT_SCREEN_WIDTH;
661 		DBG_FLT_C(dMagnify < 0.99 || dMagnify > 1.01, dMagnify);
662 	} else {
663 		dMagnify = 1.0;
664 	}
665 	tColumnWidthTotal = 0;
666 	for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
667 		atColumnWidthMax[iIndex] = tComputeColumnWidthMax(
668 					pRowInfo->asColumnWidth[iIndex],
669 					lCharWidthLarge,
670 					dMagnify);
671 		tColumnWidthTotal += atColumnWidthMax[iIndex];
672 	}
673 
674 	/*
675 	 * Get enough space for the row.
676 	 * Worst case: three bytes per UTF-8 character
677 	 */
678 	tSize = 3 * (1 + tColumnWidthTotal + (size_t)iNbrOfColumns + 3);
679 	szLine = xmalloc(tSize);
680 
681 	do {
682 		/* Print one line of a table row */
683 		bNotReady = FALSE;
684 		pcTxt = szLine;
685 		*pcTxt++ = TABLE_SEPARATOR_CHAR;
686 		for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
687 			tColumnWidthMax = atColumnWidthMax[iIndex];
688 			if (aszColTxt[iIndex] == NULL) {
689 				/* Add an empty column */
690 				for (iTmp = 0;
691 				     iTmp < (int)tColumnWidthMax;
692 				     iTmp++) {
693 					*pcTxt++ = (char)FILLER_CHAR;
694 				}
695 				*pcTxt++ = TABLE_SEPARATOR_CHAR;
696 				*pcTxt = '\0';
697 				continue;
698 			}
699 			/* Compute the length and width of the column text */
700 			tLen = tComputeStringLengthMax(
701 					aszColTxt[iIndex], tColumnWidthMax);
702 			NO_DBG_DEC(tLen);
703 			while (tLen != 0 &&
704 					(aszColTxt[iIndex][tLen - 1] == '\n' ||
705 					 aszColTxt[iIndex][tLen - 1] == ' ')) {
706 				aszColTxt[iIndex][tLen - 1] = ' ';
707 				tLen--;
708 			}
709 			tWidth = tCountColumns(aszColTxt[iIndex], tLen);
710 			fail(tWidth > tColumnWidthMax);
711 			tLen = tGetBreakingPoint(aszColTxt[iIndex],
712 					tLen, tWidth, tColumnWidthMax);
713 			tWidth = tCountColumns(aszColTxt[iIndex], tLen);
714 			if (tLen == 0 && *aszColTxt[iIndex] == '\0') {
715 				/* No text at all */
716 				aszColTxt[iIndex] = NULL;
717 			} else {
718 				/* Add the text */
719 				pcTxt += sprintf(pcTxt,
720 					"%.*s", (int)tLen, aszColTxt[iIndex]);
721 				if (tLen == 0 && *aszColTxt[iIndex] != ' ') {
722 					tLen = tGetCharacterLength(
723 							aszColTxt[iIndex]);
724 					DBG_CHR(*aszColTxt[iIndex]);
725 					DBG_FIXME();
726 					fail(tLen == 0);
727 				}
728 				aszColTxt[iIndex] += tLen;
729 				while (*aszColTxt[iIndex] == ' ') {
730 					aszColTxt[iIndex]++;
731 				}
732 				if (*aszColTxt[iIndex] == '\0') {
733 					/* This row is now complete */
734 					aszColTxt[iIndex] = NULL;
735 				} else {
736 					/* This row needs more lines */
737 					bNotReady = TRUE;
738 				}
739 			}
740 			/* Fill up the rest */
741 			for (iTmp = 0;
742 			     iTmp < (int)tColumnWidthMax - (int)tWidth;
743 			     iTmp++) {
744 				*pcTxt++ = (char)FILLER_CHAR;
745 			}
746 			/* End of column */
747 			*pcTxt++ = TABLE_SEPARATOR_CHAR;
748 			*pcTxt = '\0';
749 		}
750 		/* Output the table row line */
751 		*pcTxt = '\0';
752 		tRow = *pOutput;
753 		tRow.szStorage = szLine;
754 		fail(pcTxt < szLine);
755 		tRow.tNextFree = (size_t)(pcTxt - szLine);
756 		tRow.lStringWidth = lComputeStringWidth(
757 					tRow.szStorage,
758 					tRow.tNextFree,
759 					tRow.tFontRef,
760 					tRow.usFontSize);
761 		vString2Diagram(pDiag, &tRow);
762 		TRACE_MSG("after vString2Diagram in vTableRow2Window");
763 	} while (bNotReady);
764 	/* Clean up before you leave */
765 	szLine = xfree(szLine);
766 	TRACE_MSG("leaving vTableRow2Window");
767 #endif /* __FULL_TEXT_SEARCH */
768 } /* end of vTableRow2Window */
769