RichText: Diagram Images

This commit is contained in:
Mirek Fidler 2025-07-28 18:31:10 +02:00
parent 9d477ad65b
commit 62895f4df8
9 changed files with 288 additions and 165 deletions

View file

@ -165,30 +165,36 @@ String BinDiff(const String& base, const String& data)
return diff;
}
void BinUndoRedo::Reset(const String& current)
void BinUndoRedo::Reset(const String& current, const String& ids)
{
undo.Clear();
redo.Clear();
undosize = 0;
commit = current;
commit.data = current;
commit.ids = ids;
}
bool BinUndoRedo::Commit(const String& current, int limit)
bool BinUndoRedo::Commit(const String& current, const String& ids, int limit)
{
bool ret = false;
if(redo.GetCount()) {
redo.Clear();
ret = true;
}
if(current != commit) {
String u = BinDiff(current, commit);
if(current != commit.data || ids != commit.ids) {
String u = BinDiff(current, commit.data);
while(undo.GetCount() && undosize + u.GetCount() > limit) {
undosize -= undo[0].GetCount();
undosize -= undo[0].data.GetCount();
undosize -= undo[0].ids.GetCount();
undo.Remove(0);
}
undo.Add(u);
Entry& e = undo.Add();
e.data = u;
e.ids = commit.ids;
undosize += u.GetCount();
commit = current;
undosize += ids.GetCount();
commit.data = current;
commit.ids = ids;
ret = true;
}
return ret;
@ -197,10 +203,13 @@ bool BinUndoRedo::Commit(const String& current, int limit)
String BinUndoRedo::Undo(const String& current)
{
if(undo.GetCount()) {
String prev = undo.Pop();
commit = BinUndiff(commit, prev);
redo.Add(BinDiff(commit, current));
return commit;
Entry prev = undo.Pop();
commit.data = BinUndiff(commit.data, prev.data);
Entry& e = redo.Add();
e.data = BinDiff(commit.data, current);
e.ids = prev.ids;
return commit.data;
}
return String::GetVoid();
}
@ -208,12 +217,23 @@ String BinUndoRedo::Undo(const String& current)
String BinUndoRedo::Redo(const String& current)
{
if(redo.GetCount()) {
String next = redo.Pop();
commit = BinUndiff(commit, next);
undo.Add(BinDiff(commit, current));
return commit;
Entry next = redo.Pop();
commit.data = BinUndiff(commit.data, next.data);
Entry& e = undo.Add();
e.data = BinDiff(commit.data, current);
e.ids = next.ids;
return commit.data;
}
return String::GetVoid();
}
}
void BinUndoRedo::Ids(Event<const String&> fn)
{
fn(commit.ids);
for(const Entry& e : undo)
fn(e.ids);
for(const Entry& e : redo)
fn(e.ids);
}
}

View file

@ -2,16 +2,22 @@ String BinDiff(const String& base, const String& data);
String BinUndiff(const String& base, const String& bin_diff);
class BinUndoRedo {
String commit;
Vector<String> undo;
Vector<String> redo;
struct Entry : Moveable<Entry> {
String data;
String ids;
};
Entry commit;
Vector<Entry> undo;
Vector<Entry> redo;
int undosize = 0;
public:
void Reset(const String& current);
bool Commit(const String& current, int limit = 4096*1024);
bool IsUndo() const { return undo.GetCount(); }
bool IsRedo() const { return redo.GetCount(); }
void Reset(const String& current, const String& ids = String());
bool Commit(const String& current, const String& ids, int limit = 4096*1024);
bool Commit(const String& current, int limit = 4096*1024) { return Commit(current, String(), limit); }
bool IsUndo() const { return undo.GetCount(); }
bool IsRedo() const { return redo.GetCount(); }
String Undo(const String& current);
String Redo(const String& current);
void Ids(Event<const String&> fn);
};

View file

@ -17,8 +17,10 @@ void DiagramEditor::Delete()
void DiagramEditor::Copy()
{
StringBuffer cb;
for(int i : sel)
data.item[i].Save(cb);
for(int i : sel) {
const DiagramItem& m = data.item[i];
m.Save(cb, m.blob_id);
}
WriteClipboardText(cb);
}

View file

@ -144,7 +144,8 @@ Image DiagramEditor::MakeIcon(DiagramItem& m, Size isz)
Image Make() const override {
ImagePainter iw(isz);
iw.Clear();
m.Paint(iw, dark ? DiagramItem::DARK : 0);
VectorMap<int, String> data;
m.Paint(iw, data, dark ? DiagramItem::DARK : 0);
return iw;
}
};
@ -253,6 +254,7 @@ void DiagramEditor::ResetUndo()
void DiagramEditor::Commit()
{
RTIMING("Commit");
if(IsCursor()) {
DiagramItem& m = CursorItem();
if(!m.IsLine())

View file

@ -278,12 +278,11 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
Map(p);
auto PopPaint = [=](Draw& w, const Image& m, bool sel) {
int x = DPI(2);
int y = DPI(1);
Point p = Rect(IconSz()).CenterPos(m.GetSize());
if(sel)
w.DrawImage(x, y, m, SColorHighlightText());
w.DrawImage(p.x, p.y, m, SColorHighlightText());
else
w.DrawImage(x, y, m);
w.DrawImage(p.x, p.y, m);
};
FinishText();
@ -335,7 +334,10 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
shape.columns = 3;
shape.isz = IconSz() + Size(DPI(4), DPI(4));
shape.WhenPaintItem = [=](Draw& w, Size isz, int ii, bool sel) {
PopPaint(w, ii == DiagramItem::SHAPE_SVGPATH ? DiagramImg::FontSvg() : ShapeIcon(ii), sel);
PopPaint(w, ii == DiagramItem::SHAPE_SVGPATH ? DiagramImg::FontSvg() :
ii == DiagramItem::SHAPE_IMAGE ? CtrlImg::open()
: ShapeIcon(ii),
sel && ii != DiagramItem::SHAPE_IMAGE);
};
tool = -1;
@ -346,12 +348,39 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
return;
Sizef size;
String svgpath;
String mdata;
if(si == DiagramItem::SHAPE_SVGPATH) {
svgpath = SelectFontSymbolSvg(size);
if(IsNull(svgpath))
mdata = SelectFontSymbolSvg(size);
if(IsNull(mdata))
return;
}
if(si == DiagramItem::SHAPE_IMAGE) {
String path = SelectFileOpen("Images (*.png *.gif *.jpg *.bmp *.svg)\t*.png *.gif *.jpg *.bmp *.svg");
if(GetFileLength(path) > 17000000) {
Exclamation("Image is too large!");
return;
}
mdata = LoadFile(path);
if(IsNull(mdata))
return;
size = Null;
if(IsSVG(mdata)) {
Rectf f = GetSVGBoundingBox(mdata);
size = f.GetSize();
}
else {
StringStream ss(mdata);
One<StreamRaster> r = StreamRaster::OpenAny(ss);
if(r)
size = r->GetSize();
}
if(IsNull(size)) {
Exclamation(Format(t_("Unsupported image format in file [* \1%s\1]."), path));
return;
}
}
CancelSelection();
@ -380,8 +409,17 @@ void DiagramEditor::RightDown(Point p, dword keyflags)
m.pt[1] = p;
}
m.shape = si; // shape must be set before SetAttrs to avoid Normalise
m.data = svgpath;
if(mdata.GetCount())
m.blob_id = data.AddBlob(mdata);
m.size = size;
if(si == DiagramItem::SHAPE_IMAGE) {
m.ink = Null;
m.paper = Black();
m.width = 0;
m.pt[1] = m.pt[0] + size;
SetAttrs(ATTR_ALL & ~(ATTR_SHAPE|ATTR_PAPER|ATTR_INK|ATTR_WIDTH));
}
else
if(si == DiagramItem::SHAPE_SVGPATH) {
m.ink = Null;
m.paper = Black();

View file

@ -83,6 +83,7 @@ void SelectSymbolDlg::Variants(int codepoint)
{
String qtf = "[A5 ";
Index<Image> h;
svg.Clear();
for(int i = 0; i < Font::GetFaceCount(); i++) {
Font fnt(i, 10);
if(HasCodepoint(fnt, codepoint)) {

View file

@ -30,7 +30,7 @@ void DiagramItem::Reset()
width = 2;
ink = Black();
paper = White();
data.Clear();
blob_id = 0;
size = Null;
cap[0] = cap[1] = CAP_NONE;
@ -129,7 +129,7 @@ Rect DiagramItem::GetTextEditRect() const
return GetRect();
}
void DiagramItem::Save(StringBuffer& r) const
void DiagramItem::Save(StringBuffer& r, int blob_id) const
{
r << Shape[clamp(shape, 0, Shape.GetCount() - 1)] << ' ';
r << pt[0].x << ' ' << pt[0].y << ' ' << pt[1].x << ' ' << pt[1].y;
@ -140,8 +140,8 @@ void DiagramItem::Save(StringBuffer& r) const
return String("null");
return Format("%02x%02x%02x", (int)c.GetR(), (int)c.GetG(), (int)c.GetB());
};
if(data.GetCount())
r << " data " << AsCString(data);
if(blob_id)
r << " blob_id " << blob_id;
if(!IsNull(size))
r << " size " << size.cx << ' ' << size.cy;
if(ink != Black())
@ -202,8 +202,8 @@ void DiagramItem::Load(CParser& p)
if(p.Id("dash"))
dash = clamp(p.ReadInt(), 0, (int)DASH_COUNT);
else
if(p.Id("data"))
data = p.ReadString();
if(p.Id("blob_id"))
blob_id = p.ReadInt();
else
if(p.Id("size")) {
size.cx = p.ReadDouble();
@ -238,6 +238,19 @@ Size Diagram::GetSize() const
return Size(ceil(max(isz.cx, fsz.cx)), ceil(max(isz.cy, fsz.cy)));
}
int Diagram::AddBlob(const String& data_)
{
int newid = 1;
for(const auto& kv : ~blob) {
if(kv.value == data_)
return kv.key;
newid = max(newid, kv.key + 1);
}
blob.Add(newid, data_);
return newid;
}
void Diagram::Paint(Painter& w, const Diagram::PaintInfo& p) const
{
w.Begin();
@ -263,7 +276,7 @@ void Diagram::Paint(Painter& w, const Diagram::PaintInfo& p) const
style |= DiagramItem::GRID;
if(p.dark)
style |= DiagramItem::DARK;
item[i].Paint(w, style, &conn);
item[i].Paint(w, blob, style, &conn);
}
}
@ -282,15 +295,33 @@ void Diagram::Save(StringBuffer& r) const
r << "HD ";
r << AsCString(Base64Encode(PNGEncoder().SaveString(img))) << ";\n";
}
VectorMap<int64, String> sd;
for(const DiagramItem& m : item) {
m.Save(r);
int id = 0;
if(m.blob_id) {
int q = blob.Find(m.blob_id);
if(q >= 0) {
id = sd.Find(m.blob_id);
if(id < 0) {
sd.Add(m.blob_id, blob[q]);
id = sd.GetCount();
}
else
id++;
}
}
m.Save(r, id);
r << '\n';
}
for(int i = 0; i < sd.GetCount(); i++)
r << "blob " << i + 1 << " " << AsCString(Base64Encode(sd[i]), 1000, " ") << ";\n";
}
void Diagram::Load(CParser& p)
{
item.Clear();
blob.Clear();
while(!p.IsEof())
if(p.Id("size")) {
size.cx = clamp(p.ReadInt(), 1, 10000);
@ -303,6 +334,13 @@ void Diagram::Load(CParser& p)
img = StreamRaster::LoadStringAny(Base64Decode(p.ReadString()));
p.Char(';');
}
else
if(p.Id("blob")) {
int id = p.ReadInt();
String data = Base64Decode(p.ReadString());
blob.Add(id, data);
p.Char(';');
}
else
item.Add().Load(p);
}

View file

@ -17,7 +17,7 @@ struct DiagramItem : Point2 {
Color ink;
Color paper;
Sizef size;
String data;
int blob_id;
enum {
SHAPE_LINE,
@ -37,6 +37,7 @@ struct DiagramItem : Point2 {
SHAPE_ARROWUP,
SHAPE_ARROWVERT,
SHAPE_SVGPATH,
SHAPE_IMAGE,
SHAPE_COUNT,
};
@ -61,7 +62,7 @@ struct DiagramItem : Point2 {
int cap[2] = { CAP_NONE, CAP_NONE };
int dash = 0;
void Paint(Painter& w, dword style = 0, const Index<Pointf> *conn = nullptr) const;
void Paint(Painter& w, const VectorMap<int, String>& data, dword style = 0, const Index<Pointf> *conn = nullptr) const;
bool IsLine() const { return shape == SHAPE_LINE; }
@ -73,10 +74,10 @@ struct DiagramItem : Point2 {
void FixPosition();
void Serialize(Stream& s) { Point2::Serialize(s); s % shape % ink % paper % qtf % width % cap[0] % cap[1] % dash % size % data; }
void Serialize(Stream& s) { Point2::Serialize(s); s % shape % ink % paper % qtf % width % cap[0] % cap[1] % dash % size % blob_id; }
void Reset();
void Save(StringBuffer& r) const;
void Save(StringBuffer& r, int blob_id) const;
void Load(CParser& p);
DiagramItem() { Reset(); }
@ -87,10 +88,11 @@ private:
};
struct Diagram {
Size size = Null; // Null - auto
Array<DiagramItem> item;
Image img;
bool img_hd = false;
Size size = Null; // Null - auto
Array<DiagramItem> item;
Image img;
bool img_hd = false;
VectorMap<int, String> blob;
struct PaintInfo {
bool editor = false;
@ -102,6 +104,7 @@ struct Diagram {
Size GetSize() const;
void Paint(Painter& w, const PaintInfo& pi) const;
int AddBlob(const String& data);
void Serialize(Stream& s);
void Save(StringBuffer& r) const;
void Load(CParser& p);

View file

@ -8,7 +8,7 @@ Index<String> DiagramItem::Shape = { "line", "rect", "round_rect",
"triangle1", "triangle2",
"arrow_left", "arrow_right", "arrow_horz",
"arrow_down", "arrow_up", "arrow_vert",
"svgpath",
"svgpath", "image"
};
Vector<Pointf> DiagramItem::GetConnections() const
@ -27,7 +27,7 @@ Vector<Pointf> DiagramItem::GetConnections() const
return p;
}
void DiagramItem::Paint(Painter& w, dword style, const Index<Pointf> *conn) const
void DiagramItem::Paint(Painter& w, const VectorMap<int, String>& data, dword style, const Index<Pointf> *conn) const
{
bool dark = style & DARK;
@ -142,199 +142,212 @@ void DiagramItem::Paint(Painter& w, dword style, const Index<Pointf> *conn) cons
Rectf r(pt[0], pt[1]);
r.Normalize();
r.Deflate(width / 2);
Rect text_rect = r.Deflated(width + 2, 0);
Pointf c = r.CenterPoint();
double sz = min(r.Width(), r.Height());
double arrow_width = min(r.Width() / 3, r.Height() / 2);
double h = r.GetHeight();
double h4 = h / 4;
double th4 = r.top + h4;
double bh4 = r.bottom - h4;
double arrow_height = min(r.Height() / 3, r.Width() / 2);
w.Begin();
w.Offset(r.left, r.top);
double w1 = r.GetWidth();
double h = r.GetHeight();
double cx = r.GetWidth();
double cy = r.GetHeight();
Rect text_rect = r.Deflated(width + 2, 0);
double sz = min(cx, cy);
double arrow_width = min(cx / 3, cy / 3);
double arrow_height = min(cx / 3, cy / 3);
double h2 = h / 2;
double h4 = h / 4;
double bh4 = h - h4;
double w2 = w1 / 2;
double w4 = w1 / 4;
double lw4 = r.left + w4;
double rw4 = r.right - w4;
Pointf m(w2, h2);
double hc, thc, bhc; // cylinder
switch(shape) {
case SHAPE_ROUNDRECT:
w.RoundedRectangle(r.left, r.top, r.GetWidth(), r.GetHeight(), sz > 30 ? 8 : sz > 15 ? 4 : 2);
w.RoundedRectangle(0, 0, w1, h, sz > 30 ? 8 : sz > 15 ? 4 : 2);
break;
case SHAPE_OVAL:
if(r.GetWidth() > r.GetHeight()) {
double ra = r.GetHeight() / 2;
w.Move(r.left + ra, r.top)
.Line(r.right - ra, r.top)
.Arc(r.right - ra, r.top + ra, ra, -M_PI / 2, M_PI)
.Line(r.left + ra, r.bottom)
.Arc(r.left + ra, r.top + ra, ra, M_PI / 2, M_PI);
if(w1 > h) {
double ra = h2;
w.Move(ra, 0)
.Line(w1 - ra, 0)
.Arc(w1 - ra, 0 + ra, ra, -M_PI / 2, M_PI)
.Line(ra, cy)
.Arc(ra, 0 + ra, ra, M_PI / 2, M_PI);
break;
}
case SHAPE_ELLIPSE:
w.Ellipse(r);
w.Ellipse(m.x, m.y, w2, h2);
break;
case SHAPE_DIAMOND:
w.Move(c.x, r.top).Line(r.right, c.y).Line(c.x, r.bottom).Line(r.left, c.y).Close();
w.Move(m.x, 0).Line(cx, m.y).Line(m.x, cy).Line(0, m.y).Close();
break;
case SHAPE_PARALLELOGRAM:
w.Move(r.left + r.Width() / 6, r.top).Line(r.right, r.top)
.Line(r.right - r.Width() / 6, r.bottom).Line(r.left, r.bottom).Close();
w.Move(cx / 6, 0).Line(cx, 0).Line(5 * cx / 6, cy).Line(0, cy).Close();
break;
case SHAPE_CYLINDER:
text_rect.top += int(w1 / 4);
hc = h / 6;
thc = r.top + hc;
bhc = r.bottom - hc;
w.Move(r.left, thc)
.Arc(r.left + w2, thc, w2, hc, M_PI, M_PI)
.Line(r.right, bhc)
.Arc(r.left + w2, bhc, w2, hc, 0, M_PI)
.Line(r.left, bhc);
thc = 0 + hc;
bhc = cy - hc;
w.Move(0, thc)
.Arc(0 + w2, thc, w2, hc, M_PI, M_PI)
.Line(cx, bhc)
.Arc(0 + w2, bhc, w2, hc, 0, M_PI)
.Line(0, bhc);
break;
case SHAPE_TRIANGLE: {
text_rect.left += int(r.Width() / 4);
text_rect.right -= int(r.Width() / 4);
text_rect.top += int(r.Width() / 3);
w.Move(r.left + r.Width() / 2, r.top)
.Line(r.right, r.bottom)
.Line(r.left, r.bottom)
.Close();
text_rect.left += w4;
text_rect.right -= w4;
text_rect.top += int(cx / 3);
w.Move(w2, 0).Line(cx, cy).Line(0, cy).Close();
}
break;
case SHAPE_ITRIANGLE: {
text_rect.left += int(r.Width() / 4);
text_rect.right -= int(r.Width() / 4);
text_rect.bottom -= int(r.Width() / 3);
w.Move(r.left + r.Width() / 2, r.bottom)
.Line(r.right, r.top)
.Line(r.left, r.top)
.Close();
text_rect.left += int(cx / 4);
text_rect.right -= int(cx / 4);
text_rect.bottom -= int(cx / 3);
w.Move(w2, cy).Line(cx, 0).Line(0, 0).Close();
}
break;
case SHAPE_ARROWLEFT: {
double a = r.left + arrow_width;
double a = 0 + arrow_width;
text_rect.left += int(arrow_width / 3);
w.Move(r.left, r.top + r.Height() / 2)
.Line(a, r.top)
.Line(a, th4)
.Line(r.right, th4)
.Line(r.right, bh4)
.Line(a, bh4)
.Line(a, r.bottom)
.Close();
w.Move(0, h2).Line(a, 0).Line(a, h4).Line(cx, h4)
.Line(cx, bh4).Line(a, bh4).Line(a, cy).Close();
}
break;
case SHAPE_ARROWRIGHT:
{
double a = r.right - arrow_width;
double a = cx - arrow_width;
text_rect.right -= int(arrow_width / 3);
w.Move(r.right, r.top + r.Height() / 2)
.Line(a, r.top)
.Line(a, th4)
.Line(r.left, th4)
.Line(r.left, bh4)
w.Move(cx, cy / 2)
.Line(a, 0)
.Line(a, h4)
.Line(0, h4)
.Line(0, bh4)
.Line(a, bh4)
.Line(a, r.bottom)
.Line(a, cy)
.Close();
}
break;
case SHAPE_ARROWHORZ:
{
double a1 = r.left + arrow_width;
double a1 = 0 + arrow_width;
text_rect.left += int(arrow_width / 3);
double a2 = r.right - arrow_width;
double a2 = cx - arrow_width;
text_rect.right -= int(arrow_width / 3);
w.Move(r.left, r.top + r.Height() / 2)
.Line(a1, r.top)
.Line(a1, th4)
.Line(a2, th4)
.Line(a2, r.top)
.Line(r.right, r.top + r.Height() / 2)
.Line(a2, r.bottom)
w.Move(0, h2)
.Line(a1, 0)
.Line(a1, h4)
.Line(a2, h4)
.Line(a2, 0)
.Line(cx, h2)
.Line(a2, cy)
.Line(a2, bh4)
.Line(a1, bh4)
.Line(a1, r.bottom)
.Line(a1, cy)
.Close();
}
break;
case SHAPE_ARROWUP: {
double a = r.top + arrow_height;
double a = arrow_height;
text_rect.left += w4;
text_rect.right -= w4;
text_rect.top += 3 * arrow_height / 4;
w.Move(r.left + r.Width() / 2, r.top)
.Line(r.right, a)
.Line(rw4, a)
.Line(rw4, r.bottom)
.Line(lw4, r.bottom)
.Line(lw4, a)
.Line(r.left, a)
w.Move(w2, 0)
.Line(cx, a)
.Line(cx - w4, a)
.Line(cx - w4, cy)
.Line(w4, cy)
.Line(w4, a)
.Line(0, a)
.Close();
}
break;
case SHAPE_ARROWDOWN: {
double a = r.bottom - arrow_height;
double a = cy - arrow_height;
text_rect.left += w4;
text_rect.right -= w4;
text_rect.bottom -= 3 * arrow_height / 4;
w.Move(r.left + r.Width() / 2, r.bottom)
.Line(r.right, a)
.Line(rw4, a)
.Line(rw4, r.top)
.Line(lw4, r.top)
.Line(lw4, a)
.Line(r.left, a)
w.Move(0 + cx / 2, cy)
.Line(cx, a)
.Line(cx - w4, a)
.Line(cx - w4, 0)
.Line(w4, 0)
.Line(w4, a)
.Line(0, a)
.Close();
}
break;
case SHAPE_ARROWVERT: {
double a1 = r.top + arrow_height;
double a2 = r.bottom - arrow_height;
double a1 = 0 + arrow_height;
double a2 = cy - arrow_height;
text_rect.left += w4;
text_rect.right -= w4;
w.Move(r.left + r.Width() / 2, r.top)
.Line(r.right, a1)
.Line(rw4, a1)
.Line(rw4, a2)
.Line(r.right, a2)
.Line(r.left + r.Width() / 2, r.bottom)
.Line(r.left, a2)
.Line(lw4, a2)
.Line(lw4, a1)
.Line(r.left, a1)
w.Move(0 + cx / 2, 0)
.Line(cx, a1)
.Line(cx - w4, a1)
.Line(cx - w4, a2)
.Line(cx, a2)
.Line(0 + cx / 2, cy)
.Line(0, a2)
.Line(w4, a2)
.Line(w4, a1)
.Line(0, a1)
.Close();
}
break;
case SHAPE_SVGPATH:
if(data.GetCount() && !IsNull(size)) {
w.Begin();
w.Offset(r.TopLeft());
w.Scale(w1 / size.cx, h / size.cy);
w.Path(data);
w.Path(data.Get(blob_id, String()));
}
break;
case SHAPE_IMAGE:
if(data.GetCount()) {
String s = data.Get(blob_id, String());
if(s.GetCount()) {
StringStream ss(s);
One<StreamRaster> r = StreamRaster::OpenAny(ss);
if(r) {
w.DrawImage(0, 0, cx, cy, r->GetImage());
}
else
if(IsSVG(s)) {
Rectf f = GetSVGBoundingBox(s);
Sizef isz = f.GetSize();
w.Scale(cx / f.GetWidth(), cy / f.GetHeight());
w.Translate(-f.left, -f.top);
RenderSVG(w, s, Event<String, String&>(), paper);
}
}
}
break;
default:
w.Rectangle(r);
w.Rectangle(0, 0, cx, cy);
break;
}
DoDash();
w.Fill(paper);
Stroke();
if(shape != SHAPE_IMAGE) {
DoDash();
w.Fill(paper);
Stroke();
}
switch(shape) {
case SHAPE_CYLINDER:
w.Move(r.left, thc)
.Arc(r.left + w2, thc, w2, hc, M_PI, -M_PI);
w.Move(0, thc)
.Arc(0 + w2, thc, w2, hc, M_PI, -M_PI);
DoDash();
Stroke();
break;
case SHAPE_SVGPATH:
if(data.GetCount() && !IsNull(size))
w.End();
break;
}
w.End();
int txt_cy = txt.GetHeight(pi.zoom, text_rect.GetWidth());
txt.Paint(w, text_rect.left, text_rect.top + (text_rect.GetHeight() - txt_cy) / 2, text_rect.GetWidth(), pi);