From 83a0273a654217bd1fdfba73105cf12b33767058 Mon Sep 17 00:00:00 2001 From: Markus Himmel Date: Sat, 20 Dec 2014 16:38:53 +0000 Subject: [PATCH] Add styled RTF output to RTFTranslator and bump version Signed-off-by: Adrien Destugues --- src/add-ons/translators/rtf/Jamfile | 2 +- src/add-ons/translators/rtf/RTFTranslator.h | 2 +- .../translators/rtf/RTFTranslator.rdef | 4 +- src/add-ons/translators/rtf/convert.cpp | 183 ++++++++++++++---- 4 files changed, 154 insertions(+), 37 deletions(-) diff --git a/src/add-ons/translators/rtf/Jamfile b/src/add-ons/translators/rtf/Jamfile index b0a1288446..69481144b4 100644 --- a/src/add-ons/translators/rtf/Jamfile +++ b/src/add-ons/translators/rtf/Jamfile @@ -21,7 +21,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { RTF.cpp RTFTranslator.cpp - : be translation + : be translation [ TargetLibstdc++ ] [ MultiArchDefaultGristFiles libtranslatorsutils.a ] [ TargetLibsupc++ ] localestub : true diff --git a/src/add-ons/translators/rtf/RTFTranslator.h b/src/add-ons/translators/rtf/RTFTranslator.h index 7ad123326c..d0e85d914a 100644 --- a/src/add-ons/translators/rtf/RTFTranslator.h +++ b/src/add-ons/translators/rtf/RTFTranslator.h @@ -17,7 +17,7 @@ #include -#define RTF_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(0, 6, 0) +#define RTF_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(0, 7, 0) #define RTF_TEXT_FORMAT 'RTF ' #define RTF_IN_QUALITY 0.7 #define RTF_IN_CAPABILITY 0.9 diff --git a/src/add-ons/translators/rtf/RTFTranslator.rdef b/src/add-ons/translators/rtf/RTFTranslator.rdef index cb3883760d..208efe7c80 100644 --- a/src/add-ons/translators/rtf/RTFTranslator.rdef +++ b/src/add-ons/translators/rtf/RTFTranslator.rdef @@ -6,10 +6,10 @@ resource app_signature "application/x-vnd.Haiku-RTFTranslator"; resource app_version { major = 0, - middle = 6, + middle = 7, minor = 0, variety = 0, internal = 0, - short_info = "0.6.0", + short_info = "0.7.0", long_info = "Haiku RTFTranslator Add-Ons." }; diff --git a/src/add-ons/translators/rtf/convert.cpp b/src/add-ons/translators/rtf/convert.cpp index bc8d12f4bd..f8b8146351 100644 --- a/src/add-ons/translators/rtf/convert.cpp +++ b/src/add-ons/translators/rtf/convert.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #define READ_BUFFER_SIZE 2048 @@ -649,6 +650,13 @@ convert_to_plain_text(RTF::Header &header, BPositionIO &target) return B_OK; } +struct color_compare +{ + bool operator()(const rgb_color& left, const rgb_color& right) const + { + return (*(const uint32 *)&left) < (*(const uint32 *)&right); + } +}; status_t convert_styled_text_to_rtf( BPositionIO* source, BPositionIO* target) @@ -662,61 +670,170 @@ status_t convert_styled_text_to_rtf( TranslatorStyledTextTextHeader txtheader; char buffer[READ_BUFFER_SIZE]; + // Read STXT and TEXT headers if (source->Read(&stxtheader, kstxtsize) != kstxtsize) - return B_ERROR; - if (source->Read(&txtheader, ktxtsize) != ktxtsize) + return B_ERROR; + if (source->Read(&txtheader, ktxtsize) != ktxtsize || + swap_data(B_UINT32_TYPE, &txtheader, + sizeof(TranslatorStyledTextTextHeader), + B_SWAP_BENDIAN_TO_HOST) != B_OK) return B_ERROR; - - BString plainText; + // source now points to the beginning of the plain text section + BString plainText; ssize_t nread = 0, nreed = 0, ntotalread = 0; - nreed = min((size_t)READ_BUFFER_SIZE, + nreed = std::min((size_t)READ_BUFFER_SIZE, (size_t)txtheader.header.data_size - ntotalread); nread = source->Read(buffer, nreed); while (nread > 0) { plainText << buffer; ntotalread += nread; - nreed = min((size_t)READ_BUFFER_SIZE, + nreed = std::min((size_t)READ_BUFFER_SIZE, (size_t)txtheader.header.data_size - ntotalread); nread = source->Read(buffer, nreed); - } + } + if ((ssize_t)txtheader.header.data_size != ntotalread) return B_NO_TRANSLATOR; - BString rtfFile = - "{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard "; + BString rtfFile = + "{\\rtf1\\ansi"; - rtfFile << plainText << " }"; - target->Write((const void*)rtfFile, rtfFile.Length()); - - BNode* node = (BNode*)source; - const char* kAttrName = "styles"; - attr_info info; - - if (node->GetAttrInfo(kAttrName, &info) != B_OK - || info.type != B_RAW_TYPE || info.size < 160) + ssize_t read = 0; + TranslatorStyledTextStyleHeader stylHeader; + read = source->Read(buffer, sizeof(stylHeader)); + + if (read < 0) + { return B_ERROR; + } - uint8* flatRunArray = new (std::nothrow) uint8[info.size]; - if (flatRunArray == NULL) - return B_NO_MEMORY; + if (read != sizeof(stylHeader) && read != 0) + { + return B_NO_TRANSLATOR; + } - if (node->ReadAttr(kAttrName, B_RAW_TYPE, 0, flatRunArray, info.size - != info.size)) - return B_OK; - - //todo: convert stxt attributes to rtf commands - - //text_run_array* styles = (text_run_array*)flatRunArray; - size_t stylesSize = info.size / 3; - - for(uint32 i = 0; i < stylesSize; i++) { - + if (read == sizeof(stylHeader)) // There is a STYL section + { + memcpy(&stylHeader, buffer, sizeof(stylHeader)); + if (swap_data(B_UINT32_TYPE, &stylHeader, sizeof(stylHeader), + B_SWAP_BENDIAN_TO_HOST) != B_OK) + { + return B_ERROR; + } + + if (stylHeader.header.magic != 'STYL' + || stylHeader.header.header_size != sizeof(stylHeader)) + { + return B_NO_TRANSLATOR; + } + + + uint8 unflattened[stylHeader.header.data_size]; + source->Read(unflattened, stylHeader.header.data_size); + text_run_array *styles = BTextView::UnflattenRunArray(unflattened); + + // RTF needs us to mention font and color names in advance so + // we collect them in sets + std::set colorTable; + std::set fontTable; + + font_family out; + for (int i = 0; i < styles->count; i++) + { + colorTable.insert(styles->runs[i].color); + styles->runs[i].font.GetFamilyAndStyle(&out, NULL); + fontTable.insert(BString(out)); + } + + // Now we write them to the file + std::set::iterator it; + uint32 count = 0; + + rtfFile << "{\\fonttbl"; + for (it = fontTable.begin(); it != fontTable.end(); it++) + { + rtfFile << "{\\f" << count << " " << *it << ";}"; + count++; + } + rtfFile << "}{\\colortbl"; + + std::set::iterator cit; + for (cit = colorTable.begin(); cit != colorTable.end(); cit++) + { + rtfFile << "\\red" << cit->red + << "\\green" << cit->green + << "\\blue" << cit->blue + << ";"; + } + rtfFile << "}"; + + // Now we put out the actual text with styling information run by run + for (int i = 0; i < styles->count; i++) + { + // Find font and color indices + styles->runs[i].font.GetFamilyAndStyle(&out, NULL); + int fontIndex = std::distance(fontTable.begin(), + fontTable.find(BString(out))); + int colorIndex = std::distance(colorTable.begin(), + colorTable.find(styles->runs[i].color)); + rtfFile << "\\pard\\plain\\f" << fontIndex << "\\cf" << colorIndex; + + // Apply various font styles + uint16 fontFace = styles->runs[i].font.Face(); + if (fontFace & B_ITALIC_FACE) + { + rtfFile << "\\i"; + } + if (fontFace & B_UNDERSCORE_FACE) + { + rtfFile << "\\ul"; + } + if (fontFace & B_BOLD_FACE) + { + rtfFile << "\\b"; + } + + // RTF font size unit is half-points, but BFont::Size() returns + // points + rtfFile << "\\fs" + << static_cast(styles->runs[i].font.Size() * 2); + + int length; + if (i < styles->count - 1) + { + length = styles->runs[i + 1].offset - styles->runs[i].offset; + } + else + { + length = plainText.Length() - styles->runs[i].offset; + } + + BString segment; + plainText.CopyInto(segment, styles->runs[i].offset, length); + + // Escape control structures + segment.CharacterEscape("\\{}", '\\'); + segment.ReplaceAll("\n", "\\line"); + + rtfFile << " " << segment; + } + + delete styles; + + rtfFile << "}"; + } + else // There is no STYL section + { + // Just use a generic preamble + rtfFile << "{\\fonttbl\\f0 DejaVu Sans;}\\f0\\pard " << plainText + << "}"; } - delete[] flatRunArray; + target->Write(rtfFile.String(), rtfFile.Length()); + return B_OK; }