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