1 #include "CtrlCore.h"
2
3 namespace Upp {
4
5 #define LLOG(x) // DLOG(x)
6
TwipDotsLim(int twips)7 static int TwipDotsLim(int twips) { return minmax<int>(TwipDots(twips), 0, MAX_DOTS); }
8
FromCString(const char * p,const char ** endptr=NULL)9 static String FromCString(const char *p, const char **endptr = NULL)
10 {
11 if(endptr) {
12 const char *e = p;
13 if(*e == '\"')
14 e++;
15 while(*e && *e != '\"')
16 if(*e++ == '\\' && *e)
17 e++;
18 if(*e == '\"')
19 e++;
20 *endptr = e;
21 }
22
23 try {
24 CParser parser(p);
25 return parser.ReadOneString();
26 }
27 catch(Exc e) {
28 return Null;
29 }
30 }
31
32 class RTFParser
33 {
34 public:
35 RTFParser(const char *rtf);
36
37 RichText Run();
38
39 private:
40 enum TOKEN { T_EOF, T_TEXT, T_COMMAND, T_GROUP, T_END_GROUP };
41
42 void Flush(bool force, int itap);
43 void OpenTable(int level);
44 void FlushTable(int level);
45 TOKEN Fetch();
46 void Skip();
Token()47 TOKEN Token() { if(!is_full) Fetch(); return token; }
PassIf(bool c)48 bool PassIf(bool c) { is_full &= !c; return c; }
PassText()49 bool PassText() { return PassIf(Token() == T_TEXT); }
PassGroup()50 bool PassGroup() { return PassIf(Token() == T_GROUP); }
PassEndGroup()51 bool PassEndGroup() { return PassIf(Token() == T_END_GROUP || token == T_EOF); }
52 bool PassEndGroup(int level);
PassCmd(const char * cmd)53 bool PassCmd(const char *cmd) { return PassIf(Token() == T_COMMAND && command == cmd); }
PassQ(const char * cmd)54 bool PassQ(const char *cmd) { return PassIf(command == cmd); }
55
SkipGroup()56 void SkipGroup() { SkipGroup(stack.GetCount()); }
57 void SkipGroup(int level);
Level() const58 int Level() const { return stack.GetCount(); }
59
60 void ReadItem();
61 void ReadItemGroup(int level);
ReadItemGroup()62 void ReadItemGroup() { ReadItemGroup(Level()); }
63 void ReadText();
64 void ReadText(const WString& text);
ReadChar(word ch)65 void ReadChar(word ch) { ReadText(WString(ch, 1)); }
66 void ReadCommand();
67
68 void ReadHeader();
69 void ReadFaceTable();
70 void ReadColorTable();
71 void ReadCharSet();
72
73 void ReadMisc();
74 void ReadField();
75 bool ReadField(const char *def);
76 void ReadPict();
77 void ReadShape();
78
79 void ReadParaStyle();
80 void ReadTableStyle();
81 void DefaultParaStyle();
82
83 void ReadCharStyle();
84 void ReadCellBorder(int& width);
85
86 String ReadBinHex(char& odd) const;
87
88 private:
89 const char *rtf;
90
91 TOKEN token;
92 bool is_full;
93 bool next_command;
94 WString text;
95 String command;
96 int command_arg;
97
98 struct CellInfo {
99 CellInfo();
100
101 RichCell::Format format;
102 Rect cellmarginunits;
103 int shading;
104 Color shading_fore;
105 Color shading_back;
106 int end_dots;
107 };
108
109 struct Cell {
110 Cell();
111
112 CellInfo info;
113 RichTxt text;
114 bool merge_first;
115 bool merge;
116 int nbegin;
117 Size span;
118 };
119
120 struct Face : Moveable<Face> {
121 int face;
122 byte charset;
123 };
124
125 struct TableState {
TableStateUpp::RTFParser::TableState126 TableState() : textcol(0), stylecol(0) { cells.Add(); }
127
128 RichTable::Format tableformat;
129 Vector< Array<Cell> > cells;
130 int textcol;
131 int stylecol;
132 };
133
134 CellInfo& CellInfoAt(int i);
135 Cell& CellAt(TableState& ts, int i);
136 void SetCellMargin(Cell& cell, int Rect::*mbr);
137
138 struct State {
139 String dest;
140 RichPara::Format format;
141 RichPara::CharFormat charformat;
142 WithDeepCopy< Array<CellInfo> > cellinfo;
143 int trgaph;
144 Rect rowmargin;
145 Rect rowmarginunits;
146 Rect rowspacing;
147 Rect rowspacingunits;
148 Rect cellmarginunits;
149 int uc_value;
150 int left_margin;
151 int right_margin;
152 int first_indent;
153 bool in_table;
154 int itap;
155 bool nestprop;
156 bool new_dest;
157 byte charset;
158 };
159
160 Array<State> stack;
161 Array<TableState> table_stack;
162 State state;
163 RichPara::CharFormat plain_format;
164 RichPara::Format pard_format;
165 CellInfo std_cell_info;
166 byte plain_charset;
167 byte default_charset;
168 int default_font;
169 Alignment tab_align;
170 byte tab_fill;
171 Vector<Face> face_table;
172 Vector<Color> color_table;
173 int paper_width;
174 int left_margin;
175 int right_margin;
176
177 RichText output;
178 RichPara para;
179 };
180
ParseRTF(const char * rtf)181 RichText ParseRTF(const char *rtf) { return RTFParser(rtf).Run(); }
182
CellInfo()183 RTFParser::CellInfo::CellInfo()
184 : cellmarginunits(0, 0, 0, 0)
185 , shading(0)
186 , shading_fore(Black())
187 , shading_back(White())
188 , end_dots(0)
189 {
190 }
191
Cell()192 RTFParser::Cell::Cell()
193 : merge_first(false)
194 , merge(false)
195 , nbegin(0)
196 , span(0, 0)
197 {
198 }
199
RTFParser(const char * rtf)200 RTFParser::RTFParser(const char *rtf)
201 : rtf(rtf)
202 {
203 #ifdef _DEBUG0
204 SaveFile(ConfigFile("rtfparser.rtf"), rtf);
205 LOG(rtf);
206 #endif
207 is_full = false;
208 next_command = false;
209 default_font = 0;
210 plain_charset = default_charset = state.charset = CHARSET_WIN1250;
211 state.uc_value = 1;
212 state.new_dest = false;
213 plain_format.Face(Font::ARIAL).Height(100);
214 std_cell_info.format.align = ALIGN_TOP;
215 std_cell_info.format.margin = Rect(25, 25, 25, 25);
216 DefaultParaStyle();
217 state.charformat = plain_format;
218 tab_align = ALIGN_LEFT;
219 tab_fill = 0;
220 paper_width = 5100;
221 left_margin = right_margin = 750;
222 }
223
Run()224 RichText RTFParser::Run()
225 {
226 if(!PassGroup() || !PassCmd("rtf") || command_arg != 1 && !IsNull(command_arg))
227 return pick(output);
228 while(Token() != T_EOF)
229 ReadItem();
230 Flush(false, 1);
231 FlushTable(0);
232 return pick(output);
233 }
234
FlushTable(int level)235 void RTFParser::FlushTable(int level)
236 {
237 while(table_stack.GetCount() > level) {
238 TableState& child = table_stack.Top();
239 while(!child.cells.IsEmpty() && child.cells.Top().IsEmpty())
240 child.cells.Drop();
241 if(child.cells.IsEmpty()) {
242 table_stack.Drop();
243 continue;
244 }
245 Index<int> dot_index;
246 // int pos = child.tableformat.lm;
247 dot_index.Add(child.tableformat.lm);
248 for(int r = 0; r < child.cells.GetCount(); r++) {
249 Array<Cell>& rw = child.cells[r];
250 for(int c = 0; c < rw.GetCount(); c++)
251 dot_index.FindAdd(rw[c].info.end_dots);
252 }
253 Vector<int> dot_order = dot_index.PickKeys();
254 Sort(dot_order);
255 RichTable table;
256 if(table_stack.GetCount() == 1)
257 child.tableformat.rm = max(paper_width - left_margin - right_margin - dot_order.Top(), 0);
258 // child.tableformat.before = state.format.before;
259 // child.tableformat.after = state.format.after;
260 table.SetFormat(child.tableformat);
261 for(int c = 1; c < dot_order.GetCount(); c++)
262 table.AddColumn(dot_order[c] - dot_order[c - 1]);
263 dot_index = pick(dot_order);
264 int tbl_border = Null, tbl_grid = Null;
265 Color clr_border = Null, clr_grid = Null;
266 for(int r = 0; r < child.cells.GetCount(); r++) {
267 Array<Cell>& rw = child.cells[r];
268 int pos = child.tableformat.lm;
269 for(int c = 0; c < rw.GetCount(); c++) {
270 Cell& cell = rw[c];
271 if(cell.merge) {
272 pos = cell.info.end_dots;
273 continue;
274 }
275 cell.span.cy = 0;
276 if(cell.merge_first) {
277 for(int m = r + 1; m < child.cells.GetCount(); m++) {
278 const Array<Cell>& mrw = child.cells[m];
279 int mc = mrw.GetCount();
280 while(--mc >= 0 && mrw[mc].info.end_dots > cell.info.end_dots)
281 ;
282 if(mc >= 0 && mrw[mc].info.end_dots == cell.info.end_dots && mrw[mc].merge)
283 cell.span.cy++;
284 else
285 break;
286 }
287 }
288 cell.nbegin = dot_index.Find(pos);
289 cell.span.cx = max(0, dot_index.Find(pos = cell.info.end_dots) - cell.nbegin - 1);
290 if(cell.span.cx < 0) {
291 cell.merge = true;
292 continue;
293 }
294 bool outer_border[] = {
295 cell.nbegin == 0,
296 r == 0,
297 cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(),
298 r + cell.span.cy + 1 >= child.cells.GetCount(),
299 };
300 int border_width[] = {
301 cell.info.format.border.left,
302 cell.info.format.border.top,
303 cell.info.format.border.right,
304 cell.info.format.border.bottom,
305 };
306 for(int b = 0; b < __countof(border_width); b++) {
307 int& out_wd = (outer_border[b] ? tbl_border : tbl_grid);
308 Color& out_co = (outer_border[b] ? clr_border : clr_grid);
309 if(IsNull(cell.info.format.bordercolor) || border_width[b] <= 0
310 || !IsNull(out_co) && out_co != cell.info.format.bordercolor)
311 out_wd = 0;
312 else if(IsNull(out_wd) || border_width[b] < out_wd) {
313 out_wd = border_width[b];
314 out_co = cell.info.format.bordercolor;
315 }
316 }
317 if(cell.info.shading > 0) {
318 Color zero = White();
319 Color one = Nvl(cell.info.shading_fore, Black());
320 int r = zero.GetR() + iscale(one.GetR() - zero.GetR(), cell.info.shading, 10000);
321 int g = zero.GetG() + iscale(one.GetG() - zero.GetG(), cell.info.shading, 10000);
322 int b = zero.GetB() + iscale(one.GetB() - zero.GetB(), cell.info.shading, 10000);
323 cell.info.format.color = Color(r, g, b);
324 }
325 }
326 }
327 RichTable::Format tf = table.GetFormat();
328 tf.frame = Nvl(tbl_border, 0);
329 tf.framecolor = (tf.frame > 0 ? clr_border : Color(Null));
330 tf.grid = Nvl(tbl_grid, 0);
331 tf.gridcolor = (tf.grid > 0 ? clr_grid : Color(Null));
332 table.SetFormat(tf);
333 for(int r = 0; r < child.cells.GetCount(); r++) {
334 Array<Cell>& rw = child.cells[r];
335 // int pos = child.tableformat.lm;
336 for(int c = 0; c < rw.GetCount(); c++) {
337 Cell& cell = rw[c];
338 if(cell.merge)
339 continue;
340 if(cell.span.cx || cell.span.cy)
341 table.SetSpan(r, cell.nbegin, cell.span.cy, cell.span.cx);
342 bool outer_border[] = {
343 cell.nbegin == 0,
344 r == 0,
345 cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(),
346 r + cell.span.cy + 1 >= child.cells.GetCount(),
347 };
348 int *border_width[] = {
349 &cell.info.format.border.left,
350 &cell.info.format.border.top,
351 &cell.info.format.border.right,
352 &cell.info.format.border.bottom,
353 };
354 for(int b = 0; b < __countof(border_width); b++) {
355 int tbl_wd = (outer_border[b] ? tbl_border : tbl_grid);
356 // Color tbl_co = (outer_border[b] ? clr_border : clr_grid);
357 if(*border_width[b] <= tbl_wd)
358 *border_width[b] = 0;
359 }
360 table.SetFormat(r, cell.nbegin, cell.info.format);
361 cell.text.Normalize();
362 table.SetPick(r, cell.nbegin, pick(cell.text));
363 }
364 }
365 table.Normalize();
366 table_stack.Drop();
367 if(table_stack.IsEmpty())
368 output.CatPick(pick(table));
369 else {
370 TableState& par = table_stack.Top();
371 CellAt(par, par.textcol).text.CatPick(pick(table));
372 }
373 }
374 }
375
Flush(bool force,int itap)376 void RTFParser::Flush(bool force, int itap)
377 {
378 if(!para.part.IsEmpty() || force) {
379 int fi = state.first_indent, li = state.left_margin, ri = state.right_margin;
380 if(state.format.bullet != RichPara::BULLET_NONE) {
381 Swap(li, fi);
382 // li += fi;
383 // fi = -fi;
384 }
385 state.format.indent = minmax<int>(fi, 0, MAX_DOTS);
386 state.format.lm = minmax<int>(li, 0, MAX_DOTS);
387 state.format.rm = minmax<int>(ri, 0, MAX_DOTS);
388 para.format = state.format;
389 if(state.in_table) {
390 FlushTable(itap);
391 OpenTable(itap);
392 TableState& ts = table_stack[itap - 1];
393 CellAt(ts, ts.textcol).text.Cat(para, output.GetStyles());
394 }
395 else {
396 FlushTable(0);
397 output.Cat(para);
398 }
399 para.part.Clear();
400 }
401 else
402 FlushTable(itap);
403 }
404
Fetch()405 RTFParser::TOKEN RTFParser::Fetch()
406 {
407 is_full = true;
408 text.Clear();
409 if(next_command)
410 {
411 next_command = false;
412 return token = T_COMMAND;
413 }
414
415 command = Null;
416 command_arg = Null;
417
418 int skip = 0;
419 while(*rtf && *rtf != '{' && *rtf != '}')
420 {
421 int c = 0, nskip = max(skip - 1, 0);
422 if((byte)*rtf < ' ')
423 rtf++;
424 else if(*rtf != '\\')
425 c = ToUnicode(*rtf++, state.charset);
426 else
427 switch(rtf++, *rtf++)
428 {
429 case 0: {
430 rtf--;
431 break;
432 }
433
434 case '{':
435 case '}':
436 case '\\': {
437 c = rtf[-1];
438 break;
439 }
440
441 case '~': {
442 c = 160;
443 break;
444 }
445
446 case '|':
447 case '-':
448 case '_':
449 case ':': {
450 command = String(rtf - 1, 1);
451 LLOG("Command " << command);
452 if(text.IsEmpty())
453 return token = T_COMMAND;
454 next_command = true;
455 return token = T_TEXT;
456 }
457
458 case '\'': {
459 int c1 = ctoi(*rtf);
460 if(c1 < 16) {
461 int c2 = ctoi(*++rtf);
462 if(c2 < 16) {
463 c1 = c1 * 16 + c2;
464 rtf++;
465 }
466 c = ToUnicode(c1, state.charset);
467 }
468 break;
469 }
470
471 default: {
472 if(IsAlpha(*--rtf) || *rtf == '*' && rtf[1] == '\\' && IsAlpha(rtf[2])) {
473 if(*rtf == '*') {
474 rtf += 2;
475 state.new_dest = true;
476 LLOG("NewDest");
477 }
478 const char *b = rtf;
479 while(IsAlpha(*++rtf))
480 ;
481 command = String(b, rtf);
482 if(IsDigit(*rtf) || *rtf == '-')
483 command_arg = strtol(rtf, (char **)&rtf, 10);
484 if(*rtf == ' ')
485 rtf++;
486 if(command == "uc")
487 state.uc_value = command_arg;
488 else if(command == "u") {
489 c = command_arg;
490 nskip = state.uc_value;
491 }
492 else { // command - quit reading text
493 LLOG("Command " << command);
494 if(text.IsEmpty())
495 return token = T_COMMAND;
496 next_command = true;
497 return token = T_TEXT;
498 }
499 }
500 break;
501 }
502 }
503 if(c && !skip)
504 text.Cat(c);
505 skip = nskip;
506 }
507
508 if(!text.IsEmpty()) {
509 LLOG("TEXT, size: " << text.GetCount());
510 return token = T_TEXT;
511 }
512
513 if(*rtf == '{') {
514 stack.Add(state);
515 rtf++;
516 return token = T_GROUP;
517 }
518
519 if(*rtf == '}') {
520 if(!stack.IsEmpty()) {
521 state = stack.Top();
522 stack.Drop();
523 }
524 rtf++;
525 return token = T_END_GROUP;
526 }
527
528 return token = T_EOF;
529 }
530
PassEndGroup(int level)531 bool RTFParser::PassEndGroup(int level)
532 {
533 if(Token() == T_EOF)
534 return true;
535 if(token != T_END_GROUP)
536 return false;
537 is_full = false;
538 return Level() < level;
539 }
540
Skip()541 void RTFParser::Skip()
542 {
543 LLOG("Skip");
544 bool is_group = (token == T_GROUP || token == T_COMMAND && state.new_dest);
545 is_full = false;
546 if(is_group)
547 SkipGroup();
548 }
549
SkipGroup(int level)550 void RTFParser::SkipGroup(int level)
551 {
552 while(!PassEndGroup(level))
553 is_full = false;
554 }
555
ReadItem()556 void RTFParser::ReadItem()
557 {
558 const char *p = rtf;
559 if(token == T_COMMAND)
560 ReadCommand();
561 else if(token == T_TEXT)
562 ReadText();
563 if(rtf == p && is_full) {
564 is_full = false;
565 if(token == T_COMMAND && state.new_dest && command != "shppict") {
566 LLOG("SkipGroup new_dest " << command);
567 SkipGroup();
568 }
569 state.new_dest = false;
570 }
571 }
572
ReadItemGroup(int level)573 void RTFParser::ReadItemGroup(int level)
574 {
575 while(!PassEndGroup(level))
576 ReadItem();
577 }
578
ReadText()579 void RTFParser::ReadText()
580 {
581 if(!IsNull(text))
582 ReadText(text);
583 }
584
ReadText(const WString & text)585 void RTFParser::ReadText(const WString& text)
586 {
587 if(!IsNull(state.dest))
588 return;
589 LLOG("Output text: <" << FromUnicode(text, state.charset) << ">, " << state.charformat);
590 para.Cat(text, state.charformat);
591 }
592
ReadCommand()593 void RTFParser::ReadCommand()
594 {
595 if(Token() == T_COMMAND) ReadHeader();
596 if(Token() == T_COMMAND) ReadMisc();
597 if(Token() == T_COMMAND) ReadParaStyle();
598 if(Token() == T_COMMAND) ReadTableStyle();
599 if(Token() == T_COMMAND) ReadCharStyle();
600 }
601
ReadHeader()602 void RTFParser::ReadHeader()
603 {
604 if(PassCmd("deff"))
605 default_font = command_arg;
606 else if(PassQ("fonttbl")) {
607 state.dest = command;
608 ReadFaceTable();
609 }
610 else if(PassQ("colortbl")) {
611 state.dest = command;
612 ReadColorTable();
613 }
614 else if(PassQ("stylesheet") || PassQ("list") || PassQ("listoverride") || PassQ("info")) {
615 state.dest = command;
616 SkipGroup();
617 }
618 else if(Token() == T_COMMAND)
619 ReadCharSet();
620 }
621
ReadCharSet()622 void RTFParser::ReadCharSet()
623 {
624 if(PassQ("ansi")) {}
625 else if(PassQ("mac")) {}
626 else if(PassQ("pc")) {}
627 else if(PassQ("pca")) {}
628 else if(PassQ("ansicpg")) {
629 static const struct {
630 int ansicpg;
631 byte charset;
632 }
633 charsets[] =
634 {
635 { 1250, CHARSET_WIN1250 },
636 { 1251, CHARSET_WIN1251 },
637 { 1252, CHARSET_WIN1252 },
638 { 1253, CHARSET_WIN1253 },
639 { 1254, CHARSET_WIN1254 },
640 { 1255, CHARSET_WIN1255 },
641 { 1256, CHARSET_WIN1256 },
642 { 1257, CHARSET_WIN1257 },
643 };
644 for(int c = 0; c < __countof(charsets); c++)
645 if(charsets[c].ansicpg == command_arg) {
646 default_charset = state.charset = charsets[c].charset;
647 break;
648 }
649 }
650 }
651
ReadFaceTable()652 void RTFParser::ReadFaceTable()
653 {
654 int fx = 0;
655 while(!PassEndGroup()) {
656 if(!PassGroup()) {
657 Skip();
658 continue;
659 }
660 Face n;
661 n.face = Font::ARIAL;
662 n.charset = default_charset;
663 while(!PassEndGroup()) {
664 if(PassCmd("f"))
665 fx = command_arg;
666 else if(PassCmd("fnil"))
667 ;
668 else if(PassCmd("froman"))
669 n.face = Font::ROMAN;
670 else if(PassCmd("fswiss"))
671 n.face = Font::ARIAL;
672 else if(PassCmd("fmodern"))
673 n.face = Font::ARIAL;
674 else if(PassCmd("ftech"))
675 #ifdef PLATFORM_WIN32
676 n.face = Font::SYMBOL;
677 #else
678 n.face = Font::ARIAL;
679 #endif
680 else if(PassCmd("fcharset")) {
681 switch(command_arg) {
682 case 0: n.charset = CHARSET_WIN1252; break; // ANSI
683 case 1: n.charset = default_charset; break; // Default
684 case 2: n.charset = CHARSET_WIN1252; break; // Symbol
685 case 3: break; // Invalid
686 case 77: break; // Mac
687 case 128: break; // Shift Jis
688 case 129: break; // Hangul
689 case 130: break; // Johab
690 case 134: break; // GB2312
691 case 136: break; // Big5
692 case 161: n.charset = CHARSET_WIN1253; break; // Greek
693 case 162: n.charset = CHARSET_WIN1254; break; // Turkish
694 case 163: break; // Vietnamese
695 case 177: n.charset = CHARSET_WIN1255; break; // Hebrew
696 case 178: break; // Arabic
697 case 179: break; // Arabic Traditional
698 case 180: break; // Arabic user
699 case 181: break; // Hebrew user
700 case 186: break; // Baltic
701 case 204: n.charset = CHARSET_WIN1251; break; // Russian
702 case 222: break; // Thai
703 case 238: n.charset = CHARSET_WIN1250; break; // Eastern European
704 case 254: break; // PC 437
705 case 255: n.charset = CHARSET_WIN1252; break; // OEM
706 }
707 }
708 /* else if(PassText()) {
709 String s = FromUnicode(text, charset);
710 if(!s.IsEmpty() && *s.Last() == ';')
711 s.Trim(s.GetLength() - 1);
712 if(!s.IsEmpty())
713 f = Font::FindFaceNameIndex(s);
714 }
715 else if(PassGroup()) {
716 int level = Level();
717 if(PassCmd("falt") && PassText() && f < 0)
718 f = Font::FindFaceNameIndex(FromUnicode(text, charset));
719 SkipGroup(level);
720 }*/ //Cxl 2005-11-29 - "Arial CE" makes mess here!
721 else
722 Skip();
723 }
724 if(fx >= 0 && fx < MAX_FONTS) {
725 // if(f < 0) // Cxl 2005-11-29
726 if(default_font == fx) {
727 plain_format.Face(n.face);
728 plain_charset = n.charset;
729 }
730 Face dflt;
731 dflt.face = Font::ARIAL;
732 dflt.charset = default_charset;
733 face_table.At(fx++, dflt) = n;
734 }
735 }
736 }
737
ReadColorTable()738 void RTFParser::ReadColorTable()
739 {
740 int r = Null, g = Null, b = Null;
741 for(; !PassEndGroup(); Skip())
742 if(PassCmd("red"))
743 r = command_arg;
744 else if(PassCmd("green"))
745 g = command_arg;
746 else if(PassCmd("blue"))
747 b = command_arg;
748 else if(PassText())
749 {
750 Color c = Null;
751 if(!IsNull(r) || !IsNull(g) || !IsNull(b))
752 c = Color(Nvl(r, 0), Nvl(g, 0), Nvl(b, 0));
753 color_table.Add(c);
754 }
755 }
756
ReadMisc()757 void RTFParser::ReadMisc()
758 {
759 if(PassQ("field"))
760 ReadField();
761 else if(PassQ("nonshppict"))
762 SkipGroup();
763 else if(PassQ("pict"))
764 ReadPict();
765 else if(PassQ("shpinst"))
766 ReadShape();
767 else if(PassQ("endash"))
768 ReadChar(0x2013);
769 else if(PassQ("emdash"))
770 ReadChar(0x2014);
771 else if(PassQ("tab"))
772 ReadText(WString(9, 1));
773 else if(PassQ("enspace"))
774 ReadText(WString(" ")); // todo
775 else if(PassQ("emspace"))
776 ReadText(WString(" ")); // todo
777 else if(PassQ("bullet"))
778 ReadChar(0x2022);
779 else if(PassQ("lquote"))
780 ReadChar(0x2018);
781 else if(PassQ("rquote"))
782 ReadChar(0x2019);
783 else if(PassQ("ldblquote"))
784 ReadChar(0x201C);
785 else if(PassQ("rdblquote"))
786 ReadChar(0x201D);
787 }
788
ReadField()789 void RTFParser::ReadField()
790 {
791 bool ign_rslt = false;
792 int level = Level();
793 while(!PassEndGroup(level))
794 if(PassGroup() && Level() == level + 1) {
795 if(PassCmd("fldinst")) {
796 WString source;
797 for(; !PassEndGroup(); Skip())
798 if(PassText())
799 source.Cat(text);
800 if(ReadField(FromUnicode(source, state.charset)))
801 ign_rslt = true;
802 continue;
803 }
804 else if(PassCmd("fldrslt")) {
805 if(!ign_rslt)
806 ReadItemGroup();
807 }
808 }
809 else
810 Skip();
811 }
812
ReadField(const char * p)813 bool RTFParser::ReadField(const char *p)
814 {
815 Index<String> symdef;
816 while(*p)
817 if((byte)*p <= ' ')
818 p++;
819 else if(*p == '\"')
820 symdef.Add(FromCString(p, &p));
821 else {
822 const char *b = p;
823 while(*++p && *p != ' ')
824 ;
825 symdef.Add(String(b, p));
826 }
827 if(symdef.IsEmpty())
828 return false;
829 if(symdef[0] == "SYMBOL" && symdef.GetCount() >= 2 && IsDigit(*symdef[1])) {
830 int code = atoi(symdef[1]);
831 int face = -1;
832 int height = 0;
833 int f = symdef.Find("\\f");
834 if(f >= 0 && f + 1 < symdef.GetCount())
835 face = Font::FindFaceNameIndex(symdef[f + 1]);
836 f = symdef.Find("\\s");
837 if(f >= 0 && f + 1 < symdef.GetCount())
838 height = PointDots(fround(2 * Atof(symdef[f + 1]))) >> 1;
839 if(face < 0)
840 #ifdef PLATFORM_WIN32
841 face = Font::SYMBOL;
842 #else
843 face = Font::ARIAL;
844 #endif
845 if(height <= 0 || height >= MAX_DOT_HEIGHT)
846 height = state.charformat.GetHeight();
847 if(code >= 0 && code < 255) {
848 state.charformat.Face(face).Height(height);
849 ReadText(WString(ToUnicode(code, state.charset), 1));
850 return true;
851 }
852 }
853 return false;
854 }
855
DefaultParaStyle()856 void RTFParser::DefaultParaStyle()
857 {
858 state.format = pard_format;
859 state.first_indent = state.left_margin = state.right_margin = 0;
860 // state.cellformat = std_cell_format;
861 state.in_table = false;
862 state.itap = 1;
863 state.nestprop = false;
864 state.trgaph = 2;
865 state.rowmargin = Rect(25, 25, 25, 25);
866 state.cellmarginunits = state.rowmarginunits = Rect(0, 0, 0, 0);
867 state.rowspacing = Rect(0, 0, 0, 0);
868 state.rowspacingunits = Rect(0, 0, 0, 0);
869 state.charset = plain_charset;
870 }
871
ReadParaStyle()872 void RTFParser::ReadParaStyle()
873 {
874 if(PassQ("par"))
875 Flush(true, state.itap);
876 else if(PassQ("cell")) {
877 Flush(false, 1);
878 if(!table_stack.IsEmpty())
879 table_stack[0].textcol++;
880 }
881 else if(PassQ("nestcell")) {
882 Flush(false, state.itap);
883 if(state.itap <= table_stack.GetCount())
884 table_stack[state.itap - 1].textcol++;
885 }
886 else if(PassQ("pard"))
887 DefaultParaStyle();
888 else if(PassQ("pntext"))
889 SkipGroup();
890 else if(PassQ("pn")) {
891 SkipGroup();
892 state.format.bullet = RichPara::BULLET_ROUND;
893 }
894 else if(PassQ("pagebb"))
895 state.format.newpage = (command_arg != 0);
896 else if(PassQ("ql"))
897 state.format.align = ALIGN_LEFT;
898 else if(PassQ("qc"))
899 state.format.align = ALIGN_CENTER;
900 else if(PassQ("qr"))
901 state.format.align = ALIGN_RIGHT;
902 else if(PassQ("qj"))
903 state.format.align = ALIGN_JUSTIFY;
904 else if(PassQ("fi"))
905 state.first_indent = TwipDotsLim(command_arg);
906 else if(PassQ("li"))
907 state.left_margin = TwipDotsLim(command_arg);
908 else if(PassQ("ri"))
909 state.right_margin = TwipDotsLim(command_arg);
910 else if(PassQ("sb"))
911 state.format.before = TwipDotsLim(command_arg);
912 else if(PassQ("sa"))
913 state.format.after = TwipDotsLim(command_arg);
914 else if(PassQ("widctlpar"))
915 state.format.orphan = true;
916 else if(PassQ("nowidctlpar"))
917 state.format.orphan = false;
918 else if(PassQ("tql"))
919 tab_align = ALIGN_LEFT;
920 else if(PassQ("tqc"))
921 tab_align = ALIGN_CENTER;
922 else if(PassQ("tqr"))
923 tab_align = ALIGN_RIGHT;
924 else if(PassQ("tqdec"))
925 tab_align = ALIGN_RIGHT; // todo
926 else if(PassQ("tldot"))
927 tab_fill = 0;
928 else if(PassQ("tlhyph"))
929 tab_fill = 0;
930 else if(PassQ("tlul"))
931 tab_fill = 0;
932 else if(PassQ("tlth"))
933 tab_fill = 0;
934 else if(PassQ("tleq"))
935 tab_fill = 0;
936 else if(PassQ("tx") || PassQ("tb")) { // todo: bar tab ?
937 int pos = TwipDotSize(command_arg);
938 RichPara::Tab& tab = state.format.tab.Add();
939 tab.align = tab_align;
940 tab.fillchar = tab_fill;
941 tab.pos = pos;
942 state.format.SortTabs();
943 }
944 else if(PassQ("intbl"))
945 state.in_table = true;
946 else if(PassQ("itap")) {
947 state.itap = minmax(command_arg, 1, 10);
948 if(table_stack.GetCount() < state.itap)
949 OpenTable(state.itap);
950 }
951 }
952
ReadCharStyle()953 void RTFParser::ReadCharStyle()
954 {
955 if(PassQ("plain")) {
956 state.charformat = plain_format;
957 state.charset = plain_charset;
958 }
959 else if(PassQ("b"))
960 state.charformat.Bold(command_arg != 0);
961 else if(PassQ("i"))
962 state.charformat.Italic(command_arg != 0);
963 else if(PassQ("ul") || PassQ("uld") || PassQ("uldb")
964 || PassQ("uldash") || PassQ("uldashd") || PassQ("uldashdd")
965 || PassQ("ulth") || PassQ("ulw") || PassQ("ulwave"))
966 state.charformat.Underline(command_arg != 0);
967 else if(PassQ("ulnone"))
968 state.charformat.Underline(false);
969 else if(PassQ("strike") || PassQ("strikedl"))
970 state.charformat.Strikeout(command_arg != 0);
971 else if(PassQ("caps") || PassQ("scaps"))
972 state.charformat.capitals = (command_arg != 0);
973 else if(PassQ("super") || PassQ("up"))
974 state.charformat.sscript = 1;
975 else if(PassQ("sub") || PassQ("dn"))
976 state.charformat.sscript = 2;
977 else if(PassQ("nosupersub"))
978 state.charformat.sscript = 0;
979 else if(PassQ("f") && command_arg >= 0 && command_arg < face_table.GetCount()) {
980 LLOG("font = " << command_arg << ", face = " << face_table[command_arg].face
981 << ", charset = " << face_table[command_arg].charset);
982 state.charformat.Face(face_table[command_arg].face);
983 state.charset = face_table[command_arg].charset;
984 }
985 else if(PassQ("fs"))
986 state.charformat.Height(PointDotHeight(command_arg));
987 else if(PassQ("cf") && command_arg >= 0 && command_arg < color_table.GetCount())
988 state.charformat.ink = Nvl(color_table[command_arg], Black);
989 else if(PassQ("cb") && command_arg >= 0 && command_arg < color_table.GetCount())
990 state.charformat.paper = color_table[command_arg];
991 else if(PassQ("lang"))
992 {} // state.language = ...
993 }
994
ReadShape()995 void RTFParser::ReadShape()
996 {
997 int level = Level();
998 while(!PassEndGroup(level))
999 if(PassCmd("shppict")) {
1000 LLOG("* shppict");
1001 state.new_dest = false;
1002 ReadItemGroup();
1003 }
1004 else
1005 is_full = false;
1006 }
1007
ReadPict()1008 void RTFParser::ReadPict()
1009 {
1010 LLOG("* ReadPict");
1011 Size log_size(1, 1), out_size(1, 1), scaling(100, 100);
1012 Rect crop(0, 0, 0, 0);
1013 enum BLIPTYPE { UNK_BLIP, WMF_BLIP, PNG_BLIP, JPEG_BLIP, DIB_BLIP };
1014 BLIPTYPE blip_type = UNK_BLIP;
1015 String blip_data;
1016 char odd = 0;
1017 while(!PassEndGroup()) {
1018 if(PassText())
1019 blip_data.Cat(ReadBinHex(odd));
1020 else if(Token() == T_COMMAND) {
1021 if(PassQ("picw")) log_size.cx = minmax<int>(command_arg, 0, 30000);
1022 else if(PassQ("pich")) log_size.cy = minmax<int>(command_arg, 0, 30000);
1023 else if(PassQ("picwgoal")) out_size.cx = TwipDotSize(command_arg);
1024 else if(PassQ("pichgoal")) out_size.cy = TwipDotSize(command_arg);
1025 else if(PassQ("picscalex")) scaling.cx = minmax<int>(command_arg, 1, 1000);
1026 else if(PassQ("picscaley")) scaling.cy = minmax<int>(command_arg, 1, 1000);
1027 else if(PassQ("piccropl")) crop.left = TwipDotSize(command_arg);
1028 else if(PassQ("piccropt")) crop.top = TwipDotSize(command_arg);
1029 else if(PassQ("piccropr")) crop.right = TwipDotSize(command_arg);
1030 else if(PassQ("piccropb")) crop.bottom = TwipDotSize(command_arg);
1031 else if(PassQ("emfblip")) blip_type = WMF_BLIP;
1032 else if(PassQ("pngblip")) blip_type = PNG_BLIP;
1033 else if(PassQ("jpegblip")) blip_type = JPEG_BLIP;
1034 else if(PassQ("wmetafile")) blip_type = WMF_BLIP;
1035 else if(PassQ("dibitmap")) blip_type = DIB_BLIP;
1036 else {
1037 LLOG("Command skip " << command);
1038 Skip();
1039 }
1040 }
1041 else {
1042 LLOG("Non command skip");
1043 Skip();
1044 }
1045 }
1046 Size final_size = minmax(iscale(out_size, scaling, Size(100, 100)), Size(1, 1), Size(30000, 30000));
1047 Size drawing_size;
1048 DrawingDraw dd;
1049 RichObject object;
1050 LLOG("Pict format " << (int)blip_type << ", data size: " << blip_data.GetCount());
1051 if(blip_data.IsEmpty())
1052 return;
1053 #ifdef GUI_WIN
1054 #ifndef PLATFORM_WINCE
1055 if(blip_type == WMF_BLIP) {
1056 log_size = min(log_size, GetFitSize(log_size, final_size));
1057 dd.Create(drawing_size = log_size);
1058 WinMetaFile wmf(blip_data);
1059 wmf.Paint(dd, log_size);
1060 object = CreateDrawingObject(dd, out_size, final_size);
1061 }
1062 else
1063 #endif
1064 #endif
1065 if(blip_type == DIB_BLIP || blip_type == PNG_BLIP || blip_type == JPEG_BLIP) {
1066 Image image = StreamRaster::LoadStringAny(blip_data);
1067 LLOG("Image size: " << image.GetSize());
1068 object = CreatePNGObject(image, out_size, final_size);
1069 }
1070 if(object) {
1071 LLOG("object (" << object.GetTypeName() << ", " << object.Write().GetLength() << " B), pixel size "
1072 << object.GetPixelSize() << ", final size " << object.GetSize());
1073 para.Cat(object, state.charformat);
1074 }
1075 }
1076
ReadBinHex(char & odd) const1077 String RTFParser::ReadBinHex(char& odd) const
1078 {
1079 int t = odd;
1080 byte v = ctoi(odd);
1081 String out;
1082 for(const wchar *s = text.Begin(); *s; s++) {
1083 byte w = (*s >= '0' && *s <= '9' ? *s - '0'
1084 : *s >= 'A' && *s <= 'F' ? *s - 'A' + 10
1085 : *s >= 'a' && *s <= 'f' ? *s - 'a' + 10
1086 : 255);
1087 if(w < 16) {
1088 if(v >= 16) {
1089 t = *s;
1090 v = w;
1091 }
1092 else
1093 {
1094 out.Cat(16 * v + w);
1095 v = 255;
1096 }
1097 }
1098 }
1099 odd = (v < 16 ? t : 0);
1100 return out;
1101 }
1102
OpenTable(int level)1103 void RTFParser::OpenTable(int level)
1104 {
1105 if(table_stack.GetCount() < level) {
1106 TableState& ts = table_stack.At(level - 1);
1107 ts.stylecol = 0;
1108 // state.cellformat = std_cell_format;
1109 }
1110 }
1111
ReadCellBorder(int & width)1112 void RTFParser::ReadCellBorder(int& width)
1113 {
1114 if(Token() == T_COMMAND && !memcmp(command, "brdr", 4))
1115 is_full = false;
1116 if(PassCmd("brdrw"))
1117 width = TwipDots(command_arg);
1118 }
1119
CellAt(TableState & ts,int i)1120 RTFParser::Cell& RTFParser::CellAt(TableState& ts, int i)
1121 {
1122 Array<Cell>& top = ts.cells.Top();
1123 int p = top.GetCount();
1124 if(p <= i)
1125 top.SetCountR(i + 1);
1126 for(int n = p; n <= i; n++)
1127 top[n].info = CellInfoAt(n);
1128 return top[i];
1129 }
1130
CellInfoAt(int i)1131 RTFParser::CellInfo& RTFParser::CellInfoAt(int i)
1132 {
1133 return state.cellinfo.At(i, std_cell_info);
1134 }
1135
SetCellMargin(Cell & out,int Rect::* mbr)1136 void RTFParser::SetCellMargin(Cell& out, int Rect::*mbr)
1137 {
1138 if(out.info.cellmarginunits.*mbr == 0) {
1139 out.info.format.margin.*mbr = state.trgaph;
1140 if(state.rowmarginunits.*mbr == 3)
1141 out.info.format.margin.*mbr = state.rowmargin.*mbr;
1142 if(state.rowspacingunits.*mbr == 3)
1143 out.info.format.margin.*mbr += state.rowspacing.*mbr;
1144 }
1145 }
1146
ReadTableStyle()1147 void RTFParser::ReadTableStyle()
1148 {
1149 if(PassQ("nesttableprops")) {
1150 state.nestprop = true;
1151 return;
1152 }
1153 if(PassQ("nonesttables")) {
1154 SkipGroup();
1155 return;
1156 }
1157 int itap = (state.nestprop ? state.itap : 1);
1158 if(PassQ("trowd")) {
1159 OpenTable(itap);
1160 table_stack[itap - 1].stylecol = 0;
1161 return;
1162 }
1163 if(PassQ("row") && table_stack.GetCount() >= 1) {
1164 TableState& ts0 = table_stack[0];
1165 ts0.textcol = ts0.stylecol = 0;
1166 ts0.cells.Add();
1167 return;
1168 }
1169 if(PassQ("nestrow") && table_stack.GetCount() >= state.itap) {
1170 TableState& ts = table_stack[state.itap - 1];
1171 ts.textcol = ts.stylecol = 0;
1172 ts.cells.Add();
1173 return;
1174 }
1175 if(itap > table_stack.GetCount())
1176 return;
1177 TableState& ts = table_stack[itap - 1];
1178 if(PassQ("trgaph"))
1179 state.trgaph = TwipDotsLim(command_arg);
1180 else if(PassQ("trql")) {}
1181 else if(PassQ("trqr")) {}
1182 else if(PassQ("trqc")) {}
1183 else if(PassQ("trleft")) {
1184 ts.tableformat.lm = TwipDotsLim(command_arg);
1185 }
1186 else if(PassQ("trbrdrl")) {}
1187 else if(PassQ("trbrdrt")) {}
1188 else if(PassQ("trbrdrr")) {}
1189 else if(PassQ("trbrdrb")) {}
1190 else if(PassQ("trbrdrv")) {}
1191 else if(PassQ("trftsWidth")) {}
1192 else if(PassQ("trautofit")) {}
1193 else if(PassQ("trpaddl"))
1194 state.rowmargin.left = TwipDotsLim(command_arg);
1195 else if(PassQ("trpaddt"))
1196 state.rowmargin.top = TwipDotsLim(command_arg);
1197 else if(PassQ("trpaddr"))
1198 state.rowmargin.right = TwipDotsLim(command_arg);
1199 else if(PassQ("trpaddb"))
1200 state.rowmargin.bottom = TwipDotsLim(command_arg);
1201 else if(PassQ("trpaddfl"))
1202 state.rowmarginunits.left = command_arg;
1203 else if(PassQ("trpaddft"))
1204 state.rowmarginunits.top = command_arg;
1205 else if(PassQ("trpaddfr"))
1206 state.rowmarginunits.right = command_arg;
1207 else if(PassQ("trpaddfb"))
1208 state.rowmarginunits.bottom = command_arg;
1209 else if(PassQ("trspdl"))
1210 state.rowspacing.left = TwipDotsLim(command_arg);
1211 else if(PassQ("trspdt"))
1212 state.rowspacing.top = TwipDotsLim(command_arg);
1213 else if(PassQ("trspdr"))
1214 state.rowspacing.right = TwipDotsLim(command_arg);
1215 else if(PassQ("trspdb"))
1216 state.rowspacing.bottom = TwipDotsLim(command_arg);
1217 else if(PassQ("trspdfl"))
1218 state.rowspacingunits.left = command_arg;
1219 else if(PassQ("trspdft"))
1220 state.rowspacingunits.top = command_arg;
1221 else if(PassQ("trspdfr"))
1222 state.rowspacingunits.right = command_arg;
1223 else if(PassQ("trspdfb"))
1224 state.rowspacingunits.bottom = command_arg;
1225 else if(PassQ("clpadl"))
1226 CellInfoAt(ts.stylecol).format.margin.left = TwipDotsLim(command_arg);
1227 else if(PassQ("clpadt"))
1228 CellInfoAt(ts.stylecol).format.margin.top = TwipDotsLim(command_arg);
1229 else if(PassQ("clpadr"))
1230 CellInfoAt(ts.stylecol).format.margin.right = TwipDotsLim(command_arg);
1231 else if(PassQ("clpadb"))
1232 CellInfoAt(ts.stylecol).format.margin.bottom = TwipDotsLim(command_arg);
1233 else if(PassQ("clpadfl"))
1234 state.cellmarginunits.left = command_arg;
1235 else if(PassQ("clpadft"))
1236 state.cellmarginunits.top = command_arg;
1237 else if(PassQ("clpadfr"))
1238 state.cellmarginunits.right = command_arg;
1239 else if(PassQ("clpadfb"))
1240 state.cellmarginunits.bottom = command_arg;
1241 else if(PassQ("clbrdrl"))
1242 ReadCellBorder(CellInfoAt(ts.stylecol).format.border.left);
1243 else if(PassQ("clbrdrt"))
1244 ReadCellBorder(CellInfoAt(ts.stylecol).format.border.top);
1245 else if(PassQ("clbrdrr"))
1246 ReadCellBorder(CellInfoAt(ts.stylecol).format.border.right);
1247 else if(PassQ("clbrdrb"))
1248 ReadCellBorder(CellInfoAt(ts.stylecol).format.border.bottom);
1249 else if(PassQ("cltxlrtb")) {}
1250 else if(PassQ("clshdng"))
1251 CellInfoAt(ts.stylecol).shading = command_arg;
1252 else if(PassQ("clcbpat")) {
1253 if(command_arg >= 0 && command_arg < color_table.GetCount())
1254 CellInfoAt(ts.stylecol).format.color = color_table[command_arg];
1255 }
1256 else if(PassQ("clvmrg"))
1257 CellAt(ts, ts.stylecol).merge = true;
1258 else if(PassQ("clvmgf"))
1259 CellAt(ts, ts.stylecol).merge_first = true;
1260 else if(PassQ("clftsWidth")) {}
1261 else if(PassQ("clwWidth")) {}
1262 else if(PassQ("cellx")) {
1263 int sx = ts.stylecol++;
1264 Cell& newcell = CellAt(ts, sx);
1265 newcell.info.end_dots = TwipDotsLim(command_arg);
1266 SetCellMargin(newcell, &Rect::left);
1267 SetCellMargin(newcell, &Rect::top);
1268 SetCellMargin(newcell, &Rect::right);
1269 SetCellMargin(newcell, &Rect::bottom);
1270 CellInfoAt(sx) = newcell.info;
1271 //CellFormat(sx) = std_cell_format;
1272 }
1273 else if(PassQ("clvertalt"))
1274 CellInfoAt(ts.stylecol).format.align = ALIGN_TOP;
1275 else if(PassQ("clvertalc"))
1276 CellInfoAt(ts.stylecol).format.align = ALIGN_CENTER;
1277 else if(PassQ("clvertalb"))
1278 CellInfoAt(ts.stylecol).format.align = ALIGN_BOTTOM;
1279 }
1280
1281 }
1282