tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / sc / source / filter / rtf / rtfparse.cxx
blob3143ba36f52e0c20d4af3754dd7d4e3814ba06af
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 <memory>
21 #include <scitems.hxx>
22 #include <editeng/eeitem.hxx>
23 #include <editeng/editeng.hxx>
24 #include <editeng/editids.hrc>
25 #include <editeng/fhgtitem.hxx>
26 #include <editeng/svxrtf.hxx>
27 #include <svtools/rtftoken.h>
28 #include <osl/diagnose.h>
29 #include <svl/itempool.hxx>
31 #include <rtfparse.hxx>
33 #define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
35 ScRTFParser::ScRTFParser( EditEngine* pEditP ) :
36 ScEEParser( pEditP ),
37 mnCurPos(0),
38 pActDefault( nullptr ),
39 pDefMerge( nullptr ),
40 nStartAdjust( sal_uLong(~0) ),
41 nLastWidth(0),
42 bNewDef( false )
44 // RTF default FontSize 12Pt
45 tools::Long nMM = o3tl::convert(12, o3tl::Length::pt, o3tl::Length::mm100);
47 pPool->SetUserDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
48 // Free-flying pInsDefault
49 pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
52 ScRTFParser::~ScRTFParser()
54 pInsDefault.reset();
55 maDefaultList.clear();
58 ErrCode ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
60 Link<RtfImportInfo&,void> aOldLink = pEdit->GetRtfImportHdl();
61 pEdit->SetRtfImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
62 ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Rtf );
63 if ( nRtfLastToken == RTF_PAR )
65 if ( !maList.empty() )
67 auto& pE = maList.back();
68 if ( // Completely empty
69 ( !pE->aSel.HasRange() )
70 || // Empty paragraph
71 ( pE->aSel.start.nPara + 1 == pE->aSel.end.nPara
72 && pE->aSel.start.nIndex == pEdit->GetTextLen( pE->aSel.start.nPara )
73 && pE->aSel.end.nIndex == 0
76 { // Don't take over the last paragraph
77 maList.pop_back();
81 ColAdjust();
82 pEdit->SetRtfImportHdl( aOldLink );
83 return nErr;
86 void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel )
88 // Paragraph -2 strips the attached empty paragraph
89 pE->aSel.end.nPara = aSel.end.nPara - 2;
90 // Although it's called nEndPos, the last one is position + 1
91 pE->aSel.end.nIndex = pEdit->GetTextLen(aSel.end.nPara - 1);
94 inline void ScRTFParser::NextRow()
96 if ( nRowMax < ++nRowCnt )
97 nRowMax = nRowCnt;
100 bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
102 ScRTFColTwips::const_iterator it = aColTwips.lower_bound( nTwips );
103 bool bFound = it != aColTwips.end() && *it == nTwips;
104 sal_uInt16 nPos = it - aColTwips.begin();
105 *pCol = static_cast<SCCOL>(nPos);
106 if ( bFound )
107 return true;
108 sal_uInt16 nCount = aColTwips.size();
109 if ( !nCount )
110 return false;
111 SCCOL nCol = *pCol;
112 // nCol is insertion position; the next one higher up is there (or not)
113 if ( nCol < static_cast<SCCOL>(nCount) && ((aColTwips[nCol] - SC_RTFTWIPTOL) <= nTwips) )
114 return true;
115 // Not smaller than everything else? Then compare with the next lower one
116 else if ( nCol != 0 && ((aColTwips[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
118 (*pCol)--;
119 return true;
121 return false;
124 void ScRTFParser::ColAdjust()
126 if ( nStartAdjust == sal_uLong(~0) )
127 return;
129 SCCOL nCol = 0;
130 for (size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++i)
132 auto& pE = maList[i];
133 if ( pE->nCol == 0 )
134 nCol = 0;
135 pE->nCol = nCol;
136 if ( pE->nColOverlap > 1 )
137 nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
138 else
140 SeekTwips( pE->nTwips, &nCol );
141 if ( ++nCol <= pE->nCol )
142 nCol = pE->nCol + 1; // Moved cell X
143 pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
145 if ( nCol > nColMax )
146 nColMax = nCol;
148 nStartAdjust = sal_uLong(~0);
149 aColTwips.clear();
152 IMPL_LINK( ScRTFParser, RTFImportHdl, RtfImportInfo&, rInfo, void )
154 switch ( rInfo.eState )
156 case RtfImportState::NextToken:
157 ProcToken( &rInfo );
158 break;
159 case RtfImportState::UnknownAttr:
160 ProcToken( &rInfo );
161 break;
162 case RtfImportState::Start:
164 SvxRTFParser* pParser = static_cast<SvxRTFParser*>(rInfo.pParser);
165 pParser->SetAttrPool( pPool.get() );
166 pParser->SetPardMap(SID_ATTR_BRUSH, ATTR_BACKGROUND);
167 pParser->SetPardMap(SID_ATTR_BORDER_OUTER, ATTR_BORDER);
168 pParser->SetPardMap(SID_ATTR_BORDER_SHADOW, ATTR_SHADOW);
170 break;
171 case RtfImportState::End:
172 if ( rInfo.aSelection.end.nIndex )
173 { // If still text: create last paragraph
174 pActDefault = nullptr;
175 rInfo.nToken = RTF_PAR;
176 // EditEngine did not attach an empty paragraph anymore
177 // which EntryEnd could strip
178 rInfo.aSelection.end.nPara++;
179 ProcToken( &rInfo );
181 break;
182 case RtfImportState::SetAttr:
183 break;
184 case RtfImportState::InsertText:
185 break;
186 case RtfImportState::InsertPara:
187 break;
188 default:
189 OSL_FAIL("unknown ImportInfo.eState");
193 // Bad behavior:
194 // For RTF_INTBL or respectively at the start of the first RTF_CELL
195 // after RTF_CELLX if there was no RTF_INTBL
196 void ScRTFParser::NewCellRow()
198 if ( bNewDef )
200 bNewDef = false;
201 // Not flush on the right? => new table
202 if ( nLastWidth && !maDefaultList.empty() )
204 const ScRTFCellDefault& rD = *maDefaultList.back();
205 if (rD.nTwips != nLastWidth)
207 SCCOL n1, n2;
208 if ( !( SeekTwips( nLastWidth, &n1 )
209 && SeekTwips( rD.nTwips, &n2 )
210 && n1 == n2
214 ColAdjust();
218 // Build up TwipCols only after nLastWidth comparison!
219 for (const std::unique_ptr<ScRTFCellDefault> & pCellDefault : maDefaultList)
221 const ScRTFCellDefault& rD = *pCellDefault;
222 SCCOL nCol;
223 if ( !SeekTwips(rD.nTwips, &nCol) )
224 aColTwips.insert( rD.nTwips );
227 pDefMerge = nullptr;
228 pActDefault = maDefaultList.empty() ? nullptr : maDefaultList[0].get();
229 mnCurPos = 0;
230 OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
236 [\par]
237 \trowd \cellx \cellx ...
238 \intbl \cell \cell ...
239 \row
240 [\par]
241 [\trowd \cellx \cellx ...]
242 \intbl \cell \cell ...
243 \row
244 [\par]
246 M$-Word:
247 ~~~~~~~~
248 [\par]
249 \trowd \cellx \cellx ...
250 \intbl \cell \cell ...
251 \intbl \row
252 [\par]
253 [\trowd \cellx \cellx ...]
254 \intbl \cell \cell ...
255 \intbl \row
256 [\par]
260 void ScRTFParser::ProcToken( RtfImportInfo* pInfo )
262 switch ( pInfo->nToken )
264 case RTF_TROWD: // denotes table row default, before RTF_CELLX
266 if (!maDefaultList.empty())
267 nLastWidth = maDefaultList.back()->nTwips;
269 nColCnt = 0;
270 if (pActDefault != pInsDefault.get())
271 pActDefault = nullptr;
272 maDefaultList.clear();
273 pDefMerge = nullptr;
274 nRtfLastToken = pInfo->nToken;
275 mnCurPos = 0;
277 break;
278 case RTF_CLMGF: // The first cell of cells to be merged
280 pDefMerge = pInsDefault.get();
281 nRtfLastToken = pInfo->nToken;
283 break;
284 case RTF_CLMRG: // A cell to be merged with the preceding cell
286 if (!pDefMerge && !maDefaultList.empty())
288 pDefMerge = maDefaultList.back().get();
289 mnCurPos = maDefaultList.size() - 1;
291 OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
292 if ( pDefMerge ) // Else broken RTF
293 pDefMerge->nColOverlap++; // multiple successive ones possible
294 pInsDefault->nColOverlap = 0; // Flag: ignore these
295 nRtfLastToken = pInfo->nToken;
297 break;
298 case RTF_CELLX: // closes cell default
300 bNewDef = true;
301 pInsDefault->nCol = nColCnt;
302 pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
303 maDefaultList.push_back( std::move(pInsDefault) );
304 // New free-flying pInsDefault
305 pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
306 if ( ++nColCnt > nColMax )
307 nColMax = nColCnt;
308 nRtfLastToken = pInfo->nToken;
310 break;
311 case RTF_INTBL: // before the first RTF_CELL
313 // Once over NextToken and once over UnknownAttrToken
314 // or e.g. \intbl ... \cell \pard \intbl ... \cell
315 if ( nRtfLastToken != RTF_INTBL && nRtfLastToken != RTF_CELL && nRtfLastToken != RTF_PAR )
317 NewCellRow();
318 nRtfLastToken = pInfo->nToken;
321 break;
322 case RTF_CELL: // denotes the end of a cell.
324 OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
325 if ( bNewDef || !pActDefault )
326 NewCellRow(); // before was no \intbl, bad behavior
327 // Broken RTF? Let's save what we can
328 if ( !pActDefault )
329 pActDefault = pInsDefault.get();
330 if ( pActDefault->nColOverlap > 0 )
331 { // Not merged with preceding
332 mxActEntry->nCol = pActDefault->nCol;
333 mxActEntry->nColOverlap = pActDefault->nColOverlap;
334 mxActEntry->nTwips = pActDefault->nTwips;
335 mxActEntry->nRow = nRowCnt;
336 mxActEntry->aItemSet.Set(pActDefault->aItemSet);
337 EntryEnd(mxActEntry.get(), pInfo->aSelection);
339 if ( nStartAdjust == sal_uLong(~0) )
340 nStartAdjust = maList.size();
341 maList.push_back(mxActEntry);
342 NewActEntry(mxActEntry.get()); // New free-flying mxActEntry
344 else
345 { // Assign current Twips to MergeCell
346 if ( !maList.empty() )
348 auto& pE = maList.back();
349 pE->nTwips = pActDefault->nTwips;
351 // Adjust selection of free-flying mxActEntry
352 // Paragraph -1 due to separated text in EditEngine during parsing
353 mxActEntry->aSel.start.nPara = pInfo->aSelection.end.nPara - 1;
356 pActDefault = nullptr;
357 if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
358 pActDefault = maDefaultList[++mnCurPos].get();
360 nRtfLastToken = pInfo->nToken;
362 break;
363 case RTF_ROW: // denotes the end of a row
365 NextRow();
366 nRtfLastToken = pInfo->nToken;
368 break;
369 case RTF_PAR: // Paragraph
371 if ( !pActDefault )
372 { // text not in table
373 ColAdjust(); // close the processing table
374 mxActEntry->nCol = 0;
375 mxActEntry->nRow = nRowCnt;
376 EntryEnd(mxActEntry.get(), pInfo->aSelection);
377 maList.push_back(mxActEntry);
378 NewActEntry(mxActEntry.get()); // new mxActEntry
379 NextRow();
381 nRtfLastToken = pInfo->nToken;
383 break;
384 default:
385 { // do not set nRtfLastToken
386 switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
388 case RTF_SHADINGDEF:
389 static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBackgroundAttr(
390 pInfo->nToken, pInsDefault->aItemSet, true );
391 break;
392 case RTF_BRDRDEF:
393 static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(
394 pInfo->nToken, pInsDefault->aItemSet, true );
395 break;
401 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */