Refactored the code: put everything in the RTF namespace, separated group
functions from the former RTFElement (now RTF::Element). Put the parsing basics into a separate class RTF::Parser (used to be in the static RTFHeader::Parse()). RTF::Header is now always correctly set to RTF::TEXT_DESTINATION. Some minor other related changes. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10563 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
40d9768b92
commit
a7e0bca0f7
@ -14,191 +14,15 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
static void
|
static char read_char(BDataIO &stream, bool endOfFileAllowed = false) throw (status_t);
|
||||||
dump(RTFElement &element, int32 level = 0)
|
static int32 parse_integer(char first, BDataIO &stream, char &_last) throw (status_t);
|
||||||
{
|
|
||||||
printf("%03ld:", level);
|
|
||||||
for (int32 i = 0; i < level; i++)
|
|
||||||
printf(" ");
|
|
||||||
|
|
||||||
if (RTFHeader *header = dynamic_cast<RTFHeader *>(&element)) {
|
|
||||||
printf("<RTF header, major version %ld>\n", header->Version());
|
|
||||||
} else if (RTFCommand *command = dynamic_cast<RTFCommand *>(&element)) {
|
|
||||||
printf("<Command: %s", command->Name());
|
|
||||||
if (command->HasOption())
|
|
||||||
printf(", Option %ld", command->Option());
|
|
||||||
puts(">");
|
|
||||||
} else if (RTFText *text = dynamic_cast<RTFText *>(&element)) {
|
|
||||||
printf("<Text>");
|
|
||||||
puts(text->Text());
|
|
||||||
} else
|
|
||||||
puts("<Group>");
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < element.CountElements(); i++)
|
|
||||||
dump(*element.ElementAt(i), level + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
using namespace RTF;
|
||||||
|
|
||||||
|
|
||||||
RTFElement::RTFElement()
|
static char
|
||||||
:
|
read_char(BDataIO &stream, bool endOfFileAllowed) throw (status_t)
|
||||||
fParent(NULL),
|
|
||||||
fDestination(RTF_OTHER)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RTFElement::~RTFElement()
|
|
||||||
{
|
|
||||||
RTFElement *element;
|
|
||||||
while ((element = (RTFElement *)fElements.RemoveItem(0L)) != NULL) {
|
|
||||||
delete element;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RTFElement::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
|
||||||
{
|
|
||||||
if (first == '\0')
|
|
||||||
first = ReadChar(stream);
|
|
||||||
|
|
||||||
if (first != '{')
|
|
||||||
throw (status_t)B_BAD_TYPE;
|
|
||||||
|
|
||||||
last = ReadChar(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
RTFElement::AddElement(RTFElement *element)
|
|
||||||
{
|
|
||||||
if (element == NULL)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
if (fElements.AddItem(element)) {
|
|
||||||
element->fParent = this;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32
|
|
||||||
RTFElement::CountElements() const
|
|
||||||
{
|
|
||||||
return (uint32)fElements.CountItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RTFElement *
|
|
||||||
RTFElement::ElementAt(uint32 index) const
|
|
||||||
{
|
|
||||||
return static_cast<RTFElement *>(fElements.ItemAt(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RTFCommand *
|
|
||||||
RTFElement::FindDefinition(const char *name, int32 index) const
|
|
||||||
{
|
|
||||||
if (index < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
RTFElement *element;
|
|
||||||
int32 number = 0;
|
|
||||||
for (uint32 i = 0; (element = ElementAt(i)) != NULL; i++) {
|
|
||||||
if (RTFText *text = dynamic_cast<RTFText *>(element)) {
|
|
||||||
// the ';' indicates the next definition
|
|
||||||
if (!strcmp(text->Text(), ";"))
|
|
||||||
number++;
|
|
||||||
} else if (RTFCommand *command = dynamic_cast<RTFCommand *>(element)) {
|
|
||||||
if (command != NULL
|
|
||||||
&& !strcmp(name, command->Name())
|
|
||||||
&& number == index)
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RTFElement *
|
|
||||||
RTFElement::FindGroup(const char *name) const
|
|
||||||
{
|
|
||||||
RTFElement *group;
|
|
||||||
for (uint32 i = 0; (group = ElementAt(i)) != NULL; i++) {
|
|
||||||
RTFCommand *command = dynamic_cast<RTFCommand *>(group->ElementAt(0));
|
|
||||||
if (command != NULL && !strcmp(name, command->Name()))
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
|
||||||
RTFElement::GroupName() const
|
|
||||||
{
|
|
||||||
RTFCommand *command = dynamic_cast<RTFCommand *>(ElementAt(0));
|
|
||||||
if (command != NULL)
|
|
||||||
return command->Name();
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RTFElement *
|
|
||||||
RTFElement::Parent() const
|
|
||||||
{
|
|
||||||
return fParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RTFElement::PrintToStream()
|
|
||||||
{
|
|
||||||
dump(*this, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RTFElement::DetermineDestination()
|
|
||||||
{
|
|
||||||
const char *name = GroupName();
|
|
||||||
if (name == NULL)
|
|
||||||
fDestination = RTF_TEXT;
|
|
||||||
|
|
||||||
if (!strcmp(name, "*")) {
|
|
||||||
fDestination = RTF_COMMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *texts[] = {"rtf", "sect", "par"};
|
|
||||||
for (uint32 i = 0; i < sizeof(texts) / sizeof(texts[0]); i++) {
|
|
||||||
if (!strcmp(texts[i], name)) {
|
|
||||||
fDestination = RTF_TEXT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fDestination = RTF_OTHER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
rtf_destination
|
|
||||||
RTFElement::Destination() const
|
|
||||||
{
|
|
||||||
return fDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* static */
|
|
||||||
char
|
|
||||||
RTFElement::ReadChar(BDataIO &stream, bool endOfFileAllowed) throw (status_t)
|
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
ssize_t bytesRead = stream.Read(&c, 1);
|
ssize_t bytesRead = stream.Read(&c, 1);
|
||||||
@ -213,9 +37,8 @@ RTFElement::ReadChar(BDataIO &stream, bool endOfFileAllowed) throw (status_t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* static */
|
static int32
|
||||||
int32
|
parse_integer(char first, BDataIO &stream, char &_last) throw (status_t)
|
||||||
RTFElement::ParseInteger(char first, BDataIO &stream, char &_last) throw (status_t)
|
|
||||||
{
|
{
|
||||||
int32 integer = 0;
|
int32 integer = 0;
|
||||||
int32 count = 0;
|
int32 count = 0;
|
||||||
@ -223,7 +46,7 @@ RTFElement::ParseInteger(char first, BDataIO &stream, char &_last) throw (status
|
|||||||
char digit = first;
|
char digit = first;
|
||||||
|
|
||||||
if (digit == '\0')
|
if (digit == '\0')
|
||||||
digit = ReadChar(stream);
|
digit = read_char(stream);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isdigit(digit)) {
|
if (isdigit(digit)) {
|
||||||
@ -234,7 +57,7 @@ RTFElement::ParseInteger(char first, BDataIO &stream, char &_last) throw (status
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
digit = ReadChar(stream);
|
digit = read_char(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@ -245,141 +68,122 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump(Element &element, int32 level = 0)
|
||||||
|
{
|
||||||
|
printf("%03ld:", level);
|
||||||
|
for (int32 i = 0; i < level; i++)
|
||||||
|
printf(" ");
|
||||||
|
|
||||||
|
if (RTF::Header *header = dynamic_cast<RTF::Header *>(&element)) {
|
||||||
|
printf("<RTF header, major version %ld>\n", header->Version());
|
||||||
|
} else if (RTF::Command *command = dynamic_cast<RTF::Command *>(&element)) {
|
||||||
|
printf("<Command: %s", command->Name());
|
||||||
|
if (command->HasOption())
|
||||||
|
printf(", Option %ld", command->Option());
|
||||||
|
puts(">");
|
||||||
|
} else if (RTF::Text *text = dynamic_cast<RTF::Text *>(&element)) {
|
||||||
|
printf("<Text>");
|
||||||
|
puts(text->String());
|
||||||
|
} else if (dynamic_cast<RTF::Group *>(&element) != NULL)
|
||||||
|
puts("<Group>");
|
||||||
|
|
||||||
|
if (RTF::Group *group = dynamic_cast<RTF::Group *>(&element)) {
|
||||||
|
for (uint32 i = 0; i < group->CountElements(); i++)
|
||||||
|
dump(*group->ElementAt(i), level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
RTFHeader::RTFHeader()
|
Parser::Parser(BDataIO &stream)
|
||||||
:
|
:
|
||||||
fVersion(0)
|
fStream(stream),
|
||||||
|
fIdentified(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RTFHeader::~RTFHeader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
RTFHeader::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
|
||||||
{
|
|
||||||
int32 openBrackets = 1;
|
|
||||||
|
|
||||||
// The stream has been picked up by the static RTFHeader::Parse(), so
|
|
||||||
// the version follows in the stream -- let's pick it up
|
|
||||||
|
|
||||||
fVersion = ParseInteger(first, stream, last);
|
|
||||||
|
|
||||||
RTFElement *parent = this;
|
|
||||||
char c = last;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
RTFElement *element = NULL;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case '{':
|
|
||||||
openBrackets++;
|
|
||||||
parent->AddElement(element = new RTFElement());
|
|
||||||
parent = element;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\\':
|
|
||||||
parent->AddElement(element = new RTFCommand());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '}':
|
|
||||||
openBrackets--;
|
|
||||||
parent->DetermineDestination();
|
|
||||||
parent = parent->Parent();
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
{
|
|
||||||
ssize_t bytesRead = stream.Read(&c, 1);
|
|
||||||
if (bytesRead < B_OK)
|
|
||||||
throw (status_t)bytesRead;
|
|
||||||
else if (bytesRead != 1) {
|
|
||||||
// this is the only valid exit status
|
|
||||||
if (openBrackets == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
throw B_ERROR;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
parent->AddElement(element = new RTFText());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element == NULL)
|
|
||||||
throw (status_t)B_ERROR;
|
|
||||||
|
|
||||||
element->Parse(c, stream, last);
|
|
||||||
c = last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32
|
|
||||||
RTFHeader::Version() const
|
|
||||||
{
|
|
||||||
return fVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char *
|
|
||||||
RTFHeader::Charset() const
|
|
||||||
{
|
|
||||||
RTFCommand *command = dynamic_cast<RTFCommand *>(ElementAt(0));
|
|
||||||
if (command == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return command->Name();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
rgb_color
|
|
||||||
RTFHeader::Color(int32 index)
|
|
||||||
{
|
|
||||||
rgb_color color = {0, 0, 0, 255};
|
|
||||||
|
|
||||||
RTFElement *colorTable = FindGroup("colortbl");
|
|
||||||
|
|
||||||
if (colorTable != NULL) {
|
|
||||||
if (RTFCommand *gun = colorTable->FindDefinition("red", index))
|
|
||||||
color.red = gun->Option();
|
|
||||||
if (RTFCommand *gun = colorTable->FindDefinition("green", index))
|
|
||||||
color.green = gun->Option();
|
|
||||||
if (RTFCommand *gun = colorTable->FindDefinition("blue", index))
|
|
||||||
color.blue = gun->Option();
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
RTFHeader::Identify(BDataIO &stream)
|
Parser::Identify()
|
||||||
{
|
{
|
||||||
char header[5];
|
char header[5];
|
||||||
|
if (fStream.Read(header, sizeof(header)) < (ssize_t)sizeof(header))
|
||||||
if (stream.Read(header, sizeof(header)) < (ssize_t)sizeof(header))
|
|
||||||
return B_IO_ERROR;
|
return B_IO_ERROR;
|
||||||
|
|
||||||
return strncmp(header, "{\\rtf", 5) ? B_BAD_TYPE : B_OK;
|
if (strncmp(header, "{\\rtf", 5))
|
||||||
|
return B_BAD_TYPE;
|
||||||
|
|
||||||
|
fIdentified = true;
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
RTFHeader::Parse(BDataIO &stream, RTFHeader &header, bool identified)
|
Parser::Parse(Header &header)
|
||||||
{
|
{
|
||||||
if (!identified && Identify(stream) != B_OK)
|
if (!fIdentified && Identify() != B_OK)
|
||||||
return B_BAD_TYPE;
|
return B_BAD_TYPE;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
int32 openBrackets = 1;
|
||||||
|
|
||||||
|
// since we already preparsed parts of the RTF header, the header
|
||||||
|
// is handled here directly
|
||||||
char last;
|
char last;
|
||||||
header.Parse('\0', stream, last);
|
header.Parse('\0', fStream, last);
|
||||||
|
|
||||||
|
Group *parent = &header;
|
||||||
|
char c = last;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
Element *element = NULL;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '{':
|
||||||
|
openBrackets++;
|
||||||
|
parent->AddElement(element = new Group());
|
||||||
|
parent = static_cast<Group *>(element);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
parent->AddElement(element = new Command());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '}':
|
||||||
|
openBrackets--;
|
||||||
|
parent->DetermineDestination();
|
||||||
|
parent = parent->Parent();
|
||||||
|
// supposed to fall through
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
{
|
||||||
|
ssize_t bytesRead = fStream.Read(&c, 1);
|
||||||
|
if (bytesRead < B_OK)
|
||||||
|
throw (status_t)bytesRead;
|
||||||
|
else if (bytesRead != 1) {
|
||||||
|
// this is the only valid exit status
|
||||||
|
if (openBrackets == 0)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
throw B_ERROR;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
parent->AddElement(element = new Text());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element == NULL)
|
||||||
|
throw (status_t)B_ERROR;
|
||||||
|
|
||||||
|
element->Parse(c, fStream, last);
|
||||||
|
c = last;
|
||||||
|
}
|
||||||
} catch (status_t status) {
|
} catch (status_t status) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -391,23 +195,278 @@ RTFHeader::Parse(BDataIO &stream, RTFHeader &header, bool identified)
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
RTFText::RTFText()
|
Element::Element()
|
||||||
|
:
|
||||||
|
fParent(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RTFText::~RTFText()
|
Element::~Element()
|
||||||
{
|
{
|
||||||
SetText(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RTFText::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
Element::SetParent(Group *parent)
|
||||||
|
{
|
||||||
|
fParent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Group *
|
||||||
|
Element::Parent() const
|
||||||
|
{
|
||||||
|
return fParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Element::PrintToStream(int32 level)
|
||||||
|
{
|
||||||
|
dump(*this, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
Group::Group()
|
||||||
|
:
|
||||||
|
fDestination(OTHER_DESTINATION)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Group::~Group()
|
||||||
|
{
|
||||||
|
Element *element;
|
||||||
|
while ((element = (Element *)fElements.RemoveItem(0L)) != NULL) {
|
||||||
|
delete element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Group::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
||||||
|
{
|
||||||
|
if (first == '\0')
|
||||||
|
first = read_char(stream);
|
||||||
|
|
||||||
|
if (first != '{')
|
||||||
|
throw (status_t)B_BAD_TYPE;
|
||||||
|
|
||||||
|
last = read_char(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Group::AddElement(Element *element)
|
||||||
|
{
|
||||||
|
if (element == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
if (fElements.AddItem(element)) {
|
||||||
|
element->SetParent(this);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32
|
||||||
|
Group::CountElements() const
|
||||||
|
{
|
||||||
|
return (uint32)fElements.CountItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Element *
|
||||||
|
Group::ElementAt(uint32 index) const
|
||||||
|
{
|
||||||
|
return static_cast<Element *>(fElements.ItemAt(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Command *
|
||||||
|
Group::FindDefinition(const char *name, int32 index) const
|
||||||
|
{
|
||||||
|
if (index < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Element *element;
|
||||||
|
int32 number = 0;
|
||||||
|
for (uint32 i = 0; (element = ElementAt(i)) != NULL; i++) {
|
||||||
|
if (Text *text = dynamic_cast<Text *>(element)) {
|
||||||
|
// the ';' indicates the next definition
|
||||||
|
if (!strcmp(text->String(), ";"))
|
||||||
|
number++;
|
||||||
|
} else if (Command *command = dynamic_cast<Command *>(element)) {
|
||||||
|
if (command != NULL
|
||||||
|
&& !strcmp(name, command->Name())
|
||||||
|
&& number == index)
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Group *
|
||||||
|
Group::FindGroup(const char *name) const
|
||||||
|
{
|
||||||
|
Element *element;
|
||||||
|
for (uint32 i = 0; (element = ElementAt(i)) != NULL; i++) {
|
||||||
|
Group *group = dynamic_cast<Group *>(element);
|
||||||
|
if (group == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Command *command = dynamic_cast<Command *>(group->ElementAt(0));
|
||||||
|
if (command != NULL && !strcmp(name, command->Name()))
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *
|
||||||
|
Group::Name() const
|
||||||
|
{
|
||||||
|
Command *command = dynamic_cast<Command *>(ElementAt(0));
|
||||||
|
if (command != NULL)
|
||||||
|
return command->Name();
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Group::DetermineDestination()
|
||||||
|
{
|
||||||
|
const char *name = Name();
|
||||||
|
if (name == NULL) {
|
||||||
|
fDestination = TEXT_DESTINATION;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(name, "*")) {
|
||||||
|
fDestination = COMMENT_DESTINATION;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *texts[] = {"rtf", "sect", "par"};
|
||||||
|
for (uint32 i = 0; i < sizeof(texts) / sizeof(texts[0]); i++) {
|
||||||
|
if (!strcmp(name, texts[i])) {
|
||||||
|
fDestination = TEXT_DESTINATION;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fDestination = OTHER_DESTINATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
group_destination
|
||||||
|
Group::Destination() const
|
||||||
|
{
|
||||||
|
return fDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
Header::Header()
|
||||||
|
:
|
||||||
|
fVersion(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Header::~Header()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Header::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
||||||
|
{
|
||||||
|
// The stream has been peeked into by the parser already, and
|
||||||
|
// only the version follows in the stream -- let's pick it up
|
||||||
|
|
||||||
|
fVersion = parse_integer(first, stream, last);
|
||||||
|
|
||||||
|
// recreate "rtf" command to name this group
|
||||||
|
|
||||||
|
Command *command = new Command();
|
||||||
|
command->SetName("rtf");
|
||||||
|
command->SetOption(fVersion);
|
||||||
|
|
||||||
|
AddElement(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
Header::Version() const
|
||||||
|
{
|
||||||
|
return fVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *
|
||||||
|
Header::Charset() const
|
||||||
|
{
|
||||||
|
Command *command = dynamic_cast<Command *>(ElementAt(1));
|
||||||
|
if (command == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return command->Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rgb_color
|
||||||
|
Header::Color(int32 index)
|
||||||
|
{
|
||||||
|
rgb_color color = {0, 0, 0, 255};
|
||||||
|
|
||||||
|
Group *colorTable = FindGroup("colortbl");
|
||||||
|
|
||||||
|
if (colorTable != NULL) {
|
||||||
|
if (Command *gun = colorTable->FindDefinition("red", index))
|
||||||
|
color.red = gun->Option();
|
||||||
|
if (Command *gun = colorTable->FindDefinition("green", index))
|
||||||
|
color.green = gun->Option();
|
||||||
|
if (Command *gun = colorTable->FindDefinition("blue", index))
|
||||||
|
color.blue = gun->Option();
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
Text::Text()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text::~Text()
|
||||||
|
{
|
||||||
|
SetTo(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Text::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
||||||
{
|
{
|
||||||
char c = first;
|
char c = first;
|
||||||
if (c == '\0')
|
if (c == '\0')
|
||||||
c = ReadChar(stream);
|
c = read_char(stream);
|
||||||
|
|
||||||
fText = "";
|
fText = "";
|
||||||
|
|
||||||
@ -417,7 +476,7 @@ RTFText::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
|||||||
|
|
||||||
// ToDo: this is horribly inefficient with BStrings
|
// ToDo: this is horribly inefficient with BStrings
|
||||||
fText.Append(c, 1);
|
fText.Append(c, 1);
|
||||||
c = ReadChar(stream);
|
c = read_char(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: add support for different charsets - right now, only ASCII is supported!
|
// ToDo: add support for different charsets - right now, only ASCII is supported!
|
||||||
@ -428,21 +487,21 @@ RTFText::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
RTFText::SetText(const char *text)
|
Text::SetTo(const char *text)
|
||||||
{
|
{
|
||||||
return fText.SetTo(text) != NULL ? B_OK : B_NO_MEMORY;
|
return fText.SetTo(text) != NULL ? B_OK : B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
RTFText::Text() const
|
Text::String() const
|
||||||
{
|
{
|
||||||
return fText.String();
|
return fText.String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
RTFText::TextLength() const
|
Text::Length() const
|
||||||
{
|
{
|
||||||
return fText.Length();
|
return fText.Length();
|
||||||
}
|
}
|
||||||
@ -451,7 +510,7 @@ RTFText::TextLength() const
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
RTFCommand::RTFCommand()
|
Command::Command()
|
||||||
:
|
:
|
||||||
fName(NULL),
|
fName(NULL),
|
||||||
fHasOption(false),
|
fHasOption(false),
|
||||||
@ -460,76 +519,74 @@ RTFCommand::RTFCommand()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RTFCommand::~RTFCommand()
|
Command::~Command()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RTFCommand::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
Command::Parse(char first, BDataIO &stream, char &last) throw (status_t)
|
||||||
{
|
{
|
||||||
if (first == '\0')
|
if (first == '\0')
|
||||||
first = ReadChar(stream);
|
first = read_char(stream);
|
||||||
|
|
||||||
if (first != '\\')
|
if (first != '\\')
|
||||||
throw B_BAD_TYPE;
|
throw B_BAD_TYPE;
|
||||||
|
|
||||||
// get name
|
// get name
|
||||||
char name[kRTFCommandLength];
|
char name[kCommandLength];
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
char c;
|
char c;
|
||||||
while (isalpha(c = ReadChar(stream))) {
|
while (isalpha(c = read_char(stream))) {
|
||||||
name[length++] = c;
|
name[length++] = c;
|
||||||
if (length >= kRTFCommandLength - 1)
|
if (length >= kCommandLength - 1)
|
||||||
throw B_BAD_TYPE;
|
throw B_BAD_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
if (c == '*') {
|
if (c == '\n' || c == '\r') {
|
||||||
// we're a comment!
|
|
||||||
name[0] = 'c';
|
|
||||||
length++;
|
|
||||||
} else if (c == '\n') {
|
|
||||||
// we're a hard return
|
// we're a hard return
|
||||||
name[0] = '\n';
|
fName.SetTo("par");
|
||||||
length++;
|
} else
|
||||||
}
|
fName.SetTo(c, 1);
|
||||||
}
|
|
||||||
|
|
||||||
fName.SetTo(name, length);
|
// read over character
|
||||||
|
c = read_char(stream);
|
||||||
|
} else
|
||||||
|
fName.SetTo(name, length);
|
||||||
|
|
||||||
// parse numeric option
|
// parse numeric option
|
||||||
|
|
||||||
if (c == '-')
|
if (c == '-')
|
||||||
c = ReadChar(stream);
|
c = read_char(stream);
|
||||||
|
|
||||||
last = c;
|
last = c;
|
||||||
|
|
||||||
if (isdigit(c))
|
if (isdigit(c))
|
||||||
SetOption(ParseInteger(c, stream, last));
|
SetOption(parse_integer(c, stream, last));
|
||||||
|
|
||||||
// a space delimiter is eaten up by the command
|
// a space delimiter is eaten up by the command
|
||||||
if (isspace(last))
|
if (isspace(last))
|
||||||
last = ReadChar(stream);
|
last = read_char(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
RTFCommand::SetName(const char *name)
|
Command::SetName(const char *name)
|
||||||
{
|
{
|
||||||
return fName.SetTo(name) != NULL ? B_OK : B_NO_MEMORY;
|
return fName.SetTo(name) != NULL ? B_OK : B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
RTFCommand::Name()
|
Command::Name()
|
||||||
{
|
{
|
||||||
return fName.String();
|
return fName.String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RTFCommand::UnsetOption()
|
Command::UnsetOption()
|
||||||
{
|
{
|
||||||
fHasOption = false;
|
fHasOption = false;
|
||||||
fOption = -1;
|
fOption = -1;
|
||||||
@ -537,7 +594,7 @@ RTFCommand::UnsetOption()
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RTFCommand::SetOption(int32 option)
|
Command::SetOption(int32 option)
|
||||||
{
|
{
|
||||||
fOption = option;
|
fOption = option;
|
||||||
fHasOption = true;
|
fHasOption = true;
|
||||||
@ -545,14 +602,14 @@ RTFCommand::SetOption(int32 option)
|
|||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
RTFCommand::HasOption() const
|
Command::HasOption() const
|
||||||
{
|
{
|
||||||
return fHasOption;
|
return fHasOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
RTFCommand::Option() const
|
Command::Option() const
|
||||||
{
|
{
|
||||||
return fOption;
|
return fOption;
|
||||||
}
|
}
|
||||||
@ -561,14 +618,14 @@ RTFCommand::Option() const
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
RTFIterator::RTFIterator(RTFElement &start, rtf_destination destination)
|
Iterator::Iterator(Element &start, group_destination destination)
|
||||||
{
|
{
|
||||||
SetTo(start, destination);
|
SetTo(start, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RTFIterator::SetTo(RTFElement &start, rtf_destination destination)
|
Iterator::SetTo(Element &start, group_destination destination)
|
||||||
{
|
{
|
||||||
fStart = &start;
|
fStart = &start;
|
||||||
fDestination = destination;
|
fDestination = destination;
|
||||||
@ -578,7 +635,7 @@ RTFIterator::SetTo(RTFElement &start, rtf_destination destination)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RTFIterator::Rewind()
|
Iterator::Rewind()
|
||||||
{
|
{
|
||||||
fStack.MakeEmpty();
|
fStack.MakeEmpty();
|
||||||
fStack.Push(fStart);
|
fStack.Push(fStart);
|
||||||
@ -586,31 +643,31 @@ RTFIterator::Rewind()
|
|||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
RTFIterator::HasNext() const
|
Iterator::HasNext() const
|
||||||
{
|
{
|
||||||
return !fStack.IsEmpty();
|
return !fStack.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RTFElement *
|
Element *
|
||||||
RTFIterator::Next()
|
Iterator::Next()
|
||||||
{
|
{
|
||||||
RTFElement *element;
|
Element *element;
|
||||||
|
|
||||||
if (!fStack.Pop(&element))
|
if (!fStack.Pop(&element))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// put this element's children on the stack in
|
Group *group = dynamic_cast<Group *>(element);
|
||||||
// reverse order, so that we iterate over the
|
if (group != NULL
|
||||||
// tree in in-order
|
&& (fDestination == ALL_DESTINATIONS
|
||||||
|
|| fDestination == group->Destination())) {
|
||||||
|
// put this group's children on the stack in
|
||||||
|
// reverse order, so that we iterate over
|
||||||
|
// the tree in in-order
|
||||||
|
|
||||||
for (int32 i = element->CountElements(); i-- > 0;) {
|
for (int32 i = group->CountElements(); i-- > 0;) {
|
||||||
RTFElement *child = element->ElementAt(i);
|
fStack.Push(group->ElementAt(i));
|
||||||
|
}
|
||||||
if (fDestination == RTF_ALL_DESTINATIONS
|
|
||||||
|| child->CountElements() == 0
|
|
||||||
|| fDestination == element->Destination())
|
|
||||||
fStack.Push(child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
|
@ -13,90 +13,113 @@
|
|||||||
#include <GraphicsDefs.h>
|
#include <GraphicsDefs.h>
|
||||||
|
|
||||||
class BDataIO;
|
class BDataIO;
|
||||||
class RTFCommand;
|
|
||||||
|
|
||||||
|
namespace RTF {
|
||||||
|
|
||||||
static const size_t kRTFCommandLength = 32;
|
class Group;
|
||||||
|
class Header;
|
||||||
|
class Command;
|
||||||
|
|
||||||
|
static const size_t kCommandLength = 32;
|
||||||
|
|
||||||
enum rtf_destination {
|
enum group_destination {
|
||||||
RTF_TEXT,
|
TEXT_DESTINATION,
|
||||||
RTF_COMMENT,
|
COMMENT_DESTINATION,
|
||||||
RTF_OTHER,
|
OTHER_DESTINATION,
|
||||||
|
|
||||||
RTF_ALL_DESTINATIONS = 255
|
ALL_DESTINATIONS = 255
|
||||||
};
|
};
|
||||||
|
|
||||||
class RTFElement {
|
|
||||||
|
class Parser {
|
||||||
public:
|
public:
|
||||||
RTFElement();
|
Parser(BDataIO &stream);
|
||||||
virtual ~RTFElement();
|
|
||||||
|
|
||||||
status_t AddElement(RTFElement *element);
|
status_t Identify();
|
||||||
uint32 CountElements() const;
|
status_t Parse(RTF::Header &header);
|
||||||
RTFElement *ElementAt(uint32 index) const;
|
|
||||||
RTFCommand *FindDefinition(const char *name, int32 index = 0) const;
|
|
||||||
RTFElement *FindGroup(const char *name) const;
|
|
||||||
const char *GroupName() const;
|
|
||||||
RTFElement *Parent() const;
|
|
||||||
|
|
||||||
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
|
||||||
virtual void PrintToStream();
|
|
||||||
|
|
||||||
void DetermineDestination();
|
|
||||||
rtf_destination Destination() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static char ReadChar(BDataIO &stream, bool endOfFileAllowed = false) throw (status_t);
|
|
||||||
static int32 ParseInteger(char first, BDataIO &stream, char &last) throw (status_t);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RTFElement *fParent;
|
BDataIO &fStream;
|
||||||
BList fElements;
|
bool fIdentified;
|
||||||
rtf_destination fDestination;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RTFHeader : public RTFElement {
|
|
||||||
|
class Element {
|
||||||
public:
|
public:
|
||||||
RTFHeader();
|
Element();
|
||||||
virtual ~RTFHeader();
|
virtual ~Element();
|
||||||
|
|
||||||
|
void SetParent(Group *parent);
|
||||||
|
Group *Parent() const;
|
||||||
|
|
||||||
|
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t) = 0;
|
||||||
|
virtual void PrintToStream(int32 level = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Group *fParent;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Group : public Element {
|
||||||
|
public:
|
||||||
|
Group();
|
||||||
|
virtual ~Group();
|
||||||
|
|
||||||
|
status_t AddElement(RTF::Element *element);
|
||||||
|
uint32 CountElements() const;
|
||||||
|
Element *ElementAt(uint32 index) const;
|
||||||
|
|
||||||
|
Command *FindDefinition(const char *name, int32 index = 0) const;
|
||||||
|
Group *FindGroup(const char *name) const;
|
||||||
|
|
||||||
|
const char *Name() const;
|
||||||
|
|
||||||
|
void DetermineDestination();
|
||||||
|
group_destination Destination() const;
|
||||||
|
|
||||||
|
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BList fElements;
|
||||||
|
group_destination fDestination;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Header : public Group {
|
||||||
|
public:
|
||||||
|
Header();
|
||||||
|
virtual ~Header();
|
||||||
|
|
||||||
int32 Version() const;
|
int32 Version() const;
|
||||||
const char *Charset() const;
|
const char *Charset() const;
|
||||||
|
|
||||||
rgb_color Color(int32 index);
|
rgb_color Color(int32 index);
|
||||||
|
|
||||||
static status_t Identify(BDataIO &stream);
|
|
||||||
static status_t Parse(BDataIO &stream, RTFHeader &header, bool identified = false);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int32 fVersion;
|
int32 fVersion;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RTFText : public RTFElement {
|
class Text : public Element {
|
||||||
public:
|
public:
|
||||||
RTFText();
|
Text();
|
||||||
virtual ~RTFText();
|
virtual ~Text();
|
||||||
|
|
||||||
status_t SetText(const char *text);
|
status_t SetTo(const char *text);
|
||||||
const char *Text() const;
|
const char *String() const;
|
||||||
uint32 TextLength() const;
|
uint32 Length() const;
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BString fText;
|
BString fText;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RTFCommand : public RTFElement {
|
class Command : public Element {
|
||||||
public:
|
public:
|
||||||
RTFCommand();
|
Command();
|
||||||
virtual ~RTFCommand();
|
virtual ~Command();
|
||||||
|
|
||||||
status_t SetName(const char *name);
|
status_t SetName(const char *name);
|
||||||
const char *Name();
|
const char *Name();
|
||||||
@ -106,31 +129,32 @@ class RTFCommand : public RTFElement {
|
|||||||
bool HasOption() const;
|
bool HasOption() const;
|
||||||
int32 Option() const;
|
int32 Option() const;
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BString fName;
|
BString fName;
|
||||||
bool fHasOption;
|
bool fHasOption;
|
||||||
int32 fOption;
|
int32 fOption;
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
|
|
||||||
class RTFIterator {
|
class Iterator {
|
||||||
public:
|
public:
|
||||||
RTFIterator(RTFElement &start, rtf_destination destination = RTF_ALL_DESTINATIONS);
|
Iterator(Element &start, group_destination destination = ALL_DESTINATIONS);
|
||||||
|
|
||||||
void SetTo(RTFElement &start, rtf_destination destination = RTF_ALL_DESTINATIONS);
|
void SetTo(Element &start, group_destination destination = ALL_DESTINATIONS);
|
||||||
void Rewind();
|
void Rewind();
|
||||||
|
|
||||||
bool HasNext() const;
|
bool HasNext() const;
|
||||||
RTFElement *Next();
|
Element *Next();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RTFElement *fStart;
|
Element *fStart;
|
||||||
Stack<RTFElement *> fStack;
|
Stack<Element *> fStack;
|
||||||
rtf_destination fDestination;
|
group_destination fDestination;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace RTF
|
||||||
|
|
||||||
#endif /* RTF_H */
|
#endif /* RTF_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user