Imported from antiword-0.30.tar.gz.
[antiword.git] / out2window.c
blobcdf63bdf5f48d97c68214694f39503a9ba4562dc
1 /*
2 * out2window.c
3 * Copyright (C) 1998,1999 A.J. van Os
5 * Description:
6 * Output to a text window
7 */
9 #include <string.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include <limits.h>
13 #include "antiword.h"
16 /* Used for numbering the chapters */
17 static int aiHdrCounter[9];
21 * vString2Diagram - put a string into a diagram
23 static void
24 vString2Diagram(diagram_type *pDiag, output_type *pAnchor)
26 output_type *pOutput;
27 int iWidth;
28 unsigned char ucMaxFontsize;
30 fail(pDiag == NULL);
31 fail(pAnchor == NULL);
33 /* Compute the maximum fontsize in this string */
34 ucMaxFontsize = MIN_FONT_SIZE;
35 for (pOutput = pAnchor; pOutput != NULL; pOutput = pOutput->pNext) {
36 if (pOutput->ucFontsize > ucMaxFontsize) {
37 ucMaxFontsize = pOutput->ucFontsize;
40 /* Output all substrings */
41 for (pOutput = pAnchor; pOutput != NULL; pOutput = pOutput->pNext) {
42 iWidth = iMilliPoints2DrawUnits(pOutput->iStringWidth);
43 vSubstring2Diagram(pDiag, pOutput->szStorage,
44 pOutput->iNextFree, iWidth, pOutput->iColour,
45 pOutput->ucFontstyle, pOutput->tFontRef,
46 pOutput->ucFontsize, ucMaxFontsize);
48 /* Goto the start of the next line */
49 vMove2NextLine(pDiag, pAnchor->tFontRef, ucMaxFontsize);
50 } /* end of vString2Diagram */
53 * iComputeNetWidth - compute the net string width
55 static int
56 iComputeNetWidth(output_type *pAnchor)
58 output_type *pTmp;
59 int iNetWidth;
61 fail(pAnchor == NULL);
63 /* Step 1: Count all but the last sub-string */
64 iNetWidth = 0;
65 for (pTmp = pAnchor; pTmp->pNext != NULL; pTmp = pTmp->pNext) {
66 fail(pTmp->iStringWidth < 0);
67 iNetWidth += pTmp->iStringWidth;
69 fail(pTmp == NULL);
70 fail(pTmp->pNext != NULL);
72 /* Step 2: remove the white-space from the end of the string */
73 while (pTmp->iNextFree > 0 &&
74 isspace(pTmp->szStorage[pTmp->iNextFree - 1])) {
75 pTmp->szStorage[pTmp->iNextFree - 1] = '\0';
76 pTmp->iNextFree--;
77 NO_DBG_DEC(pTmp->iStringWidth);
78 pTmp->iStringWidth = iComputeStringWidth(
79 pTmp->szStorage,
80 pTmp->iNextFree,
81 pTmp->tFontRef,
82 pTmp->ucFontsize);
83 NO_DBG_DEC(pTmp->iStringWidth);
86 /* Step 3: Count the last sub-string */
87 iNetWidth += pTmp->iStringWidth;
88 return iNetWidth;
89 } /* end of iComputeNetWidth */
92 * iComputeHoles - compute number of holes
93 * (A hole is a number of whitespace characters followed by a
94 * non-whitespace character)
96 static int
97 iComputeHoles(output_type *pAnchor)
99 output_type *pTmp;
100 int iIndex, iCounter;
101 BOOL bWasSpace, bIsSpace;
103 fail(pAnchor == NULL);
105 iCounter = 0;
106 bIsSpace = FALSE;
107 /* Count the holes */
108 for (pTmp = pAnchor; pTmp != NULL; pTmp = pTmp->pNext) {
109 fail(pTmp->iNextFree != strlen(pTmp->szStorage));
110 for (iIndex = 0; iIndex <= pTmp->iNextFree; iIndex++) {
111 bWasSpace = bIsSpace;
112 bIsSpace = isspace(pTmp->szStorage[iIndex]);
113 if (bWasSpace && !bIsSpace) {
114 iCounter++;
118 return iCounter;
119 } /* end of iComputeHoles */
122 * Align a string and insert it into the text
124 void
125 vAlign2Window(diagram_type *pDiag, output_type *pAnchor,
126 int iScreenWidth, unsigned char ucAlignment)
128 int iNetWidth;
130 fail(pDiag == NULL || pAnchor == NULL);
131 fail(iScreenWidth < iChar2MilliPoints(MIN_SCREEN_WIDTH));
133 NO_DBG_MSG("vAlign2Window");
135 iNetWidth = iComputeNetWidth(pAnchor);
137 if (iScreenWidth > iChar2MilliPoints(MAX_SCREEN_WIDTH) ||
138 iNetWidth <= 0) {
140 * Screenwidth is "infinite", so no alignment is possible
141 * Don't bother to align an empty line
143 vString2Diagram(pDiag, pAnchor);
144 return;
147 switch (ucAlignment) {
148 case ALIGNMENT_CENTER:
149 vSetLeftIndentation(pDiag, (iScreenWidth - iNetWidth) / 2);
150 break;
151 case ALIGNMENT_RIGHT:
152 vSetLeftIndentation(pDiag, iScreenWidth - iNetWidth);
153 break;
154 case ALIGNMENT_JUSTIFY:
155 case ALIGNMENT_LEFT:
156 default:
157 break;
159 vString2Diagram(pDiag, pAnchor);
160 } /* end of vAlign2Window */
163 * vJustify2Window
165 void
166 vJustify2Window(diagram_type *pDiag, output_type *pAnchor,
167 int iScreenWidth, unsigned char ucAlignment)
169 output_type *pTmp;
170 char *pcNew, *pcOld, *szStorage;
171 int iNetWidth, iSpaceWidth, iFillerLen, iToAdd, iHoles;
173 fail(pDiag == NULL || pAnchor == NULL);
174 fail(iScreenWidth < MIN_SCREEN_WIDTH);
176 NO_DBG_MSG("vJustify2Window");
178 if (ucAlignment != ALIGNMENT_JUSTIFY) {
179 vAlign2Window(pDiag, pAnchor, iScreenWidth, ucAlignment);
180 return;
183 iNetWidth = iComputeNetWidth(pAnchor);
185 if (iScreenWidth > iChar2MilliPoints(MAX_SCREEN_WIDTH) ||
186 iNetWidth <= 0) {
188 * Screenwidth is "infinite", so justify is not possible
189 * Don't bother to align an empty line
191 vString2Diagram(pDiag, pAnchor);
192 return;
195 /* Justify */
196 fail(ucAlignment != ALIGNMENT_JUSTIFY);
197 iSpaceWidth = iComputeStringWidth(" ", 1,
198 pAnchor->tFontRef, pAnchor->ucFontsize);
199 DBG_DEC(iSpaceWidth);
200 iToAdd = iScreenWidth -
201 iNetWidth -
202 iDrawUnits2MilliPoints(pDiag->iXleft);
203 DBG_DEC_C(iToAdd < 0, iToAdd);
204 DBG_DEC_C(iToAdd < 0, iScreenWidth);
205 DBG_DEC_C(iToAdd < 0, iNetWidth);
206 DBG_DEC_C(iToAdd < 0, iDrawUnits2MilliPoints(pDiag->iXleft));
207 DBG_DEC_C(iToAdd < 0, pDiag->iXleft);
208 iToAdd /= iSpaceWidth;
209 DBG_DEC(iToAdd);
210 if (iToAdd <= 0) {
211 vString2Diagram(pDiag, pAnchor);
212 return;
215 iHoles = iComputeHoles(pAnchor);
216 /* Justify by adding spaces */
217 for (pTmp = pAnchor; pTmp != NULL; pTmp = pTmp->pNext) {
218 fail(pTmp->iNextFree != strlen(pTmp->szStorage));
219 fail(iToAdd < 0);
220 szStorage = xmalloc(pTmp->iNextFree + iToAdd + 1);
221 pcNew = szStorage;
222 for (pcOld = pTmp->szStorage; *pcOld != '\0'; pcOld++) {
223 *pcNew++ = *pcOld;
224 if (*pcOld == ' ' &&
225 *(pcOld + 1) != ' ' &&
226 iHoles > 0) {
227 iFillerLen = iToAdd / iHoles;
228 iToAdd -= iFillerLen;
229 iHoles--;
230 for (; iFillerLen > 0; iFillerLen--) {
231 *pcNew++ = ' ';
235 *pcNew = '\0';
236 pTmp->szStorage = xfree(pTmp->szStorage);
237 pTmp->szStorage = szStorage;
238 pTmp->iStorageSize = pTmp->iNextFree + iToAdd + 1;
239 pTmp->iStringWidth +=
240 (pcNew - szStorage - pTmp->iNextFree) * iSpaceWidth;
241 pTmp->iNextFree = pcNew - szStorage;
242 fail(pTmp->iNextFree != strlen(pTmp->szStorage));
244 DBG_DEC_C(iToAdd != 0, iToAdd);
245 vString2Diagram(pDiag, pAnchor);
246 } /* end of vJustify2Window */
249 * vResetStyles - reset the style information variables
251 void
252 vResetStyles(void)
254 memset(aiHdrCounter, 0, sizeof(aiHdrCounter));
255 } /* end of vResetStyles */
258 * Add the style characters to the line
261 iStyle2Window(char *szLine, const style_block_type *pStyleInfo)
263 char *pcTxt;
264 int iIndex, iStyleIndex;
266 fail(szLine == NULL || pStyleInfo == NULL);
268 pcTxt = szLine;
269 if ((int)pStyleInfo->ucStyle >= 1 && (int)pStyleInfo->ucStyle <= 9) {
270 iStyleIndex = (int)pStyleInfo->ucStyle - 1;
271 for (iIndex = 0; iIndex < 9; iIndex++) {
272 if (iIndex == iStyleIndex) {
273 aiHdrCounter[iIndex]++;
274 } else if (iIndex > iStyleIndex) {
275 aiHdrCounter[iIndex] = 0;
276 } else if (aiHdrCounter[iIndex] < 1) {
277 aiHdrCounter[iIndex] = 1;
279 if (iIndex <= iStyleIndex) {
280 pcTxt += sprintf(pcTxt, "%d",
281 aiHdrCounter[iIndex]);
282 if (iIndex < iStyleIndex) {
283 *pcTxt++ = '.';
287 *pcTxt++ = ' ';
289 *pcTxt = '\0';
290 DBG_MSG_C(szLine[0] != '\0', szLine);
291 return pcTxt - szLine;
292 } /* end of iStyle2Window */
295 * vRemoveRowEnd - remove the end of table row indicator
297 * Remove the double TABLE_SEPARATOR characters from the end of the string.
299 static void
300 vRemoveRowEnd(char *szRowTxt)
302 char *pcTmp;
304 fail(szRowTxt == NULL || szRowTxt[0] == '\0');
305 fail(szRowTxt[strlen(szRowTxt) - 1] != TABLE_SEPARATOR);
306 fail(szRowTxt[strlen(szRowTxt) - 2] != TABLE_SEPARATOR);
308 pcTmp = strrchr(szRowTxt, TABLE_SEPARATOR);
309 if (pcTmp == NULL || pcTmp <= szRowTxt) {
310 DBG_MSG(szRowTxt);
311 return;
313 *pcTmp = '\0'; /* Remove the last TABLE_SEPARATOR */
314 pcTmp--;
315 if (*pcTmp == TABLE_SEPARATOR) {
316 *pcTmp = '\0'; /* And the second last */
317 return;
319 DBG_MSG(szRowTxt);
320 } /* end of vRemoveRowEnd */
323 * vTableRow2Window - put a table row into a diagram
325 void
326 vTableRow2Window(diagram_type *pDiag, output_type *pOutput,
327 const row_block_type *pRowInfo)
329 output_type tRow;
330 char *aszColTxt[TABLE_COLUMN_MAX];
331 char *szLine, *pcTmp, *pcTxt;
332 size_t tSize;
333 int iIndex, iNbrOfColumns, iColumnWidth, iTmp;
334 int iCharWidthLarge, iCharWidthSmall;
335 int iLen1, iLen2, iLen;
336 BOOL bNotReady;
338 fail(pDiag == NULL || pOutput == NULL || pRowInfo == NULL);
339 fail(pOutput->szStorage == NULL);
340 fail(pOutput->pNext != NULL);
342 /* Character sizes */
343 iCharWidthLarge = iComputeStringWidth("W", 1,
344 pOutput->tFontRef, pOutput->ucFontsize);
345 NO_DBG_DEC(iCharWidthLarge);
346 iCharWidthSmall = iComputeStringWidth("i", 1,
347 pOutput->tFontRef, pOutput->ucFontsize);
348 NO_DBG_DEC(iCharWidthSmall);
349 /* For the time being: use a fixed width font */
350 fail(iCharWidthLarge != iCharWidthSmall);
352 /* Make room for the row */
353 tSize = iTwips2MilliPoints(pRowInfo->iColumnWidthSum) /
354 iCharWidthSmall +
355 (int)pRowInfo->ucNumberOfColumns + 3;
356 szLine = xmalloc(tSize);
358 vRemoveRowEnd(pOutput->szStorage);
360 /* Split the row text into column texts */
361 aszColTxt[0] = pOutput->szStorage;
362 for (iNbrOfColumns = 1;
363 iNbrOfColumns < TABLE_COLUMN_MAX;
364 iNbrOfColumns++) {
365 aszColTxt[iNbrOfColumns] =
366 strchr(aszColTxt[iNbrOfColumns - 1],
367 TABLE_SEPARATOR);
368 if (aszColTxt[iNbrOfColumns] == NULL) {
369 break;
371 *aszColTxt[iNbrOfColumns] = '\0';
372 aszColTxt[iNbrOfColumns]++;
373 NO_DBG_DEC(iNbrOfColumns);
374 NO_DBG_MSG(aszColTxt[iNbrOfColumns]);
377 DBG_DEC_C(iNbrOfColumns != pRowInfo->ucNumberOfColumns,
378 iNbrOfColumns);
379 DBG_DEC_C(iNbrOfColumns != pRowInfo->ucNumberOfColumns,
380 pRowInfo->ucNumberOfColumns);
381 if (iNbrOfColumns != pRowInfo->ucNumberOfColumns) {
382 werr(0, "Skipping an unmatched table row");
383 /* Clean up before you leave */
384 szLine = xfree(szLine);
385 return;
388 do {
389 /* Print a table row line */
390 bNotReady = FALSE;
391 pcTxt = szLine;
392 *pcTxt++ = TABLE_SEPARATOR_CHAR;
393 for (iIndex = 0; iIndex < iNbrOfColumns; iIndex++) {
394 iColumnWidth =
395 iTwips2MilliPoints(pRowInfo->asColumnWidth[iIndex]) /
396 iCharWidthLarge;
397 fail(iColumnWidth < 0);
398 if (iColumnWidth < 1) {
399 /* Minimum column width */
400 iColumnWidth = 1;
401 } else if (iColumnWidth > 1) {
402 /* Room for the TABLE_SEPARATOR_CHAR */
403 iColumnWidth--;
405 NO_DBG_DEC(iColumnWidth);
406 /* Compute the length of the text for a column */
407 if (aszColTxt[iIndex] == NULL) {
408 iLen = 0;
409 } else {
410 pcTmp = strchr(aszColTxt[iIndex], '\n');
411 if (pcTmp == NULL) {
412 iLen1 = INT_MAX;
413 } else {
414 iLen1 =
415 pcTmp - aszColTxt[iIndex] + 1;
417 iLen2 = strlen(aszColTxt[iIndex]);
418 if (iLen2 > iColumnWidth) {
419 iLen2 = iColumnWidth;
421 iLen = min(iLen1, iLen2);
423 NO_DBG_DEC(iLen);
424 fail(iLen < 0 || iLen > iColumnWidth);
425 if (iLen >= 1 &&
426 aszColTxt[iIndex][iLen - 1] == '\n') {
427 aszColTxt[iIndex][iLen - 1] = ' ';
429 if (iLen == iColumnWidth &&
430 !isspace(aszColTxt[iIndex][iLen])) {
431 /* Search for a breaking point */
432 for (iTmp = iLen - 1; iTmp >= 0; iTmp--) {
433 if (isspace(aszColTxt[iIndex][iTmp])) {
434 /* Found a breaking point */
435 iLen = iTmp + 1;
436 NO_DBG_DEC(iLen);
437 break;
441 /* Print the text */
442 if (iLen <= 0) {
443 aszColTxt[iIndex] = NULL;
444 } else {
445 pcTxt += sprintf(pcTxt,
446 "%.*s", iLen, aszColTxt[iIndex]);
447 aszColTxt[iIndex] += iLen;
448 while (*aszColTxt[iIndex] == ' ') {
449 aszColTxt[iIndex]++;
451 if (*aszColTxt[iIndex] != '\0') {
452 /* The row takes more lines */
453 bNotReady = TRUE;
456 /* Print the filler */
457 for (iTmp = 0; iTmp < iColumnWidth - iLen; iTmp++) {
458 *pcTxt++ = (char)FILLER_CHAR;
460 *pcTxt++ = TABLE_SEPARATOR_CHAR;
461 *pcTxt = '\0';
463 /* Output the table row line */
464 *pcTxt = '\0';
465 tRow = *pOutput;
466 tRow.szStorage = szLine;
467 tRow.iNextFree = pcTxt - szLine;
468 tRow.iStringWidth = iComputeStringWidth(
469 tRow.szStorage,
470 tRow.iNextFree,
471 tRow.tFontRef,
472 tRow.ucFontsize);
473 vString2Diagram(pDiag, &tRow);
474 } while (bNotReady);
475 /* Clean up before you leave */
476 szLine = xfree(szLine);
477 } /* end of vTableRow2Window */