linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #05292
[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2850: convert HTML fonts to RTF
------------------------------------------------------------
revno: 2850
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Fri 2012-02-03 00:16:07 +0100
message:
convert HTML fonts to RTF
modified:
dcpp/ChatMessage.cpp
win32/AspectChat.h
win32/HtmlToRtf.cpp
win32/HtmlToRtf.h
--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk
Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'dcpp/ChatMessage.cpp'
--- dcpp/ChatMessage.cpp 2012-02-02 19:06:08 +0000
+++ dcpp/ChatMessage.cpp 2012-02-02 23:16:07 +0000
@@ -52,7 +52,7 @@
if(l.size() >= 4) {
std::stringstream stream;
stream << (Util::toInt(l[3]) ? "italic" : "normal") << " " << l[2] << " " <<
- abs(Util::toFloat(l[1])) * 72.0 / 96.0 << "px " << l[0];
+ abs(Util::toFloat(l[1])) << "px '" << l[0] << "'";
return stream.str();
}
return string();
@@ -72,7 +72,7 @@
replyTo(replyTo ? replyTo->getUser() : nullptr),
timestamp(time(0)),
thirdPerson(thirdPerson),
-messageTimestamp(timestamp)
+messageTimestamp(messageTimestamp)
{
/* format the message to plain text and HTML strings before handing them over to plugins for
further processing. */
=== modified file 'win32/AspectChat.h'
--- win32/AspectChat.h 2012-02-01 19:04:48 +0000
+++ win32/AspectChat.h 2012-02-02 23:16:07 +0000
@@ -88,7 +88,7 @@
}
void addChat(const ChatMessage& message) {
- chat->addTextSteady(formatText(HtmlToRtf::convert(message.htmlMessage)));
+ chat->addTextSteady(formatText(HtmlToRtf::convert(message.htmlMessage, chat)));
t().addedChat(Text::toT(message.message));
}
=== modified file 'win32/HtmlToRtf.cpp'
--- win32/HtmlToRtf.cpp 2012-02-02 19:06:08 +0000
+++ win32/HtmlToRtf.cpp 2012-02-02 23:16:07 +0000
@@ -23,63 +23,80 @@
#include <dcpp/debug.h>
#include <dcpp/SimpleXML.h>
+#include <dcpp/StringTokenizer.h>
#include <dcpp/Text.h>
+#include <dwt/util/GDI.h>
#include <dwt/widgets/RichTextBox.h>
-#ifdef TODO
-HubFrame::FormattedChatMessage HubFrame::format(const ChatMessage& message, int* pmInfo) const {
- if(!nick.empty()) {
- nick = chat->rtfEscape(nick);
- tstring rtfHeader;
- tstring rtfFormat;
- if(!style.font.empty()) {
- auto cached = WinUtil::getUserMatchFont(style.font);
- if(cached.get()) {
- auto lf = cached->getLogFont();
- rtfHeader += _T("{\\fonttbl{\\f0\\fnil\\fcharset") + Text::toT(Util::toString(lf.lfCharSet)) + _T(" ") + lf.lfFaceName + _T(";}}");
- rtfFormat += _T("\\f0\\fs") + Text::toT(Util::toString(lf.lfHeight * 2));
- if(lf.lfWeight >= FW_BOLD) { rtfFormat += _T("\\b"); }
- if(lf.lfItalic) { rtfFormat += _T("\\i"); }
- }
- }
- if(!rtfFormat.empty() || style.textColor != -1 || style.bgColor != -1) {
- /* when creating a new context (say for a font table), always redefine colors as the Rich Edit
- control seems to randomly reset them like a boss. */
- if(style.textColor == -1) { style.textColor = chat->getTextColor(); }
- if(style.bgColor == -1) { style.bgColor = chat->getBgColor(); }
- rtfHeader += _T("{\\colortbl") + toRTF(style.textColor) + toRTF(style.bgColor) + _T("}");
- rtfFormat += _T("\\cf0\\highlight1");
- }
- if(!rtfFormat.empty()) {
- nick = _T("{") + rtfHeader + rtfFormat + _T(" ") + nick + _T("}");
- }
- ret.second += message.thirdPerson ? _T("* ") + nick + _T(" ") : _T("<") + nick + _T("> ");
- }
-}
-#endif
-
struct Parser : SimpleXMLReader::CallBack {
- Parser() : colorIndex(0) { }
+ Parser(dwt::RichTextBox* box);
void startTag(const string& name, StringPairList& attribs, bool simple);
void endTag(const string& name, const string& data);
tstring finalize();
+
private:
- void parseColor(const char* rtfCode, const string& s);
+ int addFont(string&& font);
+ static int rtfFontSize(float px);
+ int addColor(COLORREF color);
+
+ void parseFont(const string& s);
+ void parseColor(int& contextColor, const string& s);
+
tstring ret;
- string fonts;
- string colors;
- size_t colorIndex;
+
+ StringList fonts;
+ StringList colors;
+
+ struct {
+ int font;
+ int fontSize;
+ int textColor;
+ int bgColor;
+ void clear() { font = fontSize = textColor = bgColor = -1; }
+ } context, defaultContext;
+
string header;
};
-tstring HtmlToRtf::convert(const string& html) {
- Parser parser;
+tstring HtmlToRtf::convert(const string& html, dwt::RichTextBox* box) {
+ Parser parser(box);
try { SimpleXMLReader(&parser).parse(html); }
catch(const SimpleXMLException& e) { return Text::toT(e.getError()); }
return parser.finalize();
}
+Parser::Parser(dwt::RichTextBox* box) {
+ auto lf = box->getFont()->getLogFont();
+ defaultContext.font = addFont("{\\f0\\fnil\\fcharset" + Util::toString(lf.lfCharSet) + " " + Text::fromT(lf.lfFaceName) + ";}");
+ defaultContext.fontSize = rtfFontSize(abs(lf.lfHeight));
+
+ defaultContext.textColor = addColor(box->getTextColor());
+ defaultContext.bgColor = addColor(box->getBgColor());
+
+ context.clear();
+}
+
+int Parser::addFont(string&& font) {
+ auto ret = fonts.size();
+ fonts.push_back(std::forward<string>(font));
+ return ret;
+}
+
+int Parser::rtfFontSize(float px) {
+ return px * 72.0 / 96.0 // px -> font points
+ * dwt::util::dpiFactor() // respect DPI settings
+ * 2.0; // RTF font sizes are expressed in half-points
+}
+
+int Parser::addColor(COLORREF color) {
+ auto ret = colors.size();
+ colors.push_back("\\red" + Util::toString(GetRValue(color)) +
+ "\\green" + Util::toString(GetGValue(color)) +
+ "\\blue" + Util::toString(GetBValue(color)) + ";");
+ return ret;
+}
+
static const string styleAttr = "style";
static const string fontStyle = "font";
static const string textColorStyle = "color";
@@ -111,20 +128,21 @@
case Font:
{
+ parseFont(tmp);
state = Declaration;
break;
}
case TextColor:
{
- parseColor("\\cf", tmp);
+ parseColor(context.textColor, tmp);
state = Declaration;
break;
}
case BgColor:
{
- parseColor("\\highlight", tmp);
+ parseColor(context.bgColor, tmp);
state = Declaration;
break;
}
@@ -133,36 +151,77 @@
}
void Parser::endTag(const string& name, const string& data) {
- if(header.empty()) {
- // no new RTF enclosing; write directly.
- ret += dwt::RichTextBox::rtfEscape(Text::toT(Text::toDOS(data)));
-
- } else {
- ret += _T("{") + Text::toT(header) + _T(" ") + dwt::RichTextBox::rtfEscape(Text::toT(Text::toDOS(data))) + _T("}");
- header.clear();
- }
+ if(context.font == -1) { context.font = defaultContext.font; }
+ if(context.fontSize == -1) { context.fontSize = defaultContext.fontSize; }
+ if(context.textColor == -1) { context.textColor = defaultContext.textColor; }
+ if(context.bgColor == -1) { context.bgColor = defaultContext.bgColor; }
+
+ ret += Text::toT("{\\f" + Util::toString(context.font) + "\\fs" + Util::toString(context.fontSize) +
+ "\\cf" + Util::toString(context.textColor) + "\\highlight" + Util::toString(context.bgColor) +
+ header + " ") + dwt::RichTextBox::rtfEscape(Text::toT(Text::toDOS(data))) + _T("}");
+
+ context.clear();
+ header.clear();
}
tstring Parser::finalize() {
- if(fonts.empty() && colors.empty()) {
- // no new RTF enclosing; write directly.
- return ret;
- }
-
- return _T("{") + Text::toT(fonts + "{\\colortbl" + colors + "}") + ret + _T("}");
-}
-
-void Parser::parseColor(const char* rtfCode, const string& s) {
+ return Text::toT("{{\\fonttbl" + Util::toString(Util::emptyString, fonts) +
+ "}{\\colortbl" + Util::toString(Util::emptyString, colors) + "}") + ret + _T("}");
+}
+
+void Parser::parseFont(const string& s) {
+ // this contains multiple params separated by spaces.
+ StringTokenizer<string> tok(s, ' ');
+ auto& l = tok.getTokens();
+
+ // remove empty strings.
+ l.erase(std::remove_if(l.begin(), l.end(), [](const string& s) { return s.empty(); }), l.end());
+
+ auto params = l.size();
+ if(params < 2) // the last 2 params (font size & font family) are compulsory.
+ return;
+
+ // the last param (font family) may contain spaces; merge if that is the case.
+ while(*(l.back().end() - 1) == '\'' && (l.back().size() <= 1 || *l.back().begin() != '\'')) {
+ *(l.end() - 2) += ' ' + std::move(l.back());
+ l.erase(l.end() - 1);
+ if(l.size() < 2)
+ return;
+ }
+
+ // parse the last param (font family).
+ /// @todo handle multiple fonts separated by commas...
+ auto& family = l.back();
+ family.erase(std::remove(family.begin(), family.end(), '\''), family.end());
+ if(family.empty())
+ return;
+ context.font = addFont("{\\f" + Util::toString(context.font) + "\\fnil " + std::move(family) + ";}");
+
+ // parse the second to last param (font size).
+ /// @todo handle more than px sizes
+ auto& size = *(l.end() - 2);
+ if(size.size() > 2 && *(size.end() - 2) == 'p' && *(size.end() - 1) == 'x') { // 16px
+ context.fontSize = rtfFontSize(Util::toFloat(size.substr(0, size.size() - 2)));
+ }
+
+ // parse the optional third to last param (font weight).
+ if(params >= 3 && Util::toInt(*(l.end() - 3)) >= FW_BOLD) {
+ header += "\\b";
+ }
+
+ // parse the optional first param (font style).
+ if(params >= 4 && l[0] == "italic") {
+ header += "\\i";
+ }
+}
+
+void Parser::parseColor(int& contextColor, const string& s) {
auto sharp = s.find('#');
if(sharp != string::npos && s.size() > sharp + 6) {
try {
size_t pos = 0;
auto color = std::stol(s.substr(sharp + 1, 6), &pos, 16);
- colors += "\\red" + Util::toString((color & 0xFF0000) >> 16) +
- "\\green" + Util::toString((color & 0xFF00) >> 8) +
- "\\blue" + Util::toString(color & 0xFF) + ";";
- header += rtfCode;
- header += Util::toString(colorIndex++);
+ contextColor = addColor(RGB((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF));
} catch(const std::exception& e) { dcdebug("color parsing exception: %s with str: %s\n", e.what(), s.c_str()); }
}
}
=== modified file 'win32/HtmlToRtf.h'
--- win32/HtmlToRtf.h 2012-02-01 19:04:48 +0000
+++ win32/HtmlToRtf.h 2012-02-02 23:16:07 +0000
@@ -24,7 +24,7 @@
supported. */
class HtmlToRtf {
public:
- static tstring convert(const string& html);
+ static tstring convert(const string& html, dwt::RichTextBox* box);
};
#endif