/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    Convert RTF and unicode files into LaTeX 		      *
 * modul:       pass1rtf.cc                                                   *
 * description: This module contains parser for RTF and unicode documents.    *
 *              It could be optionally not compiled with WP2LaTeX package.    *
 * licency:     GPL		                                              *
 ******************************************************************************/
#define RTF_Ver "0.15"
//TODO: Dodelat znak ? - maze mezeru pred sebou
//      Autodetekce \section podle xref _TOCxxxxxx

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include <stringa.h>
#include <lists.h>
#if defined(__UNIX__)||defined(__DJGPP__)
 #include <unistd.h>
#endif

#include"wp2latex.h"
#include"images/raster.h"
#include "cp_lib/cptran.h"

extern string latex_filename;
extern CpTranslator Dummy;

typedef struct
{
    char fBold;
    char fUnderline;
    char fItalic;
} CHP;                  // CHaracter Properties

typedef enum {justL, justR, justC, justF } JUST;
typedef struct para_prop
{
    int envir;			// L - left; R - right; C - center
    int xaLeft;                 // left indent in twips
    int xaRight;                // right indent in twips
    int xaFirst;                // first line indent in twips
    JUST just;                  // justification
} PAP;                  // PAragraph Properties

typedef enum {sbkNon, sbkCol, sbkEvn, sbkOdd, sbkPg} SBK;
typedef enum {pgDec, pgURom, pgLRom, pgULtr, pgLLtr} PGN;
typedef struct
{
    int cCols;                  // number of columns
    SBK sbk;                    // section break type
    int xaPgn;                  // x position of page number in twips
    int yaPgn;                  // y position of page number in twips
    PGN pgnFormat;              // how the page number is formatted
} SEP;                  // SEction Properties

typedef struct
{
    int xaPage;                 // page width in twips
    int yaPage;                 // page height in twips
    int xaLeft;                 // left margin in twips
    int yaTop;                  // top margin in twips
    int xaRight;                // right margin in twips
    int yaBottom;               // bottom margin in twips
    int pgnStart;               // starting page number in twips
    char fFacingp;              // facing pages enabled?
    char fLandscape;            // landscape or portrait??
} DOP;                  // DOcument Properties

typedef enum { rdsNorm, rdsSkip, rdsShpInst } RDS;  // Rtf Destination State
typedef enum { risNorm, risBin, risHex } RIS;       // Rtf Internal State

typedef struct save	// property save structure
{
    struct save *pNext;	// next save

    attribute CharA;    // CHaracter Properties
    PAP pap;		// PAragraph Properties
    SEP sep;		// SEction Properties
    DOP dop;		// DOcument Properties
    RDS rds;		// Rtf Destination State
    RIS ris;		// Rtf Internal State
    RGBQuad RGB;	// The color of the current block
    BYTE Cols;		// Number of columns inside current block
    bool fSkipDestIfUnk;

    const CpTranslator *ConvertCpg; //Code page related to a current font selected
} SAVE;

// What types of properties are there?
typedef enum {ipropBold, ipropItalic, ipropUnderline, ipropLeftInd,
	      ipropRightInd, ipropFirstInd, ipropCols, ipropPgnX,
	      ipropPgnY, ipropXaPage, ipropYaPage, ipropXaLeft,
	      ipropXaRight, ipropYaTop, ipropYaBottom, ipropPgnStart,
	      ipropSbk, ipropPgnFormat, ipropFacingp, ipropLandscape,
	      ipropJust, ipropPard, ipropPlain, ipropSectd,
	      ipropMax } IPROP;

typedef enum {actnSpec, actnByte, actnWord, actnNo} ACTN;
typedef enum {propChp, propPap, propSep, propDop} PROPTYPE;

typedef struct
{
    ACTN actn;
    PROPTYPE prop;          // structure containing value
    int  offset;            // offset of value from base of structure
} PROP;

typedef enum {ipfnBin, ipfnHex, ipfnSkipDest } IPFN;
typedef enum {idestPict, idestSkip } IDEST;
typedef enum {kwdChar, kwdDest, kwdProp, kwdSpec} KWD;

typedef struct
   {
   ACTN PropSize:2;        	// size of value
   KWD  Kwd:2;   		// base action to take
   PROPTYPE PropScope:2;        // scope - structure containing value
   unsigned char ForceArg:1; 	// true to use default value from this table
   }TFlags;


typedef struct 		//symbol
{
    const char *szKeyword;	// RTF keyword
    int dflt;		// default value to use
    TFlags F;
    int idx;		// index into property table if kwd == kwdProp
			// index into destination table if kwd == kwdDest
			// character to print if kwd == kwdChar
} SYM;


// RTF parser error codes
#define ecOK		    0	// Everything's fine!
#define ecStackUnderflow    1	// Unmatched '}'
#define ecStackOverflow     2	// Too many '{' -- memory exhausted
#define ecUnmatchedBrace    3	// RTF ended during an open group.
#define ecInvalidHex        4	// invalid hex character found in data
#define ecBadTable          5	// RTF table (sym or prop) invalid
#define ecAssertion         6	// Assertion failure
#define ecEndOfFile         7	// End of file reached while reading RTF


class TconvertedPass1_RTF: public TconvertedPass1
       {
public:CpTranslator *Unicode;
       BYTE ForcedTranslator;
       string Keyword,Parameter;
       char KeywordType;
       bool isParam;
       long lParam;
       float fParam;
       RGBQuad *ColorTBL;
       int nColorTBL;
       RGBQuad RGB,NewRGB;

       RDS rds;
       RIS ris;

       CHP chp;
       PAP pap;
       SEP sep;
       DOP dop;
       int cGroup;
       bool fSkipDestIfUnk;
       long cbBin;

       SAVE *psave;

       list FontTable;
       virtual int Convert_first_pass();
       };
TconvertedPass1 *Factory_RTF(void) {return new TconvertedPass1_RTF;}
FFormatTranslator FormatRTF("RTF",Factory_RTF);


static int ProcessKeyRTF(TconvertedPass1_RTF *cq);

// RTF parser table: Keyword descriptions
SYM rgsymRtf[] = {
//  keyword     dflt      sizearg   kwd fPassDflt                  idx
//{ "b",        1,      {actnByte,kwdProp,propChp,false},  offsetof(CHP, fBold)},      // ipropBold .
//{ "u",        1,      {actnByte,kwdProp,propChp,false},  offsetof(CHP, fUnderline)}, // ipropUnderline .
//{ "i",        1,      {actnByte,kwdProp,propChp,false},  offsetof(CHP, fItalic)},    // ipropItalic .
{   "li",       0,      {actnWord,kwdProp,propPap,false},  offsetof(PAP, xaLeft)},     // ipropLeftInd
{   "ri",       0,      {actnWord,kwdProp,propPap,false},  offsetof(PAP, xaRight)},     // ipropRightInd
{   "fi",       0,      {actnWord,kwdProp,propPap,false},  offsetof(PAP, xaFirst)},     // ipropFirstInd
{   "cols",     1,      {actnWord,kwdProp,propSep,false},  offsetof(SEP, cCols)},       // ipropCols
{   "sbknone",  sbkNon, {actnByte,kwdProp,propSep,true},   offsetof(SEP, sbk)},         // ipropSbk
{   "sbkcol",   sbkCol, {actnByte,kwdProp,propSep,true},   offsetof(SEP, sbk)},         // ipropSbk
{   "sbkeven",  sbkEvn, {actnByte,kwdProp,propSep,true},   offsetof(SEP, sbk)},         // ipropSbk
{   "sbkodd",   sbkOdd, {actnByte,kwdProp,propSep,true},   offsetof(SEP, sbk)},         // ipropSbk
{   "sbkpage",  sbkPg,  {actnByte,kwdProp,propSep,true},   offsetof(SEP, sbk)},         // ipropSbk
{   "pgnx",     0,      {actnWord,kwdProp,propSep,false},  offsetof(SEP, xaPgn)},       // ipropPgnX
{   "pgny",     0,      {actnWord,kwdProp,propSep,false},  offsetof(SEP, yaPgn)},       // ipropPgnY
{   "pgndec",   pgDec,  {actnByte,kwdProp,propSep,true},   offsetof(SEP, pgnFormat)},   // ipropPgnFormat
{   "pgnucrm",  pgURom, {actnByte,kwdProp,propSep,true},   offsetof(SEP, pgnFormat)},   // ipropPgnFormat
{   "pgnlcrm",  pgLRom, {actnByte,kwdProp,propSep,true},   offsetof(SEP, pgnFormat)},   // ipropPgnFormat
{   "pgnucltr", pgULtr, {actnByte,kwdProp,propSep,true},   offsetof(SEP, pgnFormat)},   // ipropPgnFormat
{   "pgnlcltr", pgLLtr, {actnByte,kwdProp,propSep,true},   offsetof(SEP, pgnFormat)},   // ipropPgnFormat
//{  "qc",       justC,  {actnWord,kwdProp,propPap,true},   offsetof(PAP, just)},        // ipropJust
//{  "ql",       justL,  {actnWord,kwdProp,propPap,true},   offsetof(PAP, just)},        // ipropJust
//{  "qr",       justR,  {actnWord,kwdProp,propPap,true},   offsetof(PAP, just)},        // ipropJust
//{  "qj",       justF,  {actnWord,kwdProp,propPap,true},   offsetof(PAP, just)},        // ipropJust
{   "paperw",   12240,  {actnWord,kwdProp,propDop,false},  offsetof(DOP, xaPage)},      // ipropXaPage
{   "paperh",   15480,  {actnWord,kwdProp,propDop,false},  offsetof(DOP, yaPage)},      // ipropYaPage
{   "margl",    1800,   {actnWord,kwdProp,propDop,false},  offsetof(DOP, xaLeft)},      // ipropXaLeft
{   "margr",    1800,   {actnWord,kwdProp,propDop,false},  offsetof(DOP, xaRight)},     // ipropXaRight
{   "margt",    1440,   {actnWord,kwdProp,propDop,false},  offsetof(DOP, yaTop)},       // ipropYaTop
{   "margb",    1440,   {actnWord,kwdProp,propDop,false},  offsetof(DOP, yaBottom)},    // ipropYaBottom
{   "pgnstart", 1,      {actnWord,kwdProp,propDop,true},   offsetof(DOP, pgnStart)},    // ipropPgnStart
{   "facingp",  1,      {actnByte,kwdProp,propDop,true},   offsetof(DOP, fFacingp)},    // ipropFacingp
{   "landscape",1,      {actnByte,kwdProp,propDop,true},   offsetof(DOP, fLandscape)},  // ipropLandscape

{   "bin",      0,      {actnWord,kwdSpec,propChp,false},  ipfnBin},
{   "*",        0,      {actnWord,kwdSpec,propChp,false},  ipfnSkipDest},
{   "'",        0,      {actnWord,kwdSpec,propChp,false},  ipfnHex},

{   "author",   0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "buptim",   0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "colortbl", 0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "comment",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "creatim",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "doccomm",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
//{   "fonttbl",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "footer",   0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "footerf",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "footerl",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "footerr",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "footnote", 0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "ftncn",    0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "ftnsep",   0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "ftnsepc",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "header",   0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "headerf",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "headerl",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "headerr",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "info",     0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "keywords", 0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "operator", 0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "pict",     0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "printim",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "private1", 0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "revtim",   0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "rxe",      0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "stylesheet",   0,  {actnNo,kwdDest,propChp,false},    idestSkip},
{   "subject",  0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "tc",       0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "title",    0,      {actnNo,kwdDest,propChp,false},    idestSkip},
{   "txe",      0,      {actnNo,kwdDest,propChp,false},    idestSkip},
//{ "xe",       0,      {actnNo,kwdDest,propChp,false},    idestSkip},

//{ "par",      0,      {actnNo,kwdChar,propChp,false},    0x0a},
//{ "\0x0a",    0,      {actnNo,kwdChar,propChp,false},    0x0a},
//{ "\0x0d",    0,      {actnNo,kwdChar,propChp,false},    0x0a},
//{ "tab",      0,      {actnNo,kwdChar,propChp,false},    0x09},
//{ "ldblquote",0,      {actnNo,kwdChar,propChp,false},    '"'},
//{ "rdblquote",0,      {actnNo,kwdChar,propChp,false},    '"'},
//{ "{",        0,      {actnNo,kwdChar,propChp,false},    '{'},
//{ "}",        0,      {actnNo,kwdChar,propChp,false},    '}'},
//{ "\\",       0,      {actnNo,kwdChar,propChp,false},    '\\'}
    };
const int isymMax = sizeof(rgsymRtf) / sizeof(SYM);


RGBQuad RHT_Highlight[17]={
 {0,   0,   0},          //	----
 {0,   0,   0},		//1 black
 {0x00,0x00,0xFF},	//2 blue
 {0x00,0xFF,0xFF},	//3 cyan
 {0x00,0xFF,0x00},	//4 green
 {0xFF,0x00,0xFF},	//5 magenta
 {0xFF,0x00,0x00},	//6 red
 {0xFF,0xFF,0x00},	//7 yellow
 {0x00,0x00,0x00},       //	----
 {0x00,0x00,0x7F},	//9 dark blue
 {0x00,0x7F,0x7F},	//10 dark cyan
 {0x00,0x7F,0x00},       //11 dark green
 {0x7F,0x00,0x7F},	//12 dark magenta
 {0x7F,0x00,0x00},	//13 dark red
 {0x7F,0x7F,0x00},	//14 dark yellow
 {0x7F,0x7F,0x7F},	//15 dark gray
 {0xBF,0xBF,0xBF}};	//16 light gray


/// This procedure converts hexadecimal block from RTF to ist original shape, block is ended by '}.
static void HEX2BIN_Block(FILE *FileIn, FILE *FileOut)
{
char c;
BYTE HEX;

  while(!feof(FileIn))
      {
      c = toupper(fgetc(FileIn));
      if(isxdigit(c))
	{
	HEX=(c>='A'?c-'A'+10:c-'0');
	c=toupper(fgetc(FileIn));
	if(isxdigit(c))
	   {
	   HEX=16*HEX + (c>='A'?c-'A'+10:c-'0');
	   fputc(HEX,FileOut);
	   continue;
	   }
	if(isspace(c))
		{fputc(HEX,FileOut);continue;}
	}

      if(c=='}') {ungetc(c,FileIn);break;}
      }
}


/** This procedure extracts a nested stream from RTF. */
static void ExtractHexObject(TconvertedPass1 *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ExtractHexObject() ");fflush(cq->log);
#endif
FILE *txt;
string FileName;

  FileName = OutputDir+GetSomeImgName(".mtf");
  txt = OpenWrChk(FileName(),"wb",cq->err);
  if(txt == NULL) return;

  HEX2BIN_Block(cq->wpd,txt);

  fclose(txt);

  txt = fopen(FileName(),"rb");
  if(txt!=NULL)
      {
      CheckFileFormat(txt,FilForD);
      TconvertedPass1 *cqPass1 = GetConverter(FilForD.Converter);
      if(cqPass1!=NULL)
	      {
	      cq->perc.Hide();
	      cqPass1->InitMe(txt,cq->table,cq->strip,cq->log,cq->err);
	      cqPass1->Convert_first_pass();
	      cq->perc.Show();
	      delete cqPass1;
	      }
      fclose(txt);
      }
  else
      {
      if(cq->err)
          fprintf(cq->err, _("\nError: Cannot reopen file '%s' for reading."), FileName());
      }
#ifndef DEBUG
  unlink(FileName());
#endif
}


static string RTFReadFirstWord(FILE *f,char *pc=NULL)
{
string Str;
char c;

 do {
    c=fgetc(f);
    } while(isspace(c));
 while(!isspace(c))
    {
    if(c=='}' || c=='{')
	{
	ungetc(c,f);
	break;
	}
    Str+=c;
    c=fgetc(f);
    if(feof(f)) break;
    }

 if(pc!=NULL) *pc=c;
 return(Str);
}


// Set a property that requires code to evaluate.
/*
static int ecParseSpecialProperty(TconvertedPass1_RTF *cq,IPROP iprop, int val)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecParseSpecialProperty() ");fflush(cq->log);
#endif

  switch (iprop)
    {
    case ipropPard:
	memset(&cq->pap, 0, sizeof(cq->pap));
	return ecOK;
    case ipropPlain:
	memset(&cq->chp, 0, sizeof(cq->chp));
	return ecOK;
    case ipropSectd:
	memset(&cq->sep, 0, sizeof(cq->sep));
	return ecOK;
    }
  return ecBadTable;
} */



/// Set the property identified by _iprop_ to the value _val_.
static int ecApplyPropChange(TconvertedPass1_RTF *cq, int isym, int val)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecApplyPropChange() ");fflush(cq->log);
#endif
char *pb;

  if (cq->rds >= rdsSkip)                 // If we're skipping text,
	return ecOK;                    // don't do anything.

  switch (rgsymRtf[isym].F.PropScope & 3)
    {
    case propDop: pb = (char *)&cq->dop;
		  break;
    case propSep: pb = (char *)&cq->sep;
		  break;
    case propPap: pb = (char *)&cq->pap;
		  break;
    case propChp: pb = (char *)&cq->chp;
		  break;
    default: if ((rgsymRtf[isym].F.PropSize&3) != actnSpec) return ecBadTable;
	     break;
    }
  switch (rgsymRtf[isym].F.PropSize & 3)
    {
    case actnByte: pb[rgsymRtf[isym].idx] = (unsigned char) val;
		   break;
    case actnWord: (*(int *) (pb+rgsymRtf[isym].idx)) = val;
		   break;
    case actnSpec: return 0; // ecParseSpecialProperty(cq,isym, val);
    default:       return ecBadTable;
    }
  return ecOK;
}


// Change to the destination specified by idest.
// There's usually more to do here than this...
static int ecChangeDest(TconvertedPass1_RTF *cq, IDEST idest)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecChangeDest() ");fflush(cq->log);
#endif
  if (cq->rds >= rdsSkip)             // if we're skipping text,
        return ecOK;                // don't do anything

  switch (idest)
    {
    default:cq->rds = rdsSkip;              // when in doubt, skip it...
	    break;
    }
  return ecOK;
}


/// Route the character to the appropriate destination stream.
static int ecParseChar(TconvertedPass1_RTF *cq, int ch)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecParseChar(%c) ",ch);fflush(cq->log);
#endif

  if (cq->ris == risBin && --cq->cbBin <= 0)
	       cq->ris = risNorm;
  if(cq->flag==Nothing) return(ecOK);	//do not output a character if nothing is required

  switch (cq->rds)
    {
    case rdsShpInst:
    case rdsSkip: return ecOK;  // Toss this character.
    case rdsNorm:switch(ch)  // Output a character.  Properties are valid at this point.
		   {
		   case  9:return(ecOK);// ecPrintChar(cq,'\t'); - ignore tabulator
		   case 10:
		   case 13:if(cq->char_on_line==CHAR_PRESENT) NewLine(cq);
			   break;
		   default:if(ch==0) return(ecOK); // unfortunately, we don't do a whole lot here as far as layout goes...
			   if(memcmp(&cq->NewRGB,&cq->RGB,sizeof(RGBQuad)))
				{
				cq->RGB=cq->NewRGB;
				Color(cq,0,&cq->RGB);
				}
			   CharacterStr(cq,Ext_chr_str(ch,cq,cq->ConvertCpg));
			   return(ecOK);
		   }
    default:      // handle other destinations....
		  return ecOK;
    }
}


/// Evaluate an RTF control that needs special processing.
static int ecParseSpecialKeyword(TconvertedPass1_RTF *cq,IPFN ipfn)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecParseSpecialKeyword() ");fflush(cq->log);
#endif
    if (cq->rds >= rdsSkip && ipfn != ipfnBin)  // if we're skipping, and it's not
        return ecOK;                        // the \bin keyword, ignore it.
    switch (ipfn)
    {
    case ipfnBin:
	  cq->ris = risBin;
	  cq->cbBin = cq->lParam;
	  break;
    case ipfnSkipDest:
	  cq->fSkipDestIfUnk = true;
	  break;
    case ipfnHex:
	  cq->ris = risHex;
	  break;
    default:return ecBadTable;
    }
    return ecOK;
}



// Step 3.
// Search rgsymRtf for szKeyword and evaluate it appropriately.
//
// Inputs:
// szKeyword:   The RTF control to evaluate.
// param:       The parameter of the RTF control.
// fParam:      true if the control had a parameter; (i.e., if param is valid)
//              false if it did not.
static int ecTranslateKeyword(TconvertedPass1_RTF *cq,const char *szKeyword, int param, bool fParam)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecTranslateKeyword() ");fflush(cq->log);
#endif
int isym;

// search for szKeyword in rgsymRtf

  for (isym = 0; isym < isymMax; isym++)
	if (strcmp(szKeyword, rgsymRtf[isym].szKeyword) == 0)
	    break;
  if (isym == isymMax)            // control word not found
	{
	UnknownObjects++;
	if (cq->fSkipDestIfUnk)         // if this is a new destination
	    {
	    cq->rds = rdsSkip;          // skip the destination
	    }
				    // else just discard it
	cq->fSkipDestIfUnk = false;
	return ecOK;
	}

// found it!  use kwd and idx to determine what to do with it.

  cq->fSkipDestIfUnk = false;
  switch (rgsymRtf[isym].F.Kwd & 3)
    {
    case kwdProp:
	if (rgsymRtf[isym].F.ForceArg || !fParam)
	    param = rgsymRtf[isym].dflt;
	return ecApplyPropChange(cq,isym,param);
    case kwdChar:
	return ecParseChar(cq,rgsymRtf[isym].idx);
    case kwdDest:
	return ecChangeDest(cq,(IDEST)rgsymRtf[isym].idx);
    case kwdSpec:
	return ecParseSpecialKeyword(cq,(IPFN)rgsymRtf[isym].idx);
    }

  return ecBadTable;
}


// Save relevant info on a linked list of SAVE structures.
static int ecPushRtfState(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecPushRtfState() ");fflush(cq->log);
#endif
    SAVE *psaveNew = (SAVE *)malloc(sizeof(SAVE));
    if (!psaveNew)
	{
	if (cq->err != NULL)
	   {
	   cq->perc.Hide();
	   fprintf(cq->err,_("\nError: Not enough memory!"));
	   }
	return ecStackOverflow;
	}

    psaveNew->pNext = cq->psave;
    psaveNew->CharA = cq->attr;
    psaveNew->pap = cq->pap;
    psaveNew->pap.envir = cq->envir;
    psaveNew->sep = cq->sep;
    psaveNew->dop = cq->dop;
    psaveNew->rds = cq->rds;
    psaveNew->ris = cq->ris;
    psaveNew->fSkipDestIfUnk=cq->fSkipDestIfUnk;
    psaveNew->RGB = cq->NewRGB;
    psaveNew->Cols = cq->Columns;
    psaveNew->ConvertCpg = cq->ConvertCpg;
    cq->ris = risNorm;
    cq->psave = psaveNew;
    cq->cGroup++;
    return ecOK;
}


/** If we're ending a destination (i.e., the destination is changing),
//  call ecEndGroupAction.
//  Always restore relevant info from the top of the SAVE list. */
static int ecPopRtfState(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ecPopRtfState() ");fflush(cq->log);
#endif
    SAVE *psaveOld;
    string s;

    if (!cq->psave)
	{
	if (cq->err != NULL)
	   {
	   cq->perc.Hide();
	   fprintf(cq->err,_("\nError: Curly brace stack '}' underflow!"));
	   }
	return ecStackUnderflow;
	}

/*  if (cq->rds != cq->psave->rds)
	{		 //The destination specified by rds is coming to a close.
			 // If there's any cleanup that needs to be done, do it now.
	if ((ec = ecEndGroupAction(cq->rds)) != ecOK) return ec;
	}*/

    psaveOld = cq->psave;

    AttrFit(cq->attr,psaveOld->CharA,s);
    if(!s.isEmpty()) fputs(s(),cq->strip);

    cq->fSkipDestIfUnk=psaveOld->fSkipDestIfUnk &&
		       (cq->fSkipDestIfUnk || (!cq->fSkipDestIfUnk && (cq->rds!=rdsNorm)));
    cq->pap = psaveOld->pap;
    cq->sep = psaveOld->sep;
    cq->dop = psaveOld->dop;
    cq->rds = psaveOld->rds;
    cq->ris = psaveOld->ris;

    cq->NewRGB = psaveOld->RGB;
    cq->ConvertCpg = psaveOld->ConvertCpg;
    if(psaveOld->Cols != cq->Columns) Column(cq,psaveOld->Cols);

    cq->psave = psaveOld->pNext;
    cq->cGroup--;
    free(psaveOld);

    if(cq->pap.envir != cq->envir)	/*Restore paragraph enviroment information*/
	{
	cq->envir=cq->pap.envir;
	if(cq->envir != ' ')
		cq->char_on_line = LEAVE_ONE_EMPTY_LINE;
	}
    return ecOK;
}


/*
// Step 2:Get a control word (and it's associated value) and
//        call ecTranslateKeyword to dispatch the control.
static int ecParseRtfKeyword(TconvertedPass1_RTF *cq)
{
int ch;
char fParam = false;
int param = 0;

 cq->Keyword = cq->Parameter = "";
 if ((ch = getc(cq->wpd)) == EOF)
	return ecEndOfFile;
 if (!isalpha(ch))           // a control symbol; no delimiter.
      {
      cq->Keyword = (char) ch;
      return ecTranslateKeyword(cq,cq->Keyword(), 0, fParam);
      }
 while(isalpha(ch))
	{
	cq->Keyword += (char) ch;
	ch = getc(cq->wpd);
	}

 if (ch == '-')
       {
       cq->Parameter = (char) ch;
       if ((ch = getc(cq->wpd)) == EOF)
	    return ecEndOfFile;
       }
 if (isdigit(ch))
      {
      fParam = true;         // a digit after the control means we have a parameter
      while(isdigit(ch))
	 {
	 cq->Parameter += (char) ch;
	 ch = getc(cq->wpd);
	 }
      param = atoi(cq->Parameter());
      cq->lParam = atol(cq->Parameter());
      }
 if (ch != ' ')
	ungetc(ch, cq->wpd);
 return ecTranslateKeyword(cq,cq->Keyword(), param, fParam);
}
*/


//returns 0 - EOF;1-Binary;2-unknown;3-Bad Hex
//        0xA;0xD - EOL;200-{;201-};202-keyword no arg;203-wrong arg;204-keyword+arg;205-character;206-HEX
static int RTFLoadKeyword(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#RTFLoadKeyword() ");fflush(cq->log);
#endif
int ch;

 cq->Parameter.erase();
 cq->Keyword.erase();
 cq->lParam=0;
 cq->isParam=false;
 if ((ch = getc(cq->wpd)) == EOF) return(0); //no Keyword
 cq->by=ch;

 if (cq->ris == risBin)
	{
	cq->Keyword=(char)ch;
	return(1);	// if we're parsing binary data, handle it directly
	}

 if(ch==0xD || ch==0xA) return(ch);

 if(ch=='{') return(200);
 if(ch=='}') return(201);

 if(ch=='\\')
	{
	cq->Keyword.erase();
        cq->Parameter.erase();
	if ((ch = getc(cq->wpd)) == EOF) return(0);
	if (ch == '\'')
	     {
	     ch=getc(cq->wpd);
	     if(!isxdigit(ch)) {return(3);}
	     ch = toupper(ch);
	     if(ch>='A') ch-='A'-10;
		    else ch-='0';
	     cq->by=ch;
	     ch=getc(cq->wpd);
	     if(!isxdigit(ch)) {return(3);}
	     ch = toupper(ch);
	     if(ch>='A') ch-='A'-10;
		    else ch-='0';
	     cq->by=16*cq->by+ch;
	     return(206);
	     }
	if (!isalpha(ch))           // a control symbol; no delimiter.
	     {
	     cq->Keyword = (char) ch;
	     return(202);  // ecTranslateKeyword(cq,cq->Keyword(), 0, fParam);
	     }
	while(isalpha(ch))
	       {
	       cq->Keyword += (char) ch;
	       ch = getc(cq->wpd);
	       }
	if (ch == '-')
	      {
	      cq->Parameter = (char) ch;
	      if ((ch = getc(cq->wpd)) == EOF)
		   {
		   ungetc(*cq->Parameter(), cq->wpd);
		   cq->Parameter.erase();
		   return(203);		//unexpected EOF
		   }
	      }
	if(isdigit(ch))
	     {
	     cq->isParam = true;         // a digit after the control means we have a parameter
	     while(isdigit(ch))
		{
		cq->Parameter += (char) ch;
		ch = getc(cq->wpd);
		}
	     cq->fParam = atoi(cq->Parameter());
	     cq->lParam = atol(cq->Parameter());
	     }
	if(ch != ' ')
	       ungetc(ch, cq->wpd);
	return(204); // ecTranslateKeyword(cq,cq->Keyword(), param, fParam);
	}

 if (cq->ris == risNorm) return(205);	//if ((ec = ecParseChar(cq,ch)) != ecOK)

 if (cq->ris != risHex) return(2);

 if (isdigit(ch)) cq->by = (char) ch - '0';
 else {
     if (ch >= 'a' && ch <= 'f') cq->by = (char) ch - 'a';
	 else if (ch >= 'A' && ch <= 'F') cq->by = (char) ch - 'A';
	      else return(3);
     }
 if ((ch = getc(cq->wpd)) == EOF) return(206);
 cq->by = cq->by << 4;
 if (isdigit(ch)) cq->by |= (char) ch - '0';
 else {
      if (ch >= 'a' && ch <= 'f') cq->by |= (char) ch - 'a';
	 else if (ch >= 'A' && ch <= 'F') cq->by |= (char) ch - 'A';
	      else {
		   ungetc(ch, cq->wpd);
		   return(206);
		   }
      }
return(206);
}
/*-----------------End of RTF service procedures---------------------*/

/** This procedure changes codepage converter */
static void ChangeCpTranslator(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ChangeCpTranslator() ");fflush(cq->log);
#endif
 if(cq->Parameter.isEmpty())
	{cq->ConvertCpg = NULL;return;}
 cq->ConvertCpg = GetTranslator(string("cp"+cq->Parameter+"TOinternal"));
 if(cq->ConvertCpg==NULL || cq->ConvertCpg==&Dummy)
   {
   if(cq->err != NULL)
	{
	cq->perc.Hide();
	fprintf(cq->err,_("\nWarning: Cannot initialize codepage converter for codepage %s!"),cq->Parameter());
	cq->perc.Show();
	}
   }
 else cq->ForcedTranslator=1;
}


/** Read color table and store it in 'cq->ColorTBL' */
static void ColorTable(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ColorTable() ");fflush(cq->log);
#endif
RGBQuad RGB;

 if(cq->ColorTBL)
	{free(cq->ColorTBL);cq->ColorTBL=NULL;};
 cq->nColorTBL=0;

 cq->ActualPos=ftell(cq->wpd);
 RGB.O=0;
 while (!feof(cq->wpd))
	{
	switch(cq->subby=RTFLoadKeyword(cq))
	   {
	   case 0xA:break;
	   case 0xD:break;
	   case 204:if(cq->Keyword=="red")
			{
			if(!cq->isParam) cq->lParam=0;
			RGB.R=cq->lParam;
			RGB.O|=1;
			break;
			}
		    if(cq->Keyword=="green")
			{
			if(!cq->isParam) cq->lParam=0;
			RGB.G=cq->lParam;
			RGB.O|=2;
			break;
			}
		    if(cq->Keyword=="blue")
			{
			if(!cq->isParam) cq->lParam=0;
			RGB.B=cq->lParam;
			RGB.O|=3;
			break;
			}
		    goto FINISH;
	   case 205:if(isspace(cq->by)) break;
		    if(cq->by==';')
			{
			if(RGB.O>0)
			  {
			  RGB.O=0;
			  if(cq->nColorTBL==0)
			       cq->ColorTBL=(RGBQuad *)malloc(sizeof(RGBQuad));
			  else cq->ColorTBL=(RGBQuad *)realloc(cq->ColorTBL,sizeof(RGBQuad)*(1+(cq->nColorTBL)));
			  if(cq->ColorTBL)
			    {
			    cq->ColorTBL[cq->nColorTBL++]=RGB;
			    }
			  else cq->nColorTBL=0;
			  }
			break;
			}
		    goto FINISH;
	   default: goto FINISH;
	   }
	cq->ActualPos=ftell(cq->wpd);
	}
   return;
FINISH:
   fseek(cq->wpd,cq->ActualPos,SEEK_SET);
}


static void fldinst(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#fldinst() ");fflush(cq->log);
#endif
string Str;
int state;
int pos;
CpTranslator *CpTrn;
char c;
int CurlyBraces=0;

state=0;
do {
   Str = RTFReadFirstWord(cq->wpd,&c);
   switch(state)
	{
	case 0:if(Str=="SYMBOL") state=1;
	       if(Str=="PAGEREF") state=10;
	       if(Str=="REF") state=20;
	       break;
	case 1:pos=atol(Str); state=2; break;
	case 2:if(Str=="\\\\f") state=3;
	       break;
	case 3:if(Str[0]=='\"') Str=copy(Str,1,length(Str)-1);
	       if(Str[length(Str)-1]=='\"') Str=copy(Str,0,length(Str)-1);
	       Str.ToLower();
	       CpTrn = GetTranslator(string(Str+"TOinternal"));
	       if(CpTrn==NULL) {state=0;break;}  //This should be reviewed!!!!!!
	       CharacterStr(cq,Ext_chr_str(pos,cq,CpTrn));
	       state=0;
	       break;

	case 10:if(!Str.isEmpty())
		  {
		  fprintf(cq->strip, " \\pageref{%s}",Str());
		  state=0;
		  }
		break;
	case 20:if(!Str.isEmpty())
		  {
		  fprintf(cq->strip, " \\ref{%s}",Str());
		  state=0;
		  }
		break;
	}

   if(c=='{')
	{
	CurlyBraces++;
	c=fgetc(cq->wpd);
	Str=c;
	continue;
	}
   if(c=='}')
	{
	state=0;
	if(CurlyBraces-->0)
	   {
	   c=fgetc(cq->wpd);
	   Str=c;
	   continue;
	   }
	}
   } while(!Str.isEmpty());
}


