#include "RichText.h" namespace Upp { String HtmlFontStyle(Font f, Font base) { String style; if(f.GetFace() != base.GetFace()) switch(f.GetFace()) { case Font::ARIAL: style = "font-family:sans-serif;"; break; case Font::ROMAN: style = "font-family:serif;"; break; case Font::COURIER: style = "font-family:monospace;"; break; } if(f.GetHeight() != base.GetHeight()) style << Sprintf("font-size:%dpt;", f.GetHeight() * 72 / 600); if(f.IsBold() != base.IsBold()) style << (f.IsBold() ? "font-weight:bold;" : "font-weight:normal;"); if(f.IsItalic() != base.IsItalic()) style << (f.IsItalic() ? "font-style:italic;" : "font-style:normal;"); if(f.IsUnderline() != base.IsUnderline()) style << (f.IsUnderline() ? "text-decoration:underline;" : "text-decoration:none;"); return style; } String HtmlFontStyle(Font f) { String style; switch(f.GetFace()) { case Font::ARIAL: style = "font-family:sans-serif;"; break; case Font::ROMAN: style = "font-family:serif;"; break; case Font::COURIER: style = "font-family:monospace;"; break; } style << Sprintf("font-size:%dpt;", f.GetHeight() * 72 / 600); style << (f.IsBold() ? "font-weight:bold;" : "font-weight:normal;"); style << (f.IsItalic() ? "font-style:italic;" : "font-style:normal;"); style << (f.IsUnderline() ? "text-decoration:underline;" : "text-decoration:none;"); return style; } String HtmlDot(int q, Zoom z) { return String().Cat() << ' ' << z * q << "px"; } String HtmlDotl(int q, Zoom z) { return String().Cat() << ' ' << max((int)!!q, z * q) << "px"; } String HtmlStyleColor(Color c, const char *cl = "color") { return Format(String(cl) + ":#%02x%02x%02x;", c.GetR(), c.GetG(), c.GetB()); } String HtmlCharStyle(const RichPara::CharFormat& cf, const RichPara::CharFormat& sf) { String style; if(cf.ink != sf.ink) style = HtmlStyleColor(cf.ink); return style + HtmlFontStyle(cf, sf); } String HtmlParaStyle(const RichPara::Format& f, Zoom z) { String style; int lm = z * f.lm; if(f.bullet && f.bullet != RichPara::BULLET_TEXT) { style << "display:list-item;list-style-type:"; switch(f.bullet) { case RichPara::BULLET_ROUND: style << "disc"; break; case RichPara::BULLET_ROUNDWHITE: style << "circle"; break; case RichPara::BULLET_BOX: case RichPara::BULLET_BOXWHITE: style << "square"; break; } style << ';'; // style << ";list-style-position:inside;"; lm += 20; } style << Format("margin:%d`px %d`px %d`px %d`px;text-indent:%d`px;", z * f.before, z * f.rm, z * f.after, lm, z * (f.bullet ? 0 : f.indent) ); style << "text-align:"; switch(f.align) { case ALIGN_LEFT: style << "left;"; break; case ALIGN_RIGHT: style << "right;"; break; case ALIGN_CENTER: style << "center;"; break; case ALIGN_JUSTIFY: style << "justify;"; break; } style << HtmlStyleColor(f.ink) + HtmlFontStyle(f); if(!IsNull(f.paper)) style << HtmlStyleColor(f.paper, "background-color"); style << decode(f.linespacing, RichPara::LSP15, "line-height:150%", RichPara::LSP20, "line-height:200%", RichPara::LSP115, "line-height:115%", ""); return style; } String FormatClass(Index& css, const String& fmt) { return " class=\"" + FormatIntAlpha(css.FindAdd(fmt) + 1) + "\""; } void TabBorder(String& style, const char *txt, int border, Color bordercolor, const RichTable::Format& tf, Zoom z) { style << "border-" << txt << ':' << HtmlDotl(border + tf.grid, z) << " solid " << ColorToHtml(border ? bordercolor : tf.gridcolor) << ';'; } String AsHtml(const RichTxt& text, const RichStyles& styles, Index& css, const VectorMap& links, const VectorMap& labels, Zoom z, const VectorMap& escape, HtmlObjectSaver& object_saver) { String html; for(int i = 0; i < text.GetPartCount(); i++) { if(text.IsTable(i)) { const RichTable& t = text.GetTable(i); const RichTable::Format& tf = t.GetFormat(); int nx = tf.column.GetCount(); int ny = t.GetRows(); html << ""; if(tf.before > 0) html << ""; html << ""; if (tf.lm > 0) html << "\r\n"; html << "\r\n"; if (tf.rm > 0) html << ""; if(tf.after > 0) html << ""; html << "
"; String style; style << "border-collapse:collapse;table-layout:auto;" << "border:" << HtmlDotl(tf.frame, z) << " solid " << ColorToHtml(tf.framecolor) << ';'; html << ""; int sum = 0; for(int i = 0; i < nx; i++) sum += tf.column[i]; html << ""; for(int i = 0; i < nx; i++) html << ""; html << ""; html << "\r\n"; for(int i = 0; i < ny; i++) { const Array& r = t[i]; html << ""; for(int j = 0; j < r.GetCount(); j++) { if(t(i, j)) { const RichCell& c = r[j]; const RichCell::Format& cf = c.format; String style; style << "padding:" << HtmlDot(cf.margin.top, z) << HtmlDot(cf.margin.right, z) << HtmlDot(cf.margin.bottom, z) << HtmlDot(cf.margin.left, z) << ';'; TabBorder(style, "left", cf.border.left, cf.bordercolor, tf, z); TabBorder(style, "top", cf.border.top, cf.bordercolor, tf, z); TabBorder(style, "right", cf.border.right, cf.bordercolor, tf, z); TabBorder(style, "bottom", cf.border.bottom, cf.bordercolor, tf, z); style << "background-color:" << ColorToHtml(cf.color) << ';'; style << "vertical-align:"; switch(cf.align) { case ALIGN_TOP: style << "top"; break; case ALIGN_CENTER: style << "middle"; break; case ALIGN_BOTTOM: style << "bottom"; break; } style << ';'; html << "\r\n"; } } html << "\r\n"; } html << "
\r\n"; } else if(text.IsPara(i)) { RichPara p = text.Get(i, styles); if(p.format.ruler) html << "
"; bool bultext = false; if(p.format.bullet == RichPara::BULLET_TEXT) for(int i = 0; i < p.part.GetCount(); i++) { const RichPara::Part& part = p.part[i]; if(part.text.Find(9) >= 0) { bultext = true; break; } } if(bultext) { html << "" ""; int q = z * p.format.lm - 8; if(q > 0) html << Format("", q); html << Format("\r\n
\r\n", max(z * p.format.indent, 0)); p.format.ruler = p.format.after = p.format.before = p.format.indent = p.format.lm = 0; } String par = ""; html << par; for(int i = 0; i < p.part.GetCount(); i++) { const RichPara::Part& part = p.part[i]; int q; String lnk = part.format.link; if(lnk.GetCount()) { int q = links.Find(lnk); if(q < 0) { int q = lnk.ReverseFind('#'); if(q >= 0) { String l = lnk.Left(q); lnk = links.Get(l, l) + '#' + lnk.Mid(q + 1); } } else lnk = links[q]; } if(part.object) html << object_saver.GetHtml(part.object, lnk); else if(part.format.indexentry.GetCount() && (q = escape.Find(part.format.indexentry.ToString())) >= 0) html << escape[q]; else { String endtag; if(!lnk.IsEmpty() && lnk[0] != ':') { html << ""; endtag = ""; } String cs; if(part.text[0] != 9) cs = HtmlCharStyle(part.format, p.format); if(!cs.IsEmpty()) { html << ""; endtag = "" + endtag; } if(part.format.sscript == 1) { html << ""; endtag = "" + endtag; } if(part.format.sscript == 2) { html << ""; endtag = "" + endtag; } if(part.format.IsStrikeout()) { html << ""; endtag = "" + endtag; } if(part.format.capitals) { html << ""; endtag << ""; } bool spc = false; const wchar *end = part.text.End(); for(const wchar *s = part.text.Begin(); s != end; s++) { if(*s == ' ') { html.Cat(spc ? " " : " "); spc = true; } else { spc = false; if(*s == 160) html.Cat(" "); else if(*s == '<') html.Cat("<"); else if(*s == '>') html.Cat(">"); else if(*s == '&') html.Cat("&"); else if(*s == '\"') html.Cat("""); else if(*s == 9) { if(bultext) { if(!cs.IsEmpty() && part.text[0] != 9) html << ""; html << "

"; html << "
\r\n"; html << par; if(s[1]) { cs = HtmlCharStyle(part.format, p.format); if(!cs.IsEmpty()) html << ""; } } else html.Cat("    "); } else html.Cat(ToUtf8(*s)); } } html << endtag; } } if(p.part.GetCount() == 0) html << " "; html << "

"; if(bultext) html << "
"; html << "\r\n"; } } return html; } class DefaultHtmlObjectSaver : public HtmlObjectSaver { public: DefaultHtmlObjectSaver(const String& outdir_, const String& namebase_, int imtolerance_, Zoom z_) : outdir(outdir_), namebase(namebase_), z(z_), imtolerance(imtolerance_), im(0) {} virtual String GetHtml(const RichObject& object, const String& link); private: String outdir; String namebase; Zoom z; int imtolerance; int im; }; String DefaultHtmlObjectSaver::GetHtml(const RichObject& object, const String& link) { StringBuffer html; String name; name << namebase << "_" << im++ << ".png"; Size psz = object.GetPixelSize(); Size sz = z * object.GetSize(); if(abs(100 * (psz.cx - sz.cx) / sz.cx) < imtolerance) sz = psz; PNGEncoder png; png.SaveFile(AppendFileName(outdir, name), object.ToImage(psz)); String el = ""; if(IsNull(link)) { if(psz.cx * psz.cy != 0) html << ""; else el.Clear(); } else html << ""; html << Format("\"\"", name, sz.cx, sz.cy); html << el; return String(html); } String EncodeHtml(const RichText& text, Index& css, const VectorMap& links, const VectorMap& labels, const String& outdir, const String& namebase, Zoom z, const VectorMap& escape, int imt) { DefaultHtmlObjectSaver default_saver(outdir, namebase, imt, z); return AsHtml(text, text.GetStyles(), css, links, labels, z, escape, default_saver); } String EncodeHtml(const RichText& text, Index& css, const VectorMap& links, const VectorMap& labels, HtmlObjectSaver& object_saver, Zoom z, const VectorMap& escape) { return AsHtml(text, text.GetStyles(), css, links, labels, z, escape, object_saver); } String AsCss(Index& ss) { String css; for(int i = 0; i < ss.GetCount(); i++) { css << "." + FormatIntAlpha(i + 1); css << "{" << ss[i] << "}\r\n"; } return css; } }