Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "scitems.hxx"
21 : #include <editeng/eeitem.hxx>
22 :
23 :
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>
29 :
30 : #define SC_RTFPARSE_CXX
31 : #include "rtfparse.hxx"
32 : #include "global.hxx"
33 : #include "document.hxx"
34 : #include "docpool.hxx"
35 :
36 : #define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
37 :
38 :
39 0 : ScRTFParser::ScRTFParser( EditEngine* pEditP ) :
40 : ScEEParser( pEditP ),
41 : mnCurPos(0),
42 0 : pColTwips( new ScRTFColTwips ),
43 : pActDefault( NULL ),
44 : pDefMerge( NULL ),
45 : nStartAdjust( (sal_uLong)~0 ),
46 : nLastWidth(0),
47 0 : bNewDef( false )
48 : {
49 : // RTF default FontSize 12Pt
50 0 : long nMM = OutputDevice::LogicToLogic( 12, MAP_POINT, MAP_100TH_MM );
51 0 : pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
52 : // Free-flying pInsDefault
53 0 : pInsDefault = new ScRTFCellDefault( pPool );
54 0 : }
55 :
56 :
57 0 : ScRTFParser::~ScRTFParser()
58 : {
59 0 : delete pInsDefault;
60 0 : delete pColTwips;
61 0 : maDefaultList.clear();
62 0 : }
63 :
64 :
65 0 : sal_uLong ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
66 : {
67 0 : Link aOldLink = pEdit->GetImportHdl();
68 0 : pEdit->SetImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
69 0 : sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_RTF );
70 0 : if ( nLastToken == RTF_PAR )
71 : {
72 0 : if ( !maList.empty() )
73 : {
74 0 : ScEEParseEntry* pE = maList.back();
75 0 : if ( // Completely empty
76 0 : ( ( pE->aSel.nStartPara == pE->aSel.nEndPara
77 0 : && pE->aSel.nStartPos == pE->aSel.nEndPos
78 : )
79 0 : || // Empty paragraph
80 0 : ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara
81 0 : && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara )
82 0 : && pE->aSel.nEndPos == 0
83 : )
84 : )
85 : )
86 : { // Don't take over the last paragraph
87 0 : maList.pop_back();
88 : }
89 : }
90 : }
91 0 : ColAdjust();
92 0 : pEdit->SetImportHdl( aOldLink );
93 0 : return nErr;
94 : }
95 :
96 :
97 0 : void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel )
98 : {
99 : // Paragraph -2 strips the attached empty paragraph
100 0 : pE->aSel.nEndPara = aSel.nEndPara - 2;
101 : // Although it's called nEndPos, the last one is position + 1
102 0 : pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 );
103 0 : }
104 :
105 :
106 0 : inline void ScRTFParser::NextRow()
107 : {
108 0 : if ( nRowMax < ++nRowCnt )
109 0 : nRowMax = nRowCnt;
110 0 : }
111 :
112 :
113 0 : bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
114 : {
115 0 : ScRTFColTwips::const_iterator it = pColTwips->find( nTwips );
116 0 : sal_Bool bFound = it != pColTwips->end();
117 0 : sal_uInt16 nPos = it - pColTwips->begin();
118 0 : *pCol = static_cast<SCCOL>(nPos);
119 0 : if ( bFound )
120 0 : return true;
121 0 : sal_uInt16 nCount = pColTwips->size();
122 0 : if ( !nCount )
123 0 : return false;
124 0 : SCCOL nCol = *pCol;
125 : // nCol is insertion position; the next one higher up is there (or not)
126 0 : if ( nCol < static_cast<SCCOL>(nCount) && (((*pColTwips)[nCol] - SC_RTFTWIPTOL) <= nTwips) )
127 0 : return true;
128 : // Not smaller than everything else? Then compare with the next lower one
129 0 : else if ( nCol != 0 && (((*pColTwips)[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
130 : {
131 0 : (*pCol)--;
132 0 : return true;
133 : }
134 0 : return false;
135 : }
136 :
137 :
138 0 : void ScRTFParser::ColAdjust()
139 : {
140 0 : if ( nStartAdjust != (sal_uLong)~0 )
141 : {
142 0 : SCCOL nCol = 0;
143 : ScEEParseEntry* pE;
144 0 : for ( size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++ i )
145 : {
146 0 : pE = maList[ i ];
147 0 : if ( pE->nCol == 0 )
148 0 : nCol = 0;
149 0 : pE->nCol = nCol;
150 0 : if ( pE->nColOverlap > 1 )
151 0 : nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
152 : else
153 : {
154 0 : SeekTwips( pE->nTwips, &nCol );
155 0 : if ( ++nCol <= pE->nCol )
156 0 : nCol = pE->nCol + 1; // Moved cell X
157 0 : pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
158 : }
159 0 : if ( nCol > nColMax )
160 0 : nColMax = nCol;
161 : }
162 0 : nStartAdjust = (sal_uLong)~0;
163 0 : pColTwips->clear();
164 : }
165 0 : }
166 :
167 :
168 0 : IMPL_LINK( ScRTFParser, RTFImportHdl, ImportInfo*, pInfo )
169 : {
170 0 : switch ( pInfo->eState )
171 : {
172 : case RTFIMP_NEXTTOKEN:
173 0 : ProcToken( pInfo );
174 0 : break;
175 : case RTFIMP_UNKNOWNATTR:
176 0 : ProcToken( pInfo );
177 0 : break;
178 : case RTFIMP_START:
179 : {
180 0 : SvxRTFParser* pParser = (SvxRTFParser*) pInfo->pParser;
181 0 : pParser->SetAttrPool( pPool );
182 0 : RTFPardAttrMapIds& rMap = pParser->GetPardMap();
183 0 : rMap.nBrush = ATTR_BACKGROUND;
184 0 : rMap.nBox = ATTR_BORDER;
185 0 : rMap.nShadow = ATTR_SHADOW;
186 : }
187 0 : break;
188 : case RTFIMP_END:
189 0 : if ( pInfo->aSelection.nEndPos )
190 : { // If still text: create last paragraph
191 0 : pActDefault = NULL;
192 0 : pInfo->nToken = RTF_PAR;
193 : // EditEngine did not attach an empty paragraph anymore
194 : // which EntryEnd could strip
195 0 : pInfo->aSelection.nEndPara++;
196 0 : ProcToken( pInfo );
197 : }
198 0 : break;
199 : case RTFIMP_SETATTR:
200 0 : break;
201 : case RTFIMP_INSERTTEXT:
202 0 : break;
203 : case RTFIMP_INSERTPARA:
204 0 : break;
205 : default:
206 : OSL_FAIL("unknown ImportInfo.eState");
207 : }
208 0 : return 0;
209 : }
210 :
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 0 : void ScRTFParser::NewCellRow( ImportInfo* /*pInfo*/ )
215 : {
216 0 : if ( bNewDef )
217 : {
218 0 : bNewDef = false;
219 : // Not flush on the right? => new table
220 0 : if ( nLastWidth && !maDefaultList.empty() )
221 : {
222 0 : const ScRTFCellDefault& rD = maDefaultList.back();
223 0 : if (rD.nTwips != nLastWidth)
224 : {
225 : SCCOL n1, n2;
226 0 : if ( !( SeekTwips( nLastWidth, &n1 )
227 0 : && SeekTwips( rD.nTwips, &n2 )
228 0 : && n1 == n2
229 0 : )
230 : )
231 : {
232 0 : ColAdjust();
233 : }
234 : }
235 : }
236 : // Build up TwipCols only after nLastWidth comparison!
237 0 : for ( size_t i = 0, n = maDefaultList.size(); i < n; ++i )
238 : {
239 0 : const ScRTFCellDefault& rD = maDefaultList[i];
240 : SCCOL nCol;
241 0 : if ( !SeekTwips(rD.nTwips, &nCol) )
242 0 : pColTwips->insert( rD.nTwips );
243 : }
244 : }
245 0 : pDefMerge = NULL;
246 0 : pActDefault = maDefaultList.empty() ? NULL : &maDefaultList[0];
247 0 : mnCurPos = 0;
248 : OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
249 0 : }
250 :
251 :
252 : /*
253 : SW:
254 : ~~~
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]
264 :
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]
276 :
277 : */
278 :
279 0 : void ScRTFParser::ProcToken( ImportInfo* pInfo )
280 : {
281 : ScEEParseEntry* pE;
282 0 : switch ( pInfo->nToken )
283 : {
284 : case RTF_TROWD: // denotes table row defauls, before RTF_CELLX
285 : {
286 0 : if (!maDefaultList.empty())
287 0 : nLastWidth = maDefaultList.back().nTwips;
288 :
289 0 : nColCnt = 0;
290 0 : maDefaultList.clear();
291 0 : pDefMerge = NULL;
292 0 : nLastToken = pInfo->nToken;
293 0 : mnCurPos = 0;
294 : }
295 0 : break;
296 : case RTF_CLMGF: // The first cell of cells to be merged
297 : {
298 0 : pDefMerge = pInsDefault;
299 0 : nLastToken = pInfo->nToken;
300 : }
301 0 : break;
302 : case RTF_CLMRG: // A cell to be merged with the preceding cell
303 : {
304 0 : if (!pDefMerge && !maDefaultList.empty())
305 : {
306 0 : pDefMerge = &maDefaultList.back();
307 0 : mnCurPos = maDefaultList.size() - 1;
308 : }
309 : OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
310 0 : if ( pDefMerge ) // Else broken RTF
311 0 : pDefMerge->nColOverlap++; // multiple successive ones possible
312 0 : pInsDefault->nColOverlap = 0; // Flag: ignore these
313 0 : nLastToken = pInfo->nToken;
314 : }
315 0 : break;
316 : case RTF_CELLX: // closes cell default
317 : {
318 0 : bNewDef = true;
319 0 : pInsDefault->nCol = nColCnt;
320 0 : pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
321 0 : maDefaultList.push_back( pInsDefault );
322 : // New free-flying pInsDefault
323 0 : pInsDefault = new ScRTFCellDefault( pPool );
324 0 : if ( ++nColCnt > nColMax )
325 0 : nColMax = nColCnt;
326 0 : nLastToken = pInfo->nToken;
327 : }
328 0 : break;
329 : case RTF_INTBL: // before the first RTF_CELL
330 : {
331 : // Once over NextToken and once over UnknownAttrToken
332 : // or e.g. \intbl ... \cell \pard \intbl ... \cell
333 0 : if ( nLastToken != RTF_INTBL && nLastToken != RTF_CELL && nLastToken != RTF_PAR )
334 : {
335 0 : NewCellRow( pInfo );
336 0 : nLastToken = pInfo->nToken;
337 : }
338 : }
339 0 : break;
340 : case RTF_CELL: // denotes the end of a cell.
341 : {
342 : OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
343 0 : if ( bNewDef || !pActDefault )
344 0 : NewCellRow( pInfo ); // davor war kein \intbl, bad behavior
345 : // Broken RTF? Let's save what we can
346 0 : if ( !pActDefault )
347 0 : pActDefault = pInsDefault;
348 0 : if ( pActDefault->nColOverlap > 0 )
349 : { // Not merged with preceding
350 0 : pActEntry->nCol = pActDefault->nCol;
351 0 : pActEntry->nColOverlap = pActDefault->nColOverlap;
352 0 : pActEntry->nTwips = pActDefault->nTwips;
353 0 : pActEntry->nRow = nRowCnt;
354 0 : pActEntry->aItemSet.Set( pActDefault->aItemSet );
355 0 : EntryEnd( pActEntry, pInfo->aSelection );
356 :
357 0 : if ( nStartAdjust == (sal_uLong)~0 )
358 0 : nStartAdjust = maList.size();
359 0 : maList.push_back( pActEntry );
360 0 : NewActEntry( pActEntry ); // New free-flying pActEntry
361 : }
362 : else
363 : { // Assign current Twips to MergeCell
364 0 : if ( !maList.empty() )
365 : {
366 0 : pE = maList.back();
367 0 : pE->nTwips = pActDefault->nTwips;
368 : }
369 : // Adjust selection of free-flying pActEntry
370 : // Paragraph -1 due to separated text in EditEngine during parsing
371 0 : pActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1;
372 : }
373 :
374 0 : pActDefault = NULL;
375 0 : if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
376 0 : pActDefault = &maDefaultList[++mnCurPos];
377 :
378 0 : nLastToken = pInfo->nToken;
379 : }
380 0 : break;
381 : case RTF_ROW: // denotes the end of a row
382 : {
383 0 : NextRow();
384 0 : nLastToken = pInfo->nToken;
385 : }
386 0 : break;
387 : case RTF_PAR: // Paragraph
388 : {
389 0 : if ( !pActDefault )
390 : { // text not in table
391 0 : ColAdjust(); // close the processing table
392 0 : pActEntry->nCol = 0;
393 0 : pActEntry->nRow = nRowCnt;
394 0 : EntryEnd( pActEntry, pInfo->aSelection );
395 0 : maList.push_back( pActEntry );
396 0 : NewActEntry( pActEntry ); // new pActEntry
397 0 : NextRow();
398 : }
399 0 : nLastToken = pInfo->nToken;
400 : }
401 0 : break;
402 : default:
403 : { // do not set nLastToken
404 0 : switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
405 : {
406 : case RTF_SHADINGDEF:
407 : ((SvxRTFParser*)pInfo->pParser)->ReadBackgroundAttr(
408 0 : pInfo->nToken, pInsDefault->aItemSet, sal_True );
409 0 : break;
410 : case RTF_BRDRDEF:
411 : ((SvxRTFParser*)pInfo->pParser)->ReadBorderAttr(
412 0 : pInfo->nToken, pInsDefault->aItemSet, sal_True );
413 0 : break;
414 : }
415 : }
416 : }
417 0 : }
418 :
419 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|