static void FootnoteRTF(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#FootnoteRTF() ");fflush(cq->log);
#endif
  attribute OldAttr;
  int CurlyBaraceStack;
  unsigned char OldFlag;

  CurlyBaraceStack=1;

  OldFlag = cq->flag;
  cq->flag = HeaderText;
  cq->recursion++;

  Close_All_Attr(cq->attr,cq->strip);
  OldAttr=cq->attr;
  cq->attr.InitAttr();

  cq->ActualPos = ftell(cq->wpd);
  cq->subby=RTFLoadKeyword(cq);
  if(cq->subby==204 && cq->Keyword=="ftnalt")
	{
	if(!EndNotes) EndNotes=true;		/* set up endnotes */
	if(EndNotes==-1) EndNotes=-2;
	fputs("\\endnote{", cq->strip);
	}
  else  {
	fseek(cq->wpd, cq->ActualPos, SEEK_SET);
	fputs("\\footnote{", cq->strip);
	}

  while (!feof(cq->wpd) && CurlyBaraceStack > 0)
	{
	cq->subby=RTFLoadKeyword(cq);
	if(cq->subby==200) CurlyBaraceStack++;
	if(cq->subby==201) CurlyBaraceStack--;

	if(cq->subby==204)
	   {
	   if(cq->Keyword=="par" || cq->Keyword=="pard")
		{
		putc(' ', cq->strip);
		continue;
		}
	   }

	switch (cq->by)
	     {
/*	     case 0xa:
	     case 0xc:fprintf(cq->strip, "\\\\ ");
		      break;*/

	     case 0xb:
	     case 0xd:putc(' ', cq->strip);
		      break;

	     default: ProcessKeyRTF(cq);
		      break;
	     }

	   }

  cq->ActualPos = ftell(cq->wpd);
  Close_All_Attr(cq->attr,cq->strip);   /* Echt nodig ? */
  putc('}', cq->strip);

  cq->attr=OldAttr;

  cq->recursion--;
  strcpy(cq->ObjType, "Footnote");
  cq->flag = OldFlag;
}


static void FontTableRTF(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#FontTableRTF() ");fflush(cq->log);
#endif
  unsigned char OldFlag,OldEnvir;
  attribute OldAttr;
  int CurlyBaraceStack;

  CurlyBaraceStack=1;
  OldFlag = cq->flag;
  OldAttr = cq->attr;
  OldEnvir= cq->envir;

// Start parsing of a font table
  cq->recursion++;

  cq->flag = Nothing;

  while (!feof(cq->wpd) && CurlyBaraceStack > 0)
	{
	cq->subby=RTFLoadKeyword(cq);
	if(cq->subby==200) CurlyBaraceStack++;
	if(cq->subby==201) CurlyBaraceStack--;

	if(cq->subby==204)
	   {
	   if(cq->Keyword=="f")
		{
		cq->recursion++;
		if(!cq->isParam) cq->lParam=0;

		string s;
		int StackDepth=CurlyBaraceStack;
		int param=cq->lParam;

		while (!feof(cq->wpd) && CurlyBaraceStack>=StackDepth)
		  {
		  cq->subby=RTFLoadKeyword(cq);
		  switch(cq->subby)
		    {
		    case 0:   break;
		    case 1:
		    case 2:
		    case 205:
		    case 206: s+=cq->by; break;
		    case 200: CurlyBaraceStack++; break;
		    case 201: CurlyBaraceStack--; break;
		    }
		  ProcessKeyRTF(cq);
		  }
		cq->recursion--;
		s.trim();
		if(s[length(s)-1]==';') s=copy(s,0,length(s)-1);
		//printf("font:%d %s ",param,s());
		MoveSTRPos(cq->FontTable,s.ExtractString(),param);
		continue;
		}
	   }

	ProcessKeyRTF(cq);
	}
  cq->ActualPos = ftell(cq->wpd);

  cq->recursion--;

  cq->attr = OldAttr;
  cq->flag = OldFlag;
  cq->envir= OldEnvir;

  strcpy(cq->ObjType, "Font Table");
}


static void HeaderFooterRTF(TconvertedPass1_RTF *cq, unsigned char HFtype)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#HeaderFooterRTF(%d) ",(int)HFtype); fflush(cq->log);
#endif
  unsigned char OldFlag,OldEnvir;
  attribute OldAttr;
  int CurlyBaraceStack;

  CurlyBaraceStack=1;
  OldFlag = cq->flag;
  OldAttr = cq->attr;
  OldEnvir= cq->envir;

// Start a conversion of a Header or Footer
  cq->attr.InitAttr();		//Turn all attributes in the header/footer off
  cq->recursion++;

  cq->line_term = 's';    	//Soft return
  if(cq->char_on_line == LEAVE_ONE_EMPTY_LINE)  // Left one enpty line for new enviroment.
      {
      fputc('%', cq->strip);
      NewLine(cq);
      }
  if(cq->char_on_line==CHAR_PRESENT)
      {
      NewLine(cq);
      }

//	             (HFtype2 & 3) <= 1 ? "\\headtext":"\\foottext"
  InitHeaderFooter(cq,HFtype & 3,HFtype >> 2);

  cq->envir='!';		//Ignore enviroments after header/footer
  NewLine(cq);

  cq->envir = ' ';
  cq->flag = HeaderText;
  cq->char_on_line = FIRST_CHAR_MINIPAGE;
  while (!feof(cq->wpd) && CurlyBaraceStack > 0)
	{
	cq->subby=RTFLoadKeyword(cq);
	if(cq->subby==200) CurlyBaraceStack++;
	if(cq->subby==201) CurlyBaraceStack--;

	if(cq->subby==204)
	   {
	   if(cq->Keyword=="par" || cq->Keyword=="pard")
		{
		putc(' ', cq->strip);
		continue;
		}
	   }

	switch (cq->by)
	     {
/*	     case 0xa:
	     case 0xc:fprintf(cq->strip, "\\\\ ");
		      break;*/

	     case 0xb:
	     case 0xd:putc(' ', cq->strip);
		      break;

	     default: ProcessKeyRTF(cq);
		      break;
	     }

	}
  cq->ActualPos = ftell(cq->wpd);

  Close_All_Attr(cq->attr,cq->strip);
  if(cq->char_on_line==CHAR_PRESENT)
     {
     cq->line_term = 's';    	//Soft return
     NewLine(cq);
     }
  putc('}', cq->strip);

  cq->line_term = 's';    	//Soft return
  cq->envir='^';		//Ignore enviroments after header/footer
  NewLine(cq);

  cq->recursion--;

  cq->attr = OldAttr;
  cq->flag = OldFlag;
  cq->envir= OldEnvir;
  cq->char_on_line = FIRST_CHAR_MINIPAGE;

  strcpy(cq->ObjType, ((HFtype & 3) <= 1)?"Header":"Footer");
}


static void IndexRTF(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#IndexRTF() ");fflush(cq->log);
#endif
  attribute OldAttr;
  int CurlyBaraceStack;
  unsigned char OldFlag;

  CurlyBaraceStack=1;

  OldFlag = cq->flag;
  cq->flag = HeaderText;
  cq->recursion++;

  Close_All_Attr(cq->attr,cq->strip);
  OldAttr=cq->attr;
  cq->attr.InitAttr();

  cq->ActualPos = ftell(cq->wpd);
  fputs(" \\index{", cq->strip);

  while (!feof(cq->wpd) && CurlyBaraceStack > 0)
	{
	cq->subby=RTFLoadKeyword(cq);
	if(cq->subby==200) CurlyBaraceStack++;
	if(cq->subby==201) CurlyBaraceStack--;

	if(cq->subby==202 || cq->subby==203 || cq->subby==204)
	   {
	   if(cq->Keyword=="par" || cq->Keyword=="pard")
		{
		putc(' ', cq->strip);
		continue;
		}
	   continue;
	   }

	switch (cq->by)
	     {
/*	     case 0xa:
	     case 0xc:fprintf(cq->strip, "\\\\ ");
		      break;*/

	     case 0xb:
	     case 0xd:putc(' ', cq->strip);
		      break;

	     default: ProcessKeyRTF(cq);
		      break;
	     }

	   }

  cq->ActualPos = ftell(cq->wpd);
  Close_All_Attr(cq->attr,cq->strip);
  putc('}', cq->strip);
  Index=true;

  cq->attr=OldAttr;

  cq->recursion--;
  strcpy(cq->ObjType, "Index");
  cq->flag = OldFlag;
}



