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