Bump version to 4.3-4
[LibreOffice.git] / sc / source / filter / rtf / rtfparse.cxx
blob3449bb177cb5688078a19e44a87df9b0de64e4f3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "scitems.hxx"
21 #include <editeng/eeitem.hxx>
24 #include <editeng/editeng.hxx>
25 #include <editeng/fhgtitem.hxx>
26 #include <editeng/svxrtf.hxx>
27 #include <vcl/outdev.hxx>
28 #include <svtools/rtftoken.h>
30 #define SC_RTFPARSE_CXX
31 #include "rtfparse.hxx"
32 #include "global.hxx"
33 #include "document.hxx"
34 #include "docpool.hxx"
36 #define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
39 ScRTFParser::ScRTFParser( EditEngine* pEditP ) :
40 ScEEParser( pEditP ),
41 mnCurPos(0),
42 pColTwips( new ScRTFColTwips ),
43 pActDefault( NULL ),
44 pDefMerge( NULL ),
45 nStartAdjust( (sal_uLong)~0 ),
46 nLastWidth(0),
47 bNewDef( false )
49 // RTF default FontSize 12Pt
50 long nMM = OutputDevice::LogicToLogic( 12, MAP_POINT, MAP_100TH_MM );
51 pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
52 // Free-flying pInsDefault
53 pInsDefault = new ScRTFCellDefault( pPool );
57 ScRTFParser::~ScRTFParser()
59 delete pInsDefault;
60 delete pColTwips;
61 maDefaultList.clear();
65 sal_uLong ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
67 Link aOldLink = pEdit->GetImportHdl();
68 pEdit->SetImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
69 sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_RTF );
70 if ( nLastToken == RTF_PAR )
72 if ( !maList.empty() )
74 ScEEParseEntry* pE = maList.back();
75 if ( // Completely empty
76 ( ( pE->aSel.nStartPara == pE->aSel.nEndPara
77 && pE->aSel.nStartPos == pE->aSel.nEndPos
79 || // Empty paragraph
80 ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara
81 && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara )
82 && pE->aSel.nEndPos == 0
86 { // Don't take over the last paragraph
87 maList.pop_back();
91 ColAdjust();
92 pEdit->SetImportHdl( aOldLink );
93 return nErr;
97 void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel )
99 // Paragraph -2 strips the attached empty paragraph
100 pE->aSel.nEndPara = aSel.nEndPara - 2;
101 // Although it's called nEndPos, the last one is position + 1
102 pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 );
106 inline void ScRTFParser::NextRow()
108 if ( nRowMax < ++nRowCnt )
109 nRowMax = nRowCnt;
113 bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
115 ScRTFColTwips::const_iterator it = pColTwips->find( nTwips );
116 bool bFound = it != pColTwips->end();
117 sal_uInt16 nPos = it - pColTwips->begin();
118 *pCol = static_cast<SCCOL>(nPos);
119 if ( bFound )
120 return true;
121 sal_uInt16 nCount = pColTwips->size();
122 if ( !nCount )
123 return false;
124 SCCOL nCol = *pCol;
125 // nCol is insertion position; the next one higher up is there (or not)
126 if ( nCol < static_cast<SCCOL>(nCount) && (((*pColTwips)[nCol] - SC_RTFTWIPTOL) <= nTwips) )
127 return true;
128 // Not smaller than everything else? Then compare with the next lower one
129 else if ( nCol != 0 && (((*pColTwips)[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
131 (*pCol)--;
132 return true;
134 return false;
138 void ScRTFParser::ColAdjust()
140 if ( nStartAdjust != (sal_uLong)~0 )
142 SCCOL nCol = 0;
143 ScEEParseEntry* pE;
144 for ( size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++ i )
146 pE = maList[ i ];
147 if ( pE->nCol == 0 )
148 nCol = 0;
149 pE->nCol = nCol;
150 if ( pE->nColOverlap > 1 )
151 nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
152 else
154 SeekTwips( pE->nTwips, &nCol );
155 if ( ++nCol <= pE->nCol )
156 nCol = pE->nCol + 1; // Moved cell X
157 pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
159 if ( nCol > nColMax )
160 nColMax = nCol;
162 nStartAdjust = (sal_uLong)~0;
163 pColTwips->clear();
168 IMPL_LINK( ScRTFParser, RTFImportHdl, ImportInfo*, pInfo )
170 switch ( pInfo->eState )
172 case RTFIMP_NEXTTOKEN:
173 ProcToken( pInfo );
174 break;
175 case RTFIMP_UNKNOWNATTR:
176 ProcToken( pInfo );
177 break;
178 case RTFIMP_START:
180 SvxRTFParser* pParser = (SvxRTFParser*) pInfo->pParser;
181 pParser->SetAttrPool( pPool );
182 RTFPardAttrMapIds& rMap = pParser->GetPardMap();
183 rMap.nBrush = ATTR_BACKGROUND;
184 rMap.nBox = ATTR_BORDER;
185 rMap.nShadow = ATTR_SHADOW;
187 break;
188 case RTFIMP_END:
189 if ( pInfo->aSelection.nEndPos )
190 { // If still text: create last paragraph
191 pActDefault = NULL;
192 pInfo->nToken = RTF_PAR;
193 // EditEngine did not attach an empty paragraph anymore
194 // which EntryEnd could strip
195 pInfo->aSelection.nEndPara++;
196 ProcToken( pInfo );
198 break;
199 case RTFIMP_SETATTR:
200 break;
201 case RTFIMP_INSERTTEXT:
202 break;
203 case RTFIMP_INSERTPARA:
204 break;
205 default:
206 OSL_FAIL("unknown ImportInfo.eState");
208 return 0;
211 // Bad behavior:
212 // For RTF_INTBL or respectively at the start of the first RTF_CELL
213 // after RTF_CELLX if there was no RTF_INTBL
214 void ScRTFParser::NewCellRow( ImportInfo* /*pInfo*/ )
216 if ( bNewDef )
218 bNewDef = false;
219 // Not flush on the right? => new table
220 if ( nLastWidth && !maDefaultList.empty() )
222 const ScRTFCellDefault& rD = maDefaultList.back();
223 if (rD.nTwips != nLastWidth)
225 SCCOL n1, n2;
226 if ( !( SeekTwips( nLastWidth, &n1 )
227 && SeekTwips( rD.nTwips, &n2 )
228 && n1 == n2
232 ColAdjust();
236 // Build up TwipCols only after nLastWidth comparison!
237 for ( size_t i = 0, n = maDefaultList.size(); i < n; ++i )
239 const ScRTFCellDefault& rD = maDefaultList[i];
240 SCCOL nCol;
241 if ( !SeekTwips(rD.nTwips, &nCol) )
242 pColTwips->insert( rD.nTwips );
245 pDefMerge = NULL;
246 pActDefault = maDefaultList.empty() ? NULL : &maDefaultList[0];
247 mnCurPos = 0;
248 OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
255 [\par]
256 \trowd \cellx \cellx ...
257 \intbl \cell \cell ...
258 \row
259 [\par]
260 [\trowd \cellx \cellx ...]
261 \intbl \cell \cell ...
262 \row
263 [\par]
265 M$-Word:
266 ~~~~~~~~
267 [\par]
268 \trowd \cellx \cellx ...
269 \intbl \cell \cell ...
270 \intbl \row
271 [\par]
272 [\trowd \cellx \cellx ...]
273 \intbl \cell \cell ...
274 \intbl \row
275 [\par]
279 void ScRTFParser::ProcToken( ImportInfo* pInfo )
281 ScEEParseEntry* pE;
282 switch ( pInfo->nToken )
284 case RTF_TROWD: // denotes table row defauls, before RTF_CELLX
286 if (!maDefaultList.empty())
287 nLastWidth = maDefaultList.back().nTwips;
289 nColCnt = 0;
290 maDefaultList.clear();
291 pDefMerge = NULL;
292 nLastToken = pInfo->nToken;
293 mnCurPos = 0;
295 break;
296 case RTF_CLMGF: // The first cell of cells to be merged
298 pDefMerge = pInsDefault;
299 nLastToken = pInfo->nToken;
301 break;
302 case RTF_CLMRG: // A cell to be merged with the preceding cell
304 if (!pDefMerge && !maDefaultList.empty())
306 pDefMerge = &maDefaultList.back();
307 mnCurPos = maDefaultList.size() - 1;
309 OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
310 if ( pDefMerge ) // Else broken RTF
311 pDefMerge->nColOverlap++; // multiple successive ones possible
312 pInsDefault->nColOverlap = 0; // Flag: ignore these
313 nLastToken = pInfo->nToken;
315 break;
316 case RTF_CELLX: // closes cell default
318 bNewDef = true;
319 pInsDefault->nCol = nColCnt;
320 pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
321 maDefaultList.push_back( pInsDefault );
322 // New free-flying pInsDefault
323 pInsDefault = new ScRTFCellDefault( pPool );
324 if ( ++nColCnt > nColMax )
325 nColMax = nColCnt;
326 nLastToken = pInfo->nToken;
328 break;
329 case RTF_INTBL: // before the first RTF_CELL
331 // Once over NextToken and once over UnknownAttrToken
332 // or e.g. \intbl ... \cell \pard \intbl ... \cell
333 if ( nLastToken != RTF_INTBL && nLastToken != RTF_CELL && nLastToken != RTF_PAR )
335 NewCellRow( pInfo );
336 nLastToken = pInfo->nToken;
339 break;
340 case RTF_CELL: // denotes the end of a cell.
342 OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
343 if ( bNewDef || !pActDefault )
344 NewCellRow( pInfo ); // davor war kein \intbl, bad behavior
345 // Broken RTF? Let's save what we can
346 if ( !pActDefault )
347 pActDefault = pInsDefault;
348 if ( pActDefault->nColOverlap > 0 )
349 { // Not merged with preceding
350 pActEntry->nCol = pActDefault->nCol;
351 pActEntry->nColOverlap = pActDefault->nColOverlap;
352 pActEntry->nTwips = pActDefault->nTwips;
353 pActEntry->nRow = nRowCnt;
354 pActEntry->aItemSet.Set( pActDefault->aItemSet );
355 EntryEnd( pActEntry, pInfo->aSelection );
357 if ( nStartAdjust == (sal_uLong)~0 )
358 nStartAdjust = maList.size();
359 maList.push_back( pActEntry );
360 NewActEntry( pActEntry ); // New free-flying pActEntry
362 else
363 { // Assign current Twips to MergeCell
364 if ( !maList.empty() )
366 pE = maList.back();
367 pE->nTwips = pActDefault->nTwips;
369 // Adjust selection of free-flying pActEntry
370 // Paragraph -1 due to separated text in EditEngine during parsing
371 pActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1;
374 pActDefault = NULL;
375 if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
376 pActDefault = &maDefaultList[++mnCurPos];
378 nLastToken = pInfo->nToken;
380 break;
381 case RTF_ROW: // denotes the end of a row
383 NextRow();
384 nLastToken = pInfo->nToken;
386 break;
387 case RTF_PAR: // Paragraph
389 if ( !pActDefault )
390 { // text not in table
391 ColAdjust(); // close the processing table
392 pActEntry->nCol = 0;
393 pActEntry->nRow = nRowCnt;
394 EntryEnd( pActEntry, pInfo->aSelection );
395 maList.push_back( pActEntry );
396 NewActEntry( pActEntry ); // new pActEntry
397 NextRow();
399 nLastToken = pInfo->nToken;
401 break;
402 default:
403 { // do not set nLastToken
404 switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
406 case RTF_SHADINGDEF:
407 ((SvxRTFParser*)pInfo->pParser)->ReadBackgroundAttr(
408 pInfo->nToken, pInsDefault->aItemSet, sal_True );
409 break;
410 case RTF_BRDRDEF:
411 ((SvxRTFParser*)pInfo->pParser)->ReadBorderAttr(
412 pInfo->nToken, pInsDefault->aItemSet, sal_True );
413 break;
419 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */