/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    Convert Microsoft Word files into LaTeX			      *
 * modul:       pass1wrd.cc                                                   *
 * description: This module is a wrapper around WordView for Word 	      *
 *              documents 6.x, 7.x, and 8.x. It could be optionally compiled  *
 *              with WP2LaTeX package.					      *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <ctype.h>

#include <sets.h>
#include <lists.h>
#include <strings.h>

#ifdef __UNIX__
 #include <unistd.h>
#endif

#include "wp2latex.h"
#include "cole/cole.h"
#include "cp_lib/cptran.h"


#include "word/bintree.h"


extern string wpd_filename;

#include "word/wrd_type.h"
int wvGetSimpleParaBounds (int ver, PAPX_FKP * fkp, U32 * fcFirst,
	U32 * fcLim, U32 currentfc, BTE * bte, U32 * pos, int nobte, FILE * fd);

class TconvertedPass1_WORD:public TconvertedPass1
     {
public:
     COLEFS *OleFS;
     WORD ObjectCounter;
     BYTE CharType;	/* 0-unicode; 1-UTF8; 2-8bit something */
     COLEFILE *Table;
     COLEFILE *Data;
     BYTE Ver;
     CLX clx;
     FIB fib;
     STSH stsh;

     int intable;
     SWORD **vmerges;
     SWORD *cellbounds;
     WORD norows;
     int nocellbounds;

     SEP sep;
     SED *sed;
     DWORD *posSedx;
     DWORD section_intervals;
     DWORD section_fcLim;
     DWORD para_fcLim;
     int para_pendingclose;
     PAPX_FKP para_fkp;
     BTE *btePapx;
     DWORD *posPapx;
     DWORD para_intervals;
     DWORD para_fcFirst;
     DWORD char_fcFirst;
     DWORD section_fcFirst;
     DWORD char_fcLim;
     CHPX_FKP char_fkp;
     int char_pendingclose;
     DWORD *posChpx;
     BTE *bteChpx;
     DWORD char_intervals;
     CHP achp;
     PAP apap;
     DOP dop;
		//complex extension
     int cpiece;

     //int (*GetParaBounds) (int ver, PAPX_FKP * fkp, U32 * fcFirst,
     //		U32 * fcLim, U32 currentfc, BTE * bte, U32 * pos, int nobte, FILE * fd);

     virtual int Convert_first_pass(void);
     virtual int Dispatch(int FuncNo, ...);
     };

/*Register translators here*/
TconvertedPass1 *Factory_WORD(void) {return new TconvertedPass1_WORD;}
FFormatTranslator FormatWord("WORD",Factory_WORD);


/* --------- End of prototype definitions -----------*/

static char *wvAutoCharset(TconvertedPass1_WORD *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#wvAutoCharset() ");fflush(cq->log);
#endif
__u16 i = 0;
int flag;
char *ret;
ret = "iso-8859-15";

  /* If any of the pieces use unicode then we have to assume the worst and use utf-8 */
  while (i < cq->clx.nopcd)
      {
      wvNormFC(cq->clx.pcd[i].fc, &flag);
      if (flag == 0)
	  {
	  ret = "UTF-8";
	  break;
	  }
      i++;
      }

  /* Also if the document fib is not codepage 1252 we also have to assume the worst */
  if (strcmp (ret, "UTF-8"))
      {
      if( (cq->fib.lid != 0x407) && (cq->fib.lid != 0x807) &&
	  (cq->fib.lid != 0x409) && (cq->fib.lid != 0x807) &&
	  (cq->fib.lid != 0xC09))
	      ret = "UTF-8";
      }
  return (ret);
}


int cellCompEQ (void *a, void *b)
{
    int ret;
    SWORD *a2, *b2;
    a2 = (SWORD *) a;
    b2 = (SWORD *) b;
    ret = abs (*a2 - *b2);
    if (ret <= 3)
	ret = 1;
    else
	ret = 0;
    return (ret);
}

/* allow figures within 3 units of each other to be considered the same*/
int cellCompLT (void *a, void *b)
{
SWORD *a2, *b2;

    if (cellCompEQ (a, b))
	return (0);

    a2 = (SWORD *) a;
    b2 = (SWORD *) b;
    return (*a2 < (*b2) + 3);
}



/*
-------------------------
|          |            |
-------------------------
|   | | |    |     |    |
-------------------------
|                       |
-------------------------
|     |         |       |
-------------------------

==>

|   | | |  | |  |  |    |

As in this example we create a list of cell begin
positions which is a superset of all begin
positions in all rows, once we have this list we
restart at the top of the table and figure out
how many spans each cell has to achieve to match
back up to its original boundaries.

We will have to match boundaries that are with in
3 units of eachother to be the same boundary as
that occurs frequently in word tables, (gagh!)*/
static void wvSetTableInfo(TconvertedPass1_WORD * ps, TAP * ptap, int no)
{
    BintreeInfo tree;
    Node *testn, *testp;
    int i, j, k;

    if (ps->vmerges)
      {
//	  wvTrace (("vmerges is not NULL\n"));
	  for (i = 0; i < ps->norows; i++)
	      free(ps->vmerges[i]);
	  free(ps->vmerges);
	  ps->vmerges = NULL;
      }

    if (no == 0)
      {
//	  wvWarning ("Broken tables, continuing and hoping for the best\n");
	  ps->nocellbounds = 0;
	  return;
      }

    InitBintree (&tree, cellCompLT, cellCompEQ);

//    wvTrace (("we still ok, no is %d\n", no));

    for (i = 0; i < no; i++)
      {
	  for (j = 0; j < ptap[i].itcMac + 1; j++)
	    {
//		wvTrace (("%d\n", ptap[i].rgdxaCenter[j]));
		InsertNode (&tree, (void *) &(ptap[i].rgdxaCenter[j]));
	    }
      }
//    wvTrace (("end of in\n"));

    testn = NextNode (&tree, NULL);

    ps->nocellbounds = tree.no_in_tree;
    free(ps->cellbounds);
    if (tree.no_in_tree)
	ps->cellbounds = (SWORD *)malloc(sizeof (SWORD) * tree.no_in_tree);
    else
	ps->cellbounds = NULL;

    i = 0;
//    wvTrace (("No in tree is %d\n", tree.no_in_tree));
    while (testn != NULL)
      {
	  ps->cellbounds[i++] = *((SWORD *) testn->Data);
//	  wvTrace (("cellbound are %d\n", ps->cellbounds[i - 1]));
	  testp = NextNode (&tree, testn);
	  wvDeleteNode (&tree, testn);
	  testn = testp;
      }
//    wvTrace (("No in tree according to i is %d\n", i));

//    wvTrace (("end of out\n"));

    ps->vmerges = (SWORD **)malloc(sizeof (SWORD *) * no);
//    wvTrace (("no of rows is %d", no));
    for (i = 0; i < no; i++)
      {
	  ps->vmerges[i] = (SWORD *)malloc(sizeof (SWORD) * ptap[i].itcMac);
//	  wvTrace (("no of cells is %d", ptap[i].itcMac));
	  for (j = 0; j < ptap[i].itcMac; j++)
	      ps->vmerges[i][j] = 1;
      }

    for (i = no - 1; i > 0; i--)
      {
	  for (j = 0; j < ptap[i].itcMac; j++)
	    {
//		wvTrace (("Vertical merge is %d\n", ptap[i].rgtc[j].fVertMerge));
		if (ptap[i].rgtc[j].fVertMerge)
		  {
//		  wvTrace(("Vertical merge found, row %d, cell %d\n", i,j));
		      /*
			 find a cell above me with the same boundaries
			 if it is also merged increment it, and set myself to 0
			 else leave me alone
		       */
		      for (k = 0; k < ptap[i - 1].itcMac; k++)	/* the row above */
			{
//			    wvTrace(("cell begins are %d %d\n",ptap[i - 1].rgdxaCenter[k],ptap[i].rgdxaCenter[j]));
//			    wvTrace (("cell ends are %d %d\n",ptap[i - 1].rgdxaCenter[k + 1],ptap[i].rgdxaCenter[j + 1]));
			    if ((cellCompEQ
				 ((void *) &(ptap[i - 1].rgdxaCenter[k]),
				  (void *) &(ptap[i].rgdxaCenter[j])))
				&&
				(cellCompEQ
				 ((void *) &(ptap[i - 1].rgdxaCenter[k + 1]),
				  (void *) &(ptap[i].rgdxaCenter[j + 1]))))
			      {
//				  wvTrace (("found a cell above me, yippee\n"));
				  if (ptap[i - 1].rgtc[k].fVertMerge)
				    {
					ps->vmerges[i - 1][k] +=
					    ps->vmerges[i][j];
					ps->vmerges[i][j] = 0;
				    }
			      }

			}
		  }
	    }
      }


//    for (i = 0; i < no; i++)
//	for (j = 0; j < ptap[i].itcMac; j++)
//	    wvTrace (("rowspan numbers are %d\n", ps->vmerges[i][j]));
}


static void wvGetFullTableInit(TconvertedPass1_WORD * ps, U32 para_intervals, BTE * btePapx, U32 * posPapx)
{
PAPX_FKP para_fkp;
U32 para_fcFirst, para_fcLim = 0xffffffffL;
PAP apap;
U32 i, j = 0;
TAP *test = NULL;

    if(ps->intable) return;

    wvInitPAPX_FKP (&para_fkp);

    i = ftell(ps->wpd);
//    wvTrace (("TOP\n"));
    do
      {
	  wvReleasePAPX_FKP (&para_fkp);
	  wvGetSimpleParaBounds(ps->Ver, &para_fkp,
		  &para_fcFirst, &para_fcLim, i, btePapx,
		  posPapx, para_intervals, ps->wpd);
	  wvAssembleSimplePAP(ps->Ver, &apap,
		  para_fcLim, &para_fkp, &ps->stsh, ps->Data->file);
//	  wvTrace (("para from %x to %x\n", para_fcFirst, para_fcLim));
	  i = para_fcLim;

	  /* ignore the row end markers */
	  if (apap.ptap.itcMac)
	    {
		test = (TAP *) realloc (test, sizeof (TAP) * (j + 1));
		wvCopyTAP (&(test[j]), &apap.ptap);
//		wvTrace (("Row %d\n", j));
		j++;
	    }

      }
    while (apap.fInTable);
//  wvTrace (("BOTTOM\n"));

    wvReleasePAPX_FKP (&para_fkp);

    wvSetTableInfo(ps, test, j);
    ps->intable = 1;
    ps->norows = j;
    free(test);
}


static void wvGetRowTap (TconvertedPass1_WORD * ps, PAP * dpap, U32 para_intervals,
	 BTE * btePapx, U32 * posPapx)
{
PAPX_FKP para_fkp;
U32 para_fcFirst, para_fcLim = 0xffffffffL;
PAP apap;
DWORD i;

    wvCopyPAP (&apap, dpap);

    wvInitPAPX_FKP (&para_fkp);

    i = ftell(ps->wpd);
//    wvTrace (("RowTab begin\n"));
    do
      {
	  wvReleasePAPX_FKP (&para_fkp);
	  wvGetSimpleParaBounds(ps->Ver, &para_fkp,
			&para_fcFirst, &para_fcLim, i, btePapx,
			posPapx, para_intervals, ps->wpd);
//	  wvTrace (("2: para from %x to %x\n", para_fcFirst, para_fcLim));
	  wvAssembleSimplePAP(ps->Ver, &apap,
		       para_fcLim, &para_fkp, &ps->stsh, ps->Data->file);
	  i = para_fcLim;
      }
    while (apap.fTtp == 0);

//    wvTrace (("fTtp is %d\n", apap.fTtp));

    wvReleasePAPX_FKP (&para_fkp);
    wvCopyTAP (&(dpap->ptap), &apap.ptap);

//    for (i = 0; i < apap.ptap.itcMac + 1; i++)
//	wvTrace (("This Row-->%d\n", apap.ptap.rgdxaCenter[i]));
}

//--------End of TABLE processing----------



static int wvGetIntervalBounds (U32 * fcFirst, U32 * fcLim, U32 currentfc, U32 * rgfc,
		     U32 nopos)
{
U32 i = 0;
    while (i < nopos - 1)
      {
//	  wvTrace (("searching...%x %x %x\n", currentfc, wvNormFC (rgfc[i], NULL), wvNormFC (rgfc[i + 1], NULL)));
	  /*
	     if ( (wvNormFC(rgfc[i],NULL) >= currentfc) && (currentfc <= wvNormFC(rgfc[i+1],NULL)) )
	   */
	  if ((currentfc >= wvNormFC (rgfc[i], NULL))
	      && (currentfc < wvNormFC (rgfc[i + 1], NULL)))
	    {
		*fcFirst = wvNormFC (rgfc[i], NULL);
		*fcLim = wvNormFC (rgfc[i + 1], NULL);
		return (0);
	    }
	  i++;
      }
    *fcFirst = wvNormFC (rgfc[nopos - 2], NULL);
    *fcLim = wvNormFC (rgfc[nopos - 1], NULL);
//    wvTrace (("I'd rather not see this happen at all :-)\n"));
    return (0);
}



