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 <comphelper/string.hxx>
21 #include <utility>
22
23 #include "eertfpar.hxx"
24 #include "impedit.hxx"
25 #include <svl/intitem.hxx>
26 #include <editeng/escapementitem.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/fontitem.hxx>
29 #include <editeng/flditem.hxx>
30 #include <editeng/editeng.hxx>
31
32 #include <svtools/rtftoken.h>
33 #include <svtools/htmltokn.h>
34
35 using namespace com::sun::star;
36
HtmlImportInfo(HtmlImportState eSt,SvParser<HtmlTokenId> * pPrsrs,const ESelection & rSel)37 HtmlImportInfo::HtmlImportInfo( HtmlImportState eSt, SvParser<HtmlTokenId>* pPrsrs, const ESelection& rSel )
38 : aSelection( rSel )
39 {
40 pParser = pPrsrs;
41 eState = eSt;
42 nToken = HtmlTokenId::NONE;
43 }
44
~HtmlImportInfo()45 HtmlImportInfo::~HtmlImportInfo()
46 {
47 }
48
RtfImportInfo(RtfImportState eSt,SvParser<int> * pPrsrs,const ESelection & rSel)49 RtfImportInfo::RtfImportInfo( RtfImportState eSt, SvParser<int>* pPrsrs, const ESelection& rSel )
50 : aSelection( rSel )
51 {
52 pParser = pPrsrs;
53 eState = eSt;
54 nToken = 0;
55 nTokenValue = 0;
56 }
57
~RtfImportInfo()58 RtfImportInfo::~RtfImportInfo()
59 {
60 }
61
62 static constexpr MapUnit gRTFMapUnit = MapUnit::MapTwip;
63
EditRTFParser(SvStream & rIn,EditSelection aSel,SfxItemPool & rAttrPool,EditEngine * pEditEngine)64 EditRTFParser::EditRTFParser(
65 SvStream& rIn, EditSelection aSel, SfxItemPool& rAttrPool, EditEngine* pEditEngine) :
66 SvxRTFParser(rAttrPool, rIn),
67 aCurSel(std::move(aSel)),
68 mpEditEngine(pEditEngine),
69 nDefFont(0),
70 bLastActionInsertParaBreak(false)
71 {
72 SetInsPos(EditPosition(mpEditEngine, &aCurSel));
73
74 // Convert the twips values ...
75 SetCalcValue(true);
76 SetChkStyleAttr(mpEditEngine->IsImportRTFStyleSheetsSet());
77 SetNewDoc(false); // So that the Pool-Defaults are not overwritten...
78 aEditMapMode = MapMode(mpEditEngine->GetRefDevice()->GetMapMode().GetMapUnit());
79 }
80
~EditRTFParser()81 EditRTFParser::~EditRTFParser()
82 {
83 }
84
CallParser()85 SvParserState EditRTFParser::CallParser()
86 {
87 DBG_ASSERT( !aCurSel.HasRange(), "Selection for CallParser!" );
88 // Separate the part that is imported from the rest.
89 // This expression should be used for all imports.
90 // aStart1PaM: Last position before the imported content
91 // aEnd1PaM: First position after the imported content
92 // aStart2PaM: First position of the imported content
93 // aEnd2PaM: Last position of the imported content
94 EditPaM aStart1PaM( aCurSel.Min().GetNode(), aCurSel.Min().GetIndex() );
95 aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
96 EditPaM aStart2PaM = aCurSel.Min();
97 // Useful or not?
98 aStart2PaM.GetNode()->GetContentAttribs().GetItems().ClearItem();
99 AddRTFDefaultValues( aStart2PaM, aStart2PaM );
100 EditPaM aEnd1PaM = mpEditEngine->InsertParaBreak(aCurSel.Max());
101 // aCurCel now points to the gap
102
103 if (mpEditEngine->IsRtfImportHandlerSet())
104 {
105 RtfImportInfo aImportInfo(RtfImportState::Start, this, mpEditEngine->CreateESelection(aCurSel));
106 mpEditEngine->CallRtfImportHandler(aImportInfo);
107 }
108
109 SvParserState _eState = SvxRTFParser::CallParser();
110
111 if (mpEditEngine->IsRtfImportHandlerSet())
112 {
113 RtfImportInfo aImportInfo(RtfImportState::End, this, mpEditEngine->CreateESelection(aCurSel));
114 mpEditEngine->CallRtfImportHandler(aImportInfo);
115 }
116
117 if (bLastActionInsertParaBreak)
118 {
119 ContentNode* pCurNode = aCurSel.Max().GetNode();
120 sal_Int32 nPara = mpEditEngine->GetEditDoc().GetPos(pCurNode);
121 ContentNode* pPrevNode = mpEditEngine->GetEditDoc().GetObject(nPara-1);
122 DBG_ASSERT( pPrevNode, "Invalid RTF-Document?!" );
123 EditSelection aSel;
124 aSel.Min() = EditPaM( pPrevNode, pPrevNode->Len() );
125 aSel.Max() = EditPaM( pCurNode, 0 );
126 aCurSel.Max() = mpEditEngine->DeleteSelection(aSel);
127 }
128 EditPaM aEnd2PaM( aCurSel.Max() );
129 //AddRTFDefaultValues( aStart2PaM, aEnd2PaM );
130 bool bOnlyOnePara = ( aEnd2PaM.GetNode() == aStart2PaM.GetNode() );
131 // Paste the chunk again ...
132 // Problem: Paragraph attributes may not possibly be taken over
133 // => Do Character attributes.
134
135 bool bSpecialBackward = aStart1PaM.GetNode()->Len() == 0;
136 if ( bOnlyOnePara || aStart1PaM.GetNode()->Len() )
137 mpEditEngine->ParaAttribsToCharAttribs( aStart2PaM.GetNode() );
138 aCurSel.Min() = mpEditEngine->ConnectParagraphs(
139 aStart1PaM.GetNode(), aStart2PaM.GetNode(), bSpecialBackward );
140 bSpecialBackward = aEnd1PaM.GetNode()->Len() != 0;
141 // when bOnlyOnePara, then the node is gone on Connect.
142 if ( !bOnlyOnePara && aEnd1PaM.GetNode()->Len() )
143 mpEditEngine->ParaAttribsToCharAttribs( aEnd2PaM.GetNode() );
144 aCurSel.Max() = mpEditEngine->ConnectParagraphs(
145 ( bOnlyOnePara ? aStart1PaM.GetNode() : aEnd2PaM.GetNode() ),
146 aEnd1PaM.GetNode(), bSpecialBackward );
147
148 return _eState;
149 }
150
AddRTFDefaultValues(const EditPaM & rStart,const EditPaM & rEnd)151 void EditRTFParser::AddRTFDefaultValues( const EditPaM& rStart, const EditPaM& rEnd )
152 {
153 // Problem: DefFont and DefFontHeight
154 Size aSz( 12, 0 );
155 MapMode aPntMode( MapUnit::MapPoint );
156 MapMode _aEditMapMode(mpEditEngine->GetRefDevice()->GetMapMode().GetMapUnit());
157 aSz = mpEditEngine->GetRefDevice()->LogicToLogic(aSz, &aPntMode, &_aEditMapMode);
158 SvxFontHeightItem aFontHeightItem( aSz.Width(), 100, EE_CHAR_FONTHEIGHT );
159 vcl::Font aDefFont( GetFont( nDefFont ) );
160 SvxFontItem aFontItem( aDefFont.GetFamilyType(), aDefFont.GetFamilyName(),
161 aDefFont.GetStyleName(), aDefFont.GetPitch(), aDefFont.GetCharSet(), EE_CHAR_FONTINFO );
162
163 sal_Int32 nStartPara = mpEditEngine->GetEditDoc().GetPos( rStart.GetNode() );
164 sal_Int32 nEndPara = mpEditEngine->GetEditDoc().GetPos( rEnd.GetNode() );
165 for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
166 {
167 ContentNode* pNode = mpEditEngine->GetEditDoc().GetObject( nPara );
168 assert(pNode && "AddRTFDefaultValues - No paragraph?!");
169 if ( !pNode->GetContentAttribs().HasItem( EE_CHAR_FONTINFO ) )
170 pNode->GetContentAttribs().GetItems().Put( aFontItem );
171 if ( !pNode->GetContentAttribs().HasItem( EE_CHAR_FONTHEIGHT ) )
172 pNode->GetContentAttribs().GetItems().Put( aFontHeightItem );
173 }
174 }
175
NextToken(int nToken)176 void EditRTFParser::NextToken( int nToken )
177 {
178 switch( nToken )
179 {
180 case RTF_DEFF:
181 {
182 nDefFont = sal_uInt16(nTokenValue);
183 }
184 break;
185 case RTF_DEFTAB:
186 break;
187 case RTF_CELL:
188 {
189 aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
190 }
191 break;
192 case RTF_LINE:
193 {
194 aCurSel = mpEditEngine->InsertLineBreak(aCurSel);
195 }
196 break;
197 case RTF_FIELD:
198 {
199 ReadField();
200 }
201 break;
202 case RTF_SHPINST: // fdo#76776 process contents of shpinst
203 break;
204 case RTF_SP: // fdo#76776 but skip SP groups
205 {
206 SkipGroup();
207 }
208 break;
209 case RTF_PGDSCTBL: // #i29453# ignore \*\pgdsctbl destination
210 case RTF_LISTTEXT:
211 {
212 SkipGroup();
213 }
214 break;
215 default:
216 {
217 SvxRTFParser::NextToken( nToken );
218 if ( nToken == RTF_STYLESHEET )
219 CreateStyleSheets();
220 }
221 break;
222 }
223 if (mpEditEngine->IsRtfImportHandlerSet())
224 {
225 RtfImportInfo aImportInfo(RtfImportState::NextToken, this, mpEditEngine->CreateESelection(aCurSel));
226 aImportInfo.nToken = nToken;
227 aImportInfo.nTokenValue = short(nTokenValue);
228 mpEditEngine->CallRtfImportHandler(aImportInfo);
229 }
230 }
231
UnknownAttrToken(int nToken)232 void EditRTFParser::UnknownAttrToken( int nToken )
233 {
234 // for Tokens which are not evaluated in ReadAttr
235 // Actually, only for Calc (RTFTokenHdl), so that RTF_INTBL
236 if (mpEditEngine->IsRtfImportHandlerSet())
237 {
238 RtfImportInfo aImportInfo(RtfImportState::UnknownAttr, this, mpEditEngine->CreateESelection(aCurSel));
239 aImportInfo.nToken = nToken;
240 aImportInfo.nTokenValue = short(nTokenValue);
241 mpEditEngine->CallRtfImportHandler(aImportInfo);
242 }
243 }
244
InsertText()245 void EditRTFParser::InsertText()
246 {
247 OUString aText( aToken );
248 if (mpEditEngine->IsRtfImportHandlerSet())
249 {
250 RtfImportInfo aImportInfo(RtfImportState::InsertText, this, mpEditEngine->CreateESelection(aCurSel));
251 mpEditEngine->CallRtfImportHandler(aImportInfo);
252 }
253 aCurSel = mpEditEngine->InsertText(aCurSel, aText);
254 bLastActionInsertParaBreak = false;
255 }
256
InsertPara()257 void EditRTFParser::InsertPara()
258 {
259 if (mpEditEngine->IsRtfImportHandlerSet())
260 {
261 RtfImportInfo aImportInfo(RtfImportState::InsertPara, this, mpEditEngine->CreateESelection(aCurSel));
262 mpEditEngine->CallRtfImportHandler(aImportInfo);
263 }
264 aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
265 bLastActionInsertParaBreak = true;
266 }
267
MovePos(bool const bForward)268 void EditRTFParser::MovePos( bool const bForward )
269 {
270 if( bForward )
271 aCurSel = mpEditEngine->CursorRight(
272 aCurSel.Max(), i18n::CharacterIteratorMode::SKIPCHARACTER);
273 else
274 aCurSel = mpEditEngine->CursorLeft(
275 aCurSel.Max(), i18n::CharacterIteratorMode::SKIPCHARACTER);
276 }
277
SetEndPrevPara(EditNodeIdx * & rpNodePos,sal_Int32 & rCntPos)278 void EditRTFParser::SetEndPrevPara( EditNodeIdx*& rpNodePos,
279 sal_Int32& rCntPos )
280 {
281 // The Intention is to: determine the current insert position of the
282 // previous paragraph and set the end from this.
283 // This "\pard" always apply on the right paragraph.
284
285 ContentNode* pN = aCurSel.Max().GetNode();
286 sal_Int32 nCurPara = mpEditEngine->GetEditDoc().GetPos( pN );
287 DBG_ASSERT( nCurPara != 0, "Paragraph equal to 0: SetEnfPrevPara" );
288 if ( nCurPara )
289 nCurPara--;
290 ContentNode* pPrevNode = mpEditEngine->GetEditDoc().GetObject( nCurPara );
291 assert(pPrevNode && "pPrevNode = 0!");
292 rpNodePos = new EditNodeIdx(mpEditEngine, pPrevNode);
293 rCntPos = pPrevNode->Len();
294 }
295
IsEndPara(EditNodeIdx * pNd,sal_Int32 nCnt) const296 bool EditRTFParser::IsEndPara( EditNodeIdx* pNd, sal_Int32 nCnt ) const
297 {
298 return nCnt == pNd->GetNode()->Len();
299 }
300
SetAttrInDoc(SvxRTFItemStackType & rSet)301 void EditRTFParser::SetAttrInDoc( SvxRTFItemStackType &rSet )
302 {
303 ContentNode* pSttNode = const_cast<EditNodeIdx&>(rSet.GetSttNode()).GetNode();
304 ContentNode* pEndNode = const_cast<EditNodeIdx&>(rSet.GetEndNode()).GetNode();
305
306 EditPaM aStartPaM( pSttNode, rSet.GetSttCnt() );
307 EditPaM aEndPaM( pEndNode, rSet.GetEndCnt() );
308
309 // If possible adjust the Escapement-Item:
310 const SfxPoolItem* pItem;
311
312 // #i66167# adapt font heights to destination MapUnit if necessary
313 const MapUnit eDestUnit = mpEditEngine->GetEditDoc().GetItemPool().GetMetric(0);
314 if (eDestUnit != gRTFMapUnit)
315 {
316 sal_uInt16 const aFntHeightIems[3] = { EE_CHAR_FONTHEIGHT, EE_CHAR_FONTHEIGHT_CJK, EE_CHAR_FONTHEIGHT_CTL };
317 for (unsigned short aFntHeightIem : aFntHeightIems)
318 {
319 if (SfxItemState::SET == rSet.GetAttrSet().GetItemState( aFntHeightIem, false, &pItem ))
320 {
321 sal_uInt32 nHeight = static_cast<const SvxFontHeightItem*>(pItem)->GetHeight();
322 long nNewHeight;
323 nNewHeight = OutputDevice::LogicToLogic( static_cast<long>(nHeight), gRTFMapUnit, eDestUnit );
324
325 SvxFontHeightItem aFntHeightItem( nNewHeight, 100, aFntHeightIem );
326 aFntHeightItem.SetProp(
327 static_cast<const SvxFontHeightItem*>(pItem)->GetProp(),
328 static_cast<const SvxFontHeightItem*>(pItem)->GetPropUnit());
329 rSet.GetAttrSet().Put( aFntHeightItem );
330 }
331 }
332 }
333
334 if( SfxItemState::SET == rSet.GetAttrSet().GetItemState( EE_CHAR_ESCAPEMENT, false, &pItem ))
335 {
336 // the correct one
337 long nEsc = static_cast<const SvxEscapementItem*>(pItem)->GetEsc();
338 long nEscFontHeight = 0;
339 if( ( DFLT_ESC_AUTO_SUPER != nEsc ) && ( DFLT_ESC_AUTO_SUB != nEsc ) )
340 {
341 nEsc *= 10; //HalfPoints => Twips was embezzled in RTFITEM.CXX!
342 SvxFont aFont;
343 mpEditEngine->SeekCursor(aStartPaM.GetNode(), aStartPaM.GetIndex()+1, aFont);
344 nEscFontHeight = aFont.GetFontSize().Height();
345 }
346 if (nEscFontHeight)
347 {
348 nEsc = nEsc * 100 / nEscFontHeight;
349
350 SvxEscapementItem aEscItem( static_cast<short>(nEsc), static_cast<const SvxEscapementItem*>(pItem)->GetProportionalHeight(), EE_CHAR_ESCAPEMENT );
351 rSet.GetAttrSet().Put( aEscItem );
352 }
353 }
354
355 if (mpEditEngine->IsRtfImportHandlerSet())
356 {
357 EditSelection aSel( aStartPaM, aEndPaM );
358 RtfImportInfo aImportInfo(RtfImportState::SetAttr, this, mpEditEngine->CreateESelection(aSel));
359 mpEditEngine->CallRtfImportHandler(aImportInfo);
360 }
361
362 ContentNode* pSN = aStartPaM.GetNode();
363 ContentNode* pEN = aEndPaM.GetNode();
364 sal_Int32 nStartNode = mpEditEngine->GetEditDoc().GetPos( pSN );
365 sal_Int32 nEndNode = mpEditEngine->GetEditDoc().GetPos( pEN );
366 sal_Int16 nOutlLevel = 0xff;
367
368 if (rSet.StyleNo() && mpEditEngine->GetStyleSheetPool() && mpEditEngine->IsImportRTFStyleSheetsSet())
369 {
370 SvxRTFStyleTbl::iterator it = GetStyleTbl().find( rSet.StyleNo() );
371 DBG_ASSERT( it != GetStyleTbl().end(), "Template not defined in RTF!" );
372 if ( it != GetStyleTbl().end() )
373 {
374 auto const& pS = it->second;
375 mpEditEngine->SetStyleSheet(
376 EditSelection(aStartPaM, aEndPaM),
377 static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find(pS->sName, SfxStyleFamily::All)));
378 nOutlLevel = pS->nOutlineNo;
379 }
380 }
381
382 // When an Attribute goes from 0 to the current paragraph length,
383 // it should be a paragraph attribute!
384
385 // Note: Selection can reach over several paragraphs.
386 // All Complete paragraphs are paragraph attributes ...
387 for ( sal_Int32 z = nStartNode+1; z < nEndNode; z++ )
388 {
389 DBG_ASSERT(mpEditEngine->GetEditDoc().GetObject(z), "Node does not exist yet(RTF)");
390 mpEditEngine->SetParaAttribsOnly(z, rSet.GetAttrSet());
391 }
392
393 if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
394 {
395 // The rest of the StartNodes...
396 if ( aStartPaM.GetIndex() == 0 )
397 mpEditEngine->SetParaAttribsOnly(nStartNode, rSet.GetAttrSet());
398 else
399 mpEditEngine->SetAttribs(
400 EditSelection(aStartPaM, EditPaM(aStartPaM.GetNode(), aStartPaM.GetNode()->Len())), rSet.GetAttrSet());
401
402 // the beginning of the EndNodes...
403 if ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() )
404 mpEditEngine->SetParaAttribsOnly(nEndNode, rSet.GetAttrSet());
405 else
406 mpEditEngine->SetAttribs(
407 EditSelection(EditPaM(aEndPaM.GetNode(), 0), aEndPaM), rSet.GetAttrSet());
408 }
409 else
410 {
411 if ( ( aStartPaM.GetIndex() == 0 ) && ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() ) )
412 {
413 // When settings char attribs as para attribs, we must merge with existing attribs, not overwrite the ItemSet!
414 SfxItemSet aAttrs = mpEditEngine->GetBaseParaAttribs(nStartNode);
415 aAttrs.Put( rSet.GetAttrSet() );
416 mpEditEngine->SetParaAttribsOnly(nStartNode, aAttrs);
417 }
418 else
419 {
420 mpEditEngine->SetAttribs(
421 EditSelection(aStartPaM, aEndPaM), rSet.GetAttrSet());
422 }
423 }
424
425 // OutlLevel...
426 if ( nOutlLevel != 0xff )
427 {
428 for ( sal_Int32 n = nStartNode; n <= nEndNode; n++ )
429 {
430 ContentNode* pNode = mpEditEngine->GetEditDoc().GetObject( n );
431 pNode->GetContentAttribs().GetItems().Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nOutlLevel ) );
432 }
433 }
434 }
435
FindStyleSheet(const OUString & rName)436 SvxRTFStyleType* EditRTFParser::FindStyleSheet( const OUString& rName )
437 {
438 SvxRTFStyleTbl& rTable = GetStyleTbl();
439 for (auto const& iter : rTable)
440 {
441 if (iter.second->sName == rName)
442 return iter.second.get();
443 }
444 return nullptr;
445 }
446
CreateStyleSheet(SvxRTFStyleType const * pRTFStyle)447 SfxStyleSheet* EditRTFParser::CreateStyleSheet( SvxRTFStyleType const * pRTFStyle )
448 {
449 // Check if a template exists, then it will not be changed!
450 SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find( pRTFStyle->sName, SfxStyleFamily::All ));
451 if ( pStyle )
452 return pStyle;
453
454 OUString aName( pRTFStyle->sName );
455 OUString aParent;
456 if ( pRTFStyle->nBasedOn )
457 {
458 SvxRTFStyleTbl::iterator it = GetStyleTbl().find( pRTFStyle->nBasedOn );
459 if ( it != GetStyleTbl().end())
460 {
461 SvxRTFStyleType *const pS = it->second.get();
462 if ( pS && ( pS !=pRTFStyle ) )
463 aParent = pS->sName;
464 }
465 }
466
467 pStyle = static_cast<SfxStyleSheet*>( &mpEditEngine->GetStyleSheetPool()->Make( aName, SfxStyleFamily::Para ) );
468
469 // 1) convert and take over Items ...
470 ConvertAndPutItems( pStyle->GetItemSet(), pRTFStyle->aAttrSet );
471
472 // 2) As long as Parent is not in the pool, also create this ...
473 if ( !aParent.isEmpty() && ( aParent != aName ) )
474 {
475 SfxStyleSheet* pS = static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find( aParent, SfxStyleFamily::All ));
476 if ( !pS )
477 {
478 // If not found anywhere, create from RTF ...
479 SvxRTFStyleType* _pRTFStyle = FindStyleSheet( aParent );
480 if ( _pRTFStyle )
481 pS = CreateStyleSheet( _pRTFStyle );
482 }
483 // 2b) Link Itemset with Parent ...
484 if ( pS )
485 pStyle->GetItemSet().SetParent( &pS->GetItemSet() );
486 }
487 return pStyle;
488 }
489
CreateStyleSheets()490 void EditRTFParser::CreateStyleSheets()
491 {
492 // the SvxRTFParser has now created the template...
493 if (mpEditEngine->GetStyleSheetPool() && mpEditEngine->IsImportRTFStyleSheetsSet())
494 {
495 for (auto const& elem : GetStyleTbl())
496 {
497 SvxRTFStyleType* pRTFStyle = elem.second.get();
498 CreateStyleSheet( pRTFStyle );
499 }
500 }
501 }
502
CalcValue()503 void EditRTFParser::CalcValue()
504 {
505 const MapUnit eDestUnit = aEditMapMode.GetMapUnit();
506 if (eDestUnit != gRTFMapUnit)
507 nTokenValue = OutputDevice::LogicToLogic( nTokenValue, gRTFMapUnit, eDestUnit );
508 }
509
ReadField()510 void EditRTFParser::ReadField()
511 {
512 // From SwRTFParser::ReadField()
513 int _nOpenBrakets = 1; // the first was already detected earlier
514 bool bFldInst = false;
515 bool bFldRslt = false;
516 OUString aFldInst;
517 OUString aFldRslt;
518
519 while( _nOpenBrakets && IsParserWorking() )
520 {
521 switch( GetNextToken() )
522 {
523 case '}':
524 {
525 _nOpenBrakets--;
526 if ( _nOpenBrakets == 1 )
527 {
528 bFldInst = false;
529 bFldRslt = false;
530 }
531 }
532 break;
533
534 case '{': _nOpenBrakets++;
535 break;
536
537 case RTF_FIELD: SkipGroup();
538 break;
539
540 case RTF_FLDINST: bFldInst = true;
541 break;
542
543 case RTF_FLDRSLT: bFldRslt = true;
544 break;
545
546 case RTF_TEXTTOKEN:
547 {
548 if ( bFldInst )
549 aFldInst += aToken;
550 else if ( bFldRslt )
551 aFldRslt += aToken;
552 }
553 break;
554 }
555 }
556 if ( !aFldInst.isEmpty() )
557 {
558 OUString aHyperLinkMarker( "HYPERLINK " );
559 if ( aFldInst.startsWithIgnoreAsciiCase( aHyperLinkMarker ) )
560 {
561 aFldInst = aFldInst.copy( aHyperLinkMarker.getLength() );
562 aFldInst = comphelper::string::strip(aFldInst, ' ');
563 // strip start and end quotes
564 aFldInst = aFldInst.copy( 1, aFldInst.getLength()-2 );
565
566 if ( aFldRslt.isEmpty() )
567 aFldRslt = aFldInst;
568
569 SvxFieldItem aField( SvxURLField( aFldInst, aFldRslt, SvxURLFormat::Repr ), EE_FEATURE_FIELD );
570 aCurSel = mpEditEngine->InsertField(aCurSel, aField);
571 mpEditEngine->UpdateFieldsOnly();
572 bLastActionInsertParaBreak = false;
573 }
574 }
575
576 SkipToken(); // the closing brace is evaluated "above"
577 }
578
SkipGroup()579 void EditRTFParser::SkipGroup()
580 {
581 int _nOpenBrakets = 1; // the first was already detected earlier
582
583 while( _nOpenBrakets && IsParserWorking() )
584 {
585 switch( GetNextToken() )
586 {
587 case '}':
588 {
589 _nOpenBrakets--;
590 }
591 break;
592
593 case '{':
594 {
595 _nOpenBrakets++;
596 }
597 break;
598 }
599 }
600
601 SkipToken(); // the closing brace is evaluated "above"
602 }
603
EditNodeIdx(EditEngine * pEE,ContentNode * pNd)604 EditNodeIdx::EditNodeIdx(EditEngine* pEE, ContentNode* pNd) :
605 mpEditEngine(pEE), mpNode(pNd) {}
606
GetIdx() const607 sal_Int32 EditNodeIdx::GetIdx() const
608 {
609 return mpEditEngine->GetEditDoc().GetPos(mpNode);
610 }
611
EditPosition(EditEngine * pEE,EditSelection * pSel)612 EditPosition::EditPosition(EditEngine* pEE, EditSelection* pSel) :
613 mpEditEngine(pEE), mpCurSel(pSel) {}
614
Clone() const615 std::unique_ptr<EditPosition> EditPosition::Clone() const
616 {
617 return std::unique_ptr<EditPosition>(new EditPosition(mpEditEngine, mpCurSel));
618 }
619
MakeNodeIdx() const620 std::unique_ptr<EditNodeIdx> EditPosition::MakeNodeIdx() const
621 {
622 return std::make_unique<EditNodeIdx>(mpEditEngine, mpCurSel->Max().GetNode());
623 }
624
GetNodeIdx() const625 sal_Int32 EditPosition::GetNodeIdx() const
626 {
627 ContentNode* pN = mpCurSel->Max().GetNode();
628 return mpEditEngine->GetEditDoc().GetPos(pN);
629 }
630
GetCntIdx() const631 sal_Int32 EditPosition::GetCntIdx() const
632 {
633 return mpCurSel->Max().GetIndex();
634 }
635
636 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
637