1 /*
2 * RTF-to-LaTeX2e translation writer code.
3 * (c) 1999 Ujwal S. Sathyam
4 * (c) 2011, 2012 Scott Prahl
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 # include <stdint.h>
22 # include <stdio.h>
23 # include <string.h>
24 # include <stdlib.h>
25 # include <ctype.h>
26 # include <unistd.h>
27 # include <math.h>
28
29 # include "rtf.h"
30 # include "tokenscan.h"
31 # include "cole.h"
32 # include "rtf2latex2e.h"
33 # include "eqn.h"
34 # include "init.h"
35
36 void __cole_dump(void *_m, void *_start, uint32_t length, char *msg);
37 void ExamineToken(char * tag);
38 boolean ConvertRawEquationFile(char *rawFileName);
39
40 # define MAX_BLANK_LINES 2
41 # define MATH_NONE_MODE 0
42 # define MATH_INLINE_MODE 1
43 # define MATH_DISPLAY_MODE 2
44
45 # define UNDEFINED_COLUMN_VALUE -10000
46 # define ROUNDF(f) ((int)(f + 0.5))
47
48 extern FILE *ifp, *ofp;
49
50 # define EQUATION_OFFSET 35
51 # define MTEF_HEADER_SIZE 28
52
53 int UsedColor[256];
54 const char *objectClassList[] = {
55 "Unknown",
56 "Equation",
57 "Word.Picture",
58 "MSGraph.Chart",
59 NULL
60 };
61
62 const char *justificationList[] = {
63 "\\raggedright",
64 "\\centering",
65 "\\raggedleft"
66 };
67
68 const char *environmentList[] = {
69 "flushleft",
70 "center",
71 "flushright"
72 };
73
74 const char *fontSizeList[] = {
75 "\\tiny",
76 "\\scriptsize",
77 "\\footnotesize",
78 "\\small",
79 "\\normalsize",
80 "\\large",
81 "\\Large",
82 "\\LARGE",
83 "\\huge",
84 "\\Huge"
85 };
86
87 static struct {
88 int cols;
89 int writtenCols;
90 boolean newSection; /* true when \sect token has been seen */
91 boolean newPage;
92 } section;
93
94 static struct {
95 int class;
96 char className[rtfBufSiz];
97 int shape;
98 } object;
99
100 int wrapCount = 0;
101 int shapeObjectType;
102 boolean nowBetweenParagraphs;
103 boolean nowBetweenCells;
104 boolean suppressLineBreak;
105 boolean suppressSpaceBetweenParagraphs;
106 boolean requireSetspacePackage;
107 boolean requireTablePackage;
108 boolean requireMultirowPackage;
109 boolean requireGraphicxPackage;
110 boolean requireAmsSymbPackage;
111 boolean requireMultiColPackage;
112 boolean requireUlemPackage;
113 boolean requireFixLtx2ePackage;
114 boolean requireHyperrefPackage;
115 boolean requireAmsMathPackage;
116 boolean requireFancyHdrPackage;
117 boolean requireAMSSymbolPackage;
118 size_t packagePos;
119 size_t beginDocumentPos;
120 boolean insideTable;
121 boolean insideFootnote;
122 boolean insideHyperlink;
123 boolean insideHeaderFooter;
124 boolean insideShapeGroup;
125 int insideEquation;
126 int blackColor;
127
128 char *preambleFancyHeader;
129 char *preambleFancyHeaderFirst;
130
131 char *outMap[rtfSC_MaxChar];
132
133 FILE *ostream;
134 parStyleStruct paragraph, paragraphWritten;
135 textStyleStruct textStyle, textStyleWritten;
136 int current_vspace;
137 pictureStruct picture;
138 equationStruct oleEquation;
139 tableStruct table;
140 shapeStruct shape;
141
142 int g_debug_par_start = 0;
143 int g_debug_table_prescan = 0;
144 int g_debug_table_writing = 0;
145 int g_debug_char_style = 0;
146 int textWidth = 345*20; /* \textwidth for 10pt article size in twips*/
147 int latexColumnSeparation = 12 *20; /*default intercolumn separation of tabular in twips */
148 char *UnicodeSymbolFontToLatex[];
149 char *UnicodeGreekToLatex[];
150
151 /*
152 * a useful diagnostic (debugging) function to examine the token just read.
153 */
ExamineToken(char * tag)154 void ExamineToken(char * tag)
155 {
156 printf("********** %s **********\n", tag);
157 printf("* Token is %s\n", rtfTextBuf);
158 printf("* Class is %3d", rtfClass);
159 switch (rtfClass) {
160 case rtfUnknown: printf(" (rtfUnknown)\n"); break;
161 case rtfGroup: printf(" (rtfGroup)\n"); break;
162 case rtfText: printf(" (rtfText)\n"); break;
163 case rtfControl: printf(" (rtfControl)\n"); break;
164 case rtfEOF: printf(" (rtfEOF)\n"); break;
165 default: printf(" (not one of the basic five)\n"); break;
166 }
167
168 printf("* Major is %3d", rtfMajor);
169 if (rtfClass == rtfText) {
170 if (rtfMajor == 0x0A)
171 printf(" raw=LF \n");
172 else if (rtfMajor == 0x0D)
173 printf(" raw= CR \n");
174 else
175 printf(" raw='%c' \n", rtfMajor);
176 } else if (rtfClass == rtfGroup) {
177 printf(" (%s)\n", rtfMajor? "rtfEndGroup" : "rtfBeginGroup");
178 } else {
179 switch (rtfMajor) {
180 case rtfVersion: printf(" (rtfVersion)\n"); break;
181 case rtfDefFont: printf(" (rtfDefFont)\n"); break;
182 case rtfCharSet: printf(" (rtfCharSet)\n"); break;
183 case rtfDestination: printf(" (rtfDestination)\n"); break;
184 case rtfFontFamily: printf(" (rtfFontFamily)\n"); break;
185 case rtfFontAttr: printf(" (rtfFontAttr)\n"); break;
186 case rtfColorName: printf(" (rtfColorName)\n"); break;
187 case rtfFileAttr: printf(" (rtfFileAttr)\n"); break;
188 case rtfFileSource: printf(" (rtfFileSource)\n"); break;
189 case rtfStyleAttr: printf(" (rtfStyleAttr)\n"); break;
190 case rtfKeyCodeAttr: printf(" (rtfKeyCodeAttr)\n"); break;
191 case rtfDocAttr: printf(" (rtfDocAttr)\n"); break;
192 case rtfSectAttr: printf(" (rtfSectAttr)\n"); break;
193 case rtfParAttr: printf(" (rtfParAttr)\n"); break;
194 case rtfPosAttr: printf(" (rtfPosAttr)\n"); break;
195 case rtfTblAttr: printf(" (rtfTblAttr)\n"); break;
196 case rtfCharAttr: printf(" (rtfCharAttr)\n"); break;
197 case rtfACharAttr: printf(" (rtfACharAttr)\n"); break;
198 case rtfSpecialChar: printf(" (rtfSpecialChar)\n"); break;
199 case rtfBookmarkAttr: printf(" (rtfBookmarkAttr)\n"); break;
200 case rtfPictAttr: printf(" (rtfPictAttr)\n"); break;
201 case rtfObjAttr: printf(" (rtfObjAttr)\n"); break;
202 case rtfDrawAttr: printf(" (rtfDrawAttr)\n"); break;
203 case rtfFNoteAttr: printf(" (rtfFNoteAttr)\n"); break;
204 case rtfFieldAttr: printf(" (rtfFieldAttr)\n"); break;
205 case rtfIndexAttr: printf(" (rtfIndexAttr)\n"); break;
206 case rtfTOCAttr: printf(" (rtfTOCAttr)\n"); break;
207 case rtfNeXTGrAttr: printf(" (rtfNeXTGrAttr)\n"); break;
208 case rtfShapeAttr: printf(" (rtfShapeAttr)\n"); break;
209 case rtfAnsiCharAttr: printf(" (rtfAnsiCharAttr)\n"); break;
210 case rtfEquationFieldCmd: printf(" (rtfEquationFieldCmd)\n"); break;
211 default: printf(" (unknown)\n"); break;
212 }
213 }
214
215 printf("* Minor is %3d", rtfMinor);
216 if (rtfClass == rtfText) {
217 printf(" std=0x%2x\n", rtfMinor);
218 } else {
219 printf("\n");
220 }
221 printf("* Param is %3d\n\n", (int) rtfParam);
222 }
223
224 char lastCharWritten;
225
PutIntAsUtf8(int x)226 static void PutIntAsUtf8(int x)
227 {
228 x &= 0x0000FFFF;
229 if (x < 0x80){
230 fputc((char) x, ostream);
231 wrapCount++;
232 lastCharWritten = (char) x;
233 return;
234 }
235
236 lastCharWritten = 0x80;;
237
238 if (x < 0xA0) {
239 fprintf(stderr, "there should be no such character c='%c'=0x%02x\n",(char) x,x);
240 return;
241 }
242
243 if (x<0x07FF) {
244 unsigned char d, e;
245 d = 0xC0 + (x & 0x07C0)/64;
246 e = 0x80 + (x & 0x003F);
247 fputc(d, ostream);
248 fputc(e, ostream);
249 wrapCount+=2;
250 return;
251 }
252
253 if (x<0xFFFF) {
254 unsigned char c, d, e;
255 c = 0xE0 + (x & 0xF000)/4096;
256 d = 0x80 + (x & 0x0FC0)/64;
257 e = 0x80 + (x & 0x003F);
258 fputc(c, ostream);
259 fputc(d, ostream);
260 fputc(e, ostream);
261 wrapCount+=3;
262 return;
263 }
264
265 }
266
267 /*
268 * Some environments fail if there is a blank line in
269 * the argument ... e.g., \section{} will set suppressLineBreak
270 * so, in this case, make sure that there is at least one char
271 * on each line before writing a '\n' to the latex file.
272 * We can't use wrapCount for this purpose because an "empty"
273 * line of spaces will also cause these environments grief.
274 *
275 * Furthermore, make sure that at most two '\n' are output
276 * to the latex file at a time just for esthetic reasons.
277 *
278 * Finally, reset wrapCount to zero every time a '\n' is
279 * written to the LaTeX file so that WrapText() below can
280 * work properly
281 */
PutLitChar(int c)282 static void PutLitChar(int c)
283 {
284 static int lf_in_succession = 0;
285 static int no_chars_in_row = true;
286
287 if (c != '\n') {
288 lf_in_succession = 0;
289 if (c != ' ') no_chars_in_row=false;
290 PutIntAsUtf8(c & 0x00ff);
291 return;
292 }
293
294 if (suppressLineBreak && no_chars_in_row) {
295 lf_in_succession = 0;
296 PutIntAsUtf8((int) ' ');
297 return;
298 }
299
300 lf_in_succession++;
301 no_chars_in_row = true;
302 if (lf_in_succession > 2)
303 return;
304
305 wrapCount = 0;
306
307 PutIntAsUtf8(c & 0x00ff);
308 }
309
310 /* copy a sequence of bytes to the ostream, avoiding all the encoding machinery */
PutLitByteStr(char * s)311 static void PutLitByteStr(char *s)
312 {
313 char *p = s;
314 if (!s) return;
315 while (*p) {
316 fputc(*p, ostream);
317 p++;
318 }
319 }
320
PutLitStr(char * s)321 static void PutLitStr(char *s)
322 {
323 char *p = s;
324 if (!s) return;
325 while (*p) {
326 PutLitChar(*p);
327 p++;
328 }
329 }
330
PutMathLitStr(char * s)331 static void PutMathLitStr(char *s)
332 {
333 if (s && *s) {
334 if (!insideEquation) PutLitStr("$");
335 PutLitStr(s);
336 if (!insideEquation) PutLitStr("$");
337 }
338 }
339
InsertNewLine(void)340 static void InsertNewLine(void)
341 {
342 PutLitChar('\n');
343 }
344
345 /* useful for just printing filenames in latex doc */
PutEscapedLitStr(char * s)346 static void PutEscapedLitStr(char *s)
347 {
348 int i=0;
349 while (s[i]) {
350 switch (s[i]) {
351 case '_':
352 PutLitStr("\\_");
353 break;
354 case '%':
355 PutLitStr("\\%");
356 break;
357 case '\\':
358 PutLitStr("\\textbackslash{}");
359 break;
360 default:
361 PutLitChar(s[i]);
362 }
363 i++;
364 }
365 }
366
367 /*
368 * This function reads colors from the color table and defines them in
369 * LaTeX format to be included in the preamble.
370 * This is done after the color table has been read (see above).
371 */
DefineColors(int ignoreUsedColors)372 static void DefineColors(int ignoreUsedColors)
373 {
374 RTFColor *rtfColorPtr;
375 int i = 1;
376 float Red, Blue, Green;
377 char buf[rtfBufSiz];
378
379 while ((rtfColorPtr = RTFGetColor(i)) != NULL) {
380 Red = rtfColorPtr->rtfCRed / 255.0;
381 Green = rtfColorPtr->rtfCGreen / 255.0;
382 Blue = rtfColorPtr->rtfCBlue / 255.0;
383
384 if (Red==0 && Green==0 && Blue==0)
385 blackColor = i;
386
387 if (ignoreUsedColors || (!ignoreUsedColors && UsedColor[i])) {
388 snprintf(buf, rtfBufSiz, "\\definecolor{color%02d}{rgb}",i);
389 PutLitStr(buf);
390 snprintf(buf, rtfBufSiz, "{%1.2f,%1.2f,%1.2f}\n", Red, Green, Blue);
391 PutLitStr(buf);
392 }
393
394 i++;
395 }
396 }
397
WriteColors(void)398 static void WriteColors(void)
399 {
400 ReadColorTbl();
401
402 if (!prefs[pConvertTextColor]) return;
403 DefineColors(true);
404 }
405
406 /*
407 * Eventually this should keep track of the destination of the
408 * current state and only write text when in the initial state.
409 *
410 * If the output sequence is unspecified in the output map, write
411 * the character's standard name instead. This makes map deficiencies
412 * obvious and provides incentive to fix it. :-)
413 */
414
PutStdChar(int stdCode)415 static void PutStdChar(int stdCode)
416 {
417 char *oStr = NULL;
418 char buf[rtfBufSiz];
419
420 if (stdCode == rtfSC_nothing) {
421 RTFMsg("* Unknown character %c (0x%x)!\n", rtfTextBuf[0], rtfTextBuf[0]);
422 ExamineToken("PutStdChar");
423 PutLitStr("(unknown char)");
424 return;
425 }
426 oStr = outMap[stdCode];
427 if (!oStr) { /* no output sequence in map */
428 snprintf(buf, rtfBufSiz, "(%s)", RTFStdCharName(stdCode));
429 oStr = buf;
430 PutLitStr(oStr);
431 return;
432 }
433
434 if (oStr[0] == '0' && (oStr[1] == 'x' || oStr[1] == 'X')) {
435 int x = RTFHexStrToInt(oStr);
436 /*fprintf(stderr,"hex string = '%s' = %d\n",oStr,x);*/
437 PutIntAsUtf8(x);
438 return;
439 }
440
441 PutLitStr(oStr);
442 }
443
444 /*
445 * make sure we write this all important stuff. This routine is called
446 * whenever something is written to the output file.
447 */
448 static int wroteBeginDocument = false;
CheckForBeginDocument(void)449 static int CheckForBeginDocument(void)
450 {
451 char buf[100];
452
453 if (insideHeaderFooter) return 0;
454
455 if (!wroteBeginDocument) {
456
457 if (preambleOurDefs) free(preambleOurDefs);
458 preambleOurDefs = malloc(1000);
459 preambleOurDefs[0] = '\0';
460
461 if (prefs[pConvertPageSize]) {
462 snprintf(buf, 100, "\\setlength{\\oddsidemargin}{%dpt}\n", 72 - prefs[pPageLeft]/20);
463 strcat(preambleOurDefs,buf);
464 snprintf(buf, 100, "\\setlength{\\evensidemargin}{%dpt}\n", 72 - prefs[pPageRight]/20);
465 strcat(preambleOurDefs,buf);
466 snprintf(buf, 100, "\\setlength{\\textwidth}{%dpt}\n", (prefs[pPageWidth] - prefs[pPageLeft] - prefs[pPageRight])/20);
467 strcat(preambleOurDefs,buf);
468 }
469
470 if (!prefs[pConvertTextNoTab])
471 strcat(preambleOurDefs,"\\newcommand{\\tab}{\\hspace{5mm}}\n\n");
472 PutLitByteStr(preambleOurDefs);
473
474 beginDocumentPos = ftell(ofp);
475 if (g_shouldIncludePreamble)
476 PutLitStr("\\begin{document}\n");
477 wroteBeginDocument = true;
478 return 1;
479 }
480 return 0;
481 }
482
483 /*
484 * This function initializes the text style.
485 */
InitTextStyle(void)486 static void InitTextStyle(void)
487 {
488 textStyle.fontSize = normalSize;
489
490 textStyle.bold = 0;
491 textStyle.italic = 0;
492 textStyle.underlined = 0;
493 textStyle.dbUnderlined = 0;
494 textStyle.smallCaps = 0;
495 textStyle.subScript = 0;
496 textStyle.superScript = 0;
497 textStyle.foreColor = 0; /* black */
498 textStyle.fontNumber = -1;
499 textStyle.charCode = genCharCode;
500 textStyle.mathRoman = 0;
501
502 textStyle.allCaps = 0;
503 textStyle.backColor = 0;
504 }
505
506 /*
507 * This function initializes the paragraph style.
508 */
InitParagraphStyle(void)509 static void InitParagraphStyle(void)
510 {
511 paragraph.firstIndent = 0;
512 paragraph.leftIndent = 0;
513 paragraph.rightIndent = 0;
514 paragraph.spaceBefore = 0;
515 paragraph.extraIndent = 0;
516 paragraph.styleIndex = -1;
517 }
518
SameTextStyle(void)519 static int SameTextStyle(void)
520 {
521 if (prefs[pConvertTextSize] && textStyleWritten.fontSize != textStyle.fontSize) return false;
522
523 if (prefs[pConvertTextColor] && textStyleWritten.foreColor != textStyle.foreColor) return false;
524
525 if (textStyleWritten.superScript != textStyle.superScript) return false;
526
527 if (textStyleWritten.subScript != textStyle.subScript) return false;
528
529 if (!prefs[pConvertTextForm]) return true;
530
531 if (textStyleWritten.italic != textStyle.italic) return false;
532
533 if (textStyleWritten.bold != textStyle.bold) return false;
534
535 if (textStyleWritten.underlined != textStyle.underlined) return false;
536
537 if (textStyleWritten.smallCaps != textStyle.smallCaps) return false;
538
539 if (textStyleWritten.dbUnderlined != textStyle.dbUnderlined) return false;
540
541 if (textStyleWritten.charCode != textStyle.charCode) return false;
542
543 if (textStyleWritten.mathRoman != textStyle.mathRoman) return false;
544
545 return true;
546 }
547
548 /*
549 * This function ends all text styles
550 */
StopTextStyle(void)551 static void StopTextStyle(void)
552 {
553 if (prefs[pConvertTextSize] && textStyleWritten.fontSize != normalSize) {
554 PutLitStr("}");
555 textStyleWritten.fontSize=normalSize;
556 }
557
558 if (prefs[pConvertTextColor] && textStyleWritten.foreColor) {
559 PutLitStr("}");
560 textStyleWritten.foreColor=0; /* black */
561 }
562
563 if (textStyleWritten.subScript) {
564 PutLitStr("}");
565 textStyleWritten.subScript=false;
566 }
567
568 if (textStyleWritten.superScript) {
569 PutLitStr("}");
570 textStyleWritten.superScript=false;
571 }
572
573 if (!prefs[pConvertTextForm]) return;
574
575 if (textStyleWritten.italic) {
576 PutLitStr("}");
577 textStyleWritten.italic=false;
578 }
579
580 if (textStyleWritten.bold) {
581 PutLitStr("}");
582 textStyleWritten.bold=false;
583 }
584
585 if (textStyleWritten.smallCaps) {
586 PutLitStr("}");
587 textStyleWritten.smallCaps=false;
588 }
589
590 if (textStyleWritten.underlined) {
591 PutLitStr("}");
592 textStyleWritten.underlined=false;
593 }
594
595 if (textStyleWritten.dbUnderlined) {
596 PutLitStr("}");
597 textStyleWritten.dbUnderlined=false;
598 }
599
600 if (textStyleWritten.mathRoman) {
601 PutLitStr("}");
602 textStyleWritten.mathRoman=false;
603 }
604
605 }
606
607 /*
608 * Alters the written latex style so it matches the current RTF style
609 * This should only be called right before emitting a character
610 */
WriteTextStyle(void)611 static void WriteTextStyle(void)
612 {
613 char buf[100];
614
615 if (SameTextStyle()) return;
616
617 if (wroteBeginDocument || !insideHeaderFooter) StopTextStyle();
618
619 if (prefs[pConvertTextSize] && textStyleWritten.fontSize != textStyle.fontSize && !insideEquation) {
620 if (textStyle.fontSize != normalSize) {
621 snprintf(buf, 100, "{%s{}", fontSizeList[textStyle.fontSize]);
622 PutLitStr(buf);
623 }
624 textStyleWritten.fontSize=textStyle.fontSize;
625 }
626
627 if (textStyleWritten.mathRoman != textStyle.mathRoman) {
628 if (textStyle.mathRoman)
629 PutLitStr("\\mathrm{");
630 textStyleWritten.mathRoman=textStyle.mathRoman;
631 }
632
633 if (textStyleWritten.superScript != textStyle.superScript) {
634 if (textStyle.superScript) {
635 if (insideEquation)
636 PutLitStr("^{");
637 else
638 PutLitStr("\\textsuperscript{");
639 }
640 textStyleWritten.superScript=textStyle.superScript;
641 }
642
643 if (textStyleWritten.subScript != textStyle.subScript) {
644 if (textStyle.subScript) {
645 if (insideEquation)
646 PutLitStr("_{");
647 else
648 PutLitStr("\\textsubscript{");
649 }
650 textStyleWritten.subScript=textStyle.subScript;
651 requireFixLtx2ePackage = true;
652 }
653
654 if (prefs[pConvertTextColor] && textStyleWritten.foreColor != textStyle.foreColor && !insideEquation) {
655 if (textStyle.foreColor) {
656 snprintf(buf, 100, "{\\color{color%02d} ", textStyle.foreColor);
657 PutLitStr(buf);
658 UsedColor[textStyle.foreColor] = 1;
659 }
660 textStyleWritten.foreColor=textStyle.foreColor;
661 }
662
663 if (!prefs[pConvertTextForm]) return;
664
665 if (textStyleWritten.italic != textStyle.italic && !insideEquation) {
666 if (textStyle.italic)
667 PutLitStr("\\textit{");
668 textStyleWritten.italic=textStyle.italic;
669 }
670
671 if (textStyleWritten.bold != textStyle.bold) {
672 if (textStyle.bold)
673 if (insideEquation)
674 PutLitStr("\\mathbf{");
675 else
676 PutLitStr("\\textbf{");
677 textStyleWritten.bold=textStyle.bold;
678 }
679
680 if (textStyleWritten.underlined != textStyle.underlined && !insideEquation) {
681 if (textStyle.underlined)
682 PutLitStr("\\emph{");
683 requireUlemPackage = true;
684 textStyleWritten.underlined=textStyle.underlined;
685 }
686
687 if (textStyleWritten.smallCaps != textStyle.smallCaps && !insideEquation) {
688 if (textStyle.smallCaps)
689 PutLitStr("\\textsc{");
690 textStyleWritten.smallCaps=textStyle.smallCaps;
691 }
692
693 if (textStyleWritten.dbUnderlined != textStyle.dbUnderlined && !insideEquation) {
694 if (textStyle.dbUnderlined)
695 PutLitStr("\\uuline{");
696 requireUlemPackage = true;
697 textStyleWritten.dbUnderlined=textStyle.dbUnderlined;
698 }
699
700 if (textStyleWritten.charCode != textStyle.charCode) {
701 curCharCode = textStyle.charCode;
702 textStyleWritten.charCode=textStyle.charCode;
703 }
704 }
705
706 /*
707 * This handles font changing
708 *
709 * When switching fonts like Times -> Helvetica
710 *
711 * (1) the style might change from roman to sans serif
712 * (2) the character code translation may change because
713 * the font is encoded with a different codepage
714 */
SetFontStyle(void)715 static void SetFontStyle(void)
716 {
717 RTFFont *font;
718
719 textStyle.fontNumber = rtfParam;
720 font = RTFGetFont(rtfParam);
721 // fprintf(stderr, "Font %3d, cs=%3d, lookup=%p, name='%s'\n", font->rtfFNum, font->rtfFCharSet, font->rtfFCharCode, font->rtfFName);
722 textStyle.charCode = font->rtfFCharCode;
723 curCharCode = font->rtfFCharCode;
724 }
725
726 /*
727 * This function stores the text style.
728 */
SetTextStyle(void)729 static void SetTextStyle(void)
730 {
731 if (insideHyperlink)
732 return;
733
734 if (insideEquation) {
735 switch (rtfMinor) {
736 case rtfPlain:
737 InitTextStyle();
738 textStyle.mathRoman = (rtfParam) ? true : false;
739 break;
740 case rtfBold:
741 textStyle.bold = (rtfParam) ? true : false;
742 break;
743 case rtfSubScrShrink:
744 case rtfSubScript:
745 textStyle.subScript = (rtfParam) ? true : false;
746 textStyle.superScript = false;
747 break;
748 case rtfSuperScrShrink:
749 case rtfSuperScript:
750 textStyle.superScript = (rtfParam) ? true : false;
751 textStyle.subScript = false;
752 break;
753 }
754 return;
755 }
756
757 switch (rtfMinor) {
758 case rtfPlain:
759 InitTextStyle();
760 break;
761 case rtfSmallCaps:
762 textStyle.smallCaps = (rtfParam) ? true : false;
763 break;
764 case rtfAllCaps:
765 textStyle.allCaps = (rtfParam) ? true : false;
766 break;
767 case rtfItalic:
768 textStyle.italic = (rtfParam) ? true : false;
769 break;
770 case rtfBold:
771 textStyle.bold = (rtfParam) ? true : false;
772 break;
773 case rtfUnderline:
774 textStyle.underlined = (rtfParam) ? true : false;
775 break;
776 case rtfNoUnderline:
777 textStyle.underlined = false;
778 break;
779 case rtfDbUnderline:
780 textStyle.dbUnderlined = (rtfParam) ? true : false;
781 break;
782 case rtfForeColor:
783 if (rtfParam == blackColor)
784 textStyle.foreColor = 0;
785 else
786 textStyle.foreColor = rtfParam;
787 break;
788 case rtfSubScrShrink:
789 case rtfSubScript:
790 textStyle.subScript = (rtfParam) ? true : false;
791 textStyle.superScript = false;
792 break;
793 case rtfSuperScrShrink:
794 case rtfSuperScript:
795 textStyle.superScript = (rtfParam) ? true : false;
796 textStyle.subScript = false;
797 break;
798 case rtfFontSize:
799 textStyle.fontSize = normalSize;
800 if (rtfParam <= 12)
801 textStyle.fontSize = scriptSize;
802 else if (rtfParam <= 14)
803 textStyle.fontSize = footNoteSize;
804 else if (rtfParam <= 18)
805 textStyle.fontSize = smallSize;
806 else if (rtfParam <= 24)
807 textStyle.fontSize = normalSize;
808 else if (rtfParam <= 28)
809 textStyle.fontSize = largeSize;
810 else if (rtfParam <= 32)
811 textStyle.fontSize = LargeSize;
812 else if (rtfParam <= 36)
813 textStyle.fontSize = LARGESize;
814 else if (rtfParam <= 48)
815 textStyle.fontSize = giganticSize;
816 else if (rtfParam <= 72)
817 textStyle.fontSize = GiganticSize;
818 break;
819 case rtfFontNum:
820 SetFontStyle();
821 break;
822 case rtfDeleted:
823 RTFSkipGroup();
824 break;
825 }
826 }
827
setLineSpacing(void)828 static void setLineSpacing(void)
829 {
830 char buff[100];
831
832 if (!prefs[pConvertLineSpacing]) return;
833
834 if (paragraphWritten.lineSpacing == paragraph.lineSpacing)
835 return;
836
837 snprintf(buff, 100, "\\baselineskip=%dpt\n", abs(paragraph.lineSpacing)/20);
838 PutLitStr(buff);
839
840 if (g_debug_par_start) {
841 snprintf(buff, 100, "[ls=%d]", paragraph.lineSpacing);
842 PutLitStr(buff);
843 }
844
845 paragraphWritten.lineSpacing = paragraph.lineSpacing;
846 }
847
848 /*
849 * Allocate memory for new table cell
850 */
CellAllocate(void)851 static cellStruct * CellAllocate(void)
852 {
853 cellStruct *cell;
854
855 cell = New(cellStruct);
856 if (!cell) {
857 RTFPanic("Cannot allocate memory for cell entry");
858 exit(1);
859 }
860
861 return cell;
862 }
863
864 /*
865 * This routine sets attributes for the detected cell and
866 * adds it to the table.theCell list. Memory for cells is
867 * allocated dynamically as each cell is encountered.
868 */
CellInitialize(cellStruct * cell)869 static void CellInitialize(cellStruct *cell)
870 {
871 /*fprintf(stderr,"initializing cell %d, (x,y)=(%d,%d)\n",
872 table.cellCount, table.rows, (table.cellsInRow)[table.rows]);*/
873 cell->nextCell = table.theCell;
874 cell->row = table.rows;
875 cell->col = (table.cellsInRow)[table.rows];
876 if (table.cols == 0 || table.theCell == NULL)
877 cell->originalLeft = table.leftEdge;
878 else
879 cell->originalLeft = (table.theCell)->originalRight;
880 cell->originalRight = rtfParam;
881 cell->index = table.cellCount;
882 cell->verticalMerge = table.cellMergePar;
883 cell->leftBorder = table.limboCellLeftBorder;
884 cell->rightBorder = table.limboCellRightBorder;
885 cell->topBorder = table.limboCellTopBorder;
886 cell->bottomBorder = table.limboCellBottomBorder;
887 }
888
889 /*
890 * This function searches the cell list by cell index
891 * returns NULL if not found
892 */
CellGetByIndex(int cellNum)893 static cellStruct *CellGetByIndex(int cellNum)
894 {
895 cellStruct *cell;
896
897 if (cellNum == -1)
898 return (table.theCell);
899
900 for (cell = (table.theCell); cell != NULL; cell = cell->nextCell) {
901 if (cell->index == cellNum)
902 return cell;
903 }
904
905 if (!cell)
906 RTFPanic("CellGetByIndex: Attempting to access invalid cell with index %d\n", cellNum);
907
908 return NULL;
909 }
910
911 /*
912 * This function returns the cell from the current table
913 * for the specified row and column
914 */
CellGetByPosition(int therow,int thecol)915 static cellStruct *CellGetByPosition(int therow, int thecol)
916 {
917 cellStruct *cell;
918
919 if (!table.theCell)
920 RTFPanic("CellGetByPosition: Attempting to access invalid cell at row=%d, col=%d\n", therow, thecol);
921
922 for (cell = table.theCell; cell != NULL; cell = cell->nextCell) {
923 if (cell->row == therow && cell->col == thecol)
924 return cell;
925 }
926
927 return NULL;
928 }
929
930 /* returns the width of the current cell in points*/
CellWidth(cellStruct * cell)931 static int CellWidth(cellStruct *cell)
932 {
933 int cleft = (table.rightColumnBorders)[cell->col];
934 int cright = (table.rightColumnBorders)[cell->col+cell->columnSpan];
935
936 return (cright-cleft)/20;
937 }
938
939 /*
940 * Given a cell in the first row of a multirow cell, count the number
941 * of cells below that should be merged vertically
942 *
943 * unused at the moment
944
945 static int rowsInMultirow(cellStruct * cell)
946 {
947 int i;
948 for (i=cell->row+1; i < table.rows; i++) {
949 cellStruct *c = CellGetByPosition(i, cell->col);
950 if (c->verticalMerge != mergeAbove) break;
951 }
952 return i-cell->row;
953 }
954
955 *
956 * Counts the number of rows to be merged vertically for the
957 * current column and writes the corresponding \multirow statement.
958 *
959 * not used at the moment ... still needs work
960 *
961 static void CellMultirow(cellStruct * cell)
962 {
963 int rows;
964 char buf[rtfBufSiz];
965
966 rows = rowsInMultirow(cell);
967
968 if (rows < 2) return;
969
970 if (prefs[pConvertTableAlignment] && paragraph.alignment != left)
971 snprintf(buf, rtfBufSiz, "\\multirow{%d}{*}{%s{}", rows, justificationList[paragraph.alignment]);
972 else
973 snprintf(buf, rtfBufSiz, "\\multirow{%d}{*}{ ", rows);
974
975 PutLitStr(buf);
976 table.multiRow = true;
977 requireMultirowPackage = true;
978 }
979 */
980
NewSection(void)981 static void NewSection(void)
982 {
983 char buff[100];
984 if (section.cols > 1) {
985 snprintf(buff, 100, "\n\\begin{multicols}{%d}\n", section.cols);
986 PutLitStr(buff);
987 requireMultiColPackage = true;
988 section.writtenCols = section.cols;
989 }
990
991 section.newSection = false;
992 }
993
NewParagraph(void)994 static void NewParagraph(void)
995 {
996 char buff[100];
997
998 (void) CheckForBeginDocument();
999
1000 nowBetweenParagraphs = false;
1001
1002 if (insideFootnote || insideHeaderFooter) return;
1003
1004 if (prefs[pConvertInterParagraphSpace] && !insideTable) {
1005 if (!suppressSpaceBetweenParagraphs && (current_vspace || paragraph.spaceBefore) ) {
1006 snprintf(buff,100,"\\vspace{%dpt}\n", (current_vspace+paragraph.spaceBefore)/20);
1007 PutLitStr(buff);
1008 current_vspace = 0;
1009 }
1010 }
1011
1012 if (section.newSection && !insideTable) NewSection();
1013
1014 if (prefs[pConvertParagraphStyle] && paragraph.styleIndex != -1) {
1015 PutLitStr(Style2LatexOpen[paragraph.styleIndex]);
1016 paragraphWritten.styleIndex = paragraph.styleIndex;
1017 suppressLineBreak = true;
1018 return;
1019 }
1020
1021 // if ((!insideTable && prefs[pConvertParagraphAlignment]) ||
1022 // ( insideTable && prefs[pConvertTableAlignment] )) {
1023 if (!insideTable && prefs[pConvertParagraphAlignment]) {
1024 if (paragraphWritten.alignment != paragraph.alignment) {
1025
1026 if (paragraph.alignment == right)
1027 PutLitStr("\\begin{flushright}\n");
1028
1029 if (paragraph.alignment == center)
1030 PutLitStr("\\begin{center}\n");
1031
1032 paragraphWritten.alignment = paragraph.alignment;
1033 }
1034 }
1035
1036 if (insideTable) return;
1037
1038 setLineSpacing();
1039
1040 if (prefs[pConvertParagraphMargin]) {
1041 if (paragraphWritten.leftIndent != paragraph.leftIndent) {
1042 snprintf(buff, 100, "\\leftskip=%dpt\n", paragraph.leftIndent/20);
1043 if (paragraph.alignment != right && paragraph.alignment != center) {
1044 PutLitStr(buff);
1045 paragraphWritten.leftIndent = paragraph.leftIndent;
1046 }
1047 }
1048 }
1049
1050 if (prefs[pConvertParagraphIndent]) {
1051 if (paragraphWritten.firstIndent != paragraph.firstIndent+paragraph.extraIndent) {
1052 snprintf(buff, 100, "\\parindent=%dpt\n", (paragraph.firstIndent+paragraph.extraIndent)/20);
1053 if (paragraph.alignment != right && paragraph.alignment != center) {
1054 PutLitStr(buff);
1055 paragraphWritten.firstIndent = paragraph.firstIndent+paragraph.extraIndent;
1056 }
1057 paragraph.extraIndent=0;
1058 }
1059 }
1060
1061 suppressSpaceBetweenParagraphs = false;
1062 }
1063
EndSection(void)1064 static void EndSection(void)
1065 {
1066 if (section.writtenCols > 1) {
1067 PutLitStr("\n\\end{multicols}\n");
1068 section.writtenCols = 1;
1069 }
1070
1071 if (section.newPage)
1072 PutLitStr("\\newpage\n");
1073 }
1074
1075
1076 /*
1077 * This routine just closes the environments that have been written
1078 */
1079
FinalizeParagraph(void)1080 static void FinalizeParagraph(void)
1081 {
1082 char buf[rtfBufSiz];
1083
1084 if (insideHeaderFooter) return;
1085
1086 if (CheckForBeginDocument()) return;
1087
1088 StopTextStyle();
1089
1090 if (prefs[pConvertParagraphStyle] && (paragraphWritten.styleIndex != -1)) {
1091 PutLitStr(Style2LatexClose[paragraphWritten.styleIndex]);
1092 suppressLineBreak = false;
1093 paragraphWritten.styleIndex=-1;
1094 return;
1095 }
1096
1097 // if ( (!insideTable && paragraphWritten.alignment != paragraph.alignment) ||
1098 // (insideTable && prefs[pConvertTableAlignment] && nowBetweenCells)) {
1099
1100 if (insideTable && prefs[pConvertTableAlignment] ) {
1101 if (paragraphWritten.alignment == right || paragraphWritten.alignment == center){
1102 paragraphWritten.alignment = -900;
1103 paragraphWritten.leftIndent = -900;
1104 paragraphWritten.lineSpacing = -900;
1105 }
1106 }
1107
1108 if ( !insideTable && paragraphWritten.alignment != paragraph.alignment) {
1109
1110 if (g_debug_par_start) {
1111 snprintf(buf, rtfBufSiz, "[oldalign=%d, newalign=%d]", paragraphWritten.alignment, paragraph.alignment);
1112 PutLitStr(buf);
1113 }
1114
1115 if (paragraphWritten.alignment == right){
1116 PutLitStr("\n\\end{flushright}");
1117 paragraphWritten.alignment = -900;
1118 paragraphWritten.leftIndent = -900;
1119 paragraphWritten.lineSpacing = -900;
1120 }
1121
1122 if (paragraphWritten.alignment == center){
1123 PutLitStr("\n\\end{center}");
1124 paragraphWritten.alignment = -900;
1125 paragraphWritten.leftIndent = -900;
1126 paragraphWritten.lineSpacing = -900;
1127 }
1128 }
1129
1130 if (section.newSection) EndSection();
1131 insideEquation = false;
1132 }
1133
1134 /*
1135 * Closes last paragraph and insert some line feeds.
1136 */
1137
EndParagraph(void)1138 static void EndParagraph(void)
1139 {
1140 FinalizeParagraph();
1141
1142 if (insideFootnote && !suppressSpaceBetweenParagraphs) {
1143 InsertNewLine();
1144 InsertNewLine();
1145 return;
1146 }
1147
1148 if (insideTable) {
1149 PutLitStr("\\linebreak{}\n");
1150 return;
1151 }
1152
1153 if (!suppressSpaceBetweenParagraphs) {
1154 InsertNewLine();
1155 InsertNewLine();
1156 }
1157
1158 if (section.newSection) EndSection();
1159 }
1160
1161 /*
1162 * Writes cell information for each cell. Similiar to NewParagraph()
1163 * Each cell is defined in a multicolumn
1164 * environment for maximum flexibility. Useful when we have merged rows and columns.
1165 */
NewCell(void)1166 static void NewCell(void)
1167 {
1168 char buf[rtfBufSiz];
1169 cellStruct *cell = CellGetByIndex(table.cellCount);
1170
1171 if (cell->col != 0) PutLitStr(" & ");
1172
1173 if (cell->columnSpan == 1) {
1174
1175 if (prefs[pConvertTableAlignment] && paragraph.alignment != left) {
1176 snprintf(buf,rtfBufSiz, "%s ", justificationList[paragraph.alignment]);
1177 PutLitStr(buf);
1178 }
1179
1180 } else {
1181
1182 if (cell->col == 0)
1183 snprintf(buf, rtfBufSiz, "\\multicolumn{%d}{|p{%dpt}|}{", cell->columnSpan, CellWidth(cell));
1184 else
1185 snprintf(buf, rtfBufSiz, "\\multicolumn{%d}{p{%dpt}|}{", cell->columnSpan, CellWidth(cell));
1186 PutLitStr(buf);
1187
1188 if (prefs[pConvertTableAlignment]) {
1189 if (paragraph.alignment!=left){
1190 snprintf(buf,rtfBufSiz, "%s ", justificationList[paragraph.alignment]);
1191 PutLitStr(buf);
1192 }
1193 InsertNewLine();
1194 }
1195
1196 }
1197
1198 // if (cell->verticalMerge == mergeTop)
1199 // CellMultirow(cell);
1200
1201 if (g_debug_table_writing) {
1202 snprintf(buf, rtfBufSiz, "[cell \\#%d]", table.cellCount);
1203 PutLitStr(buf);
1204 }
1205 nowBetweenCells = false;
1206 NewParagraph();
1207 }
1208
EndCell(void)1209 static void EndCell(void)
1210 {
1211 cellStruct *cell;
1212
1213 if (g_debug_table_writing) fprintf(stderr,"* Ending cell #%d\n", table.cellCount);
1214
1215 if (nowBetweenCells) {
1216 if (g_debug_table_writing) fprintf(stderr,"* cell #%d is empty\n", table.cellCount);
1217 NewCell();
1218 }
1219
1220 nowBetweenCells = true;
1221 FinalizeParagraph();
1222 // StopTextStyle();
1223 cell = CellGetByIndex(table.cellCount);
1224
1225 // if (cell->verticalMerge == mergeTop)
1226 // PutLitChar('}');
1227
1228 // if (prefs[pConvertTableAlignment])
1229 // PutLitStr("\\end{minipage}");
1230
1231 if (cell->columnSpan > 1)
1232 PutLitChar('}');
1233
1234 (table.cellCount)++;
1235 paragraph.alignment = left;
1236 }
1237
1238
1239
setPreamblePackages(int ignoreUsedColor)1240 static void setPreamblePackages(int ignoreUsedColor)
1241 {
1242 if (!preamblePackages)
1243 preamblePackages = malloc(1024);
1244
1245 preamblePackages[0] = '\0';
1246 if (requireSetspacePackage)
1247 strcat(preamblePackages,"\\usepackage{setspace}\n");
1248 if (requireGraphicxPackage)
1249 strcat(preamblePackages,"\\usepackage{graphicx}\n");
1250 if (requireTablePackage)
1251 strcat(preamblePackages,"\\usepackage{array}\n");
1252 if (requireMultirowPackage)
1253 strcat(preamblePackages,"\\usepackage{multirow}\n");
1254 if (requireAmsSymbPackage)
1255 strcat(preamblePackages,"\\usepackage{amssymb}\n");
1256 if (requireMultiColPackage)
1257 strcat(preamblePackages,"\\usepackage{multicol}\n");
1258 if (requireUlemPackage)
1259 strcat(preamblePackages,"\\usepackage{ulem}\n");
1260 if (requireFixLtx2ePackage)
1261 strcat(preamblePackages,"\\usepackage{fixltx2e}\n");
1262 if (requireAmsMathPackage)
1263 strcat(preamblePackages,"\\usepackage{amsmath}\n");
1264 if (requireHyperrefPackage)
1265 strcat(preamblePackages,"\\usepackage{hyperref}\n");
1266 if (requireAMSSymbolPackage)
1267 strcat(preamblePackages,"\\usepackage{amssymb}\n");
1268
1269
1270 /* almost certainly want these packages for russian */
1271 if (genCharCode == cp1251CharCode) {
1272 strcat(preamblePackages,"\\usepackage[T2A]{fontenc}\n");
1273 strcat(preamblePackages,"\\usepackage[russian]{babel}\n");
1274 }
1275
1276 if (requireFancyHdrPackage) {
1277 strcat(preamblePackages,"\\usepackage{fancyhdr}\n");
1278 strcat(preamblePackages,"\\renewcommand{\\headrulewidth}{0pt}\n");
1279 strcat(preamblePackages,"\\renewcommand{\\footrulewidth}{0pt}\n");
1280 }
1281
1282 if (prefs[pConvertTextColor]) {
1283 int i=0;
1284 int needPackage=false;
1285
1286 for (i=0; i<256; i++)
1287 if (UsedColor[i]) needPackage = true;
1288
1289 if (ignoreUsedColor || (!ignoreUsedColor && needPackage))
1290 strcat(preamblePackages,"\\usepackage{color}\n");
1291 }
1292 }
1293
1294 /*
1295 * This function writes the LaTeX header and includes some basic packages.
1296 */
WriteLaTeXHeader(void)1297 static void WriteLaTeXHeader(void)
1298 {
1299 int i, j;
1300
1301 PutLitStr(preambleFirstText); /* from pref/r2l-pref */
1302 InsertNewLine();
1303 PutLitStr(preambleSecondText); /* from pref/r2l-pref */
1304 InsertNewLine();
1305 PutLitStr(preambleDocClass); /* from pref/r2l-pref */
1306 InsertNewLine();
1307
1308 /* insert latex-encoding qualifier */
1309 PutLitStr(preambleUserText);
1310
1311 /* insert latex-encoding qualifier */
1312 PutLitStr(preambleEncoding);
1313
1314 /* to come back and write necessary \usepackage{...}
1315 * commands if necessary */
1316 packagePos = ftell(ofp);
1317
1318 for (j = 0; j <= PACKAGES; j++) {
1319 for (i = 0; i < 100; i++)
1320 PutLitChar(' ');
1321 PutLitChar('\n');
1322 }
1323 }
1324
1325 /* This function make sure that the output TeX file does not
1326 * contain one very, very long line of text.
1327 */
WrapText(void)1328 static void WrapText(void)
1329 {
1330 if (wrapCount < WRAP_LIMIT) return;
1331
1332 if (rtfMinor == rtfSC_space)
1333 PutLitChar('\n');
1334 }
1335
MicrosoftEQFieldLiteral(void)1336 static void MicrosoftEQFieldLiteral(void)
1337 {
1338 PutLitChar(rtfMinor);
1339 }
1340
1341 /*
1342 * Convert Microsoft Equation Command to LaTeX. The parser should call
1343 * this routine when something like \\s is encountered within a EQ field.
1344 *
1345 * Array switch: \\a()
1346 * Bracket: \\b()
1347 * Fraction: \\f(,)
1348 * Integral: \\i(,,)
1349 * Radical: \\r(,)
1350 * Superscript or Subscript: \\s()
1351 *
1352 * Displace: \\d() not done
1353 * List: \\l() not done
1354 * Overstrike: \\o() not done
1355 * Box: \\x() not done
1356 */
1357
MicrosoftEQFieldCommand(void)1358 static void MicrosoftEQFieldCommand(void)
1359 {
1360 /* subscript/superscript \\s\\up8(UB)\\s\\do8(2) */
1361 if (rtfMinor == 's' || rtfMinor == 'S') {
1362 // ExamineToken("EQ Subscript");
1363 RTFGetNonWhiteSpaceToken();
1364 if (rtfMajor == rtfEquationFieldCmd) {
1365 if (rtfMinor == 'u') PutLitStr("^{");
1366 if (rtfMinor == 'd') PutLitStr("_{");
1367 }
1368 RTFSkipToToken(rtfText,'(',9);
1369 RTFExecuteToToken(rtfText,')',10);
1370 PutLitChar('}');
1371 return;
1372 }
1373
1374 /* integrals \\i \\su(1,5,3) */
1375 if (rtfMinor == 'i' || rtfMinor == 'I') {
1376 // ExamineToken("EQ Integral");
1377 RTFGetNonWhiteSpaceToken();
1378 if (rtfMajor == rtfEquationFieldCmd) {
1379 if (rtfMinor == 's') PutLitStr("\\sum\\limits_{");
1380 if (rtfMinor == 'p') PutLitStr("\\prod\\limits_{");
1381 if (rtfMinor == 'i') PutLitStr("\\int\\limits_{");
1382 }
1383 RTFSkipToToken(rtfText,'(',9);
1384 RTFExecuteToToken(rtfText,',',13);
1385 RTFGetToken();
1386 PutLitStr("}^{");
1387 RTFExecuteToToken(rtfText,')',10);
1388 PutLitChar('}');
1389 return;
1390 }
1391
1392 /* fractions \\f(2,RateChange) */
1393 if (rtfMinor == 'f' || rtfMinor == 'F') {
1394 // ExamineToken("EQ Fraction");
1395 RTFSkipToToken(rtfText,'(',9);
1396 PutLitStr("{");
1397 RTFExecuteToToken(rtfText,',',13);
1398 /* discard comma */
1399 PutLitStr("\\over ");
1400 RTFExecuteParentheses();
1401 PutLitChar('}');
1402 return;
1403 }
1404
1405 /* roots \\r(3,x) */
1406 if (rtfMinor == 'r' || rtfMinor == 'R') {
1407 // ExamineToken("EQ Root");
1408 RTFSkipToToken(rtfText,'(',9);
1409 PutLitStr("\\sqrt[");
1410 RTFExecuteToToken(rtfText,',',13);
1411 /* discard comma */
1412 PutLitStr("]{");
1413 RTFExecuteParentheses();
1414 PutLitChar('}');
1415 return;
1416 }
1417
1418 /* braces \\b \\bc\\{ (\\r(3,x)) */
1419 if (rtfMinor == 'b' || rtfMinor == 'B') {
1420 char open = '(';
1421 char close = ')';
1422
1423 // ExamineToken("EQ Brace");
1424 RTFGetNonWhiteSpaceToken();
1425
1426 /* handle \\bc\\X \\lc\\X \\rc\\X */
1427 while (rtfMajor == rtfEquationFieldCmd) {
1428 char type = rtfMinor;
1429 RTFGetToken(); /* get and discard 'c' */
1430 RTFGetToken();
1431
1432 if (type == 'l') open = rtfMinor;
1433 if (type == 'r') close = rtfMinor;
1434 if (type == 'b') {
1435 open = rtfMinor;
1436 switch (open) {
1437 case '<': close = '>'; break;
1438 case '[': close = ']'; break;
1439 case '{': close = '}'; break;
1440 case '(': close = ')'; break;
1441 default: close = open;
1442 }
1443 }
1444 RTFGetNonWhiteSpaceToken();
1445 }
1446
1447 if (rtfMajor != '(') RTFSkipToToken(rtfText,'(',9);
1448 PutLitStr("\\left");
1449 if (open=='{') PutLitChar('\\');
1450 PutLitChar(open);
1451
1452 RTFExecuteParentheses();
1453
1454 PutLitStr("\\right");
1455 if (close=='}') PutLitChar('\\');
1456 PutLitChar(close);
1457 return;
1458 }
1459
1460 /* arrays { EQ \\a \\al \\co2 \\vs3 \\hs3(Axy,Bxy,A,B) } */
1461 if (rtfMinor == 'a' || rtfMinor == 'A') {
1462 int columns = 1;
1463 int align = 'l';
1464 int elements = '1';
1465 int i;
1466
1467 // ExamineToken("EQ Array");
1468 /* handle \\al \\co2 \\vs3 \\hs3 */
1469 while (rtfMajor == rtfEquationFieldCmd) {
1470 if (rtfMinor == 'c') {
1471 RTFGetToken(); /*discard 'o' */
1472 RTFGetToken();
1473 columns = rtfMajor - '0';
1474 }
1475 if (rtfMinor == 'a') {
1476 RTFGetToken();
1477 align = rtfMajor;
1478 }
1479 RTFGetNonWhiteSpaceToken();
1480 }
1481
1482 PutLitStr("\\begin{array}{");
1483 for (i=0; i<columns; i++) {PutLitChar(align);}
1484 PutLitStr("}\n");
1485
1486 RTFGetToken();
1487 while (rtfMajor != ')') {
1488 if (rtfMajor != ',')
1489 RTFRouteToken();
1490 else {
1491 elements++;
1492 if (elements % columns == 0)
1493 PutLitStr("\\\\\n");
1494 else
1495 PutLitStr(" & ");
1496 }
1497 RTFGetToken();
1498 }
1499 PutLitStr("\n\\end{array}");
1500 return;
1501 }
1502
1503 }
1504
PrepareForChar(void)1505 static void PrepareForChar(void)
1506 {
1507 if (insideTable && nowBetweenCells) {
1508 NewCell();
1509 return;
1510 }
1511
1512 if (nowBetweenParagraphs) {
1513
1514 if (rtfMinor == rtfSC_space) {
1515 paragraph.extraIndent += 72;
1516 return;
1517 }
1518
1519 EndParagraph();
1520 NewParagraph();
1521 }
1522
1523 if (insideHyperlink) {
1524 switch (rtfMinor) {
1525 case rtfSC_underscore:
1526 PutLitChar('H');
1527 return;
1528 case rtfSC_backslash:
1529 RTFGetToken(); /* ignore backslash */
1530 RTFGetToken(); /* and next character */
1531 return;
1532 }
1533 }
1534
1535 if (insideEquation && rtfMinor == rtfSC_backslash) {
1536 RTFGetToken();
1537 MicrosoftEQFieldCommand();
1538 }
1539
1540 if (rtfMinor >= rtfSC_therefore && rtfMinor < rtfSC_currency)
1541 requireAmsSymbPackage = true;
1542
1543 WriteTextStyle();
1544 }
1545
1546 /*
1547 * Write out a character. rtfMajor contains the input character, rtfMinor
1548 * contains the corresponding standard character code.
1549 *
1550 * If the input character isn't in the charset map, try to print some
1551 * representation of it.
1552 */
1553
TextClass(void)1554 static void TextClass(void)
1555 {
1556 PrepareForChar();
1557 PutStdChar(rtfMinor);
1558 WrapText();
1559 }
1560
1561 /*
1562 * Put a footnote wrapper around whatever is inside the footnote.
1563 */
ReadFootnote(void)1564 static void ReadFootnote(void)
1565 {
1566 int footnoteGL;
1567
1568 CheckForBeginDocument();
1569 StopTextStyle();
1570 footnoteGL = RTFGetBraceLevel();
1571 PutLitStr("\\footnote{");
1572 insideFootnote = true;
1573 nowBetweenParagraphs = false; /*no need to end last paragraph */
1574 while (RTFGetBraceLevel() >= footnoteGL) {
1575 RTFGetToken();
1576 RTFRouteToken();
1577 }
1578 PutLitStr("}");
1579 insideFootnote = false;
1580 }
1581
1582 /* <celldef> = (\clmgf? & \clmrg? & \clvmgf? & \clvmrg? <celldgu>? & <celldgl>? &
1583 <cellalign>? & <celltop>? & <cellleft>? & <cellbot>? & <cellright>? &
1584 <cellshad>? & <cellflow>? & clFitText? & clNoWrap? & <cellwidth>? <cellrev>? &
1585 <cellins>? & <celldel>? & <cellpad>? & <cellsp>?) \cellxN
1586 */
1587
DoTableAttr(void)1588 static void DoTableAttr(void)
1589 {
1590 cellStruct *cell;
1591
1592 switch (rtfMinor) {
1593 case rtfRowLeftEdge:
1594 table.leftEdge = rtfParam;
1595 break;
1596 case rtfCellPos: /* only \cellx is a required cell token */
1597 cell = CellAllocate();
1598 CellInitialize(cell);
1599
1600 table.theCell = cell;
1601 table.cellMergePar = mergeNone; /* reset */
1602 table.cellCount++;
1603 ((table.cellsInRow)[table.rows])++;
1604 (table.cols)++;
1605 table.limboCellLeftBorder = false;
1606 table.limboCellRightBorder = false;
1607 table.limboCellTopBorder = false;
1608 table.limboCellBottomBorder = false;
1609 break;
1610 case rtfVertMergeRngFirst:
1611 table.cellMergePar = mergeTop;
1612 break;
1613 case rtfVertMergeRngPrevious:
1614 table.cellMergePar = mergeAbove;
1615 break;
1616 case rtfCellBordTop:
1617 table.limboCellTopBorder = true;
1618 break;
1619 case rtfCellBordBottom:
1620 table.limboCellBottomBorder = true;
1621 break;
1622 case rtfCellBordRight:
1623 table.limboCellRightBorder = true;
1624 break;
1625 case rtfCellBordLeft:
1626 table.limboCellLeftBorder = true;
1627 break;
1628 }
1629 }
1630
1631
1632 /*
1633 * In RTF, each table row need not start with a table row definition.
1634 * The next row may decide to use the row definition of the previous
1635 * row. In that case, I need to call this InheritTableRowSettings function
1636 */
InheritTableRowSettings(void)1637 static void InheritTableRowSettings(void)
1638 {
1639 int prevRow;
1640 int cellsInPrevRow;
1641 cellStruct *cell, *newCell;
1642 int i;
1643
1644 prevRow = table.rows-1;
1645 cellsInPrevRow = (table.cellsInRow)[prevRow];
1646
1647 (table.cellsInRow)[prevRow + 1] = (table.cellsInRow)[prevRow];
1648
1649 for (i = 0; i < cellsInPrevRow; i++) {
1650 cell = CellGetByPosition(prevRow, i);
1651 newCell = CellAllocate();
1652 newCell->nextCell = table.theCell;
1653 newCell->row = prevRow + 1;
1654 newCell->col = cell->col;
1655 newCell->originalLeft = cell->originalLeft;
1656 newCell->originalRight = cell->originalRight;
1657 newCell->index = table.cellCount;
1658 newCell->verticalMerge = cell->verticalMerge;
1659 table.cellMergePar = mergeNone;
1660 table.theCell = newCell;
1661 table.cellCount++;
1662 }
1663 }
1664
1665 /*
1666 * This function counts the number of columns a cell spans.
1667 * This is done by comparing the cell's left and right edges
1668 * to the sorted column border array. If the left and right
1669 * edges of the cell are not consecutive entries in the array,
1670 * the cell spans multiple columns.
1671 */
GetColumnSpan(cellStruct * cell)1672 static int GetColumnSpan(cellStruct * cell)
1673 {
1674 int i, j;
1675
1676 /* index of border that equals the left side of the cell */
1677 for (i = 0; i < table.cols; i++) {
1678 if ((table.rightColumnBorders)[i] == cell->originalLeft)
1679 break;
1680 }
1681
1682 /* index of border that equals the right side of the cell */
1683 for (j = i; j < table.cols + 1; j++){
1684 if ((table.rightColumnBorders)[j] == cell->originalRight)
1685 break;
1686 }
1687
1688 // fprintf(stderr, "sought (%5d,%5d), found (%5d,%5d), for (%d,%d)\n",
1689 // cell->originalLeft, cell->originalRight,
1690 // (table.rightColumnBorders)[i],
1691 // (table.rightColumnBorders)[j],
1692 // i,j);
1693
1694 return (j - i);
1695 }
1696
1697
1698 /*
1699 * This routine prescans the table.
1700 *
1701 * This is needed because RTF does not have a table construct. Instead, each
1702 * row is laid out as a series of cells with markup for each. So, this routine
1703 * counts how many rows there are in the table and the number of cells in each row.
1704 * In addition, it calculates the cell widths and builds an array of column borders.
1705 * The latter is useful in figuring out whether a cell spans
1706 * multiple columns.
1707 *
1708 * Finally, it turns out that to support vertically merged cells, the
1709 * contents of each cell need to also be collected. This has yet to be
1710 * implemented.
1711 */
PrescanTable(void)1712 static void PrescanTable(void)
1713 {
1714 int i, j, *rightBorders, maxCol;
1715 cellStruct *cell, *previousCell;
1716 boolean gatherCellInfo, foundRow, lastRow;
1717
1718 RTFParserState(SAVE_PARSER);
1719
1720 table.rows = 0;
1721 table.cellCount = 0;
1722 table.theCell = NULL;
1723
1724 /* Prescan each row until end of the table. */
1725 foundRow = true;
1726 lastRow = false;
1727
1728 /*
1729 * Scan the whole table. First, gather the cell layout information and then
1730 * check to see if another row of the table exists. repeat until no more rows
1731 * are found. The overall structure is
1732 *
1733 * <row> = (<tbldef> <cell>+ <tbldef> \row) | (<tbldef> <cell>+ \row) | (<cell>+ <tbldef> \row)
1734 * <cell> = (<nestrow>? <tbldef>?) & <textpar>+ \cell
1735 * <tbldef> = \trowd \irowN ... <celldef>+
1736 * <celldef> = ... \cellxN
1737 */
1738
1739 while (foundRow) {
1740 table.cols = 0;
1741
1742 if (0 && g_debug_table_prescan) fprintf(stderr,"*********** starting row %d\n", table.rows);
1743
1744 /* Gather cell layout information ... the three possible token streams are:
1745 *
1746 * 1) \trowd .... \cellxN ... \cellxM ... \trowd ... \cellxN ... \cellxM ...\row
1747 * 2) \trowd .... \cellxN ... \cellxM ...\row
1748 * 3) .... \cellxN ... \cellxM ...\row
1749 */
1750
1751 if (RTFCheckMM(rtfTblAttr, rtfRowDef)) {
1752 gatherCellInfo = true;
1753 (table.cellsInRow)[table.rows] = 0;
1754 } else {
1755 InheritTableRowSettings();
1756 gatherCellInfo = false;
1757 }
1758
1759 while (RTFGetToken() != rtfEOF) {
1760
1761 if (RTFCheckMM(rtfSpecialChar, rtfRow))
1762 break;
1763
1764 if (RTFCheckMM(rtfTblAttr, rtfRowDef)) {
1765 gatherCellInfo=false;
1766 continue;
1767 }
1768
1769 if (RTFCheckMM(rtfSpecialChar, rtfLastRow)) {
1770 lastRow = true;
1771 continue;
1772 }
1773
1774 if (RTFCheckCM(rtfControl, rtfTblAttr)) {
1775 if (gatherCellInfo) RTFRouteToken();
1776 }
1777
1778 if (RTFCheckMM(rtfSpecialChar, rtfOptDest))
1779 RTFSkipGroup();
1780 }
1781
1782 if (0 && g_debug_table_prescan) fprintf(stderr,"* reached end of row %d\n", table.rows);
1783
1784 (table.rows)++;
1785 if (lastRow) break;
1786
1787 /* Look for another row, indicated by either \trowd or \intbl
1788 * \intbl should always follow \widctrpar
1789 */
1790
1791 foundRow = false;
1792
1793 while (RTFGetToken() != rtfEOF) {
1794
1795 if (RTFCheckMM(rtfTblAttr, rtfRowDef)) {
1796 foundRow = true;
1797 break;
1798 }
1799
1800 /* \intbl must follow \widctlpar or the next paragraph is not part of the table */
1801 if (RTFCheckMM(rtfParAttr, rtfNoWidowControl) || RTFCheckMM(rtfParAttr, rtfWidowCtlPar)) {
1802 RTFGetToken();
1803 if (!RTFCheckMM(rtfParAttr, rtfInTable)) break;
1804 }
1805
1806 if (RTFCheckMM(rtfParAttr, rtfInTable)) {
1807 foundRow = true;
1808 break;
1809 }
1810
1811 if (rtfClass == rtfText)
1812 break;
1813
1814 if (RTFCheckCM(rtfControl, rtfSpecialChar))
1815 break;
1816
1817 if (RTFCheckMM(rtfSpecialChar, rtfOptDest))
1818 RTFSkipGroup();
1819 }
1820 }
1821
1822 /*************************************************************************
1823 * Determine the number of columns and positions in the table by creating
1824 * a list containing one entry for each unique right border
1825 * This list is retained as table.rightColumnBorders
1826 * The number of columns is table.cols
1827 */
1828
1829 /* largest possible list */
1830 rightBorders = (int *) RTFAlloc((table.cellCount) * sizeof(int));
1831 rightBorders[0] = 0;
1832
1833 table.cols = 0;
1834 for (cell = table.theCell; cell != NULL; cell = cell->nextCell) {
1835 boolean cellBorderExistsAlready = false;
1836
1837 for (i = 0; i < table.cols; i++) {
1838 if (rightBorders[i] == cell->originalRight) {
1839 cellBorderExistsAlready=true;
1840 break;
1841 }
1842 }
1843
1844 if (!cellBorderExistsAlready) {
1845 rightBorders[table.cols] = cell->originalRight;
1846 (table.cols)++;
1847 }
1848
1849 if (cell->col == 0)
1850 cell->originalLeft = table.leftEdge;
1851 }
1852
1853 /* since rightBorders is too large, allocate correct size array for column border entries. */
1854 table.rightColumnBorders = (int *) RTFAlloc(((table.cols) + 1) * sizeof(int));
1855
1856 (table.rightColumnBorders)[0] = table.leftEdge;
1857 for (i = 0; i < table.cols; i++)
1858 (table.rightColumnBorders)[i + 1] = rightBorders[i];
1859
1860 RTFFree((char *)rightBorders);
1861
1862 if (0 && g_debug_table_prescan) fprintf(stderr,"* table has %d rows and %d cols \n", table.rows, table.cols);
1863
1864 /*
1865 * sort rightColumnBorders into ascending order.
1866 */
1867
1868 for (i = 0; i < (table.cols); i++)
1869 for (j = i + 1; j < (table.cols + 1); j++)
1870 if ((table.rightColumnBorders)[i] > (table.rightColumnBorders)[j])
1871 Swap((table.rightColumnBorders)[i], (table.rightColumnBorders)[j]);
1872
1873 /*
1874 * fill in column spans for each cell. GetColumnSpan uses table.rightColumnBorders
1875 * to decide if a cell spans multiple columns.
1876 */
1877 maxCol = 0;
1878
1879 for (i = 0; i < table.cellCount; i++) {
1880 cell = CellGetByIndex(i);
1881
1882 cell->columnSpan = GetColumnSpan(cell);
1883
1884 /* update the column to account for multicolumn cells */
1885 if (cell->col > 0)
1886 cell->col = previousCell->col + previousCell->columnSpan;
1887
1888 previousCell = cell;
1889 }
1890
1891 /* adjust spacing for extra intercolumn space added by latex */
1892 for (i = 1; i <= table.cols; i++)
1893 (table.rightColumnBorders)[i] -= i*latexColumnSeparation;
1894
1895 /* if the table is wider than textWidth, scale it appropriately */
1896 if (prefs[pConvertTableWidths] && table.rightColumnBorders[table.cols]+latexColumnSeparation*table.cols > textWidth) {
1897 float scale = 1.0*(textWidth-latexColumnSeparation*table.cols)/table.rightColumnBorders[table.cols];
1898
1899 for (i = 1; i <= table.cols; i++)
1900 table.rightColumnBorders[i] = (int) (table.rightColumnBorders[i]*scale+0.5);
1901 }
1902
1903 if (g_debug_table_prescan) {
1904 for (cell = table.theCell; cell != NULL; cell = cell->nextCell) {
1905 fprintf(stderr,"* cell #%3d (%2d, %2d) ", cell->index, cell->row, cell->col);
1906 fprintf(stderr,"left=%3dpt right=%3dpt ", cell->originalLeft/20, cell->originalRight/20);
1907 fprintf(stderr,"width=%3dpt ", CellWidth(cell));
1908 fprintf(stderr,"and spans %d columns", cell->columnSpan);
1909 fprintf(stderr," [vertical merge = %d]\n", cell->verticalMerge);
1910 }
1911 }
1912
1913 /* go back to beginning of the table */
1914 RTFParserState(RESTORE_PARSER);
1915 }
1916
1917 /* This is where we translate each row to latex. */
TableWriteRow(void)1918 static void TableWriteRow(void)
1919 {
1920 nowBetweenCells = true;
1921
1922 while (RTFGetToken() != rtfEOF) {
1923
1924 /* these have already been processed during prescanning */
1925 if (RTFCheckCM(rtfControl, rtfTblAttr))
1926 continue;
1927
1928 /* token that signals end of the row */
1929 if (RTFCheckCMM(rtfControl, rtfSpecialChar, rtfRow)) {
1930 if (g_debug_table_writing) fprintf(stderr,"* end of row\n");
1931 suppressLineBreak = false;
1932 return;
1933 }
1934
1935 /* token that signals last row in table */
1936 if (RTFCheckCMM(rtfControl, rtfSpecialChar, rtfLastRow))
1937 continue;
1938
1939 /* token that signals the end of the current cell */
1940 if (RTFCheckCMM(rtfControl, rtfSpecialChar, rtfCell)) {
1941 EndCell();
1942 continue;
1943 }
1944
1945 RTFRouteToken();
1946 }
1947 }
1948
1949 /*
1950 * This function draws horizontal lines within a table. It looks
1951 * for vertically merged rows that do not have any bottom border
1952 */
DrawTableRowLine(int rowNum)1953 static void DrawTableRowLine(int rowNum)
1954 {
1955 int i, cellPosition;
1956 cellStruct *theCell1, *theCell2;
1957 char buf[rtfBufSiz];
1958
1959 /* if we are at the last row of the table, just draw a straight \hline. */
1960 if (rowNum == (table.rows) - 1 || !table.multiRow) {
1961 PutLitStr("\\hline");
1962 InsertNewLine();
1963 return;
1964 }
1965
1966 /* otherwise use \cline for every cell */
1967 /* this is to count cell positions as if the table is a matrix. */
1968 cellPosition = 0;
1969 for (i = 0; i < (table.cellsInRow)[rowNum]; i++) {
1970
1971 theCell1 = CellGetByPosition(rowNum, cellPosition);
1972 cellPosition += theCell1->columnSpan;
1973
1974 if (theCell1->verticalMerge == mergeNone) {
1975 snprintf(buf, rtfBufSiz, "\\cline{%d-%d}", theCell1->col + 1, theCell1->col + theCell1->columnSpan);
1976 PutLitStr(buf);
1977 continue;
1978 }
1979
1980 if (theCell1->verticalMerge == mergeAbove) {
1981 theCell2 = CellGetByPosition(rowNum + 1, i);
1982 if (theCell2->verticalMerge != mergeAbove) {
1983 snprintf(buf, rtfBufSiz, "\\cline{%d-%d}", theCell1->col + 1, theCell1->col + theCell1->columnSpan);
1984 PutLitStr(buf);
1985 }
1986 }
1987 }
1988
1989 InsertNewLine();
1990 }
1991
1992 /*
1993 \begin{tabular}{\textwidth}{|>{\raggedright}p{99pt}|>{\raggedright}p{99pt}|...}
1994 ...
1995 \end{tabular}
1996 */
DoTablePreamble(void)1997 static void DoTablePreamble(void)
1998 {
1999 char buf[200];
2000 int i, width;
2001
2002 width = (table.rightColumnBorders[table.cols]-table.rightColumnBorders[0])/20;
2003
2004 snprintf(buf, 200, "\\begin{tabular}{");
2005 PutLitStr(buf);
2006
2007 for (i = 0; i < table.cols; i++) {
2008 int width = (table.rightColumnBorders[i+1]-table.rightColumnBorders[i])/20;
2009 snprintf(buf, 200, "|>{\\raggedright}p{\%dpt}", width);
2010 PutLitStr(buf);
2011 }
2012
2013 PutLitStr("|}\n\\hline\n");
2014 }
2015
2016 /*
2017 * When we reach a table, we don't know anything about it. Initially,
2018 * we need to know the number of columns and width of each column. Later,
2019 * we need to know if a cell spans multiple columns. One day, the
2020 * borders on cells might be used ... but not now.
2021 *
2022 * Therefore, we prescan the data and collect information about every
2023 * cell into a linked list of cells. The table structure is filled in
2024 * with information that describes the table as a whole and a pointer
2025 * to the linked list of cells.
2026 *
2027 * After prescanning the table, the entire table is reread, but this time
2028 * the contents of each cell are translated.
2029 */
DoTable(void)2030 static void DoTable(void)
2031 {
2032 int i;
2033 cellStruct *cell;
2034 int oldwritten, oldparagraph;
2035
2036 EndParagraph();
2037 NewParagraph();
2038
2039 oldwritten = paragraphWritten.alignment;
2040 oldparagraph = paragraph.alignment;
2041 paragraphWritten.alignment = left;
2042 paragraph.alignment = left;
2043
2044 requireTablePackage = true;
2045
2046 /* throw away old cell information lists */
2047 while (table.theCell) {
2048 cell = (table.theCell)->nextCell;
2049 RTFFree((char *) table.theCell);
2050 table.theCell = cell;
2051 }
2052
2053 PrescanTable();
2054 table.cellCount = 0;
2055 insideTable = true;
2056
2057 DoTablePreamble();
2058
2059 for (i = 0; i < table.rows; i++) {
2060 if (g_debug_table_writing) fprintf(stderr,"* Starting row #%d\n",i+1);
2061 TableWriteRow();
2062
2063 // PutLitStr("\\\\\n");
2064 PutLitStr("\\tabularnewline\n");
2065
2066 if (i < (table.rows - 1))
2067 DrawTableRowLine(i);
2068 }
2069
2070 PutLitStr("\\hline\n");
2071 PutLitStr("\\end{tabular}");
2072
2073 nowBetweenParagraphs = true;
2074
2075 RTFFree((char *) table.rightColumnBorders);
2076 insideTable = false; /* end of table */
2077 table.multiRow = false;
2078
2079 paragraphWritten.alignment = oldwritten;
2080 paragraph.alignment = oldparagraph;
2081 }
2082
2083 /* set paragraph attributes that might be useful */
ParAttr(void)2084 static void ParAttr(void)
2085 {
2086 if (insideFootnote || insideHyperlink || insideHeaderFooter)
2087 return;
2088
2089 switch (rtfMinor) {
2090 case rtfSpaceBetween:
2091 paragraph.lineSpacing = rtfParam;
2092 break;
2093 case rtfQuadCenter:
2094 paragraph.alignment = center;
2095 break;
2096 case rtfQuadJust:
2097 case rtfQuadLeft:
2098 paragraph.alignment = left;
2099 break;
2100 case rtfQuadRight:
2101 paragraph.alignment = right;
2102 break;
2103
2104 case rtfParDef:
2105 paragraph.firstIndent = 0;
2106 paragraph.leftIndent = 0;
2107 paragraph.extraIndent = 0;
2108 paragraph.alignment = left;
2109 paragraph.styleIndex = -1;
2110 break;
2111
2112 case rtfStyleNum:
2113 if (prefs[pConvertParagraphStyle] && rtfParam < MAX_STYLE_MAPPINGS)
2114 paragraph.styleIndex = Style2LatexMapIndex[rtfParam];
2115 else
2116 paragraph.styleIndex = -1;
2117 break;
2118
2119 case rtfFirstIndent:
2120 paragraph.firstIndent = rtfParam;
2121 break;
2122 case rtfLeftIndent:
2123 paragraph.leftIndent = rtfParam;
2124 break;
2125 case rtfRightIndent:
2126 paragraph.rightIndent = rtfParam;
2127 break;
2128 case rtfSpaceBefore:
2129 paragraph.spaceBefore = rtfParam;
2130 break;
2131 case rtfSpaceAfter:
2132 paragraph.spaceAfter = rtfParam;
2133 break;
2134 }
2135
2136 }
2137
SectAttr(void)2138 static void SectAttr(void)
2139 {
2140 if (insideHeaderFooter) return;
2141
2142 switch (rtfMinor) {
2143 case rtfColumns:
2144 section.cols = rtfParam;
2145 break;
2146 case rtfSectDef:
2147 section.cols = 1;
2148 section.newPage = 1;
2149 break;
2150 default:
2151 break;
2152 }
2153 }
2154
2155 /*
2156 * This function rewrites the LaTeX file with simpler header
2157 */
EndLaTeXFile(void)2158 void EndLaTeXFile(void)
2159 {
2160 FILE *nfp=NULL;
2161 int numr,len;
2162 char* newname;
2163 char* oldname;
2164 char buffer[512];
2165
2166 /* last few bits */
2167 EndParagraph();
2168 EndSection();
2169 if (g_shouldIncludePreamble)
2170 PutLitStr("\n\n\\end{document}\n");
2171
2172 /* open new file, changing name from file.ltx to file.tex*/
2173 oldname = RTFGetOutputName();
2174 newname = strdup(oldname);
2175 len = strlen(newname);
2176 newname[len-3]='t';
2177 newname[len-2]='e';
2178 nfp = fopen(newname, "wb");
2179 if (!nfp) return;
2180
2181 /* prepare */
2182 RTFSetOutputStream(nfp);
2183 RTFSetOutputName(newname);
2184
2185 if (g_shouldIncludePreamble) {
2186 /* write improved header */
2187 suppressLineBreak = false;
2188 PutLitStr(preambleFirstText); /* from pref/r2l-pref */
2189 InsertNewLine();
2190 PutLitStr(preambleSecondText); /* from pref/r2l-pref */
2191 InsertNewLine();
2192 PutLitStr(preambleDocClass); /* from pref/r2l-pref */
2193 InsertNewLine();
2194 PutLitStr(preambleEncoding); /* from pref/latex-encoding */
2195 InsertNewLine();
2196 InsertNewLine();
2197 setPreamblePackages(false);
2198 PutLitStr(preamblePackages); /* as needed */
2199
2200 PutLitStr(preambleUserText); /* from pref/r2l-head */
2201 InsertNewLine();
2202
2203 if (preambleFancyHeader){
2204 PutLitByteStr("\\pagestyle{fancy}\n");
2205 PutLitByteStr("\\rhead{}\n\\rfoot{}\n\\chead{}\n\\cfoot{}\n");
2206 PutLitByteStr(preambleFancyHeader);
2207 InsertNewLine();
2208 }
2209
2210 if (preambleFancyHeaderFirst){
2211 PutLitByteStr("\\fancypagestyle{plain}{\n");
2212 PutLitByteStr(" \\rhead{}\n \\rfoot{}\n \\chead{}\n \\cfoot{}\n");
2213 PutLitByteStr(preambleFancyHeaderFirst);
2214 PutLitByteStr("}\n");
2215 PutLitByteStr("\\thispagestyle{plain}\n");
2216 InsertNewLine();
2217 }
2218 }
2219
2220 InsertNewLine();
2221 DefineColors(false);
2222 InsertNewLine();
2223 PutLitByteStr(preambleOurDefs); /* e.g., \tab */
2224 InsertNewLine();
2225
2226 /* now copy the body of the document */
2227 fseek(ofp, beginDocumentPos, 0);
2228
2229 while(!feof(ofp)){
2230 numr = fread(buffer,1,512,ofp);
2231 fwrite(buffer,1,numr,nfp);
2232 }
2233
2234 /* close files and delete old one */
2235 free(newname);
2236 fclose(ofp);
2237 fclose(nfp);
2238 unlink(oldname);
2239 }
2240
2241 /* sets the output stream */
RTFSetOutputStream(stream)2242 void RTFSetOutputStream(stream)
2243 FILE *stream;
2244 {
2245 ostream = stream;
2246 }
2247
2248 /* This function looks for the beginning of the hex data in a \pict structure */
HexData(void)2249 static int HexData(void)
2250 {
2251 /* get the next token */
2252 RTFGetToken();
2253
2254 /* if we fall into a group, skip the whole she-bang */
2255 if (RTFCheckCM(rtfGroup, rtfBeginGroup) != 0) {
2256 RTFPeekToken();
2257 while ((int) (rtfTextBuf[0]) == 0x0a || (int) (rtfTextBuf[0]) == 0x0d) {
2258 RTFGetToken(); /* skip any carriage returns */
2259 RTFPeekToken(); /* look at the next token to check if there is another row */
2260 }
2261
2262 /*
2263 * there are some groups within the header that contain text data that should not
2264 * be confused with hex data
2265 */
2266 if (RTFCheckMM(rtfDestination, rtfShapeProperty) != 0 || strcmp(rtfTextBuf, "\\*") == 0)
2267 {
2268 RTFSkipGroup();
2269 return (0);
2270 }
2271 }
2272
2273 /* paydirt, hex data starts */
2274 if (rtfClass == rtfText)
2275 return (1);
2276
2277 /* no such luck, but set picture attributes when encountered */
2278 if (RTFCheckCM(rtfControl, rtfPictAttr)) {
2279 RTFRouteToken();
2280 }
2281 return (0);
2282
2283 }
2284
2285 /*
2286 * return true for upper or lower case hex character
2287 */
ishex(char c)2288 static int ishex(char c)
2289 {
2290 if ( '0' <= c && c <= '9' )
2291 return 1;
2292 if ('a' <= c && c <= 'f')
2293 return 1;
2294 if ('A' <= c && c <= 'F')
2295 return 1;
2296 return 0;
2297 }
2298
2299 /*
2300 * Read two characters of hex-encoded object data as an unsigned char
2301 */
ReadHexPair(void)2302 static int ReadHexPair(void)
2303 {
2304 int hexNumber;
2305
2306 do
2307 RTFGetToken();
2308 while (rtfTextBuf[0] == 0x0a || rtfTextBuf[0] == 0x0d);
2309
2310 if (!ishex(rtfTextBuf[0])) {
2311 fprintf(stderr, "oddness encountered in hex data\n");
2312 return -1;
2313 }
2314
2315 hexNumber = 16 * RTFCharToHex(rtfTextBuf[0]);
2316
2317 do
2318 RTFGetToken();
2319 while (rtfTextBuf[0] == 0x0a || rtfTextBuf[0] == 0x0d);
2320
2321 if (!ishex(rtfTextBuf[0])) {
2322 fprintf(stderr, "oddness encountered in hex data\n");
2323 return -1;
2324 }
2325
2326 return hexNumber + RTFCharToHex(rtfTextBuf[0]);
2327 }
2328
2329 /*
2330 * Here we create an Aldus Placeable Metafile by including a 22-byte header
2331 */
WriteWMFHeader(FILE * pictureFile)2332 static void WriteWMFHeader(FILE * pictureFile)
2333 {
2334 unsigned char wmfhead[22] = {
2335 /* Magic = */ 0xd7, 0xcd, 0xc6, 0x9a,
2336 /* handle = */ 0x00, 0x00,
2337 /* left = */ 0x00, 0x00,
2338 /* top = */ 0x00, 0x00,
2339 /* right = */ 0xff, 0xff,
2340 /* bottom = */ 0xff, 0xff,
2341 /* resolution = */ 0xA0, 0x05,
2342 /* reserved = */ 0x00, 0x00, 0x00, 0x00,
2343 /* checksum = */ 0x00, 0x00
2344 };
2345
2346 int i;
2347 int height, width;
2348
2349 if (picture.goalHeight) {
2350 height = (int)(picture.goalHeight * picture.scaleY * 96.0 / rtfTpi);
2351 } else {
2352 height = (int)(picture.height * picture.scaleY * 96.0 / rtfTpi);
2353 }
2354
2355 if (picture.goalWidth) {
2356 width = (int)(picture.goalWidth * picture.scaleX * 96.0 / rtfTpi);
2357 } else {
2358 width = (int)(picture.width * picture.scaleX * 96.0 / rtfTpi);
2359 }
2360
2361 height = picture.goalHeight;
2362 width = picture.goalWidth ;
2363 wmfhead[10] = (width) % 256;
2364 wmfhead[11] = (width) / 256;
2365 wmfhead[12] = (height) % 256;
2366 wmfhead[13] = (height) / 256;
2367
2368 /* Normally, the resolution is 1440 twips per inch; however, this number may be changed
2369 * to scale the image. A value of 720 indicates that the image is double its
2370 * normal size, or scaled to a factor of 2:1. A value of 360 indicates a scale of
2371 * 4:1, while a value of 2880 indicates that the image is scaled down in size by
2372 * a factor of two. A value of 1440 indicates a 1:1 scale ratio.
2373 *
2374 * For now it is left as 1440 0x05A0
2375 */
2376
2377 /* compute Checksum */
2378 wmfhead[20] = 0;
2379 wmfhead[21] = 0;
2380 for (i = 0; i < 20; i += 2) {
2381 wmfhead[20] ^= wmfhead[i];
2382 wmfhead[21] ^= wmfhead[i + 1];
2383 }
2384 fwrite(wmfhead, 22, 1, pictureFile);
2385 }
2386
WritePICTHeader(FILE * pictureFile)2387 static void WritePICTHeader(FILE * pictureFile)
2388 {
2389 int i, h[12];
2390
2391 /* check for possibility of pre-existing 512 byte header */
2392 for (i=0; i<12; i++)
2393 h[i]=ReadHexPair();
2394
2395 /* magic numbers for version 1 and version 2 pict files */
2396 if ( (h[10]==0x11 && h[11]==0x01) || (h[10]==0x00 && h[11]==0x11) ) {
2397 for (i = 0; i < 512; i++)
2398 fputc(' ', pictureFile);
2399 }
2400
2401 /* write out the header information */
2402 for (i=0; i<12; i++)
2403 fputc(h[i], pictureFile);
2404 }
2405
NewFigureName(char * fileSuffix)2406 static char * NewFigureName(char *fileSuffix)
2407 {
2408 char dummyBuf[rtfBufSiz];
2409 char *name;
2410
2411 if (fileSuffix && fileSuffix[0] == '.') fileSuffix++;
2412
2413 /* get input file name and create corresponding picture file name */
2414 name = strdup(RTFGetOutputName());
2415 if (strlen(name) > 4)
2416 name[strlen(name)-4] = '\0';
2417
2418 if (fileSuffix == NULL)
2419 snprintf(dummyBuf, rtfBufSiz, "%s-fig%03d.???", name, picture.count);
2420 else
2421 snprintf(dummyBuf, rtfBufSiz, "%s-fig%03d.%s", name, picture.count, fileSuffix);
2422
2423 free(name);
2424 return strdup(dummyBuf);
2425 }
2426
2427 /* start reading hex encoded picture */
ConvertHexPicture(char * fileSuffix)2428 static void ConvertHexPicture(char *fileSuffix)
2429 {
2430 FILE *pictureFile;
2431 char pictByte;
2432 int groupEnd = false;
2433 short hexNumber;
2434 short hexEvenOdd = 0; /* check if we read in an even number of hex characters */
2435
2436 /* increment the picture counter */
2437 (picture.count)++;
2438
2439 if (picture.name) free(picture.name);
2440 picture.name=NewFigureName(fileSuffix);
2441
2442 /* open picture file */
2443 if ((pictureFile = fopen(picture.name, "wb")) == NULL)
2444 RTFPanic("Cannot open input file %s\n", picture.name);
2445
2446 /* write appropriate header */
2447 if (picture.type== pict)
2448 WritePICTHeader(pictureFile);
2449
2450 if (picture.type== wmf)
2451 WriteWMFHeader(pictureFile);
2452
2453 /* now we have to read the hex code in pairs of two
2454 * (1 byte total) such as ff, a1, 4c, etc...*/
2455 while (!groupEnd) {
2456
2457 do
2458 RTFGetToken();
2459 while (rtfTextBuf[0] == 0x0a || rtfTextBuf[0] == 0x0d);
2460
2461 if (rtfClass == rtfGroup)
2462 break;
2463
2464 if (!groupEnd) {
2465 hexNumber = 16 * RTFCharToHex(rtfTextBuf[0]);
2466 hexEvenOdd++;
2467 }
2468
2469 do
2470 RTFGetToken();
2471 while (rtfTextBuf[0] == 0x0a || rtfTextBuf[0] == 0x0d);
2472
2473 if (rtfClass == rtfGroup)
2474 break;
2475
2476 if (!groupEnd) {
2477 hexNumber += RTFCharToHex(rtfTextBuf[0]); /* this is the the number */
2478 hexEvenOdd--;
2479 /* shove that number into a character of 1 byte */
2480 pictByte = hexNumber;
2481 fputc(pictByte, pictureFile);
2482 }
2483 }
2484
2485 if (fclose(pictureFile) != 0)
2486 printf("* error closing picture file %s\n", picture.name);
2487 if (hexEvenOdd)
2488 printf("* Warning! Odd number of hex characters read for picture %s\n",
2489 picture.name);
2490 }
2491
2492 /*
2493 * Write appropriate commands to include the picture
2494 */
IncludeGraphics(char * pictureType)2495 static void IncludeGraphics(char *pictureType)
2496 {
2497 char *filename;
2498 char dummyBuf[rtfBufSiz];
2499 float trueWidth, trueHeight;
2500 int finalWidth, finalHeight;
2501 int displayFigure = 0;
2502 int isOpenOfficePDF = 0;
2503 int pictConverted = 0;
2504 static pict2pdf_exists = -1;
2505 static unoconv_exists = -1;
2506
2507 /* it seems that when cropping is -4319 or -6084 the picture is empty */
2508 if (picture.cropTop<-1000) {
2509 unlink(picture.name);
2510 (picture.count)--; /* decrement the picture counter to keep figures sequential*/
2511 return;
2512 }
2513
2514 #ifdef UNIX
2515 if (pict2pdf_exists == -1 && strcmp(pictureType, "pict") == 0)
2516 pict2pdf_exists = system("command -v pict2pdf") ? 0 : 1;
2517
2518 if (unoconv_exists == -1 && (strcmp(pictureType, "wmf") == 0 || strcmp(pictureType, "emf") == 0))
2519 unoconv_exists = system("command -v unoconv") ? 0 : 1;
2520
2521 if (strcmp(pictureType, "pict") == 0 && pict2pdf_exists == 1) {
2522 snprintf(dummyBuf, rtfBufSiz, "pict2pdf '%s' ", picture.name);
2523 fprintf(stderr, ">> %s\n", dummyBuf);
2524 if (!system(dummyBuf)) {
2525 // unlink(picture.name);
2526 strcpy(strrchr(picture.name,'.')+1, "pdf");
2527 pictConverted = 1;
2528 }
2529 }
2530
2531 if (unoconv_exists == 1 && (strcmp(pictureType, "wmf") == 0 ||
2532 strcmp(pictureType, "emf") == 0 ||
2533 (!pictConverted && strcmp(pictureType, "pict") == 0))) {
2534 snprintf(dummyBuf, rtfBufSiz, "unoconv -f pdf '%s' ", picture.name);
2535 fprintf(stderr, ">> %s\n", dummyBuf);
2536 if (!system(dummyBuf)) {
2537 // unlink(picture.name);
2538 isOpenOfficePDF = 1;
2539 strcpy(strrchr(picture.name,'.')+1, "pdf");
2540 }
2541 }
2542
2543 #endif
2544 #ifdef MSWIN
2545 if (strcmp(pictureType, "wmf") == 0 || strcmp(pictureType, "emf") == 0) {
2546 if (!system("which epstopdf > NUL")) {
2547 int err;
2548 char *pdfname = strdup(picture.name);
2549 strcpy(pdfname + strlen(pdfname) - 3, "pdf");
2550
2551 snprintf(dummyBuf, rtfBufSiz, "emf2pdf.bat %s %s", picture.name, pdfname);
2552 err = system(dummyBuf);
2553
2554 if (!err) {
2555 unlink(picture.name);
2556 free(picture.name);
2557 picture.name = pdfname;
2558 } else
2559 free(pdfname);
2560 }
2561 else {
2562 int err;
2563 char *epsname = strdup(picture.name);
2564 strcpy(epsname + strlen(epsname) - 3, "eps");
2565
2566 snprintf(dummyBuf, rtfBufSiz, "emf2pdf.bat %s %s", picture.name, epsname);
2567 err = system(dummyBuf);
2568
2569 if (!err) {
2570 unlink(picture.name);
2571 free(picture.name);
2572 picture.name = epsname;
2573 } else
2574 free(epsname);
2575 }
2576 }
2577 #endif
2578
2579 /* prefer picwgoal over picw */
2580 if (picture.goalWidth)
2581 trueWidth = picture.goalWidth / 20.0;
2582 else
2583 trueWidth = picture.width;
2584
2585 /* prefer pichgoal over pich */
2586 if (picture.goalHeight)
2587 trueHeight = picture.goalHeight / 20.0;
2588 else
2589 trueHeight = picture.height;
2590
2591 finalWidth = ROUNDF(trueWidth * picture.scaleX);
2592 finalHeight = ROUNDF(trueHeight * picture.scaleY);
2593
2594 filename = strrchr(picture.name, PATH_SEP);
2595 if (!filename)
2596 filename = picture.name;
2597 else
2598 filename++;
2599
2600 if (nowBetweenParagraphs) {
2601 displayFigure = 1;
2602 EndParagraph();
2603 NewParagraph();
2604 PutLitStr("%%\\begin{figure}[htbp]");
2605 } else {
2606 displayFigure = 0;
2607 StopTextStyle();
2608 }
2609
2610 if (0) {
2611 PutLitStr("\n\\fbox{");
2612 snprintf(dummyBuf,rtfBufSiz,"crop (t,b)=(%d,%d), scaled (w,h)=(%d,%d)\n",
2613 picture.cropTop,picture.cropBottom,finalWidth,finalHeight);
2614 PutLitStr(dummyBuf);
2615 PutLitStr("}\n\n");
2616 }
2617
2618 if (isOpenOfficePDF) {
2619 int lm, tm, rm, bm; /* left top right bottom margins*/
2620 lm = ROUNDF((612-trueWidth)/2);
2621 rm = ROUNDF(612-trueWidth-lm);
2622 tm = ROUNDF((792-trueHeight)/2);
2623 bm = ROUNDF(792-trueHeight-tm);
2624 snprintf(dummyBuf, rtfBufSiz, "\n\\includegraphics[trim=%dpt %dpt %dpt %dpt, clip=true, width=%dpt, height=%dpt]{%s}\n",
2625 lm, tm, rm, bm, finalWidth, finalHeight, filename);
2626 } else {
2627 snprintf(dummyBuf, rtfBufSiz, "\n\\includegraphics[width=%dpt, height=%dpt, keepaspectratio=true]{%s}\n",
2628 finalWidth, finalHeight, filename);
2629 }
2630
2631 PutLitStr(dummyBuf);
2632
2633 if (displayFigure) {
2634 PutLitStr("%%\\caption{This should be the caption for \\texttt{");
2635 PutEscapedLitStr(filename);
2636 PutLitStr("}.}\n");
2637 PutLitStr("%%\\end{figure}\n");
2638 EndParagraph();
2639 nowBetweenParagraphs = true;
2640 }
2641 }
2642
2643 /* This function reads in a picture */
ReadPicture(void)2644 static void ReadPicture(void)
2645 {
2646 requireGraphicxPackage = true;
2647 picture.type = unknownPict;
2648 picture.width = 0;
2649 picture.height = 0;
2650 picture.goalWidth = 0;
2651 picture.goalHeight = 0;
2652 picture.scaleX = 1.00;
2653 picture.scaleY = 1.00;
2654
2655 /* RTFMsg("Starting ReadPicture ...\n"); */
2656
2657 /* skip everything until we reach hex data */
2658 while (!HexData());
2659
2660 /* Put back the first hex character into the stream (removed by HexData) */
2661 RTFUngetToken();
2662
2663 /* Process picture */
2664 switch (picture.type) {
2665 case pict:
2666 RTFMsg("* Image %03d: Apple PICT format\n", picture.count+1);
2667 ConvertHexPicture("pict");
2668 IncludeGraphics("pict");
2669 break;
2670 case wmf:
2671 RTFMsg("* Image %03d: Microsoft WMF format\n", picture.count+1);
2672 ConvertHexPicture("wmf");
2673 IncludeGraphics("wmf");
2674 break;
2675 case emf:
2676 RTFMsg("* Image %03d: Microsoft EMF format\n", picture.count+1);
2677 ConvertHexPicture("emf");
2678 IncludeGraphics("emf");
2679 break;
2680 case png:
2681 RTFMsg("* Image %03d: PNG\n", picture.count+1);
2682 ConvertHexPicture("png");
2683 IncludeGraphics("png");
2684 break;
2685 case jpeg:
2686 RTFMsg("* Image %03d: JPEG\n", picture.count+1);
2687 ConvertHexPicture("jpg");
2688 IncludeGraphics("jpg");
2689 break;
2690 default:
2691 RTFMsg("* Image %03d: Unknown type\n", picture.count+1);
2692 ConvertHexPicture("???");
2693 IncludeGraphics("unknown");
2694 break;
2695 }
2696
2697 /* feed "}" back to router */
2698 RTFRouteToken();
2699
2700 /* reset picture type */
2701 picture.type = unknownPict;
2702 picture.width = 0;
2703 picture.height = 0;
2704 picture.goalWidth = 0;
2705 picture.goalHeight = 0;
2706 picture.scaleX = 1.00;
2707 picture.scaleY = 1.00;
2708 picture.name = NULL;
2709 }
2710
FileDirectory(char * path)2711 static char * FileDirectory(char *path)
2712 {
2713 char *s, *dir;
2714
2715 if (!path) return NULL;
2716
2717 dir = strdup(path);
2718 s = strrchr(dir,PATH_SEP);
2719 if (s) {
2720 *s ='\0';
2721 return dir;
2722 }
2723
2724 free(dir);
2725 return NULL;
2726 }
2727
CopyFile(char * in_path,char * out_path)2728 static void CopyFile(char *in_path, char *out_path)
2729 {
2730 FILE *in, *out;
2731 char buffer[512];
2732 size_t numr;
2733
2734 in = fopen(in_path,"rb");
2735 out = fopen(out_path,"wb");
2736
2737 if (!in || !out) {
2738 fprintf(stderr, "failed to copy '%s' as '%s'\n", in_path, out_path);
2739 return;
2740 }
2741
2742 while(!feof(in)){
2743 numr = fread(buffer,1,512,in);
2744 fwrite(buffer,1,numr,out);
2745 }
2746 fclose(in);
2747 fclose(out);
2748 }
2749
2750 /*
2751 * This function reads in a picture
2752 *
2753 * {{\NeXTGraphic build.tiff \width740 \height740 \noorient}�}
2754 *
2755 */
ReadNextGraphic(void)2756 static void ReadNextGraphic(void)
2757 {
2758 char *filename, *rtf_path, *in_path, *in_dir, *out_path;
2759 char *out_name, *fileSuffix, buff[100];
2760 int width=0;
2761 int height=0;
2762
2763 requireGraphicxPackage = true;
2764 if (nowBetweenParagraphs)
2765 NewParagraph();
2766
2767 filename = RTFGetTextWord();
2768
2769 if (!filename) {
2770 RTFSkipGroup();
2771 return;
2772 }
2773
2774 /* determine the directory containing the rtf file */
2775 rtf_path = RTFGetInputName();
2776 in_dir = FileDirectory(rtf_path);
2777
2778 /* now establish the full path to the NextGraphic */
2779 in_path=append_file_to_path(in_dir,filename);
2780 if (in_dir) free(in_dir);
2781
2782 /* create the path for the new graphic */
2783 (picture.count)++;
2784 fileSuffix=strrchr(filename,'.');
2785 out_path=NewFigureName(fileSuffix);
2786 free(filename);
2787
2788 CopyFile(in_path,out_path);
2789
2790 while (RTFGetToken() != rtfEOF) {
2791 if (RTFCheckCM(rtfGroup, rtfEndGroup)) break;
2792 if (rtfMinor == rtfNeXTGHeight) height = rtfParam/20;
2793 if (rtfMinor == rtfNeXTGWidth) width = rtfParam/20;
2794 }
2795
2796 /* skip everything until outer brace */
2797 RTFSkipGroup();
2798
2799 /* need the local name of the file */
2800 out_name = strrchr(out_path,PATH_SEP);
2801 if (out_name)
2802 out_name++;
2803 else
2804 out_name = out_path;
2805
2806 PutLitStr("\\includegraphics");
2807 if (width || height) {
2808 PutLitStr("[");
2809 if (width) {
2810 snprintf(buff, 100, "width=%dpt, ", width);
2811 PutLitStr(buff);
2812 }
2813 if (height) {
2814 snprintf(buff, 100, "height=%dpt", height);
2815 PutLitStr(buff);
2816 }
2817 PutLitStr("]");
2818 }
2819 PutLitStr("{");
2820 PutLitStr(out_name);
2821 PutLitStr("}\n");
2822 if (in_path) free(in_path);
2823 if (out_path) free(out_path);
2824 }
2825
2826 /*
2827 * slow simplistic reimplementation of strcasestr for systems that
2828 * don't include it in their library
2829 *
2830 * based on a GPL implementation in OpenTTD found under GPL v2
2831 */
2832
my_strcasestr(const char * haystack,const char * needle)2833 static char *my_strcasestr(const char *haystack, const char *needle)
2834 {
2835 size_t hay_len = strlen(haystack);
2836 size_t needle_len = strlen(needle);
2837 while (hay_len >= needle_len) {
2838 if (strncasecmp(haystack, needle, needle_len) == 0)
2839 return (char *) haystack;
2840
2841 haystack++;
2842 hay_len--;
2843 }
2844
2845 return NULL;
2846 }
2847
ReadObjWidth(void)2848 static void ReadObjWidth(void)
2849 {
2850 g_object_width = rtfParam;
2851 }
2852
2853 /*
2854 * parses \objectclass and adds the class type to the global variable 'object'
2855 */
GetObjectClass(void)2856 static int GetObjectClass(void)
2857 {
2858 int i;
2859 char *s;
2860
2861 object.class = unknownObjClass;
2862
2863 if (!RTFSkipToToken(rtfControl, rtfDestination, rtfObjClass))
2864 return -1;
2865
2866 s = RTFGetTextWord();
2867 if (s && s[0]) {
2868 strcpy(object.className, s);
2869 free(s);
2870 }
2871
2872 /* do we recognize this object class? */
2873 for (i = 0; objectClassList[i] != NULL; i++) {
2874 if (my_strcasestr(object.className, objectClassList[i])) {
2875 return i;
2876 }
2877 }
2878 return -1;
2879 }
2880
2881
2882 /*
2883 * The result section of an \object usually contains a picture of the object
2884 */
ReachedResult(void)2885 static int ReachedResult(void)
2886 {
2887 RTFGetToken();
2888
2889 if (RTFCheckMM(rtfDestination, rtfObjResult) != 0 ||
2890 RTFCheckMM(rtfShapeAttr, rtfShapeResult) != 0 ||
2891 RTFCheckMM(rtfDestination, rtfPict) != 0 ||
2892 RTFCheckMM(rtfShapeAttr, rtfShapeText) != 0) {
2893 if (RTFCheckMM(rtfDestination, rtfPict) != 0)
2894 shapeObjectType = shapePicture;
2895 else if (RTFCheckMM(rtfDestination, rtfObjResult) != 0)
2896 shapeObjectType = standardObject;
2897 else if (RTFCheckMM(rtfShapeAttr, rtfShapeResult) != 0)
2898 shapeObjectType = shapeObject;
2899 else if (RTFCheckMM(rtfShapeAttr, rtfShapeText) != 0)
2900 shapeObjectType = shapeObjText;
2901 return (1);
2902 }
2903
2904 if (rtfClass == rtfEOF) {
2905 RTFPanic("* EOF reached!\n");
2906 exit(1);
2907 }
2908
2909 return (0);
2910 }
2911
2912 /*
2913 * Decodes the OLE and extract the specified stream type into a buffer.
2914 * This function uses the cole library
2915 */
2916 static int
DecodeOLE(char * objectFileName,char * streamType,unsigned char ** nativeStream,uint32_t * size)2917 DecodeOLE(char *objectFileName, char *streamType,
2918 unsigned char **nativeStream, uint32_t * size)
2919 {
2920 COLEFS *cfs;
2921 COLERRNO colerrno;
2922 COLEFILE *coleFile;
2923
2924 cfs = cole_mount(objectFileName, &colerrno);
2925 if (cfs == NULL) {
2926 cole_perror("DecodeOLE cole_mount", colerrno, objectFileName);
2927 return (1);
2928 }
2929
2930 #ifdef COLE_VERBOSE
2931 cole_print_tree (cfs, &colerrno);
2932 #endif
2933
2934 if ((coleFile = cole_fopen(cfs, streamType, &colerrno)) == NULL) {
2935 cole_perror("DecodeOLE cole_fopen", colerrno, objectFileName);
2936 cole_umount(cfs, NULL);
2937 return 1;
2938 }
2939
2940 *size = (uint32_t) cole_fsize(coleFile);
2941
2942 *nativeStream = (unsigned char *) malloc(*size);
2943
2944 if (*nativeStream == NULL) {
2945 RTFMsg("* DecodeOLE: memory allocation failed for native stream!\n");
2946 cole_fclose(coleFile, &colerrno);
2947 cole_umount(cfs, NULL);
2948 return 1;
2949 }
2950
2951 if (cole_fread(coleFile, *nativeStream, *size, &colerrno) == 0) {
2952 cole_perror("DecodeOLE cole_fread", colerrno, objectFileName);
2953 cole_fclose(coleFile, &colerrno);
2954 cole_umount(cfs, NULL);
2955 free(nativeStream);
2956 return 1;
2957 }
2958
2959 if (cole_fclose(coleFile, &colerrno) != 0) {
2960 cole_perror("DecodeOLE cole_fclose", colerrno, objectFileName);
2961 cole_umount(cfs, NULL);
2962 free(nativeStream);
2963 return 1;
2964 }
2965
2966 if (cole_umount(cfs, &colerrno)) {
2967 cole_perror("DecodeOLE cole_umount", colerrno, objectFileName);
2968 free(nativeStream);
2969 return (1);
2970 }
2971 return 0;
2972 }
2973
2974 /*
2975 * Save the hex-encoded object data and as binary bytes in objectFileName
2976 */
ReadObjectData(char * objectFileName,int type,int offset)2977 static void ReadObjectData(char *objectFileName, int type, int offset)
2978 {
2979 char dummyBuf[20];
2980 int m[4];
2981 FILE *objFile;
2982 int i, value;
2983 uint8_t hexNumber;
2984 uint8_t hexEvenOdd = 0; /* should be even at the end */
2985
2986 if (type == EquationClass) {
2987 (oleEquation.count)++;
2988 snprintf(dummyBuf, 20, "-eqn%03d.eqn", oleEquation.count);
2989 } else
2990 snprintf(dummyBuf, 20, ".obj");
2991
2992 /* construct full path of file name (without .tex) */
2993 strcpy(objectFileName, RTFGetOutputName());
2994 objectFileName[strlen(objectFileName)-4]='\0';
2995 strcat(objectFileName, dummyBuf);
2996
2997 /* open object file */
2998 objFile = fopen(objectFileName, "wb");
2999 if (!objFile)
3000 RTFPanic("Cannot open input file %s\n", objectFileName);
3001
3002 /* OLE header
3003 * (uint) version e.g. 01000100 = 4 hex pairs
3004 * (uint) format e.g. 02000000 = 4 hex pairs
3005 * ( int) type name length (int) e.g. 0f000000 = 4 hex pairs
3006 * ( str) type name e.g. 4571 7561 7469 6f6e 2e44 534d 5434 00
3007 * 00000000 00000000 000e0000 = 3 unknown ints = 12 hex pairs
3008 *
3009 * two examples with spaces added to clarify
3010 * 01050000 02000000 0b000000 4571756174696f6e2e3300 00000000 00000000 000e0000
3011 * 01000100 02000000 0f000000 4571756174696f6e2e44534d543400 00000000 00000000 000e0000
3012 */
3013 /* skip three ints of 4 hex pairs each */
3014 for (i=0; i<12; i++) ReadHexPair();
3015
3016 /* skip the 00 hex-terminated string */
3017 do {
3018 value = ReadHexPair();
3019 // fprintf(stderr,"%c", value);
3020 } while (value>0);
3021 // fprintf(stderr,"\n");
3022
3023 if (value==-1) {
3024 RTFMsg("* OLE object does not have proper header\n");
3025 fclose(objFile);
3026 return;
3027 }
3028
3029 /* skip three ints of 4 hex characters each */
3030 for (i=0; i<12; i++) ReadHexPair();
3031
3032 /* read the OLE marker next 8 chars should be d0cf11e0 */
3033 for (i=0; i<4; i++)
3034 m[i]=ReadHexPair();
3035
3036 if (m[0]!=0xd0 || m[1]!=0xcf || m[2]!=0x11 || m[3]!=0xe0) {
3037 fprintf(stderr, "* OLE marker 0x'%02x%02x%02x%02x' is not 0xd0cf11e0\n", m[0], m[1], m[2], m[3]);
3038 fclose(objFile);
3039 return;
3040 }
3041
3042 for (i=0; i<4; i++)
3043 fputc(m[i], objFile);
3044
3045 /* each byte is encoded as two hex chars ... ff, a1, 4c, ...*/
3046 while (1) {
3047 RTFGetToken();
3048
3049 /* CR or LF in the hex stream should be skipped */
3050 while (rtfTextBuf[0] == 0x0a || rtfTextBuf[0] == 0x0d)
3051 RTFGetToken();
3052
3053 if (rtfClass == rtfGroup)
3054 break;
3055
3056 hexNumber = 16 * RTFCharToHex(rtfTextBuf[0]);
3057 hexEvenOdd++;
3058
3059 RTFGetToken();
3060
3061 while (rtfTextBuf[0] == 0x0a || rtfTextBuf[0] == 0x0d)
3062 RTFGetToken(); /* should not happen */
3063
3064 if (rtfClass == rtfGroup)
3065 break;
3066
3067 hexNumber += RTFCharToHex(rtfTextBuf[0]); /* this is the the number */
3068 hexEvenOdd--;
3069 fputc(hexNumber, objFile);
3070 }
3071
3072 if (fclose(objFile) != 0)
3073 fprintf(stderr,"* error closing object file %s\n", objectFileName);
3074
3075 if (hexEvenOdd)
3076 fprintf (stderr,"* Warning! Odd number of hex characters read for object!\n");
3077 }
3078
3079 /*
3080 * After an equation look for an equation number
3081 */
EqnNumberString(void)3082 static char * EqnNumberString(void)
3083 {
3084 char theNumber[10], comma[2], *s, *t;
3085 int stringIndex=0;
3086
3087 theNumber[0]='\0';
3088 comma[0]='\0';
3089 /* skip to text following equation, stop looking a \par or \pard */
3090 do {
3091 RTFGetToken();
3092
3093 /* paragraph ended ==> no equation number */
3094 if (RTFCheckCMM(rtfControl, rtfSpecialChar, rtfPar)) {
3095 RTFUngetToken();
3096 return NULL;
3097 }
3098
3099 /* don't emit any tabs */
3100 if (RTFCheckCMM(rtfControl, rtfSpecialChar, rtfTab))
3101 continue;
3102
3103 /* don't emit pictures */
3104 if (RTFCheckCMM(rtfControl, rtfDestination, rtfPict)) {
3105 RTFSkipGroup();
3106 RTFRouteToken();
3107 continue;
3108 }
3109
3110 if (rtfClass == rtfText) {
3111 /* don't stop for spaces */
3112 if (isspace(rtfTextBuf[0]))
3113 continue;
3114
3115 /* commas or periods are common punctuation following an equation
3116 so keep them but keep looking for a real equation number */
3117 if (rtfTextBuf[0] == ',' || rtfTextBuf[0] == '.') {
3118 comma[0]=rtfTextBuf[0];
3119 comma[1]='\0';
3120 continue;
3121 }
3122
3123 /* found a text character that might start an equation number*/
3124 break;
3125 }
3126
3127 RTFRouteToken();
3128
3129 } while (rtfClass != rtfEOF);
3130
3131 /* collect the equation number */
3132 do {
3133 if (RTFCheckCMM(rtfControl, rtfSpecialChar, rtfPar)) {
3134 RTFUngetToken();
3135 break;;
3136 }
3137
3138 if (rtfClass == rtfText) {
3139 char c=rtfTextBuf[0];
3140
3141 /* eqn numbers must start with '(', '[', or a digit */
3142 if (stringIndex==0 && !(isdigit(c) || c == '(' || c == ']') ) break;
3143
3144 theNumber[stringIndex]=c;
3145 stringIndex++;
3146 if (c==')' || c==']') break;
3147 }
3148
3149 RTFGetToken();
3150 } while (rtfClass != rtfEOF && stringIndex<10);
3151
3152 if (stringIndex == 0) {
3153 RTFUngetToken();
3154 return strdup(comma);
3155 }
3156
3157 theNumber[stringIndex]='\0';
3158 s=strdup_together(comma,"\\eqno");
3159 t=strdup_together(s,theNumber);
3160 free(s);
3161
3162 return t;
3163 }
3164
3165 /*
3166 * Convert OLE file containing equation
3167 */
3168
ConvertEquationFile(char * objectFileName)3169 boolean ConvertEquationFile(char *objectFileName)
3170 {
3171 unsigned char *nativeStream;
3172 char *EqNo;
3173 MTEquation *theEquation;
3174 uint32_t equationSize;
3175
3176 nativeStream = NULL;
3177 theEquation = NULL;
3178
3179 /* Decode the OLE and extract the equation stream into buffer nativeStream */
3180 if (DecodeOLE(objectFileName, "/Equation Native", &nativeStream, &equationSize)) {
3181 RTFMsg("* error decoding OLE equation object!\n");
3182 return (false);
3183 }
3184
3185 theEquation = (MTEquation *) malloc(sizeof(MTEquation));
3186 if (theEquation == NULL) {
3187 RTFMsg("* error allocating memory for equation!\n");
3188 free(nativeStream);
3189 return (false);
3190 }
3191
3192 /* __cole_dump(nativeStream+slop, nativeStream+slop, 64, NULL); */
3193 if (*(nativeStream) == 0x1c && *(nativeStream+1) == 0x00) {
3194 equationSize -= MTEF_HEADER_SIZE;
3195
3196 if (!Eqn_Create(theEquation, nativeStream+MTEF_HEADER_SIZE, equationSize)) {
3197 RTFMsg("* could not create equation structure!\n");
3198 free(nativeStream);
3199 free(theEquation);
3200 return (false);
3201 }
3202
3203 theEquation->m_inline = 1;
3204 EqNo=NULL;
3205
3206 if (insideTable) {
3207 if (nowBetweenCells) NewCell();
3208 } else if (nowBetweenParagraphs) {
3209 theEquation->m_inline = 0;
3210 suppressSpaceBetweenParagraphs=true;
3211 EndParagraph();
3212 if (lastCharWritten != '\n')
3213 PutLitChar('\n');
3214 current_vspace = 0;
3215 EqNo=EqnNumberString();
3216 }
3217
3218 if (g_eqn_insert_name) {
3219 PutLitStr("\\fbox{file://");
3220 PutEscapedLitStr(objectFileName);
3221 PutLitStr("}");
3222 requireHyperrefPackage = true;
3223 }
3224
3225 /* this returns the translated equation in m_latex record */
3226 Eqn_TranslateObjectList(theEquation, ostream, 0);
3227
3228 if (theEquation->m_inline){
3229 /* Add a space unless the last character was punctuation */
3230 if (lastCharWritten != ' ' && lastCharWritten != '(' &&
3231 lastCharWritten != '[' && lastCharWritten != '{' )
3232 PutLitChar(' ');
3233 }
3234
3235 PutLitStr(theEquation->m_latex_start);
3236 PutLitStr(theEquation->m_latex);
3237 PutLitStr(EqNo);
3238 PutLitStr(theEquation->m_latex_end);
3239
3240 if (theEquation->m_inline) {
3241 /* Add a space unless the next character is punctuation */
3242 RTFPeekToken();
3243 if (rtfClass == rtfText) {
3244 if (rtfTextBuf[0]!='.' &&
3245 rtfTextBuf[0]!=',' &&
3246 rtfTextBuf[0]!=':' &&
3247 rtfTextBuf[0]!=';' &&
3248 rtfTextBuf[0]!=']' &&
3249 rtfTextBuf[0]!=')') PutLitChar(' ');
3250 } else
3251 PutLitChar(' ');
3252 }
3253
3254 Eqn_Destroy(theEquation);
3255 }
3256
3257 if (theEquation != NULL)
3258 free(theEquation);
3259
3260 if (nativeStream != NULL)
3261 free(nativeStream);
3262
3263 requireAmsSymbPackage = true;
3264 requireAmsMathPackage = true;
3265 return true;
3266 }
3267
3268 /*
3269 * Convert file containing just equation
3270 */
3271
ConvertRawEquationFile(char * rawFileName)3272 boolean ConvertRawEquationFile(char *rawFileName)
3273 {
3274 FILE *fp;
3275 unsigned char *nativeStream;
3276 char *EqNo;
3277 MTEquation *theEquation;
3278 uint32_t equationSize;
3279 int x;
3280
3281 fp = fopen(rawFileName, "r");
3282 x=fgetc(fp);
3283 // fprintf(stderr, "%d == 0?\n", x);
3284 x=fgetc(fp);
3285 // fprintf(stderr, "%d == 1?\n", x);
3286 equationSize = fgetc(fp);
3287 equationSize = equationSize * 256 + fgetc(fp);
3288 // fprintf(stderr, "equation size is %d\n", equationSize);
3289
3290 nativeStream = (unsigned char *) malloc(equationSize+10);
3291 fread(nativeStream, 1, equationSize, fp);
3292 fclose(fp);
3293
3294 theEquation = (MTEquation *) malloc(sizeof(MTEquation));
3295 if (theEquation == NULL) {
3296 RTFMsg("* error allocating memory for equation!\n");
3297 free(nativeStream);
3298 return (false);
3299 }
3300
3301 if (!Eqn_Create(theEquation, nativeStream, equationSize)) {
3302 RTFMsg("* could not create equation structure!\n");
3303 free(nativeStream);
3304 free(theEquation);
3305 return (false);
3306 }
3307
3308 theEquation->m_inline = 1;
3309 theEquation->log_level = 2;
3310 EqNo=NULL;
3311
3312 if (insideTable) {
3313 if (nowBetweenCells) NewCell();
3314 } else if (nowBetweenParagraphs) {
3315 theEquation->m_inline = 0;
3316 suppressSpaceBetweenParagraphs=true;
3317 EndParagraph();
3318 if (lastCharWritten != '\n')
3319 PutLitChar('\n');
3320 current_vspace = 0;
3321 EqNo=EqnNumberString();
3322 }
3323
3324 if (g_eqn_insert_name) {
3325 PutLitStr("\\fbox{file://");
3326 PutEscapedLitStr(rawFileName);
3327 PutLitStr("}");
3328 requireHyperrefPackage = true;
3329 }
3330
3331 /* this returns the translated equation in m_latex record */
3332 Eqn_TranslateObjectList(theEquation, ostream, 0);
3333
3334 if (theEquation->m_inline){
3335 /* Add a space unless the last character was punctuation */
3336 if (lastCharWritten != ' ' && lastCharWritten != '(' &&
3337 lastCharWritten != '[' && lastCharWritten != '{' )
3338 PutLitChar(' ');
3339 }
3340
3341 PutLitStr(theEquation->m_latex_start);
3342 PutLitStr(theEquation->m_latex);
3343 PutLitStr(EqNo);
3344 PutLitStr(theEquation->m_latex_end);
3345
3346 if (theEquation->m_inline) {
3347 /* Add a space unless the next character is punctuation */
3348 RTFPeekToken();
3349 if (rtfClass == rtfText) {
3350 if (rtfTextBuf[0]!='.' &&
3351 rtfTextBuf[0]!=',' &&
3352 rtfTextBuf[0]!=':' &&
3353 rtfTextBuf[0]!=';' &&
3354 rtfTextBuf[0]!=']' &&
3355 rtfTextBuf[0]!=')') PutLitChar(' ');
3356 } else
3357 PutLitChar(' ');
3358 }
3359
3360 Eqn_Destroy(theEquation);
3361
3362
3363 if (theEquation != NULL)
3364 free(theEquation);
3365
3366 if (nativeStream != NULL)
3367 free(nativeStream);
3368
3369 requireAmsSymbPackage = true;
3370 requireAmsMathPackage = true;
3371 return true;
3372 }
3373
3374 /*
3375 * Translate an object containing a MathType equation
3376 */
ReadEquation(void)3377 static boolean ReadEquation(void)
3378 {
3379 boolean result;
3380 char objectFileName[rtfBufSiz];
3381
3382 if (!RTFSkipToToken(rtfControl, rtfDestination, rtfObjData)) {
3383 RTFMsg("* ReadEquation: objdata group not found!\n");
3384 return (false);
3385 }
3386
3387 /* save hex-encoded object data as a binary objectFileName */
3388 ReadObjectData(objectFileName, EquationClass, EQUATION_OFFSET);
3389
3390 result = ConvertEquationFile(objectFileName);
3391
3392 if (!g_eqn_keep_file)
3393 remove(objectFileName);
3394
3395 return result;
3396 }
3397
3398
3399 /*
3400 * Read and process \object token
3401 */
ReadObject(void)3402 static void ReadObject(void)
3403 {
3404 int level = RTFGetBraceLevel();
3405 boolean res = false;
3406
3407 object.class = GetObjectClass();
3408
3409 switch (object.class) {
3410 case unknownObjClass:
3411 default:
3412 // RTFMsg("*** unsupported object '%s', skipping...\n", object.className);
3413 break;
3414
3415 case EquationClass:
3416
3417 if (prefs[pConvertEquation]) {
3418 res = ReadEquation();
3419 if (!res) fprintf(stderr, "failed to convert equation\n");
3420 }
3421
3422 /* if unsuccessful, include the equation as a picture */
3423 if (!res || g_eqn_insert_image) {
3424 if (RTFSkipToToken(rtfControl,rtfDestination, rtfPict))
3425 ReadPicture();
3426 }
3427 break;
3428
3429 case WordPictureClass:
3430 case MSGraphChartClass:
3431 /*ExamineToken("WordPictureClass");*/
3432 while (!ReachedResult());
3433 ReadPicture();
3434 break;
3435 }
3436
3437 object.class = 0;
3438 strcpy(object.className, "");
3439
3440 RTFSkipToLevel(level);
3441 }
3442
3443
3444 /*
3445 * Word97 through Word 2002 pictures are different
3446 *
3447 * {\*\shppict {\pict \emfblip ...}}{\nonshppict {\pict ...}}
3448 *
3449 * \shppict identifies a Word 97 through Word 2002 picture
3450 * \nonshppict indicates a {\pict} that Word not read on input and
3451 * is for compatibility with other readers.
3452 */
ReadWord97Picture(void)3453 static void ReadWord97Picture(void)
3454 {
3455 // ExamineToken("Word97Object");
3456 RTFGetToken();
3457 if (rtfClass != rtfGroup) {RTFSkipGroup(); return;}
3458
3459 RTFGetToken(); /* should be pict */
3460 if (rtfMinor != rtfPict) {RTFSkipGroup(); return;}
3461
3462 RTFRouteToken(); /* handle pict */
3463 RTFGetToken(); /* should be } */
3464 RTFRouteToken(); /* handle last brace from shppict */
3465 RTFGetToken(); /* should be { */
3466 if (rtfClass != rtfGroup) {RTFSkipGroup(); return;}
3467
3468 RTFGetToken(); /* should be nonshppict */
3469 RTFSkipGroup(); /* because we don't want two pictures in latex file */
3470 }
3471
3472 /* \sp{\sn PropertyName}{\sv PropertyValueInformation} */
ReadShapeProperty(void)3473 static void ReadShapeProperty(void)
3474 {
3475 char *name, *value;
3476
3477 if (!RTFSkipToToken(rtfControl, rtfShapeAttr, rtfShapeName)) return;
3478
3479 name = RTFGetTextWord();
3480
3481 if (strcmp(name,"pib")==0) {
3482 RTFExecuteGroup();
3483 free(name);
3484 return;
3485 }
3486
3487 if (!RTFSkipToToken(rtfControl, rtfShapeAttr, rtfShapeValue)) return;
3488
3489 value = RTFGetTextWord();
3490
3491 RTFSkipGroup();
3492
3493 // fprintf(stderr,"shape, name=%s, value=%s\n",name,value);
3494
3495 if (strcasecmp(name,"relleft")==0)
3496 sscanf(value, "%d", &(shape.left));
3497 if (strcasecmp(name,"relright")==0)
3498 sscanf(value, "%d", &(shape.right));
3499 if (strcasecmp(name,"reltop")==0)
3500 sscanf(value, "%d", &(shape.top));
3501 if (strcasecmp(name,"relbottom")==0)
3502 sscanf(value, "%d", &(shape.bottom));
3503
3504 free(name);
3505 free(value);
3506 }
3507
ShapeAttr(void)3508 static void ShapeAttr(void)
3509 {
3510 // ExamineToken("shapeattr");
3511 switch (rtfMinor) {
3512 case rtfShapeProperty:
3513 ReadShapeProperty();
3514 break;
3515 case rtfShapeText:
3516 // ExamineToken("ShapeText");
3517 RTFExecuteGroup();
3518 break;
3519 case rtfShapeLeft:
3520 shape.left = rtfParam;
3521 break;
3522 case rtfShapeTop:
3523 shape.top = rtfParam;
3524 break;
3525 case rtfShapeBottom:
3526 shape.bottom = rtfParam;
3527 break;
3528 case rtfShapeRight:
3529 shape.right = rtfParam;
3530 break;
3531 case rtfShapeLid:
3532 case rtfShapeOrderZ:
3533 case rtfShapeHeader:
3534 case rtfShapeXPosPage:
3535 case rtfShapeXPosMargin:
3536 case rtfShapeXPosColumn:
3537 case rtfShapeXPosIgnore:
3538 case rtfShapeYPosPage:
3539 case rtfShapeYPosMargin:
3540 case rtfShapeYPosColumn:
3541 case rtfShapeYPosIgnore:
3542 case rtfShapeWrap:
3543 case rtfShapeWrapSides:
3544 case rtfShapeRelOrderZ:
3545 case rtfShapeAnchor:
3546 default:
3547 break;
3548 }
3549 }
3550
3551 /*
3552 * The parameters following \shpgrp are the same as those following \shp.
3553 * The order of the shapes inside a group is from bottom to top in z-order.
3554 * Inside a \shpgrp, no {\shprslt ...} tokens are generated (that is, only
3555 * the root-level shape can have a \shprslt token (this token describes the
3556 * entire group). For example:
3557 *
3558 * {\shpgrp ... {\shp ... } {\shp ... } {\shprslt ... }}
3559 *
3560 * {\shpgrp ... } can be substituted for {\shp ... } to create groups inside groups.
3561 *
3562 * We _nearly_ always want to process the \shprslt group
3563 * {\shp\pict...\pngblip} {\shprslt\pict...} => opt for png
3564 * {\shp\pict...} {\shprslt\object Equation ...} => opt for equation
3565 * for now, we always opt for the result group
3566 */
ReadShapeGroup(void)3567 static void ReadShapeGroup(void)
3568 {
3569 insideShapeGroup = 1;
3570 // ExamineToken("ShapeGroup");
3571 if (RTFExecuteToToken(rtfControl,rtfShapeAttr,rtfShapeResult)) {
3572 RTFSkipGroup();
3573 }
3574 insideShapeGroup = 0;
3575 }
3576
3577 /*
3578 * Shape
3579 *
3580 * {\shp <shpinfo> {\*\shpinst ... } {\*\shprslt ... } }
3581 *
3582 */
ReadShape(void)3583 static void ReadShape(void)
3584 {
3585 // ExamineToken("Shape");
3586 if (insideShapeGroup) {
3587 RTFExecuteGroup();
3588 return;
3589 }
3590
3591 if (RTFSkipToToken(rtfControl,rtfShapeAttr,rtfShapeResult)) {
3592 RTFExecuteGroup();
3593 }
3594 }
3595
ReadUnicodeSkipN(void)3596 static void ReadUnicodeSkipN(void)
3597 {
3598 textStyle.unicodeSkipN= rtfParam;
3599 }
3600
ReadUnicode(void)3601 static void ReadUnicode(void)
3602 {
3603 int thechar,i;
3604
3605 if (rtfParam<0)
3606 thechar = rtfParam + 65536;
3607 else
3608 thechar = rtfParam;
3609
3610 if (rtfMinor == rtfUnicode) {
3611 /* \uNNNNY, drop Y as fallback char (assuming \uc1) */
3612 for (i=0; i<textStyle.unicodeSkipN; i++) {
3613 RTFGetToken();
3614 }
3615 }
3616
3617 PrepareForChar();
3618
3619 // fprintf(stderr, "Unicode --- %d, 0x%04X\n", thechar, thechar);
3620 if (thechar == 8201) {
3621 PutLitStr("\\,");
3622 return;
3623 }
3624
3625 if (thechar == 8212) {
3626 PutLitStr("---");
3627 return;
3628 }
3629
3630 if (thechar == 8216) {
3631 PutLitStr("`");
3632 return;
3633 }
3634
3635 if (thechar == 8217) {
3636 PutLitStr("'");
3637 return;
3638 }
3639
3640 if (thechar == 8220) {
3641 PutLitStr("``");
3642 return;
3643 }
3644
3645 if (thechar == 8221) {
3646 PutLitStr("''");
3647 return;
3648 }
3649
3650 if (thechar == 8230) {
3651 PutLitStr("...");
3652 return;
3653 }
3654
3655 if (thechar == 64256) {
3656 PutLitStr("ff");
3657 return;
3658 }
3659
3660 if (thechar == 64257) {
3661 PutLitStr("fi");
3662 return;
3663 }
3664
3665 if (thechar == 64258) {
3666 PutLitStr("fl");
3667 return;
3668 }
3669
3670 if (thechar == 64259) {
3671 PutLitStr("ffi");
3672 return;
3673 }
3674
3675 if (thechar == 64260) {
3676 PutLitStr("ffl");
3677 return;
3678 }
3679
3680 if (0xC0 <= thechar && thechar <=0xFF) {
3681 PutLitChar(thechar);
3682 return;
3683 }
3684
3685 /* directly translate greek */
3686 if ((913 <= thechar && thechar <= 937) || (945 <= thechar && thechar <= 969)) {
3687 PutMathLitStr(UnicodeGreekToLatex[thechar-913]);
3688 return;
3689 }
3690
3691 /* directly translate special cursive greek */
3692 if (976 == thechar) {
3693 PutMathLitStr("\\beta");
3694 return;
3695 }
3696 if (977 == thechar) {
3697 PutMathLitStr("\\vartheta");
3698 return;
3699 }
3700 if (981 == thechar) {
3701 PutMathLitStr("\\varphi");
3702 return;
3703 }
3704 if (982 == thechar) {
3705 PutMathLitStr("\\varpi");
3706 return;
3707 }
3708
3709
3710 /* and also a bunch of wierd codepoints from the Symbol font
3711 that end up in a private code area of Unicode */
3712 if (61472 <= thechar && thechar <= 61632) {
3713 PutMathLitStr(UnicodeSymbolFontToLatex[thechar-61472]);
3714 return;
3715 }
3716
3717 PutIntAsUtf8(thechar);
3718 /*snprintf(unitext,20,"\\unichar{%d}",(int)thechar);
3719 if (0) fprintf(stderr,"unicode --- %s!\n",unitext);
3720 PutLitStr(unitext);
3721 */
3722 }
3723
SkipFieldResult(void)3724 static void SkipFieldResult(void)
3725 {
3726 /* skip over to the result group */
3727 while (!RTFCheckCMM(rtfControl, rtfDestination, rtfFieldResult))
3728 RTFGetToken();
3729
3730 RTFSkipGroup();
3731 RTFRouteToken();
3732 }
3733
ReadHyperlinkField(void)3734 static void ReadHyperlinkField(void)
3735 {
3736 int localGL;
3737
3738 PutLitStr("\\href{");
3739
3740 localGL = RTFGetBraceLevel();
3741
3742 insideHyperlink = true;
3743
3744 while (RTFGetBraceLevel() && RTFGetBraceLevel() >= localGL) {
3745 RTFGetToken();
3746 if (rtfClass == rtfText) {
3747 if (rtfTextBuf[0] != '"'
3748 && !RTFCheckMM(rtfSpecialChar, rtfLDblQuote)
3749 && !RTFCheckMM(rtfSpecialChar, rtfRDblQuote))
3750 RTFRouteToken();
3751 } else
3752 RTFRouteToken();
3753 }
3754
3755 PutLitStr("}{");
3756
3757 /* skip over to the result group */
3758 while (!RTFCheckCMM(rtfControl, rtfDestination, rtfFieldResult))
3759 RTFGetToken();
3760
3761 localGL = RTFGetBraceLevel();
3762 /* switch off hyperlink flag */
3763 insideHyperlink = false;
3764
3765 while (RTFGetBraceLevel() && RTFGetBraceLevel() >= localGL) {
3766 RTFGetToken();
3767 RTFRouteToken();
3768 }
3769
3770 PutLitStr("}");
3771 requireHyperrefPackage = true;
3772 }
3773
ReadSymbolField(void)3774 static void ReadSymbolField(void)
3775 {
3776 char buf[100];
3777 short major, minor;
3778 short *currentCharCode = curCharCode;
3779
3780 /* go to the start of the symbol representation */
3781 if (RTFGetToken() != rtfText) {
3782 if (RTFCheckCM(rtfGroup, rtfBeginGroup) != 0)
3783 RTFSkipGroup();
3784 RTFSkipGroup();
3785 RTFRouteToken();
3786 return;
3787 }
3788
3789 /* read in the symbol token */
3790 strcpy(buf, rtfTextBuf);
3791 while (strcmp(rtfTextBuf, " ") != 0) {
3792 RTFGetToken();
3793 if (strcmp(rtfTextBuf, " ") != 0)
3794 strcat(buf, rtfTextBuf);
3795 }
3796
3797 /* convert the text symbol token to an int */
3798 major = atoi(buf);
3799
3800 /* do the mapping */
3801 curCharCode = symCharCode;
3802 minor = RTFMapChar(major);
3803 curCharCode = currentCharCode;
3804
3805 /* set the rtf token to the new value */
3806 RTFSetToken(rtfText, major, minor, rtfNoParam, buf);
3807
3808 if (minor >= rtfSC_therefore && minor < rtfSC_currency)
3809 requireAmsSymbPackage = true;
3810
3811 /* call the handler for text */
3812 TextClass();
3813
3814 RTFSkipGroup();
3815 RTFRouteToken();
3816 }
3817
3818 /* the following streams should just emit ... HToc268803753
3819 * PAGEREF _Toc268803753 \h
3820 * HYPERLINK \l "_Toc268803753"
3821 * _Toc268803753
3822 */
emitBookmark(void)3823 static void emitBookmark(void)
3824 {
3825 boolean started = false;
3826
3827 while (RTFGetToken() == rtfText) {
3828 switch (rtfTextBuf[0]) {
3829 case ' ':
3830 if (started) { /* assume that bookmarks optionally start and end with spaces */
3831 RTFSkipGroup();
3832 return;
3833 }
3834 break;
3835 case '"':
3836 break;
3837 case '\\':
3838 RTFGetToken(); /* drop backslash and the next letter */
3839 break;
3840 case '_':
3841 started = true;
3842 PutLitChar('H');
3843 break;
3844 default:
3845 started = true;
3846 PutLitChar(rtfTextBuf[0]);
3847 break;
3848 }
3849 }
3850 }
3851
3852 /*
3853 * Just emit \pageref{HToc268612944} for {\*\fldinst {... PAGEREF _Toc268612944 \\h } ..}
3854 */
ReadPageRefField(void)3855 static void ReadPageRefField(void)
3856 {
3857 PutLitStr("\\pageref{");
3858 emitBookmark();
3859 PutLitStr("}");
3860 RTFRouteToken();
3861
3862 SkipFieldResult();
3863 }
3864
3865
3866 /*
3867 * Try to convert Microsoft Equation Field to latex. The problem is that we
3868 * now have rtf tokens mixed with the stupid field tokens. I hacked the parser
3869 * to handle read \\X in an equation and send it to MicrosoftEQFieldCommand.
3870 */
3871
ReadEquationField(void)3872 static void ReadEquationField(void)
3873 {
3874 int parenCount = 0;
3875 int braceCount = 0;
3876 int displayEquation = 0;
3877 int oldSuppressLineBreak;
3878
3879 if (insideEquation == 0) {
3880 if (nowBetweenParagraphs) {
3881 EndParagraph();
3882 NewParagraph();
3883 PutLitStr("\n$$\n");
3884 displayEquation=1;
3885 } else {
3886 StopTextStyle();
3887 PutLitChar('$');
3888 }
3889 RTFPushStack();
3890 InitTextStyle();
3891 oldSuppressLineBreak = suppressLineBreak;
3892 suppressLineBreak = 1;
3893 }
3894
3895 insideEquation++;
3896 RTFExecuteGroup();
3897 SkipFieldResult();
3898 insideEquation--;
3899
3900 if (insideEquation == 0) {
3901 if (displayEquation) {
3902 PutLitStr("\n$$\n");
3903 EndParagraph();
3904 } else {
3905 StopTextStyle();
3906 PutLitChar('$');
3907 }
3908 suppressLineBreak = oldSuppressLineBreak;
3909 RTFPopStack();
3910 }
3911
3912 }
3913
3914 /*
3915 * Just emit \pageref{HToc268612944} for {\*\fldinst {... PAGEREF _Toc268612944 \\h } ..}
3916 */
ReadPageField(void)3917 static void ReadPageField(void)
3918 {
3919 PutLitStr("\\thepage{}");
3920 SkipFieldResult();
3921 }
3922
3923 /*
3924 * Three possible types of fields
3925 * (1) supported ... translate and ignore FieldResult
3926 * (2) tolerated ... ignore FieldInst and translate FieldResult
3927 * (3) unknown ... ignore both FieldInst and FieldResult
3928 */
ReadFieldInst(void)3929 static void ReadFieldInst(void)
3930 {
3931 char *fieldName;
3932 int level = RTFGetBraceLevel();
3933
3934 fieldName = RTFGetTextWord();
3935 if (fieldName == NULL) return;
3936
3937 if (strcasecmp(fieldName, "HYPERLINK") == 0 ) {
3938 if (prefs[pConvertHypertext])
3939 ReadHyperlinkField();
3940 else
3941 RTFSkipToLevel(level);
3942 free(fieldName);
3943 return;
3944 }
3945
3946 if (strcasecmp(fieldName, "SYMBOL") == 0) {
3947 ReadSymbolField();
3948 free(fieldName);
3949 return;
3950 }
3951
3952 if (strcasecmp(fieldName, "PAGEREF") == 0) {
3953 ReadPageRefField();
3954 free(fieldName);
3955 return;
3956 }
3957
3958 if (strcasecmp(fieldName, "PAGE") == 0) {
3959 ReadPageField();
3960 free(fieldName);
3961 return;
3962 }
3963
3964 if (strcasecmp(fieldName, "HYPERLINK") == 0) {
3965 ReadPageField();
3966 free(fieldName);
3967 return;
3968 }
3969
3970 if (strcasecmp(fieldName, "EQ") == 0) {
3971 ReadEquationField();
3972 free(fieldName);
3973 return;
3974 }
3975
3976 // RTFMsg("FIELD type is '%s'\n",fieldName);
3977
3978 /* Unsupported FIELD type ... the best we can do is bail from rtfFieldInst
3979 and hope rtfFieldResult can be processed */
3980
3981 RTFSkipToLevel(level);
3982 free(fieldName);
3983 }
3984
3985 /*
3986 * Just emit \label{HToc268612944} for {\*\bkmkstart _Toc268612944}
3987 */
ReadBookmarkStart(void)3988 static void ReadBookmarkStart(void)
3989 {
3990 PutLitStr("\\label{");
3991 emitBookmark();
3992 PutLitStr("}");
3993 }
3994
HandleOptionalTokens(void)3995 static void HandleOptionalTokens(void)
3996 {
3997 RTFGetToken();
3998
3999 switch (rtfMinor) {
4000 case rtfBookmarkStart:
4001 ReadBookmarkStart();
4002 break;
4003
4004 case rtfFieldInst:
4005 ReadFieldInst();
4006 break;
4007
4008 case rtfWord97Picture:
4009 ReadWord97Picture();
4010 break;
4011
4012 case rtfDrawObject:
4013 break;
4014
4015 case rtfShapeInst:
4016 case rtfShapeResult:
4017 break;
4018
4019 default:
4020 // ExamineToken("HandleOptionalTokesn");
4021 RTFSkipGroup();
4022 break;
4023 }
4024 }
4025
4026 /*
4027 * The reason these use the rtfSC_xxx thingies instead of just writing
4028 * out ' ', '-', '"', etc., is so that the mapping for these characters
4029 * can be controlled by the latex-encoding file.
4030 */
SpecialChar(void)4031 static void SpecialChar(void)
4032 {
4033 switch (rtfMinor) {
4034 case rtfLine:
4035 case rtfPar:
4036 if (nowBetweenParagraphs && !suppressSpaceBetweenParagraphs)
4037 current_vspace += abs(paragraph.lineSpacing);
4038 nowBetweenParagraphs = true;
4039 break;
4040
4041 case rtfSect:
4042 section.newSection = true;
4043 nowBetweenParagraphs = true;
4044 break;
4045
4046 case rtfNoBrkSpace:
4047 if (nowBetweenParagraphs)
4048 paragraph.extraIndent += 0;
4049 else
4050 PutStdChar(rtfSC_nobrkspace);
4051 break;
4052 case rtfTab:
4053 if (nowBetweenParagraphs)
4054 paragraph.extraIndent += 360;
4055 else if (prefs[pConvertTextNoTab])
4056 PutLitChar(' ');
4057 else
4058 PutLitStr("\\tab ");
4059 break;
4060 case rtfNoBrkHyphen:
4061 PutStdChar(rtfSC_nobrkhyphen);
4062 break;
4063 case rtfBullet:
4064 PutStdChar(rtfSC_bullet);
4065 break;
4066 case rtfEmDash:
4067 PutLitStr("---");
4068 break;
4069 case rtfEnDash:
4070 PutLitStr("-");
4071 break;
4072 case rtfLQuote:
4073 PutLitStr("`");
4074 break;
4075 case rtfRQuote:
4076 PutLitStr("'");
4077 break;
4078 case rtfLDblQuote:
4079 PutLitStr("``");
4080 break;
4081 case rtfRDblQuote:
4082 PutLitStr("''");
4083 break;
4084 case rtfPage:
4085 if (!insideTable)
4086 PutLitStr("\\pagebreak{}");
4087 break;
4088 case rtfOptDest:
4089 HandleOptionalTokens();
4090 break;
4091 case rtfCurHeadPage:
4092 PutLitStr("\\thepage{}");
4093 break;
4094 case rtfCurFNote:
4095 break;
4096 default:
4097 ExamineToken("SpecialChar"); /* comment out before release */
4098 }
4099 }
4100
DoHeaderFooter(void)4101 static void DoHeaderFooter(void)
4102 {
4103 char *buff, *s, *option;
4104 size_t hfStartPos, hfEndPos, len;
4105 int isHeader, isFirst;
4106 int level = RTFGetBraceLevel();
4107
4108 if (insideHeaderFooter) return;
4109
4110 insideHeaderFooter = true;
4111 suppressLineBreak = true;
4112 isHeader = (rtfMinor == rtfHeader) || (rtfMinor == rtfHeaderFirst) ;
4113 isFirst = (rtfMinor == rtfHeaderFirst) || (rtfMinor == rtfFooterFirst) ;
4114
4115 StopTextStyle();
4116
4117 hfStartPos = ftell(ofp);
4118
4119 switch (rtfMinor) {
4120 case rtfHeader:
4121 option = "\\lhead{";
4122 break;
4123 case rtfHeaderRight:
4124 option = "\\fancyhead[LO]{";
4125 break;
4126 case rtfHeaderLeft:
4127 option = "\\fancyhead[LE]{";
4128 break;
4129 case rtfHeaderFirst:
4130 option = " \\lhead{";
4131 break;
4132 case rtfFooter:
4133 option = "\\lfoot{";
4134 break;
4135 case rtfFooterRight:
4136 option = "\\fancyfoot[LO]{";
4137 break;
4138 case rtfFooterLeft:
4139 option = "\\fancyfoot[LE]{";
4140 break;
4141 case rtfFooterFirst:
4142 option = " \\lfoot{";
4143 break;
4144 }
4145
4146 PutLitStr(option);
4147 while (RTFGetBraceLevel() && RTFGetBraceLevel() >= level) {
4148 RTFGetToken();
4149 RTFRouteToken();
4150 }
4151 StopTextStyle();
4152 PutLitStr("}\n");
4153
4154 /* copy header/footer */
4155 hfEndPos = ftell(ofp);
4156 len = hfEndPos - hfStartPos;
4157
4158 /* Don't bother unless the header contains something */
4159 /* and we are still in the preamble */
4160
4161 if (len<=strlen(option)+2) {
4162 /* erase empty command */
4163 fseek(ofp, hfStartPos, 0);
4164
4165 } else {
4166
4167 /* save only if still in preamble or it is a first page command */
4168 if (isFirst || !wroteBeginDocument) {
4169 requireFancyHdrPackage = true;
4170 buff = malloc(len+1);
4171 fseek(ofp, hfStartPos, 0);
4172 fread(buff,1,len,ofp);
4173 buff[len] = '\0';
4174
4175 if (!isFirst) {
4176 s = strdup_together(preambleFancyHeader,buff);
4177 if (preambleFancyHeader) free(preambleFancyHeader);
4178 preambleFancyHeader = s;
4179 } else {
4180 s = strdup_together(preambleFancyHeaderFirst,buff);
4181 if (preambleFancyHeaderFirst) free(preambleFancyHeaderFirst);
4182 preambleFancyHeaderFirst = s;
4183 }
4184 fseek(ofp, hfStartPos, 0);
4185 }
4186 }
4187
4188 suppressLineBreak = false;
4189 insideHeaderFooter = false;
4190 }
4191
4192 /*
4193 * This function notices destinations that should be ignored
4194 * and skips to their ends. This keeps, for instance, picture
4195 * data from being considered as plain text.
4196 */
4197
Destination(void)4198 static void Destination(void)
4199 {
4200 switch (rtfMinor) {
4201 case rtfFNContSep:
4202 case rtfFNContNotice:
4203 case rtfInfo:
4204 case rtfIndexRange:
4205 case rtfITitle:
4206 case rtfISubject:
4207 case rtfIAuthor:
4208 case rtfIOperator:
4209 case rtfIKeywords:
4210 case rtfIComment:
4211 case rtfIVersion:
4212 case rtfIDoccomm:
4213 case rtfUserPropsGroup:
4214 case rtfWGRFmtFilter:
4215 RTFSkipGroup();
4216 break;
4217 case rtfNeXTGraphic:
4218 ReadNextGraphic();
4219 break;
4220 case rtfUnicodeSkipN:
4221 ReadUnicodeSkipN();
4222 break;
4223 case rtfUnicode:
4224 case rtfUnicodeFake:
4225 ReadUnicode();
4226 break;
4227 case rtfHeaderLeft:
4228 case rtfHeaderRight:
4229 case rtfFooterLeft:
4230 case rtfFooterRight:
4231 case rtfFooterFirst:
4232 case rtfHeaderFirst:
4233 case rtfHeader:
4234 case rtfFooter:
4235 DoHeaderFooter();
4236 break;
4237 case rtfShapeAttr:
4238 ShapeAttr();
4239 break;
4240 case rtfShapeGroup:
4241 ReadShapeGroup();
4242 break;
4243 case rtfShape:
4244 ReadShape();
4245 break;
4246 case rtfBlipTag:
4247 // ExamineToken("BlipTag");
4248 break;
4249 }
4250 }
4251
4252 /*
4253 * In RTF, the code page is specified in at least three different places
4254 *
4255 * (1) as the third token in the file, e.g., {\rtf1\ansi
4256 * (2) in the font table for each font e.g., \fcharset2
4257 * (3) by the code page token, e.g., \ansicpg1252
4258 *
4259 * A pragmatic approach is used here. The third token is used to
4260 * set genCharCode. If \aniscpg is found, then genCharCode will be
4261 * changed to that. Here we just change if it is the symbol font.
4262 */
RTFSetGenCharSet(void)4263 static void RTFSetGenCharSet(void)
4264 {
4265 switch(rtfMinor) {
4266 case rtfAnsiCharSet:
4267 genCharCode = cp1252CharCode;
4268 break;
4269 case rtfMacCharSet:
4270 genCharCode = cpMacCharCode;
4271 break;
4272 case rtfPcCharSet:
4273 genCharCode = cp437CharCode;
4274 break;
4275 case rtfPcaCharSet:
4276 genCharCode = cp850CharCode;
4277 break;
4278 }
4279
4280 /* check for the \ansicpg control word */
4281 RTFPeekToken();
4282 if (RTFCheckCMM(rtfControl, rtfFontAttr, rtfAnsiCodePage)) { /* we will handle the token */
4283 RTFGetToken();
4284 switch (rtfParam) {
4285 case 437:
4286 genCharCode=cp437CharCode;
4287 break;
4288 case 850:
4289 genCharCode=cp850CharCode;
4290 break;
4291 case 1250:
4292 genCharCode=cp1250CharCode;
4293 break;
4294 case 1251:
4295 genCharCode=cp1251CharCode;
4296 break;
4297 case 1252:
4298 genCharCode=cp1252CharCode;
4299 break;
4300 case 1254:
4301 genCharCode=cp1254CharCode;
4302 break;
4303 case 10000:
4304 genCharCode=cpMacCharCode;
4305 break;
4306 case 10001:
4307 genCharCode=cp932CharCode;
4308 break;
4309 case 10008:
4310 genCharCode=cp936CharCode;
4311 break;
4312 }
4313 }
4314
4315 curCharCode = genCharCode;
4316 }
4317
PictureAttr(void)4318 static void PictureAttr(void)
4319 {
4320 switch (rtfMinor) {
4321 case rtfMacQD:
4322 picture.type = pict;
4323 break;
4324 case rtfWinMetafile:
4325 picture.type = wmf;
4326 break;
4327 case rtfEmf:
4328 picture.type = emf;
4329 break;
4330 case rtfPng:
4331 picture.type = png;
4332 break;
4333 case rtfJpeg:
4334 picture.type = jpeg;
4335 break;
4336 case rtfPicGoalWid:
4337 picture.goalWidth = rtfParam;
4338 break;
4339 case rtfPicGoalHt:
4340 picture.goalHeight = rtfParam;
4341 break;
4342 case rtfPicScaleX:
4343 picture.scaleX = rtfParam/100.0;
4344 break;
4345 case rtfPicWid:
4346 picture.width = rtfParam;
4347 break;
4348 case rtfPicHt:
4349 picture.height = rtfParam;
4350 break;
4351 case rtfPicScaleY:
4352 picture.scaleY = rtfParam/100.0;
4353 break;
4354 case rtfPicCropTop:
4355 picture.cropTop = rtfParam;
4356 break;
4357 case rtfPicCropBottom:
4358 picture.cropBottom = rtfParam;
4359 break;
4360 case rtfPicCropLeft:
4361 picture.cropLeft = rtfParam;
4362 break;
4363 case rtfPicCropRight:
4364 picture.cropRight = rtfParam;
4365 break;
4366 }
4367 }
4368
4369 /* decides what to do when a control word is encountered */
ControlClass(void)4370 static void ControlClass(void)
4371 {
4372 switch (rtfMajor) {
4373 case rtfDefFont:
4374 RTFSetDefaultFont(rtfParam);
4375 case rtfFontAttr:
4376 switch (rtfMinor) {
4377 case rtfAnsiCodePage:
4378 case rtfFontCodePage:
4379 /* codePage = rtfParam;*/
4380 break;
4381 }
4382 break;
4383 case rtfDestination:
4384 Destination();
4385 break;
4386 case rtfSpecialChar:
4387 SpecialChar();
4388 break;
4389 case rtfCharAttr:
4390 SetTextStyle();
4391 break;
4392 case rtfListAttr:
4393 RTFSkipGroup();
4394 break;
4395 case rtfTblAttr:
4396 if (rtfMinor == rtfRowDef && !(insideTable))
4397 DoTable(); /* not inside a table, start one */
4398 else
4399 DoTableAttr(); /* currently inside a table, set table attribute */
4400 break;
4401 case rtfParAttr:
4402 ParAttr();
4403 break;
4404 case rtfSectAttr:
4405 SectAttr();
4406 break;
4407 case rtfCharSet:
4408 RTFSetGenCharSet();
4409 break;
4410 case rtfPictAttr:
4411 PictureAttr();
4412 break;
4413 case rtfShapeAttr:
4414 ShapeAttr();
4415 break;
4416 case rtfEquationFieldCmd:
4417 MicrosoftEQFieldCommand();
4418 break;
4419 case rtfEquationFieldLiteral:
4420 MicrosoftEQFieldLiteral();
4421 break;
4422 }
4423
4424 /* handles {\*\keyword ...} */
4425 // if (RTFCheckMM(rtfSpecialChar, rtfOptDest))
4426 // RTFSkipGroup();
4427
4428 }
4429
4430 /* needed to handle groups in math environment */
GroupClass(void)4431 static void GroupClass(void)
4432 {
4433 if (!insideEquation) return;
4434 // ExamineToken("Group");
4435 if (rtfMajor == rtfEndGroup)
4436 StopTextStyle();
4437 }
4438
4439 /*
4440 * Prepares output TeX file for each input RTF file.
4441 * Sets globals and installs callbacks.
4442 */
BeginLaTeXFile(void)4443 int BeginLaTeXFile(void)
4444 {
4445 int i;
4446
4447 RTFSetDefaultFont(-1);
4448 insideFootnote = false;
4449 insideHyperlink = false;
4450 insideTable = false;
4451 insideHeaderFooter = false;
4452 section.newPage = 1;
4453 section.cols = 1;
4454 section.writtenCols = 1;
4455 section.newSection = false;
4456
4457 requireSetspacePackage = false;
4458 requireTablePackage = false;
4459 requireGraphicxPackage = false;
4460 requireAmsSymbPackage = false;
4461 requireMultiColPackage = false;
4462 requireUlemPackage = false;
4463 requireFixLtx2ePackage = false;
4464 requireHyperrefPackage = false;
4465 requireMultirowPackage = false;
4466 requireAmsMathPackage = false;
4467 requireFancyHdrPackage = true;
4468 requireAMSSymbolPackage = true;
4469
4470 preambleFancyHeader=NULL;
4471 preambleFancyHeaderFirst=NULL;
4472
4473 picture.count = 0;
4474 picture.type = unknownPict;
4475 oleEquation.count = 0;
4476 object.class = unknownObjClass;
4477 object.shape = 0;
4478 table.cellCount = 0;
4479 table.theCell = NULL;
4480 table.cellMergePar = mergeNone;
4481 table.multiRow = false;
4482
4483 InitTextStyle();
4484 textStyleWritten = textStyle;
4485
4486 InitParagraphStyle();
4487 paragraphWritten = paragraph;
4488 nowBetweenParagraphs = true;
4489 suppressLineBreak = false;
4490 suppressSpaceBetweenParagraphs=false;
4491
4492 /* install class callbacks */
4493 RTFSetClassCallback(rtfText, TextClass);
4494 RTFSetClassCallback(rtfControl, ControlClass);
4495 RTFSetClassCallback(rtfGroup, GroupClass);
4496
4497 /* install destination callbacks */
4498 RTFSetDestinationCallback(rtfObjWid, ReadObjWidth);
4499 RTFSetDestinationCallback(rtfColorTbl, WriteColors);
4500 RTFSetDestinationCallback(rtfObject, ReadObject);
4501 RTFSetDestinationCallback(rtfPict, ReadPicture);
4502 RTFSetDestinationCallback(rtfFootnote, ReadFootnote);
4503
4504 for (i=0; i<256; i++)
4505 UsedColor[i] = 0;
4506
4507 WriteLaTeXHeader();
4508 return (1);
4509 }
4510
4511
4512 /* characters from the Symbol font get written to private areas
4513 of unicode that are not well supported by latex. This is
4514 simple translation table. */
4515 char *UnicodeSymbolFontToLatex[] = {
4516 " ", /* 61472 or U+F020 */
4517 "!",
4518 "\\forall",
4519 "\\#",
4520 "\\exists",
4521 "\\%",
4522 "\\&",
4523 " ",
4524 "(",
4525 ")",
4526 "*",
4527 "+",
4528 ",",
4529 "-",
4530 ".",
4531 "/",
4532 "0",
4533 "1",
4534 "2",
4535 "3",
4536 "4",
4537 "5",
4538 "6",
4539 "7",
4540 "8",
4541 "9",
4542 ":",
4543 ";",
4544 "<",
4545 "=",
4546 ">",
4547 "?",
4548 "\\congruent",
4549 "A",
4550 "B",
4551 "X",
4552 "\\Delta",
4553 "E",
4554 "\\Phi",
4555 "\\Gamma",
4556 "H",
4557 "I",
4558 "\\vartheta",
4559 "K",
4560 "\\Lambda",
4561 "",
4562 "N",
4563 "O",
4564 "\\Pi",
4565 "\\Theta",
4566 "P",
4567 "\\Sigma",
4568 "T",
4569 "Y",
4570 "\\zeta",
4571 "\\Omega",
4572 "\\Xi",
4573 "\\Psi",
4574 "Z",
4575 "[",
4576 "\\therefore",
4577 "]",
4578 "\\bot",
4579 "\\_",
4580 "-", /* over bar?? */
4581 "\\alpha",
4582 "\\beta",
4583 "\\chi",
4584 "\\delta",
4585 "\\epsilon",
4586 "\\phi",
4587 "\\gamma",
4588 "\\eta",
4589 "\\iota",
4590 "\\varphi",
4591 "\\kappa",
4592 "\\lambda",
4593 "\\mu",
4594 "\\nu",
4595 "o",
4596 "\\pi",
4597 "\\theta",
4598 "\\rho",
4599 "\\sigma",
4600 "\\tau",
4601 "\\nu",
4602 "\\varpi",
4603 "\\omega",
4604 "\\xi",
4605 "\\psi",
4606 "\\zeta",
4607 "\\lbrace",
4608 "|",
4609 "\\rbrace",
4610 "\\sim",
4611 "Y",
4612 "\\prime",
4613 "\\le",
4614 "/",
4615 "\\infty",
4616 "\\int",
4617 "\\clubsuit",
4618 "\\diamondsuit",
4619 "\\heartsuit",
4620 "\\spadesuit",
4621 "\\rightleftarrow",
4622 "\\leftarrow",
4623 "\\uparrow",
4624 "\\rightarrow",
4625 "\\downarrow",
4626 "^\\circ",
4627 "\\pm",
4628 "\\prime\\prime",
4629 "\\ge",
4630 "\\times",
4631 "\\propto",
4632 "\\partial",
4633 "\\blackcircle",
4634 "\\division",
4635 "\\ne",
4636 "\\equiv",
4637 "\\approx",
4638 "...",
4639 "|",
4640 "\\_",
4641 "", /*corner arrow*/
4642 "\\Aleph",
4643 0
4644 };
4645
4646 /* greek characters translated directly to avoid the textgreek.sty package
4647 positions 913-969 (0x0391-0x03C9)*/
4648 char *UnicodeGreekToLatex[] = {
4649 "A",
4650 "B",
4651 "\\Gamma", /*915*/
4652 "\\Delta",
4653 "E",
4654 "Z",
4655 "H",
4656 "\\Theta",/*920*/
4657 "I",
4658 "K",
4659 "\\Lambda",
4660 "M",
4661 "N",
4662 "\\Xi",
4663 "O",
4664 "\\Pi",
4665 "P",
4666 "", /* invalid character capital rho 930*/
4667 "\\Sigma",
4668 "T",
4669 "Y",
4670 "\\Phi",
4671 "X",
4672 "\\Psi",
4673 "\\Omega",
4674 "\\\"I", /* these should not be in math mode 938*/
4675 "\\\"Y",
4676 "\\'\\alpha",
4677 "\\'\\epsilon",
4678 "\\'\\eta",
4679 "\\'\\iota",
4680 "\\\"u", /* 944 */
4681 "\\alpha",
4682 "\\beta",
4683 "\\gamma",
4684 "\\delta",
4685 "\\epsilon",
4686 "\\zeta",
4687 "\\eta",
4688 "\\theta",
4689 "\\iota",
4690 "\\kappa",
4691 "\\lambda",
4692 "\\mu",
4693 "\\nu",
4694 "\\xi",
4695 "o",
4696 "\\pi",
4697 "\\rho",
4698 "s",
4699 "\\sigma",
4700 "\\tau",
4701 "u",
4702 "\\phi",
4703 "\\chi",
4704 "\\psi",
4705 "\\omega",
4706 0
4707 };
4708