/*it is necessary to use the CP of the character to search the
plcfsed for the index i of the largest CP that is less than or equal to the
character's CP.
	plcfsed.rgcp[i] is the CP of the first character of the
section and plcfsed.rgcp[i+1] is the CP of the character following the
section mark that terminates the section (call it cpLim).
	Then retrieve plcfsed.rgsed[i]. The FC in this SED gives the location where the SEPX for
the section is stored.
	Then create a local SEP with default section properties. If the
sed.fc != 0xFFFFFFFF, then the sprms within the SEPX that is stored at offset
sed.fc must be applied to the local SEP. The process thus far has created a
SEP that describes what the section properties of the section at the last
full save.*/
static int wvGetSimpleSectionBounds(TconvertedPass1_WORD * ps,
		U32 * fcFirst, U32 * fcLim, U32 cp,
		SED * sed, U32 * spiece, U32 * posSedx,
		U32 section_intervals)
{
#ifdef DEBUG
  fprintf(ps->log,"\n#wvGetSimpleSectionBounds() ");fflush(ps->log);
#endif
U32 i = 0;
int ret = 0;
SEPX sepx;
long pos;
U32 cpTest = 0, j, dummy;

    if (section_intervals == 0)
      {
	  wvGetPieceBoundsFC (fcFirst, &dummy, &ps->clx, 0);
	  wvGetPieceBoundsFC (&dummy, fcLim, &ps->clx, ps->clx.nopcd);
	  return (0);
      }

    j = section_intervals - 1;
    if (cp == 0) j = 0;

    if(posSedx)
      {
      while (i < section_intervals)
        {
	//wvTrace (("searching for sep %d %d\n", posSedx[i], cp));
	if ((posSedx[i] <= cp) && (posSedx[i] > cpTest))
	    {
	    cpTest = posSedx[i];
	    j = i;
	    *spiece = wvGetPieceFromCP (cpTest, &ps->clx);
	    }
	i++;
        }

     //wvTrace (("found at %d %d\n", posSedx[j], posSedx[j + 1]));
     *fcFirst = wvConvertCPToFC (posSedx[j], &ps->clx);
     *fcLim = wvConvertCPToFC (posSedx[j + 1], &ps->clx);
     //wvTrace (("found at %x %x\n", *fcFirst, *fcLim));
     }
    else                        //bad posSedx
     {
     *fcFirst = 0;
     *fcLim = 0xFFFFFFFEl;
     }

    wvInitSEP(&ps->sep);

    if(sed)
     if (sed[j].fcSepx != 0xffffffffL)
	 {
         pos = ftell(ps->wpd);
	 fseek(ps->wpd, wvNormFC (sed[j].fcSepx, NULL), SEEK_SET);
	 wvGetSEPX(&sepx, ps->wpd);
	 fseek(ps->wpd, pos, SEEK_SET);

	 if(ps->Ver==8) ret = wvAddSEPXFromBucket(&ps->sep, &sepx, &ps->stsh);
		   else ret = wvAddSEPXFromBucket6(&ps->sep, &sepx, &ps->stsh);
	 wvReleaseSEPX (&sepx);
         }

  return (ret);
}


/* When a document is recorded in non-complex format, the bounds of the
paragraph that contains a particular character can be found by
1) calculating the FC coordinate of the character,
2) searching the bin table to find an FKP page that describes that FC,
3) fetching that FKP, and
4) then searching the FKP to find the interval in the rgfc that encloses the character.
5) The bounds of the interval are the fcFirst and fcLim of the containing paragraph.

Every character greater than or equal to fcFirst and less than fcLim is part of
the containing paragraph.*/
int wvGetSimpleParaBounds (int ver, PAPX_FKP * fkp, U32 * fcFirst,
	U32 * fcLim, U32 currentfc, BTE * bte, U32 * pos,
	int nobte, FILE * fd)
{
BTE entry;
long currentpos;

    /*
       currentfc = wvConvertCPToFC(currentcp,clx);
     */

//    wvTrace (("currentfc is %x\n", currentfc));
    if (currentfc == 0xffffffffL)
      {
      fprintf(stderr,_("Para Bounds not found!\n"));
      return (1);
      }

    if (0 != wvGetBTE_FromFC (&entry, currentfc, bte, pos, nobte))
      {
      fprintf(stderr,_("BTE not found!\n"));
      return (1);
      }
    currentpos = ftell(fd);
    /*The pagenumber of the FKP is entry.pn */

//    wvTrace (("pn is %d\n", entry.pn));
    wvGetPAPX_FKP (ver, fkp, entry.pn, fd);
//    wvTrace (("last entry is %x\n", fkp->rgfc[fkp->crun]));
    while (fkp->rgfc[fkp->crun] <= currentfc)
      {
	  if ((fkp->rgfc[fkp->crun] == currentfc) && (currentfc == pos[nobte]))
	      break;

	  /* Bad things man... */
	  fprintf(stderr,_("\nAlert, insane repeat \"insane\" paragraph structure,"
			   "making wild stab in the dark to attempt to continue."));
	  wvReleasePAPX_FKP (fkp);
	  entry.pn++;
	  wvGetPAPX_FKP (ver, fkp, entry.pn, fd);
      }

    fseek(fd, currentpos, SEEK_SET);

    return (wvGetIntervalBounds
	    (fcFirst, fcLim, currentfc, fkp->rgfc, fkp->crun + 1));
}



int wvGetSimpleCharBounds(CHPX_FKP * fkp, U32 * fcFirst,
	       U32 * fcLim, U32 currentcp, CLX * clx, BTE * bte,
	       U32 * pos, int nobte, FILE * fd)
{
U32 currentfc;
BTE entry;
long currentpos;

    currentfc = wvConvertCPToFC (currentcp, clx);

    if (currentfc == 0xffffffffL)
      {
      fprintf(stderr,_("Char Bounds not found !\n"));
      return(1);
      }

//  wvTrace (("char fc is %x\n", currentfc));
    if (0 != wvGetBTE_FromFC (&entry, currentfc, bte, pos, nobte))
      {
      fprintf(stderr,_("\nBTE not found !"));
      return (1);
      }
    currentpos = ftell(fd);
    /*The pagenumber of the FKP is entry.pn */

//    wvTrace (("pn is %d\n", entry.pn));
    wvGetCHPX_FKP(fkp, entry.pn, fd);

    while (fkp->rgfc[fkp->crun] <= currentfc)
      {
	  if ((fkp->rgfc[fkp->crun] == currentfc) && (currentfc == pos[nobte]))
	      break;

	  /* Bad things man... */
	  fprintf(stderr,_("Alert, insane repeat \"insane\" character run structure,"    "making wild stab in the dark to attempt to continue\n"));
	  wvReleaseCHPX_FKP (fkp);
	  entry.pn++;
	  wvGetCHPX_FKP(fkp, entry.pn, fd);
      }

    fseek(fd, currentpos, SEEK_SET);

  return(wvGetIntervalBounds(fcFirst, fcLim, currentfc, fkp->rgfc, fkp->crun + 1));
}



static void EmbeddedObj(TconvertedPass1_WORD *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#EmbeddedObj() ");fflush(cq->log);
  //if(heapcheck()<0) return;
#endif
string Operator,Object;
char by;
int i;
COLEDIR *Dir;
COLEDIRENT *cde;

// return;

 while(!feof(cq->wpd))
    {
    by=fgetc(cq->wpd);
    if(by==0x14) goto Readed;   //field seperator
    if(by==0x15) return;	//field end
    if(isspace(by)) break;
    Operator+=by;
    }
 while(isspace(by) && !feof(cq->wpd))
   {
   by=fgetc(cq->wpd);
   }
 Object=by;
 while(!feof(cq->wpd))
    {
    by=fgetc(cq->wpd);
    if(by==0x14) goto Readed;	//field seperator
    if(by==0x15) return;	//field end
    Object+=by;
    }

Readed:
 if(cq->log)
   {
   fprintf(cq->log,"\n%s %s ",Operator(),Object());
   }

 Dir=cole_diropen(cq->OleFS,"ObjectPool",NULL);
 if(Dir)
    {
    for(i=0,cde=cole_visiteddirentry(Dir); cde!=NULL; cde=cole_nextdirentry(Dir))
	{
	if(cole_direntry_isdir(cde))
	   {
	   if(i==cq->ObjectCounter)
	      {
	      COLEFILE *ColF;
	      COLEDIR *DirObj;
	      TconvertedPass1 *cq1_new;

	      DirObj=cole_opendir_direntry(cde,NULL);
	      if(DirObj)
		{
		cole_fprint_direntry(cde, cq->log);
		ColF=cole_fopen_dir(DirObj, "Equation Native",strdup("eq.mtf"), NULL);
		if(ColF)
		  {
		  cq1_new=NULL;
		  CheckFileFormat(ColF->file,FilForD);
		  cq1_new=GetConvertor(FilForD.Convertor);
		  if(cq1_new!=NULL)
		    {
		    cq->perc.Hide();
		    cq1_new->InitMe(ColF->file,cq->table,cq->strip,cq->log,cq->err);
		    cq1_new->Dispatch(DISP_WRAPPER,cq->OleFS);
		    cq1_new->Convert_first_pass();
		    cq->perc.Show();
		    delete cq1_new;
		    }
		  cole_fclose(ColF,NULL);
		  }
		cole_closedir(DirObj);
		}
	      break;
	      }
	   i++;
	   }
	}
    cole_closedir(Dir);
    }

 fprintf(cq->strip,"%% %s %s ",Operator(),Object());

 cq->line_term = 's';    	//Soft return
 Make_tableentry_envir_extra_end(cq);
 fprintf(cq->strip, "\n");
 cq->rownum++;
 Make_tableentry_attr(cq);

 cq->CharType|=0x80;
 cq->ObjectCounter++;
}


/*This is main procedure for processing one key. It is recursivelly called.*/
static void ProcessKeyWord(TconvertedPass1_WORD *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ProcessKeyWord() ");fflush(cq->log);
#endif
WORD Wby;

  if (cq->by == 0)
    {
    switch(cq->CharType)
	{
	case 0:Rd_word(cq->wpd,&Wby);
	       cq->by=Wby;
	       if(Wby>255) cq->by=255;
	       break;
	case 1:Wby=utf8_fgetc(cq->wpd);
	       cq->by=Wby;
	       if(Wby>255) cq->by=255;
	       break;
	default:cq->by=Wby=fgetc(cq->wpd);
    }
    }
  else Wby=cq->by;

  *cq->ObjType = '\0';
  cq->subby=0;

  switch(cq->by)
    {
    case 11: HardReturn(cq);	break;		//hard line break
    case 12: HardPage(cq);	break;		//hard page break or section mark (table)
    case 13: HardReturn(cq);	break;		//end of paragraph
//  case 14: 					//column break
    case 19: EmbeddedObj(cq);	break;
    case 31: SoftHyphen(cq);	break;		//non required hyphen
    case 32: fputc(' ',cq->strip); break;	//normal space

    case 160:fputc('~',cq->strip); break;	//non breaking space

    default: cq->RequiredFont = FONT_NORMAL;	/* Normal_char */
	    CharacterStr(cq,Ext_chr_str(Wby, cq, cq->ConvertCpg));
	    break;
    }

  if(cq->log != NULL)
	{
	if(*cq->ObjType != '\0')
		{
		fprintf(cq->log, "\n[%s] ", cq->ObjType);
		}
	else if(cq->by >= ' ')
		{
		fputc(cq->by,cq->log);
		}
	}

  switch(cq->CharType)
    {
    case 0:cq->ActualPos+=2;break;
    case 1:cq->ActualPos=ftell(cq->wpd);break;
    case 0x80:
    case 0x81:cq->ActualPos=ftell(cq->wpd);break;
    default:cq->ActualPos++;	//Only one byte read - simple guess of new position
    }
  cq->CharType&=0x7F;
}


static void ApplyCHPChange(TconvertedPass1_WORD *cq)
{
  if(cq->achp.fBold) Attr_ON(cq,aBold);
		else AttrOff(cq,aBold);
  if(cq->achp.fItalic) Attr_ON(cq,aItalic);
		  else AttrOff(cq,aItalic);
  if(cq->achp.kul) Attr_ON(cq,aUnderline);
	      else AttrOff(cq,aUnderline);
  if(cq->achp.fSmallCaps) Attr_ON(cq,aSmallCaps);
	      else AttrOff(cq,aSmallCaps);
  if(cq->achp.fStrike) Attr_ON(cq,aStrikeOut);
	      else AttrOff(cq,aStrikeOut);
  if(cq->achp.fShadow) Attr_ON(cq,aShadow);
	      else AttrOff(cq,aShadow);
  if(cq->achp.fOutline) Attr_ON(cq,aOutline);
	      else AttrOff(cq,aOutline);
}

static void PieceLoopSimple(TconvertedPass1_WORD *cq,DWORD beginfc, DWORD endfc, int piececount)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#PieceLoopSimple(%d) ",piececount);fflush(cq->log);
#endif
DWORD i, spiece;
//int section_dirty = 0;
//int para_dirty=0;
DWORD begincp, endcp;

	/*lvm007@aha.ru fix antiloop fix*/
 if(piececount>=0)
    if(wvGetPieceBoundsCP (&begincp, &endcp, &cq->clx, piececount)==-1)
      piececount=-1;
 i = begincp;

 fseek(cq->wpd,beginfc,SEEK_SET);

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

   if(cq->ActualPos==cq->char_fcLim) cq->char_pendingclose = 0;
   if(cq->ActualPos==cq->para_fcLim) cq->para_pendingclose = 0;

   if(piececount>=0)
     {
     if((cq->section_fcLim==0xffffffff) || (cq->section_fcLim == cq->ActualPos))
	{
	//wvTrace (("j i is %x %d\n", cq->ActualPos, i));
	//section_dirty =
	wvGetSimpleSectionBounds(cq, &cq->section_fcFirst,
		&cq->section_fcLim, i, cq->sed, &spiece,
		cq->posSedx, cq->section_intervals);
	if(cq->log)
	    fprintf(cq->log,("\nSection begins at %x ends %x "),cq->section_fcFirst, cq->section_fcLim);
	}

     if((cq->para_fcLim==0xffffffff) || (cq->para_fcLim==cq->ActualPos))
	{
	wvReleasePAPX_FKP(&cq->para_fkp);
	wvGetSimpleParaBounds(cq->Ver,
		 &cq->para_fkp, &cq->para_fcFirst, &cq->para_fcLim,
		 wvConvertCPToFC(i, &cq->clx),
		 cq->btePapx, cq->posPapx, cq->para_intervals, cq->wpd);
	if(cq->log)
	   fprintf(cq->log,_("\nPara from %lXh to %lXh, j is %lXh "), cq->para_fcFirst, cq->para_fcLim, cq->ActualPos);
	if (0 == cq->para_pendingclose)
	  {  /* if there's no paragraph open, but there should be then I believe that the fcFirst search
		has failed me, so I set it to now. I need to investigate this further. I believe it occurs
		when a the last piece ended simultaneously with the last paragraph, and that the algorithm
		for finding the beginning of a para breaks under that condition. I need more examples to
		be sure, but it happens is very large complex files so its hard to find	*/
	  if(cq->ActualPos != cq->para_fcFirst)
		{
//		if(cq->err)
//		  fprintf(cq->err,_("\nThere is no paragraph due to open but one should be, plugging the gap."));
		cq->para_fcFirst = cq->ActualPos;
		}
	  }
	}

	if(cq->ActualPos==cq->para_fcFirst)
	  {
	  if(cq->Data)  //para_dirty=
		wvAssembleSimplePAP(cq->Ver, &cq->apap, cq->para_fcLim, &cq->para_fkp,
					 &cq->stsh, cq->Data->file);
	  else //para_dirty=
		wvAssembleSimplePAP(cq->Ver, &cq->apap, cq->para_fcLim, &cq->para_fkp,
					 &cq->stsh, NULL);
	  if ((cq->apap.fInTable) && (!cq->apap.fTtp))
		{
		wvGetFullTableInit(cq, cq->para_intervals, cq->btePapx, cq->posPapx);
		wvGetRowTap(cq, &cq->apap, cq->para_intervals, cq->btePapx, cq->posPapx);
		}
	  else if (cq->apap.fInTable == 0)
		cq->intable = 0;

	  //wvHandleElement (ps, PARABEGIN, (void *) &apap, para_dirty);
	  switch(cq->apap.jc)
		{
		case 0:Justification(cq,0x80); break;	//left
		case 1:Justification(cq,0x82); break;	//center
		case 2:Justification(cq,0x83); break;	//right
		default:Justification(cq,0x81); break;	//full
		}

	  cq->char_fcLim = cq->ActualPos;
	  cq->para_pendingclose = 1;
	  }


	if((cq->char_fcLim == 0xffffffff) || (cq->char_fcLim == cq->ActualPos))
	    {
	    //wvTrace (("j i is %x %d\n", j, i));
	    wvReleaseCHPX_FKP (&cq->char_fkp);
	    wvGetSimpleCharBounds(&cq->char_fkp,
				  &cq->char_fcFirst, &cq->char_fcLim, i,
				  &cq->clx, cq->bteChpx, cq->posChpx,
				  cq->char_intervals, cq->wpd);
	    if(cq->log)
		fprintf(cq->log,_("\nChar begins at %lXh ends %lXh, pos is %lXh "), cq->char_fcFirst, cq->char_fcLim, cq->ActualPos);
	    if (0 == cq->char_pendingclose)
		{  /* if there's no character run open, but there should be then I believe that the fcFirst search
		      has failed me, so I set it to now. I need to investigate this further. */
		if (cq->ActualPos != cq->char_fcFirst)
		      {
		      //wvWarning (("There is no character run due to open but one should be, plugging the gap.\n"));
		      cq->char_fcFirst = cq->ActualPos;
		      }
		}
	    else{	/* lvm007@aha.ru fix: if currentfc>fcFirst but CHARPROP's changed look examples/charprops.doc*/
		if(cq->char_fcFirst< cq->ActualPos)
				cq->char_fcFirst = cq->ActualPos;
		}
	    }

	    if (cq->ActualPos == cq->char_fcFirst)
	       {        /* a CHP's base style is in the para style */
//	       wvTrace (("assembling CHP...\n"));
	       cq->achp.istd = cq->apap.istd;
	       if(wvAssembleSimpleCHP(cq->Ver, &cq->achp, cq->char_fcLim, &cq->char_fkp, &cq->stsh))
		 {	/* CHP is dirty */
		 ApplyCHPChange(cq);
		 }
	       // wvTrace (("CHP assembled.\n font is %d\n", achp.ftcAscii));      wvTrace (("char spec is %d\n", achp.ftcSym)); //wvTrace (("char lid is %x\n", achp.lidDefault));
	       cq->char_pendingclose = 1;
	       }
     }
   cq->by = 0;
   ProcessKeyWord(cq);
   i++;
   }
}


