Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / sc / source / filter / rtf / rtfparse.cxx
blob48a93962574111df20969da955350174d3cf0610
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>
22 #include <editeng/editeng.hxx>
23 #include <editeng/fhgtitem.hxx>
24 #include <editeng/svxrtf.hxx>
25 #include <vcl/outdev.hxx>
26 #include <svtools/rtftoken.h>
28 #include "rtfparse.hxx"
29 #include "global.hxx"
30 #include "document.hxx"
31 #include "docpool.hxx"
33 #define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
35 ScRTFParser::ScRTFParser( EditEngine* pEditP ) :
36 ScEEParser( pEditP ),
37 mnCurPos(0),
38 pColTwips( new ScRTFColTwips ),
39 pActDefault( nullptr ),
40 pDefMerge( nullptr ),
41 nStartAdjust( (sal_uLong)~0 ),
42 nLastWidth(0),
43 bNewDef( false )
45 // RTF default FontSize 12Pt
46 long nMM = OutputDevice::LogicToLogic( 12, MAP_POINT, MAP_100TH_MM );
47 pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
48 // Free-flying pInsDefault
49 pInsDefault = new ScRTFCellDefault( pPool );
52 ScRTFParser::~ScRTFParser()
54 delete pInsDefault;
55 delete pColTwips;
56 maDefaultList.clear();
59 sal_uLong ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
61 Link<ImportInfo&,void> aOldLink = pEdit->GetImportHdl();
62 pEdit->SetImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
63 sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_RTF );
64 if ( nLastToken == RTF_PAR )
66 if ( !maList.empty() )
68 ScEEParseEntry* pE = maList.back();
69 if ( // Completely empty
70 ( ( pE->aSel.nStartPara == pE->aSel.nEndPara
71 && pE->aSel.nStartPos == pE->aSel.nEndPos
73 || // Empty paragraph
74 ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara
75 && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara )
76 && pE->aSel.nEndPos == 0
80 { // Don't take over the last paragraph
81 maList.pop_back();
85 ColAdjust();
86 pEdit->SetImportHdl( aOldLink );
87 return nErr;
90 void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel )
92 // Paragraph -2 strips the attached empty paragraph
93 pE->aSel.nEndPara = aSel.nEndPara - 2;
94 // Although it's called nEndPos, the last one is position + 1
95 pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 );
98 inline void ScRTFParser::NextRow()
100 if ( nRowMax < ++nRowCnt )
101 nRowMax = nRowCnt;
104 bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
106 ScRTFColTwips::const_iterator it = pColTwips->find( nTwips );
107 bool bFound = it != pColTwips->end();
108 sal_uInt16 nPos = it - pColTwips->begin();
109 *pCol = static_cast<SCCOL>(nPos);
110 if ( bFound )
111 return true;
112 sal_uInt16 nCount = pColTwips->size();
113 if ( !nCount )
114 return false;
115 SCCOL nCol = *pCol;
116 // nCol is insertion position; the next one higher up is there (or not)
117 if ( nCol < static_cast<SCCOL>(nCount) && (((*pColTwips)[nCol] - SC_RTFTWIPTOL) <= nTwips) )
118 return true;
119 // Not smaller than everything else? Then compare with the next lower one
120 else if ( nCol != 0 && (((*pColTwips)[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
122 (*pCol)--;
123 return true;
125 return false;
128 void ScRTFParser::ColAdjust()
130 if ( nStartAdjust != (sal_uLong)~0 )
132 SCCOL nCol = 0;
133 ScEEParseEntry* pE;
134 for ( size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++ i )
136 pE = maList[ i ];
137 if ( pE->nCol == 0 )
138 nCol = 0;
139 pE->nCol = nCol;
140 if ( pE->nColOverlap > 1 )
141 nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
142 else
144 SeekTwips( pE->nTwips, &nCol );
145 if ( ++nCol <= pE->nCol )
146 nCol = pE->nCol + 1; // Moved cell X
147 pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
149 if ( nCol > nColMax )
150 nColMax = nCol;
152 nStartAdjust = (sal_uLong)~0;
153 pColTwips->clear();
157 IMPL_LINK_TYPED( ScRTFParser, RTFImportHdl, ImportInfo&, rInfo, void )
159 switch ( rInfo.eState )
161 case RTFIMP_NEXTTOKEN:
162 ProcToken( &rInfo );
163 break;
164 case RTFIMP_UNKNOWNATTR:
165 ProcToken( &rInfo );
166 break;
167 case RTFIMP_START:
169 SvxRTFParser* pParser = static_cast<SvxRTFParser*>(rInfo.pParser);
170 pParser->SetAttrPool( pPool );
171 RTFPardAttrMapIds& rMap = pParser->GetPardMap();
172 rMap.nBrush = ATTR_BACKGROUND;
173 rMap.nBox = ATTR_BORDER;
174 rMap.nShadow = ATTR_SHADOW;
176 break;
177 case RTFIMP_END:
178 if ( rInfo.aSelection.nEndPos )
179 { // If still text: create last paragraph
180 pActDefault = nullptr;
181 rInfo.nToken = RTF_PAR;
182 // EditEngine did not attach an empty paragraph anymore
183 // which EntryEnd could strip
184 rInfo.aSelection.nEndPara++;
185 ProcToken( &rInfo );
187 break;
188 case RTFIMP_SETATTR:
189 break;
190 case RTFIMP_INSERTTEXT:
191 break;
192 case RTFIMP_INSERTPARA:
193 break;
194 default:
195 OSL_FAIL("unknown ImportInfo.eState");
199 // Bad behavior:
200 // For RTF_INTBL or respectively at the start of the first RTF_CELL
201 // after RTF_CELLX if there was no RTF_INTBL
202 void ScRTFParser::NewCellRow( ImportInfo* /*pInfo*/ )
204 if ( bNewDef )
206 bNewDef = false;
207 // Not flush on the right? => new table
208 if ( nLastWidth && !maDefaultList.empty() )
210 const ScRTFCellDefault& rD = *maDefaultList.back().get();
211 if (rD.nTwips != nLastWidth)
213 SCCOL n1, n2;
214 if ( !( SeekTwips( nLastWidth, &n1 )
215 && SeekTwips( rD.nTwips, &n2 )
216 && n1 == n2
220 ColAdjust();
224 // Build up TwipCols only after nLastWidth comparison!
225 for (std::unique_ptr<ScRTFCellDefault> & pCellDefault : maDefaultList)
227 const ScRTFCellDefault& rD = *pCellDefault.get();
228 SCCOL nCol;
229 if ( !SeekTwips(rD.nTwips, &nCol) )
230 pColTwips->insert( rD.nTwips );
233 pDefMerge = nullptr;
234 pActDefault = maDefaultList.empty() ? nullptr : maDefaultList[0].get();
235 mnCurPos = 0;
236 OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
242 [\par]
243 \trowd \cellx \cellx ...
244 \intbl \cell \cell ...
245 \row
246 [\par]
247 [\trowd \cellx \cellx ...]
248 \intbl \cell \cell ...
249 \row
250 [\par]
252 M$-Word:
253 ~~~~~~~~
254 [\par]
255 \trowd \cellx \cellx ...
256 \intbl \cell \cell ...
257 \intbl \row
258 [\par]
259 [\trowd \cellx \cellx ...]
260 \intbl \cell \cell ...
261 \intbl \row
262 [\par]
266 void ScRTFParser::ProcToken( ImportInfo* pInfo )
268 switch ( pInfo->nToken )
270 case RTF_TROWD: // denotes table row defauls, before RTF_CELLX
272 if (!maDefaultList.empty())
273 nLastWidth = maDefaultList.back()->nTwips;
275 nColCnt = 0;
276 maDefaultList.clear();
277 pDefMerge = nullptr;
278 nLastToken = pInfo->nToken;
279 mnCurPos = 0;
281 break;
282 case RTF_CLMGF: // The first cell of cells to be merged
284 pDefMerge = pInsDefault;
285 nLastToken = pInfo->nToken;
287 break;
288 case RTF_CLMRG: // A cell to be merged with the preceding cell
290 if (!pDefMerge && !maDefaultList.empty())
292 pDefMerge = maDefaultList.back().get();
293 mnCurPos = maDefaultList.size() - 1;
295 OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
296 if ( pDefMerge ) // Else broken RTF
297 pDefMerge->nColOverlap++; // multiple successive ones possible
298 pInsDefault->nColOverlap = 0; // Flag: ignore these
299 nLastToken = pInfo->nToken;
301 break;
302 case RTF_CELLX: // closes cell default
304 bNewDef = true;
305 pInsDefault->nCol = nColCnt;
306 pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
307 maDefaultList.push_back( std::unique_ptr<ScRTFCellDefault>(pInsDefault) );
308 // New free-flying pInsDefault
309 pInsDefault = new ScRTFCellDefault( pPool );
310 if ( ++nColCnt > nColMax )
311 nColMax = nColCnt;
312 nLastToken = pInfo->nToken;
314 break;
315 case RTF_INTBL: // before the first RTF_CELL
317 // Once over NextToken and once over UnknownAttrToken
318 // or e.g. \intbl ... \cell \pard \intbl ... \cell
319 if ( nLastToken != RTF_INTBL && nLastToken != RTF_CELL && nLastToken != RTF_PAR )
321 NewCellRow( pInfo );
322 nLastToken = pInfo->nToken;
325 break;
326 case RTF_CELL: // denotes the end of a cell.
328 OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
329 if ( bNewDef || !pActDefault )
330 NewCellRow( pInfo ); // davor war kein \intbl, bad behavior
331 // Broken RTF? Let's save what we can
332 if ( !pActDefault )
333 pActDefault = pInsDefault;
334 if ( pActDefault->nColOverlap > 0 )
335 { // Not merged with preceding
336 pActEntry->nCol = pActDefault->nCol;
337 pActEntry->nColOverlap = pActDefault->nColOverlap;
338 pActEntry->nTwips = pActDefault->nTwips;
339 pActEntry->nRow = nRowCnt;
340 pActEntry->aItemSet.Set( pActDefault->aItemSet );
341 EntryEnd( pActEntry, pInfo->aSelection );
343 if ( nStartAdjust == (sal_uLong)~0 )
344 nStartAdjust = maList.size();
345 maList.push_back( pActEntry );
346 NewActEntry( pActEntry ); // New free-flying pActEntry
348 else
349 { // Assign current Twips to MergeCell
350 if ( !maList.empty() )
352 ScEEParseEntry* pE = maList.back();
353 pE->nTwips = pActDefault->nTwips;
355 // Adjust selection of free-flying pActEntry
356 // Paragraph -1 due to separated text in EditEngine during parsing
357 pActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1;
360 pActDefault = nullptr;
361 if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
362 pActDefault = maDefaultList[++mnCurPos].get();
364 nLastToken = pInfo->nToken;
366 break;
367 case RTF_ROW: // denotes the end of a row
369 NextRow();
370 nLastToken = pInfo->nToken;
372 break;
373 case RTF_PAR: // Paragraph
375 if ( !pActDefault )
376 { // text not in table
377 ColAdjust(); // close the processing table
378 pActEntry->nCol = 0;
379 pActEntry->nRow = nRowCnt;
380 EntryEnd( pActEntry, pInfo->aSelection );
381 maList.push_back( pActEntry );
382 NewActEntry( pActEntry ); // new pActEntry
383 NextRow();
385 nLastToken = pInfo->nToken;
387 break;
388 default:
389 { // do not set nLastToken
390 switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
392 case RTF_SHADINGDEF:
393 static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBackgroundAttr(
394 pInfo->nToken, pInsDefault->aItemSet, true );
395 break;
396 case RTF_BRDRDEF:
397 static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(
398 pInfo->nToken, pInsDefault->aItemSet, true );
399 break;
405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */