Add styled RTF output to RTFTranslator and bump version
Signed-off-by: Adrien Destugues <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
fa267963e0
commit
83a0273a65
@@ -21,7 +21,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
|||||||
RTF.cpp
|
RTF.cpp
|
||||||
RTFTranslator.cpp
|
RTFTranslator.cpp
|
||||||
|
|
||||||
: be translation
|
: be translation [ TargetLibstdc++ ]
|
||||||
[ MultiArchDefaultGristFiles libtranslatorsutils.a ]
|
[ MultiArchDefaultGristFiles libtranslatorsutils.a ]
|
||||||
[ TargetLibsupc++ ] localestub
|
[ TargetLibsupc++ ] localestub
|
||||||
: true
|
: true
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include <fs_attr.h>
|
#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_TEXT_FORMAT 'RTF '
|
||||||
#define RTF_IN_QUALITY 0.7
|
#define RTF_IN_QUALITY 0.7
|
||||||
#define RTF_IN_CAPABILITY 0.9
|
#define RTF_IN_CAPABILITY 0.9
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ resource app_signature "application/x-vnd.Haiku-RTFTranslator";
|
|||||||
|
|
||||||
resource app_version {
|
resource app_version {
|
||||||
major = 0,
|
major = 0,
|
||||||
middle = 6,
|
middle = 7,
|
||||||
minor = 0,
|
minor = 0,
|
||||||
variety = 0,
|
variety = 0,
|
||||||
internal = 0,
|
internal = 0,
|
||||||
short_info = "0.6.0",
|
short_info = "0.7.0",
|
||||||
long_info = "Haiku RTFTranslator Add-Ons."
|
long_info = "Haiku RTFTranslator Add-Ons."
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <fs_attr.h>
|
#include <fs_attr.h>
|
||||||
#define READ_BUFFER_SIZE 2048
|
#define READ_BUFFER_SIZE 2048
|
||||||
|
|
||||||
@@ -649,6 +650,13 @@ convert_to_plain_text(RTF::Header &header, BPositionIO &target)
|
|||||||
return B_OK;
|
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(
|
status_t convert_styled_text_to_rtf(
|
||||||
BPositionIO* source, BPositionIO* target)
|
BPositionIO* source, BPositionIO* target)
|
||||||
@@ -662,61 +670,170 @@ status_t convert_styled_text_to_rtf(
|
|||||||
TranslatorStyledTextTextHeader txtheader;
|
TranslatorStyledTextTextHeader txtheader;
|
||||||
char buffer[READ_BUFFER_SIZE];
|
char buffer[READ_BUFFER_SIZE];
|
||||||
|
|
||||||
|
// Read STXT and TEXT headers
|
||||||
if (source->Read(&stxtheader, kstxtsize) != kstxtsize)
|
if (source->Read(&stxtheader, kstxtsize) != kstxtsize)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
if (source->Read(&txtheader, ktxtsize) != ktxtsize)
|
if (source->Read(&txtheader, ktxtsize) != ktxtsize ||
|
||||||
|
swap_data(B_UINT32_TYPE, &txtheader,
|
||||||
|
sizeof(TranslatorStyledTextTextHeader),
|
||||||
|
B_SWAP_BENDIAN_TO_HOST) != B_OK)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
|
// source now points to the beginning of the plain text section
|
||||||
BString plainText;
|
BString plainText;
|
||||||
ssize_t nread = 0, nreed = 0, ntotalread = 0;
|
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);
|
(size_t)txtheader.header.data_size - ntotalread);
|
||||||
nread = source->Read(buffer, nreed);
|
nread = source->Read(buffer, nreed);
|
||||||
while (nread > 0) {
|
while (nread > 0) {
|
||||||
plainText << buffer;
|
plainText << buffer;
|
||||||
|
|
||||||
ntotalread += nread;
|
ntotalread += nread;
|
||||||
nreed = min((size_t)READ_BUFFER_SIZE,
|
nreed = std::min((size_t)READ_BUFFER_SIZE,
|
||||||
(size_t)txtheader.header.data_size - ntotalread);
|
(size_t)txtheader.header.data_size - ntotalread);
|
||||||
nread = source->Read(buffer, nreed);
|
nread = source->Read(buffer, nreed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ssize_t)txtheader.header.data_size != ntotalread)
|
if ((ssize_t)txtheader.header.data_size != ntotalread)
|
||||||
return B_NO_TRANSLATOR;
|
return B_NO_TRANSLATOR;
|
||||||
|
|
||||||
BString rtfFile =
|
BString rtfFile =
|
||||||
"{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard ";
|
"{\\rtf1\\ansi";
|
||||||
|
|
||||||
rtfFile << plainText << " }";
|
|
||||||
target->Write((const void*)rtfFile, rtfFile.Length());
|
|
||||||
|
|
||||||
|
|
||||||
BNode* node = (BNode*)source;
|
ssize_t read = 0;
|
||||||
const char* kAttrName = "styles";
|
TranslatorStyledTextStyleHeader stylHeader;
|
||||||
attr_info info;
|
read = source->Read(buffer, sizeof(stylHeader));
|
||||||
|
|
||||||
if (node->GetAttrInfo(kAttrName, &info) != B_OK
|
if (read < 0)
|
||||||
|| info.type != B_RAW_TYPE || info.size < 160)
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
uint8* flatRunArray = new (std::nothrow) uint8[info.size];
|
if (read != sizeof(stylHeader) && read != 0)
|
||||||
if (flatRunArray == NULL)
|
{
|
||||||
return B_NO_MEMORY;
|
return B_NO_TRANSLATOR;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->ReadAttr(kAttrName, B_RAW_TYPE, 0, flatRunArray, info.size
|
if (read == sizeof(stylHeader)) // There is a STYL section
|
||||||
!= info.size))
|
{
|
||||||
return B_OK;
|
memcpy(&stylHeader, buffer, sizeof(stylHeader));
|
||||||
|
if (swap_data(B_UINT32_TYPE, &stylHeader, sizeof(stylHeader),
|
||||||
//todo: convert stxt attributes to rtf commands
|
B_SWAP_BENDIAN_TO_HOST) != B_OK)
|
||||||
|
{
|
||||||
//text_run_array* styles = (text_run_array*)flatRunArray;
|
return B_ERROR;
|
||||||
size_t stylesSize = info.size / 3;
|
}
|
||||||
|
|
||||||
for(uint32 i = 0; i < stylesSize; i++) {
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user