mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
750 lines
23 KiB
C++
750 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");
|
|
PutBinHex(PNGEncoder().SaveString(object.ToImage(out_size)));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
}
|