/******************************************************************************
 * 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.20"
// More info about RTF: https://www.biblioscape.com/rtf15_spec.htm
//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 "raster.h"

#include"wp2latex.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
    uint8_t 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;
       uint8_t 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();

       int ecApplyPropChange(int isym, int val);
       int ecTranslateKeyword(const char *szKeyword, int param, bool fParam);
       int ecParseChar(int ch);
       int ecChangeDest(IDEST idest);
       int ecParseSpecialKeyword(IPFN ipfn);
       void MakeLabelRTF(void);
       void SpecialObject(void);
       void fldinst(void);
       int ProcessKeyRTF(void);
       int RTFLoadKeyword(void);
       void HeaderFooterRTF(unsigned char HFtype);
       void IndexRTF(void);
       void ScanTableFormat(void);
       bool CellPeak(void);
       };
TconvertedPass1 *Factory_RTF(void) {return new TconvertedPass1_RTF;}
FFormatTranslator FormatRTF("RTF",Factory_RTF);


// 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;
uint8_t 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_.
int TconvertedPass1_RTF::ecApplyPropChange(int isym, int val)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::ecApplyPropChange() ");fflush(log);
#endif
char *pb;

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

  switch (rgsymRtf[isym].F.PropScope & 3)
    {
    case propDop: pb = (char *)&dop;
		  break;
    case propSep: pb = (char *)&sep;
		  break;
    case propPap: pb = (char *)&pap;
		  break;
    case propChp: pb = (char *)&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...
int TconvertedPass1_RTF::ecChangeDest(IDEST idest)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::ecChangeDest() ");fflush(log);
#endif
  if (rds >= rdsSkip)             // if we're skipping text,
        return ecOK;                // don't do anything

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


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

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

  switch (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(char_on_line==CHAR_PRESENT) NewLine(this);
			   break;
		   default:if(ch==0) return(ecOK); // unfortunately, we don't do a whole lot here as far as layout goes...
			   if(memcmp(&NewRGB,&RGB,sizeof(RGBQuad)))
				{
				RGB=NewRGB;
				Color(this,0,&RGB);
				}
			   {
			   const char * const CharCode = Ext_chr_str(ch,this,ConvertCpg);
			   CharacterStr(this,CharCode);
			   if(log)
			   {
			     if(ch>128 && CharCode!=NULL)
			     {
			       if(!strcmp(CharCode," -?- "))
			         fprintf(log,"Chr(%Xh)", ch);
			       else
			         fprintf(log,"(%s)", CharCode);
                             }
			   }
			   }
			   return(ecOK);
		   }
    default:      // Handle other destinations.
		  break;
    }
return ecOK;
}


/// Evaluate an RTF control that needs special processing.
int TconvertedPass1_RTF::ecParseSpecialKeyword(IPFN ipfn)
{
#ifdef DEBUG
  fprintf(log,"\n#ecParseSpecialKeyword() ");fflush(log);
#endif
    if (rds >= rdsSkip && ipfn != ipfnBin)  // if we're skipping, and it's not
        return ecOK;                        // the \bin keyword, ignore it.
    switch (ipfn)
    {
    case ipfnBin:
	  ris = risBin;
	  cbBin = lParam;
	  break;
    case ipfnSkipDest:
	  fSkipDestIfUnk = true;
	  break;
    case ipfnHex:
	  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.
int TconvertedPass1_RTF::ecTranslateKeyword(const char *szKeyword, int param, bool fParam)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::ecTranslateKeyword() ");fflush(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(fSkipDestIfUnk)         // if this is a new destination
	    {
	    rds = rdsSkip;          // skip the destination
	    }
				    // else just discard it
	fSkipDestIfUnk = false;
	return ecOK;
	}

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

  fSkipDestIfUnk = false;
  switch(rgsymRtf[isym].F.Kwd & 3)
    {
    case kwdProp:
	if (rgsymRtf[isym].F.ForceArg || !fParam)
	    param = rgsymRtf[isym].dflt;
	return ecApplyPropChange(isym,param);
    case kwdChar:
	return ecParseChar(rgsymRtf[isym].idx);
    case kwdDest:
	return ecChangeDest((IDEST)rgsymRtf[isym].idx);
    case kwdSpec:
	return ecParseSpecialKeyword((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
int TconvertedPass1_RTF::RTFLoadKeyword(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::RTFLoadKeyword() ");fflush(log);
#endif
int ch;

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

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

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

 if(ris != risHex) return(2);

 if (isdigit(ch)) by = (char) ch - '0';
 else {
     if (ch >= 'a' && ch <= 'f') by = (char) ch - 'a';
	 else if (ch >= 'A' && ch <= 'F') by = (char) ch - 'A';
	      else return(3);
     }
 if ((ch = getc(wpd)) == EOF) return(206);
 by = by << 4;
 if (isdigit(ch)) by |= (char) ch - '0';
 else {
      if (ch >= 'a' && ch <= 'f') by |= (char) ch - 'a';
	 else if (ch >= 'A' && ch <= 'F') by |= (char) ch - 'A';
	      else {
		   ungetc(ch, 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=cq->RTFLoadKeyword())
	   {
	   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);
}


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

state=0;
do {
   Str = RTFReadFirstWord(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(this,Ext_chr_str(pos,this,CpTrn));
	       state=0;
	       break;

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

   if(c=='{')
	{
	CurlyBraces++;
	c=fgetc(wpd);
	Str=c;
	continue;
	}
   if(c=='}')
	{
	state=0;
	if(CurlyBraces-->0)
	   {
	   c=fgetc(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 = cq->RTFLoadKeyword();
  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 = cq->RTFLoadKeyword();
	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: cq->ProcessKeyRTF();
		      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 = cq->RTFLoadKeyword();
	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 = cq->RTFLoadKeyword();
		  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;
		    }
		  cq->ProcessKeyRTF();
		  }
		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;
		}
	   }

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

  cq->recursion--;

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

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


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

  CurlyBaraceStack = 1;
  OldFlag = flag;

  Close_All_Attr(attr, strip);
  OldAttr = attr;
  OldEnvir = envir;
  OldFont = Font;
  attr.InitAttr();		//Turn all attributes in the header/footer off

// Start a conversion of a Header or Footer
  recursion++;

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

  if(OldEnvir=='L' || OldEnvir=='B') // || BkEnvir=='i')
  {
    envir = '!';		//Ignore enviroments after header/footer
    NewLine(this);
    envir = ' ';
  }
  //envir = ' ';

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

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

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

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

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

	     default: ProcessKeyRTF();
		      break;
	     }

	}
  ActualPos = ftell(wpd);

  Close_All_Attr(attr,strip);

  if(envir != ' ')
     {
     line_term = 's';    	//Soft return
     NewLine(this);
     }
  putc('}', strip);

  if(envir != ' ')
    {
    envir = ' ';
    NewLine(this);
    }
  if(OldEnvir=='L' || OldEnvir=='B') // || BkEnvir=='i')
  {
    line_term = 's';    	//Soft return
    envir = '^';		//Ignore enviroments Before
    NewLine(this);
    char_on_line = JUNK_CHARS;
  }

  recursion--;

  attr = OldAttr;
  flag = OldFlag;
  envir= OldEnvir;
  Font = OldFont;
  char_on_line = FIRST_CHAR_MINIPAGE;	// stronger false;

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


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

  CurlyBaraceStack=1;

  OldFlag = flag;
  flag = HeaderText;
  recursion++;

  Close_All_Attr(attr,strip);
  OldAttr=attr;
  attr.InitAttr();

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

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

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

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

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

	     default: ProcessKeyRTF();
		      break;
	     }

	   }

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

  attr=OldAttr;

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


/// This is intended to calculata amount of columns of the first row
void TconvertedPass1_RTF::ScanTableFormat(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ScanTableFormat() ");fflush(log);
#endif
  attribute OldAttr;
  int CurlyBaraceStack = 1;
  unsigned char OldFlag;
  long BackupPos;
  int cols = 0;
  char *FieldPos = NULL;

  OldFlag = flag;
  flag = Nothing;
  recursion++;

  BackupPos = ftell(wpd);
/*
  Close_All_Attr(attr,strip);
  OldAttr=attr;
  attr.InitAttr();

  ActualPos = ftell(wpd);
  fputs(" \\index{", strip);
*/

  FieldPos = (char*)calloc(100,1);
  if(FieldPos==NULL) goto SkipParsing;

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

    if(subby==202 || subby==203 || subby==204)
    {
      if(Keyword=="row" ||		// official end of one table row.
         Keyword=="par" || Keyword=="pard" ||
         Keyword=="intbl" || Keyword=="cell")
      {
        break;
      }

      if(cols<100)
      {
        if(Keyword=="trql")
        {
          FieldPos[cols] = 'l';
          continue;
        }
        if(Keyword=="trqr")
        {
          FieldPos[cols] = 'r';
          continue;
        }
        if(Keyword=="trqc")
        {
          FieldPos[cols] = 'c';
          continue;
        }
      }

      if(Keyword=="cellx")
      {
        cols++;
        continue;
      }
      continue;
    }
  }
/*
  ActualPos = ftell(wpd);
  Close_All_Attr(attr,strip);
  putc('}', strip);
  Index=true;

  attr=OldAttr;
*/
SkipParsing:
  fseek(wpd, BackupPos, SEEK_SET);

  recursion--;

  if(FieldPos)
  {
    if(envir=='b') envir='B';
    else if(cols>0 && envir!='B')
    {
      line_term = 's';   /* Soft return */
      if(char_on_line == LEAVE_ONE_EMPTY_LINE)  // Left one enpty line for new enviroment.
      {
        fputc('%', table);fputc('%', strip);
        NewLine(this);
      }
      if(char_on_line>=CHAR_PRESENT) //  if(char_on_line>=-1)
      {
        NewLine(this);
      }
      envir='!';
      fputc('%', table);fputc('%', strip);
      NewLine(this);

      envir = 'B';

      fprintf(strip, "{|");
      for(int i=0; i<cols; i++)
      {
        if(FieldPos[i]==' ' || FieldPos[i]==0)
            FieldPos[i] = 'l';
        fprintf(strip, "%c|", FieldPos[i]);
      }
      putc('}', strip);
      fputs(" \\hline ", strip);

      char_on_line = false;
      nomore_valid_tabs = false;
      rownum++;
      Make_tableentry_attr(this);
      latex_tabpos = 0;
    }

    free(FieldPos);FieldPos=NULL;
  }

  strcpy(ObjType, "trowd");
  flag = OldFlag;
}


