← Back to team overview

linuxdcpp-team team mailing list archive

[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