3 * Copyright (C) 1998-2004 A.J. van Os; Released under GPL
6 * Output to a text window
15 /* Used for numbering the chapters */
16 static unsigned int auiHdrCounter
[9];
20 * vString2Diagram - put a string into a diagram
23 vString2Diagram(diagram_type
*pDiag
, output_type
*pAnchor
)
30 fail(pAnchor
== NULL
);
32 /* Compute the maximum fontsize in this string */
33 usMaxFontSize
= MIN_FONT_SIZE
;
34 for (pOutput
= pAnchor
; pOutput
!= NULL
; pOutput
= pOutput
->pNext
) {
35 if (pOutput
->usFontSize
> usMaxFontSize
) {
36 usMaxFontSize
= pOutput
->usFontSize
;
40 /* Goto the next line */
41 vMove2NextLine(pDiag
, pAnchor
->tFontRef
, usMaxFontSize
);
43 /* Output all substrings */
44 for (pOutput
= pAnchor
; pOutput
!= NULL
; pOutput
= pOutput
->pNext
) {
45 lWidth
= lMilliPoints2DrawUnits(pOutput
->lStringWidth
);
46 vSubstring2Diagram(pDiag
, pOutput
->szStorage
,
47 pOutput
->tNextFree
, lWidth
, pOutput
->ucFontColor
,
48 pOutput
->usFontStyle
, pOutput
->tFontRef
,
49 pOutput
->usFontSize
, usMaxFontSize
);
52 /* Goto the start of the line */
54 } /* end of vString2Diagram */
57 * vSetLeftIndentation - set the left indentation of the specified diagram
60 vSetLeftIndentation(diagram_type
*pDiag
, long lLeftIndentation
)
65 fail(lLeftIndentation
< 0);
67 lX
= lMilliPoints2DrawUnits(lLeftIndentation
);
73 } /* end of vSetLeftIndentation */
76 * lComputeNetWidth - compute the net string width
79 lComputeNetWidth(output_type
*pAnchor
)
84 fail(pAnchor
== NULL
);
86 /* Step 1: Count all but the last sub-string */
88 for (pTmp
= pAnchor
; pTmp
->pNext
!= NULL
; pTmp
= pTmp
->pNext
) {
89 fail(pTmp
->lStringWidth
< 0);
90 lNetWidth
+= pTmp
->lStringWidth
;
93 fail(pTmp
->pNext
!= NULL
);
95 /* Step 2: remove the white-space from the end of the string */
96 while (pTmp
->tNextFree
!= 0 &&
97 isspace((int)(UCHAR
)pTmp
->szStorage
[pTmp
->tNextFree
- 1])) {
98 pTmp
->szStorage
[pTmp
->tNextFree
- 1] = '\0';
100 NO_DBG_DEC(pTmp
->lStringWidth
);
101 pTmp
->lStringWidth
= lComputeStringWidth(
106 NO_DBG_DEC(pTmp
->lStringWidth
);
109 /* Step 3: Count the last sub-string */
110 lNetWidth
+= pTmp
->lStringWidth
;
112 } /* end of lComputeNetWidth */
115 * iComputeHoles - compute number of holes
116 * (A hole is a number of whitespace characters followed by a
117 * non-whitespace character)
120 iComputeHoles(output_type
*pAnchor
)
125 BOOL bWasSpace
, bIsSpace
;
127 fail(pAnchor
== NULL
);
131 /* Count the holes */
132 for (pTmp
= pAnchor
; pTmp
!= NULL
; pTmp
= pTmp
->pNext
) {
133 fail(pTmp
->tNextFree
!= strlen(pTmp
->szStorage
));
134 for (tIndex
= 0; tIndex
<= pTmp
->tNextFree
; tIndex
++) {
135 bWasSpace
= bIsSpace
;
136 bIsSpace
= isspace((int)(UCHAR
)pTmp
->szStorage
[tIndex
]);
137 if (bWasSpace
&& !bIsSpace
) {
143 } /* end of iComputeHoles */
146 * vAlign2Window - Align a string and insert it into the text
149 vAlign2Window(diagram_type
*pDiag
, output_type
*pAnchor
,
150 long lScreenWidth
, UCHAR ucAlignment
)
152 long lNetWidth
, lLeftIndentation
;
154 fail(pDiag
== NULL
|| pAnchor
== NULL
);
155 fail(lScreenWidth
< lChar2MilliPoints(MIN_SCREEN_WIDTH
));
157 NO_DBG_MSG("vAlign2Window");
159 lNetWidth
= lComputeNetWidth(pAnchor
);
161 if (lScreenWidth
> lChar2MilliPoints(MAX_SCREEN_WIDTH
) ||
164 * Screenwidth is "infinite", so no alignment is possible
165 * Don't bother to align an empty line
167 vString2Diagram(pDiag
, pAnchor
);
171 switch (ucAlignment
) {
172 case ALIGNMENT_CENTER
:
173 lLeftIndentation
= (lScreenWidth
- lNetWidth
) / 2;
174 DBG_DEC_C(lLeftIndentation
< 0, lLeftIndentation
);
175 if (lLeftIndentation
> 0) {
176 vSetLeftIndentation(pDiag
, lLeftIndentation
);
179 case ALIGNMENT_RIGHT
:
180 lLeftIndentation
= lScreenWidth
- lNetWidth
;
181 DBG_DEC_C(lLeftIndentation
< 0, lLeftIndentation
);
182 if (lLeftIndentation
> 0) {
183 vSetLeftIndentation(pDiag
, lLeftIndentation
);
186 case ALIGNMENT_JUSTIFY
:
191 vString2Diagram(pDiag
, pAnchor
);
192 } /* end of vAlign2Window */
195 * vJustify2Window - Justify a string and insert it into the text
198 vJustify2Window(diagram_type
*pDiag
, output_type
*pAnchor
,
199 long lScreenWidth
, long lRightIndentation
, UCHAR ucAlignment
)
202 char *pcNew
, *pcOld
, *szStorage
;
203 long lNetWidth
, lSpaceWidth
, lToAdd
;
204 int iFillerLen
, iHoles
;
206 fail(pDiag
== NULL
|| pAnchor
== NULL
);
207 fail(lScreenWidth
< MIN_SCREEN_WIDTH
);
208 fail(lRightIndentation
> 0);
210 NO_DBG_MSG("vJustify2Window");
212 if (ucAlignment
!= ALIGNMENT_JUSTIFY
) {
213 vAlign2Window(pDiag
, pAnchor
, lScreenWidth
, ucAlignment
);
217 lNetWidth
= lComputeNetWidth(pAnchor
);
219 if (lScreenWidth
> lChar2MilliPoints(MAX_SCREEN_WIDTH
) ||
222 * Screenwidth is "infinite", so justify is not possible
223 * Don't bother to justify an empty line
225 vString2Diagram(pDiag
, pAnchor
);
230 fail(ucAlignment
!= ALIGNMENT_JUSTIFY
);
231 lSpaceWidth
= lComputeStringWidth(" ", 1,
232 pAnchor
->tFontRef
, pAnchor
->usFontSize
);
233 lToAdd
= lScreenWidth
-
235 lDrawUnits2MilliPoints(pDiag
->lXleft
) +
238 if (lToAdd
/ lSpaceWidth
< -1) {
239 DBG_DEC(lSpaceWidth
);
241 DBG_DEC(lScreenWidth
);
243 DBG_DEC(lDrawUnits2MilliPoints(pDiag
->lXleft
));
244 DBG_DEC(pDiag
->lXleft
);
245 DBG_DEC(lRightIndentation
);
248 lToAdd
/= lSpaceWidth
;
249 DBG_DEC_C(lToAdd
< 0, lToAdd
);
251 vString2Diagram(pDiag
, pAnchor
);
255 /* Justify by adding spaces */
256 iHoles
= iComputeHoles(pAnchor
);
257 for (pTmp
= pAnchor
; pTmp
!= NULL
; pTmp
= pTmp
->pNext
) {
258 fail(pTmp
->tNextFree
!= strlen(pTmp
->szStorage
));
260 szStorage
= xmalloc(pTmp
->tNextFree
+ (size_t)lToAdd
+ 1);
262 for (pcOld
= pTmp
->szStorage
; *pcOld
!= '\0'; pcOld
++) {
265 *(pcOld
+ 1) != ' ' &&
267 iFillerLen
= (int)(lToAdd
/ iHoles
);
268 lToAdd
-= iFillerLen
;
270 for (; iFillerLen
> 0; iFillerLen
--) {
276 pTmp
->szStorage
= xfree(pTmp
->szStorage
);
277 pTmp
->szStorage
= szStorage
;
278 pTmp
->tStorageSize
= pTmp
->tNextFree
+ (size_t)lToAdd
+ 1;
279 pTmp
->lStringWidth
+=
280 (pcNew
- szStorage
- (long)pTmp
->tNextFree
) *
282 fail(pcNew
< szStorage
);
283 pTmp
->tNextFree
= (size_t)(pcNew
- szStorage
);
284 fail(pTmp
->tNextFree
!= strlen(pTmp
->szStorage
));
286 DBG_DEC_C(lToAdd
!= 0, lToAdd
);
287 vString2Diagram(pDiag
, pAnchor
);
288 } /* end of vJustify2Window */
291 * vResetStyles - reset the style information variables
296 (void)memset(auiHdrCounter
, 0, sizeof(auiHdrCounter
));
297 } /* end of vResetStyles */
300 * tStyle2Window - Add the style characters to the line
302 * Returns the length of the resulting string
305 tStyle2Window(char *szLine
, const style_block_type
*pStyle
,
306 const section_block_type
*pSection
)
309 size_t tIndex
, tStyleIndex
;
311 level_type_enum eNumType
;
314 fail(szLine
== NULL
|| pStyle
== NULL
|| pSection
== NULL
);
316 if (pStyle
->usIstd
== 0 || pStyle
->usIstd
> 9) {
321 /* Set the numbers */
322 tStyleIndex
= (size_t)pStyle
->usIstd
- 1;
323 for (tIndex
= 0; tIndex
< 9; tIndex
++) {
324 if (tIndex
== tStyleIndex
) {
325 auiHdrCounter
[tIndex
]++;
326 } else if (tIndex
> tStyleIndex
) {
327 auiHdrCounter
[tIndex
] = 0;
328 } else if (auiHdrCounter
[tIndex
] == 0) {
329 auiHdrCounter
[tIndex
] = 1;
333 eNumType
= eGetNumType(pStyle
->ucNumLevel
);
334 if (eNumType
!= level_type_outline
) {
339 /* Print the numbers */
341 bNeedPrevLvl
= (pSection
->usNeedPrevLvl
& BIT(tStyleIndex
)) != 0;
342 for (tIndex
= 0; tIndex
<= tStyleIndex
; tIndex
++) {
343 if (tIndex
== tStyleIndex
||
344 (bNeedPrevLvl
&& tIndex
< tStyleIndex
)) {
345 ucNFC
= pSection
->aucNFC
[tIndex
];
347 case LIST_ARABIC_NUM
:
348 case LIST_NUMBER_TXT
:
349 case LIST_ORDINAL_TXT
:
350 pcTxt
+= sprintf(pcTxt
, "%u",
351 auiHdrCounter
[tIndex
]);
353 case LIST_UPPER_ROMAN
:
354 case LIST_LOWER_ROMAN
:
355 pcTxt
+= tNumber2Roman(
356 auiHdrCounter
[tIndex
],
357 ucNFC
== LIST_UPPER_ROMAN
,
360 case LIST_UPPER_ALPHA
:
361 case LIST_LOWER_ALPHA
:
362 pcTxt
+= tNumber2Alpha(
363 auiHdrCounter
[tIndex
],
364 ucNFC
== LIST_UPPER_ALPHA
,
367 case LIST_OUTLINE_NUM
:
368 pcTxt
+= sprintf(pcTxt
, "%02u",
369 auiHdrCounter
[tIndex
]);
374 pcTxt
+= sprintf(pcTxt
, "%u",
375 auiHdrCounter
[tIndex
]);
378 if (tIndex
< tStyleIndex
) {
380 } else if (tIndex
== tStyleIndex
) {
386 NO_DBG_MSG_C((int)pStyle
->usIstd
>= 1 &&
387 (int)pStyle
->usIstd
<= 9 &&
388 eNumType
!= level_type_none
&&
389 eNumType
!= level_type_outline
, szLine
);
390 NO_DBG_MSG_C(szLine
[0] != '\0', szLine
);
391 fail(pcTxt
< szLine
);
392 return (size_t)(pcTxt
- szLine
);
393 } /* end of tStyle2Window */
396 * vRemoveRowEnd - remove the end of table row indicator
398 * Remove the double TABLE_SEPARATOR characters from the end of the string.
399 * Special: remove the TABLE_SEPARATOR, 0x0a sequence
402 vRemoveRowEnd(char *szRowTxt
)
406 fail(szRowTxt
== NULL
|| szRowTxt
[0] == '\0');
408 iLastIndex
= (int)strlen(szRowTxt
) - 1;
410 if (szRowTxt
[iLastIndex
] == TABLE_SEPARATOR
||
411 szRowTxt
[iLastIndex
] == (char)0x0a) {
412 szRowTxt
[iLastIndex
] = '\0';
415 DBG_HEX(szRowTxt
[iLastIndex
]);
418 if (iLastIndex
>= 0 && szRowTxt
[iLastIndex
] == (char)0x0a) {
419 szRowTxt
[iLastIndex
] = '\0';
423 if (iLastIndex
>= 0 && szRowTxt
[iLastIndex
] == TABLE_SEPARATOR
) {
424 szRowTxt
[iLastIndex
] = '\0';
429 DBG_HEX(szRowTxt
[iLastIndex
]);
431 } /* end of vRemoveRowEnd */
434 * tComputeStringLengthMax - max string length in relation to max column width
436 * Return the maximum string length
439 tComputeStringLengthMax(const char *szString
, size_t tColumnWidthMax
)
442 size_t tLengthMax
, tLenPrev
, tLen
, tWidth
;
444 fail(szString
== NULL
);
445 fail(tColumnWidthMax
== 0);
447 pcTmp
= strchr(szString
, '\n');
449 tLengthMax
= (size_t)(pcTmp
- szString
+ 1);
451 tLengthMax
= strlen(szString
);
453 if (tLengthMax
== 0) {
461 tLen
+= tGetCharacterLength(szString
+ tLen
);
462 DBG_DEC_C(tLen
> tLengthMax
, tLen
);
463 DBG_DEC_C(tLen
> tLengthMax
, tLengthMax
);
464 fail(tLen
> tLengthMax
);
465 tWidth
= tCountColumns(szString
, tLen
);
466 if (tWidth
> tColumnWidthMax
) {
469 if (tLen
>= tLengthMax
) {
473 } /* end of tComputeStringLengthMax */
476 * tGetBreakingPoint - get the number of bytes that fit the column
478 * Returns the number of bytes that fit the column
481 tGetBreakingPoint(const char *szString
,
482 size_t tLen
, size_t tWidth
, size_t tColumnWidthMax
)
486 fail(szString
== NULL
);
487 fail(tLen
> strlen(szString
));
488 fail(tWidth
> tColumnWidthMax
);
490 if (tWidth
< tColumnWidthMax
||
491 (tWidth
== tColumnWidthMax
&&
492 (szString
[tLen
] == ' ' ||
493 szString
[tLen
] == '\n' ||
494 szString
[tLen
] == '\0'))) {
495 /* The string already fits, do nothing */
498 /* Search for a breaking point */
499 for (iIndex
= (int)tLen
- 1; iIndex
>= 0; iIndex
--) {
500 if (szString
[iIndex
] == ' ') {
501 return (size_t)iIndex
;
504 /* No breaking point found, just fill the column */
506 } /* end of tGetBreakingPoint */
509 * tComputeColumnWidthMax - compute the maximum column width
512 tComputeColumnWidthMax(short sWidth
, long lCharWidth
, double dFactor
)
514 size_t tColumnWidthMax
;
517 fail(lCharWidth
<= 0);
518 fail(dFactor
<= 0.0);
520 tColumnWidthMax
= (size_t)(
521 (lTwips2MilliPoints(sWidth
) * dFactor
+ lCharWidth
/ 2.0) /
523 if (tColumnWidthMax
== 0) {
524 /* Minimum column width */
527 if (tColumnWidthMax
> 1) {
528 /* Make room for the TABLE_SEPARATOR_CHAR */
531 NO_DBG_DEC(tColumnWidthMax
);
532 return tColumnWidthMax
;
533 } /* end of tComputeColumnWidthMax */
536 * vTableRow2Window - put a table row into a diagram
539 vTableRow2Window(diagram_type
*pDiag
, output_type
*pOutput
,
540 const row_block_type
*pRowInfo
,
541 conversion_type eConversionType
, int iParagraphBreak
)
544 char *aszColTxt
[TABLE_COLUMN_MAX
];
545 char *szLine
, *pcTxt
;
547 long lCharWidthLarge
, lCharWidthSmall
;
548 size_t tColumnWidthTotal
, atColumnWidthMax
[TABLE_COLUMN_MAX
];
549 size_t tSize
, tColumnWidthMax
, tWidth
, tLen
;
550 int iIndex
, iNbrOfColumns
, iTmp
;
553 fail(pDiag
== NULL
|| pOutput
== NULL
|| pRowInfo
== NULL
);
554 fail(pOutput
->szStorage
== NULL
);
555 fail(pOutput
->pNext
!= NULL
);
556 fail(iParagraphBreak
< 0);
558 /* Character sizes */
559 lCharWidthLarge
= lComputeStringWidth("W", 1,
560 pOutput
->tFontRef
, pOutput
->usFontSize
);
561 NO_DBG_DEC(lCharWidthLarge
);
562 lCharWidthSmall
= lComputeStringWidth("i", 1,
563 pOutput
->tFontRef
, pOutput
->usFontSize
);
564 NO_DBG_DEC(lCharWidthSmall
);
565 /* For the time being: use a fixed width font */
566 fail(lCharWidthLarge
!= lCharWidthSmall
);
568 vRemoveRowEnd(pOutput
->szStorage
);
570 /* Split the row text into a set of column texts */
571 aszColTxt
[0] = pOutput
->szStorage
;
572 for (iNbrOfColumns
= 1;
573 iNbrOfColumns
< TABLE_COLUMN_MAX
;
575 aszColTxt
[iNbrOfColumns
] =
576 strchr(aszColTxt
[iNbrOfColumns
- 1],
578 if (aszColTxt
[iNbrOfColumns
] == NULL
) {
581 *aszColTxt
[iNbrOfColumns
] = '\0';
582 aszColTxt
[iNbrOfColumns
]++;
583 NO_DBG_DEC(iNbrOfColumns
);
584 NO_DBG_MSG(aszColTxt
[iNbrOfColumns
]);
587 /* Work around a bug in Word */
588 while (iNbrOfColumns
> (int)pRowInfo
->ucNumberOfColumns
&&
589 pRowInfo
->asColumnWidth
[iNbrOfColumns
] == 0) {
593 DBG_DEC_C(iNbrOfColumns
!= (int)pRowInfo
->ucNumberOfColumns
,
595 DBG_DEC_C(iNbrOfColumns
!= (int)pRowInfo
->ucNumberOfColumns
,
596 pRowInfo
->ucNumberOfColumns
);
597 if (iNbrOfColumns
!= (int)pRowInfo
->ucNumberOfColumns
) {
598 werr(0, "Skipping an unmatched table row");
602 #if defined(__FULL_TEXT_SEARCH)
603 /* No table formatting: use for full-text search (untested) */
604 for (iIndex
= 0; iIndex
< iNbrOfColumns
; iIndex
++) {
605 fprintf(pDiag
->pOutFile
, "%s\n" , aszColTxt
[iIndex
]);
608 if (bAddTableRow(pDiag
, aszColTxt
, iNbrOfColumns
,
609 pRowInfo
->asColumnWidth
, pRowInfo
->ucBorderInfo
)) {
610 /* All work has been done */
614 /* Fill the table with maximum column widths */
615 if (eConversionType
== conversion_text
||
616 eConversionType
== conversion_fmt_text
) {
617 if (iParagraphBreak
== 0 ||
618 iParagraphBreak
>= MAX_SCREEN_WIDTH
) {
619 dMagnify
= (double)MAX_SCREEN_WIDTH
;
620 } else if (iParagraphBreak
<= MIN_SCREEN_WIDTH
) {
621 dMagnify
= (double)MIN_SCREEN_WIDTH
;
623 dMagnify
= (double)iParagraphBreak
;
625 dMagnify
/= (double)DEFAULT_SCREEN_WIDTH
;
626 DBG_FLT_C(dMagnify
< 0.99 || dMagnify
> 1.01, dMagnify
);
630 tColumnWidthTotal
= 0;
631 for (iIndex
= 0; iIndex
< iNbrOfColumns
; iIndex
++) {
632 atColumnWidthMax
[iIndex
] = tComputeColumnWidthMax(
633 pRowInfo
->asColumnWidth
[iIndex
],
636 tColumnWidthTotal
+= atColumnWidthMax
[iIndex
];
640 * Get enough space for the row.
641 * Worst case: three bytes per UTF-8 character
643 tSize
= 3 * (1 + tColumnWidthTotal
+ (size_t)iNbrOfColumns
+ 3);
644 szLine
= xmalloc(tSize
);
647 /* Print one line of a table row */
650 *pcTxt
++ = TABLE_SEPARATOR_CHAR
;
651 for (iIndex
= 0; iIndex
< iNbrOfColumns
; iIndex
++) {
652 tColumnWidthMax
= atColumnWidthMax
[iIndex
];
653 if (aszColTxt
[iIndex
] == NULL
) {
654 /* Add an empty column */
656 iTmp
< (int)tColumnWidthMax
;
658 *pcTxt
++ = (char)FILLER_CHAR
;
660 *pcTxt
++ = TABLE_SEPARATOR_CHAR
;
664 /* Compute the length and width of the column text */
665 tLen
= tComputeStringLengthMax(
666 aszColTxt
[iIndex
], tColumnWidthMax
);
669 (aszColTxt
[iIndex
][tLen
- 1] == '\n' ||
670 aszColTxt
[iIndex
][tLen
- 1] == ' ')) {
671 aszColTxt
[iIndex
][tLen
- 1] = ' ';
674 tWidth
= tCountColumns(aszColTxt
[iIndex
], tLen
);
675 fail(tWidth
> tColumnWidthMax
);
676 tLen
= tGetBreakingPoint(aszColTxt
[iIndex
],
677 tLen
, tWidth
, tColumnWidthMax
);
678 tWidth
= tCountColumns(aszColTxt
[iIndex
], tLen
);
679 if (tLen
== 0 && *aszColTxt
[iIndex
] == '\0') {
681 aszColTxt
[iIndex
] = NULL
;
684 pcTxt
+= sprintf(pcTxt
,
685 "%.*s", (int)tLen
, aszColTxt
[iIndex
]);
686 if (tLen
== 0 && *aszColTxt
[iIndex
] != ' ') {
687 tLen
= tGetCharacterLength(
689 DBG_CHR(*aszColTxt
[iIndex
]);
693 aszColTxt
[iIndex
] += tLen
;
694 while (*aszColTxt
[iIndex
] == ' ') {
697 if (*aszColTxt
[iIndex
] == '\0') {
698 /* This row is now complete */
699 aszColTxt
[iIndex
] = NULL
;
701 /* This row needs more lines */
705 /* Fill up the rest */
707 iTmp
< (int)tColumnWidthMax
- (int)tWidth
;
709 *pcTxt
++ = (char)FILLER_CHAR
;
712 *pcTxt
++ = TABLE_SEPARATOR_CHAR
;
715 /* Output the table row line */
718 tRow
.szStorage
= szLine
;
719 fail(pcTxt
< szLine
);
720 tRow
.tNextFree
= (size_t)(pcTxt
- szLine
);
721 tRow
.lStringWidth
= lComputeStringWidth(
726 vString2Diagram(pDiag
, &tRow
);
728 /* Clean up before you leave */
729 szLine
= xfree(szLine
);
730 #endif /* __FULL_TEXT_SEARCH */
731 } /* end of vTableRow2Window */