bool TconvertedPass1_RTF::CellPeak(void)
{
bool ValidCell = true;
long BackupPos;
const unsigned char OldFlag = flag;
int CurlyBaraceStack = 1;

  if(toupper(envir)!='B') return false;
  flag = Nothing;
  BackupPos = ftell(wpd);
  recursion++;

  //printf("\n**START**");
  while(!feof(wpd) && CurlyBaraceStack>=0)
  {
    subby = RTFLoadKeyword();
    if(subby==200) CurlyBaraceStack++;
    if(subby==201) CurlyBaraceStack--;

    //if(subby==205) fputc(by,stdout);

    if(subby==202 || subby==203 || subby==204)
    {
      //printf("\\%s",Keyword());
      if(Keyword=="row" || Keyword=="trowd" ||
         Keyword=="par")
      {
        ValidCell = false;
        break;
      }
      if(Keyword=="cell") break;
    }
  }

  if(CurlyBaraceStack==0) ValidCell=false;
  //printf("**STOP_%c**", ValidCell?'T':'F');
  recursion--;
  fseek(wpd, BackupPos, SEEK_SET);
  flag = OldFlag;
return ValidCell;
}



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 = cq->RTFLoadKeyword();
	cq->ProcessKeyRTF();

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

	if(cq->cGroup>CurrentLevel) continue;

	if(cq->subby==205)	//regular char
	   {
	   //if(cq->log) fprintf(cq->log," pos=%Xh ",ftell(cq->wpd));
	   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
	}

}


void TconvertedPass1_RTF::MakeLabelRTF(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::MakeLabelRTF() ");fflush(log);
#endif
  string LBL_Text;
  char c;

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


static void UnicodeSymbol(TconvertedPass1_RTF *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#UnicodeSymbol() ");fflush(cq->log);
#endif
char dummy[10];
uint32_t 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)
	{
	snprintf(dummy,sizeof(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. */
void TconvertedPass1_RTF::SpecialObject(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::SpecialObject() ");fflush(log);
#endif
uint32_t FilePos;
string ObjectName,Type;
char c;

  if (rds == rdsSkip) return;

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


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

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

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

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

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

	ExtractHexObject(this);
	return;
	}

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


//printf("'%s:%s'   ",Type(),ObjectName());
/*
  if(Type=="objclass")
	{
	while(isspace(c)) c=fgetc(wpd);
	do {
	   if(c=='}' || c=='\\') {ungetc(c,wpd);break;}
	   if(c=='{')
	     {
	     fseek(wpd,FilePos,SEEK_SET);
	     ecParseSpecialKeyword(cq,ipfnSkipDest);
	     return;
	     }
	   ObjectName+=c;
	   if(feof(wpd))
	     {
	     ecParseSpecialKeyword(cq,ipfnSkipDest);
	     return;
	     }
	   c=fgetc(wpd);
	   } while(!isspace(c));
//	asm int 3;
	}*/
  ecParseSpecialKeyword(ipfnSkipDest);
  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.
int TconvertedPass1_RTF::ProcessKeyRTF(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_RTF::ProcessKeyRTF() ");fflush(log);
#endif
int ec;
int KeywordType;

  if (cGroup < 0) return ecStackUnderflow;

  if(by==0) KeywordType=RTFLoadKeyword();
	   else KeywordType=subby;

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

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

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

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

	      if(Keyword=="trowd")	// Mandatory row definition for every table
	      {
                ScanTableFormat();
	        break;
	      }

	      if(Keyword=="cell")
              {             
                if(CellPeak())
                {
                  if(char_on_line==NO_CHAR) char_on_line=JUNK_CHARS;
	          CellTable(this);
                }
                else
                  fputc(' ',strip);
                break;
              }
	      if(Keyword=="row")	// Mandatory row definition for every table
              {
                if(NO_CHAR==char_on_line)
                {
                  char_on_line = JUNK_CHARS;
                }
		RowTable(this);
                if(envir=='B') envir='b';
	        if(psave!=NULL)
                {
                  if(psave->pap.envir=='B')
                      psave->pap.envir = 'b';
                }
                break;
              }

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

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

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

  ActualPos = ftell(wpd);
  return ecOK;
}


int TconvertedPass1_RTF::Convert_first_pass()
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_RTF() ");fflush(log);
#endif
uint32_t 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() != 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, uint32_t (*CharReader)(FILE *F), int Increment=0, const char *MESSAGE="UNICODE")
{
uint32_t fsize;
uint32_t 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);
