blob: 6dbc745933f365e79897bada92ff59c741fb996f [file] [log] [blame]
/**************************************************************
*
* 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;
}
}
}
}