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 <config_features.h>
21
22 #include <sal/types.h>
23 #include <tools/solar.h>
24 #include <comphelper/processfactory.hxx>
25 #include <comphelper/string.hxx>
26 #include <comphelper/simplefileaccessinteraction.hxx>
27 #include <com/sun/star/embed/XStorage.hpp>
28 #include <com/sun/star/embed/ElementModes.hpp>
29 #include <com/sun/star/embed/XTransactedObject.hpp>
30 #include <com/sun/star/task/InteractionHandler.hpp>
31
32 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
33 #include <svl/cintitem.hxx>
34 #include <svl/lngmisc.hxx>
35 #include <svl/urihelper.hxx>
36 #include <svl/zforlist.hxx>
37 #include <svl/zformat.hxx>
38 #include <sfx2/linkmgr.hxx>
39 #include <rtl/character.hxx>
40 #include <unotools/charclass.hxx>
41
42 #include <ucbhelper/content.hxx>
43 #include <ucbhelper/commandenvironment.hxx>
44
45 #include <com/sun/star/i18n/XBreakIterator.hpp>
46 #include <hintids.hxx>
47 #include <editeng/fontitem.hxx>
48 #include <editeng/fhgtitem.hxx>
49 #include <editeng/langitem.hxx>
50 #include <fmtfld.hxx>
51 #include <fmtanchr.hxx>
52 #include <pam.hxx>
53 #include <doc.hxx>
54 #include <IDocumentFieldsAccess.hxx>
55 #include <IDocumentMarkAccess.hxx>
56 #include <IDocumentState.hxx>
57 #include <flddat.hxx>
58 #include <docufld.hxx>
59 #include <reffld.hxx>
60 #include <IMark.hxx>
61 #include <expfld.hxx>
62 #include <dbfld.hxx>
63 #include <tox.hxx>
64 #include <section.hxx>
65 #include <ndtxt.hxx>
66 #include <fmtinfmt.hxx>
67 #include <chpfld.hxx>
68 #include <fmtruby.hxx>
69 #include <charfmt.hxx>
70 #include <breakit.hxx>
71 #include <fmtclds.hxx>
72 #include <poolfmt.hxx>
73 #include <SwStyleNameMapper.hxx>
74
75 #include "ww8scan.hxx"
76 #include "ww8par.hxx"
77 #include "writerhelper.hxx"
78 #include <o3tl/safeint.hxx>
79 #include <unotools/fltrcfg.hxx>
80 #include <xmloff/odffields.hxx>
81 #include <osl/diagnose.h>
82
83 #include <algorithm>
84
85 #define MAX_FIELDLEN 64000
86
87 #define WW8_TOX_LEVEL_DELIM ':'
88
89 using namespace ::com::sun::star;
90 using namespace msfilter::util;
91 using namespace sw::util;
92 using namespace sw::mark;
93 using namespace std; // #i24377#
94 using namespace nsSwDocInfoSubType;
95
96 // Bookmarks
97 namespace
98 {
99 // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention
IsTOCBookmarkName(const OUString & rName)100 bool IsTOCBookmarkName(const OUString& rName)
101 {
102 return rName.startsWith("_Toc") || rName.startsWith(OUString(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"));
103 }
104
EnsureTOCBookmarkName(const OUString & rName)105 OUString EnsureTOCBookmarkName(const OUString& rName)
106 {
107 OUString sTmp = rName;
108 if ( IsTOCBookmarkName ( rName ) )
109 {
110 if ( ! rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
111 sTmp = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName;
112 }
113 return sTmp;
114 }
115 }
116
Read_Book(WW8PLCFManResult *)117 tools::Long SwWW8ImplReader::Read_Book(WW8PLCFManResult*)
118 {
119 // should also work via pRes.nCo2OrIdx
120 WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
121 if( !pB )
122 {
123 OSL_ENSURE( pB, "WW8PLCFx_Book - Pointer does not exist" );
124 return 0;
125 }
126
127 eBookStatus eB = pB->GetStatus();
128 if (eB & BOOK_IGNORE)
129 return 0; // ignore bookmark
130
131 if (pB->GetIsEnd())
132 {
133 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true,
134 pB->GetHandle(), (eB & BOOK_FIELD)!=0);
135 return 0;
136 }
137
138 // "_Hlt*" are unnecessary
139 const OUString* pName = pB->GetName();
140 // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available.
141 // So the hidden bookmarks inside for hyperlink jumping also should be kept.
142 if ( !pName ||
143 pName->startsWithIgnoreAsciiCase( "_Hlt" ) )
144 {
145 return 0;
146 }
147
148 // do NOT call ToUpper as the bookmark name can also be a hyperlink target!
149
150 OUString aVal;
151 if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::BOOK_TO_VAR_REF ) )
152 {
153 // set variable for translation bookmark
154 tools::Long nLen = pB->GetLen();
155 if( nLen > MAX_FIELDLEN )
156 nLen = MAX_FIELDLEN;
157
158 tools::Long nOldPos = m_pStrm->Tell();
159 m_xSBase->WW8ReadString( *m_pStrm, aVal, pB->GetStartPos(), nLen,
160 m_eStructCharSet );
161 m_pStrm->Seek( nOldPos );
162
163 // now here the implementation of the old "QuoteString" and
164 // I hope with a better performance as before. It's also only
165 // needed if the filterflags say we will convert bookmarks
166 // to SetExpFields! And this the exception!
167
168 bool bSetAsHex;
169 bool bAllowCr = SwFltGetFlag(m_nFieldFlags,
170 SwFltControlStack::ALLOW_FLD_CR);
171
172 for( sal_Int32 nI = 0;
173 nI < aVal.getLength() && aVal.getLength() < (MAX_FIELDLEN - 4);
174 ++nI )
175 {
176 const sal_Unicode cChar = aVal[nI];
177 switch( cChar )
178 {
179 case 0x0b:
180 case 0x0c:
181 case 0x0d:
182 if( bAllowCr )
183 {
184 aVal = aVal.replaceAt( nI, 1, "\n" );
185 bSetAsHex = false;
186 }
187 else
188 bSetAsHex = true;
189 break;
190
191 case 0xFE:
192 case 0xFF:
193 bSetAsHex = true;
194 break;
195
196 default:
197 bSetAsHex = 0x20 > cChar;
198 break;
199 }
200
201 if( bSetAsHex )
202 {
203 //all Hex-Numbers with \x before
204 OUString sTmp( "\\x" );
205 if( cChar < 0x10 )
206 sTmp += "0";
207 sTmp += OUString::number( cChar, 16 );
208 aVal = aVal.replaceAt( nI, 1 , sTmp );
209 nI += sTmp.getLength() - 1;
210 }
211 }
212
213 if ( aVal.getLength() > (MAX_FIELDLEN - 4))
214 aVal = aVal.copy( 0, MAX_FIELDLEN - 4 );
215 }
216
217 //e.g. inserting bookmark around field result, so we need to put
218 //it around the entire writer field, as we don't have the separation
219 //of field and field result of word, see #i16941#
220 SwPosition aStart(*m_pPaM->GetPoint());
221 if (!m_aFieldStack.empty())
222 {
223 const WW8FieldEntry &rTest = m_aFieldStack.back();
224 aStart = rTest.maStartPos;
225 }
226
227 const OUString sOrigName = BookmarkToWriter(*pName);
228 m_xReffedStck->NewAttr( aStart,
229 SwFltBookmark( EnsureTOCBookmarkName( sOrigName ), aVal, pB->GetHandle(), IsTOCBookmarkName( sOrigName ) ));
230 return 0;
231 }
232
Read_AtnBook(WW8PLCFManResult *)233 tools::Long SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult*)
234 {
235 if (WW8PLCFx_AtnBook* pAtnBook = m_xPlcxMan->GetAtnBook())
236 {
237 if (pAtnBook->getIsEnd())
238 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_ANNOTATIONMARK, true, pAtnBook->getHandle());
239 else
240 m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK, pAtnBook->getHandle()));
241 }
242 return 0;
243 }
244
Read_FactoidBook(WW8PLCFManResult *)245 tools::Long SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult*)
246 {
247 if (WW8PLCFx_FactoidBook* pFactoidBook = m_xPlcxMan->GetFactoidBook())
248 {
249 if (pFactoidBook->getIsEnd())
250 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_RDFMARK, true, pFactoidBook->getHandle());
251 else
252 {
253 SwFltRDFMark aMark;
254 aMark.SetHandle(pFactoidBook->getHandle());
255 GetSmartTagInfo(aMark);
256 m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), aMark);
257 }
258 }
259 return 0;
260 }
261
262 // general help methods to separate parameters
263
264 /// translate FieldParameter names into the system character set and
265 /// at the same time, double backslashes are converted into single ones
ConvertFFileName(const OUString & rOrg)266 OUString SwWW8ImplReader::ConvertFFileName(const OUString& rOrg)
267 {
268 OUString aName = rOrg.replaceAll("\\\\", "\\");
269 aName = aName.replaceAll("%20", " ");
270
271 // remove attached quotation marks
272 if (aName.endsWith("\""))
273 aName = aName.copy(0, aName.getLength()-1);
274
275 // Need the more sophisticated url converter.
276 if (!aName.isEmpty())
277 aName = URIHelper::SmartRel2Abs(
278 INetURLObject(m_sBaseURL), aName, Link<OUString *, bool>(), false);
279
280 return aName;
281 }
282
283 namespace
284 {
285 /// translate FieldParameter names into the
286 /// system character set and makes them uppercase
ConvertUFName(OUString & rName)287 void ConvertUFName( OUString& rName )
288 {
289 rName = GetAppCharClass().uppercase( rName );
290 }
291 }
292
lcl_ConvertSequenceName(OUString & rSequenceName)293 static void lcl_ConvertSequenceName(OUString& rSequenceName)
294 {
295 ConvertUFName(rSequenceName);
296 if ('0' <= rSequenceName[0] && '9' >= rSequenceName[0])
297 rSequenceName = "_" + rSequenceName;
298 }
299
300 // FindParaStart() finds 1st Parameter that follows '\' and cToken
301 // and returns start of this parameter or -1
FindParaStart(const OUString & rStr,sal_Unicode cToken,sal_Unicode cToken2)302 static sal_Int32 FindParaStart( const OUString& rStr, sal_Unicode cToken, sal_Unicode cToken2 )
303 {
304 bool bStr = false; // ignore inside a string
305
306 for( sal_Int32 nBuf = 0; nBuf+1 < rStr.getLength(); nBuf++ )
307 {
308 if( rStr[ nBuf ] == '"' )
309 bStr = !bStr;
310
311 if( !bStr
312 && rStr[ nBuf ] == '\\'
313 && ( rStr[ nBuf + 1 ] == cToken
314 || rStr[ nBuf + 1 ] == cToken2 ) )
315 {
316 nBuf += 2;
317 // skip spaces between cToken and its parameters
318 while( nBuf < rStr.getLength()
319 && rStr[ nBuf ] == ' ' )
320 nBuf++;
321 // return start of parameters
322 return nBuf < rStr.getLength() ? nBuf : -1;
323 }
324 }
325 return -1;
326 }
327
328 // FindPara() finds the first parameter including '\' and cToken.
329 // A new String will be allocated (has to be deallocated by the caller)
330 // and everything that is part of the parameter will be returned.
FindPara(const OUString & rStr,sal_Unicode cToken,sal_Unicode cToken2)331 static OUString FindPara( const OUString& rStr, sal_Unicode cToken, sal_Unicode cToken2 )
332 {
333 sal_Int32 n2; // end
334 sal_Int32 n = FindParaStart( rStr, cToken, cToken2 ); // start
335 if( n == -1)
336 return OUString();
337
338 if( rStr[ n ] == '"'
339 || rStr[ n ] == 132 )
340 { // Quotationmark in front of parameter
341 n++; // Skip quotationmark
342 n2 = n; // search for the end starting from here
343 while( n2 < rStr.getLength()
344 && rStr[ n2 ] != 147
345 && rStr[ n2 ] != '"' )
346 n2++; // search end of parameter
347 }
348 else
349 { // no quotationmarks
350 n2 = n; // search for the end starting from here
351 while( n2 < rStr.getLength()
352 && rStr[ n2 ] != ' ' )
353 n2++; // search end of parameter
354 }
355 return rStr.copy( n, n2-n );
356 }
357
GetNumTypeFromName(const OUString & rStr,bool bAllowPageDesc=false)358 static SvxNumType GetNumTypeFromName(const OUString& rStr,
359 bool bAllowPageDesc = false)
360 {
361 SvxNumType eTyp = bAllowPageDesc ? SVX_NUM_PAGEDESC : SVX_NUM_ARABIC;
362 if( rStr.startsWithIgnoreAsciiCase( "Arabi" ) ) // Arabisch, Arabic
363 eTyp = SVX_NUM_ARABIC;
364 else if( rStr.startsWith( "misch" ) ) // r"omisch
365 eTyp = SVX_NUM_ROMAN_LOWER;
366 else if( rStr.startsWith( "MISCH" ) ) // R"OMISCH
367 eTyp = SVX_NUM_ROMAN_UPPER;
368 else if( rStr.startsWithIgnoreAsciiCase( "alphabeti" ) )// alphabetisch, alphabetic
369 eTyp = ( rStr[0] == 'A' )
370 ? SVX_NUM_CHARS_UPPER_LETTER_N
371 : SVX_NUM_CHARS_LOWER_LETTER_N;
372 else if( rStr.startsWithIgnoreAsciiCase( "roman" ) ) // us
373 eTyp = ( rStr[0] == 'R' )
374 ? SVX_NUM_ROMAN_UPPER
375 : SVX_NUM_ROMAN_LOWER;
376 return eTyp;
377 }
378
GetNumberPara(const OUString & rStr,bool bAllowPageDesc=false)379 static SvxNumType GetNumberPara(const OUString& rStr, bool bAllowPageDesc = false)
380 {
381 OUString s( FindPara( rStr, '*', '*' ) ); // Type of number
382 SvxNumType aType = GetNumTypeFromName( s, bAllowPageDesc );
383 return aType;
384 }
385
ForceFieldLanguage(SwField & rField,LanguageType nLang)386 bool SwWW8ImplReader::ForceFieldLanguage(SwField &rField, LanguageType nLang)
387 {
388 bool bRet(false);
389
390 const SvxLanguageItem *pLang =
391 static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
392 OSL_ENSURE(pLang, "impossible");
393 LanguageType nDefault = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
394
395 if (nLang != nDefault)
396 {
397 rField.SetAutomaticLanguage(false);
398 rField.SetLanguage(nLang);
399 bRet = true;
400 }
401
402 return bRet;
403 }
404
GetWordDefaultDateStringAsUS(SvNumberFormatter * pFormatter,LanguageType nLang)405 static OUString GetWordDefaultDateStringAsUS(SvNumberFormatter* pFormatter, LanguageType nLang)
406 {
407 //Get the system date in the correct final language layout, convert to
408 //a known language and modify the 2 digit year part to be 4 digit, and
409 //convert back to the correct language layout.
410 const sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, nLang);
411
412 SvNumberformat aFormat = *(pFormatter->GetEntry(nIndex));
413 aFormat.ConvertLanguage(*pFormatter, nLang, LANGUAGE_ENGLISH_US);
414
415 OUString sParams(aFormat.GetFormatstring());
416 // #i36594#
417 // Fix provided by mloiseleur@openoffice.org.
418 // A default date can have already 4 year digits, in some case
419 const sal_Int32 pos = sParams.indexOf("YYYY");
420 if ( pos == -1 )
421 {
422 sParams = sParams.replaceFirst("YY", "YYYY");
423 }
424 return sParams;
425 }
426
GetTimeDatePara(OUString const & rStr,sal_uInt32 & rFormat,LanguageType & rLang,int nWhichDefault,bool bHijri)427 SvNumFormatType SwWW8ImplReader::GetTimeDatePara(OUString const & rStr, sal_uInt32& rFormat,
428 LanguageType &rLang, int nWhichDefault, bool bHijri)
429 {
430 bool bRTL = false;
431 if (m_xPlcxMan && !m_bVer67)
432 {
433 SprmResult aResult = m_xPlcxMan->HasCharSprm(0x85A);
434 if (aResult.pSprm && aResult.nRemainingData >= 1 && *aResult.pSprm)
435 bRTL = true;
436 }
437 sal_uInt16 eLang = bRTL ? RES_CHRATR_CTL_LANGUAGE : RES_CHRATR_LANGUAGE;
438 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(eLang));
439 OSL_ENSURE(pLang, "impossible");
440 rLang = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
441
442 SvNumberFormatter* pFormatter = m_rDoc.GetNumberFormatter();
443 OUString sParams( FindPara( rStr, '@', '@' ) );// Date/Time
444 if (sParams.isEmpty())
445 {
446 bool bHasTime = false;
447 switch (nWhichDefault)
448 {
449 case ww::ePRINTDATE:
450 case ww::eSAVEDATE:
451 sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
452 sParams += " HH:MM:SS AM/PM";
453 bHasTime = true;
454 break;
455 case ww::eCREATEDATE:
456 sParams += "DD/MM/YYYY HH:MM:SS";
457 bHasTime = true;
458 break;
459 default:
460 case ww::eDATE:
461 sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
462 break;
463 }
464
465 if (bHijri)
466 sParams = "[~hijri]" + sParams;
467
468 sal_Int32 nCheckPos = 0;
469 SvNumFormatType nType = SvNumFormatType::DEFINED;
470 rFormat = 0;
471
472 OUString sTemp(sParams);
473 pFormatter->PutandConvertEntry(sTemp, nCheckPos, nType, rFormat,
474 LANGUAGE_ENGLISH_US, rLang, false);
475 sParams = sTemp;
476
477 return bHasTime ? SvNumFormatType::DATETIME : SvNumFormatType::DATE;
478 }
479
480 sal_uLong nFormatIdx =
481 sw::ms::MSDateTimeFormatToSwFormat(sParams, pFormatter, rLang, bHijri,
482 GetFib().m_lid);
483 SvNumFormatType nNumFormatType = SvNumFormatType::UNDEFINED;
484 if (nFormatIdx)
485 nNumFormatType = pFormatter->GetType(nFormatIdx);
486 rFormat = nFormatIdx;
487
488 return nNumFormatType;
489 }
490
491 // Fields
492
493 // Update respective fields after loading (currently references)
UpdateFields()494 void SwWW8ImplReader::UpdateFields()
495 {
496 m_rDoc.getIDocumentState().SetUpdateExpFieldStat(true);
497 m_rDoc.SetInitDBFields(true); // Also update fields in the database
498 }
499
End_Field()500 sal_uInt16 SwWW8ImplReader::End_Field()
501 {
502 sal_uInt16 nRet = 0;
503 WW8PLCFx_FLD* pF = m_xPlcxMan->GetField();
504 OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available");
505 WW8_CP nCP = 0;
506 if (!pF || !pF->EndPosIsFieldEnd(nCP))
507 return nRet;
508
509 const SvtFilterOptions &rOpt = SvtFilterOptions::Get();
510 bool bUseEnhFields = rOpt.IsUseEnhancedFields();
511
512 OSL_ENSURE(!m_aFieldStack.empty(), "Empty field stack");
513 if (!m_aFieldStack.empty())
514 {
515 /*
516 only hyperlinks currently need to be handled like this, for the other
517 cases we have inserted a field not an attribute with an unknown end
518 point
519 */
520 nRet = m_aFieldStack.back().mnFieldId;
521 switch (nRet)
522 {
523 case ww::eFORMTEXT:
524 if (bUseEnhFields && m_pPaM!=nullptr && m_pPaM->GetPoint()!=nullptr) {
525 SwPosition aEndPos = *m_pPaM->GetPoint();
526 SwPaM aFieldPam( m_aFieldStack.back().GetPtNode(), m_aFieldStack.back().GetPtContent(), aEndPos.nNode, aEndPos.nContent.GetIndex());
527 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
528 IFieldmark *pFieldmark = pMarksAccess->makeFieldBookmark(
529 aFieldPam, m_aFieldStack.back().GetBookmarkName(), ODF_FORMTEXT,
530 aFieldPam.Start() /*same pos as start!*/ );
531 OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
532 if (pFieldmark!=nullptr) {
533 // adapt redline positions to inserted field mark start
534 // dummy char (assume not necessary for end dummy char)
535 m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start());
536 const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
537 pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
538 }
539 }
540 break;
541 // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field
542 case ww::eTOC://TOX
543 case ww::eINDEX://index
544 if (m_bLoadingTOXCache)
545 {
546 if (m_nEmbeddedTOXLevel > 0)
547 {
548 JoinNode(*m_pPaM);
549 --m_nEmbeddedTOXLevel;
550 }
551 else
552 {
553 m_aTOXEndCps.insert(nCP);
554 m_bLoadingTOXCache = false;
555 if ( m_pPaM->End() &&
556 m_pPaM->End()->nNode.GetNode().GetTextNode() &&
557 m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() == 0 )
558 {
559 JoinNode(*m_pPaM);
560 }
561 else
562 {
563 m_bCareLastParaEndInToc = true;
564 }
565
566 if (m_pPosAfterTOC)
567 {
568 *m_pPaM = *m_pPosAfterTOC;
569 m_pPosAfterTOC.reset();
570 }
571 }
572 }
573 break;
574 case ww::ePAGEREF: //REF
575 if (m_bLoadingTOXCache && !m_bLoadingTOXHyperlink)
576 {
577 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_TXTATR_INETFMT);
578 }
579 break;
580 case ww::eHYPERLINK:
581 if (m_bLoadingTOXHyperlink)
582 m_bLoadingTOXHyperlink = false;
583 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_INETFMT);
584 break;
585 case ww::eMERGEINC:
586 case ww::eINCLUDETEXT:
587 //Move outside the section associated with this type of field
588 *m_pPaM->GetPoint() = m_aFieldStack.back().maStartPos;
589 break;
590 case ww::eIF: // IF-field
591 {
592 // conditional field parameters
593 const OUString& fieldDefinition = m_aFieldStack.back().GetBookmarkCode();
594
595 OUString paramCondition;
596 OUString paramTrue;
597 OUString paramFalse;
598
599 SwHiddenTextField::ParseIfFieldDefinition(fieldDefinition, paramCondition, paramTrue, paramFalse);
600
601 // create new field
602 SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText);
603 SwHiddenTextField aHTField(
604 static_cast<SwHiddenTextFieldType*>(pFieldType),
605 paramCondition,
606 paramTrue,
607 paramFalse,
608 SwFieldTypesEnum::ConditionalText);
609
610 // insert new field into document
611 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aHTField));
612 break;
613 }
614 default:
615 OUString aCode = m_aFieldStack.back().GetBookmarkCode();
616 if (!aCode.isEmpty() && !aCode.trim().startsWith("SHAPE"))
617 {
618 // Unhandled field with stored code
619 SwPosition aEndPos = *m_pPaM->GetPoint();
620 SwPaM aFieldPam(
621 m_aFieldStack.back().GetPtNode(), m_aFieldStack.back().GetPtContent(),
622 aEndPos.nNode, aEndPos.nContent.GetIndex());
623
624 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
625
626 IFieldmark* pFieldmark = pMarksAccess->makeFieldBookmark(
627 aFieldPam,
628 m_aFieldStack.back().GetBookmarkName(),
629 ODF_UNHANDLED,
630 aFieldPam.Start() /*same pos as start!*/ );
631 if ( pFieldmark )
632 {
633 // adapt redline positions to inserted field mark start
634 // dummy char (assume not necessary for end dummy char)
635 m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start());
636 const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
637 pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
638 OUString sFieldId = OUString::number( m_aFieldStack.back().mnFieldId );
639 pFieldmark->GetParameters()->insert(
640 std::pair< OUString, uno::Any > (
641 ODF_ID_PARAM,
642 uno::makeAny( sFieldId ) ) );
643 pFieldmark->GetParameters()->insert(
644 std::pair< OUString, uno::Any > (
645 ODF_CODE_PARAM,
646 uno::makeAny( aCode ) ) );
647
648 if ( m_aFieldStack.back().mnObjLocFc > 0 )
649 {
650 // Store the OLE object as an internal link
651 OUString sOleId = "_" +
652 OUString::number( m_aFieldStack.back().mnObjLocFc );
653
654 tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(SL::aObjectPool);
655 tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( sOleId, StreamMode::READ );
656
657 // Store it now!
658 uno::Reference< embed::XStorage > xDocStg = GetDoc().GetDocStorage();
659 if (xDocStg.is())
660 {
661 uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement(
662 "OLELinks", embed::ElementModes::WRITE );
663 tools::SvRef<SotStorage> xObjDst = SotStorage::OpenOLEStorage( xOleStg, sOleId );
664
665 if ( xObjDst.is() )
666 {
667 xSrc1->CopyTo( xObjDst.get() );
668
669 if ( !xObjDst->GetError() )
670 xObjDst->Commit();
671 }
672
673 uno::Reference< embed::XTransactedObject > xTransact( xOleStg, uno::UNO_QUERY );
674 if ( xTransact.is() )
675 xTransact->commit();
676 }
677
678 // Store the OLE Id as a parameter
679 pFieldmark->GetParameters()->insert(
680 std::pair< OUString, uno::Any >(
681 ODF_OLE_PARAM, uno::makeAny( sOleId ) ) );
682 }
683 }
684 }
685
686 break;
687 }
688 m_aFieldStack.pop_back();
689 }
690 return nRet;
691 }
692
AcceptableNestedField(sal_uInt16 nFieldCode)693 static bool AcceptableNestedField(sal_uInt16 nFieldCode)
694 {
695 switch (nFieldCode)
696 {
697 case ww::eINDEX: // allow recursive field in TOC...
698 case ww::eTOC: // allow recursive field in TOC...
699 case ww::eMERGEINC:
700 case ww::eINCLUDETEXT:
701 case ww::eAUTOTEXT:
702 case ww::eHYPERLINK:
703 // Accept AutoTextList field as nested field.
704 // Thus, the field result is imported as plain text.
705 case ww::eAUTOTEXTLIST:
706 // tdf#129247 CONTROL contains a nested SHAPE field in the result
707 case ww::eCONTROL:
708 return true;
709 default:
710 return false;
711 }
712 }
713
WW8FieldEntry(SwPosition const & rPos,sal_uInt16 nFieldId)714 WW8FieldEntry::WW8FieldEntry(SwPosition const &rPos, sal_uInt16 nFieldId) noexcept
715 : maStartPos(rPos), mnFieldId(nFieldId), mnObjLocFc(0)
716 {
717 }
718
WW8FieldEntry(const WW8FieldEntry & rOther)719 WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry &rOther) noexcept
720 : maStartPos(rOther.maStartPos), mnFieldId(rOther.mnFieldId), mnObjLocFc(rOther.mnObjLocFc)
721 {
722 }
723
Swap(WW8FieldEntry & rOther)724 void WW8FieldEntry::Swap(WW8FieldEntry &rOther) noexcept
725 {
726 std::swap(maStartPos, rOther.maStartPos);
727 std::swap(mnFieldId, rOther.mnFieldId);
728 }
729
operator =(const WW8FieldEntry & rOther)730 WW8FieldEntry &WW8FieldEntry::operator=(const WW8FieldEntry &rOther) noexcept
731 {
732 WW8FieldEntry aTemp(rOther);
733 Swap(aTemp);
734 return *this;
735 }
736
737
SetBookmarkName(const OUString & bookmarkName)738 void WW8FieldEntry::SetBookmarkName(const OUString& bookmarkName)
739 {
740 msBookmarkName=bookmarkName;
741 }
742
SetBookmarkType(const OUString & bookmarkType)743 void WW8FieldEntry::SetBookmarkType(const OUString& bookmarkType)
744 {
745 msMarkType=bookmarkType;
746 }
747
SetBookmarkCode(const OUString & bookmarkCode)748 void WW8FieldEntry::SetBookmarkCode(const OUString& bookmarkCode)
749 {
750 msMarkCode = bookmarkCode;
751 }
752
753
754 // Read_Field reads a field or returns 0 if the field cannot be read,
755 // so that the calling function reads the field in text format.
756 // Returnvalue: Total length of field
Read_Field(WW8PLCFManResult * pRes)757 tools::Long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes)
758 {
759 typedef eF_ResT (SwWW8ImplReader::*FNReadField)( WW8FieldDesc*, OUString& );
760 enum Limits {eMax = 96};
761 static const FNReadField aWW8FieldTab[eMax+1] =
762 {
763 nullptr,
764 &SwWW8ImplReader::Read_F_Input,
765 nullptr,
766 &SwWW8ImplReader::Read_F_Ref, // 3
767 nullptr,
768 nullptr,
769 &SwWW8ImplReader::Read_F_Set, // 6
770 nullptr,
771 &SwWW8ImplReader::Read_F_Tox, // 8
772 nullptr,
773 &SwWW8ImplReader::Read_F_Styleref, // 10
774 nullptr,
775 &SwWW8ImplReader::Read_F_Seq, // 12
776 &SwWW8ImplReader::Read_F_Tox, // 13
777 &SwWW8ImplReader::Read_F_DocInfo, // 14
778 &SwWW8ImplReader::Read_F_DocInfo, // 15
779 &SwWW8ImplReader::Read_F_DocInfo, // 16
780 &SwWW8ImplReader::Read_F_Author, // 17
781 &SwWW8ImplReader::Read_F_DocInfo, // 18
782 &SwWW8ImplReader::Read_F_DocInfo, // 19
783 &SwWW8ImplReader::Read_F_DocInfo, // 20
784 &SwWW8ImplReader::Read_F_DocInfo, // 21
785 &SwWW8ImplReader::Read_F_DocInfo, // 22
786 &SwWW8ImplReader::Read_F_DocInfo, // 23
787 &SwWW8ImplReader::Read_F_DocInfo, // 24
788 &SwWW8ImplReader::Read_F_DocInfo, // 25
789 &SwWW8ImplReader::Read_F_Num, // 26
790 &SwWW8ImplReader::Read_F_Num, // 27
791 &SwWW8ImplReader::Read_F_Num, // 28
792 &SwWW8ImplReader::Read_F_FileName, // 29
793 &SwWW8ImplReader::Read_F_TemplName, // 30
794 &SwWW8ImplReader::Read_F_DateTime, // 31
795 &SwWW8ImplReader::Read_F_DateTime, // 32
796 &SwWW8ImplReader::Read_F_CurPage, // 33
797 nullptr,
798 nullptr,
799 &SwWW8ImplReader::Read_F_IncludeText, // 36
800 &SwWW8ImplReader::Read_F_PgRef, // 37
801 &SwWW8ImplReader::Read_F_InputVar, // 38
802 &SwWW8ImplReader::Read_F_Input, // 39
803 nullptr,
804 &SwWW8ImplReader::Read_F_DBNext, // 41
805 nullptr,
806 nullptr,
807 &SwWW8ImplReader::Read_F_DBNum, // 44
808 nullptr,
809 nullptr,
810 nullptr,
811 nullptr,
812 &SwWW8ImplReader::Read_F_Equation, // 49
813 nullptr,
814 &SwWW8ImplReader::Read_F_Macro, // 51
815 &SwWW8ImplReader::Read_F_ANumber, // 52
816 &SwWW8ImplReader::Read_F_ANumber, // 53
817 &SwWW8ImplReader::Read_F_ANumber, // 54
818 nullptr,
819
820 nullptr, // 56
821
822 &SwWW8ImplReader::Read_F_Symbol, // 57
823 &SwWW8ImplReader::Read_F_Embedd, // 58
824 &SwWW8ImplReader::Read_F_DBField, // 59
825 nullptr,
826 nullptr,
827 nullptr,
828 nullptr,
829 &SwWW8ImplReader::Read_F_DocInfo, // 64 - DOCVARIABLE
830 nullptr,
831 nullptr,
832 &SwWW8ImplReader::Read_F_IncludePicture, // 67
833 &SwWW8ImplReader::Read_F_IncludeText, // 68
834 nullptr,
835 &SwWW8ImplReader::Read_F_FormTextBox, // 70
836 &SwWW8ImplReader::Read_F_FormCheckBox, // 71
837 &SwWW8ImplReader::Read_F_NoteReference, // 72
838 nullptr, /*&SwWW8ImplReader::Read_F_Tox*/
839 nullptr,
840 nullptr,
841 nullptr,
842 nullptr,
843 nullptr,
844 nullptr,
845 nullptr,
846 nullptr,
847 nullptr,
848 &SwWW8ImplReader::Read_F_FormListBox, // 83
849 nullptr, // 84
850 &SwWW8ImplReader::Read_F_DocInfo, // 85
851 nullptr, // 86
852 &SwWW8ImplReader::Read_F_OCX, // 87
853 &SwWW8ImplReader::Read_F_Hyperlink, // 88
854 nullptr, // 89
855 nullptr, // 90
856 &SwWW8ImplReader::Read_F_HTMLControl, // 91
857 nullptr, // 92
858 nullptr, // 93
859 nullptr, // 94
860 &SwWW8ImplReader::Read_F_Shape, // 95
861 nullptr // eMax - Dummy empty method
862 };
863 OSL_ENSURE( SAL_N_ELEMENTS( aWW8FieldTab ) == eMax+1, "FieldFunc table not right" );
864
865 WW8PLCFx_FLD* pF = m_xPlcxMan->GetField();
866 OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available");
867
868 if (!pF || !pF->StartPosIsFieldStart())
869 return 0;
870
871 bool bNested = false;
872 if (!m_aFieldStack.empty())
873 {
874 bNested = std::any_of(m_aFieldStack.cbegin(), m_aFieldStack.cend(),
875 [](const WW8FieldEntry& aField) { return !AcceptableNestedField(aField.mnFieldId); });
876 }
877
878 WW8FieldDesc aF;
879 bool bOk = pF->GetPara(pRes->nCp2OrIdx, aF);
880
881 OSL_ENSURE(bOk, "WW8: Bad Field!");
882 if (aF.nId == 33) aF.bCodeNest=false; // do not recurse into nested page fields
883 bool bCodeNest = aF.bCodeNest;
884 if ( aF.nId == 6 ) bCodeNest = false; // We can handle them and lose the inner data
885 if (aF.nId == 70) bCodeNest = false; // need to import 0x01 in FORMTEXT
886
887 m_aFieldStack.emplace_back(*m_pPaM->GetPoint(), aF.nId);
888
889 if (bNested)
890 return 0;
891
892 sal_uInt16 n = (aF.nId <= eMax) ? aF.nId : o3tl::narrowing<sal_uInt16>(eMax);
893 sal_uInt16 nI = n / 32; // # of sal_uInt32
894 sal_uInt32 nMask = 1 << ( n % 32 ); // Mask for bits
895
896 if (SAL_N_ELEMENTS(m_nFieldTagAlways) <= nI)
897 { // if indexes larger than 95 are needed, then a new configuration
898 // item has to be added, and nFieldTagAlways/nFieldTagBad expanded!
899 return aF.nLen;
900 }
901
902 if( m_nFieldTagAlways[nI] & nMask ) // Flag: Tag it
903 return Read_F_Tag( &aF ); // Result not as text
904
905 if( !bOk || !aF.nId ) // Field corrupted
906 return aF.nLen; // -> ignore
907
908 if( aF.nId > eMax - 1) // WW: Nested Field
909 {
910 if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
911 return Read_F_Tag( &aF ); // Result not as text
912 else
913 return aF.nLen;
914 }
915
916 //Only one type of field (hyperlink) in drawing textboxes exists
917 if (aF.nId != 88 && m_xPlcxMan->GetDoingDrawTextBox())
918 return aF.nLen;
919
920 bool bHasHandler = aWW8FieldTab[aF.nId] != nullptr;
921 if (aF.nId == 10) // STYLEREF
922 {
923 // STYLEREF, by default these are not handled.
924 bHasHandler = false;
925 sal_uInt64 nOldPos = m_pStrm->Tell();
926 OUString aStr;
927 aF.nLCode = m_xSBase->WW8ReadString(*m_pStrm, aStr, m_xPlcxMan->GetCpOfs() + aF.nSCode, aF.nLCode, m_eTextCharSet);
928 m_pStrm->Seek(nOldPos);
929
930 WW8ReadFieldParams aReadParam(aStr);
931 sal_Int32 nRet = aReadParam.SkipToNextToken();
932 if (nRet == -2 && !aReadParam.GetResult().isEmpty())
933 // Single numeric argument: this can be handled by SwChapterField.
934 bHasHandler = rtl::isAsciiDigit(aReadParam.GetResult()[0]);
935
936 if (bHasHandler)
937 {
938 nRet = aReadParam.SkipToNextToken();
939 // Handle using SwChapterField only in case there is no \[a-z]
940 // switch after the field argument.
941 bHasHandler = nRet < 0 || nRet == '*';
942 }
943 }
944
945 // no routine available
946 if (!bHasHandler || bCodeNest)
947 {
948 if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
949 return Read_F_Tag( &aF ); // Result not as text
950 // only read result
951 if (aF.bResNest && !AcceptableNestedField(aF.nId))
952 return aF.nLen; // Result nested -> unusable
953
954 tools::Long nOldPos = m_pStrm->Tell();
955 OUString aStr;
956 aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+
957 aF.nSCode, aF.nLCode, m_eTextCharSet );
958 m_pStrm->Seek( nOldPos );
959
960 // field codes which contain '/' or '.' are not displayed in WinWord
961 // skip if it is formula field or found space before. see #i119446, #i119585.
962 const sal_Int32 nDotPos = aStr.indexOf('.');
963 const sal_Int32 nSlashPos = aStr.indexOf('/');
964 sal_Int32 nSpacePos = aStr.indexOf( ' ', 1 );
965 if ( nSpacePos<0 )
966 nSpacePos = aStr.getLength();
967
968 if ( ( aStr.getLength() <= 1 || aStr[1] != '=') &&
969 (( nDotPos>=0 && nDotPos < nSpacePos ) ||
970 ( nSlashPos>=0 && nSlashPos < nSpacePos )))
971 return aF.nLen;
972 else
973 {
974 // Link fields aren't supported, but they are bound to an OLE object
975 // that needs to be roundtripped
976 if ( aF.nId == 56 )
977 m_bEmbeddObj = true;
978 // Field not supported: store the field code for later use
979 m_aFieldStack.back().SetBookmarkCode( aStr );
980 return aF.nLen - aF.nLRes - 1; // skipped too many, the resulted field will be read like main text
981 }
982 }
983 else
984 { // read field
985 auto nOldPos = m_pStrm->Tell();
986 OUString aStr;
987 if ( aF.nId == 6 && aF.bCodeNest )
988 {
989 // TODO Extract the whole code string using the nested codes
990 aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs() +
991 aF.nSCode, aF.nSRes - aF.nSCode - 1, m_eTextCharSet );
992 }
993 else
994 {
995 aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+
996 aF.nSCode, aF.nLCode, m_eTextCharSet );
997 }
998
999 // #i51312# - graphics inside field code not supported by Writer.
1000 // Thus, delete character 0x01, which stands for such a graphic.
1001 if (aF.nId==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01.
1002 {
1003 aStr = aStr.replaceAll("\x01", "");
1004 }
1005
1006 eF_ResT eRes = (this->*aWW8FieldTab[aF.nId])( &aF, aStr );
1007 m_pStrm->Seek(nOldPos);
1008
1009 switch ( eRes )
1010 {
1011 case eF_ResT::OK:
1012 return aF.nLen;
1013 case eF_ResT::TEXT:
1014 // skipped too many, the resulted field will be read like main text
1015 // attributes can start at char 0x14 so skip one
1016 // char more back == "-2"
1017 if (aF.nLRes)
1018 return aF.nLen - aF.nLRes - 2;
1019 else
1020 return aF.nLen;
1021 case eF_ResT::TAGIGN:
1022 if ( m_nFieldTagBad[nI] & nMask ) // Flag: Tag bad
1023 return Read_F_Tag( &aF ); // Tag it
1024 return aF.nLen; // or ignore
1025 case eF_ResT::READ_FSPA:
1026 return aF.nLen - aF.nLRes - 2; // position on char 1
1027 default:
1028 return aF.nLen; // ignore
1029 }
1030 }
1031 }
1032
1033 // Tag fields
1034
1035 // MakeTagString() returns the position of the first CR / end of line / page break
1036 // in pText and converts only up to this point.
1037 // If none of these special characters is found, the function returns 0.
MakeTagString(OUString & rStr,const OUString & rOrg)1038 void SwWW8ImplReader::MakeTagString( OUString& rStr, const OUString& rOrg )
1039 {
1040 bool bAllowCr = SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT )
1041 || SwFltGetFlag( m_nFieldFlags, SwFltControlStack::ALLOW_FLD_CR );
1042 sal_Unicode cChar;
1043 rStr = rOrg;
1044
1045 for( sal_Int32 nI = 0;
1046 nI < rStr.getLength() && rStr.getLength() < (MAX_FIELDLEN - 4); ++nI )
1047 {
1048 bool bSetAsHex = false;
1049 cChar = rStr[ nI ];
1050 switch( cChar )
1051 {
1052 case 132: // Exchange typographical quotation marks for normal ones
1053 case 148:
1054 case 147:
1055 rStr = rStr.replaceAt( nI, 1, "\"" );
1056 break;
1057 case 19:
1058 rStr = rStr.replaceAt( nI, 1, "{" );
1059 break; // 19..21 to {|}
1060 case 20:
1061 rStr = rStr.replaceAt( nI, 1, "|" );
1062 break;
1063 case 21:
1064 rStr = rStr.replaceAt( nI, 1, "}" );
1065 break;
1066 case '\\': // Tag \{|} with \ ...
1067 case '{':
1068 case '|':
1069 case '}':
1070 rStr = rStr.replaceAt( nI, 0, "\\" );
1071 ++nI;
1072 break;
1073 case 0x0b:
1074 case 0x0c:
1075 case 0x0d:
1076 if( bAllowCr )
1077 rStr = rStr.replaceAt( nI, 1, "\n" );
1078 else
1079 bSetAsHex = true;
1080 break;
1081 case 0xFE:
1082 case 0xFF:
1083 bSetAsHex = true;
1084 break;
1085 default:
1086 bSetAsHex = 0x20 > cChar;
1087 break;
1088 }
1089
1090 if( bSetAsHex )
1091 {
1092 //all Hex-Numbers with \x before
1093 OUString sTmp( "\\x" );
1094 if( cChar < 0x10 )
1095 sTmp += "0";
1096 sTmp += OUString::number( cChar, 16 );
1097 rStr = rStr.replaceAt( nI, 1 , sTmp );
1098 nI += sTmp.getLength() - 1;
1099 }
1100 }
1101
1102 if( rStr.getLength() > (MAX_FIELDLEN - 4))
1103 rStr = rStr.copy( 0, MAX_FIELDLEN - 4 );
1104 }
1105
InsertTagField(const sal_uInt16 nId,const OUString & rTagText)1106 void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId, const OUString& rTagText )
1107 {
1108 OUString aName("WwFieldTag");
1109 if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_DO_ID ) ) // Number?
1110 aName += OUString::number( nId ); // return it?
1111
1112 if( SwFltGetFlag(m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT))
1113 {
1114 aName += rTagText; // tag as text
1115 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, aName,
1116 SwInsertFlags::NOHINTEXPAND);
1117 }
1118 else
1119 { // tag normally
1120
1121 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1122 SwSetExpFieldType( &m_rDoc, aName, nsSwGetSetExpType::GSE_STRING ) );
1123 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), rTagText ); // SUB_INVISIBLE
1124 sal_uInt16 nSubType = ( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_VISIBLE ) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE;
1125 aField.SetSubType(nSubType | nsSwGetSetExpType::GSE_STRING);
1126
1127 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1128 }
1129 }
1130
Read_F_Tag(WW8FieldDesc * pF)1131 WW8_CP SwWW8ImplReader::Read_F_Tag( WW8FieldDesc* pF )
1132 {
1133 tools::Long nOldPos = m_pStrm->Tell();
1134
1135 WW8_CP nStart = pF->nSCode - 1; // starting with 0x19
1136 WW8_CP nL = pF->nLen; // Total length with result and nest
1137 if( nL > MAX_FIELDLEN )
1138 nL = MAX_FIELDLEN; // MaxLength, by quoting
1139 // max. 4 times as big
1140 OUString sFText;
1141 m_xSBase->WW8ReadString( *m_pStrm, sFText,
1142 m_xPlcxMan->GetCpOfs() + nStart, nL, m_eStructCharSet);
1143
1144 OUString aTagText;
1145 MakeTagString( aTagText, sFText );
1146 InsertTagField( pF->nId, aTagText );
1147
1148 m_pStrm->Seek( nOldPos );
1149 return pF->nLen;
1150 }
1151
1152 // normal fields
1153
Read_F_Input(WW8FieldDesc * pF,OUString & rStr)1154 eF_ResT SwWW8ImplReader::Read_F_Input( WW8FieldDesc* pF, OUString& rStr )
1155 {
1156 OUString aDef;
1157 OUString aQ;
1158 WW8ReadFieldParams aReadParam( rStr );
1159 for (;;)
1160 {
1161 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1162 if ( nRet==-1 )
1163 break;
1164 switch( nRet )
1165 {
1166 case -2:
1167 if( aQ.isEmpty() )
1168 aQ = aReadParam.GetResult();
1169 break;
1170 case 'd':
1171 case 'D':
1172 if ( aReadParam.GoToTokenParam() )
1173 aDef = aReadParam.GetResult();
1174 break;
1175 }
1176 }
1177 if( aDef.isEmpty() )
1178 aDef = GetFieldResult( pF );
1179
1180 if ( pF->nId != 0x01 ) // 0x01 fields have no result
1181 {
1182 SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
1183 aDef, aQ, INP_TXT, 0, false );
1184 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1185 }
1186
1187 return eF_ResT::OK;
1188 }
1189
1190 // GetFieldResult allocates a string and reads the resulted field
GetFieldResult(WW8FieldDesc const * pF)1191 OUString SwWW8ImplReader::GetFieldResult( WW8FieldDesc const * pF )
1192 {
1193 tools::Long nOldPos = m_pStrm->Tell();
1194
1195 WW8_CP nStart = pF->nSRes; // result start
1196 WW8_CP nL = pF->nLRes; // result length
1197 if( !nL )
1198 return OUString(); // no result
1199
1200 if( nL > MAX_FIELDLEN )
1201 nL = MAX_FIELDLEN; // MaxLength, by quoting
1202 // max. 4 times as big
1203
1204 OUString sRes;
1205 m_xSBase->WW8ReadString( *m_pStrm, sRes, m_xPlcxMan->GetCpOfs() + nStart,
1206 nL, m_eStructCharSet );
1207
1208 m_pStrm->Seek( nOldPos );
1209
1210 //replace both CR 0x0D and VT 0x0B with LF 0x0A
1211 // at least in the cases where the result is added to an SwInputField
1212 // there must not be control characters in it
1213 OUStringBuffer buf(sRes.getLength());
1214 for (sal_Int32 i = 0; i < sRes.getLength(); ++i)
1215 {
1216 sal_Unicode const ch(sRes[i]);
1217 if (!linguistic::IsControlChar(ch))
1218 {
1219 buf.append(ch);
1220 }
1221 else
1222 {
1223 switch (ch)
1224 {
1225 case 0x0B:
1226 case '\r':
1227 buf.append('\n');
1228 break;
1229 case '\n':
1230 case '\t':
1231 buf.append(ch);
1232 break;
1233 default:
1234 SAL_INFO("sw.ww8", "GetFieldResult(): filtering control character");
1235 break;
1236 }
1237 }
1238 }
1239 return buf.makeStringAndClear();
1240 }
1241
1242 /*
1243 Bookmarks can be set with fields SET and ASK, and they can be referenced with
1244 REF. When set, they behave like variables in writer, otherwise they behave
1245 like normal bookmarks. We can check whether we should use a show variable
1246 instead of a normal bookmark ref by converting to "show variable" at the end
1247 of the document those refs which look for the content of a bookmark but whose
1248 bookmarks were set with SET or ASK. (See SwWW8FltRefStack)
1249
1250 The other piece of the puzzle is that refs that point to the "location" of the
1251 bookmark will in word actually point to the last location where the bookmark
1252 was set with SET or ASK, not the actual bookmark. This is only noticeable when
1253 a document sets the bookmark more than once. This is because word places the
1254 true bookmark at the location of the last set, but the refs will display the
1255 position of the first set before the ref.
1256
1257 So what we will do is
1258
1259 1) keep a list of all bookmarks that were set, any bookmark names mentioned
1260 here that are referred by content will be converted to show variables.
1261
1262 2) create pseudo bookmarks for every position that a bookmark is set with SET
1263 or ASK but has no existing bookmark. We can then keep a map from the original
1264 bookmark name to the new one. As we parse the document new pseudo names will
1265 replace the older ones, so the map always contains the bookmark of the
1266 location that msword itself would use.
1267
1268 3) word's bookmarks are case insensitive, writers are not. So we need to
1269 map case different versions together, regardless of whether they are
1270 variables or not.
1271
1272 4) when a reference is (first) SET or ASK, the bookmark associated with it
1273 is placed around the 0x14 0x15 result part of the field. We will fiddle
1274 the placement to be the writer equivalent of directly before and after
1275 the field, which gives the same effect and meaning, to do so we must
1276 get any bookmarks in the field range, and begin them immediately before
1277 the set/ask field, and end them directly afterwards. MapBookmarkVariables
1278 returns an identifier of the bookmark attribute to close after inserting
1279 the appropriate set/ask field.
1280 */
MapBookmarkVariables(const WW8FieldDesc * pF,OUString & rOrigName,const OUString & rData)1281 tools::Long SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc* pF,
1282 OUString &rOrigName, const OUString &rData)
1283 {
1284 OSL_ENSURE(m_xPlcxMan, "No pPlcxMan");
1285 tools::Long nNo;
1286 /*
1287 If there was no bookmark associated with this set field, then we create a
1288 pseudo one and insert it in the document.
1289 */
1290 sal_uInt16 nIndex;
1291 m_xPlcxMan->GetBook()->MapName(rOrigName);
1292 OUString sName = m_xPlcxMan->GetBook()->GetBookmark(
1293 pF->nSCode, pF->nSCode + pF->nLen, nIndex);
1294 if (!sName.isEmpty())
1295 {
1296 m_xPlcxMan->GetBook()->SetStatus(nIndex, BOOK_IGNORE);
1297 nNo = nIndex;
1298 }
1299 else
1300 {
1301 nNo = m_xReffingStck->aFieldVarNames.size()+1;
1302 sName = "WWSetBkmk" + OUString::number(nNo);
1303 nNo += m_xPlcxMan->GetBook()->GetIMax();
1304 }
1305 m_xReffedStck->NewAttr(*m_pPaM->GetPoint(),
1306 SwFltBookmark( BookmarkToWriter(sName), rData, nNo ));
1307 m_xReffingStck->aFieldVarNames[rOrigName] = sName;
1308 return nNo;
1309 }
1310
1311 /*
1312 Word can set a bookmark with set or with ask, such a bookmark is equivalent to
1313 our variables, but until the end of a document we cannot be sure if a bookmark
1314 is a variable or not, at the end we will have a list of reference names which
1315 were set or asked, all bookmarks using the content of those bookmarks are
1316 converted to show variables, those that reference the position of the field
1317 can be left as references, because a bookmark is also inserted at the position
1318 of a set or ask field, either by word, or in some special cases by the import
1319 filter itself.
1320 */
RefToVar(const SwField * pField,SwFltStackEntry & rEntry)1321 SwFltStackEntry *SwWW8FltRefStack::RefToVar(const SwField* pField,
1322 SwFltStackEntry &rEntry)
1323 {
1324 SwFltStackEntry *pRet=nullptr;
1325 if (pField && SwFieldIds::GetRef == pField->Which())
1326 {
1327 //Get the name of the ref field, and see if actually a variable
1328 const OUString sName = pField->GetPar1();
1329 std::map<OUString, OUString, SwWW8::ltstr>::const_iterator
1330 aResult = aFieldVarNames.find(sName);
1331
1332 if (aResult != aFieldVarNames.end())
1333 {
1334 SwGetExpField aField( static_cast<SwGetExpFieldType*>(
1335 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp)), sName, nsSwGetSetExpType::GSE_STRING, 0);
1336 SwFormatField aTmp(aField);
1337 rEntry.m_pAttr.reset( aTmp.Clone() );
1338 pRet = &rEntry;
1339 }
1340 }
1341 return pRet;
1342 }
1343
GetMappedBookmark(std::u16string_view rOrigName)1344 OUString SwWW8ImplReader::GetMappedBookmark(std::u16string_view rOrigName)
1345 {
1346 OUString sName(BookmarkToWriter(rOrigName));
1347 OSL_ENSURE(m_xPlcxMan, "no pPlcxMan");
1348 m_xPlcxMan->GetBook()->MapName(sName);
1349
1350 //See if there has been a variable set with this name, if so get
1351 //the pseudo bookmark name that was set with it.
1352 std::map<OUString, OUString, SwWW8::ltstr>::const_iterator aResult =
1353 m_xReffingStck->aFieldVarNames.find(sName);
1354
1355 return (aResult == m_xReffingStck->aFieldVarNames.end())
1356 ? sName : (*aResult).second;
1357 }
1358
1359 // "ASK"
Read_F_InputVar(WW8FieldDesc * pF,OUString & rStr)1360 eF_ResT SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc* pF, OUString& rStr )
1361 {
1362 OUString sOrigName, aQ;
1363 OUString aDef;
1364 WW8ReadFieldParams aReadParam( rStr );
1365 for (;;)
1366 {
1367 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1368 if ( nRet==-1 )
1369 break;
1370 switch( nRet )
1371 {
1372 case -2:
1373 if (sOrigName.isEmpty())
1374 sOrigName = aReadParam.GetResult();
1375 else if (aQ.isEmpty())
1376 aQ = aReadParam.GetResult();
1377 break;
1378 case 'd':
1379 case 'D':
1380 if ( aReadParam.GoToTokenParam() )
1381 aDef = aReadParam.GetResult();
1382 break;
1383 }
1384 }
1385
1386 if (sOrigName.isEmpty())
1387 return eF_ResT::TAGIGN; // does not make sense without textmark
1388
1389 const OUString aResult(GetFieldResult(pF));
1390
1391 //#i24377#, munge Default Text into title as we have only one slot
1392 //available for aResult and aDef otherwise
1393 if (!aDef.isEmpty())
1394 {
1395 if (!aQ.isEmpty())
1396 aQ += " - ";
1397 aQ += aDef;
1398 }
1399
1400 const tools::Long nNo = MapBookmarkVariables(pF, sOrigName, aResult);
1401
1402 SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1403 SwSetExpFieldType(&m_rDoc, sOrigName, nsSwGetSetExpType::GSE_STRING)));
1404 SwSetExpField aField(pFT, aResult);
1405 aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
1406 aField.SetInputFlag(true);
1407 aField.SetPromptText( aQ );
1408
1409 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1410
1411 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
1412 return eF_ResT::OK;
1413 }
1414
1415 // "AUTONR"
Read_F_ANumber(WW8FieldDesc *,OUString & rStr)1416 eF_ResT SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc*, OUString& rStr )
1417 {
1418 if( !m_pNumFieldType ){ // 1st time
1419 SwSetExpFieldType aT( &m_rDoc, "AutoNr", nsSwGetSetExpType::GSE_SEQ );
1420 m_pNumFieldType = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aT );
1421 }
1422 SwSetExpField aField( static_cast<SwSetExpFieldType*>(m_pNumFieldType), OUString(),
1423 GetNumberPara( rStr ) );
1424 aField.SetValue( ++m_nFieldNum, nullptr );
1425 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1426 return eF_ResT::OK;
1427 }
1428
1429 // "SEQ"
Read_F_Seq(WW8FieldDesc *,OUString & rStr)1430 eF_ResT SwWW8ImplReader::Read_F_Seq( WW8FieldDesc*, OUString& rStr )
1431 {
1432 OUString aSequenceName;
1433 OUString aBook;
1434 bool bHidden = false;
1435 bool bFormat = false;
1436 bool bCountOn = true;
1437 OUString sStart;
1438 SvxNumType eNumFormat = SVX_NUM_ARABIC;
1439 WW8ReadFieldParams aReadParam( rStr );
1440 for (;;)
1441 {
1442 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1443 if ( nRet==-1 )
1444 break;
1445 switch( nRet )
1446 {
1447 case -2:
1448 if( aSequenceName.isEmpty() )
1449 aSequenceName = aReadParam.GetResult();
1450 else if( aBook.isEmpty() )
1451 aBook = aReadParam.GetResult();
1452 break;
1453
1454 case 'h':
1455 if( !bFormat )
1456 bHidden = true; // activate hidden flag
1457 break;
1458
1459 case '*':
1460 bFormat = true; // activate format flag
1461 if ( aReadParam.SkipToNextToken()!=-2 )
1462 break;
1463 if ( aReadParam.GetResult()!="MERGEFORMAT" && aReadParam.GetResult()!="CHARFORMAT" )
1464 eNumFormat = GetNumTypeFromName( aReadParam.GetResult() );
1465 break;
1466
1467 case 'r':
1468 bCountOn = false;
1469 if ( aReadParam.SkipToNextToken()==-2 )
1470 sStart = aReadParam.GetResult();
1471 break;
1472
1473 case 'c':
1474 bCountOn = false;
1475 break;
1476
1477 case 'n':
1478 bCountOn = true; // Increase value by one (default)
1479 break;
1480
1481 case 's': // Outline Level
1482 //#i19682, what I have to do with this value?
1483 break;
1484 }
1485 }
1486 if (aSequenceName.isEmpty() && aBook.isEmpty())
1487 return eF_ResT::TAGIGN;
1488
1489 SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1490 SwSetExpFieldType( &m_rDoc, aSequenceName, nsSwGetSetExpType::GSE_SEQ ) ) );
1491 SwSetExpField aField( pFT, OUString(), eNumFormat );
1492
1493 //#i120654# Add bHidden for /h flag (/h: Hide the field result.)
1494 if (bHidden)
1495 aField.SetSubType(aField.GetSubType() | nsSwExtendedSubType::SUB_INVISIBLE);
1496
1497 if (!sStart.isEmpty())
1498 aField.SetFormula( aSequenceName + "=" + sStart );
1499 else if (!bCountOn)
1500 aField.SetFormula(aSequenceName);
1501
1502 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1503 return eF_ResT::OK;
1504 }
1505
Read_F_Styleref(WW8FieldDesc *,OUString & rString)1506 eF_ResT SwWW8ImplReader::Read_F_Styleref(WW8FieldDesc*, OUString& rString)
1507 {
1508 WW8ReadFieldParams aReadParam(rString);
1509 sal_Int32 nRet = aReadParam.SkipToNextToken();
1510 if (nRet != -2)
1511 // \param was found, not normal text.
1512 return eF_ResT::TAGIGN;
1513
1514 OUString aResult = aReadParam.GetResult();
1515 sal_Int32 nResult = aResult.toInt32();
1516 if (nResult < 1)
1517 return eF_ResT::TAGIGN;
1518
1519 SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Chapter);
1520 SwChapterField aField(static_cast<SwChapterFieldType*>(pFieldType), CF_TITLE);
1521 aField.SetLevel(nResult - 1);
1522 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1523
1524 return eF_ResT::OK;
1525 }
1526
Read_F_DocInfo(WW8FieldDesc * pF,OUString & rStr)1527 eF_ResT SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr )
1528 {
1529 sal_uInt16 nSub=0;
1530 // RegInfoFormat, DefaultFormat for DocInfoFields
1531 sal_uInt16 nReg = DI_SUB_AUTHOR;
1532 bool bDateTime = false;
1533
1534 if( 85 == pF->nId )
1535 {
1536 OUString aDocProperty;
1537 WW8ReadFieldParams aReadParam( rStr );
1538 for (;;)
1539 {
1540 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1541 if ( nRet==-1 )
1542 break;
1543 switch( nRet )
1544 {
1545 case -2:
1546 if( aDocProperty.isEmpty() )
1547 aDocProperty = aReadParam.GetResult();
1548 break;
1549 case '*':
1550 //Skip over MERGEFORMAT
1551 (void)aReadParam.SkipToNextToken();
1552 break;
1553 }
1554 }
1555
1556 aDocProperty = aDocProperty.replaceAll("\"", "");
1557
1558 /*
1559 There are up to 26 fields that may be meant by 'DocumentProperty'.
1560 Which of them is to be inserted here ?
1561 This Problem can only be solved by implementing a name matching
1562 method that compares the given Parameter String with the four
1563 possible name sets (english, german, french, spanish)
1564 */
1565
1566 static const char* aName10 = "\x0F"; // SW field code
1567 static const char* aName11 // German
1568 = "TITEL";
1569 static const char* aName12 // French
1570 = "TITRE";
1571 static const char* aName13 // English
1572 = "TITLE";
1573 static const char* aName14 // Spanish
1574 = "TITRO";
1575 static const char* aName20 = "\x15"; // SW field code
1576 static const char* aName21 // German
1577 = "ERSTELLDATUM";
1578 static const char* aName22 // French
1579 = "CR\xC9\xC9";
1580 static const char* aName23 // English
1581 = "CREATED";
1582 static const char* aName24 // Spanish
1583 = "CREADO";
1584 static const char* aName30 = "\x16"; // SW field code
1585 static const char* aName31 // German
1586 = "ZULETZTGESPEICHERTZEIT";
1587 static const char* aName32 // French
1588 = "DERNIERENREGISTREMENT";
1589 static const char* aName33 // English
1590 = "SAVED";
1591 static const char* aName34 // Spanish
1592 = "MODIFICADO";
1593 static const char* aName40 = "\x17"; // SW field code
1594 static const char* aName41 // German
1595 = "ZULETZTGEDRUCKT";
1596 static const char* aName42 // French
1597 = "DERNI\xC8" "REIMPRESSION";
1598 static const char* aName43 // English
1599 = "LASTPRINTED";
1600 static const char* aName44 // Spanish
1601 = "HUPS PUPS";
1602 static const char* aName50 = "\x18"; // SW field code
1603 static const char* aName51 // German
1604 = "\xDC" "BERARBEITUNGSNUMMER";
1605 static const char* aName52 // French
1606 = "NUM\xC9" "RODEREVISION";
1607 static const char* aName53 // English
1608 = "REVISIONNUMBER";
1609 static const char* aName54 // Spanish
1610 = "SNUBBEL BUBBEL";
1611 static const sal_uInt16 nFieldCnt = 5;
1612
1613 // additional fields are to be coded soon!
1614
1615 static const sal_uInt16 nLangCnt = 4;
1616 static const char *aNameSet_26[nFieldCnt][nLangCnt+1] =
1617 {
1618 {aName10, aName11, aName12, aName13, aName14},
1619 {aName20, aName21, aName22, aName23, aName24},
1620 {aName30, aName31, aName32, aName33, aName34},
1621 {aName40, aName41, aName42, aName43, aName44},
1622 {aName50, aName51, aName52, aName53, aName54}
1623 };
1624
1625 bool bFieldFound= false;
1626 sal_uInt16 nFIdx;
1627 for(sal_uInt16 nLIdx=1; !bFieldFound && (nLangCnt > nLIdx); ++nLIdx)
1628 {
1629 for(nFIdx = 0; !bFieldFound && (nFieldCnt > nFIdx); ++nFIdx)
1630 {
1631 if( aDocProperty == OUString( aNameSet_26[nFIdx][nLIdx], strlen(aNameSet_26[nFIdx][nLIdx]),
1632 RTL_TEXTENCODING_MS_1252 ) )
1633 {
1634 bFieldFound = true;
1635 pF->nId = aNameSet_26[nFIdx][0][0];
1636 }
1637 }
1638 }
1639
1640 if( !bFieldFound )
1641 {
1642 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1643 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )), DI_CUSTOM|nReg, aDocProperty, GetFieldResult( pF ) );
1644 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1645
1646 return eF_ResT::OK;
1647 }
1648 }
1649
1650 switch( pF->nId )
1651 {
1652 case 14:
1653 /* supports all INFO variables! */
1654 nSub = DI_KEYS;
1655 break;
1656 case 15:
1657 nSub = DI_TITLE;
1658 break;
1659 case 16:
1660 nSub = DI_SUBJECT;
1661 break;
1662 case 18:
1663 nSub = DI_KEYS;
1664 break;
1665 case 19:
1666 nSub = DI_COMMENT;
1667 break;
1668 case 20:
1669 nSub = DI_CHANGE;
1670 nReg = DI_SUB_AUTHOR;
1671 break;
1672 case 21:
1673 nSub = DI_CREATE;
1674 nReg = DI_SUB_DATE;
1675 bDateTime = true;
1676 break;
1677 case 23:
1678 nSub = DI_PRINT;
1679 nReg = DI_SUB_DATE;
1680 bDateTime = true;
1681 break;
1682 case 24:
1683 nSub = DI_DOCNO;
1684 break;
1685 case 22:
1686 nSub = DI_CHANGE;
1687 nReg = DI_SUB_DATE;
1688 bDateTime = true;
1689 break;
1690 case 25:
1691 nSub = DI_CHANGE;
1692 nReg = DI_SUB_TIME;
1693 bDateTime = true;
1694 break;
1695 case 64: // DOCVARIABLE
1696 nSub = DI_CUSTOM;
1697 break;
1698 }
1699
1700 sal_uInt32 nFormat = 0;
1701
1702 LanguageType nLang(LANGUAGE_SYSTEM);
1703 if (bDateTime)
1704 {
1705 SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, pF->nId);
1706 switch (nDT)
1707 {
1708 case SvNumFormatType::DATE:
1709 nReg = DI_SUB_DATE;
1710 break;
1711 case SvNumFormatType::TIME:
1712 nReg = DI_SUB_TIME;
1713 break;
1714 case SvNumFormatType::DATETIME:
1715 nReg = DI_SUB_DATE;
1716 break;
1717 default:
1718 nReg = DI_SUB_DATE;
1719 break;
1720 }
1721 }
1722
1723 OUString aData;
1724 // Extract DOCVARIABLE varname
1725 if ( 64 == pF->nId )
1726 {
1727 WW8ReadFieldParams aReadParam( rStr );
1728 for (;;)
1729 {
1730 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1731 if ( nRet==-1)
1732 break;
1733 switch( nRet )
1734 {
1735 case -2:
1736 if( aData.isEmpty() )
1737 aData = aReadParam.GetResult();
1738 break;
1739 case '*':
1740 //Skip over MERGEFORMAT
1741 (void)aReadParam.SkipToNextToken();
1742 break;
1743 }
1744 }
1745
1746 aData = aData.replaceAll("\"", "");
1747 }
1748
1749 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1750 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )), nSub|nReg, aData, nFormat );
1751 if (bDateTime)
1752 ForceFieldLanguage(aField, nLang);
1753 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1754
1755 return eF_ResT::OK;
1756 }
1757
Read_F_Author(WW8FieldDesc *,OUString &)1758 eF_ResT SwWW8ImplReader::Read_F_Author( WW8FieldDesc*, OUString& )
1759 {
1760 // SH: The SwAuthorField refers not to the original author but to the current user, better use DocInfo
1761 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1762 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )),
1763 DI_CREATE|DI_SUB_AUTHOR, OUString() );
1764 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1765 return eF_ResT::OK;
1766 }
1767
Read_F_TemplName(WW8FieldDesc *,OUString &)1768 eF_ResT SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc*, OUString& )
1769 {
1770 SwTemplNameField aField( static_cast<SwTemplNameFieldType*>(
1771 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName )), FF_NAME );
1772 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1773 return eF_ResT::OK;
1774 }
1775
1776 // Both the date and the time fields can be used for showing a date a time or both.
Read_F_DateTime(WW8FieldDesc * pF,OUString & rStr)1777 eF_ResT SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc*pF, OUString& rStr )
1778 {
1779 bool bHijri = false;
1780 WW8ReadFieldParams aReadParam(rStr);
1781 for (;;)
1782 {
1783 const sal_Int32 nTok = aReadParam.SkipToNextToken();
1784 if ( nTok==-1 )
1785 break;
1786 switch (nTok)
1787 {
1788 default:
1789 case 'l':
1790 case -2:
1791 break;
1792 case 'h':
1793 bHijri = true;
1794 break;
1795 case 's':
1796 //Saka Calendar, should we do something with this ?
1797 break;
1798 }
1799 }
1800
1801 sal_uInt32 nFormat = 0;
1802
1803 LanguageType nLang(LANGUAGE_SYSTEM);
1804 SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, ww::eDATE, bHijri);
1805
1806 if( SvNumFormatType::UNDEFINED == nDT ) // no D/T-Formatstring
1807 {
1808 if (32 == pF->nId)
1809 {
1810 nDT = SvNumFormatType::TIME;
1811 nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
1812 NF_TIME_START, LANGUAGE_SYSTEM );
1813 }
1814 else
1815 {
1816 nDT = SvNumFormatType::DATE;
1817 nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
1818 NF_DATE_START, LANGUAGE_SYSTEM );
1819 }
1820 }
1821
1822 if (nDT & SvNumFormatType::DATE || nDT == SvNumFormatType::TIME)
1823 {
1824 SwDateTimeField aField(static_cast<SwDateTimeFieldType*>(
1825 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime)),
1826 nDT & SvNumFormatType::DATE ? DATEFLD : TIMEFLD, nFormat);
1827 if (pF->nOpt & 0x10) // Fixed field
1828 {
1829 double fSerial;
1830 if (!m_rDoc.GetNumberFormatter()->IsNumberFormat(GetFieldResult(pF), nFormat, fSerial,
1831 SvNumInputOptions::LAX_TIME))
1832 return eF_ResT::TEXT; // just drop the field and insert the plain text.
1833 aField.SetSubType(aField.GetSubType() | FIXEDFLD);
1834 DateTime aSetDateTime(m_rDoc.GetNumberFormatter()->GetNullDate());
1835 aSetDateTime.AddTime(fSerial);
1836 aField.SetDateTime(aSetDateTime);
1837 }
1838 ForceFieldLanguage(aField, nLang);
1839 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1840 }
1841
1842 return eF_ResT::OK;
1843 }
1844
Read_F_FileName(WW8FieldDesc *,OUString & rStr)1845 eF_ResT SwWW8ImplReader::Read_F_FileName(WW8FieldDesc*, OUString &rStr)
1846 {
1847 SwFileNameFormat eType = FF_NAME;
1848 WW8ReadFieldParams aReadParam(rStr);
1849 for (;;)
1850 {
1851 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1852 if ( nRet==-1 )
1853 break;
1854 switch (nRet)
1855 {
1856 case 'p':
1857 eType = FF_PATHNAME;
1858 break;
1859 case '*':
1860 //Skip over MERGEFORMAT
1861 (void)aReadParam.SkipToNextToken();
1862 break;
1863 default:
1864 OSL_ENSURE(false, "unknown option in FileName field");
1865 break;
1866 }
1867 }
1868
1869 SwFileNameField aField(
1870 static_cast<SwFileNameFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename)), eType);
1871 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1872 return eF_ResT::OK;
1873 }
1874
Read_F_Num(WW8FieldDesc * pF,OUString & rStr)1875 eF_ResT SwWW8ImplReader::Read_F_Num( WW8FieldDesc* pF, OUString& rStr )
1876 {
1877 sal_uInt16 nSub = DS_PAGE; // page number
1878 switch ( pF->nId ){
1879 case 27: nSub = DS_WORD; break; // number of words
1880 case 28: nSub = DS_CHAR; break; // number of characters
1881 }
1882 SwDocStatField aField( static_cast<SwDocStatFieldType*>(
1883 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocStat )), nSub,
1884 GetNumberPara( rStr ) );
1885 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1886 return eF_ResT::OK;
1887 }
1888
Read_F_CurPage(WW8FieldDesc *,OUString & rStr)1889 eF_ResT SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc*, OUString& rStr )
1890 {
1891 // page number
1892 SwPageNumberField aField( static_cast<SwPageNumberFieldType*>(
1893 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber )), PG_RANDOM,
1894 GetNumberPara(rStr, true));
1895
1896 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1897 return eF_ResT::OK;
1898 }
1899
Read_F_Symbol(WW8FieldDesc *,OUString & rStr)1900 eF_ResT SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc*, OUString& rStr )
1901 {
1902 //e.g. #i20118#
1903 OUString aQ;
1904 OUString aName;
1905 sal_Int32 nSize = 0;
1906 WW8ReadFieldParams aReadParam( rStr );
1907 for (;;)
1908 {
1909 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1910 if ( nRet==-1 )
1911 break;
1912 switch( nRet )
1913 {
1914 case -2:
1915 if( aQ.isEmpty() )
1916 aQ = aReadParam.GetResult();
1917 break;
1918 case 'f':
1919 case 'F':
1920 if ( aReadParam.GoToTokenParam() )
1921 aName = aReadParam.GetResult();
1922 break;
1923 case 's':
1924 case 'S':
1925 if ( aReadParam.GoToTokenParam() )
1926 {
1927 const OUString aSiz = aReadParam.GetResult();
1928 if (!aSiz.isEmpty())
1929 {
1930 bool bFail = o3tl::checked_multiply<sal_Int32>(aSiz.toInt32(), 20, nSize); // pT -> twip
1931 if (bFail)
1932 nSize = -1;
1933 }
1934 }
1935 break;
1936 }
1937 }
1938 if( aQ.isEmpty() )
1939 return eF_ResT::TAGIGN; // -> no 0-char in text
1940
1941 sal_Unicode const cChar = static_cast<sal_Unicode>(aQ.toInt32());
1942 if (!linguistic::IsControlChar(cChar) || cChar == '\r' || cChar == '\n' || cChar == '\t')
1943 {
1944 if (!aName.isEmpty()) // Font Name set ?
1945 {
1946 SvxFontItem aFont(FAMILY_DONTKNOW, aName, OUString(),
1947 PITCH_DONTKNOW, RTL_TEXTENCODING_SYMBOL, RES_CHRATR_FONT);
1948 NewAttr(aFont); // new Font
1949 }
1950
1951 if (nSize > 0) //#i20118#
1952 {
1953 SvxFontHeightItem aSz(nSize, 100, RES_CHRATR_FONTSIZE);
1954 NewAttr(aSz);
1955 }
1956
1957 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(cChar));
1958
1959 if (nSize > 0)
1960 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONTSIZE);
1961 if (!aName.isEmpty())
1962 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
1963 }
1964 else
1965 {
1966 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, "###");
1967 }
1968
1969 return eF_ResT::OK;
1970 }
1971
1972 // "EMBED"
Read_F_Embedd(WW8FieldDesc *,OUString & rStr)1973 eF_ResT SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc*, OUString& rStr )
1974 {
1975 WW8ReadFieldParams aReadParam( rStr );
1976 for (;;)
1977 {
1978 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1979 if ( nRet==-1 )
1980 break;
1981 switch( nRet )
1982 {
1983 case -2:
1984 // sHost
1985 break;
1986
1987 case 's':
1988 // use ObjectSize
1989 break;
1990 }
1991 }
1992
1993 if( m_bObj && m_nPicLocFc )
1994 m_nObjLocFc = m_nPicLocFc;
1995 m_bEmbeddObj = true;
1996 return eF_ResT::TEXT;
1997 }
1998
1999 // "SET"
Read_F_Set(WW8FieldDesc * pF,OUString & rStr)2000 eF_ResT SwWW8ImplReader::Read_F_Set( WW8FieldDesc* pF, OUString& rStr )
2001 {
2002 OUString sOrigName;
2003 OUString sVal;
2004 WW8ReadFieldParams aReadParam( rStr );
2005 for (;;)
2006 {
2007 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2008 if ( nRet==-1 )
2009 break;
2010 switch( nRet )
2011 {
2012 case -2:
2013 if (sOrigName.isEmpty())
2014 sOrigName = aReadParam.GetResult();
2015 else if (sVal.isEmpty())
2016 sVal = aReadParam.GetResult();
2017 break;
2018 }
2019 }
2020
2021 const tools::Long nNo = MapBookmarkVariables(pF, sOrigName, sVal);
2022
2023 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc, sOrigName,
2024 nsSwGetSetExpType::GSE_STRING ) );
2025 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), sVal, ULONG_MAX );
2026 aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
2027
2028 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2029
2030 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
2031
2032 return eF_ResT::OK;
2033 }
2034
2035 // "REF"
Read_F_Ref(WW8FieldDesc *,OUString & rStr)2036 eF_ResT SwWW8ImplReader::Read_F_Ref( WW8FieldDesc*, OUString& rStr )
2037 { // Reference - Field
2038 OUString sOrigBkmName;
2039 REFERENCEMARK eFormat = REF_CONTENT;
2040
2041 WW8ReadFieldParams aReadParam( rStr );
2042 for (;;)
2043 {
2044 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2045 if ( nRet==-1 )
2046 break;
2047 switch( nRet )
2048 {
2049 case -2:
2050 if( sOrigBkmName.isEmpty() ) // get name of bookmark
2051 sOrigBkmName = aReadParam.GetResult();
2052 break;
2053
2054 /* References to numbers in Word could be either to a numbered
2055 paragraph or to a chapter number. However Word does not seem to
2056 have the capability we do, of referring to the chapter number some
2057 other bookmark is in. As a result, cross-references to chapter
2058 numbers in a word document will be cross-references to a numbered
2059 paragraph, being the chapter heading paragraph. As it happens, our
2060 cross-references to numbered paragraphs will do the right thing
2061 when the target is a numbered chapter heading, so there is no need
2062 for us to use the REF_CHAPTER bookmark format on import.
2063 */
2064 case 'n':
2065 eFormat = REF_NUMBER_NO_CONTEXT;
2066 break;
2067 case 'r':
2068 eFormat = REF_NUMBER;
2069 break;
2070 case 'w':
2071 eFormat = REF_NUMBER_FULL_CONTEXT;
2072 break;
2073
2074 case 'p':
2075 eFormat = REF_UPDOWN;
2076 break;
2077 case 'h':
2078 break;
2079 default:
2080 // unimplemented switch: just do 'nix nought nothing' :-)
2081 break;
2082 }
2083 }
2084
2085 OUString sBkmName(GetMappedBookmark(sOrigBkmName));
2086
2087 // #i120879# add cross reference bookmark name prefix, if it
2088 // matches internal TOC bookmark naming convention
2089 if ( IsTOCBookmarkName( sBkmName ) )
2090 {
2091 sBkmName = EnsureTOCBookmarkName(sBkmName);
2092 // track <sBookmarkName> as referenced TOC bookmark.
2093 m_xReffedStck->aReferencedTOCBookmarks.insert( sBkmName );
2094 }
2095
2096 SwGetRefField aField(
2097 static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),
2098 sBkmName,"",REF_BOOKMARK,0,eFormat);
2099
2100 if (eFormat == REF_CONTENT)
2101 {
2102 /*
2103 If we are just inserting the contents of the bookmark, then it
2104 is possible that the bookmark is actually a variable, so we
2105 must store it until the end of the document to see if it was,
2106 in which case we'll turn it into a show variable
2107 */
2108 m_xReffingStck->NewAttr( *m_pPaM->GetPoint(), SwFormatField(aField) );
2109 m_xReffingStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2110 }
2111 else
2112 {
2113 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
2114 }
2115 return eF_ResT::OK;
2116 }
2117
2118 // Note Reference - Field
Read_F_NoteReference(WW8FieldDesc *,OUString & rStr)2119 eF_ResT SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc*, OUString& rStr )
2120 {
2121 OUString aBkmName;
2122 bool bAboveBelow = false;
2123
2124 WW8ReadFieldParams aReadParam( rStr );
2125 for (;;)
2126 {
2127 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2128 if ( nRet==-1 )
2129 break;
2130 switch( nRet )
2131 {
2132 case -2:
2133 if( aBkmName.isEmpty() ) // get name of foot/endnote
2134 aBkmName = aReadParam.GetResult();
2135 break;
2136 case 'r':
2137 // activate flag 'Chapter Number'
2138 break;
2139 case 'p':
2140 bAboveBelow = true;
2141 break;
2142 case 'h':
2143 break;
2144 default:
2145 // unimplemented switch: just do 'nix nought nothing' :-)
2146 break;
2147 }
2148 }
2149
2150 // set Sequence No of corresponding Foot-/Endnote to Zero
2151 // (will be corrected in
2152 SwGetRefField aField( static_cast<SwGetRefFieldType*>(
2153 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )), aBkmName, "", REF_FOOTNOTE, 0,
2154 REF_ONLYNUMBER );
2155 m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField));
2156 m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2157 if (bAboveBelow)
2158 {
2159 SwGetRefField aField2( static_cast<SwGetRefFieldType*>(
2160 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),aBkmName, "", REF_FOOTNOTE, 0,
2161 REF_UPDOWN );
2162 m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField2));
2163 m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2164 }
2165 return eF_ResT::OK;
2166 }
2167
2168 // "PAGEREF"
Read_F_PgRef(WW8FieldDesc *,OUString & rStr)2169 eF_ResT SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc*, OUString& rStr )
2170 {
2171 OUString sOrigName;
2172 WW8ReadFieldParams aReadParam( rStr );
2173 for (;;)
2174 {
2175 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2176 if ( nRet==-1 )
2177 break;
2178 else if ( nRet == -2 && sOrigName.isEmpty() )
2179 {
2180 sOrigName = aReadParam.GetResult();
2181 }
2182 }
2183
2184 const OUString sName(GetMappedBookmark(sOrigName));
2185
2186 // loading page reference field in TOX
2187 if (m_bLoadingTOXCache)
2188 {
2189 // insert page ref representation as plain text --> return FLD_TEXT
2190 // if there is no hyperlink settings for current toc and referenced bookmark is available,
2191 // assign link to current ref area
2192 if (!m_bLoadingTOXHyperlink && !sName.isEmpty())
2193 {
2194 // #i120879# add cross reference bookmark name prefix, if it
2195 // matches internal TOC bookmark naming convention
2196 OUString sBookmarkName;
2197 if ( IsTOCBookmarkName( sName ) )
2198 {
2199 sBookmarkName = EnsureTOCBookmarkName(sName);
2200 // track <sBookmarkName> as referenced TOC bookmark.
2201 m_xReffedStck->aReferencedTOCBookmarks.insert( sBookmarkName );
2202 }
2203 else
2204 {
2205 sBookmarkName = sName;
2206 }
2207 OUString sURL = "#" + sBookmarkName;
2208 SwFormatINetFormat aURL( sURL, "" );
2209 static const OUStringLiteral sLinkStyle(u"Index Link");
2210 const sal_uInt16 nPoolId =
2211 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
2212 aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId);
2213 aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
2214 m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
2215 }
2216 return eF_ResT::TEXT;
2217 }
2218
2219 // #i120879# add cross reference bookmark name prefix, if it matches
2220 // internal TOC bookmark naming convention
2221 OUString sPageRefBookmarkName;
2222 if ( IsTOCBookmarkName( sName ) )
2223 {
2224 sPageRefBookmarkName = EnsureTOCBookmarkName(sName);
2225 // track <sPageRefBookmarkName> as referenced TOC bookmark.
2226 m_xReffedStck->aReferencedTOCBookmarks.insert( sPageRefBookmarkName );
2227 }
2228 else
2229 {
2230 sPageRefBookmarkName = sName;
2231 }
2232 SwGetRefField aField( static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),
2233 sPageRefBookmarkName, "", REF_BOOKMARK, 0, REF_PAGE );
2234 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2235
2236 return eF_ResT::OK;
2237 }
2238
2239 //helper function
2240 //For MS MacroButton field, the symbol in plain text is always "(" (0x28),
2241 //which should be mapped according to the macro type
ConvertMacroSymbol(std::u16string_view rName,OUString & rReference)2242 static bool ConvertMacroSymbol( std::u16string_view rName, OUString& rReference )
2243 {
2244 bool bConverted = false;
2245 if( rReference == "(" )
2246 {
2247 bConverted = true;
2248 sal_Unicode cSymbol = sal_Unicode(); // silence false warning
2249 if (rName == u"CheckIt")
2250 cSymbol = 0xF06F;
2251 else if (rName == u"UncheckIt")
2252 cSymbol = 0xF0FE;
2253 else if (rName == u"ShowExample")
2254 cSymbol = 0xF02A;
2255 //else if... : todo
2256 else
2257 bConverted = false;
2258
2259 if( bConverted )
2260 rReference = OUString(cSymbol);
2261 }
2262 return bConverted;
2263 }
2264
2265 // "MACROBUTTON"
Read_F_Macro(WW8FieldDesc *,OUString & rStr)2266 eF_ResT SwWW8ImplReader::Read_F_Macro( WW8FieldDesc*, OUString& rStr)
2267 {
2268 OUString aName;
2269 OUString aVText;
2270 bool bNewVText = true;
2271 bool bBracket = false;
2272 WW8ReadFieldParams aReadParam( rStr );
2273
2274 sal_Int32 nOffset = 0;
2275
2276 for (;;)
2277 {
2278 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2279 if ( nRet==-1 )
2280 break;
2281 switch( nRet )
2282 {
2283 case -2:
2284 if( aName.isEmpty() )
2285 aName = aReadParam.GetResult();
2286 else if( aVText.isEmpty() || bBracket )
2287 {
2288 nOffset = aReadParam.GetTokenSttPtr() + 1;
2289
2290 if( bBracket )
2291 aVText += " ";
2292 aVText += aReadParam.GetResult();
2293 if (bNewVText)
2294 {
2295 bBracket = (aVText[0] == '[');
2296 bNewVText = false;
2297 }
2298 else if( aVText.endsWith("]") )
2299 bBracket = false;
2300 }
2301 break;
2302 }
2303 }
2304 if( aName.isEmpty() )
2305 return eF_ResT::TAGIGN; // makes no sense without Macro-Name
2306
2307 NotifyMacroEventRead();
2308
2309 //try converting macro symbol according to macro name
2310 bool bApplyWingdings = ConvertMacroSymbol( aName, aVText );
2311 aName = "StarOffice.Standard.Modul1." + aName;
2312
2313 SwMacroField aField( static_cast<SwMacroFieldType*>(
2314 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Macro )), aName, aVText );
2315
2316 if( !bApplyWingdings )
2317 {
2318
2319 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2320 WW8_CP nOldCp = m_xPlcxMan->Where();
2321 WW8_CP nCp = nOldCp + nOffset;
2322
2323 SwPaM aPaM(*m_pPaM, m_pPaM);
2324 aPaM.SetMark();
2325 aPaM.Move(fnMoveBackward);
2326 aPaM.Exchange();
2327
2328 m_pPostProcessAttrsInfo.reset(new WW8PostProcessAttrsInfo(nCp, nCp, aPaM));
2329 }
2330 else
2331 {
2332 //set Wingdings font
2333 sal_uInt16 i = 0;
2334 for ( ; i < m_xFonts->GetMax(); i++ )
2335 {
2336 FontFamily eFamily;
2337 OUString aFontName;
2338 FontPitch ePitch;
2339 rtl_TextEncoding eSrcCharSet;
2340 if( GetFontParams( i, eFamily, aFontName, ePitch, eSrcCharSet )
2341 && aFontName=="Wingdings" )
2342 {
2343 break;
2344 }
2345 }
2346
2347 if ( i < m_xFonts->GetMax() )
2348 {
2349
2350 SetNewFontAttr( i, true, RES_CHRATR_FONT );
2351 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2352 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT );
2353 ResetCharSetVars();
2354 }
2355 }
2356
2357 return eF_ResT::OK;
2358 }
2359
WW8PostProcessAttrsInfo(WW8_CP nCpStart,WW8_CP nCpEnd,SwPaM & rPaM)2360 WW8PostProcessAttrsInfo::WW8PostProcessAttrsInfo(WW8_CP nCpStart, WW8_CP nCpEnd,
2361 SwPaM & rPaM)
2362 : mbCopy(false)
2363 , mnCpStart(nCpStart)
2364 , mnCpEnd(nCpEnd)
2365 , mPaM(*rPaM.GetMark(), *rPaM.GetPoint())
2366 , mItemSet(rPaM.GetDoc().GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_END - 1>{})
2367 {
2368 }
2369
CanUseRemoteLink(const OUString & rGrfName)2370 bool CanUseRemoteLink(const OUString &rGrfName)
2371 {
2372 bool bUseRemote = false;
2373 try
2374 {
2375 // Related: tdf#102499, add a default css::ucb::XCommandEnvironment
2376 // in order to have https protocol manage certificates correctly
2377 uno::Reference< task::XInteractionHandler > xIH(
2378 task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr));
2379
2380 uno::Reference< ucb::XProgressHandler > xProgress;
2381 rtl::Reference<::ucbhelper::CommandEnvironment> pCommandEnv =
2382 new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH ), xProgress);
2383
2384 ::ucbhelper::Content aCnt(rGrfName,
2385 static_cast< ucb::XCommandEnvironment* >(pCommandEnv.get()),
2386 comphelper::getProcessComponentContext());
2387
2388 if ( !INetURLObject( rGrfName ).isAnyKnownWebDAVScheme() )
2389 {
2390 OUString aTitle;
2391 aCnt.getPropertyValue("Title") >>= aTitle;
2392 bUseRemote = !aTitle.isEmpty();
2393 }
2394 else
2395 {
2396 // is a link to a WebDAV resource
2397 // need to use MediaType to check for link usability
2398 OUString aMediaType;
2399 aCnt.getPropertyValue("MediaType") >>= aMediaType;
2400 bUseRemote = !aMediaType.isEmpty();
2401 }
2402 }
2403 catch ( ... )
2404 {
2405 // this file did not exist, so we will not set this as graphiclink
2406 bUseRemote = false;
2407 }
2408 return bUseRemote;
2409 }
2410
2411 // "INCLUDEPICTURE"
Read_F_IncludePicture(WW8FieldDesc *,OUString & rStr)2412 eF_ResT SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr )
2413 {
2414 OUString aGrfName;
2415 bool bEmbedded = true;
2416
2417 WW8ReadFieldParams aReadParam( rStr );
2418 for (;;)
2419 {
2420 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2421 if ( nRet==-1 )
2422 break;
2423 switch( nRet )
2424 {
2425 case -2:
2426 if (aGrfName.isEmpty())
2427 aGrfName = ConvertFFileName(aReadParam.GetResult());
2428 break;
2429
2430 case 'd':
2431 bEmbedded = false;
2432 break;
2433
2434 case 'c':// skip the converter name
2435 aReadParam.FindNextStringPiece();
2436 break;
2437 }
2438 }
2439
2440 if (!bEmbedded)
2441 bEmbedded = !CanUseRemoteLink(aGrfName);
2442
2443 if (!bEmbedded)
2444 {
2445 /*
2446 Special case:
2447
2448 Now we write the Link into the Doc and remember the SwFlyFrameFormat.
2449 Since we end on return FLD_READ_FSPA below, the skip value will be set
2450 so that Char-1 will still be read.
2451 When we then call SwWW8ImplReader::ImportGraf() it will then recognize
2452 that we have inserted a graphic link and the suiting SwAttrSet will be
2453 inserted into the frame format.
2454 */
2455 SfxItemSet aFlySet( m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN,
2456 RES_FRMATR_END-1>{} );
2457 aFlySet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
2458 aFlySet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
2459 m_pFlyFormatOfJustInsertedGraphic =
2460 m_rDoc.getIDocumentContentOperations().InsertGraphic(*m_pPaM,
2461 aGrfName,
2462 OUString(),
2463 nullptr, // Graphic*
2464 &aFlySet,
2465 nullptr, nullptr); // SwFrameFormat*
2466 m_aGrfNameGenerator.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic,
2467 INetURLObject(aGrfName).GetBase());
2468 }
2469 return eF_ResT::READ_FSPA;
2470 }
2471
UniqueName()2472 OUString wwSectionNamer::UniqueName()
2473 {
2474 const OUString aName(msFileLinkSeed + OUString::number(++mnFileSectionNo));
2475 return mrDoc.GetUniqueSectionName(&aName);
2476 }
2477
2478 // "INCLUDETEXT"
Read_F_IncludeText(WW8FieldDesc *,OUString & rStr)2479 eF_ResT SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc* /*pF*/, OUString& rStr )
2480 {
2481 OUString aPara;
2482 OUString aBook;
2483 WW8ReadFieldParams aReadParam( rStr );
2484 for (;;)
2485 {
2486 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2487 if ( nRet==-1 )
2488 break;
2489 switch( nRet )
2490 {
2491 case -2:
2492 if( aPara.isEmpty() )
2493 aPara = aReadParam.GetResult();
2494 else if( aBook.isEmpty() )
2495 aBook = aReadParam.GetResult();
2496 break;
2497 case '*':
2498 //Skip over MERGEFORMAT
2499 (void)aReadParam.SkipToNextToken();
2500 break;
2501 }
2502 }
2503 aPara = ConvertFFileName(aPara);
2504
2505 if (!aBook.isEmpty() && aBook[ 0 ] != '\\')
2506 {
2507 // Section from Source (no switch)?
2508 ConvertUFName(aBook);
2509 aPara += OUStringChar(sfx2::cTokenSeparator)
2510 + OUStringChar(sfx2::cTokenSeparator) + aBook;
2511 }
2512
2513 /*
2514 ##509##
2515 What we will do is insert a section to be linked to a file, but just in
2516 case the file is not available we will fill in the section with the stored
2517 content of this winword field as a fallback.
2518 */
2519 SwPosition aTmpPos(*m_pPaM->GetPoint());
2520
2521 SwSectionData aSection(SectionType::FileLink,
2522 m_aSectionNameGenerator.UniqueName());
2523 aSection.SetLinkFileName( aPara );
2524 aSection.SetProtectFlag(true);
2525
2526 SwSection *const pSection =
2527 m_rDoc.InsertSwSection(*m_pPaM, aSection, nullptr, nullptr, false);
2528 OSL_ENSURE(pSection, "no section inserted");
2529 if (!pSection)
2530 return eF_ResT::TEXT;
2531 const SwSectionNode* pSectionNode = pSection->GetFormat()->GetSectionNode();
2532 OSL_ENSURE(pSectionNode, "no section node!");
2533 if (!pSectionNode)
2534 return eF_ResT::TEXT;
2535
2536 m_pPaM->GetPoint()->nNode = pSectionNode->GetIndex()+1;
2537 m_pPaM->GetPoint()->nContent.Assign(m_pPaM->GetContentNode(), 0 );
2538
2539 //we have inserted a section before this point, so adjust pos
2540 //for future page/section segment insertion
2541 m_aSectionManager.PrependedInlineNode(aTmpPos, m_pPaM->GetNode());
2542
2543 return eF_ResT::TEXT;
2544 }
2545
2546 // "SERIALPRINT"
Read_F_DBField(WW8FieldDesc * pF,OUString & rStr)2547 eF_ResT SwWW8ImplReader::Read_F_DBField( WW8FieldDesc* pF, OUString& rStr )
2548 {
2549 #if !HAVE_FEATURE_DBCONNECTIVITY
2550 (void) pF;
2551 (void) rStr;
2552 #else
2553 OUString aName;
2554 WW8ReadFieldParams aReadParam( rStr );
2555 for (;;)
2556 {
2557 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2558 if ( nRet==-1 )
2559 break;
2560 switch( nRet )
2561 {
2562 case -2:
2563 if( aName.isEmpty() )
2564 aName = aReadParam.GetResult();
2565 break;
2566 }
2567 }
2568 SwDBFieldType aD( &m_rDoc, aName, SwDBData() ); // Database: nothing
2569
2570 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aD );
2571 SwDBField aField( static_cast<SwDBFieldType*>(pFT) );
2572 aField.SetFieldCode( rStr );
2573
2574 OUString aResult;
2575 m_xSBase->WW8ReadString( *m_pStrm, aResult, m_xPlcxMan->GetCpOfs()+
2576 pF->nSRes, pF->nLRes, m_eTextCharSet );
2577
2578 aResult = aResult.replace( '\xb', '\n' );
2579
2580 aField.InitContent(aResult);
2581
2582 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField( aField ));
2583 #endif
2584 return eF_ResT::OK;
2585 }
2586
2587 // "NEXT"
Read_F_DBNext(WW8FieldDesc *,OUString &)2588 eF_ResT SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc*, OUString& )
2589 {
2590 #if HAVE_FEATURE_DBCONNECTIVITY
2591 SwDBNextSetFieldType aN;
2592 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
2593 SwDBNextSetField aField( static_cast<SwDBNextSetFieldType*>(pFT), OUString(),
2594 SwDBData() ); // Database: nothing
2595 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2596 #endif
2597 return eF_ResT::OK;
2598 }
2599
2600 // "DATASET"
Read_F_DBNum(WW8FieldDesc *,OUString &)2601 eF_ResT SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc*, OUString& )
2602 {
2603 #if HAVE_FEATURE_DBCONNECTIVITY
2604 SwDBSetNumberFieldType aN;
2605 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
2606 SwDBSetNumberField aField( static_cast<SwDBSetNumberFieldType*>(pFT),
2607 SwDBData() ); // Datenbase: nothing
2608 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2609 #endif
2610 return eF_ResT::OK;
2611 }
2612
2613 /*
2614 EQ , only the usage for
2615 a. Combined Characters supported, must be exactly in the form that word
2616 only accepts as combined characters, i.e.
2617 eq \o(\s\up Y(XXX),\s\do Y(XXX))
2618 b. Ruby Text supported, must be in the form that word recognizes as being
2619 ruby text
2620 ...
2621 */
Read_F_Equation(WW8FieldDesc *,OUString & rStr)2622 eF_ResT SwWW8ImplReader::Read_F_Equation( WW8FieldDesc*, OUString& rStr )
2623 {
2624 WW8ReadFieldParams aReadParam( rStr );
2625 const sal_Int32 cChar = aReadParam.SkipToNextToken();
2626 if ('o' == cChar || 'O' == cChar)
2627 {
2628 EquationResult aResult(ParseCombinedChars(rStr));
2629
2630 if (aResult.sType == "Input")
2631 {
2632 SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
2633 aResult.sResult, aResult.sResult, INP_TXT, 0 );
2634 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); // insert input field
2635 }
2636 else if (aResult.sType == "CombinedCharacters")
2637 {
2638 SwCombinedCharField aField(static_cast<SwCombinedCharFieldType*>(
2639 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars)), aResult.sType);
2640 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
2641 }
2642 }
2643 else if ('*' == cChar)
2644 Read_SubF_Ruby(aReadParam);
2645
2646 return eF_ResT::OK;
2647 }
2648
Read_SubF_Ruby(WW8ReadFieldParams & rReadParam)2649 void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams& rReadParam)
2650 {
2651 sal_uInt16 nJustificationCode=0;
2652 OUString sFontName;
2653 sal_uInt32 nFontSize=0;
2654 OUString sRuby;
2655 OUString sText;
2656 for (;;)
2657 {
2658 const sal_Int32 nRet = rReadParam.SkipToNextToken();
2659 if ( nRet==-1 )
2660 break;
2661 switch( nRet )
2662 {
2663 case -2:
2664 {
2665 OUString sTemp = rReadParam.GetResult();
2666 if( sTemp.startsWithIgnoreAsciiCase( "jc" ) )
2667 {
2668 sTemp = sTemp.copy(2);
2669 nJustificationCode = o3tl::narrowing<sal_uInt16>(sTemp.toInt32());
2670 }
2671 else if( sTemp.startsWithIgnoreAsciiCase( "hps" ) )
2672 {
2673 sTemp = sTemp.copy(3);
2674 nFontSize= static_cast<sal_uInt32>(sTemp.toInt32());
2675 }
2676 else if( sTemp.startsWithIgnoreAsciiCase( "Font:" ) )
2677 {
2678 sTemp = sTemp.copy(5);
2679 sFontName = sTemp;
2680 }
2681 }
2682 break;
2683 case '*':
2684 break;
2685 case 'o':
2686 for (;;)
2687 {
2688 const sal_Int32 nRes = rReadParam.SkipToNextToken();
2689 if ( nRes==-1 )
2690 break;
2691 if ('u' == nRes)
2692 {
2693 if (-2 == rReadParam.SkipToNextToken() &&
2694 rReadParam.GetResult().startsWithIgnoreAsciiCase("p"))
2695 {
2696 if (-2 == rReadParam.SkipToNextToken())
2697 {
2698 OUString sPart = rReadParam.GetResult();
2699 sal_Int32 nBegin = sPart.indexOf('(');
2700
2701 //Word disallows brackets in this field,
2702 sal_Int32 nEnd = sPart.indexOf(')');
2703
2704 if ((nBegin != -1) &&
2705 (nEnd != -1) && (nBegin < nEnd))
2706 {
2707 sRuby = sPart.copy(nBegin+1,nEnd-nBegin-1);
2708 }
2709 if (-1 != nEnd)
2710 {
2711 nBegin = sPart.indexOf(',',nEnd);
2712 if (-1 == nBegin)
2713 {
2714 nBegin = sPart.indexOf(';',nEnd);
2715 }
2716 nEnd = sPart.lastIndexOf(')');
2717 }
2718 if ((nBegin != -1) && (nEnd != -1) && (nBegin < nEnd))
2719 {
2720 sText = sPart.copy(nBegin+1,nEnd-nBegin-1);
2721 sText = sw::FilterControlChars(sText);
2722 }
2723 }
2724 }
2725 }
2726 }
2727 break;
2728 }
2729 }
2730
2731 //Translate and apply
2732 if (sRuby.isEmpty() || sText.isEmpty() || sFontName.isEmpty() || !nFontSize)
2733 return;
2734
2735 css::text::RubyAdjust eRubyAdjust;
2736 switch (nJustificationCode)
2737 {
2738 case 0:
2739 eRubyAdjust = css::text::RubyAdjust_CENTER;
2740 break;
2741 case 1:
2742 eRubyAdjust = css::text::RubyAdjust_BLOCK;
2743 break;
2744 case 2:
2745 eRubyAdjust = css::text::RubyAdjust_INDENT_BLOCK;
2746 break;
2747 default:
2748 case 3:
2749 eRubyAdjust = css::text::RubyAdjust_LEFT;
2750 break;
2751 case 4:
2752 eRubyAdjust = css::text::RubyAdjust_RIGHT;
2753 break;
2754 }
2755
2756 SwFormatRuby aRuby(sRuby);
2757 const SwCharFormat *pCharFormat=nullptr;
2758 //Make a guess at which of asian of western we should be setting
2759 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
2760 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(sRuby, 0);
2761
2762 //Check to see if we already have a ruby charstyle that this fits
2763 for(const auto& rpCharFormat : m_aRubyCharFormats)
2764 {
2765 const SvxFontHeightItem &rFH =
2766 ItemGet<SvxFontHeightItem>(*rpCharFormat,
2767 GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
2768 if (rFH.GetHeight() == nFontSize*10)
2769 {
2770 const SvxFontItem &rF = ItemGet<SvxFontItem>(*rpCharFormat,
2771 GetWhichOfScript(RES_CHRATR_FONT,nScript));
2772 if (rF.GetFamilyName() == sFontName)
2773 {
2774 pCharFormat = rpCharFormat;
2775 break;
2776 }
2777 }
2778 }
2779
2780 //Create a new char style if necessary
2781 if (!pCharFormat)
2782 {
2783 OUString aNm;
2784 //Take this as the base name
2785 SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT,aNm);
2786 aNm+=OUString::number(m_aRubyCharFormats.size()+1);
2787 SwCharFormat *pFormat = m_rDoc.MakeCharFormat(aNm, m_rDoc.GetDfltCharFormat());
2788 SvxFontHeightItem aHeightItem(nFontSize*10, 100, RES_CHRATR_FONTSIZE);
2789 SvxFontItem aFontItem(FAMILY_DONTKNOW,sFontName,
2790 OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
2791 aHeightItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
2792 aFontItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONT,nScript));
2793 pFormat->SetFormatAttr(aHeightItem);
2794 pFormat->SetFormatAttr(aFontItem);
2795 m_aRubyCharFormats.push_back(pFormat);
2796 pCharFormat = pFormat;
2797 }
2798
2799 //Set the charstyle and justification
2800 aRuby.SetCharFormatName(pCharFormat->GetName());
2801 aRuby.SetCharFormatId(pCharFormat->GetPoolFormatId());
2802 aRuby.SetAdjustment(eRubyAdjust);
2803
2804 NewAttr(aRuby);
2805 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, sText );
2806 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CJK_RUBY );
2807
2808 }
2809
2810 // "table of ..." fields
2811
lcl_toxMatchACSwitch(SwDoc const & rDoc,SwTOXBase & rBase,WW8ReadFieldParams & rParam,SwCaptionDisplay eCaptionType)2812 static void lcl_toxMatchACSwitch(SwDoc const & rDoc,
2813 SwTOXBase& rBase,
2814 WW8ReadFieldParams& rParam,
2815 SwCaptionDisplay eCaptionType)
2816 {
2817 if ( rParam.GoToTokenParam() )
2818 {
2819 SwTOXType* pType = const_cast<SwTOXType*>(rDoc.GetTOXType( TOX_ILLUSTRATIONS, 0));
2820 rBase.RegisterToTOXType( *pType );
2821 rBase.SetCaptionDisplay( eCaptionType );
2822 // Read Sequence Name and store in TOXBase
2823 OUString sSeqName( rParam.GetResult() );
2824 lcl_ConvertSequenceName( sSeqName );
2825 rBase.SetSequenceName( sSeqName );
2826 }
2827 }
2828
EnsureMaxLevelForTemplates(SwTOXBase & rBase)2829 static void EnsureMaxLevelForTemplates(SwTOXBase& rBase)
2830 {
2831 //If the TOC contains Template entries at levels > the evaluation level
2832 //that was initially taken from the max normal outline level of the word TOC
2833 //then we cannot use that for the evaluation level because writer cuts off
2834 //all styles above that level, while word just cuts off the "standard"
2835 //outline styles, we have no option but to expand to the highest level
2836 //Word included.
2837 if ((rBase.GetLevel() != MAXLEVEL) && (SwTOXElement::Template & rBase.GetCreateType()))
2838 {
2839 for (sal_uInt16 nI = MAXLEVEL; nI > 0; --nI)
2840 {
2841 if (!rBase.GetStyleNames(nI-1).isEmpty())
2842 {
2843 rBase.SetLevel(nI);
2844 break;
2845 }
2846 }
2847 }
2848 }
2849
lcl_toxMatchTSwitch(SwWW8ImplReader const & rReader,SwTOXBase & rBase,WW8ReadFieldParams & rParam)2850 static void lcl_toxMatchTSwitch(SwWW8ImplReader const & rReader, SwTOXBase& rBase,
2851 WW8ReadFieldParams& rParam)
2852 {
2853 if ( !rParam.GoToTokenParam() )
2854 return;
2855
2856 OUString sParams( rParam.GetResult() );
2857 if( sParams.isEmpty() )
2858 return;
2859
2860 sal_Int32 nIndex = 0;
2861
2862 // Delimiters between styles and style levels appears to allow both ; and ,
2863
2864 OUString sTemplate( sParams.getToken(0, ';', nIndex) );
2865 if( -1 == nIndex )
2866 {
2867 nIndex=0;
2868 sTemplate = sParams.getToken(0, ',', nIndex);
2869 }
2870 if( -1 == nIndex )
2871 {
2872 const SwFormat* pStyle = rReader.GetStyleWithOrgWWName(sTemplate);
2873 if( pStyle )
2874 sTemplate = pStyle->GetName();
2875 // Store Style for Level 0 into TOXBase
2876 rBase.SetStyleNames( sTemplate, 0 );
2877 }
2878 else while( -1 != nIndex )
2879 {
2880 sal_Int32 nOldIndex=nIndex;
2881 sal_uInt16 nLevel = o3tl::narrowing<sal_uInt16>(
2882 sParams.getToken(0, ';', nIndex).toInt32());
2883 if( -1 == nIndex )
2884 {
2885 nIndex = nOldIndex;
2886 nLevel = o3tl::narrowing<sal_uInt16>(
2887 sParams.getToken(0, ',', nIndex).toInt32());
2888 }
2889
2890 if( (0 < nLevel) && (MAXLEVEL >= nLevel) )
2891 {
2892 nLevel--;
2893 // Store Style and Level into TOXBase
2894 const SwFormat* pStyle
2895 = rReader.GetStyleWithOrgWWName( sTemplate );
2896
2897 if( pStyle )
2898 sTemplate = pStyle->GetName();
2899
2900 OUString sStyles( rBase.GetStyleNames( nLevel ) );
2901 if( !sStyles.isEmpty() )
2902 sStyles += OUStringChar(TOX_STYLE_DELIMITER);
2903 sStyles += sTemplate;
2904 rBase.SetStyleNames( sStyles, nLevel );
2905 }
2906 // read next style name...
2907 nOldIndex = nIndex;
2908 sTemplate = sParams.getToken(0, ';', nIndex);
2909 if( -1 == nIndex )
2910 {
2911 nIndex=nOldIndex;
2912 sTemplate = sParams.getToken(0, ',', nIndex);
2913 }
2914 }
2915 }
2916
CurrentSectionColCount() const2917 sal_uInt16 wwSectionManager::CurrentSectionColCount() const
2918 {
2919 sal_uInt16 nIndexCols = 1;
2920 if (!maSegments.empty())
2921 nIndexCols = maSegments.back().maSep.ccolM1 + 1;
2922 return nIndexCols;
2923 }
2924
2925 //Will there be a new pagebreak at this position (don't know what type
2926 //until later)
WillHavePageDescHere(const SwNodeIndex & rIdx) const2927 bool wwSectionManager::WillHavePageDescHere(const SwNodeIndex& rIdx) const
2928 {
2929 bool bRet = false;
2930 if (!maSegments.empty())
2931 {
2932 if (!maSegments.back().IsContinuous() &&
2933 maSegments.back().maStart == rIdx)
2934 {
2935 bRet = true;
2936 }
2937 }
2938 return bRet;
2939 }
2940
lcl_GetMaxValidWordTOCLevel(const SwForm & rForm)2941 static sal_uInt16 lcl_GetMaxValidWordTOCLevel(const SwForm &rForm)
2942 {
2943 // GetFormMax() returns level + 1, hence the -1
2944 sal_uInt16 nRet = rForm.GetFormMax()-1;
2945
2946 // If the max of this type of TOC is greater than the max of a word
2947 // possible toc, then clip to the word max
2948 if (nRet > WW8ListManager::nMaxLevel)
2949 nRet = WW8ListManager::nMaxLevel;
2950
2951 return nRet;
2952 }
2953
Read_F_Tox(WW8FieldDesc * pF,OUString & rStr)2954 eF_ResT SwWW8ImplReader::Read_F_Tox( WW8FieldDesc* pF, OUString& rStr )
2955 {
2956 if (!m_bLoadingTOXCache)
2957 {
2958 m_bLoadingTOXCache = true;
2959 }
2960 else
2961 {
2962 // Embedded TOX --> continue reading its content, but no further TOX
2963 // field
2964 ++m_nEmbeddedTOXLevel;
2965 return eF_ResT::TEXT;
2966 }
2967
2968 if (pF->nLRes < 3)
2969 return eF_ResT::TEXT; // ignore (#i25440#)
2970
2971 TOXTypes eTox; // create a ToxBase
2972 switch( pF->nId )
2973 {
2974 case 8:
2975 eTox = TOX_INDEX;
2976 break;
2977 case 13:
2978 eTox = TOX_CONTENT;
2979 break;
2980 default:
2981 eTox = TOX_USER;
2982 break;
2983 }
2984
2985 SwTOXElement nCreateOf = (eTox == TOX_CONTENT) ? SwTOXElement::OutlineLevel : SwTOXElement::Mark;
2986
2987 sal_uInt16 nIndexCols = 1;
2988
2989 const SwTOXType* pType = m_rDoc.GetTOXType( eTox, 0 );
2990 SwForm aOrigForm(eTox);
2991 std::shared_ptr<SwTOXBase> pBase = std::make_shared<SwTOXBase>( pType, aOrigForm, nCreateOf, OUString() );
2992 pBase->SetProtected(m_aSectionManager.CurrentSectionIsProtected());
2993 switch( eTox ){
2994 case TOX_INDEX:
2995 {
2996 SwTOIOptions eOptions = SwTOIOptions::SameEntry | SwTOIOptions::CaseSensitive;
2997
2998 // We set SwTOXElement::OutlineLevel only if
2999 // the parameter \o is within 1 to 9
3000 // or the parameter \f exists
3001 // or NO switch parameter are given at all.
3002 WW8ReadFieldParams aReadParam( rStr );
3003 for (;;)
3004 {
3005 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3006 if ( nRet==-1 )
3007 break;
3008 switch( nRet )
3009 {
3010 case 'c':
3011 if ( aReadParam.GoToTokenParam() )
3012 {
3013 const OUString sParams( aReadParam.GetResult() );
3014 // if NO OUString just ignore the \c
3015 if( !sParams.isEmpty() )
3016 {
3017 nIndexCols = o3tl::narrowing<sal_uInt16>(sParams.toInt32());
3018 }
3019 }
3020 break;
3021 case 'e':
3022 {
3023 if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \e
3024 {
3025 OUString sDelimiter( aReadParam.GetResult() );
3026 SwForm aForm( pBase->GetTOXForm() );
3027
3028 // Attention: if TOX_CONTENT brave
3029 // GetFormMax() returns MAXLEVEL + 1 !!
3030 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3031
3032 for(sal_uInt16 nLevel = 1;
3033 nLevel <= nEnd;
3034 ++nLevel)
3035 {
3036 // Levels count from 1
3037 // Level 0 is reserved for CAPTION
3038
3039 // Insert delimiter instead of tab in front of the page number if there is one:
3040 FormTokenType ePrevType = TOKEN_END;
3041 FormTokenType eType;
3042 // -> #i21237#
3043 SwFormTokens aPattern =
3044 aForm.GetPattern(nLevel);
3045 SwFormTokens::iterator aIt = aPattern.begin();
3046 do
3047 {
3048 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3049
3050 if (eType == TOKEN_PAGE_NUMS)
3051 {
3052 if (TOKEN_TAB_STOP == ePrevType)
3053 {
3054 --aIt;
3055
3056 if(0x09 == sDelimiter[0])
3057 aIt->eTabAlign = SvxTabAdjust::End;
3058 else
3059 {
3060 SwFormToken aToken(TOKEN_TEXT);
3061 aToken.sText = sDelimiter;
3062 *aIt = aToken;
3063 }
3064 aForm.SetPattern(nLevel, aPattern);
3065 }
3066
3067 eType = TOKEN_END;
3068 }
3069
3070 ePrevType = eType;
3071 }
3072 while (TOKEN_END != eType);
3073 // <- #i21237#
3074 }
3075 pBase->SetTOXForm( aForm );
3076 }
3077 }
3078 break;
3079 case 'h':
3080 {
3081 eOptions |= SwTOIOptions::AlphaDelimiter;
3082 }
3083 break;
3084 }
3085 }
3086 pBase->SetOptions( eOptions );
3087 }
3088 break;
3089
3090 case TOX_CONTENT:
3091 {
3092 bool bIsHyperlink = false;
3093 // We set SwTOXElement::OutlineLevel only if
3094 // the parameter \o is within 1 to 9
3095 // or the parameter \f exists
3096 // or NO switch parameter are given at all.
3097 SwTOXElement eCreateFrom = SwTOXElement::NONE;
3098 sal_Int32 nMaxLevel = 0;
3099 WW8ReadFieldParams aReadParam( rStr );
3100 for (;;)
3101 {
3102 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3103 if ( nRet==-1 )
3104 break;
3105 switch( nRet )
3106 {
3107 case 'h':
3108 bIsHyperlink = true;
3109 break;
3110 case 'a':
3111 case 'c':
3112 lcl_toxMatchACSwitch(m_rDoc, *pBase, aReadParam,
3113 ('c' == nRet)
3114 ? CAPTION_COMPLETE
3115 : CAPTION_TEXT );
3116 break;
3117 case 'o':
3118 {
3119 sal_Int32 nVal;
3120 if( !aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
3121 nVal = lcl_GetMaxValidWordTOCLevel(aOrigForm);
3122 if( nMaxLevel < nVal )
3123 nMaxLevel = nVal;
3124 eCreateFrom |= SwTOXElement::OutlineLevel;
3125 }
3126 break;
3127 case 'f':
3128 eCreateFrom |= SwTOXElement::Mark;
3129 break;
3130 case 'l':
3131 {
3132 sal_Int32 nVal;
3133 if( aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
3134 {
3135 if( nMaxLevel < nVal )
3136 nMaxLevel = nVal;
3137 eCreateFrom |= SwTOXElement::Mark;
3138 }
3139 }
3140 break;
3141 case 't': // paragraphs using special styles shall
3142 // provide the TOX's content
3143 lcl_toxMatchTSwitch(*this, *pBase, aReadParam);
3144 eCreateFrom |= SwTOXElement::Template;
3145 break;
3146 case 'p':
3147 {
3148 if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \p
3149 {
3150 OUString sDelimiter( aReadParam.GetResult() );
3151 SwForm aForm( pBase->GetTOXForm() );
3152
3153 // Attention: if TOX_CONTENT brave
3154 // GetFormMax() returns MAXLEVEL + 1 !!
3155 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3156
3157 for(sal_uInt16 nLevel = 1;
3158 nLevel <= nEnd;
3159 ++nLevel)
3160 {
3161 // Levels count from 1
3162 // Level 0 is reserved for CAPTION
3163
3164 // Insert delimiter instead of tab in front of the pagenumber if there is one:
3165 FormTokenType ePrevType = TOKEN_END;
3166 FormTokenType eType;
3167
3168 // -> #i21237#
3169 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3170 SwFormTokens::iterator aIt = aPattern.begin();
3171 do
3172 {
3173 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3174
3175 if (eType == TOKEN_PAGE_NUMS)
3176 {
3177 if (TOKEN_TAB_STOP == ePrevType)
3178 {
3179 --aIt;
3180
3181 SwFormToken aToken(TOKEN_TEXT);
3182 aToken.sText = sDelimiter;
3183
3184 *aIt = aToken;
3185 aForm.SetPattern(nLevel,
3186 aPattern);
3187 }
3188 eType = TOKEN_END;
3189 }
3190 ePrevType = eType;
3191 }
3192 while( TOKEN_END != eType );
3193 // <- #i21237#
3194 }
3195 pBase->SetTOXForm( aForm );
3196 }
3197 }
3198 break;
3199 case 'n': // don't print page numbers
3200 {
3201 // read START and END param
3202 sal_Int32 nStart(0);
3203 sal_Int32 nEnd(0);
3204 if( !aReadParam.GetTokenSttFromTo( &nStart, &nEnd,
3205 WW8ListManager::nMaxLevel ) )
3206 {
3207 nStart = 1;
3208 nEnd = aOrigForm.GetFormMax()-1;
3209 }
3210 // remove page numbers from this levels
3211 SwForm aForm( pBase->GetTOXForm() );
3212 if (aForm.GetFormMax() <= nEnd)
3213 nEnd = aForm.GetFormMax()-1;
3214 for ( sal_Int32 nLevel = nStart; nLevel<=nEnd; ++nLevel )
3215 {
3216 // Levels count from 1
3217 // Level 0 is reserved for CAPTION
3218
3219 // Remove pagenumber and if necessary the tab in front of it:
3220 FormTokenType eType;
3221 // -> #i21237#
3222 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3223 SwFormTokens::iterator aIt = aPattern.begin();
3224 do
3225 {
3226 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3227
3228 if (eType == TOKEN_PAGE_NUMS)
3229 {
3230 aIt = aPattern.erase(aIt);
3231 --aIt;
3232 if (
3233 TOKEN_TAB_STOP ==
3234 aIt->eTokenType
3235 )
3236 {
3237 aPattern.erase(aIt);
3238 aForm.SetPattern(nLevel, aPattern);
3239 }
3240 eType = TOKEN_END;
3241 }
3242 }
3243 while (TOKEN_END != eType);
3244 // <- #i21237#
3245 }
3246 pBase->SetTOXForm( aForm );
3247 }
3248 break;
3249
3250 /*
3251 // the following switches are not (yet) supported
3252 // by good old StarWriter:
3253 case 'b':
3254 case 's':
3255 case 'd':
3256 break;
3257 */
3258 }
3259 }
3260
3261 // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens
3262 // also include the hyperlinks and page references
3263 SwFormToken aLinkStart(TOKEN_LINK_START);
3264 SwFormToken aLinkEnd(TOKEN_LINK_END);
3265 aLinkStart.sCharStyleName = "Index Link";
3266 aLinkEnd.sCharStyleName = "Index Link";
3267 SwForm aForm(pBase->GetTOXForm());
3268 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3269
3270 for(sal_uInt16 nLevel = 1; nLevel <= nEnd; ++nLevel)
3271 {
3272 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3273 if ( bIsHyperlink )
3274 {
3275 aPattern.insert(aPattern.begin(), aLinkStart);
3276 }
3277 else
3278 {
3279 auto aItr = std::find_if(aPattern.begin(), aPattern.end(),
3280 [](const SwFormToken& rToken) { return rToken.eTokenType == TOKEN_PAGE_NUMS; });
3281 if (aItr != aPattern.end())
3282 aPattern.insert(aItr, aLinkStart);
3283 }
3284 aPattern.push_back(aLinkEnd);
3285 aForm.SetPattern(nLevel, aPattern);
3286 }
3287 pBase->SetTOXForm(aForm);
3288
3289 if (!nMaxLevel)
3290 nMaxLevel = WW8ListManager::nMaxLevel;
3291 pBase->SetLevel(nMaxLevel);
3292
3293 const TOXTypes eType = pBase->GetTOXType()->GetType();
3294 switch( eType )
3295 {
3296 case TOX_CONTENT:
3297 {
3298 //If we would be created from outlines, either explicitly or by default
3299 //then see if we need extra styles added to the outlines
3300 SwTOXElement eEffectivelyFrom = eCreateFrom != SwTOXElement::NONE ? eCreateFrom : SwTOXElement::OutlineLevel;
3301 if (eEffectivelyFrom & SwTOXElement::OutlineLevel)
3302 {
3303 // #i19683# Insert a text token " " between the number and entry token.
3304 // In an ideal world we could handle the tab stop between the number and
3305 // the entry correctly, but I currently have no clue how to obtain
3306 // the tab stop position. It is _not_ set at the paragraph style.
3307 std::unique_ptr<SwForm> pForm;
3308 for (const SwWW8StyInf & rSI : m_vColl)
3309 {
3310 if (rSI.IsOutlineNumbered())
3311 {
3312 sal_uInt16 nStyleLevel = rSI.mnWW8OutlineLevel;
3313 const SwNumFormat& rFormat = rSI.GetOutlineNumrule()->Get( nStyleLevel );
3314 if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() )
3315 {
3316 ++nStyleLevel;
3317
3318 if ( !pForm )
3319 pForm.reset(new SwForm( pBase->GetTOXForm() ));
3320
3321 SwFormTokens aPattern = pForm->GetPattern(nStyleLevel);
3322 SwFormTokens::iterator aIt =
3323 find_if(aPattern.begin(), aPattern.end(),
3324 SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
3325
3326 if ( aIt != aPattern.end() )
3327 {
3328 SwFormToken aNumberEntrySeparator( TOKEN_TEXT );
3329 aNumberEntrySeparator.sText = " ";
3330 aPattern.insert( ++aIt, aNumberEntrySeparator );
3331 pForm->SetPattern( nStyleLevel, aPattern );
3332 }
3333 }
3334 }
3335 }
3336 if ( pForm )
3337 {
3338 pBase->SetTOXForm( *pForm );
3339 }
3340 }
3341
3342 if (eCreateFrom != SwTOXElement::NONE)
3343 pBase->SetCreate(eCreateFrom);
3344 EnsureMaxLevelForTemplates(*pBase);
3345 }
3346 break;
3347 case TOX_ILLUSTRATIONS:
3348 {
3349 if( eCreateFrom == SwTOXElement::NONE )
3350 eCreateFrom = SwTOXElement::Sequence;
3351 pBase->SetCreate( eCreateFrom );
3352
3353 /*
3354 We don't know until here if we are an illustration
3355 or not, and so have being used a TOX_CONTENT so far
3356 which has 10 levels, while TOX has only two, this
3357 level is set only in the constructor of SwForm, so
3358 create a new one and copy over anything that could
3359 be set in the old one, and remove entries from the
3360 pattern which do not apply to illustration indices
3361 */
3362 SwForm aOldForm( pBase->GetTOXForm() );
3363 SwForm aNewForm( eType );
3364 sal_uInt16 nNewEnd = aNewForm.GetFormMax()-1;
3365
3366 // #i21237#
3367 for(sal_uInt16 nLevel = 1; nLevel <= nNewEnd; ++nLevel)
3368 {
3369 SwFormTokens aPattern = aOldForm.GetPattern(nLevel);
3370 SwFormTokens::iterator new_end =
3371 remove_if(aPattern.begin(), aPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
3372 aPattern.erase(new_end, aPattern.end() ); // table index imported with wrong page number format
3373 aForm.SetPattern( nLevel, aPattern );
3374 aForm.SetTemplate( nLevel, aOldForm.GetTemplate(nLevel) );
3375 }
3376
3377 pBase->SetTOXForm( aNewForm );
3378 }
3379 break;
3380 default:
3381 OSL_ENSURE(false, "Unhandled toc options!");
3382 break;
3383 }
3384 }
3385 break;
3386 case TOX_USER:
3387 break;
3388 default:
3389 OSL_ENSURE(false, "Unhandled toc options!");
3390 break;
3391 } // ToxBase fertig
3392
3393 // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX
3394 pBase->AdjustTabStops( m_rDoc );
3395
3396 //#i10028# inserting a toc implicitly acts like a parabreak in word and writer
3397 if ( m_pPaM->End() &&
3398 m_pPaM->End()->nNode.GetNode().GetTextNode() &&
3399 m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() != 0 )
3400 {
3401 m_bCareFirstParaEndInToc = true;
3402 }
3403
3404 if (m_pPaM->GetPoint()->nContent.GetIndex())
3405 AppendTextNode(*m_pPaM->GetPoint());
3406
3407 const SwPosition* pPos = m_pPaM->GetPoint();
3408
3409 SwFltTOX aFltTOX( pBase );
3410
3411 // test if there is already a break item on this node
3412 if(SwContentNode* pNd = pPos->nNode.GetNode().GetContentNode())
3413 {
3414 const SfxItemSet* pSet = pNd->GetpSwAttrSet();
3415 if( pSet )
3416 {
3417 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
3418 aFltTOX.SetHadBreakItem(true);
3419 if (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false))
3420 aFltTOX.SetHadPageDescItem(true);
3421 }
3422 }
3423
3424 //Will there be a new pagebreak at this position (don't know what type
3425 //until later)
3426 if (m_aSectionManager.WillHavePageDescHere(pPos->nNode))
3427 aFltTOX.SetHadPageDescItem(true);
3428
3429 // Set start in stack
3430 m_xReffedStck->NewAttr( *pPos, aFltTOX );
3431
3432 m_rDoc.InsertTableOf(*m_pPaM->GetPoint(), aFltTOX.GetBase());
3433
3434 //The TOC field representation contents should be inserted into TOC section, but not after TOC section.
3435 //So we need update the document position when loading TOC representation and after loading TOC;
3436 m_pPosAfterTOC.reset(new SwPaM(*m_pPaM, m_pPaM));
3437 (*m_pPaM).Move(fnMoveBackward);
3438 SwPaM aRegion(*m_pPaM, m_pPaM);
3439
3440 OSL_ENSURE(SwDoc::GetCurTOX(*aRegion.GetPoint()), "Misunderstood how toc works");
3441 if (SwTOXBase* pBase2 = SwDoc::GetCurTOX(*aRegion.GetPoint()))
3442 {
3443 pBase2->SetMSTOCExpression(rStr);
3444
3445 if ( nIndexCols > 1 )
3446 {
3447 // Set the column number for index
3448 SfxItemSet aSet( m_rDoc.GetAttrPool(), svl::Items<RES_COL, RES_COL>{} );
3449 SwFormatCol aCol;
3450 aCol.Init( nIndexCols, 708, USHRT_MAX );
3451 aSet.Put( aCol );
3452 pBase2->SetAttrSet( aSet );
3453 }
3454
3455 // inserting a toc inserts a section before this point, so adjust pos
3456 // for future page/section segment insertion
3457 m_aSectionManager.PrependedInlineNode( *m_pPosAfterTOC->GetPoint(), aRegion.GetNode() );
3458 }
3459
3460 // Set end in stack
3461 m_xReffedStck->SetAttr( *pPos, RES_FLTR_TOX );
3462
3463 if (!m_aApos.back()) //a para end in apo doesn't count
3464 m_bWasParaEnd = true;
3465
3466 //Return FLD_TEXT, instead of FLD_OK
3467 //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed
3468 //FLD_OK means the current field loading is finished. The rest part should be ignored.
3469 return eF_ResT::TEXT;
3470 }
3471
Read_F_Shape(WW8FieldDesc *,OUString &)3472 eF_ResT SwWW8ImplReader::Read_F_Shape(WW8FieldDesc* /*pF*/, OUString& /*rStr*/)
3473 {
3474 /*
3475 #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring
3476 to be ignored followed by a 0x1 with an empty drawing. Detect in inserting
3477 the drawing that we are in the Shape field and respond accordingly
3478 */
3479 return eF_ResT::TEXT;
3480 }
3481
Read_F_Hyperlink(WW8FieldDesc *,OUString & rStr)3482 eF_ResT SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc* /*pF*/, OUString& rStr )
3483 {
3484 OUString sURL, sTarget, sMark;
3485
3486 //HYPERLINK "filename" [switches]
3487 rStr = comphelper::string::stripEnd(rStr, 1);
3488
3489 bool bOptions = false;
3490 WW8ReadFieldParams aReadParam( rStr );
3491 for (;;)
3492 {
3493 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3494 if ( nRet==-1 )
3495 break;
3496 switch( nRet )
3497 {
3498 case -2:
3499 if (sURL.isEmpty() && !bOptions)
3500 sURL = ConvertFFileName(aReadParam.GetResult());
3501 break;
3502
3503 case 'n':
3504 sTarget = "_blank";
3505 bOptions = true;
3506 break;
3507
3508 case 'l':
3509 bOptions = true;
3510 if ( aReadParam.SkipToNextToken()==-2 )
3511 {
3512 sMark = aReadParam.GetResult();
3513 if( sMark.endsWith("\""))
3514 {
3515 sMark = sMark.copy( 0, sMark.getLength() - 1 );
3516 }
3517 // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention
3518 if ( IsTOCBookmarkName( sMark ) )
3519 {
3520 sMark = EnsureTOCBookmarkName(sMark);
3521 // track <sMark> as referenced TOC bookmark.
3522 m_xReffedStck->aReferencedTOCBookmarks.insert( sMark );
3523 }
3524
3525 if (m_bLoadingTOXCache)
3526 {
3527 m_bLoadingTOXHyperlink = true; //on loading a TOC field nested hyperlink field
3528 }
3529 }
3530 break;
3531 case 't':
3532 bOptions = true;
3533 if ( aReadParam.SkipToNextToken()==-2 )
3534 sTarget = aReadParam.GetResult();
3535 break;
3536 case 'h':
3537 case 'm':
3538 OSL_ENSURE( false, "Analysis still missing - unknown data" );
3539 [[fallthrough]];
3540 case 's': //worthless fake anchor option
3541 bOptions = true;
3542 break;
3543 }
3544 }
3545
3546 // use the result
3547 OSL_ENSURE(!sURL.isEmpty() || !sMark.isEmpty(), "WW8: Empty URL");
3548
3549 if( !sMark.isEmpty() )
3550 sURL += "#" + sMark;
3551
3552 SwFormatINetFormat aURL(sURL, sTarget);
3553 // If on loading TOC field, change the default style into the "index link"
3554 if (m_bLoadingTOXCache)
3555 {
3556 OUString sLinkStyle("Index Link");
3557 sal_uInt16 nPoolId =
3558 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
3559 aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId );
3560 aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
3561 }
3562
3563 //As an attribute this needs to be closed, and that'll happen from
3564 //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms
3565 //between the start and begin, their hyperlinks will be set at that time
3566 //as well.
3567 m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
3568 return eF_ResT::TEXT;
3569 }
3570
lcl_ImportTox(SwDoc & rDoc,SwPaM const & rPaM,const OUString & rStr,bool bIdx)3571 static void lcl_ImportTox(SwDoc &rDoc, SwPaM const &rPaM, const OUString &rStr, bool bIdx)
3572 {
3573 TOXTypes eTox = ( !bIdx ) ? TOX_CONTENT : TOX_INDEX; // Default
3574
3575 sal_uInt16 nLevel = 1;
3576
3577 OUString sFieldText;
3578 WW8ReadFieldParams aReadParam(rStr);
3579 for (;;)
3580 {
3581 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3582 if ( nRet==-1 )
3583 break;
3584 switch( nRet )
3585 {
3586 case -2:
3587 if( sFieldText.isEmpty() )
3588 {
3589 // PrimaryKey without ":", 2nd after
3590 sFieldText = aReadParam.GetResult();
3591 }
3592 break;
3593
3594 case 'f':
3595 if ( aReadParam.GoToTokenParam() )
3596 {
3597 const OUString sParams( aReadParam.GetResult() );
3598 if( sParams[0]!='C' && sParams[0]!='c' )
3599 eTox = TOX_USER;
3600 }
3601 break;
3602
3603 case 'l':
3604 if ( aReadParam.GoToTokenParam() )
3605 {
3606 const OUString sParams( aReadParam.GetResult() );
3607 // if NO String just ignore the \l
3608 if( !sParams.isEmpty() && sParams[0]>'0' && sParams[0]<='9' )
3609 {
3610 nLevel = o3tl::narrowing<sal_uInt16>(sParams.toInt32());
3611 }
3612 }
3613 break;
3614 }
3615 }
3616
3617 OSL_ENSURE( rDoc.GetTOXTypeCount( eTox ), "Doc.GetTOXTypeCount() == 0 :-(" );
3618
3619 const SwTOXType* pT = rDoc.GetTOXType( eTox, 0 );
3620 SwTOXMark aM( pT );
3621
3622 if( eTox != TOX_INDEX )
3623 aM.SetLevel( nLevel );
3624 else
3625 {
3626 sal_Int32 nFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM );
3627 if( -1 != nFnd ) // it exist levels
3628 {
3629 aM.SetPrimaryKey( sFieldText.copy( 0, nFnd ) );
3630 sal_Int32 nScndFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM, nFnd+1 );
3631 if( -1 != nScndFnd )
3632 {
3633 aM.SetSecondaryKey( sFieldText.copy( nFnd+1, nScndFnd - nFnd - 1 ));
3634 nFnd = nScndFnd;
3635 }
3636 sFieldText = sFieldText.copy( nFnd+1 );
3637 }
3638 }
3639
3640 if (!sFieldText.isEmpty())
3641 {
3642 aM.SetAlternativeText( sFieldText );
3643 rDoc.getIDocumentContentOperations().InsertPoolItem( rPaM, aM );
3644 }
3645 }
3646
ImportTox(int nFieldId,const OUString & aStr)3647 void SwWW8ImplReader::ImportTox( int nFieldId, const OUString& aStr )
3648 {
3649 bool bIdx = (nFieldId != 9);
3650 lcl_ImportTox(m_rDoc, *m_pPaM, aStr, bIdx);
3651 }
3652
Read_FieldVanish(sal_uInt16,const sal_uInt8 *,short nLen)3653 void SwWW8ImplReader::Read_FieldVanish( sal_uInt16, const sal_uInt8*, short nLen )
3654 {
3655 //Meaningless in a style
3656 if (m_pCurrentColl || !m_xPlcxMan)
3657 return;
3658
3659 const int nChunk = 64; //number of characters to read at one time
3660
3661 // Careful: MEMICMP doesn't work with fieldnames including umlauts!
3662 const static char *aFieldNames[] = { "\x06""INHALT", "\x02""XE", // dt.
3663 "\x02""TC" }; // us
3664 const static sal_uInt8 aFieldId[] = { 9, 4, 9 };
3665
3666 if( nLen < 0 )
3667 {
3668 m_bIgnoreText = false;
3669 return;
3670 }
3671
3672 // our method was called from
3673 // ''Skip attributes of field contents'' loop within ReadTextAttr()
3674 if( m_bIgnoreText )
3675 return;
3676
3677 m_bIgnoreText = true;
3678 tools::Long nOldPos = m_pStrm->Tell();
3679
3680 WW8_CP nStartCp = m_xPlcxMan->Where() + m_xPlcxMan->GetCpOfs();
3681
3682 OUString sFieldName;
3683 sal_Int32 nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sFieldName, nStartCp,
3684 nChunk, m_eStructCharSet );
3685 nStartCp+=nFieldLen;
3686
3687 sal_Int32 nC = 0;
3688 //If the first chunk did not start with a field start then
3689 //reset the stream position and give up
3690 if( !nFieldLen || sFieldName[nC]!=0x13 ) // Field Start Mark
3691 {
3692 // If Field End Mark found
3693 if( nFieldLen && sFieldName[nC]==0x15 )
3694 m_bIgnoreText = false;
3695 m_pStrm->Seek( nOldPos );
3696 return; // no field found
3697 }
3698
3699 sal_Int32 nFnd;
3700 //If this chunk does not contain a field end, keep reading chunks
3701 //until we find one, or we run out of text,
3702 for (;;)
3703 {
3704 nFnd = sFieldName.indexOf(0x15);
3705 //found field end, we can stop now
3706 if (nFnd != -1)
3707 break;
3708 OUString sTemp;
3709 nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sTemp,
3710 nStartCp, nChunk, m_eStructCharSet );
3711 sFieldName+=sTemp;
3712 nStartCp+=nFieldLen;
3713 if (!nFieldLen)
3714 break;
3715 }
3716
3717 m_pStrm->Seek( nOldPos );
3718
3719 //if we have no 0x15 give up, otherwise erase everything from the 0x15
3720 //onwards
3721 if (nFnd<0)
3722 return;
3723
3724 sFieldName = sFieldName.copy(0, nFnd);
3725
3726 nC++;
3727 while ( sFieldName[nC]==' ' )
3728 nC++;
3729
3730 for( int i = 0; i < 3; i++ )
3731 {
3732 const char* pName = aFieldNames[i];
3733 const sal_Int32 nNameLen = static_cast<sal_Int32>(*pName++);
3734 if( sFieldName.matchIgnoreAsciiCaseAsciiL( pName, nNameLen, nC ) )
3735 {
3736 ImportTox( aFieldId[i], sFieldName.copy( nC + nNameLen ) );
3737 break; // no duplicates allowed
3738 }
3739 }
3740 m_bIgnoreText = true;
3741 m_pStrm->Seek( nOldPos );
3742 }
3743
3744 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3745