/*The process thus far has created a SEP that describes what the section properties of
 the section at the last full save.
 1) Now apply any section sprms that were linked to the piece that contains the 
    section's section mark.
 2) If pcd.prm.fComplex is 0, pcd.prm contains 1 sprm which should be applied to 
    the local SEP if it is a section sprm.
 3) If pcd.prm.fComplex is 1, pcd.prm.igrpprl is the index of a grpprl in the CLX. 
    If that grpprl contains any section sprms, they should be applied to the local SEP */
static int wvGetComplexSEP(int ver, SEP * sep, __u32 cpiece, STSH * stsh, CLX * clx)
{
int ret = 0;
__u16 sprm, pos = 0, i = 0;
__u8 *pointer;
__u16 index;
__u8 val;
Sprm RetSprm;

    if (clx->pcd[cpiece].prm.fComplex == 0)
      {
	  val = clx->pcd[cpiece].prm.para.var1.val;
	  pointer = &val;
#ifdef SPRMTEST
	  wvError (("singleton\n", clx->pcd[cpiece].prm.para.var1.isprm));
#endif
	  RetSprm =
	      wvApplySprmFromBucket (ver,
				     (U16) wvGetrgsprmPrm ( (U16) clx->pcd[cpiece].prm.
						     para.var1.isprm), NULL,
				     NULL, sep, stsh, pointer, &pos, NULL);
	  if (RetSprm.sgc == sgcSep)
	      ret = 1;
      }
    else
      {
	  index = clx->pcd[cpiece].prm.para.var2.igrpprl;
#ifdef SPRMTEST
	  fprintf (stderr, "\n");
	  while (i < clx->cbGrpprl[index])
	    {
		fprintf (stderr, "%x (%d)\n", *(clx->grpprl[index] + i),
			 *(clx->grpprl[index] + i));
		i++;
	    }
	  fprintf (stderr, "\n");
	  i = 0;
#endif
	  while (i < clx->cbGrpprl[index])
	    {
		if (ver == 8)
		    sprm = bread_16ubit (clx->grpprl[index] + i, &i);
		else
		  {
		      sprm = bread_8ubit (clx->grpprl[index] + i, &i);
		      sprm = (U8) wvGetrgsprmWord6 ( (U8) sprm);
		  }
		pointer = clx->grpprl[index] + i;
		RetSprm =
		    wvApplySprmFromBucket (ver, sprm, NULL, NULL, sep, stsh,
					   pointer, &i, NULL);
		if (RetSprm.sgc == sgcSep)
		    ret = 1;
	    }
      }
    return (ret);
}


static int
  wvGetComplexParafcFirst (int ver, U32 * fcFirst, U32 currentfc,
			 CLX * clx, BTE * bte, U32 * pos, int nobte,
			 U32 piece, PAPX_FKP * fkp, FILE * fd)
{
    U32 fcTest, endfc;
    BTE entry;
    fcTest = wvSearchNextLargestFCPAPX_FKP (fkp, currentfc);

//    wvTrace (("fcTest (s) is %x\n", fcTest));

    if (wvQuerySamePiece (fcTest - 1, clx, piece))
      {
//	  wvTrace (("same piece\n"));
	  *fcFirst = fcTest - 1;
      }
    else
      {
	  /*
	     get end fc of previous piece ??, or use the end of the current piece
	   */
	  piece--;
	  while (piece != 0xffffffffL)
	    {
//		wvTrace (("piece is %d\n", piece));
		endfc = wvGetEndFCPiece (piece, clx);
//		wvTrace (("endfc is %x\n", endfc));
		if (0 != wvGetBTE_FromFC (&entry, endfc, bte, pos, nobte))
		  {
//		      wvError (("BTE not found !\n"));
		      return (-1);
		  }
		wvReleasePAPX_FKP (fkp);
		wvGetPAPX_FKP (ver, fkp, entry.pn, fd);
		fcTest = wvSearchNextLargestFCPAPX_FKP (fkp, endfc);
//		wvTrace (("fcTest(ft) is %x\n", fcTest));
		if (wvQuerySamePiece (fcTest - 1, clx, piece))
		  {
		      *fcFirst = fcTest - 1;
		      break;
		  }
		piece--;
	    }

      }
    if (piece == 0xffffffffL)
      {
//	  wvTrace(("failed to find a solution to the beginning of the paragraph\n"));
	  *fcFirst = currentfc;
      }
//    wvTrace (("fcFirst is finally %x\n", *fcFirst));
    return (0);
}


