android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / ww8par5.cxx
bloba5f1d6892fbb747acd8d828dfe053433a19d5c6e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
21 #include <config_fuzzers.h>
23 #include <sal/types.h>
24 #include <tools/solar.h>
25 #include <comphelper/processfactory.hxx>
26 #include <comphelper/string.hxx>
27 #include <comphelper/simplefileaccessinteraction.hxx>
28 #include <com/sun/star/embed/XStorage.hpp>
29 #include <com/sun/star/embed/ElementModes.hpp>
30 #include <com/sun/star/embed/XTransactedObject.hpp>
31 #include <com/sun/star/task/InteractionHandler.hpp>
33 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
34 #include <svl/cintitem.hxx>
35 #include <svl/lngmisc.hxx>
36 #include <svl/urihelper.hxx>
37 #include <svl/numformat.hxx>
38 #include <svl/zforlist.hxx>
39 #include <svl/zformat.hxx>
40 #include <sfx2/linkmgr.hxx>
41 #include <rtl/character.hxx>
42 #include <unotools/charclass.hxx>
44 #include <ucbhelper/content.hxx>
45 #include <ucbhelper/commandenvironment.hxx>
47 #include <com/sun/star/i18n/XBreakIterator.hpp>
48 #include <hintids.hxx>
49 #include <editeng/fontitem.hxx>
50 #include <editeng/fhgtitem.hxx>
51 #include <editeng/langitem.hxx>
52 #include <fmtfld.hxx>
53 #include <fmtanchr.hxx>
54 #include <pam.hxx>
55 #include <doc.hxx>
56 #include <IDocumentFieldsAccess.hxx>
57 #include <IDocumentMarkAccess.hxx>
58 #include <IDocumentState.hxx>
59 #include <flddat.hxx>
60 #include <docufld.hxx>
61 #include <reffld.hxx>
62 #include <IMark.hxx>
63 #include <expfld.hxx>
64 #include <dbfld.hxx>
65 #include <tox.hxx>
66 #include <section.hxx>
67 #include <ndtxt.hxx>
68 #include <fmtinfmt.hxx>
69 #include <chpfld.hxx>
70 #include <fmtruby.hxx>
71 #include <charfmt.hxx>
72 #include <breakit.hxx>
73 #include <fmtclds.hxx>
74 #include <poolfmt.hxx>
75 #include <SwStyleNameMapper.hxx>
77 #include "ww8scan.hxx"
78 #include "ww8par.hxx"
79 #include "writerhelper.hxx"
80 #include <o3tl/safeint.hxx>
81 #include <o3tl/string_view.hxx>
82 #include <unotools/fltrcfg.hxx>
83 #include <xmloff/odffields.hxx>
84 #include <osl/diagnose.h>
86 #include <algorithm>
87 #include <string_view>
89 #define MAX_FIELDLEN 64000
91 #define WW8_TOX_LEVEL_DELIM ':'
93 using namespace ::com::sun::star;
94 using namespace msfilter::util;
95 using namespace sw::util;
96 using namespace sw::mark;
97 using namespace nsSwDocInfoSubType;
99 // Bookmarks
100 namespace
102 // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention
103 bool IsTOCBookmarkName(std::u16string_view rName)
105 return o3tl::starts_with(rName, u"_Toc") || o3tl::starts_with(rName, Concat2View(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"));
108 OUString EnsureTOCBookmarkName(const OUString& rName)
110 OUString sTmp = rName;
111 if ( IsTOCBookmarkName ( rName ) )
113 if ( ! rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
114 sTmp = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName;
116 return sTmp;
120 tools::Long SwWW8ImplReader::Read_Book(WW8PLCFManResult*)
122 // should also work via pRes.nCo2OrIdx
123 WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
124 if( !pB )
126 OSL_ENSURE( pB, "WW8PLCFx_Book - Pointer does not exist" );
127 return 0;
130 eBookStatus eB = pB->GetStatus();
131 if (eB & BOOK_IGNORE)
132 return 0; // ignore bookmark
134 if (pB->GetIsEnd())
136 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true,
137 pB->GetHandle(), (eB & BOOK_FIELD)!=0);
138 return 0;
141 // "_Hlt*" are unnecessary
142 const OUString* pName = pB->GetName();
143 // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available.
144 // So the hidden bookmarks inside for hyperlink jumping also should be kept.
145 if ( !pName ||
146 pName->startsWithIgnoreAsciiCase( "_Hlt" ) )
148 return 0;
151 // do NOT call ToUpper as the bookmark name can also be a hyperlink target!
153 OUString aVal;
154 if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::BOOK_TO_VAR_REF ) )
156 // set variable for translation bookmark
157 tools::Long nLen = pB->GetLen();
158 if( nLen > MAX_FIELDLEN )
159 nLen = MAX_FIELDLEN;
161 sal_uInt64 nOldPos = m_pStrm->Tell();
162 m_xSBase->WW8ReadString( *m_pStrm, aVal, pB->GetStartPos(), nLen,
163 m_eStructCharSet );
164 m_pStrm->Seek( nOldPos );
166 // now here the implementation of the old "QuoteString" and
167 // I hope with a better performance as before. It's also only
168 // needed if the filterflags say we will convert bookmarks
169 // to SetExpFields! And this the exception!
171 bool bSetAsHex;
172 bool bAllowCr = SwFltGetFlag(m_nFieldFlags,
173 SwFltControlStack::ALLOW_FLD_CR);
175 for( sal_Int32 nI = 0;
176 nI < aVal.getLength() && aVal.getLength() < (MAX_FIELDLEN - 4);
177 ++nI )
179 const sal_Unicode cChar = aVal[nI];
180 switch( cChar )
182 case 0x0b:
183 case 0x0c:
184 case 0x0d:
185 if( bAllowCr )
187 aVal = aVal.replaceAt( nI, 1, u"\n" );
188 bSetAsHex = false;
190 else
191 bSetAsHex = true;
192 break;
194 case 0xFE:
195 case 0xFF:
196 bSetAsHex = true;
197 break;
199 default:
200 bSetAsHex = 0x20 > cChar;
201 break;
204 if( bSetAsHex )
206 //all Hex-Numbers with \x before
207 OUString sTmp( "\\x" );
208 if( cChar < 0x10 )
209 sTmp += "0";
210 sTmp += OUString::number( cChar, 16 );
211 aVal = aVal.replaceAt( nI, 1 , sTmp );
212 nI += sTmp.getLength() - 1;
216 if ( aVal.getLength() > (MAX_FIELDLEN - 4))
217 aVal = aVal.copy( 0, MAX_FIELDLEN - 4 );
220 //e.g. inserting bookmark around field result, so we need to put
221 //it around the entire writer field, as we don't have the separation
222 //of field and field result of word, see #i16941#
223 SwPosition aStart(*m_pPaM->GetPoint());
224 if (!m_aFieldStack.empty())
226 const WW8FieldEntry &rTest = m_aFieldStack.back();
227 aStart = rTest.maStartPos;
230 const OUString sOrigName = BookmarkToWriter(*pName);
231 m_xReffedStck->NewAttr( aStart,
232 SwFltBookmark( EnsureTOCBookmarkName( sOrigName ), aVal, pB->GetHandle(), IsTOCBookmarkName( sOrigName ) ));
233 return 0;
236 tools::Long SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult*)
238 if (WW8PLCFx_AtnBook* pAtnBook = m_xPlcxMan->GetAtnBook())
240 if (pAtnBook->getIsEnd())
241 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_ANNOTATIONMARK, true, pAtnBook->getHandle());
242 else
243 m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK, pAtnBook->getHandle()));
245 return 0;
248 tools::Long SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult*)
250 if (WW8PLCFx_FactoidBook* pFactoidBook = m_xPlcxMan->GetFactoidBook())
252 if (pFactoidBook->getIsEnd())
253 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_RDFMARK, true, pFactoidBook->getHandle());
254 else
256 SwFltRDFMark aMark;
257 aMark.SetHandle(pFactoidBook->getHandle());
258 GetSmartTagInfo(aMark);
259 m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), aMark);
262 return 0;
265 // general help methods to separate parameters
267 /// translate FieldParameter names into the system character set and
268 /// at the same time, double backslashes are converted into single ones
269 OUString SwWW8ImplReader::ConvertFFileName(const OUString& rOrg)
271 OUString aName = rOrg.replaceAll("\\\\", "\\");
272 aName = aName.replaceAll("%20", " ");
274 // remove attached quotation marks
275 if (aName.endsWith("\""))
276 aName = aName.copy(0, aName.getLength()-1);
278 // Need the more sophisticated url converter.
279 if (!aName.isEmpty())
280 aName = URIHelper::SmartRel2Abs(
281 INetURLObject(m_sBaseURL), aName, Link<OUString *, bool>(), false);
283 return aName;
286 namespace
288 /// translate FieldParameter names into the
289 /// system character set and makes them uppercase
290 void ConvertUFName( OUString& rName )
292 rName = GetAppCharClass().uppercase( rName );
296 static void lcl_ConvertSequenceName(OUString& rSequenceName)
298 ConvertUFName(rSequenceName);
299 if ('0' <= rSequenceName[0] && '9' >= rSequenceName[0])
300 rSequenceName = "_" + rSequenceName;
303 // FindParaStart() finds 1st Parameter that follows '\' and cToken
304 // and returns start of this parameter or -1
305 static sal_Int32 FindParaStart( std::u16string_view aStr, sal_Unicode cToken, sal_Unicode cToken2 )
307 bool bStr = false; // ignore inside a string
309 for( size_t nBuf = 0; nBuf+1 < aStr.size(); nBuf++ )
311 if( aStr[ nBuf ] == '"' )
312 bStr = !bStr;
314 if( !bStr
315 && aStr[ nBuf ] == '\\'
316 && ( aStr[ nBuf + 1 ] == cToken
317 || aStr[ nBuf + 1 ] == cToken2 ) )
319 nBuf += 2;
320 // skip spaces between cToken and its parameters
321 while( nBuf < aStr.size()
322 && aStr[ nBuf ] == ' ' )
323 nBuf++;
324 // return start of parameters
325 return nBuf < aStr.size() ? nBuf : -1;
328 return -1;
331 // FindPara() finds the first parameter including '\' and cToken.
332 // A new String will be allocated (has to be deallocated by the caller)
333 // and everything that is part of the parameter will be returned.
334 static OUString FindPara( std::u16string_view aStr, sal_Unicode cToken, sal_Unicode cToken2 )
336 sal_Int32 n2; // end
337 sal_Int32 n = FindParaStart( aStr, cToken, cToken2 ); // start
338 if( n == -1)
339 return OUString();
341 if( aStr[ n ] == '"'
342 || aStr[ n ] == 132 )
343 { // Quotationmark in front of parameter
344 n++; // Skip quotationmark
345 n2 = n; // search for the end starting from here
346 while( n2 < sal_Int32(aStr.size())
347 && aStr[ n2 ] != 147
348 && aStr[ n2 ] != '"' )
349 n2++; // search end of parameter
351 else
352 { // no quotationmarks
353 n2 = n; // search for the end starting from here
354 while( n2 < sal_Int32(aStr.size())
355 && aStr[ n2 ] != ' ' )
356 n2++; // search end of parameter
358 return OUString(aStr.substr( n, n2-n ));
361 static SvxNumType GetNumTypeFromName(const OUString& rStr,
362 bool bAllowPageDesc = false)
364 SvxNumType eTyp = bAllowPageDesc ? SVX_NUM_PAGEDESC : SVX_NUM_ARABIC;
365 if (rStr.isEmpty())
366 return eTyp;
368 if( rStr.startsWithIgnoreAsciiCase( "Arabi" ) ) // Arabisch, Arabic
369 eTyp = SVX_NUM_ARABIC;
370 else if( rStr.startsWith( "misch" ) ) // r"omisch
371 eTyp = SVX_NUM_ROMAN_LOWER;
372 else if( rStr.startsWith( "MISCH" ) ) // R"OMISCH
373 eTyp = SVX_NUM_ROMAN_UPPER;
374 else if( rStr.startsWithIgnoreAsciiCase( "alphabeti" ) )// alphabetisch, alphabetic
375 eTyp = ( rStr[0] == 'A' )
376 ? SVX_NUM_CHARS_UPPER_LETTER_N
377 : SVX_NUM_CHARS_LOWER_LETTER_N;
378 else if( rStr.startsWithIgnoreAsciiCase( "roman" ) ) // us
379 eTyp = ( rStr[0] == 'R' )
380 ? SVX_NUM_ROMAN_UPPER
381 : SVX_NUM_ROMAN_LOWER;
382 return eTyp;
385 static SvxNumType GetNumberPara(std::u16string_view aStr, bool bAllowPageDesc = false)
387 OUString s( FindPara( aStr, '*', '*' ) ); // Type of number
388 SvxNumType aType = GetNumTypeFromName( s, bAllowPageDesc );
389 return aType;
392 bool SwWW8ImplReader::ForceFieldLanguage(SwField &rField, LanguageType nLang)
394 bool bRet(false);
396 const SvxLanguageItem *pLang =
397 static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
398 OSL_ENSURE(pLang, "impossible");
399 LanguageType nDefault = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
401 if (nLang != nDefault)
403 rField.SetAutomaticLanguage(false);
404 rField.SetLanguage(nLang);
405 bRet = true;
408 return bRet;
411 static OUString GetWordDefaultDateStringAsUS(SvNumberFormatter* pFormatter, LanguageType nLang)
413 //Get the system date in the correct final language layout, convert to
414 //a known language and modify the 2 digit year part to be 4 digit, and
415 //convert back to the correct language layout.
416 const sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, nLang);
418 SvNumberformat aFormat = *(pFormatter->GetEntry(nIndex));
419 aFormat.ConvertLanguage(*pFormatter, nLang, LANGUAGE_ENGLISH_US);
421 OUString sParams(aFormat.GetFormatstring());
422 // #i36594#
423 // Fix provided by mloiseleur@openoffice.org.
424 // A default date can have already 4 year digits, in some case
425 const sal_Int32 pos = sParams.indexOf("YYYY");
426 if ( pos == -1 )
428 sParams = sParams.replaceFirst("YY", "YYYY");
430 return sParams;
433 SvNumFormatType SwWW8ImplReader::GetTimeDatePara(std::u16string_view aStr, sal_uInt32& rFormat,
434 LanguageType &rLang, int nWhichDefault, bool bHijri)
436 bool bRTL = false;
437 if (m_xPlcxMan && !m_bVer67)
439 SprmResult aResult = m_xPlcxMan->HasCharSprm(0x85A);
440 if (aResult.pSprm && aResult.nRemainingData >= 1 && *aResult.pSprm)
441 bRTL = true;
443 sal_uInt16 eLang = bRTL ? RES_CHRATR_CTL_LANGUAGE : RES_CHRATR_LANGUAGE;
444 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(eLang));
445 OSL_ENSURE(pLang, "impossible");
446 rLang = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
448 SvNumberFormatter* pFormatter = m_rDoc.GetNumberFormatter();
449 OUString sParams( FindPara( aStr, '@', '@' ) );// Date/Time
450 if (sParams.isEmpty())
452 bool bHasTime = false;
453 switch (nWhichDefault)
455 case ww::ePRINTDATE:
456 case ww::eSAVEDATE:
457 sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
458 sParams += " HH:MM:SS AM/PM";
459 bHasTime = true;
460 break;
461 case ww::eCREATEDATE:
462 sParams += "DD/MM/YYYY HH:MM:SS";
463 bHasTime = true;
464 break;
465 default:
466 case ww::eDATE:
467 sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
468 break;
471 if (bHijri)
472 sParams = "[~hijri]" + sParams;
474 sal_Int32 nCheckPos = 0;
475 SvNumFormatType nType = SvNumFormatType::DEFINED;
476 rFormat = 0;
478 OUString sTemp(sParams);
479 pFormatter->PutandConvertEntry(sTemp, nCheckPos, nType, rFormat,
480 LANGUAGE_ENGLISH_US, rLang, false);
481 sParams = sTemp;
483 return bHasTime ? SvNumFormatType::DATETIME : SvNumFormatType::DATE;
486 sal_uLong nFormatIdx =
487 sw::ms::MSDateTimeFormatToSwFormat(sParams, pFormatter, rLang, bHijri,
488 GetFib().m_lid);
489 SvNumFormatType nNumFormatType = SvNumFormatType::UNDEFINED;
490 if (nFormatIdx)
491 nNumFormatType = pFormatter->GetType(nFormatIdx);
492 rFormat = nFormatIdx;
494 return nNumFormatType;
497 // Fields
499 // Update respective fields after loading (currently references)
500 void SwWW8ImplReader::UpdateFields()
502 m_rDoc.getIDocumentState().SetUpdateExpFieldStat(true);
503 m_rDoc.SetInitDBFields(true); // Also update fields in the database
506 // Sanity check the PaM to see if it makes sense wrt sw::CalcBreaks
507 static bool SanityCheck(const SwPaM& rFieldPam)
509 SwNodeOffset const nEndNode(rFieldPam.End()->GetNodeIndex());
510 SwNodes const& rNodes(rFieldPam.GetPoint()->GetNodes());
511 SwNode *const pFinalNode(rNodes[nEndNode]);
512 if (pFinalNode->IsTextNode())
514 SwTextNode & rTextNode(*pFinalNode->GetTextNode());
515 return (rTextNode.Len() >= rFieldPam.End()->GetContentIndex());
517 return true;
520 sal_uInt16 SwWW8ImplReader::End_Field()
522 sal_uInt16 nRet = 0;
523 WW8PLCFx_FLD* pF = m_xPlcxMan->GetField();
524 OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available");
525 WW8_CP nCP = 0;
526 if (!pF || !pF->EndPosIsFieldEnd(nCP))
527 return nRet;
529 const SvtFilterOptions &rOpt = SvtFilterOptions::Get();
530 bool bUseEnhFields = rOpt.IsUseEnhancedFields();
532 OSL_ENSURE(!m_aFieldStack.empty(), "Empty field stack");
533 if (!m_aFieldStack.empty())
536 only hyperlinks currently need to be handled like this, for the other
537 cases we have inserted a field not an attribute with an unknown end
538 point
540 nRet = m_aFieldStack.back().mnFieldId;
541 switch (nRet)
543 case ww::eFORMTEXT:
544 if (bUseEnhFields && m_pPaM!=nullptr && m_pPaM->GetPoint()!=nullptr) {
545 SwPosition aEndPos = *m_pPaM->GetPoint();
546 SwPaM aFieldPam( m_aFieldStack.back().GetPtNode().GetNode(), m_aFieldStack.back().GetPtContent(), aEndPos.GetNode(), aEndPos.GetContentIndex());
548 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
549 IFieldmark *pFieldmark = SanityCheck(aFieldPam) ? pMarksAccess->makeFieldBookmark(
550 aFieldPam, m_aFieldStack.back().GetBookmarkName(), ODF_FORMTEXT,
551 aFieldPam.Start() /*same pos as start!*/ ) : nullptr;
552 OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
553 if (pFieldmark!=nullptr) {
554 // adapt redline positions to inserted field mark start
555 // dummy char (assume not necessary for end dummy char)
556 m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start());
557 const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
558 pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
561 break;
562 // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field
563 case ww::eTOC://TOX
564 case ww::eINDEX://index
565 if (m_bLoadingTOXCache)
567 if (m_nEmbeddedTOXLevel > 0)
569 JoinNode(*m_pPaM);
570 --m_nEmbeddedTOXLevel;
572 else
574 m_aTOXEndCps.insert(nCP);
575 m_bLoadingTOXCache = false;
576 if ( m_pPaM->End() &&
577 m_pPaM->End()->GetNode().GetTextNode() &&
578 m_pPaM->End()->GetNode().GetTextNode()->Len() == 0 )
580 JoinNode(*m_pPaM);
582 else
584 m_bCareLastParaEndInToc = true;
587 if (m_oPosAfterTOC)
589 *m_pPaM = *m_oPosAfterTOC;
590 m_oPosAfterTOC.reset();
594 break;
595 case ww::ePAGEREF: //REF
596 if (m_bLoadingTOXCache && !m_bLoadingTOXHyperlink)
598 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_TXTATR_INETFMT);
600 break;
601 case ww::eHYPERLINK:
602 if (m_bLoadingTOXHyperlink)
603 m_bLoadingTOXHyperlink = false;
604 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_INETFMT);
605 break;
606 case ww::eMERGEINC:
607 case ww::eINCLUDETEXT:
609 //Move outside the section associated with this type of field
610 SwPosition aRestorePos(m_aFieldStack.back().maStartPos);
612 SwContentNode* pNd = aRestorePos.GetNode().GetContentNode();
613 sal_Int32 nMaxValidIndex = pNd ? pNd->Len() : 0;
614 if (aRestorePos.GetContentIndex() > nMaxValidIndex)
616 SAL_WARN("sw.ww8", "Attempt to restore to invalid content position");
617 aRestorePos.SetContent(nMaxValidIndex);
620 *m_pPaM->GetPoint() = aRestorePos;
621 break;
623 case ww::eIF: // IF-field
625 // conditional field parameters
626 OUString fieldDefinition = m_aFieldStack.back().GetBookmarkCode();
628 OUString paramCondition;
629 OUString paramTrue;
630 OUString paramFalse;
632 // ParseIfFieldDefinition expects: IF <some condition> "true result" "false result"
633 // while many fields include '\* MERGEFORMAT' after that.
634 // So first trim off the switches that are not supported anyway
635 sal_Int32 nLastIndex = fieldDefinition.lastIndexOf("\\*");
636 sal_Int32 nOtherIndex = fieldDefinition.lastIndexOf("\\#"); //number format
637 if (nOtherIndex > 0 && (nOtherIndex < nLastIndex || nLastIndex < 0))
638 nLastIndex = nOtherIndex;
639 nOtherIndex = fieldDefinition.lastIndexOf("\\@"); //date format
640 if (nOtherIndex > 0 && (nOtherIndex < nLastIndex || nLastIndex < 0))
641 nLastIndex = nOtherIndex;
642 nOtherIndex = fieldDefinition.lastIndexOf("\\!"); //locked result
643 if (nOtherIndex > 0 && (nOtherIndex < nLastIndex || nLastIndex < 0))
644 nLastIndex = nOtherIndex;
645 if (nLastIndex > 0)
646 fieldDefinition = fieldDefinition.copy(0, nLastIndex);
648 SwHiddenTextField::ParseIfFieldDefinition(fieldDefinition, paramCondition, paramTrue, paramFalse);
650 // create new field
651 SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText);
652 SwHiddenTextField aHTField(
653 static_cast<SwHiddenTextFieldType*>(pFieldType),
654 paramCondition,
655 paramTrue,
656 paramFalse,
657 SwFieldTypesEnum::ConditionalText);
659 // insert new field into document
660 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aHTField));
661 break;
663 default:
664 OUString aCode = m_aFieldStack.back().GetBookmarkCode();
665 if (!aCode.isEmpty() && !o3tl::starts_with(o3tl::trim(aCode), u"SHAPE"))
667 // Unhandled field with stored code
668 SwPosition aEndPos = *m_pPaM->GetPoint();
669 SwPaM aFieldPam(
670 m_aFieldStack.back().GetPtNode().GetNode(), m_aFieldStack.back().GetPtContent(),
671 aEndPos.GetNode(), aEndPos.GetContentIndex());
673 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
675 IFieldmark* pFieldmark = pMarksAccess->makeFieldBookmark(
676 aFieldPam,
677 m_aFieldStack.back().GetBookmarkName(),
678 ODF_UNHANDLED,
679 aFieldPam.Start() /*same pos as start!*/ );
680 if ( pFieldmark )
682 // adapt redline positions to inserted field mark start
683 // dummy char (assume not necessary for end dummy char)
684 m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start());
685 const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
686 pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
687 OUString sFieldId = OUString::number( m_aFieldStack.back().mnFieldId );
688 pFieldmark->GetParameters()->insert(
689 std::pair< OUString, uno::Any > (
690 ODF_ID_PARAM,
691 uno::Any( sFieldId ) ) );
692 pFieldmark->GetParameters()->insert(
693 std::pair< OUString, uno::Any > (
694 ODF_CODE_PARAM,
695 uno::Any( aCode ) ) );
697 if ( m_aFieldStack.back().mnObjLocFc > 0 )
699 // Store the OLE object as an internal link
700 OUString sOleId = "_" +
701 OUString::number( m_aFieldStack.back().mnObjLocFc );
703 tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(SL::aObjectPool);
704 tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( sOleId, StreamMode::READ );
706 // Store it now!
707 uno::Reference< embed::XStorage > xDocStg = GetDoc().GetDocStorage();
708 if (xDocStg.is())
710 uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement(
711 "OLELinks", embed::ElementModes::WRITE );
712 tools::SvRef<SotStorage> xObjDst = SotStorage::OpenOLEStorage( xOleStg, sOleId );
714 if ( xObjDst.is() )
716 xSrc1->CopyTo( xObjDst.get() );
718 if ( !xObjDst->GetError() )
719 xObjDst->Commit();
722 uno::Reference< embed::XTransactedObject > xTransact( xOleStg, uno::UNO_QUERY );
723 if ( xTransact.is() )
724 xTransact->commit();
727 // Store the OLE Id as a parameter
728 pFieldmark->GetParameters()->insert(
729 std::pair< OUString, uno::Any >(
730 ODF_OLE_PARAM, uno::Any( sOleId ) ) );
735 break;
737 m_aFieldStack.pop_back();
739 return nRet;
742 static bool AcceptableNestedField(sal_uInt16 nFieldCode)
744 switch (nFieldCode)
746 case ww::eINDEX: // allow recursive field in TOC...
747 case ww::eTOC: // allow recursive field in TOC...
748 case ww::eMERGEINC:
749 case ww::eINCLUDETEXT:
750 case ww::eAUTOTEXT:
751 case ww::eHYPERLINK:
752 // Accept AutoTextList field as nested field.
753 // Thus, the field result is imported as plain text.
754 case ww::eAUTOTEXTLIST:
755 // tdf#129247 CONTROL contains a nested SHAPE field in the result
756 case ww::eCONTROL:
757 return true;
758 default:
759 return false;
763 WW8FieldEntry::WW8FieldEntry(SwPosition const &rPos, sal_uInt16 nFieldId) noexcept
764 : maStartPos(rPos), mnFieldId(nFieldId), mnObjLocFc(0)
768 WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry &rOther) noexcept
769 : maStartPos(rOther.maStartPos), mnFieldId(rOther.mnFieldId), mnObjLocFc(rOther.mnObjLocFc)
773 void WW8FieldEntry::Swap(WW8FieldEntry &rOther) noexcept
775 std::swap(maStartPos, rOther.maStartPos);
776 std::swap(mnFieldId, rOther.mnFieldId);
779 WW8FieldEntry &WW8FieldEntry::operator=(const WW8FieldEntry &rOther) noexcept
781 WW8FieldEntry aTemp(rOther);
782 Swap(aTemp);
783 return *this;
787 void WW8FieldEntry::SetBookmarkName(const OUString& bookmarkName)
789 msBookmarkName=bookmarkName;
792 void WW8FieldEntry::SetBookmarkType(const OUString& bookmarkType)
794 msMarkType=bookmarkType;
797 void WW8FieldEntry::SetBookmarkCode(const OUString& bookmarkCode)
799 msMarkCode = bookmarkCode;
803 // Read_Field reads a field or returns 0 if the field cannot be read,
804 // so that the calling function reads the field in text format.
805 // Returnvalue: Total length of field
806 tools::Long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes)
808 typedef eF_ResT (SwWW8ImplReader::*FNReadField)( WW8FieldDesc*, OUString& );
809 enum Limits {eMax = 96};
810 static const FNReadField aWW8FieldTab[eMax+1] =
812 nullptr,
813 &SwWW8ImplReader::Read_F_Input,
814 nullptr,
815 &SwWW8ImplReader::Read_F_Ref, // 3
816 nullptr,
817 nullptr,
818 &SwWW8ImplReader::Read_F_Set, // 6
819 nullptr,
820 &SwWW8ImplReader::Read_F_Tox, // 8
821 nullptr,
822 &SwWW8ImplReader::Read_F_Styleref, // 10
823 nullptr,
824 &SwWW8ImplReader::Read_F_Seq, // 12
825 &SwWW8ImplReader::Read_F_Tox, // 13
826 &SwWW8ImplReader::Read_F_DocInfo, // 14
827 &SwWW8ImplReader::Read_F_DocInfo, // 15
828 &SwWW8ImplReader::Read_F_DocInfo, // 16
829 &SwWW8ImplReader::Read_F_Author, // 17
830 &SwWW8ImplReader::Read_F_DocInfo, // 18
831 &SwWW8ImplReader::Read_F_DocInfo, // 19
832 &SwWW8ImplReader::Read_F_DocInfo, // 20
833 &SwWW8ImplReader::Read_F_DocInfo, // 21
834 &SwWW8ImplReader::Read_F_DocInfo, // 22
835 &SwWW8ImplReader::Read_F_DocInfo, // 23
836 &SwWW8ImplReader::Read_F_DocInfo, // 24
837 &SwWW8ImplReader::Read_F_DocInfo, // 25
838 &SwWW8ImplReader::Read_F_Num, // 26
839 &SwWW8ImplReader::Read_F_Num, // 27
840 &SwWW8ImplReader::Read_F_Num, // 28
841 &SwWW8ImplReader::Read_F_FileName, // 29
842 &SwWW8ImplReader::Read_F_TemplName, // 30
843 &SwWW8ImplReader::Read_F_DateTime, // 31
844 &SwWW8ImplReader::Read_F_DateTime, // 32
845 &SwWW8ImplReader::Read_F_CurPage, // 33
846 nullptr,
847 nullptr,
848 &SwWW8ImplReader::Read_F_IncludeText, // 36
849 &SwWW8ImplReader::Read_F_PgRef, // 37
850 &SwWW8ImplReader::Read_F_InputVar, // 38
851 &SwWW8ImplReader::Read_F_Input, // 39
852 nullptr,
853 &SwWW8ImplReader::Read_F_DBNext, // 41
854 nullptr,
855 nullptr,
856 &SwWW8ImplReader::Read_F_DBNum, // 44
857 nullptr,
858 nullptr,
859 nullptr,
860 nullptr,
861 &SwWW8ImplReader::Read_F_Equation, // 49
862 nullptr,
863 &SwWW8ImplReader::Read_F_Macro, // 51
864 &SwWW8ImplReader::Read_F_ANumber, // 52
865 &SwWW8ImplReader::Read_F_ANumber, // 53
866 &SwWW8ImplReader::Read_F_ANumber, // 54
867 nullptr,
869 nullptr, // 56
871 &SwWW8ImplReader::Read_F_Symbol, // 57
872 &SwWW8ImplReader::Read_F_Embedd, // 58
873 &SwWW8ImplReader::Read_F_DBField, // 59
874 nullptr,
875 nullptr,
876 nullptr,
877 nullptr,
878 &SwWW8ImplReader::Read_F_DocInfo, // 64 - DOCVARIABLE
879 nullptr,
880 nullptr,
881 &SwWW8ImplReader::Read_F_IncludePicture, // 67
882 &SwWW8ImplReader::Read_F_IncludeText, // 68
883 nullptr,
884 &SwWW8ImplReader::Read_F_FormTextBox, // 70
885 &SwWW8ImplReader::Read_F_FormCheckBox, // 71
886 &SwWW8ImplReader::Read_F_NoteReference, // 72
887 nullptr, /*&SwWW8ImplReader::Read_F_Tox*/
888 nullptr,
889 nullptr,
890 nullptr,
891 nullptr,
892 nullptr,
893 nullptr,
894 nullptr,
895 nullptr,
896 nullptr,
897 &SwWW8ImplReader::Read_F_FormListBox, // 83
898 nullptr, // 84
899 &SwWW8ImplReader::Read_F_DocInfo, // 85
900 nullptr, // 86
901 &SwWW8ImplReader::Read_F_OCX, // 87
902 &SwWW8ImplReader::Read_F_Hyperlink, // 88
903 nullptr, // 89
904 nullptr, // 90
905 &SwWW8ImplReader::Read_F_HTMLControl, // 91
906 nullptr, // 92
907 nullptr, // 93
908 nullptr, // 94
909 &SwWW8ImplReader::Read_F_Shape, // 95
910 nullptr // eMax - Dummy empty method
912 OSL_ENSURE( SAL_N_ELEMENTS( aWW8FieldTab ) == eMax+1, "FieldFunc table not right" );
914 WW8PLCFx_FLD* pF = m_xPlcxMan->GetField();
915 OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available");
917 if (!pF || !pF->StartPosIsFieldStart())
918 return 0;
920 bool bNested = false;
921 if (!m_aFieldStack.empty())
923 bNested = std::any_of(m_aFieldStack.cbegin(), m_aFieldStack.cend(),
924 [](const WW8FieldEntry& aField) { return !AcceptableNestedField(aField.mnFieldId); });
927 WW8FieldDesc aF;
928 bool bOk = pF->GetPara(pRes->nCp2OrIdx, aF);
930 OSL_ENSURE(bOk, "WW8: Bad Field!");
931 if (aF.nId == 33) aF.bCodeNest=false; // do not recurse into nested page fields
932 bool bCodeNest = aF.bCodeNest;
933 if ( aF.nId == 6 ) bCodeNest = false; // We can handle them and lose the inner data
934 if (aF.nId == 70) bCodeNest = false; // need to import 0x01 in FORMTEXT
936 m_aFieldStack.emplace_back(*m_pPaM->GetPoint(), aF.nId);
938 if (bNested)
939 return 0;
941 sal_uInt16 n = (aF.nId <= eMax) ? aF.nId : o3tl::narrowing<sal_uInt16>(eMax);
942 sal_uInt16 nI = n / 32; // # of sal_uInt32
943 sal_uInt32 nMask = 1 << ( n % 32 ); // Mask for bits
945 if (SAL_N_ELEMENTS(m_nFieldTagAlways) <= nI)
946 { // if indexes larger than 95 are needed, then a new configuration
947 // item has to be added, and nFieldTagAlways/nFieldTagBad expanded!
948 return aF.nLen;
951 if( m_nFieldTagAlways[nI] & nMask ) // Flag: Tag it
952 return Read_F_Tag( &aF ); // Result not as text
954 if( !bOk || !aF.nId ) // Field corrupted
955 return aF.nLen; // -> ignore
957 if( aF.nId > eMax - 1) // WW: Nested Field
959 if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
960 return Read_F_Tag( &aF ); // Result not as text
961 else
962 return aF.nLen;
965 //Only one type of field (hyperlink) in drawing textboxes exists
966 if (aF.nId != 88 && m_xPlcxMan->GetDoingDrawTextBox())
967 return aF.nLen;
969 bool bHasHandler = aWW8FieldTab[aF.nId] != nullptr;
970 if (aF.nId == 10) // STYLEREF
972 // STYLEREF, by default these are not handled.
973 bHasHandler = false;
974 sal_uInt64 nOldPos = m_pStrm->Tell();
975 OUString aStr;
976 aF.nLCode = m_xSBase->WW8ReadString(*m_pStrm, aStr, m_xPlcxMan->GetCpOfs() + aF.nSCode, aF.nLCode, m_eTextCharSet);
977 m_pStrm->Seek(nOldPos);
979 WW8ReadFieldParams aReadParam(aStr);
980 sal_Int32 nRet = aReadParam.SkipToNextToken();
981 if (nRet == -2 && !aReadParam.GetResult().isEmpty())
982 // Single numeric argument: this can be handled by SwChapterField.
983 bHasHandler = rtl::isAsciiDigit(aReadParam.GetResult()[0]);
985 if (bHasHandler)
987 nRet = aReadParam.SkipToNextToken();
988 // Handle using SwChapterField only in case there is no \[a-z]
989 // switch after the field argument.
990 bHasHandler = nRet < 0 || nRet == '*';
994 // no routine available
995 if (!bHasHandler || bCodeNest)
997 if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
998 return Read_F_Tag( &aF ); // Result not as text
999 // only read result
1000 if (aF.bResNest && !AcceptableNestedField(aF.nId))
1001 return aF.nLen; // Result nested -> unusable
1003 sal_uInt64 nOldPos = m_pStrm->Tell();
1004 OUString aStr;
1005 aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+
1006 aF.nSCode, aF.nLCode, m_eTextCharSet );
1007 m_pStrm->Seek( nOldPos );
1009 // field codes which contain '/' or '.' are not displayed in WinWord
1010 // skip if it is formula field or found space before. see #i119446, #i119585.
1011 const sal_Int32 nDotPos = aStr.indexOf('.');
1012 const sal_Int32 nSlashPos = aStr.indexOf('/');
1013 sal_Int32 nSpacePos = aStr.indexOf( ' ', 1 );
1014 if ( nSpacePos<0 )
1015 nSpacePos = aStr.getLength();
1017 if ( ( aStr.getLength() <= 1 || aStr[1] != '=') &&
1018 (( nDotPos>=0 && nDotPos < nSpacePos ) ||
1019 ( nSlashPos>=0 && nSlashPos < nSpacePos )))
1020 return aF.nLen;
1021 else
1023 // Link fields aren't supported, but they are bound to an OLE object
1024 // that needs to be roundtripped
1025 if ( aF.nId == 56 )
1026 m_bEmbeddObj = true;
1027 // Field not supported: store the field code for later use
1028 m_aFieldStack.back().SetBookmarkCode( aStr );
1030 if (aF.nId == ww::eIF)
1032 // In MS Word, the IF field is editable and requires a manual refresh
1033 // so the last, saved result might not match either of the true or false options.
1034 // But in LO the field is automatically updated and not editable,
1035 // so the previous result is of no value to import since it could never be seen.
1036 return aF.nLen;
1039 return aF.nLen - aF.nLRes - 1; // skipped too many, the resulted field will be read like main text
1042 else
1043 { // read field
1044 auto nOldPos = m_pStrm->Tell();
1045 OUString aStr;
1046 if ( aF.nId == 6 && aF.bCodeNest )
1048 // TODO Extract the whole code string using the nested codes
1049 aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs() +
1050 aF.nSCode, aF.nSRes - aF.nSCode - 1, m_eTextCharSet );
1052 else
1054 aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+
1055 aF.nSCode, aF.nLCode, m_eTextCharSet );
1058 // #i51312# - graphics inside field code not supported by Writer.
1059 // Thus, delete character 0x01, which stands for such a graphic.
1060 if (aF.nId==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01.
1062 aStr = aStr.replaceAll("\x01", "");
1065 eF_ResT eRes = (this->*aWW8FieldTab[aF.nId])( &aF, aStr );
1066 m_pStrm->Seek(nOldPos);
1068 switch ( eRes )
1070 case eF_ResT::OK:
1071 return aF.nLen;
1072 case eF_ResT::TEXT:
1073 // skipped too many, the resulted field will be read like main text
1074 // attributes can start at char 0x14 so skip one
1075 // char more back == "-2"
1076 if (aF.nLRes)
1077 return aF.nLen - aF.nLRes - 2;
1078 else
1079 return aF.nLen;
1080 case eF_ResT::TAGIGN:
1081 if ( m_nFieldTagBad[nI] & nMask ) // Flag: Tag bad
1082 return Read_F_Tag( &aF ); // Tag it
1083 return aF.nLen; // or ignore
1084 case eF_ResT::READ_FSPA:
1085 return aF.nLen - aF.nLRes - 2; // position on char 1
1086 default:
1087 return aF.nLen; // ignore
1092 // Tag fields
1094 // MakeTagString() returns the position of the first CR / end of line / page break
1095 // in pText and converts only up to this point.
1096 // If none of these special characters is found, the function returns 0.
1097 void SwWW8ImplReader::MakeTagString( OUString& rStr, const OUString& rOrg )
1099 bool bAllowCr = SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT )
1100 || SwFltGetFlag( m_nFieldFlags, SwFltControlStack::ALLOW_FLD_CR );
1101 sal_Unicode cChar;
1102 rStr = rOrg;
1104 for( sal_Int32 nI = 0;
1105 nI < rStr.getLength() && rStr.getLength() < (MAX_FIELDLEN - 4); ++nI )
1107 bool bSetAsHex = false;
1108 cChar = rStr[ nI ];
1109 switch( cChar )
1111 case 132: // Exchange typographical quotation marks for normal ones
1112 case 148:
1113 case 147:
1114 rStr = rStr.replaceAt( nI, 1, u"\"" );
1115 break;
1116 case 19:
1117 rStr = rStr.replaceAt( nI, 1, u"{" );
1118 break; // 19..21 to {|}
1119 case 20:
1120 rStr = rStr.replaceAt( nI, 1, u"|" );
1121 break;
1122 case 21:
1123 rStr = rStr.replaceAt( nI, 1, u"}" );
1124 break;
1125 case '\\': // Tag \{|} with \ ...
1126 case '{':
1127 case '|':
1128 case '}':
1129 rStr = rStr.replaceAt( nI, 0, u"\\" );
1130 ++nI;
1131 break;
1132 case 0x0b:
1133 case 0x0c:
1134 case 0x0d:
1135 if( bAllowCr )
1136 rStr = rStr.replaceAt( nI, 1, u"\n" );
1137 else
1138 bSetAsHex = true;
1139 break;
1140 case 0xFE:
1141 case 0xFF:
1142 bSetAsHex = true;
1143 break;
1144 default:
1145 bSetAsHex = 0x20 > cChar;
1146 break;
1149 if( bSetAsHex )
1151 //all Hex-Numbers with \x before
1152 OUString sTmp( "\\x" );
1153 if( cChar < 0x10 )
1154 sTmp += "0";
1155 sTmp += OUString::number( cChar, 16 );
1156 rStr = rStr.replaceAt( nI, 1 , sTmp );
1157 nI += sTmp.getLength() - 1;
1161 if( rStr.getLength() > (MAX_FIELDLEN - 4))
1162 rStr = rStr.copy( 0, MAX_FIELDLEN - 4 );
1165 void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId, const OUString& rTagText )
1167 OUString aName("WwFieldTag");
1168 if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_DO_ID ) ) // Number?
1169 aName += OUString::number( nId ); // return it?
1171 if( SwFltGetFlag(m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT))
1173 aName += rTagText; // tag as text
1174 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, aName,
1175 SwInsertFlags::NOHINTEXPAND);
1177 else
1178 { // tag normally
1180 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1181 SwSetExpFieldType( &m_rDoc, aName, nsSwGetSetExpType::GSE_STRING ) );
1182 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), rTagText ); // SUB_INVISIBLE
1183 sal_uInt16 nSubType = ( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_VISIBLE ) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE;
1184 aField.SetSubType(nSubType | nsSwGetSetExpType::GSE_STRING);
1186 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1190 WW8_CP SwWW8ImplReader::Read_F_Tag( WW8FieldDesc* pF )
1192 sal_uInt64 nOldPos = m_pStrm->Tell();
1194 WW8_CP nStart = pF->nSCode - 1; // starting with 0x19
1195 WW8_CP nL = pF->nLen; // Total length with result and nest
1196 if( nL > MAX_FIELDLEN )
1197 nL = MAX_FIELDLEN; // MaxLength, by quoting
1198 // max. 4 times as big
1199 OUString sFText;
1200 m_xSBase->WW8ReadString( *m_pStrm, sFText,
1201 m_xPlcxMan->GetCpOfs() + nStart, nL, m_eStructCharSet);
1203 OUString aTagText;
1204 MakeTagString( aTagText, sFText );
1205 InsertTagField( pF->nId, aTagText );
1207 m_pStrm->Seek( nOldPos );
1208 return pF->nLen;
1211 // normal fields
1213 eF_ResT SwWW8ImplReader::Read_F_Input( WW8FieldDesc* pF, OUString& rStr )
1215 OUString aDef;
1216 OUString aQ;
1217 WW8ReadFieldParams aReadParam( rStr );
1218 for (;;)
1220 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1221 if ( nRet==-1 )
1222 break;
1223 switch( nRet )
1225 case -2:
1226 if( aQ.isEmpty() )
1227 aQ = aReadParam.GetResult();
1228 break;
1229 case 'd':
1230 case 'D':
1231 if ( aReadParam.GoToTokenParam() )
1232 aDef = aReadParam.GetResult();
1233 break;
1236 if( aDef.isEmpty() )
1237 aDef = GetFieldResult( pF );
1239 if ( pF->nId != 0x01 ) // 0x01 fields have no result
1241 SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
1242 aDef, aQ, INP_TXT, 0, false );
1243 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1246 return eF_ResT::OK;
1249 // GetFieldResult allocates a string and reads the resulted field
1250 OUString SwWW8ImplReader::GetFieldResult( WW8FieldDesc const * pF )
1252 sal_uInt64 nOldPos = m_pStrm->Tell();
1254 WW8_CP nStart = pF->nSRes; // result start
1255 WW8_CP nL = pF->nLRes; // result length
1256 if( !nL )
1257 return OUString(); // no result
1259 if( nL > MAX_FIELDLEN )
1260 nL = MAX_FIELDLEN; // MaxLength, by quoting
1261 // max. 4 times as big
1263 OUString sRes;
1264 m_xSBase->WW8ReadString( *m_pStrm, sRes, m_xPlcxMan->GetCpOfs() + nStart,
1265 nL, m_eStructCharSet );
1267 m_pStrm->Seek( nOldPos );
1269 //replace both CR 0x0D and VT 0x0B with LF 0x0A
1270 // at least in the cases where the result is added to an SwInputField
1271 // there must not be control characters in it
1272 OUStringBuffer buf(sRes.getLength());
1273 for (sal_Int32 i = 0; i < sRes.getLength(); ++i)
1275 sal_Unicode const ch(sRes[i]);
1276 if (!linguistic::IsControlChar(ch))
1278 buf.append(ch);
1280 else
1282 switch (ch)
1284 case 0x0B:
1285 case '\r':
1286 buf.append('\n');
1287 break;
1288 case '\n':
1289 case '\t':
1290 buf.append(ch);
1291 break;
1292 default:
1293 SAL_INFO("sw.ww8", "GetFieldResult(): filtering control character");
1294 break;
1298 return buf.makeStringAndClear();
1302 Bookmarks can be set with fields SET and ASK, and they can be referenced with
1303 REF. When set, they behave like variables in writer, otherwise they behave
1304 like normal bookmarks. We can check whether we should use a show variable
1305 instead of a normal bookmark ref by converting to "show variable" at the end
1306 of the document those refs which look for the content of a bookmark but whose
1307 bookmarks were set with SET or ASK. (See SwWW8FltRefStack)
1309 The other piece of the puzzle is that refs that point to the "location" of the
1310 bookmark will in word actually point to the last location where the bookmark
1311 was set with SET or ASK, not the actual bookmark. This is only noticeable when
1312 a document sets the bookmark more than once. This is because word places the
1313 true bookmark at the location of the last set, but the refs will display the
1314 position of the first set before the ref.
1316 So what we will do is
1318 1) keep a list of all bookmarks that were set, any bookmark names mentioned
1319 here that are referred by content will be converted to show variables.
1321 2) create pseudo bookmarks for every position that a bookmark is set with SET
1322 or ASK but has no existing bookmark. We can then keep a map from the original
1323 bookmark name to the new one. As we parse the document new pseudo names will
1324 replace the older ones, so the map always contains the bookmark of the
1325 location that msword itself would use.
1327 3) word's bookmarks are case insensitive, writers are not. So we need to
1328 map case different versions together, regardless of whether they are
1329 variables or not.
1331 4) when a reference is (first) SET or ASK, the bookmark associated with it
1332 is placed around the 0x14 0x15 result part of the field. We will fiddle
1333 the placement to be the writer equivalent of directly before and after
1334 the field, which gives the same effect and meaning, to do so we must
1335 get any bookmarks in the field range, and begin them immediately before
1336 the set/ask field, and end them directly afterwards. MapBookmarkVariables
1337 returns an identifier of the bookmark attribute to close after inserting
1338 the appropriate set/ask field.
1340 tools::Long SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc* pF,
1341 OUString &rOrigName, const OUString &rData)
1343 OSL_ENSURE(m_xPlcxMan, "No pPlcxMan");
1344 tools::Long nNo;
1346 If there was no bookmark associated with this set field, then we create a
1347 pseudo one and insert it in the document.
1349 sal_uInt16 nIndex;
1350 m_xPlcxMan->GetBook()->MapName(rOrigName);
1351 OUString sName = m_xPlcxMan->GetBook()->GetBookmark(
1352 pF->nSCode, pF->nSCode + pF->nLen, nIndex);
1353 if (!sName.isEmpty())
1355 m_xPlcxMan->GetBook()->SetStatus(nIndex, BOOK_IGNORE);
1356 nNo = nIndex;
1358 else
1360 nNo = m_xReffingStck->m_aFieldVarNames.size()+1;
1361 sName = "WWSetBkmk" + OUString::number(nNo);
1362 nNo += m_xPlcxMan->GetBook()->GetIMax();
1364 m_xReffedStck->NewAttr(*m_pPaM->GetPoint(),
1365 SwFltBookmark( BookmarkToWriter(sName), rData, nNo ));
1366 m_xReffingStck->m_aFieldVarNames[rOrigName] = sName;
1367 return nNo;
1371 Word can set a bookmark with set or with ask, such a bookmark is equivalent to
1372 our variables, but until the end of a document we cannot be sure if a bookmark
1373 is a variable or not, at the end we will have a list of reference names which
1374 were set or asked, all bookmarks using the content of those bookmarks are
1375 converted to show variables, those that reference the position of the field
1376 can be left as references, because a bookmark is also inserted at the position
1377 of a set or ask field, either by word, or in some special cases by the import
1378 filter itself.
1380 SwFltStackEntry *SwWW8FltRefStack::RefToVar(const SwField* pField,
1381 SwFltStackEntry &rEntry)
1383 SwFltStackEntry *pRet=nullptr;
1384 if (pField && SwFieldIds::GetRef == pField->Which())
1386 //Get the name of the ref field, and see if actually a variable
1387 const OUString sName = pField->GetPar1();
1388 std::map<OUString, OUString, SwWW8::ltstr>::const_iterator
1389 aResult = m_aFieldVarNames.find(sName);
1391 if (aResult != m_aFieldVarNames.end())
1393 SwGetExpField aField( static_cast<SwGetExpFieldType*>(
1394 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp)), sName, nsSwGetSetExpType::GSE_STRING, 0);
1395 SwFormatField aTmp(aField);
1396 rEntry.m_pAttr.reset( aTmp.Clone() );
1397 pRet = &rEntry;
1400 return pRet;
1403 OUString SwWW8ImplReader::GetMappedBookmark(std::u16string_view rOrigName)
1405 OUString sName(BookmarkToWriter(rOrigName));
1406 OSL_ENSURE(m_xPlcxMan, "no pPlcxMan");
1407 m_xPlcxMan->GetBook()->MapName(sName);
1409 //See if there has been a variable set with this name, if so get
1410 //the pseudo bookmark name that was set with it.
1411 std::map<OUString, OUString, SwWW8::ltstr>::const_iterator aResult =
1412 m_xReffingStck->m_aFieldVarNames.find(sName);
1414 return (aResult == m_xReffingStck->m_aFieldVarNames.end())
1415 ? sName : (*aResult).second;
1418 // "ASK"
1419 eF_ResT SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc* pF, OUString& rStr )
1421 OUString sOrigName, aQ;
1422 OUString aDef;
1423 WW8ReadFieldParams aReadParam( rStr );
1424 for (;;)
1426 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1427 if ( nRet==-1 )
1428 break;
1429 switch( nRet )
1431 case -2:
1432 if (sOrigName.isEmpty())
1433 sOrigName = aReadParam.GetResult();
1434 else if (aQ.isEmpty())
1435 aQ = aReadParam.GetResult();
1436 break;
1437 case 'd':
1438 case 'D':
1439 if ( aReadParam.GoToTokenParam() )
1440 aDef = aReadParam.GetResult();
1441 break;
1445 if (sOrigName.isEmpty())
1446 return eF_ResT::TAGIGN; // does not make sense without textmark
1448 const OUString aResult(GetFieldResult(pF));
1450 //#i24377#, munge Default Text into title as we have only one slot
1451 //available for aResult and aDef otherwise
1452 if (!aDef.isEmpty())
1454 if (!aQ.isEmpty())
1455 aQ += " - ";
1456 aQ += aDef;
1459 const tools::Long nNo = MapBookmarkVariables(pF, sOrigName, aResult);
1461 SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1462 SwSetExpFieldType(&m_rDoc, sOrigName, nsSwGetSetExpType::GSE_STRING)));
1463 SwSetExpField aField(pFT, aResult);
1464 aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
1465 aField.SetInputFlag(true);
1466 aField.SetPromptText( aQ );
1468 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1470 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
1471 return eF_ResT::OK;
1474 // "AUTONR"
1475 eF_ResT SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc*, OUString& rStr )
1477 if( !m_pNumFieldType ){ // 1st time
1478 SwSetExpFieldType aT( &m_rDoc, "AutoNr", nsSwGetSetExpType::GSE_SEQ );
1479 m_pNumFieldType = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aT );
1481 SwSetExpField aField( static_cast<SwSetExpFieldType*>(m_pNumFieldType), OUString(),
1482 GetNumberPara( rStr ) );
1483 aField.SetValue( ++m_nFieldNum, nullptr );
1484 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1485 return eF_ResT::OK;
1488 // "SEQ"
1489 eF_ResT SwWW8ImplReader::Read_F_Seq( WW8FieldDesc*, OUString& rStr )
1491 OUString aSequenceName;
1492 OUString aBook;
1493 bool bHidden = false;
1494 bool bFormat = false;
1495 bool bCountOn = true;
1496 OUString sStart;
1497 SvxNumType eNumFormat = SVX_NUM_ARABIC;
1498 WW8ReadFieldParams aReadParam( rStr );
1499 for (;;)
1501 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1502 if ( nRet==-1 )
1503 break;
1504 switch( nRet )
1506 case -2:
1507 if( aSequenceName.isEmpty() )
1508 aSequenceName = aReadParam.GetResult();
1509 else if( aBook.isEmpty() )
1510 aBook = aReadParam.GetResult();
1511 break;
1513 case 'h':
1514 if( !bFormat )
1515 bHidden = true; // activate hidden flag
1516 break;
1518 case '*':
1519 bFormat = true; // activate format flag
1520 if ( aReadParam.SkipToNextToken()!=-2 )
1521 break;
1522 if ( aReadParam.GetResult()!="MERGEFORMAT" && aReadParam.GetResult()!="CHARFORMAT" )
1523 eNumFormat = GetNumTypeFromName( aReadParam.GetResult() );
1524 break;
1526 case 'r':
1527 bCountOn = false;
1528 if ( aReadParam.SkipToNextToken()==-2 )
1529 sStart = aReadParam.GetResult();
1530 break;
1532 case 'c':
1533 bCountOn = false;
1534 break;
1536 case 'n':
1537 bCountOn = true; // Increase value by one (default)
1538 break;
1540 case 's': // Outline Level
1541 //#i19682, what I have to do with this value?
1542 break;
1545 if (aSequenceName.isEmpty() && aBook.isEmpty())
1546 return eF_ResT::TAGIGN;
1548 SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1549 SwSetExpFieldType( &m_rDoc, aSequenceName, nsSwGetSetExpType::GSE_SEQ ) ) );
1550 SwSetExpField aField( pFT, OUString(), eNumFormat );
1552 //#i120654# Add bHidden for /h flag (/h: Hide the field result.)
1553 if (bHidden)
1554 aField.SetSubType(aField.GetSubType() | nsSwExtendedSubType::SUB_INVISIBLE);
1556 if (!sStart.isEmpty())
1557 aField.SetFormula( aSequenceName + "=" + sStart );
1558 else if (!bCountOn)
1559 aField.SetFormula(aSequenceName);
1561 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1562 return eF_ResT::OK;
1565 eF_ResT SwWW8ImplReader::Read_F_Styleref(WW8FieldDesc*, OUString& rString)
1567 WW8ReadFieldParams aReadParam(rString);
1568 sal_Int32 nRet = aReadParam.SkipToNextToken();
1569 if (nRet != -2)
1570 // \param was found, not normal text.
1571 return eF_ResT::TAGIGN;
1573 OUString aResult = aReadParam.GetResult();
1574 sal_Int32 nResult = aResult.toInt32();
1575 if (nResult < 1)
1576 return eF_ResT::TAGIGN;
1578 SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Chapter);
1579 SwChapterField aField(static_cast<SwChapterFieldType*>(pFieldType), CF_TITLE);
1580 aField.SetLevel(nResult - 1);
1581 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1583 return eF_ResT::OK;
1586 eF_ResT SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr )
1588 sal_uInt16 nSub=0;
1589 // RegInfoFormat, DefaultFormat for DocInfoFields
1590 sal_uInt16 nReg = DI_SUB_AUTHOR;
1591 bool bDateTime = false;
1592 const sal_uInt16 nFldLock = (pF->nOpt & 0x10) ? DI_SUB_FIXED : 0;
1594 if( 85 == pF->nId )
1596 OUString aDocProperty;
1597 WW8ReadFieldParams aReadParam( rStr );
1598 for (;;)
1600 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1601 if ( nRet==-1 )
1602 break;
1603 switch( nRet )
1605 case -2:
1606 if( aDocProperty.isEmpty() )
1607 aDocProperty = aReadParam.GetResult();
1608 break;
1609 case '*':
1610 //Skip over MERGEFORMAT
1611 (void)aReadParam.SkipToNextToken();
1612 break;
1616 aDocProperty = aDocProperty.replaceAll("\"", "");
1619 There are up to 26 fields that may be meant by 'DocumentProperty'.
1620 Which of them is to be inserted here ?
1621 This Problem can only be solved by implementing a name matching
1622 method that compares the given Parameter String with the four
1623 possible name sets (english, german, french, spanish)
1626 static const char* aName10 = "\x0F"; // SW field code
1627 static const char* aName11 // German
1628 = "TITEL";
1629 static const char* aName12 // French
1630 = "TITRE";
1631 static const char* aName13 // English
1632 = "TITLE";
1633 static const char* aName14 // Spanish
1634 = "TITRO";
1635 static const char* aName20 = "\x15"; // SW field code
1636 static const char* aName21 // German
1637 = "ERSTELLDATUM";
1638 static const char* aName22 // French
1639 = "CR\xC9\xC9";
1640 static const char* aName23 // English
1641 = "CREATED";
1642 static const char* aName24 // Spanish
1643 = "CREADO";
1644 static const char* aName30 = "\x16"; // SW field code
1645 static const char* aName31 // German
1646 = "ZULETZTGESPEICHERTZEIT";
1647 static const char* aName32 // French
1648 = "DERNIERENREGISTREMENT";
1649 static const char* aName33 // English
1650 = "SAVED";
1651 static const char* aName34 // Spanish
1652 = "MODIFICADO";
1653 static const char* aName40 = "\x17"; // SW field code
1654 static const char* aName41 // German
1655 = "ZULETZTGEDRUCKT";
1656 static const char* aName42 // French
1657 = "DERNI\xC8" "REIMPRESSION";
1658 static const char* aName43 // English
1659 = "LASTPRINTED";
1660 static const char* aName44 // Spanish
1661 = "HUPS PUPS";
1662 static const char* aName50 = "\x18"; // SW field code
1663 static const char* aName51 // German
1664 = "\xDC" "BERARBEITUNGSNUMMER";
1665 static const char* aName52 // French
1666 = "NUM\xC9" "RODEREVISION";
1667 static const char* aName53 // English
1668 = "REVISIONNUMBER";
1669 static const char* aName54 // Spanish
1670 = "SNUBBEL BUBBEL";
1671 static const sal_uInt16 nFieldCnt = 5;
1673 // additional fields are to be coded soon!
1675 static const sal_uInt16 nLangCnt = 4;
1676 static const char *aNameSet_26[nFieldCnt][nLangCnt+1] =
1678 {aName10, aName11, aName12, aName13, aName14},
1679 {aName20, aName21, aName22, aName23, aName24},
1680 {aName30, aName31, aName32, aName33, aName34},
1681 {aName40, aName41, aName42, aName43, aName44},
1682 {aName50, aName51, aName52, aName53, aName54}
1685 bool bFieldFound= false;
1686 sal_uInt16 nFIdx;
1687 for(sal_uInt16 nLIdx=1; !bFieldFound && (nLangCnt > nLIdx); ++nLIdx)
1689 for(nFIdx = 0; !bFieldFound && (nFieldCnt > nFIdx); ++nFIdx)
1691 if( aDocProperty == OUString( aNameSet_26[nFIdx][nLIdx], strlen(aNameSet_26[nFIdx][nLIdx]),
1692 RTL_TEXTENCODING_MS_1252 ) )
1694 bFieldFound = true;
1695 pF->nId = aNameSet_26[nFIdx][0][0];
1700 if( !bFieldFound )
1702 // LO always automatically updates a DocInfo field from the File-Properties-Custom Prop
1703 // while MS Word requires the user to manually refresh the field (with F9).
1704 // In other words, Word lets the field to be out of sync with the controlling variable.
1705 // Marking as FIXEDFLD solves the automatic replacement problem, but of course prevents
1706 // Writer from making any changes, even on an F9 refresh.
1707 // TODO: Extend LO to allow a linked field that doesn't automatically update.
1708 IDocumentContentOperations& rIDCO(m_rDoc.getIDocumentContentOperations());
1709 const auto pType(static_cast<SwDocInfoFieldType*>(
1710 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo)));
1711 const OUString sDisplayed = GetFieldResult(pF);
1712 SwDocInfoField aField(pType, DI_CUSTOM | nReg, aDocProperty);
1714 // If text already matches the DocProperty var, then safe to treat as refreshable field.
1715 OUString sVariable = aField.ExpandField(/*bCache=*/false, nullptr);
1716 if (sDisplayed.getLength() != sVariable.getLength())
1718 sal_Int32 nLen = sVariable.indexOf('\x0');
1719 if (nLen >= 0)
1720 sVariable = sVariable.copy(0, nLen);
1722 if (sDisplayed == sVariable)
1723 rIDCO.InsertPoolItem(*m_pPaM, SwFormatField(aField));
1724 else
1726 // They don't match, so use a fixed field to prevent LO from altering the contents.
1727 SwDocInfoField aFixedField(pType, DI_CUSTOM | DI_SUB_FIXED | nReg, aDocProperty,
1728 sDisplayed);
1729 rIDCO.InsertPoolItem(*m_pPaM, SwFormatField(aFixedField));
1732 return eF_ResT::OK;
1736 switch( pF->nId )
1738 case 14:
1739 /* supports all INFO variables! */
1740 nSub = DI_KEYS;
1741 break;
1742 case 15:
1743 nSub = DI_TITLE;
1744 break;
1745 case 16:
1746 nSub = DI_SUBJECT;
1747 break;
1748 case 18:
1749 nSub = DI_KEYS;
1750 break;
1751 case 19:
1752 nSub = DI_COMMENT;
1753 break;
1754 case 20:
1755 // MS Word never updates this automatically, so mark as fixed for best compatibility
1756 nSub = DI_CHANGE | DI_SUB_FIXED;
1757 nReg = DI_SUB_AUTHOR;
1758 break;
1759 case 21:
1760 // The real create date can never change, so mark as fixed for best compatibility
1761 nSub = DI_CREATE | DI_SUB_FIXED;
1762 nReg = DI_SUB_DATE;
1763 bDateTime = true;
1764 break;
1765 case 23:
1766 nSub = DI_PRINT | nFldLock;
1767 nReg = DI_SUB_DATE;
1768 bDateTime = true;
1769 break;
1770 case 24:
1771 nSub = DI_DOCNO;
1772 break;
1773 case 22:
1774 nSub = DI_CHANGE | nFldLock;
1775 nReg = DI_SUB_DATE;
1776 bDateTime = true;
1777 break;
1778 case 25:
1779 nSub = DI_CHANGE | nFldLock;
1780 nReg = DI_SUB_TIME;
1781 bDateTime = true;
1782 break;
1783 case 64: // DOCVARIABLE
1784 nSub = DI_CUSTOM;
1785 break;
1788 sal_uInt32 nFormat = 0;
1790 LanguageType nLang(LANGUAGE_SYSTEM);
1791 if (bDateTime)
1793 SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, pF->nId);
1794 switch (nDT)
1796 case SvNumFormatType::DATE:
1797 nReg = DI_SUB_DATE;
1798 break;
1799 case SvNumFormatType::TIME:
1800 nReg = DI_SUB_TIME;
1801 break;
1802 case SvNumFormatType::DATETIME:
1803 nReg = DI_SUB_DATE;
1804 break;
1805 default:
1806 nReg = DI_SUB_DATE;
1807 break;
1811 OUString aData;
1812 // Extract DOCVARIABLE varname
1813 if ( 64 == pF->nId )
1815 WW8ReadFieldParams aReadParam( rStr );
1816 for (;;)
1818 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1819 if ( nRet==-1)
1820 break;
1821 switch( nRet )
1823 case -2:
1824 if( aData.isEmpty() )
1825 aData = aReadParam.GetResult();
1826 break;
1827 case '*':
1828 //Skip over MERGEFORMAT
1829 (void)aReadParam.SkipToNextToken();
1830 break;
1834 aData = aData.replaceAll("\"", "");
1837 const auto pType(static_cast<SwDocInfoFieldType*>(
1838 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo)));
1839 SwDocInfoField aField(pType, nSub|nReg, aData, GetFieldResult(pF), nFormat);
1840 if (bDateTime)
1841 ForceFieldLanguage(aField, nLang);
1842 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1844 return eF_ResT::OK;
1847 eF_ResT SwWW8ImplReader::Read_F_Author(WW8FieldDesc* pF, OUString&)
1849 // SH: The SwAuthorField refers not to the original author but to the current user, better use DocInfo
1850 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1851 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )),
1852 DI_CREATE|DI_SUB_AUTHOR|DI_SUB_FIXED, OUString(), GetFieldResult(pF));
1853 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1854 return eF_ResT::OK;
1857 eF_ResT SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc*, OUString& )
1859 SwTemplNameField aField( static_cast<SwTemplNameFieldType*>(
1860 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName )), FF_NAME );
1861 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1862 return eF_ResT::OK;
1865 // Both the date and the time fields can be used for showing a date a time or both.
1866 eF_ResT SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc*pF, OUString& rStr )
1868 bool bHijri = false;
1869 WW8ReadFieldParams aReadParam(rStr);
1870 for (;;)
1872 const sal_Int32 nTok = aReadParam.SkipToNextToken();
1873 if ( nTok==-1 )
1874 break;
1875 switch (nTok)
1877 default:
1878 case 'l':
1879 case -2:
1880 break;
1881 case 'h':
1882 bHijri = true;
1883 break;
1884 case 's':
1885 //Saka Calendar, should we do something with this ?
1886 break;
1890 sal_uInt32 nFormat = 0;
1892 LanguageType nLang(LANGUAGE_SYSTEM);
1893 SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, ww::eDATE, bHijri);
1895 if( SvNumFormatType::UNDEFINED == nDT ) // no D/T-Formatstring
1897 if (32 == pF->nId)
1899 nDT = SvNumFormatType::TIME;
1900 nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
1901 NF_TIME_START, LANGUAGE_SYSTEM );
1903 else
1905 nDT = SvNumFormatType::DATE;
1906 nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
1907 NF_DATE_START, LANGUAGE_SYSTEM );
1911 if (nDT & SvNumFormatType::DATE || nDT == SvNumFormatType::TIME)
1913 SwDateTimeField aField(static_cast<SwDateTimeFieldType*>(
1914 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime)),
1915 nDT & SvNumFormatType::DATE ? DATEFLD : TIMEFLD, nFormat);
1916 if (pF->nOpt & 0x10) // Fixed field
1918 double fSerial;
1919 if (!m_rDoc.GetNumberFormatter()->IsNumberFormat(GetFieldResult(pF), nFormat, fSerial,
1920 SvNumInputOptions::LAX_TIME))
1921 return eF_ResT::TEXT; // just drop the field and insert the plain text.
1922 aField.SetSubType(aField.GetSubType() | FIXEDFLD);
1923 DateTime aSetDateTime(m_rDoc.GetNumberFormatter()->GetNullDate());
1924 aSetDateTime.AddTime(fSerial);
1925 aField.SetDateTime(aSetDateTime);
1927 ForceFieldLanguage(aField, nLang);
1928 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1931 return eF_ResT::OK;
1934 eF_ResT SwWW8ImplReader::Read_F_FileName(WW8FieldDesc*, OUString &rStr)
1936 SwFileNameFormat eType = FF_NAME;
1937 WW8ReadFieldParams aReadParam(rStr);
1938 for (;;)
1940 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1941 if ( nRet==-1 )
1942 break;
1943 switch (nRet)
1945 case 'p':
1946 eType = FF_PATHNAME;
1947 break;
1948 case '*':
1949 //Skip over MERGEFORMAT
1950 (void)aReadParam.SkipToNextToken();
1951 break;
1952 default:
1953 OSL_ENSURE(false, "unknown option in FileName field");
1954 break;
1958 SwFileNameField aField(
1959 static_cast<SwFileNameFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename)), eType);
1960 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1961 return eF_ResT::OK;
1964 eF_ResT SwWW8ImplReader::Read_F_Num( WW8FieldDesc* pF, OUString& rStr )
1966 sal_uInt16 nSub = DS_PAGE; // page number
1967 switch ( pF->nId ){
1968 case 27: nSub = DS_WORD; break; // number of words
1969 case 28: nSub = DS_CHAR; break; // number of characters
1971 SwDocStatField aField( static_cast<SwDocStatFieldType*>(
1972 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocStat )), nSub,
1973 GetNumberPara( rStr ) );
1974 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1975 return eF_ResT::OK;
1978 eF_ResT SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc*, OUString& rStr )
1980 // page number
1981 SwPageNumberField aField( static_cast<SwPageNumberFieldType*>(
1982 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber )), PG_RANDOM,
1983 GetNumberPara(rStr, true));
1985 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1986 return eF_ResT::OK;
1989 eF_ResT SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc*, OUString& rStr )
1991 //e.g. #i20118#
1992 OUString aQ;
1993 OUString aName;
1994 sal_Int32 nSize = 0;
1995 WW8ReadFieldParams aReadParam( rStr );
1996 for (;;)
1998 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1999 if ( nRet==-1 )
2000 break;
2001 switch( nRet )
2003 case -2:
2004 if( aQ.isEmpty() )
2005 aQ = aReadParam.GetResult();
2006 break;
2007 case 'f':
2008 case 'F':
2009 if ( aReadParam.GoToTokenParam() )
2010 aName = aReadParam.GetResult();
2011 break;
2012 case 's':
2013 case 'S':
2014 if ( aReadParam.GoToTokenParam() )
2016 const OUString aSiz = aReadParam.GetResult();
2017 if (!aSiz.isEmpty())
2019 bool bFail = o3tl::checked_multiply<sal_Int32>(aSiz.toInt32(), 20, nSize); // pT -> twip
2020 if (bFail)
2021 nSize = -1;
2024 break;
2027 if( aQ.isEmpty() )
2028 return eF_ResT::TAGIGN; // -> no 0-char in text
2030 sal_Unicode const cChar = static_cast<sal_Unicode>(aQ.toInt32());
2031 if (!linguistic::IsControlChar(cChar) || cChar == '\r' || cChar == '\n' || cChar == '\t')
2033 if (!aName.isEmpty()) // Font Name set ?
2035 SvxFontItem aFont(FAMILY_DONTKNOW, aName, OUString(),
2036 PITCH_DONTKNOW, RTL_TEXTENCODING_SYMBOL, RES_CHRATR_FONT);
2037 NewAttr(aFont); // new Font
2040 if (nSize > 0) //#i20118#
2042 SvxFontHeightItem aSz(nSize, 100, RES_CHRATR_FONTSIZE);
2043 NewAttr(aSz);
2046 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(cChar));
2048 if (nSize > 0)
2049 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONTSIZE);
2050 if (!aName.isEmpty())
2051 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
2053 else
2055 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, "###");
2058 return eF_ResT::OK;
2061 // "EMBED"
2062 eF_ResT SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc*, OUString& rStr )
2064 WW8ReadFieldParams aReadParam( rStr );
2065 for (;;)
2067 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2068 if ( nRet==-1 )
2069 break;
2070 switch( nRet )
2072 case -2:
2073 // sHost
2074 break;
2076 case 's':
2077 // use ObjectSize
2078 break;
2082 if( m_bObj && m_nPicLocFc )
2083 m_nObjLocFc = m_nPicLocFc;
2084 m_bEmbeddObj = true;
2085 return eF_ResT::TEXT;
2088 // "SET"
2089 eF_ResT SwWW8ImplReader::Read_F_Set( WW8FieldDesc* pF, OUString& rStr )
2091 OUString sOrigName;
2092 OUString sVal;
2093 WW8ReadFieldParams aReadParam( rStr );
2094 for (;;)
2096 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2097 if ( nRet==-1 )
2098 break;
2099 switch( nRet )
2101 case -2:
2102 if (sOrigName.isEmpty())
2103 sOrigName = aReadParam.GetResult();
2104 else if (sVal.isEmpty())
2105 sVal = aReadParam.GetResult();
2106 break;
2110 const tools::Long nNo = MapBookmarkVariables(pF, sOrigName, sVal);
2112 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc, sOrigName,
2113 nsSwGetSetExpType::GSE_STRING ) );
2114 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), sVal, ULONG_MAX );
2115 aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
2117 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2119 m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
2121 return eF_ResT::OK;
2124 // "REF"
2125 eF_ResT SwWW8ImplReader::Read_F_Ref( WW8FieldDesc*, OUString& rStr )
2126 { // Reference - Field
2127 OUString sOrigBkmName;
2128 REFERENCEMARK eFormat = REF_CONTENT;
2130 WW8ReadFieldParams aReadParam( rStr );
2131 for (;;)
2133 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2134 if ( nRet==-1 )
2135 break;
2136 switch( nRet )
2138 case -2:
2139 if( sOrigBkmName.isEmpty() ) // get name of bookmark
2140 sOrigBkmName = aReadParam.GetResult();
2141 break;
2143 /* References to numbers in Word could be either to a numbered
2144 paragraph or to a chapter number. However Word does not seem to
2145 have the capability we do, of referring to the chapter number some
2146 other bookmark is in. As a result, cross-references to chapter
2147 numbers in a word document will be cross-references to a numbered
2148 paragraph, being the chapter heading paragraph. As it happens, our
2149 cross-references to numbered paragraphs will do the right thing
2150 when the target is a numbered chapter heading, so there is no need
2151 for us to use the REF_CHAPTER bookmark format on import.
2153 case 'n':
2154 eFormat = REF_NUMBER_NO_CONTEXT;
2155 break;
2156 case 'r':
2157 eFormat = REF_NUMBER;
2158 break;
2159 case 'w':
2160 eFormat = REF_NUMBER_FULL_CONTEXT;
2161 break;
2163 case 'p':
2164 eFormat = REF_UPDOWN;
2165 break;
2166 case 'h':
2167 break;
2168 default:
2169 // unimplemented switch: just do 'nix nought nothing' :-)
2170 break;
2174 OUString sBkmName(GetMappedBookmark(sOrigBkmName));
2176 // #i120879# add cross reference bookmark name prefix, if it
2177 // matches internal TOC bookmark naming convention
2178 if ( IsTOCBookmarkName( sBkmName ) )
2180 sBkmName = EnsureTOCBookmarkName(sBkmName);
2181 // track <sBookmarkName> as referenced TOC bookmark.
2182 m_xReffedStck->m_aReferencedTOCBookmarks.insert( sBkmName );
2185 SwGetRefField aField(
2186 static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),
2187 sBkmName,"",REF_BOOKMARK,0,eFormat);
2189 if (eFormat == REF_CONTENT)
2192 If we are just inserting the contents of the bookmark, then it
2193 is possible that the bookmark is actually a variable, so we
2194 must store it until the end of the document to see if it was,
2195 in which case we'll turn it into a show variable
2197 m_xReffingStck->NewAttr( *m_pPaM->GetPoint(), SwFormatField(aField) );
2198 m_xReffingStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2200 else
2202 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
2204 return eF_ResT::OK;
2207 // Note Reference - Field
2208 eF_ResT SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc*, OUString& rStr )
2210 OUString aBkmName;
2211 bool bAboveBelow = false;
2213 WW8ReadFieldParams aReadParam( rStr );
2214 for (;;)
2216 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2217 if ( nRet==-1 )
2218 break;
2219 switch( nRet )
2221 case -2:
2222 if( aBkmName.isEmpty() ) // get name of foot/endnote
2223 aBkmName = aReadParam.GetResult();
2224 break;
2225 case 'r':
2226 // activate flag 'Chapter Number'
2227 break;
2228 case 'p':
2229 bAboveBelow = true;
2230 break;
2231 case 'h':
2232 break;
2233 default:
2234 // unimplemented switch: just do 'nix nought nothing' :-)
2235 break;
2239 // set Sequence No of corresponding Foot-/Endnote to Zero
2240 // (will be corrected in
2241 SwGetRefField aField( static_cast<SwGetRefFieldType*>(
2242 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )), aBkmName, "", REF_FOOTNOTE, 0,
2243 REF_ONLYNUMBER );
2244 m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField));
2245 m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2246 if (bAboveBelow)
2248 SwGetRefField aField2( static_cast<SwGetRefFieldType*>(
2249 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),aBkmName, "", REF_FOOTNOTE, 0,
2250 REF_UPDOWN );
2251 m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField2));
2252 m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2254 return eF_ResT::OK;
2257 // "PAGEREF"
2258 eF_ResT SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc*, OUString& rStr )
2260 OUString sOrigName;
2261 WW8ReadFieldParams aReadParam( rStr );
2262 for (;;)
2264 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2265 if ( nRet==-1 )
2266 break;
2267 else if ( nRet == -2 && sOrigName.isEmpty() )
2269 sOrigName = aReadParam.GetResult();
2273 const OUString sName(GetMappedBookmark(sOrigName));
2275 // loading page reference field in TOX
2276 if (m_bLoadingTOXCache)
2278 // insert page ref representation as plain text --> return FLD_TEXT
2279 // if there is no hyperlink settings for current toc and referenced bookmark is available,
2280 // assign link to current ref area
2281 if (!m_bLoadingTOXHyperlink && !sName.isEmpty())
2283 // #i120879# add cross reference bookmark name prefix, if it
2284 // matches internal TOC bookmark naming convention
2285 OUString sBookmarkName;
2286 if ( IsTOCBookmarkName( sName ) )
2288 sBookmarkName = EnsureTOCBookmarkName(sName);
2289 // track <sBookmarkName> as referenced TOC bookmark.
2290 m_xReffedStck->m_aReferencedTOCBookmarks.insert( sBookmarkName );
2292 else
2294 sBookmarkName = sName;
2296 OUString sURL = "#" + sBookmarkName;
2297 SwFormatINetFormat aURL( sURL, "" );
2298 static const OUStringLiteral sLinkStyle(u"Index Link");
2299 const sal_uInt16 nPoolId =
2300 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
2301 aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId);
2302 aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
2303 m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
2305 return eF_ResT::TEXT;
2308 // #i120879# add cross reference bookmark name prefix, if it matches
2309 // internal TOC bookmark naming convention
2310 OUString sPageRefBookmarkName;
2311 if ( IsTOCBookmarkName( sName ) )
2313 sPageRefBookmarkName = EnsureTOCBookmarkName(sName);
2314 // track <sPageRefBookmarkName> as referenced TOC bookmark.
2315 m_xReffedStck->m_aReferencedTOCBookmarks.insert( sPageRefBookmarkName );
2317 else
2319 sPageRefBookmarkName = sName;
2321 SwGetRefField aField( static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),
2322 sPageRefBookmarkName, "", REF_BOOKMARK, 0, REF_PAGE );
2323 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2325 return eF_ResT::OK;
2328 //helper function
2329 //For MS MacroButton field, the symbol in plain text is always "(" (0x28),
2330 //which should be mapped according to the macro type
2331 static bool ConvertMacroSymbol( std::u16string_view rName, OUString& rReference )
2333 bool bConverted = false;
2334 if( rReference == "(" )
2336 bConverted = true;
2337 sal_Unicode cSymbol = sal_Unicode(); // silence false warning
2338 if (rName == u"CheckIt")
2339 cSymbol = 0xF06F;
2340 else if (rName == u"UncheckIt")
2341 cSymbol = 0xF0FE;
2342 else if (rName == u"ShowExample")
2343 cSymbol = 0xF02A;
2344 //else if... : todo
2345 else
2346 bConverted = false;
2348 if( bConverted )
2349 rReference = OUString(cSymbol);
2351 return bConverted;
2354 // "MACROBUTTON"
2355 eF_ResT SwWW8ImplReader::Read_F_Macro( WW8FieldDesc*, OUString& rStr)
2357 OUString aName;
2358 OUString aVText;
2359 bool bNewVText = true;
2360 bool bBracket = false;
2361 WW8ReadFieldParams aReadParam( rStr );
2363 for (;;)
2365 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2366 if ( nRet==-1 )
2367 break;
2368 switch( nRet )
2370 case -2:
2371 if( aName.isEmpty() )
2372 aName = aReadParam.GetResult();
2373 else if( aVText.isEmpty() || bBracket )
2375 if( bBracket )
2376 aVText += " ";
2377 aVText += aReadParam.GetResult();
2378 if (bNewVText)
2380 bBracket = (aVText[0] == '[');
2381 bNewVText = false;
2383 else if( aVText.endsWith("]") )
2384 bBracket = false;
2386 break;
2389 if( aName.isEmpty() )
2390 return eF_ResT::TAGIGN; // makes no sense without Macro-Name
2392 NotifyMacroEventRead();
2394 //try converting macro symbol according to macro name
2395 bool bApplyWingdings = ConvertMacroSymbol( aName, aVText );
2396 aName = "StarOffice.Standard.Modul1." + aName;
2398 SwMacroField aField( static_cast<SwMacroFieldType*>(
2399 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Macro )), aName, aVText );
2401 if( !bApplyWingdings )
2402 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2403 else
2405 //set Wingdings font
2406 sal_uInt16 i = 0;
2407 for ( ; i < m_xFonts->GetMax(); i++ )
2409 FontFamily eFamily;
2410 OUString aFontName;
2411 FontPitch ePitch;
2412 rtl_TextEncoding eSrcCharSet;
2413 if( GetFontParams( i, eFamily, aFontName, ePitch, eSrcCharSet )
2414 && aFontName=="Wingdings" )
2416 break;
2420 if ( i < m_xFonts->GetMax() )
2423 SetNewFontAttr( i, true, RES_CHRATR_FONT );
2424 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2425 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT );
2426 ResetCharSetVars();
2430 return eF_ResT::OK;
2433 bool CanUseRemoteLink(const OUString &rGrfName)
2435 bool bUseRemote = false;
2438 // Related: tdf#102499, add a default css::ucb::XCommandEnvironment
2439 // in order to have https protocol manage certificates correctly
2440 uno::Reference< task::XInteractionHandler > xIH(
2441 task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr));
2443 uno::Reference< ucb::XProgressHandler > xProgress;
2444 rtl::Reference<::ucbhelper::CommandEnvironment> pCommandEnv =
2445 new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH ), xProgress);
2447 ::ucbhelper::Content aCnt(rGrfName,
2448 static_cast< ucb::XCommandEnvironment* >(pCommandEnv.get()),
2449 comphelper::getProcessComponentContext());
2451 if ( !INetURLObject( rGrfName ).isAnyKnownWebDAVScheme() )
2453 OUString aTitle;
2454 aCnt.getPropertyValue("Title") >>= aTitle;
2455 bUseRemote = !aTitle.isEmpty();
2457 else
2459 // is a link to a WebDAV resource
2460 // need to use MediaType to check for link usability
2461 OUString aMediaType;
2462 aCnt.getPropertyValue("MediaType") >>= aMediaType;
2463 bUseRemote = !aMediaType.isEmpty();
2466 catch ( ... )
2468 // this file did not exist, so we will not set this as graphiclink
2469 bUseRemote = false;
2471 return bUseRemote;
2474 // "INCLUDEPICTURE"
2475 eF_ResT SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr )
2477 OUString aGrfName;
2478 bool bEmbedded = true;
2480 WW8ReadFieldParams aReadParam( rStr );
2481 for (;;)
2483 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2484 if ( nRet==-1 )
2485 break;
2486 switch( nRet )
2488 case -2:
2489 if (aGrfName.isEmpty())
2490 aGrfName = ConvertFFileName(aReadParam.GetResult());
2491 break;
2493 case 'd':
2494 bEmbedded = false;
2495 break;
2497 case 'c':// skip the converter name
2498 aReadParam.FindNextStringPiece();
2499 break;
2503 if (!bEmbedded)
2504 bEmbedded = !CanUseRemoteLink(aGrfName);
2506 if (!bEmbedded)
2509 Special case:
2511 Now we write the Link into the Doc and remember the SwFlyFrameFormat.
2512 Since we end on return FLD_READ_FSPA below, the skip value will be set
2513 so that Char-1 will still be read.
2514 When we then call SwWW8ImplReader::ImportGraf() it will then recognize
2515 that we have inserted a graphic link and the suiting SwAttrSet will be
2516 inserted into the frame format.
2518 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFlySet( m_rDoc.GetAttrPool() );
2519 aFlySet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
2520 aFlySet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
2521 m_pFlyFormatOfJustInsertedGraphic =
2522 m_rDoc.getIDocumentContentOperations().InsertGraphic(*m_pPaM,
2523 aGrfName,
2524 OUString(),
2525 nullptr, // Graphic*
2526 &aFlySet,
2527 nullptr, nullptr); // SwFrameFormat*
2528 m_aGrfNameGenerator.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic,
2529 INetURLObject(aGrfName).GetBase());
2531 return eF_ResT::READ_FSPA;
2534 OUString wwSectionNamer::UniqueName()
2536 const OUString aName(msFileLinkSeed + OUString::number(++mnFileSectionNo));
2537 return mrDoc.GetUniqueSectionName(&aName);
2540 // "INCLUDETEXT"
2541 eF_ResT SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc* /*pF*/, OUString& rStr )
2543 OUString aPara;
2544 OUString aBook;
2545 WW8ReadFieldParams aReadParam( rStr );
2546 for (;;)
2548 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2549 if ( nRet==-1 )
2550 break;
2551 switch( nRet )
2553 case -2:
2554 if( aPara.isEmpty() )
2555 aPara = aReadParam.GetResult();
2556 else if( aBook.isEmpty() )
2557 aBook = aReadParam.GetResult();
2558 break;
2559 case '*':
2560 //Skip over MERGEFORMAT
2561 (void)aReadParam.SkipToNextToken();
2562 break;
2565 aPara = ConvertFFileName(aPara);
2567 if (!aBook.isEmpty() && aBook[ 0 ] != '\\')
2569 // Section from Source (no switch)?
2570 ConvertUFName(aBook);
2571 aPara += OUStringChar(sfx2::cTokenSeparator)
2572 + OUStringChar(sfx2::cTokenSeparator) + aBook;
2576 ##509##
2577 What we will do is insert a section to be linked to a file, but just in
2578 case the file is not available we will fill in the section with the stored
2579 content of this winword field as a fallback.
2581 SwPosition aTmpPos(*m_pPaM->GetPoint());
2583 SwSectionData aSection(SectionType::FileLink,
2584 m_aSectionNameGenerator.UniqueName());
2585 aSection.SetLinkFileName( aPara );
2586 aSection.SetProtectFlag(true);
2588 SwSection *const pSection =
2589 m_rDoc.InsertSwSection(*m_pPaM, aSection, nullptr, nullptr, false);
2590 OSL_ENSURE(pSection, "no section inserted");
2591 if (!pSection)
2592 return eF_ResT::TEXT;
2593 const SwSectionNode* pSectionNode = pSection->GetFormat()->GetSectionNode();
2594 OSL_ENSURE(pSectionNode, "no section node!");
2595 if (!pSectionNode)
2596 return eF_ResT::TEXT;
2598 m_pPaM->GetPoint()->Assign( pSectionNode->GetIndex()+1 );
2600 //we have inserted a section before this point, so adjust pos
2601 //for future page/section segment insertion
2602 m_aSectionManager.PrependedInlineNode(aTmpPos, m_pPaM->GetPointNode());
2604 return eF_ResT::TEXT;
2607 // "SERIALPRINT"
2608 eF_ResT SwWW8ImplReader::Read_F_DBField( WW8FieldDesc* pF, OUString& rStr )
2610 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
2611 (void) pF;
2612 (void) rStr;
2613 #else
2614 OUString aName;
2615 WW8ReadFieldParams aReadParam( rStr );
2616 for (;;)
2618 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2619 if ( nRet==-1 )
2620 break;
2621 switch( nRet )
2623 case -2:
2624 if( aName.isEmpty() )
2625 aName = aReadParam.GetResult();
2626 break;
2629 SwDBFieldType aD( &m_rDoc, aName, SwDBData() ); // Database: nothing
2631 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aD );
2632 SwDBField aField( static_cast<SwDBFieldType*>(pFT) );
2633 aField.SetFieldCode( rStr );
2635 OUString aResult;
2636 m_xSBase->WW8ReadString( *m_pStrm, aResult, m_xPlcxMan->GetCpOfs()+
2637 pF->nSRes, pF->nLRes, m_eTextCharSet );
2639 aResult = aResult.replace( '\xb', '\n' );
2641 aField.InitContent(aResult);
2643 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField( aField ));
2644 #endif
2645 return eF_ResT::OK;
2648 // "NEXT"
2649 eF_ResT SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc*, OUString& )
2651 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
2652 SwDBNextSetFieldType aN;
2653 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
2654 SwDBNextSetField aField( static_cast<SwDBNextSetFieldType*>(pFT), OUString(),
2655 SwDBData() ); // Database: nothing
2656 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2657 #endif
2658 return eF_ResT::OK;
2661 // "DATASET"
2662 eF_ResT SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc*, OUString& )
2664 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
2665 SwDBSetNumberFieldType aN;
2666 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
2667 SwDBSetNumberField aField( static_cast<SwDBSetNumberFieldType*>(pFT),
2668 SwDBData() ); // Datenbase: nothing
2669 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2670 #endif
2671 return eF_ResT::OK;
2675 EQ , only the usage for
2676 a. Combined Characters supported, must be exactly in the form that word
2677 only accepts as combined characters, i.e.
2678 eq \o(\s\up Y(XXX),\s\do Y(XXX))
2679 b. Ruby Text supported, must be in the form that word recognizes as being
2680 ruby text
2683 eF_ResT SwWW8ImplReader::Read_F_Equation( WW8FieldDesc*, OUString& rStr )
2685 WW8ReadFieldParams aReadParam( rStr );
2686 const sal_Int32 cChar = aReadParam.SkipToNextToken();
2687 if ('o' == cChar || 'O' == cChar)
2689 EquationResult aResult(ParseCombinedChars(rStr));
2691 if (aResult.sType == "Input")
2693 SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
2694 aResult.sResult, aResult.sResult, INP_TXT, 0 );
2695 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); // insert input field
2697 else if (aResult.sType == "CombinedCharacters")
2699 SwCombinedCharField aField(static_cast<SwCombinedCharFieldType*>(
2700 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars)), aResult.sType);
2701 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
2704 else if ('*' == cChar)
2705 Read_SubF_Ruby(aReadParam);
2707 return eF_ResT::OK;
2710 void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams& rReadParam)
2712 sal_uInt16 nJustificationCode=0;
2713 OUString sFontName;
2714 sal_uInt32 nFontSize=0;
2715 OUString sRuby;
2716 OUString sText;
2717 for (;;)
2719 const sal_Int32 nRet = rReadParam.SkipToNextToken();
2720 if ( nRet==-1 )
2721 break;
2722 switch( nRet )
2724 case -2:
2726 OUString sTemp = rReadParam.GetResult();
2727 if( sTemp.startsWithIgnoreAsciiCase( "jc" ) )
2729 sTemp = sTemp.copy(2);
2730 nJustificationCode = o3tl::narrowing<sal_uInt16>(sTemp.toInt32());
2732 else if( sTemp.startsWithIgnoreAsciiCase( "hps" ) )
2734 sTemp = sTemp.copy(3);
2735 nFontSize= static_cast<sal_uInt32>(sTemp.toInt32());
2737 else if( sTemp.startsWithIgnoreAsciiCase( "Font:" ) )
2739 sTemp = sTemp.copy(5);
2740 sFontName = sTemp;
2743 break;
2744 case '*':
2745 break;
2746 case 'o':
2747 for (;;)
2749 const sal_Int32 nRes = rReadParam.SkipToNextToken();
2750 if ( nRes==-1 )
2751 break;
2752 if ('u' == nRes)
2754 if (-2 == rReadParam.SkipToNextToken() &&
2755 rReadParam.GetResult().startsWithIgnoreAsciiCase("p"))
2757 if (-2 == rReadParam.SkipToNextToken())
2759 OUString sPart = rReadParam.GetResult();
2760 sal_Int32 nBegin = sPart.indexOf('(');
2762 //Word disallows brackets in this field,
2763 sal_Int32 nEnd = sPart.indexOf(')');
2765 if ((nBegin != -1) &&
2766 (nEnd != -1) && (nBegin < nEnd))
2768 sRuby = sPart.copy(nBegin+1,nEnd-nBegin-1);
2770 if (-1 != nEnd)
2772 nBegin = sPart.indexOf(',',nEnd);
2773 if (-1 == nBegin)
2775 nBegin = sPart.indexOf(';',nEnd);
2777 nEnd = sPart.lastIndexOf(')');
2779 if ((nBegin != -1) && (nEnd != -1) && (nBegin < nEnd))
2781 sText = sPart.copy(nBegin+1,nEnd-nBegin-1);
2782 sText = sw::FilterControlChars(sText);
2788 break;
2792 //Translate and apply
2793 if (sRuby.isEmpty() || sText.isEmpty() || sFontName.isEmpty() || !nFontSize)
2794 return;
2796 css::text::RubyAdjust eRubyAdjust;
2797 switch (nJustificationCode)
2799 case 0:
2800 eRubyAdjust = css::text::RubyAdjust_CENTER;
2801 break;
2802 case 1:
2803 eRubyAdjust = css::text::RubyAdjust_BLOCK;
2804 break;
2805 case 2:
2806 eRubyAdjust = css::text::RubyAdjust_INDENT_BLOCK;
2807 break;
2808 default:
2809 case 3:
2810 eRubyAdjust = css::text::RubyAdjust_LEFT;
2811 break;
2812 case 4:
2813 eRubyAdjust = css::text::RubyAdjust_RIGHT;
2814 break;
2817 SwFormatRuby aRuby(sRuby);
2818 const SwCharFormat *pCharFormat=nullptr;
2819 //Make a guess at which of asian of western we should be setting
2820 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
2821 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(sRuby, 0);
2823 //Check to see if we already have a ruby charstyle that this fits
2824 for(const auto& rpCharFormat : m_aRubyCharFormats)
2826 const SvxFontHeightItem &rFH =
2827 rpCharFormat->GetFormatAttr(
2828 GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
2829 if (rFH.GetHeight() == nFontSize*10)
2831 const SvxFontItem &rF = rpCharFormat->GetFormatAttr(
2832 GetWhichOfScript(RES_CHRATR_FONT,nScript));
2833 if (rF.GetFamilyName() == sFontName)
2835 pCharFormat = rpCharFormat;
2836 break;
2841 //Create a new char style if necessary
2842 if (!pCharFormat)
2844 OUString aNm;
2845 //Take this as the base name
2846 SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT,aNm);
2847 aNm+=OUString::number(m_aRubyCharFormats.size()+1);
2848 SwCharFormat *pFormat = m_rDoc.MakeCharFormat(aNm, m_rDoc.GetDfltCharFormat());
2849 SvxFontHeightItem aHeightItem(nFontSize*10, 100, RES_CHRATR_FONTSIZE);
2850 SvxFontItem aFontItem(FAMILY_DONTKNOW,sFontName,
2851 OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
2852 aHeightItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
2853 aFontItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONT,nScript));
2854 pFormat->SetFormatAttr(aHeightItem);
2855 pFormat->SetFormatAttr(aFontItem);
2856 m_aRubyCharFormats.push_back(pFormat);
2857 pCharFormat = pFormat;
2860 //Set the charstyle and justification
2861 aRuby.SetCharFormatName(pCharFormat->GetName());
2862 aRuby.SetCharFormatId(pCharFormat->GetPoolFormatId());
2863 aRuby.SetAdjustment(eRubyAdjust);
2865 NewAttr(aRuby);
2866 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, sText );
2867 m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CJK_RUBY );
2871 // "table of ..." fields
2873 static void lcl_toxMatchACSwitch(SwDoc const & rDoc,
2874 SwTOXBase& rBase,
2875 WW8ReadFieldParams& rParam,
2876 SwCaptionDisplay eCaptionType)
2878 if ( rParam.GoToTokenParam() )
2880 SwTOXType* pType = const_cast<SwTOXType*>(rDoc.GetTOXType( TOX_ILLUSTRATIONS, 0));
2881 rBase.RegisterToTOXType( *pType );
2882 rBase.SetCaptionDisplay( eCaptionType );
2883 // Read Sequence Name and store in TOXBase
2884 OUString sSeqName( rParam.GetResult() );
2885 lcl_ConvertSequenceName( sSeqName );
2886 rBase.SetSequenceName( sSeqName );
2890 static void EnsureMaxLevelForTemplates(SwTOXBase& rBase)
2892 //If the TOC contains Template entries at levels > the evaluation level
2893 //that was initially taken from the max normal outline level of the word TOC
2894 //then we cannot use that for the evaluation level because writer cuts off
2895 //all styles above that level, while word just cuts off the "standard"
2896 //outline styles, we have no option but to expand to the highest level
2897 //Word included.
2898 if ((rBase.GetLevel() != MAXLEVEL) && (SwTOXElement::Template & rBase.GetCreateType()))
2900 for (sal_uInt16 nI = MAXLEVEL; nI > 0; --nI)
2902 if (!rBase.GetStyleNames(nI-1).isEmpty())
2904 rBase.SetLevel(nI);
2905 break;
2911 static void lcl_toxMatchTSwitch(SwWW8ImplReader const & rReader, SwTOXBase& rBase,
2912 WW8ReadFieldParams& rParam)
2914 if ( !rParam.GoToTokenParam() )
2915 return;
2917 OUString sParams( rParam.GetResult() );
2918 if( sParams.isEmpty() )
2919 return;
2921 sal_Int32 nIndex = 0;
2923 // Delimiters between styles and style levels appears to allow both ; and ,
2925 OUString sTemplate( sParams.getToken(0, ';', nIndex) );
2926 if( -1 == nIndex )
2928 nIndex=0;
2929 sTemplate = sParams.getToken(0, ',', nIndex);
2931 if( -1 == nIndex )
2933 const SwFormat* pStyle = rReader.GetStyleWithOrgWWName(sTemplate);
2934 if( pStyle )
2935 sTemplate = pStyle->GetName();
2936 // Store Style for Level 0 into TOXBase
2937 rBase.SetStyleNames( sTemplate, 0 );
2939 else while( -1 != nIndex )
2941 sal_Int32 nOldIndex=nIndex;
2942 sal_uInt16 nLevel = o3tl::narrowing<sal_uInt16>(
2943 o3tl::toInt32(o3tl::getToken(sParams, 0, ';', nIndex)));
2944 if( -1 == nIndex )
2946 nIndex = nOldIndex;
2947 nLevel = o3tl::narrowing<sal_uInt16>(
2948 o3tl::toInt32(o3tl::getToken(sParams, 0, ',', nIndex)));
2951 if( (0 < nLevel) && (MAXLEVEL >= nLevel) )
2953 nLevel--;
2954 // Store Style and Level into TOXBase
2955 const SwFormat* pStyle
2956 = rReader.GetStyleWithOrgWWName( sTemplate );
2958 if( pStyle )
2959 sTemplate = pStyle->GetName();
2961 OUString sStyles( rBase.GetStyleNames( nLevel ) );
2962 if( !sStyles.isEmpty() )
2963 sStyles += OUStringChar(TOX_STYLE_DELIMITER);
2964 sStyles += sTemplate;
2965 rBase.SetStyleNames( sStyles, nLevel );
2967 // read next style name...
2968 nOldIndex = nIndex;
2969 sTemplate = sParams.getToken(0, ';', nIndex);
2970 if( -1 == nIndex )
2972 nIndex=nOldIndex;
2973 sTemplate = sParams.getToken(0, ',', nIndex);
2978 sal_uInt16 wwSectionManager::CurrentSectionColCount() const
2980 sal_uInt16 nIndexCols = 1;
2981 if (!maSegments.empty())
2982 nIndexCols = maSegments.back().maSep.ccolM1 + 1;
2983 return nIndexCols;
2986 //Will there be a new pagebreak at this position (don't know what type
2987 //until later)
2988 bool wwSectionManager::WillHavePageDescHere(const SwNode& rNd) const
2990 bool bRet = false;
2991 if (!maSegments.empty())
2993 if (!maSegments.back().IsContinuous() &&
2994 maSegments.back().maStart == rNd)
2996 bRet = true;
2999 return bRet;
3002 static sal_uInt16 lcl_GetMaxValidWordTOCLevel(const SwForm &rForm)
3004 // GetFormMax() returns level + 1, hence the -1
3005 sal_uInt16 nRet = rForm.GetFormMax()-1;
3007 // If the max of this type of TOC is greater than the max of a word
3008 // possible toc, then clip to the word max
3009 if (nRet > WW8ListManager::nMaxLevel)
3010 nRet = WW8ListManager::nMaxLevel;
3012 return nRet;
3015 eF_ResT SwWW8ImplReader::Read_F_Tox( WW8FieldDesc* pF, OUString& rStr )
3017 if (!m_bLoadingTOXCache)
3019 m_bLoadingTOXCache = true;
3021 else
3023 // Embedded TOX --> continue reading its content, but no further TOX
3024 // field
3025 ++m_nEmbeddedTOXLevel;
3026 return eF_ResT::TEXT;
3029 if (pF->nLRes < 3)
3030 return eF_ResT::TEXT; // ignore (#i25440#)
3032 TOXTypes eTox; // create a ToxBase
3033 switch( pF->nId )
3035 case 8:
3036 eTox = TOX_INDEX;
3037 break;
3038 case 13:
3039 eTox = TOX_CONTENT;
3040 break;
3041 default:
3042 eTox = TOX_USER;
3043 break;
3046 SwTOXElement nCreateOf = (eTox == TOX_CONTENT) ? SwTOXElement::OutlineLevel : SwTOXElement::Mark;
3048 sal_uInt16 nIndexCols = 1;
3050 const SwTOXType* pType = m_rDoc.GetTOXType( eTox, 0 );
3051 SwForm aOrigForm(eTox);
3052 std::shared_ptr<SwTOXBase> pBase = std::make_shared<SwTOXBase>( pType, aOrigForm, nCreateOf, OUString() );
3053 pBase->SetProtected(m_aSectionManager.CurrentSectionIsProtected());
3054 switch( eTox ){
3055 case TOX_INDEX:
3057 SwTOIOptions eOptions = SwTOIOptions::SameEntry | SwTOIOptions::CaseSensitive;
3059 // We set SwTOXElement::OutlineLevel only if
3060 // the parameter \o is within 1 to 9
3061 // or the parameter \f exists
3062 // or NO switch parameter are given at all.
3063 WW8ReadFieldParams aReadParam( rStr );
3064 for (;;)
3066 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3067 if ( nRet==-1 )
3068 break;
3069 switch( nRet )
3071 case 'c':
3072 if ( aReadParam.GoToTokenParam() )
3074 const OUString sParams( aReadParam.GetResult() );
3075 // if NO OUString just ignore the \c
3076 if( !sParams.isEmpty() )
3078 nIndexCols = o3tl::narrowing<sal_uInt16>(sParams.toInt32());
3081 break;
3082 case 'e':
3084 if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \e
3086 OUString sDelimiter( aReadParam.GetResult() );
3087 SwForm aForm( pBase->GetTOXForm() );
3089 // Attention: if TOX_CONTENT brave
3090 // GetFormMax() returns MAXLEVEL + 1 !!
3091 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3093 for(sal_uInt16 nLevel = 1;
3094 nLevel <= nEnd;
3095 ++nLevel)
3097 // Levels count from 1
3098 // Level 0 is reserved for CAPTION
3100 // Insert delimiter instead of tab in front of the page number if there is one:
3101 FormTokenType ePrevType = TOKEN_END;
3102 FormTokenType eType;
3103 // -> #i21237#
3104 SwFormTokens aPattern =
3105 aForm.GetPattern(nLevel);
3106 SwFormTokens::iterator aIt = aPattern.begin();
3109 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3111 if (eType == TOKEN_PAGE_NUMS)
3113 if (TOKEN_TAB_STOP == ePrevType)
3115 --aIt;
3117 if(0x09 == sDelimiter[0])
3118 aIt->eTabAlign = SvxTabAdjust::End;
3119 else
3121 SwFormToken aToken(TOKEN_TEXT);
3122 aToken.sText = sDelimiter;
3123 *aIt = aToken;
3125 aForm.SetPattern(nLevel, std::move(aPattern));
3128 eType = TOKEN_END;
3131 ePrevType = eType;
3133 while (TOKEN_END != eType);
3134 // <- #i21237#
3136 pBase->SetTOXForm( aForm );
3139 break;
3140 case 'h':
3142 eOptions |= SwTOIOptions::AlphaDelimiter;
3144 break;
3147 pBase->SetOptions( eOptions );
3149 break;
3151 case TOX_CONTENT:
3153 bool bIsHyperlink = false;
3154 // We set SwTOXElement::OutlineLevel only if
3155 // the parameter \o is within 1 to 9
3156 // or the parameter \f exists
3157 // or NO switch parameter are given at all.
3158 SwTOXElement eCreateFrom = SwTOXElement::NONE;
3159 sal_Int32 nMaxLevel = 0;
3160 WW8ReadFieldParams aReadParam( rStr );
3161 for (;;)
3163 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3164 if ( nRet==-1 )
3165 break;
3166 switch( nRet )
3168 case 'h':
3169 bIsHyperlink = true;
3170 break;
3171 case 'a':
3172 case 'c':
3173 lcl_toxMatchACSwitch(m_rDoc, *pBase, aReadParam,
3174 ('c' == nRet)
3175 ? CAPTION_COMPLETE
3176 : CAPTION_TEXT );
3177 break;
3178 case 'o':
3180 sal_Int32 nVal;
3181 if( !aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
3182 nVal = lcl_GetMaxValidWordTOCLevel(aOrigForm);
3183 if( nMaxLevel < nVal )
3184 nMaxLevel = nVal;
3185 eCreateFrom |= SwTOXElement::OutlineLevel;
3187 break;
3188 case 'f':
3189 eCreateFrom |= SwTOXElement::Mark;
3190 break;
3191 case 'l':
3193 sal_Int32 nVal;
3194 if( aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
3196 if( nMaxLevel < nVal )
3197 nMaxLevel = nVal;
3198 eCreateFrom |= SwTOXElement::Mark;
3201 break;
3202 case 't': // paragraphs using special styles shall
3203 // provide the TOX's content
3204 lcl_toxMatchTSwitch(*this, *pBase, aReadParam);
3205 eCreateFrom |= SwTOXElement::Template;
3206 break;
3207 case 'p':
3209 if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \p
3211 OUString sDelimiter( aReadParam.GetResult() );
3212 SwForm aForm( pBase->GetTOXForm() );
3214 // Attention: if TOX_CONTENT brave
3215 // GetFormMax() returns MAXLEVEL + 1 !!
3216 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3218 for(sal_uInt16 nLevel = 1;
3219 nLevel <= nEnd;
3220 ++nLevel)
3222 // Levels count from 1
3223 // Level 0 is reserved for CAPTION
3225 // Insert delimiter instead of tab in front of the pagenumber if there is one:
3226 FormTokenType ePrevType = TOKEN_END;
3227 FormTokenType eType;
3229 // -> #i21237#
3230 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3231 SwFormTokens::iterator aIt = aPattern.begin();
3234 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3236 if (eType == TOKEN_PAGE_NUMS)
3238 if (TOKEN_TAB_STOP == ePrevType)
3240 --aIt;
3242 SwFormToken aToken(TOKEN_TEXT);
3243 aToken.sText = sDelimiter;
3245 *aIt = aToken;
3246 aForm.SetPattern(nLevel,
3247 std::move(aPattern));
3249 eType = TOKEN_END;
3251 ePrevType = eType;
3253 while( TOKEN_END != eType );
3254 // <- #i21237#
3256 pBase->SetTOXForm( aForm );
3259 break;
3260 case 'n': // don't print page numbers
3262 // read START and END param
3263 sal_Int32 nStart(0);
3264 sal_Int32 nEnd(0);
3265 if( !aReadParam.GetTokenSttFromTo( &nStart, &nEnd,
3266 WW8ListManager::nMaxLevel ) )
3268 nStart = 1;
3269 nEnd = aOrigForm.GetFormMax()-1;
3271 // remove page numbers from this levels
3272 SwForm aForm( pBase->GetTOXForm() );
3273 if (aForm.GetFormMax() <= nEnd)
3274 nEnd = aForm.GetFormMax()-1;
3275 for ( sal_Int32 nLevel = nStart; nLevel<=nEnd; ++nLevel )
3277 // Levels count from 1
3278 // Level 0 is reserved for CAPTION
3280 // Remove pagenumber and if necessary the tab in front of it:
3281 FormTokenType eType;
3282 // -> #i21237#
3283 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3284 SwFormTokens::iterator aIt = aPattern.begin();
3287 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3289 if (eType == TOKEN_PAGE_NUMS)
3291 aIt = aPattern.erase(aIt);
3292 --aIt;
3293 if (
3294 TOKEN_TAB_STOP ==
3295 aIt->eTokenType
3298 aPattern.erase(aIt);
3299 aForm.SetPattern(nLevel, std::move(aPattern));
3301 eType = TOKEN_END;
3304 while (TOKEN_END != eType);
3305 // <- #i21237#
3307 pBase->SetTOXForm( aForm );
3309 break;
3312 // the following switches are not (yet) supported
3313 // by good old StarWriter:
3314 case 'b':
3315 case 's':
3316 case 'd':
3317 break;
3322 // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens
3323 // also include the hyperlinks and page references
3324 SwFormToken aLinkStart(TOKEN_LINK_START);
3325 SwFormToken aLinkEnd(TOKEN_LINK_END);
3326 aLinkStart.sCharStyleName = "Index Link";
3327 aLinkEnd.sCharStyleName = "Index Link";
3328 SwForm aForm(pBase->GetTOXForm());
3329 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3331 for(sal_uInt16 nLevel = 1; nLevel <= nEnd; ++nLevel)
3333 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3334 if ( bIsHyperlink )
3336 aPattern.insert(aPattern.begin(), aLinkStart);
3338 else
3340 auto aItr = std::find_if(aPattern.begin(), aPattern.end(),
3341 [](const SwFormToken& rToken) { return rToken.eTokenType == TOKEN_PAGE_NUMS; });
3342 if (aItr != aPattern.end())
3343 aPattern.insert(aItr, aLinkStart);
3345 aPattern.push_back(aLinkEnd);
3346 aForm.SetPattern(nLevel, std::move(aPattern));
3348 pBase->SetTOXForm(aForm);
3350 if (!nMaxLevel)
3351 nMaxLevel = WW8ListManager::nMaxLevel;
3352 pBase->SetLevel(nMaxLevel);
3354 const TOXTypes eType = pBase->GetTOXType()->GetType();
3355 switch( eType )
3357 case TOX_CONTENT:
3359 //If we would be created from outlines, either explicitly or by default
3360 //then see if we need extra styles added to the outlines
3361 SwTOXElement eEffectivelyFrom = eCreateFrom != SwTOXElement::NONE ? eCreateFrom : SwTOXElement::OutlineLevel;
3362 if (eEffectivelyFrom & SwTOXElement::OutlineLevel)
3364 // #i19683# Insert a text token " " between the number and entry token.
3365 // In an ideal world we could handle the tab stop between the number and
3366 // the entry correctly, but I currently have no clue how to obtain
3367 // the tab stop position. It is _not_ set at the paragraph style.
3368 std::unique_ptr<SwForm> pForm;
3369 for (const SwWW8StyInf & rSI : m_vColl)
3371 if (rSI.IsOutlineNumbered())
3373 sal_uInt16 nStyleLevel = rSI.mnWW8OutlineLevel;
3374 const SwNumFormat& rFormat = rSI.GetOutlineNumrule()->Get( nStyleLevel );
3375 if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() )
3377 ++nStyleLevel;
3379 if ( !pForm )
3380 pForm.reset(new SwForm( pBase->GetTOXForm() ));
3382 SwFormTokens aPattern = pForm->GetPattern(nStyleLevel);
3383 SwFormTokens::iterator aIt =
3384 find_if(aPattern.begin(), aPattern.end(),
3385 SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
3387 if ( aIt != aPattern.end() )
3389 SwFormToken aNumberEntrySeparator( TOKEN_TEXT );
3390 aNumberEntrySeparator.sText = " ";
3391 aPattern.insert( ++aIt, aNumberEntrySeparator );
3392 pForm->SetPattern( nStyleLevel, std::move(aPattern) );
3397 if ( pForm )
3399 pBase->SetTOXForm( *pForm );
3403 if (eCreateFrom != SwTOXElement::NONE)
3404 pBase->SetCreate(eCreateFrom);
3405 EnsureMaxLevelForTemplates(*pBase);
3407 break;
3408 case TOX_ILLUSTRATIONS:
3410 if( eCreateFrom == SwTOXElement::NONE )
3411 eCreateFrom = SwTOXElement::Sequence;
3412 pBase->SetCreate( eCreateFrom );
3415 We don't know until here if we are an illustration
3416 or not, and so have being used a TOX_CONTENT so far
3417 which has 10 levels, while TOX has only two, this
3418 level is set only in the constructor of SwForm, so
3419 create a new one and copy over anything that could
3420 be set in the old one, and remove entries from the
3421 pattern which do not apply to illustration indices
3423 SwForm aOldForm( pBase->GetTOXForm() );
3424 SwForm aNewForm( eType );
3425 sal_uInt16 nNewEnd = aNewForm.GetFormMax()-1;
3427 // #i21237#
3428 for(sal_uInt16 nLevel = 1; nLevel <= nNewEnd; ++nLevel)
3430 SwFormTokens aPattern = aOldForm.GetPattern(nLevel);
3431 SwFormTokens::iterator new_end =
3432 remove_if(aPattern.begin(), aPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
3433 aPattern.erase(new_end, aPattern.end() ); // table index imported with wrong page number format
3434 aForm.SetPattern( nLevel, std::move(aPattern) );
3435 aForm.SetTemplate( nLevel, aOldForm.GetTemplate(nLevel) );
3438 pBase->SetTOXForm( aNewForm );
3440 break;
3441 default:
3442 OSL_ENSURE(false, "Unhandled toc options!");
3443 break;
3446 break;
3447 case TOX_USER:
3448 break;
3449 default:
3450 OSL_ENSURE(false, "Unhandled toc options!");
3451 break;
3452 } // ToxBase fertig
3454 // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX
3455 pBase->AdjustTabStops( m_rDoc );
3457 //#i10028# inserting a toc implicitly acts like a parabreak in word and writer
3458 if ( m_pPaM->End() &&
3459 m_pPaM->End()->GetNode().GetTextNode() &&
3460 m_pPaM->End()->GetNode().GetTextNode()->Len() != 0 )
3462 m_bCareFirstParaEndInToc = true;
3465 if (m_pPaM->GetPoint()->GetContentIndex())
3466 AppendTextNode(*m_pPaM->GetPoint());
3468 const SwPosition* pPos = m_pPaM->GetPoint();
3470 SwFltTOX aFltTOX( pBase );
3472 // test if there is already a break item on this node
3473 if(SwContentNode* pNd = pPos->GetNode().GetContentNode())
3475 const SfxItemSet* pSet = pNd->GetpSwAttrSet();
3476 if( pSet )
3478 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
3479 aFltTOX.SetHadBreakItem(true);
3480 if (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false))
3481 aFltTOX.SetHadPageDescItem(true);
3485 //Will there be a new pagebreak at this position (don't know what type
3486 //until later)
3487 if (m_aSectionManager.WillHavePageDescHere(pPos->GetNode()))
3488 aFltTOX.SetHadPageDescItem(true);
3490 // Set start in stack
3491 m_xReffedStck->NewAttr( *pPos, aFltTOX );
3493 m_rDoc.InsertTableOf(*m_pPaM->GetPoint(), aFltTOX.GetBase());
3495 //The TOC field representation contents should be inserted into TOC section, but not after TOC section.
3496 //So we need update the document position when loading TOC representation and after loading TOC;
3497 m_oPosAfterTOC.emplace(*m_pPaM, m_pPaM);
3498 (*m_pPaM).Move(fnMoveBackward);
3499 SwPaM aRegion(*m_pPaM, m_pPaM);
3501 OSL_ENSURE(SwDoc::GetCurTOX(*aRegion.GetPoint()), "Misunderstood how toc works");
3502 if (SwTOXBase* pBase2 = SwDoc::GetCurTOX(*aRegion.GetPoint()))
3504 pBase2->SetMSTOCExpression(rStr);
3506 if ( nIndexCols > 1 )
3508 // Set the column number for index
3509 SfxItemSetFixed<RES_COL, RES_COL> aSet( m_rDoc.GetAttrPool() );
3510 SwFormatCol aCol;
3511 aCol.Init( nIndexCols, 708, USHRT_MAX );
3512 aSet.Put( aCol );
3513 pBase2->SetAttrSet( aSet );
3516 // inserting a toc inserts a section before this point, so adjust pos
3517 // for future page/section segment insertion
3518 m_aSectionManager.PrependedInlineNode( *m_oPosAfterTOC->GetPoint(), aRegion.GetPointNode() );
3521 // Set end in stack
3522 m_xReffedStck->SetAttr( *pPos, RES_FLTR_TOX );
3524 if (!m_aApos.back()) //a para end in apo doesn't count
3525 m_bWasParaEnd = true;
3527 //Return FLD_TEXT, instead of FLD_OK
3528 //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed
3529 //FLD_OK means the current field loading is finished. The rest part should be ignored.
3530 return eF_ResT::TEXT;
3533 eF_ResT SwWW8ImplReader::Read_F_Shape(WW8FieldDesc* /*pF*/, OUString& /*rStr*/)
3536 #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring
3537 to be ignored followed by a 0x1 with an empty drawing. Detect in inserting
3538 the drawing that we are in the Shape field and respond accordingly
3540 return eF_ResT::TEXT;
3543 eF_ResT SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc* /*pF*/, OUString& rStr )
3545 OUString sURL, sTarget, sMark;
3547 //HYPERLINK "filename" [switches]
3548 rStr = comphelper::string::stripEnd(rStr, 1);
3550 bool bOptions = false;
3551 WW8ReadFieldParams aReadParam( rStr );
3552 for (;;)
3554 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3555 if ( nRet==-1 )
3556 break;
3557 switch( nRet )
3559 case -2:
3560 if (sURL.isEmpty() && !bOptions)
3561 sURL = ConvertFFileName(aReadParam.GetResult());
3562 break;
3564 case 'n':
3565 sTarget = "_blank";
3566 bOptions = true;
3567 break;
3569 case 'l':
3570 bOptions = true;
3571 if ( aReadParam.SkipToNextToken()==-2 )
3573 sMark = aReadParam.GetResult();
3574 if( sMark.endsWith("\""))
3576 sMark = sMark.copy( 0, sMark.getLength() - 1 );
3578 // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention
3579 if ( IsTOCBookmarkName( sMark ) )
3581 sMark = EnsureTOCBookmarkName(sMark);
3582 // track <sMark> as referenced TOC bookmark.
3583 m_xReffedStck->m_aReferencedTOCBookmarks.insert( sMark );
3586 if (m_bLoadingTOXCache)
3588 m_bLoadingTOXHyperlink = true; //on loading a TOC field nested hyperlink field
3591 break;
3592 case 't':
3593 bOptions = true;
3594 if ( aReadParam.SkipToNextToken()==-2 )
3595 sTarget = aReadParam.GetResult();
3596 break;
3597 case 'h':
3598 case 'm':
3599 OSL_ENSURE( false, "Analysis still missing - unknown data" );
3600 [[fallthrough]];
3601 case 's': //worthless fake anchor option
3602 bOptions = true;
3603 break;
3607 // use the result
3608 OSL_ENSURE(!sURL.isEmpty() || !sMark.isEmpty(), "WW8: Empty URL");
3610 if( !sMark.isEmpty() )
3611 sURL += "#" + sMark;
3613 SwFormatINetFormat aURL(sURL, sTarget);
3614 // If on loading TOC field, change the default style into the "index link"
3615 if (m_bLoadingTOXCache)
3617 OUString sLinkStyle("Index Link");
3618 sal_uInt16 nPoolId =
3619 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
3620 aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId );
3621 aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
3624 //As an attribute this needs to be closed, and that'll happen from
3625 //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms
3626 //between the start and begin, their hyperlinks will be set at that time
3627 //as well.
3628 m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
3629 return eF_ResT::TEXT;
3632 static void lcl_ImportTox(SwDoc &rDoc, SwPaM const &rPaM, const OUString &rStr, bool bIdx)
3634 TOXTypes eTox = ( !bIdx ) ? TOX_CONTENT : TOX_INDEX; // Default
3636 sal_uInt16 nLevel = 1;
3638 OUString sFieldText;
3639 WW8ReadFieldParams aReadParam(rStr);
3640 for (;;)
3642 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3643 if ( nRet==-1 )
3644 break;
3645 switch( nRet )
3647 case -2:
3648 if( sFieldText.isEmpty() )
3650 // PrimaryKey without ":", 2nd after
3651 sFieldText = aReadParam.GetResult();
3653 break;
3655 case 'f':
3656 if ( aReadParam.GoToTokenParam() )
3658 const OUString sParams( aReadParam.GetResult() );
3659 if( sParams[0]!='C' && sParams[0]!='c' )
3660 eTox = TOX_USER;
3662 break;
3664 case 'l':
3665 if ( aReadParam.GoToTokenParam() )
3667 const OUString sParams( aReadParam.GetResult() );
3668 // if NO String just ignore the \l
3669 if( !sParams.isEmpty() && sParams[0]>'0' && sParams[0]<='9' )
3671 nLevel = o3tl::narrowing<sal_uInt16>(sParams.toInt32());
3674 break;
3678 OSL_ENSURE( rDoc.GetTOXTypeCount( eTox ), "Doc.GetTOXTypeCount() == 0 :-(" );
3680 const SwTOXType* pT = rDoc.GetTOXType( eTox, 0 );
3681 SwTOXMark aM( pT );
3683 if( eTox != TOX_INDEX )
3684 aM.SetLevel( nLevel );
3685 else
3687 sal_Int32 nFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM );
3688 if( -1 != nFnd ) // it exist levels
3690 aM.SetPrimaryKey( sFieldText.copy( 0, nFnd ) );
3691 sal_Int32 nScndFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM, nFnd+1 );
3692 if( -1 != nScndFnd )
3694 aM.SetSecondaryKey( sFieldText.copy( nFnd+1, nScndFnd - nFnd - 1 ));
3695 nFnd = nScndFnd;
3697 sFieldText = sFieldText.copy( nFnd+1 );
3701 if (!sFieldText.isEmpty())
3703 aM.SetAlternativeText( sFieldText );
3704 rDoc.getIDocumentContentOperations().InsertPoolItem( rPaM, aM );
3708 void SwWW8ImplReader::ImportTox( int nFieldId, const OUString& aStr )
3710 bool bIdx = (nFieldId != 9);
3711 lcl_ImportTox(m_rDoc, *m_pPaM, aStr, bIdx);
3714 void SwWW8ImplReader::Read_FieldVanish( sal_uInt16, const sal_uInt8*, short nLen )
3716 //Meaningless in a style
3717 if (m_pCurrentColl || !m_xPlcxMan)
3718 return;
3720 const int nChunk = 64; //number of characters to read at one time
3722 // Careful: MEMICMP doesn't work with fieldnames including umlauts!
3723 const static char *aFieldNames[] = { "\x06""INHALT", "\x02""XE", // dt.
3724 "\x02""TC" }; // us
3725 const static sal_uInt8 aFieldId[] = { 9, 4, 9 };
3727 if( nLen < 0 )
3729 m_bIgnoreText = false;
3730 return;
3733 // our method was called from
3734 // ''Skip attributes of field contents'' loop within ReadTextAttr()
3735 if( m_bIgnoreText )
3736 return;
3738 m_bIgnoreText = true;
3739 sal_uInt64 nOldPos = m_pStrm->Tell();
3741 WW8_CP nStartCp = m_xPlcxMan->Where() + m_xPlcxMan->GetCpOfs();
3743 OUString sFieldName;
3744 sal_Int32 nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sFieldName, nStartCp,
3745 nChunk, m_eStructCharSet );
3746 nStartCp+=nFieldLen;
3748 sal_Int32 nC = 0;
3749 //If the first chunk did not start with a field start then
3750 //reset the stream position and give up
3751 if( !nFieldLen || sFieldName[nC]!=0x13 ) // Field Start Mark
3753 // If Field End Mark found
3754 if( nFieldLen && sFieldName[nC]==0x15 )
3755 m_bIgnoreText = false;
3756 m_pStrm->Seek( nOldPos );
3757 return; // no field found
3760 sal_Int32 nFnd;
3761 //If this chunk does not contain a field end, keep reading chunks
3762 //until we find one, or we run out of text,
3763 for (;;)
3765 nFnd = sFieldName.indexOf(0x15);
3766 //found field end, we can stop now
3767 if (nFnd != -1)
3768 break;
3769 OUString sTemp;
3770 nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sTemp,
3771 nStartCp, nChunk, m_eStructCharSet );
3772 sFieldName+=sTemp;
3773 nStartCp+=nFieldLen;
3774 if (!nFieldLen)
3775 break;
3778 m_pStrm->Seek( nOldPos );
3780 //if we have no 0x15 give up, otherwise erase everything from the 0x15
3781 //onwards
3782 if (nFnd<0)
3783 return;
3785 sFieldName = sFieldName.copy(0, nFnd);
3787 nC++;
3788 while ( sFieldName[nC]==' ' )
3789 nC++;
3791 for( int i = 0; i < 3; i++ )
3793 const char* pName = aFieldNames[i];
3794 const sal_Int32 nNameLen = static_cast<sal_Int32>(*pName++);
3795 if( sFieldName.matchIgnoreAsciiCaseAsciiL( pName, nNameLen, nC ) )
3797 ImportTox( aFieldId[i], sFieldName.copy( nC + nNameLen ) );
3798 break; // no duplicates allowed
3801 m_bIgnoreText = true;
3802 m_pStrm->Seek( nOldPos );
3805 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */