LibreOffice Module sc (master) 1
rtfparse.cxx
Go to the documentation of this file.
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 <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>
30
31#include <rtfparse.hxx>
32
33#define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
34
36 ScEEParser( pEditP ),
37 mnCurPos(0),
38 pActDefault( nullptr ),
39 pDefMerge( nullptr ),
40 nStartAdjust( sal_uLong(~0) ),
41 nLastWidth(0),
42 bNewDef( false )
43{
44 // RTF default FontSize 12Pt
46
47 pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
48 // Free-flying pInsDefault
49 pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
50}
51
53{
54 pInsDefault.reset();
55 maDefaultList.clear();
56}
57
58ErrCode ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
59{
61 pEdit->SetRtfImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
62 ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Rtf );
63 if ( nRtfLastToken == RTF_PAR )
64 {
65 if ( !maList.empty() )
66 {
67 auto& pE = maList.back();
68 if ( // Completely empty
69 ( pE->aSel.nStartPara == pE->aSel.nEndPara
70 && pE->aSel.nStartPos == pE->aSel.nEndPos
71 )
72 || // Empty paragraph
73 ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara
74 && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara )
75 && pE->aSel.nEndPos == 0
76 )
77 )
78 { // Don't take over the last paragraph
79 maList.pop_back();
80 }
81 }
82 }
83 ColAdjust();
84 pEdit->SetRtfImportHdl( aOldLink );
85 return nErr;
86}
87
89{
90 // Paragraph -2 strips the attached empty paragraph
91 pE->aSel.nEndPara = aSel.nEndPara - 2;
92 // Although it's called nEndPos, the last one is position + 1
93 pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 );
94}
95
97{
98 if ( nRowMax < ++nRowCnt )
100}
101
102bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
103{
105 bool bFound = it != aColTwips.end();
106 sal_uInt16 nPos = it - aColTwips.begin();
107 *pCol = static_cast<SCCOL>(nPos);
108 if ( bFound )
109 return true;
110 sal_uInt16 nCount = aColTwips.size();
111 if ( !nCount )
112 return false;
113 SCCOL nCol = *pCol;
114 // nCol is insertion position; the next one higher up is there (or not)
115 if ( nCol < static_cast<SCCOL>(nCount) && ((aColTwips[nCol] - SC_RTFTWIPTOL) <= nTwips) )
116 return true;
117 // Not smaller than everything else? Then compare with the next lower one
118 else if ( nCol != 0 && ((aColTwips[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
119 {
120 (*pCol)--;
121 return true;
122 }
123 return false;
124}
125
127{
128 if ( nStartAdjust == sal_uLong(~0) )
129 return;
130
131 SCCOL nCol = 0;
132 for (size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++i)
133 {
134 auto& pE = maList[i];
135 if ( pE->nCol == 0 )
136 nCol = 0;
137 pE->nCol = nCol;
138 if ( pE->nColOverlap > 1 )
139 nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
140 else
141 {
142 SeekTwips( pE->nTwips, &nCol );
143 if ( ++nCol <= pE->nCol )
144 nCol = pE->nCol + 1; // Moved cell X
145 pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
146 }
147 if ( nCol > nColMax )
148 nColMax = nCol;
149 }
152}
153
154IMPL_LINK( ScRTFParser, RTFImportHdl, RtfImportInfo&, rInfo, void )
155{
156 switch ( rInfo.eState )
157 {
158 case RtfImportState::NextToken:
159 ProcToken( &rInfo );
160 break;
161 case RtfImportState::UnknownAttr:
162 ProcToken( &rInfo );
163 break;
164 case RtfImportState::Start:
165 {
166 SvxRTFParser* pParser = static_cast<SvxRTFParser*>(rInfo.pParser);
167 pParser->SetAttrPool( pPool.get() );
168 pParser->SetPardMap(SID_ATTR_BRUSH, ATTR_BACKGROUND);
169 pParser->SetPardMap(SID_ATTR_BORDER_OUTER, ATTR_BORDER);
170 pParser->SetPardMap(SID_ATTR_BORDER_SHADOW, ATTR_SHADOW);
171 }
172 break;
173 case RtfImportState::End:
174 if ( rInfo.aSelection.nEndPos )
175 { // If still text: create last paragraph
176 pActDefault = nullptr;
177 rInfo.nToken = RTF_PAR;
178 // EditEngine did not attach an empty paragraph anymore
179 // which EntryEnd could strip
180 rInfo.aSelection.nEndPara++;
181 ProcToken( &rInfo );
182 }
183 break;
184 case RtfImportState::SetAttr:
185 break;
186 case RtfImportState::InsertText:
187 break;
188 case RtfImportState::InsertPara:
189 break;
190 default:
191 OSL_FAIL("unknown ImportInfo.eState");
192 }
193}
194
195// Bad behavior:
196// For RTF_INTBL or respectively at the start of the first RTF_CELL
197// after RTF_CELLX if there was no RTF_INTBL
199{
200 if ( bNewDef )
201 {
202 bNewDef = false;
203 // Not flush on the right? => new table
204 if ( nLastWidth && !maDefaultList.empty() )
205 {
206 const ScRTFCellDefault& rD = *maDefaultList.back();
207 if (rD.nTwips != nLastWidth)
208 {
209 SCCOL n1, n2;
210 if ( !( SeekTwips( nLastWidth, &n1 )
211 && SeekTwips( rD.nTwips, &n2 )
212 && n1 == n2
213 )
214 )
215 {
216 ColAdjust();
217 }
218 }
219 }
220 // Build up TwipCols only after nLastWidth comparison!
221 for (const std::unique_ptr<ScRTFCellDefault> & pCellDefault : maDefaultList)
222 {
223 const ScRTFCellDefault& rD = *pCellDefault;
224 SCCOL nCol;
225 if ( !SeekTwips(rD.nTwips, &nCol) )
226 aColTwips.insert( rD.nTwips );
227 }
228 }
229 pDefMerge = nullptr;
230 pActDefault = maDefaultList.empty() ? nullptr : maDefaultList[0].get();
231 mnCurPos = 0;
232 OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
233}
234
235/*
236 SW:
237 ~~~
238 [\par]
239 \trowd \cellx \cellx ...
240 \intbl \cell \cell ...
241 \row
242 [\par]
243 [\trowd \cellx \cellx ...]
244 \intbl \cell \cell ...
245 \row
246 [\par]
247
248 M$-Word:
249 ~~~~~~~~
250 [\par]
251 \trowd \cellx \cellx ...
252 \intbl \cell \cell ...
253 \intbl \row
254 [\par]
255 [\trowd \cellx \cellx ...]
256 \intbl \cell \cell ...
257 \intbl \row
258 [\par]
259
260 */
261
263{
264 switch ( pInfo->nToken )
265 {
266 case RTF_TROWD: // denotes table row default, before RTF_CELLX
267 {
268 if (!maDefaultList.empty())
269 nLastWidth = maDefaultList.back()->nTwips;
270
271 nColCnt = 0;
272 if (pActDefault != pInsDefault.get())
273 pActDefault = nullptr;
274 maDefaultList.clear();
275 pDefMerge = nullptr;
276 nRtfLastToken = pInfo->nToken;
277 mnCurPos = 0;
278 }
279 break;
280 case RTF_CLMGF: // The first cell of cells to be merged
281 {
282 pDefMerge = pInsDefault.get();
283 nRtfLastToken = pInfo->nToken;
284 }
285 break;
286 case RTF_CLMRG: // A cell to be merged with the preceding cell
287 {
288 if (!pDefMerge && !maDefaultList.empty())
289 {
290 pDefMerge = maDefaultList.back().get();
291 mnCurPos = maDefaultList.size() - 1;
292 }
293 OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
294 if ( pDefMerge ) // Else broken RTF
295 pDefMerge->nColOverlap++; // multiple successive ones possible
296 pInsDefault->nColOverlap = 0; // Flag: ignore these
297 nRtfLastToken = pInfo->nToken;
298 }
299 break;
300 case RTF_CELLX: // closes cell default
301 {
302 bNewDef = true;
303 pInsDefault->nCol = nColCnt;
304 pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
305 maDefaultList.push_back( std::move(pInsDefault) );
306 // New free-flying pInsDefault
307 pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
308 if ( ++nColCnt > nColMax )
310 nRtfLastToken = pInfo->nToken;
311 }
312 break;
313 case RTF_INTBL: // before the first RTF_CELL
314 {
315 // Once over NextToken and once over UnknownAttrToken
316 // or e.g. \intbl ... \cell \pard \intbl ... \cell
318 {
319 NewCellRow();
320 nRtfLastToken = pInfo->nToken;
321 }
322 }
323 break;
324 case RTF_CELL: // denotes the end of a cell.
325 {
326 OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
327 if ( bNewDef || !pActDefault )
328 NewCellRow(); // before was no \intbl, bad behavior
329 // Broken RTF? Let's save what we can
330 if ( !pActDefault )
331 pActDefault = pInsDefault.get();
332 if ( pActDefault->nColOverlap > 0 )
333 { // Not merged with preceding
334 mxActEntry->nCol = pActDefault->nCol;
335 mxActEntry->nColOverlap = pActDefault->nColOverlap;
336 mxActEntry->nTwips = pActDefault->nTwips;
337 mxActEntry->nRow = nRowCnt;
338 mxActEntry->aItemSet.Set(pActDefault->aItemSet);
339 EntryEnd(mxActEntry.get(), pInfo->aSelection);
340
341 if ( nStartAdjust == sal_uLong(~0) )
342 nStartAdjust = maList.size();
343 maList.push_back(mxActEntry);
344 NewActEntry(mxActEntry.get()); // New free-flying mxActEntry
345 }
346 else
347 { // Assign current Twips to MergeCell
348 if ( !maList.empty() )
349 {
350 auto& pE = maList.back();
351 pE->nTwips = pActDefault->nTwips;
352 }
353 // Adjust selection of free-flying mxActEntry
354 // Paragraph -1 due to separated text in EditEngine during parsing
355 mxActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1;
356 }
357
358 pActDefault = nullptr;
359 if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
361
362 nRtfLastToken = pInfo->nToken;
363 }
364 break;
365 case RTF_ROW: // denotes the end of a row
366 {
367 NextRow();
368 nRtfLastToken = pInfo->nToken;
369 }
370 break;
371 case RTF_PAR: // Paragraph
372 {
373 if ( !pActDefault )
374 { // text not in table
375 ColAdjust(); // close the processing table
376 mxActEntry->nCol = 0;
377 mxActEntry->nRow = nRowCnt;
378 EntryEnd(mxActEntry.get(), pInfo->aSelection);
379 maList.push_back(mxActEntry);
380 NewActEntry(mxActEntry.get()); // new mxActEntry
381 NextRow();
382 }
383 nRtfLastToken = pInfo->nToken;
384 }
385 break;
386 default:
387 { // do not set nRtfLastToken
388 switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
389 {
390 case RTF_SHADINGDEF:
391 static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBackgroundAttr(
392 pInfo->nToken, pInsDefault->aItemSet, true );
393 break;
394 case RTF_BRDRDEF:
395 static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(
396 pInfo->nToken, pInsDefault->aItemSet, true );
397 break;
398 }
399 }
400 }
401}
402
403/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ErrCode Read(SvStream &rInput, const OUString &rBaseURL, EETextFormat, SvKeyValueIterator *pHTTPHeaderAttrs=nullptr)
sal_Int32 GetTextLen() const
const Link< RtfImportInfo &, void > & GetRtfImportHdl() const
void SetRtfImportHdl(const Link< RtfImportInfo &, void > &rLink)
void NewActEntry(const ScEEParseEntry *)
Definition: eeimpars.cxx:655
SCCOL nColCnt
Definition: eeparser.hxx:110
EditEngine * pEdit
Definition: eeparser.hxx:103
std::vector< std::shared_ptr< ScEEParseEntry > > maList
Definition: eeparser.hxx:106
std::shared_ptr< ScEEParseEntry > mxActEntry
Definition: eeparser.hxx:107
SCROW nRowMax
Definition: eeparser.hxx:113
SCROW nRowCnt
Definition: eeparser.hxx:111
SCCOL nColMax
Definition: eeparser.hxx:112
int nRtfLastToken
Definition: eeparser.hxx:109
rtl::Reference< SfxItemPool > pPool
Definition: eeparser.hxx:104
ScRTFCellDefault * pActDefault
Definition: rtfparse.hxx:58
bool bNewDef
Definition: rtfparse.hxx:62
ScRTFParser(EditEngine *)
Definition: rtfparse.cxx:35
void ColAdjust()
Definition: rtfparse.cxx:126
std::unique_ptr< ScRTFCellDefault > pInsDefault
Definition: rtfparse.hxx:57
void NewCellRow()
Definition: rtfparse.cxx:198
bool SeekTwips(sal_uInt16 nTwips, SCCOL *pCol)
Definition: rtfparse.cxx:102
ScRTFColTwips aColTwips
Definition: rtfparse.hxx:56
ScRTFCellDefault * pDefMerge
Definition: rtfparse.hxx:59
void EntryEnd(ScEEParseEntry *, const ESelection &)
Definition: rtfparse.cxx:88
void NextRow()
Definition: rtfparse.cxx:96
sal_uInt16 nLastWidth
Definition: rtfparse.hxx:61
virtual ~ScRTFParser() override
Definition: rtfparse.cxx:52
void ProcToken(RtfImportInfo *)
Definition: rtfparse.cxx:262
DefaultList maDefaultList
Definition: rtfparse.hxx:53
sal_uLong nStartAdjust
Definition: rtfparse.hxx:60
virtual ErrCode Read(SvStream &, const OUString &rBaseURL) override
Definition: rtfparse.cxx:58
size_t mnCurPos
Definition: rtfparse.hxx:54
void SetPardMap(TypedWhichId< T > wid, TypedWhichId< T > widTrue)
void SetAttrPool(SfxItemPool *pNewPool)
const_iterator begin() const
std::vector< Value >::const_iterator const_iterator
const_iterator find(const Value &x) const
const_iterator end() const
size_type size() const
std::pair< const_iterator, bool > insert(Value &&x)
int nCount
FormulaCommand pE
constexpr TypedWhichId< SvxFontHeightItem > EE_CHAR_FONTHEIGHT(EE_CHAR_START+2)
sal_uInt16 nPos
int n2
int n1
int i
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
long Long
#define SC_RTFTWIPTOL
Definition: rtfparse.cxx:33
IMPL_LINK(ScRTFParser, RTFImportHdl, RtfImportInfo &, rInfo, void)
Definition: rtfparse.cxx:154
RTF_BRDRDEF
RTF_TABLEDEF
RTF_SHADINGDEF
RTF_TROWD
RTF_ROW
RTF_INTBL
RTF_CLMRG
RTF_CELL
RTF_CLMGF
RTF_CELLX
RTF_PAR
constexpr TypedWhichId< SvxBrushItem > ATTR_BACKGROUND(148)
constexpr TypedWhichId< SvxShadowItem > ATTR_SHADOW(152)
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
sal_uIntPtr sal_uLong
sal_Int32 nEndPara
ESelection aSelection
SvParser< int > * pParser
sal_uInt16 nTwips
Definition: rtfparse.hxx:32
SfxItemSet aItemSet
Definition: rtfparse.hxx:30
sal_Int16 SCCOL
Definition: types.hxx:21