static int wvGetComplexParafcLim (int ver, U32 * fcLim, U32 currentfc, CLX * clx,
	BTE * bte, U32 * pos, int nobte, U32 piece, PAPX_FKP * fkp, FILE * fd)
{
U32 fcTest, beginfc;
BTE entry;

    *fcLim = 0xffffffffL;
    //wvTrace (("here is fcLim, currentfc is %x\n", currentfc));
    fcTest = wvSearchNextSmallestFCPAPX_FKP (fkp, currentfc);

//    wvTrace (("fcTest is %x, end is %x\n", fcTest, wvGetEndFCPiece (piece, clx)));


    if (fcTest <= wvGetEndFCPiece (piece, clx))
      {
	  *fcLim = fcTest;
      }
    else
      {
	  /*get end fc of previous piece */
	  piece++;
	  while (piece < clx->nopcd)
	    {
//		wvTrace (("piece is %d\n", piece));
		beginfc = wvNormFC (clx->pcd[piece].fc, NULL);
		if (0 != wvGetBTE_FromFC (&entry, beginfc, bte, pos, nobte))
		  {
//		      wvError (("BTE not found !\n"));
		      return (-1);
		  }
		wvReleasePAPX_FKP (fkp);
		wvGetPAPX_FKP (ver, fkp, entry.pn, fd);
		fcTest = wvSearchNextSmallestFCPAPX_FKP (fkp, beginfc);
//		wvTrace ( ("fcTest(t) is %x, end is %x\n", fcTest, wvGetEndFCPiece (piece, clx)));
		if (fcTest <= wvGetEndFCPiece (piece, clx))
		  {
		      *fcLim = fcTest;
		      break;
		  }
		piece++;
	    }
      }
//    wvTrace (("fcLim is %x\n", *fcLim));
    if (piece == clx->nopcd)
      {
//	  wvTrace (("failed to find a solution to end of paragraph\n"));
	  *fcLim = fcTest;
	  return (clx->nopcd - 1);	/* test using this */
      }
    return (piece);
}



/* To find the beginning of the paragraph containing a character in a complex
document, it's first necessary to:
1) search for the piece containing the character in the piece table.
2) Then calculate the FC in the file that stores the character from the piece
	table information.
3) Using the FC, search the FCs FKP for the largest FC less than the character's
	FC, call it fcTest.
4) If the character at fcTest-1 is contained in the current piece, then the
	character corresponding to that FC in the piece is the first character of
	the paragraph.
5) If that FC is before or marks the beginning of the piece, scan a piece at a
time towards the beginning of the piece table until a piece is found that
contains a paragraph mark.
   (This can be done by using the end of the piece FC, finding the largest FC in
its FKP that is less than or equal to the end of piece FC, and checking to see
if the character in front of the FKP FC (which must mark a paragraph end) is
within the piece.)
6) When such an FKP FC is found, the FC marks the first byte of paragraph text.
*/

/* To find the end of a paragraph for a character in a complex format file,
again:
1) it is necessary to know the piece that contains the character and the
FC assigned to the character.
2) Using the FC of the character, first search the FKP that describes the
character to find the smallest FC in the rgfc that is larger than the character
FC.
3) If the FC found in the FKP is less than or equal to the limit FC of the
piece, the end of the paragraph that contains the character is at the FKP FC
minus 1.
4) If the FKP FC that was found was greater than the FC of the end of the
piece, scan piece by piece toward the end of the document until a piece is
found that contains a paragraph end mark.
5) It's possible to check if a piece contains a paragraph mark by using the
FC of the beginning of the piece to search in the FKPs for the smallest FC in
the FKP rgfc that is greater than the FC of the beginning of the piece.
If the FC found is less than or equal to the limit FC of the
piece, then the character that ends the paragraph is the character
immediately before the FKP FC.
*/
static int
 wvGetComplexParaBounds (int ver, PAPX_FKP * fkp, U32 * fcFirst,
			U32 * fcLim, U32 currentfc, CLX * clx, BTE * bte,
			U32 * pos, int nobte, U32 piece, FILE * fd)
{
    /*
       U32 currentfc;
     */
    BTE entry;
    long currentpos;

    if (currentfc == 0xffffffffL)
      {
	  //wvError (("Para Bounds not found !, this is ok if this is the last para, otherwise its a disaster\n"));
	  return (-1);
      }

    if (0 != wvGetBTE_FromFC (&entry, currentfc, bte, pos, nobte))
      {
	  //wvError (("BTE not found !\n"));
	  return (-1);
      }
    currentpos = ftell(fd);
    /*The pagenumber of the FKP is entry.pn */

//    wvTrace (("the entry.pn is %d\n", entry.pn));
    wvGetPAPX_FKP (ver, fkp, entry.pn, fd);

    wvGetComplexParafcFirst (ver, fcFirst, currentfc, clx, bte, pos, nobte,
			     piece, fkp, fd);

    wvReleasePAPX_FKP (fkp);
//    wvTrace (("BREAK\n"));
    wvGetPAPX_FKP (ver, fkp, entry.pn, fd);

    piece =
	wvGetComplexParafcLim (ver, fcLim, currentfc, clx, bte, pos, nobte,
			       piece, fkp, fd);

    fseek(fd, currentpos, SEEK_SET);
    return (piece);
}



/* The process thus far has created a PAP that describes
what the paragraph properties of the paragraph were at the last full save.
1) Now it's necessary to apply any paragraph sprms that were linked to the
   piece that contains the paragraph's paragraph mark.
2) If pcd.prm.fComplex is 0, pcd.prm contains 1 sprm which should only be
   applied to the local PAP if it is a paragraph sprm.
3) If pcd.prm.fComplex is 1, pcd.prm.igrpprl is the index of a grpprl in the
   CLX.  If that grpprl contains any paragraph sprms, they should be applied to
   the local PAP.*/
static int wvAssembleComplexPAP(int ver, PAP * apap, U32 cpiece, STSH * stsh,
		      CLX * clx, FILE * data)
{
    int ret = 0;
    U16 sprm, pos = 0, i = 0;
    U8 sprm8;
    U8 *pointer;
    U16 index;
    U8 val;
    Sprm RetSprm;

    if (clx->pcd[cpiece].prm.fComplex == 0)
      {
	  val = clx->pcd[cpiece].prm.para.var1.val;
	  pointer = &val;
#ifdef SPRMTEST
	  wvError (("singleton\n", clx->pcd[cpiece].prm.para.var1.isprm));
#endif
	  RetSprm =
	      wvApplySprmFromBucket (ver,
				     (U16) wvGetrgsprmPrm ( (U16) clx->pcd[cpiece].prm.
						     para.var1.isprm), apap,
				     NULL, NULL, stsh, pointer, &pos, data);
	  if (RetSprm.sgc == sgcPara)
	      ret = 1;
      }
    else
      {
	  index = clx->pcd[cpiece].prm.para.var2.igrpprl;
#ifdef SPRMTEST
	  wvError (("HERE-->\n"));
	  fprintf (stderr, "\n");
	  for (i = 0; i < clx->cbGrpprl[index]; i++)
	      fprintf (stderr, "%x ", *(clx->grpprl[index] + i));
	  fprintf (stderr, "\n");
	  i = 0;
#endif
	  while (i < clx->cbGrpprl[index])
	    {
		if (ver == 8)
		    sprm = bread_16ubit (clx->grpprl[index] + i, &i);
		else
		  {
		      sprm8 = bread_8ubit (clx->grpprl[index] + i, &i);
		      sprm = (U16) wvGetrgsprmWord6 (sprm8);
//		      wvTrace (("sprm is %x\n", sprm));
		  }
		pointer = clx->grpprl[index] + i;
		RetSprm =
		    wvApplySprmFromBucket (ver, sprm, apap, NULL, NULL, stsh,
					   pointer, &i, data);
		if (RetSprm.sgc == sgcPara)
		    ret = 1;
	    }
      }
    return (ret);
}


