#include "ET++.ph" #include "Converter.h" #include "Class.h" #include "VObjectText.h" #include "VObjectTView.h" #include "TextReader.h" #include "TextMarks.h" #include "BorderItems.h" #include "String.h" #include "Filler.h" #include "Picture.h" #include "PictureItem.h" #include "Data.h" #include "Stream.h" #include "Alert_e.h" #include "Env.h" #include "HexFilter.h" #include "MemBuf.h" #include "TypeMatcher.h" #include "RunArray.h" #include "TextStyles.h" #include "CType.h" static const Symbol cDocTypeMacPict("MACPICT"), cDocTypeRtf("RTF"); static TypeMatcher matcher(cDocTypeRtf, "rtf", "{\\rtf", TRUE); byte ansimap[]= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xd2, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xa5, 0xb8, 0xb9, 0xd3, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xa9, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; byte macmap[]= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; //---- RTF control Words ------------------------------------------------------- const int cPar = 1, cLine = 2, cTTab = 3, cFont = 4, cColor = 5, cSize = 6, cFace = 7, cCPlain = 8, cMacMap = 9, cNoUnder= 10, cFontTbl= 12, cColorTbl= 13, cPict = 14, cRTF = 15, cIgnore = 16, cRGB = 17, cPicSize= 18, cMacPict= 19, cFontStyle= 20, cNextPict= 21, cHeader = 22, cFooter = 23, cFootNote= 24, cPard = 25, cPCenter= 26, cPJust = 27, cPLeft = 28, cPRight = 29, cPFInd = 30, cPLInd = 31, cPRInd = 32, cPSpAfter= 33, cPSpBefore= 34, cPSpLine = 35, cPage = 36, cTabPosL= 37, cTabPosR= 38, cTabPosC= 39, cTabPosD= 40, cTabDef = 41, cHidden = 42, cNonBrSpace=43, cNonBrHyphen=44, cOptHyphen= 45, cETVobject= 46, cETVobjComment= 47, cVobjStart= 48, cPicGoal = 49, cPKeep = 50, cPKeepN = 51; //---- font table -------------------------------------------------------------- struct FTab { int id; GrFont eid; char *name; }; //---- control Word action table ----------------------------------------------- static struct stab { char *name; int id, attr; } tab[] = { // keep sorted { "NeXTGraphic", cNextPict, 0 }, { "b", cFace, (int) eFaceBold }, { "blue", cRGB, 2 }, { "cell", cTTab, 0 }, { "cf", cColor, 0 }, { "colortbl", cColorTbl, 0 }, { "deftab", cTabDef, 0 }, { "etvobject", cETVobject, 0 }, { "f", cFont, 0 }, { "fi", cPFInd, 0 }, { "fmodern", cFontStyle, 2 }, { "fnil", cFontStyle, 0 }, { "fonttbl", cFontTbl, 0 }, { "footer", cFooter, 0 }, { "footerl", cFooter, 1 }, { "footerr", cFooter, 2 }, { "footnote", cFootNote, 0 }, { "froman", cFontStyle, 0 }, { "fs", cSize, 0 }, { "fswiss", cFontStyle, 1 }, { "ftech", cFontStyle, 3 }, { "green", cRGB, 1 }, { "header", cHeader, 0 }, { "headerl", cHeader, 1 }, { "headerr", cHeader, 2 }, { "height", cPicSize, 1 }, { "i", cFace, (int) eFaceItalic }, { "info", cIgnore, 0 }, { "keep", cPKeep, 0 }, { "keepn", cPKeepN, 0 }, { "li", cPLInd, 0 }, { "line", cLine, 0 }, { "mac", cMacMap, 0 }, { "macpict", cMacPict, 0 }, { "outl", cFace, (int) eFaceOutline }, { "page", cPage, 0 }, { "par", cPar, 0 }, { "pard", cPard, 0 }, { "pich", cPicSize, 1 }, { "pichGoal", cPicGoal, 1 }, { "pict", cPict, 0 }, { "picw", cPicSize, 0 }, { "picwGoal", cPicGoal, 0 }, { "plain", cCPlain, 0 }, { "qc", cPCenter, 0 }, { "qj", cPJust, 0 }, { "ql", cPLeft, 0 }, { "qr", cPRight, 0 }, { "red", cRGB, 0 }, { "ri", cPRInd, 0 }, { "row", cPar, 0 }, { "rtf", cRTF, 0 }, { "sa", cPSpAfter, 0 }, { "sb", cPSpBefore, 0 }, { "sect", cPar, 0 }, { "shad", cFace, (int) eFaceShadow }, { "sl", cPSpLine, 0 }, { "stylesheet", cIgnore, 0 }, { "tab", cTTab, 0 }, { "tqc", cTabPosC, 0 }, { "tqdec", cTabPosD, 0 }, { "tqr", cTabPosR, 0 }, { "tx", cTabPosL, 0 }, { "ul", cFace, (int) eFaceUnderline }, { "uld", cFace, (int) eFaceUnderline }, { "uldb", cFace, (int) eFaceUnderline }, { "ulnone", cNoUnder, 0 }, { "ulw", cFace, (int) eFaceUnderline }, { "v", cIgnore, 0 }, { "vobjfollows", cETVobjComment, 0 }, { "vobjstart", cVobjStart, 0 }, { "width", cPicSize, 0 } }; static stab *find(const char *name) { register int position; int last= sizeof(tab)/sizeof(stab)-1, base= 0, result; if (tab && name) { while (last >= base) { position= (base+last) >> 1; if ((result= strcmp(tab[position].name, name)) == 0) return &tab[position]; if (result > 0) last= position-1; else base= position+1; } } return 0; } inline int TwipsToPix(int t) { return t/20; } //---- RTF_Dest ---------------------------------------------------------------- class RTF_Global; class RTF_State; class RTF_Dest { public: RTF_Dest(RTF_Dest *b); virtual ~RTF_Dest() { } virtual void Word(RTF_State*, int, int, int) { } virtual RTF_Dest *MakeDestination(RTF_State*, int); virtual void Out(RTF_State*, byte) { } virtual void InsertPicture(Picture*, Point) { } virtual void InsertText(Text*) { } virtual void InsertVObject(VObject*) { } virtual void InsertVisualMark(VisualMark*) { } RTF_Global *GetGlobal() { return global; } protected: RTF_Dest *base; RTF_Global *global; }; //---- RTF_Global -------------------------------------------------------------- class RTF_Global : public RTF_Dest { public: Class *textClass; Data *src; Text *body; byte *charmap; int cx; Ink *ctab[256]; struct FTab ftab[50]; bool ignoreVObjects; bool askForVObjects; public: RTF_Global(Class *want, Data *d); ~RTF_Global() { } void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State*, byte) { } void InsertText(Text *t) { body= t; } bool IgnoreVObjects(); Class *TextClass() { return textClass; } Data *SourceData() { return src; } GrFont FindFont(int id); Ink *FindInk(int colorid); }; RTF_Global::RTF_Global(Class *want, Data *dp) : RTF_Dest(0) { global= this; charmap= ansimap; cx= 0; askForVObjects= TRUE; textClass= want; src= dp; ctab[cx++]= gInkBlack; } void RTF_Global::Word(RTF_State*, int, int, int) { } GrFont RTF_Global::FindFont(int id) { for (int i= 0; ftab[i].name; i++) if (ftab[i].id == id) return ftab[i].eid; return eFontTimes; } Ink *RTF_Global::FindInk(int colorid) { return ctab[colorid]; } bool RTF_Global::IgnoreVObjects() { if (askForVObjects) { switch (ShowAlert(eAlertCaution, "Read VObjects in RTF ?")) { case cIdYes: case cIdCancel: ignoreVObjects= FALSE; break; case cIdNo: ignoreVObjects= TRUE; break; } } askForVObjects= FALSE; return ignoreVObjects; } //---- RTF_Global -------------------------------------------------------------- class RTF_GlobalPureText : public RTF_Global { public: RTF_GlobalPureText(Class *want, Data *dp); ~RTF_GlobalPureText() { } RTF_Dest *MakeDestination(RTF_State *d, int code); }; //---- RTF_DocDest ------------------------------------------------------------- class RTF_DocDest : public RTF_Dest { public: RTF_DocDest(RTF_Dest *b); ~RTF_DocDest(); void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State*, byte c); void InsertPicture(Picture*, Point e); void InsertText(Text *op); void InsertVObject(VObject *vop); void InsertVisualMark(VisualMark *gip); private: StyledText *vt; TextReader *reader; GrFont font; Ink *ink; void FlushParagraph(RTF_State *d); void OutVObjectPlaceHolder(RTF_State *d); }; //---- RTF_AsciiDest ----------------------------------------------------------- class RTF_AsciiDest : public RTF_Dest { public: RTF_AsciiDest(RTF_Dest *b); ~RTF_AsciiDest(); RTF_Dest *MakeDestination(RTF_State *d, int code); void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State*, byte c); private: Text *gt; }; //---- RTF_FontTable ----------------------------------------------------------- class RTF_FontTable : public RTF_Dest { public: RTF_FontTable(RTF_Dest *b) : RTF_Dest(b) { ix= 0; fx= 0; } ~RTF_FontTable(); void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State*, byte c); private: int fx, ix; char name[100]; }; //---- RTF_ColorTable ---------------------------------------------------------- class RTF_ColorTable : public RTF_Dest { public: RTF_ColorTable(RTF_Dest *b); void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State*, byte c); private: int rgb[3]; }; //---- RTF_MacPict ------------------------------------------------------------- class RTF_MacPict : public RTF_Dest { public: RTF_MacPict(RTF_Dest *b); void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State*, byte c); protected: Point e; bool inTwips, readpict; }; //---- RTF_ETVObject ----------------------------------------------------------- class RTF_ETVObject : public RTF_Dest { public: RTF_ETVObject(RTF_Dest *b); void Word(RTF_State *d, int code, int num, int attr); }; //---- RTF_IgnoreETVObject ----------------------------------------------------- class RTF_IgnoreETVObject : public RTF_Dest { public: RTF_IgnoreETVObject(RTF_Dest *b); void Word(RTF_State *d, int code, int num, int attr); }; //---- RTF_NeXTPict ------------------------------------------------------------ class RTF_NeXTPict : public RTF_Dest { public: RTF_NeXTPict(RTF_Dest *b); ~RTF_NeXTPict(); void Word(RTF_State *d, int code, int num, int attr); void Out(RTF_State *s, byte b); protected: Point e; char *fileName; int fi; }; //---- RTF_State --------------------------------------------------------------- class RTF_State { public: GrFace _face; GrFont _font; Ink *_color; int _size; TxtParaProp tabmode; ParaDesc parProp; IStream *readfrom; public: RTF_State(RTF_Dest*); RTF_State(RTF_State *super); ~RTF_State(); void PlainChar(); void SetFont(int f); void SetColor(int c); void SetSize(int s) { _size= s; } void SetTabMode(TxtParaProp tm) { tabmode= tm; } void SetFace(GrFace fc, int b) { _face= (GrFace) (b ? _face | fc : _face & ~fc); } void SetFace(GrFace fc) { _face= fc; } void SetParProp(TxtParaProp what, int value); void PlainPar(); void CopyParProp(ParaDesc &p); void CopyCharProp(GrFont &f, int &s, GrFace &fc, Ink *&i); void Out(byte c) { dest->Out(this, c); } void Word(int code, int num, int attr); void scan(IStream &s); IStream *GetStream(); private: RTF_State *super; RTF_Dest *dest; }; //---- RTF_Dest ---------------------------------------------------------------- RTF_Dest::RTF_Dest(RTF_Dest *b) { base= b; if (base) global= base->global; } RTF_Dest *RTF_Dest::MakeDestination(RTF_State *d, int code) { RTF_Dest *dest; switch (code) { case cIgnore: dest= new RTF_Dest(this); // skip break; case cFontTbl: dest= new RTF_FontTable(this); break; case cColorTbl: dest= new RTF_ColorTable(this); break; case cRTF: dest= new RTF_DocDest(this); break; case cPict: dest= new RTF_MacPict(this); break; case cETVobject: if (!GetGlobal()->IgnoreVObjects()) dest= new RTF_ETVObject(this); else dest= new RTF_IgnoreETVObject(this); break; case cETVobjComment: if (GetGlobal()->IgnoreVObjects()) Word(d, cETVobject, 0, 0); dest= new RTF_Dest(this); // skip break; case cNextPict: dest= new RTF_NeXTPict(this); break; case cHeader: case cFooter: case cFootNote: dest= new RTF_Dest(this); // skip // dest= new RTF_DocDest(this); break; default: dest= new RTF_Dest(this); // skip break; } return dest; } void IgnoreVObjects() { } //---- RTF_State --------------------------------------------------------------- RTF_State::RTF_State(RTF_Dest *d) { super= 0; dest= d; PlainChar(); PlainPar(); } RTF_State::RTF_State(RTF_State *sup) { super= sup; dest= super->dest; super->CopyCharProp(_font, _size, _face, _color); super->CopyParProp(parProp); } RTF_State::~RTF_State() { if (super && dest != super->dest) SafeDelete(dest); } void RTF_State::SetFont(int f) { _font= dest->GetGlobal()->FindFont(f); } void RTF_State::SetColor(int c) { _color= dest->GetGlobal()->FindInk(c); } void RTF_State::CopyParProp(ParaDesc &p) { p= parProp; } void RTF_State::CopyCharProp(GrFont &f, int &s, GrFace &fc, Ink *&i) { f= _font; s= _size; fc= _face; i= _color; } void RTF_State::PlainChar() { SetFont(0); SetSize(12); SetColor(0); SetFace(eFacePlain); } void RTF_State::PlainPar() { SetParProp(eTxtPAlign, (int)eTxtParaLeft); SetParProp(eTxtPLeft, 0); SetParProp(eTxtPRight, 0); SetParProp(eTxtPFirstIndent, 0); SetParProp(eTxtPSpacing, 12); SetParProp(eTxtPSpaceBefore, 0); SetParProp(eTxtPSpaceAfter, 0); SetParProp(eTxtPEmptyTabs, 0); SetParProp(eTxtPNoBreak, 0); SetParProp(eTxtPKeepNext, 0); SetTabMode(eTxtPAddTab); } void RTF_State::SetParProp(TxtParaProp what, int value) { parProp.SetProperty(what, value); } void RTF_State::Word(int code, int num, int attr) { switch (code) { case cIgnore: case cFontTbl: case cColorTbl: case cRTF: case cPict: case cETVobject: case cETVobjComment: case cNextPict: case cHeader: case cFooter: case cFootNote: PlainChar(); dest= dest->MakeDestination(this, code); break; default: if (dest) dest->Word(this, code, num, attr); break; } } void RTF_State::scan(IStream &is) { byte c, buf[100]; bool ignoreDest= FALSE; int i, minus, num; readfrom= &is; while (is.get(c)) { switch (c) { case '\011': // tab Word(cTTab, 0, 0); continue; case '\012': case '\015': continue; case '{': { RTF_State(this).scan(is); } break; case '}': return; case '\\': is.get(c); switch (c) { case '*': ignoreDest= TRUE; continue; case '\n': Word(cPar, 0, 0); continue; case '~': Word(cNonBrSpace, 0, 0); continue; case '_': Word(cNonBrHyphen, 0, 0); continue; case '-': Word(cOptHyphen, 0, 0); continue; case ':': case '{': case '}': case '\\': Out(c); continue; case '\'': Out(is.GetHex()); continue; default: if (Isalpha(c)) { i= num= minus= 0; do { buf[i++]= c; is.get(c); } while (Isalpha(c)); switch (c) { case ' ': num= 1; break; case '-': is.get(c); minus= 1; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': do { num= num*10+c-'0'; is.get(c); } while (Isdigit(c)); if (c != ' ') is.putback(c); break; default: num= 1; is.putback(c); } buf[i]= '\0'; if (minus) num= -num; stab *st= find((char*)buf); if (st) Word(st->id, num, st->attr); else if (ignoreDest) Word(cIgnore, 0, 0); ignoreDest= FALSE; } else Out(c); break; } break; default: Out(c); break; } } } IStream *RTF_State::GetStream() { return readfrom; } //---- RTF_GlobalPureText ------------------------------------------------------ RTF_GlobalPureText::RTF_GlobalPureText(Class *want, Data *dp) : RTF_Global(want, dp) { } RTF_Dest *RTF_GlobalPureText::MakeDestination(RTF_State *d, int code) { switch (code) { case cRTF: return new RTF_AsciiDest(this); default: return RTF_Global::MakeDestination(d, code); } } //---- RTF_DocDest ------------------------------------------------------------- RTF_DocDest::RTF_DocDest(RTF_Dest *b) : RTF_Dest(b) { ink= ePatBlack; font= global->FindFont(0); vt= (StyledText*)global->TextClass()->Proto()->New(); reader= new TextReader( vt, new_CharStyle(font, eFacePlain, 12, ink), new_ParaStyle() ); } RTF_DocDest::~RTF_DocDest() { reader->Flush(); SafeDelete(reader); if (base) base->InsertText(vt); } void RTF_DocDest::Word(RTF_State *d, int code, int num, int attr) { switch (code) { case cETVobject: OutVObjectPlaceHolder(d); break; case cPCenter: d->SetParProp(eTxtPAlign, eTxtParaCenter); break; case cPJust: d->SetParProp(eTxtPAlign, eTxtParaJustified); break; case cPLeft: d->SetParProp(eTxtPAlign, eTxtParaLeft); break; case cPRight: d->SetParProp(eTxtPAlign, eTxtParaRight); break; case cPage: Out(d, '\f'); break; case cPard: d->PlainPar(); break; case cPar: FlushParagraph(d); Out(d, '\n'); break; case cNonBrSpace: Out(d, ' '); break; case cNonBrHyphen: Out(d, '-'); break; case cOptHyphen: break; case cLine: Out(d, '\r'); break; case cTTab: Out(d, '\t'); break; case cPFInd: d->SetParProp(eTxtPFirstIndent, TwipsToPix(num)); break; case cPLInd: d->SetParProp(eTxtPLeft, TwipsToPix(num)); break; case cPRInd: d->SetParProp(eTxtPRight, TwipsToPix(num)); break; case cPSpAfter: d->SetParProp(eTxtPSpaceAfter, TwipsToPix(num)); break; case cPKeep: d->SetParProp(eTxtPNoBreak, 1); break; case cPKeepN: d->SetParProp(eTxtPKeepNext, 1); break; case cTabPosL: d->SetParProp(d->tabmode, TwipsToPix(num)); d->SetTabMode(eTxtPAddTab); break; case cTabPosC: d->SetTabMode(eTxtPAddTabC); break; case cTabPosD: d->SetTabMode(eTxtPAddTabD); break; case cTabPosR: d->SetTabMode(eTxtPAddTabR); break; case cTabDef: vt->SetDefTab(TwipsToPix(num)); break; case cPSpBefore: d->SetParProp(eTxtPSpaceBefore, TwipsToPix(num)); break; case cPSpLine: d->SetParProp(eTxtPSpacing, TwipsToPix(num)); break; case cFont: d->SetFont(num); break; case cColor: d->SetColor(num); break; case cSize: d->SetSize(num/2); break; case cCPlain: d->PlainChar(); break; case cFace: d->SetFace((GrFace)attr, num); break; case cNoUnder: d->SetFace(eFaceUnderline, 0); break; case cMacMap: global->charmap= macmap; break; } } void RTF_DocDest::Out(RTF_State *d, byte c) { if (d->_font != reader->GetFont()) reader->SetFont(d->_font); if (d->_size != reader->GetSize()) reader->SetSize(d->_size); if (d->_face != reader->GetFace()) reader->SetFace(d->_face); if (d->_color != reader->GetInk()) reader->SetInk(d->_color); reader->Append(global->charmap[c]); } void RTF_DocDest::OutVObjectPlaceHolder(RTF_State *d) { byte *msg= (byte*)"[***ET++ Image***]"; for(byte *p= msg; *p != '\0'; p++) Out(d, *p); } void RTF_DocDest::FlushParagraph(RTF_State *d) { reader->SetParaProp(d->parProp); } void RTF_DocDest::InsertPicture(Picture *pic, Point e) { VObject *vop; if (pic) vop= new PictureItem(pic, e); else vop= new Filler(e, ePatGrey50); InsertVObject(vop); } void RTF_DocDest::InsertVObject(VObject *vop) { reader->InsertVObject(vop); } void RTF_DocDest::InsertVisualMark(VisualMark *gip) { reader->InsertVisualMark(gip); } void RTF_DocDest::InsertText(Text *t) { if (vt->IsKindOf(VObjectText)) { VObject *vop= new BorderItem(new VObjectTextView(0, Point(cFit,cFit), (VObjectText*)t)); reader->InsertVObject(vop); } } //---- RTF_AsciiDest ----------------------------------------------------------- RTF_AsciiDest::RTF_AsciiDest(RTF_Dest *b) : RTF_Dest(b) { gt= (Text*)global->TextClass()->Proto()->New(); } RTF_AsciiDest::~RTF_AsciiDest() { if (base) base->InsertText(gt); } RTF_Dest *RTF_AsciiDest::MakeDestination(RTF_State *d, int code) { switch (code) { case cHeader: case cFooter: case cFootNote: return new RTF_Dest(this); default: return RTF_Dest::MakeDestination(d, code); } } void RTF_AsciiDest::Word(RTF_State *d, int code, int, int) { switch (code) { case cPage: Out(d, '\f'); break; case cPar: Out(d, '\n'); Out(d, '\n'); break; case cNonBrSpace: Out(d, ' '); break; case cNonBrHyphen: Out(d, '-'); break; case cOptHyphen: break; case cLine: Out(d, '\n'); break; case cTTab: Out(d, '\t'); break; case cMacMap: global->charmap= macmap; break; } } void RTF_AsciiDest::Out(RTF_State*, byte c) { if (gt->IsKindOf(GapText)) { // hack GapText *t= (GapText*)gt; t->AddChar(gt->End(), global->charmap[c]); } else gt->Append(global->charmap[c]); } //---- RTF_FontTable ----------------------------------------------------------- static struct fontnames { char *name; GrFont font; } FontNames[] = { { "Times", eFontTimes }, { "Helvetica", eFontHelvetica }, { "Courier", eFontCourier }, { "Symbol", eFontSymbol }, { "Chicago", eFontChicago }, { "Avantgarde", eFontAvantgarde }, { "Bookman", eFontBookman }, { "Schoolbook", eFontSchoolbook }, { "NHelvetica", eFontNHelvetica }, { "Palatino", eFontPalatino }, { "Chancery", eFontChancery }, { "Dingbats", eFontDingbats }, { 0, eFontDingbats } }; RTF_FontTable::~RTF_FontTable() { for (int i= 0; i < fx; i++) { for (int j= 0; FontNames[j].name; j++) { if(strcmp(FontNames[j].name, global->ftab[i].name) == 0) { global->ftab[i].eid= FontNames[j].font; break; } } } } void RTF_FontTable::Out(RTF_State*, byte c) { if (c == ';') { name[ix]= 0; ix= 0; global->ftab[fx++].name= strsave(name); } else name[ix++]= c; } static GrFont ft[] = { eFontTimes, eFontHelvetica, eFontCourier, eFontSymbol }; void RTF_FontTable::Word(RTF_State*, int code, int num, int attr) { switch (code) { case cFont: global->ftab[fx].id= num; break; case cFontStyle: global->ftab[fx].eid= ft[attr]; break; } } //---- RTF_ColorTable ---------------------------------------------------------- RTF_ColorTable::RTF_ColorTable(RTF_Dest *b) : RTF_Dest(b) { rgb[0]= rgb[1]= rgb[2]= global->cx= 0; } void RTF_ColorTable::Out(RTF_State*, byte c) { if (c == ';') { if (rgb[0] == 0 && rgb[1] == 0 && rgb[2] == 0) global->ctab[global->cx]= gInkBlack; else if (rgb[0] == 0 && rgb[1] == 0 && rgb[2] == 0) global->ctab[global->cx]= gInkWhite; else global->ctab[global->cx]= new_RGBColor(rgb[0], rgb[1], rgb[2]); global->cx++; rgb[0]= rgb[1]= rgb[2]= 0; } } void RTF_ColorTable::Word(RTF_State*, int code, int num, int attr) { if (code == cRGB) rgb[attr]= num; } //---- RTF_MacPict ------------------------------------------------------------- RTF_MacPict::RTF_MacPict(RTF_Dest *b) : RTF_Dest(b) { readpict= FALSE; inTwips= TRUE; } void RTF_MacPict::Word(RTF_State*, int code, int num, int attr) { switch (code) { case cMacPict: readpict= TRUE; inTwips= FALSE; break; case cPicSize: case cPicGoal: if (inTwips) num= TwipsToPix(num); if (attr == 0) e.x= num; else e.y= num; // e[attr]= inTwips ? TwipsToPix(num) : num; } } void RTF_MacPict::Out(RTF_State *s, byte b) { if (readpict) { MemBuf mb(BUFSIZE*4, new char[BUFSIZE*4]); OStream os(&mb); IStream *isp= s->GetStream(); isp->putback(b); isp->Push(new HexDecoder); os << (*isp); isp->Pop(); base->InsertVisualMark(new ReferenceMark( new MemoryData((byte*)mb.Base(), (int)mb.tell(), cDocTypeMacPict, FALSE, TRUE), e)); } } //---- RTF_ETVObject ----------------------------------------------------------- RTF_ETVObject::RTF_ETVObject(RTF_Dest *b) : RTF_Dest(b) { } void RTF_ETVObject::Word(RTF_State *s, int code, int, int) { if (code == cVobjStart) { IStream oios(s->GetStream()->getsbuf(), 0, FALSE); VisualMark *vmp= 0; oios >> vmp; if (vmp) base->InsertVisualMark(vmp); } } //---- RTF_NeXTPict ------------------------------------------------------------ RTF_NeXTPict::RTF_NeXTPict(RTF_Dest *b) : RTF_Dest(b) { fileName= 0; } RTF_NeXTPict::~RTF_NeXTPict() { if (fileName) { fileName[fi-1]= '\0'; // strip trailing blank static char buf[1000]; strcpy(buf, GetGlobal()->SourceData()->FullName()); char *dirName= strrchr(buf, '/'); if (dirName) *dirName= '\0'; if (strcmp(&fileName[strlen(fileName)-4], ".eps") == 0) base->InsertVObject(new Filler(e, ePatGrey50)); else base->InsertVisualMark(new ReferenceMark(form("%s/%s", buf, fileName), e)); SafeDelete(fileName); } } void RTF_NeXTPict::Word(RTF_State*, int code, int num, int attr) { if (code == cPicSize) { num= TwipsToPix(num); if (attr == 0) e.x= num; else e.y= num; } } void RTF_NeXTPict::Out(RTF_State*, byte b) { if (fileName == 0) { fileName= new char[1000]; fi= 0; } fileName[fi++]= b; } //---- RTF_IgnoreETVObject ----------------------------------------------------- RTF_IgnoreETVObject::RTF_IgnoreETVObject(RTF_Dest *b) : RTF_Dest(b) { } void RTF_IgnoreETVObject::Word(RTF_State *s, int code, int, int) { if (code == cVobjStart) { int br= 0; char c; IStream *is= s->GetStream(); while ((*is).get(c)) { if (c == '{') br++; else if (c == '}') { br--; if(br == 0) break; } } } } //---- RTF_FontMap ------------------------------------------------------------- const int cMaxFonts= 100; static struct familytab { GrFont font; char *family; } famtab[] = { { eFontTimes, "froman" }, { eFontHelvetica, "fswiss" }, { eFontCourier, "fmodern" }, { eFontSymbol, "ftech" }, { eFontChicago, "fswiss" }, { eFontAvantgarde, "fswiss" }, { eFontBookman, "froman" }, { eFontSchoolbook, "fnil" }, { eFontNHelvetica, "fswiss" }, { eFontPalatino, "froman" }, { eFontChancery, "fdecor" }, { eFontDingbats, "fdecor" }, { eFontDingbats, 0 } }; class RTF_FontMap { int nfonts; GrFont font[cMaxFonts]; public: RTF_FontMap(GrFont dflt= eFontDefault); int Add(GrFont fnt); int Map(GrFont fnt); char *Family(GrFont fnt); }; RTF_FontMap::RTF_FontMap(GrFont) { nfonts= 0; } int RTF_FontMap::Add(GrFont fnt) { for (int i= 0; i < nfonts; i++) if (font[i] == fnt) return -1; if (i < cMaxFonts) { font[nfonts++]= fnt; return i; } return -1; } int RTF_FontMap::Map(GrFont fnt) { for (int i= 0; i < nfonts; i++) if (font[i] == fnt) return i; return 0; } char *RTF_FontMap::Family(GrFont fnt) { for (int i= 0; famtab[i].family != 0; i++) if (famtab[i].font == fnt) return famtab[i].family; return "fnil"; } //---- RTF_InkMap -------------------------------------------------------------- const int cMaxInks= 100; class RTF_InkMap { int nInks; Ink *inks[cMaxInks]; public: RTF_InkMap(); int Add(Ink *); int Map(Ink *); }; RTF_InkMap::RTF_InkMap() { nInks= 0; } int RTF_InkMap::Add(Ink *ink) { for (int i= 0; i < nInks; i++) if (ink->IsEqual(inks[i])) return -1; if (i < cMaxInks) { inks[nInks++]= ink; return i; } return -1; } int RTF_InkMap::Map(Ink *ink) { for (int i= 0; i < nInks; i++) if (ink->IsEqual(inks[i])) return i; return 0; } //---- RTF_Writer -------------------------------------------------------------- class RTF_Writer { public: RTF_Writer(OStream &os); ~RTF_Writer(); void Doit(StyledText *st); private: OStream &os; StyledText *st; int charpos; // approximate character position on a line RTF_FontMap *fm; RTF_InkMap *im; GrFace face; GrFont font; int size; Ink *color; byte vobjectEscape; bool ignoreVObjects; void Begin(); void End(); void FontTable(); void ColorTable(); void Body(); void Out(char ch); void OutHex(byte b); void OutStr(const char *s); void OutVisualMark(VisualMark *); void Word(char *s); void Word(char *s, int val); void Char(byte ch); void OutPara(ParaStyle *ps); void OutTabs(const ParaTabs &pt); void OutCharStyle(CharStyle *cs); void LineBreak(); int PixToTwips(int t); void PlainChar(); void SetFont(GrFont f) { font= f; } void SetSize(int s) { size= s; } void SetColor(Ink *c) { color= c; } void SetFace(GrFace fc, int b) { face= (GrFace) (b ? face | fc : face & ~fc); } void SetFace(GrFace fc) { face= fc; } }; void RTF_Writer::Out(char ch) { charpos++; if (charpos > 80) LineBreak(); os.put(ch); } void RTF_Writer::OutHex(byte b) { Out('\\'); Out('\''); Out(hexChars[b/16]); Out(hexChars[b%16]); } void RTF_Writer::OutStr(const char *str) { charpos+= strlen(str); if (charpos > 80) LineBreak(); os << str; } inline int RTF_Writer::PixToTwips(int t) { return t*20; } inline void RTF_Writer::Word(char *s) { OutStr(form("\\%s ", s)); } inline void RTF_Writer::Word(char *s, int val) { OutStr(form("\\%s%d ", s, val)); } void RTF_Writer::PlainChar() { SetFont(GrFont(99)); // force output of font SetSize(12); SetColor(gInkBlack); SetFace(eFacePlain); Word("plain"); } void RTF_Writer::LineBreak() { charpos= 0; os.put('\n'); } RTF_Writer::RTF_Writer(OStream &to) : os(to) { } RTF_Writer::~RTF_Writer() { SafeDelete(fm); SafeDelete(im); } void RTF_Writer::Doit(StyledText *t) { st= t; charpos= 0; ignoreVObjects= FALSE; vobjectEscape= st->GetMarkChar(); Begin(); FontTable(); ColorTable(); Body(); End(); } void RTF_Writer::Begin() { OutStr("{"); Word("rtf", 1); Word("mac"); Word("deff", 0); } void RTF_Writer::End() { OutStr("}"); os.flush(); } void RTF_Writer::FontTable() { fm= new RTF_FontMap(); Iter next(gCharStyles->MakeIterator()); CharStyle *cs; Font *fp; GrFont fnt; int i; OutStr("{"); Word("fonttbl"); while (cs= (CharStyle*) next()) { fp= cs->GetFont(); fnt= fp->Fid(); i= fm->Add(fnt); if (i != -1) { Word("f", i); Word(fm->Family(fnt)); OutStr(fp->Name()); Out(';'); } } OutStr("}"); } void RTF_Writer::ColorTable() { im= new RTF_InkMap(); Iter next(gCharStyles->MakeIterator()); CharStyle *cs; Ink *ink; RGBColor *rgb; int i; OutStr("{"); Word("colortbl"); while (cs= (CharStyle*) next()) { ink= cs->GetInk(); if (ink->IsKindOf(RGBColor)) { rgb= (RGBColor*)ink; i= im->Add(rgb); if (i != -1) { Word("red", rgb->GetRed()); Word("green", rgb->GetGreen()); Word("blue", rgb->GetBlue()); Out(';'); } } if (ink == gInkBlack || ink == gInkWhite) { i= im->Add(ink); if (i != -1) { int val= 0; if (ink == gInkWhite) val= 255; Word("red", val); Word("green", val); Word("blue", val); Out(';'); } } } OutStr("}"); } void RTF_Writer::Body() { AutoTextIter next(st); int fChange= 0; int pChange= 0; int ch; Word("deftab", PixToTwips(st->GetDefTab())); while ((ch= next()) != cEOT) { int last= next->GetLastPos(); if (last == pChange) { OutPara(st->GetParaStyle(last)); PlainChar(); OutCharStyle(st->GetCharStyle(last)); pChange+= st->GetParaStyles()->RunSizeAt(pChange); } if (last == fChange) { fChange+= st->GetCharStyles()->RunSizeAt(fChange); OutCharStyle(st->GetCharStyle(last)); } if (ch == vobjectEscape && st->IsVisualMark(last)) { VisualMark *gip= st->GetVisualMarkAt(last); // if (!ConvertTo(cDocTypeRtf, gip, os)) if (gip) OutVisualMark(gip); } else Char((byte)ch); } } static struct parproptab { TxtParaProp prop; int deflt; char *word; } ParPropTab[] = { { eTxtPFirstIndent, 0, "fi" }, { eTxtPLeft, 0, "li" }, { eTxtPRight, 0, "ri" }, { eTxtPSpacing, 0, "sl" }, { eTxtPSpaceBefore, 0, "sb" }, { eTxtPSpaceAfter, 0, "sa" }, { eTxtPAlign, 0, 0 } }; static struct aligntab { TxtParaAlign align; char *word; } AlignTab[] = { { eTxtParaLeft, 0 }, { eTxtParaRight, "qr" }, { eTxtParaCenter, "qc" }, { eTxtParaJustified, "qj" } }; void RTF_Writer::OutPara(ParaStyle *ps) { Word("pard"); TxtParaAlign align= (TxtParaAlign)ps->GetProperty(eTxtPAlign); char *aword= AlignTab[align].word; if (aword) Word(aword); int prop; for (int i= 0; ParPropTab[i].word; i++) { prop= ps->GetProperty(ParPropTab[i].prop); if (prop != ParPropTab[i].deflt) Word(ParPropTab[i].word, PixToTwips(prop)); } if (ps->GetProperty(eTxtPNoBreak)) Word("keep"); if (ps->GetProperty(eTxtPKeepNext)) Word("keepn"); const ParaTabs &tabs= ps->GetTabs(); if (tabs.ntabs != 0) OutTabs(tabs); } static struct facetab { GrFace face; char *word; char *wordoff; } FaceTab[] = { { eFaceBold, "b", "b0" }, { eFaceItalic, "i", "i0" }, { eFaceUnderline, "ul", "ul0" }, { eFaceOutline, "outl", "outl0" }, { eFaceShadow, "shad", "shad0" }, { eFacePlain, "" , "" } }; void RTF_Writer::OutCharStyle(CharStyle *cs) { Font *fp= cs->GetFont(); Ink *ink= cs->GetInk(); if (ink != color) { Word("cf", im->Map(ink)); SetColor(ink); } if (fp->Size() != size) { Word("fs", 2*fp->Size()); SetSize(fp->Size()); } if (fp->Fid() != font) { Word("f", fm->Map(fp->Fid())); SetFont(fp->Fid()); } if (fp->Face() != face) { for (int i= 0; FaceTab[i].face != eFacePlain; i++) { int fbit= (int)FaceTab[i].face; int fold= (int)face&fbit; int fnew= (int)fp->Face()&fbit; if (fold != fnew) { if (fnew) Word(FaceTab[i].word); else Word(FaceTab[i].wordoff); } SetFace((GrFace)fbit, fnew != 0); } } } static struct tabtab { TxtTabStop kind; char *word; } TabTab[] = { { eTxtTLeft, 0 }, { eTxtTRight, "tqr" }, { eTxtTCenter, "tqc" }, { eTxtTDecimal, "tqdec" } }; void RTF_Writer::OutTabs(const ParaTabs &pt) { for (int i= 0; i < pt.ntabs; i++) { TabStop *ts= &pt.stops[i]; char *word= TabTab[(int)ts->kind].word; if (word) Word(word); Word("tx", PixToTwips(ts->x)); } } void RTF_Writer::Char(byte c) { switch(c) { case '\t': Word("tab"); break; case '\r': Word("line"); break; case '\0': case '\n': Word("par"); break; case '\f': Word("page"); break; case ':': case '{': case '}': case '\\': OutStr(form("%c%c",'\\', c)); break; default: if (!Isascii(c)) OutHex(c); else Out(c); } } void RTF_Writer::OutVisualMark(VisualMark *gip) { // VObject comment, ignored by ET++ RTF reader OutStr("{"); Word("vobjfollows"); OutStr("[***ET++ Image***]"); OutStr("}"); // VObject destination if (!ignoreVObjects) { OutStr("\\*"); OutStr("{"); Word("etvobject"); Word("vobjstart"); OStream oios(os.getsbuf(), FALSE); oios << gip NL; oios.flush(); OutStr("}"); } } //---- RtfConverter ------------------------------------------------------------ class RtfConverter : public Converter { public: MetaDef(RtfConverter); RtfConverter() { } const char *AsString() { return "Rich Text Format (RTF)"; } Object *Convert(Data *data, Class *want); bool CanConvert(Data *data, Class *want) { return data->Type() == cDocTypeRtf && want->isKindOf(Meta(Text)); } bool CanConvertTo(Object *op) { return op->IsKindOf(StyledText); } bool ConvertTo(Data *data, Object *op); }; NewMetaImpl0(RtfConverter,Converter); Object *RtfConverter::Convert(Data *data, Class *want) { RTF_Global *d; IStream is(data->GetStreamBufForReading(), 0, TRUE); if (want->isKindOf(Meta(StyledText))) d= new RTF_Global(want, data); else if (want->isKindOf(Meta(Text))) d= new RTF_GlobalPureText(want, data); RTF_State(d).scan(is); return d->body; } bool RtfConverter::ConvertTo(Data *data, Object *op) { if (op->IsKindOf(StyledText)) { OStream to((char*) data->Name()); RTF_Writer rtfw(to); rtfw.Doit((StyledText*)op); return TRUE; } return FALSE; }