Add styled RTF output to RTFTranslator and bump version

Signed-off-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
Markus Himmel 2014-12-20 16:38:53 +00:00 committed by Adrien Destugues
parent fa267963e0
commit 83a0273a65
4 changed files with 154 additions and 37 deletions

View File

@ -21,7 +21,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
RTF.cpp
RTFTranslator.cpp
: be translation
: be translation [ TargetLibstdc++ ]
[ MultiArchDefaultGristFiles libtranslatorsutils.a ]
[ TargetLibsupc++ ] localestub
: true

View File

@ -17,7 +17,7 @@
#include <fs_attr.h>
#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

View File

@ -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."
};

View File

@ -23,6 +23,7 @@
#include <string.h>
#include <algorithm>
#include <set>
#include <fs_attr.h>
#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<rgb_color, color_compare> colorTable;
std::set<BString> 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<BString>::iterator it;
uint32 count = 0;
rtfFile << "{\\fonttbl";
for (it = fontTable.begin(); it != fontTable.end(); it++)
{
rtfFile << "{\\f" << count << " " << *it << ";}";
count++;
}
rtfFile << "}{\\colortbl";
std::set<rgb_color, color_compare>::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<int>(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;
}