static void LanguageRTF(TconvertedPass1_RTF *cq)
{
 if(!cq->isParam) return;
 switch(cq->lParam)
   {
   case 0x405:Language(cq,'C'+256*'Z');
	      if(!cq->ForcedTranslator)
	        {
		cq->Parameter="1250";
		ChangeCpTranslator(cq);
		cq->ForcedTranslator=0;
		}
	      break;
   case 0x407:Language(cq,'D'+256*'E');
	      break;
   case 0x409:Language(cq,'U'+256*'S');
	      break;
   case 0x809:Language(cq,'U'+256*'K');
	      break;
   }
}


static void PictureRTF(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#PictureRTF() ");fflush(cq->log);
#endif
int CurrentLevel=cq->cGroup;
FILE *ImgFile;
string FileName;
TBox Box;

  initBox(Box);  
  Box.HorizontalPos = 3;	/*3-Full */  
  //Box.Image_type = 0;		/*external*/
  Box.Contents = 3; 		/*contents image*/  

  while (!feof(cq->wpd))
	{
	cq->subby=RTFLoadKeyword(cq);
	ProcessKeyRTF(cq);

//	if(cq->subby>=202 && cq->subby<=204) printf("%s\n",cq->Keyword() );

	if(cq->cGroup>CurrentLevel) continue;

	if(cq->subby==205)	//regular char
	   {
	   if(isxdigit(cq->by))
		{
		ungetc(cq->by,cq->wpd);

                FileName = MergePaths(OutputDir,RelativeFigDir)+GetSomeImgName();
		ImgFile = OpenWrChk(FileName(),"wb",cq->err);
		if(ImgFile == NULL) break;

		HEX2BIN_Block(cq->wpd,ImgFile);
		fclose(ImgFile);

		ImageWP(cq,FileName,Box);
#ifndef DEBUG
		//printf("\n%d %s\n",i,FileName());
                if(SaveWPG<0)
		    unlink(FileName());
#endif
		break;
		}
	   }

	if(cq->cGroup<CurrentLevel) break;  //The pict destination read finished
	}

}


static void MakeLabelRTF(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#MakeLabelRTF() ");fflush(cq->log);
#endif
  string LBL_Text;
  char c;

  LBL_Text=RTFReadFirstWord(cq->wpd,&c);
  if(c!='}') {
	     ecParseSpecialKeyword(cq,ipfnSkipDest);
	     cq->rds=rdsSkip;
	     }
	else c=fgetc(cq->wpd); 		//read the closing curly brace
  if(!LBL_Text.isEmpty())
    {
    fprintf(cq->strip, "%s\\label{%s}",cq->char_on_line>0?" ":"",LBL_Text());
    if(cq->char_on_line == false)
		cq->char_on_line = -1;
    }
  strcpy(cq->ObjType, "Label");
}


static void UnicodeSymbol(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#UnicodeSymbol() ");fflush(cq->log);
#endif
char dummy[10];
DWORD pos;
int ch;

  pos = ftell(cq->wpd);		// remove \' that follows imediatelly after \u
  ch = fgetc(cq->wpd);
  if(ch=='\\')
  {
    ch = fgetc(cq->wpd);
    if(ch=='\'')
    {
    ch = fgetc(cq->wpd);
    if(isxdigit(ch))
      {
      ch = fgetc(cq->wpd);
      if(isxdigit(ch))
        {
	ch = -32000;
	}
      }
    }
  }

  if(ch!=-32000)
  {
    fseek(cq->wpd,pos,SEEK_SET);
  }


 if(cq->Unicode==NULL)
	{
	sprintf(dummy,"U%ld ",cq->lParam);
	CharacterStr(cq,dummy);
	}
 else {
      CharacterStr(cq,Ext_chr_str(cq->lParam, cq, cq->Unicode));
      }
}


/*special object is the stream marked with \*\xxx command*/
static void SpecialObject(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#SpecialObject() ");fflush(cq->log);
#endif
DWORD FilePos;
string ObjectName,Type;
char c;

  if (cq->rds == rdsSkip) return;

  FilePos=ftell(cq->wpd);
  c=fgetc(cq->wpd);
  if(c=='\\') c=fgetc(cq->wpd);
  do {
     if(c=='\\') {ungetc(c,cq->wpd);break;}
     if(c=='}' || c=='{')
	{
	ungetc(c,cq->wpd);
	ecParseSpecialKeyword(cq,ipfnSkipDest);
	return;
	}
     Type+=c;
     c=fgetc(cq->wpd);
     } while(!isspace(c));


  strncpy(cq->ObjType,Type(),sizeof(cq->ObjType));
  cq->ObjType[sizeof(cq->ObjType)-1]=0;

  if(Type=="bkmkstart")
	{
	MakeLabelRTF(cq);
	return;
	}

  if(Type=="fldinst")
	{
	fldinst(cq);
	return;
	}

/*  if(Type=="blipuid")
	{
	}*/

  if(Type=="objdata")
	{
	do {
	   do {
	      if(feof(cq->wpd))
		{
		ecParseSpecialKeyword(cq,ipfnSkipDest);
		return;
		}
	      c=fgetc(cq->wpd);
	      } while(isspace(c));
	   if(c=='}' || c=='\\') {ungetc(c,cq->wpd);break;}
	   if(c=='{' || !isxdigit(c))
	     {
	     fseek(cq->wpd,FilePos,SEEK_SET);
	     ecParseSpecialKeyword(cq,ipfnSkipDest);
	     return;
	     }
	   ObjectName+=c;
	   if(length(ObjectName)==70)	//Equation.3 needs 70 bytes
		{			//Equation.DSMT4 needs 78 bytes
		c=fgetc(cq->wpd);
		ungetc(c,cq->wpd);
		if(!isxdigit(c)) break;
		}
	   } while(length(ObjectName)<78);

	ExtractHexObject(cq);
	return;
	}

  if(Type=="shpinst")
	{
	cq->rds=rdsShpInst;
	return;
	}


//printf("'%s:%s'   ",Type(),ObjectName());
/*
  if(Type=="objclass")
	{
	while(isspace(c)) c=fgetc(cq->wpd);
	do {
	   if(c=='}' || c=='\\') {ungetc(c,cq->wpd);break;}
	   if(c=='{')
	     {
	     fseek(cq->wpd,FilePos,SEEK_SET);
	     ecParseSpecialKeyword(cq,ipfnSkipDest);
	     return;
	     }
	   ObjectName+=c;
	   if(feof(cq->wpd))
	     {
	     ecParseSpecialKeyword(cq,ipfnSkipDest);
	     return;
	     }
	   c=fgetc(cq->wpd);
	   } while(!isspace(c));
//	asm int 3;
	}*/
  ecParseSpecialKeyword(cq,ipfnSkipDest);
  cq->rds=rdsSkip;		//skip all "\*" stream because it is not known
}



