1 /* vertical.c - routines that handle vertical space issues
2
3 Copyright (C) 2002 The Free Software Foundation
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 This file is available from http://sourceforge.net/projects/latex2rtf/
20
21 **************************************************************************
22
23 The tricky part is that latex2rtf is a one-pass converter. The right thing
24 must be done with each character. Consider the sequence
25
26 \vspace{1cm}\noindent New paragraph.
27
28 When latex2rtf reaches the 'N', then a new paragraph should be started. We
29 know it is a new paragraph because \vspace should have put the converter
30 into a MODE_VERTICAL and characters are only emitted in MODE_HORIZONTAL.
31
32 RTF needs to set up the entire paragraph at one time and the
33
34 * text alignment
35 * line spacing
36 * vertical space above
37 * left margin
38 * right margin
39 * paragraph indentation
40
41 must all be emitted at this time. This file contains routines
42 that affect these quantities
43 ******************************************************************************/
44
45 #include <string.h>
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <ctype.h>
49 #include "main.h"
50 #include "funct1.h"
51 #include "cfg.h"
52 #include "utils.h"
53 #include "parser.h"
54 #include "lengths.h"
55 #include "vertical.h"
56 #include "convert.h"
57 #include "commands.h"
58 #include "styles.h"
59 #include "fonts.h"
60 #include "stack.h"
61 #include "xrefs.h"
62 #include "counters.h"
63 #include "fields.h"
64 #include "acronyms.h"
65
66 static int g_TeX_mode = MODE_VERTICAL;
67 static int g_line_spacing = 240;
68 static int g_paragraph_no_indent = FALSE;
69 static int g_paragraph_inhibit_indent = FALSE;
70 static int g_vertical_space_to_add = 0;
71 static int g_right_margin_indent;
72 static int g_left_margin_indent;
73 static int g_page_new = FALSE;
74 static int g_column_new = FALSE;
75 static int g_alignment = JUSTIFIED;
76 static int g_par_brace = 0;
77
78 char TexModeName[7][25] = { "bad", "internal vertical", "horizontal",
79 "restricted horizontal", "math", "displaymath", "vertical"
80 };
81
82 char ParOptionName[8][13] = { "bad", "FIRST", "GENERIC", "SECTION",
83 "EQUATION", "SLASHSLASH", "LIST", "ENVIRONMENT"};
84
85 /******************************************************************************
86 left and right margin accessor functions
87 ******************************************************************************/
setLeftMarginIndent(int indent)88 void setLeftMarginIndent(int indent)
89 {
90 g_left_margin_indent = indent;
91 }
92
setRightMarginIndent(int indent)93 void setRightMarginIndent(int indent)
94 {
95 g_right_margin_indent = indent;
96 }
97
getLeftMarginIndent(void)98 int getLeftMarginIndent(void)
99 {
100 return g_left_margin_indent;
101 }
102
getRightMarginIndent(void)103 int getRightMarginIndent(void)
104 {
105 return g_right_margin_indent;
106 }
107
108 /******************************************************************************
109 paragraph alignment accessor functions
110 ******************************************************************************/
setAlignment(int align)111 void setAlignment(int align)
112 {
113 g_alignment = align;
114 }
115
getAlignment(void)116 int getAlignment(void)
117 {
118 return g_alignment;
119 }
120
121 /******************************************************************************
122 vertical space between paragraph accessor functions
123 ******************************************************************************/
setVspace(int space)124 void setVspace(int space)
125 {
126 g_vertical_space_to_add = space;
127 }
128
getVspace(void)129 int getVspace(void)
130 {
131 return g_vertical_space_to_add;
132 }
133
134 /******************************************************************************
135 line spacing accessor functions
136 ******************************************************************************/
setLineSpacing(int spacing)137 static void setLineSpacing(int spacing)
138 {
139 g_line_spacing = spacing;
140 }
141
getLineSpacing(void)142 static int getLineSpacing(void)
143 {
144 return g_line_spacing;
145 }
146
147
148 /******************************************************************************
149 TeX has six modes:
150
151 MODE_VERTICAL Building the main vertical list, from which the
152 pages of output are derived
153
154 MODE_INTERNAL_VERTICAL Building a vertical list from a vbox
155
156 MODE_HORIZONTAL Building a horizontal list for a paragraph
157
158 MODE_RESTICTED_HORIZONTAL Building a horizontal list for an hbox
159
160 MODE_MATH Building a mathematical formula to be placed in a
161 horizontal list
162
163 MODE_DISPLAYMATH Building a mathematical formula to be placed on a
164 line by itself, temporarily interrupting the current paragraph
165
166 LaTeX has three modes: paragraph mode, math mode, or left-to-right mode.
167 This is not a particularly useful, since paragraph mode is a combination of
168 vertical and horizontal modes.
169
170 Why bother keeping track of modes? Mostly so that paragraph indentation gets handled
171 correctly, as well as vertical and horizontal space. The mode is also the primary
172 way that the end of a paragraph is signalled.
173 ******************************************************************************/
setTexMode(int mode)174 void setTexMode(int mode)
175 {
176 diagnostics(6, "TeX mode setting from [%s] -> [%s]", TexModeName[g_TeX_mode], TexModeName[mode]);
177 g_TeX_mode = mode;
178 }
179
getTexMode(void)180 int getTexMode(void)
181 {
182 return g_TeX_mode;
183 }
184
changeTexMode(int mode)185 void changeTexMode(int mode)
186 {
187 diagnostics(6, "TeX mode changing from [%s] -> [%s]", TexModeName[g_TeX_mode], TexModeName[mode]);
188
189 if (g_TeX_mode == MODE_VERTICAL && mode == MODE_HORIZONTAL)
190 startParagraph("Normal", PARAGRAPH_GENERIC);
191
192 if (g_TeX_mode == MODE_HORIZONTAL && mode == MODE_VERTICAL)
193 CmdEndParagraph(0);
194
195 g_TeX_mode = mode;
196 }
197
198 /******************************************************************************
199 RTF codes to create a new paragraph. If the paragraph should
200 not be indented then emit \fi0 otherwise use the current value
201 of \parindent as the indentation of the first line.
202
203 style describes the type of paragraph ...
204 "Normal"
205 "caption"
206 "author"
207 "bibitem"
208 "section"
209 etc.
210
211 indenting describes how this paragraph and (perhaps) the following
212 paragraph should be indented
213
214 PARAGRAPH_SECTION_TITLE (do not indent this paragraph or the next)
215 PARAGRAPH_FIRST (do not indent this paragraph but indent the next)
216 PARAGRAPH_GENERIC (indent as needed)
217 PARAGRAPH_LIST (the first paragraph of a list item)
218 PARAGRAPH_SLASHSLASH
219 PARAGRAPH_ENVIRONMENT (include topsep before and after final --- not impl.)
220
221 Sometimes it is necessary to influence the next paragraph will
222 be before it has been parsed. For example, a section command
223 should create a paragraph for the section title and then the
224 next paragraph encountered should be handled like as a first
225 paragraph.
226
227 The problem arises because "\n\n" means different things in different
228 contexts. After \section{aaa} "\n\n" does not indicate that the next
229 paragraph should be indented. However after \end{itemize} "\n\n"
230 means that the next paragraph should be indented. Now CmdEndParagraph()
231 will set g_paragraph_inhibit_indent to FALSE so that the common case
232 of starting new paragraphs is handled appropriately.
233
234 For PARAGRAPH_FIRST, then it is the first paragraph in a section.
235 Usually the first paragraph is not indented. However, when the
236 document is being typeset in french it should have normal indentation.
237 Another special case occurs when the paragraph being typeset is
238 in a list environment. In this case, we need to indent according
239 to the current parindent to obtain the proper hanging indentation
240
241 The default is to indent according to
242 the current parindent. However, if the g_paragraph_inhibit_indent
243 flag or the g_paragraph_no_indent flag is TRUE, then do not indent
244 the next line. Typically these flags are set just after a figure
245 or equation or table.
246 ******************************************************************************/
startParagraph(const char * style,int indenting)247 void startParagraph(const char *style, int indenting)
248 {
249 int width, a, b, c;
250 int parindent,parskip;
251 static char last_style[50] = "Normal";
252 static char the_style[50] = "Normal";
253 static int last_indent = 0;
254 static int next_paragraph_after_section = TRUE;
255
256 int orig_font_family = CurrentFontFamily();
257 int orig_font_size = CurrentFontSize();
258 int orig_font_series = CurrentFontSeries();
259 int orig_font_shape = CurrentFontShape();
260
261 /* special style "last" will just repeat previous */
262 if (strcmp(style,"last")==0) {
263 diagnostics(4,"using last style = '%s'",last_style);
264 if (indenting != PARAGRAPH_SLASHSLASH)
265 indenting = last_indent;
266 strcpy(the_style,last_style);
267 } else {
268 diagnostics(4,"using style = '%s'",style);
269 last_indent = indenting;
270 strcpy(last_style,style);
271 strcpy(the_style,style);
272 }
273
274 parindent = getLength("parindent");
275 parskip = getLength("parskip");
276
277 if (g_par_brace !=0 )
278 diagnostics(5,"******************* starting %s paragraph with braces = %d", style, g_par_brace);
279
280 if (g_par_brace == 1)
281 CmdEndParagraph(0);
282
283 width = getLength("textwidth");
284 a = (int) (0.45 * width);
285 b = (int) (0.50 * width);
286 c = (int) (0.55 * width);
287
288 switch(indenting) {
289
290 case PARAGRAPH_SECTION_TITLE: /* titles are never indented */
291 diagnostics(5, "PARAGRAPH_SECTION_TITLE");
292 parindent = 0;
293 break;
294
295 case PARAGRAPH_FIRST: /* French indents first paragraph */
296 diagnostics(5, "PARAGRAPH_FIRST");
297 if (!FrenchMode)
298 parindent = 0;
299 break;
300
301 case PARAGRAPH_EQUATION: /* typically centered with no indent */
302 diagnostics(5, "PARAGRAPH_EQUATION");
303 parindent = 0;
304 break;
305
306 case PARAGRAPH_SLASHSLASH: /* \\ is a line break, don't indent */
307 diagnostics(5, "PARAGRAPH_SLASHSLASH");
308 parindent = 0;
309 break;
310
311 case PARAGRAPH_LIST: /* first paragraph in a list, don't monkey */
312 diagnostics(5, "PARAGRAPH_LIST"); /* with indenting */
313 break;
314
315 default: /* Worry about not indenting */
316 diagnostics(5, "PARAGRAPH_GENERIC");
317 if (next_paragraph_after_section || g_paragraph_no_indent ||
318 g_paragraph_inhibit_indent || g_processing_list_environment)
319 parindent = 0;
320 break;
321 }
322
323 if (g_processing_preamble) {
324 diagnostics(5,"Encountered StartParagraph() in preamble");
325 return;
326 }
327
328 if (g_par_brace != 0)
329 diagnostics(5,"starting paragraph with braces = %d", g_par_brace);
330 g_par_brace++;
331
332 diagnostics(5, "Paragraph mode %s", TexModeName[getTexMode()]);
333 diagnostics(5, "Paragraph option %s", ParOptionName[indenting]);
334 diagnostics(5, "Noindent is %s", (g_paragraph_no_indent) ? "TRUE" : "FALSE");
335 diagnostics(5, "Inhibit is %s", (g_paragraph_inhibit_indent) ? "TRUE" : "FALSE");
336 diagnostics(5, "left indent is %d", g_left_margin_indent);
337 diagnostics(5, "right indent is %d", g_right_margin_indent);
338 diagnostics(5, "current parindent %d", getLength("parindent"));
339 diagnostics(5, "this parindent %d", parindent);
340 diagnostics(5, "current style is %s", the_style);
341 diagnostics(6, "current family %d", CurrentFontFamily());
342 diagnostics(6, "current font size %d", CurrentFontSize());
343 diagnostics(6, "current font series %d", CurrentFontSeries());
344 diagnostics(6, "current font shape %d", CurrentFontShape());
345
346 if (g_page_new) {
347 fprintRTF("\\page\n"); /* causes new page */
348 g_page_new = FALSE;
349 g_column_new = FALSE;
350 }
351
352 if (g_column_new) {
353 fprintRTF("\\column\n"); /* causes new column */
354 g_column_new = FALSE;
355 }
356
357 fprintRTF("\\pard\\plain");
358 InsertStyle(the_style);
359 if (strcmp(the_style,"equation")==0)
360 fprintRTF("\\tqc\\tx%d", b);
361 if (strcmp(the_style,"equationNum")==0)
362 fprintRTF("\\tqc\\tx%d\\tqr\\tx%d", b, width);
363 if (strcmp(the_style,"equationAlign")==0)
364 fprintRTF("\\tqr\\tx%d\\tql\\tx%d", a, b);
365 if (strcmp(the_style,"equationAlignNum")==0)
366 fprintRTF("\\tqr\\tx%d\\tql\\tx%d\\tqr\\tx%d", a, b, width);
367 if (strcmp(the_style,"equationArray")==0)
368 fprintRTF("\\tqr\\tx%d\\tqc\\tx%d\\tql\\tx%d", a, b, c);
369 if (strcmp(the_style,"equationArrayNum")==0)
370 fprintRTF("\\tqr\\tx%d\\tqc\\tx%d\\tql\\tx%d\\tqr\\tx%d", a, b, c, width);
371
372 if (strcmp(the_style,"bitmapCenter")==0)
373 fprintRTF("\\tqc\\tx%d\\tqr\\tx%d", b, width);
374
375 /* TODO change width/6 with hint */
376 if (strcmp(the_style,"acronym")==0)
377 fprintRTF("\\tx%d\\tqr\\tldot\\tx%d", acronymHint(width), width);
378
379 fprintRTF("\\sl%i\\slmult1 ", getLineSpacing());
380
381 if (getVspace() > 0)
382 fprintRTF("\\sb%d ", getVspace());
383 setVspace(parskip);
384
385 if (g_left_margin_indent != 0)
386 fprintRTF("\\li%d", g_left_margin_indent);
387
388 if (g_right_margin_indent != 0)
389 fprintRTF("\\ri%d", g_right_margin_indent);
390
391 fprintRTF("\\fi%d ", parindent);
392
393 /* these are strstr because might end in 0 */
394 if (strstr("part",the_style) == NULL &&
395 strstr("title",the_style) == NULL &&
396 strstr("chapter",the_style) == NULL &&
397 strstr("section",the_style) == NULL ) {
398
399 if (CurrentFontFamily() != orig_font_family)
400 fprintRTF("\\f%d ", orig_font_family);
401
402 if (CurrentFontSize() != orig_font_size)
403 fprintRTF("\\fs%d ", orig_font_size);
404
405 if (CurrentFontSeries() != orig_font_series)
406 CmdFontSeries(orig_font_series);
407
408 if (CurrentFontShape() != orig_font_shape)
409 CmdFontShape(orig_font_shape);
410 }
411
412 setTexMode(MODE_HORIZONTAL);
413
414 if (!g_processing_list_environment) {
415 g_paragraph_no_indent = FALSE;
416 if (indenting == PARAGRAPH_SECTION_TITLE)
417 g_paragraph_inhibit_indent = TRUE;
418 else
419 g_paragraph_inhibit_indent = FALSE;
420 }
421
422 if (indenting == PARAGRAPH_SECTION_TITLE && !FrenchMode)
423 next_paragraph_after_section = TRUE;
424 else
425 next_paragraph_after_section = FALSE;
426
427 }
428
CmdEndParagraph(int code)429 void CmdEndParagraph(int code)
430
431 /******************************************************************************
432 purpose : ends the current paragraph and return to MODE_VERTICAL.
433 ******************************************************************************/
434 {
435 int mode = getTexMode();
436 diagnostics(5, "CmdEndParagraph mode = %s, braces=%d", TexModeName[mode], g_par_brace);
437
438 if (g_par_brace == 1) {
439 endAllFields();
440 fprintRTF("\\par\n");
441 setTexMode(MODE_VERTICAL);
442 g_par_brace=0;
443 g_paragraph_inhibit_indent = FALSE;
444 } else {
445 if (getTexMode() != MODE_VERTICAL)
446 diagnostics(5,"*********************** ending paragraph with braces = %d", g_par_brace);
447 g_paragraph_inhibit_indent = FALSE;
448 }
449
450 }
451
CmdHfill(int code)452 void CmdHfill(int code)
453
454 /******************************************************************************
455 purpose : should do something about the paragraph style but
456 for now, just make sure that we are in horizontal mode.
457 ******************************************************************************/
458 {
459 if (getTexMode()==MODE_VERTICAL)
460 changeTexMode(MODE_HORIZONTAL);
461 }
462
CmdVspace(int code)463 void CmdVspace(int code)
464
465 /******************************************************************************
466 purpose : vspace, vspace*, and vskip
467
468 note that \vskip3mm will end a paragraph, but \vspace{1cm} will not.
469 ******************************************************************************/
470 {
471 int vspace=0;
472 char *s;
473
474 switch (code) {
475 case VSPACE_VSPACE:
476 s = getBraceParam();
477 vspace = getStringDimension(s);
478 free(s);
479 break;
480
481 case VSPACE_VSKIP:
482 vspace = getDimension();
483 if (getTexMode() != MODE_VERTICAL) {
484 CmdEndParagraph(0);
485 CmdIndent(INDENT_INHIBIT);
486 }
487 break;
488
489 case VSPACE_SMALL_SKIP:
490 vspace = getLength("smallskipamount");
491 if (getTexMode() != MODE_VERTICAL) {
492 CmdEndParagraph(0);
493 CmdIndent(INDENT_INHIBIT);
494 }
495 break;
496
497 case VSPACE_MEDIUM_SKIP:
498 vspace = getLength("medskipamount");
499 if (getTexMode() != MODE_VERTICAL) {
500 CmdEndParagraph(0);
501 CmdIndent(INDENT_INHIBIT);
502 }
503 break;
504
505 case VSPACE_BIG_SKIP:
506 vspace = getLength("bigskipamount");
507 if (getTexMode() != MODE_VERTICAL) {
508 CmdEndParagraph(0);
509 CmdIndent(INDENT_INHIBIT);
510 }
511 break;
512 }
513
514 if (getTexMode() == MODE_VERTICAL)
515 setVspace(getVspace()+vspace);
516 }
517
CmdIndent(int code)518 void CmdIndent(int code)
519
520 /******************************************************************************
521 purpose : set flags so that startParagraph() does the right thing
522
523 INDENT_INHIBIT allows the next paragraph to be indented if
524 a paragraph break occurs before startParagraph() is called
525
526 INDENT_NONE tells startParagraph() to not indent the next paragraph
527
528 INDENT_USUAL has startParagraph() use the value of \parindent
529 ******************************************************************************/
530 {
531 diagnostics(5, "CmdIndent TeX Mode = %s", TexModeName[getTexMode()]);
532 if (code == INDENT_NONE)
533 g_paragraph_no_indent = TRUE;
534
535 else if (code == INDENT_INHIBIT)
536 g_paragraph_inhibit_indent = TRUE;
537
538 else if (code == INDENT_USUAL) {
539 g_paragraph_no_indent = FALSE;
540 g_paragraph_inhibit_indent = FALSE;
541 }
542 diagnostics(5, "Noindent is %d", (int) g_paragraph_no_indent);
543 diagnostics(5, "Inhibit is %d", (int) g_paragraph_inhibit_indent);
544 }
545
CmdNewPage(int code)546 void CmdNewPage(int code)
547
548 /******************************************************************************
549 purpose: starts a new page
550 parameter: code: newpage or newcolumn-option
551 ******************************************************************************/
552 {
553 diagnostics(5, "CmdNewPage mode = %d", getTexMode());
554 switch (code) {
555 case NewPage:
556 g_page_new = TRUE;
557 break;
558
559 case NewColumn:
560 g_column_new = TRUE;
561 break;
562 }
563 }
564
565 /******************************************************************************
566 purpose: support for \singlespacing, \onehalfspacing, and \doublespacing
567 ******************************************************************************/
CmdLineSpacing(int code)568 void CmdLineSpacing(int code)
569 {
570 setLineSpacing(code);
571 }
572
573 /******************************************************************************
574 purpose: support \begin{spacing}{xx} ... \end{spacing}
575 ******************************************************************************/
CmdSpacingEnviron(int code)576 void CmdSpacingEnviron(int code)
577 {
578 char *sizeParam;
579 static int originalSpacing=240;
580 float spacing;
581 int true_code = code & ~ON;
582
583 if (code & ON) {
584 originalSpacing = getLineSpacing();
585 if (true_code==2)
586 setLineSpacing(480);
587 else {
588 sizeParam = getBraceParam();
589 if (*sizeParam) {
590 sscanf(sizeParam, "%f", &spacing);
591 setLineSpacing((int)240*spacing);
592 free(sizeParam);
593 }
594 }
595 PushEnvironment(SPACING_MODE);
596 return;
597 }
598
599 CmdEndParagraph(0);
600 PopEnvironment();
601 setLineSpacing(originalSpacing);
602 }
603
CmdAlign(int code)604 void CmdAlign(int code)
605
606 /*****************************************************************************
607 purpose : sets the alignment for a paragraph
608 parameter : code: alignment centered, justified, left or right
609 ********************************************************************************/
610 {
611 char *s;
612 static char old_alignment_before_center = JUSTIFIED;
613 static char old_alignment_before_right = JUSTIFIED;
614 static char old_alignment_before_left = JUSTIFIED;
615 static char old_alignment_before_centerline = JUSTIFIED;
616
617 if (code == PAR_VCENTER) {
618 int restart_field=0;
619
620 if (EQ_field_active()) {
621 diagnostics(4,"ending field due to \\vcenter");
622 restart_field = 1;
623 endCurrentField();
624 }
625
626 s = getBraceParam();
627 ConvertString(s);
628 free(s);
629
630 if (restart_field)
631 startField(FIELD_EQ);
632 return;
633 }
634
635 CmdEndParagraph(0);
636 switch (code) {
637 case (PAR_CENTERLINE):
638 old_alignment_before_centerline = getAlignment();
639 setAlignment(CENTERED);
640 fprintRTF("{");
641 diagnostics(4, "Entering Convert from CmdAlign (centerline)");
642 Convert();
643 diagnostics(4, "Exiting Convert from CmdAlign (centerline)");
644 setAlignment(old_alignment_before_centerline);
645 CmdEndParagraph(0);
646 fprintRTF("}");
647 break;
648
649 case (PAR_RAGGEDRIGHT):
650 old_alignment_before_centerline = getAlignment();
651 setAlignment(LEFT);
652
653 diagnostics(4, "Entering Convert from CmdAlign (raggedright)");
654 Convert();
655 diagnostics(4, "Exiting Convert from CmdAlign (raggedright)");
656 setAlignment(old_alignment_before_centerline);
657 CmdEndParagraph(0);
658
659 break;
660
661 case (PAR_CENTER | ON):
662 CmdIndent(INDENT_NONE);
663 old_alignment_before_center = getAlignment();
664 setAlignment(CENTERED);
665 break;
666 case (PAR_CENTER | OFF):
667 setAlignment(old_alignment_before_center);
668 CmdEndParagraph(0);
669 CmdIndent(INDENT_INHIBIT);
670 break;
671
672 case (PAR_RIGHT | ON):
673 old_alignment_before_right = getAlignment();
674 setAlignment(RIGHT);
675 CmdIndent(INDENT_NONE);
676 break;
677 case (PAR_RIGHT | OFF):
678 setAlignment(old_alignment_before_right);
679 CmdIndent(INDENT_INHIBIT);
680 break;
681
682 case (PAR_LEFT | ON):
683 old_alignment_before_left = getAlignment();
684 setAlignment(LEFT);
685 CmdIndent(INDENT_NONE);
686 break;
687 case (PAR_LEFT | OFF):
688 setAlignment(old_alignment_before_left);
689 CmdIndent(INDENT_INHIBIT);
690 break;
691 case (PAR_CENTERING):
692 CmdIndent(INDENT_NONE);
693 old_alignment_before_center = getAlignment();
694 setAlignment(CENTERED);
695 break;
696 }
697 }
698
699