mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
752 lines
23 KiB
C++
752 lines
23 KiB
C++
#include "CtrlCore.h"
|
|
|
|
#define LLOG(x)
|
|
|
|
#include <plugin/png/png.h>
|
|
|
|
namespace Upp {
|
|
|
|
static int GetParaHeight(const Array<RichPara::Part>& parts)
|
|
{
|
|
int ht = 0;
|
|
for(int i = 0; i < parts.GetCount(); i++) {
|
|
int pht = 0;
|
|
const RichPara::Part& part = parts[i];
|
|
if(part.object)
|
|
pht = part.object.GetSize().cy;
|
|
else if(part.field)
|
|
pht = GetParaHeight(part.fieldpart);
|
|
else
|
|
pht = tabs(part.format.GetHeight());
|
|
if(pht > ht)
|
|
ht = pht;
|
|
}
|
|
return ht;
|
|
}
|
|
|
|
class RTFEncoder {
|
|
public:
|
|
RTFEncoder(Stream& stream, const RichText& richtext, byte charset,
|
|
Size dot_page_size, const Rect& dot_margins, void *context);
|
|
|
|
void Run();
|
|
|
|
private:
|
|
void FacesAddFormat(const RichPara::CharFormat& format);
|
|
void GetFaces();
|
|
void GetTxtFaces(const RichTxt& txt);
|
|
|
|
void PutHeader();
|
|
void PutDocument();
|
|
void PutTxt(const RichTxt& txt, int nesting, int dot_width);
|
|
void PutTable(const RichTable& table, int nesting, int dot_width);
|
|
void PutParts(const Array<RichPara::Part>& parts,
|
|
RichPara::CharFormat& base, int bpart, int boff, int epart, int eoff);
|
|
|
|
void Begin() { stream.Put('{'); }
|
|
void Begin(const char *cmd) { Begin(); Command(cmd); }
|
|
void Begin(const char *cmd, int param) { Begin(); Command(cmd, param); }
|
|
void End() { stream.Put('}'); }
|
|
|
|
void Command(const char *cmd);
|
|
void Command(const char *cmd, int param);
|
|
void Space() { stream.Put(' '); }
|
|
void PutText(const wchar *text);
|
|
void PutObject(const RichObject& object);
|
|
void PutTabs(const Vector<RichPara::Tab>& tabs);
|
|
void PutBinHex(const byte *data, int count);
|
|
void PutBinHex(const String& s) { PutBinHex(s, s.GetLength()); }
|
|
void PutBorder(StringBuffer& rowfmt, int wd, Color c, const char *clpar);
|
|
|
|
bool PutParaFormat(const RichPara::Format& pf, const RichPara::Format& difpf);
|
|
bool PutCharFormat(const RichPara::CharFormat& cf, const RichPara::CharFormat& difcf, bool pn);
|
|
|
|
struct Group;
|
|
|
|
friend struct Group;
|
|
|
|
struct Group {
|
|
Group(RTFEncoder *owner) : owner(owner) { owner->Begin(); }
|
|
Group(RTFEncoder *owner, const char *cmd) : owner(owner) { owner->Begin(cmd); }
|
|
Group(RTFEncoder *owner, const char *cmd, int param) : owner(owner) { owner->Begin(cmd, param); }
|
|
~Group() { owner->End(); }
|
|
RTFEncoder *owner;
|
|
};
|
|
|
|
private:
|
|
Stream& stream;
|
|
const RichText& richtext;
|
|
byte charset;
|
|
Size dot_page_size;
|
|
Rect dot_margins;
|
|
|
|
RichPara::CharFormat charfmt;
|
|
RichPara::Format parafmt;
|
|
Uuid oldstyle;
|
|
int old_ht;
|
|
int para_ht;
|
|
|
|
Index<int> used_faces;
|
|
enum { SYMBOL_INDEX = 1, WINGDINGS_INDEX = 2 };
|
|
VectorMap<Color, int> used_ink, used_paper;
|
|
Index<Color> phys_colors;
|
|
Index<Uuid> styleid;
|
|
};
|
|
|
|
void EncodeRTF(Stream& stream, const RichText& richtext, byte charset,
|
|
Size dot_page_size, const Rect& dot_margins, void *context)
|
|
{
|
|
RTFEncoder(stream, richtext, charset, dot_page_size, dot_margins, context).Run();
|
|
}
|
|
|
|
String EncodeRTF(const RichText& richtext, byte charset, Size dot_page_size, const Rect& dot_margins, void *context)
|
|
{
|
|
StringStream out;
|
|
EncodeRTF(out, richtext, charset, dot_page_size, dot_margins, context);
|
|
String s = out.GetResult();
|
|
LLOG("EncodeRTF <<<<<\n" << s << "\n>>>>> EncodeRTF");
|
|
return s;
|
|
}
|
|
|
|
String EncodeRTF(const RichText& richtext, byte charset, int dot_page_width)
|
|
{
|
|
int dot_page_height = iscale(dot_page_width, 1414, 1000);
|
|
Rect margins(472, 472, 472, 472);
|
|
return EncodeRTF(richtext, charset, Size(dot_page_width + margins.left + margins.right,
|
|
dot_page_height + margins.top + margins.bottom), margins);
|
|
}
|
|
|
|
String EncodeRTF(const RichText& richtext)
|
|
{
|
|
return EncodeRTF(richtext, GetDefaultCharset() == CHARSET_UTF8 ? GetLNGCharset(GetSystemLNG())
|
|
: GetDefaultCharset());
|
|
}
|
|
|
|
RTFEncoder::RTFEncoder(Stream& stream_, const RichText& richtext_, byte charset_,
|
|
Size dot_page_size_, const Rect& dot_margins_, void *context_)
|
|
: stream(stream_)
|
|
, richtext(richtext_)
|
|
, charset(charset_)
|
|
, dot_page_size(dot_page_size_)
|
|
, dot_margins(dot_margins_)
|
|
{
|
|
for(int i = 0; i < richtext.GetStyleCount(); i++)
|
|
styleid.Add(richtext.GetStyleId(i));
|
|
}
|
|
|
|
void RTFEncoder::Run()
|
|
{
|
|
GetFaces();
|
|
Group docgrp(this, "rtf", 1);
|
|
Command("ansi");
|
|
PutHeader();
|
|
PutDocument();
|
|
}
|
|
|
|
void RTFEncoder::FacesAddFormat(const RichPara::CharFormat& format)
|
|
{
|
|
used_faces.FindAdd(format.GetFace());
|
|
if(used_ink.Find(format.ink) < 0) {
|
|
Color i(format.ink.GetR(), format.ink.GetG(), format.ink.GetB());
|
|
int x = used_ink.Get(i, -1);
|
|
if(x < 0)
|
|
x = phys_colors.FindAdd(i);
|
|
used_ink.Add(format.ink, x);
|
|
}
|
|
if(used_paper.Find(format.paper) < 0) {
|
|
Color p(format.paper.GetR(), format.paper.GetG(), format.paper.GetB());
|
|
int x = used_paper.Get(p, -1);
|
|
if(x < 0)
|
|
x = phys_colors.FindAdd(p);
|
|
used_paper.Add(format.paper, x);
|
|
}
|
|
}
|
|
|
|
void RTFEncoder::GetFaces()
|
|
{
|
|
used_faces.Add(Font::ARIAL); // default font
|
|
#ifdef PLATFORM_WIN32
|
|
used_faces.Add(Font::SYMBOL); // used for bullets
|
|
used_faces.Add(Font::WINGDINGS); // used for bullets
|
|
#endif
|
|
phys_colors.Add(Null);
|
|
used_ink.Add(Null, 0);
|
|
used_paper.Add(Null, 0);
|
|
used_ink.Add(Black(), 0);
|
|
used_paper.Add(White(), 0);
|
|
GetTxtFaces(richtext);
|
|
for(int i = 0; i < richtext.GetStyleCount(); i++)
|
|
FacesAddFormat(richtext.GetStyle(i).format);
|
|
}
|
|
|
|
void RTFEncoder::GetTxtFaces(const RichTxt& rt)
|
|
{
|
|
for(int i = 0; i < rt.GetPartCount(); i++) {
|
|
if(rt.IsTable(i)) {
|
|
const RichTable& table = rt.GetTable(i);
|
|
const RichTable::Format& tfmt = table.GetFormat();
|
|
phys_colors.FindAdd(tfmt.framecolor);
|
|
phys_colors.FindAdd(tfmt.gridcolor);
|
|
for(int r = 0; r < table.GetRows(); r++)
|
|
for(int c = 0; c < table.GetColumns(); c++) {
|
|
const RichCell& cell = table[r][c];
|
|
phys_colors.FindAdd(cell.format.color);
|
|
phys_colors.FindAdd(cell.format.bordercolor);
|
|
GetTxtFaces(cell.text);
|
|
}
|
|
}
|
|
else {
|
|
const RichPara& para = rt.Get(i, richtext.GetStyles());
|
|
FacesAddFormat(para.format);
|
|
for(int p = 0; p < para.part.GetCount(); p++) {
|
|
const RichPara::Part& part = para.part[p];
|
|
if(part.IsText())
|
|
FacesAddFormat(part.format);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFEncoder::Command(const char *cmd)
|
|
{
|
|
stream.Put('\\');
|
|
stream.Put(cmd);
|
|
}
|
|
|
|
void RTFEncoder::Command(const char *cmd, int param)
|
|
{
|
|
stream.Put('\\');
|
|
stream.Put(cmd);
|
|
stream.Put(IntStr(param));
|
|
}
|
|
|
|
void RTFEncoder::PutText(const wchar *text)
|
|
{
|
|
for(; *text; text++)
|
|
if(*text >= 128) {
|
|
if(*text >= 65536) {
|
|
Vector<char16> h = ToUtf16(*text);
|
|
stream << "\\uc0 \\u" << (unsigned)h[0] << "\\uc1 \\u" << (unsigned)h[1];
|
|
}
|
|
else
|
|
stream.Put(Format("\\u%d?", (int16)*text));
|
|
}
|
|
else {
|
|
if(*text == '{' || *text == '}' || *text == '\\')
|
|
stream.Put('\\');
|
|
stream.Put((byte)*text);
|
|
}
|
|
}
|
|
|
|
void RTFEncoder::PutBinHex(const byte *b, int count)
|
|
{
|
|
enum { BLOCK = 32 };
|
|
for(int l = count; l > 0; l -= BLOCK) {
|
|
stream.PutCrLf();
|
|
for(const byte *e = b + min<int>(l, BLOCK); b < e; b++) {
|
|
static const char binhex[] = "0123456789abcdef";
|
|
stream.Put(binhex[*b >> 4]);
|
|
stream.Put(binhex[*b & 15]);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RTFEncoder::PutCharFormat(const RichPara::CharFormat& cf, const RichPara::CharFormat& difcf, bool pn)
|
|
{
|
|
int64 pos = stream.GetPos();
|
|
int pn2 = (pn ? 0 : 2);
|
|
bool f;
|
|
int t;
|
|
if(cf.GetFace() != difcf.GetFace())
|
|
Command(("pnf") + pn2, used_faces.Find(cf.GetFace()));
|
|
if((t = DotPoints(2 * tabs(cf.GetHeight()))) != DotPoints(2 * tabs(difcf.GetHeight())))
|
|
Command(("pnfs") + pn2, t);
|
|
if(!pn && dword(t = cf.sscript) != difcf.sscript)
|
|
Command(t == 0 ? "nosupersub" : t == 1 ? "super" : "sub");
|
|
if((f = cf.IsBold()) != difcf.IsBold()) Command((f ? "pnb" : "pnb0") + pn2);
|
|
if((f = cf.IsItalic()) != difcf.IsItalic()) Command((f ? "pni" : "pni0") + pn2);
|
|
if((f = cf.IsUnderline()) != difcf.IsUnderline()) Command((f ? "pnul" : "pnul0") + pn2);
|
|
if((f = cf.IsStrikeout()) != difcf.IsStrikeout()) Command((f ? "pnstrike" : "pnstrike0") + pn2);
|
|
if((f = cf.capitals) != difcf.capitals) Command((f ? "pncaps" : "pncaps0") + pn2);
|
|
if((t = used_ink.Get(cf.ink)) != used_ink.Get(difcf.ink)) Command(("pncf") + pn2, t);
|
|
if((t = used_paper.Get(cf.paper)) != used_paper.Get(difcf.paper)) Command(("pncb") + pn2, t);
|
|
#ifdef PLATFORM_WIN32
|
|
if(!pn && cf.language != difcf.language) Command("lang", GetLanguageLCID(cf.language));
|
|
#endif
|
|
// todo: link
|
|
return stream.GetPos() != pos;
|
|
}
|
|
|
|
void RTFEncoder::PutObject(const RichObject& object)
|
|
{
|
|
Size log_size = object.GetPhysicalSize(); // / 6;
|
|
Size out_size = object.GetSize();
|
|
Size out_size_01mm = iscale(out_size, 254, 60);
|
|
if(log_size.cx <= 0 || log_size.cy <= 0) log_size = out_size / 6;
|
|
Group pict_grp(this, "pict");
|
|
Command("picw", out_size_01mm.cx);
|
|
Command("pich", out_size_01mm.cy);
|
|
Command("picwgoal", DotTwips(out_size.cx));
|
|
Command("pichgoal", DotTwips(out_size.cy));
|
|
Command("pngblip");
|
|
RichObjectPaintInfo pi;
|
|
pi.ink = charfmt.ink;
|
|
PutBinHex(PNGEncoder().SaveString(object.ToImage(out_size, pi)));
|
|
}
|
|
|
|
bool RTFEncoder::PutParaFormat(const RichPara::Format& pf, const RichPara::Format& difpf)
|
|
{
|
|
int64 pos = stream.GetPos();
|
|
if(pf.align != difpf.align)
|
|
switch(pf.align) {
|
|
case ALIGN_NULL:
|
|
case ALIGN_LEFT: Command("ql"); break;
|
|
case ALIGN_CENTER: Command("qc"); break;
|
|
case ALIGN_RIGHT: Command("qr"); break;
|
|
case ALIGN_JUSTIFY: Command("qj"); break;
|
|
default: NEVER();
|
|
}
|
|
int oind = difpf.indent, olm = difpf.lm;
|
|
if(difpf.bullet != RichPara::BULLET_NONE) {
|
|
olm += oind;
|
|
oind = -oind;
|
|
}
|
|
int nind = pf.indent, nlm = pf.lm;
|
|
if(pf.bullet != RichPara::BULLET_NONE) {
|
|
nlm += nind;
|
|
nind = -nind;
|
|
}
|
|
// if(nind != oind) Command("fi", DotTwips(nind));
|
|
if(nlm != olm) Command("li", DotTwips(nlm));
|
|
if(pf.rm != difpf.rm) Command("ri", DotTwips(pf.rm));
|
|
if(pf.newpage)
|
|
Command("pagebb");
|
|
if(pf.before != difpf.before) Command("sb", DotTwips(pf.before));
|
|
if(pf.after != difpf.after) Command("sa", DotTwips(pf.after));
|
|
if(pf.tab != difpf.tab) PutTabs(pf.tab);
|
|
Command("fi", DotTwips(nind)); // we always put number last as quick patch to "space at bullet" problem
|
|
return stream.GetPos() != pos;
|
|
}
|
|
|
|
void RTFEncoder::PutTabs(const Vector<RichPara::Tab>& tabs)
|
|
{
|
|
for(int i = 0; i < tabs.GetCount(); i++) {
|
|
RichPara::Tab t = tabs[i];
|
|
switch(t.align) {
|
|
case ALIGN_NULL:
|
|
case ALIGN_LEFT: break;
|
|
case ALIGN_CENTER: Command("tqc"); break;
|
|
case ALIGN_RIGHT: Command("tqr"); break;
|
|
default: NEVER();
|
|
}
|
|
switch(t.fillchar) {
|
|
case 0: break;
|
|
case 1: Command("tldot"); break;
|
|
case 2: Command("tlhyph"); break;
|
|
case 3: Command("tlul"); break;
|
|
default: NEVER();
|
|
}
|
|
Command("tx", DotTwips(t.pos));
|
|
}
|
|
}
|
|
|
|
void RTFEncoder::PutParts(const Array<RichPara::Part>& parts,
|
|
RichPara::CharFormat& base, int bpart, int boff, int epart, int eoff)
|
|
{
|
|
if(epart >= parts.GetCount()) {
|
|
epart = parts.GetCount() - 1;
|
|
eoff = INT_MAX;
|
|
}
|
|
ASSERT(boff >= 0 && bpart >= 0);
|
|
for(int p = bpart; p <= epart; p++) {
|
|
const RichPara::Part& part = parts[p];
|
|
if(part.IsText()) {
|
|
WString px = part.text;
|
|
if(p == epart && px.GetLength() > eoff)
|
|
px.Trim(eoff);
|
|
if(p == bpart)
|
|
px.Remove(0, boff);
|
|
if(!px.IsEmpty()) {
|
|
if(PutCharFormat(part.format, base, false) || p == 0)
|
|
Space();
|
|
base = part.format;
|
|
PutText(px);
|
|
}
|
|
}
|
|
else if(part.object)
|
|
PutObject(part.object);
|
|
}
|
|
|
|
}
|
|
|
|
void RTFEncoder::PutHeader()
|
|
{
|
|
struct CodePage {
|
|
byte charset;
|
|
short ansicpg;
|
|
short fontcpg;
|
|
};
|
|
|
|
static const CodePage ansicpg[] = {
|
|
{ CHARSET_WIN1250, 1250, 238 },
|
|
{ CHARSET_WIN1251, 1251, 204 },
|
|
{ CHARSET_WIN1252, 1252, 0 },
|
|
{ CHARSET_WIN1253, 1253, 161 },
|
|
{ CHARSET_WIN1254, 1254, 162 },
|
|
{ CHARSET_WIN1255, 1255, 177 },
|
|
{ CHARSET_WIN1256, 1256, 178 },
|
|
{ CHARSET_WIN1257, 1257, 186 },
|
|
{ CHARSET_WIN1258, 1258, 163 },
|
|
};
|
|
int fontcpg = Null;
|
|
for(int i = __countof(ansicpg); --i >= 0;)
|
|
if(charset == ansicpg[i].charset) {
|
|
Command("ansicpg", ansicpg[i].ansicpg);
|
|
fontcpg = ansicpg[i].fontcpg;
|
|
break;
|
|
}
|
|
|
|
Command("deff", 0);
|
|
{
|
|
Group ftbl(this, "fonttbl");
|
|
for(int i = 0; i < used_faces.GetCount(); i++) {
|
|
Group fnt(this, "f", i);
|
|
int fn = used_faces[i];
|
|
int cs = fontcpg;
|
|
dword info = Font::GetFaceInfo(fn);
|
|
#ifdef PLATFORM_WIN32
|
|
if(fn == Font::SYMBOL) {
|
|
Command("ftech");
|
|
cs = 2;
|
|
}
|
|
else
|
|
#endif
|
|
if(info & Font::FIXEDPITCH)
|
|
Command("fmodern");
|
|
else if(fn == Font::ROMAN)
|
|
Command("froman");
|
|
else if(fn == Font::ARIAL
|
|
#ifdef PLATFORM_WIN32
|
|
|| fn == Font::TAHOMA
|
|
#endif
|
|
)
|
|
Command("fswiss");
|
|
if(!IsNull(fontcpg))
|
|
Command("fcharset", cs);
|
|
Space();
|
|
stream.Put(Font::GetFaceName(fn));
|
|
stream.Put(';');
|
|
}
|
|
}
|
|
|
|
if(phys_colors.GetCount() > 1) {
|
|
Group ctbl(this, "colortbl");
|
|
stream.Put(';');
|
|
for(int i = 1; i < phys_colors.GetCount(); i++) {
|
|
Color rgb = phys_colors[i];
|
|
Command("red", rgb.GetR());
|
|
Command("green", rgb.GetG());
|
|
Command("blue", rgb.GetB());
|
|
stream.Put(';');
|
|
}
|
|
}
|
|
|
|
Begin("stylesheet");
|
|
RichPara::CharFormat empcfmt;
|
|
RichPara::Format emppfmt;
|
|
|
|
for(int i = 0; i < richtext.GetStyleCount(); i++) {
|
|
const RichStyle& style = richtext.GetStyle(i);
|
|
Begin("s", i);
|
|
PutParaFormat(style.format, emppfmt);
|
|
PutCharFormat(style.format, empcfmt, false);
|
|
Command("sbasedon", 222);
|
|
if(style.next != richtext.GetStyleId(i))
|
|
Command("snext", styleid.Find(style.next));
|
|
Space();
|
|
for(const char *n = style.name; *n; n++)
|
|
stream.Put(*n == ';' ? '_' : *n);
|
|
stream.Put(';');
|
|
End();
|
|
}
|
|
End();
|
|
|
|
Command("paperw", DotTwips(dot_page_size.cx));
|
|
Command("paperh", DotTwips(dot_page_size.cy));
|
|
Command("margl", DotTwips(dot_margins.left));
|
|
Command("margr", DotTwips(dot_margins.right));
|
|
Command("margt", DotTwips(dot_margins.top));
|
|
Command("margb", DotTwips(dot_margins.bottom));
|
|
}
|
|
|
|
void RTFEncoder::PutTable(const RichTable& table, int nesting, int dot_width)
|
|
{
|
|
Vector<int> vspan_counts;
|
|
const RichTable::Format& tfmt = table.GetFormat();
|
|
Vector<int> column_pos;
|
|
column_pos.SetCount(tfmt.column.GetCount() + 1);
|
|
column_pos[0] = tfmt.lm;
|
|
int sum = 0;
|
|
for(int c = 0; c < tfmt.column.GetCount(); c++)
|
|
sum += tfmt.column[c];
|
|
int rem_width = dot_width - tfmt.lm - tfmt.rm;
|
|
for(int c = 0; c < tfmt.column.GetCount(); c++) {
|
|
int part = iscale(tfmt.column[c], rem_width, max(sum, 1));
|
|
sum -= tfmt.column[c];
|
|
rem_width -= part;
|
|
column_pos[c + 1] = column_pos[c] + part;
|
|
}
|
|
for(int r = 0; r < table.GetRows(); r++) {
|
|
StringBuffer rowfmt;
|
|
if(nesting)
|
|
rowfmt << "\\*\\nesttableprops";
|
|
rowfmt << "\\trowd"
|
|
"\\trleft" << DotTwips(tfmt.lm)
|
|
// trbrdr[ltrbv]
|
|
;
|
|
Vector<int> cellindex;
|
|
Rect dflt_margin(600, 600, 600, 600);
|
|
for(int c = 0; c < table.GetColumns(); c++) {
|
|
const RichCell& cell = table[r][c];
|
|
dflt_margin.left = min(dflt_margin.left, cell.format.margin.left);
|
|
dflt_margin.top = min(dflt_margin.top, cell.format.margin.top);
|
|
dflt_margin.right = min(dflt_margin.right, cell.format.margin.right);
|
|
dflt_margin.bottom = min(dflt_margin.bottom, cell.format.margin.bottom);
|
|
c += cell.hspan;
|
|
}
|
|
/*
|
|
rowfmt <<
|
|
"\\trpaddl" << DotTwips(dflt_margin.left) << "\\trpaddfl3"
|
|
"\\trpaddt" << DotTwips(dflt_margin.top) << "\\trpaddft3"
|
|
"\\trpaddr" << DotTwips(dflt_margin.right) << "\\trpaddfr3"
|
|
"\\trpaddb" << DotTwips(dflt_margin.bottom) << "\\trpaddfb3";
|
|
*/
|
|
bool istop = (r == 0);
|
|
bool isbottom = (r == table.GetRows() - 1);
|
|
for(int c = 0; c < table.GetColumns(); c++) {
|
|
const RichCell& cell = table[r][c];
|
|
/*
|
|
if(cell.format.margin.left != dflt_margin.left)
|
|
rowfmt << "\\clpadl" << DotTwips(cell.format.margin.left) << "\\clpadfl3";
|
|
if(cell.format.margin.top != dflt_margin.top)
|
|
rowfmt << "\\clpadt" << DotTwips(cell.format.margin.top) << "\\clpadft3";
|
|
if(cell.format.margin.right != dflt_margin.right)
|
|
rowfmt << "\\clpadr" << DotTwips(cell.format.margin.right) << "\\clpadfr3";
|
|
if(cell.format.margin.bottom != dflt_margin.bottom)
|
|
rowfmt << "\\clpadb" << DotTwips(cell.format.margin.bottom) << "\\clpadfb3";
|
|
*/
|
|
switch(cell.format.align) {
|
|
case ALIGN_TOP: rowfmt << "\\clvertalt"; break;
|
|
default:
|
|
case ALIGN_CENTER: rowfmt << "\\clvertalc"; break;
|
|
case ALIGN_BOTTOM: rowfmt << "\\clvertalb"; break;
|
|
}
|
|
bool isleft = (c == 0);
|
|
cellindex.Add(c);
|
|
if(cell.hspan)
|
|
c += cell.hspan;
|
|
bool isright = (c + 1 == table.GetColumns());
|
|
int cell_end = column_pos[c + 1];
|
|
if(cell.vspan) {
|
|
vspan_counts.At(c, 0) = cell.vspan;
|
|
rowfmt << "\\clvmgf";
|
|
}
|
|
else if(c < vspan_counts.GetCount() && vspan_counts[c] > 0)
|
|
rowfmt << "\\clvmrg";
|
|
if(!IsNull(cell.format.color))
|
|
rowfmt << "\\clcbpat" << phys_colors.Find(cell.format.color);
|
|
int lb, tb, rb, bb;
|
|
Color lc, tc, rc, bc;
|
|
const RichTable::Format& tf = table.GetFormat();
|
|
lc = tc = rc = bc = tf.gridcolor;
|
|
lb = tb = rb = bb = (!IsNull(tf.gridcolor) ? tf.grid : 0);
|
|
int fw = (!IsNull(tf.framecolor) ? tf.frame : 0);
|
|
if(isleft) {
|
|
lb = fw;
|
|
lc = tf.framecolor;
|
|
}
|
|
if(isright) {
|
|
rb = fw;
|
|
rc = tf.framecolor;
|
|
}
|
|
if(istop) {
|
|
tb = fw;
|
|
tc = tf.framecolor;
|
|
}
|
|
if(isbottom) {
|
|
bb = fw;
|
|
bc = tf.framecolor;
|
|
}
|
|
if(!IsNull(cell.format.bordercolor)) {
|
|
if(cell.format.border.left >= max(lb, 1)) {
|
|
lb = cell.format.border.left;
|
|
lc = cell.format.bordercolor;
|
|
}
|
|
if(cell.format.border.top >= max(tb, 1)) {
|
|
tb = cell.format.border.top;
|
|
tc = cell.format.bordercolor;
|
|
}
|
|
if(cell.format.border.right >= max(rb, 1)) {
|
|
rb = cell.format.border.right;
|
|
rc = cell.format.bordercolor;
|
|
}
|
|
if(cell.format.border.bottom >= max(bb, 1)) {
|
|
bb = cell.format.border.bottom;
|
|
bc = cell.format.bordercolor;
|
|
}
|
|
}
|
|
PutBorder(rowfmt, lb, lc, "clbrdrl");
|
|
PutBorder(rowfmt, tb, tc, "clbrdrt");
|
|
PutBorder(rowfmt, rb, rc, "clbrdrr");
|
|
PutBorder(rowfmt, bb, bc, "clbrdrb");
|
|
rowfmt << "\\cellx" << DotTwips(cell_end);
|
|
}
|
|
Begin();
|
|
Command("pard");
|
|
Command("intbl");
|
|
parafmt = RichPara::Format();
|
|
charfmt = RichPara::CharFormat();
|
|
if(nesting)
|
|
Command("itap", nesting + 1);
|
|
String fmtstr = rowfmt;
|
|
if(!nesting)
|
|
stream.Put(fmtstr);
|
|
for(int c = 0; c < cellindex.GetCount(); c++) {
|
|
int cx = cellindex[c];
|
|
const RichCell& cell = table[r][cx];
|
|
int cell_wd = column_pos[cx + cell.hspan + 1] - column_pos[cx];
|
|
PutTxt(cell.text, nesting + 1, cell_wd);
|
|
Command(nesting ? "nestcell" : "cell");
|
|
}
|
|
if(nesting)
|
|
stream.Put(fmtstr);
|
|
Command(nesting ? "nestrow" : "row");
|
|
End();
|
|
}
|
|
oldstyle = Null;
|
|
}
|
|
|
|
void RTFEncoder::PutBorder(StringBuffer& rowfmt, int wd, Color c, const char *clpar)
|
|
{
|
|
if(wd > 0) {
|
|
rowfmt << "\\" << clpar << "\\brdrs\\brdrw" << wd << "\\brdrcf"
|
|
<< phys_colors.Find(Nvl(c, Black())) << " ";
|
|
}
|
|
}
|
|
|
|
void RTFEncoder::PutTxt(const RichTxt& txt, int nesting, int dot_width)
|
|
{
|
|
for(int i = 0; i < txt.GetPartCount(); i++) {
|
|
if(txt.IsTable(i))
|
|
PutTable(txt.GetTable(i), nesting, dot_width);
|
|
else {
|
|
const RichPara& para = txt.Get(i, richtext.GetStyles());
|
|
Uuid pstyle = txt.GetParaStyle(i);
|
|
int new_para_ht = GetParaHeight(para.part);
|
|
int first_part = 0, first_ind = 0;
|
|
bool hdiff = (para.format.bullet != RichPara::BULLET_NONE
|
|
&& para.format.bullet != RichPara::BULLET_TEXT && new_para_ht != para_ht);
|
|
if(pstyle != oldstyle
|
|
|| para.format.bullet != parafmt.bullet || para.format.bullet == RichPara::BULLET_TEXT
|
|
|| hdiff || para.format.tab != parafmt.tab || para.format.newpage || parafmt.newpage) {
|
|
Command("s", styleid.Find(pstyle));
|
|
oldstyle = pstyle;
|
|
parafmt = richtext.GetStyle(pstyle).format;
|
|
Command("pard");
|
|
if(nesting)
|
|
Command("intbl");
|
|
if(nesting > 1)
|
|
Command("itap", nesting);
|
|
// parafmt = RichPara::Format();
|
|
// bool isul = charfmt.IsUnderline();
|
|
// charfmt = RichPara::CharFormat();
|
|
// charfmt.Underline(isul);
|
|
para_ht = 0;
|
|
}
|
|
if(para.format.bullet == RichPara::BULLET_TEXT) {
|
|
int epart = 0, eoff = 0;
|
|
while(epart < para.part.GetCount() && (eoff = para.part[epart].text.Find('\t')) < 0)
|
|
epart++;
|
|
|
|
RichPara::CharFormat rcf = charfmt;
|
|
rcf.Height(0);
|
|
Begin("pntext");
|
|
PutParts(para.part, rcf, 0, 0, epart, eoff);
|
|
stream.Put('\t');
|
|
End();
|
|
|
|
Begin("*\\pn");
|
|
Command("pnhang");
|
|
Command("pnql");
|
|
rcf = charfmt;
|
|
if(epart > 0 || eoff > 0)
|
|
PutCharFormat(para.part[0].format, rcf, true);
|
|
Begin("pntxta");
|
|
End();
|
|
Begin("pntxtb");
|
|
Space();
|
|
PutParts(para.part, rcf, 0, 0, epart, eoff);
|
|
stream.Put('\t');
|
|
End();
|
|
End();
|
|
}
|
|
else if(para.format.bullet != RichPara::BULLET_NONE) {
|
|
int sym_face = SYMBOL_INDEX;
|
|
byte sym_char = 0xB7;
|
|
|
|
switch(para.format.bullet) {
|
|
default:
|
|
NEVER();
|
|
|
|
case RichPara::BULLET_ROUND: sym_face = SYMBOL_INDEX; sym_char = 0xB7; break;
|
|
case RichPara::BULLET_ROUNDWHITE: sym_face = WINGDINGS_INDEX; sym_char = 0xA1; break;
|
|
case RichPara::BULLET_BOX: sym_face = WINGDINGS_INDEX; sym_char = 'n'; break;
|
|
case RichPara::BULLET_BOXWHITE: sym_face = WINGDINGS_INDEX; sym_char = 'o'; break;
|
|
}
|
|
|
|
Begin("pntext");
|
|
// Command("tqr");
|
|
// Command("tx", DotTwips(para.format.indent));
|
|
// Command("qr");
|
|
Command("f", sym_face);
|
|
Command("fs", DotPoints(2 * new_para_ht));
|
|
Space();
|
|
stream.Put(sym_char);
|
|
stream.Put('\t');
|
|
End();
|
|
|
|
Begin("*\\pn");
|
|
Command("pnlvlblt");
|
|
Command("pnf", sym_face);
|
|
Command("pnfs", DotPoints(2 * new_para_ht));
|
|
Command("pnql");
|
|
Begin("pntxtb");
|
|
Space();
|
|
stream.Put(sym_char);
|
|
// stream.Put('\t');
|
|
End();
|
|
Begin("pntxta");
|
|
End();
|
|
End();
|
|
}
|
|
PutParaFormat(para.format, parafmt);
|
|
para_ht = new_para_ht;
|
|
parafmt = para.format;
|
|
PutParts(para.part, charfmt, first_part, first_ind, para.part.GetCount(), 0);
|
|
if(i + 1 < txt.GetPartCount())
|
|
Command("par");
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFEncoder::PutDocument()
|
|
{
|
|
charfmt.Height(PointDots(0));
|
|
charfmt.language = 0;
|
|
old_ht = DotPoints(2 * tabs(charfmt.GetHeight()));
|
|
para_ht = 0;
|
|
oldstyle = Uuid::Create();
|
|
PutTxt(richtext, 0, dot_page_size.cx - dot_margins.left - dot_margins.right);
|
|
}
|
|
|
|
}
|