Logo Search packages:      
Sourcecode: abiword version File versions

ie_imp_RTF.cpp

/* -*- c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- */
/* AbiWord
 * Copyright (C) 1999 AbiSource, Inc.
 * Copyright (C) 2003 Tomas Frydrych <tomas@frydrych.uklinux.net>
 * Copyright (C) 2003 Martin Sevior <msevior@physics.unimelb.edu.au> 
 * Copyright (C) 2001, 2004 Hubert Figuiere <hfiguiere@teaser.fr>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */


/* RTF importer by Peter Arnold <petera@intrinsica.co.uk> */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include <math.h>

#include "ut_locale.h"
#include "ut_iconv.h"
#include "ut_types.h"
#include "ut_assert.h"
#include "ut_debugmsg.h"
#include "ut_math.h"
#include "ut_path.h"
#include "ut_string.h"
#include "ut_string_class.h"
#include "ut_units.h"
#include "ie_types.h"
#include "ie_imp_RTF.h"
#include "pd_Document.h"
#include "xap_EncodingManager.h"
#include "ie_impGraphic.h"
#include "fg_Graphic.h"
#include "fg_GraphicRaster.h"
#include "fg_GraphicVector.h"
#include "ut_bytebuf.h"
#include "ut_rand.h"
#include "pd_Style.h"
#include "fv_View.h"
#include "fl_AutoLists.h"
#include "pf_Frag.h"
#include "pf_Frag_Strux.h"
#include "xap_App.h"
#include "xap_Frame.h"
#include "wv.h" // for wvLIDToLangConverter

#include "ie_imp_RTFParse.h"

class fl_AutoNum;

/*!
  This macros allow the use of an iconv fallback name if needed.
 */
#define CPNAME_OR_FALLBACK(destination,name,fallbackname) \
{  \
      static char* cpname = NULL; \
      if (!cpname)    \
      {       \
            UT_iconv_t cd = UT_iconv_open(name,name);     \
            if (!UT_iconv_isValid(cd)) \
            { \
                  cpname = fallbackname;\
            } \
            else \
            { \
                  cpname = name;  \
                  UT_iconv_close(cd); \
            } \
      } \
      destination = cpname;  \
}


// This should probably be defined in pt_Types.h
// this used to be 8, which way to small ...
static const UT_uint32 PT_MAX_ATTRIBUTES = 20;


static char g_dbgLastKeyword [256];
static UT_sint32 g_dbgLastParam; 

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

IE_Imp_RTF_Sniffer::IE_Imp_RTF_Sniffer ()
      : IE_ImpSniffer(IE_IMPEXPNAME_RTF, true)
{
      // 
}

UT_Confidence_t IE_Imp_RTF_Sniffer::supportsMIME (const char * szMIME)
{
      if (UT_strcmp (IE_FileInfo::mapAlias (szMIME), IE_MIME_RTF) == 0)
            {
                  return UT_CONFIDENCE_GOOD;
            }
      return UT_CONFIDENCE_ZILCH;
}

UT_Confidence_t IE_Imp_RTF_Sniffer::recognizeContents(const char * szBuf,
                                                                                UT_uint32 iNumbytes)
{
      if ( iNumbytes < 5 )
      {
            return(UT_CONFIDENCE_ZILCH);
      }
      if ( strncmp( szBuf, "{\\rtf", 5 ) == 0 )
      {
            return(UT_CONFIDENCE_PERFECT) ;
      }
      return(UT_CONFIDENCE_ZILCH);
}

UT_Confidence_t IE_Imp_RTF_Sniffer::recognizeSuffix(const char * szSuffix)
{
      if (!UT_stricmp(szSuffix, ".rtf"))
      {
            return UT_CONFIDENCE_PERFECT;
      }
      if (!UT_stricmp(szSuffix, ".doc"))
      {
            return UT_CONFIDENCE_SOSO;
      }
      return UT_CONFIDENCE_ZILCH;
}

UT_Error IE_Imp_RTF_Sniffer::constructImporter(PD_Document * pDocument,
                                                                     IE_Imp ** ppie)
{
      IE_Imp_RTF * p = new IE_Imp_RTF(pDocument);
      *ppie = p;
      return UT_OK;
}

bool  IE_Imp_RTF_Sniffer::getDlgLabels(const char ** pszDesc,
                                                             const char ** pszSuffixList,
                                                             IEFileType * ft)
{
      *pszDesc = "Rich Text Format (.rtf)";
      *pszSuffixList = "*.rtf";
      *ft = getFileType();
      return true;
}

//////////////////////////////////////////////////////////////////
// List class definitions
//////////////////////////////////////////////////////////////////
RTF_msword97_level::RTF_msword97_level(RTF_msword97_list * pmsword97List, UT_uint32 level)
{
      m_levelStartAt = 1;
#if 0
      m_AbiLevelID = UT_rand();
      while(m_AbiLevelID < 10000)
            m_AbiLevelID = UT_rand();
#else
      //m_AbiLevelID = m_sLastAssignedLevelID++;
      UT_return_if_fail(pmsword97List);
      m_AbiLevelID = pmsword97List->m_pie_rtf->getDoc()->getUID(UT_UniqueId::List);
#endif
      m_pParaProps = NULL;
      m_pCharProps = NULL;
      m_pbParaProps = NULL;
      m_pbCharProps = NULL;
      m_pMSWord97_list = pmsword97List;
      m_localLevel = level;
      m_bStartNewList = false;
      m_listDelim = "%L";
      m_cLevelFollow = '\0';
      m_bRestart = true;
}

// Static data members must be initialized at file scope.
//UT_uint32 RTF_msword97_level::m_sLastAssignedLevelID = 100000;
UT_uint32 RTF_msword97_level::m_sPreviousLevel =0;


RTF_msword97_level::~RTF_msword97_level(void)
{
      DELETEP(m_pParaProps);
      DELETEP(m_pCharProps);
      DELETEP(m_pbParaProps);
      DELETEP(m_pbCharProps);
}

void RTF_msword97_level::buildAbiListProperties( const char ** szListID,
                                                 const char ** szParentID,
                                                 const char ** szLevel,
                                                 const char ** szStartat,
                                                 const char ** szFieldFont,
                                                 const char ** szListDelim,
                                                 const char ** szListDecimal,
                                                 const char ** szAlign,
                                                 const char ** szIndent,
                                                 const char ** szListStyle)
{
      static UT_String buf;
      static UT_String ListID, ParentID, Level, StartAt, FieldFont, ListDelim, ListDecimal,Align,Indent;
//
// Start with List ID.
//
//
// HACKALERT - this is ugly. We need to restart the list numbering if
//   - m_bRestart is set AND
//   - the previous list inserted into the document was a higher level.
//   I use a static member variable to determine what level list was
//   entered previously.
//   TODO: this assumes that the document is not structured like
//   1. Some level one text
//      a. Some level two text
//   2. Some more level one text
//          i. Some level three text
//      b. Some more level two text - note the label was not reset!!!
//
      if (m_bRestart && (m_sPreviousLevel < m_localLevel))
      {
            //m_AbiLevelID = m_sLastAssignedLevelID++;
            m_AbiLevelID = m_pMSWord97_list->m_pie_rtf->getDoc()->getUID(UT_UniqueId::List);
      }
      m_sPreviousLevel = m_localLevel;
      UT_String_sprintf(buf,"%d",m_AbiLevelID);
      ListID = buf;
      *szListID = ListID.c_str();
      buf[0] = 0;
//
// Now Parent ID.
//
      UT_uint32 iParentID = 0;
      if(m_localLevel> 0 && !m_bStartNewList)
      {
            iParentID = m_pMSWord97_list->m_RTF_level[m_localLevel-1]->m_AbiLevelID;
      }
      UT_String_sprintf(buf,"%d", iParentID);
      ParentID = buf;
    *szParentID = ParentID.c_str();
      buf[0] = 0;
//
// level
//
      UT_String_sprintf(buf,"%d",m_localLevel);
    Level = buf;
    *szLevel = Level.c_str();
    buf[0] = 0;
//
// Start At.
//
      UT_String_sprintf(buf,"%d",m_levelStartAt);
    StartAt = buf;
    *szStartat = StartAt.c_str();
    buf[0] = 0;
//
// List Style
//
    FL_ListType abiListType;
      if(m_RTFListType == 0)
    {
        abiListType = NUMBERED_LIST;
      }
      else if( m_RTFListType == 1)
      {
        abiListType = UPPERROMAN_LIST;
      }
    else if(m_RTFListType == 2)
      {
        abiListType = LOWERROMAN_LIST;
      }
    else if( m_RTFListType == 3)
      {
        abiListType = UPPERCASE_LIST;
      }
      else if (m_RTFListType == 4)
      {
        abiListType = LOWERCASE_LIST;
      }
    else if (m_RTFListType == 5)
      {
        abiListType = UPPERCASE_LIST;
      }
    else if (m_RTFListType == 23)
      {
            *szStartat = "1";
        abiListType = BULLETED_LIST;
      }
    else if (m_RTFListType == 23 + IMPLIES_LIST)
      {
            *szStartat = "1";
        abiListType = IMPLIES_LIST;
      }
      else if (m_RTFListType == 45)
      {
            abiListType = HEBREW_LIST;
      }
      else
      {
            abiListType = NUMBERED_LIST;
      }


      fl_AutoLists al;
    *szListStyle = al.getXmlList(abiListType);
//
// Field Font
//
    FieldFont = "NULL";
    if(m_pParaProps &&  m_pParaProps->m_pszFieldFont)
          FieldFont = m_pParaProps->m_pszFieldFont;
    if(abiListType == BULLETED_LIST)
    {
        FieldFont = "Symbol";
    }
    if(abiListType == IMPLIES_LIST)
    {
        FieldFont = "Symbol";
    }
    *szFieldFont = FieldFont.c_str();
//
// List Delim
//
    *szListDelim = m_listDelim.c_str();
//
// List Decimal
//
      *szListDecimal = ".";
//
// szAlign - left position of the paragraph
//
      if(m_pbParaProps && m_pbParaProps->bm_indentLeft)
      {
            Align = UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_pParaProps->m_indentLeft)/1440);
      }
      else
      {
            Align = UT_convertInchesToDimensionString(DIM_IN, (static_cast<double>(m_localLevel))*0.5);
      }
      *szAlign = Align.c_str();
//
// szIndent - offset from the left position for the listlabel
//
      if(m_pbParaProps && m_pbParaProps->bm_indentLeft)
      {
            Indent = UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_pParaProps->m_indentFirst)/1440);
      }
      else
      {
            Indent = "-0.3in";
      }
      *szIndent = Indent.c_str();
}

/*!
  Parse the leveltext and levelnumbers values.
  The idea is to translate the strings into AbiWord compatible format.

  From the RTF standard: "... a level three number such as '1.a.(i)' would
  generate the following RTF: '{\leveltext \'07\'00.\'01.(\'02)'}' where \'07
  is the string length, the \'00 \'01 and \'02 are level place holders, and the
  rest is surrounding text. The corresponding levelnumbers would be
  {\levelnumbers \'01\'03\'06} because the level place holders have indices
  1,3,6.

  In AbiWord, either this level resuses the parent label and adds on more text, or
  starts a new label. So we can only get 1.a.(i) if the parent label is 1.a. or 1.a

  \todo look up the parent label and be more precise about what is added by this label.
 */
bool RTF_msword97_level::ParseLevelText(const UT_String & szLevelText,const UT_String & szLevelNumbers, UT_uint32 iLevel)
{
      //read the text string into a int array, set the place holders to
      //values less than zero.
      UT_sint32 iLevelText[1000];
      char const * pText = szLevelText.c_str();
      UT_sint32 ilength = 0;
      UT_sint32 icurrent = 0;
      UT_sint32 istrlen = szLevelText.size();
      bool bIsUnicode;
      while (pText[0] != '\0')
      {
            bIsUnicode = ((pText[0] == '\\') && (pText[1] == '\'') && UT_UCS4_isdigit(pText[2]) && UT_UCS4_isdigit(pText[3]));
            // A broken exporter writes some junk at the beginning of the string.
            // Look for the first \'nn string
            if ((bIsUnicode) && (ilength == 0))
            {
                  ilength = 10*(pText[2] - '0') + (pText[3] - '0');
                  pText += 3;
            }
            else if (ilength >0)
            {
                  // I have read the string length, I can read off the values.
                  if (bIsUnicode)
                  {
                        iLevelText[icurrent++] = -10*(pText[2] - '0') - (pText[3] - '0');
                        pText += 3;
                  }
                  else
                  {
                        iLevelText[icurrent++] = pText[0];
                  }
            }
            // sanity check
            if (istrlen <= pText - szLevelText.c_str())
            {
                  UT_DEBUGMSG(("RTF: parsed past the end of leveltext string %s\n",szLevelText.c_str()));
                  return false;
            }
            pText++;
      }
      if(ilength != icurrent)
      {
            ilength = icurrent;
      }
      // Find the occurance of a previous level place holder
      for (icurrent = ilength-1; icurrent>=0; icurrent--)
      {
            if(-iLevelText[icurrent] >= 0 && (-iLevelText[icurrent] < static_cast<UT_sint32>(iLevel)))
            {
                  break;
            }
      }
      if (icurrent < 0)
      {
            // No previous level place holder
            UT_DEBUGMSG(("RTF Import: No previous level - restart list \n"));
            m_bStartNewList = true;
      }
      else
      {
            //TODO - find the end of the previous level place holder label
      }
      // Stuff the rest of the string in JUST this level in listDelim

      m_listDelim = "";
      bool bFound = false;
      for (icurrent++; icurrent < ilength; icurrent++)
      {
            if (iLevelText[icurrent]<=0 && !bFound)
            {

#if 0
// Matti's code
                  m_listDelim += "%L";
                  UT_return_val_if_fail(-iLevelText[icurrent] == static_cast<UT_sint32>(iLevel), false);
#endif
                  if(-iLevelText[icurrent] == static_cast<UT_sint32>(iLevel))
                  {
                        m_listDelim += "%L";
                        bFound = true;
                        continue;
                  }
            }
            else if ( bFound && (iLevelText[icurrent] >= 0))
            {
                  m_listDelim += static_cast<char>(iLevelText[icurrent]);
            }
            else if(bFound && iLevelText[icurrent] < 0)
            {
                  break;
            }
      }
      return true;
}

///////////////////////////////
// Paste Table class
///////////////////////////////
ABI_Paste_Table::ABI_Paste_Table(void):
      m_bHasPastedTableStrux(false),
      m_bHasPastedCellStrux(false),
      m_iRowNumberAtPaste(0),
      m_bHasPastedBlockStrux(false),
      m_iMaxRightCell(0),
      m_iCurRightCell(0),
      m_iCurTopCell(0),
      m_bPasteAfterRow(false)
{
}

ABI_Paste_Table::~ABI_Paste_Table(void)
{
}

/////////////////////////////////////////////////////////////////////////////////////////

RTF_msword97_list::RTF_msword97_list(IE_Imp_RTF * pie_rtf)
{
      m_RTF_listID = 0;
      m_RTF_listTemplateID = 0;
      m_pie_rtf = pie_rtf;
      for(UT_uint32 i=0; i< 9 ; i++)
      {
            m_RTF_level[i] = new RTF_msword97_level(this,i);
      }
}

RTF_msword97_list::~RTF_msword97_list(void)
{
      m_RTF_listID = 0;
      m_RTF_listTemplateID = 0;
      for(UT_uint32 i=0; i< 9 ; i++)
      {
            delete m_RTF_level[i];
      }
}

//////////////////////////////////////////////////////////////////////////////////////////

RTF_msword97_listOverride::RTF_msword97_listOverride(IE_Imp_RTF * pie_rtf )
{
      // Ideally, the default ID should be 0 which is a reserved ID in
      // the spec, but OpenOffice uses it, so use -1 instead (which
      // should be OK: spec sez 1-2000 is valid).
      m_RTF_listID = (UT_uint32)-1;

      m_OverrideCount = 0;
      m_pParaProps = NULL;
m_pParaProps = NULL;
      m_pbParaProps = NULL;
      m_pbCharProps = NULL;
      m_pie_rtf = pie_rtf;
      m_pList = NULL;
}

RTF_msword97_listOverride::~RTF_msword97_listOverride(void)
{
      DELETEP(m_pParaProps);
      DELETEP(m_pParaProps);
      DELETEP(m_pbParaProps);
      DELETEP(m_pbCharProps);
}

/*!
 * This function sets the pointer to the list structure for this override.
 * It just scans the defined lists and looks for a matching Identifier.
 */
bool RTF_msword97_listOverride::setList(void)
{
      UT_sint32 count = m_pie_rtf->get_vecWord97ListsCount();
      UT_sint32 i = 0;
      for(i=0; i< count; i++)
      {
            RTF_msword97_list * pList = m_pie_rtf->get_vecWord97NthList(i);
            if(pList->m_RTF_listID == m_RTF_listID)
            {
                  m_pList = pList;
                  return true;
            }
      }
      return false;
}
/*!
 * This returns returns a pointer to the tabstop vector defined in the list level.
 */
UT_NumberVector * RTF_msword97_listOverride::getTabStopVect(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return &(pLevel->m_pParaProps->m_tabStops);
}
/*!
 * This returns returns a pointer to the tab Type vector defined in the list level.
 */
UT_NumberVector * RTF_msword97_listOverride::getTabTypeVect(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return &(pLevel->m_pParaProps->m_tabTypes);
}
/*!
 * This returns returns a pointer to the tab Leadervector defined in the list level.
 */
UT_NumberVector * RTF_msword97_listOverride::getTabLeaderVect(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return &(pLevel->m_pParaProps->m_tabLeader);
}
/*!
 * This function returns true is there is a tab defined in the list definition.
 */
bool RTF_msword97_listOverride::isTab(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbParaProps->bm_tabStops);
}
/*!
 * This function returns true if deleted is changed in the list definition.
 */
bool RTF_msword97_listOverride::isDeletedChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_deleted);
}
/*!
 * This function returns the Deleted state in the list definition
 */
bool RTF_msword97_listOverride::getDeleted(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_deleted);
}
/*!
 * This function returns true if Bold is changed in the list definition.
 */
bool RTF_msword97_listOverride::isBoldChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_bold);
}
/*!
 * This function the bold state in the List definition.
 */
bool RTF_msword97_listOverride::getBold(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_bold);
}
/*!
 * This function returns true if Italic is changed in the list definition.
 */
bool RTF_msword97_listOverride::isItalicChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_italic);
}
/*!
 * This function returns the Italic state in the list definition.
 */
bool RTF_msword97_listOverride::getItalic(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_italic);
}
/*!
 * This function returns true if Underline is changed in the list definition.
 */
bool RTF_msword97_listOverride::isUnderlineChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_underline);
}
/*!
 * This function returns the Underline state in the list definition.
 */
bool RTF_msword97_listOverride::getUnderline(UT_uint32 iLevel)
{

      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_underline);
}
/*!
 * This function returns true if Strikeout is changed in the list definition.
 */
bool RTF_msword97_listOverride::isStrikeoutChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_strikeout);
}
/*!
 * This function returns the Strikeout state in the list definition.
 */
bool RTF_msword97_listOverride::getStrikeout(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_strikeout);
}
/*!
 * This function returns true if Superscript is changed in the list definition.
 */
bool RTF_msword97_listOverride::isSuperscriptChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_superscript);
}
/*!
 * This function returns the Superscript state in the list definition.
 */
bool RTF_msword97_listOverride::getSuperscript(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_superscript);
}
/*!
 * This function returns true if Superscript Position is changed in the list
 * definition.
 */
bool RTF_msword97_listOverride::isSuperscriptPosChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_superscript_pos);
}
/*!
 * This function returns the Superscript Position in the list definition.
 */
double RTF_msword97_listOverride::getSuperscriptPos(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_superscript_pos);
}
/*!
 * This function returns true if Subscript is changed in the list definition.
 */
bool RTF_msword97_listOverride::isSubscriptChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_subscript);
}
/*!
 * This function returns the Subscript state in the list definition.
 */
bool RTF_msword97_listOverride::getSubscript(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_subscript);
}
/*!
 * This function returns the Subscript state in the list definition.
 */
bool RTF_msword97_listOverride::isSubscriptPosChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_subscript_pos);
}
/*!
 * This function returns the Subscript state in the list definition.
 */
double RTF_msword97_listOverride::getSubscriptPos(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_subscript_pos);
}
/*!
 * This function returns true if Fontsize is changed in the list definition.
 */
bool RTF_msword97_listOverride::isFontSizeChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_fontSize);
}
/*!
 * This function returns the Fontsize in the list definition.
 */
double RTF_msword97_listOverride::getFontSize(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_fontSize);
}
/*!
 * This function returns true if the Hascolor state has changed.
 */
bool RTF_msword97_listOverride::isHasColourChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_hasColour);
}
/*!
 * This function returns the Hascolor state in the list definition.
 */
bool RTF_msword97_listOverride::getHasColour(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_hasColour);
}
/*!
 * This function returns true if ColourNumber is changed in the list definition.
 */
bool RTF_msword97_listOverride::isColourNumberChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_colourNumber);
}
/*!
 * This function returns the ColourNumber in the list definition.
 */
UT_uint32 RTF_msword97_listOverride::getColourNumber(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_colourNumber);
}
/*!
 * This function returns true if HasBgcolour is changed in the list definition.
 */
bool RTF_msword97_listOverride::isHasBgColourChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_hasBgColour);
}
/*!
 * This function returns the HasBgcolour state in the list definition.
 */
bool RTF_msword97_listOverride::getHasBgColour(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_hasBgColour);
}
/*!
 * This function returns true if BgColourNumber is changed in the list definition.
 */
bool RTF_msword97_listOverride::isBgColourNumberChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_bgcolourNumber);
}
/*!
 * This function returns the BgColourNumber  in the list definition.
 */
UT_uint32 RTF_msword97_listOverride::getBgColourNumber(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_bgcolourNumber);
}
/*!
 * This function returns true if FontNumber is changed in the list definition.
 */
bool RTF_msword97_listOverride::isFontNumberChanged(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pbCharProps->bm_fontNumber);
}
/*!
 * This function returns the FontNumber if changed in the list definition.
 */
UT_uint32 RTF_msword97_listOverride::getFontNumber(UT_uint32 iLevel)
{
      RTF_msword97_level * pLevel = m_pList->m_RTF_level[iLevel];
      return (pLevel->m_pCharProps->m_fontNumber);
}


/*!
 * This method returns all the stuff Abi needs to reconstruct a list
 */
void RTF_msword97_listOverride::buildAbiListProperties( const char ** szListID,
                                                 const char ** szParentID,
                                                 const char ** szLevel,
                                                 const char ** szStartat,
                                                 const char ** szFieldFont,
                                                 const char ** szListDelim,
                                                 const char ** szListDecimal,
                                                 const char ** szAlign,
                                                 const char ** szIndent,
                                                 const char ** szListStyle,
                                                 UT_uint32 iLevel)
{
      m_pList->m_RTF_level[iLevel]->buildAbiListProperties(szListID, szParentID, szLevel, szStartat, szFieldFont,
                                                       szListDelim, szListDecimal, szAlign, szIndent, szListStyle);

}

// constructor class to tell if a character property has been defined in a list structure.
RTFProps_bCharProps::RTFProps_bCharProps(void):
      bm_deleted(false),
      bm_bold(false),
      bm_italic(false),
      bm_underline(false),
      bm_overline(false),
      bm_strikeout(false),
      bm_topline(false),
      bm_botline(false),
      bm_superscript(false),
      bm_superscript_pos(false),
      bm_subscript(false),
      bm_subscript_pos(false),
      bm_fontSize(false),
      bm_fontNumber(false),
      bm_hasColour(false),
      bm_colourNumber(false),
      bm_hasBgColour(false),
      bm_bgcolourNumber(false),
      bm_listTag(false),
      bm_RTL(false),
      bm_dirOverride(false)
{
}

RTFProps_bCharProps::~RTFProps_bCharProps(void)
{
}

// These are set true if changed in list definitions.
RTFProps_bParaProps::RTFProps_bParaProps(void):
    bm_justification(false),
      bm_spaceBefore(false),
      bm_spaceAfter(false),
      bm_indentLeft(false),
      bm_indentRight(false),
      bm_indentFirst(false),
      bm_lineSpaceVal(false),
      bm_lineSpaceExact(false),
      bm_tabStops(false),
      bm_tabTypes(false),
      bm_tabLeader(false),
      bm_curTabType(false),
      bm_curTabLeader(false),
      bm_rtfListTable(false),
      bm_dom_dir(false)
{
}

RTFProps_bParaProps::~RTFProps_bParaProps(void)
{
}

//////////////////////////////////////////////////////////////////////////
// End List definitions
//////////////////////////////////////////////////////////////////////////



// Font table items
RTFFontTableItem::RTFFontTableItem(FontFamilyEnum fontFamily, int charSet, int codepage, FontPitch pitch,
                                                      unsigned char* panose, char* pFontName, char* pAlternativeFontName)
{
      m_family = fontFamily;
      m_charSet = charSet;
      m_codepage = codepage;
      m_szEncoding = 0;
      m_pitch = pitch;
      memcpy(m_panose, panose, 10*sizeof(unsigned char));
      m_pFontName = pFontName;
      m_pAlternativeFontName = pAlternativeFontName;

      // Set charset/codepage converter
      if (m_codepage && m_charSet)
      {
            UT_DEBUGMSG(("RTF Font has codepage *and* charset\n"));
            UT_ASSERT_NOT_REACHED();
      }
      else if (m_codepage)
      {
            // These are the valid values from the documentation:
            // TODO Many are not supported by iconv
            switch (m_codepage)
            {
                  // 708      Arabic (ASMO 708)
            case 708:
                  m_szEncoding = "ASMO-708";    // ISO-8859-6
                  break;
                  // 709      Arabic (ASMO 449+, BCON V4)
                  // 710      Arabic (Transparent Arabic)
                  // 711      Arabic (Nafitha Enhanced)
                  // 720      Arabic (Transparent ASMO)
                  // 819      Windows 3.1 (United States & Western Europe)
            case 819:
                  m_szEncoding = "CP819"; // ISO-8859-1
                  break;

                  // 437: IBM
            case 437:
                  m_szEncoding = "CP437";
                  break;

                  // 850      IBM Multilingual
            case 850:
                  m_szEncoding = "CP850";
                  break;
                  // 852      Eastern European
                  // 860      Portuguese
                  // 862      Hebrew
                  // 863      French Canadian
                  // 864      Arabic
                  // 865      Norwegian
                  // 866      Soviet Union
            case 866:
                  m_szEncoding = "CP866";
                  break;
                  // 932      Japanese
            case 932:
                  m_szEncoding = "CP932";
                  break;
                  // 936  Chinese: Simplified
            case 936:
                  CPNAME_OR_FALLBACK(m_szEncoding,"CP936","GBK");
                  break;
                  // 950  Chinese: Traditional
            case 950:
                  CPNAME_OR_FALLBACK(m_szEncoding,"CP950","BIG5");
                  break;
                  // 1250     Windows 3.1 (Eastern European)
            case 1250:
                  m_szEncoding = "CP1250";      // MS-EE
                  break;
                  // 1251     Windows 3.1 (Soviet Union)
            case 1251:
                  m_szEncoding = "CP1251";      // MS-CYRL
                  break;

                  // These were produced by MS WordPad 5.0 on Win2K
                  // TODO What do we do with negative values?
                  // -8534 - Devanagari   (57002)
                  // -8533 - Bengali            (57003)
                  // -8532 - Tamil        (57004)
                  // -8531 - Telugu       (57005)
                  // -8530 - Assamese           (57006)
                  // -8529 - Oriya        (57007)
                  // -8528 - Kannada            (57008)
                  // -8527 - Malayalam    (57009)
                  // -8526 - Gujarathi    (57010)
                  // -8525 - Panjabi            (57011)
                  // -7536 - Georgian           (58000)
                  // -7535 - Armenian           (58001)
            default:
                  m_szEncoding = XAP_EncodingManager::get_instance()->charsetFromCodepage(m_codepage);
            }
      }
      else if (m_charSet)
      {
            switch (m_charSet)
            {
                  case 0:           // ANSI_CHARSET
                        m_szEncoding = "CP1252";      // MS-ANSI
                        break;
                  case 2:           // SYMBOL_CHARSET
                        m_szEncoding = NULL;    // MS-ANSI
                        UT_DEBUGMSG(("RTF Font charset 'Symbol' worked around \n"));
                        break;
                  case 77:    // Source Vlad Harchev from OpenOffice
                        m_szEncoding = "MACINTOSH";
                        break;
                  case 78:    // fjf: some kind of Japanese, let's guess:
                        m_szEncoding = "SJIS";
                        break;
                  case 102:    // fjf: some kind of Chinese, let's guess:
                        CPNAME_OR_FALLBACK(m_szEncoding,"CP936","GBK");
                        break;
                  case 128:   // SHIFTJIS_CHARSET
                        m_szEncoding = "CP932";
                        break;
                  case 129:   // Hangul - undocumented?
                        m_szEncoding = "CP949";
                        break;
                  case 130:   // Source Vlad Harchev from OpenOffice
                        m_szEncoding = "CP1361";
                        break;
                  case 134:   // Chinese GB - undocumented?
                        CPNAME_OR_FALLBACK(m_szEncoding,"CP936","GBK");
                        break;
                  case 136:   // Chinese BIG5 - undocumented?
                        CPNAME_OR_FALLBACK(m_szEncoding,"CP950","BIG5");
                        break;
                  case 161:   // GREEK_CHARSET
                        m_szEncoding = "CP1253";      // MS-GREEK
                        break;
                  case 162:   // TURKISH_CHARSET
                        m_szEncoding = "CP1254";      // MS-TURK
                        break;
                  case 163:   // Vietnamese - undocumented?
                        m_szEncoding = "CP1258";
                        break;
                  // TODO What is different?  Iconv only supports one MS Hebrew codepage.
                  case 181:   // HEBREWUSER_CHARSET
                        UT_DEBUGMSG(("RTF Font charset 'HEBREWUSER'??\n"));
                  case 177:   // HEBREW_CHARSET
                        m_szEncoding = "CP1255";      // MS-HEBR
                        break;
                  // TODO What is different?  Iconv only supports one MS Arabic codepage.
                  case 178:   // ARABICSIMPLIFIED_CHARSET
                        UT_DEBUGMSG(("RTF Font charset 'ARABICSIMPLIFIED'??\n"));
                        m_szEncoding = "CP1256";      // MS-ARAB
                        break;
                  case 179:   // ARABICTRADITIONAL_CHARSET
                        UT_DEBUGMSG(("RTF Font charset 'ARABICTRADITIONAL'??\n"));
                        m_szEncoding = "CP1256";      // MS-ARAB
                        break;
                  case 180:   // ARABICUSER_CHARSET
                        UT_DEBUGMSG(("RTF Font charset 'ARABICUSER'??\n"));
                        m_szEncoding = "CP1256";      // MS-ARAB
                        break;
                  case 186:   // Baltic - undocumented?
                        m_szEncoding = "CP1257";
                        break;
                  case 204:   // CYRILLIC_CHARSET
                        m_szEncoding = "CP1251";      // MS-CYRL
                        break;
                  case 222:   // Thai - undocumented?
                        m_szEncoding = "CP874";
                        break;
                  case 238:   // EASTERNEUROPE_CHARSET
                        m_szEncoding = "CP1250";      // MS-EE
                        break;
                  case 254:   // PC437_CHARSET
                        m_szEncoding = "CP437";
                        break;
                  case 255:   // OEM_CHARSET
                        // TODO Can iconv do this?
                        UT_DEBUGMSG(("RTF Font charset 'OEM'??\n"));
                        UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
                        break;
                  default:
                        UT_DEBUGMSG(("RTF Font charset unknown: %d\n", m_charSet));
                        // don't assert like we used to do. just silently ignore.
            }
      }
}

RTFFontTableItem::~RTFFontTableItem()
{
      free(m_pFontName);
      free(m_pAlternativeFontName);
}

// Character properties
RTFProps_CharProps::RTFProps_CharProps(void)
{
      m_deleted = false;
      m_bold = false;
      m_italic = false;
      m_underline = false;
      m_overline = false;
      m_topline = false;
      m_botline = false;
      m_strikeout = false;
      m_superscript = false;
      m_superscript_pos = 0.0;
      m_subscript = false;
      m_subscript_pos = 0.0;
      m_fontSize = 12.0;
      m_fontNumber = 0;
      m_hasColour = false;
      m_colourNumber = 0;
      m_hasBgColour = false;
      m_bgcolourNumber = 0;
      m_styleNumber = -1;
      m_listTag = 0;
      m_szLang = 0;
      m_dir = UT_BIDI_UNSET;
      m_dirOverride = UT_BIDI_UNSET;
      m_Hidden = false;
      m_eRevision = PP_REVISION_NONE;
      m_iCurrentRevisionId = 0;
}

RTFProps_CharProps::~RTFProps_CharProps(void)
{
}

// Paragraph properties
RTFProps_ParaProps::RTFProps_ParaProps(void)
{
      m_justification = pjLeft;
      m_spaceBefore = 0;
      m_spaceAfter = 0;
      m_indentLeft = 0;
      m_indentRight = 0;
      m_indentFirst = 0;
      m_lineSpaceExact = false;
      m_lineSpaceVal = 240;
      m_isList = false;
      m_level = 0;
      memset(&m_pszStyle, 0, sizeof(m_pszStyle));
      m_rawID = 0;
      m_rawParentID = 0;
      memset(m_pszListDecimal,0,sizeof(m_pszListDecimal)) ;
      memset(m_pszListDelim,0,sizeof(m_pszListDelim)) ;
      memset(m_pszFieldFont,0,sizeof(m_pszFieldFont)) ;
      m_startValue = 0;
      m_curTabType = FL_TAB_LEFT;
      m_curTabLeader = FL_LEADER_NONE;
      m_iOverride = 0;
      m_iOverrideLevel = 0;
      m_styleNumber = -1;
      m_dir = UT_BIDI_UNSET;
      m_tableLevel = 1; // Has to be 1 because the RTF spec has itap defaulting 
                        // to this value
      m_bInTable = false;
      m_eRevision = PP_REVISION_NONE;
      m_iCurrentRevisionId = 0;
}

RTFProps_ParaProps& RTFProps_ParaProps::operator=(const RTFProps_ParaProps& other)
{
      if (this != &other)
      {
            m_tabStops.clear();
            m_tabTypes.clear();
            m_tabLeader.clear();
            m_justification = other.m_justification;
            m_spaceBefore = other.m_spaceBefore;
            m_spaceAfter = other.m_spaceAfter;
            m_indentLeft = other.m_indentLeft;
            m_indentRight = other.m_indentRight;
            m_indentFirst = other.m_indentFirst;
            m_lineSpaceVal = other.m_lineSpaceVal;
            m_lineSpaceExact = other.m_lineSpaceExact;

            if (other.m_tabStops.getItemCount() > 0)
            {
                  for (UT_uint32 i = 0; i < other.m_tabStops.getItemCount(); i++)
                  {
                        m_tabStops.addItem(other.m_tabStops.getNthItem(i));
                  }
            }
            if (other.m_tabTypes.getItemCount() > 0)
            {
                  for (UT_uint32 i = 0; i < other.m_tabTypes.getItemCount(); i++)
                  {
                        m_tabTypes.addItem(other.m_tabTypes.getNthItem(i));
                  }
            }
            if (other.m_tabLeader.getItemCount() > 0)
            {
                  for (UT_uint32 i = 0; i < other.m_tabLeader.getItemCount(); i++)
                  {
                        m_tabLeader.addItem(other.m_tabLeader.getNthItem(i));
                  }
            }
            UT_ASSERT_HARMLESS(m_tabStops.getItemCount() ==
                              other.m_tabTypes.getItemCount() );
            UT_ASSERT_HARMLESS(m_tabStops.getItemCount() ==
                              other.m_tabLeader.getItemCount() );

            m_isList = other.m_isList;
            m_level = other.m_level;
            strcpy(static_cast<char *>(m_pszStyle), static_cast<const char *>(other.m_pszStyle));
            m_rawID = other.m_rawID;
            m_rawParentID = other.m_rawParentID;
            strcpy(static_cast<char *>(m_pszListDecimal), static_cast<const char *>(other.m_pszListDecimal));
            strcpy(static_cast<char *>(m_pszListDelim), static_cast<const char *>(other.m_pszListDelim));
            strcpy(static_cast<char *>(m_pszFieldFont), static_cast<const char *>(other.m_pszFieldFont));
            m_startValue = other.m_startValue;
            m_iOverride = other.m_iOverride;
            m_iOverrideLevel = other.m_iOverrideLevel;
            if(m_tabTypes.getItemCount() > 0)
            {
                  UT_uint32 dum = m_tabTypes.getNthItem(0);
                  m_curTabType = static_cast<eTabType>(dum);
                  dum = m_tabLeader.getNthItem(0);
                  m_curTabLeader = static_cast<eTabLeader>(dum);
            }
            else
            {
                  m_curTabType = FL_TAB_LEFT;
                  m_curTabLeader = FL_LEADER_NONE;
            }
            m_rtfListTable = other.m_rtfListTable;
            m_styleNumber = other.m_styleNumber;
            m_bInTable = other.m_bInTable;
      }

      m_dir = other.m_dir;
      m_tableLevel = other.m_tableLevel;
      return *this;
}

RTFProps_ImageProps::RTFProps_ImageProps()
{
      sizeType = ipstNone;
      wGoal = hGoal = width = height = 0;
      scaleX = scaleY = 100;
      bCrop = false;
      cropt = cropb = cropl = cropr = 0;
}

RTFProps_CellProps::RTFProps_CellProps()
{
      m_bVerticalMerged = false;
      m_bVerticalMergedFirst = false;
      m_bHorizontalMerged = false;
      m_bHorizontalMergedFirst = false;
      m_sCellProps.clear();
      m_iCurBorder = rtfCellBorderTop;
      m_iCellx = 0;
}

RTFProps_CellProps& RTFProps_CellProps::operator=(const RTFProps_CellProps& other)
{
      if (this != &other)
      {
             m_bVerticalMerged = other.m_bVerticalMerged;
             m_bVerticalMergedFirst = other. m_bVerticalMergedFirst;
             m_bHorizontalMerged = other.m_bHorizontalMerged;
             m_bHorizontalMergedFirst = other. m_bHorizontalMergedFirst;
             m_sCellProps = other.m_sCellProps;
             m_iCurBorder = other.m_iCurBorder;
             m_iCellx = other.m_iCellx;
      }
      return *this;
}

RTFProps_TableProps::RTFProps_TableProps()
{
      m_bAutoFit = false;
}

RTFProps_TableProps& RTFProps_TableProps::operator=(const RTFProps_TableProps& other)
{
      if (this != &other)
      {
            m_bAutoFit = other.m_bAutoFit;
      }
      return *this;
}

RTFProps_SectionProps::RTFProps_SectionProps()
{
      m_numCols = 1;
      m_breakType = sbkPage;    /* the default in RTF is page section break */
      m_pageNumFormat = pgDecimal;
      m_bColumnLine = false;
      m_leftMargTwips = 1800;
      m_rightMargTwips = 1800;
      m_topMargTwips = 1440;
      m_bottomMargTwips = 1440;
      m_headerYTwips = 720;
      m_footerYTwips = 720;
      m_gutterTwips = 0;
    m_colSpaceTwips = 0;
      m_dir = UT_BIDI_UNSET;
}


RTFStateStore::RTFStateStore()
{
      m_destinationState = rdsNorm;
      m_internalState = risNorm;
      m_unicodeAlternateSkipCount = 1;
      m_unicodeInAlternate = 0;
}


RTFStateStore * RTFStateStore::clone(void)
{
      RTFStateStore * pNew = new RTFStateStore();
      pNew->m_destinationState = m_destinationState;
      pNew->m_charProps = m_charProps;
      pNew->m_paraProps = m_paraProps;
      pNew->m_sectionProps = m_sectionProps;
      pNew->m_cellProps = m_cellProps;
      pNew->m_tableProps = m_tableProps;
      pNew->m_unicodeAlternateSkipCount = m_unicodeAlternateSkipCount;
      pNew->m_unicodeInAlternate = m_unicodeInAlternate;
      return pNew;
}

/*****************************************************************/
/*****************************************************************/

IE_Imp_RTF::IE_Imp_RTF(PD_Document * pDocument)
:     IE_Imp(pDocument),
      m_gbBlock(1024),
      m_szFileDirName(NULL),
      m_groupCount(0),
      m_newParaFlagged(false),
      m_newSectionFlagged(false),
      m_cbBin(0),
      m_currentHdrID(0),
      m_currentFtrID(0),
      m_currentHdrEvenID(0),
      m_currentFtrEvenID(0),
      m_currentHdrFirstID(0),
      m_currentFtrFirstID(0),
      m_currentHdrLastID(0),
      m_currentFtrLastID(0),
      m_numLists(0),
      m_pImportFile(NULL),
      m_pPasteBuffer(NULL),
      m_lenPasteBuffer(0),
      m_pCurrentCharInPasteBuffer(NULL),
      deflangid(0),
      m_mbtowc (XAP_EncodingManager::get_instance()->getNative8BitEncodingName()),
      m_parsingHdrFtr(false),
      m_icurOverride(0),
      m_icurOverrideLevel(0),
      m_bAppendAnyway(false),
      m_TableControl(pDocument),
      m_lastCellSDH(NULL),
      m_bNestTableProps(false),
      m_bParaWrittenForSection(false),
      m_bCellBlank(true),
      m_bEndTableOpen(false),
      m_bInFootnote(false),
      m_iDepthAtFootnote(0),
      m_iLastFootnoteId((UT_uint32)pDocument->getUID(UT_UniqueId::Footnote)),
      m_iHyperlinkOpen(0),
      m_bBidiMode(false),
      m_bFootnotePending(false),
      m_bFtnReferencePending(false),
      m_bNoteIsFNote(true),
      m_bStyleImportDone(false),
      m_bCellHandled(false),
      m_bContentFlushed(false),
      m_bRowJustPassed(false),
      m_iStackLevelAtRow(0),
      m_bDoCloseTable(false),
      m_iNoCellsSinceLastRow(0),
      m_bFieldRecognized(false),
      m_iIsInHeaderFooter(0),
      m_bSectionHasPara(false),
      m_bStruxInserted(false),
      m_bStruxImage(false),
      m_bFrameStruxIn(false),
      m_iAutoBidiOverride(UT_BIDI_UNSET),
      m_iBidiLastType(UT_BIDI_UNSET),
      m_iBidiNextType(UT_BIDI_UNSET)
{
      m_sImageName.clear();
      if (!IE_Imp_RTF::keywordSorted) {
            _initialKeywordSort();
      }

      if(m_vecAbiListTable.getItemCount() != 0)
      {
            UT_VECTOR_PURGEALL(_rtfAbiListTable *,m_vecAbiListTable);
      }
      m_mbtowc.setInCharset(XAP_EncodingManager::get_instance()->getNativeEncodingName());
      m_hyperlinkBase.clear();
      m_pasteTableStack.push(NULL);
}


IE_Imp_RTF::~IE_Imp_RTF()
{
      // Empty the state stack
      while (m_stateStack.getDepth() > 0)
      {
            RTFStateStore* pItem = NULL;
            m_stateStack.pop((void**)(&pItem));
            delete pItem;
      }
      closePastedTableIfNeeded();

      // and the font table (can't use the macro as we allow NULLs in the vector
      UT_sint32 size = m_fontTable.getItemCount();
      UT_sint32 i =0;
      for (i = size-1; i>=0; i--)
      {
            RTFFontTableItem* pItem = m_fontTable.getNthItem(i);
            delete pItem;
      }

      // and the styleName table.

      size = m_styleTable.getItemCount();
      for (i = 0; i < size; i++)
      {
            char * pItem = m_styleTable.getNthItem(i);
            FREEP(pItem);
      }
      UT_VECTOR_PURGEALL(_rtfAbiListTable *,m_vecAbiListTable);
      UT_VECTOR_PURGEALL(RTFHdrFtr *, m_hdrFtrTable);
      UT_VECTOR_PURGEALL(RTF_msword97_list *, m_vecWord97Lists);
      UT_VECTOR_PURGEALL(RTF_msword97_listOverride *, m_vecWord97ListOverride);
      UT_DEBUGMSG(("SEVIOR:DOing last close \n"));
      while(getTable() && getTable()->wasTableUsed())
      {
            CloseTable(true);
      }
      FREEP (m_szFileDirName);
}

UT_Error IE_Imp_RTF::importFile(const char * szFilename)
{
      m_newParaFlagged = true;
      m_newSectionFlagged = true;

      m_szFileDirName = UT_strdup (szFilename);
      // UT_basename returns a point INSIDE the passed string.
      // the trick is to truncate the string by setting the char pointed
      // by tmp to NULL. This IS useful code. (2 LOC)
      char * tmp = const_cast<char *>(UT_basename (m_szFileDirName));
      *tmp = 0;
      FILE *fp = fopen(szFilename, "r");
      if (!fp)
      {
            UT_DEBUGMSG(("Could not open file %s\n",szFilename));
            return UT_errnoToUTError ();
      }

      UT_Error error = _writeHeader(fp);

      if (!error)
      {
            error = _parseFile(fp);
            m_bAppendAnyway = true;
            _appendHdrFtr ();
      }

      fclose(fp);

      // check if the doc is empty or not
      if (getDoc()->getLastFrag() == NULL)
      {
            error = UT_IE_BOGUSDOCUMENT;
      }


      return error;
}


UT_Error IE_Imp_RTF::_writeHeader(FILE * /*fp*/)
{
            return UT_OK;
}


bool IE_Imp_RTF::digVal(char ch, int& value, int base)
{
      value = ch - '0';

      return (value >= 0) && (value < base);
}

bool IE_Imp_RTF::hexVal(char c, int& value)
{
      bool ok = true;

      if (isdigit(c))
      {
            ok = digVal(c, value, 10);
      }
      else if (islower(c))
      {
            ok = (c >= 'a' && c <= 'f');
            value = c - 'a' + 10;
      }
      else
      {
            ok = (c >= 'A' && c <= 'F');
            value = static_cast<char>(c) - 'A' + 10;
      }

      return ok;
}

ie_imp_cell * IE_Imp_RTF::getCell(void)
{
      UT_return_val_if_fail(getTable(),NULL);
      return getTable()->getCurCell();
}

ie_imp_table * IE_Imp_RTF::getTable(void)
{
      return m_TableControl.getTable();
}

/*!
 * Opens a table by inserting a table and cell strux. The boolean argument
 * bDontFlush is false by default. If true the current stored chars are
 * not flushed.
 */
void IE_Imp_RTF::OpenTable(bool bDontFlush)
{
      if(bUseInsertNotAppend())
      {
            return;
      }
      if(!m_bParaWrittenForSection)
      {
            if(!bDontFlush)
            {
                  FlushStoredChars(true);
            }
      }
      else
      {
            if(!bDontFlush)
            {
                  FlushStoredChars();
            }
      }
      m_TableControl.OpenTable();
//
// Need to have a block to append a table to
//
      if((m_TableControl.getNestDepth() > 1) && m_bCellBlank)
      {
                  xxx_UT_DEBUGMSG(("Append block 6 \n"));

            getDoc()->appendStrux(PTX_Block,NULL);
      }
      getDoc()->appendStrux(PTX_SectionTable,NULL);
      xxx_UT_DEBUGMSG(("SEVIOR: Appending Table strux to doc nestdepth %d \n", m_TableControl.getNestDepth()));
      PL_StruxDocHandle sdh = getDoc()->getLastStruxOfType(PTX_SectionTable);
      xxx_UT_DEBUGMSG(("SEVIOR: Table strux sdh is %x \n",sdh));
      getTable()->setTableSDH(sdh);
      getTable()->OpenCell();
      if(!bDontFlush)
      {
            FlushCellProps();
            ResetCellAttributes();
      }
      getDoc()->appendStrux(PTX_SectionCell,NULL);
      sdh = getDoc()->getLastStruxOfType(PTX_SectionCell);
      getCell()->setCellSDH(sdh);
      m_currentRTFState.m_cellProps = RTFProps_CellProps();
      m_currentRTFState.m_tableProps = RTFProps_TableProps();
      m_lastCellSDH = NULL; // This is in the table structure and can be deleted from there.
      m_bCellBlank = true;
//    m_iNoCellsSinceLastRow = 0;
}

/*!
 * This Method saves the information about the current row
 */
void IE_Imp_RTF::SaveRowInfo(void)
{
}

/*!
 * This Method Clears any information about the current row
 */
void IE_Imp_RTF::RemoveRowInfo(void)
{
}

UT_sint32 IE_Imp_RTF::getPasteDepth(void)
{
      return m_pasteTableStack.getDepth();
}

/*!
 * Close off pasted Tables
 */
void IE_Imp_RTF::closePastedTableIfNeeded(void)
{
      while(m_pasteTableStack.getDepth() > 0)
      {
            ABI_Paste_Table * pPaste = NULL;
            m_pasteTableStack.pop((void**)(&pPaste));
            if(pPaste != NULL)
            {
                  if(pPaste->m_bHasPastedCellStrux && !pPaste->m_bHasPastedBlockStrux)
                  {
                        insertStrux(PTX_Block);
                        UT_DEBUGMSG(("Paste block in destructor 1 \n"));
                  }
                  if(pPaste->m_bHasPastedCellStrux)
                  {
                        insertStrux(PTX_EndCell);
                        UT_DEBUGMSG(("Paste EndCell in destructor 1 \n"));
                  }
                  if(!pPaste->m_bPasteAfterRow)
                  {
//
// Now fill out any remaining cells needed to finish the row of the table
//
                        UT_sint32 i = pPaste->m_iCurRightCell;
                        UT_String sTop =  UT_String_sprintf("%d",pPaste->m_iCurTopCell);
                        UT_String sBot =  UT_String_sprintf("%d",pPaste->m_iCurTopCell+1);
                        UT_String sCellProps;
                        UT_String sVal;
                        UT_String sDum;
                        const XML_Char * attrs[3] = {"props",NULL,NULL};
                        for(i = pPaste->m_iCurRightCell; i<pPaste->m_iMaxRightCell; i++)
                        {
                              sCellProps.clear();
                              sVal = UT_String_sprintf("%d",i);
                              sDum = "left-attach";
                              UT_String_setProperty(sCellProps,sDum,sVal);
                              sVal = UT_String_sprintf("%d",i+1);
                              sDum = "right-attach";
                              UT_String_setProperty(sCellProps,sDum,sVal);
                              sDum = "top-attach";
                              UT_String_setProperty(sCellProps,sDum,sTop);
                              sDum = "bot-attach";
                              UT_String_setProperty(sCellProps,sDum,sBot);
                              
                              attrs[1] = sCellProps.c_str();
                              insertStrux(PTX_SectionCell,attrs,NULL);
                              
                              insertStrux(PTX_Block);
                              
                              insertStrux(PTX_EndCell);
                        }
                        if(pPaste->m_bHasPastedTableStrux)
                        {
                              insertStrux(PTX_EndTable);
                              
//                            insertStrux(PTX_Block); // we don't need this now
                        }
                  }
                  else
                  {
//
// Close off pasted rows by incrementing the top and botton's of the cell's
// below
//
                        UT_sint32 numRows = pPaste->m_iCurTopCell -pPaste->m_iRowNumberAtPaste +1;
                        PL_StruxDocHandle sdhCell = NULL;
                        PL_StruxDocHandle sdhTable = NULL;
                        PL_StruxDocHandle sdhEndTable = NULL;
                        bool b = getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_SectionTable,&sdhTable);
                        PT_DocPosition posTable = getDoc()->getStruxPosition(sdhTable);
                        UT_ASSERT(b);
                        sdhEndTable = getDoc()->getEndTableStruxFromTableSDH(sdhTable);
                        UT_ASSERT(sdhEndTable);
                        PT_DocPosition posEndTable = getDoc()->getStruxPosition(sdhEndTable);
                        b = getDoc()->getStruxOfTypeFromPosition(m_dposPaste-1,PTX_SectionCell,&sdhCell);
                        b = getDoc()->getNextStruxOfType(sdhCell,PTX_SectionCell,&sdhCell);
                        UT_String sTop;
                        UT_String sBot;
                        const char * szVal = NULL;
                        const XML_Char * sProps[5] = {NULL,NULL,NULL,NULL};
                        PT_DocPosition posCell = 0;
                        if(b)
                        {
                              posCell = getDoc()->getStruxPosition(sdhCell);
                        }
                        while(b && (posCell < posEndTable))
                        {
                              getDoc()->getPropertyFromSDH(sdhCell,
                                                                         true,
                                                                         PD_MAX_REVISION,
                                                                         "top-attach", &szVal);
                              UT_ASSERT(szVal);
                              UT_sint32 iTop = atoi(szVal);
                              iTop += numRows;
                              UT_String_sprintf(sTop,"%d",iTop);
                              getDoc()->getPropertyFromSDH(sdhCell,
                                                                         true,
                                                                         PD_MAX_REVISION,
                                                                         "bot-attach", &szVal);
                              UT_ASSERT(szVal);
                              UT_sint32 iBot = atoi(szVal);
                              iBot += numRows;
                              UT_String_sprintf(sBot,"%d",iBot);
                              UT_DEBUGMSG(("Change cell top to %d bot to %d \n",iTop,iBot));
                              sProps[0] = "top-attach";
                              sProps[1] = sTop.c_str();
                              sProps[2] = "bot-attach";
                              sProps[3] = sBot.c_str();
                              getDoc()->changeStruxFmt(PTC_AddFmt,posCell+1,posCell+1,NULL,sProps,PTX_SectionCell);
                              b = getDoc()->getNextStruxOfType(sdhCell,PTX_SectionCell,&sdhCell);
                              if(b)
                              {
                                    posCell = getDoc()->getStruxPosition(sdhCell);
                              }
                        }
//
// Now a change strux on the table to make it rebuild.
//
                        sProps[0] = "list-tag";
                        UT_String sVal;
                        UT_String_sprintf(sVal,"%d",getDoc()->getUID(UT_UniqueId::List));
                        sProps[1] = sVal.c_str();
                        sProps[2] = NULL;
                        sProps[3] = NULL;
                        getDoc()->changeStruxFmt(PTC_AddFmt,posTable+1,posTable+1,NULL,sProps,PTX_SectionTable);
                  }
                  delete pPaste;
            }
      }
}
/*!
 * Closes the current table. Does all the book keeping of inserting
 * endstruxs and deleting used ones.
 */ 
void IE_Imp_RTF::CloseTable(bool bForce /* = false */)
{
//
// Close table removes extraneous struxes like unmatched PTX_SectionCell's
//
      if(!bForce && (bUseInsertNotAppend() || (getTable() == NULL)))
      {
            return;
      }
      if(getTable() && getTable()->wasTableUsed())
      {
            xxx_UT_DEBUGMSG(("SEVIOR: Table used appened end Table, block \n"));
            if(m_lastCellSDH != NULL )
            {
                  getDoc()->insertStruxNoUpdateBefore(m_lastCellSDH,PTX_EndTable,NULL);
//
// Need this one for dp_Instructions. Sevior
//
//                getDoc()->insertStruxNoUpdateBefore(m_lastCellSDH,PTX_Block,NULL);
                  PL_StruxDocHandle cellSDH = m_lastCellSDH;
                  getDoc()->deleteStruxNoUpdate(cellSDH);
                  m_bEndTableOpen = true;
            }
            m_TableControl.CloseTable();
            if(m_lastCellSDH == NULL)
            {
                  getDoc()->appendStrux(PTX_EndTable,NULL);
                  m_bEndTableOpen = true;
            }
            m_lastCellSDH = NULL;
      }
      else if(getTable())
      {
            if(m_lastCellSDH != NULL )
            {
                  PL_StruxDocHandle cellSDH = m_lastCellSDH;
                  getDoc()->deleteStruxNoUpdate(cellSDH);
                  m_lastCellSDH = NULL;
            }
            m_TableControl.CloseTable();
            m_bEndTableOpen = true;
            UT_DEBUGMSG(("SEVIOR: Table not used. \n"));
      }
      else
      {
            if(m_lastCellSDH != NULL )
            {
                  PL_StruxDocHandle cellSDH = m_lastCellSDH;
                  getDoc()->deleteStruxNoUpdate(cellSDH);
                  m_lastCellSDH = NULL;
            }
      }
}

void IE_Imp_RTF::HandleCell(void)
{
//
// Look if the has been some text output before this with an open row. If
// so, close the table and make copy of the last cells.
//
      if(m_bRowJustPassed && m_bDoCloseTable && (getTable()!= NULL))
      {
            UT_GenericVector<ie_imp_cell *> vecOldCells;
            UT_GenericVector<ie_imp_cell *> vecCopyCells;
            UT_sint32 row = getTable()->getRow();
            getTable()->getVecOfCellsOnRow(row-1, &vecOldCells);
            UT_uint32 i =0;
            for(i=0; i< vecOldCells.getItemCount();i++)
            {
                  ie_imp_cell * pCell = vecOldCells.getNthItem(i);
                  ie_imp_cell * pNewCell = new ie_imp_cell(NULL,NULL,NULL,0);
                  pNewCell->copyCell(pCell);
                  vecCopyCells.addItem(pNewCell);
            }
            UT_ASSERT_HARMLESS(vecOldCells.getItemCount() > 0);
            CloseTable();
            OpenTable(true);
            for(i=0; i< vecCopyCells.getItemCount();i++)
            {
                  ie_imp_cell * pCopyCell = vecCopyCells.getNthItem(i);
                  if(i>0)
                  {
//
// Already open openned from OpenTable()
//
                        getTable()->OpenCell();
                  }
                  ie_imp_cell * pCell = getTable()->getNthCellOnRow(i);
                  pCell->copyCell(pCopyCell);
                  xxx_UT_DEBUGMSG(("Got Cell number %d CellX %d \n",i,pCell->getCellX()));
            }
            UT_VECTOR_PURGEALL(ie_imp_cell *, vecCopyCells);
      }
      m_bRowJustPassed = false;
      m_bCellHandled = true;
      m_bDoCloseTable = false;
      m_iNoCellsSinceLastRow++;
      if(bUseInsertNotAppend())
      {
            return;
      }
      if(m_bCellBlank && (m_gbBlock.getLength() == 0))
      {
                  xxx_UT_DEBUGMSG(("Append block 7 \n"));

            getDoc()->appendStrux(PTX_Block,NULL);
      }
      else
      {
//
// Flush out anything we've been holding
//    
            FlushStoredChars();
      }
      if(getTable() == NULL)
      {
            OpenTable();
      }
      PL_StruxDocHandle sdh = getDoc()->getLastStruxOfType(PTX_SectionCell);
      ie_imp_cell * pCell = getTable()->getNthCellOnRow(getTable()->getPosOnRow());
      UT_return_if_fail(sdh);
      if(!pCell)
      {
//
// Cell class doesn't exist so create it.
//
            UT_sint32 pos  = getTable()->OpenCell();
            getTable()->setPosOnRow(pos);
            xxx_UT_DEBUGMSG(("SEVIOR: created cell %x for posOnRow %d \n",getCell(),getTable()->getPosOnRow()));
      }
      xxx_UT_DEBUGMSG(("SEVIOR: set cell %x sdh %x  at pos %d on row %d \n",getCell(),sdh,getTable()->getPosOnRow(),getTable()->getRow()));
      getTable()->setNthCellOnThisRow(getTable()->getPosOnRow());
      if(getCell()->isMergedAbove())
      {
            xxx_UT_DEBUGMSG(("Cell %x is merged Above \n"));
      } 
      if(getCell()->isMergedLeft())
      {
            xxx_UT_DEBUGMSG(("Cell %x is merged left \n"));
      } 

      if(!getCell()->isMergedAbove() && !getCell()->isMergedLeft())
      {
            getCell()->setCellSDH(sdh);
            xxx_UT_DEBUGMSG(("SEVIOR: At posOnRow %d cellx %d \n",getTable()->getPosOnRow(),getCell()->getCellX()));
            getTable()->incPosOnRow();
            FlushStoredChars();           
            xxx_UT_DEBUGMSG(("SEVIOR: Non posonrow %d \n",getTable()->getPosOnRow()));
            getDoc()->appendStrux(PTX_EndCell,NULL);
//
// Look to see if this is just has a cell/endCell with no content. If so
// repair it.
//
            PL_StruxDocHandle sdhEndCell = reinterpret_cast<PL_StruxDocHandle>(getDoc()->getLastStruxOfType(PTX_EndCell));
            if(getDoc()->isStruxBeforeThis(sdhEndCell,PTX_SectionCell))
            {
                  getDoc()->insertStruxNoUpdateBefore(sdhEndCell,PTX_Block,NULL);
                  const pf_Frag * pf = static_cast<const pf_Frag *>(sdhEndCell);
                  getDoc()->insertFmtMarkBeforeFrag(const_cast<pf_Frag *>(pf));
            }
            getTable()->CloseCell();
            getDoc()->appendStrux(PTX_SectionCell,NULL);
            m_lastCellSDH = getDoc()->getLastStruxOfType(PTX_SectionCell);
      }
      else
      {
            getTable()->incPosOnRow();
      }
      m_bCellBlank = true;
}

void IE_Imp_RTF::FlushCellProps(void)
{
      if(bUseInsertNotAppend())
      {
            return;
      }
      if(m_currentRTFState.m_cellProps.m_bVerticalMerged)
      {
            UT_DEBUGMSG(("Set merged above to cell %x \n",getCell()));
      }
      if(m_currentRTFState.m_cellProps.m_bHorizontalMerged)
      {
            UT_DEBUGMSG(("Set merged left to cell %x \n",getCell()));
      }
      getCell()->setMergeAbove( m_currentRTFState.m_cellProps.m_bVerticalMerged );
      getCell()->setFirstVerticalMerge( m_currentRTFState.m_cellProps.m_bVerticalMergedFirst );
      getCell()->setFirstHorizontalMerge( m_currentRTFState.m_cellProps.m_bHorizontalMergedFirst );
      getCell()->setMergeLeft( m_currentRTFState.m_cellProps.m_bHorizontalMerged );
      getCell()->addPropString( m_currentRTFState.m_cellProps.m_sCellProps );
      
}

/*!
 * Set a property, value pair in the supplied string. This is just a convience
 * wrapper function to use const char * strings
 */
void IE_Imp_RTF::_setStringProperty(UT_String & sPropsString, 
                                    const char * szProp, const char * szVal)
{
      UT_String sProp(szProp);
      UT_String sVal(szVal);
      UT_String_setProperty(sPropsString,sProp,sVal);
}


void IE_Imp_RTF::FlushTableProps(void)
{
      if(bUseInsertNotAppend())
      {
            return;
      }
      getTable()->setAutoFit( m_currentRTFState.m_tableProps.m_bAutoFit );
}

void IE_Imp_RTF::HandleCellX(UT_sint32 cellx)
{
      if(bUseInsertNotAppend())
      {
            return;
      }

      if(getTable() == NULL)
      {
            OpenTable();
      }
//    UT_ASSERT_HARMLESS(cellx != 3652);
      UT_sint32 iRow = 0;
      bool bNewCell = true;
//
// Look to see if a cell with cellx already exists on the current row. If so set the
// current cell pointer to point to it.
//
      UT_DEBUGMSG(("Original cellx %d \n",cellx));
      iRow = getTable()->getRow();
      ie_imp_cell * pOldCell = getTable()->getCellAtRowColX(iRow,cellx);
      if(pOldCell && !m_currentRTFState.m_cellProps.m_bHorizontalMergedFirst && !m_currentRTFState.m_cellProps.m_bHorizontalMerged )
      {
            bNewCell = false;
            getTable()->setCell(pOldCell);
            cellx = pOldCell->getCellX();
      }
      if(!pOldCell)
      {
            pOldCell = getTable()->getNthCellOnRow(getTable()->getCellXOnRow());
            xxx_UT_DEBUGMSG(("SEVIOR: Looking for cellx num %d on row %d found %x \n",getTable()->getCellXOnRow(),iRow,pOldCell));
            if(pOldCell)
            {
                  bNewCell = false;
                  getTable()->setCell(pOldCell);
            }
      }
      if(bNewCell)
      {
            getTable()->OpenCell();
            xxx_UT_DEBUGMSG(("SEVIOR: created cell %x for cellx %d on row \n",getCell(),cellx,getTable()->getRow()));
      }
      UT_ASSERT_HARMLESS(cellx>1);
      getTable()->setCellX(cellx);
      UT_DEBUGMSG(("set cellx for class %x to %d \n",getCell(),cellx));
      getTable()->incCellXOnRow();
      FlushCellProps();
      ResetCellAttributes();
}

void IE_Imp_RTF::HandleRow(void)
{
      if(bUseInsertNotAppend())
      {
            return;
      }

      UT_DEBUGMSG(("ie_imp_RTF: Handle Row now NUm cells in row %d \n",m_iNoCellsSinceLastRow));
      if(m_iNoCellsSinceLastRow > 0)
      {
            m_TableControl.NewRow();
      }
      else
      {
            UT_DEBUGMSG(("One of those stupid rows without cells found. \n"));
            UT_DEBUGMSG(("Handle it now. RTF totally sucks. \n"));
            if(getTable()) {
                  getTable()->removeCurrentRow();
                  getDoc()->miniDump(m_lastCellSDH,8);
            }

            m_bCellBlank = true;
            UT_ASSERT_HARMLESS(0);
      }
//
// Need these for strange barely legal docs like that in bug 4111
//
      m_bCellHandled = false;
      m_bContentFlushed = false;
      m_bRowJustPassed = true;
      m_iStackLevelAtRow = m_stateStack.getDepth();
      m_bDoCloseTable = false;
      m_iNoCellsSinceLastRow = 0;
}


void IE_Imp_RTF::HandleNoteReference(void)
{
      // see if we have a reference marker pending ...
      const XML_Char * attribs[3] ={"footnote-id",NULL,NULL};

      if(!m_bNoteIsFNote)
      {
            attribs[0] = "endnote-id";
      }
      UT_String footpid;
      if(m_bInFootnote && !m_bFtnReferencePending)
      {
            UT_String_sprintf(footpid,"%i",m_iLastFootnoteId-1);
            attribs[1] = footpid.c_str();

            if(m_bNoteIsFNote)
            {
                  _appendField ("footnote_anchor",attribs);
                  return;
            }
            else
            {
                  _appendField ("endnote_anchor",attribs);
                  return;
            }
      }
      else if(m_bInFootnote && m_bFtnReferencePending)
      {
            // we have a pending reference mark; since the \footnote
            // has removed the RTF state, we need to temporarily
            // place the saved RTF state on the stack. We pop it afterwards
            RTFStateStore * pSaved = m_currentRTFState.clone();
            m_stateStack.push(pSaved);
            m_stateStack.push(&m_FootnoteRefState);
            m_currentRTFState = m_FootnoteRefState;
            m_iLastFootnoteId = getDoc()->getUID(UT_UniqueId::Footnote);
            UT_String_sprintf(footpid,"%i",m_iLastFootnoteId-1);
            attribs[1] = footpid.c_str();

            if(m_bNoteIsFNote)
            {
                  _appendField ("footnote_ref",attribs);
            }
            else
            {
                  _appendField ("endnote_ref",attribs);
            }
                  
            m_bFtnReferencePending = false;

            // now we pop the saved state off and restore the current state
            RTFStateStore* pState = NULL;
            m_stateStack.pop(reinterpret_cast<void**>(&pState));
            m_stateStack.pop(reinterpret_cast<void**>(&pState));
            m_currentRTFState = *pState;
            DELETEP(pState);
      }
      else
      {
            m_bFtnReferencePending = true;
//
// Save current RTF state.
//
            m_FootnoteRefState = m_currentRTFState;
      }
}

void IE_Imp_RTF::HandleNote(void)
{
      
      m_bInFootnote = true;
      if(m_bFtnReferencePending)
      {
            HandleNoteReference();
      }
      else
      {
            // if there is no reference pending, this is a note with a
            // manually set marker; we have to flush characters before
            // inserting the footnote strux
            FlushStoredChars(true);
      }
      
      m_iDepthAtFootnote = m_stateStack.getDepth();
      const XML_Char * attribs[3] ={"footnote-id",NULL,NULL};

      if(!m_bNoteIsFNote)
      {
            attribs[0] = "endnote-id";
      }
      
      UT_String footpid;
      UT_String_sprintf(footpid,"%i",m_iLastFootnoteId-1);
      attribs[1] = footpid.c_str();
      UT_DEBUGMSG(("ie_imp_RTF: Handle Footnote now \n"));

      if(!bUseInsertNotAppend())
      {
            if(m_bNoteIsFNote)
                  getDoc()->appendStrux(PTX_SectionFootnote,attribs);
            else
                  getDoc()->appendStrux(PTX_SectionEndnote,attribs);
                  
            xxx_UT_DEBUGMSG(("Append block 8 \n"));
            getDoc()->appendStrux(PTX_Block,NULL);
      }
      else
      {
            if(m_bNoteIsFNote)
                  insertStrux(PTX_SectionFootnote,attribs,NULL);
            else
                  insertStrux(PTX_SectionEndnote,attribs,NULL);
                  
            UT_DEBUGMSG((" Insert Block at 7 \n"));
            markPasteBlock();
            insertStrux(PTX_Block);
      }
}


UT_Error IE_Imp_RTF::_parseText()
{
      bool ok = true;
    int cNibble = 2;
      int b = 0;
      unsigned char c;

      // remember the depth of stack on entry, and if the depth of stack
      // drops bellow this level return (this is so that we can call
      // this method recursively)
      UT_uint32 iRTFStackDepth = m_stateStack.getDepth();
      UT_DEBUGMSG(("IE_Imp_RTF::_parseText: stack depth %d\n", iRTFStackDepth));
      
      
      while (ok  &&  m_stateStack.getDepth() >= iRTFStackDepth && ReadCharFromFile(&c))
      {
            if (m_currentRTFState.m_internalState == RTFStateStore::risBin)
            {
                  // if we're parsing binary data, handle it directly
                  ok = ParseChar(c);
            }
            else
            {
                  if(m_bFootnotePending && c != '\\')
                  {
                        // not followed by a keyword, this is an ordinary
                        // footnote
                        m_bNoteIsFNote = true;
                        HandleNote();
                        m_bFootnotePending = false;
                  }
                  else if(m_bFootnotePending && c == '\\')
                  {
                        // need to see if the keyword is \ftnalt indicating
                        // endnote
                        unsigned char keyword[MAX_KEYWORD_LEN];
                        UT_sint32 parameter = 0;
                        bool parameterUsed = false;
                        if (ReadKeyword(keyword, &parameter, &parameterUsed, MAX_KEYWORD_LEN))
                        {
                              if(0 == strcmp((const char*)&keyword[0], "ftnalt"))
                              {
                                    UT_DEBUGMSG(("Have Endnote \n"));
                                    // we have an end-note
                                    m_bNoteIsFNote = false;
                                    HandleNote();
                                    m_bFootnotePending = false;
                                    continue;
                              }
                              else
                              {
                                    // we have some other keyword ...
                                    m_bNoteIsFNote = true;
                                    HandleNote();
                                    m_bFootnotePending = false;
                                    
                                    TranslateKeyword(keyword, parameter, parameterUsed);
                                    continue;
                              }
                        }
                        else
                        {
                              // something seriously wrong ...
                              UT_DEBUGMSG(("RTF: could not read keyword (l: %d)\n", __LINE__));
                              continue;
                        }
                  }
                  
                  
                  switch (c)
                  {
                  case '{':
                        ok = PushRTFState();
                        if (!ok) {
                              UT_DEBUGMSG(("PushRTFState()\n"));
                        }
                        break;
                  case '}':
                  {
                        ok = PopRTFState();
                        if (!ok) {
                              UT_DEBUGMSG(("PopRTFState() bug\n"));
                              bool bCont = true;
                              char lastc =c;
                              while(ReadCharFromFile(&c) && bCont)
                              {
                                    lastc = c;
                                    bCont = (c == '}');
                              }
                              if(lastc == '}') // reached end of file with extra "}"
                              {
                                    ok = true;
                                    break;
                              }
#if 0 //enable to debug
                              return UT_OK; // try to finish the import anyway
#endif
                        }
                  }
                        break;
                  case '\\':
                  {
                        ok = ParseRTFKeyword();

                        if (!ok) {
                              UT_DEBUGMSG(("ParseRTFKeyword() failed import aborted \n"));
                              UT_DEBUGMSG(("Last valid keyword was %s \n",g_dbgLastKeyword));
                        }
                        break;
                  }
                              
                  default:
                        if (m_currentRTFState.m_internalState == RTFStateStore::risNorm)
                        {
                              ok = ParseChar(c);
                              if (!ok) {
                                    UT_DEBUGMSG(("ParseChar()\n"));
                              }
                        }
                        else
                        {
                              UT_return_val_if_fail(m_currentRTFState.m_internalState == RTFStateStore::risHex, UT_ERROR);

                              b = b << 4;
                              int digit;

                              // hexval calls digval
                              ok = hexVal(static_cast<char>(c), digit);
                              b += digit;
                              cNibble--;
                              if (!cNibble  &&  ok)
                              {
                                    ok = ParseChar(b,0);
                                    if (!ok) {
                                          UT_DEBUGMSG(("ParseChar()\n"));
                                    }
                                    cNibble = 2;
                                    b = 0;
                                    m_currentRTFState.m_internalState = RTFStateStore::risNorm;
                                    // actually don't handle the following space since
                                    // this is NOT a delimiter
                                    // see bug #886
                              }
                        }
                  }
            }

            if(getLoadStylesOnly() && m_bStyleImportDone)
                  break;
      }

      if (ok && !getLoadStylesOnly())
      {
            // do not force this -- a correctly formed rtf document should end in \par keyword
            // so m_newParaFlagged will be set, but there will be nothing in the buffer, so no
            // para should be output. If the doc is malformed and there is some stuff after
            // the last \par, it will be output.
            ok = FlushStoredChars(false);
            if (!ok) {
                  UT_DEBUGMSG(("FlushStoredChars()\n"));
            }
      }
//    UT_DEBUGMSG(("dumping document\n"));
//    getDoc()->__dump(stderr);
      return ok ? UT_OK : UT_ERROR;

}

/*
   Scans the entire document for any rtl tokens and set m_bBidiMode
   accordingly. Please note that this results in a minimal performance
   loss (a fraction of a second on a 30 page doc), and saves us much
   work and time if the document is LTR-only.
*/
UT_Error IE_Imp_RTF::_isBidiDocument()
{
      UT_return_val_if_fail(m_pImportFile, UT_ERROR);

      char buff[8192 + 1];
      char * token = NULL;
      
      size_t iBytes = fread((void*)&buff[0], 1, sizeof(buff) - 1, m_pImportFile);

      UT_DEBUGMSG(("IE_Imp_RTF::_isBidiDocument: looking for RTL tokens\n"));
      while (iBytes)
      {
            buff[iBytes] = 0;
            token = strstr(buff, "rtlsect");
            if(token)
            {
                  break;
            }
            
            token = strstr(buff, "rtlpar");
            if(token)
                  break;

            token = strstr(buff, "rtlch");
            if(token)
                  break;

            iBytes = fread((void*)&buff[0], 1, sizeof(buff) - 1, m_pImportFile);    
      }

      if(token)
      {
            UT_DEBUGMSG(("IE_Imp_RTF::_isBidiDocument: found rtl token [%s]\n", token));
            m_bBidiMode = true;
      }
      else
      {
            UT_DEBUGMSG(("IE_Imp_RTF::_isBidiDocument: no rtl token found\n"));
            m_bBidiMode = false;
      }
      
      
      // reset the file pointer to the beginning
      if(fseek(m_pImportFile, 0, SEEK_SET))
            return UT_ERROR;

      UT_DEBUGMSG(("IE_Imp_RTF::_isBidiDocument: looking for RTL tokens -- done\n"));
      return UT_OK;
}



UT_Error IE_Imp_RTF::_parseFile(FILE* fp)
{
      m_pImportFile = fp;

      m_currentRTFState.m_internalState = RTFStateStore::risNorm;
      m_currentRTFState.m_destinationState = RTFStateStore::rdsNorm;
      m_currentHdrID = 0;
      m_currentFtrID = 0;
      m_currentHdrEvenID = 0;
      m_currentFtrEvenID = 0;
      m_currentHdrFirstID = 0;
      m_currentFtrFirstID = 0;
      m_currentHdrLastID = 0;
      m_currentFtrLastID = 0;
#if 0
      if(m_pImportFile && UT_OK != _isBidiDocument())
            return UT_ERROR;
#endif
      if(m_pImportFile && !getLoadStylesOnly())
      {
            // need to init docs Attributes and props
            getDoc()->setAttrProp(NULL);
      }
      
//
// OK Set the Default page size, in case it isn't set in RTF
//
      if(!getLoadStylesOnly() && !m_parsingHdrFtr)
      {
            double width = 12240./1440.; // default width in twips
            double height = 15840./1440;; // default height in twips
            getDoc()->m_docPageSize.Set(width,height,DIM_IN);
      }
      return _parseText();
}

/*****************************************************************/
/*****************************************************************/

bool IE_Imp_RTF::HandleParKeyword()
{
      // NB: the \par keyword really represents '\r' and concludes a paragraph rather than
      // begins it -- some paragraph properties are indicated by formating applied to the
      // \par keyword, for example revisions are. This means that we sometimes have to
      // change fmt of the last block

      if(!m_bSectionHasPara || m_newParaFlagged)
      {
            if(m_newSectionFlagged)
                  ApplySectionAttributes();
            
            m_newSectionFlagged = false;

            ApplyParagraphAttributes();
            
            //getDoc()->appendStrux(PTX_Block,NULL);
            m_newParaFlagged = false;
            m_bSectionHasPara = true;
      }
      
      UT_String sProps;
      const XML_Char * attrs[3] = {NULL, NULL, NULL};
      const XML_Char * props = NULL;
      UT_String rev;

      UT_return_val_if_fail( buildCharacterProps(sProps), false);
      props = sProps.c_str();

      if(m_currentRTFState.m_charProps.m_eRevision != PP_REVISION_NONE)
      {
            const XML_Char * pStyle = NULL;
            
            if(m_currentRTFState.m_charProps.m_styleNumber >= 0
               && (UT_uint32)m_currentRTFState.m_charProps.m_styleNumber < m_styleTable.size())
            {
                  pStyle = static_cast<const char *>
                        (m_styleTable[m_currentRTFState.m_charProps.m_styleNumber]);
            }

            _formRevisionAttr(rev, sProps, pStyle);
            attrs[0] = "revision";
            attrs[1] = rev.c_str();
            props = NULL;
      }

      if((props && *props) || attrs[0])
      {
            if(m_pImportFile)
            {
                  UT_return_val_if_fail(getDoc()->appendLastStruxFmt(PTX_Block, attrs, props,true), false);
            }
            else
            {
                  UT_return_val_if_fail(getDoc()->changeLastStruxFmtNoUndo(m_dposPaste, PTX_Block, attrs, props, true),false );
            }
            
      }
      
      return StartNewPara();
}

// flush any remaining text in the previous para and flag
// a new para to be started.  Don't actually start a new
// para as it may turn out to be empty
//
bool IE_Imp_RTF::StartNewPara()
{
      // force this, if new para is flagged (so we import empty paragraphs)
      // if it is not, then we do not flagged, then we do not want new block appended (it
      // has already been done somewhere else
      bool ok = FlushStoredChars(m_newParaFlagged);
      m_newParaFlagged = true;

      // need to reset any left-over direction override
      m_currentRTFState.m_charProps.m_dirOverride = UT_BIDI_UNSET;
      return ok;
}


// flush any remaining text in the previous sction and
// flag a new section to be started.  Don't actually
// start a new section as it may turn out to be empty
//
bool IE_Imp_RTF::StartNewSection()
{
      // force this, if new para is flagged (so we import empty paragraphs)
      // if it is not, then we do not flagged, then we do not want new block appended (it
      // has already been done somewhere else
      bool ok = FlushStoredChars(m_newParaFlagged);

      m_newSectionFlagged = true;
      m_newParaFlagged = true;
      m_bSectionHasPara = false;
      return ok;
}


// add a new character.  Characters are actually cached and
// inserted into the document in batches - see FlushStoredChars
//
bool IE_Imp_RTF::AddChar(UT_UCSChar ch)
{
      if(!m_gbBlock.ins(m_gbBlock.getLength(), reinterpret_cast<UT_GrowBufElement*>(&ch), 1))
            return false;

      return true;
}

/*!
 * returns true if we've pasted a table strux and have not yet pasted a cell
 * or we've pasted an endcell and have not yet pasted a cell
 */
bool IE_Imp_RTF::isPastedTableOpen(void)
{
      ABI_Paste_Table * pPaste = NULL;
      if(m_pasteTableStack.getDepth() == 0)
      {
            return false;
      }
      m_pasteTableStack.viewTop(reinterpret_cast<void **>(&pPaste));
      if(pPaste == NULL)
      {
            return false;
      }
      if(!pPaste->m_bHasPastedCellStrux)
      {
            return true;
      }
      if(!pPaste->m_bHasPastedTableStrux)
      {
            return false;
      }
      return false;
}

// flush any stored text into the document
//
bool IE_Imp_RTF::FlushStoredChars(bool forceInsertPara)
{

      // start a new para if we have to
      bool ok = true;
//
// Don't insert anything if we're between a table strux and a cell or between
// cell's
//
      if(isPastedTableOpen() && !forceInsertPara)
      {
            return true;
      }
      if (m_newSectionFlagged && (forceInsertPara || (m_gbBlock.getLength() > 0)) )
      {
            m_bContentFlushed = true;
            ok = ApplySectionAttributes();
            m_newSectionFlagged = false;
      }
      if (ok  && m_newParaFlagged  &&  (forceInsertPara  ||  (m_gbBlock.getLength() > 0)) )
      {
            bool bSave = m_newParaFlagged;
            m_newParaFlagged = false;
            ok = ApplyParagraphAttributes();
            if(m_gbBlock.getLength() == 0)
            {
//
// This forces empty lines to have the same height as the previous line
//
                  m_newParaFlagged = bSave;
                  if(!bUseInsertNotAppend())
                  {
                        getDoc()->appendFmtMark();
                  }
            }
            m_newParaFlagged = false;

      }

      if (ok  &&  (m_gbBlock.getLength() > 0))
      {
            if(ok && m_bCellBlank && (getTable() != NULL))
            {
                  xxx_UT_DEBUGMSG(("Append block 10 \n"));

                  getDoc()->appendStrux(PTX_Block,NULL);
                  m_bSectionHasPara = true;
                  m_bCellBlank = false;
                  m_bEndTableOpen = false;
            }
            else if( ok && m_bEndTableOpen)
            {
                  xxx_UT_DEBUGMSG(("Append block 11 \n"));

                  getDoc()->appendStrux(PTX_Block,NULL);
                  m_bSectionHasPara = true;
                  m_bEndTableOpen = false;
            }
//          else if( ok && m_bEndFrameOpen)
//          {
//                UT_DEBUGMSG(("Append block for EndFrameOpen 12 \n"));
// CVS CONFLICT               m_bSectionHasPara = true;
//                getDoc()->appendStrux(PTX_Block,NULL);
//          }
            ok = ApplyCharacterAttributes();
            m_bCellBlank = false;
//          m_bEndFrameOpen = false;
      }
      if( ok && m_bInFootnote && (m_stateStack.getDepth() < m_iDepthAtFootnote))
      {
            if(!bUseInsertNotAppend())
            {
                  if(m_bNoteIsFNote)
                        getDoc()->appendStrux(PTX_EndFootnote,NULL);
                  else
                        getDoc()->appendStrux(PTX_EndEndnote,NULL);
                        
            }
            else
            {
                  if(m_bNoteIsFNote)
                        ok = insertStrux(PTX_EndFootnote);
                  else
                        ok = insertStrux(PTX_EndEndnote);
                        
            }
            m_bInFootnote = false;
            m_iDepthAtFootnote = 0;
      }
//    if( ok && m_bFrameOpen && (static_cast<UT_sint32>(m_stateStack.getDepth()) < m_iStackDepthAtFrame))   
//    {
//          HandleEndShape();
//    }
      return ok;
}


// Get a font out of the font table, making sure we dont attempt to access off the end
RTFFontTableItem* IE_Imp_RTF::GetNthTableFont(UT_uint32 fontNum)
{
      if (fontNum < m_fontTable.getItemCount())
      {
            return m_fontTable.getNthItem(fontNum);
      }
      else
      {
            return NULL;
      }
}


// Get a colour out of the colour table, making sure we dont attempt to access off the end
UT_uint32 IE_Imp_RTF::GetNthTableColour(UT_uint32 colNum)
{
      if (colNum < m_colourTable.getItemCount())
      {
            return m_colourTable.getNthItem(colNum);
      }
      else
      {
            return 0;   // black
      }
}

UT_sint32 IE_Imp_RTF::GetNthTableBgColour(UT_uint32 colNum)
{
      if (colNum < m_colourTable.getItemCount())
      {
            return m_colourTable.getNthItem(colNum);
      }
      else
      {
            return -1;  // invalid
      }
}


// Process a single character from the RTF stream
//
bool IE_Imp_RTF::ParseChar(UT_UCSChar ch,bool no_convert)
{
    // Have we reached the end of the binary skip?
      if (m_currentRTFState.m_internalState == RTFStateStore::risBin  && --m_cbBin <= 0)
      {
            m_currentRTFState.m_internalState = RTFStateStore::risNorm;
      }

      switch (m_currentRTFState.m_destinationState)
      {
            case RTFStateStore::rdsSkip:
                  // Toss this character.
                  return true;
            case RTFStateStore::rdsNorm:
                  if (m_currentRTFState.m_unicodeInAlternate > 0)
                  {
                        m_currentRTFState.m_unicodeInAlternate--;
                        return true;
                  }
                  // Insert a character into the story
            if ((ch >= 32  ||  ch == 9 || ch == UCS_FF || ch == UCS_LF || ch == UCS_VTAB)  &&  !m_currentRTFState.m_charProps.m_deleted)
                  {
                        if (no_convert==0 && ch<=0xff)
                        {
                              UT_UCS4Char wc;
                              // TODO Doesn't handle multibyte encodings (CJK)
                              if (m_mbtowc.mbtowc(wc,static_cast<UT_Byte>(ch)))
                                    return AddChar(wc);
                        } else
                              return AddChar(ch);
                  }
            default:
                  // handle other destinations....
                  return true;
      }
      UT_DEBUGMSG (("went thru all ParseChar() withou doing anything\n"));
      return true;
}


/*!
  Reads and proccesses a RTF control word and its parameter
  \return false if something goes wrong.
  \desc Read and handle the RTF keyword. Commands are dispatched by calling
  TranslateKeyword
  \fixme This is too generic: keywords are most of the time contextual
  so context should be taken care of.
  \see IE_Imp_RTF::ReadKeyword, IE_Imp_RTF::TranslateKeyword
*/
bool IE_Imp_RTF::ParseRTFKeyword()
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      UT_sint32 parameter = 0;
      bool parameterUsed = false;
      if (ReadKeyword(keyword, &parameter, &parameterUsed, MAX_KEYWORD_LEN))
      {
            UT_DEBUGMSG(("SEVIOR: keyword = %s  par= %d \n",keyword,parameter));
            bool bres = TranslateKeyword(keyword, parameter, parameterUsed);
            if(!bres)
            {
                  UT_DEBUGMSG(("SEVIOR: error in translation last valid %s \n",g_dbgLastKeyword));
            }
            return bres;
      }
      else
      {
            UT_DEBUGMSG(("Error in ReadKeyword Last vaild %s \n",g_dbgLastKeyword));
            return false;
      }
}


/*!
  Read a keyword from the file.
  \retval pKeyword the keyword buffer whose len is in keywordBuffLen
  Can not be NULL on input.
  \retval pParam the keyword parameter as specified by the RTF spec. 0
  is there is no param.
  \retval pParamUsed true if the keyword does really have a param. false
  otherwise (pParam is 0 then).
  \param keywordBuffLen the length of the pKeyword memory block
  \return false if any problem
  \desc This function parse and read the keyword. It is called if a
  \\ is encountered in the flow. *pKeyword never contains the \\
 */
bool IE_Imp_RTF::ReadKeyword(unsigned char* pKeyword, UT_sint32* pParam, bool* pParamUsed, UT_uint32 keywordBuffLen)
{
      bool fNegative = false;
      *pParam = 0;
      *pParamUsed = false;
      *pKeyword = 0;
      const unsigned int max_param = 256;
      unsigned char parameter[max_param];
      unsigned int count = 0;
      unsigned char * savedKeyword = pKeyword;

      // Read the first character of the control word
      unsigned char ch;
      if (!ReadCharFromFileWithCRLF(&ch))
            return false;

      UT_return_val_if_fail(keywordBuffLen > 1, false);
      --keywordBuffLen;

      // If it's a control symbol there is no delimiter, its just one character
      if (!isalpha(ch))
      {
            pKeyword[0] = ch;
            pKeyword[1] = 0;
            return true;
      }

      // Read in the rest of the control word
      while (isalpha(ch))
      {
            if (0 == --keywordBuffLen)
            {
                  UT_DEBUGMSG(("Keyword too large. Bogus RTF!\n"));
                  return false;
            }

            *pKeyword = ch;
            pKeyword++;
            if (!ReadCharFromFileWithCRLF(&ch))
                  return false;
      }
      *pKeyword = 0;

    // If the delimeter was '-' then the following parameter is negative
    if (ch == '-')
    {
        fNegative = true;
            if (!ReadCharFromFileWithCRLF(&ch))
                  return false;
    }

    // Read the numeric parameter (if there is one)
      // According to the specs, a dttm parameter (e.g. \revdttm), which is a long, has the
      // individual bytes emited as ASCII characters
      if (isdigit(ch))
      {
            *pParamUsed = true;
            while (isdigit(ch))
            {
                  // Avoid buffer overflow
                  if (count == max_param )
                  {
                        UT_DEBUGMSG(("Parameter too large. Bogus RTF!\n"));
                        return false;
                  }

                  parameter[count++] = ch;
                  if (!ReadCharFromFileWithCRLF(&ch))
                        return false;
            }
            parameter[count] = 0;

            *pParam = atol(reinterpret_cast<char*>(&parameter[0]));
            if (fNegative)
                  *pParam = -*pParam;
      }

      // If the delimeter was non-whitespace then this character is part of the following text!
      if ((ch != ' ') && (ch != 10) && (ch != 13))
      {
            SkipBackChar(ch);
      }

      strcpy(g_dbgLastKeyword, (const char *)savedKeyword);
      g_dbgLastParam = *pParam;
      xxx_UT_DEBUGMSG(("Valid Keyword %s Here \n",savedKeyword));
      return true;
}

/*!
  Reads a character from the file. Doesn't ignore CR and LF
  \retval pCh the char read
  \return false if an error occured.
  \see IE_Imp_RTF::ReadCharFromFile
*/
bool IE_Imp_RTF::ReadCharFromFileWithCRLF(unsigned char* pCh)
{     
      bool ok = false;

      if (m_pImportFile)                              // if we are reading a file
      {
            if (fread(pCh, 1, sizeof(unsigned char), m_pImportFile) > 0)
            {
                  ok = true;
            }
      }
      else                                            // else we are pasting from a buffer
      {
            if (m_pCurrentCharInPasteBuffer < m_pPasteBuffer+m_lenPasteBuffer)
            {
                  *pCh = *m_pCurrentCharInPasteBuffer++;
                  ok = true;
            }
      }

      return ok;
}

/*!
  Reads a character from the file ignoring CR and LF
  \retval pCh the char read
  \return false if an error occured.
  \see IE_Imp_RTF::ReadCharFromFileWithCRLF
*/
bool IE_Imp_RTF::ReadCharFromFile(unsigned char* pCh)
{
      // line feed and cr should be ignored in RTF files
      do
      {
            if (ReadCharFromFileWithCRLF(pCh) == false)
            {
                  return false;
            }
      } while (*pCh == 10  ||  *pCh == 13);

      return true;

}


UT_UCS4Char IE_Imp_RTF::ReadHexChar(void) 
{
      UT_UCS4Char wc = 0;
      unsigned char ch;
      int val;

      if (ReadCharFromFile(&ch))
      {
            if (hexVal(ch, val)) {
                  wc = val << 4;
            }
            else {
                  UT_DEBUGMSG(("invalid Hex %c\n", ch));
            }
            if (ReadCharFromFile(&ch))
            {
                  if (hexVal(ch, val)) {
                        wc += val;
                  }
                  else {
                        UT_DEBUGMSG(("invalid Hex %c\n", ch));
                  }
            }
      }
      return wc;
}

/*!
  Push a char back to the stream.
  \param ch the char to push back
  \return false if any problem
  \desc Push back the char ch to the stream so it can be re-read
  after. Since we use buffered stdio from lib C, there should be
  no noticeable I/O impact.
  \fixme check that ungetc() works under MacOS
 */
bool IE_Imp_RTF::SkipBackChar(unsigned char ch)
{
      if (m_pImportFile)                              // if we are reading a file
      {
            // TODO - I've got a sneaking suspicion that this doesn't work on the Macintosh
            return (ungetc(ch, m_pImportFile) != EOF);
      }
      else                                            // else we are pasting from a buffer
      {
            bool bStatus = (m_pCurrentCharInPasteBuffer > m_pPasteBuffer);
            if (bStatus)
                  m_pCurrentCharInPasteBuffer--;
            return bStatus;
      }
}


/*!
  Skip the current group
  \param  bConsumeLastBrace pass true to discard the last }
  \return false if any problem raised
  \desc This function read until the current group and all nested
  subgroups are passed. This allow skipping a chunk of the RTF file
  we do not understand.
 */
bool IE_Imp_RTF::SkipCurrentGroup(bool bConsumeLastBrace)
{
      int nesting = 1;
      unsigned char ch;

      do {
            if (!ReadCharFromFileWithCRLF(&ch))
                  return false;

            if (ch == '{')
            {
                  ++nesting;
            }
            else if (ch == '}')
            {
                  --nesting;
            }
      } while (nesting > 0);

      // to avoid corrupting the state stack
      // ( the caller indicates whether this is necesary or not)
      if (!bConsumeLastBrace)
            SkipBackChar(ch);

      return true;
}


/*!
  Stuff the current group into the buffer
  \param  buf the buffer to stuff RTF in.
  \return false if any problem raised
  \desc This function read until the current group and all nested
  subgroups are passed and stuff them into the buffer. This allow saving a
  chunk of the RTF file for future use.
 */
bool IE_Imp_RTF::StuffCurrentGroup(UT_ByteBuf & buf)
{
      int nesting = 1;
      unsigned char ch;

      // add an intial bracket as it is supposed to have a final
      ch = '{';
      buf.append(&ch, 1);

      do {
            if (!ReadCharFromFileWithCRLF(&ch))
                  return false;

            if (ch == '{')
            {
                  ++nesting;
            }
            else if (ch == '}')
            {
                  --nesting;
            }
            buf.append(&ch, 1);
      } while (nesting > 0);

      // we don't want the last }
      SkipBackChar(ch);

      return true;
}


/*!
  Handle a RTF field
  \return false if failed
  \desc Once the \\field has been read, handle the object contained in
  the current group. This is really tricky as fields are really
  hard to handle since most writers do whatever they want, including
  RTF code interleaved with field instruction. Thank you Microsoft
  (sorry for the rant). Call IE_Imp_RTF::_parseFldinstBlock to
  parse field instructions
  See p44 for specs.
  \see IE_Imp_RTF::_parseFldinstBlock
 */
bool IE_Imp_RTF::HandleField()
{
      RTFTokenType tokenType;
      unsigned char keyword[MAX_KEYWORD_LEN];
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      bool bUseResult = false;  // true if field instruction can not be used
      int nested = 0;           // nesting level

      int rtfFieldAttr = 0;

      typedef enum {
            fldAttrDirty = 1,
            fldAttrEdit = 2,
            fldAttrLock = 4,
            fldAttrPriv = 8
      } RTFFieldAttr;

      m_bFieldRecognized = false;

      UT_uint32 iHyperlinkOpen = m_iHyperlinkOpen;
      
      tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN);
      if (tokenType == RTF_TOKEN_ERROR)
      {
            return false;
      }

      // read the optional attribute for the field.
      while (tokenType == RTF_TOKEN_KEYWORD)
      {
            if (strcmp (reinterpret_cast<char*>(&keyword[0]), "flddirty") == 0)
            {
                  rtfFieldAttr &= fldAttrDirty;
            }
            else if (strcmp (reinterpret_cast<char*>(&keyword[0]), "fldedit") == 0)
            {
                  rtfFieldAttr &= fldAttrEdit;
            }
            else if (strcmp (reinterpret_cast<char*>(&keyword[0]), "fldlock") == 0)
            {
                  rtfFieldAttr &= fldAttrLock;
            }
            else if (strcmp (reinterpret_cast<char*>(&keyword[0]), "fldpriv") == 0)
            {
                  rtfFieldAttr &= fldAttrPriv;
            }
            else
            {
                  UT_DEBUGMSG (("RTF: Invalid keyword '%s' in field\n"));
                  // don't return as we simply skip it
            }


            tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN);
      }

      // field instruction
      if (tokenType == RTF_TOKEN_OPEN_BRACE)
      {
            UT_ByteBuf fldBuf;
            XML_Char * xmlField = NULL;
            bool gotStarKW = false;
            bool gotFldinstKW = false;
            // bUseResult will to be set to false if we encounter a field
            // instruction we know about. Otherwise, we use the result by default
            bUseResult = true;
            // since we enter a brace group, we push the RTFState.
            PushRTFState ();
            nested = 0;
            do
            {
                  tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN,false);
                  switch (tokenType)
                  {
                  case RTF_TOKEN_ERROR:
                        UT_ASSERT_NOT_REACHED();
                        return false;
                        break;
                  case RTF_TOKEN_KEYWORD:
                        if (strcmp(reinterpret_cast<const char *>(&keyword[0]), "*") == 0)
                        {
                              if (gotStarKW)
                              {
                                    UT_DEBUGMSG (("RTF: was not supposed to get '*' here\n"));
                              }
                              gotStarKW = true;
                        }
                        else if (strcmp(reinterpret_cast<const char *>(&keyword[0]), "fldinst") == 0)
                        {
                              if (!gotStarKW)
                              {
                                    UT_DEBUGMSG (("Ohoh, we were not supposed to get a 'fldinst' without a '*'. Go ahead.\n"));
                              }
                              gotFldinstKW = true;
                        }
                        else if (strcmp(reinterpret_cast<const char *>(&keyword[0]), "\\") == 0)
                        {
                              fldBuf.append (keyword, 1);
                        }
                        break;
                  case RTF_TOKEN_OPEN_BRACE:
                        nested++;
                        PushRTFState ();
                        break;
                  case RTF_TOKEN_CLOSE_BRACE:
                        nested--;
                        PopRTFState ();
                        break;
                  case RTF_TOKEN_DATA:
                        // add data to the field
                        fldBuf.append (keyword, 1);
                        break;
                  default:
                        break;
                  }
            }
            while ((tokenType != RTF_TOKEN_CLOSE_BRACE) || (nested >= 0));
            bool isXML = false;
            xmlField = _parseFldinstBlock (fldBuf, xmlField, isXML);
            bUseResult = (xmlField == NULL) && (!isXML);
            if (!bUseResult)
            {
                  bool ok;
                  xxx_UT_DEBUGMSG(("Append field type %s \n",xmlField));
                  ok = _appendField (xmlField);
                  UT_ASSERT_HARMLESS (ok);
                  // we own xmlField, so we delete it after use.
                  FREEP (xmlField);
            }
      }
      else
      {
            xxx_UT_DEBUGMSG (("RTF: Field instruction not present. Found '%s' in stream\n", keyword));
            UT_ASSERT_HARMLESS (UT_SHOULD_NOT_HAPPEN);
            // continue
      }

      tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN,false);
      if (tokenType == RTF_TOKEN_ERROR)
      {
            return false;
      }

      // field result
      // TODO: push and pop the state as expected.
      if (tokenType == RTF_TOKEN_OPEN_BRACE)
      {
            PushRTFState();
            tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN);
            if (tokenType == RTF_TOKEN_ERROR)
            {
                  return false;
            }
            if (tokenType == RTF_TOKEN_KEYWORD)
            {
                  // here we expect fldrslt keyword, nothing else
                  if (strcmp (reinterpret_cast<char*>(&keyword[0]), "fldrslt") != 0)
                  {
                        UT_DEBUGMSG (("RTF: Invalid keyword '%s' in field\n"));
                        // don't return as we simply skip it
                  }
                  else
                  {
                        if(m_bFieldRecognized && (m_iHyperlinkOpen== 0))
                        {
                              SkipCurrentGroup(false);
                              return true;
                        }
                  }
            }
            
            // The original code parsing the result was not enough: the
            // field result can contain full-blown rtf markup, including
            // other fields, etc. That means that we have to parse it just
            // like we do ordinary text.
            if(bUseResult)
            {
                  if(UT_OK != _parseText())
                        return false;
            }
      }
      else if(tokenType == RTF_TOKEN_CLOSE_BRACE)
      {
            PopRTFState ();
      }
      else
      {
            UT_DEBUGMSG (("RTF: Field result not present. Found '%s' in stream. Ignoring.\n", keyword));
            // UT_ASSERT_HARMLESS (UT_SHOULD_NOT_HAPPEN);
            // continue
      }

      if(m_iHyperlinkOpen > iHyperlinkOpen)
      {
            FlushStoredChars(true);

            if (!bUseInsertNotAppend()) 
            {
                  if(m_bCellBlank || m_bEndTableOpen)
                  {
                        xxx_UT_DEBUGMSG(("Append block 14 \n"));

                        getDoc()->appendStrux(PTX_Block,NULL);
                        m_bCellBlank = false;
                        m_bEndTableOpen = false;
                  }
                  getDoc()->appendObject(PTO_Hyperlink, NULL);
            }
            else 
            {
                  const XML_Char * props[] = {"list-tag","dummy",NULL};
                  getDoc()->insertObject(m_dposPaste, PTO_Hyperlink, props, NULL);
                  m_dposPaste++;
            }
            m_iHyperlinkOpen--;
            UT_ASSERT_HARMLESS( m_iHyperlinkOpen == iHyperlinkOpen );
      }
            
      return true;
}


/*!
  \param buf the buffer that contains the RTF.
  \param xmlField the XML attributes for the field.
  \param isXML whether xmlField is used or not.
  \see IE_Imp_RTF::HandleField
 */
XML_Char *IE_Imp_RTF::_parseFldinstBlock (UT_ByteBuf & buf, XML_Char *xmlField, bool & isXML)
{
      // this is quite complex as field instructions are not really document in the RTF specs.
      // we will guess as much us possible.
      // thing that complexify is that a field instruction can contain nested block and
      // DATA blocks. We will try to limit first to the standard fields.

      // Here are a couple of examples from the spec
      /*
        {\field {\*\fldinst AUTHOR \\*MERGEFORMAT    }{\fldrslt Joe Smith}}\par\pard
        {\field{\*\fldinst time \\@ "h:mm AM/PM"}{\fldrslt 8:12 AM}}
        {\field{\*\fldinst NOTEREF _RefNumber } {\fldrslt 1}}
        {\field{\*\fldinst NOTEREF _RefNumber \fldalt } {\fldrslt I}}
      */
      // Here is an example from StarOffice 5.2 export: an Hyperlink
      /*
        {\field{\*\fldinst HYPERLINK "http://www.sas.com/techsup/download/misc/cleanwork.c" }
        {\fldrslt \*\cs21\cf1\ul http://www.sas.com/techsup/download/misc/cleanwork.c}}
      */
      // This time it is an image: StarOffice exports images in .jpg as a separate file
      /*
        {\field\fldpriv{\*\fldinst{\\import sv8968971.jpg}}{\fldrslt }}
      */
      /*
        OpenOffice/StarOffice 6 do this in a similar way. See OpenOffice bug 2244.
      */
      /* Microsoft doc on field usages in Word is at:
         <http://support.microsoft.com/support/word/usage/fields/>
      */

      /* IMPORTANT: field results can contain full-blown rtf markup,
         incuding embeded field, etc. For instnace the result for a TOC
         field contains hyperlinks that allow jumping from the TOC to a
         relevant page. This means that the result cannot be simply
         pasted into the document
      */

      char *instr;
      char *newBuf;
      UT_String Instr;
      UT_uint32  len;
      isXML = false;

      // buffer is empty, nothing to parse
      if (buf.getLength() == 0)
      {
            FREEP (xmlField);
            return NULL;
      }

      len = buf.getLength ();
      const UT_Byte *pBuf = buf.getPointer (0);

      newBuf =  static_cast<char *>(malloc (sizeof (char) * (len + 1)));
      memcpy (newBuf, pBuf, len);
      newBuf [len] = 0;
      Instr = newBuf;
      instr = const_cast<char *>(Instr.c_str());
      instr = strtok (instr, " \\{}"); // This writes a NULL into Instr somewhere
                                       // I assume this is OK since the char storage
                                       // Within the class is contiguous.
      if (instr == NULL)
      {
            free (newBuf);
            free (xmlField);
            return NULL;
      }

      switch (*instr)
      {
      case 'A':
            if (strcmp (instr, "AUTHOR") == 0)
            {
                  UT_DEBUGMSG (("RTF: AUTHOR fieldinst not handled yet\n"));
            }
            break;
      case 'F':
            if (strcmp (instr, "FILENAME") == 0)
            {
                  // TODO handle parameters
                  xmlField = UT_strdup ("file_name");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            break;
      case 'H':
            if (strcmp (instr, "HYPERLINK") == 0)
            {
                  xxx_UT_DEBUGMSG (("RTF: HYPERLINK fieldinst not handled yet\n"));
                  // set these so that HandleField outputs the result
                  xmlField = NULL;
                  isXML = false;
                  
                  instr = strtok(0, " \\{}");
                  const XML_Char *new_atts[3];

                  new_atts[0] = "xlink:href";
                  UT_String href;
                  if ( !strcmp(instr, "l") )
                  {
                        instr = strtok (NULL, " \\{}");
                        href = "#";
                  }
                  else
                  {
                        href.clear();
                  }

                  // the full address is enclosed in quotation marks,
                  // which need to be removed
                  if(*instr == '\"')
                        instr++;

                  if(instr[strlen(instr)-1])
                        instr[strlen(instr)-1] = 0;

                  href += instr;

                  UT_String full_href;

                  const char * s = href.c_str();

                  if(*s != '#' && !UT_isUrl(s))
                  {
                        // TODO we are dealing with a relative URL; until AW
                        // can handle relative URLs we will convert it into an
                        // absolute one
                        full_href = m_hyperlinkBase;
                        const char * s2 = full_href.c_str();
                        
                        if(*s != '/' && s2[strlen(s2)-1] != '/')
                        {
                              full_href += '/';
                              full_href += s;
                        }
                        else if(*s == '/' && s2[strlen(s2)-1] == '/')
                        {
                              full_href += (s+1);
                        }
                        else
                        {
                              full_href += s;
                        }
                        
                        s = full_href.c_str();
                  }
                  
                  new_atts[1] = s;
                  new_atts[2] = 0;

                  FlushStoredChars(true);

                  if (!bUseInsertNotAppend()) 
                  {

                        if(m_bCellBlank || m_bEndTableOpen)
                        {
                              xxx_UT_DEBUGMSG(("Append block 15 \n"));

                              getDoc()->appendStrux(PTX_Block,NULL);
                              m_bCellBlank = false;
                              m_bEndTableOpen = false;
                        }
                        getDoc()->appendObject(PTO_Hyperlink, new_atts);
                  }
                  else 
                  {
                        getDoc()->insertObject(m_dposPaste, PTO_Hyperlink, new_atts, NULL);
                        m_dposPaste++;
                  }
                  m_iHyperlinkOpen++;
            }
            break;
      case 'I':
            if (strcmp (instr, "INCLUDEPICTURE") == 0)
            {
                  UT_DEBUGMSG (("RTF: INCLUDEPICTURE fieldinst not handled yet\n"));
            }
            break;
      case 'P':
            if (strcmp (instr, "PAGE") == 0)
            {
                  xmlField = UT_strdup ("page_number");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            else if (strcmp (instr, "PRIVATE") == 0)
            {
                  UT_DEBUGMSG (("RTF: PRIVATE fieldinst not handled yet\n"));
            }
            break;
      case 'N':
            if (strcmp (instr, "NUMCHARS") == 0)
            {
                  xmlField = UT_strdup ("char_count");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            // this one have been found with ApplixWare exported RTF.
            else if (strcmp (instr, "NUMPAGES") == 0)
            {
                  xmlField = UT_strdup ("page_count");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            else if (strcmp (instr, "NUMWORDS") == 0)
            {
                  xmlField = UT_strdup ("word_count");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            break;
      case 'S':
            if (strcmp (instr, "SAVEDATE") == 0)
            {
                  xmlField = UT_strdup ("date_dfl");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            break;
      case 'T':
            if (strcmp (instr, "TIME") == 0)
            {
                  // Some Parameters from MS Word 2000 output

                  if(strstr(newBuf,"dddd, MMMM dd, yyyy") != NULL)
                  {
                        xmlField = UT_strdup("date");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"m/d/yy") != NULL)
                  {
                        xmlField = UT_strdup("date_ddmmyy");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"MMMM d, yyyy") != NULL)
                  {
                        xmlField = UT_strdup("date_mdy");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"MMM d, yy") != NULL)
                  {
                        xmlField = UT_strdup("date_mthdy");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"MMM d, yy") != NULL)
                  {
                        xmlField = UT_strdup("date_mthdy");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"MM-d-yy") != NULL)
                  {
                        xmlField = UT_strdup("date_ntdfl");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"HH:mm:ss") != NULL)
                  {
                        xmlField = UT_strdup("time_miltime");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else if( strstr(newBuf,"h:mm:ss am/pm") != NULL)
                  {
                        xmlField = UT_strdup("time_ampm");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
//
// Make this the second last one since it's not unique
//
                  else if( strstr(newBuf,"dddd") != NULL)
                  {
                        xmlField = UT_strdup("date_wkday");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
                  else
                  {
                        xmlField = UT_strdup ("time");
                        UT_ASSERT_HARMLESS (xmlField);
                        isXML = (xmlField != NULL);
                  }
            }
            else if (strcmp (instr, "TOC") == 0)
            {
                  // Table-of-contents field
                  UT_DEBUGMSG (("RTF: TOC fieldinst not handled yet\n"));
            }
            
            break;
      case 'd':
            if (strcmp (instr, "date") == 0)
            {
                  // TODO handle parameters
                  xmlField = UT_strdup ("date");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            break;
      case '\\':
            /* mostly StarOffice RTF fields */
            if (strcmp (instr, "\\filename") == 0)
            {
                  xmlField = UT_strdup ("file_name");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            else if (strcmp (instr, "\\import") == 0)
            {
                  // need to read the filename.
                  UT_DEBUGMSG (("importing StarOffice image\n"));

                  if (m_szFileDirName     != NULL)
                  {
                        char * fileName = NULL;
                        char * tok  = strtok (NULL, " ");
                        fileName = UT_catPathname (m_szFileDirName, tok);
                        UT_DEBUGMSG (("fileName is %s\n", fileName));

                        bool ok = FlushStoredChars ();
                        if (ok)
                        {
                              // insert the image in the piece table AFTER flushing
                              // current output
                              IE_ImpGraphic * pGraphicImporter = NULL;
                              FG_Graphic* pFG;
                              UT_Error error = IE_ImpGraphic::constructImporter(fileName, IEGFT_JPEG, &pGraphicImporter);

                              // load file to buffer
                              if (error == UT_OK)
                              {
                                    RTFProps_ImageProps imgProps;
                                    error = pGraphicImporter->importGraphic(fileName, &pFG);
                                    DELETEP(pGraphicImporter);
                                    UT_ByteBuf * buf;
                                    buf = static_cast<FG_GraphicRaster *>(pFG)->getRaster_PNG();
                                    ok = InsertImage (buf, fileName, imgProps);
                              }
                              else
                              {
                                    UT_DEBUGMSG (("RTF: error while importing SO image: %d\n", error));
                              }
                        }
                        else
                        {
                              UT_DEBUGMSG (("RTF: we don't know the current filename path\n"));
                        }
                        FREEP (fileName);
                  }
            }
            else if (strcmp (instr, "\\page") == 0)
            {
                  xmlField = UT_strdup ("page_number");
                  UT_ASSERT_HARMLESS (xmlField);
                  isXML = (xmlField != NULL);
            }
            break;
      default:
            UT_DEBUGMSG (("RTF: unhandled fieldinstr %s\n", instr));
            break;
      }
      free (newBuf);
      return xmlField;
}


/*!
  Handle a header
  \retvalue header return the created header, for information
  purpose since it belongs to the header/footer table
  \note it does not set the RTF state.
 */
bool IE_Imp_RTF::HandleHeaderFooter(RTFHdrFtr::HdrFtrType hftype, UT_uint32 & headerID)
{
      RTFHdrFtr * header;
      UT_DEBUGMSG(("SEVIOR: Doing handle header/footer \n"));
      header = new RTFHdrFtr ();
      header->m_type = hftype;
      UT_uint32 id = getDoc()->getUID(UT_UniqueId::HeaderFtr);
#if 0
      while(id < 10000)
      {
            id  = UT_rand();
      }
#endif
      header->m_id = id;

      m_hdrFtrTable.addItem (header);
      headerID = header->m_id;

      switch (hftype)
      {
      case RTFHdrFtr::hftHeader:
            UT_DEBUGMSG(("RTF: \\header stuffed into %d\n",headerID));
            m_currentHdrID = headerID;
            break;
      case RTFHdrFtr::hftHeaderEven:
            UT_DEBUGMSG(("RTF: \\header Even stuffed into %d\n",headerID));
            m_currentHdrEvenID = headerID;
            break;
      case RTFHdrFtr::hftHeaderFirst:
            UT_DEBUGMSG(("RTF: \\header First stuffed into %d\n",headerID));
            m_currentHdrFirstID = headerID;
            break;
      case RTFHdrFtr::hftHeaderLast:
            UT_DEBUGMSG(("RTF: \\header Last stuffed into %d\n",headerID));
            m_currentHdrLastID = headerID;
            break;
      case RTFHdrFtr::hftFooter:
            UT_DEBUGMSG(("RTF: \\footer stuffed into %d\n",headerID));
            m_currentFtrID = headerID;
            break;
      case RTFHdrFtr::hftFooterEven:
            UT_DEBUGMSG(("RTF: \\footer Even stuffed into %d\n",headerID));
            m_currentFtrEvenID = headerID;
            break;
      case RTFHdrFtr::hftFooterFirst:
            UT_DEBUGMSG(("RTF: \\footer stuffed into %d\n",headerID));
            m_currentFtrFirstID = headerID;
            break;
      case RTFHdrFtr::hftFooterLast:
            UT_DEBUGMSG(("RTF: \\footer stuffed into %d\n",headerID));
            m_currentFtrLastID = headerID;
            break;
      default:
            UT_ASSERT_NOT_REACHED();
      }

      // read the whole group content and put it into a buffer to
      // decode it later, when appending footer to the document.
      return StuffCurrentGroup (header->m_buf);
}


// Test the keyword against all the known handlers
bool IE_Imp_RTF::TranslateKeyword(unsigned char* pKeyword, UT_sint32 param, bool fParam)
{
      // switch on the first char to reduce the number of string comparisons
      // NB. all RTF keywords are lowercase.
      // after handling the keyword, return true

      // When adding keywords expressing document properties, maker sure
      // that if we are only loading styles, these are ignored
      // (the docs say these can be scattered among the header tables)
      xxx_UT_DEBUGMSG(("Translating keyword %s \n",pKeyword));
      RTF_KEYWORD_ID keywordID = KeywordToID(reinterpret_cast<char *>(pKeyword));
      return TranslateKeywordID(keywordID, param, fParam);
}



bool IE_Imp_RTF::TranslateKeywordID(RTF_KEYWORD_ID keywordID, 
                                                  UT_sint32 param, bool fParam)
{
      switch (keywordID)
      {
      case RTF_KW_ansicpg:
      {
            const char *szEncoding = NULL;
            if(param == -1)
            {
                  // IE issues this value on copy (ctrl+c), and I could not find out from anywhere what it is
                  // supposed to mean; I will assume it means use the current system page
                  szEncoding = XAP_EncodingManager::get_instance()->getNative8BitEncodingName();
            }
            else
            {
                  szEncoding = XAP_EncodingManager::get_instance()->charsetFromCodepage(static_cast<UT_uint32>(param));
            }
            
            m_mbtowc.setInCharset(szEncoding);
            
            if(!getLoadStylesOnly()) {
                  getDoc()->setEncodingName(szEncoding);
            }
            return true;
      }
      case RTF_KW_abitopline:
            return HandleTopline(true);
      case RTF_KW_abibotline:
            return HandleBotline(true);
      case RTF_KW_abinodiroverride:
      {
// this keyword will be immediately followed by either the
// ltrch or rtlch keyword, which we need to eat up ...
            unsigned char kwrd[MAX_KEYWORD_LEN];
            UT_sint32 par = 0;
            bool parUsed = false;
            bool ok = true;
            unsigned char c;
            
// swallow "\" first
            ok = ReadCharFromFileWithCRLF(&c);
            if (ReadKeyword(kwrd, &par, &parUsed, MAX_KEYWORD_LEN))
            {
                  if(!(0 == strncmp((const char*)&kwrd[0],"rtlch",MAX_KEYWORD_LEN) ||
                         0 == strncmp((const char*)&kwrd[0],"ltrch",MAX_KEYWORD_LEN)))
                  {
                        UT_DEBUGMSG(("RTF import: keyword \\%s found where \\ltrch"
                                           " or \\rtlch expected\n", kwrd));
                  }
            }
            xxx_UT_DEBUGMSG(("abinoveride found - swallowed keyword %s \n",kwrd));
            return true;
      }
      case RTF_KW_ansi:
      {
            // this is charset Windows-1252
            const char *szEncoding = XAP_EncodingManager::get_instance()->charsetFromCodepage(1252);
            m_mbtowc.setInCharset(szEncoding);
            if(!getLoadStylesOnly())
                  getDoc()->setEncodingName(szEncoding);
            return true;
      }
      case RTF_KW_aendnotes:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-place-endsection", "1",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aenddoc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-place-enddoc", "1",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnstart:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-initial", NULL,
                                                            NULL};
                  UT_String i;
                  UT_String_sprintf(i,"%d",param);
                  props[1] = i.c_str();
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnrestart:
            if(!getLoadStylesOnly())
            {
                  
                  const XML_Char * props[] = {"document-endnote-restart-section", "1",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnnar:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-type", "numeric",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnnalc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-type", "lower",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnnauc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-type", "upper",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnnrlc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-endnote-type", "lower-roman",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_aftnnruc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-type", "upper-roman",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_b:
            // bold - either on or off depending on the parameter
            return HandleBold(fParam ? false : true);
      case RTF_KW_bullet:
            return ParseChar(UCS_BULLET);
      case RTF_KW_brdrs:
            if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderTop)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"top-style","solid");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderLeft)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"left-style","solid");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderBot)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"bot-style","solid");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderRight)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"right-style","solid");
            }
            return true;
      case RTF_KW_brdrdot:
            if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderTop)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"top-style","dotted");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderLeft)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"left-style","dotted");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderBot)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"bot-style","dotted");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderRight)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"right-style","dotted");
            }
            return true;
      case RTF_KW_brdrdash:
            if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderTop)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"top-style","dashed");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderLeft)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"left-style","dashed");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderBot)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"bot-style","dashed");
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderRight)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"right-style","dashed");
            }
            return true;
      case RTF_KW_brdrw:
      {
            double dWidth = static_cast<double>(param)/1440; // convert to inches
            UT_String sWidth;
            {
                  UT_LocaleTransactor(LC_NUMERIC, "C");
                  UT_String_sprintf(sWidth,"%fin",dWidth);
            }
            if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderTop)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"top-thickness",sWidth.c_str());
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderLeft)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"left-thickness",sWidth.c_str());
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderBot)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"bot-thickness",sWidth.c_str());
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderRight)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"right-thickness",sWidth.c_str());
            }
            return true;
      }
      case RTF_KW_brdrcf:
      {
            UT_String sColor;
            UT_sint32 iCol = static_cast<UT_sint32>(param);
            UT_uint32 colour = GetNthTableColour(iCol);
            UT_String_sprintf(sColor, "%06x", colour);
            if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderTop)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"top-color",sColor.c_str());
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderLeft)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"left-color",sColor.c_str());
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderBot)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"bot-color",sColor.c_str());
            }
            else if (m_currentRTFState.m_cellProps.m_iCurBorder == rtfCellBorderRight)
            {
                  _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"right-color",sColor.c_str());
            }
            return true;
      }
      case RTF_KW_colortbl:
            return ReadColourTable();
      case RTF_KW_cf:
            return HandleColour(fParam ? param : 0);
      case RTF_KW_cb:
            return HandleBackgroundColour (fParam ? param : 0);
      case RTF_KW_cols:
            m_currentRTFState.m_sectionProps.m_numCols = static_cast<UT_uint32>(param);
            return true;
      case RTF_KW_colsx:
            m_currentRTFState.m_sectionProps.m_colSpaceTwips = static_cast<UT_uint32>(param);
            return true;
      case RTF_KW_column:
            return ParseChar(UCS_VTAB);
      case RTF_KW_chdate:
            return _appendField ("date");
      case RTF_KW_chtime:
            return _appendField ("time");
      case RTF_KW_chdpl:
      {
            const XML_Char * attribs[3] ={"param",NULL,NULL};
            attribs[1] = "%A, %B %d, %Y";
            return _appendField ("datetime_custom", attribs);
      }
      case RTF_KW_chdpa:
      {
            const XML_Char * attribs[3] ={"param",NULL,NULL};
            attribs[1] = "%a, %b %d, %Y";
            return _appendField ("datetime_custom");
      }
      case RTF_KW_chpgn:
            return _appendField ("page_number");
      case RTF_KW_chftn:
            HandleNoteReference();
      case RTF_KW_cs:
            m_currentRTFState.m_charProps.m_styleNumber = param;
            return true;
      case RTF_KW_cell:
            xxx_UT_DEBUGMSG(("SEVIOR: Processing cell \n"));
            HandleCell();
            return true;
      case RTF_KW_cellx:
            HandleCellX(param);
            return true;
      case RTF_KW_clvmrg:
            xxx_UT_DEBUGMSG(("Found Vertical merge cell clvmrg \n"));
            m_currentRTFState.m_cellProps.m_bVerticalMerged = true;
            return true;
      case RTF_KW_clvmgf:
            xxx_UT_DEBUGMSG(("Found Vertical merge cell first clvmgf \n"));
            m_currentRTFState.m_cellProps.m_bVerticalMergedFirst = true;
            return true;
      case RTF_KW_clmrg:
            m_currentRTFState.m_cellProps.m_bHorizontalMerged = true;
            return true;
      case RTF_KW_clmgf:
            m_currentRTFState.m_cellProps.m_bHorizontalMergedFirst = true;
            return true;
      case RTF_KW_clbrdrt:
            m_currentRTFState.m_cellProps.m_iCurBorder = rtfCellBorderTop;
            return true;
      case RTF_KW_clbrdrl:
            m_currentRTFState.m_cellProps.m_iCurBorder = rtfCellBorderLeft;
            return true;
      case RTF_KW_clbrdrb:
            m_currentRTFState.m_cellProps.m_iCurBorder = rtfCellBorderBot;
            return true;
      case RTF_KW_clbrdrr:
            m_currentRTFState.m_cellProps.m_iCurBorder = rtfCellBorderRight;
            return true;
      case RTF_KW_clcbpat:
      {
            UT_String sColor;
            UT_sint32 iCol = static_cast<UT_sint32>(param);
            UT_uint32 colour = GetNthTableColour(iCol);
            UT_String_sprintf(sColor, "%06x", colour);
            xxx_UT_DEBUGMSG(("Writing background color %s to properties \n",sColor.c_str()));
            _setStringProperty(m_currentRTFState.m_cellProps.m_sCellProps,"background-color",sColor.c_str());
      }
      case RTF_KW_dn:
            // subscript with position. Default is 6.
            // superscript: see up keyword
            return HandleSubscriptPosition (fParam ? param : 6);
      case RTF_KW_emdash:
            return ParseChar(UCS_EM_DASH);
      case RTF_KW_endash:
            return ParseChar(UCS_EN_DASH);
      case RTF_KW_emspace:
            return ParseChar(UCS_EM_SPACE);
      case RTF_KW_enspace:
            return ParseChar(UCS_EN_SPACE);
      case RTF_KW_endnotes:
      {
            const XML_Char * props[] = {"document-endnote-place-endsection", "1",
                                                      NULL};
            getDoc()->setProperties(&props[0]);
      }
      break;
      case RTF_KW_enddoc:
      {
            const XML_Char * props[] = {"document-endnote-place-enddoc", "1",
                                                      NULL};
            getDoc()->setProperties(&props[0]);
      }
      break;
      case RTF_KW_fonttbl:
            // read in the font table
            return ReadFontTable();
      case RTF_KW_fs:
            return HandleFontSize(fParam ? param : 24);
      case RTF_KW_f:
            return HandleFace(fParam ? param : 0); // TODO read the deff prop and use that instead of 0
      case RTF_KW_fi:
            m_currentRTFState.m_paraProps.m_indentFirst = param;
            return true;
      case RTF_KW_field:
            return HandleField ();
      case RTF_KW_fldrslt:
            if(m_bFieldRecognized && (m_iHyperlinkOpen== 0))
            {
//
// skip this until the next "}"
//
                  SkipCurrentGroup();
            }
            else
            {
//
// Just parse the text found
//
                  return true;
            }
            break;
      case RTF_KW_footer:
      {
            UT_uint32 footerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftFooter, footerID);
      }
      case RTF_KW_footerf:
      {
            UT_uint32 footerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftFooterFirst, footerID);
      }
      case RTF_KW_footerr:
      {
            UT_uint32 footerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftFooterEven, footerID);
      }
      case RTF_KW_footerl:
      {
            UT_uint32 footerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftFooter, footerID);
      }
      case RTF_KW_footery:
            // Height ot the header in twips
            m_currentRTFState.m_sectionProps.m_footerYTwips = param;
            break;
      case RTF_KW_gutter:
            // Gap between text and left (or right) margin in twips
            m_currentRTFState.m_sectionProps.m_gutterTwips = param;
            break;
      case RTF_KW_footnote:
            // can be both footnote and endnote ...
// No pasting footnotes/endnotes in HdrFtrs
            if(bUseInsertNotAppend())
            {
                  XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
                  if(pFrame == NULL)
                  {
                        m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                        return true;
                  }
                  FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
                  if(pView == NULL)
                  {
                        m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                        return true;
                  }
                  if(pView->isHdrFtrEdit())
                  {
                        m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                        return true;
                  }
            }

            m_bFootnotePending = true;
            return true;
      case RTF_KW_ftnalt:
            // should not be here, since this keyword is supposed to
            // follow \footnote and is handled separately
            UT_DEBUGMSG(("RTF Keyword \'ftnalt\' where it should not have been\n"));
            return true;
      case RTF_KW_ftnstart:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-initial", NULL,
                                                            NULL};
                  UT_String i;
                  UT_String_sprintf(i,"%d",param);
                  props[1] = i.c_str();
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnrstpg:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-restart-page", "1",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnrestart:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-restart-section", "1",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnnar:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-type", "numeric",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnnalc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-type", "lower",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnnauc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-type", "upper",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnnrlc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-type", "lower-roman",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_ftnnruc:
            if(!getLoadStylesOnly())
            {
                  const XML_Char * props[] = {"document-footnote-type", "upper-roman",
                                                            NULL};
                  getDoc()->setProperties(&props[0]);
            }
            break;
      case RTF_KW_headery:
            // Height ot the header in twips
            m_currentRTFState.m_sectionProps.m_headerYTwips = param;
            break;
      case RTF_KW_header:
      {
            UT_uint32 headerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftHeader, headerID);
      }
      case RTF_KW_headerf:
      {
            UT_uint32 headerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftHeaderFirst, headerID);
      }
      case RTF_KW_headerr:
      {
            UT_uint32 headerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftHeaderEven, headerID);
      }
      case RTF_KW_headerl:
      {
            UT_uint32 headerID = 0;
            return HandleHeaderFooter (RTFHdrFtr::hftHeader, headerID);
      }
      case RTF_KW_highlight:
            return HandleBackgroundColour(param);
      case RTF_KW_i:
            // italic - either on or off depending on the parameter
            return HandleItalic(fParam ? false : true);
      case RTF_KW_info:
            // TODO Ignore document info for the moment
            return HandleInfoMetaData();
      case RTF_KW_ilvl:
            m_currentRTFState.m_paraProps.m_iOverrideLevel = static_cast<UT_uint32>(param);
            return true;
      case RTF_KW_intbl:
            UT_DEBUGMSG(("done intbl \n"));
            m_currentRTFState.m_paraProps.m_bInTable = true;
            return true;
      case RTF_KW_itap:
            if(bUseInsertNotAppend())
            {
                  return true;
            }
            if(m_bInFootnote)
            {
                  return true;
            }

            m_currentRTFState.m_paraProps.m_tableLevel = param;
//
// Look to see if the nesting level of our tables has changed.
//
            xxx_UT_DEBUGMSG(("SEVIOR!!! itap m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
            if(m_currentRTFState.m_paraProps.m_tableLevel > m_TableControl.getNestDepth())
            {
                  while(m_currentRTFState.m_paraProps.m_tableLevel > m_TableControl.getNestDepth())
                  {
                        xxx_UT_DEBUGMSG(("SEVIOR: Doing itap OpenTable \n"));
                        OpenTable();
                  }
            }
            else if((m_currentRTFState.m_paraProps.m_tableLevel >= 0) && m_currentRTFState.m_paraProps.m_tableLevel < m_TableControl.getNestDepth())
            {
                  while(m_currentRTFState.m_paraProps.m_tableLevel < m_TableControl.getNestDepth())
                  {
                        CloseTable();
                  }

                  if(param == 0)
                  {
                        // we closed the highest level table; in rtf table is equivalent to a block,
                        // while our table is contained in a block
                        // so we have to insert a block to get the same effect
                        StartNewPara();
                  }
            
            }
            xxx_UT_DEBUGMSG(("SEVIOR!!! After itap m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));

            return true;
      case RTF_KW_lquote:
            return ParseChar(UCS_LQUOTE);
      case RTF_KW_ldblquote:
            return ParseChar(UCS_LDBLQUOTE);
      case RTF_KW_li:
            m_currentRTFState.m_paraProps.m_indentLeft = param;
            return true;
      case RTF_KW_line:
            return ParseChar(UCS_LF);
      case RTF_KW_linebetcol:
            m_currentRTFState.m_sectionProps.m_bColumnLine = true;
            return true;
      case RTF_KW_lang:
            xxx_UT_DEBUGMSG(("DOM: lang code (0x%x, %s)\n", param, wvLIDToLangConverter(static_cast<unsigned short>(param))));
            // mark language for spell checking
            m_currentRTFState.m_charProps.m_szLang = wvLIDToLangConverter(static_cast<unsigned short>(param));
            return true;
      case RTF_KW_listoverridetable:
            return ReadListOverrideTable();
      case RTF_KW_listtext:
            // This paragraph is a member of a list.
            SkipCurrentGroup( false);
            return true;
      case RTF_KW_ls:
            // This paragraph is a member of a list.
            m_currentRTFState.m_paraProps.m_iOverride = static_cast<UT_uint32>(param);
            m_currentRTFState.m_paraProps.m_isList = true;
            return true;
      case RTF_KW_landscape:
        // TODO
        // Just set landscape mode.
        //
            break;
      case RTF_KW_ltrpar:
            xxx_UT_DEBUGMSG(("rtf imp.: ltrpar\n"));
            m_currentRTFState.m_paraProps.m_dir = UT_BIDI_LTR;
            //reset doc bidi attribute
            m_bBidiMode = false;
            return true;
      case RTF_KW_ltrsect:
            xxx_UT_DEBUGMSG(("rtf imp.: ltrsect\n"));
            m_currentRTFState.m_sectionProps.m_dir = UT_BIDI_LTR;
            return true;
      case RTF_KW_ltrmark:
      case RTF_KW_ltrch:
            xxx_UT_DEBUGMSG(("rtf imp.: ltrch\n"));
            m_currentRTFState.m_charProps.m_dir = UT_BIDI_LTR;
            
            // we enter bidi mode if we encounter a character
            // formatting inconsistent with the base direction of the
            // paragraph; once in bidi mode, we have to stay there
            // until the end of the current pragraph
            m_bBidiMode = m_bBidiMode ||
                  (m_currentRTFState.m_charProps.m_dir ^ m_currentRTFState.m_paraProps.m_dir);
            return true;
      case RTF_KW_mac:
            // TODO some iconv's may have a different name - "MacRoman"
            // TODO EncodingManager should handle encoding names
            m_mbtowc.setInCharset("MACINTOSH");
            if(!getLoadStylesOnly())
                  getDoc()->setEncodingName("MacRoman");
            return true;
      case RTF_KW_marglsxn:
            // Left margin of section
            m_currentRTFState.m_sectionProps.m_leftMargTwips = param;
            break;
      case RTF_KW_margl:
            m_sectdProps.m_leftMargTwips = param ;
            break;
      case RTF_KW_margrsxn:
            // Right margin of section
            m_currentRTFState.m_sectionProps.m_rightMargTwips = param;
            break;
      case RTF_KW_margr:
            m_sectdProps.m_rightMargTwips = param;
            break;
      case RTF_KW_margtsxn:
            // top margin of section
            m_currentRTFState.m_sectionProps.m_topMargTwips = param;
            break;
      case RTF_KW_margt:
            m_sectdProps.m_topMargTwips = param;
            break;
      case RTF_KW_margbsxn:
            // bottom margin of section
            m_currentRTFState.m_sectionProps.m_bottomMargTwips = param;
            break;
      case RTF_KW_margb:
            m_sectdProps.m_bottomMargTwips = param;
            break;
      case RTF_KW_nestrow:
            HandleRow();
            return true;
      case RTF_KW_nestcell:
            UT_DEBUGMSG(("SEVIOR: Processing nestcell \n"));
            HandleCell();
            return true;
      case RTF_KW_nonesttables:
            //
            // skip this!
            //
            UT_DEBUGMSG(("SEVIOR: doing nonesttables \n"));
            SkipCurrentGroup();
            return true;
      case RTF_KW_nonshppict:
            // we ignore this one since we handle shppict.
            UT_DEBUGMSG(("Hub: skipping nonshppict\n"));
            SkipCurrentGroup(false);
            break;
      case RTF_KW_noproof:
            // Set language to none for \noproof
            // TODO actually implement proofing flag separate to language setting
            UT_DEBUGMSG(("HIPI: RTF import keyword \\noproof\n"));
            // mark language for spell checking
            m_currentRTFState.m_charProps.m_szLang = "-none-";
            return true;
      case RTF_KW_ol:
            return HandleOverline(fParam ? (param != 0) : true);
      case RTF_KW_object:
            // get picture
            return HandleObject();
      case RTF_KW_par:
            // start new paragraph, continue current attributes
            xxx_UT_DEBUGMSG(("Done par \n"));
            return HandleParKeyword();
      case RTF_KW_plain:
            // reset character attributes
            return ResetCharacterAttributes();
      case RTF_KW_pard:
      {
            // reset paragraph attributes
            xxx_UT_DEBUGMSG(("Done pard \n"));
            bool bres = ResetParagraphAttributes();
            
            return bres;
      }
      case RTF_KW_page:
            return ParseChar(UCS_FF);
      case RTF_KW_pntext:
            //
            // skip this!
            //
            //SkipCurrentGroup( false);
            m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
            return true;
      case RTF_KW_pict:
            // get picture
            UT_DEBUGMSG(("FOund a pict!!! \n"));
            return HandlePicture();
      case RTF_KW_pc:
            m_mbtowc.setInCharset(XAP_EncodingManager::get_instance()->charsetFromCodepage(437));
            return true;
      case RTF_KW_pca:
            m_mbtowc.setInCharset(XAP_EncodingManager::get_instance()->charsetFromCodepage(850));
            return true;
      case RTF_KW_paperw:
        //
        // Just set the page width
        //
            if(!getLoadStylesOnly())
            {
                  double height = getDoc()->m_docPageSize.Height(DIM_IN);
                  double width = (static_cast<double>(param))/1440.0;
                  getDoc()->m_docPageSize.Set(width,height,DIM_IN);
                  UT_DEBUGMSG(("Page set to width %f height %f \n",width,height));
            }
            break;
      case RTF_KW_paperh:
        //
            // Just set the page height
            //
            if(!getLoadStylesOnly())
            {
                  double width = getDoc()->m_docPageSize.Width(DIM_IN);
                  double height = (static_cast<double>(param))/1440.0;
                  getDoc()->m_docPageSize.Set(width,height,DIM_IN);
                  UT_DEBUGMSG(("Page set to width %f height %f \n",width,height));
            }
            break;

      case RTF_KW_ql:
            return SetParaJustification(RTFProps_ParaProps::pjLeft);
      case RTF_KW_qc:
            return SetParaJustification(RTFProps_ParaProps::pjCentre);
      case RTF_KW_qr:
            return SetParaJustification(RTFProps_ParaProps::pjRight);
      case RTF_KW_qj:
            return SetParaJustification(RTFProps_ParaProps::pjFull);

      case RTF_KW_rquote:
            return ParseChar(UCS_RQUOTE);
      case RTF_KW_rdblquote:
            return ParseChar(UCS_RDBLQUOTE);

      // various revision keywords
      case RTF_KW_deleted: // skip this -- it is redundant
      case RTF_KW_revised:
            return true;
            
      case RTF_KW_revauthdel:
            return HandleRevisedText(PP_REVISION_DELETION, param);
      case RTF_KW_revauth:
            return HandleRevisedText(PP_REVISION_ADDITION, param);
      case RTF_KW_crauth:
            return HandleRevisedText(PP_REVISION_FMT_CHANGE, param);
            
      case RTF_KW_crdate:
      case RTF_KW_revdttmdel:
      case RTF_KW_revdttm:
            return HandleRevisedTextTimestamp(param);
            
            
      case RTF_KW_ri:
            m_currentRTFState.m_paraProps.m_indentRight = param;
            return true;
      case RTF_KW_rtf:
            return true;
      case RTF_KW_rtlpar:
            xxx_UT_DEBUGMSG(("rtf imp.: rtlpar\n"));
            m_currentRTFState.m_paraProps.m_dir = UT_BIDI_RTL;
            // reset the doc bidi attribute
            m_bBidiMode = false;
            return true;
      case RTF_KW_rtlsect:
            UT_DEBUGMSG(("rtf imp.: rtlsect\n"));
            m_currentRTFState.m_sectionProps.m_dir = UT_BIDI_RTL;
            return true;

      case RTF_KW_rtlmark:
      case RTF_KW_rtlch:
            xxx_UT_DEBUGMSG(("rtf imp.: rtlch\n"));
            m_currentRTFState.m_charProps.m_dir = UT_BIDI_RTL;
            // we enter bidi mode if we encounter a character
            // formatting inconsistent with the base direction of the
            // paragraph; once in bidi mode, we have to stay there
            // until the end of the current pragraph
            m_bBidiMode = m_bBidiMode ||
                  (m_currentRTFState.m_charProps.m_dir ^ m_currentRTFState.m_paraProps.m_dir);
            return true;
      case RTF_KW_row:
            HandleRow();
            return true;

      case RTF_KW_s:
            m_currentRTFState.m_paraProps.m_styleNumber = param;
            return true;
      case RTF_KW_stylesheet:
            return HandleStyleDefinition();
      case RTF_KW_strike:
      case RTF_KW_striked:
            return HandleStrikeout(fParam ? (param != 0) : true);
      case RTF_KW_sect:
            return StartNewSection();
      case RTF_KW_sectd:
            return ResetSectionAttributes();
      case RTF_KW_sa:
            m_currentRTFState.m_paraProps.m_spaceAfter = param;
            return true;
      case RTF_KW_sb:
            m_currentRTFState.m_paraProps.m_spaceBefore = param;
            return true;
      case RTF_KW_sbknone:
            m_currentRTFState.m_sectionProps.m_breakType = RTFProps_SectionProps::sbkNone;
            return true;
      case RTF_KW_sbkcol:
            m_currentRTFState.m_sectionProps.m_breakType = RTFProps_SectionProps::sbkColumn;
            return true;
      case RTF_KW_sbkpage:
            m_currentRTFState.m_sectionProps.m_breakType = RTFProps_SectionProps::sbkPage;
            return true;
      case RTF_KW_sbkeven:
            m_currentRTFState.m_sectionProps.m_breakType = RTFProps_SectionProps::sbkEven;
            return true;
      case RTF_KW_sbkodd:
            m_currentRTFState.m_sectionProps.m_breakType = RTFProps_SectionProps::sbkOdd;
            return true;
      case RTF_KW_shp:
// Found a positioned thingy
            HandleShape();
            return true;
      case RTF_KW_sl:
            if (!fParam  ||  param == 0) {
                  m_currentRTFState.m_paraProps.m_lineSpaceVal = 360;
            }
            else {
                  m_currentRTFState.m_paraProps.m_lineSpaceVal = param;
            }
            return true;
      case RTF_KW_slmult:
            m_currentRTFState.m_paraProps.m_lineSpaceExact = (!fParam  ||  param == 0);
            return true;
      case RTF_KW_super:
            return HandleSuperscript(fParam ? false : true);
      case RTF_KW_sub:
            return HandleSubscript(fParam ? false : true);

      case RTF_KW_tab:
            return ParseChar('\t');
      case RTF_KW_tx:
      {
            UT_return_val_if_fail(fParam, false);     // tabstops should have parameters
            bool bres = AddTabstop(param,
                                             m_currentRTFState.m_paraProps.m_curTabType,
                                             m_currentRTFState.m_paraProps.m_curTabLeader);
            m_currentRTFState.m_paraProps.m_curTabType = FL_TAB_LEFT;
//                m_currentRTFState.m_paraProps.m_curTabLeader = FL_LEADER_NONE;
            return bres;
      }
      case RTF_KW_tb:
      {
            UT_return_val_if_fail(fParam, false);     // tabstops should have parameters

            bool bres = AddTabstop(param,FL_TAB_BAR,
                                             m_currentRTFState.m_paraProps.m_curTabLeader);
            m_currentRTFState.m_paraProps.m_curTabType = FL_TAB_BAR;
//                m_currentRTFState.m_paraProps.m_curTabLeader = FL_LEADER_NONE;
            return bres;
      }
      case RTF_KW_tqr:
            m_currentRTFState.m_paraProps.m_curTabType = FL_TAB_RIGHT;
            return true;
      case RTF_KW_tqc:
            m_currentRTFState.m_paraProps.m_curTabType = FL_TAB_CENTER;
            return true;
      case RTF_KW_tqdec:
            m_currentRTFState.m_paraProps.m_curTabType = FL_TAB_DECIMAL;
            return true;
      case RTF_KW_tldot:
            m_currentRTFState.m_paraProps.m_curTabLeader = FL_LEADER_DOT;
            return true;
      case RTF_KW_tlhyph:
            m_currentRTFState.m_paraProps.m_curTabLeader = FL_LEADER_HYPHEN;
            return true;
      case RTF_KW_trautofit:
            if(getTable())
            {
                  if(param==1)
                  {
                        getTable()->setAutoFit(true);
                  }
            }
            return true;
      case RTF_KW_trleft:
            if(getTable())
            {
                  double dLeftPos = static_cast<double>(param)/1440.0;
                  UT_String sLeftPos = UT_formatDimensionString(DIM_IN,dLeftPos,NULL);
                  getTable()->setProp("table-column-leftpos",sLeftPos);
            }
            return true;
      case RTF_KW_tlul:
            m_currentRTFState.m_paraProps.m_curTabLeader = FL_LEADER_UNDERLINE;
            return true;
      case RTF_KW_tleq:
            m_currentRTFState.m_paraProps.m_curTabLeader = FL_LEADER_EQUALSIGN;
            return true;
      case RTF_KW_trowd:
            m_bRowJustPassed = false;
            m_bDoCloseTable = false;
            if(getTable() == NULL)
            {
                  OpenTable();
                  m_currentRTFState.m_paraProps.m_tableLevel = m_TableControl.getNestDepth();
            }
//
// Look to see if the nesting level of our tables has changed.
//
            if(m_currentRTFState.m_paraProps.m_tableLevel > m_TableControl.getNestDepth())
            {
                  xxx_UT_DEBUGMSG(("At trowd m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
                  while(m_currentRTFState.m_paraProps.m_tableLevel > m_TableControl.getNestDepth())
                  {
                        xxx_UT_DEBUGMSG(("SEVIOR: Doing pard OpenTable \n"));
                        OpenTable();
                  }
                  xxx_UT_DEBUGMSG(("After trowd m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
            }
            else if((m_currentRTFState.m_paraProps.m_tableLevel >= 0) && m_currentRTFState.m_paraProps.m_tableLevel < m_TableControl.getNestDepth())
            {
                  xxx_UT_DEBUGMSG(("At trowd m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
                  while(m_currentRTFState.m_paraProps.m_tableLevel < m_TableControl.getNestDepth())
                  {
                        xxx_UT_DEBUGMSG(("SEVIOR:Close Table trowd1  \n"));
                        CloseTable();
                        m_bCellBlank = true;
                  }
                  xxx_UT_DEBUGMSG(("After trowd m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
            }
//
// Look to see if m_bNestTableProps is true for nested tables.
//
// To all RTF hackers, getting these 0's and 1's is extremely important.
// Don't change them unless you can verify that a huge range of RTF docs with
// tables (nested and unnested) get imported corectly. Martin 10/5/2003
            else if((m_TableControl.getNestDepth() > 0) && !m_bNestTableProps)
            {
                  while(m_TableControl.getNestDepth() > 1)
                  {
                        xxx_UT_DEBUGMSG(("SEVIOR:Close Table trowd2 \n"));
                        CloseTable();
                        m_bCellBlank = true;
                  }
                  m_currentRTFState.m_paraProps.m_tableLevel = 1; 
            }
//
// If a trowd appears without  a preceding \cell we close the previous table
//
            if(!m_bCellBlank && !m_bNestTableProps)
            {
                  xxx_UT_DEBUGMSG(("After trowd closing table coz no cell detected -1\n"));
                  CloseTable();
            }
//
// Another way of detecting if a trowd appears without a preceding \cell.
// Close the previous table. This should always work.
            
            else if(!m_bCellHandled && m_bContentFlushed)
            {
                  xxx_UT_DEBUGMSG(("After trowd closing table coz no cell detected - 2\n"));
                  CloseTable();
            }
            m_bContentFlushed = false;
            m_bNestTableProps = false;
            ResetCellAttributes();
            ResetTableAttributes();
            break;

      case RTF_KW_ul:
      case RTF_KW_uld:
      case RTF_KW_uldash:
      case RTF_KW_uldashd:
      case RTF_KW_uldashdd:
      case RTF_KW_uldb:
      case RTF_KW_ulth:
      case RTF_KW_ulw:
      case RTF_KW_ulwave:
            return HandleUnderline(fParam ? (param != 0) : true);
      case RTF_KW_ulnone:
            return HandleUnderline(0);
      case RTF_KW_uc:
            // "\uc<n>" defines the number of chars immediately following
            // any "\u<u>" unicode character that are needed to represent
            // a reasonable approximation for the unicode character.
            // generally, this is done by stripping off accents from latin-n
            // characters so that they fold into latin1.
            //
            // the spec says that we need to allow any arbitrary length
            // of chars for this and that we need to maintain a stack of
            // these lengths (as content is nested within {} groups) so
            // that different 'destinations' can have different approximations
            // or have a local diversion for a hard-to-represent character
            // or something like that.
            //
            // this is bullshit (IMHO) -- jeff
            
            m_currentRTFState.m_unicodeAlternateSkipCount = param;
            m_currentRTFState.m_unicodeInAlternate = 0;
            return true;
      case RTF_KW_u:
      {
            /* RTF is limited to +/-32K ints so we need to use negative numbers for large unicode values.
             * So, check for Unicode chars wrapped to negative values.
             */
            if (param < 0)
            {
                  unsigned short tmp = (unsigned short) ((signed short) param);
                  param = (UT_sint32) tmp;
            }
            bool bResult = ParseChar(static_cast<UT_UCSChar>(param));
            m_currentRTFState.m_unicodeInAlternate = m_currentRTFState.m_unicodeAlternateSkipCount;
            return bResult;
      }
      case RTF_KW_up:
            // superscript with position. Default is 6.
            // subscript: see dn keyword
            return HandleSuperscriptPosition (fParam ? param : 6);

      case RTF_KW_v:
            HandleHidden(fParam ? (param != 0) : true);
            break;
            
      case RTF_KW_STAR:
            return HandleStarKeyword();
            break;
      case RTF_KW_QUOTE:
            m_currentRTFState.m_internalState = RTFStateStore::risHex;
            return true;
            break;
      case RTF_KW_OPENCBRACE:
            ParseChar('{');
            return true;
            break;
      case RTF_KW_CLOSECBRACE:
            ParseChar('}');
            return true;
            break;
      case RTF_KW_BACKSLASH:
            ParseChar('\\');
            return true;
            break;
      case RTF_KW_TILDE:
            ParseChar(UCS_NBSP);
            return true;
            break;
      case RTF_KW_HYPHEN:
            // TODO handle optional hyphen. Currently simply ignore them.
            xxx_UT_DEBUGMSG (("RTF: TODO handle optionnal hyphen\n"));
            return true;
            break;
      case RTF_KW_UNDERSCORE:
            // currently simply make a standard hyphen
            ParseChar('-');   // TODO - make these optional and nonbreaking
            return true;
            break;
      case RTF_KW_CR:   // see bug 2174 ( Cocoa RTF)
      case RTF_KW_LF:
            return StartNewPara();
            break;
      default:
            UT_DEBUGMSG(("Unhandled keyword in dispatcher: %d\n", keywordID));
      }
      return true;
}




bool IE_Imp_RTF::HandleStarKeyword() 
{
      unsigned char keyword_star[MAX_KEYWORD_LEN];
      UT_sint32 parameter_star = 0;
      bool parameterUsed_star = false;
            
      if (ReadKeyword(keyword_star, &parameter_star, &parameterUsed_star,
                              MAX_KEYWORD_LEN))
      {
            xxx_UT_DEBUGMSG(("keyword_star %s read after * \n",keyword_star));

            if( strcmp(reinterpret_cast<char*>(keyword_star), "\\")== 0)
            {
                  if (ReadKeyword(keyword_star, &parameter_star, &parameterUsed_star,
                                          MAX_KEYWORD_LEN))
                  {
                        RTF_KEYWORD_ID keywordID = KeywordToID(reinterpret_cast<char *>(keyword_star));
                        switch (keywordID) {
                        case RTF_KW_ol:
                              return HandleOverline(parameterUsed_star ?
                                                              (parameter_star != 0): true);
                              break;
                        case RTF_KW_pn:
                              return HandleLists( m_currentRTFState.m_paraProps.m_rtfListTable);
                              break;
                        case RTF_KW_listtable:
                              return ReadListTable();
                              break;
                        case RTF_KW_listoverridetable:
                              return ReadListOverrideTable();
                              break;
                        case RTF_KW_abilist:
                              return HandleAbiLists();
                              break;
                        case RTF_KW_topline:
                              return HandleTopline(parameterUsed_star ?
                                                             (parameter_star != 0): true);
                              break;
                        case RTF_KW_botline:
                              return HandleBotline(parameterUsed_star ?
                                                             (parameter_star != 0): true);
                              break;
                        case RTF_KW_listtag:
                              return HandleListTag(parameter_star);
                              break;
                        case RTF_KW_abicellprops:
                              if(!bUseInsertNotAppend())
                              {
                                    xxx_UT_DEBUGMSG (("ignoring abicellprops on file import \n"));
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              if(m_iIsInHeaderFooter == 1)
                              {
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              return HandleAbiCell();
                              break;
                        case RTF_KW_abitableprops:
                              if(!bUseInsertNotAppend())
                              {
                                    UT_DEBUGMSG (("ignoring abictableprops on file import \n"));
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              if(m_iIsInHeaderFooter == 1)
                              {
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              if(m_iIsInHeaderFooter == 0)
                              {
                                    XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
                                    if(pFrame == NULL)
                                    {
                                          m_iIsInHeaderFooter =1;
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }
                                    // TODO fix this as it appears to be a real hack. We shouldn't have access to 
                                    // this from importers.
                                    FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
                                    if(pView == NULL)
                                    {
                                          m_iIsInHeaderFooter =1;
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }
                                    if(pView->isInEndnote() || pView->isInFootnote())
                                    {
                                          m_iIsInHeaderFooter =1;
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }
                                    if(pView->isHdrFtrEdit() && (pView->isInTable() || m_pasteTableStack.getDepth() == 2) )
                                    {
//
// No nested Tables in header/footer
//
                                          m_iIsInHeaderFooter =1;
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }

                                    m_iIsInHeaderFooter = 2;
                              }
                              return HandleAbiTable();
                              break;
                        case RTF_KW_abiendtable:
                              if(!bUseInsertNotAppend())
                              {
                                    UT_DEBUGMSG (("ignoring abiendtable on file import \n"));
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              if(m_iIsInHeaderFooter == 1)
                              {
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              return HandleAbiEndTable();
                              break;
                        case RTF_KW_abiendcell:
                              if(!bUseInsertNotAppend())
                              {
                                    UT_DEBUGMSG (("ignoring abiendcell on file import \n"));
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              if(m_iIsInHeaderFooter == 1)
                              {
                                    m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                    return true;
                              }
                              return HandleAbiEndCell();
                              break;
                        case RTF_KW_shppict:
                              UT_DEBUGMSG (("handling shppict\n"));
                              HandleShapePict();
                              return true;
                              break;
                        case RTF_KW_shpinst:
                              UT_DEBUGMSG(("Doing shpinst \n"));
                              SkipCurrentGroup();
                              /*
                              m_iStackDepthAtFrame = m_stateStack.getDepth();
                              m_bFrameOpen = true;
                              */
                              return true;
                              break;
                        case RTF_KW_nesttableprops:
                              UT_DEBUGMSG(("SEVIOR: Doing nestableprops opentable \n"));
                              m_bNestTableProps = true;
                              // OpenTable();
                              return true;
                              break;
                        case RTF_KW_bkmkstart:
                              return HandleBookmark (RBT_START);
                              break;
                        case RTF_KW_bkmkend:
                              return HandleBookmark (RBT_END);
                        case RTF_KW_cs:
                              UT_DEBUGMSG(("Found cs in readword stream \n"));
                              break;
#if 1
//
// Fixme I need to be able to handle footnotes inside tables in RTF
//
                        case RTF_KW_footnote:
                              if(bUseInsertNotAppend())
                              {
                                    XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
                                    if(pFrame == NULL)
                                    {
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }
                                    FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
                                    if(pView == NULL)
                                    {
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }
                                    if(pView->isHdrFtrEdit())
                                    {
                                          m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                                          return true;
                                    }
                              }
                              //HandleFootnote();

                              m_bFootnotePending = true;
                              return true;
                              break;
#endif
//
// Decode our own field extensions
//
                        case RTF_KW_abifieldD:
                        {
                              char * pszField = strstr(reinterpret_cast<char *>(keyword_star),"D");
                              pszField++;
                              char * pszAbiField = UT_strdup(pszField);
                              char * pszD = strstr(pszAbiField,"D");
                              if(pszD)
                              {
                                    *pszD = '_';
                                    UT_DEBUGMSG(("Appending Abi field %s \n",pszAbiField));
                                    return _appendField(pszAbiField);
                              }
                              FREEP(pszAbiField);
                              break;                              
                        }
                        case RTF_KW_hlinkbase:
                        {
                              m_hyperlinkBase.clear();
                              unsigned char ch = 0;

                              if(!ReadCharFromFile(&ch))
                                    return false;

                              while (ch != '}')
                              {
                                    m_hyperlinkBase += ch;
                                    if(!ReadCharFromFile(&ch))
                                          return false;
                              }
                                          
                              PopRTFState();
                              return true;
                        }
                        case RTF_KW_revtbl:
                              return ReadRevisionTable();
                        
                        default:
                              UT_DEBUGMSG (("RTF: default case star keyword %s not handled\n", keyword_star));
                              break;
                        }
                  }
            }
            UT_DEBUGMSG (("RTF: star keyword %s not handled\n", keyword_star));
      }

      // Ignore all other \* tags
      // TODO different destination (all unhandled at the moment, so enter skip mode)
      //m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip; was this
      SkipCurrentGroup();
      return true;
}

void IE_Imp_RTF::_formRevisionAttr(UT_String & attr, UT_String & props, const XML_Char * style)
{
      attr.clear();
      
      if(m_currentRTFState.m_charProps.m_eRevision == PP_REVISION_NONE)
      {
            return;
      }
      
      UT_String temp;
      
      switch(m_currentRTFState.m_charProps.m_eRevision)
      {
            case PP_REVISION_DELETION: attr += '-'; break;
            case PP_REVISION_FMT_CHANGE: attr += '!'; break;
                  
            case PP_REVISION_ADDITION:
            case PP_REVISION_ADDITION_AND_FMT:
            default:; // nothing
      }

      UT_String_sprintf(temp, "%d", m_currentRTFState.m_charProps.m_iCurrentRevisionId);

      attr += temp;

      if(m_currentRTFState.m_charProps.m_eRevision == PP_REVISION_DELETION)
      {
            // ignore props
            return;
      }
      
      attr += '{';
      attr += props;
      attr += '}';
      if(style)
      {
            attr += '{';
            attr += PT_STYLE_ATTRIBUTE_NAME;
            attr += ';';
            attr += style;
            attr += '}';
      }
      
}



bool IE_Imp_RTF::buildCharacterProps(UT_String & propBuffer)
{
      UT_String tempBuffer;
      // bold
      propBuffer += "font-weight:";
      propBuffer += m_currentRTFState.m_charProps.m_bold ? "bold" : "normal";

      // italic
      propBuffer += "; font-style:";
      propBuffer += m_currentRTFState.m_charProps.m_italic ? "italic" : "normal";

      // hidden
      if(m_currentRTFState.m_charProps.m_Hidden)
      {
            propBuffer += "; display:none";
      }

      // underline & overline & strike-out
      propBuffer += "; text-decoration:";
      static UT_String decors;
      decors.clear();
      if (m_currentRTFState.m_charProps.m_underline)
      {
            decors += "underline ";
      }
      if (m_currentRTFState.m_charProps.m_strikeout)
      {
            decors += "line-through ";
      }
      if(m_currentRTFState.m_charProps.m_overline)
      {
            decors += "overline ";
      }
      if(m_currentRTFState.m_charProps.m_topline)
      {
            decors += "topline ";
      }
      if(m_currentRTFState.m_charProps.m_botline)
      {
            decors += "bottomline";
      }
      if(!m_currentRTFState.m_charProps.m_underline  &&
         !m_currentRTFState.m_charProps.m_strikeout &&
         !m_currentRTFState.m_charProps.m_overline &&
         !m_currentRTFState.m_charProps.m_topline &&
         !m_currentRTFState.m_charProps.m_botline)
      {
            decors = "none";
      }
      propBuffer += decors.c_str();

      //superscript and subscript
      propBuffer += "; text-position:";
      if (m_currentRTFState.m_charProps.m_superscript)
      {
            if (m_currentRTFState.m_charProps.m_superscript != 0.0)
            {
                  UT_DEBUGMSG (("RTF: TODO: Handle text position in pt.\n"));
            }
            propBuffer += "superscript";
      }
      else if (m_currentRTFState.m_charProps.m_subscript)
      {
            if (m_currentRTFState.m_charProps.m_subscript != 0.0)
            {
                  UT_DEBUGMSG (("RTF: TODO: Handle text position in pt.\n"));
            }
            propBuffer += "subscript";
      }
      else
      {
            propBuffer += "normal";
      }
      // font size
      UT_String_sprintf(tempBuffer, "; font-size:%spt", std_size_string(static_cast<float>(m_currentRTFState.m_charProps.m_fontSize)));
      propBuffer += tempBuffer;
      // typeface
      RTFFontTableItem* pFont = GetNthTableFont(m_currentRTFState.m_charProps.m_fontNumber);
      if (pFont != NULL)
      {
            propBuffer += "; font-family:";

            // see bug 2633 - we get a font entry like this (unika.rtf):
            // {\f83\fnil\fcharset0\fprq0{\*\panose 00000000000000000000} ;}
            // note the empty slot after the panose entry
            // later it gets referenced: {\b\f83\fs24\cf1\cgrid0 Malte Cornils
            // this turns those into "Times New Roman" for now, as a hack to keep from crashing
            if ( pFont->m_pFontName != NULL )
                  propBuffer += pFont->m_pFontName;
            else
                  propBuffer += "Times New Roman";
      }
      if (m_currentRTFState.m_charProps.m_hasColour)
      {
            // colour, only if one has been set. See bug 1324
            UT_uint32 colour = GetNthTableColour(m_currentRTFState.m_charProps.m_colourNumber);
            UT_String_sprintf(tempBuffer, "; color:%06x", colour);
            propBuffer += tempBuffer;
      }

      if (m_currentRTFState.m_charProps.m_hasBgColour)
      {
            // colour, only if one has been set. See bug 1324
            UT_sint32 bgColour = GetNthTableBgColour(m_currentRTFState.m_charProps.m_bgcolourNumber);

            if (bgColour != -1) // invalid and should be white
            {
                  UT_String_sprintf(tempBuffer, "; bgcolor:%06x", bgColour);
                  propBuffer += tempBuffer;
            }
      }

      if(m_currentRTFState.m_charProps.m_listTag != 0)
      {
// List Tag to hang lists off
            UT_String_sprintf(tempBuffer, "; list-tag:%d",m_currentRTFState.m_charProps.m_listTag);
            propBuffer += tempBuffer;
      }

      if(m_currentRTFState.m_charProps.m_szLang != 0)
      {
            propBuffer += "; lang:";
            propBuffer += m_currentRTFState.m_charProps.m_szLang;
      }

      if(m_currentRTFState.m_charProps.m_dirOverride == UT_BIDI_LTR)
      {
            propBuffer += ";dir-override:ltr";
      }
      else if(m_currentRTFState.m_charProps.m_dirOverride == UT_BIDI_RTL)
      {
            propBuffer += ";dir-override:rtl";
      }
      
      return true;
}


/*!
 * Returns true if we're pasting text rather than parsing a file
 */
bool IE_Imp_RTF::bUseInsertNotAppend(void)
{
      return ((m_pImportFile == NULL) && !m_parsingHdrFtr );
}

// in non-bidi doc we just append the current format and text; in bidi
// documents we crawl over the text looking for neutral characters;
// when we find one, we see if the implied override is identical to
// directional properties on either side of the character: if yes, we
// leave the character as it is; if not we issued the override. This
// saves us inserting overrides on most space characters in the document
bool IE_Imp_RTF::_appendSpan()
{
      const XML_Char* pProps = "props";
      const XML_Char* pRevs  = "revision";
      const XML_Char* pStyle = PT_STYLE_ATTRIBUTE_NAME;

      const XML_Char* propsArray[5] = {NULL, NULL, NULL, NULL, NULL};

      UT_String prop_basic;
      UT_String revision;
      buildCharacterProps(prop_basic);

      UT_String prop_ltr;
      UT_String prop_rtl;

      UT_uint32 iPropOffset = 0;
      
      bool bRevised = m_currentRTFState.m_charProps.m_eRevision != PP_REVISION_NONE;

      propsArray[0] = bRevised ? pRevs : pProps;
      propsArray[1] = prop_basic.c_str();

      if(m_currentRTFState.m_charProps.m_styleNumber >= 0
         && (UT_uint32)m_currentRTFState.m_charProps.m_styleNumber < m_styleTable.size())
      {
            propsArray[2] = pStyle;
            propsArray[3] = static_cast<const char *>(m_styleTable[m_currentRTFState.m_charProps.m_styleNumber]);
      }

      if(bRevised)
      {
            _formRevisionAttr(revision, prop_basic, propsArray[3]);

            // the style attribute is inside the revision, clear it out of the props array
            propsArray[1] = revision.c_str();
            propsArray[2] = NULL;
            propsArray[3] = NULL;

            iPropOffset = 2;
      }
      else
      {
            prop_ltr = prop_basic;
            prop_rtl = prop_basic;

            prop_ltr += ';';
            prop_rtl += ';';
      }

      prop_ltr += "dir-override:ltr";
      prop_rtl += "dir-override:rtl";
      
      
      UT_UCS4Char * p;
      UT_uint32 iLen = m_gbBlock.getLength();

      if(m_bBidiMode)
      {
            UT_BidiCharType cType;
            UT_uint32 iLast = 0;
            UT_UCS4Char c = *(reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(0)));
      
            cType = UT_bidiGetCharType(c);
      
            for(UT_uint32 i = 0; i < iLen; i++)
            {
                  if(i < iLen - 1 )
                  {
                        c = *(reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(i+1)));
                        m_iBidiNextType = UT_bidiGetCharType(c);
                  }
                  else
                  {
                        m_iBidiNextType = UT_BIDI_UNSET;
                  }
            
            
                  if(UT_BIDI_IS_NEUTRAL(cType))
                  {
                        if(m_currentRTFState.m_charProps.m_dir == UT_BIDI_LTR
                           && m_iAutoBidiOverride != UT_BIDI_LTR
                           && (m_iBidiLastType != UT_BIDI_LTR || m_iBidiNextType != UT_BIDI_LTR))
                        {
                              if(i - iLast > 0)
                              {
                                    p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                                    if(!getDoc()->appendFmt(propsArray))
                                          return false;
                              
                                    if(!getDoc()->appendSpan(p, i - iLast))
                                          return false;
                              }
                              m_iAutoBidiOverride = UT_BIDI_LTR;
                              propsArray[iPropOffset] = pProps;
                              propsArray[iPropOffset + 1] = prop_ltr.c_str();
                              iLast = i;
                        }
                        else if(m_currentRTFState.m_charProps.m_dir == UT_BIDI_RTL
                                    && m_iAutoBidiOverride != UT_BIDI_RTL
                                    && (m_iBidiLastType != UT_BIDI_RTL || m_iBidiNextType != UT_BIDI_RTL))
                        {
                              if(i - iLast > 0)
                              {
                                    p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                                    if(!getDoc()->appendFmt(propsArray))
                                          return false;

                                    if(!getDoc()->appendSpan(p, i - iLast))
                                          return false;
                              }
                              m_iAutoBidiOverride = UT_BIDI_RTL;
                              propsArray[iPropOffset] = pProps;
                              propsArray[iPropOffset + 1] = prop_rtl.c_str();
                              iLast = i;
                        }
                  }
                  else
                  {
                        // strong character; if we previously issued an override,
                        // we need to cancel it
                        if(m_iAutoBidiOverride != UT_BIDI_UNSET)
                        {
                              if(i - iLast > 0)
                              {
                                    p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                                    if(!getDoc()->appendFmt(propsArray))
                                          return false;
                              
                                    if(!getDoc()->appendSpan(p, i - iLast))
                                          return false;
                              }
                              m_iAutoBidiOverride = UT_BIDI_UNSET;

                              if(bRevised)
                              {
                                    propsArray[iPropOffset] = NULL;
                                    propsArray[iPropOffset + 1] = NULL;
                              }
                              else
                              {
                                    propsArray[1] = prop_basic.c_str();
                              }
                              
                              iLast = i;
                        }
                  }

                  m_iBidiLastType = cType;
                  cType = m_iBidiNextType;
            }

            // insert what is left over
            if(iLen - iLast > 0)
            {
                  p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                  if(!getDoc()->appendFmt(propsArray))
                        return false;
                              
                  if(!getDoc()->appendSpan(p, iLen - iLast))
                        return false;
            }
      }
      else
      {
            // not a bidi doc, just do it the simple way
            p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(0));
            if(!getDoc()->appendFmt(propsArray))
                  return false;
                              
            if(!getDoc()->appendSpan(p, iLen))
                  return false;
            
      }
      
      return true;
}
      
bool IE_Imp_RTF::_insertSpan()
{
      const XML_Char* pProps = "props";
      const XML_Char* pRevs  = "revision";
      const XML_Char* pStyle = PT_STYLE_ATTRIBUTE_NAME;

      const XML_Char* propsArray[5] = {NULL, NULL, NULL, NULL, NULL};

      UT_String prop_basic;
      UT_String revision;
      buildCharacterProps(prop_basic);

      UT_String prop_ltr;
      UT_String prop_rtl;

      UT_uint32 iPropOffset = 0;
      
      bool bRevised = m_currentRTFState.m_charProps.m_eRevision != PP_REVISION_NONE;

      propsArray[0] = bRevised ? pRevs : pProps;
      propsArray[1] = prop_basic.c_str();
      
      UT_UCS4Char * p;
      UT_uint32 iLen = m_gbBlock.getLength();

      if(m_currentRTFState.m_charProps.m_styleNumber >= 0
         && (UT_uint32)m_currentRTFState.m_charProps.m_styleNumber < m_styleTable.size())
      {
            propsArray[2] = pStyle;
            propsArray[3] = static_cast<const char *>(m_styleTable[m_currentRTFState.m_charProps.m_styleNumber]);
      }
      

      if(bRevised)
      {
            _formRevisionAttr(revision, prop_basic, propsArray[3]);

            // the style attribute is inside the revision, clear it out of the props array
            propsArray[1] = revision.c_str();
            propsArray[2] = NULL;
            propsArray[3] = NULL;

            iPropOffset = 2;
      }
      else
      {
            prop_ltr = prop_basic;
            prop_rtl = prop_basic;

            prop_ltr += ';';
            prop_rtl += ';';
      }

      prop_ltr += "dir-override:ltr";
      prop_rtl += "dir-override:rtl";
      
      

      if(m_bBidiMode)
      {
            UT_BidiCharType cType;
            UT_uint32 iLast = 0;
            UT_UCS4Char c = *(reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(0)));
      
            cType = UT_bidiGetCharType(c);
      
            for(UT_uint32 i = 0; i < iLen; i++)
            {
                  if(i < iLen - 1 )
                  {
                        c = *(reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(i+1)));
                        m_iBidiNextType = UT_bidiGetCharType(c);
                  }
                  else
                  {
                        m_iBidiNextType = UT_BIDI_UNSET;
                  }
            
            
                  if(UT_BIDI_IS_NEUTRAL(cType))
                  {
                        if(m_currentRTFState.m_charProps.m_dir == UT_BIDI_LTR
                           && m_iAutoBidiOverride != UT_BIDI_LTR
                           && (m_iBidiLastType != UT_BIDI_LTR || m_iBidiNextType != UT_BIDI_LTR))
                        {
                              if(i - iLast > 0)
                              {
                                    p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                                    if(!getDoc()->insertSpan(m_dposPaste, p ,i - iLast))
                                          return false;
                                    
                                    if(!getDoc()->changeSpanFmt(PTC_SetFmt, m_dposPaste,m_dposPaste+ i - iLast,
                                                                              propsArray,NULL))
                                          return false;
                                    
                                    m_dposPaste += i - iLast;                                   
                              }
                              m_iAutoBidiOverride = UT_BIDI_LTR;
                              propsArray[iPropOffset] = pProps;
                              propsArray[iPropOffset + 1] = prop_ltr.c_str();
                              iLast = i;
                        }
                        else if(m_currentRTFState.m_charProps.m_dir == UT_BIDI_RTL
                                    && m_iAutoBidiOverride != UT_BIDI_RTL
                                    && (m_iBidiLastType != UT_BIDI_RTL || m_iBidiNextType != UT_BIDI_RTL))
                        {
                              if(i - iLast > 0)
                              {
                                    p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                                    if(!getDoc()->insertSpan(m_dposPaste, p ,i - iLast))
                                          return false;
                                    
                                    if(!getDoc()->changeSpanFmt(PTC_SetFmt, m_dposPaste,m_dposPaste+ i - iLast,
                                                                              propsArray,NULL))
                                          return false;
                                    
                                    m_dposPaste += i - iLast;                                   
                              }
                              m_iAutoBidiOverride = UT_BIDI_RTL;
                              propsArray[iPropOffset] = pProps;
                              propsArray[iPropOffset + 1] = prop_rtl.c_str();
                              iLast = i;
                        }
                  }
                  else
                  {
                        // strong character; if we previously issued an override,
                        // we need to cancel it
                        if(m_iAutoBidiOverride != UT_BIDI_UNSET)
                        {
                              if(i - iLast > 0)
                              {
                                    p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                                    if(!getDoc()->insertSpan(m_dposPaste, p ,i - iLast))
                                          return false;
                                    
                                    if(!getDoc()->changeSpanFmt(PTC_SetFmt, m_dposPaste, m_dposPaste + i - iLast,
                                                                              propsArray,NULL))
                                          return false;
                                    
                                    m_dposPaste += i - iLast;                                   
                              }
                              m_iAutoBidiOverride = UT_BIDI_UNSET;
                              if(bRevised)
                              {
                                    propsArray[iPropOffset] = NULL;
                                    propsArray[iPropOffset + 1] = NULL;
                              }
                              else
                              {
                                    propsArray[1] = prop_basic.c_str();
                              }
                              
                              iLast = i;
                        }
                  }

                  m_iBidiLastType = cType;
                  cType = m_iBidiNextType;
            }

            // insert what is left over
            if(iLen - iLast > 0)
            {
                  p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(iLast));
                  if(!getDoc()->insertSpan(m_dposPaste, p ,iLen - iLast))
                        return false;
                                    
                  if(!getDoc()->changeSpanFmt(PTC_SetFmt, m_dposPaste, m_dposPaste + iLen - iLast,
                                                            propsArray,NULL))
                        return false;
                                    
                  m_dposPaste += iLen - iLast;                                
            }
      }
      else
      {
            // not a bidi doc, just do it the simple way
            p = reinterpret_cast<UT_UCS4Char*>(m_gbBlock.getPointer(0));
            if(!getDoc()->insertSpan(m_dposPaste, p ,iLen))
                  return false;
                                    
            if(!getDoc()->changeSpanFmt(PTC_SetFmt, m_dposPaste, m_dposPaste + iLen,
                                                      propsArray,NULL))
                  return false;
                                    
            m_dposPaste += iLen;                                  
      }
      
      return true;
}

bool IE_Imp_RTF::ApplyCharacterAttributes()
{
      bool ok;
      if(isBlockNeededForPasteTable())
      {
            ApplyParagraphAttributes();
      }
      if(m_gbBlock.getLength() > 0)
      {
            if (!bUseInsertNotAppend())   // if we are reading from a file or parsing headers and footers
            {
                  ok = _appendSpan();
            }
            else                                            // else we are pasting from a buffer
            {
                  if( m_currentRTFState.m_paraProps.m_isList && (m_dposPaste == m_dOrigPos))
                  {
                        ApplyParagraphAttributes(true);     
                  }
                  ok = _insertSpan();
            }
            m_gbBlock.truncate(0);
            m_bContentFlushed = true;
            return ok;
      }
      else
      {
            const XML_Char* pProps = "props";
            const XML_Char* pStyle = PT_STYLE_ATTRIBUTE_NAME;
            UT_String propBuffer;
            buildCharacterProps(propBuffer);

            const XML_Char* propsArray[5];
            propsArray[0] = pProps;
            propsArray[1] = propBuffer.c_str();
            propsArray[2] = NULL;
            propsArray[3] = NULL;
            propsArray[4] = NULL;

            if(m_currentRTFState.m_charProps.m_styleNumber >= 0
               && (UT_uint32)m_currentRTFState.m_charProps.m_styleNumber < m_styleTable.size())
            {
                  propsArray[2] = pStyle;
                  propsArray[3] = static_cast<const char *>(m_styleTable[m_currentRTFState.m_charProps.m_styleNumber]);
            }
            
            if (!bUseInsertNotAppend())   // if we are reading from a file or parsing headers and footers
            {
                  ok = getDoc()->appendFmt(propsArray);
                  ok = ok && getDoc()->appendFmtMark();
            }
            else                                            // else we are pasting from a buffer
            {
                  ok = getDoc()->changeSpanFmt(PTC_SetFmt,
                                                                        m_dposPaste,m_dposPaste,
                                                                        propsArray,NULL);
            }
            return ok;
      }
}


bool IE_Imp_RTF::ResetCharacterAttributes()
{
//    bool ok = FlushStoredChars();
      bool ok = true;
      m_currentRTFState.m_charProps = RTFProps_CharProps();

      return ok;
}

/*!
 *    OK if we are pasting into the text we have to decide if the list we paste
 *    should be a new list or an old list. The user might want to swap paragraphs
 *    in a list for example.

 *    Use the following algorithim to decide. If the docpos of the paste is
 *    within a list of the same ID as our list or if the docpos is immediately
 *    before or after a list of the same ID reuse the ID. Otherwise change it.
*/
UT_uint32 IE_Imp_RTF::mapID(UT_uint32 id)
{
      UT_uint32 mappedID = id;
      if(id == 0)
      {
            UT_ASSERT_NOT_REACHED();
            return id;
      }
      if (!bUseInsertNotAppend())  // if we are reading a file - dont remap the ID
      {
              return id;
      }
//
// Handle case of no id in any lists. If this is the case no need to remap
//
      fl_AutoNum * pAuto = getDoc()->getListByID(id);
      if(pAuto == NULL)
      {
              return id;
      }
      ///
      /// Now look to see if the ID has been remapped.
      ///
      UT_uint32 i,j;
      for(i=0; i<m_numLists; i++)
      {
            if(getAbiList(i)->orig_id == id)
            {
                  if(getAbiList(i)->hasBeenMapped == true )
                  {
                        mappedID =  getAbiList(i)->mapped_id;
                  }
                  else if(!m_bStruxInserted)
                      ///
                      /// Do the remapping!
                      ///
                  {
                        fl_AutoNum * pMapAuto = NULL;
                        UT_uint32 nLists = getDoc()->getListsCount();
                        UT_uint32 highestLevel = 0;
                        PL_StruxDocHandle sdh;
//
// Get the List Type
//
                        FL_ListType myType = NOT_A_LIST;
                        fl_AutoLists al;
                        UT_uint32 size_xml_lists = al.getXmlListsSize();
                        for(j=0; j < size_xml_lists; j++)
                        {
                              if( UT_XML_strcmp(m_currentRTFState.m_paraProps.m_pszStyle,al.getXmlList(j))==0)
                                    break;
                        }
                        if(j < size_xml_lists)
                              myType = static_cast<FL_ListType>(j);

                        getDoc()->getStruxOfTypeFromPosition(m_dposPaste, PTX_Block,&sdh);
                        for(j=0; j< nLists; j++)
                        {
                              fl_AutoNum * pAuto = getDoc()->getNthList(j);
                              if(pAuto->isContainedByList(sdh) == true)
                              {
                                    if(highestLevel < pAuto->getLevel())
                                    {
                                          highestLevel = pAuto->getLevel();
                                          FL_ListType thisType = pAuto->getType();
                                          if(thisType == myType)
                                          {
                                                pMapAuto = pAuto;
                                          }
                                    }
                              }
                        }
                        if(pMapAuto == NULL )
                        {
                              mappedID = getDoc()->getUID(UT_UniqueId::List);
                        }
                        else if( getAbiList(i)->level <= pMapAuto->getLevel() && pMapAuto->getID() != 0)
                        {
                              mappedID = pMapAuto->getID();
                        }
                        else
                        {
                              mappedID = getDoc()->getUID(UT_UniqueId::List);
                        }
                        getAbiList(i)->hasBeenMapped = true;
                        getAbiList(i)->mapped_id = mappedID;
                        if(highestLevel > 0)
                        {
                              getAbiList(i)->mapped_parentid =  getAbiList(i)->orig_parentid;
                        }
                        else
                        {
                              getAbiList(i)->mapped_parentid = 0;
                              getAbiList(i)->orig_parentid = 0;
                              getAbiList(i)->level = 1;
                        }
                  }

                  ///
                  /// Now look to see if the parent ID has been remapped, if so update mapped_parentid
                  ///
                  for(j = 0;  j<m_numLists; j++)
                  {
                        if(getAbiList(j)->orig_id == getAbiList(i)->orig_parentid)
                        {
                              getAbiList(i)->mapped_parentid = getAbiList(j)->mapped_id;
                        }
                  }

            }
      }
      return mappedID;

}

/*!
 *   OK if we are pasting into the text we have to decide if the list we paste
 *   should be a new list or an old list. The user might want to swap paragraphs
 *   for example.
 */
UT_uint32 IE_Imp_RTF::mapParentID(UT_uint32 id)
{
  //
  // For the parent ID we have to look to see if the parent ID has been
  // remapped or if the id
  //
      UT_uint32 mappedID;
      mappedID = id;
      if (!bUseInsertNotAppend())  // if we are reading a file
      {
            return id;
      }
      UT_uint32 i;
      for(i=0; i<m_numLists ; i++)
      {
            if(getAbiList(i)->orig_id == id)
                  break;
      }
      if( i < m_numLists && getAbiList(i)->orig_id == id)
      {
          mappedID =  getAbiList(i)->mapped_id;
      }
      return mappedID;
}

bool IE_Imp_RTF::ApplyParagraphAttributes(bool bDontInsert)
{
      const XML_Char* attribs[PT_MAX_ATTRIBUTES*2 + 1];
      UT_uint32 attribsCount=0;

//
// Look to see if the nesting level of our tables has changed.
//
      if(!bUseInsertNotAppend())
      {
            if(m_currentRTFState.m_paraProps.m_tableLevel > m_TableControl.getNestDepth())
            {
                  if(m_bParaWrittenForSection)
                  {
                        xxx_UT_DEBUGMSG(("At Apply Paragraph m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
                        while(m_currentRTFState.m_paraProps.m_tableLevel > m_TableControl.getNestDepth())
                        {
                              xxx_UT_DEBUGMSG(("SEVIOR: Doing pard OpenTable \n"));
                              m_bCellBlank = false;
                              m_bEndTableOpen = false;
                              OpenTable();
                        }
                        xxx_UT_DEBUGMSG(("After Apply Paragraph m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
                  }
            }
            else if((m_currentRTFState.m_paraProps.m_tableLevel >= 0) && m_currentRTFState.m_paraProps.m_tableLevel < m_TableControl.getNestDepth())
            {
                  xxx_UT_DEBUGMSG(("At Apply Paragraph m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
                  while(m_currentRTFState.m_paraProps.m_tableLevel < m_TableControl.getNestDepth())
                  {
                        CloseTable();
                        if(m_bCellBlank)
                        {
                              m_bEndTableOpen = true;
                        }
                  }
                  xxx_UT_DEBUGMSG(("After Apply Paragraph m_tableLevel %d nestDepth %d \n",m_currentRTFState.m_paraProps.m_tableLevel,m_TableControl.getNestDepth()));
            }
      }
      m_bParaWrittenForSection = true;
//
// Determine if we've dropped out of a table
//
      if(getTable() != NULL)
      {
            if(!m_currentRTFState.m_paraProps.m_bInTable)
            {
                  m_bDoCloseTable = true;
            }
      }

      UT_String propBuffer;
      UT_String tempBuffer;
      bool bWord97List = m_currentRTFState.m_paraProps.m_isList && isWord97Lists();
      bool bAbiList = m_currentRTFState.m_paraProps.m_isList && ( 0 != m_currentRTFState.m_paraProps.m_rawID);
      bWord97List = bWord97List && !bAbiList;
      RTF_msword97_listOverride * pOver = NULL;
      UT_uint32 iLevel = 0;
      UT_uint32 iOverride = 0;
//
// Need to get some pointers to add List tabs to the tab definitions.
//
      if(bWord97List)
      {
            iOverride = m_currentRTFState.m_paraProps.m_iOverride;
            iLevel = m_currentRTFState.m_paraProps.m_iOverrideLevel;
            pOver = _getTableListOverride(iOverride);
      }

      // tabs
      if ((pOver != NULL && pOver->isTab(iLevel)))
      {
//
// The Word 97 RTF list definition has some extra tab stops. Add them here.
//
            if(pOver->isTab(iLevel))
            {
                  UT_uint32 i = 0;
                  UT_uint32 count = pOver->getTabStopVect(iLevel)->getItemCount();
                  for(i=0; i< count; i++)
                  {
                        m_currentRTFState.m_paraProps.m_tabStops.addItem(pOver->getTabStopVect(iLevel)->getNthItem(i));
                        m_currentRTFState.m_paraProps.m_tabTypes.addItem(pOver->getTabTypeVect(iLevel)->getNthItem(i));
                        m_currentRTFState.m_paraProps.m_tabLeader.addItem(pOver->getTabLeaderVect(iLevel)->getNthItem(i));
                  }
            }
      }
      if(m_currentRTFState.m_paraProps.m_tabStops.getItemCount() > 0)
      {
            UT_ASSERT_HARMLESS(m_currentRTFState.m_paraProps.m_tabStops.getItemCount() ==
                              m_currentRTFState.m_paraProps.m_tabTypes.getItemCount() );
            UT_ASSERT_HARMLESS(m_currentRTFState.m_paraProps.m_tabStops.getItemCount() ==
                              m_currentRTFState.m_paraProps.m_tabLeader.getItemCount() );
            propBuffer += "tabstops:";
            for (UT_uint32 i = 0; i < m_currentRTFState.m_paraProps.m_tabStops.getItemCount(); i++)
            {
                  if (i > 0)
                        propBuffer += ",";

                  UT_sint32 tabTwips = m_currentRTFState.m_paraProps.m_tabStops.getNthItem(i);
                  double tabIn = tabTwips/(20.0*72.);
                  UT_uint32 idum = m_currentRTFState.m_paraProps.m_tabTypes.getNthItem(i);
                  eTabType tabType = static_cast<eTabType>(idum);
                  idum = static_cast<UT_uint32>((m_currentRTFState.m_paraProps.m_tabLeader.getNthItem(i)));
                  eTabLeader tabLeader = static_cast<eTabLeader>(idum);
                  char  cType = ' ';
                  switch(tabType)
                  {
                  case FL_TAB_LEFT:
                        cType ='L';
                        break;
                  case FL_TAB_RIGHT:
                        cType ='R';
                        break;
                  case FL_TAB_CENTER:
                        cType ='C';
                        break;
                  case FL_TAB_DECIMAL:
                        cType ='D';
                        break;
                  case FL_TAB_BAR:
                        cType ='B';
                        break;
                  default:
                        UT_ASSERT_NOT_REACHED();
                  }
                  char cLeader = '0' + static_cast<char>(tabLeader);
                  UT_String_sprintf(tempBuffer, "%s/%c%c", UT_convertInchesToDimensionString(DIM_IN,tabIn,"04"),cType,cLeader);
                  propBuffer += tempBuffer;
            }

            propBuffer += "; ";
      }

      // justification
      propBuffer += "text-align:";
      switch (m_currentRTFState.m_paraProps.m_justification)
      {
            case RTFProps_ParaProps::pjCentre:
                  propBuffer += "center";
                  break;
            case RTFProps_ParaProps::pjRight:
                  propBuffer += "right";
                  break;
            case RTFProps_ParaProps::pjFull:
                  propBuffer += "justify";
                  break;
            default:
                  UT_ASSERT_NOT_REACHED();      // so what is it?
            case RTFProps_ParaProps::pjLeft:
                  propBuffer += "left";
                  break;
      }
      propBuffer += "; ";

      // indents - first, left and right, top and bottom
      UT_String_sprintf(tempBuffer, "margin-top:%s; ",            UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_currentRTFState.m_paraProps.m_spaceBefore)/1440));
      propBuffer += tempBuffer;
      UT_String_sprintf(tempBuffer, "margin-bottom:%s; ",   UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_currentRTFState.m_paraProps.m_spaceAfter)/1440));
      propBuffer += tempBuffer;

      propBuffer += "dom-dir:";
      if(m_currentRTFState.m_paraProps.m_dir == UT_BIDI_RTL)
            propBuffer += "rtl; ";
      else
            propBuffer += "ltr; ";

      //
      // Filled from List deefinition
      //
      if(!bWord97List || bAbiList)
      {
            UT_String_sprintf(tempBuffer, "margin-left:%s; ", UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_currentRTFState.m_paraProps.m_indentLeft)/1440));
            propBuffer += tempBuffer;
      }
      UT_String_sprintf(tempBuffer, "margin-right:%s; ", UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_currentRTFState.m_paraProps.m_indentRight)/1440));
      propBuffer += tempBuffer;
//
// Filled from List definition
//
      if(!bWord97List || bAbiList)
      {
            UT_String_sprintf(tempBuffer, "text-indent:%s; ", UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(m_currentRTFState.m_paraProps.m_indentFirst)/1440));
            propBuffer += tempBuffer;
      }
// line spacing
      if (m_currentRTFState.m_paraProps.m_lineSpaceExact)
      {
        if (m_currentRTFState.m_paraProps.m_lineSpaceVal < 0) {  // exact spacing
                  UT_String_sprintf(tempBuffer, "line-height:%spt;",    UT_convertToDimensionlessString(fabs(m_currentRTFState.m_paraProps.m_lineSpaceVal/20.0)));
            }
            else                                                         // "at least" spacing
            {
                  UT_String_sprintf(tempBuffer, "line-height:%spt+;",    UT_convertToDimensionlessString(fabs(m_currentRTFState.m_paraProps.m_lineSpaceVal/20.0)));
            }
      }
      else                 // multiple line spacing
      {
            UT_String_sprintf(tempBuffer, "line-height:%s;",      UT_convertToDimensionlessString(fabs(m_currentRTFState.m_paraProps.m_lineSpaceVal/240)));
      }

      propBuffer += tempBuffer;


      // Lists. If the paragraph has a list element handle it.
      UT_String szLevel;
      UT_String szStyle;
      UT_String szListID;
      UT_String szParentID;
      UT_uint32 id = 0,pid = 0,startValue = 0;
//
// This is for our own extensions to RTF.
//
      if(bUseInsertNotAppend())
      {
            //
            // don't paste lists into hdrftr's
            //
            XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
            FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
            if(pView && pView->isHdrFtrEdit())
            {
                  bAbiList = false;
                  bWord97List = false;
            }
      }
      if( bAbiList)
      {
        //
        // First off assemble the list attributes
        //
            id = mapID(m_currentRTFState.m_paraProps.m_rawID);
            UT_String_sprintf(szListID,"%d",id);
            pid = mapParentID(m_currentRTFState.m_paraProps.m_rawParentID);
            UT_String_sprintf(szParentID,"%d",pid);
            if(pid == 0)
                  m_currentRTFState.m_paraProps.m_level = 1;
            UT_String_sprintf(szLevel,"%d",m_currentRTFState.m_paraProps.m_level);

            attribs[attribsCount++] = PT_LISTID_ATTRIBUTE_NAME;
            UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
            attribs[attribsCount++] = szListID.c_str();
            UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
            attribs[attribsCount++] = PT_PARENTID_ATTRIBUTE_NAME;
            UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
            attribs[attribsCount++] = szParentID.c_str();
            UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
            attribs[attribsCount++] = PT_LEVEL_ATTRIBUTE_NAME;
            UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
            attribs[attribsCount++] = szLevel.c_str();
            UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
            attribs[attribsCount] = NULL;
      }

//
// This is for Word97 Lists
//
      if(bWord97List && pOver)
      {
//
// Now get the properties we've painstakingly put together.
//
            const char * szListID = NULL;
            const char * szParentID = NULL;
            const char * szLevel = NULL;
            const char * szStartat = NULL;
            const char * szFieldFont = NULL;
            const char * szListDelim = NULL;
            const char * szListDecimal = NULL;
            const char * szAlign = NULL;
            const char * szIndent = NULL;
            const char * szListStyle = NULL;
            pOver->buildAbiListProperties( &szListID, &szParentID, &szLevel, &szStartat, &szFieldFont,
                                                         &szListDelim, &szListDecimal, &szAlign, &szIndent,
                                                         &szListStyle, iLevel);

//
// fix up indents.
//
            UT_String val;
            double firstLine = UT_convertToInches(szAlign);
            double leftIndent = -firstLine + 0.01;
            if(szIndent && *szIndent)
            {
                  leftIndent = UT_convertToInches(szIndent);
            }
            else
            {
                  val =  UT_formatDimensionedValue(leftIndent,"in");
                  szIndent = val.c_str();
            }
            if((firstLine + leftIndent) < 0.0)
            {
                  leftIndent = -firstLine +0.01;
                  val =  UT_formatDimensionedValue(leftIndent,"in");
                  szIndent = val.c_str();
            }
//
// Got attributes
//
            attribs[attribsCount++] = PT_LISTID_ATTRIBUTE_NAME;
            attribs[attribsCount++] = szListID;
            attribs[attribsCount++] = PT_PARENTID_ATTRIBUTE_NAME;
            attribs[attribsCount++] = szParentID;
            attribs[attribsCount++] = PT_LEVEL_ATTRIBUTE_NAME;
            attribs[attribsCount++] = szLevel;
            attribs[attribsCount]   = NULL;

//
// Next do character properties redefined in this list
//
            // bold
            if(pOver->isBoldChanged(iLevel))
            {
                  propBuffer += "font-weight:";
                  if ( pOver->getBold(iLevel) )
                        propBuffer +=  "bold";
                  else
                        propBuffer += "normal";
                  propBuffer += ";";
            }
            // italic
            if(pOver->isItalicChanged(iLevel))
            {
                  propBuffer += " font-style:";
                  if ( pOver->getItalic(iLevel) )
                        propBuffer += "italic";
                  else
                        propBuffer += "normal";
                  propBuffer += ";";
            }
            // underline & overline & strike-out
            if(pOver->isUnderlineChanged(iLevel) || pOver->isStrikeoutChanged(iLevel))
            {
                  propBuffer += "; text-decoration:";
                  static UT_String decors;
                  decors.clear();
                  if (pOver->getUnderline(iLevel))
                  {
                        decors += "underline ";
                  }
                  if (pOver->getStrikeout(iLevel))
                  {
                        decors += "line-through ";
                  }
                  if(!pOver->getUnderline(iLevel)  &&
                     !pOver->getStrikeout(iLevel))
                  {
                        decors = "none";
                  }
                  propBuffer += decors.c_str();
                  propBuffer += ";";
            }
            //superscript and subscript
            if(pOver->isSuperscriptChanged(iLevel) || pOver->isSubscriptChanged(iLevel))
            {
                  propBuffer += " text-position:";
                  if (pOver->getSuperscript(iLevel))
                  {
                        if (pOver->getSuperscriptPos(iLevel) != 0.0)
                        {
                              UT_DEBUGMSG (("RTF: TODO: Handle text position in pt.\n"));
                        }
                        propBuffer += "superscript;";
                  }
                  else if (pOver->getSubscript(iLevel))
                  {
                        if (pOver->getSubscriptPos(iLevel) != 0.0)
                        {
                              UT_DEBUGMSG (("RTF: TODO: Handle text position in pt.\n"));
                        }
                        propBuffer += "subscript;";
                  }
                  else
                  {
                        propBuffer += "normal;";
                  }
            }

            // font size
            if(pOver->isFontSizeChanged(iLevel))
            {
                  UT_String_sprintf(tempBuffer, " font-size:%spt;", std_size_string(static_cast<float>(pOver->getFontSize(iLevel))));
                  propBuffer += tempBuffer;
                  UT_DEBUGMSG(("RTF: IMPORT!!!!! font sized changed in override %d \n",pOver->getFontSize(iLevel)));
            }
            // typeface
            if(pOver->isFontNumberChanged(iLevel))
            {
                  RTFFontTableItem* pFont = GetNthTableFont(pOver->getFontNumber(iLevel));
                  if (pFont != NULL)
                  {
                        propBuffer += " font-family:";
                        propBuffer += pFont->m_pFontName;
                        propBuffer += ";";
                  }
            }
            // Foreground Colour
            if(pOver->isHasColourChanged(iLevel))
            {
                  if (pOver->getHasColour(iLevel))
                  {
                        // colour, only if one has been set. See bug 1324
                        UT_uint32 colour = GetNthTableColour(pOver->getColourNumber(iLevel));
                        UT_String_sprintf(tempBuffer, " color:%06x;", colour);
                        propBuffer += tempBuffer;
                  }
            }
            // BackGround Colour
            if (pOver->isHasBgColourChanged(iLevel))
            {
                  if(pOver->getHasBgColour(iLevel))
                  {
                        // colour, only if one has been set. See bug 1324
                        UT_sint32 bgColour = GetNthTableBgColour(pOver->getBgColourNumber(iLevel));
                        if (bgColour != -1) // invalid and should be white
                        {
                              UT_String_sprintf(tempBuffer, " bgcolor:%06x;", bgColour);
                              propBuffer += tempBuffer;
                        }
                  }
            }
            //
            // Now handle the List properties
            //

            UT_String_sprintf(tempBuffer,"list-style:%s;",szListStyle);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "list-decimal:%s; ",szListDecimal);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "list-delim:%s; ",szListDelim);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "field-font:%s; ",szFieldFont);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "start-value:%s; ",szStartat);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "margin-left:%s; ",szAlign);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "text-indent:%s;", szIndent); // Note last entry has no ;
            propBuffer += tempBuffer;
      }


      if(bAbiList)
      {
            //
            // Now handle the Abi List properties
            //
            UT_String_sprintf(tempBuffer,"list-style:%s;",m_currentRTFState.m_paraProps.m_pszStyle);
            UT_String_sprintf(szStyle,"%s",m_currentRTFState.m_paraProps.m_pszStyle);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "list-decimal:%s; ",m_currentRTFState.m_paraProps.m_pszListDecimal);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "list-delim:%s; ",m_currentRTFState.m_paraProps.m_pszListDelim);
            propBuffer += tempBuffer;
            UT_String_sprintf(tempBuffer, "field-font:%s; ",m_currentRTFState.m_paraProps.m_pszFieldFont);
            propBuffer += tempBuffer;
            startValue = m_currentRTFState.m_paraProps.m_startValue;
            UT_String_sprintf(tempBuffer, "start-value:%d",startValue);
            propBuffer += tempBuffer;
      }
      // Style name
      if( static_cast<UT_uint32>(m_currentRTFState.m_paraProps.m_styleNumber) < m_styleTable.size() &&(m_currentRTFState.m_paraProps.m_styleNumber >= 0) )
      {
            UT_uint32 styleNumber = m_currentRTFState.m_paraProps.m_styleNumber;
            const char * styleName = static_cast<const char *>(m_styleTable[styleNumber]);
            attribs[attribsCount++] = PT_STYLE_ATTRIBUTE_NAME;
            attribs[attribsCount++] = styleName;
            attribs[attribsCount]   = NULL;
      }
//
// If there are character properties defined now write them into our buffer
//

//
// Remove the trailing ";" if needed.
//
      UT_sint32 eol = propBuffer.size();
      while(eol >= 0 && (propBuffer[eol] == ' ' || propBuffer[eol] == 0))
      {
            eol--;
      }
      if(propBuffer[eol] == ';')
      {
            propBuffer[eol] = 0;
      }
      attribs[attribsCount++] = PT_PROPS_ATTRIBUTE_NAME;
//
// if we are reading a file or parsing header and footers
// and we're in a list, append char props to this.
//
      if ( !(bUseInsertNotAppend()) && (bAbiList || bWord97List ))
      {
            buildCharacterProps(propBuffer);
            xxx_UT_DEBUGMSG(("SEVIOR: propBuffer = %s \n",propBuffer.c_str()));
      }
      attribs[attribsCount++] = propBuffer.c_str();
      attribs[attribsCount++] = NULL;

      if (!bUseInsertNotAppend()) // if we are reading a file or parsing header and footers
      {
            if(bAbiList || bWord97List )
            {
                  xxx_UT_DEBUGMSG(("Append block 1 \n"));
                  bool bret = getDoc()->appendStrux(PTX_Block, attribs);
                  m_bEndTableOpen = false;
                  m_bCellBlank = false;
                  m_newParaFlagged = false;
                  m_bSectionHasPara = true;
                  getDoc()->appendFmtMark();
                  //
                  // Insert a list-label field??
                  //
                  const XML_Char* fielddef[3];
                  fielddef[0] ="type";
                  fielddef[1] = "list_label";
                  fielddef[2] = NULL;
                  bret =   getDoc()->appendObject(PTO_Field,fielddef);
                  UT_UCSChar cTab = UCS_TAB;
//
// Put the tab back in.
//
                  getDoc()->appendSpan(&cTab,1);
                  return bret;
            }
            else
            {
                  xxx_UT_DEBUGMSG(("SEVIOR: Apply Para's append strux -2 \n"));
                  bool ok = getDoc()->appendStrux(PTX_Block, attribs);
                  m_newParaFlagged = false;
                  m_bSectionHasPara = true;
                  m_bEndTableOpen = false;
                  m_bCellBlank = false;
                  return ok;
            }
      }
      else
      {
            bool bSuccess = true;
            if(bAbiList && (bUseInsertNotAppend()))
            {
                  if(!bDontInsert)
                  {
                        UT_DEBUGMSG(("Insert block at 1 \n"));
                        markPasteBlock();
                        insertStrux(PTX_Block);
                  }
                  //
                  // Put the tab back in.
                  //
                  UT_UCSChar cTab = UCS_TAB;
                  getDoc()->insertSpan(m_dposPaste,&cTab,1);
                  m_newParaFlagged = false;
                  m_bSectionHasPara = true;
                  m_dposPaste++;
                  PL_StruxDocHandle sdh_cur;
                  UT_uint32 j;
                  fl_AutoNum * pAuto = getDoc()->getListByID(id);
                  if(pAuto == NULL)
                  /*
                  * Got to create a new list here.
                  * Old one may have been cut out or ID may have
                  * been remapped.
                  */
                  {
                        FL_ListType lType = NOT_A_LIST;
                        fl_AutoLists al;
                        UT_uint32 size_xml_lists = al.getXmlListsSize();
                        for(j=0; j< size_xml_lists; j++)
                        {
                              if( UT_XML_strcmp(szStyle.c_str(),al.getXmlList(j)) ==0)
                              {
                                    break;
                              }
                        }
                        if(j < size_xml_lists)
                              lType = static_cast<FL_ListType>(j);
                        else
                              lType = static_cast<FL_ListType>(0);
                        pAuto = new fl_AutoNum(id, pid, lType, startValue,static_cast<XML_Char *>(m_currentRTFState.m_paraProps.m_pszListDelim),static_cast<XML_Char *>(m_currentRTFState.m_paraProps.m_pszListDecimal), getDoc(), NULL);
                        getDoc()->addList(pAuto);
                        pAuto->fixHierarchy();
                  }
                  bSuccess = getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_Block,&sdh_cur);
                  ///
                  /// Now insert this into the pAuto List
                  ///
                  pAuto->addItem(sdh_cur);
                  if(pid != 0)
                  {
                        pAuto->findAndSetParentItem();
                        pAuto->markAsDirty();
                  }
                  bSuccess = getDoc()->changeStruxFmt(PTC_SetFmt,m_dposPaste,m_dposPaste,attribs, NULL,PTX_Block);
            }
            else if(bUseInsertNotAppend())
            {
                  ABI_Paste_Table * pPaste = NULL;          
                  m_pasteTableStack.viewTop((void**)(&pPaste));
                  if(pPaste != NULL)
                  {
                        if(!pPaste->m_bHasPastedCellStrux && pPaste->m_bHasPastedTableStrux)
                        {
//
// We have either a bare table strux or a bare endcell strux. No blocks
// allowed here.
//
                              return true;
                        }
                  }
                  UT_DEBUGMSG((" Insert block at 2 \n"));
                  if(!bDontInsert)
                  {
                        markPasteBlock();
                        insertStrux(PTX_Block);
                  }
                  m_newParaFlagged = false;
                  m_bSectionHasPara = true;
                  bSuccess = getDoc()->changeStruxFmt(PTC_SetFmt,m_dposPaste,m_dposPaste, attribs,NULL,PTX_Block);
                  //
                  // Now check if this strux has associated list element. If so stop the list!
                  //
                  PL_StruxDocHandle sdh = NULL;
                  getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_Block,&sdh);
                  UT_uint32 nLists = getDoc()->getListsCount();
                  bool bisListItem = false;
                  //
                  // Have to loop so that multi-level lists get stopped. Each StopList removes
                  // the sdh from the next highest level.
                  //
                  UT_sint32 iLoop = 20;
                  do
                  {
                        fl_AutoNum * pAuto = NULL;
                        bisListItem = false;
                        for(UT_uint32 i=0; (i< nLists && !bisListItem); i++)
                        {
                              pAuto = getDoc()->getNthList(i);
                              if(pAuto)
                                    bisListItem = pAuto->isItem(sdh);
                        }
                        //
                        // We've created a list element where we should not. Stop it now!!
                        //
                        if(bisListItem)
                        {
                              UT_DEBUGMSG(("SEVIOR: Stopping list at %x \n",sdh));
                              getDoc()->StopList(sdh);
                        }
                        iLoop--;
                  }
                  while(bisListItem && (iLoop > 0));
            }
            return bSuccess;
      }
}


bool IE_Imp_RTF::ResetCellAttributes(void)
{
      bool ok = FlushStoredChars();
      m_currentRTFState.m_cellProps = RTFProps_CellProps();
      return ok;
}


bool IE_Imp_RTF::ResetTableAttributes(void)
{
      bool ok = FlushStoredChars();
      m_currentRTFState.m_tableProps = RTFProps_TableProps();
      return ok;
}

bool IE_Imp_RTF::ResetParagraphAttributes()
{
      xxx_UT_DEBUGMSG(("Reset Para Attributes \n"));
      // the \pard keyword always implies we are already in a paragraph
      bool ok = FlushStoredChars();
      m_currentRTFState.m_paraProps = RTFProps_ParaProps();

      return ok;
}


bool IE_Imp_RTF::ResetSectionAttributes()
{
      // the \sectd keyword always implies we are in a section
      bool ok = FlushStoredChars();

      // not quite correct. a sectd will reset the section defaults
      // to the previously acquired page defaults

      // margr, margl, margt, margb, paperh, gutter

      m_currentRTFState.m_sectionProps = m_sectdProps ;
      m_bParaWrittenForSection = false;
      return ok;
}


bool IE_Imp_RTF::ApplySectionAttributes()
{
      XML_Char* pProps = "props";
      UT_String propBuffer;
      UT_String tempBuffer;
      UT_String szHdrID;
      UT_String szFtrID;
      UT_String szHdrEvenID;
      UT_String szFtrEvenID;
      UT_String szHdrFirstID;
      UT_String szFtrFirstID;
      UT_String szHdrLastID;
      UT_String szFtrLastID;
      short paramIndex = 0;

      UT_DEBUGMSG (("Applying SectionAttributes\n"));

      // columns
      UT_String_sprintf(tempBuffer, "columns:%d", m_currentRTFState.m_sectionProps.m_numCols);
      propBuffer += tempBuffer;

      if (m_currentRTFState.m_sectionProps.m_bColumnLine)
      {
            propBuffer += "; column-line:on";
      }
      {
            switch (m_currentRTFState.m_sectionProps.m_breakType) {
            case RTFProps_SectionProps::sbkNone:
//                propBuffer += "; ";
                  break;
            case RTFProps_SectionProps::sbkColumn:
                  break;
            case RTFProps_SectionProps::sbkPage:
                  break;
            case RTFProps_SectionProps::sbkEven:
                  break;
            case RTFProps_SectionProps::sbkOdd:
                  break;
            default:
                  UT_ASSERT_HARMLESS (UT_SHOULD_NOT_HAPPEN);
            }
      }
      if(true /*m_currentRTFState.m_sectionProps.m_leftMargTwips != 0*/)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");

            propBuffer += "; page-margin-left:";
            double inch = static_cast<double>(m_currentRTFState.m_sectionProps.m_leftMargTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      if(true /*m_currentRTFState.m_sectionProps.m_rightMargTwips != 0*/)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");
            propBuffer += "; page-margin-right:";
            double inch = static_cast<double>(m_currentRTFState.m_sectionProps.m_rightMargTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      if(true /*m_currentRTFState.m_sectionProps.m_topMargTwips != 0*/)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");
            propBuffer += "; page-margin-top:";
            double inch = static_cast<double>(m_currentRTFState.m_sectionProps.m_topMargTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      if(true /*m_currentRTFState.m_sectionProps.m_bottomMargTwips != 0*/)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");
            propBuffer += "; page-margin-bottom:";
            double inch = static_cast<double>(m_currentRTFState.m_sectionProps.m_bottomMargTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      if(true /*m_currentRTFState.m_sectionProps.m_colSpaceTwips != 0*/)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");
            propBuffer += "; column-gap:";
            double inch = static_cast<double>(m_currentRTFState.m_sectionProps.m_colSpaceTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      if(m_currentRTFState.m_sectionProps.m_headerYTwips != 0)
      {
            UT_sint32 sheader = 0;
//
// The RTF spec is to define a fixed height for the header. We calculate
// the header height as Top margin - header margin.
//
// So the header margin = topmargin - header height.
//
            if(m_currentRTFState.m_sectionProps.m_topMargTwips != 0)
            {
                  sheader = m_currentRTFState.m_sectionProps.m_headerYTwips;
                  if(sheader < 0)
                  {
                        sheader = 0;
                  }
            }
            propBuffer += "; page-margin-header:";
            double inch = static_cast<double>(sheader)/1440.;
            UT_String sinch;

            UT_LocaleTransactor(LC_NUMERIC, "C");
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
#if 0
      if(m_currentRTFState.m_sectionProps.m_gutterTwips != 0)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");
            propBuffer += "; page-margin-footer:";
            double inch = static_cast<double>( m_currentRTFState.m_sectionProps.m_gutterTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      UT_DEBUGMSG(("SEVIOR: propBuffer = %s \n",propBuffer.c_str()));
#endif
      if(m_currentRTFState.m_sectionProps.m_footerYTwips != 0)
      {
            UT_LocaleTransactor(LC_NUMERIC, "C");
            propBuffer += "; page-margin-footer:";
            double inch = static_cast<double>( m_currentRTFState.m_sectionProps.m_footerYTwips)/1440.;
            UT_String sinch;
            UT_String_sprintf(sinch,"%fin",inch);
            propBuffer += sinch;
      }
      UT_DEBUGMSG(("SEVIOR: propBuffer = %s \n",propBuffer.c_str()));
      if(m_currentRTFState.m_sectionProps.m_dir != UT_BIDI_UNSET)
      {
            const char r[] = "rtl";
            const char l[] = "ltr";
            const char ar[] = "right";
            const char al[] = "left";
            const char * d, * a;
            if(m_currentRTFState.m_sectionProps.m_dir == UT_BIDI_RTL)
            {
                  d = r;
                  a = ar;
            }
            else
            {
                  d = l;
                  a = al;
            }

            UT_String_sprintf(tempBuffer, "; dom-dir:%s; text-align:%s",d,a);
        propBuffer += tempBuffer;
        UT_DEBUGMSG(("Apply sect prop: [%s]\n", tempBuffer.c_str()));
      }

      UT_DEBUGMSG(("SEVIOR: propBuffer = %s \n",propBuffer.c_str()));

      const XML_Char* propsArray[15];
      propsArray[0] = pProps;
      propsArray[1] = propBuffer.c_str();
      paramIndex = 2;
      if (m_currentHdrID != 0)
      {
            UT_DEBUGMSG (("Applying header\n"));
            propsArray [paramIndex] = "header";
            paramIndex++;
            UT_String_sprintf (szHdrID, "%u", m_currentHdrID);
            propsArray [paramIndex] = szHdrID.c_str();
            paramIndex++;
      }
      if (m_currentHdrEvenID != 0)
      {
            UT_DEBUGMSG (("Applying header even\n"));
            propsArray [paramIndex] = "header-even";
            paramIndex++;
            UT_String_sprintf (szHdrEvenID, "%u", m_currentHdrEvenID);
            propsArray [paramIndex] = szHdrEvenID.c_str();
            paramIndex++;
      }
      if (m_currentHdrFirstID != 0)
      {
            UT_DEBUGMSG (("Applying header first\n"));
            propsArray [paramIndex] = "header-first";
            paramIndex++;
            UT_String_sprintf (szHdrFirstID, "%u", m_currentHdrFirstID);
            propsArray [paramIndex] = szHdrFirstID.c_str();
            paramIndex++;
      }
      if (m_currentHdrLastID != 0)
      {
            UT_DEBUGMSG (("Applying header last\n"));
            propsArray [paramIndex] = "header-last";
            paramIndex++;
            UT_String_sprintf (szHdrLastID, "%u", m_currentHdrLastID);
            propsArray [paramIndex] = szHdrLastID.c_str();
            paramIndex++;
      }
      if (m_currentFtrID != 0)
      {
            UT_DEBUGMSG (("Applying footer\n"));
            propsArray [paramIndex] = "footer";
            paramIndex++;
            UT_String_sprintf (szFtrID, "%u", m_currentFtrID);
            propsArray [paramIndex] = szFtrID.c_str();
            paramIndex++;
      }
      if (m_currentFtrEvenID != 0)
      {
            UT_DEBUGMSG (("Applying footer even\n"));
            propsArray [paramIndex] = "footer-even";
            paramIndex++;
            UT_String_sprintf (szFtrEvenID, "%u", m_currentFtrEvenID);
            propsArray [paramIndex] = szFtrEvenID.c_str();
            paramIndex++;
      }
      if (m_currentFtrFirstID != 0)
      {
            UT_DEBUGMSG (("Applying footer first\n"));
            propsArray [paramIndex] = "footer-first";
            paramIndex++;
            UT_String_sprintf (szFtrFirstID, "%u", m_currentFtrFirstID);
            propsArray [paramIndex] = szFtrFirstID.c_str();
            paramIndex++;
      }
      if (m_currentFtrLastID != 0)
      {
            UT_DEBUGMSG (("Applying footer last\n"));
            propsArray [paramIndex] = "footer-last";
            paramIndex++;
            UT_String_sprintf (szFtrLastID, "%u", m_currentFtrLastID);
            propsArray [paramIndex] = szFtrLastID.c_str();
            paramIndex++;
      }
      UT_ASSERT_HARMLESS (paramIndex < 15);
      propsArray [paramIndex] = NULL;

      if (!bUseInsertNotAppend()) // if we are reading a file or parsing a header and footer
      {
            UT_DEBUGMSG(("Appending Section strux now \n"));
            return getDoc()->appendStrux(PTX_Section, propsArray);
      }
      else
      {
            // Add a block before the section so there's something content
            // can be inserted into.
            UT_DEBUGMSG(("Insert block at 3 \n"));
            markPasteBlock();
            bool bSuccess = insertStrux(PTX_Block);

            if (bSuccess)
            {
                  m_dposPaste--;
                  XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
                  if(pFrame == NULL)
                  {
                        return false;
                  }
                  FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
                  if(pView == NULL)
                  {
                        return false;
                  }
                  if(!pView->isInDocSection(m_dposPaste))
                  {
                        return false;;
                  }
                  bSuccess = insertStrux(PTX_Section);
                  if (bSuccess)
                  {
                        bSuccess = getDoc()->changeStruxFmt(PTC_SetFmt,m_dposPaste,m_dposPaste,
                                                                                 propsArray,NULL,PTX_Section);
                  }
            }
            return bSuccess;
      }
}

//////////////////////////////////////////////////////////////////////////////
// List Table reader
/////////////////////////////////////////////////////////////////////////////

/*!
 * This is a general purpose parameter reader. It returns the value of a keyword
 * surrounded by a brace. For a construct of the form...
 * {\mehere fred}
 *         ^
 *         Current read point.
 * It will return fred and swallow the closing brace.
 * For a construct of the form
 * {\mehere fred;}
 *         ^
 *         Current read point.
 * It will return fred and swallow the closing brace and semicolon.
 * For a construct of the form
 * {\mehere {\key1 fred;} {\key2 fred2} {\key3 {\key4 fred}}}
 *         ^
 *         Current read point.
 * It will return {\key1 fred;} {\key2 fred2} {\key3 {\key4 fred}}  and
 * swallow the closing brace.
 * returns NULL on error.
 */
char * IE_Imp_RTF::getCharsInsideBrace(void)
{
      unsigned static char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;

      // OK scan through the text until a closing delimeter is
      // found

      UT_sint32 count = 0;
      UT_uint32 nesting = 1;
      while(nesting > 0)
      {
            if (!ReadCharFromFile(&ch))
                  return NULL;
        if( nesting == 1 && (ch == '}'  || ch == ';'))
            {
                  nesting--;
            }
            else
            {
                  if(ch == '{')
                  {
                        nesting++;
                  }
                  if(ch == '}')
                  {
                        nesting--;
                  }
                  keyword[count++] = ch;
            }
      }
      if(ch == ';')
      {
//
// Swallow closing brace if ";}" finishes
//
            if (!ReadCharFromFile(&ch))
                  return NULL;
//
// if character is not a '}' put it back in the input stream.
//
            if(ch != '}')
            {
                  SkipBackChar(ch);
            }
      }
      keyword[count++] = 0;
      return reinterpret_cast<char*>(&keyword[0]);
}


bool IE_Imp_RTF::ReadListTable()
{
//
// Ensure the list tables are empty to start.
//
      UT_VECTOR_PURGEALL(RTF_msword97_list*, m_vecWord97Lists);
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      UT_uint32 nesting = 1;
      xxx_UT_DEBUGMSG(("Doing Read List Table \n"));
      while (nesting >0) // Outer loop
      {
            xxx_UT_DEBUGMSG(("Nesting %d \n",nesting));
            if (!ReadCharFromFile(&ch))
            {
                  return false;
            }
            if(ch == '{')  //new list or listoverride?
            {
                  nesting++;
                  if (!ReadCharFromFile(&ch))
                  {
                        return false;
                  }
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]), "list") == 0)
                  {
                        if(!HandleTableList())
                              return false;

// HandleTableList eats the last "}"

                        nesting--;
                  }
                  else
                  {
                        UT_DEBUGMSG(("Unexpected keyword in listable %s Here \n",keyword));
                  }
            }
            else if(ch == '}')
            {
                  nesting--;
            }
      }
      xxx_UT_DEBUGMSG(("Return from List Table \n"));
      return true;
}

/*!
 * This method parses out the \list item in a list tabledefinition.
 */
bool IE_Imp_RTF::HandleTableList(void)
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
    UT_sint32 parameter = 0;
      bool paramUsed = false;
      UT_uint32 nesting = 1;
      UT_uint32 levelCount = 0;
//
// Increment list counting vector
//
      RTF_msword97_list * pList = new  RTF_msword97_list(this);
      m_vecWord97Lists.addItem(pList);
//
// OK Parse this \list
//
      while(nesting > 0) // Outer loop
      {
            if (!ReadCharFromFile(&ch))
                  return false;
            if(ch == '{')  // listlevel
            {
                  if (!ReadCharFromFile(&ch))
                  {
                        return false;
                  }
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if(strcmp(reinterpret_cast<char*>(&keyword[0]),"listlevel") == 0)
                  {
                        HandleListLevel(pList,levelCount);
                        levelCount++;
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"listid") == 0)
                  {
                        pList->m_RTF_listID = static_cast<UT_uint32>(parameter);
                  }
                  else
                  {
                        char * szLevelText = getCharsInsideBrace();
                        if(!szLevelText)
                              return false;
                  }
            }
            else if(ch == '}')
            {
                  nesting--;
            }
            else
            {
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if(strcmp(reinterpret_cast<char*>(&keyword[0]),"listtemplateid") == 0)
                  {
                        pList->m_RTF_listTemplateID = parameter;
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"listid") == 0)
                  {
                        pList->m_RTF_listID = static_cast<UT_uint32>(parameter);
                  }
                  else
                  {
                        UT_DEBUGMSG(("SEVIOR: keyword %s found and ignored in listtable definition \n",keyword));
                  }
            }
      }
      return true;
}

/*!
 * This method parses out the list table definition of listlevel's
 */
bool IE_Imp_RTF::HandleListLevel(RTF_msword97_list * pList, UT_uint32 levelCount  )
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      UT_uint32 nesting = 1;
      UT_String szLevelNumbers;
      UT_String szLevelText;
//
// OK define this in the data structure.
//
      RTF_msword97_level * pLevel = new RTF_msword97_level(pList, levelCount);
    RTFProps_ParaProps * pParas =  new RTFProps_ParaProps();
      RTFProps_CharProps *  pChars = new  RTFProps_CharProps();
    RTFProps_bParaProps * pbParas =  new RTFProps_bParaProps();
      RTFProps_bCharProps *  pbChars = new      RTFProps_bCharProps();
      pLevel->m_pParaProps = pParas;
      pLevel->m_pCharProps = pChars;
      pLevel->m_pbParaProps = pbParas;
      pLevel->m_pbCharProps = pbChars;
      pList->m_RTF_level[levelCount] = pLevel;
#if 0 // Sevior use this!! The other method can lead to inccorect results upon
      // import. If we export RTF list ID starting at 10000 they might clash
    // with these later.
      pLevel->m_AbiLevelID = UT_rand();
      while(pLevel->m_AbiLevelID < 10000)
            pLevel->m_AbiLevelID = UT_rand();
#else
      //pLevel->m_AbiLevelID = pLevel->m_sLastAssignedLevelID++;
      pLevel->m_AbiLevelID = getDoc()->getUID(UT_UniqueId::List);
#endif
      while(nesting > 0)
      {
            if (!ReadCharFromFile(&ch))
                  return false;
            if(ch == '{')  // levelnumber and leveltext
            {
                  if (!ReadCharFromFile(&ch))
                        return false;
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelnumbers") == 0)
                  {
                        szLevelNumbers = getCharsInsideBrace();
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"leveltext") == 0)
                  {
                        szLevelText = getCharsInsideBrace();
                  }
                  else
                  {
                        getCharsInsideBrace();
                  }
            }
            else if(ch == '}')  // Probabally finished here.
            {
                  nesting--;
            }
            else
            {
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelnfc") == 0) // RTF list Type
                  {
                        pLevel->m_RTFListType = static_cast<UT_uint32>(parameter);
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelnfcn") == 0)  // Not in my docs
                  {
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"leveljc") == 0) // Justification
                  {
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"leveljcn") == 0) // Not in my docs
                  {
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelfollow") == 0) // Tab following
                  {
                        switch (parameter)
                        {
                        case 0: // Tab
                              pLevel->m_cLevelFollow = '\t';
                              break;
                        case 1: // Space
                              pLevel->m_cLevelFollow = ' ';
                              break;
                        case 2: // Nothing
                              pLevel->m_cLevelFollow = '\0';
                              break;
                        default:
                              UT_ASSERT_NOT_REACHED();
                              break;
                        }
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelstartat") == 0)
                  {
                        pLevel->m_levelStartAt = static_cast<UT_uint32>(parameter);
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelspace") == 0) // ignore
                  {
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"levelindent") == 0) // ignore
                  {
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]), "levelnorestart") ==0)
                  {
                        pLevel->m_bRestart = (parameter == 1);
                  }
//
// OK parse againgst all the character and allowed paragraph properties.
//
                  else
                  {
                        if(!ParseCharParaProps(static_cast<unsigned char *>(keyword), parameter, paramUsed,pChars,pParas,pbChars,pbParas))
                              return false;
                  }
            }
      }
      if(pLevel->m_RTFListType != 23)
      {
            pLevel->ParseLevelText(szLevelText,szLevelNumbers, levelCount);
      }
      else
      {
            pLevel->m_listDelim = "%L";
            if(strstr(szLevelText.c_str(),"u-3913") != 0)
            {
                  pLevel->m_RTFListType = 23; // Bulleted List
            }
            if(strstr(szLevelText.c_str(),"u-3880") != 0)
            {
                  pLevel->m_RTFListType = 23 + IMPLIES_LIST; // IMPLIES List
            }
      }
      return true;
}

/*!
 * OK this method parses the RTF against all the character and 
 * paragraph properties.
 * and fills the pointers to the character and paragraph classes.
 * These are used by the list table and stylesheet reader.
 */
bool IE_Imp_RTF::ParseCharParaProps( unsigned char * pKeyword, 
                                     UT_sint32 param, bool fParam, 
                                     RTFProps_CharProps * pChars, 
                                     RTFProps_ParaProps * pParas, 
                                     RTFProps_bCharProps * pbChars, 
                                     RTFProps_bParaProps * pbParas)
{
      if (strcmp(reinterpret_cast<char*>(pKeyword), "b") == 0) // bold
      {
            pbChars->bm_bold = true;
            pChars->m_bold = fParam ? false : true;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "cf") == 0) // color
      {
            pChars->m_hasColour = true;
            pbChars->bm_hasColour = true;
            pbChars->bm_colourNumber = true;
            pChars->m_colourNumber = static_cast<UT_uint32>(param);
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "cb") == 0) // background color
      {
            pbChars->bm_bgcolourNumber = true;
            return HandleU32CharacterProp(static_cast<UT_uint32>(param), &(pChars->m_bgcolourNumber));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "deleted") == 0) // deleted
      {
            pbChars->bm_deleted = true;
            return HandleBoolCharacterProp(fParam ? false : true, &(pChars->m_deleted));
      }
      else if (strcmp(reinterpret_cast<char *>(pKeyword),"dn") == 0) // subscript with position
      {
            // subscript with position. Default is 6.
            // superscript: see up keyword
            bool ok;
            UT_uint32 pos = (UT_uint32) (fParam ? param : 6);
            ok = HandleBoolCharacterProp((pos != 0) ? true : false, &(pChars->m_superscript));
            if (ok)
            {
                  pbChars->bm_superscript_pos = true;
                  ok = HandleFloatCharacterProp (pos*0.5, &(pChars->m_superscript_pos));
            }
            return ok;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "fs") == 0)
      {
            pbChars->bm_fontSize = true;
            return HandleFloatCharacterProp ((fParam ? param : 24)*0.5, &(pChars->m_fontSize));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "f") == 0)
      {
            UT_uint32 fontNumber = (UT_uint32) (fParam ? param : 0);
            RTFFontTableItem* pFont = GetNthTableFont(fontNumber);
            if (pFont != NULL && pFont->m_szEncoding)
                  m_mbtowc.setInCharset(pFont->m_szEncoding);

            pbChars->bm_fontNumber = true;
            return HandleU32CharacterProp(fontNumber, &(pChars->m_fontNumber));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "fi") == 0)
      {
            pParas->m_indentFirst = param;
            pbParas->bm_indentFirst = true;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "i") == 0)
      {
            // italic - either on or off depending on the parameter
            pbChars->bm_italic = true;
            return HandleBoolCharacterProp((fParam ? false : true), &(pChars->m_italic));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "lang") == 0)
      {
            pChars->m_szLang = wvLIDToLangConverter(static_cast<unsigned short>(param));
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "li") == 0)
      {
            pbParas->bm_indentLeft = true;
            pParas->m_indentLeft = param;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "listtag") == 0)
      {
            pbChars->bm_listTag = true;
            pChars->m_listTag = param;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword),"ol") == 0)
      {
            pbChars->bm_overline = true;
            return HandleBoolCharacterProp((fParam ? (param != 0) : true), &(pChars->m_overline));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "ql") == 0)
      {
            pbParas->bm_justification = true;
            pParas->m_justification = RTFProps_ParaProps::pjLeft;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "qc") == 0)
      {
            pbParas->bm_justification = true;
            pParas->m_justification = RTFProps_ParaProps::pjCentre;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "qr") == 0)
      {
            pbParas->bm_justification = true;
            pParas->m_justification = RTFProps_ParaProps::pjRight;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "qj") == 0)
      {
            pbParas->bm_justification = true;
            pParas->m_justification = RTFProps_ParaProps::pjFull;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "ri") == 0)
      {
            pbParas->bm_indentRight = true;
            pParas->m_indentRight = param;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "strike") == 0  ||  strcmp(reinterpret_cast<char*>(pKeyword), "striked") == 0)
      {
            pbChars->bm_strikeout = true;
            return HandleBoolCharacterProp((fParam ? (param != 0) : true), &(pChars->m_strikeout));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "sa") == 0)
      {
            pbParas->bm_spaceAfter = true;
            pParas->m_spaceAfter = param;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "sb") == 0)
      {
            pbParas->bm_spaceBefore = true;
            pParas->m_spaceBefore = param;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "sl") == 0)
      {
            pbParas->bm_lineSpaceVal = true;
            if (!fParam  ||  param == 0)
            {
                  pParas->m_lineSpaceVal = 360;
            }
            else
            {
                  pParas->m_lineSpaceVal = param;
            }
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "slmult") == 0)
      {
            pbParas->bm_lineSpaceExact = true;
            pParas->m_lineSpaceExact = (!fParam  ||  param == 0);   // this means exact or "at least" - which depends on sign of \sl param
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "super") == 0)
      {
            pbChars->bm_superscript = true;
            return HandleBoolCharacterProp((fParam ? false : true), &(pChars->m_superscript));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "sub") == 0)
      {
            pbChars->bm_subscript = true;
            return HandleBoolCharacterProp((fParam ? false : true), &(pChars->m_subscript));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tx") == 0)
      {
            UT_return_val_if_fail(fParam, false);     // tabstops should have parameters
            bool bres = AddTabstop(param,
                                             pParas->m_curTabType,
                                             pParas->m_curTabLeader,pParas);
            pParas->m_curTabType = FL_TAB_LEFT;
            pParas->m_curTabLeader = FL_LEADER_NONE;
            pbParas->bm_curTabType = true;
            pbParas->bm_curTabLeader = true;
            return bres;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tb") == 0)
      {
            UT_return_val_if_fail(fParam, false);     // tabstops should have parameters

            bool bres = AddTabstop(param,FL_TAB_BAR,
                                             pParas->m_curTabLeader,pParas);
            pParas->m_curTabType = FL_TAB_LEFT;
            pParas->m_curTabLeader = FL_LEADER_NONE;
            pbParas->bm_curTabType = true;
            pbParas->bm_curTabLeader = true;
            return bres;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "jclisttab") == 0)
      {
            UT_DEBUGMSG(("SEVIOR: jclisttab found ignore for now \n"));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tqr") == 0)
      {
            pbParas->bm_curTabType = true;
            pParas->m_curTabType = FL_TAB_RIGHT;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tqc") == 0)
      {
            pbParas->bm_curTabType = true;
            pParas->m_curTabType = FL_TAB_CENTER;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tqdec") == 0)
      {
            pbParas->bm_curTabType = true;
            pParas->m_curTabType = FL_TAB_DECIMAL;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tldot") == 0)
      {
            pbParas->bm_curTabLeader = true;
            pParas->m_curTabLeader = FL_LEADER_DOT;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tlhyph") == 0)
      {
            pbParas->bm_curTabLeader = true;
            pParas->m_curTabLeader = FL_LEADER_HYPHEN;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tlul") == 0)
      {
            pbParas->bm_curTabLeader = true;
            pParas->m_curTabLeader = FL_LEADER_UNDERLINE;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "tleq") == 0)
      {
            pbParas->bm_curTabLeader = true;
            pParas->m_curTabLeader = FL_LEADER_EQUALSIGN;
            return true;
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "ul") == 0        ||  strcmp(reinterpret_cast<char*>(pKeyword), "uld") == 0  ||
                   strcmp(reinterpret_cast<char*>(pKeyword), "uldash") == 0    ||  strcmp(reinterpret_cast<char*>(pKeyword), "uldashd") == 0  ||
                   strcmp(reinterpret_cast<char*>(pKeyword), "uldashdd") == 0  ||  strcmp(reinterpret_cast<char*>(pKeyword), "uldb") == 0  ||
                   strcmp(reinterpret_cast<char*>(pKeyword), "ulth") == 0      ||  strcmp(reinterpret_cast<char*>(pKeyword), "ulw") == 0  ||
                   strcmp(reinterpret_cast<char*>(pKeyword), "ulwave") == 0)
      {
            pbChars->bm_underline = true;
            return HandleBoolCharacterProp((fParam ? (param != 0) : true), &(pChars->m_underline));
      }
      else if (strcmp(reinterpret_cast<char*>(pKeyword), "ulnone") == 0)
      {
            pbChars->bm_underline = true;
            return HandleBoolCharacterProp(false, &(pChars->m_underline));
      }
      else if (strcmp(reinterpret_cast<char *>(pKeyword),"up") == 0)
      {
            // superscript with position. Default is 6.
            // subscript: see dn keyword
            bool ok;
            UT_uint32 pos = (UT_uint32) (fParam ? param : 6);
            pbChars->bm_superscript = true;
            pChars->m_superscript = (pos != 0) ? true : false ;
            pbChars->bm_superscript_pos = true;
            ok = HandleFloatCharacterProp (pos*0.5, &(pChars->m_superscript_pos));
            return ok;
      }
      return true;
}




bool IE_Imp_RTF::ReadListOverrideTable(void)
{
//
// Ensure the list tables are empty to start.
//
      UT_VECTOR_PURGEALL(RTF_msword97_listOverride*, m_vecWord97ListOverride);
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      UT_uint32 nesting = 1;
      while (nesting >0) // Outer loop
      {
            if (!ReadCharFromFile(&ch))
                  return false;
            if(ch == '{')  //new list or listoverride?
            {
                  if (!ReadCharFromFile(&ch))
                        return false;
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]), "listoverride") == 0)
                  {
                        if(!HandleTableListOverride())
                        {
                              return false;
                        }
                  }
            }
            else if(ch == '}')
            {
                  nesting--;
            }
      }
      return true;
}

/*!
  Get list override of given id
  \param id Id of list override
  \return List override or NULL if not found

  The old code in ApplyParagraphAttributes would use the given
  id as an index to the vector of list overrides. But these
  can be given arbitrary ids from 1 to 2000, so the code
  would not always have worked. Also, and more relevant,
  this function handles an id of 0, as output by 
  StarWriter/OpenOffice
  even though it is not allowed in the spec.
*/
RTF_msword97_listOverride*
IE_Imp_RTF::_getTableListOverride(UT_uint32 id)
{
      UT_uint32 i;
      RTF_msword97_listOverride* pLOver;

      for (i = 0; i < m_vecWord97ListOverride.size(); i++)
      {
            pLOver = m_vecWord97ListOverride.getNthItem(i);
            if (id == pLOver->m_RTF_listID)
            {
                  return pLOver;
            }
      }

      // Client requested a list override that was not defined.
      UT_ASSERT_NOT_REACHED();
      return NULL;
}

bool IE_Imp_RTF::HandleTableListOverride(void)
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
//
// OK define this in the data structure.
//
      RTF_msword97_listOverride * pLOver = new  RTF_msword97_listOverride(this);
//
// Increment override counting vector
//
      m_vecWord97ListOverride.addItem(pLOver);
    RTFProps_ParaProps * pParas =  new RTFProps_ParaProps();
      RTFProps_CharProps *  pChars = new  RTFProps_CharProps();
    RTFProps_bParaProps * pbParas =  new RTFProps_bParaProps();
      RTFProps_bCharProps *  pbChars = new      RTFProps_bCharProps();
      pLOver->m_pParaProps = pParas;
      pLOver->m_pCharProps = pChars;
      pLOver->m_pbParaProps = pbParas;
      pLOver->m_pbCharProps = pbChars;

      UT_uint32 nesting = 1;
      while (nesting >0) // Outer loop
      {
            if (!ReadCharFromFile(&ch))
            {
                  return false;
            }
            if(ch == '}')
            {
                  nesting--;
            }
            else if(ch == '{')
            {
                  nesting++;
            }
            else if(ch == '\\')
            {
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  if(strcmp(reinterpret_cast<char*>(&keyword[0]),"listid") == 0)
                  {
                        pLOver->m_RTF_listID = static_cast<UT_uint32>(parameter);
                        if(!pLOver->setList())
                        {
                              return false;
                        }
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"listoverridecount")==0)
                  {
                        xxx_UT_DEBUGMSG(("SEVIOR: Found list listoverride count. Ignore for now\n"));
                  }
                  else if(strcmp(reinterpret_cast<char*>(&keyword[0]),"ls")== 0)
                  {
                        pLOver->m_RTF_listID = static_cast<UT_uint32>(parameter);
                  }
                else
                  {
                        ParseCharParaProps(reinterpret_cast<unsigned char *>(keyword), parameter,paramUsed,pChars,pParas,pbChars,pbParas);
                  }
            }
      }
      return true;
}

// the revision table looks
// \*\revtb{{Author1;}{Author2;} ... }
bool IE_Imp_RTF::ReadRevisionTable()
{
      unsigned char ch = 0;
      UT_UCS4String s;
      UT_sint32 i = 1;

      while(ReadCharFromFile(&ch) && ch != '}')
      {
            while(ch != '{' && ReadCharFromFile(&ch)){;}
      
            if(ch != '{')
                  return false;
            
            s.clear();
            
            while(ReadCharFromFile(&ch) && ch != ';')
            {
                  s += ch;
            }
            // the semicolon should be followed by a closing brace
            ReadCharFromFile(&ch);
            UT_return_val_if_fail(ch == '}', false);

            // now add the revision to the document
            // the rtf doc stores the time stamp for each individual rev. operation rather
            // than for the revision set; we will set it when we encounter the first revision

            // the first entry is typically author Unknown; we will ignore it
            // hack around non stricmp
            UT_UCS4Char u1[] = {'U','n','k','n','o','w','n',0};
            UT_UCS4Char u2[] = {'u','n','k','n','o','w','n',0};
            
            if(i == 1 && (!UT_UCS4_strcmp(s.ucs4_str(), u1) || !UT_UCS4_strcmp(s.ucs4_str(), u2)))
                  continue;
            
            getDoc()->addRevision(i,s.ucs4_str(),s.length(),0,0);
            ++i;
      }

      UT_return_val_if_fail( ch == '}', false );
      return true;
}

//////////////////////////////////////////////////////////////////////////////
// Font table reader
//////////////////////////////////////////////////////////////////////////////

// Reads the RTF font table, storing it for future use
//
bool IE_Imp_RTF::ReadFontTable()
{
      // Ensure the font table is empty before we start
      if (m_fontTable.getItemCount() != 0) {
            UT_DEBUGMSG (("Font table already contains %d items !\n", m_fontTable.getItemCount()));
      }

// don't purge the vector as we may have several fonttable....
// just handle dupes as seen below.

//    UT_VECTOR_PURGEALL(RTFFontTableItem*, m_fontTable);

      unsigned char ch;
 _next_font:
      if (!ReadCharFromFile(&ch))
            return false;

      if (ch == '\\')
      {
            SkipBackChar(ch);
            // one entry in the font table
            // TODO - Test one item font tables!
            if (!ReadOneFontFromTable(false))
                  return false;

            goto _next_font; // Cocoa RTF: {\fonttbl\f0\fnil\fcharset78 HiraKakuPro-W3;\f1\fnil\fcharset102 STXihei;}
      }
      else
      {
// don't choke if there is no data (malformed RTF)
// see bug 1383 and 1384
            while (ch != '}' && ch != '{') {
                  if (!ReadCharFromFile(&ch))
                        return false;
            }

            // multiple entries in font table
            while (ch != '}')
            {
                  if (ch != '{')
                  {
                              return false;
                  }

                  if (!ReadOneFontFromTable(true))
                        return false;

                  // now eat whitespace until we hit either '}' (end of font group) or '{' (another font item)
                  do
                  {
                        if (!ReadCharFromFile(&ch))
                              return false;
                  } while (ch != '}'  &&  ch != '{');
            }
      }

      // Put the close group symbol back into the input stream
      return SkipBackChar(ch);
}


// Reads one font item from the font table in the RTF font table.  When called
// the file must be at the f of the 'f' (fontnum) keyword.
// Our life is made easier as the order of items in the table is specified.
//
bool IE_Imp_RTF::ReadOneFontFromTable(bool bNested)
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
    UT_sint32 parameter = 0;
      bool paramUsed = false;

      int nesting = 0;

      // run though the item reading in these values
      RTFFontTableItem::FontFamilyEnum fontFamily = RTFFontTableItem::ffNone;
      RTFFontTableItem::FontPitch pitch = RTFFontTableItem::fpDefault;
      UT_uint16 fontIndex = 0;
      int charSet = 0;
      int codepage = 0;
      unsigned char panose[10];
      memset(panose, 0, sizeof(unsigned char));
      char* pFontName = NULL;
      char* pAlternativeFontName = NULL;
      RTFTokenType tokenType;

      //TODO - this should be intialized once for the whole RTF reader.
      UT_StringPtrMap keywordMap;
      keywordMap.insert("fcharset",&charSet);
      keywordMap.insert("cpg",&codepage);
      //TODO - handle the other keywords
      int * pValue;

      tokenType = NextToken(keyword,&parameter,&paramUsed,MAX_KEYWORD_LEN,true);
      if (tokenType != RTF_TOKEN_KEYWORD || (strcmp(reinterpret_cast<char*>(&keyword[0]), "f") != 0))
      {
            return false;
      }
      else
      {
            // parameter is signed. fontIndex is unsigned. We convert here. Both
            // are 16-bits integer as per de RTF spec.
            fontIndex = parameter;
      }

    // ignore white space here to work around some broken docs. See 2719

      tokenType = NextToken(keyword,&parameter,&paramUsed,MAX_KEYWORD_LEN,true);
      if (tokenType != RTF_TOKEN_KEYWORD)
      {
            // Read the font family (must be specified), but we'll let it slide...
            // tokenType will be RTF_TOKEN_DATA, so we'll bypass the following while() loop
            fontFamily = RTFFontTableItem::ffNone;
      }
      else
      {
            if (strcmp(reinterpret_cast<char*>(&keyword[0]), "fnil") == 0)
                  fontFamily = RTFFontTableItem::ffNone;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "froman") == 0)
                  fontFamily = RTFFontTableItem::ffRoman;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "fswiss") == 0)
                  fontFamily = RTFFontTableItem::ffSwiss;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "fmodern") == 0)
                  fontFamily = RTFFontTableItem::ffModern;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "fscript") == 0)
                  fontFamily = RTFFontTableItem::ffScript;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "fdecor") == 0)
                  fontFamily = RTFFontTableItem::ffDecorative;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "ftech") == 0)
                  fontFamily = RTFFontTableItem::ffTechnical;
            else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "fbidi") == 0)
                  fontFamily = RTFFontTableItem::ffBiDirectional;
            else
            {
                  fontFamily = RTFFontTableItem::ffNone;
            }
      }
      // Now (possibly) comes some optional keyword before the fontname
      while (tokenType != RTF_TOKEN_DATA || nesting > 0)
      {
      tokenType = NextToken(keyword,&parameter,&paramUsed,MAX_KEYWORD_LEN,true);
            switch (tokenType)
            {
            case RTF_TOKEN_OPEN_BRACE:
                  nesting ++;
                  break;
            case RTF_TOKEN_CLOSE_BRACE:
                  nesting --;
                  break;
            case RTF_TOKEN_DATA:
                  break;
            case RTF_TOKEN_KEYWORD:
                  pValue = const_cast<int*>(static_cast<const int*>(keywordMap.pick(reinterpret_cast<char*>(&keyword[0]))));
                  if (pValue != NULL)
                  {
                        UT_return_val_if_fail(paramUsed, false);
                        *pValue = parameter;
                  }
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]),"panose") == 0)
                  {
                        // These take the form of {\*\panose 020b0604020202020204}
                        // Panose numbers are used to classify Latin-1 fonts for matching
                        // If you are really interested, see
                        // http://www.w3.org/TR/REC-CSS2/notes.html#panose
                        if (!ReadCharFromFile(&ch))
                        {
                              return false;
                        }

                        if (isdigit(ch))
                        {
                              SkipBackChar(ch);
                        }

                        for (int i = 0; i < 10; i++)
                        {
                              // TODO: Matti Picus commented out the original code
                              // since he read the web page above and realized it
                              // is broken.  Perhaps someone can explain what the
                              // original author's intent was? The code now just
                              // grabs the second char from each byte of the number
                              //
                              //unsigned char buf[3] = "00";
                              //if ( !ReadCharFromFile(&(buf[0]))  ||  !ReadCharFromFile(&(buf[1])) )
                              //{
                              //    return false;
                              //}
                              //unsigned char val = static_cast<unsigned char>(atoi(static_cast<char*>(buf)));
                              //panose[i] = val;
                              if ( !ReadCharFromFile(&ch)  ||  !ReadCharFromFile(&ch) )
                              {
                                    return false;
                              }
                              panose[i] = ch;
                        }
                  }
                  break;
            default:
                  //TODO: handle errors
                  break;
            }
      }
      if (nesting == -1)
      {
            UT_DEBUGMSG(("RTF: Font name not found in font definition %d",fontIndex));
      }
      // Now comes the font name, terminated by either a close brace or a slash or a semi-colon
      ch = keyword[0];
      int count = 0;
      /*
          FIXME: CJK font names come in form \'aa\'cd\'ef - so we have to
          parse \'HH correctly (currently we ignore them!) - VH
      */
      while ( ch != '}'  &&  ch != '\\'  &&  ch != ';' && ch!= '{')
      {
            keyword[count++] = ch;
            if (!ReadCharFromFile(&ch))
            {
                  return false;
            }
      }
      if (ch=='{')
      {
            ++nesting;
      }

      keyword[count] = 0;
#ifndef XP_TARGET_COCOA
      /*work around "helvetica" font name -replace it with "Helvetic"*/
      if (!UT_stricmp(reinterpret_cast<char*>(&keyword[0]),"helvetica"))
      {
            strcpy(reinterpret_cast<char*>(&keyword[0]),"Helvetic");
      }
#endif /* ! XP_TARGET_COCOA */

      if (!UT_cloneString(pFontName, reinterpret_cast<char*>(&keyword[0])))
      {
            // TODO outofmem
      }
      for (int i=0; i <= nesting; ++i)
      {
            // Munch the remaining control words down to the close brace
            while (ch != '}')
            {
                  if (ch == ';' && !bNested && (i == nesting))
                  {
                        break; // Cocoa RTF: {\fonttbl\f0\fnil\fcharset78 HiraKakuPro-W3;\f1\fnil\fcharset102 STXihei;}
                  }
                  if (!ReadCharFromFile(&ch))
                  {
                        return false;
                  }
                  if (ch=='{')
                  {
                        ++nesting;
                  }
            }
            if (nesting>0 && i!=nesting) //we need to skip '}' we've just seen.
            {
                  if (!ReadCharFromFile(&ch))
                  {
                        return false;
                  }
            }
      }

      // Create the font entry and put it into the font table
      RTFFontTableItem* pNewFont = new RTFFontTableItem(fontFamily, charSet,
                                                                                codepage, pitch,
                                                                                panose, pFontName,
                                                                                pAlternativeFontName);
      if (pNewFont == NULL)
      {
            return false;
      }

      // ensure that the font table is large enough for this index
      while (m_fontTable.getItemCount() <= fontIndex)
      {
            m_fontTable.addItem(NULL);
      }
      RTFFontTableItem* pOld = NULL;
      // some RTF files define the fonts several time. This is INVALID according to the
      // specifications. So we ignore it.
      if (m_fontTable[fontIndex] == NULL)
      {
            UT_sint32 res = m_fontTable.setNthItem(fontIndex, pNewFont, &pOld);
            UT_return_val_if_fail(res == 0, false);
            UT_return_val_if_fail(pOld == NULL, false);
      }
      else
      {
            UT_DEBUGMSG (("RTF: font %d (named %s) already defined. Ignoring\n", fontIndex, pFontName));
            DELETEP (pNewFont);
      }

      return true;
}




//////////////////////////////////////////////////////////////////////////////
// Colour table reader
//////////////////////////////////////////////////////////////////////////////

bool IE_Imp_RTF::ReadColourTable()
{
      // Ensure the table is empty before we start
      UT_return_val_if_fail(m_colourTable.getItemCount() == 0, false);

      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      if (!ReadCharFromFile(&ch))
            return false;
      bool bValidColor = false;
      while (ch != '}')
      {
            UT_uint32 colour = 0;
            bool tableError = false;
            while(ch == ' ')
            {
                  if (!ReadCharFromFile(&ch))
                        return false;
            }
            bValidColor = false;
            // Create a new entry for the colour table
            if (ch == ';')
            {
                  // Default colour required, black it is
                  colour = 0;
            }
            else if(ch != '}')
            {
                  if (ch == '\\')
                  {
                        // read colour definition
                        long red = 0;
                        long green = 0;
                        long blue = 0;
                        bool hasRed, hasGreen, hasBlue;
                        hasRed = false;
                        hasGreen = false;
                        hasBlue = false;
                        
                        for (int i = 0; i < 3; i++)
                        {
                              // read Red, Green and Blue values (will be in that order).
                              if (!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN)) 
                              {
                                    UT_DEBUGMSG (("ReadKeyword() failed in ReadColourTable()\n"));
                                    return false;
                              }
                              if (strcmp(reinterpret_cast<char*>(&keyword[0]), "red") == 0  &&  paramUsed)
                              {
                                    if (!hasRed) {
                                          red = parameter;
                                          hasRed = true;
                                    }
                                    else {
                                          tableError = true;
                                    }
                              }
                              else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "green") == 0  &&  paramUsed)
                              {
                                    if (!hasGreen) {
                                          green = parameter;
                                          hasGreen = true;
                                    }
                                    else {
                                          tableError = true;
                                    }
                              }
                              else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "blue") == 0  &&  paramUsed)
                              {
                                    if (!hasBlue) {
                                          blue = parameter;
                                          hasBlue = true;
                                    }
                                    else {
                                          tableError = true;
                                    }
                              }
                              else 
                              {
                                    tableError = true;
                              }
                              // Read slash at start of next keyword
                              if (!ReadCharFromFile(&ch) 
                                          || ((ch != '\\') && (ch != ';')))
                              {
                                    tableError = true;
                              }
                        }
                        colour = static_cast<UT_uint32>(red << 16) | static_cast<UT_uint32>(green << 8) | static_cast<UT_uint32>(blue);
                        UT_DEBUGMSG(("colour %d red %d green %d blue %d \n",colour,red,green,blue)); 
                        bValidColor = true;
                  }
                  else {
                        tableError = true;
                  }
            }

            if (tableError)
            {
                  UT_DEBUGMSG (("RTF color Table error\n"));
                  return false;
            }
            else if(ch!= '}' || bValidColor)
            {
                  UT_DEBUGMSG(("Add colour %d to table \n",colour));
                  m_colourTable.addItem(colour);

                  // Read in the next char
                  if (!ReadCharFromFile(&ch))
                        return false;
            }
      }

      // Put the '}' back into the input stream
      return SkipBackChar(ch);
}




//////////////////////////////////////////////////////////////////////////////
// List properties
//////////////////////////////////////////////////////////////////////////////

bool IE_Imp_RTF::HandleLists(_rtfListTable & rtfTable )
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      if (!ReadCharFromFile(&ch))
            return false;

      while (ch != '}') // Outer loop
      {
        //SkipBackChar(ch); // Put char back in stream
            if(ch == '{')  // pntxta or pntxtb
            {
                  if (!ReadCharFromFile(&ch))
                        return false;
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  else
                  {
                        /* 
                           dest indicate the destination 
                           0 = none (ignore)
                           1 = after
                           2 = before
                           Any other value has no legal meaning.
                        */
                        int dest = 0; 
                        if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pntxta") == 0) {
                              dest = 1;
                        }
                        else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pntxtb") == 0) {
                              dest = 2;
                        } 
                        else {
                              UT_DEBUGMSG(("Unknown keyword %s found in List stream  \n",keyword));
                        }
                        if (dest != 0) {
                              // OK scan through the text until a closing delimeter is
                              // found
                              int level = 0;
                              int count = 0;
                              if (!ReadCharFromFile(&ch))
                                    return false;
                              while ( level != 0 || (ch != '}' && ch != ';'))
                              {
                                    if (ch == '{') {
                                          level++;
                                    }
                                    else if (ch == '}') {
                                          UT_ASSERT_HARMLESS(level);
                                          level--;
                                    }
                                    else {
                                          keyword[count++] = ch;
                                    }
                                    if (!ReadCharFromFile(&ch))
                                          return false;
                              }
                              keyword[count++] = 0;
                              switch (dest) {
                              case 1:
                                    strcpy(rtfTable.textafter,reinterpret_cast<char*>(&keyword[0]));
                                    UT_DEBUGMSG(("FOUND pntxta in stream, copied %s to input  \n",keyword));
                                    break;
                              case 2:
                                    strcpy(rtfTable.textbefore,reinterpret_cast<char*>(&keyword[0]));
                                    UT_DEBUGMSG(("FOUND pntxtb in stream,copied %s to input  \n",keyword));
                                    break;
                              default:
                                    UT_ASSERT_NOT_REACHED();
                              }
                        }
                  }
                  goto nextChar;
            }
            if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
            {
                    return false;
            }
            else
            {
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]), "m_levelStartAt") == 0)
                  {
                        rtfTable.start_value = static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND m_levelStartAt in stream \n"));
                  }
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnstart") == 0)
                  {
                        rtfTable.start_value = static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND pnstart in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnlvl") == 0)
                  {
                        rtfTable.level = static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND pnlvl in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnlvlblt") == 0)
                  {
                        rtfTable.bullet = true;
                        UT_DEBUGMSG(("FOUND pnlvlblt in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnlvlbody") == 0)
                  {
                        rtfTable.simple = true;
                        UT_DEBUGMSG(("FOUND pnlvlbody in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnlvlcont") == 0)
                  {
                        rtfTable.continueList = true;
                        UT_DEBUGMSG(("FOUND pnlvlcont in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnnumonce") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnnumonce in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnacross") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnacross in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnhang") == 0)
                  {
                        rtfTable.hangingIndent = true;
                        UT_DEBUGMSG(("FOUND pnhang in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pncard") == 0)
                  {
                        rtfTable.type = NUMBERED_LIST;
                        UT_DEBUGMSG(("FOUND pncard in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pndec") == 0)
                  {
                        rtfTable.type = NUMBERED_LIST;
                        UT_DEBUGMSG(("FOUND pndec in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnucltr") == 0)
                  {
                        rtfTable.type = UPPERCASE_LIST;
                        UT_DEBUGMSG(("FOUND pnucltr in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnuclrm") == 0)
                  {
                        rtfTable.type = UPPERROMAN_LIST;
                        UT_DEBUGMSG(("FOUND pnucrm in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnlcltr") == 0)
                  {
                        rtfTable.type = LOWERCASE_LIST;
                        UT_DEBUGMSG(("FOUND pnlctr in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnlclrm") == 0)
                  {
                        rtfTable.type = LOWERROMAN_LIST;
                        UT_DEBUGMSG(("FOUND pnlcrm in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnord") == 0)
                  {
                        rtfTable.type = NUMBERED_LIST;
                        UT_DEBUGMSG(("FOUND pnord in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnordt") == 0)
                  {
                        rtfTable.type = NUMBERED_LIST;
                        UT_DEBUGMSG(("FOUND pnordt in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnb") == 0)
                  {
                        rtfTable.bold = true;
                        UT_DEBUGMSG(("FOUND pnb in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pni") == 0)
                  {
                        rtfTable.italic = true;
                        UT_DEBUGMSG(("FOUND pni in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pncaps") == 0)
                  {
                        rtfTable.caps = true;
                        UT_DEBUGMSG(("FOUND pncaps in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnscaps") == 0)
                  {
                        rtfTable.scaps = true;
                        UT_DEBUGMSG(("FOUND pnscaps in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnul") == 0)
                  {
                        rtfTable.underline = true;
                        UT_DEBUGMSG(("FOUND pnul in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnuld") == 0)
                  {
                        rtfTable.underline = true;
                        UT_DEBUGMSG(("FOUND pnuld in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnuldb") == 0)
                  {
                        rtfTable.underline = true;
                        UT_DEBUGMSG(("FOUND pnuldb in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnulnone") == 0)
                  {
                        rtfTable.nounderline = true;
                        UT_DEBUGMSG(("FOUND pnulnone in stream \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnulw") == 0)
                  {
                         UT_DEBUGMSG(("FOUND pnulw in stream - ignore for now \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnstrike") == 0)
                  {
                        rtfTable.strike = true;
                        UT_DEBUGMSG(("FOUND pnstrike in stream  \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pncf") == 0)
                  {
                        rtfTable.forecolor =  static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND pncf in stream  \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnf") == 0)
                  {
                        rtfTable.font =  static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND pnf in stream  \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnfs") == 0)
                  {
                        rtfTable.fontsize =  static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND pnfs in stream  \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnindent") == 0)
                  {
                        rtfTable.indent =  static_cast<UT_uint32>(parameter);
                        UT_DEBUGMSG(("FOUND pnindent in stream  \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnsp") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnsp in stream  - ignored for now \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnprev") == 0)
                  {
                        rtfTable.prevlist =  true;
                        UT_DEBUGMSG(("FOUND pnprev in stream  \n"));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnqc") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnqc in stream - ignored for now \n"));
                        // centered numbering
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnql") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnql in stream - ignored for now \n"));
                        // left justified numbering
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnqr") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnqr in stream - ignored for now \n"));
                        // right justified numbering
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "ls") == 0)
                  {
                        UT_DEBUGMSG(("FOUND ls in stream - override number \n",parameter));
                        rtfTable.iWord97Override =  static_cast<UT_uint32>(parameter);
                        // Word 97 list table identifier
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "ilvl") == 0)
                  {
                        UT_DEBUGMSG(("FOUND ilvl in stream - levelnumber \n",parameter));
                        rtfTable.iWord97Level = static_cast<UT_uint32>(parameter);
                        // Word 97 list level
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "pnrnot") == 0)
                  {
                        UT_DEBUGMSG(("FOUND pnrnot in stream - ignored for now \n"));
                        // Don't know this
                  }
                  else
                  {
                        UT_DEBUGMSG(("Unknown keyword %s found in List stream  \n",keyword));
                  }
         nextChar:      if (!ReadCharFromFile(&ch))
                           return false;
            }
      }
      // Put the '}' back into the input stream
      return SkipBackChar(ch);
}
///////////////////////////////////////////////////////////////////////////
// Handle copy and paste of tables by extending RTF. These handlers do that
///////////////////////////////////////////////////////////////////////////

bool IE_Imp_RTF::HandleAbiTable(void)
{
      UT_String sProps;
      unsigned char ch;
      if (!ReadCharFromFile(&ch))
            return false;
      while(ch == ' ')
      {
            if (!ReadCharFromFile(&ch))
                  return false;
      }
      while (ch != '}') // Outer loop
      {
            sProps += ch;
            if (!ReadCharFromFile(&ch))
                  return false;
      }
      ABI_Paste_Table * pPaste = new ABI_Paste_Table();
      m_pasteTableStack.push(pPaste);
      pPaste->m_bHasPastedTableStrux = false;
      pPaste->m_iRowNumberAtPaste = 0;
      
      UT_DEBUGMSG(("RTF_Import: Paste: Tables props are: %s \n",sProps.c_str()));
      bool bIsPasteIntoSame = false;
      PL_StruxDocHandle sdhTable = NULL;
      PL_StruxDocHandle sdhEndTable = NULL;
      bool bFound = getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_SectionTable,&sdhTable);
      PT_DocPosition posTable = 0;
      XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
      if(pFrame == NULL)
      {
            return false;
      }
      FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
      if(pView == NULL)
      {
            return false;
      }
      if(bFound)
      {
            posTable = getDoc()->getStruxPosition(sdhTable);
            sdhEndTable = getDoc()->getEndTableStruxFromTableSDH(sdhTable);
            if(sdhEndTable != NULL)
            {
                  PT_DocPosition posEndTable = getDoc()->getStruxPosition(sdhEndTable);
                  if(posEndTable > m_dposPaste)
                  {
                        UT_String sPasteTableSDH;
                        UT_String sProp = "table-sdh";
                        sPasteTableSDH = UT_String_getPropVal(sProps,sProp);
                        UT_String sThisTableSDH;
                        UT_String_sprintf(sThisTableSDH,"%x",sdhTable);
                        UT_DEBUGMSG(("sThisTableSDH %s sPasteTableSDH %s \n",sThisTableSDH.c_str(),sPasteTableSDH.c_str()));
                        bool isRow = (pView->getSelectionMode() == FV_SelectionMode_TableRow);
                        if(!isRow && pView->getSelectionMode() == FV_SelectionMode_NONE)
                        {
                              isRow = (pView->getPrevSelectionMode() == FV_SelectionMode_TableRow);
                        }
                        if((sThisTableSDH == sPasteTableSDH) && isRow)
                        {
                              UT_DEBUGMSG(("Paste Whole Row into same Table!!!!! \n"));
                              bIsPasteIntoSame = true;
                              pPaste->m_bPasteAfterRow = true;
                              PL_StruxDocHandle sdhCell = NULL;
                              bool b = getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_SectionCell,&sdhCell);
                              UT_return_val_if_fail(b,false);
                              const char * szTop = NULL;
                              getDoc()->getPropertyFromSDH(sdhCell,
                                                                         true,
                                                                         PD_MAX_REVISION,
                                                                         "top-attach",&szTop);
                              UT_return_val_if_fail(szTop,false);
                              UT_sint32 iOldTop = atoi(szTop); 
                              PT_DocPosition posCell = getDoc()->getStruxPosition(sdhCell);
                              b = true;
                              bool atEnd = false;
                              while(b)
                              {
                                    b = getDoc()->getNextStruxOfType(sdhCell,PTX_SectionCell,&sdhCell);
                                    if(b && sdhCell)
                                    {
                                          posCell = getDoc()->getStruxPosition(sdhCell);
                                    }
                                    if(!b || (posCell > posEndTable))
                                    {
                                          atEnd = true;
                                          b = false;
                                    }
                                    else if(b)
                                    {
                                          getDoc()->getPropertyFromSDH(sdhCell,
                                                                                     true,
                                                                                     PD_MAX_REVISION,
                                                                                     "top-attach",&szTop);
                                          UT_return_val_if_fail(szTop,false);
                                          UT_sint32 iNewTop = atoi(szTop); 
                                          b = (iNewTop == iOldTop);
                                    }
                              }
                              pPaste->m_iRowNumberAtPaste = iOldTop;
                              if(atEnd)
                              {
//
// At end of Table. Position just after last encCell strux
//
                                    m_dposPaste = posEndTable;
                              }
                              else
                              {
//
// Position just before the first cell of the next row.
//
                                    m_dposPaste = getDoc()->getStruxPosition(sdhCell);
                              }
//
// Now a change strux on the table to make it rebuild.
//
                              const char * sDumProp[3] = {NULL,NULL,NULL};
                              sDumProp[0] = "list-tag";
                              UT_String sVal;
                              UT_String_sprintf(sVal,"%d",getDoc()->getUID(UT_UniqueId::List));
                              sDumProp[1] = sVal.c_str();
                              sDumProp[2] = NULL;
                              getDoc()->changeStruxFmt(PTC_AddFmt,posTable+1,posTable+1,NULL,sDumProp,PTX_SectionTable);

                        }
                  }
            }
      }

//
// Remove the table-sdh property
//
      UT_String sProp = "table-sdh";      
      UT_String_removeProperty(sProps,sProp);
      const XML_Char * attrs[3] = {"props",NULL,NULL};
      if(! bIsPasteIntoSame)
      {
            attrs[1] = sProps.c_str();
//
// insert a block to terminate the text before this.
//
            FlushStoredChars(true); 
//
// Insert the table strux at the same spot. This will make the table link correctly in the
// middle of the broken text.
            pPaste->m_bHasPastedTableStrux = true;
            insertStrux(PTX_SectionTable,attrs,NULL);
      }     
      return true;
}

bool IE_Imp_RTF:: HandleAbiEndTable(void)
{
      ABI_Paste_Table * pPaste = NULL;
      m_pasteTableStack.viewTop((void**)(&pPaste));
      if(pPaste == NULL)
      {
            return false;
      }
      if(pPaste->m_bPasteAfterRow)
      {
            UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
            UT_sint32 numRows = pPaste->m_iCurTopCell -pPaste->m_iRowNumberAtPaste;
            PL_StruxDocHandle sdhCell = NULL;
            PL_StruxDocHandle sdhTable = NULL;
            PL_StruxDocHandle sdhEndTable = NULL;
            bool b = getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_SectionTable,&sdhTable);
            UT_return_val_if_fail(b,false);
            sdhEndTable = getDoc()->getEndTableStruxFromTableSDH(sdhTable);
            UT_return_val_if_fail(sdhEndTable,false);
            PT_DocPosition posEndTable = getDoc()->getStruxPosition(sdhEndTable);
            b = getDoc()->getStruxOfTypeFromPosition(m_dposPaste,PTX_SectionCell,&sdhCell);
            b = getDoc()->getNextStruxOfType(sdhCell,PTX_SectionCell,&sdhCell);
            UT_String sTop;
            UT_String sBot;
            const char * szVal = NULL;
            const XML_Char * sProps[5] = {NULL,NULL,NULL,NULL};
            PT_DocPosition posCell = getDoc()->getStruxPosition(sdhCell);
            while(b && (posCell < posEndTable))
            {
                  getDoc()->getPropertyFromSDH(sdhCell,
                                                             true,
                                                             PD_MAX_REVISION,
                                                             "top-attach", &szVal);
                  UT_return_val_if_fail(szVal,false);
                  UT_sint32 iTop = atoi(szVal);
                  iTop += numRows;
                  UT_String_sprintf(sTop,"%d",iTop);
                  getDoc()->getPropertyFromSDH(sdhCell,
                                                             true,
                                                             PD_MAX_REVISION,
                                                             "bot-attach", &szVal);
                  UT_return_val_if_fail(szVal,false);
                  UT_sint32 iBot = atoi(szVal);
                  iBot += numRows;
                  UT_String_sprintf(sTop,"%d",iBot);
                  sProps[0] = "top-attach";
                  sProps[1] = sTop.c_str();
                  sProps[2] = "bot-attach";
                  sProps[3] = sBot.c_str();
                  getDoc()->changeStruxFmt(PTC_AddFmt,posCell+1,posCell+1,NULL,sProps,PTX_SectionCell);
                  b = getDoc()->getNextStruxOfType(sdhCell,PTX_SectionCell,&sdhCell);
                  if(b)
                  {
                        posCell = getDoc()->getStruxPosition(sdhCell);
                  }
            }
            return true;
      }

      insertStrux(PTX_EndTable);
      m_pasteTableStack.pop((void**)(&pPaste));
      delete pPaste;
      return true;
}

/*!
 * If there is an open paste cell mark that the a block has been pasted.
 */
bool IE_Imp_RTF::markPasteBlock(void)
{
      if(!bUseInsertNotAppend())
      {
            return false;
      }
      ABI_Paste_Table * pPaste = NULL;
      m_pasteTableStack.viewTop((void**)(&pPaste));
      if(pPaste == NULL)
      {
            return false;
      }
      if(!pPaste->m_bHasPastedBlockStrux)
      {
            pPaste->m_bHasPastedBlockStrux = true;
            return true;
      }
      return false;
}

bool IE_Imp_RTF::isBlockNeededForPasteTable(void)
{
      ABI_Paste_Table * pPaste = NULL;
      if(m_pasteTableStack.getDepth() == 0)
      {
            return false;
      }
      m_pasteTableStack.viewTop((void**)(&pPaste));
      if(pPaste == NULL)
      {
            return false;
      }
      if(!pPaste->m_bHasPastedBlockStrux)
      {
            return true;
      }
      return false;
}

bool IE_Imp_RTF::HandleAbiEndCell(void)
{
      ABI_Paste_Table * pPaste = NULL;
      m_pasteTableStack.viewTop((void**)(&pPaste));
      if(pPaste == NULL)
      {
            return false;
      }
      if(!pPaste->m_bHasPastedBlockStrux)
      {
            UT_DEBUGMSG(("Insert Block  -4 \n"));
          insertStrux(PTX_Block);
      }
      UT_DEBUGMSG(("Insert EndCell -1!!!!!!!!!!!!!! \n"));
      insertStrux(PTX_EndCell);
      pPaste->m_bHasPastedCellStrux = false;
      pPaste->m_bHasPastedBlockStrux = false;
      return true;
}

bool IE_Imp_RTF::HandleAbiCell(void)
{
      UT_String sProps;
      unsigned char ch;
      if (!ReadCharFromFile(&ch))
            return false;
      while(ch == ' ')
      {
            if (!ReadCharFromFile(&ch))
                  return false;
      }
      while (ch != '}') // Outer loop
      {
            sProps += ch;
            if (!ReadCharFromFile(&ch))
                  return false;
      }
      ABI_Paste_Table * pPaste = NULL;
      m_pasteTableStack.viewTop((void**)(&pPaste));
      if(pPaste == NULL)
      {
            return false;
      }
      UT_String sDum = "top-attach";
      UT_String sTop = UT_String_getPropVal(sProps,sDum);
      pPaste->m_iCurTopCell = atoi(sTop.c_str());
      sDum = "right-attach";
      UT_String sRight = UT_String_getPropVal(sProps,sDum);
      pPaste->m_iCurRightCell = atoi(sRight.c_str());
      if(pPaste->m_iCurRightCell > pPaste->m_iMaxRightCell)
      {
            pPaste->m_iMaxRightCell = pPaste->m_iCurRightCell;
      }
      pPaste->m_bHasPastedCellStrux = true;
      pPaste->m_bHasPastedBlockStrux = false;
      UT_sint32 iMyTop = pPaste->m_iCurTopCell;
      sDum = "bot-attach";
      UT_String sBot =  UT_String_getPropVal(sProps,sDum);
      UT_sint32 iMyBot = atoi(sBot.c_str());
      if(pPaste->m_bPasteAfterRow)
      {
            UT_sint32 idiff = pPaste->m_iRowNumberAtPaste - iMyTop +1;
            iMyTop += idiff;
            sTop = UT_String_sprintf("%d",iMyTop);
            iMyBot += idiff;
            sBot = UT_String_sprintf("%d",iMyBot);
            UT_String sTopProp = "top-attach";
            UT_String sBotProp = "bot-attach";
            UT_String_setProperty(sProps,sTopProp,sTop);
            UT_String_setProperty(sProps,sBotProp,sBot);
            pPaste->m_iCurTopCell = iMyTop;
      }
      UT_DEBUGMSG(("RTF_Import: Pos %d Paste: Cell props are: %s \n",m_dposPaste,sProps.c_str()));
      const XML_Char * attrs[3] = {"props",NULL,NULL};
      attrs[1] = sProps.c_str();
      insertStrux(PTX_SectionCell,attrs,NULL);
      m_newParaFlagged = true;
      m_bSectionHasPara = true;
      return true;
}

/*!
 * Handle context senstive inserts. Like inserting a table into a block
 * Requires an extra block insert
 * Insert into a hyperlink means the m_dposPate is additionally incremented
 * to handle the extra end hyperlink run.
 */
bool IE_Imp_RTF::insertStrux(PTStruxType pts , const XML_Char ** attrs, const XML_Char ** props)
{
      bool bInHyperlink = false;
      bool bDoExtraBlock = false;
      bool res = false;
      XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
      if(pFrame == NULL)
      {
            m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
            return true;
      }
      FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
      PT_DocPosition posEOD = 0;
      pView->getEditableBounds(true,posEOD);
      if(pView == NULL)
      {
            m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
            return true;
      }
      if(!m_bStruxInserted)
      {
            if((pView->getHyperLinkRun(m_dposPaste) != NULL) ||(m_iHyperlinkOpen > 0) )
            {
                  bInHyperlink = true;
            }
            fl_BlockLayout * pBL = pView->getBlockAtPosition(m_dposPaste);
            if(pBL->getPosition() < m_dposPaste)
            {
                  bDoExtraBlock = true;
            }
      }
      bool isInHdrFtr = pView->isInHdrFtr(m_dposPaste);
      if(isInHdrFtr)
      {
            if((pts != PTX_Block) &&  (pts != PTX_SectionTable) && (pts != PTX_SectionCell) && (pts != PTX_EndTable) &&(pts != PTX_EndCell))
            {
                  m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                  return true;
            } 
            //
            // No nested tables in header/footers
            //
            if((m_pasteTableStack.getDepth() > 2) && 
               ((pts == PTX_SectionTable) || (pts == PTX_SectionCell) || (pts == PTX_EndTable) || (pts == PTX_EndCell)))
            {
                  return true;
            } 
      }
//
// Can't insert into a TOC 
//
      if(getDoc()->isTOCAtPos(m_dposPaste) && 
         getDoc()->isTOCAtPos(m_dposPaste-1) && 
            (pts != PTX_EndTOC))
      {
            m_dposPaste--;
      }
      if(bDoExtraBlock && (pts == PTX_SectionTable))
      {
            getDoc()->insertStrux(m_dposPaste,PTX_Block);
            if(bInHyperlink)
            {
                  m_dposPaste++;
                  bInHyperlink = false;
            }
      }
      if(pts == PTX_SectionFrame)
      {
            pf_Frag_Strux * pfs = NULL;
            if(pView->isInFrame(m_dposPaste))
            {
                  PT_DocPosition pos = m_dposPaste;
                  
                  while(( getDoc()->isFrameAtPos(pos) || pView->isInFrame(pos)) && pos <= posEOD)
                  {
                        pos++;
                  } 
                  if(pos > posEOD)
                  {
                        pos = posEOD;
                  }
                  m_dposPaste = pos;
            }
            res = getDoc()->insertStrux(m_dposPaste,pts,attrs,props,&pfs);
            m_dposPaste = pfs->getPos()+1;
            return res;
      }
      //
      // Can't  paste sections in Footnotes/Endnotes
      //
      if(pts == PTX_Section)
      { 
            if(pView->getEmbedDepth(m_dposPaste) > 0)
            {
                  return false;
            }
            fl_BlockLayout * pBL = pView->getBlockAtPosition(m_dposPaste);
            if(pBL == NULL)
            {
                  return false;
            }
            if(pBL->myContainingLayout() == NULL)
            {
                  return false;
            }
            if(pBL->myContainingLayout()->getContainerType() !=  FL_CONTAINER_DOCSECTION)
            {
                  return false;
            }
            if(pBL->getPosition() > m_dposPaste)
            {
                  return false;
            }
            if((pBL->getPosition(true) + pBL->getLength()) < m_dposPaste)
            {
                  return false;
            }
            if((pBL->getPrev() == NULL))
            {
                  return false;
            }
            if((pBL->getNext() == NULL))
            {
                  return false;
            }
            if(pBL->getNext()->getContainerType() != FL_CONTAINER_BLOCK)
            {
                  return false;
            }
            if(pBL->getPrev()->getContainerType() != FL_CONTAINER_BLOCK)
            {
                  return false;
            }
      }
      res = getDoc()->insertStrux(m_dposPaste,pts,attrs,props);
      m_dposPaste++;
      if(   bInHyperlink)
      {
            m_dposPaste++;
            m_iHyperlinkOpen =0;
      }
      m_bStruxInserted = true;
      return res;
}

            

//////////////////////////////////////////////////////////////////////////////
// AbiList table reader
//////////////////////////////////////////////////////////////////////////////

bool IE_Imp_RTF::HandleAbiLists()
{
      unsigned char keyword[MAX_KEYWORD_LEN];
      unsigned char ch;
      UT_sint32 parameter = 0;
      bool paramUsed = false;
      if (!ReadCharFromFile(&ch))
            return false;

      while (ch != '}') // Outer loop
      {
            if(ch == '{')  // abiliststyle, abilistdecimal, abilistdelim
            {
                  if (!ReadCharFromFile(&ch))
                        return false;
                  if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
                  {
                        return false;
                  }
                  else
                  {
                        if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abiliststyle") == 0)
                         {
                    // OK scan through the text until a closing delimeter is
                    // found
                               int count = 0;
                               if (!ReadCharFromFile(&ch))
                                     return false;
                               while ( ch != '}'  && ch != ';')
                               {
                                     keyword[count++] = ch;
                                     if (!ReadCharFromFile(&ch))
                                           return false;
                               }
                               keyword[count++] = 0;
                               strcpy(m_currentRTFState.m_paraProps.m_pszStyle,reinterpret_cast<char*>(&keyword[0]));
                         }
                         else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abilistdecimal") == 0)
                         {
                    // OK scan through the text until a closing delimeter is
                    // found
                               int count = 0;
                               if (!ReadCharFromFile(&ch))
                                     return false;
                               while ( ch != '}'  && ch != ';' )
                               {
                                     keyword[count++] = ch;
                                     if (!ReadCharFromFile(&ch))
                                           return false;
                               }
                               keyword[count++] = 0;
                               strcpy(m_currentRTFState.m_paraProps.m_pszListDecimal,reinterpret_cast<char*>(&keyword[0]));
                         }
                         else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abilistdelim") == 0)
                         {
                    // OK scan through the text until a closing delimeter is
                    // found
                               int count = 0;
                               if (!ReadCharFromFile(&ch))
                                     return false;
                               while ( ch != '}'  && ch != ';' )
                               {
                                     keyword[count++] = ch;
                                     if (!ReadCharFromFile(&ch))
                                           return false;
                               }
                               keyword[count++] = 0;
                               strcpy(m_currentRTFState.m_paraProps.m_pszListDelim,reinterpret_cast<char*>(&keyword[0]));
                         }
                         else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abifieldfont") == 0)
                         {
                    // OK scan through the text until a closing delimeter is
                    // found
                               int count = 0;
                               if (!ReadCharFromFile(&ch))
                                     return false;
                               while ( ch != '}'  && ch != ';' )
                               {
                                     keyword[count++] = ch;
                                     if (!ReadCharFromFile(&ch))
                                           return false;
                               }
                               keyword[count++] = 0;
                               strcpy(m_currentRTFState.m_paraProps.m_pszFieldFont,reinterpret_cast<char*>(&keyword[0]));
                         }
                         else
                         {
                               UT_DEBUGMSG(("Unknown keyword %s found in List stream  \n",keyword));
                         }
                  }
                  goto nextChar;
            }
            if(!ReadKeyword(keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN))
            {
                  return false;
            }
            else
            {
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abistartat") == 0)
                  {
                        m_currentRTFState.m_paraProps.m_startValue= static_cast<UT_uint32>(parameter);
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abilistid") == 0)
                  {
                        m_currentRTFState.m_paraProps.m_rawID = static_cast<UT_uint32>(parameter);
                        m_currentRTFState.m_paraProps.m_isList = true;

                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abilistparentid") == 0)
                  {
                        m_currentRTFState.m_paraProps.m_rawParentID = static_cast<UT_uint32>(parameter);
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "abilistlevel") == 0)
                  {
                        m_currentRTFState.m_paraProps.m_level = static_cast<UT_uint32>(parameter);
                  }
                  else
                  {
                        UT_DEBUGMSG(("Unknown keyword %s found in List stream  \n",keyword));
                  }
            }
      nextChar:   if (!ReadCharFromFile(&ch))
            return false;
      }
      //
      // Increment the list mapping table if necessary
      //
      UT_uint32 i;
      if(m_currentRTFState.m_paraProps.m_rawID != 0)
      {
            for(i=0; i < m_numLists; i++)
            {
                  if(m_currentRTFState.m_paraProps.m_rawID == getAbiList(i)->orig_id)
                        break;
            }
            if(i >= m_numLists)
            {
                  m_vecAbiListTable.addItem(new _rtfAbiListTable);
                  getAbiList(m_numLists)->orig_id = m_currentRTFState.m_paraProps.m_rawID ;
                  getAbiList(m_numLists)->orig_parentid = m_currentRTFState.m_paraProps.m_rawParentID ;
                  getAbiList(m_numLists)->level = m_currentRTFState.m_paraProps.m_level ;
                  getAbiList(m_numLists)->hasBeenMapped = false;
                  getAbiList(m_numLists)->start_value = 0;
                  getAbiList(m_numLists)->mapped_id = 0;
                  getAbiList(m_numLists)->mapped_parentid = 0;
                  m_numLists++;
            }
      }

      // Put the '}' back into the input stream

      //return SkipBackChar(ch);
      return true;
}



//////////////////////////////////////////////////////////////////////////////
// Character Properties keyword handlers
//////////////////////////////////////////////////////////////////////////////
bool IE_Imp_RTF::HandleRevisedText(PP_RevisionType eType, UT_uint32 iId)
{
      UT_return_val_if_fail( FlushStoredChars(), false);

      // remember the id for future reference
      m_currentRTFState.m_charProps.m_iCurrentRevisionId = iId;
      m_currentRTFState.m_charProps.m_eRevision = eType;
      
#if 0
      switch(eType)
      {
            case PP_REVISION_ADDITION:
            case PP_REVISION_ADDITION_AND_FMT:

            case PP_REVISION_DELETION:

            case PP_REVISION_FMT_CHANGE:
                  ;
      }
#endif
      return true;
}

bool IE_Imp_RTF::HandleRevisedTextTimestamp(UT_uint32 iDttm)
{
      // we basically rely on the dttm keyword to follow the auth keyword -- Word outputs
      // them in this sequence, and so do we
      UT_return_val_if_fail( m_currentRTFState.m_charProps.m_iCurrentRevisionId > 0,false);
      
      const UT_GenericVector<AD_Revision*> & Rtbl = getDoc()->getRevisions();
      UT_return_val_if_fail(Rtbl.getItemCount(),false);

      // valid revision id's start at 1, but vector is 0-based
      AD_Revision * pRev = Rtbl.getNthItem(m_currentRTFState.m_charProps.m_iCurrentRevisionId - 1);

      UT_return_val_if_fail( pRev, false );

      if(!pRev->getStartTime())
      {
            // set the start time to what ever is represented by dttm
            struct tm TM;
            TM.tm_sec = 0;
            TM.tm_min = (iDttm & 0x3f);
            TM.tm_hour = (iDttm & 0x7c0) >> 6;
            TM.tm_mday = (iDttm & 0xf800) >> 11;
            TM.tm_mon = ((iDttm & 0xf0000) >> 16)- 1;
            TM.tm_year = (iDttm & 0x1ff00000) >> 20;
            TM.tm_isdst = 0;

            time_t tT = mktime(&TM);
            pRev->setStartTime(tT);
      }
      
      return true;
}


bool IE_Imp_RTF::HandleBoolCharacterProp(bool state, bool* pProp)
{
      bool ok = FlushStoredChars();
      *pProp = state;
      return ok;
}

bool IE_Imp_RTF::HandleDeleted(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_deleted);
}

bool IE_Imp_RTF::HandleBold(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_bold);
}

bool IE_Imp_RTF::HandleItalic(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_italic);
}

bool IE_Imp_RTF::HandleHidden(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_Hidden);
}

bool IE_Imp_RTF::HandleUnderline(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_underline);
}

bool IE_Imp_RTF::HandleOverline(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_overline);
}


bool IE_Imp_RTF::HandleTopline(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_topline);
}


bool IE_Imp_RTF::HandleBotline(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_botline);
}

bool IE_Imp_RTF::HandleStrikeout(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_strikeout);
}

bool IE_Imp_RTF::HandleSuperscript(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_superscript);
}

// pos is in 1/2pt like in RTF
bool IE_Imp_RTF::HandleSuperscriptPosition(UT_uint32 pos)
{
      bool ok;
      ok = HandleBoolCharacterProp((pos != 0) ? true : false, &m_currentRTFState.m_charProps.m_superscript);
      if (ok)
      {
            ok = HandleFloatCharacterProp (pos*0.5, &m_currentRTFState.m_charProps.m_superscript_pos);
      }
      return ok;
}

bool IE_Imp_RTF::HandleSubscript(bool state)
{
      return HandleBoolCharacterProp(state, &m_currentRTFState.m_charProps.m_subscript);
}

// pos is in 1/2pt like in RTF
bool IE_Imp_RTF::HandleSubscriptPosition(UT_uint32 pos)
{
      bool ok;
      ok = HandleBoolCharacterProp((pos != 0) ? true : false, &m_currentRTFState.m_charProps.m_subscript);
      if (ok)
      {
            ok = HandleFloatCharacterProp (pos*0.5, &m_currentRTFState.m_charProps.m_subscript_pos);
      }
      return ok;
}

bool IE_Imp_RTF::HandleFontSize(long sizeInHalfPoints)
{
      return HandleFloatCharacterProp (sizeInHalfPoints*0.5, &m_currentRTFState.m_charProps.m_fontSize);
}


bool IE_Imp_RTF::HandleFloatCharacterProp(double val, double* pProp)
{
      bool ok = FlushStoredChars();
      *pProp = val;
      return ok;
}

bool IE_Imp_RTF::HandleU32CharacterProp(UT_uint32 val, UT_uint32* pProp)
{
      bool ok = FlushStoredChars();
      *pProp = val;
      return ok;
}

bool IE_Imp_RTF::HandleListTag(long id)
{
      UT_uint32 sid = static_cast<UT_uint32>(id);
      return HandleU32CharacterProp(sid, &m_currentRTFState.m_charProps.m_listTag);
}

bool IE_Imp_RTF::HandleFace(UT_uint32 fontNumber)
{
      RTFFontTableItem* pFont = GetNthTableFont(fontNumber);
      if (pFont != NULL && pFont->m_szEncoding)
            m_mbtowc.setInCharset(pFont->m_szEncoding);

      return HandleU32CharacterProp(fontNumber, &m_currentRTFState.m_charProps.m_fontNumber);
}

bool IE_Imp_RTF::HandleColour(UT_uint32 colourNumber)
{
      if (HandleBoolCharacterProp(true, &m_currentRTFState.m_charProps.m_hasColour))
      {
            return HandleU32CharacterProp(colourNumber, &m_currentRTFState.m_charProps.m_colourNumber);
      }
      return false;
}

bool IE_Imp_RTF::HandleBackgroundColour(UT_uint32 colourNumber)
{
      if (HandleBoolCharacterProp(true, &m_currentRTFState.m_charProps.m_hasBgColour))
      {
            return HandleU32CharacterProp(colourNumber, &m_currentRTFState.m_charProps.m_bgcolourNumber);
      }
      return false;
}

bool IE_Imp_RTF::SetParaJustification(RTFProps_ParaProps::ParaJustification just)
{
      m_currentRTFState.m_paraProps.m_justification = just;

      return true;
}

bool IE_Imp_RTF::AddTabstop(UT_sint32 stopDist, eTabType tabType, eTabLeader tabLeader)
{
      m_currentRTFState.m_paraProps.m_tabStops.addItem(stopDist); // convert from twip to inch
      if(tabType >=FL_TAB_LEFT && tabType <= FL_TAB_BAR  )
      {
            m_currentRTFState.m_paraProps.m_tabTypes.addItem(tabType);
      }
      else
      {
            m_currentRTFState.m_paraProps.m_tabTypes.addItem(FL_TAB_LEFT);
      }
      if(tabLeader >= FL_LEADER_NONE  && tabLeader <= FL_LEADER_EQUALSIGN)
      {
            m_currentRTFState.m_paraProps.m_tabLeader.addItem(tabLeader);
      }
      else
      {
            m_currentRTFState.m_paraProps.m_tabLeader.addItem(FL_LEADER_NONE);
      }

      return true;
}



bool IE_Imp_RTF::AddTabstop(UT_sint32 stopDist, eTabType tabType, eTabLeader tabLeader,  RTFProps_ParaProps * pParas)
{
      pParas->m_tabStops.addItem(stopDist);     // convert from twip to inch
      if(tabType >=FL_TAB_LEFT && tabType <= FL_TAB_BAR  )
      {
            pParas->m_tabTypes.addItem(tabType);
      }
      else
      {
            pParas->m_tabTypes.addItem(FL_TAB_LEFT);
      }
      if(tabLeader >= FL_LEADER_NONE  && tabLeader <= FL_LEADER_EQUALSIGN)
      {
            pParas->m_tabLeader.addItem(tabLeader);
      }
      else
      {
            pParas->m_tabLeader.addItem(FL_LEADER_NONE);
      }

      return true;
}

/*!
  Get the next token, put it into buf, if there is an error return RTF_TOKEN_ERROR
  otherwise returns the RTFTokenType
  If it is RTF_TOKEN_DATA, data is returned byte by byte.
  RTF_TOKEN_KEYWORD includes control words and control symbols
  (like hex char).
  It is up to the caller to distinguish beetween them and parse them.
  \retval pKeyword is the data
  \retval pParam is the keyword parameter if any, otherwise "". RTF spec says it should be
  a signed 16-bits int.
  \retval pParamUsed is a flag to tell whether there is a parameter.
  \return the type of the next token parsed.
  \note Both pParam amd pParamUsed are only used if tokenType is
  RTF_TOKEN_KEYWORD
  \note this changes the state of the file
*/
IE_Imp_RTF::RTFTokenType IE_Imp_RTF::NextToken (unsigned char *pKeyword, UT_sint32* pParam,
                                                                        bool* pParamUsed, UT_uint32 len, bool bIgnoreWhiteSpace /* = false */ )
{
      RTFTokenType tokenType = RTF_TOKEN_NONE;
      bool ok;

      UT_return_val_if_fail (pKeyword, RTF_TOKEN_NONE);
      UT_return_val_if_fail (len, RTF_TOKEN_NONE);
      UT_return_val_if_fail (pParamUsed, RTF_TOKEN_NONE);
      UT_return_val_if_fail (pParam, RTF_TOKEN_NONE);
      *pParam = 0;
      *pParamUsed = false;
      pKeyword [0] = ' ';

      if(bIgnoreWhiteSpace)
      {
            // see bug 1211 and bug 1207.rtf - invalid RTF coming in
            // this code instead violates the RTF spec in order to fix that problem,
            // but instead breaks other things like field values:
            // If a space delimits the control word, the space does not appear in the
            // document. Any characters following the delimiter, including spaces, will
            // appear in the document. For this reason, you should use spaces only
            // where necessary; do not use spaces merely to break up RTF code.

            // OK Sevior put in bool to choose this behaviour for some parts of documents
        // where we can work around this broken behaviour and still import the doc.

            while(pKeyword[0] == ' ')
            {
                  if (!ReadCharFromFile(pKeyword))
                  {
                        tokenType = RTF_TOKEN_ERROR;
                        return tokenType;
                  }
            }
      }
      else
      {
            if (!ReadCharFromFile(pKeyword))
            {
                  tokenType = RTF_TOKEN_ERROR;
                  return tokenType;
            }
      }

      switch (*pKeyword)
      {
      case '\\':
            tokenType = RTF_TOKEN_KEYWORD;
            ok = ReadKeyword (pKeyword, pParam, pParamUsed, len);
            if (!ok)
            {
                  tokenType = RTF_TOKEN_ERROR;
            }
            break;
      case '{':
            tokenType = RTF_TOKEN_OPEN_BRACE;
            break;
      case '}':
            tokenType = RTF_TOKEN_CLOSE_BRACE;
            break;
      default:
            tokenType = RTF_TOKEN_DATA;
            break;
      }

      return tokenType;
}


/*!
  Handle a bookmark keyword
 */
bool IE_Imp_RTF::HandleBookmark (RTFBookmarkType type)
{
      UT_UTF8String bookmarkName;

      xxx_UT_DEBUGMSG(("hub: HandleBookmark of type %d\n", type));

      /* read the bookmark name. It is PCDATA hence we are likely to find non ASCII data.*/
      HandlePCData(bookmarkName);

      const XML_Char * props [5];
      props [0] = "type";
      switch (type) {
      case RBT_START:
            props [1] = "start";
            break;
      case RBT_END:
            props [1] = "end";
            break;
      default:
            UT_ASSERT_NOT_REACHED();
            props [1] = NULL;
            break;
      }
      props [2] = "name";
      props [3] = bookmarkName.utf8_str();
      props [4] = NULL;
      UT_DEBUGMSG(("SEVIOR: Appending Object 3 m_bCellBlank %d m_bEndTableOpen %d \n",m_bCellBlank,m_bEndTableOpen));
      if(m_bCellBlank || m_bEndTableOpen || !m_bSectionHasPara)
      {
            UT_DEBUGMSG(("Insert/Append block 3 \n"));
            if (m_newSectionFlagged)
            {
                  ApplySectionAttributes();
                  m_newSectionFlagged = false;
            }
            if(!bUseInsertNotAppend()) 
            {
                  getDoc()->appendStrux(PTX_Block,NULL);
            }
            else
            {
                  markPasteBlock();
                  insertStrux(PTX_Block);
            }
            m_bCellBlank = false;
            m_bEndTableOpen = false;
            m_bSectionHasPara = true;
            m_newParaFlagged = false;
      }

      if (!bUseInsertNotAppend()) 
      {
            getDoc()->appendObject(PTO_Bookmark, props);
      }
      else 
      {
            if(isBlockNeededForPasteTable())
            {
                  markPasteBlock();
                  insertStrux(PTX_Block);
            }
            getDoc()->insertObject(m_dposPaste, PTO_Bookmark, props, NULL);
            m_dposPaste++;
      }
      return true;
}


void IE_Imp_RTF::_appendHdrFtr ()
{
      UT_uint32 i;
      UT_uint32 numHdrFtr;
      const RTFHdrFtr * header;
      UT_String tempBuffer;
      const XML_Char* szType = NULL;

      UT_return_if_fail(m_pImportFile);

      numHdrFtr = m_hdrFtrTable.getItemCount();

      for (i = 0; i < numHdrFtr; i++)
      {
            header = static_cast<const RTFHdrFtr *>(m_hdrFtrTable[i]);

            m_pPasteBuffer = reinterpret_cast<const unsigned char *>(header->m_buf.getPointer (0));
            m_lenPasteBuffer = header->m_buf.getLength ();
            m_pCurrentCharInPasteBuffer = m_pPasteBuffer;
            m_dposPaste = FV_DOCPOS_EOD;
            const XML_Char* propsArray[9];
            UT_String hdrftrID;
            switch (header->m_type)
            {
            case RTFHdrFtr::hftHeader:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "header";
                  break;
            case RTFHdrFtr::hftHeaderEven:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "header-even";
                  break;
            case RTFHdrFtr::hftHeaderFirst:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "header-first";
                  break;
            case RTFHdrFtr::hftHeaderLast:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "header-last";
                  break;
            case RTFHdrFtr::hftFooter:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "footer";
                  break;
            case RTFHdrFtr::hftFooterEven:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "footer-even";
                  break;
            case RTFHdrFtr::hftFooterFirst:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "footer-first";
                  break;
            case RTFHdrFtr::hftFooterLast:
                  UT_String_sprintf (tempBuffer, "%u", header->m_id);
                  szType = "footer-last";
                  break;
            default:
                  UT_ASSERT_NOT_REACHED();
            }
            UT_DEBUGMSG (("id is %s\n", tempBuffer.c_str()));
            hdrftrID = tempBuffer;
            propsArray[0] = "type";
            propsArray[1] = szType;
            propsArray[2] = "id";
            propsArray[3] = tempBuffer.c_str();
            propsArray[4] = "listid";
            propsArray[5] = "0";
            propsArray[6] = "parentid";
            propsArray[7] = "0";
            propsArray[8] = NULL;

            if(!getDoc()->verifySectionID(hdrftrID.c_str()))
            {
                  PL_StruxDocHandle sdh = getDoc()->getLastSectionSDH();
                  getDoc()->changeStruxAttsNoUpdate(sdh,szType,hdrftrID.c_str());
            }
            getDoc()->appendStrux (PTX_SectionHdrFtr, propsArray);
            propsArray[0] = NULL;
            // actually it appears that we have to append a block for some cases.
                  xxx_UT_DEBUGMSG(("Append block 4 \n"));
#if 0 //#TF
            getDoc()->appendStrux(PTX_Block, propsArray);
#endif
            // tell that we are parsing headers and footers
            m_parsingHdrFtr = true;
            m_newParaFlagged = true;
            m_bSectionHasPara = false;
            _parseFile (NULL);
            m_parsingHdrFtr = false;
      }
}

/*!
  Appends a field to the document.
  \param xmlField the field type value
  \return true if OK
 */
bool IE_Imp_RTF::_appendField (const XML_Char *xmlField, const XML_Char ** pszAttribs)
{
      bool ok;
      const XML_Char** propsArray = NULL;
      UT_String propBuffer;
      buildCharacterProps(propBuffer);

      const XML_Char * pStyle = NULL;
    const XML_Char * pStyleName = NULL;
      if(m_currentRTFState.m_charProps.m_styleNumber >= 0
         && (UT_uint32)m_currentRTFState.m_charProps.m_styleNumber < m_styleTable.size())
      {
            pStyle = PT_STYLE_ATTRIBUTE_NAME;
            pStyleName = m_styleTable[m_currentRTFState.m_charProps.m_styleNumber];
      }
      bool bNoteRef = false;
      if((UT_strcmp(xmlField,"endnote_ref") == 0) || (UT_strcmp(xmlField,"footnote_ref") == 0))
      {
            bNoteRef = true;
      }
      if(pszAttribs == NULL)
      {
            propsArray = static_cast<const XML_Char **>(UT_calloc(7, sizeof(XML_Char *)));            
            propsArray [0] = "type";
            propsArray [1] = xmlField;
            propsArray [2] = "props";
            propsArray [3] = propBuffer.c_str();
            propsArray [4] = pStyle;
            propsArray [5] = pStyleName;
            propsArray [6] = NULL;
      }
      else
      {
            UT_uint32 isize =0;
            while(pszAttribs[isize] != NULL)
            {
                  isize++;
            }
            propsArray = static_cast<const XML_Char **>(UT_calloc(7+isize, sizeof(XML_Char *)));

            UT_uint32 iEmptyAttrib = 4;
            propsArray [0] = "type";
            propsArray [1] = xmlField;
            propsArray [2] = "props";
            propsArray [3] = propBuffer.c_str();
            propsArray [4] = NULL;
            propsArray [5] = NULL;

            if(pStyle)
            {
                  propsArray[iEmptyAttrib++] = pStyle;
                  propsArray[iEmptyAttrib++] = pStyleName;
            }
            
            
            UT_uint32 i = 0;
            for(i=0; i< isize;i++)
            {
                  propsArray[iEmptyAttrib+i] = pszAttribs[i];
            }
            propsArray[iEmptyAttrib+isize] = NULL;
      }
      // TODO get text props to apply them to the field
      ok = FlushStoredChars (true);
      UT_return_val_if_fail (ok, false);
      if (!bUseInsertNotAppend() || m_bAppendAnyway)
      {
            xxx_UT_DEBUGMSG(("SEVIOR: Appending Object m_bCellBlank %d m_bEndTableOpen %d \n",m_bCellBlank,m_bEndTableOpen));
            if(m_bCellBlank || m_bEndTableOpen)
            {
                  xxx_UT_DEBUGMSG(("Append block 5 \n"));

                  getDoc()->appendStrux(PTX_Block,NULL);
                  m_bCellBlank = false;
                  m_bEndTableOpen = false;
            }
            getDoc()->appendObject(PTO_Field, propsArray);
      }
      else
      {
            XAP_Frame * pFrame = XAP_App::getApp()->getLastFocussedFrame();
            if(pFrame == NULL)
             {
                   m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                   return true;
             }
            FV_View * pView = static_cast<FV_View*>(pFrame->getCurrentView());
            if(pView == NULL)
            {
                  m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                  return true;
            }
            //
            // No pasting footnotes/endnotes into text boxes, paste just before
            // it.
            //
            if(bNoteRef && pView->isInFrame(m_dposPaste))
            {
                  fl_FrameLayout * pFL = pView->getFrameLayout(m_dposPaste);
                  if(pFL == NULL)
                  {
                        m_currentRTFState.m_destinationState = RTFStateStore::rdsSkip;
                        return true;
                  }
                  PT_DocPosition newPos = pFL->getPosition(true);
                  while(newPos > 2 && getDoc()->isEndFrameAtPos(newPos-1))
                  {
                        pFL = pView->getFrameLayout(newPos-2);
                        if(pFL)
                        {
                              newPos = pFL->getPosition(true);
                        }
                  }
                  m_dposPaste = newPos;
            }
            getDoc()->insertObject(m_dposPaste, PTO_Field, propsArray, NULL);
            m_dposPaste++;
      }
      free(propsArray);
      m_bFieldRecognized = true;
      return ok;
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

bool IE_Imp_RTF::pasteFromBuffer(PD_DocumentRange * pDocRange,
                                                 const unsigned char * pData, UT_uint32 lenData, const char * /* szEncoding */)
{
      UT_return_val_if_fail(getDoc() == pDocRange->m_pDoc,false);
      UT_return_val_if_fail(pDocRange->m_pos1 == pDocRange->m_pos2,false);

      m_pPasteBuffer = pData;
      m_lenPasteBuffer = lenData;
      m_pCurrentCharInPasteBuffer = pData;
      m_dposPaste = pDocRange->m_pos1;
      m_dOrigPos = m_dposPaste;
      // some values to start with -- most often we are somewhere in the middle of doc,
      // i.e., in section and in block
      m_newParaFlagged = false;
      m_bSectionHasPara = true;
      m_newSectionFlagged = false;
      
      // we need to work out if we are in section and block
      pf_Frag * pf = getDoc()->getFragFromPosition(m_dposPaste);

      if(!pf)
      {
            // the doc is entirely empty
            m_newParaFlagged = true;
            m_bSectionHasPara = false;
            m_newSectionFlagged = true;
      }
      else
      {
            // pf is a frag that starts at m_dposPaste -- we want the frag that ends there
            pf = pf->getPrev();
            
            // now find the nearest strux to the left
            while(pf && pf->getType() != pf_Frag::PFT_Strux)
                  pf = pf->getPrev();

            if(!pf)
            {
                  // this is a malformed doc -- it has content but no stuxes !!!
                  UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
                  m_newParaFlagged = true;
                  m_bSectionHasPara = false;
                  m_newSectionFlagged = true;
            }
            else
            {
                  // what kind of strux have we hit ?
                  pf_Frag_Strux * pfs = (pf_Frag_Strux*) pf;
                  switch(pfs->getStruxType())
                  {
                        case PTX_Block:
                        case PTX_EndFootnote:
                        case PTX_EndEndnote:
                              // we are ok
                              break;

                        default:
                              UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
                              // fall through
                              
                        case PTX_Section:
                        case PTX_SectionHdrFtr:
                        case PTX_SectionEndnote:
                        case PTX_SectionTable:
                        case PTX_SectionCell:
                        case PTX_SectionFootnote:
                        case PTX_SectionMarginnote:
                        case PTX_SectionFrame:
                        case PTX_EndCell:
                        case PTX_EndTable:
                        case PTX_EndMarginnote:
                        case PTX_EndFrame:
                        case PTX_SectionTOC:
                        case PTX_EndTOC:
                        case PTX_StruxDummy:
                              // flag block
                              m_newParaFlagged = true;
                              m_bSectionHasPara = false;
                  }
            }
            
            
      }
      
      

      UT_DEBUGMSG(("Pasting %d bytes of RTF\n",lenData));
#if 0 //def DEBUG
      {
            const char * p = (const char*)pData;
            for(UT_uint32 i = 0; i < lenData; i += 50)
            {
                  if(lenData - i < 50)
                  {
                        UT_String s(p);
                        UT_DEBUGMSG(("%s\n", s.c_str()));
                  }
                  else
                  {
                        UT_String s(p, 50);
                        UT_DEBUGMSG(("%s\n", s.c_str()));
                        p += 50;
                  }
            }
      }
#endif

      // to do a paste, we set the fp to null and let the
      // read-a-char routines know about our paste buffer.

      UT_return_val_if_fail(m_pImportFile==NULL,false);

      // note, we skip the _writeHeader() call since we don't
      // want to assume that selection starts with a section
      // break.
      _parseFile(NULL);

      if(m_newParaFlagged)
      {
//
// Finish off any remaining stuff
//
            FlushStoredChars(false);
      }
      
      m_pPasteBuffer = NULL;
      m_lenPasteBuffer = 0;
      m_pCurrentCharInPasteBuffer = NULL;

      return true;
}


/*!
Define a new style, here is the formal syntax:
<style>     '{' <styledef>?<keycode>? <formatting> <additive>? <based>? <next>?
            <stylename>? ';' '}'
<styledef>  \s  |\*\cs  | \ds
<keycode>   '{' \keycode <keys> '}'
<additive>  \additive
<based>     \sbasedon
<next>      \snext
<autoupd>   \sautoupd
<hidden>    \shidden
<formatting>      (<brdrdef> | <parfmt> | <apoctl> | <tabdef> | <shading> | <chrfmt>)+
<stylename> #PCDATA
<keys>      ( \shift? & \ctrl? & \alt?) <key>
<key> \fn | #PCDATA

The style definition is located within a {\stylesheet } sequence.

The minimum (useless) example would be {}
A more typical set of styles would be
{\stylesheet
    {\fs20 \sbasedon222\snext0{\*\keycode \shift\ctrl n} Normal;}
      {\s1\qr \fs20 \sbasedon0\snext1 FLUSHRIGHT;}
      {\s2\fi-720\li720\fs20\ri2880\sbasedon0\snext2 IND;}
}

*/

#define RTF_BASEDON_NONE            222         // the default

bool IE_Imp_RTF::HandleStyleDefinition(void)
{
      bool status = true;
      int nesting = 1;
      unsigned char ch;
      char * styleTypeP = "P";
      char * styleTypeC = "C";
      char * styleType = styleTypeP;
      
      UT_sint32 BasedOn[2000]; // 2000 styles. I know this should be a Vector.
      UT_sint32 FollowedBy[2000]; // 2000 styles. I know this should be a Vector.
      UT_sint32 styleCount = 0;
      UT_GenericVector<UT_GenericVector<const XML_Char*>*> vecStyles;
      RTFProps_ParaProps * pParas =  new RTFProps_ParaProps();
      RTFProps_CharProps *  pChars = new  RTFProps_CharProps();
      RTFProps_bParaProps * pbParas =  new RTFProps_bParaProps();
      RTFProps_bCharProps *  pbChars = new      RTFProps_bCharProps();

      static char  propBuffer[1024];
      propBuffer[0] = 0;

      const XML_Char* attribs[PT_MAX_ATTRIBUTES*2 + 1];
      UT_uint32 attribsCount=0;
      UT_UCS4String styleName;// = "";
      UT_sint32 styleNumber = 0;
      while (nesting>0 && status == true)
      {
        unsigned char keyword[MAX_KEYWORD_LEN];
        UT_sint32 parameter = 0;
          bool parameterUsed = false;

            if (!ReadCharFromFile(&ch))
                return false;

            switch(ch)
            {
            case '\\':
            status = ReadKeyword(keyword, &parameter, &parameterUsed, MAX_KEYWORD_LEN);
                  if (!status)
                  {
                        return status;
                  }
                  else if (strcmp(reinterpret_cast<const char *>(&keyword[0]), "'") == 0) {
                        /* FIXME really hackish. What if we have this in middle of keywords */
                        UT_UCS4Char wc;
                        wc = ReadHexChar();
                        styleName += wc;
                  }
                  else if (strcmp(reinterpret_cast<const char *>(&keyword[0]), "sbasedon") == 0)
                  {
                        if ((parameter != styleNumber) &&
                              (parameter != RTF_BASEDON_NONE))
                        {
//
// Have to deal with out of sequence styles. ie A style maybe basedon a style that
// has not yet been seen.
//
// So remember it and fill it later..
//
                              BasedOn[styleCount] = static_cast<UT_sint32>(parameter);
                              attribs[attribsCount++] = PT_BASEDON_ATTRIBUTE_NAME;
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                              attribs[attribsCount++] = NULL;
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                              attribs[attribsCount]   = NULL;
                        }
                        else if(0)
                        {
                              // TODO: Why is this code here? It left over from before the BasedOn array
                              char * val = m_styleTable.getNthItem(parameter);
                              if (val != NULL)
                              {
                                    attribs[attribsCount++] = PT_BASEDON_ATTRIBUTE_NAME;
                                    UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                                    attribs[attribsCount++] = val;
                                    UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                                    attribs[attribsCount]   = NULL;
                              }
                        }
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "snext") == 0)
                  {
                        if (parameter != styleNumber)
                        {
//
// Have to deal with out of sequence styles. ie A style may have a followed-by style
// that has not yet been seen.
//
// So remember it and fill it later..
//
                              FollowedBy[styleCount] = static_cast<UT_sint32>(parameter);
                              attribs[attribsCount++] = PT_FOLLOWEDBY_ATTRIBUTE_NAME;
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                              attribs[attribsCount++] = NULL;
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                              attribs[attribsCount]   = NULL;
                        }
                        else if(parameter < styleNumber)
                        {
                              // TODO: Why is this code here? It left over from before the FollowedBy array
                              char * val = m_styleTable.getNthItem(parameter);
                              if (val != NULL)
                              {
                              attribs[attribsCount++] = PT_FOLLOWEDBY_ATTRIBUTE_NAME;
                                    UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                                    attribs[attribsCount++] = val;
                                    UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                                    attribs[attribsCount]   = NULL;
                              }
                        }
                  }
                  else if ((strcmp(reinterpret_cast<char*>(&keyword[0]),  "s") == 0) ||
                             (strcmp(reinterpret_cast<char*>(&keyword[0]), "ds") == 0) ||
                               (strcmp(reinterpret_cast<char*>(&keyword[0]), "ts") == 0))
                  {
                        styleNumber = parameter;
                        styleType = styleTypeP;
                        xxx_UT_DEBUGMSG(("Stylesheet RTF Found style number %d Paragraph type \n",styleNumber));
                  }
                  if (strcmp(reinterpret_cast<char*>(&keyword[0]), "cs") == 0)
                  {
                        styleNumber = parameter;
                        styleType = styleTypeC;
                        xxx_UT_DEBUGMSG(("Stylesheet: RTF Found style number %d Character type \n",styleNumber));
                  }
                  else if (strcmp(reinterpret_cast<char*>(&keyword[0]), "*") == 0)
                  {
//
// Get next keyword
//
                        xxx_UT_DEBUGMSG(("Found * in StyleSheet reading \n"));
                  }
                  else
                  {
                      status = ParseCharParaProps(static_cast<unsigned char *>(keyword), parameter, parameterUsed,pChars,pParas,pbChars,pbParas);
                  }
                  break;
            case '{':
                  nesting++;
                  break;
            case '}':
                  nesting--;
                  break;
            default:
                  // The only thing that should be left is the style name

                  // clear the m_mbtowc buffer
                  m_mbtowc.initialize(true);
                  
                  while (ch != '}' && ch != ';')
                  {
                        /* 
                           we have seen cases, including AbiWord, were stylename
                           were generated with non ASCII names encoded as 8bits...
                           We assume it is the document charset.
                        */
                        UT_UCS4Char wc;
                        if(m_mbtowc.mbtowc(wc,static_cast<UT_Byte>(ch)))
                              styleName += wc;
                        else
                              styleName += ch;
                        
                if (!ReadCharFromFile(&ch)) {
                        return false;
                        }
                        if (ch =='}')
                        {
                              UT_DEBUGMSG(("RTF: Badly formatted style name, no ';'"));
                              nesting--;
                        }
                  }
                  char * buffer  = UT_strdup(styleName.utf8_str());
                  char * oldbuffer;
                  m_styleTable.setNthItem(styleNumber,buffer,&oldbuffer);
                  FREEP(oldbuffer);
                  break;
            }

            // if the stylesheet is malformed there might be nothing in the table ...
            if (nesting == 1 && m_styleTable.getItemCount() > styleNumber )
            {
                  // Reached the end of a single style definition.
                  // Use it.
                  buildAllProps(static_cast<char *>(&propBuffer[0]) ,pParas,pChars,pbParas,pbChars);
                  attribs[attribsCount++] = PT_PROPS_ATTRIBUTE_NAME;
                  UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                  attribs[attribsCount++] = static_cast<const char *>(&propBuffer[0]);
                  UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );

                  attribs[attribsCount++] = PT_NAME_ATTRIBUTE_NAME;
                  UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                  attribs[attribsCount++] = m_styleTable[styleNumber];
                  UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );

                  attribs[attribsCount++] = PT_TYPE_ATTRIBUTE_NAME;
                  UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                  attribs[attribsCount++] = styleType;
                  UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
//                attribs[attribsCount] = NULL;
//
// OK now we clone this and save it so we can set basedon's and followedby's
//
                  UT_sint32 i =0;
                  UT_GenericVector<const XML_Char*>* pVecAttr = new UT_GenericVector<const XML_Char*>();
                  for( i= 0; i< static_cast<UT_sint32>(attribsCount); i++)
                  {
                        if(attribs[i] != NULL)
                        {
                              pVecAttr->addItem(UT_strdup(attribs[i]));
                        }
                        else
                        {
                              pVecAttr->addItem(NULL);
                        }
                  }
                  vecStyles.addItem(pVecAttr);

                  // Reset
                  styleCount++;
                  attribsCount = 0;
                  attribs[attribsCount] = NULL;
                  styleNumber = 0;
                  styleName = "";
                  styleType = "P";
                  DELETEP(pParas);
                  DELETEP(pChars);
                  DELETEP(pbParas);
                  DELETEP(pbChars);
                  pParas =  new RTFProps_ParaProps();
                  pChars = new      RTFProps_CharProps();
                  pbParas =  new RTFProps_bParaProps();
                  pbChars = new     RTFProps_bCharProps();
                  propBuffer[0] = 0;
            }
      }
//
// Finished Style definitions
//
      DELETEP(pParas);
      DELETEP(pChars);
      DELETEP(pbParas);
      DELETEP(pbChars);
//
// Now we loop through them all and write them into our document.
//
      UT_sint32 count = static_cast<UT_sint32>(vecStyles.getItemCount());
      UT_sint32 i = 0;
      for(i=0; i< count; i++)
      {
            // Reset

            attribsCount = 0;
            attribs[attribsCount] = NULL;
            UT_GenericVector<const XML_Char*> * pCurStyleVec = vecStyles.getNthItem(i);
            UT_sint32 nAtts = static_cast<UT_sint32>(pCurStyleVec->getItemCount());
            UT_sint32 j = 0;
            const char * szName = NULL;

            while(j < nAtts)
            {
                  const char * szAtt = pCurStyleVec->getNthItem(j++);
                  attribs[attribsCount++] = szAtt;
                  if( UT_strcmp(szAtt, PT_NAME_ATTRIBUTE_NAME)== 0)
                  {
                        szName = pCurStyleVec->getNthItem(j++);
                        attribs[attribsCount++] = szName;
                  }
                  else if( UT_strcmp(szAtt, PT_BASEDON_ATTRIBUTE_NAME)== 0)
                  {
                        const char * szNext = pCurStyleVec->getNthItem(j++);
                        if(NULL == szNext)
                        {
                              UT_sint32 istyle = BasedOn[i];
                              // must not mix static and dynamically allocated strings in the same
                              // array, otherwise there is no way we can free it !!!
                              //attribs[attribsCount++] = UT_strdup(static_cast<const char *>(m_styleTable[istyle]));
                              attribs[attribsCount++] = m_styleTable[istyle];
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                        }
                        else
                        {
                              attribs[attribsCount++] = szNext;
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                        }
                  }
                  else if( UT_strcmp(szAtt, PT_FOLLOWEDBY_ATTRIBUTE_NAME)== 0)
                  {
                        const char * szNext = pCurStyleVec->getNthItem(j++);
                        if(NULL == szNext)
                        {
                              UT_sint32 istyle = FollowedBy[i];
                              // must not mix static and dynamically allocated strings in the same
                              // array, otherwise there is no way we can free it !!!
                              // attribs[attribsCount++] = UT_strdup(static_cast<const char *>(m_styleTable[istyle]));
                              attribs[attribsCount++] = m_styleTable[istyle];
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                        }
                        else
                        {
                              attribs[attribsCount++] = szNext;
                              UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                        }
                  }
                  else
                  {
                        szAtt = pCurStyleVec->getNthItem(j++);
                        attribs[attribsCount++] = szAtt;
                        UT_return_val_if_fail( attribsCount < PT_MAX_ATTRIBUTES * 2,false );
                  }
                  attribs[attribsCount] = NULL;
            }
//
// If style exists we have to redefine it like this
//
            // need to test that we have a name, as there are some malformed docs around ...
            if(szName && *szName)
            {
                  xxx_UT_DEBUGMSG(("Looking at style %s \n",szName));
                  PD_Style * pStyle = NULL;
                  if(getDoc()->getStyle(szName, &pStyle))
                  {
                        if (!isPasting())
                        {
                              pStyle->addAttributes(attribs);
                              pStyle->getBasedOn();
                              pStyle->getFollowedBy();
                        } 
                        else
                        {
                              UT_DEBUGMSG(("DOM: refusing to append props to an already existing style while pasting\n"));
                        }
                  }
                  else
                  {
                        getDoc()->appendStyle(attribs);
                  }
            }
            
//
// OK Now delete all this allocated memory...
//
            for(j=0; j< nAtts; j++)
            {
                  const XML_Char * sz = pCurStyleVec->getNthItem(j);
                  if(sz != NULL)
                  {
                        // MUST NOT USED delete[] on strings allocated by malloc/calloc !!!
                        // delete [] sz;
                        free(const_cast<XML_Char*>(sz));
                        sz = NULL;
                  }
            }
            delete pCurStyleVec;

      }
      status = PopRTFState();
      m_bStyleImportDone = true;
      return status;

}

/*!
 * This method builds the property list from Paragraph and character classes pParas
 * and pChars
 */
bool IE_Imp_RTF::buildAllProps(char * propBuffer,  RTFProps_ParaProps * pParas,
                                             RTFProps_CharProps * pChars,
                                             RTFProps_bParaProps * pbParas,
                                             RTFProps_bCharProps * pbChars)
{
//
// Tab stops.
//
      UT_String tempBuffer;
      UT_sint32 count =pParas->m_tabStops.getItemCount();
      if(count > 0)
            strcat(propBuffer, "tabstops:");
      UT_sint32 i=0;
      for (i = 0; i < count; i++)
      {
            if (i > 0)
                  strcat(propBuffer, ",");

            UT_sint32 tabTwips = pParas->m_tabStops.getNthItem(i);
            double tabIn = tabTwips/(20.0*72.);
            UT_uint32 idum = pParas->m_tabTypes.getNthItem(i);
            eTabType tabType = static_cast<eTabType>(idum);
            idum = pParas->m_tabLeader.getNthItem(i);
            eTabLeader tabLeader = static_cast<eTabLeader>(idum);
            char  cType = ' ';
            switch(tabType)
            {
            case FL_TAB_LEFT:
                  cType ='L';
                  break;
            case FL_TAB_RIGHT:
                  cType ='R';
                  break;
            case FL_TAB_CENTER:
                  cType ='C';
                  break;
            case FL_TAB_DECIMAL:
                  cType ='D';
                  break;
            case FL_TAB_BAR:
                  cType ='B';
                  break;
            default:
                  UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
            }
            char cLeader = '0' + static_cast<char>(tabLeader);
            UT_String_sprintf(tempBuffer, "%s/%c%c", UT_convertInchesToDimensionString(DIM_IN,tabIn,"04"),cType,cLeader);
            strcat(propBuffer, tempBuffer.c_str());
      }
      if( count > 0)
            strcat(propBuffer, "; ");
//
// Top and bottom paragraph margins
//
      if(pbParas->bm_spaceBefore)
      {
            UT_String_sprintf(tempBuffer, "margin-top:%s; ",            UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(pParas->m_spaceBefore)/1440));
            strcat(propBuffer, tempBuffer.c_str());
      }
      if(pbParas->bm_spaceAfter)
      {
            UT_String_sprintf(tempBuffer, "margin-bottom:%s; ",   UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(pParas->m_spaceAfter)/1440));
            strcat(propBuffer, tempBuffer.c_str());
      }
//
// Left and right margins
//
      if(pbParas->bm_indentLeft)
      {
            UT_String_sprintf(tempBuffer, "margin-left:%s; ",           UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(pParas->m_indentLeft)/1440));
            strcat(propBuffer, tempBuffer.c_str());
      }
      if(pbParas->bm_indentRight)
      {
            UT_String_sprintf(tempBuffer, "margin-right:%s; ",    UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(pParas->m_indentRight)/1440));
            strcat(propBuffer, tempBuffer.c_str());
      }
//
// First line indent
//

      if(pbParas->bm_indentFirst)
      {
            UT_String_sprintf(tempBuffer, "text-indent:%s; ",     UT_convertInchesToDimensionString(DIM_IN, static_cast<double>(pParas->m_indentFirst)/1440));
            strcat(propBuffer, tempBuffer.c_str());
      }

//
// line spacing
//
      if(pbParas->bm_lineSpaceVal)
      {
            if (pParas->m_lineSpaceExact)
            {
                  if (pParas->m_lineSpaceVal < 0) {  // exact spacing
                        UT_String_sprintf(tempBuffer, "line-height:%spt; ",    UT_convertToDimensionlessString(fabs(pParas->m_lineSpaceVal/20.0)));
                  }
                  else                                                         // "at least" spacing
                  {
                        UT_String_sprintf(tempBuffer, "line-height:%spt+; ",    UT_convertToDimensionlessString(fabs(pParas->m_lineSpaceVal/20.0)));
                  }
            }
            else   // multiple spacing
            {
                  UT_String_sprintf(tempBuffer, "line-height:%s; ",     UT_convertToDimensionlessString(fabs(pParas->m_lineSpaceVal/240)));
            }
            strcat(propBuffer, tempBuffer.c_str());
      }

//
// justification
//
      if (pbParas->bm_justification)
      {
            strcat(propBuffer, "text-align:");
            switch(pParas->m_justification)
            {
                  case RTFProps_ParaProps::pjCentre:
                        strcat(propBuffer, "center; ");
                        break;
                case RTFProps_ParaProps::pjRight:
                      strcat(propBuffer, "right; ");
                      break;
                case RTFProps_ParaProps::pjFull:
                      strcat(propBuffer, "justify; ");
                  break;
                default:
                      UT_ASSERT_NOT_REACHED();  // so what is it?
                case RTFProps_ParaProps::pjLeft:
                      strcat(propBuffer,"left; ");
                      break;
            }
            strcat(propBuffer, tempBuffer.c_str());
      }

//
// Character Properties.
//
      // bold
      if(pbChars->bm_bold)
      {
            strcat(propBuffer, "font-weight:");
            strcat(propBuffer, pChars->m_bold ? "bold" : "normal");
            strcat(propBuffer,";");
      }
      // italic
      if(pbChars->bm_italic)
      {
            strcat(propBuffer, " font-style:");
            strcat(propBuffer, pChars->m_italic ? "italic" : "normal");
            strcat(propBuffer,";");
      }
      // underline & overline & strike-out
      if(pbChars->bm_underline || pbChars->bm_strikeout || pbChars->bm_overline
         || pbChars->bm_topline || pbChars->bm_botline )
      {
            strcat(propBuffer, "; text-decoration:");
            static UT_String decors;
            decors.clear();
            if (pChars->m_underline)
            {
                  decors += "underline ";
            }
            if (pChars->m_strikeout)
            {
                  decors += "line-through ";
            }
            if (pChars->m_overline)
            {
                  decors += "line-through ";
            }
            if (pChars->m_topline)
            {
                  decors += "line-through ";
            }
            if (pChars->m_botline)
            {
                  decors += "line-through ";
            }
            if(!pChars->m_underline  &&
               !pChars->m_strikeout &&
               !pChars->m_overline &&
               !pChars->m_topline &&
               !pChars->m_botline)
            {
                  decors = "none";
            }
            strcat(propBuffer, decors.c_str());
            strcat(propBuffer,";");
      }
      //superscript and subscript
      if(pbChars->bm_superscript || pbChars->bm_subscript)
      {
            strcat(propBuffer, " text-position:");
            if (pChars->m_superscript)
            {
                  if (pbChars->bm_superscript_pos)
                  {
                        UT_DEBUGMSG (("RTF: TODO: Handle text position in pt.\n"));
                  }
                  strcat(propBuffer, "superscript;");
            }
            else if (pChars->m_subscript)
            {
                  if (pbChars->bm_subscript_pos)
                  {
                        UT_DEBUGMSG (("RTF: TODO: Handle text position in pt.\n"));
                  }
                  strcat(propBuffer, "subscript;");
            }
            else
            {
                  strcat(propBuffer, "normal;");
            }
      }

      // font size
      if(pbChars->bm_fontSize)
      {
            UT_String_sprintf(tempBuffer, " font-size:%spt;", std_size_string(static_cast<float>(pChars->m_fontSize)));
            strcat(propBuffer, tempBuffer.c_str());
      }
      // typeface
      if(pbChars->bm_fontNumber)
      {
            RTFFontTableItem* pFont = GetNthTableFont(pChars->m_fontNumber);
            if (pFont != NULL)
            {
                  strcat(propBuffer, " font-family:");
                  strcat(propBuffer, pFont->m_pFontName);
                  strcat(propBuffer, ";");
            }
      }
      // Foreground Colour
      if(pbChars->bm_hasColour)
      {
            if (pChars->m_hasColour)
            {
                  // colour, only if one has been set. See bug 1324
                  UT_uint32 colour = GetNthTableColour(pChars->m_colourNumber);
                  UT_String_sprintf(tempBuffer, " color:%06x;", colour);
                  strcat(propBuffer, tempBuffer.c_str());
            }
      }
      // BackGround Colour
      if (pbChars->bm_hasBgColour)
      {
            if(pbChars->bm_hasBgColour)
            {
                  // colour, only if one has been set. See bug 1324
                  UT_sint32 bgColour = GetNthTableBgColour(pChars->m_bgcolourNumber);
                  if (bgColour != -1) // invalid and should be white
                  {
                        UT_String_sprintf(tempBuffer, " bgcolor:%06x;", bgColour);
                        strcat(propBuffer, tempBuffer.c_str());
                  }
            }
      }
// Language
      if (pChars->m_szLang)
      {
            strcat(propBuffer, " lang:");
            strcat(propBuffer, pChars->m_szLang);
            strcat(propBuffer, ";");
      }
// List Tag to hang lists off
      if(pbChars->bm_listTag)
      {
            UT_String_sprintf(tempBuffer, " list-tag:%d; ",pChars->m_listTag);
            strcat(propBuffer, tempBuffer.c_str());
      }
//
// Now remove any trailing ";"'s
//
      UT_sint32 eol = strlen(propBuffer);
      while(eol >= 0 && (propBuffer[eol] == ' ' || propBuffer[eol] == 0))
      {
            eol--;
      }
      if(propBuffer[eol] == ';')
      {
            propBuffer[eol] = 0;
      }
      return true;
}


/*!
  Handle document meta data
 */
bool IE_Imp_RTF::HandleInfoMetaData()
{
      RTF_KEYWORD_ID keywordID;
      RTFTokenType tokenType;
      unsigned char keyword[MAX_KEYWORD_LEN];
      UT_sint32 parameter = 0;
      bool paramUsed = false; 
      int nested = 0;
      bool result;
      char * metaDataKey;
      UT_UTF8String metaDataProp;
      enum {
            ACT_NONE,
            ACT_PCDATA,
            ACT_DATETIME
      } action = ACT_NONE;

      
      do {
            tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN,false);
            switch (tokenType) {
            case RTF_TOKEN_ERROR:
                  UT_ASSERT_NOT_REACHED();
                  return false;
                  break;
            case RTF_TOKEN_KEYWORD:             
                  keywordID = KeywordToID(reinterpret_cast<char *>(keyword));
                  
                  switch(keywordID) {
                  case RTF_KW_title:
                        metaDataKey = PD_META_KEY_TITLE;
                        action = ACT_PCDATA;
                        break;
                  case RTF_KW_subject:
                        metaDataKey = PD_META_KEY_SUBJECT;
                        action = ACT_PCDATA;
                        break;
                  case RTF_KW_author:
                        metaDataKey = PD_META_KEY_CREATOR;
                        action = ACT_PCDATA;
                        break;
                  case RTF_KW_manager:
                        metaDataKey = PD_META_KEY_PUBLISHER;
                        action = ACT_PCDATA;
                        break;
                  case RTF_KW_company:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  case RTF_KW_operator:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  case RTF_KW_keywords:
                        metaDataKey = PD_META_KEY_KEYWORDS;
                        action = ACT_PCDATA;
                        break;
                  case RTF_KW_comment:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  case RTF_KW_doccomm:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;            
                  case RTF_KW_hlinkbase:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  case RTF_KW_creatim:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  case RTF_KW_revtim:
                        metaDataKey = PD_META_KEY_DATE_LAST_CHANGED;
                        action = ACT_DATETIME;
                        break;
                  case RTF_KW_printim:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  case RTF_KW_buptim:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        break;
                  default:
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                  }
                  if (action == ACT_PCDATA) {
                        metaDataProp = "";
                        result = HandlePCData(metaDataProp);
                  }
                  else if (action == ACT_DATETIME) {
                        result = SkipCurrentGroup();
                        action = ACT_NONE;
                        //result = HandlMetaDataTime(&metaTime);
                  }
                  // if any action needs to be done
                  if (action !=  ACT_NONE) {
                        getDoc()->setMetaDataProp(metaDataKey, metaDataProp);
                  }
                  break;
            case RTF_TOKEN_OPEN_BRACE:
                  nested++;
                  break;
            case RTF_TOKEN_CLOSE_BRACE:
                  nested--;
                  break;
            case RTF_TOKEN_DATA:
                  // Ignore data because we don't know what to do with it
                  break;
            default:
                  break;
            }
      } while ((tokenType != RTF_TOKEN_CLOSE_BRACE) || (nested >= 0));
      return true;
}


bool IE_Imp_RTF::HandlePCData(UT_UTF8String & str)
{
      RTFTokenType tokenType;
      unsigned char keyword[MAX_KEYWORD_LEN];
      UT_sint32 parameter = 0;
      bool paramUsed = false; 

      UT_ByteBuf buf;
      
      do {
            tokenType = NextToken (keyword, &parameter, &paramUsed, MAX_KEYWORD_LEN, false);
            switch (tokenType) {
            case RTF_TOKEN_KEYWORD:             
                  if ((*keyword == '\'') && (keyword[1] == 0)) {
                        UT_UCS4Char wc;
                        UT_Byte ch;
                        wc = ReadHexChar();
                        // here we assume that the read char fit on ONE byte. Should be correct.
                        ch = static_cast<UT_Byte>(wc);
                        buf.append(&ch, 1);
                  }
                  break;
            case RTF_TOKEN_DATA:
                  buf.append(keyword, 1);
                  break;
            case RTF_TOKEN_ERROR:
                  // force close brace to exit loop
                  tokenType = RTF_TOKEN_CLOSE_BRACE;
                  break;
            case RTF_TOKEN_CLOSE_BRACE:
                  SkipBackChar('}');
                  break;
            default:
                  UT_DEBUGMSG(("Unknown token !!!!!!!!!!!\n"));
                  break;
            }
      }
      while (tokenType != RTF_TOKEN_CLOSE_BRACE);

      str.appendBuf(buf, m_mbtowc);

      return true;
}

Generated by  Doxygen 1.6.0   Back to index