| /************************************************************** |
| * |
| * 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_sc.hxx" |
| |
| |
| |
| //------------------------------------------------------------------------ |
| |
| #include "scitems.hxx" |
| #include <editeng/eeitem.hxx> |
| |
| |
| #include <editeng/editeng.hxx> |
| #include <editeng/fhgtitem.hxx> |
| #include <editeng/svxrtf.hxx> |
| #include <vcl/outdev.hxx> |
| #include <svtools/rtftoken.h> |
| |
| #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<SCCOL>(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<SCCOL>(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; |
| } |
| } |
| } |
| } |
| |
| |
| |
| |