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
46  tools::Long nMM = OutputDevice::LogicToLogic( 12, MapUnit::MapPoint, MapUnit::Map100thMM );
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 
58 ErrCode 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 
96 inline void ScRTFParser::NextRow()
97 {
98  if ( nRowMax < ++nRowCnt )
99  nRowMax = nRowCnt;
100 }
101 
102 bool 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  }
150  nStartAdjust = sal_uLong(~0);
151  aColTwips.clear();
152 }
153 
154 IMPL_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 )
309  nColMax = nColCnt;
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: */
EditEngine * pEdit
Definition: eeparser.hxx:102
FormulaCommand pE
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
SCCOL nColCnt
Definition: eeparser.hxx:109
RTF_CLMGF
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
ESelection aSelection
void SetRtfImportHdl(const Link< RtfImportInfo &, void > &rLink)
void NewActEntry(const ScEEParseEntry *)
Definition: eeimpars.cxx:644
void NextRow()
Definition: rtfparse.cxx:96
int n1
bool SeekTwips(sal_uInt16 nTwips, SCCOL *pCol)
Definition: rtfparse.cxx:102
ESelection aSel
Definition: eeparser.hxx:55
sal_uIntPtr sal_uLong
long Long
#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
ErrCode Read(SvStream &rInput, const OUString &rBaseURL, EETextFormat, SvKeyValueIterator *pHTTPHeaderAttrs=nullptr)
RTF_PAR
RTF_ROW
void ColAdjust()
Definition: rtfparse.cxx:126
sal_Int32 nEndPara
void EntryEnd(ScEEParseEntry *, const ESelection &)
Definition: rtfparse.cxx:88
int i
sal_uInt32 GetTextLen() const
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:198
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:52
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:58
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:262
IMPL_LINK(ScRTFParser, RTFImportHdl, RtfImportInfo &, rInfo, void)
Definition: rtfparse.cxx:154
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