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