/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_scfilt.hxx" //------------------------------------------------------------------------ #include "scitems.hxx" #include #include #include #include #include #include #define SC_RTFPARSE_CXX #include "rtfparse.hxx" #include "global.hxx" #include "document.hxx" #include "docpool.hxx" #define SC_RTFTWIPTOL 10 // 10 Twips Toleranz bei Spaltenbestimmung SV_IMPL_VARARR_SORT( ScRTFColTwips, sal_uLong ); ScRTFParser::ScRTFParser( EditEngine* pEditP ) : ScEEParser( pEditP ), pDefaultList( new ScRTFDefaultList ), pColTwips( new ScRTFColTwips ), pActDefault( NULL ), pDefMerge( NULL ), nStartAdjust( (sal_uLong)~0 ), nLastWidth(0), bNewDef( sal_False ) { // RTF default FontSize 12Pt long nMM = OutputDevice::LogicToLogic( 12, MAP_POINT, MAP_100TH_MM ); pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) ); // freifliegender pInsDefault pInsDefault = new ScRTFCellDefault( pPool ); } ScRTFParser::~ScRTFParser() { delete pInsDefault; delete pColTwips; for ( ScRTFCellDefault* pD = pDefaultList->First(); pD; pD = pDefaultList->Next() ) delete pD; delete pDefaultList; } sal_uLong ScRTFParser::Read( SvStream& rStream, const String& rBaseURL ) { Link aOldLink = pEdit->GetImportHdl(); pEdit->SetImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) ); sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_RTF ); if ( nLastToken == RTF_PAR ) { ScEEParseEntry* pE = pList->Last(); if ( pE // komplett leer && (( pE->aSel.nStartPara == pE->aSel.nEndPara && pE->aSel.nStartPos == pE->aSel.nEndPos) // leerer Paragraph || ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara ) && pE->aSel.nEndPos == 0 )) ) { // den letzten leeren Absatz nicht uebernehmen pList->Remove(); delete pE; } } ColAdjust(); pEdit->SetImportHdl( aOldLink ); return nErr; } void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel ) { // Paragraph -2 stript den angehaengten leeren Paragraph pE->aSel.nEndPara = aSel.nEndPara - 2; // obwohl das nEndPos heisst, ist das letzte Position + 1 pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 ); } inline void ScRTFParser::NextRow() { if ( nRowMax < ++nRowCnt ) nRowMax = nRowCnt; } sal_Bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol ) { sal_uInt16 nPos; sal_Bool bFound = pColTwips->Seek_Entry( nTwips, &nPos ); *pCol = static_cast(nPos); if ( bFound ) return sal_True; sal_uInt16 nCount = pColTwips->Count(); if ( !nCount ) return sal_False; SCCOL nCol = *pCol; // nCol ist Einfuegeposition, da liegt der Naechsthoehere (oder auch nicht) if ( nCol < static_cast(nCount) && (((*pColTwips)[nCol] - SC_RTFTWIPTOL) <= nTwips) ) return sal_True; // nicht kleiner als alles andere? dann mit Naechstniedrigerem vergleichen else if ( nCol != 0 && (((*pColTwips)[nCol-1] + SC_RTFTWIPTOL) >= nTwips) ) { (*pCol)--; return sal_True; } return sal_False; } void ScRTFParser::ColAdjust() { if ( nStartAdjust != (sal_uLong)~0 ) { SCCOL nCol = 0; ScEEParseEntry* pE; pE = pList->Seek( nStartAdjust ); while ( pE ) { if ( pE->nCol == 0 ) nCol = 0; pE->nCol = nCol; if ( pE->nColOverlap > 1 ) nCol = nCol + pE->nColOverlap; // merged cells mit \clmrg else { SeekTwips( pE->nTwips, &nCol ); if ( ++nCol <= pE->nCol ) nCol = pE->nCol + 1; // verschobene Zell-X pE->nColOverlap = nCol - pE->nCol; // merged cells ohne \clmrg } if ( nCol > nColMax ) nColMax = nCol; pE = pList->Next(); } nStartAdjust = (sal_uLong)~0; pColTwips->Remove( (sal_uInt16)0, pColTwips->Count() ); } } IMPL_LINK( ScRTFParser, RTFImportHdl, ImportInfo*, pInfo ) { switch ( pInfo->eState ) { case RTFIMP_NEXTTOKEN: ProcToken( pInfo ); break; case RTFIMP_UNKNOWNATTR: ProcToken( pInfo ); break; case RTFIMP_START: { SvxRTFParser* pParser = (SvxRTFParser*) pInfo->pParser; pParser->SetAttrPool( pPool ); RTFPardAttrMapIds& rMap = pParser->GetPardMap(); rMap.nBrush = ATTR_BACKGROUND; rMap.nBox = ATTR_BORDER; rMap.nShadow = ATTR_SHADOW; } break; case RTFIMP_END: if ( pInfo->aSelection.nEndPos ) { // falls noch Text: letzten Absatz erzeugen pActDefault = NULL; pInfo->nToken = RTF_PAR; // EditEngine hat keinen leeren Paragraph mehr angehaengt // den EntryEnd strippen koennte pInfo->aSelection.nEndPara++; ProcToken( pInfo ); } break; case RTFIMP_SETATTR: break; case RTFIMP_INSERTTEXT: break; case RTFIMP_INSERTPARA: break; default: DBG_ERRORFILE("unknown ImportInfo.eState"); } return 0; } // bei RTF_INTBL bzw. am Anfang von erstem RTF_CELL nach RTF_CELLX wenn es // kein RTF_INTBL gab, bad behavior void ScRTFParser::NewCellRow( ImportInfo* /*pInfo*/ ) { if ( bNewDef ) { ScRTFCellDefault* pD; bNewDef = sal_False; // rechts nicht buendig? => neue Tabelle if ( nLastWidth && ((pD = pDefaultList->Last()) != 0) && pD->nTwips != nLastWidth ) { SCCOL n1, n2; if ( !( SeekTwips( nLastWidth, &n1 ) && SeekTwips( pD->nTwips, &n2 ) && n1 == n2) ) ColAdjust(); } // TwipCols aufbauen, erst nach nLastWidth Vergleich! for ( pD = pDefaultList->First(); pD; pD = pDefaultList->Next() ) { SCCOL n; if ( !SeekTwips( pD->nTwips, &n ) ) pColTwips->Insert( pD->nTwips ); } } pDefMerge = NULL; pActDefault = pDefaultList->First(); DBG_ASSERT( pActDefault, "NewCellRow: pActDefault==0" ); } /* SW: ~~~ [\par] \trowd \cellx \cellx ... \intbl \cell \cell ... \row [\par] [\trowd \cellx \cellx ...] \intbl \cell \cell ... \row [\par] M$-Word: ~~~~~~~~ [\par] \trowd \cellx \cellx ... \intbl \cell \cell ... \intbl \row [\par] [\trowd \cellx \cellx ...] \intbl \cell \cell ... \intbl \row [\par] */ void ScRTFParser::ProcToken( ImportInfo* pInfo ) { ScRTFCellDefault* pD; ScEEParseEntry* pE; switch ( pInfo->nToken ) { case RTF_TROWD: // denotes table row defauls, before RTF_CELLX { if ( (pD = pDefaultList->Last()) != 0 ) nLastWidth = pD->nTwips; nColCnt = 0; for ( pD = pDefaultList->First(); pD; pD = pDefaultList->Next() ) delete pD; pDefaultList->Clear(); pDefMerge = NULL; nLastToken = pInfo->nToken; } break; case RTF_CLMGF: // The first cell of cells to be merged { pDefMerge = pInsDefault; nLastToken = pInfo->nToken; } break; case RTF_CLMRG: // A cell to be merged with the preceding cell { if ( !pDefMerge ) pDefMerge = pDefaultList->Last(); DBG_ASSERT( pDefMerge, "RTF_CLMRG: pDefMerge==0" ); if ( pDefMerge ) // sonst rottes RTF pDefMerge->nColOverlap++; // mehrere nacheinander moeglich pInsDefault->nColOverlap = 0; // Flag: ignoriere diese nLastToken = pInfo->nToken; } break; case RTF_CELLX: // closes cell default { bNewDef = sal_True; pInsDefault->nCol = nColCnt; pInsDefault->nTwips = pInfo->nTokenValue; // rechter Zellenrand pDefaultList->Insert( pInsDefault, LIST_APPEND ); // neuer freifliegender pInsDefault pInsDefault = new ScRTFCellDefault( pPool ); if ( ++nColCnt > nColMax ) nColMax = nColCnt; nLastToken = pInfo->nToken; } break; case RTF_INTBL: // before the first RTF_CELL { // einmal ueber NextToken und einmal ueber UnknownAttrToken // oder z.B. \intbl ... \cell \pard \intbl ... \cell if ( nLastToken != RTF_INTBL && nLastToken != RTF_CELL && nLastToken != RTF_PAR ) { NewCellRow( pInfo ); nLastToken = pInfo->nToken; } } break; case RTF_CELL: // denotes the end of a cell. { DBG_ASSERT( pActDefault, "RTF_CELL: pActDefault==0" ); if ( bNewDef || !pActDefault ) NewCellRow( pInfo ); // davor war kein \intbl, bad behavior // rottes RTF? retten was zu retten ist if ( !pActDefault ) pActDefault = pInsDefault; if ( pActDefault->nColOverlap > 0 ) { // nicht merged mit vorheriger pActEntry->nCol = pActDefault->nCol; pActEntry->nColOverlap = pActDefault->nColOverlap; pActEntry->nTwips = pActDefault->nTwips; pActEntry->nRow = nRowCnt; pActEntry->aItemSet.Set( pActDefault->aItemSet ); EntryEnd( pActEntry, pInfo->aSelection ); if ( nStartAdjust == (sal_uLong)~0 ) nStartAdjust = pList->Count(); pList->Insert( pActEntry, LIST_APPEND ); NewActEntry( pActEntry ); // neuer freifliegender pActEntry } else { // aktuelle Twips der MergeCell zuweisen if ( (pE = pList->Last()) != 0 ) pE->nTwips = pActDefault->nTwips; // Selection des freifliegenden pActEntry anpassen // Paragraph -1 wg. Textaufbruch in EditEngine waehrend Parse pActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1; } pActDefault = pDefaultList->Next(); nLastToken = pInfo->nToken; } break; case RTF_ROW: // means the end of a row { NextRow(); nLastToken = pInfo->nToken; } break; case RTF_PAR: // Paragraph { if ( !pActDefault ) { // text not in table ColAdjust(); // close the processing table pActEntry->nCol = 0; pActEntry->nRow = nRowCnt; EntryEnd( pActEntry, pInfo->aSelection ); pList->Insert( pActEntry, LIST_APPEND ); NewActEntry( pActEntry ); // new pActEntry NextRow(); } nLastToken = pInfo->nToken; } break; default: { // do not set nLastToken switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) ) { case RTF_SHADINGDEF: ((SvxRTFParser*)pInfo->pParser)->ReadBackgroundAttr( pInfo->nToken, pInsDefault->aItemSet, sal_True ); break; case RTF_BRDRDEF: ((SvxRTFParser*)pInfo->pParser)->ReadBorderAttr( pInfo->nToken, pInsDefault->aItemSet, sal_True ); break; } } } }