|
|
/*
* @doc INTERNAL * * @module RTFREAD.CPP - RichEdit RTF reader (w/o objects) | * * This file contains the nonobject code of RichEdit RTF reader. * See rtfread2.cpp for embedded-object code. * * Authors:<nl> * Original RichEdit 1.0 RTF converter: Anthony Francisco <nl> * Conversion to C++ and RichEdit 2.0 w/o objects: Murray Sargent * Lots of enhancements/maintenance: Brad Olenick * * @devnote * sz's in the RTF*.cpp and RTF*.h files usually refer to a LPSTRs, * not LPWSTRs. * * @todo * 1. Unrecognized RTF. Also some recognized won't round trip <nl> * 2. In font.c, add overstrike for CFE_DELETED and underscore for * CFE_REVISED. Would also be good to change color for CF.bRevAuthor * * Copyright (c) 1995-2002, Microsoft Corporation. All rights reserved. */
#include "_common.h"
#include "_rtfread.h"
#include "_util.h"
#include "_font.h"
#include "_disp.h"
ASSERTDATA
/*
* Global Variables */
#define PFM_ALLRTF (PFM_ALL2 | PFM_COLLAPSED | PFM_OUTLINELEVEL | PFM_BOX)
// for object attachment placeholder list
#define cobPosInitial 8
#define cobPosChunk 8
#if CFE_SMALLCAPS != 0x40 || CFE_ALLCAPS != 0x80 || CFE_HIDDEN != 0x100 \
|| CFE_OUTLINE != 0x200 || CFE_SHADOW != 0x400 #error "Need to change RTF char effect conversion routines
#endif
// for RTF tag coverage testing
#if defined(DEBUG) && !defined(NOFULLDEBUG)
#define TESTPARSERCOVERAGE() \
{ \ if(GetProfileIntA("RICHEDIT DEBUG", "RTFCOVERAGE", 0)) \ { \ TestParserCoverage(); \ } \ } #define PARSERCOVERAGE_CASE() \
{ \ if(_fTestingParserCoverage) \ { \ return ecNoError; \ } \ } #define PARSERCOVERAGE_DEFAULT() \
{ \ if(_fTestingParserCoverage) \ { \ return ecStackOverflow; /* some bogus error */ \ } \ } #else
#define TESTPARSERCOVERAGE()
#define PARSERCOVERAGE_CASE()
#define PARSERCOVERAGE_DEFAULT()
#endif
static WCHAR szRowEnd[] = {ENDFIELD, CR, 0}; static WCHAR szRowStart[] = {STARTFIELD, CR, 0}; WCHAR pchSeparateField[] = {SEPARATOR, 'F'}; WCHAR pchStartField[] = {STARTFIELD, 'F'};
// FF's should not have paragraph number prepended to them
inline BOOL CharGetsNumbering(WORD ch) { return ch != FF; }
// V-GUYB: PWord Converter requires loss notification.
#ifdef REPORT_LOSSAGE
typedef struct { IStream *pstm; BOOL bFirstCallback; LPVOID *ppwdPWData; BOOL bLoss; } LOST_COOKIE; #endif
//======================== OLESTREAM functions =======================================
DWORD CALLBACK RTFGetFromStream ( RTFREADOLESTREAM *OLEStream, //@parm OleStream
void FAR * pvBuffer, //@parm Buffer to read
DWORD cb) //@parm Bytes to read
{ return OLEStream->Reader->ReadData ((BYTE *)pvBuffer, cb); }
DWORD CALLBACK RTFGetBinaryDataFromStream ( RTFREADOLESTREAM *OLEStream, //@parm OleStream
void FAR * pvBuffer, //@parm Buffer to read
DWORD cb) //@parm Bytes to read
{ return OLEStream->Reader->ReadBinaryData ((BYTE *)pvBuffer, cb); }
//============================ STATE Structure =================================
/*
* STATE::AddPF(PF, lDocType, dwMask, dwMask2) * * @mfunc * If the PF contains new info, this info is applied to the PF for the * state. If this state was sharing a PF with a previous state, a new * PF is created for the state, and the new info is applied to it. * * @rdesc * TRUE unless needed new PF and couldn't allocate it */ BOOL STATE::AddPF( const CParaFormat &PF, //@parm Current RTFRead _PF
LONG lDocType, //@parm Default doc type to use if no prev state
DWORD dwMask, //@parm Mask to use
DWORD dwMask2) //@parm Mask to use
{ // Create a new PF if:
// 1. The state doesn't have one yet
// 2. The state has one, but it is shared by the previous state and
// there are PF deltas to apply to the state's PF
if(!pPF || dwMask && pstatePrev && pPF == pstatePrev->pPF) { Assert(!pstatePrev || pPF);
pPF = new CParaFormat; if(!pPF) return FALSE;
// Give the new PF some initial values - either from the previous
// state's PF or by CParaFormat initialization
if(pstatePrev) { // Copy the PF from the previous state
*pPF = *pstatePrev->pPF; dwMaskPF = pstatePrev->dwMaskPF; } else { // We've just created a new PF for the state - there is no
// previous state to copy from. Use default values.
pPF->InitDefault(lDocType == DT_RTLDOC ? PFE_RTLPARA : 0); dwMaskPF = PFM_ALLRTF; dwMaskPF2 = PFM2_TABLEROWSHIFTED; } }
// Apply the new PF deltas to the state's PF
if(dwMask) { if(dwMask & PFM_TABSTOPS) // Don't change _iTabs here
{ pPF->_bTabCount = PF._bTabCount; dwMask &= ~PFM_TABSTOPS; } pPF->Apply(&PF, dwMask, dwMaskPF2); }
return TRUE; }
/*
* STATE::DeletePF() * * @mfunc * If the state's PF is not shared by the previous state, the PF for this * state is deleted. */ void STATE::DeletePF() { if(pPF && (!pstatePrev || pPF != pstatePrev->pPF)) delete pPF;
pPF = NULL; }
/*
* STATE::SetCodePage(CodePage) * * @mfunc * If current nCodePage is CP_UTF8, use it for all conversions (yes, even * for SYMBOL_CHARSET). Else use CodePage. */ void STATE::SetCodePage( LONG CodePage) { if(nCodePage != CP_UTF8) nCodePage = CodePage; }
//============================ CRTFRead Class ==================================
/*
* CRTFRead::CRTFRead(prg, pes, dwFlags) * * @mfunc * Constructor for RTF reader */ CRTFRead::CRTFRead ( CTxtRange * prg, //@parm CTxtRange to read into
EDITSTREAM * pes, //@parm Edit stream to read from
DWORD dwFlags //@parm Read flags
) : CRTFConverter(prg, pes, dwFlags, TRUE) { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::CRTFRead");
Assert(prg->GetCch() == 0);
//TODO(BradO): We should examine the member data in the constructor
// and determine which data we want initialized on construction and
// which at the beginning of every read (in CRTFRead::ReadRtf()).
_sDefaultFont = -1; // No \deff n control word yet
_sDefaultLanguage = INVALID_LANGUAGE; _sDefaultLanguageFE = INVALID_LANGUAGE; _sDefaultTabWidth = 0; _sDefaultBiDiFont = -1; _dwMaskCF = 0; // No char format changes yet
_dwMaskCF2 = 0; _dwMaskPF = 0; // No char format changes yet
_dwMaskPF2 = 0; // No char format changes yet
_fSeenFontTable = FALSE; // No \fonttbl yet
_fCharSet = FALSE; // No \fcharset yet
_fNon0CharSet = FALSE; // No nonANSI_CHARSET \fcharset yet
_dwFlagsUnion = 0; // No flags yet
_fNotifyLowFiRTF = (_ped->_dwEventMask & ENM_LOWFIRTF) != 0; _fBody = FALSE; // Wait till something's inserted
_pes->dwError = 0; // No error yet
_cchUsedNumText = 0; // No numbering text yet
_cTab = 0; _pstateStackTop = NULL; _pstateLast = NULL; _szText = _pchRTFBuffer = // No input buffer yet
_pchRTFCurrent = _pchRTFEnd = NULL; _prtfObject = NULL; _pcpObPos = NULL; _bTabLeader = 0; _bTabType = 0; _bShapeNameIndex = 0; _pobj = 0; _fSymbolField = FALSE; _fMac = FALSE; _fNo_iTabsTable = FALSE; _fRTLRow = FALSE; _dwRowResolveFlags = 0; _bTableLevelIP = 0; _iTabsLevel1 = -1; InitializeTableRowParms(); // Initialize table parms
// Does story size exceed the maximum text size? Be very careful to
// use unsigned comparisons here since _cchMax has to be unsigned
// (EM_LIMITTEXT allows 0xFFFFFFFF to be a large positive maximum
// value). I.e., don't use signed arithmetic.
DWORD cchAdj = _ped->GetAdjustedTextLength(); _cchMax = _ped->TxGetMaxLength();
if(_cchMax > cchAdj) _cchMax = _cchMax - cchAdj; // Room left
else _cchMax = 0; // No room left
ZeroMemory(_rgStyles, sizeof(_rgStyles)); // No style levels yet
_iCharRepBiDi = 0; if(_ped->IsBiDi()) { _iCharRepBiDi = ARABIC_INDEX; // Default Arabic charset
BYTE iCharRep; CFormatRunPtr rpCF(prg->_rpCF);
// Look backward in text, trying to find a RTL CharRep.
// NB: \fN with an RTL charset updates _iCharRepBiDi.
do { iCharRep = _ped->GetCharFormat(rpCF.GetFormat())->_iCharRep; if(IsRTLCharRep(iCharRep)) { _iCharRepBiDi = iCharRep; break; } } while (rpCF.PrevRun()); } // Initialize OleStream
RTFReadOLEStream.Reader = this; RTFReadOLEStream.lpstbl->Get = (DWORD (CALLBACK*)(LPOLESTREAM, void *, DWORD)) RTFGetFromStream; RTFReadOLEStream.lpstbl->Put = NULL;
#if defined(DEBUG) && !defined(NOFULLDEBUG)
_fTestingParserCoverage = FALSE; _prtflg = NULL;
if(GetProfileIntA("RICHEDIT DEBUG", "RTFLOG", 0)) { _prtflg = new CRTFLog; if(_prtflg && !_prtflg->FInit()) { delete _prtflg; _prtflg = NULL; } AssertSz(_prtflg, "CRTFRead::CRTFRead: Error creating RTF log"); } #endif // DEBUG
}
/*
* CRTFRead::HandleStartGroup() * * @mfunc * Handle start of new group. Alloc and push new state onto state * stack * * @rdesc * EC The error code */ EC CRTFRead::HandleStartGroup() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleStartGroup");
STATE * pstate = _pstateStackTop; STATE * pstateNext = NULL;
if(pstate) // At least one STATE already
{ // allocated
Apply_CF(); // Apply any collected char
// Note (igorzv) we don't Apply_PF() here so as not to change para
// properties before we run into \par i.e. not to use paragraph
// properties if we copy only one word from paragraph. We can use an
// assertion here that neither we nor Word use end of group for
// restoring paragraph properties. So everything will be OK with stack
pstate->iCF = (SHORT)_prg->Get_iCF(); // Save current CF
pstate = pstate->pstateNext; // Use previously allocated
if(pstate) // STATE frame if it exists
pstateNext = pstate->pstateNext; // It does; save its forward
} // link for restoration below
if(!pstate) // No new STATE yet: alloc one
{ pstate = new STATE(); if(!pstate) // Couldn't alloc new STATE
goto memerror;
_pstateLast = pstate; // Update ptr to last STATE
} // alloc'd
STATE *pstateGetsPF;
// Apply the accumulated PF delta's to the old current state or, if there
// is no current state, to the newly created state.
pstateGetsPF = _pstateStackTop ? _pstateStackTop : pstate; if(!pstateGetsPF->AddPF(_PF, _bDocType, _dwMaskPF, _dwMaskPF2)) goto memerror;
_dwMaskPF = _dwMaskPF2 = 0; // _PF contains delta's from
// *_pstateStackTop->pPF
if(_pstateStackTop) // There's a previous STATE
{ *pstate = *_pstateStackTop; // Copy current state info
// N.B. This will cause the current and previous state to share
// the same PF. PF delta's are accumulated in _PF. A new PF
// is created for _pstateStackTop when the _PF deltas are applied.
_pstateStackTop->pstateNext = pstate; } else // Setup default for first state
{ pstate->nCodePage = IsUTF8 ? CP_UTF8 : _nCodePage;
for(LONG i = ARRAY_SIZE(pstate->rgDefFont); i--; ) pstate->rgDefFont[i].sHandle = -1; // Default undefined associate
}
pstate->pstatePrev = _pstateStackTop; // Link STATEs both ways
pstate->pstateNext = pstateNext; _pstateStackTop = pstate; // Push stack
done: TRACEERRSZSC("HandleStartGroup()", -_ecParseError); return _ecParseError;
memerror: _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecStackOverflow; goto done; }
/*
* CRTFRead::HandleEndGroup() * * @mfunc * Handle end of new group * * @rdesc * EC The error code */ EC CRTFRead::HandleEndGroup() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleEndGroup");
STATE * pstate = _pstateStackTop; STATE * pstatePrev;
Assert(_PF._iTabs == -1);
if(!pstate) // No stack to pop
{ _ecParseError = ecStackUnderflow; goto done; }
_pstateStackTop = // Pop stack
pstatePrev = pstate->pstatePrev;
if(!pstatePrev) { Assert(pstate->pPF);
// We're ending the parse. Copy the final PF into _PF so that
// subsequent calls to Apply_PF will have a PF to apply.
_PF = *pstate->pPF; //TODO honwch _dwMaskPF = pstate->dwMaskPF;
_PF._iTabs = -1; // Force recache
_PF._wEffects &= ~PFE_TABLE; }
// Adjust the PF for the new _pstateStackTop and delete unused PF's.
if(pstate->sDest == destParaNumbering || pstate->sDest == destParaNumText) { if(pstatePrev && pstate->pPF != pstatePrev->pPF) { // Bleed the PF of the current state into the previous state for
// paragraph numbering groups
Assert(pstatePrev->pPF); pstatePrev->DeletePF(); pstatePrev->pPF = pstate->pPF; pstate->pPF = NULL; } else pstate->DeletePF(); // N.B. Here, we retain the _PF diffs since they apply to the
// enclosing group along with the PF of the group we are leaving
} else { // We're popping the state, so delete its PF and discard the _PF diffs
Assert(pstate->pPF); pstate->DeletePF();
// If !pstatePrev, we're ending the parse, in which case the _PF
// structure contains final PF (so don't toast it).
if(pstatePrev) _dwMaskPF = _dwMaskPF2 = 0; }
if(pstatePrev) { LONG i; _dwMaskCF = _dwMaskCF2 = 0; // Discard any CF deltas
switch(pstate->sDest) { case destParaNumbering: // {\pn ...}
pstatePrev->sIndentNumbering = pstate->sIndentNumbering; pstatePrev->fBullet = pstate->fBullet; break;
case destObject: // Clear our object flags just in case we have corrupt RTF
if(_fNeedPres) { _fNeedPres = FALSE; _fNeedIcon = FALSE; _pobj = NULL; } break;
case destFontTable: if(pstatePrev->sDest == destFontTable) { // Leaving subgroup within \fonttbl group
break; }
// Leaving {\fonttbl...} group
_fSeenFontTable = TRUE;
// Default font should now be defined, so select it (this
// creates CF deltas).
SetPlain(pstate);
if(_sDefaultFont != -1) { pstate->rgDefFont[DEFFONT_LTRCH].sHandle = _sDefaultFont;
Assert(pstate->pstatePrev); if (pstate->pstatePrev) { pstate->pstatePrev->rgDefFont[DEFFONT_LTRCH].sHandle = _sDefaultFont; Assert(pstate->pstatePrev->pstatePrev == NULL); } }
if(_sDefaultBiDiFont != -1) { // Validate default BiDi font since Word 2000 may choose
// a nonBiDi font
i = _fonts.Count(); TEXTFONT *ptf = _fonts.Elem(0); for(; i-- && _sDefaultBiDiFont != ptf->sHandle; ptf++) ;
if(i < 0 || !IsRTLCharRep(ptf->iCharRep)) { _sDefaultBiDiFont = -1;
// Bad DefaultBiDiFont, find the first good bidi font
i = _fonts.Count(); ptf = _fonts.Elem(0); for (; i--; ptf++) { if (IsRTLCharRep(ptf->iCharRep)) { _sDefaultBiDiFont = ptf->sHandle; break; } } }
if(_sDefaultBiDiFont != -1) { pstate->rgDefFont[DEFFONT_RTLCH].sHandle = _sDefaultBiDiFont;
Assert(pstate->pstatePrev); if (pstate->pstatePrev) { pstate->pstatePrev->rgDefFont[DEFFONT_RTLCH].sHandle = _sDefaultBiDiFont; Assert(pstate->pstatePrev->pstatePrev == NULL); } } }
// Ensure that a document-level codepage has been determined and
// then scan the font names and retry the conversion to Unicode,
// if necessary.
if(_nCodePage == INVALID_CODEPAGE) { // We haven't determined a document-level codepage
// from the \ansicpgN tag, nor from the font table
// \fcharsetN and \cpgN values. As a last resort,
// let's use the \deflangN and \deflangfeN tags
LANGID langid;
if(_sDefaultLanguageFE != INVALID_LANGUAGE) langid = _sDefaultLanguageFE;
else if(_sDefaultLanguage != INVALID_LANGUAGE && _sDefaultLanguage != sLanguageEnglishUS) { // _sDefaultLanguage == sLanguageEnglishUS is inreliable
// in the absence of \deflangfeN. Many FE RTF writers
// write \deflang1033 (sLanguageEnglishUS).
langid = _sDefaultLanguage; } else if(_dwFlags & SFF_SELECTION) { // For copy/paste case, if nothing available, try the system
// default langid. This is to fix FE Excel95 problem.
langid = GetSystemDefaultLangID(); } else goto NoLanguageInfo;
_nCodePage = CodePageFromCharRep(CharRepFromLID(langid)); }
NoLanguageInfo: if(_nCodePage == INVALID_CODEPAGE) break;
// Fixup misconverted font face names
for(i = 0; i < _fonts.Count(); i++) { TEXTFONT *ptf = _fonts.Elem(i);
if (ptf->sCodePage == INVALID_CODEPAGE || ptf->sCodePage == CP_SYMBOL) { if(ptf->fNameIsDBCS) { char szaTemp[LF_FACESIZE]; BOOL fMissingCodePage;
// Unconvert misconverted face name
SideAssert(WCTMB(ptf->sCodePage, 0, ptf->szName, -1, szaTemp, sizeof(szaTemp), NULL, NULL, &fMissingCodePage) > 0); Assert(ptf->sCodePage == CP_SYMBOL || fMissingCodePage);
// Reconvert face name using new codepage info
SideAssert(MBTWC(_nCodePage, 0, szaTemp, -1, ptf->szName, sizeof(ptf->szName), &fMissingCodePage) > 0);
if(!fMissingCodePage) ptf->fNameIsDBCS = FALSE; } } } break; } _prg->Set_iCF(pstatePrev->iCF); // Restore previous CharFormat
ReleaseFormats(pstatePrev->iCF, -1); }
done: TRACEERRSZSC("HandleEndGroup()", - _ecParseError); return _ecParseError; }
/*
* CRTFRead::HandleFieldEndGroup() * * @mfunc * Handle end of \field */ //FUTURE (keithcu) If I knew the first thing about RTF, I'd cleanup the symbol handling.
void CRTFRead::HandleFieldEndGroup() { STATE * pstate = _pstateStackTop;
if (!IN_RANGE(destField, pstate->sDest, destFieldInstruction) || pstate->sDest != destField && !_ecParseError) { return; }
// For SYMBOLS
if(_fSymbolField) { _fSymbolField = FALSE; return; }
// Walk backwards, removing the STARTFIELD and SEPARATOR character.
// Make the instruction field hidden, and mark the whole range
// with CFE_LINK | CFE_LINKPROTECTED so that our automatic URL
// detection code doesn't get in the way and remove this stuff.
// If it is not a hyperlink field, delete fldinst.
CTxtRange rg(*_prg); long cchInst = -2, cchResult = -2; WCHAR ch, chNext = 0; LONG cpSave = _prg->GetCp();
rg.SetIgnoreFormatUpdate(TRUE);
// Find boundary between instruction and result field
while(!IN_RANGE(STARTFIELD, (ch = rg._rpTX.GetChar()), SEPARATOR) || chNext != 'F') { if(!rg.Move(-1, FALSE)) return; // Nothing to fixup
cchResult++; chNext = ch; }
BOOL fBackSlash = FALSE; if (ch == SEPARATOR) { rg.Move(2, TRUE); rg.ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK);
chNext = 0; while((ch = rg.CRchTxtPtr::GetChar()) != STARTFIELD || chNext != 'F') { if(ch == BSLASH) fBackSlash = TRUE; // Need backslash pass
if(!rg.Move(-1, FALSE)) return; // Nothing to fix up
cchInst++; chNext = ch; } } else //No field result
{ cchInst = cchResult; cchResult = 0; }
rg.Move(_ecParseError ? tomForward : 2, TRUE); rg.ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK); if(_ecParseError) return;
// If it's a hyperlink field, set properties, else, delete fldinst
CTxtPtr tp(rg._rpTX); if(tp.FindText(rg.GetCp() + cchInst, FR_DOWN, L"HYPERLINK", 9) != -1) { while((ch = tp.GetChar()) == ' ' || ch == '\"') tp.Move(1); ch = tp.GetPrevChar();
if(ch == '\"' && fBackSlash) // Word quadruples backslash, so
{ // need to delete every other one
LONG cp0 = rg.GetCp(); // Save cp at start of instruction
LONG cp1 = tp.GetCp(); // Save cp at start of URL
LONG cpMax = cp0 + cchInst; // Max cp for the instruction
rg.SetCp(cp1, FALSE); while(rg.GetCp() < cpMax) { ch = rg._rpTX.GetChar(); if(ch == '\"') break; if(ch == BSLASH) { if (!rg.Move(1, TRUE)) break;
ch = rg._rpTX.GetChar(); if(ch == '\"') break; if(ch == BSLASH) { cchInst--; rg.ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK); } }
if (!rg.Move(1, FALSE)) break; } rg.SetCp(cp0, FALSE); // Restore rg and tp
tp = rg._rpTX; // Rebind tp, since deletions may
tp.SetCp(cp1); // change plain-text arrays
} CCharFormat CF; DWORD dwMask, dwMask2; LONG cch1 = 0; CTxtPtr tp1(_prg->_rpTX);
tp1.Move(-cchResult); // Point at start of link result
for(LONG cch = cchResult; cch; cch--) { DWORD ch1 = tp.GetChar(); if(ch1 != tp1.GetChar()) break; if(ch1 == ' ') cch1 = 1; tp.Move(1); // This could be much
tp1.Move(1); // faster if it matters
} if(!cch && tp.GetChar() == ch) // Perfect match, so delete
{ // instruction and use built-in
WCHAR chLeftBracket = '<'; // RichEdit URLs. Store autocolor
rg.Move(cchInst, TRUE); // and no underlining
rg.ReplaceRange(cch1, &chLeftBracket, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK); CF._dwEffects = CFE_LINK | CFE_AUTOCOLOR; CF._crTextColor = 0; // Won't be used but will be stored
dwMask = CFM_LINK | CFM_COLOR | CFM_UNDERLINE; dwMask2 = 0; if(cch1) { WCHAR chRightBracket = '>'; _prg->ReplaceRange(cch1, &chRightBracket, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK); } } else { rg.Move(cchInst, TRUE); // Set properties on instruction
CF._dwEffects = CFE_HIDDEN | CFE_LINK | CFE_LINKPROTECTED ; rg.SetCharFormat(&CF, 0, 0, CFM_LINK | CFM_HIDDEN, CFM2_LINKPROTECTED); dwMask = CFM_LINK; dwMask2 = CFM2_LINKPROTECTED; } rg.Set(rg.GetCp(), -cchResult); // Set properties on result
rg.SetCharFormat(&CF, 0, 0, dwMask, dwMask2); } else { _iKeyword = i_field; // Might be nice to have field name
if((tp.GetChar() | 0x20) == 'e') // Only fire notification for EQ fields
{ tp.Move(1); if((tp.GetChar() | 0x20) == 'q') { CheckNotifyLowFiRTF(TRUE); if(HandleEq(rg, tp) == ecNoError) return; } } rg.Move(cchInst, TRUE); rg.ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK); } if(_cpThisPara > rg.GetCp()) // Field result had \par's, so
_cpThisPara -= cpSave - _prg->GetCp();// subtract cch deleted
}
/*
* CRTFRead::SelectCurrentFont(iFont) * * @mfunc * Set active font to that with index <p iFont>. Take into account * bad font numbers. */ void CRTFRead::SelectCurrentFont( INT iFont) //@parm Font handle of font to select
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::SelectCurrentFont");
LONG i = _fonts.Count(); STATE * pstate = _pstateStackTop; TEXTFONT * ptf = _fonts.Elem(0);
AssertSz(i, "CRTFRead::SelectCurrentFont: bad font collection"); for(; i-- && iFont != ptf->sHandle; ptf++) // Search for font with handle
; // iFont
// Font handle not found: use default, which is valid
// since \rtf copied _prg's
if(i < 0) ptf = _fonts.Elem(0); BOOL fDefFontFromSystem = (i == (LONG)_fonts.Count() - 1 || i < 0) && !_fReadDefFont;
_CF._iFont = GetFontNameIndex(ptf->szName); _dwMaskCF2 |= CFM2_FACENAMEISDBCS; _CF._dwEffects &= ~CFE_FACENAMEISDBCS; if(ptf->fNameIsDBCS) _CF._dwEffects |= CFE_FACENAMEISDBCS;
if(pstate->sDest != destFontTable) { _CF._iCharRep = ptf->iCharRep; _CF._bPitchAndFamily = ptf->bPitchAndFamily; _dwMaskCF |= CFM_FACE | CFM_CHARSET; if (IsRTLCharRep(_CF._iCharRep) && ptf->sCodePage == 1252) ptf->sCodePage = (SHORT)CodePageFromCharRep(_CF._iCharRep); // Fix sCodePage to match charset
}
if (_ped->Get10Mode() && !_fSeenFontTable && _nCodePage == INVALID_CODEPAGE && ptf->sCodePage == 1252) { if (W32->IsFECodePage(GetACP())) _nCodePage = GetACP(); }
// Ensure that the state's codepage is not supplied by the system.
// That is, if we are using the codepage info from the default font,
// be sure that the default font info was read from the RTF file.
pstate->SetCodePage((fDefFontFromSystem && _nCodePage != INVALID_CODEPAGE) || ptf->sCodePage == INVALID_CODEPAGE ? _nCodePage : ptf->sCodePage); pstate->ptf = ptf; }
/*
* CRTFRead::SetPlain(pstate) * * @mfunc * Setup _CF for \plain */ void CRTFRead::SetPlain( STATE *pstate) { ZeroMemory(&_CF, sizeof(CCharFormat));
_dwMaskCF = CFM_ALL2; _dwMaskCF2 = CFM2_LINKPROTECTED; if(_dwFlags & SFF_SELECTION && _prg->GetCp() == _cpFirst && !_fCharSet) { // Let NT 4.0 CharMap use insertion point size
_CF._yHeight = _ped->GetCharFormat(_prg->Get_iFormat())->_yHeight; } else _CF._yHeight = PointsToFontHeight(yDefaultFontSize);
_CF._dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR; // Set default effects
if(_sDefaultLanguage == INVALID_LANGUAGE) _dwMaskCF &= ~CFM_LCID; else _CF._lcid = MAKELCID((WORD)_sDefaultLanguage, SORT_DEFAULT);
_CF._bUnderlineType = CFU_UNDERLINE; SelectCurrentFont(_sDefaultFont); }
/*
* CRTFRead::ReadFontName(pstate, iAllASCII) * * @mfunc * read font name _szText into <p pstate>->ptf->szName and deal with * tagged fonts */ void CRTFRead::ReadFontName( STATE * pstate, //@parm state whose font name is to be read into
int iAllASCII) //@parm indicates that _szText is all ASCII chars
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::ReadFontName");
if (pstate->ptf) { INT cchName = LF_FACESIZE - 1; WCHAR * pchDst = pstate->ptf->szName; char * pachName = (char *)_szText ; // Append additional text from _szText to TEXTFONT::szName
// We need to append here since some RTF writers decide
// to break up a font name with other RTF groups
while(*pchDst && cchName > 0) { pchDst++; cchName--; } if(!cchName) // Probably illegal file
{ // e.g., extra {
_ecParseError = ecFontTableOverflow; return; } INT cchLimit = cchName; BOOL fTaggedName = FALSE; while (*pachName && *pachName != ';' && cchLimit) // Remove semicolons
{ pachName++; cchLimit--;
if (*pachName == '(') fTaggedName = TRUE; } *pachName = '\0';
// Use the codepage of the font in all cases except where the font uses
// the symbol charset (and the codepage has been mapped from the charset)
// and UTF-8 isn't being used
LONG nCodePage = pstate->nCodePage != CP_SYMBOL ? pstate->nCodePage : _nCodePage;
BOOL fMissingCodePage; Assert(!IsUTF8 || nCodePage == CP_UTF8); INT cch = MBTWC(nCodePage, 0, (char *)_szText, -1, pchDst, cchName, &fMissingCodePage);
if(cch > 0 && fMissingCodePage && iAllASCII == CONTAINS_NONASCII) pstate->ptf->fNameIsDBCS = TRUE; else if(pstate->ptf->iCharRep == DEFAULT_INDEX && W32->IsFECodePage(nCodePage) && GetTrailBytesCount(*_szText, nCodePage)) pstate->ptf->iCharRep = CharRepFromCodePage(nCodePage); // Fix up charset
// Make sure destination is null terminated
if(cch > 0) pchDst[cch] = 0;
// Fall through even if MBTWC <= 0, since we may be appending text to an
// existing font name.
if(pstate->ptf == _fonts.Elem(0)) // If it's the default font,
SelectCurrentFont(_sDefaultFont); // update _CF accordingly
WCHAR * szNormalName;
if(pstate->ptf->iCharRep && pstate->fRealFontName) { // if we don't know about this font don't use the real name
if(!FindTaggedFont(pstate->ptf->szName, pstate->ptf->iCharRep, &szNormalName)) { pstate->fRealFontName = FALSE; pstate->ptf->szName[0] = 0; } } else if(IsTaggedFont(pstate->ptf->szName, &pstate->ptf->iCharRep, &szNormalName)) { wcscpy(pstate->ptf->szName, szNormalName); pstate->ptf->sCodePage = (SHORT)CodePageFromCharRep(pstate->ptf->iCharRep); pstate->SetCodePage(pstate->ptf->sCodePage); } else if(fTaggedName && !fMissingCodePage) { HDC hDC = W32->GetScreenDC(); if (!W32->IsFontAvail( hDC, 0, 0, NULL, pstate->ptf->szName)) { // Fix up tagged name by removing characters after the ' ('
INT i = 0; WCHAR *pwchTag = pstate->ptf->szName;
while (pwchTag[i] && pwchTag[i] != L'(') // Search for '('
i++;
if(pwchTag[i] && i > 0) { while (i > 0 && pwchTag[i-1] == 0x20) // Remove spaces before the '('
i--; pwchTag[i] = 0; } } } } }
/*
* CRTFRead::GetColor (dwMask) * * @mfunc * Store the autocolor or autobackcolor effect bit and return the * COLORREF for color _iParam * * @rdesc * COLORREF for color _iParam * * @devnote * If the entry in _colors corresponds to tomAutoColor, gets the value * RGB(0,0,0) (since no \red, \green, and \blue fields are used), but * isn't used by the RichEdit engine. Entry 1 corresponds to the first * explicit entry in the \colortbl and is usually RGB(0,0,0). The _colors * table is built by HandleToken() when it handles the token tokenText * for text consisting of a ';' for a destination destColorTable. */ COLORREF CRTFRead::GetColor( DWORD dwMask) //@parm Color mask bit
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::GetColor");
if(_iParam < 0 || _iParam >= _colors.Count()) // Illegal _iParam
return RGB(0,0,0);
COLORREF Color = *_colors.Elem(_iParam);
if(dwMask) { _dwMaskCF |= dwMask; // Turn on appropriate mask bit
_CF._dwEffects &= ~dwMask; // auto(back)color off: color is to be used
if(Color == tomAutoColor) { _CF._dwEffects |= dwMask; // auto(back)color on
Color = RGB(0,0,0); } } return Color; }
/*
* CRTFRead::GetStandardColorIndex () * * @mfunc * Return the color index into the standard 16-entry Word \colortbl * corresponding to the color index _iParam for the current \colortbl * * @rdesc * Standard color index corresponding to the color associated with _iParam */ LONG CRTFRead::GetStandardColorIndex() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::GetColorIndex");
if(_iParam < 0 || _iParam >= _colors.Count()) // Illegal _iParam:
return 0; // use autocolor
COLORREF Color = *_colors.Elem(_iParam);
for(LONG i = 0; i < 16; i++) { if(Color == g_Colors[i]) return i + 1; } return 0; // Not there: use autocolor
}
/*
* CRTFRead::GetCellColorIndex () * * @mfunc * Return the color index into the standard 16-entry Word \colortbl * corresponding to the color index _iParam for the current \colortbl. * If color isn't found, use _crCellCustom1 or _crCellCustom2. * * @rdesc * Standard or custom color index corresponding to the color associated * with _iParam */ LONG CRTFRead::GetCellColorIndex() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::GetColorIndex");
LONG i = GetStandardColorIndex(); // 16 standard colors (1 - 16)
if(i || _iParam >= _colors.Count() || _iParam < 0) // plus autocolor (0)
return i;
COLORREF Color = *_colors.Elem(_iParam);// Not there: try using custom colors
if(!_crCellCustom1 || Color == _crCellCustom1) { _crCellCustom1 = Color; // First custom cr
return 17; }
if(!_crCellCustom2 || Color == _crCellCustom2) { _crCellCustom2 = Color; // Second custom cr
return 18; } return 0; // No custom cr available
}
/*
* CRTFRead::HandleEq(&rg, &tp) * * @mfunc * Handle Word EQ field * * @rdesc * EC The error code */ EC CRTFRead::HandleEq( CTxtRange & rg, //@parm Range to use
CTxtPtr & tp) //@parm TxtPtr to use
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleEq");
#if 0
while(tp.GetCp() < _prg->GetCp()) { } #endif
return ecGeneralFailure; // Not yet implemented, but don't set _ecParseError
}
/*
* CRTFRead::HandleCell() * * @mfunc * Handle \cell command * * @rdesc * EC The error code */ void CRTFRead::HandleCell() { if(!_bTableLevel) { if(!_fStartRow) return; DelimitRow(szRowStart); // \cell directly follows \row
} if(_bTableLevel + _bTableLevelIP > MAXTABLENEST) { _iCell++; HandleChar(TAB); return; } LONG cCell = _cCell; CTabs * pTabs = NULL; CELLPARMS *pCellParms = NULL;
if(_bTableLevel == 1 && _iTabsLevel1 >= 0 && !_fNo_iTabsTable) { pTabs = GetTabsCache()->Elem(_iTabsLevel1); pCellParms = (CELLPARMS *)(pTabs->_prgxTabs); cCell = pTabs->_cTab/(CELL_EXTRA + 1); } if(!cCell) // _rgxCell has no cell defs
{ if(_iTabsTable < 0) // No cells defined yet
{ _iCell++; // Count cells inserted
HandleEndOfPara(); // Insert cell delimiter
return; } // Use previous table defs
Assert(_bTableLevel == 1 && !_fNo_iTabsTable);
pTabs = GetTabsCache()->Elem(_iTabsTable); cCell = pTabs->_cTab/(CELL_EXTRA + 1); } if(_iCell < cCell) // Don't add more cells than
{ // defined, since Word crashes
if(pCellParms && IsLowCell(pCellParms[_iCell].uCell)) HandleChar(NOTACHAR); // Signal pseudo cell (\clvmrg)
_iCell++; // Count cells inserted
HandleEndOfPara(); // Insert cell delimiter
} }
/*
* CRTFRead::HandleCellx(iParam) * * @mfunc * Handle \cell command * * @rdesc * EC The error code */ void CRTFRead::HandleCellx( LONG iParam) { if(!_fCellxOK) // Missing \trowd
{ _ecParseError = ecUnexpectedToken; return; } if(_cCell < MAX_TABLE_CELLS) // Save cell right boundaries
{ if(!_cCell) { // Save border info
_wBorders = _PF._wBorders; _wBorderSpace = _PF._wBorderSpace; _wBorderWidth = _PF._wBorderWidth; _xCellPrev = _xRowOffset; } CELLPARMS *pCellParms = (CELLPARMS *)&_rgxCell[0]; // Store cell widths instead of right boundaries relative to \trleftN
LONG i = iParam - _xCellPrev; i = max(i, 0); i = min(i, 1440*22); pCellParms[_cCell].uCell = i + (_bCellFlags << 24); pCellParms[_cCell].dxBrdrWidths = _dwCellBrdrWdths; pCellParms[_cCell].dwColors = _dwCellColors; pCellParms[_cCell].bShading = (BYTE)min(200, _dwShading); _dwCellColors = 0; // Default autocolor for next cell
_dwShading = 0; _xCellPrev = iParam; _cCell++; _dwCellBrdrWdths = 0; _bCellFlags = 0; } }
/*
* CRTFRead::HandleChar(ch) * * @mfunc * Handle single Unicode character <p ch> * * @rdesc * EC The error code */ EC CRTFRead::HandleChar( WCHAR ch) //@parm char token to be handled
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleChar");
if(!_ped->_pdp->IsMultiLine() && IsASCIIEOP(ch)) _ecParseError = ecTruncateAtCRLF; else { AssertNr(ch <= 0x7F || ch > 0xFF || FTokIsSymbol(ch)); _dwMaskCF2 |= CFM2_RUNISDBCS; _CF._dwEffects &= ~CFE_RUNISDBCS; AddText(&ch, 1, CharGetsNumbering(ch)); }
TRACEERRSZSC("HandleChar()", - _ecParseError);
return _ecParseError; }
/*
* CRTFRead::HandleEndOfPara() * * @mfunc * Insert EOP and apply current paraformat * * @rdesc * EC the error code */ EC CRTFRead::HandleEndOfPara() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleEndOfPara");
if(!_ped->_pdp->IsMultiLine()) // No EOPs permitted in single-
{ // line controls
Apply_PF(); // Apply any paragraph formatting
_ecParseError = ecTruncateAtCRLF; // Cause RTF reader to finish up
return ecTruncateAtCRLF; }
Apply_CF(); // Apply _CF and save iCF, since
LONG iFormat = _prg->Get_iCF(); // it gets changed if processing
// CFE2_RUNISDBCS chars
EC ec; if(IN_RANGE(tokenCell, _token, tokenNestCell) || _token == tokenRow) ec = HandleChar((unsigned)CELL); else ec = _ped->fUseCRLF() // If RichEdit 1.0 compatibility
? HandleText(szaCRLF, ALL_ASCII) // mode, use CRLF; else CR or VT
: HandleChar((unsigned)(_token == tokenLineBreak ? VT : _token == tokenPage ? FF : CR)); if(ec == ecNoError) { Apply_PF(); _cpThisPara = _prg->GetCp(); // New para starts after CRLF
} _prg->Set_iCF(iFormat); // Restore iFormat if changed
ReleaseFormats(iFormat, -1); // Release iFormat (AddRef'd by
// Get_iCF())
return _ecParseError; }
/*
* CRTFRead::HandleText(szText, iAllASCII) * * @mfunc * Handle the string of Ansi characters <p szText> * * @rdesc * EC The error code */ EC CRTFRead::HandleText( BYTE * szText, //@parm string to be handled
int iAllASCII, //@parm enum indicating if string is all ASCII chars
LONG cchText) //@parm size of szText in bytes
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleText");
LONG cch; BOOL fStateChng = FALSE; WCHAR * pch; STATE * pstate = _pstateStackTop; TEXTFONT * ptf = pstate->ptf;
struct TEXTFONTSAVE : TEXTFONT { TEXTFONTSAVE(TEXTFONT *ptf) { if (ptf) { iCharRep = ptf->iCharRep; sCodePage = ptf->sCodePage; fCpgFromSystem = ptf->fCpgFromSystem; } } };
BOOL fAdjustPtf = FALSE;
if(IN_RANGE(DEFFONT_LTRCH, pstate->iDefFont, DEFFONT_RTLCH)) { // CharSet resolution based on directional control words
BOOL frtlch = pstate->iDefFont == DEFFONT_RTLCH; if(_CF._iCharRep == DEFAULT_INDEX) { _CF._iCharRep = (BYTE)(frtlch ? _iCharRepBiDi : ANSI_INDEX); _dwMaskCF |= CFM_CHARSET; fAdjustPtf = TRUE; } else { BOOL fBiDiCharRep = IsRTLCharRep(_CF._iCharRep);
// If direction token doesn't correspond to current charset
if(fBiDiCharRep ^ frtlch) { _dwMaskCF |= CFM_CHARSET; fAdjustPtf = TRUE; if(!fBiDiCharRep) // Wasn't BiDi, but is now
SelectCurrentFont(_sDefaultBiDiFont); _CF._iCharRep = (BYTE)(frtlch ? _iCharRepBiDi : ANSI_INDEX); } else if (fBiDiCharRep && ptf && !W32->IsBiDiCodePage(ptf->sCodePage)) fAdjustPtf = TRUE; } } else if(_ped->IsBiDi() && _CF._iCharRep == DEFAULT_INDEX) { _CF._iCharRep = ANSI_INDEX; _dwMaskCF |= CFM_CHARSET; fAdjustPtf = TRUE; } if (fAdjustPtf && ptf) { ptf->sCodePage = (SHORT)CodePageFromCharRep(_CF._iCharRep); pstate->SetCodePage(ptf->sCodePage); }
TEXTFONTSAVE tfSave(ptf);
// TODO: what if szText cuts off in middle of DBCS?
if(!*szText) goto CleanUp;
if (cchText != -1 && _cchUnicode < cchText) { // Re-allocate a bigger buffer
_szUnicode = (WCHAR *)PvReAlloc(_szUnicode, (cchText + 1) * sizeof(WCHAR)); if(!_szUnicode) // Re-allocate space for Unicode conversions
{ _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecNoMemory; goto CleanUp; } _cchUnicode = cchText + 1; }
if(iAllASCII == ALL_ASCII || pstate->nCodePage == CP_SYMBOL) { // Don't use MBTWC() in cases where text contains
// only ASCII chars (which don't require conversion)
for(cch = 0, pch = _szUnicode; *szText; cch++) { Assert(*szText <= 0x7F || pstate->nCodePage == CP_SYMBOL); *pch++ = (WCHAR)*szText++; } *pch = 0;
_dwMaskCF2 |= CFM2_RUNISDBCS; _CF._dwEffects &= ~CFE_RUNISDBCS;
// Fall through to AddText at end of HandleText()
} else { BOOL fMissingCodePage;
// Run of text contains bytes > 0x7F.
// Ensure that we have the correct codepage with which to interpret
// these (possibly DBCS) bytes.
if(ptf && ptf->sCodePage == INVALID_CODEPAGE && !ptf->fCpgFromSystem) { if(_dwFlags & SF_USECODEPAGE) { _CF._iCharRep = CharRepFromCodePage(_nCodePage); _dwMaskCF |= CFM_CHARSET; }
// Determine codepage from the font name
else if(CpgInfoFromFaceName(pstate->ptf)) { fStateChng = TRUE; SelectCurrentFont(pstate->ptf->sHandle); Assert(ptf->sCodePage != INVALID_CODEPAGE && ptf->fCpgFromSystem); } else { // Here, we were not able to determine a cpg/charset value
// from the font name. We have two choices: (1) either choose
// some fallback value like 1252/0 or (2) rely on the
// document-level cpg value.
//
// I think choosing the document-level cpg value will give
// us the best results. In FE cases, it will likely err
// on the side of tagging too many runs as CFE2_RUNISDBCS, but
// that is safer than using a western cpg and potentially missing
// runs which should be CFE2_RUNISDBCS.
} }
Assert(!IsUTF8 || pstate->nCodePage == CP_UTF8);
if (pstate->nCodePage == INVALID_CODEPAGE && ptf) pstate->nCodePage = ptf->sCodePage;
cch = MBTWC(pstate->nCodePage, 0, (char *)szText, -1, _szUnicode, _cchUnicode, &fMissingCodePage);
AssertSz(cch > 0, "CRTFRead::HandleText(): MBTWC implementation changed" " such that it returned a value <= 0");
if(!fMissingCodePage || !W32->IsFECodePage(pstate->nCodePage)) { // Use result of MBTWC if:
// (1) we converted some chars and did the conversion with the codepage
// provided.
// (2) we converted some chars but couldn't use the codepage provided,
// but the codepage is invalid. Since the codepage is invalid,
// we can't do anything more sophisticated with the text before
// adding to the backing store
cch--; // don't want char count to including terminating NULL
_dwMaskCF2 |= CFM2_RUNISDBCS; _CF._dwEffects &= ~CFE_RUNISDBCS; if(pstate->nCodePage == INVALID_CODEPAGE) _CF._dwEffects |= CFE_RUNISDBCS;
// fall through to AddText at end of HandleText()
} else { // Conversion to Unicode failed. Break up the string of
// text into runs of ASCII and non-ASCII characters.
// FUTURE(BradO): Here, I am saving dwMask and restoring it before
// each AddText. I'm not sure this is neccessary. When I have
// the time, I should revisit this save/restoring and
// determine that it is indeed neccessary.
BOOL fPrevIsASCII = ((*szText <= 0x7F) ? TRUE : FALSE); BOOL fCurrentIsASCII = FALSE; BOOL fLastChunk = FALSE; DWORD dwMaskSave = _dwMaskCF; #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
CCharFormat CFSave = _CF; #endif
pch = _szUnicode; cch = 0;
// (!*szText && *pch) is the case where we do the AddText for the
// last chunk of text
while(*szText || fLastChunk) { // fCurrentIsASCII assumes that no byte <= 0x7F is a
// DBCS lead-byte
if(fLastChunk || (fPrevIsASCII != (fCurrentIsASCII = (*szText <= 0x7F)))) { _dwMaskCF = dwMaskSave; #if defined(DEBUG) || defined(_RELEASE_ASSERTS_)
_CF = CFSave; #endif
*pch = 0;
_dwMaskCF2 |= CFM2_RUNISDBCS; _CF._dwEffects |= CFE_RUNISDBCS; if(fPrevIsASCII) _CF._dwEffects &= ~CFE_RUNISDBCS;
Assert(cch); pch = _szUnicode;
AddText(pch, cch, TRUE);
cch = 0; fPrevIsASCII = fCurrentIsASCII;
// My assumption in saving _dwMaskCF is that the remainder
// of the _CF is unchanged by AddText. This assert verifies
// this assumption.
AssertSz(!CompareMemory(&CFSave._iCharRep, &_CF._iCharRep, sizeof(CCharFormat) - sizeof(DWORD)) && !((CFSave._dwEffects ^ _CF._dwEffects) & ~CFE_RUNISDBCS), "CRTFRead::HandleText(): AddText has been changed " "and now alters the _CF structure.");
if(fLastChunk) // Last chunk of text was AddText'd
break; }
// Not the last chunk of text.
Assert(*szText);
// Advance szText pointer
if (!fCurrentIsASCII && *(szText + 1) && GetTrailBytesCount(*szText, pstate->nCodePage)) { // Current byte is a lead-byte of a DBCS character
*pch++ = *szText++; ++cch; } *pch++ = *szText++; ++cch;
// Must do an AddText for the last chunk of text
if(!*szText || cch >= _cchUnicode - 1) fLastChunk = TRUE; } goto CleanUp; } }
if(cch > 0) { if(!_cCell || _iCell < _cCell) AddText(_szUnicode, cch, TRUE); if(fStateChng && ptf) { ptf->iCharRep = tfSave.iCharRep; ptf->sCodePage = tfSave.sCodePage; ptf->fCpgFromSystem = tfSave.fCpgFromSystem; SelectCurrentFont(ptf->sHandle); } }
CleanUp: TRACEERRSZSC("HandleText()", - _ecParseError); return _ecParseError; }
/*
* CRTFRead::HandleUN(pstate) * * @mfunc * Handle run of Unicode characters given by \uN control words */ void CRTFRead::HandleUN( STATE *pstate) { char ach = 0; LONG cch = 0; DWORD ch; // Treat as unsigned quantity
WCHAR * pch = _szUnicode;
_dwMaskCF2 |= CFM2_RUNISDBCS; _CF._dwEffects &= ~CFE_RUNISDBCS;
do { if(_iParam < 0) _iParam = (WORD)_iParam; ch = _iParam; if(pstate->ptf && pstate->ptf->iCharRep == SYMBOL_INDEX) { if(IN_RANGE(0xF000, ch, 0xF0FF))// Compensate for converters
ch -= 0xF000; // that write symbol codes
// up high
else if(ch > 255) // Whoops, RTF is using con-
{ // verted value for symbol:
char ach; // convert back
WCHAR wch = ch; // Avoid endian problems
WCTMB(1252, 0, &wch, 1, &ach, 1, NULL, NULL, NULL); ch = (BYTE)ach; // Review: use CP_ACP??
} } if(IN_RANGE(0x10000, ch, 0x10FFFF)) // Higher-plane char:
{ // Store as Unicode surrogate
ch -= 0x10000; // pair
*pch++ = 0xD800 + (ch >> 10); ch = 0xDC00 + (ch & 0x3FF); cch++; } if(!IN_RANGE(STARTFIELD, ch, NOTACHAR) && // Don't insert 0xFFF9 - 0xFFFF
(!IN_RANGE(0xDC00, ch, 0xDFFF) || // or lone trail surrogate
IN_RANGE(0xD800, cch ? *(pch - 1) : _prg->GetPrevChar(), 0xDBFF))) { *pch++ = ch; cch++; } for(LONG i = pstate->cbSkipForUnicodeMax; i--; ) GetCharEx(); // Eat the \uc N chars
ach = GetChar(); // Get next char
while(IsASCIIEOP(ach)) { ach = GetChar(); if (_ecParseError != ecNoError) return; } if(ach != BSLASH) // Not followed by \, so
{ // not by \uN either
UngetChar(); // Put back BSLASH
break; } ach = GetChar(); if((ach | ' ') != 'u') { UngetChar(2); // Not \u; put back \x
break; } GetParam(GetChar()); // \u so try for _iParam = N
if(!_fParam) // No \uN; put back \u
{ UngetChar(2); break; } } while(cch < _cchUnicode - 2 && _iParam);
AddText(_szUnicode, cch, TRUE, TRUE); }
/*
* CRTFRead::HandleNumber() * * @mfunc * Insert the number _iParam as text. Useful as a workaround for * Word RTF that leaves out the blank between \ltrch and a number. * * @rdesc * EC The error code */ EC CRTFRead::HandleNumber() { if(!_fParam) // Nothing to do
return _ecParseError;
LONG n = _iParam; BYTE *pch = _szText;
if(n < 0) { n = -n; *pch++ = '-'; } for(LONG d = 1; d < n; d *= 10) // d = smallest power of 10 > n
; if(d > n) d /= 10; // d = largest power of 10 < n
while(d) { *pch++ = (WORD)(n/d + '0'); // Store digit
n = n % d; // Setup remainder
d /= 10; } *pch = 0; _token = tokenASCIIText; HandleTextToken(_pstateStackTop); return _ecParseError; }
/*
* CRTFRead::HandleTextToken(pstate) * * @mfunc * Handle tokenText * * @rdesc * EC The error code */ EC CRTFRead::HandleTextToken( STATE *pstate) { COLORREF *pclrf;
switch (pstate->sDest) { case destColorTable: pclrf = _colors.Add(1, NULL); if(!pclrf) goto OutOfRAM;
*pclrf = _fGetColorYet ? RGB(pstate->bRed, pstate->bGreen, pstate->bBlue) : tomAutoColor;
// Prepare for next color table entry
pstate->bRed = pstate->bGreen = pstate->bBlue = 0; _fGetColorYet = FALSE; // in case more "empty" color
break;
case destFontTable: if(!pstate->fRealFontName) { ReadFontName(pstate, _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII); } break;
case destRealFontName: { STATE * const pstatePrev = pstate->pstatePrev;
if(pstatePrev && pstatePrev->sDest == destFontTable) { // Mark previous state so that tagged font name will be ignored
// AROO: Do this before calling ReadFontName so that
// AROO: it doesn't try to match font name
pstatePrev->fRealFontName = TRUE; ReadFontName(pstatePrev, _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII); } break; }
case destFieldInstruction: HandleFieldInstruction(); break;
case destObjectClass: if(StrAlloc(&_prtfObject->szClass, _szText)) goto OutOfRAM; break;
case destObjectName: if(StrAlloc(&_prtfObject->szName, _szText)) goto OutOfRAM; break;
case destStyleSheet: // _szText has style name, e.g., "heading 1"
if(W32->ASCIICompareI(_szText, (unsigned char *)"heading", 7)) { DWORD dwT = (unsigned)(_szText[8] - '0'); if(dwT < NSTYLES) _rgStyles[dwT] = (BYTE)_Style; } break;
case destShapeName: if(pstate->fBackground) { TOKEN token = TokenFindKeyword(_szText, rgShapeKeyword, cShapeKeywords); _bShapeNameIndex = (token != tokenUnknownKeyword) ? (BYTE)token : 0; } break;
case destShapeValue: if(_bShapeNameIndex) { CDocInfo *pDocInfo = _ped->GetDocInfoNC(); BYTE *pch = _szText; BOOL fNegative = FALSE;
if(*pch == '-') { fNegative = TRUE; pch++; } for(_iParam = 0; IsDigit(*pch); pch++) _iParam = _iParam*10 + *pch - '0';
if(fNegative) _iParam = -_iParam;
switch(_bShapeNameIndex) { case shapeFillBackColor: case shapeFillColor: { if(_iParam > 0xFFFFFF) _iParam = 0; if(_bShapeNameIndex == shapeFillColor) pDocInfo->_crColor = (COLORREF)_iParam; else pDocInfo->_crBackColor = (COLORREF)_iParam; if(pDocInfo->_nFillType == -1) pDocInfo->_nFillType = 0; break; } case shapeFillType: // DEBUGGG: more general _nFillType commented out for now
// pDocInfo->_nFillType = _iParam;
if(pDocInfo->_nFillType) CheckNotifyLowFiRTF(TRUE); pDocInfo->_crColor = RGB(255, 255, 255); pDocInfo->_crBackColor = RGB(255, 255, 255); break;
case shapeFillAngle: pDocInfo->_sFillAngle = HIWORD(_iParam); break;
case shapeFillFocus: pDocInfo->_bFillFocus = _iParam; break; } } break;
case destNestTableProps: _ecParseError = ecUnexpectedToken; break;
case destDocumentArea: case destFollowingPunct: case destLeadingPunct: break;
// This has been changed now. We will store the Punct strings as
// raw text strings. So, we don't have to convert them.
// This code is keep here just in case we want to change back.
#if 0
case destDocumentArea: if (_tokenLast != tokenFollowingPunct && _tokenLast != tokenLeadingPunct) { break; } // Else fall thru to
// destFollowingPunct
case destFollowingPunct: case destLeadingPunct: // TODO(BradO): Consider some kind of merging heuristic when
// we paste FE RTF (for lead and follow chars, that is).
if(!(_dwFlags & SFF_SELECTION)) { int cwch = MBTWC(INVALID_CODEPAGE, 0, (char *)_szText, -1, NULL, 0, NULL); Assert(cwch); WCHAR *pwchBuf = (WCHAR *)PvAlloc(cwch * sizeof(WCHAR), GMEM_ZEROINIT);
if(!pwchBuf) goto OutOfRAM;
SideAssert(MBTWC(INVALID_CODEPAGE, 0, (char *)_szText, -1, pwchBuf, cwch, NULL) > 0);
if(pstate->sDest == destFollowingPunct) _ped->SetFollowingPunct(pwchBuf); else { Assert(pstate->sDest == destLeadingPunct); _ped->SetLeadingPunct(pwchBuf); } FreePv(pwchBuf); } break; #endif
default: if(!IsLowMergedCell()) HandleText(_szText, _token == tokenASCIIText ? ALL_ASCII : CONTAINS_NONASCII); } return _ecParseError;
OutOfRAM: _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecNoMemory; return _ecParseError; }
/*
* CRTFRead::AddText(pch, cch, fNumber, fUN) * * @mfunc * Add <p cch> chars of the string <p pch> to the range _prg * * @rdesc * error code placed in _ecParseError */ EC CRTFRead::AddText( WCHAR * pch, //@parm Text to add
LONG cch, //@parm Count of chars to add
BOOL fNumber, //@parm Indicates whether or not to prepend numbering
BOOL fUN) //@parm TRUE means caller is \uN handler
{ TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::AddText");
LONG cchAdded; LONG cchT; STATE * const pstate = _pstateStackTop; WCHAR * szDest; LONG cchMove;
// AROO: No saving state before this point (other than pstate)
// AROO: This would screw recursion below
//AssertSz(pstate, "CRTFRead::AddText: no state");
if((DWORD)cch > _cchMax) { cch = (LONG)_cchMax; _ecParseError = ecTextMax; if(*pch == STARTFIELD) return ecTextMax; // Don't enter partial startfield
}
if(!cch) return _ecParseError;
if(_fStartRow) DelimitRow(szRowStart);
// FUTURE(BradO): This strategy for \pntext is prone to bugs, I believe.
// The recursive call to AddText to add the \pntext will trounce the
// accumulated _CF diffs associated with the text for which AddText is
// called. I believe we should save and restore _CF before and after
// the recursive call to AddText below. Also, it isn't sufficient to
// accumulate bits of \pntext as below, since each bit might be formatted
// with different _CF properties. Instead, we should accumulate a mini-doc
// complete with multiple text, char and para runs (or some stripped down
// version of this strategy).
if(pstate && pstate->sDest == destParaNumText && pch != szRowStart) { szDest = _szNumText + _cchUsedNumText; cch = min(cch, cchMaxNumText - 1 - _cchUsedNumText); if(cch > 0) { MoveMemory((BYTE *)szDest, (BYTE *)pch, cch*2); szDest[cch] = TEXT('\0'); // HandleText() takes sz
_cchUsedNumText += cch; } return ecNoError; }
if(pstate && _cchUsedNumText && fNumber) // Some \pntext available
{ // Bug 3496 - The fNumber flag is an ugly hack to work around RTF
// commonly written by Word. Often, to separate a numbered list
// by page breaks, Word will write:
// <NUMBERING INFO> \page <PARAGRAPH TEXT>
// The paragraph numbering should precede the paragraph text rather
// than the page break. The fNumber flag is set to FALSE when the
// the text being added should not be prepended with the para-numbering,
// as is the case with \page (mapped to FF).
cchT = _cchUsedNumText; _cchUsedNumText = 0; // Prevent infinite recursion
if(!pstate->fBullet) { // If there are any _CF diffs to be injected, they will be trounced
// by this recursive call (see FUTURE comment above).
// Since we didn't save _CF data from calls to AddText with
// pstate->sDest == destParaNumText, we have no way of setting up
// CFE2_RUNISDBCS and CFM2_RUNISDBCS (see FUTURE comment above).
AddText(_szNumText, cchT, FALSE); } else if(_PF.IsListNumbered() && _szNumText[cchT - 1] == TAB) { AssertSz(cchT >= 1, "Invalid numbered text count");
if (cchT > 1) { WCHAR ch = _szNumText[cchT - 2];
_wNumberingStyle = (_wNumberingStyle & ~0x300) | (ch == '.' ? PFNS_PERIOD : ch != ')' ? PFNS_PLAIN : _szNumText[0] == '(' ? PFNS_PARENS : PFNS_PAREN); } else { // There is only a tab so we will assume they meant to
// skip numbering.
_wNumberingStyle = PFNS_NONUMBER; } } }
Apply_CF(); // Apply formatting changes in _CF
// CTxtRange::ReplaceRange will change the character formatting
// and possibly adjust the _rpCF forward if the current char
// formatting includes protection. The changes affected by
// CTxtRange::ReplaceRange are necessary only for non-streaming
// input, so we save state before and restore it after the call
// to CTxtRange::ReplaceRange
LONG iFormatSave = _prg->Get_iCF(); // Save state
QWORD qwFlags = GetCharFlags(pch, cch);
if(fUN && // \uN generated string
(!pstate->ptf || pstate->ptf->sCodePage == INVALID_CODEPAGE || qwFlags & FOTHER || (qwFlags & GetFontSignatureFromFace(_ped->GetCharFormat(iFormatSave)->_iFont)) != qwFlags && (!(qwFlags & (FARABIC | FHEBREW)) || _fNon0CharSet))) { // No charset info for \uN or current charset doesn't support char
cchAdded = _prg->CleanseAndReplaceRange(cch, pch, FALSE, NULL, pch); } else { cchAdded = _prg->ReplaceRange(cch, pch, NULL, SELRR_IGNORE, &cchMove, RR_NO_LP_CHECK);
for(cchT = cch - 1; cchT; cchT--) qwFlags |= GetCharFlags(++pch, cchT);// Note if ComplexScript
_ped->OrCharFlags(qwFlags); } _fBody = TRUE; _prg->Set_iCF(iFormatSave); // Restore state
ReleaseFormats(iFormatSave, -1); Assert(!_prg->GetCch());
if(cchAdded != cch) { Tracef(TRCSEVERR, "AddText(): Only added %d out of %d", cchAdded, cch); _ecParseError = ecGeneralFailure; if(cchAdded <= 0) return _ecParseError; } _cchMax -= cchAdded;
return _ecParseError; }
/*
* CRTFRead::Apply_CF() * * @mfunc * Apply character formatting changes collected in _CF */ void CRTFRead::Apply_CF() { // If any CF changes, update range's _iFormat
if(_dwMaskCF || _dwMaskCF2) { AssertSz(_prg->GetCch() == 0, "CRTFRead::Apply_CF: nondegenerate range");
_prg->SetCharFormat(&_CF, 0, NULL, _dwMaskCF, _dwMaskCF2); _dwMaskCF = 0; _dwMaskCF2 = 0; } }
/*
* CRTFRead::Apply_PF() * * @mfunc * Apply paragraph format given by _PF * * @rdesc * if table row delimiter with nonzero cell count, PF::_iTabs; else -1 */ SHORT CRTFRead::Apply_PF() { LONG cp = _prg->GetCp(); DWORD dwMask = _dwMaskPF; DWORD dwMask2 = _dwMaskPF2; SHORT iTabs = -1; CParaFormat *pPF = &_PF;
if(_pstateStackTop) { Assert(_pstateStackTop->pPF);
// Add PF diffs to *_pstateStackTop->pPF
if(!_pstateStackTop->AddPF(_PF, _bDocType, _dwMaskPF, _dwMaskPF2)) { _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecNoMemory; return -1; } _dwMaskPF = _dwMaskPF2 = 0; // _PF contains delta's from *_pstateStackTop->pPF
pPF = _pstateStackTop->pPF; dwMask = _pstateStackTop->dwMaskPF; Assert(dwMask == PFM_ALLRTF); if(pPF->_wNumbering) { pPF->_wNumberingTab = _pstateStackTop->sIndentNumbering; pPF->_wNumberingStyle = _wNumberingStyle; }
if(_bTableLevelIP + _bTableLevel) { pPF->_wEffects |= PFE_TABLE; dwMask |= PFM_TABLE; pPF->_bTableLevel = min(_bTableLevel + _bTableLevelIP, MAXTABLENEST); } } if(dwMask & PFM_TABSTOPS) { LONG cTab = _cTab; BOOL fIsTableRowDelimiter = pPF->IsTableRowDelimiter(); const LONG *prgxTabs = &_rgxCell[0];
if(fIsTableRowDelimiter) { dwMask2 = PFM2_ALLOWTRDCHANGE; if(!_cCell) { if(_iTabsTable >= 0) // No cells defined here;
{ // Use previous table defs
Assert(_bTableLevel == 1 && !_fNo_iTabsTable); CTabs *pTabs = GetTabsCache()->Elem(_iTabsTable); _cCell = pTabs->_cTab/(CELL_EXTRA + 1); prgxTabs = pTabs->_prgxTabs; } else if(_prg->_rpTX.IsAfterTRD(ENDFIELD) && _iCell) { LONG x = 2000; // Bad RTF: no \cellx's. Def 'em
for(LONG i = 1; i <= _iCell; i++) { HandleCellx(x); x += 2000; } } } cTab = _cCell; } // Caching a tabs array AddRefs the corresponding cached tabs entry.
// Be absolutely sure to release the entry before exiting the routine
// that caches it (see GetTabsCache()->Release at end of this function).
pPF->_bTabCount = cTab; if(fIsTableRowDelimiter) cTab *= CELL_EXTRA + 1; pPF->_iTabs = GetTabsCache()->Cache(prgxTabs, cTab); if(fIsTableRowDelimiter && _bTableLevel == 1) { iTabs = pPF->_iTabs; if(!_fNo_iTabsTable) _iTabsTable = pPF->_iTabs; } AssertSz(!cTab || pPF->_iTabs >= 0, "CRTFRead::Apply_PF: illegal pPF->_iTabs"); }
LONG fCell = (_prg->GetPrevChar() == CELL); LONG fIsAfterTRD = _prg->_rpTX.IsAfterTRD(0);
if(fCell || fIsAfterTRD) // Deal with table delimiters
{ // in hidden text and with
_prg->_rpCF.AdjustBackward(); // custom colors
if(_prg->IsHidden()) { // Turn off hidden text
CCharFormat CF; CF._dwEffects = 0;
_prg->Set(cp, fCell ? 1 : 2); _prg->SetCharFormat(&CF, 0, NULL, CFM_HIDDEN, 0); CheckNotifyLowFiRTF(TRUE); _CF._dwEffects |= CFE_HIDDEN; // Restore CFE_HIDDEN
_dwMaskCF |= CFM_HIDDEN; } _prg->_rpCF.AdjustForward(); if(fIsAfterTRD && _crCellCustom1) { pPF->_crCustom1 = _crCellCustom1; dwMask |= PFM_SHADING; if(_crCellCustom2) { pPF->_crCustom2 = _crCellCustom2; dwMask |= PFM_NUMBERINGSTART | PFM_NUMBERINGSTYLE; } } }
if(dwMask) { _prg->Set(cp, cp - _cpThisPara); // Select back to _cpThisPara
_prg->SetParaFormat(pPF, NULL, dwMask, dwMask2); } _prg->Set(cp, 0); // Restore _prg to an IP
GetTabsCache()->Release(pPF->_iTabs); pPF->_iTabs = -1; return iTabs; }
/*
* CRTFRead::StoreDestination(pstate, dest) * * @mfunc * Store STATE destination if processing the first control word of a group * * @rdesc * TRUE iff destination stored */ BOOL CRTFRead::StoreDestination( STATE * pstate, LONG dest) { if(pstate && _f1stControlWord) { pstate->sDest = (SHORT)dest; return TRUE; } return FALSE; }
/*
* CRTFRead::SetBorderParm(&Parm, Value) * * @mfunc * Set the border pen width in half points for the current border * (_bBorder) */ void CRTFRead::SetBorderParm( WORD& Parm, LONG Value) { Assert(_bBorder <= 3);
Value = min(Value, 15); Value = max(Value, 0); Parm &= ~(0xF << 4*_bBorder); Parm |= Value << 4*_bBorder; _dwMaskPF |= PFM_BORDER; }
/*
* CRTFRead::HandleToken() * * @mfunc * Grand switch board that handles all tokens. Switches on _token * * @rdesc * EC The error code * * @comm * Token values are chosen contiguously (see tokens.h and tokens.c) to * encourage the compiler to use a jump table. The lite-RTF keywords * come first, so that an optimized OLE-free version works well. Some * groups of keyword tokens are ordered so as to simplify the code, e.g, * those for font family names, CF effects, and paragraph alignment. */ EC CRTFRead::HandleToken() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::HandleToken");
BOOL f1stControlWord = FALSE;// Default not 1st control word of current group
DWORD dwT; // Temporary DWORD
LONG dy, i; LONG iParam = _iParam; const CCharFormat * pCF; STATE * pstate = _pstateStackTop; TEXTFONT * ptf; WORD wT; // Temporary WORD
if(!pstate && _token != tokenStartGroup || IN_RANGE(tokenPicFirst, _token, tokenObjLast) && !_prtfObject) { abort: _ecParseError = ecAbort; return ecAbort; } switch (_token) { case tokenURtf: // \urtf N - Preferred RE format
PARSERCOVERAGE_CASE(); // Currently we ignore the N
_dwFlags &= 0xFFFF; // Kill possible codepage
_dwFlags |= SF_USECODEPAGE | (CP_UTF8 << 16); // Save bit for Asserts
pstate->SetCodePage(CP_UTF8); goto rtf;
case tokenPocketWord: // \pwd N - Pocket Word
_dwFlags |= SFF_PWD;
case tokenRtf: // \rtf N - Backward compatible
PARSERCOVERAGE_CASE(); rtf: if(pstate->pstatePrev) goto abort; // Can't handle nested rtf
pstate->sDest = destRTF; Assert(pstate->nCodePage == INVALID_CODEPAGE || pstate->nCodePage == (int)(_dwFlags >> 16) && (_dwFlags & SF_USECODEPAGE));
if(!_fonts.Count() && !_fonts.Add(1, NULL)) // If can't add a font,
goto OutOfRAM; // report the bad news
_sDefaultFont = 0; // Set up valid default font
ptf = _fonts.Elem(0); pstate->ptf = ptf; // Get char set, pitch, family
pCF = _prg->GetCF(); // from current range font
ptf->iCharRep = pCF->_iCharRep; // These are guaranteed OK
ptf->bPitchAndFamily = pCF->_bPitchAndFamily; ptf->sCodePage = (SHORT)CodePageFromCharRep(pCF->_iCharRep); wcscpy(ptf->szName, GetFontName(pCF->_iFont)); ptf->fNameIsDBCS = (pCF->_dwEffects & CFE_FACENAMEISDBCS) != 0; pstate->cbSkipForUnicodeMax = iUnicodeCChDefault; break;
case tokenViewKind: // \viewkind N
if(!(_dwFlags & SFF_SELECTION) && IsUTF8)// RTF applies to document:
_ped->SetViewKind(iParam); // For now, only do for \urtf
break; // (need to work some more
// on OutlineView)
case tokenViewScale: // \viewscale N
if(_dwFlags & SFF_PERSISTVIEWSCALE && !(_dwFlags & SFF_SELECTION)) // RTF applies to document:
_ped->SetViewScale(iParam); break;
case tokenCharacterDefault: // \plain
PARSERCOVERAGE_CASE(); SetPlain(pstate); break;
case tokenCharSetAnsi: // \ansi
PARSERCOVERAGE_CASE(); _iCharRep = ANSI_INDEX; break;
case tokenMac: // \mac
_fMac = TRUE; break;
case tokenDefaultLanguage: // \deflang
PARSERCOVERAGE_CASE(); _sDefaultLanguage = (SHORT)iParam; break;
case tokenDefaultLanguageFE: // \deflangfe
PARSERCOVERAGE_CASE(); _sDefaultLanguageFE = (SHORT)iParam; break;
case tokenDefaultTabWidth: // \deftab
PARSERCOVERAGE_CASE(); _sDefaultTabWidth = (SHORT)iParam; break;
//--------------------------- Font Control Words -------------------------------
case tokenDefaultFont: // \deff N
PARSERCOVERAGE_CASE(); if(iParam >= 0) { if(!_fonts.Count() && !_fonts.Add(1, NULL)) // If can't add a font,
goto OutOfRAM; // report the bad news
_fonts.Elem(0)->sHandle = _sDefaultFont = (SHORT)iParam; } TRACEERRSZSC("tokenDefaultFont: Negative value", iParam); break;
case tokenDefaultBiDiFont: // \adeff N
PARSERCOVERAGE_CASE(); if(iParam >=0 && _fonts.Count() == 1) { if(!_fonts.Add(1, NULL)) goto OutOfRAM; _fonts.Elem(1)->sHandle = _sDefaultBiDiFont = (SHORT)iParam; } TRACEERRSZSC("tokenDefaultBiDiFont: Negative value", iParam); break;
case tokenFontTable: // \fonttbl
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destFontTable); pstate->ptf = NULL; break;
case tokenFontFamilyBidi: // \fbidi
case tokenFontFamilyTechnical: // \ftech
case tokenFontFamilyDecorative: // \fdecor
case tokenFontFamilyScript: // \fscript
case tokenFontFamilyModern: // \fmodern
case tokenFontFamilySwiss: // \fswiss
case tokenFontFamilyRoman: // \froman
case tokenFontFamilyDefault: // \fnil
PARSERCOVERAGE_CASE(); AssertSz(tokenFontFamilyRoman - tokenFontFamilyDefault == 1, "CRTFRead::HandleToken: invalid token definition");
if(pstate->ptf) { pstate->ptf->bPitchAndFamily = (BYTE)((_token - tokenFontFamilyDefault) << 4 | (pstate->ptf->bPitchAndFamily & 0xF));
// Setup SYMBOL_CHARSET charset for \ftech if there isn't any charset info
if(tokenFontFamilyTechnical == _token && pstate->ptf->iCharRep == DEFAULT_INDEX) pstate->ptf->iCharRep = SYMBOL_INDEX; } break;
case tokenPitch: // \fprq
PARSERCOVERAGE_CASE(); if(pstate->ptf) pstate->ptf->bPitchAndFamily = (BYTE)(iParam | (pstate->ptf->bPitchAndFamily & 0xF0)); break;
case tokenAnsiCodePage: // \ansicpg
PARSERCOVERAGE_CASE(); #if !defined(NOFULLDEBUG) && defined(DEBUG)
if(_fSeenFontTable && _nCodePage == INVALID_CODEPAGE) TRACEWARNSZ("CRTFRead::HandleToken(): Found an \ansicpgN tag after " "the font table. Should have code to fix-up " "converted font names and document text."); #endif
if(!(_dwFlags & SF_USECODEPAGE)) { _nCodePage = iParam; pstate->SetCodePage(iParam); } Assert(!IsUTF8 || pstate->nCodePage == CP_UTF8); break;
case tokenCodePage: // \cpg
PARSERCOVERAGE_CASE(); pstate->SetCodePage(iParam); if(pstate->sDest == destFontTable && pstate->ptf) { pstate->ptf->sCodePage = (SHORT)iParam; pstate->ptf->iCharRep = CharRepFromCodePage(iParam);
// If a document-level code page has not been specified,
// grab this from the first font table entry containing a
// \fcharsetN or \cpgN
if(_nCodePage == INVALID_CODEPAGE) _nCodePage = iParam; } break;
case tokenCharSet: // \fcharset N
PARSERCOVERAGE_CASE(); if(pstate->ptf) { pstate->ptf->iCharRep = CharRepFromCharSet((BYTE)iParam); pstate->ptf->sCodePage = (SHORT)CodePageFromCharRep(pstate->ptf->iCharRep); pstate->SetCodePage(pstate->ptf->sCodePage);
// If a document-level code page has not been specified,
// grab this from the first font table entry containing a
// \fcharsetN or \cpgN
if (pstate->nCodePage != CP_SYMBOL && _nCodePage == INVALID_CODEPAGE) { _nCodePage = pstate->nCodePage; } if(IsRTLCharSet(iParam)) { if(_sDefaultBiDiFont == -1) _sDefaultBiDiFont = pstate->ptf->sHandle;
if(!IsRTLCharRep(_iCharRepBiDi)) _iCharRepBiDi = pstate->ptf->iCharRep; } _fCharSet = TRUE; if(iParam) _fNon0CharSet = TRUE; // Not HTML converter
} break;
case tokenRealFontName: // \fname
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destRealFontName); break;
case tokenAssocFontSelect: // \af N
PARSERCOVERAGE_CASE(); pstate->rgDefFont[pstate->iDefFont].sHandle = iParam; iParam = 0; // Fall thru to \afs N to 0 sSize
case tokenAssocFontSize: // \afs N
PARSERCOVERAGE_CASE(); pstate->rgDefFont[pstate->iDefFont].sSize = iParam; break;
case tokenFontSelect: // \f N
PARSERCOVERAGE_CASE(); if(iParam == -1) // Can't handle this bizarre choice
goto skip_group;
if(pstate->sDest == destFontTable) // Building font table
{ if(iParam == _sDefaultFont) { _fReadDefFont = TRUE; ptf = _fonts.Elem(0); } else if(iParam == _sDefaultBiDiFont) ptf = _fonts.Elem(1);
else if(!(ptf =_fonts.Add(1,NULL))) // Make room in font table for
{ // font to be parsed
OutOfRAM: _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecNoMemory; break; } pstate->ptf = ptf; ptf->sHandle = (SHORT)iParam; // Save handle
ptf->szName[0] = '\0'; // Start with null string
ptf->bPitchAndFamily = 0; ptf->fNameIsDBCS = FALSE; ptf->sCodePage = INVALID_CODEPAGE; ptf->fCpgFromSystem = FALSE; ptf->iCharRep = DEFAULT_INDEX; } else if(_fonts.Count() && pstate->sDest != destStyleSheet) // Font switch in text
{ SHORT idx = DEFFONT_LTRCH;
SelectCurrentFont(iParam); if(IsRTLCharRep(pstate->ptf->iCharRep)) { _iCharRepBiDi = pstate->ptf->iCharRep; idx = DEFFONT_RTLCH; if(pstate->iDefFont == DEFFONT_LTRCH) pstate->iDefFont = DEFFONT_RTLCH; } pstate->rgDefFont[idx].sHandle = iParam; pstate->rgDefFont[idx].sSize = 0; } break;
case tokenDBChars: // \dbch
case tokenHIChars: // \hich
case tokenLOChars: // \loch
case tokenRToLChars: // \rtlch
case tokenLToRChars: // \ltrch
pstate->iDefFont = _token - tokenLToRChars + DEFFONT_LTRCH; if(!IN_RANGE(DEFFONT_LTRCH, pstate->iDefFont, DEFFONT_RTLCH)) break; i = pstate->rgDefFont[pstate->iDefFont].sHandle; if(i == -1) break; SelectCurrentFont(i); HandleNumber(); // Fix Word \ltrchN bug
iParam = pstate->rgDefFont[pstate->iDefFont].sSize; if(!iParam) break; // No \afs N value specified
// Fall thru to \fs N
case tokenFontSize: // \fs N
PARSERCOVERAGE_CASE(); pstate->rgDefFont[pstate->iDefFont].sSize = iParam; _CF._yHeight = PointsToFontHeight(iParam); // Convert font size in
_dwMaskCF |= CFM_SIZE; // half points to logical
break; // units
// NOTE: \*\fontemb and \*\fontfile are discarded. The font mapper will
// have to do the best it can given font name, family, and pitch.
// Embedded fonts are particularly nasty because legal use should
// only support read-only which parser cannot enforce.
case tokenLanguage: // \lang N
PARSERCOVERAGE_CASE(); _CF._lcid = MAKELCID(iParam, SORT_DEFAULT); _dwMaskCF |= CFM_LCID;
if(W32->IsBiDiLcid(_CF._lcid)) { _iCharRepBiDi = CharRepFromLID(iParam); if(pstate->iDefFont == DEFFONT_LTRCH) // Workaround Word 10 bug
pstate->iDefFont = DEFFONT_RTLCH; } break;
//-------------------------- Color Control Words ------------------------------
case tokenColorTable: // \colortbl
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destColorTable); _fGetColorYet = FALSE; break;
case tokenColorRed: // \red
PARSERCOVERAGE_CASE(); pstate->bRed = (BYTE)iParam; _fGetColorYet = TRUE; break;
case tokenColorGreen: // \green
PARSERCOVERAGE_CASE(); pstate->bGreen = (BYTE)iParam; _fGetColorYet = TRUE; break;
case tokenColorBlue: // \blue
PARSERCOVERAGE_CASE(); pstate->bBlue = (BYTE)iParam; _fGetColorYet = TRUE; break;
case tokenColorForeground: // \cf
PARSERCOVERAGE_CASE(); _CF._crTextColor = GetColor(CFM_COLOR); break;
case tokenColorBackground: // \highlight
PARSERCOVERAGE_CASE(); _CF._crBackColor = GetColor(CFM_BACKCOLOR); break;
case tokenExpand: // \expndtw N
PARSERCOVERAGE_CASE(); _CF._sSpacing = (SHORT) iParam; _dwMaskCF |= CFM_SPACING; break;
case tokenCharStyle: // \cs N
PARSERCOVERAGE_CASE(); /* FUTURE (alexgo): we may want to support character styles
in some future version. _CF._sStyle = (SHORT)iParam; _dwMaskCF |= CFM_STYLE; */
if(pstate->sDest == destStyleSheet) goto skip_group; break;
case tokenAnimText: // \animtext N
PARSERCOVERAGE_CASE(); _CF._bAnimation = (BYTE)iParam; _dwMaskCF |= CFM_ANIMATION; CheckNotifyLowFiRTF(TRUE); break;
case tokenKerning: // \kerning N
PARSERCOVERAGE_CASE(); _CF._wKerning = (WORD)(10 * iParam); // Convert to twips
_dwMaskCF |= CFM_KERNING; break;
case tokenHorzInVert: // \horzvert N
PARSERCOVERAGE_CASE(); CheckNotifyLowFiRTF(TRUE); break;
case tokenFollowingPunct: // \*\fchars
PARSERCOVERAGE_CASE(); if(StoreDestination(pstate, destFollowingPunct)) { char *pwchBuf=NULL; if (ReadRawText((_dwFlags & SFF_SELECTION) ? NULL : &pwchBuf) && pwchBuf) { if (_ped->SetFollowingPunct(pwchBuf) != NOERROR) // Store this buffer inside doc
FreePv(pwchBuf); } else if (pwchBuf) FreePv(pwchBuf); } break;
case tokenLeadingPunct: // \*\lchars
PARSERCOVERAGE_CASE(); if(StoreDestination(pstate, destLeadingPunct)) { char *pwchBuf=NULL; if (ReadRawText((_dwFlags & SFF_SELECTION) ? NULL : &pwchBuf) && pwchBuf) { if (_ped->SetLeadingPunct(pwchBuf) != NOERROR) // Store this buffer inside doc
FreePv(pwchBuf); } else if (pwchBuf) FreePv(pwchBuf); } break;
case tokenDocumentArea: // \info
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destDocumentArea); break;
case tokenVerticalRender: // \vertdoc
PARSERCOVERAGE_CASE(); TRACEINFOSZ("Vertical" ); if (!(_dwFlags & SFF_SELECTION)) HandleSTextFlow(1); break;
case tokenSTextFlow: // \stextflow N
PARSERCOVERAGE_CASE(); TRACEINFOSZ("STextFlow" ); if (!(_dwFlags & SFF_SELECTION) && !_ped->Get10Mode()) HandleSTextFlow(iParam); break;
#ifdef FE
USHORT usPunct; // Used for FE word breaking
case tokenNoOverflow: // \nooverflow
PARSERCOVERAGE_CASE(); TRACEINFOSZ("No Overflow"); usPunct = ~WBF_OVERFLOW; goto setBrkOp;
case tokenNoWordBreak: // \nocwrap
PARSERCOVERAGE_CASE(); TRACEINFOSZ("No Word Break" ); usPunct = ~WBF_WORDBREAK; goto setBrkOp;
case tokenNoWordWrap: // \nowwrap
PARSERCOVERAGE_CASE(); TRACEINFOSZ("No Word Word Wrap" ); usPunct = ~WBF_WORDWRAP;
setBrkOp: if(!(_dwFlags & fRTFFE)) { usPunct &= UsVGetBreakOption(_ped->lpPunctObj); UsVSetBreakOption(_ped->lpPunctObj, usPunct); } break;
case tokenHorizontalRender: // \horzdoc
PARSERCOVERAGE_CASE(); TRACEINFOSZ("Horizontal" ); if(pstate->sDest == destDocumentArea && !(_dwFlags & fRTFFE)) _ped->fModeDefer = FALSE; break;
#endif
//-------------------- Character Format Control Words -----------------------------
case tokenUnderlineThickLongDash: // \ulthldash [18]
case tokenUnderlineThickDotted: // \ulthd [17]
case tokenUnderlineThickDashDotDot: // \ulthdashdd [16]
case tokenUnderlineThickDashDot: // \ulthdashd [15]
case tokenUnderlineThickDash: // \ulthdash [14]
case tokenUnderlineLongDash: // \ulldash [13]
case tokenUnderlineHeavyWave: // \ulhwave [12]
case tokenUnderlineDoubleWave: // \ululdbwave [11]
case tokenUnderlineHairline: // \ulhair [10]
case tokenUnderlineThick: // \ulth [9]
case tokenUnderlineDouble: // \uldb [3]
case tokenUnderlineWord: // \ulw [2]
// CheckNotifyLowFiRTF();
case tokenUnderlineWave: // \ulwave [8]
case tokenUnderlineDashDotDotted: // \uldashdd [7]
case tokenUnderlineDashDotted: // \uldashd [6]
case tokenUnderlineDash: // \uldash [5]
case tokenUnderlineDotted: // \uld [4]
PARSERCOVERAGE_CASE(); _CF._bUnderlineType = (BYTE)(_token - tokenUnderlineWord + 2); _token = tokenUnderline; // CRenderer::RenderUnderline()
goto under; // reveals which of these are
// rendered specially
case tokenUnderline: // \ul [Effect 4]
PARSERCOVERAGE_CASE(); // (see handleCF)
_CF._bUnderlineType = CFU_UNDERLINE; under: _dwMaskCF |= CFM_UNDERLINETYPE; goto handleCF;
case tokenDeleted: // \deleted
PARSERCOVERAGE_CASE(); _dwMaskCF2 = CFM2_DELETED; dwT = CFE_DELETED; goto hndlCF;
// These effects are turned on if their control word parameter is missing
// or nonzero. They are turned off if the parameter is zero. This
// behavior is usually identified by an asterisk (*) in the RTF spec.
// The code uses fact that CFE_xxx = CFM_xxx
case tokenImprint: // \impr [1000]
case tokenEmboss: // \embo [800]
case tokenShadow: // \shad [400]
case tokenOutline: // \outl [200]
case tokenSmallCaps: // \scaps [40]
CheckNotifyLowFiRTF();
handleCF: case tokenRevised: // \revised [4000]
case tokenDisabled: // \disabled [2000]
case tokenHiddenText: // \v [100]
case tokenCaps: // \caps [80]
case tokenLink: // \link [20]
case tokenProtect: // \protect [10]
case tokenStrikeOut: // \strike [8]
case tokenItalic: // \i [2]
case tokenBold: // \b [1]
PARSERCOVERAGE_CASE(); dwT = 1 << (_token - tokenBold); // Generate effect mask
_dwMaskCF |= dwT; hndlCF: _CF._dwEffects &= ~dwT; // Default attribute off
if(!_fParam || _iParam) // Effect is on
_CF._dwEffects |= dwT; // In either case, the effect
break; // is defined
case tokenStopUnderline: // \ulnone
PARSERCOVERAGE_CASE(); _CF._dwEffects &= ~CFE_UNDERLINE; // Kill all underlining
_dwMaskCF |= CFM_UNDERLINE; break;
case tokenRevAuthor: // \revauth N
PARSERCOVERAGE_CASE(); /* FUTURE: (alexgo) this doesn't work well now since we don't support
revision tables. We may want to support this better in the future. So what we do now is the 1.0 technique of using a color for the author */ if(iParam > 0) { _CF._dwEffects &= ~CFE_AUTOCOLOR; _dwMaskCF |= CFM_COLOR; _CF._crTextColor = rgcrRevisions[(iParam - 1) & REVMASK]; } break;
case tokenUp: // \up
PARSERCOVERAGE_CASE(); dy = 10; goto StoreOffset;
case tokenDown: // \down
PARSERCOVERAGE_CASE(); dy = -10;
StoreOffset: if(!_fParam) iParam = dyDefaultSuperscript; _CF._yOffset = iParam * dy; // Half points->twips
_dwMaskCF |= CFM_OFFSET; break;
case tokenSuperscript: // \super
PARSERCOVERAGE_CASE(); dwT = CFE_SUPERSCRIPT; goto SetSubSuperScript;
case tokenSubscript: // \sub
PARSERCOVERAGE_CASE(); dwT = CFE_SUBSCRIPT; goto SetSubSuperScript;
case tokenNoSuperSub: // \nosupersub
PARSERCOVERAGE_CASE(); dwT = 0; SetSubSuperScript: _dwMaskCF |= (CFE_SUPERSCRIPT | CFE_SUBSCRIPT); _CF._dwEffects &= ~(CFE_SUPERSCRIPT | CFE_SUBSCRIPT); _CF._dwEffects |= dwT; break;
//--------------------- Paragraph Control Words -----------------------------
case tokenStyleSheet: // \stylesheet
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destStyleSheet); _Style = 0; // Default normal style
break;
case tokenTabBar: // \tb
PARSERCOVERAGE_CASE(); _bTabType = PFT_BAR; // Fall thru to \tx
case tokenTabPosition: // \tx
PARSERCOVERAGE_CASE(); if(_cTab < MAX_TAB_STOPS && (unsigned)iParam < 0x1000000) { _rgxCell[_cTab++] = GetTabPos(iParam) + (_bTabType << 24) + (_bTabLeader << 28); _dwMaskPF |= PFM_TABSTOPS; } _cCell = 0; // Invalidate _rgxCell array
break; // for table purposes
case tokenDecimalTab: // \tqdec
case tokenFlushRightTab: // \tqr
case tokenCenterTab: // \tqc
PARSERCOVERAGE_CASE(); _bTabType = (BYTE)(_token - tokenCenterTab + PFT_CENTER); break;
case tokenTabLeaderEqual: // \tleq
case tokenTabLeaderThick: // \tlth
case tokenTabLeaderUnderline: // \tlul
case tokenTabLeaderHyphen: // \tlhyph
CheckNotifyLowFiRTF(); case tokenTabLeaderDots: // \tldot
PARSERCOVERAGE_CASE(); _bTabLeader = (BYTE)(_token - tokenTabLeaderDots + PFTL_DOTS); break;
// The following need to be kept in sync with PFE_xxx
case tokenRToLPara: // \rtlpar
_ped->OrCharFlags(FRTL);
case tokenCollapsed: // \collapsed
case tokenSideBySide: // \sbys
case tokenHyphPar: // \hyphpar
case tokenNoWidCtlPar: // \nowidctlpar
case tokenNoLineNumber: // \noline
case tokenPageBreakBefore: // \pagebb
case tokenKeepNext: // \keepn
case tokenKeep: // \keep
PARSERCOVERAGE_CASE(); wT = (WORD)(1 << (_token - tokenRToLPara)); _PF._wEffects |= wT; _dwMaskPF |= (wT << 16); break;
case tokenLToRPara: // \ltrpar
PARSERCOVERAGE_CASE(); _PF._wEffects &= ~PFE_RTLPARA; _dwMaskPF |= PFM_RTLPARA; break;
case tokenLineSpacing: // \sl N
PARSERCOVERAGE_CASE(); _PF._dyLineSpacing = abs(iParam); _PF._bLineSpacingRule // Handle nonmultiple rules
= (BYTE)(!iParam || iParam == 1000 ? 0 : (iParam > 0) ? tomLineSpaceAtLeast : tomLineSpaceExactly); // \slmult can change (has to
_dwMaskPF |= PFM_LINESPACING; // follow if it appears)
break;
case tokenDropCapLines: // \dropcapliN
if(_PF._bLineSpacingRule == tomLineSpaceExactly) // Don't chop off
_PF._bLineSpacingRule = tomLineSpaceAtLeast; // drop cap
_fBody = TRUE; break;
case tokenLineSpacingRule: // \slmult N
PARSERCOVERAGE_CASE(); if(iParam) { // It's multiple line spacing
_PF._bLineSpacingRule = tomLineSpaceMultiple; _PF._dyLineSpacing /= 12; // RE line spacing multiple is
_dwMaskPF |= PFM_LINESPACING; // given in 20ths of a line,
} // while RTF uses 240ths
break;
case tokenSpaceBefore: // \sb N
PARSERCOVERAGE_CASE(); _PF._dySpaceBefore = iParam; _dwMaskPF |= PFM_SPACEBEFORE; break;
case tokenSpaceAfter: // \sa N
PARSERCOVERAGE_CASE(); _PF._dySpaceAfter = iParam; _dwMaskPF |= PFM_SPACEAFTER; break;
case tokenStyle: // \s N
PARSERCOVERAGE_CASE(); _Style = iParam; // Save it in case in StyleSheet
if(pstate->sDest != destStyleSheet) { // Select possible heading level
_PF._sStyle = STYLE_NORMAL; // Default Normal style
_PF._bOutlineLevel |= 1;
for(i = 0; i < NSTYLES && iParam != _rgStyles[i]; i++) ; // Check for heading style
if(i < NSTYLES) // Found one
{ _PF._sStyle = (SHORT)(-i - 1); // Store desired heading level
_PF._bOutlineLevel = (BYTE)(2*(i-1));// Update outline level for
} // nonheading styles
_dwMaskPF |= PFM_ALLRTF; } break;
case tokenIndentFirst: // \fi N
PARSERCOVERAGE_CASE(); _PF._dxStartIndent += _PF._dxOffset // Cancel current offset
+ iParam; // and add in new one
_PF._dxOffset = -iParam; // Offset for all but 1st line
// = -RTF_FirstLineIndent
_dwMaskPF |= (PFM_STARTINDENT | PFM_OFFSET); break;
case tokenIndentLeft: // \li N
case tokenIndentRight: // \ri N
PARSERCOVERAGE_CASE(); // AymanA: For RtL para indents has to be flipped.
Assert(PFE_RTLPARA == 0x0001); if((_token == tokenIndentLeft) ^ (_PF.IsRtlPara())) { _PF._dxStartIndent = iParam - _PF._dxOffset; _dwMaskPF |= PFM_STARTINDENT; } else { _PF._dxRightIndent = iParam; _dwMaskPF |= PFM_RIGHTINDENT; } break;
case tokenAlignLeft: // \ql
case tokenAlignRight: // \qr
case tokenAlignCenter: // \qc
case tokenAlignJustify: // \qj
PARSERCOVERAGE_CASE(); _PF._bAlignment = (WORD)(_token - tokenAlignLeft + PFA_LEFT); _dwMaskPF |= PFM_ALIGNMENT; break;
case tokenBorderOutside: // \brdrbar
case tokenBorderBetween: // \brdrbtw
case tokenBorderShadow: // \brdrsh
PARSERCOVERAGE_CASE(); _PF._dwBorderColor |= 1 << (_token - tokenBorderShadow + 20); _dwBorderColors = _PF._dwBorderColor; break;
// Paragraph and cell border segments
case tokenBox: // \box
PARSERCOVERAGE_CASE(); CheckNotifyLowFiRTF(); _PF._wEffects |= PFE_BOX; _dwMaskPF |= PFM_BOX; _bBorder = 0; // Store parms as if for
break; // \brdrt
case tokenBorderBottom: // \brdrb
case tokenBorderRight: // \brdrr
case tokenBorderTop: // \brdrt
if((rgKeyword[_iKeyword].szKeyword[0] | 0x20) != 't') CheckNotifyLowFiRTF(); case tokenBorderLeft: // \brdrl
case tokenCellBorderBottom: // \clbrdrb
case tokenCellBorderRight: // \clbrdrr
case tokenCellBorderTop: // \clbrdrt
case tokenCellBorderLeft: // \clbrdrl
PARSERCOVERAGE_CASE(); _bBorder = (BYTE)(_token - tokenBorderLeft); break;
// Paragraph border styles
case tokenBorderTriple: // \brdrtriple
case tokenBorderDoubleThick: // \brdrth
case tokenBorderSingleThick: // \brdrs
case tokenBorderHairline: // \brdrhair
case tokenBorderDot: // \brdrdot
case tokenBorderDouble: // \brdrdb
case tokenBorderDashSmall: // \brdrdashsm
case tokenBorderDash: // \brdrdash
PARSERCOVERAGE_CASE(); if(_bBorder < 4) // Only for paragraphs
SetBorderParm(_PF._wBorders, _token - tokenBorderDash); break;
case tokenBorderColor: // \brdrcf
PARSERCOVERAGE_CASE(); if(_bBorder < 4) // Only for paragraphs
{ iParam = GetStandardColorIndex(); _PF._dwBorderColor &= ~(0x1F << (5*_bBorder)); _PF._dwBorderColor |= iParam << (5*_bBorder); _dwBorderColors = _PF._dwBorderColor; } else // Cell borders
_dwCellColors |= GetCellColorIndex() << (5*(_bBorder - 4)); break;
case tokenBorderWidth: // \brdrw
PARSERCOVERAGE_CASE(); // Store width in half pts
// iParam is in twips
if(_bBorder < 4) // Paragraphs
{ iParam = TwipsToQuarterPoints(iParam); SetBorderParm(_PF._wBorderWidth, iParam); } else // Table cells
{ iParam = CheckTwips(iParam); _dwCellBrdrWdths |= iParam << 8*(_bBorder - 4); } break;
case tokenBorderSpace: // \brsp
PARSERCOVERAGE_CASE(); // Space is in pts
if(_bBorder < 4) // Only for paragraphs
SetBorderParm(_PF._wBorderSpace, iParam/20);// iParam is in twips
break;
// Paragraph shading
case tokenBckgrndVert: // \bgvert
case tokenBckgrndHoriz: // \bghoriz
case tokenBckgrndFwdDiag: // \bgfdiag
case tokenBckgrndDrkVert: // \bgdkvert
case tokenBckgrndDrkHoriz: // \bgdkhoriz
case tokenBckgrndDrkFwdDiag: // \bgdkfdiag
case tokenBckgrndDrkDiagCross: // \bgdkdcross
case tokenBckgrndDrkCross: // \bgdkcross
case tokenBckgrndDrkBckDiag: // \bgdkbdiag
case tokenBckgrndDiagCross: // \bgdcross
case tokenBckgrndCross: // \bgcross
case tokenBckgrndBckDiag: // \bgbdiag
PARSERCOVERAGE_CASE(); _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0xFFC0) | (_token - tokenBckgrndBckDiag + 1)); _dwMaskPF |= PFM_SHADING; break;
case tokenColorBckgrndPat: // \cbpat
PARSERCOVERAGE_CASE(); iParam = GetStandardColorIndex(); _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0x07FF) | (iParam << 11)); _dwMaskPF |= PFM_SHADING; break;
case tokenColorForgrndPat: // \cfpat
PARSERCOVERAGE_CASE(); iParam = GetStandardColorIndex(); _PF._wShadingStyle = (WORD)((_PF._wShadingStyle & 0xF83F) | (iParam << 6)); _dwMaskPF |= PFM_SHADING; break;
case tokenShading: // \shading
PARSERCOVERAGE_CASE(); _PF._wShadingWeight = (WORD)iParam; _dwMaskPF |= PFM_SHADING; break;
// Paragraph numbering
case tokenParaNum: // \pn
PARSERCOVERAGE_CASE(); if(StoreDestination(pstate, destParaNumbering)) { pstate->fBullet = FALSE; _PF._wNumberingStart = 1; _dwMaskPF |= PFM_NUMBERINGSTART; } break;
case tokenParaNumIndent: // \pnindent N
PARSERCOVERAGE_CASE(); if(pstate->sDest == destParaNumbering) pstate->sIndentNumbering = (SHORT)iParam; break;
case tokenParaNumStart: // \pnstart N
PARSERCOVERAGE_CASE(); if(pstate->sDest == destParaNumbering) { _PF._wNumberingStart = (WORD)iParam; _dwMaskPF |= PFM_NUMBERINGSTART; } break;
case tokenParaNumCont: // \pnlvlcont
PARSERCOVERAGE_CASE(); _prg->_rpPF.AdjustBackward(); // Maintain numbering mode
_PF._wNumbering = _prg->GetPF()->_wNumbering; _prg->_rpPF.AdjustForward(); _wNumberingStyle = PFNS_NONUMBER; // Signal no number
_dwMaskPF |= PFM_NUMBERING; // Note: can be new para with
pstate->fBullet = TRUE; // its own indents
break;
case tokenParaNumBody: // \pnlvlbody
PARSERCOVERAGE_CASE(); _wNumberingStyle = PFNS_PAREN; _token = tokenParaNumDecimal; // Default to decimal
goto setnum; case tokenParaNumBullet: // \pnlvlblt
_wNumberingStyle = 0; // Reset numbering styles
goto setnum;
case tokenParaNumDecimal: // \pndec
case tokenParaNumLCLetter: // \pnlcltr
case tokenParaNumUCLetter: // \pnucltr
case tokenParaNumLCRoman: // \pnlcrm
case tokenParaNumUCRoman: // \pnucrm
PARSERCOVERAGE_CASE(); if(_PF._wNumbering == PFN_BULLET && pstate->fBullet) break; // Ignore above for bullets
setnum: if(pstate->sDest == destParaNumbering) { _PF._wNumbering = (WORD)(PFN_BULLET + _token - tokenParaNumBullet); _dwMaskPF |= PFM_NUMBERING; pstate->fBullet = TRUE; // We do bullets, so don't
} // output the \pntext group
break;
case tokenParaNumText: // \pntext
PARSERCOVERAGE_CASE(); // Throw away previously read paragraph numbering and use
// the most recently read to apply to next run of text.
StoreDestination(pstate, destParaNumText); _cchUsedNumText = 0; break;
case tokenParaNumAlignCenter: // \pnqc
case tokenParaNumAlignRight: // \pnqr
PARSERCOVERAGE_CASE(); _wNumberingStyle = (_wNumberingStyle & ~3) | _token - tokenParaNumAlignCenter + 1; break;
case tokenPictureQuickDraw: // \macpict
case tokenPictureOS2Metafile: // \pmmetafile
CheckNotifyLowFiRTF(TRUE);
case tokenParaNumAfter: // \pntxta
case tokenParaNumBefore: // \pntxtb
PARSERCOVERAGE_CASE();
skip_group: if(!SkipToEndOfGroup()) { // During \fonttbl processing, we may hit unknown destinations,
// e.g., \panose, that cause the HandleEndGroup to select the
// default font, which may not be defined yet. So, we change
// sDest to avoid this problem.
if(pstate->sDest == destFontTable || pstate->sDest == destStyleSheet) pstate->sDest = destNULL; HandleEndGroup(); } break;
// Tables
case tokenInTable: // \intbl
PARSERCOVERAGE_CASE(); if(pstate->sDest != destRTF && pstate->sDest != destFieldResult && pstate->sDest != destParaNumText) { _ecParseError = ecUnexpectedToken; break; } if(!_iCell && !_bTableLevel) DelimitRow(szRowStart); // Start row
break;
case tokenNestCell: // \nestcell
case tokenCell: // \cell
PARSERCOVERAGE_CASE(); HandleCell(); break;
case tokenRowHeight: // \trrh N
PARSERCOVERAGE_CASE(); _dyRow = iParam; break; case tokenCellHalfGap: // \trgaph N
PARSERCOVERAGE_CASE(); // Save half space between
if((unsigned)iParam > 255) // Illegal value: use default
iParam = 108; _dxCell = iParam; // cells to add to tabs
break; // Roundtrip value at end of
// tab array
case tokenCellX: // \cellx N
PARSERCOVERAGE_CASE(); HandleCellx(iParam); break;
case tokenRowDefault: // \trowd
PARSERCOVERAGE_CASE(); if(_ped->fUsePassword() || pstate->sDest == destParaNumText) { _ecParseError = ecUnexpectedToken; break; } // Insert a newline if we are inserting a table behind characters in the
// same line. This follows the Word9 model.
if (_cpFirst == _prg->GetCp() && _cpThisPara != _cpFirst) { EC ec = _ped->fUseCRLF() // If RichEdit 1.0 compatibility
? HandleText(szaCRLF, ALL_ASCII)// mode, use CRLF; else CR
: HandleChar((unsigned)(CR)); if(ec == ecNoError) _cpThisPara = _prg->GetCp(); // New para starts after CRLF
}
_cCell = 0; // No cell right boundaries
_dxCell = 0; // or half gap defined yet
_xRowOffset = 0; _dwCellBrdrWdths = 0; _dyRow = 0; // No row height yet
_wBorderWidth = 0; // No borders yet
_dwBorderColors = 0; // No colors yet
_dwCellColors = 0; // No colors yet
_dwShading = 0; // No shading yet
_bAlignment = PFA_LEFT; _iTabsTable = -1; // No cell widths yet
_bCellFlags = 0; // No cell vert merge
_crCellCustom1 = 0; _crCellCustom2 = 0; _fRTLRow = FALSE; _fStartRow = FALSE; _fCellxOK = TRUE; break;
case tokenRowLeft: // \trleft N
PARSERCOVERAGE_CASE(); _xRowOffset = iParam; break; case tokenRowAlignCenter: // \trqc
case tokenRowAlignRight: // \trqr
PARSERCOVERAGE_CASE(); _bAlignment = (WORD)(_token - tokenRowAlignRight + PFA_RIGHT); break;
case tokenRToLRow: // \rtlrow
_fRTLRow = TRUE; break;
case tokenNestRow: // \nestrow
_fNo_iTabsTable = TRUE; goto row;
case tokenRow: // \row
PARSERCOVERAGE_CASE(); _iTabsLevel1 = -1; row: if(!_bTableLevel) // Ignore \row and \nestrow if not in table
break; while(_iCell < _cCell) // If not enuf cells, add
HandleCell(); // them since Word crashes
DelimitRow(szRowEnd); if(_fNo_iTabsTable && !_bTableLevel) // New nested table format
InitializeTableRowParms(); // used so reset _cCell
break; // (new values will be given)
case tokenCellBackColor: // \clcbpat N
_dwCellColors |= GetCellColorIndex() << 4*5; break;
case tokenCellForeColor: // \clcfpat N
_dwCellColors |= GetCellColorIndex() << 5*5; break;
case tokenCellShading: // \clshdng N
_dwShading = iParam/50; // Store in .5 per cents
break; // (N is in .01 per cent)
case tokenCellAlignBottom: // \clvertalb
case tokenCellAlignCenter: // \clvertalc
PARSERCOVERAGE_CASE(); _bCellFlags |= _token - tokenCellAlignCenter + 1; break;
case tokenCellMergeDown: // \clvmgf
_bCellFlags |= fTopCell >> 24; break;
case tokenCellMergeUp: // \clvmrg
_bCellFlags |= fLowCell >> 24; break;
case tokenCellTopBotRLVert: // \cltxtbrlv
PARSERCOVERAGE_CASE(); _bCellFlags |= fVerticalCell >> 24; break;
case tokenCellLRTB: // \cltxlrtb
break; // This is the default
// so don't fire LowFiRTF
case tokenTableLevel: // \itap N
PARSERCOVERAGE_CASE(); // Set table level
if(pstate->fShape) // Bogus shape RTF
break; AssertSz(iParam >= _bTableLevel, "CRTFRead::HandleToken: illegal itap N"); if(iParam) { if(pstate->sDest != destRTF && pstate->sDest != destFieldResult || iParam > 127) goto abort; _iTabsTable = -1; // Previous cell widths invalid
_cCell = 0; while(iParam > _bTableLevel) DelimitRow(szRowStart); // Insert enuf table row headers
} _fNo_iTabsTable = TRUE; break;
case tokenNestTableProps: // \nesttableprops
StoreDestination(pstate, destNestTableProps); break; // Control word is recognized
case tokenNoNestTables: // \nonesttables
goto skip_group; // Ignore info for nesttable
// unaware readers
case tokenPage: // \page
// FUTURE: we want to be smarter about handling FF. But for
// now we ignore it for bulletted and number paragraphs
// and RE 1.0 mode.
if (_PF._wNumbering != 0 || _ped->Get10Mode()) break;
// Intentional fall thru to EOP
case tokenEndParagraph: // \par
case tokenLineBreak: // \line
PARSERCOVERAGE_CASE(); HandleEndOfPara(); break;
case tokenParagraphDefault: // \pard
PARSERCOVERAGE_CASE(); if(pstate->sDest != destParaNumText) // Ignore if \pn destination
Pard(pstate); break; case tokenEndSection: // \sect
CheckNotifyLowFiRTF(); // Fall thru to \sectd
case tokenSectionDefault: // \sectd
PARSERCOVERAGE_CASE(); Pard(pstate); break;
case tokenBackground: // \background
if(_dwFlags & SFF_SELECTION) // If pasting a selection,
goto skip_group; // skip background
pstate->fBackground = TRUE; // Enable background. NB:
break; // InitBackground() already called
//----------------------- Field and Group Control Words --------------------------------
case tokenField: // \field
PARSERCOVERAGE_CASE();
if (pstate->sDest == destDocumentArea || pstate->sDest == destLeadingPunct || pstate->sDest == destFollowingPunct) { // We're not equipped to handle symbols in these destinations, and
// we don't want the fields added accidentally to document text.
goto skip_group; } StoreDestination(pstate, destField); break;
case tokenFieldResult: // \fldrslt
PARSERCOVERAGE_CASE();
if(_fSymbolField) goto skip_group;
if(StoreDestination(pstate, destFieldResult)) AddText(pchSeparateField, 2, FALSE); break;
case tokenFieldInstruction: // \fldinst
PARSERCOVERAGE_CASE(); if(_f1stControlWord && AddText(pchStartField, 2, FALSE) == ecNoError) pstate->sDest = destFieldInstruction; break;
case tokenStartGroup: // Save current state by
PARSERCOVERAGE_CASE(); // pushing it onto stack
HandleStartGroup(); if (_fNoRTFtoken) { // Hack Alert !!!!! For 1.0 compatibility set up to allow no \rtf token.
_fNoRTFtoken = FALSE; pstate = _pstateStackTop; goto rtf; } f1stControlWord = TRUE; // Signal 1st control word of group
break;
case tokenEndGroup: PARSERCOVERAGE_CASE(); HandleFieldEndGroup(); // Special end group handling for \field
HandleEndGroup(); // Restore save state by
break; // popping stack
case tokenOptionalDestination: // \* (see case tokenUnknown)
PARSERCOVERAGE_CASE(); f1stControlWord = _f1stControlWord; // Maintain current _f1stControlWord state
break;
case tokenNullDestination: // Found a destination whose group
PARSERCOVERAGE_CASE(); // should be skipped
// tokenNullDestination triggers a loss notification here for...
// Footer related tokens - "footer", "footerf", "footerl", "footerr",
// "footnote", "ftncn", "ftnsep", "ftnsepc"
// Header related tokens - "header", "headerf", "headerl", "headerr"
// Table of contents - "tc"
// Index entries - "xe"
CheckNotifyLowFiRTF(); // V-GUYB: PWord Converter requires loss notification.
#ifdef REPORT_LOSSAGE
if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
{ ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE; } #endif // REPORT_LOSSAGE
goto skip_group;
case tokenUnknownKeyword: PARSERCOVERAGE_CASE(); if(_tokenLast == tokenOptionalDestination) goto skip_group; break;
//-------------------------- Text Control Words --------------------------------
case tokenUnicode: // \u N
PARSERCOVERAGE_CASE(); HandleUN(pstate); break;
case tokenUnicodeCharByteCount: // \uc N
PARSERCOVERAGE_CASE(); if(IN_RANGE(0, iParam, 2)) pstate->cbSkipForUnicodeMax = iParam; break;
case tokenText: // Lexer concludes tokenText
case tokenASCIIText: PARSERCOVERAGE_CASE(); HandleTextToken(pstate); break;
// \ltrmark, \rtlmark, \zwj, and \zwnj are translated directly into
// their Unicode values. \ltrmark and \rtlmark cause no further
// processing here because we assume that the current font has the
// CharSet needed to identify the direction.
case tokenLToRDocument: // \ltrdoc
PARSERCOVERAGE_CASE(); _bDocType = DT_LTRDOC; break;
case tokenRToLDocument: // \rtldoc
PARSERCOVERAGE_CASE(); _bDocType = DT_RTLDOC; _ped->OrCharFlags(FRTL); break;
//--------------------------Shape Control Words---------------------------------
case tokenShape: // \shp
if(!pstate->fBackground) CheckNotifyLowFiRTF(TRUE); pstate->fShape = TRUE; _dwFlagsShape = 0; break;
case tokenShapeName: // \sn name
pstate->sDest = destShapeName; break;
case tokenShapeValue: // \sv value
pstate->sDest = destShapeValue; break;
case tokenShapeWrap: // \shpwr N
if(iParam == 2) _dwFlagsShape |= REO_WRAPTEXTAROUND; break;
case tokenPositionRight: // \posxr
_dwFlagsShape |= REO_ALIGNTORIGHT; break;
//------------------------- Object Control Words --------------------------------
case tokenObject: // \object
PARSERCOVERAGE_CASE(); // V-GUYB: PWord Converter requires loss notification.
#ifdef REPORT_LOSSAGE
if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any kind of paste is being done.
{ ((LOST_COOKIE*)(_pes->dwCookie))->bLoss = TRUE; } #endif // REPORT_LOSSAGE
// Assume that the object failed to load until proven otherwise
// by RTFRead::ObjectReadFromEditStream
// This works for both:
// - an empty \objdata tag
// - a non-existent \objdata tag
_fFailedPrevObj = TRUE;
case tokenPicture: // \pict
PARSERCOVERAGE_CASE();
FreeRtfObject(); if (IN_RANGE(destColorTable, _pstateStackTop->sDest, destPicture)) { // Don't want new pict\object if
_ecParseError = ecUnexpectedToken; // current state can't handle it.
break; }
if(!StoreDestination(pstate, _token == tokenPicture ? destPicture : destObject)) break;
_prtfObject = (RTFOBJECT *) PvAlloc(sizeof(RTFOBJECT), GMEM_ZEROINIT); if(!_prtfObject) goto OutOfRAM; _prtfObject->xScale = _prtfObject->yScale = 100; _prtfObject->cBitsPerPixel = 1; _prtfObject->cColorPlanes = 1; _prtfObject->szClass = NULL; _prtfObject->szName = NULL; _prtfObject->sType = -1; break;
case tokenObjectEBookImage: // Added by VikramM for E-Book
//
_prtfObject->sType = ROT_EBookImage; break;
case tokenObjectEmbedded: // \objemb
case tokenObjectLink: // \objlink
case tokenObjectAutoLink: // \objautlink
PARSERCOVERAGE_CASE(); _prtfObject->sType = (SHORT)(_token - tokenObjectEmbedded + ROT_Embedded); break;
case tokenObjectMacSubscriber: // \objsub
case tokenObjectMacPublisher: // \objpub
case tokenObjectMacICEmbedder: PARSERCOVERAGE_CASE(); _prtfObject->sType = ROT_MacEdition; break;
case tokenWidth: // \picw N or \objw N
PARSERCOVERAGE_CASE(); _prtfObject->xExt = iParam; break;
case tokenHeight: // \pic N or \objh N
PARSERCOVERAGE_CASE(); _prtfObject->yExt = iParam; break;
case tokenObjectSetSize: // \objsetsize
PARSERCOVERAGE_CASE(); _prtfObject->fSetSize = TRUE; break;
case tokenScaleX: // \picscalex N or \objscalex N
PARSERCOVERAGE_CASE(); _prtfObject->xScale = iParam; break;
case tokenScaleY: // \picscaley N or \objscaley N
PARSERCOVERAGE_CASE(); _prtfObject->yScale = iParam; break;
case tokenCropLeft: // \piccropl or \objcropl
case tokenCropTop: // \piccropt or \objcropt
case tokenCropRight: // \piccropr or \objcropr
case tokenCropBottom: // \piccropb or \objcropb
PARSERCOVERAGE_CASE(); *((LONG *)&_prtfObject->rectCrop + (_token - tokenCropLeft)) = iParam; break;
case tokenObjectClass: // \objclass
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destObjectClass); break;
case tokenObjectName: // \objname
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destObjectName); break;
case tokenObjectResult: // \result
PARSERCOVERAGE_CASE(); if(_fMac || // If it's Mac stuff, we don't
_prtfObject->sType==ROT_MacEdition ||// understand the data, or if
_fFailedPrevObj || _fNeedPres) // we need an obj presentation,
{ pstate->sDest = destRTF; // use the object results
break; } goto skip_group;
case tokenObjectData: // \objdata
PARSERCOVERAGE_CASE(); StoreDestination(pstate, destObjectData); if(_prtfObject->sType==ROT_MacEdition) // It's Mac stuff so just
goto skip_group; // throw away the data
break;
case tokenPictureWindowsMetafile: // \wmetafile
#ifdef NOMETAFILES
goto skip_group; #endif NOMETAFILES
case tokenPngBlip: // \pngblip
case tokenJpegBlip: // \jpegblip
case tokenPictureWindowsDIB: // \dibitmap N
case tokenPictureWindowsBitmap: // \wbitmap N
PARSERCOVERAGE_CASE(); _prtfObject->sType = (SHORT)(_token - tokenPictureWindowsBitmap + ROT_Bitmap); _prtfObject->sPictureType = (SHORT)iParam; break;
case tokenBitmapBitsPerPixel: // \wbmbitspixel N
PARSERCOVERAGE_CASE(); _prtfObject->cBitsPerPixel = (SHORT)iParam; break;
case tokenBitmapNumPlanes: // \wbmplanes N
PARSERCOVERAGE_CASE(); _prtfObject->cColorPlanes = (SHORT)iParam; break;
case tokenBitmapWidthBytes: // \wbmwidthbytes N
PARSERCOVERAGE_CASE(); _prtfObject->cBytesPerLine = (SHORT)iParam; break;
case tokenDesiredWidth: // \picwgoal N
PARSERCOVERAGE_CASE(); _prtfObject->xExtGoal = (SHORT)iParam; break;
case tokenDesiredHeight: // \pichgoal N
PARSERCOVERAGE_CASE(); _prtfObject->yExtGoal = (SHORT)iParam; break;
case tokenBinaryData: // \bin N
PARSERCOVERAGE_CASE();
// Update OleGet function
RTFReadOLEStream.lpstbl->Get = (DWORD (CALLBACK* )(LPOLESTREAM, void FAR*, DWORD)) RTFGetBinaryDataFromStream; _cbBinLeft = iParam; // Set data length
switch (pstate->sDest) { case destObjectData: _fFailedPrevObj = !ObjectReadFromEditStream(); break;
case destPicture: StaticObjectReadFromEditStream(iParam); break;
default: AssertSz(FALSE, "Binary data hit but don't know where to put it"); } // Restore OleGet function
RTFReadOLEStream.lpstbl->Get = (DWORD (CALLBACK* )(LPOLESTREAM, void FAR*, DWORD)) RTFGetFromStream; break;
case tokenObjectDataValue: PARSERCOVERAGE_CASE(); if(_prtfObject->sType != ROT_EBookImage) // Added by VikramM for E-Book
{ // Normal processing
_fFailedPrevObj = !ObjectReadFromEditStream(); } else { // Do the Ebook Image callback here and set the _prtfObject size here
// Don't need to read the image data at this point, we just want to
// do a callback at a later point to have the E-Book shell render the image
_fFailedPrevObj = !ObjectReadEBookImageInfoFromEditStream(); } goto EndOfObjectStream; case tokenPictureDataValue: PARSERCOVERAGE_CASE(); StaticObjectReadFromEditStream(); EndOfObjectStream: if(!SkipToEndOfGroup()) HandleEndGroup(); break;
case tokenObjectPlaceholder: PARSERCOVERAGE_CASE(); if(_ped->GetEventMask() & ENM_OBJECTPOSITIONS) { if(!_pcpObPos) { _pcpObPos = (LONG *)PvAlloc(sizeof(ULONG) * cobPosInitial, GMEM_ZEROINIT); if(!_pcpObPos) { _ecParseError = ecNoMemory; break; } _cobPosFree = cobPosInitial; _cobPos = 0; } if(_cobPosFree-- <= 0) { const int cobPosNew = _cobPos + cobPosChunk; LPVOID pv;
pv = PvReAlloc(_pcpObPos, sizeof(ULONG) * cobPosNew); if(!pv) { _ecParseError = ecNoMemory; break; } _pcpObPos = (LONG *)pv; _cobPosFree = cobPosChunk - 1; } _pcpObPos[_cobPos++] = _prg->GetCp(); } break;
default: PARSERCOVERAGE_DEFAULT(); if(pstate->sDest != destFieldInstruction && // Values outside token
(DWORD)(_token - tokenMin) > // range are treated
(DWORD)(tokenMax - tokenMin)) // as Unicode chars
{ // 1.0 mode doesn't use Unicode bullets nor smart quotes
if (_ped->Get10Mode() && IN_RANGE(LQUOTE, _token, RDBLQUOTE)) { if (_token == LQUOTE || _token == RQUOTE) _token = L'\''; else if (_token == LDBLQUOTE || _token == RDBLQUOTE) _token = L'\"'; }
if(!IsLowMergedCell()) HandleChar(_token); } #if defined(DEBUG) && !defined(NOFULLDEBUG)
else { if(GetProfileIntA("RICHEDIT DEBUG", "RTFCOVERAGE", 0)) { CHAR *pszKeyword = PszKeywordFromToken(_token); CHAR szBuf[256];
sprintf(szBuf, "CRTFRead::HandleToken(): Token not processed - token = %d, %s%s%s", _token, "keyword = ", pszKeyword ? "\\" : "<unknown>", pszKeyword ? pszKeyword : "");
AssertSz(0, szBuf); } } #endif
}
TRACEERRSZSC("HandleToken()", - _ecParseError); _f1stControlWord = f1stControlWord; // Update 1st control word status
return _ecParseError; }
/*
* CRTFRead::IsLowMergedCell() * * @mfunc * Return TRUE iff _prg is currently in a low merged table cell. Note * that RichEdit can't insert any text into a low merged cell, but * Word's RTF sometimes attempts to, e.g., {\listtext...} ignored * by Word can be (erroneously) emitted for insertion into these cells. * Hence we discard such insertions. * * @rdesc * Return TRUE iff _prg is currently in a low merged table cell */ BOOL CRTFRead::IsLowMergedCell() { if(!_bTableLevel) return FALSE;
CELLPARMS *pCellParms = (CELLPARMS *)&_rgxCell[0];
return IsLowCell(pCellParms[_iCell].uCell); }
/*
* CRTFRead::Pard(pstate) * * @mfunc * Reset paragraph and pstate properties to default values */ void CRTFRead::Pard( STATE *pstate) { if(IN_RANGE(destColorTable, pstate->sDest, destPicture)) { _ecParseError = ecAbort; return; } BYTE bT = _PF._bOutlineLevel; // Save outline level
_PF.InitDefault(_bDocType == DT_RTLDOC ? PFE_RTLPARA : 0); // Reset para formatting
pstate->fBullet = FALSE; pstate->sIndentNumbering = 0; _cTab = 0; // No tabs defined
_bTabLeader = 0; _bTabType = 0; _bBorder = 0; _fStartRow = FALSE; _PF._bOutlineLevel = (BYTE)(bT | 1); _dwMaskPF = PFM_ALLRTF; _dwMaskPF2 = PFM2_TABLEROWSHIFTED; }
/*
* CRTFRead::DelimitRow(szRowDelimiter) * * @mfunc * Insert start-of-row or end-of-row paragraph with current table * properties */ void CRTFRead::DelimitRow( WCHAR *szRowDelimiter) //@parm Delimit text to insert
{ if(!_ped->_pdp->IsMultiLine()) // No tables in single line
{ // controls
_ecParseError = ecTruncateAtCRLF; return; }
_fCellxOK = FALSE; _fStartRow = FALSE; LONG nTableIndex = _bTableLevel; if(szRowDelimiter == szRowEnd) { if(!_iCell) // Bad RTF: \row with no \cell,
HandleCell(); // so fake one
nTableIndex--; } if(nTableIndex + _bTableLevelIP >= MAXTABLENEST) { if(szRowDelimiter == szRowEnd) // Maintain _bTableLevel
_bTableLevel--; else _bTableLevel++; _token = tokenEndParagraph; HandleEndOfPara(); return; } if(szRowDelimiter == szRowStart && _prg->GetCp() && !_prg->_rpTX.IsAfterEOP()) { _token = tokenEndParagraph; HandleEndOfPara(); } Assert(_pstateStackTop && _pstateStackTop->pPF);
// Add _PF diffs to *_pstateStackTop->pPF
if(!_pstateStackTop->AddPF(_PF, _bDocType, _dwMaskPF, _dwMaskPF2)) { _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecNoMemory; return; }
DWORD dwMaskPF = _pstateStackTop->dwMaskPF; // Save PF for restoration
DWORD dwMaskPF2 = _pstateStackTop->dwMaskPF2; // Save PF for restoration
SHORT iTabs = -1; CParaFormat PF = *_pstateStackTop->pPF; // on return
_PF.InitDefault(_fRTLRow ? PFE_RTLPARA : 0); _dwMaskPF = PFM_ALLRTF; _dwMaskPF2 = 0; if(_wBorderWidth) // Store any border info
{ _PF._dwBorderColor = _dwBorderColors; _PF._wBorders = _wBorders; _PF._wBorderSpace = _wBorderSpace; _PF._wBorderWidth = _wBorderWidth; _dwMaskPF |= PFM_BORDER; }
_PF._bAlignment = _bAlignment; // Row alignment (no cell align)
_PF._dxStartIndent = _xRowOffset; // \trleft N
_PF._dxOffset = max(_dxCell, 10); // \trgaph N
_PF._dyLineSpacing = _dyRow; // \trrh N
_PF._wEffects |= PFE_TABLE | PFE_TABLEROWDELIMITER;
BOOL fHidden = _ped->GetCharFormat(_prg->Get_iFormat())->_dwEffects & CFE_HIDDEN; _prg->_rpCF.AdjustBackward(); if(_prg->IsHidden()) { CCharFormat CF; CF._dwEffects = 0; // Don't hide EOP preceding TRD
_prg->BackupCRLF(CSC_NORMAL, TRUE); _prg->SetCharFormat(&CF, 0, NULL, CFM_HIDDEN, 0); CheckNotifyLowFiRTF(TRUE); _prg->AdvanceCRLF(CSC_NORMAL, FALSE); } _prg->_rpCF.AdjustForward();
AssertSz(!_prg->GetCp() || IsEOP(_prg->GetPrevChar()), "CRTFRead::DelimitRow: no EOP precedes TRD");
if(AddText(szRowDelimiter, 2, FALSE) != ecNoError) goto cleanup;
if(!_bTableLevel && _PF._dxStartIndent < 50)// Move neg shifted table right
{ // (handles common default Word table)
_PF._wEffects |= PFE_TABLEROWSHIFTED; _dwMaskPF2 |= PFM2_TABLEROWSHIFTED; _PF._dxStartIndent += _dxCell + 50; // 50 gives room for left border
} if(szRowDelimiter == szRowStart) _bTableLevel++; _PF._bTableLevel = _bTableLevel + _bTableLevelIP; iTabs = Apply_PF(); if(szRowDelimiter == szRowStart) { if(_bTableLevel == 1) _iTabsLevel1 = iTabs; _rgTableState[nTableIndex]._iCell = _iCell; _rgTableState[nTableIndex]._cCell = _cCell;
if(_token == tokenTableLevel) _cCell = 0; _iCell = 0;
if(!_cCell) // Cache if need to recompute row PF
_dwRowResolveFlags |= 1 << _bTableLevel; } else { Assert(szRowDelimiter == szRowEnd); DWORD dwMask = 1 << _bTableLevel; if(_dwRowResolveFlags & dwMask) { // Copy iPF over to corresponding
CPFRunPtr rpPF(*_prg); // row header
rpPF.ResolveRowStartPF(); _dwRowResolveFlags &= (dwMask - 1); // Insert NOTACHARs for cells
LONG cp = _prg->GetCp(); // vert merged with cells above
LONG j = _cCell - 1; CELLPARMS *pCellParms = (CELLPARMS *)&_rgxCell[0]; WCHAR szNOTACHAR[1] = {NOTACHAR};
_prg->Move(-2, FALSE); // Move before row-end delimiter
for(LONG i = _cCell; i--;) // and CELL mark
{ if(IsLowCell(pCellParms[i].uCell)) { if(i != j) _prg->Move(tomCell, i - j, NULL); if(_prg->GetPrevChar() == CELL) _prg->Move(-1, FALSE); // Backspace over CELL mark
Assert(_prg->_rpTX.GetChar() == CELL);
if(_prg->_rpTX.GetPrevChar() == NOTACHAR) _prg->Move(-1, FALSE); else { _prg->ReplaceRange(1, szNOTACHAR, NULL, SELRR_IGNORE, NULL, 0); _prg->Move(-2, FALSE); // Backspace over NOTACHAR CELL combo
cp++; } j = i - 1; } } _prg->SetCp(cp, FALSE); // Reposition rg after end-row delim
Assert(_prg->_rpTX.IsAfterTRD(ENDFIELD)); } _bTableLevel--; // End of current row
_iCell = _rgTableState[nTableIndex]._iCell; _cCell = _rgTableState[nTableIndex]._cCell;
if(!_bTableLevel) _fStartRow = TRUE; // Tell AddText to start new row
} // unless \pard terminates it
_cpThisPara = _prg->GetCp(); // New para starts after CRLF
cleanup: _PF = PF; _dwMaskPF = dwMaskPF; _dwMaskPF2 = dwMaskPF2;
if(fHidden) // Restore hidden property
{ _CF._dwEffects |= CFE_HIDDEN; _dwMaskCF |= CFM_HIDDEN; }
Assert(!(_PF._wEffects & PFE_TABLEROWDELIMITER)); }
/*
* CRTFRead::InitializeTableRowParms() * * @mfunc * Initialize table parms to no table state */ void CRTFRead::InitializeTableRowParms() { // Initialize table parms
_cCell = 0; // No table cells yet
_iCell = 0; _fCellxOK = FALSE; _fStartRow = FALSE; _wBorderWidth = 0; _bAlignment = PFA_LEFT; _xRowOffset = 0; _dxCell = 0; _dyRow = 0; _iTabsTable = -1; }
/*
* CRTFRead::ReadRtf() * * @mfunc * The range _prg is replaced by RTF data resulting from parsing the * input stream _pes. The CRTFRead object assumes that the range is * already degenerate (caller has to delete the range contents, if * any, before calling this routine). Currently any info not used * or supported by RICHEDIT is thrown away. * * @rdesc * Number of chars inserted into text. 0 means none were inserted * OR an error occurred. */ LONG CRTFRead::ReadRtf() { TRACEBEGIN(TRCSUBSYSRTFR, TRCSCOPEINTERN, "CRTFRead::ReadRtf");
LONG cpFirst; LONG cpFirstInPara; CTxtRange * prg = _prg; STATE * pstate;
cpFirst = _cpFirst = prg->GetCp();
if (!_cchMax) { // At text limit already, forget it.
_ecParseError = ecTextMax; goto Quit; }
if(!InitLex()) goto Quit;
TESTPARSERCOVERAGE();
AssertSz(!prg->GetCch(), "CRTFRead::ReadRtf: range must be deleted");
if(!(_dwFlags & SFF_SELECTION)) { // SFF_SELECTION is set if any kind of paste is being done, i.e.,
// not just that using the selection. If it isn't set, data is
// being streamed in and we allow this to reset the doc params
if(_ped->InitDocInfo() != NOERROR) { _ecParseError = ecNoMemory; goto Quit; } }
prg->SetIgnoreFormatUpdate(TRUE);
_szUnicode = (WCHAR *)PvAlloc(cachTextMax * sizeof(WCHAR), GMEM_ZEROINIT); if(!_szUnicode) // Allocate space for Unicode conversions
{ _ped->GetCallMgr()->SetOutOfMemory(); _ecParseError = ecNoMemory; goto CleanUp; } _cchUnicode = cachTextMax;
// Initialize per-read variables
_nCodePage = (_dwFlags & SF_USECODEPAGE) ? (_dwFlags >> 16) : INVALID_CODEPAGE;
// Populate _PF with initial paragraph formatting properties
_PF = *prg->GetPF(); _dwMaskPF = PFM_ALLRTF; // Setup initial MaskPF
_PF._iTabs = -1; // In case it's not -1
if(_PF.IsTableRowDelimiter()) // Do _not_ insert with this property!
{ if(prg->_rpTX.IsAtTRD(ENDFIELD)) { prg->AdvanceCRLF(CSC_NORMAL, FALSE);// Bypass table row-end delimiter
cpFirst = prg->GetCp(); // Update value
_PF = *prg->GetPF(); // Might still be row-start delimiter
_PF._iTabs = -1; Assert(!prg->_rpTX.IsAtTRD(ENDFIELD)); } if(prg->_rpTX.IsAtTRD(STARTFIELD)) { // REVIEW: this if can probably be omitted now since the caller calls
// DeleteWithTRDCheck()
_ecParseError = ecGeneralFailure; goto CleanUp; } } _bTableLevelIP = _PF._bTableLevel; // Save table level of insertion pt
AssertSz(_bTableLevelIP >= 0, "CRTFRead::ReadRtf: illegal table level");
// V-GUYB: PWord Converter requires loss notification.
#ifdef REPORT_LOSSAGE
if(!(_dwFlags & SFF_SELECTION)) // SFF_SELECTION is set if any
{ // kind of paste is being done
((LOST_COOKIE*)(_pes->dwCookie))->bLoss = FALSE; } #endif // REPORT_LOSSAGE
// Valid RTF files start with "{\rtf", "{urtf", or "{\pwd"
GetChar(); // Fill input buffer
UngetChar(); // Put char back
if(!IsRTF((char *)_pchRTFCurrent, _pchRTFEnd - _pchRTFCurrent)) // Is it RTF?
{ // No
if (_ped->Get10Mode()) _fNoRTFtoken = TRUE; else { _ecParseError = ecUnexpectedToken; // Signal bad file
goto CleanUp; } }
// If initial cp follows EOP, use it for _cpThisPara. Else
// search for start of para containing the initial cp.
_cpThisPara = prg->GetCp(); if(!prg->_rpTX.IsAfterEOP()) { CTxtPtr tp(prg->_rpTX); tp.FindEOP(tomBackward); _cpThisPara = tp.GetCp(); } cpFirstInPara = _cpThisPara; // Backup to start of para before
// parsing
while ( TokenGetToken() != tokenEOF && // Process tokens
_token != tokenError && !HandleToken() && _pstateStackTop ) ;
if(_ecParseError == ecAbort) // Really vile error: delete anything
{ // that was inserted
prg->Set(prg->GetCp(), prg->GetCp() - cpFirst); prg->ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK | RR_NO_TRD_CHECK | RR_NO_CHECK_TABLE_SEL); goto CleanUp; } if(_bTableLevel) // Whoops! still in middle of table
{ LONG cpEnd = prg->GetCp(); while(prg->GetCp() > _cpFirst) // Move back into text that has
{ // table formatting
prg->_rpPF.AdjustBackward(); // Get preceding level
prg->Move(-prg->_rpPF.GetIch(), FALSE);// Move back to start of run
if(prg->GetPF()->_bTableLevel > _bTableLevelIP) break; // Found table formatting
} LONG cpMin = prg->GetCp(); if(cpMin > _cpFirst) // Find beginning of row
prg->FindRow(&cpMin, NULL, _bTableLevelIP + 1); cpMin = max(cpMin, _cpFirst); prg->Set(cpEnd, cpEnd - cpMin); // Delete row
prg->ReplaceRange(0, NULL, NULL, SELRR_IGNORE, NULL, RR_NO_LP_CHECK | RR_NO_TRD_CHECK | RR_NO_CHECK_TABLE_SEL); #ifdef DEBUG
prg->_rpTX.MoveGapToEndOfBlock(); #endif
} _cCell = _iCell = 0;
prg->SetIgnoreFormatUpdate(FALSE); // Enable range _iFormat updates
prg->Update_iFormat(-1); // Update _iFormat to CF
// at current active end
if(!(_dwFlags & SFF_SELECTION)) // RTF applies to document:
{ // update CDocInfo
// Apply char and para formatting of
// final text run to final CR
if (prg->GetCp() == _ped->GetAdjustedTextLength() && !(_dwMaskPF & (PFM_TABLEROWDELIMITER | PFM_TABLE))) { // REVIEW: we need to think about what para properties should
// be transferred here. E.g., borders were being transferred
// incorrectly
_dwMaskPF &= ~(PFM_BORDER | PFM_SHADING); Apply_PF(); prg->ExtendFormattingCRLF(); }
// Update the per-document information from the RTF read
CDocInfo *pDocInfo = _ped->GetDocInfoNC();
if(!pDocInfo) { Assert(FALSE); // Should be allocated by
_ecParseError = ecNoMemory; // earlier call in this function
goto CleanUp; }
if (ecNoError == _ecParseError) // If range end EOP wasn't
prg->DeleteTerminatingEOP(NULL); // deleted and new text
// ends with an EOP, delete that EOP
pDocInfo->_wCpg = (WORD)(_nCodePage == INVALID_CODEPAGE ? tomInvalidCpg : _nCodePage); if (pDocInfo->_wCpg == CP_UTF8) pDocInfo->_wCpg = 1252;
_ped->SetDefaultLCID(_sDefaultLanguage == INVALID_LANGUAGE ? tomInvalidLCID : MAKELCID(_sDefaultLanguage, SORT_DEFAULT));
_ped->SetDefaultLCIDFE(_sDefaultLanguageFE == INVALID_LANGUAGE ? tomInvalidLCID : MAKELCID(_sDefaultLanguageFE, SORT_DEFAULT));
_ped->SetDefaultTabStop(TWIPS_TO_FPPTS(_sDefaultTabWidth)); _ped->SetDocumentType(_bDocType); }
if(_ped->IsComplexScript() && prg->GetCp() > cpFirstInPara) { Assert(!prg->GetCch()); LONG cpSave = prg->GetCp(); LONG cpLastInPara = cpSave; if(_ped->IsBiDi() && !prg->_rpTX.IsAtEOP()) { CTxtPtr tp(prg->_rpTX); tp.FindEOP(tomForward); cpLastInPara = tp.GetCp(); prg->Move(cpLastInPara - cpSave, FALSE); } // Itemize from the start of paragraph to be inserted till the end of
// paragraph inserting. We need to cover all affected paragraphs because
// paragraphs we're playing could possibly in conflict direction. Think
// about the case that the range covers one LTR para and one RTL para, then
// the inserting text covers one RTL and one LTR. Both paragraphs' direction
// could have been changed after this insertion.
prg->ItemizeReplaceRange(cpLastInPara - cpFirstInPara, 0, NULL, _ped->IsBiDi() && !_fNon0CharSet); if (cpLastInPara != cpSave) prg->SetCp(cpSave, FALSE); }
CleanUp: FreeRtfObject();
pstate = _pstateStackTop; if(pstate) // Illegal RTF file. Release
{ // unreleased format indices
if(ecNoError == _ecParseError) // It's only an overflow if no
_ecParseError = ecStackOverflow; // other error has occurred
if(_ecParseError != ecAbort) HandleFieldEndGroup(); // Cleanup possible partial field
while(pstate->pstatePrev) { pstate = pstate->pstatePrev; ReleaseFormats(pstate->iCF, -1); } }
pstate = _pstateLast; if(pstate) { while(pstate->pstatePrev) // Free all but first STATE
{ pstate->DeletePF(); pstate = pstate->pstatePrev; FreePv(pstate->pstateNext); } pstate->DeletePF(); } Assert(_PF._iTabs == -1); FreePv(pstate); // Free first STATE
FreePv(_szUnicode);
Quit: DeinitLex();
if(_pcpObPos) { if((_ped->GetEventMask() & ENM_OBJECTPOSITIONS) && _cobPos > 0) { OBJECTPOSITIONS obpos;
obpos.cObjectCount = _cobPos; obpos.pcpPositions = _pcpObPos;
if (_ped->Get10Mode()) { LONG *pcpPositions = _pcpObPos;
for (LONG i = 0; i < _cobPos; i++, pcpPositions++) *pcpPositions = _ped->GetAcpFromCp(*pcpPositions); } _ped->TxNotify(EN_OBJECTPOSITIONS, &obpos); }
FreePv(_pcpObPos); _pcpObPos = NULL; }
// transcribed from winerror.h
#define ERROR_HANDLE_EOF 38L
// FUTURE(BradO): We should devise a direct mapping from our error codes
// to Win32 error codes. In particular our clients are
// not expecting the error code produced by:
// _pes->dwError = (DWORD) -(LONG) _ecParseError;
if(_ecParseError) { AssertSz(_ecParseError >= 0, "Parse error is negative");
if(_ecParseError == ecTextMax) { _ped->GetCallMgr()->SetMaxText(); _pes->dwError = (DWORD)STG_E_MEDIUMFULL; } if(_ecParseError == ecUnexpectedEOF) _pes->dwError = (DWORD)HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
if(!_pes->dwError && _ecParseError != ecTruncateAtCRLF) _pes->dwError = (DWORD) -(LONG) _ecParseError;
#if defined(DEBUG)
TRACEERRSZSC("CchParse_", _pes->dwError); if(ecNoError < _ecParseError && _ecParseError < ecLastError) Tracef(TRCSEVERR, "Parse error: %s", rgszParseError[_ecParseError]); #endif
} if(cpFirst > _cpFirst && prg->GetCp() == cpFirst) { prg->SetCp(_cpFirst, FALSE); // Restore prg cp, since nothing inserted
return 0; } return prg->GetCp() - cpFirst; }
/*
* CRTFRead::CpgInfoFromFaceName() * * @mfunc * This routine fills in the TEXTFONT::bCharSet and TEXTFONT::nCodePage * members of the TEXTFONT structure by querying the system for the * metrics of the font described by TEXTFONT::szName. * * @rdesc * A flag indicating whether the charset and codepage were successfully * determined. */ BOOL CRTFRead::CpgInfoFromFaceName( TEXTFONT *ptf) { // FUTURE(BradO): This code is a condensed version of a more sophisticated
// algorithm we use in font.cpp to second-guess the font-mapper.
// We should factor out the code from font.cpp for use here as well.
// Indicates that we've tried to obtain the cpg info from the system,
// so that after a failure we don't re-call this routine.
ptf->fCpgFromSystem = TRUE;
if(ptf->fNameIsDBCS) { // If fNameIsDBCS, we have high-ANSI characters in the facename, and
// no codepage with which to interpret them. The facename is gibberish,
// so don't waste time calling the system to match it.
return FALSE; }
HDC hdc = _ped->TxGetDC(); if(!hdc) return FALSE;
LOGFONT lf = {0}; TEXTMETRIC tm;
wcscpy(lf.lfFaceName, ptf->szName); lf.lfCharSet = CharSetFromCharRep(CharRepFromCodePage(GetSystemDefaultCodePage()));
if(!GetTextMetrics(hdc, lf, tm) || tm.tmCharSet != lf.lfCharSet) { lf.lfCharSet = DEFAULT_CHARSET; // Doesn't match default sys
GetTextMetrics(hdc, lf, tm); // charset, so see what
} // DEFAULT_CHARSET gives
_ped->TxReleaseDC(hdc);
if(tm.tmCharSet != DEFAULT_CHARSET) // Got something, so use it
{ ptf->iCharRep = CharRepFromCharSet(tm.tmCharSet); ptf->sCodePage = (SHORT)CodePageFromCharRep(ptf->iCharRep); return TRUE; }
return FALSE; }
// Including a source file, but we only want to compile this code for debug purposes
#if defined(DEBUG)
#include "rtflog.cpp"
#endif
|