1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "scitems.hxx"
21 #include <editeng/eeitem.hxx>
22 #include <editeng/editeng.hxx>
23 #include <editeng/fhgtitem.hxx>
24 #include <editeng/svxrtf.hxx>
25 #include <vcl/outdev.hxx>
26 #include <svtools/rtftoken.h>
28 #include "rtfparse.hxx"
30 #include "document.hxx"
31 #include "docpool.hxx"
33 #define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
35 ScRTFParser::ScRTFParser( EditEngine
* pEditP
) :
38 pColTwips( new ScRTFColTwips
),
39 pActDefault( nullptr ),
41 nStartAdjust( (sal_uLong
)~0 ),
45 // RTF default FontSize 12Pt
46 long nMM
= OutputDevice::LogicToLogic( 12, MAP_POINT
, MAP_100TH_MM
);
47 pPool
->SetPoolDefaultItem( SvxFontHeightItem( nMM
, 100, EE_CHAR_FONTHEIGHT
) );
48 // Free-flying pInsDefault
49 pInsDefault
= new ScRTFCellDefault( pPool
);
52 ScRTFParser::~ScRTFParser()
56 maDefaultList
.clear();
59 sal_uLong
ScRTFParser::Read( SvStream
& rStream
, const OUString
& rBaseURL
)
61 Link
<ImportInfo
&,void> aOldLink
= pEdit
->GetImportHdl();
62 pEdit
->SetImportHdl( LINK( this, ScRTFParser
, RTFImportHdl
) );
63 sal_uLong nErr
= pEdit
->Read( rStream
, rBaseURL
, EE_FORMAT_RTF
);
64 if ( nLastToken
== RTF_PAR
)
66 if ( !maList
.empty() )
68 ScEEParseEntry
* pE
= maList
.back();
69 if ( // Completely empty
70 ( ( pE
->aSel
.nStartPara
== pE
->aSel
.nEndPara
71 && pE
->aSel
.nStartPos
== pE
->aSel
.nEndPos
74 ( pE
->aSel
.nStartPara
+ 1 == pE
->aSel
.nEndPara
75 && pE
->aSel
.nStartPos
== pEdit
->GetTextLen( pE
->aSel
.nStartPara
)
76 && pE
->aSel
.nEndPos
== 0
80 { // Don't take over the last paragraph
86 pEdit
->SetImportHdl( aOldLink
);
90 void ScRTFParser::EntryEnd( ScEEParseEntry
* pE
, const ESelection
& aSel
)
92 // Paragraph -2 strips the attached empty paragraph
93 pE
->aSel
.nEndPara
= aSel
.nEndPara
- 2;
94 // Although it's called nEndPos, the last one is position + 1
95 pE
->aSel
.nEndPos
= pEdit
->GetTextLen( aSel
.nEndPara
- 1 );
98 inline void ScRTFParser::NextRow()
100 if ( nRowMax
< ++nRowCnt
)
104 bool ScRTFParser::SeekTwips( sal_uInt16 nTwips
, SCCOL
* pCol
)
106 ScRTFColTwips::const_iterator it
= pColTwips
->find( nTwips
);
107 bool bFound
= it
!= pColTwips
->end();
108 sal_uInt16 nPos
= it
- pColTwips
->begin();
109 *pCol
= static_cast<SCCOL
>(nPos
);
112 sal_uInt16 nCount
= pColTwips
->size();
116 // nCol is insertion position; the next one higher up is there (or not)
117 if ( nCol
< static_cast<SCCOL
>(nCount
) && (((*pColTwips
)[nCol
] - SC_RTFTWIPTOL
) <= nTwips
) )
119 // Not smaller than everything else? Then compare with the next lower one
120 else if ( nCol
!= 0 && (((*pColTwips
)[nCol
-1] + SC_RTFTWIPTOL
) >= nTwips
) )
128 void ScRTFParser::ColAdjust()
130 if ( nStartAdjust
!= (sal_uLong
)~0 )
134 for ( size_t i
= nStartAdjust
, nListSize
= maList
.size(); i
< nListSize
; ++ i
)
140 if ( pE
->nColOverlap
> 1 )
141 nCol
= nCol
+ pE
->nColOverlap
; // Merged cells with \clmrg
144 SeekTwips( pE
->nTwips
, &nCol
);
145 if ( ++nCol
<= pE
->nCol
)
146 nCol
= pE
->nCol
+ 1; // Moved cell X
147 pE
->nColOverlap
= nCol
- pE
->nCol
; // Merged cells without \clmrg
149 if ( nCol
> nColMax
)
152 nStartAdjust
= (sal_uLong
)~0;
157 IMPL_LINK_TYPED( ScRTFParser
, RTFImportHdl
, ImportInfo
&, rInfo
, void )
159 switch ( rInfo
.eState
)
161 case RTFIMP_NEXTTOKEN
:
164 case RTFIMP_UNKNOWNATTR
:
169 SvxRTFParser
* pParser
= static_cast<SvxRTFParser
*>(rInfo
.pParser
);
170 pParser
->SetAttrPool( pPool
);
171 RTFPardAttrMapIds
& rMap
= pParser
->GetPardMap();
172 rMap
.nBrush
= ATTR_BACKGROUND
;
173 rMap
.nBox
= ATTR_BORDER
;
174 rMap
.nShadow
= ATTR_SHADOW
;
178 if ( rInfo
.aSelection
.nEndPos
)
179 { // If still text: create last paragraph
180 pActDefault
= nullptr;
181 rInfo
.nToken
= RTF_PAR
;
182 // EditEngine did not attach an empty paragraph anymore
183 // which EntryEnd could strip
184 rInfo
.aSelection
.nEndPara
++;
190 case RTFIMP_INSERTTEXT
:
192 case RTFIMP_INSERTPARA
:
195 OSL_FAIL("unknown ImportInfo.eState");
200 // For RTF_INTBL or respectively at the start of the first RTF_CELL
201 // after RTF_CELLX if there was no RTF_INTBL
202 void ScRTFParser::NewCellRow( ImportInfo
* /*pInfo*/ )
207 // Not flush on the right? => new table
208 if ( nLastWidth
&& !maDefaultList
.empty() )
210 const ScRTFCellDefault
& rD
= *maDefaultList
.back().get();
211 if (rD
.nTwips
!= nLastWidth
)
214 if ( !( SeekTwips( nLastWidth
, &n1
)
215 && SeekTwips( rD
.nTwips
, &n2
)
224 // Build up TwipCols only after nLastWidth comparison!
225 for (std::unique_ptr
<ScRTFCellDefault
> & pCellDefault
: maDefaultList
)
227 const ScRTFCellDefault
& rD
= *pCellDefault
.get();
229 if ( !SeekTwips(rD
.nTwips
, &nCol
) )
230 pColTwips
->insert( rD
.nTwips
);
234 pActDefault
= maDefaultList
.empty() ? nullptr : maDefaultList
[0].get();
236 OSL_ENSURE( pActDefault
, "NewCellRow: pActDefault==0" );
243 \trowd \cellx \cellx ...
244 \intbl \cell \cell ...
247 [\trowd \cellx \cellx ...]
248 \intbl \cell \cell ...
255 \trowd \cellx \cellx ...
256 \intbl \cell \cell ...
259 [\trowd \cellx \cellx ...]
260 \intbl \cell \cell ...
266 void ScRTFParser::ProcToken( ImportInfo
* pInfo
)
268 switch ( pInfo
->nToken
)
270 case RTF_TROWD
: // denotes table row defauls, before RTF_CELLX
272 if (!maDefaultList
.empty())
273 nLastWidth
= maDefaultList
.back()->nTwips
;
276 maDefaultList
.clear();
278 nLastToken
= pInfo
->nToken
;
282 case RTF_CLMGF
: // The first cell of cells to be merged
284 pDefMerge
= pInsDefault
;
285 nLastToken
= pInfo
->nToken
;
288 case RTF_CLMRG
: // A cell to be merged with the preceding cell
290 if (!pDefMerge
&& !maDefaultList
.empty())
292 pDefMerge
= maDefaultList
.back().get();
293 mnCurPos
= maDefaultList
.size() - 1;
295 OSL_ENSURE( pDefMerge
, "RTF_CLMRG: pDefMerge==0" );
296 if ( pDefMerge
) // Else broken RTF
297 pDefMerge
->nColOverlap
++; // multiple successive ones possible
298 pInsDefault
->nColOverlap
= 0; // Flag: ignore these
299 nLastToken
= pInfo
->nToken
;
302 case RTF_CELLX
: // closes cell default
305 pInsDefault
->nCol
= nColCnt
;
306 pInsDefault
->nTwips
= pInfo
->nTokenValue
; // Right cell border
307 maDefaultList
.push_back( std::unique_ptr
<ScRTFCellDefault
>(pInsDefault
) );
308 // New free-flying pInsDefault
309 pInsDefault
= new ScRTFCellDefault( pPool
);
310 if ( ++nColCnt
> nColMax
)
312 nLastToken
= pInfo
->nToken
;
315 case RTF_INTBL
: // before the first RTF_CELL
317 // Once over NextToken and once over UnknownAttrToken
318 // or e.g. \intbl ... \cell \pard \intbl ... \cell
319 if ( nLastToken
!= RTF_INTBL
&& nLastToken
!= RTF_CELL
&& nLastToken
!= RTF_PAR
)
322 nLastToken
= pInfo
->nToken
;
326 case RTF_CELL
: // denotes the end of a cell.
328 OSL_ENSURE( pActDefault
, "RTF_CELL: pActDefault==0" );
329 if ( bNewDef
|| !pActDefault
)
330 NewCellRow( pInfo
); // davor war kein \intbl, bad behavior
331 // Broken RTF? Let's save what we can
333 pActDefault
= pInsDefault
;
334 if ( pActDefault
->nColOverlap
> 0 )
335 { // Not merged with preceding
336 pActEntry
->nCol
= pActDefault
->nCol
;
337 pActEntry
->nColOverlap
= pActDefault
->nColOverlap
;
338 pActEntry
->nTwips
= pActDefault
->nTwips
;
339 pActEntry
->nRow
= nRowCnt
;
340 pActEntry
->aItemSet
.Set( pActDefault
->aItemSet
);
341 EntryEnd( pActEntry
, pInfo
->aSelection
);
343 if ( nStartAdjust
== (sal_uLong
)~0 )
344 nStartAdjust
= maList
.size();
345 maList
.push_back( pActEntry
);
346 NewActEntry( pActEntry
); // New free-flying pActEntry
349 { // Assign current Twips to MergeCell
350 if ( !maList
.empty() )
352 ScEEParseEntry
* pE
= maList
.back();
353 pE
->nTwips
= pActDefault
->nTwips
;
355 // Adjust selection of free-flying pActEntry
356 // Paragraph -1 due to separated text in EditEngine during parsing
357 pActEntry
->aSel
.nStartPara
= pInfo
->aSelection
.nEndPara
- 1;
360 pActDefault
= nullptr;
361 if (!maDefaultList
.empty() && (mnCurPos
+1) < maDefaultList
.size())
362 pActDefault
= maDefaultList
[++mnCurPos
].get();
364 nLastToken
= pInfo
->nToken
;
367 case RTF_ROW
: // denotes the end of a row
370 nLastToken
= pInfo
->nToken
;
373 case RTF_PAR
: // Paragraph
376 { // text not in table
377 ColAdjust(); // close the processing table
379 pActEntry
->nRow
= nRowCnt
;
380 EntryEnd( pActEntry
, pInfo
->aSelection
);
381 maList
.push_back( pActEntry
);
382 NewActEntry( pActEntry
); // new pActEntry
385 nLastToken
= pInfo
->nToken
;
389 { // do not set nLastToken
390 switch ( pInfo
->nToken
& ~(0xff | RTF_TABLEDEF
) )
393 static_cast<SvxRTFParser
*>(pInfo
->pParser
)->ReadBackgroundAttr(
394 pInfo
->nToken
, pInsDefault
->aItemSet
, true );
397 static_cast<SvxRTFParser
*>(pInfo
->pParser
)->ReadBorderAttr(
398 pInfo
->nToken
, pInsDefault
->aItemSet
, true );
405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */