commit eb453aaca94cc029b16ded25726ae45ed3fc75e8
parent 4b3d4eaeb0b466543862a3e9b08e83d51a5ba555
Author: cfillion <cfillion@users.noreply.github.com>
Date: Wed, 6 May 2020 19:31:33 -0400
strip away RTF to enable richtext display on Linux [p=2247988]
Diffstat:
4 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/src/richedit-generic.cpp b/src/richedit-generic.cpp
@@ -17,6 +17,8 @@
#include "richedit.hpp"
+#include "string.hpp"
+
// This is the Linux implementation of RichEdit
// See also richedit.mm and richedit-win32.cpp
@@ -42,6 +44,6 @@ void RichEdit::setPlainText(const std::string &text)
bool RichEdit::setRichText(const std::string &rtf)
{
- (void)rtf;
- return false;
+ setPlainText(String::stripRtf(rtf));
+ return true;
}
diff --git a/src/string.cpp b/src/string.cpp
@@ -17,8 +17,10 @@
#include "string.hpp"
+#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <cstdarg>
+#include <regex>
#include <sstream>
std::string String::format(const char *fmt, ...)
@@ -63,6 +65,34 @@ std::string String::indent(const std::string &text)
return output;
}
+std::string String::stripRtf(const std::string &rtf)
+{
+ static const std::regex rtfRules(
+ // passthrough for later replacement to \n
+ R"((\\line |\\par\}))" "|"
+ R"(\\line(\n)\s*)" "|"
+
+ // preserve literal braces
+ R"(\\([\{\}]))" "|"
+
+ // hidden groups (strip contents)
+ R"(\{\\(?:f\d+|fonttbl|colortbl)[^\{\}]+\})" "|"
+
+ // formatting tags and groups (keep contents)
+ R"(\\\w+\s?|\{|\})" "|"
+
+ // newlines and indentation
+ R"(\s*\n\s*)"
+ );
+
+ std::string text = std::regex_replace(rtf, rtfRules, "$1$2$3");
+ boost::algorithm::replace_all(text, "\\line ", "\n");
+ boost::algorithm::replace_all(text, "\\par}", "\n\n");
+ boost::algorithm::trim(text);
+
+ return text;
+}
+
void String::ImplDetail::imbueStream(std::ostream &stream)
{
class NumPunct : public std::numpunct<char>
diff --git a/src/string.hpp b/src/string.hpp
@@ -32,6 +32,7 @@ namespace String {
std::string format(const char *fmt, ...);
std::string indent(const std::string &);
+ std::string stripRtf(const std::string &);
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
std::string number(const T v)
diff --git a/test/string.cpp b/test/string.cpp
@@ -25,3 +25,38 @@ TEST_CASE("indent string", M) {
TEST_CASE("pretty-print numbers", M) {
REQUIRE(String::number(42'000'000) == "42,000,000");
}
+
+TEST_CASE("strip RTF header", M) {
+ REQUIRE("Hello World" == String::stripRtf(R"(
+ {\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard
+ Hello World
+ }
+ )"));
+}
+
+TEST_CASE("strip RTF color header", M) {
+ REQUIRE("Hello World" == String::stripRtf(R"(
+ {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 Courier;}}
+ {\colortbl;\red255\green0\blue0;\red0\green0\blue255;}
+ \widowctrl\hyphauto
+ Hello World
+ }
+ )"));
+}
+
+TEST_CASE("strip RTF paragraphs", M) {
+ REQUIRE("foo\n\nbar\n\nbaz" == String::stripRtf(R"(
+ {\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 foo\par}
+ {\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 bar\par}
+ {\pard \ql \f0 \sa180 \li0 \fi0 \b \fs36 baz\par}
+ )"));
+}
+
+TEST_CASE("strip RTF line breaks", M) {
+ REQUIRE("foo\nbar\nbaz" == String::stripRtf(R"(foo\line bar\line
+ baz\line)"));
+}
+
+TEST_CASE("strip RTF literal braces", M) {
+ REQUIRE("{ }" == String::stripRtf(R"(\{ \})"));
+}