// Step 1:
// Isolate RTF keywords and send them to ecParseRtfKeyword;
// Push and pop state at the start and end of RTF groups;
// Send text to ecParseChar for further processing.
static int ProcessKeyRTF(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ProcessKeyRTF() ");fflush(cq->log);
#endif
int ec;
int KeywordType;

  if (cq->cGroup < 0) return ecStackUnderflow;

  if(cq->by==0) KeywordType=RTFLoadKeyword(cq);
	   else KeywordType=cq->subby;

  switch(KeywordType)
     {
     case 0:return ecOK;	 		    //EOF;
     case 1:if((ec = ecParseChar(cq,cq->by)) != ecOK)  //Binary; if we're parsing binary data, handle it directly
		return ec;
	    goto RTFnext;
     case 2:if((ec = ecParseChar(cq,cq->by)) != ecOK)  //unknown;
			return ec;
	    break;
     case 3:return ecInvalidHex;		    //Bad Hex
     case 0xA:
     case 0xD:if(cq->char_on_line==CHAR_PRESENT) NewLine(cq);
		      break;          // cr and lf are noise characters...    - EOL;
     case 200:if ((ec = ecPushRtfState(cq)) != ecOK)	// {
			 return ec;
	      break;
     case 201:if ((ec = ecPopRtfState(cq)) != ecOK)	 // }
			 return ec;
	       break;
     case 202:				 //keyword no arg
     case 203:				 //wrong arg
     case 204:strncpy(cq->ObjType+1,cq->Keyword,sizeof(cq->ObjType)-1);
	      *(cq->ObjType)='\\';

	     if(cq->Keyword=='*') {SpecialObject(cq);break;}

	      if(cq->Keyword=='~')
		  {
		  fputc('~', cq->strip);
		  break;
		  }
	      if(cq->Keyword=="ansi")
		  {
		  cq->ConvertCpg = NULL;
		  break;
		  }
	      if(cq->Keyword=="ansicpg")
		  {
		  ChangeCpTranslator(cq);
		  break;
		  }
	      if(cq->Keyword=='b')
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) AttrOn(cq->attr,12);	/* Start boldface */
			    else AttrOff(cq,12);	/* End boldface */
		  break;
		  }
	      if(cq->Keyword=="blue")
		  {
		  if(!cq->isParam) cq->lParam=0;
		  cq->NewRGB.B=cq->lParam;
		  break;
		  }
	      if(cq->Keyword=="bullet")
		  {
		  CharacterStr(cq,Ext_chr_str(8226, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="cf")
		  {
		  if(cq->isParam)
		    if(cq->lParam<cq->nColorTBL && cq->lParam>=1)
			{
			cq->NewRGB=cq->ColorTBL[cq->lParam-1];
			}
		  }
	      if(cq->Keyword=="chdate")
		  {
		  DateCode(cq);break;
		  }
	      if(cq->Keyword=="chpgn")
		  {
		  PageNumber(cq);break;
		  }
	      if(cq->Keyword=="colortbl")
		  {
		  ColorTable(cq);break;
		  }
	      if(cq->Keyword=="cols")
		  {
		  if(!cq->isParam) cq->lParam=1;
		  Column(cq,cq->lParam);
		  }
	      if(cq->Keyword=="cpg")
		  {
		  ChangeCpTranslator(cq);
		  break;
		  }
	      if(cq->Keyword=="emdash")
		  {
		  CharacterStr(cq,Ext_chr_str(151, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="endash")
		  {
		  CharacterStr(cq,Ext_chr_str(150, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="f")
		  {
		  if(!cq->isParam) cq->lParam=0;
		  const char *FX=cq->FontTable[cq->lParam];
		  int Len=StrLen(FX);

		  //cq->ConvertCpg = &Dummy;
		  if(Len>3)
		    {
		    if(FX[Len-3]==' ' && FX[Len-2]=='C' && FX[Len-1]=='E')
		      {
		      cq->Parameter="1250";
		      ChangeCpTranslator(cq);
		      }
		    }
		  sprintf(cq->ObjType,"Font:%ld",cq->lParam);
		  break;
		  }
	      if(cq->Keyword=="fonttbl")
		  {
		  FontTableRTF(cq);break;
		  }
	      if(cq->Keyword=="footer")
		  {
		  HeaderFooterRTF(cq,6);break;		// 0110b
		  }
	      if(cq->Keyword=="footerl")
		  {
		  HeaderFooterRTF(cq,0xE);break;	// 1110b
		  }
	      if(cq->Keyword=="footerr")
		  {
		  HeaderFooterRTF(cq,0xA);break;	// 1010b
		  }
	      if(cq->Keyword=="footnote")
		  {
		  FootnoteRTF(cq);break;
		  }
	       if(cq->Keyword=="green")
		  {
		  if(!cq->isParam) cq->lParam=0;
		  cq->NewRGB.G=cq->lParam;
		  break;
		  }
	      if(cq->Keyword=="header")
		  {
		  HeaderFooterRTF(cq,4);break;		// 0100b
		  }
	      if(cq->Keyword=="headerl")
		  {
		  HeaderFooterRTF(cq,0xC);break;	// 1100b
		  }
	      if(cq->Keyword=="headerr")
		  {
		  HeaderFooterRTF(cq,8);break;		// 1000b
		  }
	      if(cq->Keyword=="highlight")
		  {
		  if(!cq->isParam)
		    if(cq->lParam<=16 && cq->lParam>=1)
			cq->NewRGB=RHT_Highlight[cq->lParam];
		  break;
		  }
	      if(cq->Keyword=='i')
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) AttrOn(cq->attr,8);	/* Start italic */
			    else AttrOff(cq,8);		/* End italic */
		  break;
		  }
	      if(cq->Keyword=="leveltext" || cq->Keyword=="levelnumbers")
		  {
		  if(cq->rds==rdsNorm) cq->rds=rdsSkip;	//toss garbage after level commands
		  break;
		  }
	      if(cq->Keyword=="li")
		  {
		  cq->pap.xaLeft=cq->isParam?cq->lParam:0;
		  break;
		  }
	      if(cq->Keyword=="listname")
		  {
		  if(cq->rds==rdsNorm) cq->rds=rdsSkip;	//toss garbage after listname command
		  break;
		  }
	      if(cq->Keyword=="lquote")
		  {
		  CharacterStr(cq,Ext_chr_str(145, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="ldblquote")
		  {
		  CharacterStr(cq,Ext_chr_str(147, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="lang")
		  {
		  LanguageRTF(cq);break;
		  }
	      if(cq->Keyword=="line")
		  {
		  Terminate_Line(cq,'h');		/* Hard line */
		  break;
		  }
	      if(cq->Keyword=="outl")
		  {
		  if(cq->lParam) AttrOn(cq->attr,7);	/* Start underline */
			    else AttrOff(cq,7);		/* End underline */
		  break;
		  }
	      if(cq->Keyword=="page")
		  {
		  Terminate_Line(cq,'P');		/* Hard page */
		  break;
		  }
	      if(cq->Keyword=="par")
		  {
		  if(cq->char_on_line) Terminate_Line(cq, 'h');	/* Hard return */
		  Terminate_Line(cq, 'h');
		  break;
		  }
	      if(cq->Keyword=="pard")
		  {
		  if(cq->char_on_line) Terminate_Line(cq, 'h');	/* Hard return */
		  cq->envir=' '; 			/*Finish any enviromnet opened*/
		  break;
		  }
	      if(cq->Keyword=="pc")
		  {
		  cq->Parameter="437";
		  ChangeCpTranslator(cq);
		  break;
		  }
	      if(cq->Keyword=="pca")
		  {
		  cq->Parameter="850";
		  ChangeCpTranslator(cq);
		  break;
		  }
	      if(cq->Keyword=="pict")
		  {
		  PictureRTF(cq);  break;
		  }
	      if(cq->Keyword=="plain")
		  {
		  AttrOff(cq,8);
		  AttrOff(cq,9);
		  AttrOff(cq,12);	/* End boldface */
		  AttrOff(cq,13);
		  AttrOff(cq,14);	/* End underline */
		  break;
		  }
	      if(cq->Keyword=="qc")
		  {
		  Justification(cq,0x82);
		  break;
		  }
	      if(cq->Keyword=="qj")
		  {
		  Justification(cq,0x81);
		  break;
		  }
	      if(cq->Keyword=="ql")
		  {
		  Justification(cq,0x80);
		  break;
		  }
	      if(cq->Keyword=="qr")
		  {
		  Justification(cq,0x83);
		  break;
		  }
	      if(cq->Keyword=="red")
		  {
		  if(!cq->isParam) cq->lParam=0;
		  cq->NewRGB.R=cq->lParam;
		  break;
		  }
	      if(cq->Keyword=="rquote")
		  {
		  CharacterStr(cq,Ext_chr_str(146, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="rdblquote")
		  {
		  CharacterStr(cq,Ext_chr_str(148, cq, cq->Unicode)); break;
		  }
	      if(cq->Keyword=="scaps")
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) {AttrOn(cq->attr,15);strcpy(cq->ObjType, "smcap");}	/* Start small capitals */
			    else {AttrOff(cq,15);strcpy(cq->ObjType, "~smcap");}	/* End small capitals */
		  break;
		  }
	      if(cq->Keyword=="sectd")
		  {
		  if(cq->Columns>1) Column(cq,1);	/*Reset number of columns to 1*/
		  break;
		  }
	      if(cq->Keyword=="shad")
		  {
		  if(cq->lParam) {AttrOn(cq->attr,9);strcpy(cq->ObjType, "shad");}  /* Start shadow */
			    else {AttrOff(cq,9);strcpy(cq->ObjType, "~shad");}	   /* End shadow */
		  break;
		  }
	      if(cq->Keyword=="softpage")
		  {
		  Terminate_Line(cq,'p');		/* Soft page */
		  break;
		  }
	      if(cq->Keyword=="softline")
		  {
		  Terminate_Line(cq,'s');		/* Soft line break */
		  break;
		  }
	      if(cq->Keyword=="strike")
		  {
		  if(cq->lParam) {AttrOn(cq->attr,13);strcpy(cq->ObjType, "stkout");} /* Start strike out */
			    else {AttrOff(cq,13);strcpy(cq->ObjType, "~stkout");}     /* End strike out */
		  break;
		  }
	      if(cq->Keyword=="sub")
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) AttrOn(cq->attr,6);	/* Start subscript */
			    else AttrOff(cq,6);		/* End subscript */
		  break;
		  }
	      if(cq->Keyword=="super")
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) AttrOn(cq->attr,5);	/* Start superscript */
			    else AttrOff(cq,5);		/* End superscript */
		  break;
		  }
	      if(cq->Keyword=="tab")
		  {
		  fputc(' ',cq->strip);
		  break;
		  }
	      if(cq->Keyword=='u')
		  {
		  UnicodeSymbol(cq);
		  break;
		  }
	      if(cq->Keyword=="ul")
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) AttrOn(cq->attr,14);	/* Start underline */
			    else AttrOff(cq,14);	/* End underline */
		  break;
		  }
	      if(cq->Keyword=="uldb")
		  {
		  if(!cq->isParam) cq->lParam=1;
		  if(cq->lParam) AttrOn(cq->attr,11);	/* Start double underline */
			    else AttrOff(cq,11);	/* End double underline */
		  break;
		  }
	      if(cq->Keyword=="ulnone")
		  {
		  AttrOff(cq,14); AttrOff(cq,11);	/* End of all underlines */
		  break;
		  }
	      if(cq->Keyword=="xe")
		  {
		  IndexRTF(cq);
		  break;
		  }

	      *(cq->ObjType)='!';
	      ecTranslateKeyword(cq,cq->Keyword(), cq->lParam, cq->isParam); //keyword+arg

	      break;
     case 205:if((ec = ecParseChar(cq,cq->by)) != ecOK)	// character;
			return ec;
	      break;
     case 206:if((ec = ecParseChar(cq,cq->by)) != ecOK)	// HEX
			    return ec;
	      break;
     }


RTFnext:
	  if (cq->log != NULL)
    {   /**/
    //fprintf(cq->log," %d ",cq->rds);
    switch(KeywordType)
      {
      case 205: case 206:
	     if (cq->by >= ' ' && cq->by <= 'z')
		putc(cq->by, cq->log);
	     break;
      case 0: case 1: case 2: case 3:
	     fprintf(cq->log,"0x%2X ",cq->by);
		putc(cq->by, cq->log);
	     break;
      default:
	     if(!cq->Keyword.isEmpty())
		{
		char *Str;
		fprintf(cq->log, "\n%*s[\\%s",
			cq->cGroup+cq->recursion * 2,
			*cq->ObjType=='!'?"!":"",cq->Keyword());
		if(cq->isParam) fprintf(cq->log,"%ld",cq->lParam);
		if(*cq->ObjType)
		  {
		  Str=cq->ObjType;
		  if(*Str=='!') Str++;
		  if(*Str=='\\') Str++;
		  if(cq->Keyword != Str) fprintf(cq->log," %s",Str);
		  }
		fprintf(cq->log,"] ");
	fprintf(cq->log," rds=%d fSkipDestIfUnk=%d",cq->rds,cq->fSkipDestIfUnk);
	}
//	     else putc(cq->by, cq->log);
      }
    }

  cq->ActualPos = ftell(cq->wpd);
  return ecOK;
}