static void PieceLoopComplex(TconvertedPass1_WORD *cq,DWORD beginfc, DWORD endfc, int piececount)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#PieceLoopComplex(%d) ",piececount);fflush(cq->log);
#endif
DWORD i, spiece;
//int section_dirty = 0;
//int para_dirty=0;
DWORD begincp, endcp;

 spiece=0;
	/*lvm007@aha.ru fix antiloop fix*/
 if(piececount>=0)
    if(wvGetPieceBoundsCP (&begincp, &endcp, &cq->clx, piececount)==-1)
      piececount=-1;
 i = begincp;

 fseek(cq->wpd,beginfc,SEEK_SET);

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

   if(cq->ActualPos==cq->char_fcLim) cq->char_pendingclose = 0;
   if(cq->ActualPos==cq->para_fcLim) cq->para_pendingclose = 0;

   if(piececount>=0)
     {
     if((cq->section_fcLim==0xffffffff) || (cq->section_fcLim == cq->ActualPos))
	{
	//wvTrace (("j i is %x %d\n", cq->ActualPos, i));
	//section_dirty =
	wvGetSimpleSectionBounds(cq, &cq->section_fcFirst,
		&cq->section_fcLim, i, cq->sed, &spiece,
		cq->posSedx, cq->section_intervals);
	//section_dirty =
		wvGetComplexSEP(cq->Ver, &cq->sep, spiece,
			    &cq->stsh, &cq->clx); //? 1 : section_dirty);
	if(cq->log)
	    fprintf(cq->log,("\nSection begins at %x ends %x "),cq->section_fcFirst, cq->section_fcLim);
	}

     if((cq->para_fcLim==0xffffffff) || (cq->para_fcLim==cq->ActualPos))
	{
	wvReleasePAPX_FKP (&para_fkp);
	//wvTrace(("cp and fc are %x(%d) %x\n", i, i,wvConvertCPToFC (i, &ps->clx)));
	cq->cpiece =
	  wvGetComplexParaBounds(cq->Ver, &cq->para_fkp,
			 &cq->para_fcFirst, &cq->para_fcLim,
			 wvConvertCPToFC(i, &cq->clx), &cq->clx,
			 cq->btePapx, cq->posPapx, cq->para_intervals, piececount,
			 cq->wpd);
	if(cq->log)
	   fprintf(cq->log,_("\nPara from %lXh to %lXh, j is %lXh "), cq->para_fcFirst, cq->para_fcLim, cq->ActualPos);
	if (0 == cq->para_pendingclose)
	  {  /* if there's no paragraph open, but there should be then I believe that the fcFirst search
		has failed me, so I set it to now. I need to investigate this further. I believe it occurs
		when a the last piece ended simultaneously with the last paragraph, and that the algorithm
		for finding the beginning of a para breaks under that condition. I need more examples to
		be sure, but it happens is very large complex files so its hard to find	*/
	  if(cq->ActualPos != cq->para_fcFirst)
		{
//		if(cq->err)
//		  fprintf(cq->err,_("\nThere is no paragraph due to open but one should be, plugging the gap."));
		cq->para_fcFirst = cq->ActualPos;
		}
	  }
	}

	if(cq->ActualPos==cq->para_fcFirst)
	  {
	  if(cq->Data) //para_dirty=
		wvAssembleSimplePAP(cq->Ver, &cq->apap, cq->para_fcLim, &cq->para_fkp,
					 &cq->stsh, cq->Data->file);
	  else //para_dirty=
		wvAssembleSimplePAP(cq->Ver, &cq->apap, cq->para_fcLim, &cq->para_fkp,
					 &cq->stsh, NULL);
	   //para_dirty =
		(wvAssembleComplexPAP(cq->Ver, &cq->apap, cq->cpiece, &cq->stsh, &cq->clx, cq->wpd)
			//? 1 : para_dirty
		);
	  if ((cq->apap.fInTable) && (!cq->apap.fTtp))
		{
		wvGetFullTableInit(cq, cq->para_intervals, cq->btePapx, cq->posPapx);
		wvGetRowTap(cq, &cq->apap, cq->para_intervals, cq->btePapx, cq->posPapx);
		}
	  else if (cq->apap.fInTable == 0)
		cq->intable = 0;
	  //wvHandleElement (ps, PARABEGIN, (void *) &apap, para_dirty);
	  switch(cq->apap.jc)
		{
		case 0:Justification(cq,0x80); break;	//left
		case 1:Justification(cq,0x82); break;	//center
		case 2:Justification(cq,0x83); break;	//right
		default:Justification(cq,0x81); break;	//full
		}

	  cq->char_fcLim = cq->ActualPos;
	  cq->para_pendingclose = 1;
	  }


	if((cq->char_fcLim == 0xffffffff) || (cq->char_fcLim == cq->ActualPos))
	    {
	    //wvTrace (("j i is %x %d\n", j, i));
	    wvReleaseCHPX_FKP (&cq->char_fkp);
	    wvGetSimpleCharBounds(&cq->char_fkp,
				  &cq->char_fcFirst, &cq->char_fcLim, i,
				  &cq->clx, cq->bteChpx, cq->posChpx,
				  cq->char_intervals, cq->wpd);
	    if(cq->log)
		fprintf(cq->log,_("\nChar begins at %lXh ends %lXh, pos is %lXh "), cq->char_fcFirst, cq->char_fcLim, cq->ActualPos);
	    if (0 == cq->char_pendingclose)
		{  /* if there's no character run open, but there should be then I believe that the fcFirst search
		      has failed me, so I set it to now. I need to investigate this further. */
		if (cq->ActualPos != cq->char_fcFirst)
		      {
		      //wvWarning (("There is no character run due to open but one should be, plugging the gap.\n"));
		      cq->char_fcFirst = cq->ActualPos;
		      }
		}
	    else{	/* lvm007@aha.ru fix: if currentfc>fcFirst but CHARPROP's changed look examples/charprops.doc*/
		if(cq->char_fcFirst< cq->ActualPos)
				cq->char_fcFirst = cq->ActualPos;
		}
	    }

	    if (cq->ActualPos == cq->char_fcFirst)
	       {        /* a CHP's base style is in the para style */
//	       wvTrace (("assembling CHP...\n"));
	       cq->achp.istd = cq->apap.istd;
	       if(wvAssembleSimpleCHP(cq->Ver, &cq->achp, cq->char_fcLim, &cq->char_fkp, &cq->stsh))
		 {	/* CHP is dirty */
		 ApplyCHPChange(cq);
		 }
	       // wvTrace (("CHP assembled.\n font is %d\n", achp.ftcAscii));      wvTrace (("char spec is %d\n", achp.ftcSym)); //wvTrace (("char lid is %x\n", achp.lidDefault));
	       cq->char_pendingclose = 1;
	       }
     }
   cq->by = 0;
   ProcessKeyWord(cq);
   i++;
   }
}


