ultimatepp/uppdev/RichTextP/ParseQtf.cpp
cxl 893a7924da Updating uppdev
git-svn-id: svn://ultimatepp.org/upp/trunk@527 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-10-13 20:06:12 +00:00

967 lines
23 KiB
C++

#include "RichText.h"
NAMESPACE_UPP
Color (*QTFColor[])() = {
Black, LtGray, White, Red, Green, Blue, LtRed, WhiteGray, LtCyan, Yellow
};
Color (*QTFSColor[])()= {
SColorText, SColorFace, SColorPaper, Red, Green, Blue, LtRed, WhiteGray, LtCyan, Yellow
};
Color NullColorF()
{
return Null;
}
static Color (*QTFColorl[])() = {
/*a*/White, /*b*/Blue, /*c*/Cyan, /*d*/White, /*e*/White, /*f*/White, /*g*/ Green, /*h*/White,
/*i*/White, /*j*/White, /*k*/Black, /*l*/LtGray, /*m*/Magenta, /*n*/NullColorF, /*o*/Brown, /*p*/White,
/*q*/White, /*r*/Red, /*s*/White, /*t*/White, /*u*/White, /*v*/White, /*w*/WhiteGray, /*x*/White,
/*y*/Yellow, /*z*/ White
};
static Color (*QTFColorL[])() = {
/*A*/White, /*B*/LtBlue, /*C*/LtCyan, /*D*/White, /*E*/White, /*F*/White, /*G*/LtGreen, /*H*/White,
/*I*/White, /*J*/White, /*K*/Gray, /*L*/WhiteGray, /*M*/LtMagenta, /*N*/NullColorF, /*O*/Brown, /*P*/White,
/*Q*/White, /*R*/LtRed, /*S*/White, /*T*/White, /*U*/White, /*V*/White, /*W*/White, /*X*/White,
/*Y*/LtYellow, /*Z*/White
};
static Color (*QTFSColorl[])() = { //TODO
/*a*/SColorPaper, /*b*/SColorHighlight, /*c*/Cyan, /*d*/SColorPaper, /*e*/SColorPaper, /*f*/SColorPaper, /*g*/ Green, /*h*/SColorPaper,
/*i*/SColorPaper, /*j*/SColorPaper, /*k*/SColorText, /*l*/SColorFace, /*m*/Magenta, /*n*/NullColorF, /*o*/Brown, /*p*/SColorPaper,
/*q*/SColorPaper, /*r*/Red, /*s*/SColorPaper, /*t*/SColorPaper, /*u*/SColorPaper, /*v*/SColorPaper, /*w*/WhiteGray, /*x*/SColorPaper,
/*y*/Yellow, /*z*/ White
};
static Color (*QTFSColorL[])() = { //TODO
/*A*/SColorPaper, /*B*/LtBlue, /*C*/LtCyan, /*D*/SColorPaper, /*E*/SColorPaper, /*F*/SColorPaper, /*G*/LtGreen, /*H*/SColorPaper,
/*I*/SColorPaper, /*J*/SColorPaper, /*K*/SColorShadow, /*L*/WhiteGray, /*M*/LtMagenta, /*N*/NullColorF, /*O*/Brown, /*P*/SColorPaper,
/*Q*/SColorPaper, /*R*/LtRed, /*S*/SColorPaper, /*T*/SColorPaper, /*U*/SColorPaper, /*V*/SColorPaper, /*W*/SColorPaper, /*X*/SColorPaper,
/*Y*/LtYellow, /*Z*/White
};
int QTFFontHeight[] = {
50, 67, 84, 100, 134, 167, 200, 234, 300, 400
};
class RichQtfParser {
const char *term;
bool scolors;
WString text;
RichPara paragraph;
RichTable tablepart;
bool istable;
bool breakpage;
byte accesskey;
struct PFormat : public RichPara::Format {
byte charset;
};
struct Tab {
RichCell::Format format;
int vspan, hspan;
RichTxt text;
RichTable table;
int cell;
Vector<int> rown;
void Old() { RichTable::Format fmt; fmt.grid = 10; table.SetFormat(fmt); }
Tab() { cell = 0; vspan = hspan = 0; }
};
PFormat format;
Array<PFormat> fstack;
Vector<Uuid> styleid;
Vector<int> stylenext;
Array<Tab> table;
bool oldtab;
bool Key(int c) { if(*term == c) { term++; return true; } return false; }
bool Key2(int c, int d);
bool Key2(int c) { return Key2(c, c); }
int GetNumber();
int ReadNumber();
String GetText(int delim);
Color GetColor();
void Number2(int& a, int& b);
void Flush();
void SetFormat();
void FlushStyles();
void Error(const char *s);
void ReadObject();
RichTable& Table();
void TableFormat(bool bw = false);
void FinishCell();
void FinishTable();
void FinishOldTable();
void S(int& x, int a);
void EndPart();
void Cat(int chr);
public:
struct Exc {};
RichText target;
void Parse(const char *qtf, byte accesskey);
RichQtfParser(bool scolors);
};
void init_s_nodeqtf();
RichQtfParser::RichQtfParser(bool _scolors)
{
format.Face(Font::ARIAL);
format.Height(100);
format.charset = GetDefaultCharset();
format.language = 0;
breakpage = false;
istable = false;
oldtab = false;
init_s_nodeqtf();
scolors = _scolors;
}
bool RichQtfParser::Key2(int c, int d)
{
if(term[0] == c && term[1] == d) {
term += 2;
return true;
}
return false;
}
int RichQtfParser::GetNumber()
{
int n = 0;
int sgn = 1;
if(*term == '-') {
sgn = -1;
term++;
}
while(IsDigit(*term))
n = n * 10 + *term++ - '0';
return sgn * n;
}
String RichQtfParser::GetText(int delim) {
String s;
for(;;) {
if(*term == '\0') return s;
if(*term == '`') {
term++;
if(*term == '\0') return s;
s.Cat(*term++);
}
else
if(*term == delim) {
term++;
return s;
}
else
s.Cat(*term++);
}
}
int RichQtfParser::ReadNumber()
{
if(!IsDigit(*term))
Error("Expected number");
return GetNumber();
}
void RichQtfParser::Number2(int& a, int& b)
{
a = -1;
b = -1;
if(IsDigit(*term))
a = GetNumber();
if(*term == '/') {
term++;
b = GetNumber();
}
}
Color RichQtfParser::GetColor()
{
int c = *term++;
if(c == '(') {
byte r = GetNumber();
if(Key(')')) {
r &= 255;
return Color(r, r, r);
}
Key('.');
byte g = GetNumber();
Key('.');
byte b = GetNumber();
Key(')');
return Color(r & 255, g & 255, b & 255);
}
else
if(c >= '0' && c <= '9')
return (*((scolors ? QTFSColor : QTFColor)[c - '0']))();
else
if(c >= 'a' && c <= 'z')
return (*((scolors ? QTFSColorl : QTFColorl)[c - 'a']))();
else
if(c >= 'A' && c <= 'Z')
return (*((scolors ? QTFSColorL : QTFColorL)[c - 'A']))();
else
return Red;
}
void RichQtfParser::SetFormat()
{
paragraph.format = format;
}
void RichQtfParser::Flush() {
if(text.GetLength()) {
ASSERT(!istable);
paragraph.Cat(text, format);
text.Clear();
}
}
void RichQtfParser::EndPart()
{
if(istable) {
if(paragraph.GetCount() == 0 && text.GetCount() == 0)
if(table.GetCount())
table.Top().text.CatPick(tablepart);
else
target.CatPick(tablepart);
else {
paragraph.part.Clear();
text.Clear();
}
}
else {
Flush();
bool b = paragraph.format.newpage;
if(breakpage)
paragraph.format.newpage = true;
if(table.GetCount())
table.Top().text.Cat(paragraph, target.GetStyles());
else
target.Cat(paragraph);
paragraph.part.Clear();;
paragraph.format.newpage = b;
SetFormat();
breakpage = false;
}
istable = false;
}
void RichQtfParser::ReadObject()
{
Flush();
RichObject obj;
if(*term == '#') {
term++;
#ifdef CPU_64
obj = *(RichObject *)stou64(term, &term);
#else
obj = *(RichObject *)stou(term, &term);
#endif
term++;
}
else {
String type;
while(IsAlNum(*term) || *term == '_')
type.Cat(*term++);
Size sz;
Key(':');
sz.cx = ReadNumber();
bool keepratio = false;
if(Key('&'))
keepratio = true;
else
Key('*');
sz.cy = ReadNumber();
int yd = 0;
if(Key('/'))
yd = GetNumber();
StringBuffer data;
for(;;) {
while(*term < 32 && *term > 0) term++;
if((byte)*term >= ' ' && (byte)*term <= 127 || *term == '\0') break;
byte seven = *term++;
for(int i = 0; i < 7; i++) {
while((byte)*term < 32 && (byte)*term > 0) term++;
if((byte)*term >= ' ' && (byte)*term <= 127 || *term == '\0') break;
data.Cat((*term++ & 0x7f) | ((seven << 7) & 0x80));
seven >>= 1;
}
}
obj.Read(type, data, sz);
obj.KeepRatio(keepratio);
obj.SetYDelta(yd);
}
paragraph.Cat(obj, format);
}
int NoLow(int c) {
return (byte)c >= 32 ? c : 0;
}
void RichQtfParser::Error(const char *s) {
RichPara::CharFormat ef;
(Font&) ef = Arial(84).Bold().Underline();
ef.ink = Red;
paragraph.Cat(("ERROR: " + String(s) + ": " +
Filter(String(term, min<int>((int)strlen(term), 20)), NoLow)).ToWString(), ef);
target.Cat(paragraph);
FlushStyles();
throw Exc();
}
void RichQtfParser::FlushStyles()
{
for(int i = 0; i < styleid.GetCount(); i++)
if(stylenext[i] >= 0 && stylenext[i] < styleid.GetCount()) {
RichStyle s = target.GetStyle(styleid[i]);
s.next = styleid[stylenext[i]];
target.SetStyle(styleid[i], s);
}
}
RichTable& RichQtfParser::Table()
{
if(table.GetCount() == 0)
Error("Not in table");
return table.Top().table;
}
void RichQtfParser::S(int& x, int a)
{
if(a >= 0)
x = a;
}
void RichQtfParser::TableFormat(bool bw)
{
RichTable& tab = Table();
Tab& t = table.Top();
int a, b;
for(;;) {
if(bw && IsDigit(*term)) {
t.hspan = GetNumber();
}
else
switch(*term++) {
case ' ': return;
case ';': break;
case '<': tab.format.lm = ReadNumber(); break;
case '>': tab.format.rm = ReadNumber(); break;
case 'B': tab.format.before = ReadNumber(); break;
case 'A': tab.format.after = ReadNumber(); break;
case 'f': tab.format.frame = ReadNumber(); break;
case '_':
case 'F': tab.format.framecolor = GetColor(); break;
case 'g': tab.format.grid = ReadNumber(); break;
case 'G': tab.format.gridcolor = GetColor(); break;
case 'h': tab.format.header = GetNumber(); break;
case '~': tab.format.frame = tab.format.grid = 0; break;
case '^': t.format.align = ALIGN_TOP; break;
case '=': t.format.align = ALIGN_CENTER; break;
case 'v': t.format.align = ALIGN_BOTTOM; break;
case 'l': Number2(a, b); S(t.format.border.left, a); S(t.format.margin.left, b); break;
case 'r': Number2(a, b); S(t.format.border.right, a); S(t.format.margin.right, b); break;
case 't': Number2(a, b); S(t.format.border.top, a); S(t.format.margin.top, b); break;
case 'b': Number2(a, b); S(t.format.border.bottom, a); S(t.format.margin.bottom, b); break;
case 'H': t.format.minheight = ReadNumber(); break;
case '@': t.format.color = GetColor(); break;
case 'R': t.format.bordercolor = GetColor(); break;
case '!': t.format = RichCell::Format(); break;
case 'k': t.format.keep = true;
case 'K': tab.format.keep = true;
case 'a':
Number2(a, b);
if(a >= 0)
t.format.border.left = t.format.border.right = t.format.border.top = t.format.border.bottom = a;
if(b >= 0)
t.format.margin.left = t.format.margin.right = t.format.margin.top = t.format.margin.bottom = b;
break; //!!cell all lines
case '-': t.hspan = GetNumber(); break;
case '+':
case '|': t.vspan = GetNumber(); break;
default:
Error("Invalid cell format");
}
}
}
void RichQtfParser::FinishCell()
{
EndPart();
RichTable& t = Table();
Tab& b = table.Top();
int i, j;
if(oldtab) {
i = b.rown.GetCount() - 1;
j = b.rown.Top();
b.rown.Top()++;
}
else {
i = b.cell / t.GetColumns();
j = b.cell % t.GetColumns();
}
t.SetPick(i, j, b.text);
b.text.Clear();
t.SetFormat(i, j, b.format);
t.SetSpan(i, j, b.vspan, b.hspan);
if(oldtab && b.rown.GetCount() > 1 && j + 1 < b.rown[0])
b.format = t.GetFormat(0, j + 1);
else {
b.cell++;
b.vspan = 0;
b.hspan = oldtab;
}
b.format.keep = false;
}
void RichQtfParser::FinishTable()
{
FinishCell();
tablepart = Table();
istable = true;
table.Drop();
}
void RichQtfParser::FinishOldTable()
{
FinishCell();
Index<int> pos;
Vector<int> srow;
RichTable& t = Table();
Tab& b = table.Top();
for(int i = 0; i < t.GetRows(); i++) {
int& s = srow.Add();
s = 0;
int nx = b.rown[i];
for(int j = 0; j < nx; j++)
s += t.GetSpan(i, j).cx;
int xn = 0;
for(int j = 0; j < nx; j++) {
pos.FindAdd(xn * 10000 / s);
xn += t.GetSpan(i, j).cx;
}
}
Vector<int> h = pos.PickKeys();
if(h.GetCount() == 0)
Error("table");
Sort(h);
pos = h;
pos.Add(10000);
RichTable tab;
tab.SetFormat(t.GetFormat());
for(int i = 0; i < pos.GetCount() - 1; i++) {
tab.AddColumn(pos[i + 1] - pos[i]);
}
for(int i = 0; i < t.GetRows(); i++) {
int s = srow[i];
int nx = b.rown[i];
int xn = 0;
int xi = 0;
for(int j = 0; j < nx; j++) {
Size span = t.GetSpan(i, j);
xn += span.cx;
int nxi = pos.Find(xn * 10000 / s);
tab.SetPick(i, xi, t.GetPick(i, j));
tab.SetFormat(i, xi, t.GetFormat(i, j));
tab.SetSpan(i, xi, max(span.cy - 1, 0), nxi - xi - 1);
xi = nxi;
}
}
table.Drop();
if(table.GetCount())
table.Top().text.CatPick(tab);
else
target.CatPick(tab);
oldtab = false;
}
void RichQtfParser::Cat(int chr)
{
if(accesskey && ToUpper(ToAscii(chr)) == accesskey) {
Flush();
format.Underline(!format.IsUnderline());
text.Cat(chr);
Flush();
format.Underline(!format.IsUnderline());
accesskey = 0;
}
else {
text.Cat(chr);
}
}
extern bool s_nodeqtf[128];
void RichQtfParser::Parse(const char *qtf, byte _accesskey)
{
accesskey = _accesskey;
term = qtf;
while(*term) {
if(Key('[')) {
Flush();
fstack.Add(format);
for(;;) {
int c = *term;
if(!c)
Error("Unexpected end of text");
term++;
if(c == ' ' || c == '\n') break;
switch(c) {
case 's': {
Uuid id;
c = *term;
if(Key('\"') || Key('\''))
id = target.GetStyleId(GetText(c));
else {
int i = ReadNumber();
if(i >= 0 && i < styleid.GetCount())
id = styleid[i];
else
id = RichStyle::GetDefaultId();
}
const RichStyle& s = target.GetStyle(id);
bool p = format.newpage;
int lng = format.language;
(RichPara::Format&) format = s.format;
format.styleid = id;
format.language = lng;
format.newpage = p;
break;
}
case '/': format.Italic(!format.IsItalic()); break;
case '*': format.Bold(!format.IsBold()); break;
case '_': format.Underline(!format.IsUnderline()); break;
case 'T': format.NonAntiAliased(!format.IsNonAntiAliased()); break;
case '-': format.Strikeout(!format.IsStrikeout()); break;
case 'c': format.capitals = !format.capitals; break;
case 'd': format.dashed = !format.dashed; break;
case '`': format.sscript = format.sscript == 1 ? 0 : 1; break;
case ',': format.sscript = format.sscript == 2 ? 0 : 2; break;
case '^': format.link = GetText('^'); break;
case 'I': format.indexentry = FromUtf8(GetText(';')); break;
case '+': format.Height(GetNumber()); break;
case '@': format.ink = GetColor(); break;
case '$': format.paper = GetColor(); break;
case 'A': format.Face(Font::ARIAL); break;
case 'R': format.Face(Font::ROMAN); break;
case 'C': format.Face(Font::COURIER); break;
case 'G': format.Face(Font::STDFONT); break;
case 'S': format.Face(Font::SYMBOL); break;
case '.': {
int n = GetNumber();
if(n >= Font::GetFaceCount())
Error("Invalid face number");
format.Face(n); break;
}
case '!': {
String fn = GetText('!');
int i = Font::FindFaceNameIndex(fn);
if(i < 0)
i = Font::ARIAL;
format.Face(i);
}
break;
case '{': {
String cs = GetText('}');
if(cs.GetLength() == 1) {
int c = *cs;
if(c == '_')
format.charset = CHARSET_UTF8;
if(c >= '0' && c <= '8')
format.charset = c - '0' + CHARSET_WIN1250;
if(c >= 'A' && c <= 'Z')
format.charset = c - '0' + CHARSET_ISO8859_1;
}
else {
for(int i = 0; i < CharsetCount(); i++)
if(stricmp(CharsetName(i), cs) == 0) {
format.charset = i;
break;
}
}
break;
}
case '%': {
String h;
if(*term == '-') {
format.language = 0;
term++;
}
else
if(*term == '%') {
format.language = LNG_ENGLISH;
term++;
}
else {
while(*term && h.GetLength() < 5)
h.Cat(*term++);
format.language = LNGFromText(h);
}
break;
}
default:
if(c >= '0' && c <= '9') {
format.Height(QTFFontHeight[c - '0']);
break;
}
switch(c) {
case ':': format.label = GetText(':'); break;
case '<': format.align = ALIGN_LEFT; break;
case '>': format.align = ALIGN_RIGHT; break;
case '=': format.align = ALIGN_CENTER; break;
case '#': format.align = ALIGN_JUSTIFY; break;
case 'l': format.lm = GetNumber(); break;
case 'r': format.rm = GetNumber(); break;
case 'i': format.indent = GetNumber(); break;
case 'b': format.before = GetNumber(); break;
case 'a': format.after = GetNumber(); break;
case 'P': format.newpage = !format.newpage; break;
case 'k': format.keep = !format.keep; break;
case 'K': format.keepnext = !format.keepnext; break;
case 'H': format.ruler = GetNumber(); break;
case 'h': format.rulerink = GetColor(); break;
case 'Q': format.orphan = !format.orphan; break;
case 'n': format.before_number = GetText(';'); break;
case 'm': format.after_number = GetText(';'); break;
case 'N': {
memset(format.number, 0, sizeof(format.number));
format.reset_number = false;
int i = 0;
while(i < 8) {
int c;
if(Key('-'))
c = RichPara::NUMBER_NONE;
else
if(Key('1'))
c = RichPara::NUMBER_1;
else
if(Key('0'))
c = RichPara::NUMBER_0;
else
if(Key('a'))
c = RichPara::NUMBER_a;
else
if(Key('A'))
c = RichPara::NUMBER_A;
else
if(Key('i'))
c = RichPara::NUMBER_i;
else
if(Key('I'))
c = RichPara::NUMBER_I;
else
break;
format.number[i++] = c;
}
if(Key('!'))
format.reset_number = true;
break;
}
case 'o': format.bullet = RichPara::BULLET_ROUND;
format.indent = 150; break;
case 'O':
if(Key('_'))
format.bullet = RichPara::BULLET_NONE;
else {
int c = *term++;
if(!c)
Error("Unexpected end of text");
format.bullet =
c == '1' ? RichPara::BULLET_ROUNDWHITE :
c == '2' ? RichPara::BULLET_BOX :
c == '3' ? RichPara::BULLET_BOXWHITE :
c == '9' ? RichPara::BULLET_TEXT :
RichPara::BULLET_ROUND;
}
break;
case 'p':
switch(*term++) {
case 0: Error("Unexpected end of text");
case 'h': format.linespacing = RichPara::LSP15; break;
case 'd': format.linespacing = RichPara::LSP20; break;
default: format.linespacing = RichPara::LSP10;
}
break;
case 't':
if(IsDigit(*term)) //temporary fix... :(
format.tabsize = ReadNumber();
break;
case '~': {
if(Key('~'))
format.tab.Clear();
else {
RichPara::Tab tab;
Key('<');
if(Key('>'))
tab.align = ALIGN_RIGHT;
if(Key('='))
tab.align = ALIGN_CENTER;
if(Key('.'))
tab.fillchar = 1;
if(Key('-'))
tab.fillchar = 2;
if(Key('_'))
tab.fillchar = 3;
tab.pos = ReadNumber();
format.tab.Add(tab);
}
}
break;
default:
continue;
}
}
}
SetFormat();
}
else
if(Key(']')) {
Flush();
if(fstack.GetCount()) {
format = fstack.Top();
fstack.Drop();
}
else
Error("Unmatched ']'");
}
else
if(Key2('{')) {
if(oldtab)
Error("{{ in ++ table");
if(text.GetLength() || paragraph.GetCount())
EndPart();
table.Add();
int r = IsDigit(*term) ? ReadNumber() : 1;
Table().AddColumn(r);
while(Key(':'))
Table().AddColumn(ReadNumber());
TableFormat();
}
else
if(Key2('}')) {
if(oldtab)
Error("}} in ++ table");
FinishTable();
}
else
if(Key2('+'))
if(oldtab)
FinishOldTable();
else {
Flush();
if(text.GetLength() || paragraph.GetCount())
EndPart();
Tab& b = table.Add();
b.rown.Add(0);
b.hspan = 1;
b.Old();
oldtab = true;
}
else
if(Key2('|'))
FinishCell();
else
if(Key2('-')) {
FinishCell();
table.Top().rown.Add(0);
}
else
if(Key2(':')) {
if(!oldtab)
FinishCell();
TableFormat(oldtab);
}
else
if(Key2('^')) {
EndPart();
breakpage = true;
}
else
if(Key2('@')) {
ReadObject();
}
else
if(Key2('@', '$')) {
String xu;
while(isxdigit(*term))
xu.Cat(*term++);
int c = stou(~xu, NULL, 16);
if(c >= 32)
Cat(c);
if(*term == ';')
term++;
SetFormat();
}
else
if(Key2('{', ':')) {
Flush();
String field = GetText(':');
String param = GetText(':');
Id fid(field);
if(RichPara::fieldtype().Find(fid) >= 0)
paragraph.Cat(fid, param, format);
Key('}');
}
else
if(Key('&')) {
SetFormat();
EndPart();
}
else
if(Key2('$')) {
Flush();
int i = GetNumber();
Uuid id;
RichStyle style;
style.format = format;
if(Key(','))
stylenext.At(i, 0) = GetNumber();
else
stylenext.At(i, 0) = i;
if(Key('#')) {
String xu;
while(isxdigit(*term))
xu.Cat(*term++);
if(xu.GetLength() != 32)
Error("Invalid UUID !");
id = ScanUuid(xu);
}
else
if(i)
id = Uuid::Create();
else
id = RichStyle::GetDefaultId();
if(Key(':'))
style.name = GetText(']');
if(fstack.GetCount()) {
format = fstack.Top();
fstack.Drop();
}
target.SetStyle(id, style);
styleid.At(i, RichStyle::GetDefaultId()) = id;
if(id == RichStyle::GetDefaultId()) {
bool p = format.newpage;
int lng = format.language;
(RichPara::Format&) format = style.format;
format.styleid = id;
format.language = lng;
format.newpage = p;
}
}
else
if(*term == '_') {
SetFormat();
text.Cat(160);
term++;
}
else
if(Key2('-', '|')) {
SetFormat();
text.Cat(9);
}
else
if(*term == '\1') {
if(istable)
EndPart();
SetFormat();
const char *b = ++term;
for(; *term && *term != '\1'; term++)
if(*term == '\n') {
text.Cat(ToUnicode(b, (int)(term - b), format.charset));
EndPart();
b = term + 1;
}
text.Cat(ToUnicode(b, (int)(term - b), format.charset));
if(*term == '\1')
term++;
}
else {
if(!Key('`')) Key('\\');
if((byte)*term >= ' ') {
SetFormat();
do {
if(istable)
EndPart();
if(format.charset == CHARSET_UTF8) {
word code = (byte)*term++;
if(code <= 0x7F)
Cat(code);
else
if(code <= 0xDF) {
if(*term == '\0') break;
int c0 = (byte)*term++;
if(c0 < 0x80)
Error("Invalid UTF-8 sequence");
Cat(((code - 0xC0) << 6) + c0 - 0x80);
}
else
if(code <= 0xEF) {
int c0 = (byte)*term++;
int c1 = (byte)*term++;
if(c0 < 0x80 || c1 < 0x80)
Error("Invalid UTF-8 sequence");
Cat(((code - 0xE0) << 12) + ((c0 - 0x80) << 6) + c1 - 0x80);
}
else
Error("Invalid UTF-8 sequence");
}
else
Cat(ToUnicode((byte)*term++, format.charset));
}
while((byte)*term >= 128 || s_nodeqtf[(byte)*term]);
}
else
if(*term)
term++;
}
}
if(paragraph.GetCount() == 0)
SetFormat();
if(oldtab)
FinishOldTable();
else
while(table.GetCount())
FinishTable();
EndPart();
FlushStyles();
}
RichText ParseQTF(const char *qtf, bool scolors, byte accesskey)
{
RichQtfParser p(scolors);
try {
p.Parse(qtf, accesskey);
}
catch(RichQtfParser::Exc) {}
return p.target;
}
String QtfRichObject::ToString() const
{
return String("@@#").Cat() << uintptr_t(&obj) << ";";
}
QtfRichObject::QtfRichObject(const RichObject& o)
: obj(o)
{}
END_UPP_NAMESPACE