int TconvertedPass1_RTF::Convert_first_pass()
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_RTF() ");fflush(log);
#endif
DWORD fsize;

  if(Verbosing >= 1)
     printf(_("\n>>>RTF2LaTeX<<< Conversion program: From RTF to LaTeX Version %s\n"
	      "      Made by J.Fojtik  (Hosted on WP2LaTeX :))))\n\n"),
	    RTF_Ver);

  ColorTBL=NULL;
  nColorTBL=0;
  RGB.R=0;RGB.G=0;RGB.B=0;RGB.O=0;
  NewRGB=RGB;
  ForcedTranslator=0;

  Unicode=GetTranslator("unicodeTOinternal");

  DocumentStart = ftell(wpd);

  envir = ' ';
  psave = NULL;
  nomore_valid_tabs = false;


  fsize=filesize(wpd);
  perc.Init(ftell(wpd), fsize,_("First pass RTF:") );

  rownum = 0;

  ris=risNorm;
  rds=rdsNorm;
  cGroup=0;
  fSkipDestIfUnk=false;

  ActualPos = ftell(wpd);
  while (ActualPos < fsize)
      {
      if(Verbosing >= 1)		//actualise a procentage counter
	      perc.Actualise(ActualPos);
      by=0;

      if(ProcessKeyRTF(this) != ecOK) break;
      }

  if(err!=NULL)
    {
    if(cGroup < 0) fprintf(err,_("\nError: Stack Underflow while parsing rtf.\n"));
    if(cGroup > 0) fprintf(err,_("\nError: Unmatched brace while parsing rtf.\n"));
    }

  if(ColorTBL)
	{free(ColorTBL);ColorTBL=NULL;nColorTBL=0;};
  Finalise_Conversion(this);
  return(1);
}


////////////////////Unicode stuff///////////////////////

static void ProcessUnicode(TconvertedPass1 *cq, DWORD (*CharReader)(FILE *F), int Increment=0, const char *MESSAGE="UNICODE")
{
DWORD fsize;
DWORD WChar;

 if(CharReader==NULL) return;  //a serious problem occured

 cq->ConvertCpg=GetTranslator("unicodeTOinternal");

 fsize=filesize(cq->wpd);
 cq->ActualPos = ftell(cq->wpd);
 cq->perc.Init(cq->ActualPos, fsize, MESSAGE);

 if(cq->ConvertCpg==NULL || cq->ConvertCpg->number()==0)
   if(cq->err != NULL)
     {
     cq->perc.Hide();
     fprintf(cq->err,_("\nError: Cannot initialize unicode charset converter!"));
     }

 while(cq->ActualPos < fsize)
      {
      if(Verbosing >= 1)		//actualise a procentage counter
	      cq->perc.Actualise(cq->ActualPos);

      WChar=CharReader(cq->wpd);
      if(WChar==0xFFFFFFFF) break;
      if(Increment>0) cq->ActualPos+=Increment;
		 else cq->ActualPos=ftell(cq->wpd);

      if(WChar==0xD) {Terminate_Line(cq,'h');continue;}
      if(WChar==9) {fputc(' ',cq->strip);continue;}
      if(WChar<32) continue;
      if(WChar==32) {fputc(' ',cq->strip);continue;}
      CharacterStr(cq,Ext_chr_str(WChar, cq, cq->ConvertCpg));
      }

  Finalise_Conversion(cq);
}


class TconvertedPass1_Unicode: public TconvertedPass1
{
public:
	virtual int Convert_first_pass(void);
};

/*This is a separate converter for unicode texts*/
int TconvertedPass1_Unicode::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_unicode() ");fflush(log);
#endif

  DocumentStart=ftell(wpd);
  fseek(wpd, 2L, SEEK_CUR);		//skip unicode ID

  ProcessUnicode(this,w_fgetc,2,_("First pass unicode:"));
  return(1);
}

/*Register translators here*/
TconvertedPass1 *Factory_unicode(void) {return new TconvertedPass1_Unicode;}
FFormatTranslator FormatUNICODE("UNICODE",Factory_unicode);


class TconvertedPass1_HiEndUnicode: public TconvertedPass1
{
public:
	virtual int Convert_first_pass(void);
};


/*This is a separate converter for unicode High Endian texts*/
int TconvertedPass1_HiEndUnicode::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_HiEndUnicode() ");fflush(log);
#endif
  DocumentStart=ftell(wpd);
  fseek(wpd, 2L, SEEK_CUR);		//skip unicode ID

  ProcessUnicode(this,W_fgetc,2,_("First pass Big Endian unicode:"));
  return(1);
}


/*Register translators here*/
TconvertedPass1 *Factory_HiEndUnicode(void) {return new TconvertedPass1_HiEndUnicode;}
FFormatTranslator FormatHiEndUNICODE("HIENDUNICODE",Factory_HiEndUnicode);


class TconvertedPass1_UTF8: public TconvertedPass1
{
public:
	virtual int Convert_first_pass(void);
};


/*This is a separate converter for unicode UTF8 texts*/
int TconvertedPass1_UTF8::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_UTF8Unicode() ");fflush(log);
#endif
  DocumentStart=ftell(wpd);
  fseek(wpd, 3L, SEEK_CUR);		//skip unicode ID

  ProcessUnicode(this,utf8_fgetc,0,_("First pass UTF-8:"));
  return(1);
}

/*Register translators here*/
TconvertedPass1 *Factory_UTF8(void) {return new TconvertedPass1_UTF8;}
FFormatTranslator FormatUTF8("UTF8",Factory_UTF8);