int TconvertedPass1_WORD::Dispatch(int FuncNo, ...)
{
va_list ap;

 va_start(ap, FuncNo);
 switch(FuncNo)
   {
   case DISP_WRAPPER:
	 OleFS=(COLEFS *)va_arg(ap,COLEFS *);
	 return 0;
   }
return(-1);
}

/*******************************************************************/
/* This procedure provides all needed processing for the first pass*/
int TconvertedPass1_WORD::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Convert_pass1_Word() ");fflush(log);
#endif
DWORD fsize;
string temp_filename;

  ObjectCounter=0;

  wvInitCLX(&clx);

  DocumentStart=ftell(wpd);

  fsize=filesize(wpd);

  Ver=FilForD.DocVersion>>8;

  sed = NULL;
  posSedx = NULL;
  wvInitSTSH(&stsh);
  section_fcLim = 0xFFFFFFFF;
  para_pendingclose = 0;
  para_fcLim = 0xFFFFFFFF;
  posPapx=NULL;
  btePapx=NULL;
  char_pendingclose = 0;
  char_fcLim = 0xFFFFFFFF;
  posChpx=NULL;
  bteChpx=NULL;
  wvInitPAPX_FKP (&para_fkp);
  wvInitCHPX_FKP (&char_fkp);
  cpiece=0;

  perc.Init(ftell(wpd), fsize,_("First pass Word:"));

  switch(Ver)
      {
      case 2:wvGetFIB2(&fib,wpd);	break;
      case 5:
      case 6:
      case 7:wvGetFIB6(&fib,wpd);	break;
      case 8:wvGetFIB8(&fib,wpd);	break;
      default:Finalise_Conversion(this);		//unknown version of Word
	      return(0);
      }

  if(fib.fComplex)
     {
     if(err != NULL)
	  {fprintf(err,_("\nWarning: Complex format is not finished yet!"),fib.fcMin);}
     //GetParaBounds=wvGetSimpleParaBounds;
     }
  else
     {
     //GetParaBounds=wvGetSimpleParaBounds;
     }


  if(OleFS!=NULL)
	{
	temp_filename = copy(wpd_filename,0,
		   GetExtension(wpd_filename()) - wpd_filename()) + ".T$$";

	if(fib.fWhichTblStm==0)
	   Table=cole_fopen(OleFS,"0Table",strdup(temp_filename()),NULL);
	else
	   Table=cole_fopen(OleFS,"1Table",strdup(temp_filename()),NULL);

	Data=cole_fopen(OleFS,"Data",strdup(temp_filename()),NULL);

	if(Table!=NULL)
	   {
	   wvGetCLX(Ver,&clx,fib.fcClx, fib.lcbClx,
		fib.fExtChar, Table->file);

	   if(clx.nopcd == 0)	/* for word 6 and just in case */
		wvBuildCLXForSimple6(&clx, &fib);

	   para_fcFirst = char_fcFirst = section_fcFirst =
			wvConvertCPToFC (0, &clx);
			//wvGetBeginFC (ps, whichdoc);
	   /*we will need the stylesheet to do anything useful with layout and look */
	   wvGetSTSH (&stsh, fib.fcStshf, fib.lcbStshf, Table->file);

	   /*dop */
//	   wvGetDOP(Ver, &dop, fib.fcDop, fib.lcbDop, tablefd);
	   //wvTrace (("tabstops are every %d twips\n", ps->dop.dxaTab));


	   /* we will need the paragraph and character bounds table to make decisions as  to where a para/char run begins and ends */
	   if(Ver == 6 || Ver == 7)
	     {
		 wvGetBTE_PLCF6 (&btePapx, &posPapx, &para_intervals,
				 fib.fcPlcfbtePapx, fib.lcbPlcfbtePapx,
				 Table->file);
		 wvGetBTE_PLCF6 (&bteChpx, &posChpx, &char_intervals,
				 fib.fcPlcfbteChpx, fib.lcbPlcfbteChpx,
				 Table->file);
	     }
	   else			/* word 97 */
	     {
		 wvGetBTE_PLCF (&btePapx, &posPapx, &para_intervals,
				fib.fcPlcfbtePapx, fib.lcbPlcfbtePapx,
				Table->file);
		 wvGetBTE_PLCF (&bteChpx, &posChpx, &char_intervals,
				fib.fcPlcfbteChpx, fib.lcbPlcfbteChpx,
				Table->file);
	     }

	   wvGetSED_PLCF (&sed, &posSedx, &section_intervals, fib.fcPlcfsed,
		   fib.lcbPlcfsed, Table->file);
	   }

	//wvTrace (("section_intervals is %d\n", section_intervals));

	COLEDIR *Dir;
	Dir=cole_diropen(OleFS,"ObjectPool",NULL);
	if(Dir)
	   {
	   cole_fprint_tree(Dir, log);
	   cole_closedir(Dir);
	   }
	}

  ActualPos=ftell(wpd);

  if(fib.fcMin<ActualPos || fib.fcMin>fib.fcMac)
	{
	if(err != NULL)
	  {perc.Hide();fprintf(err,_("\nError: FIB.fcMin is corrupted %ld !"),fib.fcMin);}
	fib.fcMin=ActualPos;
	}
  if(fib.fcMac>fsize)
	{
	if(err != NULL)
	  {perc.Hide();fprintf(err,_("\nError: FIB.fcMac is bigger than file size %ld !"),fib.fcMac);}
	fib.fcMac=fsize;
	}

//printf("%s\n",wvAutoCharset(&cq));

  if(clx.nopcd>0)
	{    	    /*for each piece */
	for(int piececount = 0; piececount < clx.nopcd; piececount++)
	    {
	    int ichartype;
	    DWORD beginfc, endfc;

	    ichartype = wvGetPieceBoundsFC(&beginfc, &endfc, &clx, piececount);
	    if(ichartype==-1) break;
	    if(ichartype==0) ConvertCpg=GetTranslator("unicodeTOinternal");
			else ConvertCpg=NULL;
	    CharType = ichartype;

	    if(log) fprintf(log,_("\n Document fragment #%d "),piececount);

	    if(beginfc>fsize || endfc>fsize)
		  {
		  if(err != NULL)
		    {
		    perc.Hide();
		    fprintf(err,_("Piece Bounds out of range begin=%ld end=%ld!\n"),beginfc,endfc);
		    }
		  if(endfc>fsize) endfc=fsize;	//Try to fix end bound
		  if(beginfc>endfc) continue;
		  }

	    if(fib.fComplex)
		PieceLoopSimple(this,beginfc,endfc,piececount);
	    else
		PieceLoopComplex(this,beginfc,endfc,piececount);
	    }
	}
  else  {
	CharType = 2;	//normal 8bit char
	PieceLoopSimple(this,fib.fcMin, fib.fcMac, -1);
	}

  if(Table!=NULL) {cole_fclose(Table,NULL);Table=NULL;}
  if(Data!=NULL)  {cole_fclose(Data,NULL);Data=NULL;}
  Finalise_Conversion(this);

  wvReleaseCLX(&clx);
  free(sed);
  free(posSedx);
  free(btePapx);
  free(posPapx);
  free(bteChpx);
  free(posChpx);

  wvReleaseSTSH(&stsh);
  internal_wvReleasePAPX_FKP(&para_fkp);
  internal_wvReleaseCHPX_FKP(&char_fkp);

  if(cache_TAP!=NULL) {free(cache_TAP);cache_TAP=NULL;} //Globaly cached tap - I hate leaks
  return(1);
}


/*--------------------End of PASS1_Word--------------------*/
