From 40d9768b92ca344dae41e926888d6847ae6831c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 2 Jan 2005 23:28:30 +0000 Subject: [PATCH] Added a simple RTF translator. It only supports colors and bold fonts beside plain ASCII text, but it's a start. The RTF* class hierarchy should get a cleanup, though (and will soon). You'll need Haiku's StyledEdit to make use of this translator. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10562 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/translators/Jamfile | 1 + .../translators/rtftranslator/ConfigView.cpp | 55 ++ .../translators/rtftranslator/ConfigView.h | 19 + src/add-ons/translators/rtftranslator/Jamfile | 27 + src/add-ons/translators/rtftranslator/RTF.cpp | 618 ++++++++++++++++++ src/add-ons/translators/rtftranslator/RTF.h | 136 ++++ .../rtftranslator/RTFTranslator.cpp | 193 ++++++ .../translators/rtftranslator/RTFTranslator.h | 64 ++ src/add-ons/translators/rtftranslator/Stack.h | 68 ++ .../translators/rtftranslator/convert.cpp | 341 ++++++++++ .../translators/rtftranslator/convert.h | 16 + .../translators/rtftranslator/main.cpp | 31 + 12 files changed, 1569 insertions(+) create mode 100644 src/add-ons/translators/rtftranslator/ConfigView.cpp create mode 100644 src/add-ons/translators/rtftranslator/ConfigView.h create mode 100644 src/add-ons/translators/rtftranslator/Jamfile create mode 100644 src/add-ons/translators/rtftranslator/RTF.cpp create mode 100644 src/add-ons/translators/rtftranslator/RTF.h create mode 100644 src/add-ons/translators/rtftranslator/RTFTranslator.cpp create mode 100644 src/add-ons/translators/rtftranslator/RTFTranslator.h create mode 100644 src/add-ons/translators/rtftranslator/Stack.h create mode 100644 src/add-ons/translators/rtftranslator/convert.cpp create mode 100644 src/add-ons/translators/rtftranslator/convert.h create mode 100644 src/add-ons/translators/rtftranslator/main.cpp diff --git a/src/add-ons/translators/Jamfile b/src/add-ons/translators/Jamfile index 955ebd7250..305a94852f 100644 --- a/src/add-ons/translators/Jamfile +++ b/src/add-ons/translators/Jamfile @@ -7,6 +7,7 @@ SubInclude OBOS_TOP src add-ons translators jpeg2000translator ; SubInclude OBOS_TOP src add-ons translators libtifftranslator ; SubInclude OBOS_TOP src add-ons translators pngtranslator ; SubInclude OBOS_TOP src add-ons translators ppmtranslator ; +SubInclude OBOS_TOP src add-ons translators rtftranslator ; SubInclude OBOS_TOP src add-ons translators sgitranslator ; SubInclude OBOS_TOP src add-ons translators stxttranslator ; SubInclude OBOS_TOP src add-ons translators tgatranslator ; diff --git a/src/add-ons/translators/rtftranslator/ConfigView.cpp b/src/add-ons/translators/rtftranslator/ConfigView.cpp new file mode 100644 index 0000000000..61d54bf89c --- /dev/null +++ b/src/add-ons/translators/rtftranslator/ConfigView.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "ConfigView.h" +#include "RTFTranslator.h" + +#include + +#include +#include + + +ConfigView::ConfigView(const BRect &frame, uint32 resize, uint32 flags) + : BView(frame, "RTF-Translator Settings", resize, flags) +{ + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + font_height fontHeight; + be_bold_font->GetHeight(&fontHeight); + float height = fontHeight.descent + fontHeight.ascent + fontHeight.leading; + + BRect rect(10, 10, 200, 10 + height); + BStringView *stringView = new BStringView(rect, "title", "Rich Text Format (RTF) Files"); + stringView->SetFont(be_bold_font); + stringView->ResizeToPreferred(); + AddChild(stringView); + + rect.OffsetBy(0, height + 10); + char version[256]; + sprintf(version, "Version %d.%d.%d, %s", + int(B_TRANSLATION_MAJOR_VER(RTF_TRANSLATOR_VERSION)), + int(B_TRANSLATION_MINOR_VER(RTF_TRANSLATOR_VERSION)), + int(B_TRANSLATION_REVSN_VER(RTF_TRANSLATOR_VERSION)), + __DATE__); + stringView = new BStringView(rect, "version", version); + stringView->ResizeToPreferred(); + AddChild(stringView); + + GetFontHeight(&fontHeight); + height = fontHeight.descent + fontHeight.ascent + fontHeight.leading; + + rect.OffsetBy(0, height + 5); + stringView = new BStringView(rect, "copyright", B_UTF8_COPYRIGHT "2004-2005 Haiku Inc."); + stringView->ResizeToPreferred(); + AddChild(stringView); +} + + +ConfigView::~ConfigView() +{ +} + diff --git a/src/add-ons/translators/rtftranslator/ConfigView.h b/src/add-ons/translators/rtftranslator/ConfigView.h new file mode 100644 index 0000000000..8be5d04f18 --- /dev/null +++ b/src/add-ons/translators/rtftranslator/ConfigView.h @@ -0,0 +1,19 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef CONFIG_VIEW_H +#define CONFIG_VIEW_H + + +#include + + +class ConfigView : public BView { + public: + ConfigView(const BRect &frame, uint32 resize = B_FOLLOW_ALL, + uint32 flags = B_WILL_DRAW); + virtual ~ConfigView(); +}; + +#endif /* CONFIG_VIEW_H */ diff --git a/src/add-ons/translators/rtftranslator/Jamfile b/src/add-ons/translators/rtftranslator/Jamfile new file mode 100644 index 0000000000..76019c2bf7 --- /dev/null +++ b/src/add-ons/translators/rtftranslator/Jamfile @@ -0,0 +1,27 @@ +SubDir OBOS_TOP src add-ons translators rtftranslator ; + +# Include code from shared translator directory +SEARCH_SOURCE += [ FDirName $(OBOS_TOP) src add-ons translators shared ] ; + +# It's called RTF-Translator (with a dash) to differentiate it from the +# RTFTranslator that comes with Gobe Productive (that doesn't support +# STXT or plain text files output). + +Translator RTF-Translator : + # RTFTranslator classes + main.cpp + RTFTranslator.cpp + ConfigView.cpp + RTF.cpp + convert.cpp + + # shared classes + TranslatorWindow.cpp + ; + +LinkSharedOSLibs RTF-Translator : be translation ; + +Package haiku-translationkit-cvs : + RTF-Translator + : boot home config add-ons Translators + ; diff --git a/src/add-ons/translators/rtftranslator/RTF.cpp b/src/add-ons/translators/rtftranslator/RTF.cpp new file mode 100644 index 0000000000..743d25c467 --- /dev/null +++ b/src/add-ons/translators/rtftranslator/RTF.cpp @@ -0,0 +1,618 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "RTF.h" + +#include + +#include +#include +#include +#include + + +static void +dump(RTFElement &element, int32 level = 0) +{ + printf("%03ld:", level); + for (int32 i = 0; i < level; i++) + printf(" "); + + if (RTFHeader *header = dynamic_cast(&element)) { + printf("\n", header->Version()); + } else if (RTFCommand *command = dynamic_cast(&element)) { + printf("Name()); + if (command->HasOption()) + printf(", Option %ld", command->Option()); + puts(">"); + } else if (RTFText *text = dynamic_cast(&element)) { + printf(""); + puts(text->Text()); + } else + puts(""); + + for (uint32 i = 0; i < element.CountElements(); i++) + dump(*element.ElementAt(i), level + 1); +} + + +// #pragma mark - + + +RTFElement::RTFElement() + : + 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(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(element)) { + // the ';' indicates the next definition + if (!strcmp(text->Text(), ";")) + number++; + } else if (RTFCommand *command = dynamic_cast(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(group->ElementAt(0)); + if (command != NULL && !strcmp(name, command->Name())) + return group; + } + + return NULL; +} + + +const char * +RTFElement::GroupName() const +{ + RTFCommand *command = dynamic_cast(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; + ssize_t bytesRead = stream.Read(&c, 1); + + if (bytesRead < B_OK) + throw (status_t)bytesRead; + + if (bytesRead == 0 && !endOfFileAllowed) + throw (status_t)B_ERROR; + + return c; +} + + +/* static */ +int32 +RTFElement::ParseInteger(char first, BDataIO &stream, char &_last) throw (status_t) +{ + int32 integer = 0; + int32 count = 0; + + char digit = first; + + if (digit == '\0') + digit = ReadChar(stream); + + while (true) { + if (isdigit(digit)) { + integer = integer * 10 + digit - '0'; + count++; + } else { + _last = digit; + goto out; + } + + digit = ReadChar(stream); + } + +out: + if (count == 0) + throw (status_t)B_BAD_TYPE; + + return integer; +} + + +// #pragma mark - + + +RTFHeader::RTFHeader() + : + fVersion(0) +{ +} + + +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(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 +RTFHeader::Identify(BDataIO &stream) +{ + char header[5]; + + if (stream.Read(header, sizeof(header)) < (ssize_t)sizeof(header)) + return B_IO_ERROR; + + return strncmp(header, "{\\rtf", 5) ? B_BAD_TYPE : B_OK; +} + + +status_t +RTFHeader::Parse(BDataIO &stream, RTFHeader &header, bool identified) +{ + if (!identified && Identify(stream) != B_OK) + return B_BAD_TYPE; + + try { + char last; + header.Parse('\0', stream, last); + } catch (status_t status) { + return status; + } + + return B_OK; +} + + +// #pragma mark - + + +RTFText::RTFText() +{ +} + + +RTFText::~RTFText() +{ + SetText(NULL); +} + + +void +RTFText::Parse(char first, BDataIO &stream, char &last) throw (status_t) +{ + char c = first; + if (c == '\0') + c = ReadChar(stream); + + fText = ""; + + while (true) { + if (c == '\\' || c == '}') + break; + + // ToDo: this is horribly inefficient with BStrings + fText.Append(c, 1); + c = ReadChar(stream); + } + + // ToDo: add support for different charsets - right now, only ASCII is supported! + // To achieve this, we should just translate everything into UTF-8 here + + last = c; +} + + +status_t +RTFText::SetText(const char *text) +{ + return fText.SetTo(text) != NULL ? B_OK : B_NO_MEMORY; +} + + +const char * +RTFText::Text() const +{ + return fText.String(); +} + + +uint32 +RTFText::TextLength() const +{ + return fText.Length(); +} + + +// #pragma mark - + + +RTFCommand::RTFCommand() + : + fName(NULL), + fHasOption(false), + fOption(-1) +{ +} + + +RTFCommand::~RTFCommand() +{ +} + + +void +RTFCommand::Parse(char first, BDataIO &stream, char &last) throw (status_t) +{ + if (first == '\0') + first = ReadChar(stream); + + if (first != '\\') + throw B_BAD_TYPE; + + // get name + char name[kRTFCommandLength]; + size_t length = 0; + char c; + while (isalpha(c = ReadChar(stream))) { + name[length++] = c; + if (length >= kRTFCommandLength - 1) + throw B_BAD_TYPE; + } + + if (length == 0) { + if (c == '*') { + // we're a comment! + name[0] = 'c'; + length++; + } else if (c == '\n') { + // we're a hard return + name[0] = '\n'; + length++; + } + } + + fName.SetTo(name, length); + + // parse numeric option + + if (c == '-') + c = ReadChar(stream); + + last = c; + + if (isdigit(c)) + SetOption(ParseInteger(c, stream, last)); + + // a space delimiter is eaten up by the command + if (isspace(last)) + last = ReadChar(stream); +} + + +status_t +RTFCommand::SetName(const char *name) +{ + return fName.SetTo(name) != NULL ? B_OK : B_NO_MEMORY; +} + + +const char * +RTFCommand::Name() +{ + return fName.String(); +} + + +void +RTFCommand::UnsetOption() +{ + fHasOption = false; + fOption = -1; +} + + +void +RTFCommand::SetOption(int32 option) +{ + fOption = option; + fHasOption = true; +} + + +bool +RTFCommand::HasOption() const +{ + return fHasOption; +} + + +int32 +RTFCommand::Option() const +{ + return fOption; +} + + +// #pragma mark - + + +RTFIterator::RTFIterator(RTFElement &start, rtf_destination destination) +{ + SetTo(start, destination); +} + + +void +RTFIterator::SetTo(RTFElement &start, rtf_destination destination) +{ + fStart = &start; + fDestination = destination; + + Rewind(); +} + + +void +RTFIterator::Rewind() +{ + fStack.MakeEmpty(); + fStack.Push(fStart); +} + + +bool +RTFIterator::HasNext() const +{ + return !fStack.IsEmpty(); +} + + +RTFElement * +RTFIterator::Next() +{ + RTFElement *element; + + if (!fStack.Pop(&element)) + return NULL; + + // put this element'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;) { + RTFElement *child = element->ElementAt(i); + + if (fDestination == RTF_ALL_DESTINATIONS + || child->CountElements() == 0 + || fDestination == element->Destination()) + fStack.Push(child); + } + + return element; +} + diff --git a/src/add-ons/translators/rtftranslator/RTF.h b/src/add-ons/translators/rtftranslator/RTF.h new file mode 100644 index 0000000000..2a66bc1980 --- /dev/null +++ b/src/add-ons/translators/rtftranslator/RTF.h @@ -0,0 +1,136 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef RTF_H +#define RTF_H + + +#include "Stack.h" + +#include +#include +#include + +class BDataIO; +class RTFCommand; + + +static const size_t kRTFCommandLength = 32; + + +enum rtf_destination { + RTF_TEXT, + RTF_COMMENT, + RTF_OTHER, + + RTF_ALL_DESTINATIONS = 255 +}; + +class RTFElement { + public: + RTFElement(); + virtual ~RTFElement(); + + status_t AddElement(RTFElement *element); + uint32 CountElements() const; + 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: + RTFElement *fParent; + BList fElements; + rtf_destination fDestination; +}; + +class RTFHeader : public RTFElement { + public: + RTFHeader(); + virtual ~RTFHeader(); + + int32 Version() const; + const char *Charset() const; + + 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); + + private: + int32 fVersion; + +}; + +class RTFText : public RTFElement { + public: + RTFText(); + virtual ~RTFText(); + + status_t SetText(const char *text); + const char *Text() const; + uint32 TextLength() const; + + protected: + virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t); + + private: + BString fText; +}; + +class RTFCommand : public RTFElement { + public: + RTFCommand(); + virtual ~RTFCommand(); + + status_t SetName(const char *name); + const char *Name(); + + void UnsetOption(); + void SetOption(int32 option); + bool HasOption() const; + int32 Option() const; + + protected: + virtual void Parse(char first, BDataIO &stream, char &last) throw (status_t); + + private: + BString fName; + bool fHasOption; + int32 fOption; +}; + +//--------------------------------- + +class RTFIterator { + public: + RTFIterator(RTFElement &start, rtf_destination destination = RTF_ALL_DESTINATIONS); + + void SetTo(RTFElement &start, rtf_destination destination = RTF_ALL_DESTINATIONS); + void Rewind(); + + bool HasNext() const; + RTFElement *Next(); + + private: + RTFElement *fStart; + Stack fStack; + rtf_destination fDestination; +}; + +#endif /* RTF_H */ diff --git a/src/add-ons/translators/rtftranslator/RTFTranslator.cpp b/src/add-ons/translators/rtftranslator/RTFTranslator.cpp new file mode 100644 index 0000000000..2b6098bc4b --- /dev/null +++ b/src/add-ons/translators/rtftranslator/RTFTranslator.cpp @@ -0,0 +1,193 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "RTFTranslator.h" +#include "ConfigView.h" +#include "RTF.h" +#include "convert.h" + +#include +#include +#include + + +#define READ_BUFFER_SIZE 2048 +#define DATA_BUFFER_SIZE 64 + + +// The input formats that this translator supports. +translation_format sInputFormats[] = { + { + RTF_TEXT_FORMAT, + B_TRANSLATOR_TEXT, + RTF_IN_QUALITY, + RTF_IN_CAPABILITY, + "text/rtf", + "RichTextFormat file" + }, +}; + +// The output formats that this translator supports. +translation_format sOutputFormats[] = { + { + B_TRANSLATOR_TEXT, + B_TRANSLATOR_TEXT, + TEXT_OUT_QUALITY, + TEXT_OUT_CAPABILITY, + "text/plain", + "Plain text file" + }, + { + B_STYLED_TEXT_FORMAT, + B_TRANSLATOR_TEXT, + STXT_OUT_QUALITY, + STXT_OUT_CAPABILITY, + "text/x-vnd.Be-stxt", + "Be styled text file" + } +}; + + +RTFTranslator::RTFTranslator() +{ + char info[256]; + sprintf(info, "Rich Text Format Translator v%d.%d.%d %s", + int(B_TRANSLATION_MAJOR_VER(RTF_TRANSLATOR_VERSION)), + int(B_TRANSLATION_MINOR_VER(RTF_TRANSLATOR_VERSION)), + int(B_TRANSLATION_REVSN_VER(RTF_TRANSLATOR_VERSION)), + __DATE__); + + fInfo = strdup(info); +} + + +RTFTranslator::~RTFTranslator() +{ + free(fInfo); +} + + +const char * +RTFTranslator::TranslatorName() const +{ + return "RTF Text Files"; +} + + +const char * +RTFTranslator::TranslatorInfo() const +{ + return "Rich Text Format Translator"; +} + + +int32 +RTFTranslator::TranslatorVersion() const +{ + return RTF_TRANSLATOR_VERSION; +} + + +const translation_format * +RTFTranslator::InputFormats(int32 *_outCount) const +{ + if (_outCount == NULL) + return NULL; + + *_outCount = sizeof(sInputFormats) / sizeof(translation_format); + return sInputFormats; +} + + +const translation_format * +RTFTranslator::OutputFormats(int32 *_outCount) const +{ + *_outCount = sizeof(sOutputFormats) / sizeof(translation_format); + return sOutputFormats; +} + + +status_t +RTFTranslator::Identify(BPositionIO *stream, + const translation_format *format, BMessage *ioExtension, + translator_info *info, uint32 outType) +{ + if (!outType) + outType = B_TRANSLATOR_TEXT; + if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT) + return B_NO_TRANSLATOR; + + status_t status = RTFHeader::Identify(*stream); + if (status != B_OK) + return B_NO_TRANSLATOR; + + // return information about the data in the stream + info->type = B_TRANSLATOR_TEXT; //RTF_TEXT_FORMAT; + info->group = B_TRANSLATOR_TEXT; + info->quality = RTF_IN_QUALITY; + info->capability = RTF_IN_CAPABILITY; + strcpy(info->name, "RichTextFormat file"); + strcpy(info->MIME, "text/rtf"); + + return B_OK; +} + + +status_t +RTFTranslator::Translate(BPositionIO *source, + const translator_info *inInfo, BMessage *ioExtension, + uint32 outType, BPositionIO *target) +{ + if (target == NULL || source == NULL) + return B_BAD_VALUE; + + if (!outType) + outType = B_TRANSLATOR_TEXT; + if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT) + return B_NO_TRANSLATOR; + + RTFHeader header; + status_t status = RTFHeader::Parse(*source, header); + if (status != B_OK) + return status; + + // we support two different output formats + + if (outType == B_TRANSLATOR_TEXT) + return convert_to_plain_text(header, *target); + + return convert_to_stxt(header, *target); +} + + +status_t +RTFTranslator::MakeConfigurationView(BMessage *ioExtension, BView **_view, BRect *_extent) +{ + if (_view == NULL || _extent == NULL) + return B_BAD_VALUE; + + BView *view = new ConfigView(BRect(0, 0, 225, 175)); + if (view == NULL) + return BTranslator::MakeConfigurationView(ioExtension, _view, _extent); + + *_view = view; + *_extent = view->Bounds(); + return B_OK; +} + + +// #pragma mark - + + +BTranslator * +make_nth_translator(int32 n, image_id you, uint32 flags, ...) +{ + if (n != 0) + return NULL; + + return new RTFTranslator(); +} + diff --git a/src/add-ons/translators/rtftranslator/RTFTranslator.h b/src/add-ons/translators/rtftranslator/RTFTranslator.h new file mode 100644 index 0000000000..10ba1c63cc --- /dev/null +++ b/src/add-ons/translators/rtftranslator/RTFTranslator.h @@ -0,0 +1,64 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef RTF_TRANSLATOR_H +#define RTF_TRANSLATOR_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BaseTranslator.h" + + +#define RTF_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VER(1, 0, 0) +#define RTF_TEXT_FORMAT 'RTF ' +#define RTF_IN_QUALITY 0.7 +#define RTF_IN_CAPABILITY 0.9 + +#define TEXT_OUT_QUALITY 0.3 +#define TEXT_OUT_CAPABILITY 0.6 +#define STXT_OUT_QUALITY 0.5 +#define STXT_OUT_CAPABILITY 0.5 + + +class RTFTranslator : public BTranslator { + public: + RTFTranslator(); + + virtual const char *TranslatorName() const; + virtual const char *TranslatorInfo() const; + virtual int32 TranslatorVersion() const; + + virtual const translation_format *InputFormats(int32 *_outCount) const; + virtual const translation_format *OutputFormats(int32 *_outCount) const; + + virtual status_t Identify(BPositionIO *inSource, + const translation_format *inFormat, BMessage *ioExtension, + translator_info *outInfo, uint32 outType); + + virtual status_t Translate(BPositionIO *inSource, + const translator_info *inInfo, BMessage *ioExtension, + uint32 outType, BPositionIO *outDestination); + + virtual status_t MakeConfigurationView(BMessage *ioExtension, + BView **outView, BRect *outExtent); + + protected: + virtual ~RTFTranslator(); + // this is protected because the object is deleted by the + // Release() function instead of being deleted directly by + // the user + + private: + char *fInfo; +}; + +#endif /* RTF_TRANSLATOR_H */ diff --git a/src/add-ons/translators/rtftranslator/Stack.h b/src/add-ons/translators/rtftranslator/Stack.h new file mode 100644 index 0000000000..5fa6ed6b8a --- /dev/null +++ b/src/add-ons/translators/rtftranslator/Stack.h @@ -0,0 +1,68 @@ +/* Stack - a template stack class + * + * Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ +#ifndef STACK_H +#define STACK_H + + +#include + + +template class Stack { + public: + Stack() + : + fArray(NULL), + fUsed(0), + fMax(0) + { + } + + ~Stack() + { + free(fArray); + } + + bool IsEmpty() const + { + return fUsed == 0; + } + + void MakeEmpty() + { + // could also free the memory + fUsed = 0; + } + + status_t Push(T value) + { + if (fUsed >= fMax) { + fMax += 16; + T *newArray = (T *)realloc(fArray, fMax * sizeof(T)); + if (newArray == NULL) + return B_NO_MEMORY; + + fArray = newArray; + } + fArray[fUsed++] = value; + return B_OK; + } + + bool Pop(T *value) + { + if (fUsed == 0) + return false; + + *value = fArray[--fUsed]; + return true; + } + + private: + T *fArray; + int32 fUsed; + int32 fMax; +}; + +#endif /* STACK_H */ diff --git a/src/add-ons/translators/rtftranslator/convert.cpp b/src/add-ons/translators/rtftranslator/convert.cpp new file mode 100644 index 0000000000..17a4f49941 --- /dev/null +++ b/src/add-ons/translators/rtftranslator/convert.cpp @@ -0,0 +1,341 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "convert.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +class AppServerConnection { + public: + AppServerConnection(); + ~AppServerConnection(); + + private: + BApplication *fApplication; +}; + + +AppServerConnection::AppServerConnection() + : + fApplication(NULL) +{ + // This is not nice, but it's the only we can provide all features on command + // line tools that don't create a BApplication - without a BApplicatio, we + // could not support any text styles (colors and fonts) + + if (be_app == NULL) + fApplication = new BApplication("application/x-vnd.Haiku-RTF-Translator"); +} + + +AppServerConnection::~AppServerConnection() +{ + delete fApplication; +} + + +// #pragma mark - + + +static size_t +get_text_for_command(RTFCommand *command, char *text, size_t size) +{ + const char *name = command->Name(); + + if (!strcmp(name, "\n") + || !strcmp(name, "par") + || !strcmp(name, "sect")) { + if (text != NULL) + strlcpy(text, "\n", size); + return 1; + } + + return 0; +} + + +static text_run * +get_style(BList &runs, text_run *run, int32 offset) +{ + if (run != NULL && offset == run->offset) + return run; + + text_run *newRun = new text_run(); + if (newRun == NULL) + throw (status_t)B_NO_MEMORY; + + // take over previous styles + if (run != NULL) + *newRun = *run; + + newRun->offset = offset; + runs.AddItem(newRun); + return newRun; +} + + +static text_run_array * +get_text_run_array(RTFHeader &header) +{ + // collect styles + + RTFIterator iterator(header, RTF_TEXT); + text_run *current = NULL; + int32 offset = 0; + BList runs; + + while (iterator.HasNext()) { + RTFElement *element = iterator.Next(); + if (RTFText *text = dynamic_cast(element)) { + offset += text->TextLength(); + continue; + } + + RTFCommand *command = dynamic_cast(element); + if (command == NULL) + continue; + + if (!strcmp(command->Name(), "cf")) { + // foreground color + current = get_style(runs, current, offset); + current->color = header.Color(command->Option()); + } else if (!strcmp(command->Name(), "b")) { + // bold style + current = get_style(runs, current, offset); + + if (command->Option() != 0) + current->font.SetFace(B_BOLD_FACE); + else + current->font.SetFace(B_REGULAR_FACE); + } else + offset += get_text_for_command(command, NULL, 0); + } + + // are there any styles? + if (runs.CountItems() == 0) + return NULL; + + // create array + + text_run_array *array = (text_run_array *)malloc(sizeof(text_run_array) + + sizeof(text_run) * (runs.CountItems() - 1)); + if (array == NULL) + throw (status_t)B_NO_MEMORY; + + array->count = runs.CountItems(); + + for (int32 i = 0; i < array->count; i++) { + text_run *run = (text_run *)runs.RemoveItem(0L); + array->runs[i] = *run; + delete run; + } + + return array; +} + + +status_t +write_plain_text(RTFHeader &header, BDataIO &target) +{ + RTFIterator iterator(header, RTF_TEXT); + + while (iterator.HasNext()) { + RTFElement *element = iterator.Next(); + char buffer[1024]; + const char *string = NULL; + size_t size = 0; + + if (RTFText *text = dynamic_cast(element)) { + string = text->Text(); + size = text->TextLength(); + } else if (RTFCommand *command = dynamic_cast(element)) { + size = get_text_for_command(command, buffer, sizeof(buffer)); + if (size != 0) + string = buffer; + } + + if (size == 0) + continue; + + ssize_t written = target.Write(string, size); + if (written < B_OK) + return written; + if ((size_t)written != size) + return B_IO_ERROR; + } + + return B_OK; +} + + +// #pragma mark - + + +status_t +convert_to_stxt(RTFHeader &header, BDataIO &target) +{ + // count text bytes + + size_t textSize = 0; + + RTFIterator iterator(header, RTF_TEXT); + while (iterator.HasNext()) { + RTFElement *element = iterator.Next(); + if (RTFText *text = dynamic_cast(element)) { + textSize += text->TextLength(); + } else if (RTFCommand *command = dynamic_cast(element)) { + textSize += get_text_for_command(command, NULL, 0); + } + } + + // put out header + + TranslatorStyledTextStreamHeader stxtHeader; + stxtHeader.header.magic = 'STXT'; + stxtHeader.header.header_size = sizeof(TranslatorStyledTextStreamHeader); + stxtHeader.header.data_size = 0; + stxtHeader.version = 100; + status_t status = swap_data(B_UINT32_TYPE, &stxtHeader, sizeof(stxtHeader), + B_SWAP_HOST_TO_BENDIAN); + if (status != B_OK) + return status; + + ssize_t written = target.Write(&stxtHeader, sizeof(stxtHeader)); + if (written < B_OK) + return written; + if (written != sizeof(stxtHeader)) + return B_IO_ERROR; + + TranslatorStyledTextTextHeader textHeader; + textHeader.header.magic = 'TEXT'; + textHeader.header.header_size = sizeof(TranslatorStyledTextTextHeader); + textHeader.header.data_size = textSize; + textHeader.charset = B_UNICODE_UTF8; + status = swap_data(B_UINT32_TYPE, &textHeader, sizeof(textHeader), + B_SWAP_HOST_TO_BENDIAN); + if (status != B_OK) + return status; + + written = target.Write(&textHeader, sizeof(textHeader)); + if (written < B_OK) + return written; + if (written != sizeof(textHeader)) + return B_IO_ERROR; + + // put out main text + + status = write_plain_text(header, target); + if (status < B_OK) + return status; + + // prepare styles + + AppServerConnection connection; + // we need that for the the text_run/BFont stuff + + text_run_array *runs = NULL; + try { + runs = get_text_run_array(header); + } catch (status_t status) { + return status; + } + + if (runs == NULL) + return B_OK; + + int32 flattenedSize; + void *flattenedRuns = BTextView::FlattenRunArray(runs, &flattenedSize); + if (flattenedRuns == NULL) + return B_NO_MEMORY; + + // put out styles + + TranslatorStyledTextStyleHeader styleHeader; + styleHeader.header.magic = 'STYL'; + styleHeader.header.header_size = sizeof(TranslatorStyledTextStyleHeader); + styleHeader.header.data_size = flattenedSize; + styleHeader.apply_offset = 0; + styleHeader.apply_length = textSize; + + status = swap_data(B_UINT32_TYPE, &styleHeader, sizeof(styleHeader), + B_SWAP_HOST_TO_BENDIAN); + if (status != B_OK) + return status; + + written = target.Write(&styleHeader, sizeof(styleHeader)); + if (written < B_OK) + return written; + if (written != sizeof(styleHeader)) + return B_IO_ERROR; + + // output actual style information + written = target.Write(flattenedRuns, flattenedSize); + if (written < B_OK) + return written; + if (written != flattenedSize) + return B_IO_ERROR; + + return B_OK; +} + + +status_t +convert_to_plain_text(RTFHeader &header, BPositionIO &target) +{ + // put out main text + + status_t status = write_plain_text(header, target); + if (status < B_OK) + return status; + + // ToDo: this is not really nice, we should adopt the BPositionIO class + // from Dano/Zeta which has meta data support + BNode *node = dynamic_cast(&target); + if (node == NULL) { + // we can't write the styles + return B_OK; + } + + // prepare styles + + AppServerConnection connection; + // we need that for the the text_run/BFont stuff + + text_run_array *runs = NULL; + try { + runs = get_text_run_array(header); + } catch (status_t status) { + // doesn't matter too much if we could write the styles or not + return B_OK; + } + + if (runs == NULL) + return B_OK; + + int32 flattenedSize; + void *flattenedRuns = BTextView::FlattenRunArray(runs, &flattenedSize); + if (flattenedRuns == NULL) + return B_OK; + + // put out styles + + ssize_t written = node->WriteAttr("styles", B_RAW_TYPE, 0, flattenedRuns, flattenedSize); + if (written >= B_OK && written != flattenedSize) + node->RemoveAttr("styles"); + + return B_OK; +} diff --git a/src/add-ons/translators/rtftranslator/convert.h b/src/add-ons/translators/rtftranslator/convert.h new file mode 100644 index 0000000000..03e412daa9 --- /dev/null +++ b/src/add-ons/translators/rtftranslator/convert.h @@ -0,0 +1,16 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef CONVERT_H +#define CONVERT_H + + +#include +#include + + +extern status_t convert_to_stxt(RTFHeader &header, BDataIO &target); +extern status_t convert_to_plain_text(RTFHeader &header, BPositionIO &target); + +#endif /* CONVERT_H */ diff --git a/src/add-ons/translators/rtftranslator/main.cpp b/src/add-ons/translators/rtftranslator/main.cpp new file mode 100644 index 0000000000..cb02e984af --- /dev/null +++ b/src/add-ons/translators/rtftranslator/main.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "RTFTranslator.h" +#include "RTF.h" + +#include +#include +#include + +#include +#include + + +int +main(int /*argc*/, char **/*argv*/) +{ + BApplication app("application/x-vnd.haiku-rtf-translator"); + + status_t result; + result = LaunchTranslatorWindow(new RTFTranslator, "RTF Settings", BRect(0, 0, 225, 175)); + if (result != B_OK) + return 1; + + app.Run(); + return 0; +} +