mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
1300 lines
34 KiB
C++
1300 lines
34 KiB
C++
#include "CtrlCore.h"
|
|
|
|
namespace Upp {
|
|
|
|
#define LLOG(x) // DLOG(x)
|
|
|
|
static int TwipDotsLim(int twips) { return minmax<int>(TwipDots(twips), 0, MAX_DOTS); }
|
|
|
|
static String FromCString(const char *p, const char **endptr = NULL)
|
|
{
|
|
if(endptr) {
|
|
const char *e = p;
|
|
if(*e == '\"')
|
|
e++;
|
|
while(*e && *e != '\"')
|
|
if(*e++ == '\\' && *e)
|
|
e++;
|
|
if(*e == '\"')
|
|
e++;
|
|
*endptr = e;
|
|
}
|
|
|
|
try {
|
|
CParser parser(p);
|
|
return parser.ReadOneString();
|
|
}
|
|
catch(Exc e) {
|
|
return Null;
|
|
}
|
|
}
|
|
|
|
class RTFParser
|
|
{
|
|
public:
|
|
RTFParser(const char *rtf);
|
|
|
|
RichText Run();
|
|
|
|
private:
|
|
enum TOKEN { T_EOF, T_TEXT, T_COMMAND, T_GROUP, T_END_GROUP };
|
|
|
|
void Flush(bool force, int itap);
|
|
void OpenTable(int level);
|
|
void FlushTable(int level);
|
|
TOKEN Fetch();
|
|
void Skip();
|
|
TOKEN Token() { if(!is_full) Fetch(); return token; }
|
|
bool PassIf(bool c) { is_full &= !c; return c; }
|
|
bool PassText() { return PassIf(Token() == T_TEXT); }
|
|
bool PassGroup() { return PassIf(Token() == T_GROUP); }
|
|
bool PassEndGroup() { return PassIf(Token() == T_END_GROUP || token == T_EOF); }
|
|
bool PassEndGroup(int level);
|
|
bool PassCmd(const char *cmd) { return PassIf(Token() == T_COMMAND && command == cmd); }
|
|
bool PassQ(const char *cmd) { return PassIf(command == cmd); }
|
|
|
|
void SkipGroup() { SkipGroup(stack.GetCount()); }
|
|
void SkipGroup(int level);
|
|
int Level() const { return stack.GetCount(); }
|
|
|
|
void ReadItem();
|
|
void ReadItemGroup(int level);
|
|
void ReadItemGroup() { ReadItemGroup(Level()); }
|
|
void ReadText();
|
|
void ReadText(const WString& text);
|
|
void ReadChar(word ch) { ReadText(WString(ch, 1)); }
|
|
void ReadCommand();
|
|
|
|
void ReadHeader();
|
|
void ReadFaceTable();
|
|
void ReadColorTable();
|
|
void ReadCharSet();
|
|
|
|
void ReadMisc();
|
|
void ReadField();
|
|
bool ReadField(const char *def);
|
|
void ReadPict();
|
|
void ReadShape();
|
|
|
|
void ReadParaStyle();
|
|
void ReadTableStyle();
|
|
void DefaultParaStyle();
|
|
|
|
void ReadCharStyle();
|
|
void ReadCellBorder(int& width);
|
|
|
|
String ReadBinHex(char& odd) const;
|
|
|
|
private:
|
|
const char *rtf;
|
|
|
|
TOKEN token;
|
|
bool is_full;
|
|
bool next_command;
|
|
WString text;
|
|
String command;
|
|
int command_arg;
|
|
|
|
struct CellInfo {
|
|
CellInfo();
|
|
|
|
RichCell::Format format;
|
|
Rect cellmarginunits;
|
|
int shading;
|
|
Color shading_fore;
|
|
Color shading_back;
|
|
int end_dots;
|
|
};
|
|
|
|
struct Cell {
|
|
Cell();
|
|
|
|
CellInfo info;
|
|
RichTxt text;
|
|
bool merge_first;
|
|
bool merge;
|
|
int nbegin;
|
|
Size span;
|
|
};
|
|
|
|
struct Face : Moveable<Face> {
|
|
int face;
|
|
byte charset;
|
|
};
|
|
|
|
struct TableState {
|
|
TableState() : textcol(0), stylecol(0) { cells.Add(); }
|
|
|
|
RichTable::Format tableformat;
|
|
Vector< Array<Cell> > cells;
|
|
int textcol;
|
|
int stylecol;
|
|
};
|
|
|
|
CellInfo& CellInfoAt(int i);
|
|
Cell& CellAt(TableState& ts, int i);
|
|
void SetCellMargin(Cell& cell, int Rect::*mbr);
|
|
|
|
struct State {
|
|
String dest;
|
|
RichPara::Format format;
|
|
RichPara::CharFormat charformat;
|
|
WithDeepCopy< Array<CellInfo> > cellinfo;
|
|
int trgaph;
|
|
Rect rowmargin;
|
|
Rect rowmarginunits;
|
|
Rect rowspacing;
|
|
Rect rowspacingunits;
|
|
Rect cellmarginunits;
|
|
int uc_value;
|
|
int left_margin;
|
|
int right_margin;
|
|
int first_indent;
|
|
bool in_table;
|
|
int itap;
|
|
bool nestprop;
|
|
bool new_dest;
|
|
byte charset;
|
|
};
|
|
|
|
Array<State> stack;
|
|
Array<TableState> table_stack;
|
|
State state;
|
|
RichPara::CharFormat plain_format;
|
|
RichPara::Format pard_format;
|
|
CellInfo std_cell_info;
|
|
byte plain_charset;
|
|
byte default_charset;
|
|
int default_font;
|
|
Alignment tab_align;
|
|
byte tab_fill;
|
|
Vector<Face> face_table;
|
|
Vector<Color> color_table;
|
|
int paper_width;
|
|
int left_margin;
|
|
int right_margin;
|
|
|
|
RichText output;
|
|
RichPara para;
|
|
};
|
|
|
|
RichText ParseRTF(const char *rtf) { return RTFParser(rtf).Run(); }
|
|
|
|
RTFParser::CellInfo::CellInfo()
|
|
: cellmarginunits(0, 0, 0, 0)
|
|
, shading(0)
|
|
, shading_fore(Black())
|
|
, shading_back(White())
|
|
, end_dots(0)
|
|
{
|
|
}
|
|
|
|
RTFParser::Cell::Cell()
|
|
: merge_first(false)
|
|
, merge(false)
|
|
, nbegin(0)
|
|
, span(0, 0)
|
|
{
|
|
}
|
|
|
|
RTFParser::RTFParser(const char *rtf)
|
|
: rtf(rtf)
|
|
{
|
|
#ifdef _DEBUG
|
|
_DBG_
|
|
SaveFile(ConfigFile("rtfparser.rtf"), rtf);
|
|
LOG(rtf);
|
|
#endif
|
|
is_full = false;
|
|
next_command = false;
|
|
default_font = 0;
|
|
plain_charset = default_charset = state.charset = CHARSET_WIN1250;
|
|
state.uc_value = 1;
|
|
state.new_dest = false;
|
|
plain_format.Face(Font::ARIAL).Height(100);
|
|
std_cell_info.format.align = ALIGN_TOP;
|
|
std_cell_info.format.margin = Rect(25, 25, 25, 25);
|
|
DefaultParaStyle();
|
|
state.charformat = plain_format;
|
|
tab_align = ALIGN_LEFT;
|
|
tab_fill = 0;
|
|
paper_width = 5100;
|
|
left_margin = right_margin = 750;
|
|
}
|
|
|
|
RichText RTFParser::Run()
|
|
{
|
|
if(!PassGroup() || !PassCmd("rtf") || command_arg != 1 && !IsNull(command_arg))
|
|
return pick(output);
|
|
while(Token() != T_EOF)
|
|
ReadItem();
|
|
Flush(false, 1);
|
|
FlushTable(0);
|
|
return pick(output);
|
|
}
|
|
|
|
void RTFParser::FlushTable(int level)
|
|
{
|
|
while(table_stack.GetCount() > level) {
|
|
TableState& child = table_stack.Top();
|
|
while(!child.cells.IsEmpty() && child.cells.Top().IsEmpty())
|
|
child.cells.Drop();
|
|
if(child.cells.IsEmpty()) {
|
|
table_stack.Drop();
|
|
continue;
|
|
}
|
|
Index<int> dot_index;
|
|
// int pos = child.tableformat.lm;
|
|
dot_index.Add(child.tableformat.lm);
|
|
for(int r = 0; r < child.cells.GetCount(); r++) {
|
|
Array<Cell>& rw = child.cells[r];
|
|
for(int c = 0; c < rw.GetCount(); c++)
|
|
dot_index.FindAdd(rw[c].info.end_dots);
|
|
}
|
|
Vector<int> dot_order = dot_index.PickKeys();
|
|
Sort(dot_order);
|
|
RichTable table;
|
|
if(table_stack.GetCount() == 1)
|
|
child.tableformat.rm = max(paper_width - left_margin - right_margin - dot_order.Top(), 0);
|
|
// child.tableformat.before = state.format.before;
|
|
// child.tableformat.after = state.format.after;
|
|
table.SetFormat(child.tableformat);
|
|
for(int c = 1; c < dot_order.GetCount(); c++)
|
|
table.AddColumn(dot_order[c] - dot_order[c - 1]);
|
|
dot_index = pick(dot_order);
|
|
int tbl_border = Null, tbl_grid = Null;
|
|
Color clr_border = Null, clr_grid = Null;
|
|
for(int r = 0; r < child.cells.GetCount(); r++) {
|
|
Array<Cell>& rw = child.cells[r];
|
|
int pos = child.tableformat.lm;
|
|
for(int c = 0; c < rw.GetCount(); c++) {
|
|
Cell& cell = rw[c];
|
|
if(cell.merge) {
|
|
pos = cell.info.end_dots;
|
|
continue;
|
|
}
|
|
cell.span.cy = 0;
|
|
if(cell.merge_first) {
|
|
for(int m = r + 1; m < child.cells.GetCount(); m++) {
|
|
const Array<Cell>& mrw = child.cells[m];
|
|
int mc = mrw.GetCount();
|
|
while(--mc >= 0 && mrw[mc].info.end_dots > cell.info.end_dots)
|
|
;
|
|
if(mc >= 0 && mrw[mc].info.end_dots == cell.info.end_dots && mrw[mc].merge)
|
|
cell.span.cy++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
cell.nbegin = dot_index.Find(pos);
|
|
cell.span.cx = max(0, dot_index.Find(pos = cell.info.end_dots) - cell.nbegin - 1);
|
|
if(cell.span.cx < 0) {
|
|
cell.merge = true;
|
|
continue;
|
|
}
|
|
bool outer_border[] = {
|
|
cell.nbegin == 0,
|
|
r == 0,
|
|
cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(),
|
|
r + cell.span.cy + 1 >= child.cells.GetCount(),
|
|
};
|
|
int border_width[] = {
|
|
cell.info.format.border.left,
|
|
cell.info.format.border.top,
|
|
cell.info.format.border.right,
|
|
cell.info.format.border.bottom,
|
|
};
|
|
for(int b = 0; b < __countof(border_width); b++) {
|
|
int& out_wd = (outer_border[b] ? tbl_border : tbl_grid);
|
|
Color& out_co = (outer_border[b] ? clr_border : clr_grid);
|
|
if(IsNull(cell.info.format.bordercolor) || border_width[b] <= 0
|
|
|| !IsNull(out_co) && out_co != cell.info.format.bordercolor)
|
|
out_wd = 0;
|
|
else if(IsNull(out_wd) || border_width[b] < out_wd) {
|
|
out_wd = border_width[b];
|
|
out_co = cell.info.format.bordercolor;
|
|
}
|
|
}
|
|
if(cell.info.shading > 0) {
|
|
Color zero = White();
|
|
Color one = Nvl(cell.info.shading_fore, Black());
|
|
int r = zero.GetR() + iscale(one.GetR() - zero.GetR(), cell.info.shading, 10000);
|
|
int g = zero.GetG() + iscale(one.GetG() - zero.GetG(), cell.info.shading, 10000);
|
|
int b = zero.GetB() + iscale(one.GetB() - zero.GetB(), cell.info.shading, 10000);
|
|
cell.info.format.color = Color(r, g, b);
|
|
}
|
|
}
|
|
}
|
|
RichTable::Format tf = table.GetFormat();
|
|
tf.frame = Nvl(tbl_border, 0);
|
|
tf.framecolor = (tf.frame > 0 ? clr_border : Color(Null));
|
|
tf.grid = Nvl(tbl_grid, 0);
|
|
tf.gridcolor = (tf.grid > 0 ? clr_grid : Color(Null));
|
|
table.SetFormat(tf);
|
|
for(int r = 0; r < child.cells.GetCount(); r++) {
|
|
Array<Cell>& rw = child.cells[r];
|
|
// int pos = child.tableformat.lm;
|
|
for(int c = 0; c < rw.GetCount(); c++) {
|
|
Cell& cell = rw[c];
|
|
if(cell.merge)
|
|
continue;
|
|
if(cell.span.cx || cell.span.cy)
|
|
table.SetSpan(r, cell.nbegin, cell.span.cy, cell.span.cx);
|
|
bool outer_border[] = {
|
|
cell.nbegin == 0,
|
|
r == 0,
|
|
cell.nbegin + cell.span.cx + 2 >= dot_index.GetCount(),
|
|
r + cell.span.cy + 1 >= child.cells.GetCount(),
|
|
};
|
|
int *border_width[] = {
|
|
&cell.info.format.border.left,
|
|
&cell.info.format.border.top,
|
|
&cell.info.format.border.right,
|
|
&cell.info.format.border.bottom,
|
|
};
|
|
for(int b = 0; b < __countof(border_width); b++) {
|
|
int tbl_wd = (outer_border[b] ? tbl_border : tbl_grid);
|
|
// Color tbl_co = (outer_border[b] ? clr_border : clr_grid);
|
|
if(*border_width[b] <= tbl_wd)
|
|
*border_width[b] = 0;
|
|
}
|
|
table.SetFormat(r, cell.nbegin, cell.info.format);
|
|
cell.text.Normalize();
|
|
table.SetPick(r, cell.nbegin, pick(cell.text));
|
|
}
|
|
}
|
|
table.Normalize();
|
|
table_stack.Drop();
|
|
if(table_stack.IsEmpty())
|
|
output.CatPick(pick(table));
|
|
else {
|
|
TableState& par = table_stack.Top();
|
|
CellAt(par, par.textcol).text.CatPick(pick(table));
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFParser::Flush(bool force, int itap)
|
|
{
|
|
if(!para.part.IsEmpty() || force) {
|
|
int fi = state.first_indent, li = state.left_margin, ri = state.right_margin;
|
|
if(state.format.bullet != RichPara::BULLET_NONE) {
|
|
Swap(li, fi);
|
|
// li += fi;
|
|
// fi = -fi;
|
|
}
|
|
state.format.indent = minmax<int>(fi, 0, MAX_DOTS);
|
|
state.format.lm = minmax<int>(li, 0, MAX_DOTS);
|
|
state.format.rm = minmax<int>(ri, 0, MAX_DOTS);
|
|
para.format = state.format;
|
|
if(state.in_table) {
|
|
FlushTable(itap);
|
|
OpenTable(itap);
|
|
TableState& ts = table_stack[itap - 1];
|
|
CellAt(ts, ts.textcol).text.Cat(para, output.GetStyles());
|
|
}
|
|
else {
|
|
FlushTable(0);
|
|
output.Cat(para);
|
|
}
|
|
para.part.Clear();
|
|
}
|
|
else
|
|
FlushTable(itap);
|
|
}
|
|
|
|
RTFParser::TOKEN RTFParser::Fetch()
|
|
{
|
|
is_full = true;
|
|
text.Clear();
|
|
if(next_command)
|
|
{
|
|
next_command = false;
|
|
return token = T_COMMAND;
|
|
}
|
|
|
|
command = Null;
|
|
command_arg = Null;
|
|
|
|
int skip = 0;
|
|
while(*rtf && *rtf != '{' && *rtf != '}')
|
|
{
|
|
int c = 0, nskip = max(skip - 1, 0);
|
|
if((byte)*rtf < ' ')
|
|
rtf++;
|
|
else if(*rtf != '\\')
|
|
c = ToUnicode(*rtf++, state.charset);
|
|
else
|
|
switch(rtf++, *rtf++)
|
|
{
|
|
case 0: {
|
|
rtf--;
|
|
break;
|
|
}
|
|
|
|
case '{':
|
|
case '}':
|
|
case '\\': {
|
|
c = rtf[-1];
|
|
break;
|
|
}
|
|
|
|
case '~': {
|
|
c = 160;
|
|
break;
|
|
}
|
|
|
|
case '|':
|
|
case '-':
|
|
case '_':
|
|
case ':': {
|
|
command = String(rtf - 1, 1);
|
|
LLOG("Command " << command);
|
|
if(text.IsEmpty())
|
|
return token = T_COMMAND;
|
|
next_command = true;
|
|
return token = T_TEXT;
|
|
}
|
|
|
|
case '\'': {
|
|
int c1 = ctoi(*rtf);
|
|
if(c1 < 16) {
|
|
int c2 = ctoi(*++rtf);
|
|
if(c2 < 16) {
|
|
c1 = c1 * 16 + c2;
|
|
rtf++;
|
|
}
|
|
c = ToUnicode(c1, state.charset);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
if(IsAlpha(*--rtf) || *rtf == '*' && rtf[1] == '\\' && IsAlpha(rtf[2])) {
|
|
if(*rtf == '*') {
|
|
rtf += 2;
|
|
state.new_dest = true;
|
|
LLOG("NewDest");
|
|
}
|
|
const char *b = rtf;
|
|
while(IsAlpha(*++rtf))
|
|
;
|
|
command = String(b, rtf);
|
|
if(IsDigit(*rtf) || *rtf == '-')
|
|
command_arg = strtol(rtf, (char **)&rtf, 10);
|
|
if(*rtf == ' ')
|
|
rtf++;
|
|
if(command == "uc")
|
|
state.uc_value = command_arg;
|
|
else if(command == "u") {
|
|
c = command_arg;
|
|
nskip = state.uc_value;
|
|
}
|
|
else { // command - quit reading text
|
|
LLOG("Command " << command);
|
|
if(text.IsEmpty())
|
|
return token = T_COMMAND;
|
|
next_command = true;
|
|
return token = T_TEXT;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(c && !skip) {
|
|
text.Cat(c);
|
|
if(text.GetCount() >= 2) {
|
|
char16 h[2];
|
|
h[0] = text[text.GetCount() - 2];
|
|
h[1] = text[text.GetCount() - 1];
|
|
wchar c = ReadSurrogatePair(h, h + 2);
|
|
if(c) {
|
|
text.TrimLast(2);
|
|
text.Cat(c);
|
|
}
|
|
}
|
|
}
|
|
skip = nskip;
|
|
}
|
|
|
|
if(!text.IsEmpty()) {
|
|
LLOG("TEXT, size: " << text.GetCount());
|
|
return token = T_TEXT;
|
|
}
|
|
|
|
if(*rtf == '{') {
|
|
stack.Add(state);
|
|
rtf++;
|
|
return token = T_GROUP;
|
|
}
|
|
|
|
if(*rtf == '}') {
|
|
if(!stack.IsEmpty()) {
|
|
state = stack.Top();
|
|
stack.Drop();
|
|
}
|
|
rtf++;
|
|
return token = T_END_GROUP;
|
|
}
|
|
|
|
return token = T_EOF;
|
|
}
|
|
|
|
bool RTFParser::PassEndGroup(int level)
|
|
{
|
|
if(Token() == T_EOF)
|
|
return true;
|
|
if(PassQ("pict")) { // insert anchored images too
|
|
Flush(false, 1);
|
|
ReadPict();
|
|
Flush(false, 1);
|
|
para.part.Clear();
|
|
output.Cat(para);
|
|
}
|
|
if(token != T_END_GROUP)
|
|
return false;
|
|
is_full = false;
|
|
return Level() < level;
|
|
}
|
|
|
|
void RTFParser::Skip()
|
|
{
|
|
LLOG("Skip");
|
|
bool is_group = (token == T_GROUP || token == T_COMMAND && state.new_dest);
|
|
is_full = false;
|
|
if(is_group)
|
|
SkipGroup();
|
|
}
|
|
|
|
void RTFParser::SkipGroup(int level)
|
|
{
|
|
while(!PassEndGroup(level))
|
|
is_full = false;
|
|
}
|
|
|
|
void RTFParser::ReadItem()
|
|
{
|
|
const char *p = rtf;
|
|
if(token == T_COMMAND)
|
|
ReadCommand();
|
|
else if(token == T_TEXT)
|
|
ReadText();
|
|
if(rtf == p && is_full) {
|
|
is_full = false;
|
|
if(token == T_COMMAND && state.new_dest && command != "shppict") {
|
|
LLOG("SkipGroup new_dest " << command);
|
|
SkipGroup();
|
|
}
|
|
state.new_dest = false;
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadItemGroup(int level)
|
|
{
|
|
while(!PassEndGroup(level))
|
|
ReadItem();
|
|
}
|
|
|
|
void RTFParser::ReadText()
|
|
{
|
|
if(!IsNull(text))
|
|
ReadText(text);
|
|
}
|
|
|
|
void RTFParser::ReadText(const WString& text)
|
|
{
|
|
if(!IsNull(state.dest))
|
|
return;
|
|
LLOG("Output text: <" << FromUnicode(text, state.charset) << ">, " << state.charformat);
|
|
para.Cat(text, state.charformat);
|
|
}
|
|
|
|
void RTFParser::ReadCommand()
|
|
{
|
|
if(Token() == T_COMMAND) ReadHeader();
|
|
if(Token() == T_COMMAND) ReadMisc();
|
|
if(Token() == T_COMMAND) ReadParaStyle();
|
|
if(Token() == T_COMMAND) ReadTableStyle();
|
|
if(Token() == T_COMMAND) ReadCharStyle();
|
|
}
|
|
|
|
void RTFParser::ReadHeader()
|
|
{
|
|
if(PassCmd("deff"))
|
|
default_font = command_arg;
|
|
else if(PassQ("fonttbl")) {
|
|
state.dest = command;
|
|
ReadFaceTable();
|
|
}
|
|
else if(PassQ("colortbl")) {
|
|
state.dest = command;
|
|
ReadColorTable();
|
|
}
|
|
else if(PassQ("stylesheet") || PassQ("list") || PassQ("listoverride") || PassQ("info")) {
|
|
state.dest = command;
|
|
SkipGroup();
|
|
}
|
|
else if(Token() == T_COMMAND)
|
|
ReadCharSet();
|
|
}
|
|
|
|
void RTFParser::ReadCharSet()
|
|
{
|
|
if(PassQ("ansi")) {}
|
|
else if(PassQ("mac")) {}
|
|
else if(PassQ("pc")) {}
|
|
else if(PassQ("pca")) {}
|
|
else if(PassQ("ansicpg")) {
|
|
static const struct {
|
|
int ansicpg;
|
|
byte charset;
|
|
}
|
|
charsets[] =
|
|
{
|
|
{ 1250, CHARSET_WIN1250 },
|
|
{ 1251, CHARSET_WIN1251 },
|
|
{ 1252, CHARSET_WIN1252 },
|
|
{ 1253, CHARSET_WIN1253 },
|
|
{ 1254, CHARSET_WIN1254 },
|
|
{ 1255, CHARSET_WIN1255 },
|
|
{ 1256, CHARSET_WIN1256 },
|
|
{ 1257, CHARSET_WIN1257 },
|
|
};
|
|
for(int c = 0; c < __countof(charsets); c++)
|
|
if(charsets[c].ansicpg == command_arg) {
|
|
default_charset = state.charset = charsets[c].charset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadFaceTable()
|
|
{
|
|
int fx = 0;
|
|
while(!PassEndGroup()) {
|
|
if(!PassGroup()) {
|
|
Skip();
|
|
continue;
|
|
}
|
|
Face n;
|
|
n.face = Font::ARIAL;
|
|
n.charset = default_charset;
|
|
String facename;
|
|
while(!PassEndGroup()) {
|
|
if(PassCmd("f"))
|
|
fx = command_arg;
|
|
else if(PassCmd("fnil"))
|
|
;
|
|
else if(PassCmd("froman"))
|
|
n.face = Font::ROMAN;
|
|
else if(PassCmd("fswiss"))
|
|
n.face = Font::ARIAL;
|
|
else if(PassCmd("fmodern"))
|
|
n.face = Font::ARIAL;
|
|
else if(PassCmd("ftech"))
|
|
#ifdef PLATFORM_WIN32
|
|
n.face = Font::SYMBOL;
|
|
#else
|
|
n.face = Font::ARIAL;
|
|
#endif
|
|
else if(PassCmd("fcharset")) {
|
|
switch(command_arg) {
|
|
case 0: n.charset = CHARSET_WIN1252; break; // ANSI
|
|
case 1: n.charset = default_charset; break; // Default
|
|
case 2: n.charset = CHARSET_WIN1252; break; // Symbol
|
|
case 3: break; // Invalid
|
|
case 77: break; // Mac
|
|
case 128: break; // Shift Jis
|
|
case 129: break; // Hangul
|
|
case 130: break; // Johab
|
|
case 134: break; // GB2312
|
|
case 136: break; // Big5
|
|
case 161: n.charset = CHARSET_WIN1253; break; // Greek
|
|
case 162: n.charset = CHARSET_WIN1254; break; // Turkish
|
|
case 163: break; // Vietnamese
|
|
case 177: n.charset = CHARSET_WIN1255; break; // Hebrew
|
|
case 178: break; // Arabic
|
|
case 179: break; // Arabic Traditional
|
|
case 180: break; // Arabic user
|
|
case 181: break; // Hebrew user
|
|
case 186: break; // Baltic
|
|
case 204: n.charset = CHARSET_WIN1251; break; // Russian
|
|
case 222: break; // Thai
|
|
case 238: n.charset = CHARSET_WIN1250; break; // Eastern European
|
|
case 254: break; // PC 437
|
|
case 255: n.charset = CHARSET_WIN1252; break; // OEM
|
|
}
|
|
}
|
|
else if(PassText())
|
|
facename = text.ToString();
|
|
/*
|
|
else if(PassGroup()) {
|
|
int level = Level();
|
|
if(PassCmd("falt") && PassText() && f < 0)
|
|
f = Font::FindFaceNameIndex(FromUnicode(text, charset));
|
|
SkipGroup(level);
|
|
}*/ //Cxl 2005-11-29 - "Arial CE" makes mess here!
|
|
else
|
|
Skip();
|
|
}
|
|
if(fx >= 0 && fx < MAX_FONTS) {
|
|
facename.TrimEnd(";");
|
|
int fi = Font::FindFaceNameIndex(facename);
|
|
if(fi >= 0 && (Font::GetFaceInfo(fi) & Font::FIXEDPITCH))
|
|
n.face = Font::MONOSPACE;
|
|
if(default_font == fx) {
|
|
plain_format.Face(n.face);
|
|
plain_charset = n.charset;
|
|
}
|
|
Face dflt;
|
|
dflt.face = Font::ARIAL;
|
|
dflt.charset = default_charset;
|
|
face_table.At(fx++, dflt) = n;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadColorTable()
|
|
{
|
|
int r = Null, g = Null, b = Null;
|
|
for(; !PassEndGroup(); Skip())
|
|
if(PassCmd("red"))
|
|
r = command_arg;
|
|
else if(PassCmd("green"))
|
|
g = command_arg;
|
|
else if(PassCmd("blue"))
|
|
b = command_arg;
|
|
else if(PassText())
|
|
{
|
|
Color c = Null;
|
|
if(!IsNull(r) || !IsNull(g) || !IsNull(b))
|
|
c = Color(Nvl(r, 0), Nvl(g, 0), Nvl(b, 0));
|
|
color_table.Add(c);
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadMisc()
|
|
{
|
|
if(PassQ("field"))
|
|
ReadField();
|
|
else if(PassQ("nonshppict"))
|
|
SkipGroup();
|
|
else if(PassQ("pict"))
|
|
ReadPict();
|
|
else if(PassQ("shpinst"))
|
|
ReadShape();
|
|
else if(PassQ("endash"))
|
|
ReadChar(0x2013);
|
|
else if(PassQ("emdash"))
|
|
ReadChar(0x2014);
|
|
else if(PassQ("tab"))
|
|
ReadText(WString(9, 1));
|
|
else if(PassQ("enspace"))
|
|
ReadText(WString(" ")); // todo
|
|
else if(PassQ("emspace"))
|
|
ReadText(WString(" ")); // todo
|
|
else if(PassQ("bullet"))
|
|
ReadChar(0x2022);
|
|
else if(PassQ("lquote"))
|
|
ReadChar(0x2018);
|
|
else if(PassQ("rquote"))
|
|
ReadChar(0x2019);
|
|
else if(PassQ("ldblquote"))
|
|
ReadChar(0x201C);
|
|
else if(PassQ("rdblquote"))
|
|
ReadChar(0x201D);
|
|
}
|
|
|
|
void RTFParser::ReadField()
|
|
{
|
|
bool ign_rslt = false;
|
|
int level = Level();
|
|
while(!PassEndGroup(level))
|
|
if(PassGroup() && Level() == level + 1) {
|
|
if(PassCmd("fldinst")) {
|
|
WString source;
|
|
for(; !PassEndGroup(); Skip())
|
|
if(PassText())
|
|
source.Cat(text);
|
|
if(ReadField(FromUnicode(source, state.charset)))
|
|
ign_rslt = true;
|
|
continue;
|
|
}
|
|
else if(PassCmd("fldrslt")) {
|
|
if(!ign_rslt)
|
|
ReadItemGroup();
|
|
}
|
|
}
|
|
else
|
|
Skip();
|
|
}
|
|
|
|
bool RTFParser::ReadField(const char *p)
|
|
{
|
|
Index<String> symdef;
|
|
while(*p)
|
|
if((byte)*p <= ' ')
|
|
p++;
|
|
else if(*p == '\"')
|
|
symdef.Add(FromCString(p, &p));
|
|
else {
|
|
const char *b = p;
|
|
while(*++p && *p != ' ')
|
|
;
|
|
symdef.Add(String(b, p));
|
|
}
|
|
if(symdef.IsEmpty())
|
|
return false;
|
|
if(symdef[0] == "SYMBOL" && symdef.GetCount() >= 2 && IsDigit(*symdef[1])) {
|
|
int code = atoi(symdef[1]);
|
|
int face = -1;
|
|
int height = 0;
|
|
int f = symdef.Find("\\f");
|
|
if(f >= 0 && f + 1 < symdef.GetCount())
|
|
face = Font::FindFaceNameIndex(symdef[f + 1]);
|
|
f = symdef.Find("\\s");
|
|
if(f >= 0 && f + 1 < symdef.GetCount())
|
|
height = PointDots(fround(2 * Atof(symdef[f + 1]))) >> 1;
|
|
if(face < 0)
|
|
#ifdef PLATFORM_WIN32
|
|
face = Font::SYMBOL;
|
|
#else
|
|
face = Font::ARIAL;
|
|
#endif
|
|
if(height <= 0 || height >= MAX_DOT_HEIGHT)
|
|
height = state.charformat.GetHeight();
|
|
if(code >= 0 && code < 255) {
|
|
state.charformat.Face(face).Height(height);
|
|
ReadText(WString(ToUnicode(code, state.charset), 1));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void RTFParser::DefaultParaStyle()
|
|
{
|
|
state.format = pard_format;
|
|
state.first_indent = state.left_margin = state.right_margin = 0;
|
|
// state.cellformat = std_cell_format;
|
|
state.in_table = false;
|
|
state.itap = 1;
|
|
state.nestprop = false;
|
|
state.trgaph = 2;
|
|
state.rowmargin = Rect(25, 25, 25, 25);
|
|
state.cellmarginunits = state.rowmarginunits = Rect(0, 0, 0, 0);
|
|
state.rowspacing = Rect(0, 0, 0, 0);
|
|
state.rowspacingunits = Rect(0, 0, 0, 0);
|
|
state.charset = plain_charset;
|
|
}
|
|
|
|
void RTFParser::ReadParaStyle()
|
|
{
|
|
if(PassQ("par"))
|
|
Flush(true, state.itap);
|
|
else if(PassQ("cell")) {
|
|
Flush(false, 1);
|
|
if(!table_stack.IsEmpty())
|
|
table_stack[0].textcol++;
|
|
}
|
|
else if(PassQ("nestcell")) {
|
|
Flush(false, state.itap);
|
|
if(state.itap <= table_stack.GetCount())
|
|
table_stack[state.itap - 1].textcol++;
|
|
}
|
|
else if(PassQ("pard"))
|
|
DefaultParaStyle();
|
|
else if(PassQ("pntext"))
|
|
SkipGroup();
|
|
else if(PassQ("pn")) {
|
|
SkipGroup();
|
|
state.format.bullet = RichPara::BULLET_ROUND;
|
|
}
|
|
else if(PassQ("pagebb"))
|
|
state.format.newpage = (command_arg != 0);
|
|
else if(PassQ("ql"))
|
|
state.format.align = ALIGN_LEFT;
|
|
else if(PassQ("qc"))
|
|
state.format.align = ALIGN_CENTER;
|
|
else if(PassQ("qr"))
|
|
state.format.align = ALIGN_RIGHT;
|
|
else if(PassQ("qj"))
|
|
state.format.align = ALIGN_JUSTIFY;
|
|
else if(PassQ("fi"))
|
|
state.first_indent = TwipDotsLim(command_arg);
|
|
else if(PassQ("li"))
|
|
state.left_margin = TwipDotsLim(command_arg);
|
|
else if(PassQ("ri"))
|
|
state.right_margin = TwipDotsLim(command_arg);
|
|
else if(PassQ("sb"))
|
|
state.format.before = TwipDotsLim(command_arg);
|
|
else if(PassQ("sa"))
|
|
state.format.after = TwipDotsLim(command_arg);
|
|
else if(PassQ("widctlpar"))
|
|
state.format.orphan = true;
|
|
else if(PassQ("nowidctlpar"))
|
|
state.format.orphan = false;
|
|
else if(PassQ("tql"))
|
|
tab_align = ALIGN_LEFT;
|
|
else if(PassQ("tqc"))
|
|
tab_align = ALIGN_CENTER;
|
|
else if(PassQ("tqr"))
|
|
tab_align = ALIGN_RIGHT;
|
|
else if(PassQ("tqdec"))
|
|
tab_align = ALIGN_RIGHT; // todo
|
|
else if(PassQ("tldot"))
|
|
tab_fill = 0;
|
|
else if(PassQ("tlhyph"))
|
|
tab_fill = 0;
|
|
else if(PassQ("tlul"))
|
|
tab_fill = 0;
|
|
else if(PassQ("tlth"))
|
|
tab_fill = 0;
|
|
else if(PassQ("tleq"))
|
|
tab_fill = 0;
|
|
else if(PassQ("tx") || PassQ("tb")) { // todo: bar tab ?
|
|
int pos = TwipDotSize(command_arg);
|
|
RichPara::Tab& tab = state.format.tab.Add();
|
|
tab.align = tab_align;
|
|
tab.fillchar = tab_fill;
|
|
tab.pos = pos;
|
|
state.format.SortTabs();
|
|
}
|
|
else if(PassQ("intbl"))
|
|
state.in_table = true;
|
|
else if(PassQ("itap")) {
|
|
state.itap = minmax(command_arg, 1, 10);
|
|
if(table_stack.GetCount() < state.itap)
|
|
OpenTable(state.itap);
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadCharStyle()
|
|
{
|
|
if(PassQ("plain")) {
|
|
state.charformat = plain_format;
|
|
state.charset = plain_charset;
|
|
}
|
|
else if(PassQ("b"))
|
|
state.charformat.Bold(command_arg != 0);
|
|
else if(PassQ("i"))
|
|
state.charformat.Italic(command_arg != 0);
|
|
else if(PassQ("ul") || PassQ("uld") || PassQ("uldb")
|
|
|| PassQ("uldash") || PassQ("uldashd") || PassQ("uldashdd")
|
|
|| PassQ("ulth") || PassQ("ulw") || PassQ("ulwave"))
|
|
state.charformat.Underline(command_arg != 0);
|
|
else if(PassQ("ulnone"))
|
|
state.charformat.Underline(false);
|
|
else if(PassQ("strike") || PassQ("strikedl"))
|
|
state.charformat.Strikeout(command_arg != 0);
|
|
else if(PassQ("caps") || PassQ("scaps"))
|
|
state.charformat.capitals = (command_arg != 0);
|
|
else if(PassQ("super") || PassQ("up"))
|
|
state.charformat.sscript = 1;
|
|
else if(PassQ("sub") || PassQ("dn"))
|
|
state.charformat.sscript = 2;
|
|
else if(PassQ("nosupersub"))
|
|
state.charformat.sscript = 0;
|
|
else if(PassQ("f") && command_arg >= 0 && command_arg < face_table.GetCount()) {
|
|
LLOG("font = " << command_arg << ", face = " << face_table[command_arg].face
|
|
<< ", charset = " << face_table[command_arg].charset);
|
|
state.charformat.Face(face_table[command_arg].face);
|
|
state.charset = face_table[command_arg].charset;
|
|
}
|
|
else if(PassQ("fs"))
|
|
state.charformat.Height(PointDotHeight(command_arg));
|
|
else if(PassQ("cf") && command_arg >= 0 && command_arg < color_table.GetCount())
|
|
state.charformat.ink = Nvl(color_table[command_arg], Black);
|
|
else if(PassQ("cb") && command_arg >= 0 && command_arg < color_table.GetCount())
|
|
state.charformat.paper = color_table[command_arg];
|
|
else if(PassQ("lang"))
|
|
{} // state.language = ...
|
|
}
|
|
|
|
void RTFParser::ReadShape()
|
|
{
|
|
int level = Level();
|
|
while(!PassEndGroup(level))
|
|
if(PassCmd("shppict")) {
|
|
LLOG("* shppict");
|
|
state.new_dest = false;
|
|
ReadItemGroup();
|
|
}
|
|
else
|
|
is_full = false;
|
|
}
|
|
|
|
void RTFParser::ReadPict()
|
|
{
|
|
LLOG("* ReadPict");
|
|
Size log_size(1, 1), out_size(1, 1), scaling(100, 100);
|
|
Rect crop(0, 0, 0, 0);
|
|
enum BLIPTYPE { UNK_BLIP, WMF_BLIP, PNG_BLIP, JPEG_BLIP, DIB_BLIP };
|
|
BLIPTYPE blip_type = UNK_BLIP;
|
|
String blip_data;
|
|
char odd = 0;
|
|
while(!PassEndGroup()) {
|
|
if(PassText())
|
|
blip_data.Cat(ReadBinHex(odd));
|
|
else if(Token() == T_COMMAND) {
|
|
if(PassQ("picw")) log_size.cx = minmax<int>(command_arg, 0, 30000);
|
|
else if(PassQ("pich")) log_size.cy = minmax<int>(command_arg, 0, 30000);
|
|
else if(PassQ("picwgoal")) out_size.cx = TwipDotSize(command_arg);
|
|
else if(PassQ("pichgoal")) out_size.cy = TwipDotSize(command_arg);
|
|
else if(PassQ("picscalex")) scaling.cx = minmax<int>(command_arg, 1, 1000);
|
|
else if(PassQ("picscaley")) scaling.cy = minmax<int>(command_arg, 1, 1000);
|
|
else if(PassQ("piccropl")) crop.left = TwipDotSize(command_arg);
|
|
else if(PassQ("piccropt")) crop.top = TwipDotSize(command_arg);
|
|
else if(PassQ("piccropr")) crop.right = TwipDotSize(command_arg);
|
|
else if(PassQ("piccropb")) crop.bottom = TwipDotSize(command_arg);
|
|
else if(PassQ("emfblip")) blip_type = WMF_BLIP;
|
|
else if(PassQ("pngblip")) blip_type = PNG_BLIP;
|
|
else if(PassQ("jpegblip")) blip_type = JPEG_BLIP;
|
|
else if(PassQ("wmetafile")) blip_type = WMF_BLIP;
|
|
else if(PassQ("dibitmap")) blip_type = DIB_BLIP;
|
|
else {
|
|
LLOG("Command skip " << command);
|
|
Skip();
|
|
}
|
|
}
|
|
else {
|
|
LLOG("Non command skip");
|
|
Skip();
|
|
}
|
|
}
|
|
Size final_size = minmax(iscale(out_size, scaling, Size(100, 100)), Size(1, 1), Size(30000, 30000));
|
|
Size drawing_size;
|
|
DrawingDraw dd;
|
|
RichObject object;
|
|
LLOG("Pict format " << (int)blip_type << ", data size: " << blip_data.GetCount());
|
|
if(blip_data.IsEmpty())
|
|
return;
|
|
#ifdef GUI_WIN
|
|
#ifndef PLATFORM_WINCE
|
|
if(blip_type == WMF_BLIP) {
|
|
log_size = min(log_size, GetFitSize(log_size, final_size));
|
|
dd.Create(drawing_size = log_size);
|
|
WinMetaFile wmf(blip_data);
|
|
wmf.Paint(dd, log_size);
|
|
object = CreateDrawingObject(dd, out_size, final_size);
|
|
}
|
|
else
|
|
#endif
|
|
#endif
|
|
if(blip_type == DIB_BLIP || blip_type == PNG_BLIP || blip_type == JPEG_BLIP) {
|
|
Image image = StreamRaster::LoadStringAny(blip_data);
|
|
LLOG("Image size: " << image.GetSize());
|
|
object = CreatePNGObject(image, out_size, final_size);
|
|
}
|
|
if(object) {
|
|
LLOG("object (" << object.GetTypeName() << ", " << object.Write().GetLength() << " B), pixel size "
|
|
<< object.GetPixelSize() << ", final size " << object.GetSize());
|
|
para.Cat(object, state.charformat);
|
|
}
|
|
}
|
|
|
|
String RTFParser::ReadBinHex(char& odd) const
|
|
{
|
|
int t = odd;
|
|
byte v = ctoi(odd);
|
|
String out;
|
|
for(const wchar *s = text.Begin(); *s; s++) {
|
|
byte w = (byte)(*s >= '0' && *s <= '9' ? *s - '0'
|
|
: *s >= 'A' && *s <= 'F' ? *s - 'A' + 10
|
|
: *s >= 'a' && *s <= 'f' ? *s - 'a' + 10
|
|
: 255);
|
|
if(w < 16) {
|
|
if(v >= 16) {
|
|
t = *s;
|
|
v = w;
|
|
}
|
|
else
|
|
{
|
|
out.Cat(16 * v + w);
|
|
v = 255;
|
|
}
|
|
}
|
|
}
|
|
odd = (v < 16 ? t : 0);
|
|
return out;
|
|
}
|
|
|
|
void RTFParser::OpenTable(int level)
|
|
{
|
|
if(table_stack.GetCount() < level) {
|
|
TableState& ts = table_stack.At(level - 1);
|
|
ts.stylecol = 0;
|
|
// state.cellformat = std_cell_format;
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadCellBorder(int& width)
|
|
{
|
|
if(Token() == T_COMMAND && !memcmp(command, "brdr", 4))
|
|
is_full = false;
|
|
if(PassCmd("brdrw"))
|
|
width = TwipDots(command_arg);
|
|
}
|
|
|
|
RTFParser::Cell& RTFParser::CellAt(TableState& ts, int i)
|
|
{
|
|
Array<Cell>& top = ts.cells.Top();
|
|
int p = top.GetCount();
|
|
if(p <= i)
|
|
top.SetCountR(i + 1);
|
|
for(int n = p; n <= i; n++)
|
|
top[n].info = CellInfoAt(n);
|
|
return top[i];
|
|
}
|
|
|
|
RTFParser::CellInfo& RTFParser::CellInfoAt(int i)
|
|
{
|
|
return state.cellinfo.At(i, std_cell_info);
|
|
}
|
|
|
|
void RTFParser::SetCellMargin(Cell& out, int Rect::*mbr)
|
|
{
|
|
if(out.info.cellmarginunits.*mbr == 0) {
|
|
out.info.format.margin.*mbr = state.trgaph;
|
|
if(state.rowmarginunits.*mbr == 3)
|
|
out.info.format.margin.*mbr = state.rowmargin.*mbr;
|
|
if(state.rowspacingunits.*mbr == 3)
|
|
out.info.format.margin.*mbr += state.rowspacing.*mbr;
|
|
}
|
|
}
|
|
|
|
void RTFParser::ReadTableStyle()
|
|
{
|
|
if(PassQ("nesttableprops")) {
|
|
state.nestprop = true;
|
|
return;
|
|
}
|
|
if(PassQ("nonesttables")) {
|
|
SkipGroup();
|
|
return;
|
|
}
|
|
int itap = (state.nestprop ? state.itap : 1);
|
|
if(PassQ("trowd")) {
|
|
OpenTable(itap);
|
|
table_stack[itap - 1].stylecol = 0;
|
|
return;
|
|
}
|
|
if(PassQ("row") && table_stack.GetCount() >= 1) {
|
|
TableState& ts0 = table_stack[0];
|
|
ts0.textcol = ts0.stylecol = 0;
|
|
ts0.cells.Add();
|
|
return;
|
|
}
|
|
if(PassQ("nestrow") && table_stack.GetCount() >= state.itap) {
|
|
TableState& ts = table_stack[state.itap - 1];
|
|
ts.textcol = ts.stylecol = 0;
|
|
ts.cells.Add();
|
|
return;
|
|
}
|
|
if(itap > table_stack.GetCount())
|
|
return;
|
|
TableState& ts = table_stack[itap - 1];
|
|
if(PassQ("trgaph"))
|
|
state.trgaph = TwipDotsLim(command_arg);
|
|
else if(PassQ("trql")) {}
|
|
else if(PassQ("trqr")) {}
|
|
else if(PassQ("trqc")) {}
|
|
else if(PassQ("trleft")) {
|
|
ts.tableformat.lm = TwipDotsLim(command_arg);
|
|
}
|
|
else if(PassQ("trbrdrl")) {}
|
|
else if(PassQ("trbrdrt")) {}
|
|
else if(PassQ("trbrdrr")) {}
|
|
else if(PassQ("trbrdrb")) {}
|
|
else if(PassQ("trbrdrv")) {}
|
|
else if(PassQ("trftsWidth")) {}
|
|
else if(PassQ("trautofit")) {}
|
|
else if(PassQ("trpaddl"))
|
|
state.rowmargin.left = TwipDotsLim(command_arg);
|
|
else if(PassQ("trpaddt"))
|
|
state.rowmargin.top = TwipDotsLim(command_arg);
|
|
else if(PassQ("trpaddr"))
|
|
state.rowmargin.right = TwipDotsLim(command_arg);
|
|
else if(PassQ("trpaddb"))
|
|
state.rowmargin.bottom = TwipDotsLim(command_arg);
|
|
else if(PassQ("trpaddfl"))
|
|
state.rowmarginunits.left = command_arg;
|
|
else if(PassQ("trpaddft"))
|
|
state.rowmarginunits.top = command_arg;
|
|
else if(PassQ("trpaddfr"))
|
|
state.rowmarginunits.right = command_arg;
|
|
else if(PassQ("trpaddfb"))
|
|
state.rowmarginunits.bottom = command_arg;
|
|
else if(PassQ("trspdl"))
|
|
state.rowspacing.left = TwipDotsLim(command_arg);
|
|
else if(PassQ("trspdt"))
|
|
state.rowspacing.top = TwipDotsLim(command_arg);
|
|
else if(PassQ("trspdr"))
|
|
state.rowspacing.right = TwipDotsLim(command_arg);
|
|
else if(PassQ("trspdb"))
|
|
state.rowspacing.bottom = TwipDotsLim(command_arg);
|
|
else if(PassQ("trspdfl"))
|
|
state.rowspacingunits.left = command_arg;
|
|
else if(PassQ("trspdft"))
|
|
state.rowspacingunits.top = command_arg;
|
|
else if(PassQ("trspdfr"))
|
|
state.rowspacingunits.right = command_arg;
|
|
else if(PassQ("trspdfb"))
|
|
state.rowspacingunits.bottom = command_arg;
|
|
else if(PassQ("clpadl"))
|
|
CellInfoAt(ts.stylecol).format.margin.left = TwipDotsLim(command_arg);
|
|
else if(PassQ("clpadt"))
|
|
CellInfoAt(ts.stylecol).format.margin.top = TwipDotsLim(command_arg);
|
|
else if(PassQ("clpadr"))
|
|
CellInfoAt(ts.stylecol).format.margin.right = TwipDotsLim(command_arg);
|
|
else if(PassQ("clpadb"))
|
|
CellInfoAt(ts.stylecol).format.margin.bottom = TwipDotsLim(command_arg);
|
|
else if(PassQ("clpadfl"))
|
|
state.cellmarginunits.left = command_arg;
|
|
else if(PassQ("clpadft"))
|
|
state.cellmarginunits.top = command_arg;
|
|
else if(PassQ("clpadfr"))
|
|
state.cellmarginunits.right = command_arg;
|
|
else if(PassQ("clpadfb"))
|
|
state.cellmarginunits.bottom = command_arg;
|
|
else if(PassQ("clbrdrl"))
|
|
ReadCellBorder(CellInfoAt(ts.stylecol).format.border.left);
|
|
else if(PassQ("clbrdrt"))
|
|
ReadCellBorder(CellInfoAt(ts.stylecol).format.border.top);
|
|
else if(PassQ("clbrdrr"))
|
|
ReadCellBorder(CellInfoAt(ts.stylecol).format.border.right);
|
|
else if(PassQ("clbrdrb"))
|
|
ReadCellBorder(CellInfoAt(ts.stylecol).format.border.bottom);
|
|
else if(PassQ("cltxlrtb")) {}
|
|
else if(PassQ("clshdng"))
|
|
CellInfoAt(ts.stylecol).shading = command_arg;
|
|
else if(PassQ("clcbpat")) {
|
|
if(command_arg >= 0 && command_arg < color_table.GetCount())
|
|
CellInfoAt(ts.stylecol).format.color = color_table[command_arg];
|
|
}
|
|
else if(PassQ("clvmrg"))
|
|
CellAt(ts, ts.stylecol).merge = true;
|
|
else if(PassQ("clvmgf"))
|
|
CellAt(ts, ts.stylecol).merge_first = true;
|
|
else if(PassQ("clftsWidth")) {}
|
|
else if(PassQ("clwWidth")) {}
|
|
else if(PassQ("cellx")) {
|
|
int sx = ts.stylecol++;
|
|
Cell& newcell = CellAt(ts, sx);
|
|
newcell.info.end_dots = TwipDotsLim(command_arg);
|
|
SetCellMargin(newcell, &Rect::left);
|
|
SetCellMargin(newcell, &Rect::top);
|
|
SetCellMargin(newcell, &Rect::right);
|
|
SetCellMargin(newcell, &Rect::bottom);
|
|
CellInfoAt(sx) = newcell.info;
|
|
//CellFormat(sx) = std_cell_format;
|
|
}
|
|
else if(PassQ("clvertalt"))
|
|
CellInfoAt(ts.stylecol).format.align = ALIGN_TOP;
|
|
else if(PassQ("clvertalc"))
|
|
CellInfoAt(ts.stylecol).format.align = ALIGN_CENTER;
|
|
else if(PassQ("clvertalb"))
|
|
CellInfoAt(ts.stylecol).format.align = ALIGN_BOTTOM;
|
|
}
|
|
|
|
}
|