build fix
[LibreOffice.git] / sw / source / filter / ww8 / ww8par5.cxx
blob853ec7c2619a0a7a3000cdc41c59e8faa2a528b5
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>
22 #include <sal/types.h>
23 #include <tools/solar.h>
24 #include <comphelper/processfactory.hxx>
25 #include <comphelper/storagehelper.hxx>
26 #include <comphelper/string.hxx>
27 #include <comphelper/simplefileaccessinteraction.hxx>
28 #include <sot/storinfo.hxx>
29 #include <com/sun/star/embed/XStorage.hpp>
30 #include <com/sun/star/embed/ElementModes.hpp>
31 #include <com/sun/star/embed/XTransactedObject.hpp>
32 #include <com/sun/star/io/XStream.hpp>
33 #include <com/sun/star/task/InteractionHandler.hpp>
35 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
36 #include <svl/urihelper.hxx>
37 #include <svl/zforlist.hxx>
38 #include <svl/zformat.hxx>
39 #include <sfx2/linkmgr.hxx>
41 #include <ucbhelper/content.hxx>
42 #include <ucbhelper/commandenvironment.hxx>
44 #include <com/sun/star/i18n/ScriptType.hpp>
45 #include <hintids.hxx>
46 #include <editeng/fontitem.hxx>
47 #include <editeng/fhgtitem.hxx>
48 #include <editeng/langitem.hxx>
49 #include <fmtfld.hxx>
50 #include <fmtanchr.hxx>
51 #include <pam.hxx>
52 #include <doc.hxx>
53 #include <IDocumentFieldsAccess.hxx>
54 #include <IDocumentState.hxx>
55 #include <charatr.hxx>
56 #include <flddat.hxx>
57 #include <docufld.hxx>
58 #include <reffld.hxx>
59 #include <IMark.hxx>
60 #include <expfld.hxx>
61 #include <dbfld.hxx>
62 #include <usrfld.hxx>
63 #include <tox.hxx>
64 #include <section.hxx>
65 #include <ndtxt.hxx>
66 #include <fmtinfmt.hxx>
67 #include <chpfld.hxx>
68 #include <ftnidx.hxx>
69 #include <txtftn.hxx>
70 #include <viewsh.hxx>
71 #include <shellres.hxx>
72 #include <fmtruby.hxx>
73 #include <charfmt.hxx>
74 #include <txtatr.hxx>
75 #include <breakit.hxx>
76 #include <fmtclds.hxx>
77 #include <pagedesc.hxx>
78 #include <SwStyleNameMapper.hxx>
80 #include "ww8scan.hxx"
81 #include "ww8par.hxx"
82 #include "ww8par2.hxx"
83 #include "writerhelper.hxx"
84 #include "fields.hxx"
85 #include <unotools/fltrcfg.hxx>
86 #include <xmloff/odffields.hxx>
88 #include <algorithm>
90 #define MAX_FIELDLEN 64000
92 #define WW8_TOX_LEVEL_DELIM ':'
94 using namespace ::com::sun::star;
95 using namespace msfilter::util;
96 using namespace sw::util;
97 using namespace sw::mark;
98 using namespace std; // #i24377#
99 using namespace nsSwDocInfoSubType;
101 // Bookmarks
102 namespace
104 // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention
105 bool IsTOCBookmarkName(const OUString& rName)
107 return rName.startsWith("_Toc") || rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc");
110 OUString EnsureTOCBookmarkName(const OUString& rName)
112 OUString sTmp = rName;
113 if ( IsTOCBookmarkName ( rName ) )
115 if ( ! rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
116 sTmp = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName;
118 return sTmp;
122 long SwWW8ImplReader::Read_Book(WW8PLCFManResult*)
124 // should also work via pRes.nCo2OrIdx
125 WW8PLCFx_Book* pB = m_pPlcxMan->GetBook();
126 if( !pB )
128 OSL_ENSURE( pB, "WW8PLCFx_Book - Pointer does not exist" );
129 return 0;
132 eBookStatus eB = pB->GetStatus();
133 if (eB & BOOK_IGNORE)
134 return 0; // ignore bookmark
136 if (pB->GetIsEnd())
138 m_pReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true,
139 pB->GetHandle(), (eB & BOOK_FIELD)!=0);
140 return 0;
143 // "_Hlt*" are unnecessary
144 const OUString* pName = pB->GetName();
145 // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available.
146 // So the hidden bookmarks inside for hyperlink jumping also should be kept.
147 if ( !pName ||
148 pName->startsWithIgnoreAsciiCase( "_Hlt" ) )
150 return 0;
153 // do NOT call ToUpper as the bookmark name can also be a hyperlink target!
155 OUString aVal;
156 if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::BOOK_TO_VAR_REF ) )
158 // set variable for translation bookmark
159 long nLen = pB->GetLen();
160 if( nLen > MAX_FIELDLEN )
161 nLen = MAX_FIELDLEN;
163 long nOldPos = m_pStrm->Tell();
164 m_pSBase->WW8ReadString( *m_pStrm, aVal, pB->GetStartPos(), nLen,
165 m_eStructCharSet );
166 m_pStrm->Seek( nOldPos );
168 // now here the implementation of the old "QuoteString" and
169 // I hope with a better performance as before. It's also only
170 // needed if the filterflags say we will convert bookmarks
171 // to SetExpFields! And this the exception!
173 OUString sHex("\\x");
174 bool bSetAsHex;
175 bool bAllowCr = SwFltGetFlag(m_nFieldFlags,
176 SwFltControlStack::ALLOW_FLD_CR);
178 for( sal_Int32 nI = 0;
179 nI < aVal.getLength() && aVal.getLength() < (MAX_FIELDLEN - 4);
180 ++nI )
182 const sal_Unicode cChar = aVal[nI];
183 switch( cChar )
185 case 0x0b:
186 case 0x0c:
187 case 0x0d:
188 if( bAllowCr )
190 aVal = aVal.replaceAt( nI, 1, "\n" );
191 bSetAsHex = false;
193 else
194 bSetAsHex = true;
195 break;
197 case 0xFE:
198 case 0xFF:
199 bSetAsHex = true;
200 break;
202 default:
203 bSetAsHex = 0x20 > cChar;
204 break;
207 if( bSetAsHex )
209 //all Hex-Numbers with \x before
210 OUString sTmp( sHex );
211 if( cChar < 0x10 )
212 sTmp += "0";
213 sTmp += OUString::number( cChar, 16 );
214 aVal = aVal.replaceAt( nI, 1 , sTmp );
215 nI += sTmp.getLength() - 1;
219 if ( aVal.getLength() > (MAX_FIELDLEN - 4))
220 aVal = aVal.copy( 0, MAX_FIELDLEN - 4 );
223 //e.g. inserting bookmark around field result, so we need to put
224 //it around the entire writer field, as we don't have the separation
225 //of field and field result of word, see #i16941#
226 SwPosition aStart(*m_pPaM->GetPoint());
227 if (!m_aFieldStack.empty())
229 const WW8FieldEntry &rTest = m_aFieldStack.back();
230 aStart = rTest.maStartPos;
233 const OUString sOrigName = BookmarkToWriter(*pName);
234 m_pReffedStck->NewAttr( aStart,
235 SwFltBookmark( EnsureTOCBookmarkName( sOrigName ), aVal, pB->GetHandle(), IsTOCBookmarkName( sOrigName ) ));
236 return 0;
239 long SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult*)
241 if (WW8PLCFx_AtnBook* pAtnBook = m_pPlcxMan->GetAtnBook())
243 if (pAtnBook->getIsEnd())
244 m_pReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_ANNOTATIONMARK, true, pAtnBook->getHandle());
245 else
246 m_pReffedStck->NewAttr(*m_pPaM->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK, pAtnBook->getHandle()));
248 return 0;
251 long SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult*)
253 if (WW8PLCFx_FactoidBook* pFactoidBook = m_pPlcxMan->GetFactoidBook())
255 if (pFactoidBook->getIsEnd())
256 m_pReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_RDFMARK, true, pFactoidBook->getHandle());
257 else
259 SwFltRDFMark aMark;
260 aMark.SetHandle(pFactoidBook->getHandle());
261 GetSmartTagInfo(aMark);
262 m_pReffedStck->NewAttr(*m_pPaM->GetPoint(), aMark);
265 return 0;
268 // general help methods to separate parameters
270 /// translate FieldParameter names into the system character set and
271 /// at the same time, double backslashes are converted into single ones
272 OUString SwWW8ImplReader::ConvertFFileName(const OUString& rOrg)
274 OUString aName = rOrg;
275 aName = aName.replaceAll("\\\\", "\\");
276 aName = aName.replaceAll("%20", " ");
278 // remove attached quotation marks
279 if (aName.endsWith("\""))
280 aName = aName.copy(0, aName.getLength()-1);
282 // Need the more sophisticated url converter.
283 if (!aName.isEmpty())
284 aName = URIHelper::SmartRel2Abs(
285 INetURLObject(m_sBaseURL), aName, Link<OUString *, bool>(), false);
287 return aName;
290 namespace
292 /// translate FieldParameter names into the
293 /// system character set and makes them uppercase
294 void ConvertUFName( OUString& rName )
296 rName = GetAppCharClass().uppercase( rName );
300 static void lcl_ConvertSequenceName(OUString& rSequenceName)
302 ConvertUFName(rSequenceName);
303 if ('0' <= rSequenceName[0] && '9' >= rSequenceName[0])
304 rSequenceName = "_" + rSequenceName;
307 // FindParaStart() finds 1st Parameter that follows '\' and cToken
308 // and returns start of this parameter or -1
309 sal_Int32 FindParaStart( const OUString& rStr, sal_Unicode cToken, sal_Unicode cToken2 )
311 bool bStr = false; // ignore inside a string
313 for( sal_Int32 nBuf = 0; nBuf+1 < rStr.getLength(); nBuf++ )
315 if( rStr[ nBuf ] == '"' )
316 bStr = !bStr;
318 if( !bStr
319 && rStr[ nBuf ] == '\\'
320 && ( rStr[ nBuf + 1 ] == cToken
321 || rStr[ nBuf + 1 ] == cToken2 ) )
323 nBuf += 2;
324 // skip spaces between cToken and its parameters
325 while( nBuf < rStr.getLength()
326 && rStr[ nBuf ] == ' ' )
327 nBuf++;
328 // return start of parameters
329 return nBuf < rStr.getLength() ? nBuf : -1;
332 return -1;
335 // FindPara() findet den ersten Parameter mit '\' und cToken. Es wird
336 // ein neuer String allokiert ( der vom Aufrufer deallokiert werden muss )
337 // und alles, was zum Parameter gehoert, wird in ihm zurueckgeliefert.
338 OUString FindPara( const OUString& rStr, sal_Unicode cToken, sal_Unicode cToken2 )
340 sal_Int32 n2; // end
341 sal_Int32 n = FindParaStart( rStr, cToken, cToken2 ); // start
342 if( n == -1)
343 return OUString();
345 if( rStr[ n ] == '"'
346 || rStr[ n ] == 132 )
347 { // Anfuehrungszeichen vor Para
348 n++; // Anfuehrungszeichen ueberlesen
349 n2 = n; // ab hier nach Ende suchen
350 while( n2 < rStr.getLength()
351 && rStr[ n2 ] != 147
352 && rStr[ n2 ] != '"' )
353 n2++; // Ende d. Paras suchen
355 else
356 { // keine Anfuehrungszeichen
357 n2 = n; // ab hier nach Ende suchen
358 while( n2 < rStr.getLength()
359 && rStr[ n2 ] != ' ' )
360 n2++; // Ende d. Paras suchen
362 return rStr.copy( n, n2-n );
365 static SvxExtNumType GetNumTypeFromName(const OUString& rStr,
366 bool bAllowPageDesc = false)
368 SvxExtNumType eTyp = bAllowPageDesc ? SVX_NUM_PAGEDESC : SVX_NUM_ARABIC;
369 if( rStr.startsWithIgnoreAsciiCase( "Arabi" ) ) // Arabisch, Arabic
370 eTyp = SVX_NUM_ARABIC;
371 else if( rStr.startsWith( "misch" ) ) // r"omisch
372 eTyp = SVX_NUM_ROMAN_LOWER;
373 else if( rStr.startsWith( "MISCH" ) ) // R"OMISCH
374 eTyp = SVX_NUM_ROMAN_UPPER;
375 else if( rStr.startsWithIgnoreAsciiCase( "alphabeti" ) )// alphabetisch, alphabetic
376 eTyp = ( rStr[0] == 'A' )
377 ? SVX_NUM_CHARS_UPPER_LETTER_N
378 : SVX_NUM_CHARS_LOWER_LETTER_N;
379 else if( rStr.startsWithIgnoreAsciiCase( "roman" ) ) // us
380 eTyp = ( rStr[0] == 'R' )
381 ? SVX_NUM_ROMAN_UPPER
382 : SVX_NUM_ROMAN_LOWER;
383 return eTyp;
386 static SvxExtNumType GetNumberPara(const OUString& rStr, bool bAllowPageDesc = false)
388 OUString s( FindPara( rStr, '*', '*' ) ); // Ziffernart
389 SvxExtNumType aType = GetNumTypeFromName( s, bAllowPageDesc );
390 return aType;
393 bool SwWW8ImplReader::ForceFieldLanguage(SwField &rField, sal_uInt16 nLang)
395 bool bRet(false);
397 const SvxLanguageItem *pLang =
398 static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE));
399 OSL_ENSURE(pLang, "impossible");
400 sal_uInt16 nDefault = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
402 if (nLang != nDefault)
404 rField.SetAutomaticLanguage(false);
405 rField.SetLanguage(nLang);
406 bRet = true;
409 return bRet;
412 OUString GetWordDefaultDateStringAsUS(SvNumberFormatter* pFormatter, sal_uInt16 nLang)
414 //Get the system date in the correct final language layout, convert to
415 //a known language and modify the 2 digit year part to be 4 digit, and
416 //convert back to the correct language layout.
417 const sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, nLang);
419 SvNumberformat aFormat = const_cast<SvNumberformat &>
420 (*(pFormatter->GetEntry(nIndex)));
421 aFormat.ConvertLanguage(*pFormatter, nLang, LANGUAGE_ENGLISH_US);
423 OUString sParams(aFormat.GetFormatstring());
424 // #i36594#
425 // Fix provided by mloiseleur@openoffice.org.
426 // A default date can have already 4 year digits, in some case
427 const sal_Int32 pos = sParams.indexOf("YYYY");
428 if ( pos == -1 )
430 sParams = sParams.replaceFirst("YY", "YYYY");
432 return sParams;
435 short SwWW8ImplReader::GetTimeDatePara(OUString& rStr, sal_uInt32& rFormat,
436 sal_uInt16 &rLang, int nWhichDefault, bool bHijri)
438 bool bRTL = false;
439 if (m_pPlcxMan && !m_bVer67)
441 const sal_uInt8 *pResult = m_pPlcxMan->HasCharSprm(0x85A);
442 if (pResult && *pResult)
443 bRTL = true;
445 RES_CHRATR eLang = bRTL ? RES_CHRATR_CTL_LANGUAGE : RES_CHRATR_LANGUAGE;
446 const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr( static_cast< sal_uInt16 >(eLang)));
447 OSL_ENSURE(pLang, "impossible");
448 rLang = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
450 SvNumberFormatter* pFormatter = m_rDoc.GetNumberFormatter();
451 OUString sParams( FindPara( rStr, '@', '@' ) );// Date/Time
452 if (sParams.isEmpty())
454 bool bHasTime = false;
455 switch (nWhichDefault)
457 case ww::ePRINTDATE:
458 case ww::eSAVEDATE:
459 sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
460 sParams += " HH:MM:SS AM/PM";
461 bHasTime = true;
462 break;
463 case ww::eCREATEDATE:
464 sParams += "DD/MM/YYYY HH:MM:SS";
465 bHasTime = true;
466 break;
467 default:
468 case ww::eDATE:
469 sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
470 break;
473 if (bHijri)
474 sParams = "[~hijri]" + sParams;
476 sal_Int32 nCheckPos = 0;
477 short nType = css::util::NumberFormat::DEFINED;
478 rFormat = 0;
480 OUString sTemp(sParams);
481 pFormatter->PutandConvertEntry(sTemp, nCheckPos, nType, rFormat,
482 LANGUAGE_ENGLISH_US, rLang);
483 sParams = sTemp;
485 return bHasTime ? css::util::NumberFormat::DATETIME : css::util::NumberFormat::DATE;
488 sal_uLong nFormatIdx =
489 sw::ms::MSDateTimeFormatToSwFormat(sParams, pFormatter, rLang, bHijri,
490 GetFib().m_lid);
491 short nNumFormatType = css::util::NumberFormat::UNDEFINED;
492 if (nFormatIdx)
493 nNumFormatType = pFormatter->GetType(nFormatIdx);
494 rFormat = nFormatIdx;
496 return nNumFormatType;
499 // Felder
501 // Am Ende des Einlesens entsprechende Felder updaten ( z.Zt. die Referenzen )
502 void SwWW8ImplReader::UpdateFields()
504 m_rDoc.getIDocumentState().SetUpdateExpFieldStat(true); // JP: neu fuer alles wichtige
505 m_rDoc.SetInitDBFields(true); // Datenbank-Felder auch
508 sal_uInt16 SwWW8ImplReader::End_Field()
510 sal_uInt16 nRet = 0;
511 WW8PLCFx_FLD* pF = m_pPlcxMan->GetField();
512 OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer nicht da");
513 WW8_CP nCP = 0;
514 if (!pF || !pF->EndPosIsFieldEnd(nCP))
515 return nRet;
517 const SvtFilterOptions &rOpt = SvtFilterOptions::Get();
518 bool bUseEnhFields = rOpt.IsUseEnhancedFields();
520 OSL_ENSURE(!m_aFieldStack.empty(), "Empty field stack\n");
521 if (!m_aFieldStack.empty())
524 only hyperlinks currently need to be handled like this, for the other
525 cases we have inserted a field not an attribute with an unknown end
526 point
528 nRet = m_aFieldStack.back().mnFieldId;
529 switch (nRet)
531 case 70:
532 if (bUseEnhFields && m_pPaM!=nullptr && m_pPaM->GetPoint()!=nullptr) {
533 SwPosition aEndPos = *m_pPaM->GetPoint();
534 SwPaM aFieldPam( m_aFieldStack.back().GetPtNode(), m_aFieldStack.back().GetPtContent(), aEndPos.nNode, aEndPos.nContent.GetIndex());
535 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
536 IFieldmark *pFieldmark = dynamic_cast<IFieldmark*>( pMarksAccess->makeFieldBookmark(
537 aFieldPam, m_aFieldStack.back().GetBookmarkName(), ODF_FORMTEXT ) );
538 OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
539 if (pFieldmark!=nullptr) {
540 const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
541 pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
544 break;
545 // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field
546 case 13://TOX
547 case 8://index
548 if (m_bLoadingTOXCache)
550 if (m_nEmbeddedTOXLevel > 0)
552 JoinNode(*m_pPaM);
553 --m_nEmbeddedTOXLevel;
555 else
557 m_aTOXEndCps.insert(nCP);
558 m_bLoadingTOXCache = false;
559 if ( m_pPaM->End() &&
560 m_pPaM->End()->nNode.GetNode().GetTextNode() &&
561 m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() == 0 )
563 JoinNode(*m_pPaM);
565 else
567 m_bCareLastParaEndInToc = true;
570 if (m_pPosAfterTOC)
572 *m_pPaM = *m_pPosAfterTOC;
573 m_pPosAfterTOC.reset();
577 break;
578 case 37: //REF
579 if (m_bLoadingTOXCache && !m_bLoadingTOXHyperlink)
581 m_pCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_TXTATR_INETFMT);
583 break;
584 case 88:
585 if (m_bLoadingTOXHyperlink)
586 m_bLoadingTOXHyperlink = false;
587 m_pCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_INETFMT);
588 break;
589 case 36:
590 case 68:
591 //Move outside the section associated with this type of field
592 *m_pPaM->GetPoint() = m_aFieldStack.back().maStartPos;
593 break;
594 default:
595 OUString aCode = m_aFieldStack.back().GetBookmarkCode();
596 if ( !aCode.isEmpty() )
598 // Unhandled field with stored code
599 SwPosition aEndPos = *m_pPaM->GetPoint();
600 SwPaM aFieldPam(
601 m_aFieldStack.back().GetPtNode(), m_aFieldStack.back().GetPtContent(),
602 aEndPos.nNode, aEndPos.nContent.GetIndex());
604 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
606 IFieldmark* pFieldmark = pMarksAccess->makeFieldBookmark(
607 aFieldPam,
608 m_aFieldStack.back().GetBookmarkName(),
609 ODF_UNHANDLED );
610 if ( pFieldmark )
612 // adapt redline positions to inserted field mark start
613 // dummy char (assume not necessary for end dummy char)
614 m_pRedlineStack->MoveAttrs(*aFieldPam.Start());
615 const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
616 pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
617 OUString sFieldId = OUString::number( m_aFieldStack.back().mnFieldId );
618 pFieldmark->GetParameters()->insert(
619 std::pair< OUString, uno::Any > (
620 ODF_ID_PARAM,
621 uno::makeAny( sFieldId ) ) );
622 pFieldmark->GetParameters()->insert(
623 std::pair< OUString, uno::Any > (
624 ODF_CODE_PARAM,
625 uno::makeAny( aCode ) ) );
627 if ( m_aFieldStack.back().mnObjLocFc > 0 )
629 // Store the OLE object as an internal link
630 OUString sOleId('_');
631 sOleId += OUString::number( m_aFieldStack.back().mnObjLocFc );
633 tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(OUString(SL::aObjectPool));
634 tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( sOleId, StreamMode::READ );
636 // Store it now!
637 uno::Reference< embed::XStorage > xDocStg = GetDoc().GetDocStorage();
638 if (xDocStg.is())
640 uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement(
641 "OLELinks", embed::ElementModes::WRITE );
642 tools::SvRef<SotStorage> xObjDst = SotStorage::OpenOLEStorage( xOleStg, sOleId );
644 if ( xObjDst.Is() )
646 xSrc1->CopyTo( xObjDst.get() );
648 if ( !xObjDst->GetError() )
649 xObjDst->Commit();
652 uno::Reference< embed::XTransactedObject > xTransact( xOleStg, uno::UNO_QUERY );
653 if ( xTransact.is() )
654 xTransact->commit();
657 // Store the OLE Id as a parameter
658 pFieldmark->GetParameters()->insert(
659 std::pair< OUString, uno::Any >(
660 ODF_OLE_PARAM, uno::makeAny( OUString( sOleId ) ) ) );
665 break;
667 m_aFieldStack.pop_back();
669 return nRet;
672 bool AcceptableNestedField(sal_uInt16 nFieldCode)
674 switch (nFieldCode)
676 case 8: // allow recursive field in TOC...
677 case 13: // allow recursive field in TOC...
678 case 36:
679 case 68:
680 case 79:
681 case 88:
682 // Accept AutoTextList field as nested field.
683 // Thus, the field result is imported as plain text.
684 case 89:
685 return true;
686 default:
687 return false;
691 WW8FieldEntry::WW8FieldEntry(SwPosition &rPos, sal_uInt16 nFieldId) throw()
692 : maStartPos(rPos), mnFieldId(nFieldId), mnObjLocFc(0)
696 WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry &rOther) throw()
697 : maStartPos(rOther.maStartPos), mnFieldId(rOther.mnFieldId), mnObjLocFc(rOther.mnObjLocFc)
701 void WW8FieldEntry::Swap(WW8FieldEntry &rOther) throw()
703 std::swap(maStartPos, rOther.maStartPos);
704 std::swap(mnFieldId, rOther.mnFieldId);
707 WW8FieldEntry &WW8FieldEntry::operator=(const WW8FieldEntry &rOther) throw()
709 WW8FieldEntry aTemp(rOther);
710 Swap(aTemp);
711 return *this;
715 void WW8FieldEntry::SetBookmarkName(const OUString& bookmarkName)
717 msBookmarkName=bookmarkName;
720 void WW8FieldEntry::SetBookmarkType(const OUString& bookmarkType)
722 msMarkType=bookmarkType;
725 void WW8FieldEntry::SetBookmarkCode(const OUString& bookmarkCode)
727 msMarkCode = bookmarkCode;
731 // Read_Field liest ein Feld ein oder, wenn es nicht gelesen werden kann,
732 // wird 0 zurueckgegeben, so dass das Feld vom Aufrufer textuell gelesen wird.
733 // Returnwert: Gesamtlaenge des Feldes ( zum UEberlesen )
734 long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes)
736 typedef eF_ResT (SwWW8ImplReader:: *FNReadField)( WW8FieldDesc*, OUString& );
737 enum Limits {eMax = 96};
738 static const FNReadField aWW8FieldTab[eMax+1] =
740 nullptr,
741 &SwWW8ImplReader::Read_F_Input,
742 nullptr,
743 &SwWW8ImplReader::Read_F_Ref, // 3
744 nullptr,
745 nullptr,
746 &SwWW8ImplReader::Read_F_Set, // 6
747 nullptr,
748 &SwWW8ImplReader::Read_F_Tox, // 8
749 nullptr,
750 &SwWW8ImplReader::Read_F_Styleref, // 10
751 nullptr,
752 &SwWW8ImplReader::Read_F_Seq, // 12
753 &SwWW8ImplReader::Read_F_Tox, // 13
754 &SwWW8ImplReader::Read_F_DocInfo, // 14
755 &SwWW8ImplReader::Read_F_DocInfo, // 15
756 &SwWW8ImplReader::Read_F_DocInfo, // 16
757 &SwWW8ImplReader::Read_F_Author, // 17
758 &SwWW8ImplReader::Read_F_DocInfo, // 18
759 &SwWW8ImplReader::Read_F_DocInfo, // 19
760 &SwWW8ImplReader::Read_F_DocInfo, // 20
761 &SwWW8ImplReader::Read_F_DocInfo, // 21
762 &SwWW8ImplReader::Read_F_DocInfo, // 22
763 &SwWW8ImplReader::Read_F_DocInfo, // 23
764 &SwWW8ImplReader::Read_F_DocInfo, // 24
765 &SwWW8ImplReader::Read_F_DocInfo, // 25
766 &SwWW8ImplReader::Read_F_Num, // 26
767 &SwWW8ImplReader::Read_F_Num, // 27
768 &SwWW8ImplReader::Read_F_Num, // 28
769 &SwWW8ImplReader::Read_F_FileName, // 29
770 &SwWW8ImplReader::Read_F_TemplName, // 30
771 &SwWW8ImplReader::Read_F_DateTime, // 31
772 &SwWW8ImplReader::Read_F_DateTime, // 32
773 &SwWW8ImplReader::Read_F_CurPage, // 33
774 nullptr,
775 nullptr,
776 &SwWW8ImplReader::Read_F_IncludeText, // 36
777 &SwWW8ImplReader::Read_F_PgRef, // 37
778 &SwWW8ImplReader::Read_F_InputVar, // 38
779 &SwWW8ImplReader::Read_F_Input, // 39
780 nullptr,
781 &SwWW8ImplReader::Read_F_DBNext, // 41
782 nullptr,
783 nullptr,
784 &SwWW8ImplReader::Read_F_DBNum, // 44
785 nullptr,
786 nullptr,
787 nullptr,
788 nullptr,
789 &SwWW8ImplReader::Read_F_Equation, // 49
790 nullptr,
791 &SwWW8ImplReader::Read_F_Macro, // 51
792 &SwWW8ImplReader::Read_F_ANumber, // 52
793 &SwWW8ImplReader::Read_F_ANumber, // 53
794 &SwWW8ImplReader::Read_F_ANumber, // 54
795 nullptr,
797 nullptr, // 56
799 &SwWW8ImplReader::Read_F_Symbol, // 57
800 &SwWW8ImplReader::Read_F_Embedd, // 58
801 &SwWW8ImplReader::Read_F_DBField, // 59
802 nullptr,
803 nullptr,
804 nullptr,
805 nullptr,
806 &SwWW8ImplReader::Read_F_DocInfo, // 64 - DOCVARIABLE
807 nullptr,
808 nullptr,
809 &SwWW8ImplReader::Read_F_IncludePicture, // 67
810 &SwWW8ImplReader::Read_F_IncludeText, // 68
811 nullptr,
812 &SwWW8ImplReader::Read_F_FormTextBox, // 70
813 &SwWW8ImplReader::Read_F_FormCheckBox, // 71
814 &SwWW8ImplReader::Read_F_NoteReference, // 72
815 nullptr, /*&SwWW8ImplReader::Read_F_Tox*/
816 nullptr,
817 nullptr,
818 nullptr,
819 nullptr,
820 nullptr,
821 nullptr,
822 nullptr,
823 nullptr,
824 nullptr,
825 &SwWW8ImplReader::Read_F_FormListBox, // 83
826 nullptr, // 84
827 &SwWW8ImplReader::Read_F_DocInfo, // 85
828 nullptr, // 86
829 &SwWW8ImplReader::Read_F_OCX, // 87
830 &SwWW8ImplReader::Read_F_Hyperlink, // 88
831 nullptr, // 89
832 nullptr, // 90
833 &SwWW8ImplReader::Read_F_HTMLControl, // 91
834 nullptr, // 92
835 nullptr, // 93
836 nullptr, // 94
837 &SwWW8ImplReader::Read_F_Shape, // 95
838 nullptr // eMax - Dummy leer Methode
840 OSL_ENSURE( ( sizeof( aWW8FieldTab ) / sizeof( *aWW8FieldTab ) == eMax+1 ),
841 "FeldFunc-Tabelle stimmt nicht" );
843 WW8PLCFx_FLD* pF = m_pPlcxMan->GetField();
844 OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer nicht da");
846 if (!pF || !pF->StartPosIsFieldStart())
847 return 0;
849 bool bNested = false;
850 if (!m_aFieldStack.empty())
852 mycFieldIter aEnd = m_aFieldStack.end();
853 for(mycFieldIter aIter = m_aFieldStack.begin(); aIter != aEnd; ++aIter)
855 bNested = !AcceptableNestedField(aIter->mnFieldId);
856 if (bNested)
857 break;
861 WW8FieldDesc aF;
862 bool bOk = pF->GetPara(pRes->nCp2OrIdx, aF);
864 OSL_ENSURE(bOk, "WW8: Bad Field!\n");
865 if (aF.nId == 33) aF.bCodeNest=false; // do not recurse into nested page fields
866 bool bCodeNest = aF.bCodeNest;
867 if ( aF.nId == 6 ) bCodeNest = false; // We can handle them and lose the inner data
869 m_aFieldStack.push_back(WW8FieldEntry(*m_pPaM->GetPoint(), aF.nId));
871 if (bNested)
872 return 0;
874 sal_uInt16 n = (aF.nId <= eMax) ? aF.nId : static_cast<sal_uInt16>(eMax);
875 sal_uInt16 nI = n / 32; // # des sal_uInt32
876 sal_uInt32 nMask = 1 << ( n % 32 ); // Maske fuer Bits
878 if (SAL_N_ELEMENTS(m_nFieldTagAlways) <= nI)
879 { // if indexes larger than 95 are needed, then a new configuration
880 // item has to be added, and nFieldTagAlways/nFieldTagBad expanded!
881 return aF.nLen;
884 if( m_nFieldTagAlways[nI] & nMask ) // Flag: Tag it
885 return Read_F_Tag( &aF ); // Resultat nicht als Text
887 if( !bOk || !aF.nId ) // Feld kaputt
888 return aF.nLen; // -> ignorieren
890 if( aF.nId > eMax - 1) // WW: Nested Field
892 if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
893 return Read_F_Tag( &aF ); // Resultat nicht als Text
894 else
895 return aF.nLen;
898 //Only one type of field (hyperlink) in drawing textboxes exists
899 if (aF.nId != 88 && m_pPlcxMan->GetDoingDrawTextBox())
900 return aF.nLen;
902 bool bHasHandler = aWW8FieldTab[aF.nId] != nullptr;
903 if (aF.nId == 10) // STYLEREF
905 // STYLEREF, by default these are not handled.
906 bHasHandler = false;
907 sal_uInt64 nOldPos = m_pStrm->Tell();
908 OUString aStr;
909 aF.nLCode = m_pSBase->WW8ReadString(*m_pStrm, aStr, m_pPlcxMan->GetCpOfs() + aF.nSCode, aF.nLCode, m_eTextCharSet);
910 m_pStrm->Seek(nOldPos);
912 WW8ReadFieldParams aReadParam(aStr);
913 sal_Int32 nRet = aReadParam.SkipToNextToken();
914 if (nRet == -2 && !aReadParam.GetResult().isEmpty())
915 // Single numeric argument: this can be handled by SwChapterField.
916 bHasHandler = rtl::isAsciiDigit(aReadParam.GetResult()[0]);
918 if (bHasHandler)
920 nRet = aReadParam.SkipToNextToken();
921 // Handle using SwChapterField only in case there is no \[a-z]
922 // switch after the field argument.
923 bHasHandler = nRet < 0 || nRet == '*';
927 // keine Routine vorhanden
928 if (bNested || !bHasHandler || bCodeNest)
930 if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
931 return Read_F_Tag( &aF ); // Resultat nicht als Text
932 // Lese nur Resultat
933 if (aF.bResNest && !AcceptableNestedField(aF.nId))
934 return aF.nLen; // Result nested -> nicht brauchbar
936 long nOldPos = m_pStrm->Tell();
937 OUString aStr;
938 aF.nLCode = m_pSBase->WW8ReadString( *m_pStrm, aStr, m_pPlcxMan->GetCpOfs()+
939 aF.nSCode, aF.nLCode, m_eTextCharSet );
940 m_pStrm->Seek( nOldPos );
942 // field codes which contain '/' or '.' are not displayed in WinWord
943 // skip if it is formula field or found space before. see #i119446, #i119585.
944 const sal_Int32 nDotPos = aStr.indexOf('.');
945 const sal_Int32 nSlashPos = aStr.indexOf('/');
946 sal_Int32 nSpacePos = aStr.indexOf( ' ', 1 );
947 if ( nSpacePos<0 )
948 nSpacePos = aStr.getLength();
950 if ( !( aStr.getLength()>1 && aStr[1]=='=') &&
951 (( nDotPos>=0 && nDotPos < nSpacePos ) ||
952 ( nSlashPos>=0 && nSlashPos < nSpacePos )))
953 return aF.nLen;
954 else
956 // Link fields aren't supported, but they are bound to an OLE object
957 // that needs to be roundtripped
958 if ( aF.nId == 56 )
959 m_bEmbeddObj = true;
960 // Field not supported: store the field code for later use
961 m_aFieldStack.back().SetBookmarkCode( aStr );
962 return aF.nLen - aF.nLRes - 1; // so viele ueberlesen, das Resultfeld
963 // wird wie Haupttext eingelesen
966 else
967 { // Lies Feld
968 long nOldPos = m_pStrm->Tell();
969 OUString aStr;
970 if ( aF.nId == 6 && aF.bCodeNest )
972 // TODO Extract the whole code string using the nested codes
973 aF.nLCode = m_pSBase->WW8ReadString( *m_pStrm, aStr, m_pPlcxMan->GetCpOfs() +
974 aF.nSCode, aF.nSRes - aF.nSCode - 1, m_eTextCharSet );
976 else
978 aF.nLCode = m_pSBase->WW8ReadString( *m_pStrm, aStr, m_pPlcxMan->GetCpOfs()+
979 aF.nSCode, aF.nLCode, m_eTextCharSet );
982 // #i51312# - graphics inside field code not supported by Writer.
983 // Thus, delete character 0x01, which stands for such a graphic.
984 if (aF.nId==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01.
986 aStr = aStr.replaceAll("\x01", "");
989 eF_ResT eRes = (this->*aWW8FieldTab[aF.nId])( &aF, aStr );
990 m_pStrm->Seek( nOldPos );
992 switch ( eRes )
994 case FLD_OK:
995 return aF.nLen; // alles OK
996 case FLD_TAGTXT:
997 if ((m_nFieldTagBad[nI] & nMask)) // Flag: Tag bad
998 return Read_F_Tag(&aF); // Taggen
999 SAL_FALLTHROUGH;
1000 case FLD_TEXT:
1001 // so viele ueberlesen, das Resultfeld wird wie Haupttext
1002 // eingelesen
1003 // attributes can start at char 0x14 so skip one
1004 // char more back == "-2"
1005 if (aF.nLRes)
1006 return aF.nLen - aF.nLRes - 2;
1007 else
1008 return aF.nLen;
1009 case FLD_TAGIGN:
1010 if( ( m_nFieldTagBad[nI] & nMask ) ) // Flag: Tag bad
1011 return Read_F_Tag( &aF ); // Taggen
1012 return aF.nLen; // oder ignorieren
1013 case FLD_READ_FSPA:
1014 return aF.nLen - aF.nLRes - 2; // auf Char 1 positionieren
1015 default:
1016 return aF.nLen; // ignorieren
1021 // Felder Taggen
1023 // MakeTagString() gibt als Returnwert die Position des ersten
1024 // CR / Zeilenende / Seitenumbruch in pText und wandelt auch nur bis dort
1025 // Wenn keins dieser Sonderzeichen enthalten ist, wird 0 zurueckgeliefert.
1026 void SwWW8ImplReader::MakeTagString( OUString& rStr, const OUString& rOrg )
1028 OUString sHex("\\x");
1029 bool bAllowCr = SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT )
1030 || SwFltGetFlag( m_nFieldFlags, SwFltControlStack::ALLOW_FLD_CR );
1031 sal_Unicode cChar;
1032 rStr = rOrg;
1034 for( sal_Int32 nI = 0;
1035 nI < rStr.getLength() && rStr.getLength() < (MAX_FIELDLEN - 4); ++nI )
1037 bool bSetAsHex = false;
1038 switch( cChar = rStr[ nI ] )
1040 case 132: // Typographische Anfuehrungszeichen
1041 case 148: // gegen normale tauschen
1042 case 147:
1043 rStr = rStr.replaceAt( nI, 1, "\"" );
1044 break;
1045 case 19:
1046 rStr = rStr.replaceAt( nI, 1, "{" );
1047 break; // 19..21 zu {|}
1048 case 20:
1049 rStr = rStr.replaceAt( nI, 1, "|" );
1050 break;
1051 case 21:
1052 rStr = rStr.replaceAt( nI, 1, "}" );
1053 break;
1054 case '\\': // \{|} per \ Taggen
1055 case '{':
1056 case '|':
1057 case '}':
1058 rStr = rStr.replaceAt( nI, 0, "\\" );
1059 ++nI;
1060 break;
1061 case 0x0b:
1062 case 0x0c:
1063 case 0x0d:
1064 if( bAllowCr )
1065 rStr = rStr.replaceAt( nI, 1, "\n" );
1066 else
1067 bSetAsHex = true;
1068 break;
1069 case 0xFE:
1070 case 0xFF:
1071 bSetAsHex = true;
1072 break;
1073 default:
1074 bSetAsHex = 0x20 > cChar;
1075 break;
1078 if( bSetAsHex )
1080 //all Hex-Numbers with \x before
1081 OUString sTmp( sHex );
1082 if( cChar < 0x10 )
1083 sTmp += "0";
1084 sTmp += OUString::number( cChar, 16 );
1085 rStr = rStr.replaceAt( nI, 1 , sTmp );
1086 nI += sTmp.getLength() - 1;
1090 if( rStr.getLength() > (MAX_FIELDLEN - 4))
1091 rStr = rStr.copy( 0, MAX_FIELDLEN - 4 );
1094 void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId, const OUString& rTagText )
1096 OUString aName("WwFieldTag");
1097 if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_DO_ID ) ) // Nummer?
1098 aName += OUString::number( nId ); // ausgeben ?
1100 if( SwFltGetFlag(m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT))
1102 aName += rTagText; // als Text taggen
1103 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, aName,
1104 SwInsertFlags::NOHINTEXPAND);
1106 else
1107 { // normal taggen
1109 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1110 SwSetExpFieldType( &m_rDoc, aName, nsSwGetSetExpType::GSE_STRING ) );
1111 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), rTagText ); // SUB_INVISIBLE
1112 sal_uInt16 nSubType = ( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_VISIBLE ) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE;
1113 aField.SetSubType(nSubType | nsSwGetSetExpType::GSE_STRING);
1115 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1119 long SwWW8ImplReader::Read_F_Tag( WW8FieldDesc* pF )
1121 long nOldPos = m_pStrm->Tell();
1123 WW8_CP nStart = pF->nSCode - 1; // mit 0x19 am Anfang
1124 long nL = pF->nLen; // Gesamtlaenge mit Resultat u. Nest
1125 if( nL > MAX_FIELDLEN )
1126 nL = MAX_FIELDLEN; // MaxLaenge, durch Quoten
1127 // max. 4* so gross
1128 OUString sFText;
1129 m_pSBase->WW8ReadString( *m_pStrm, sFText,
1130 m_pPlcxMan->GetCpOfs() + nStart, nL, m_eStructCharSet);
1132 OUString aTagText;
1133 MakeTagString( aTagText, sFText );
1134 InsertTagField( pF->nId, aTagText );
1136 m_pStrm->Seek( nOldPos );
1137 return pF->nLen;
1140 // normal fields
1142 eF_ResT SwWW8ImplReader::Read_F_Input( WW8FieldDesc* pF, OUString& rStr )
1144 OUString aDef;
1145 OUString aQ;
1146 WW8ReadFieldParams aReadParam( rStr );
1147 for (;;)
1149 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1150 if ( nRet==-1 )
1151 break;
1152 switch( nRet )
1154 case -2:
1155 if( aQ.isEmpty() )
1156 aQ = aReadParam.GetResult();
1157 break;
1158 case 'd':
1159 case 'D':
1160 if ( aReadParam.GoToTokenParam() )
1161 aDef = aReadParam.GetResult();
1162 break;
1165 if( aDef.isEmpty() )
1166 aDef = GetFieldResult( pF );
1168 if ( pF->nId != 0x01 ) // 0x01 fields have no result
1170 SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_INPUTFLD )),
1171 aDef, aQ, INP_TXT, 0, false );
1172 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1175 return FLD_OK;
1178 // GetFieldResult alloziert einen String und liest das Feld-Resultat ein
1179 OUString SwWW8ImplReader::GetFieldResult( WW8FieldDesc* pF )
1181 long nOldPos = m_pStrm->Tell();
1183 WW8_CP nStart = pF->nSRes; // result start
1184 long nL = pF->nLRes; // result length
1185 if( !nL )
1186 return OUString(); // no result
1188 if( nL > MAX_FIELDLEN )
1189 nL = MAX_FIELDLEN; // MaxLength, by quoting
1190 // max. 4* as big
1192 OUString sRes;
1193 m_pSBase->WW8ReadString( *m_pStrm, sRes, m_pPlcxMan->GetCpOfs() + nStart,
1194 nL, m_eStructCharSet );
1196 m_pStrm->Seek( nOldPos );
1198 //replace both CR 0x0D and VT 0x0B with LF 0x0A
1199 return sRes.replace(0x0D, 0x0A).replace(0x0B, 0x0A);
1203 Bookmarks can be set with fields SET and ASK, and they can be referenced with
1204 REF. When set, they behave like variables in writer, otherwise they behave
1205 like normal bookmarks. We can check whether we should use a show variable
1206 instead of a normal bookmark ref by converting to "show variable" at the end
1207 of the document those refs which look for the content of a bookmark but whose
1208 bookmarks were set with SET or ASK. (See SwWW8FltRefStack)
1210 The other piece of the puzzle is that refs that point to the "location" of the
1211 bookmark will in word actually point to the last location where the bookmark
1212 was set with SET or ASK, not the actual bookmark. This is only noticeable when
1213 a document sets the bookmark more than once. This is because word places the
1214 true bookmark at the location of the last set, but the refs will display the
1215 position of the first set before the ref.
1217 So what we will do is
1219 1) keep a list of all bookmarks that were set, any bookmark names mentioned
1220 here that are referred by content will be converted to show variables.
1222 2) create pseudo bookmarks for every position that a bookmark is set with SET
1223 or ASK but has no existing bookmark. We can then keep a map from the original
1224 bookmark name to the new one. As we parse the document new pseudo names will
1225 replace the older ones, so the map always contains the bookmark of the
1226 location that msword itself would use.
1228 3) word's bookmarks are case insensitive, writers are not. So we need to
1229 map case different versions together, regardless of whether they are
1230 variables or not.
1232 4) when a reference is (first) SET or ASK, the bookmark associated with it
1233 is placed around the 0x14 0x15 result part of the field. We will fiddle
1234 the placement to be the writer equivalent of directly before and after
1235 the field, which gives the same effect and meaning, to do so we must
1236 get any bookmarks in the field range, and begin them immediately before
1237 the set/ask field, and end them directly afterwards. MapBookmarkVariables
1238 returns an identifier of the bookmark attribute to close after inserting
1239 the appropriate set/ask field.
1241 long SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc* pF,
1242 OUString &rOrigName, const OUString &rData)
1244 OSL_ENSURE(m_pPlcxMan,"No pPlcxMan");
1245 long nNo;
1247 If there was no bookmark associated with this set field, then we create a
1248 pseudo one and insert it in the document.
1250 sal_uInt16 nIndex;
1251 m_pPlcxMan->GetBook()->MapName(rOrigName);
1252 OUString sName = m_pPlcxMan->GetBook()->GetBookmark(
1253 pF->nSCode, pF->nSCode + pF->nLen, nIndex);
1254 if (!sName.isEmpty())
1256 m_pPlcxMan->GetBook()->SetStatus(nIndex, BOOK_IGNORE);
1257 nNo = nIndex;
1259 else
1261 nNo = m_pReffingStck->aFieldVarNames.size()+1;
1262 sName = "WWSetBkmk" + OUString::number(nNo);
1263 nNo += m_pPlcxMan->GetBook()->GetIMax();
1265 m_pReffedStck->NewAttr(*m_pPaM->GetPoint(),
1266 SwFltBookmark( BookmarkToWriter(sName), rData, nNo ));
1267 m_pReffingStck->aFieldVarNames[rOrigName] = sName;
1268 return nNo;
1272 Word can set a bookmark with set or with ask, such a bookmark is equivalent to
1273 our variables, but until the end of a document we cannot be sure if a bookmark
1274 is a variable or not, at the end we will have a list of reference names which
1275 were set or asked, all bookmarks using the content of those bookmarks are
1276 converted to show variables, those that reference the position of the field
1277 can be left as references, because a bookmark is also inserted at the position
1278 of a set or ask field, either by word, or in some special cases by the import
1279 filter itself.
1281 SwFltStackEntry *SwWW8FltRefStack::RefToVar(const SwField* pField,
1282 SwFltStackEntry &rEntry)
1284 SwFltStackEntry *pRet=nullptr;
1285 if (pField && RES_GETREFFLD == pField->Which())
1287 //Get the name of the ref field, and see if actually a variable
1288 const OUString sName = pField->GetPar1();
1289 std::map<OUString, OUString, SwWW8::ltstr>::const_iterator
1290 aResult = aFieldVarNames.find(sName);
1292 if (aResult != aFieldVarNames.end())
1294 SwGetExpField aField( static_cast<SwGetExpFieldType*>(
1295 pDoc->getIDocumentFieldsAccess().GetSysFieldType(RES_GETEXPFLD)), sName, nsSwGetSetExpType::GSE_STRING, 0);
1296 delete rEntry.pAttr;
1297 SwFormatField aTmp(aField);
1298 rEntry.pAttr = aTmp.Clone();
1299 pRet = &rEntry;
1302 return pRet;
1305 OUString SwWW8ImplReader::GetMappedBookmark(const OUString &rOrigName)
1307 OUString sName(BookmarkToWriter(rOrigName));
1308 OSL_ENSURE(m_pPlcxMan,"no pPlcxMan");
1309 m_pPlcxMan->GetBook()->MapName(sName);
1311 //See if there has been a variable set with this name, if so get
1312 //the pseudo bookmark name that was set with it.
1313 std::map<OUString, OUString, SwWW8::ltstr>::const_iterator aResult =
1314 m_pReffingStck->aFieldVarNames.find(sName);
1316 return (aResult == m_pReffingStck->aFieldVarNames.end())
1317 ? sName : (*aResult).second;
1320 // "ASK"
1321 eF_ResT SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc* pF, OUString& rStr )
1323 OUString sOrigName, aQ;
1324 OUString aDef;
1325 WW8ReadFieldParams aReadParam( rStr );
1326 for (;;)
1328 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1329 if ( nRet==-1 )
1330 break;
1331 switch( nRet )
1333 case -2:
1334 if (sOrigName.isEmpty())
1335 sOrigName = aReadParam.GetResult();
1336 else if (aQ.isEmpty())
1337 aQ = aReadParam.GetResult();
1338 break;
1339 case 'd':
1340 case 'D':
1341 if ( aReadParam.GoToTokenParam() )
1342 aDef = aReadParam.GetResult();
1343 break;
1347 if (sOrigName.isEmpty())
1348 return FLD_TAGIGN; // macht ohne Textmarke keinen Sinn
1350 const OUString aResult(GetFieldResult(pF));
1352 //#i24377#, munge Default Text into title as we have only one slot
1353 //available for aResult and aDef otherwise
1354 if (!aDef.isEmpty())
1356 if (!aQ.isEmpty())
1357 aQ += " - ";
1358 aQ += aDef;
1361 const long nNo = MapBookmarkVariables(pF, sOrigName, aResult);
1363 SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1364 SwSetExpFieldType(&m_rDoc, sOrigName, nsSwGetSetExpType::GSE_STRING)));
1365 SwSetExpField aField(pFT, aResult);
1366 aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
1367 aField.SetInputFlag(true);
1368 aField.SetPromptText( aQ );
1370 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1372 m_pReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
1373 return FLD_OK;
1376 // "AUTONR"
1377 eF_ResT SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc*, OUString& rStr )
1379 if( !m_pNumFieldType ){ // 1. Mal
1380 SwSetExpFieldType aT( &m_rDoc, OUString("AutoNr"), nsSwGetSetExpType::GSE_SEQ );
1381 m_pNumFieldType = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aT );
1383 SwSetExpField aField( static_cast<SwSetExpFieldType*>(m_pNumFieldType), OUString(),
1384 GetNumberPara( rStr ) );
1385 aField.SetValue( ++m_nFieldNum );
1386 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1387 return FLD_OK;
1390 // "SEQ"
1391 eF_ResT SwWW8ImplReader::Read_F_Seq( WW8FieldDesc*, OUString& rStr )
1393 OUString aSequenceName;
1394 OUString aBook;
1395 bool bHidden = false;
1396 bool bFormat = false;
1397 bool bCountOn = true;
1398 OUString sStart;
1399 SvxExtNumType eNumFormat = SVX_NUM_ARABIC;
1400 WW8ReadFieldParams aReadParam( rStr );
1401 for (;;)
1403 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1404 if ( nRet==-1 )
1405 break;
1406 switch( nRet )
1408 case -2:
1409 if( aSequenceName.isEmpty() )
1410 aSequenceName = aReadParam.GetResult();
1411 else if( aBook.isEmpty() )
1412 aBook = aReadParam.GetResult();
1413 break;
1415 case 'h':
1416 if( !bFormat )
1417 bHidden = true; // Hidden-Flag aktivieren
1418 break;
1420 case '*':
1421 bFormat = true; // Format-Flag aktivieren
1422 if ( aReadParam.SkipToNextToken()!=-2 )
1423 break;
1424 if ( aReadParam.GetResult()!="MERGEFORMAT" && aReadParam.GetResult()!="CHARFORMAT" )
1425 eNumFormat = GetNumTypeFromName( aReadParam.GetResult() );
1426 break;
1428 case 'r':
1429 bCountOn = false;
1430 if ( aReadParam.SkipToNextToken()==-2 )
1431 sStart = aReadParam.GetResult();
1432 break;
1434 case 'c':
1435 bCountOn = false;
1436 break;
1438 case 'n':
1439 bCountOn = true; // Nummer um eins erhoehen (default)
1440 break;
1442 case 's': // Outline Level
1443 //#i19682, what am I to do with this value
1444 break;
1447 if (aSequenceName.isEmpty() && aBook.isEmpty())
1448 return FLD_TAGIGN;
1450 SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
1451 SwSetExpFieldType( &m_rDoc, aSequenceName, nsSwGetSetExpType::GSE_SEQ ) ) );
1452 SwSetExpField aField( pFT, OUString(), eNumFormat );
1454 //#i120654# Add bHidden for /h flag (/h: Hide the field result.)
1455 if (bHidden)
1456 aField.SetSubType(aField.GetSubType() | nsSwExtendedSubType::SUB_INVISIBLE);
1458 if (!sStart.isEmpty())
1459 aField.SetFormula( ( aSequenceName += "=" ) += sStart );
1460 else if (!bCountOn)
1461 aField.SetFormula(aSequenceName);
1463 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1464 return FLD_OK;
1467 eF_ResT SwWW8ImplReader::Read_F_Styleref(WW8FieldDesc*, OUString& rString)
1469 WW8ReadFieldParams aReadParam(rString);
1470 sal_Int32 nRet = aReadParam.SkipToNextToken();
1471 if (nRet != -2)
1472 // \param was found, not normal text.
1473 return FLD_TAGIGN;
1475 OUString aResult = aReadParam.GetResult();
1476 sal_Int32 nResult = aResult.toInt32();
1477 if (nResult < 1)
1478 return FLD_TAGIGN;
1480 SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(RES_CHAPTERFLD);
1481 SwChapterField aField(static_cast<SwChapterFieldType*>(pFieldType), CF_TITLE);
1482 aField.SetLevel(nResult - 1);
1483 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1485 return FLD_OK;
1488 eF_ResT SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr )
1490 sal_uInt16 nSub=0;
1491 // RegInfoFormat, DefaultFormat fuer DocInfoFelder
1492 sal_uInt16 nReg = DI_SUB_AUTHOR;
1493 bool bDateTime = false;
1495 if( 85 == pF->nId )
1497 OUString aDocProperty;
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( aDocProperty.isEmpty() )
1508 aDocProperty = aReadParam.GetResult();
1509 break;
1510 case '*':
1511 //Skip over MERGEFORMAT
1512 (void)aReadParam.SkipToNextToken();
1513 break;
1517 aDocProperty = aDocProperty.replaceAll("\"", "");
1520 There are up to 26 fields that may be meant by 'DocumentProperty'.
1521 Which of them is to be inserted here ?
1522 This Problem can only be solved by implementing a name matching
1523 method that compares the given Parameter String with the four
1524 possible name sets (english, german, french, spanish)
1527 static const sal_Char* aName10 = "\x0F"; // SW field code
1528 static const sal_Char* aName11 // German
1529 = "TITEL";
1530 static const sal_Char* aName12 // French
1531 = "TITRE";
1532 static const sal_Char* aName13 // English
1533 = "TITLE";
1534 static const sal_Char* aName14 // Spanish
1535 = "TITRO";
1536 static const sal_Char* aName20 = "\x15"; // SW filed code
1537 static const sal_Char* aName21 // German
1538 = "ERSTELLDATUM";
1539 static const sal_Char* aName22 // French
1540 = "CR\xC9\xC9";
1541 static const sal_Char* aName23 // English
1542 = "CREATED";
1543 static const sal_Char* aName24 // Spanish
1544 = "CREADO";
1545 static const sal_Char* aName30 = "\x16"; // SW filed code
1546 static const sal_Char* aName31 // German
1547 = "ZULETZTGESPEICHERTZEIT";
1548 static const sal_Char* aName32 // French
1549 = "DERNIERENREGISTREMENT";
1550 static const sal_Char* aName33 // English
1551 = "SAVED";
1552 static const sal_Char* aName34 // Spanish
1553 = "MODIFICADO";
1554 static const sal_Char* aName40 = "\x17"; // SW filed code
1555 static const sal_Char* aName41 // German
1556 = "ZULETZTGEDRUCKT";
1557 static const sal_Char* aName42 // French
1558 = "DERNI\xC8" "REIMPRESSION";
1559 static const sal_Char* aName43 // English
1560 = "LASTPRINTED";
1561 static const sal_Char* aName44 // Spanish
1562 = "HUPS PUPS";
1563 static const sal_Char* aName50 = "\x18"; // SW filed code
1564 static const sal_Char* aName51 // German
1565 = "\xDC" "BERARBEITUNGSNUMMER";
1566 static const sal_Char* aName52 // French
1567 = "NUM\xC9" "RODEREVISION";
1568 static const sal_Char* aName53 // English
1569 = "REVISIONNUMBER";
1570 static const sal_Char* aName54 // Spanish
1571 = "SNUBBEL BUBBEL";
1572 static const sal_uInt16 nFieldCnt = 5;
1574 // additional fields are to be coded soon! :-)
1576 static const sal_uInt16 nLangCnt = 4;
1577 static const sal_Char *aNameSet_26[nFieldCnt][nLangCnt+1] =
1579 {aName10, aName11, aName12, aName13, aName14},
1580 {aName20, aName21, aName22, aName23, aName24},
1581 {aName30, aName31, aName32, aName33, aName34},
1582 {aName40, aName41, aName42, aName43, aName44},
1583 {aName50, aName51, aName52, aName53, aName54}
1586 bool bFieldFound= false;
1587 sal_uInt16 nFIdx;
1588 for(sal_uInt16 nLIdx=1; !bFieldFound && (nLangCnt > nLIdx); ++nLIdx)
1590 for(nFIdx = 0; !bFieldFound && (nFieldCnt > nFIdx); ++nFIdx)
1592 if( aDocProperty == OUString( aNameSet_26[nFIdx][nLIdx], strlen(aNameSet_26[nFIdx][nLIdx]),
1593 RTL_TEXTENCODING_MS_1252 ) )
1595 bFieldFound = true;
1596 pF->nId = aNameSet_26[nFIdx][0][0];
1601 if( !bFieldFound )
1603 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1604 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_DOCINFOFLD )), DI_CUSTOM|nReg, aDocProperty, GetFieldResult( pF ) );
1605 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1607 return FLD_OK;
1611 switch( pF->nId )
1613 case 14:
1614 /* kann alle INFO-Vars!! */
1615 nSub = DI_KEYS;
1616 break;
1617 case 15:
1618 nSub = DI_TITEL;
1619 break;
1620 case 16:
1621 nSub = DI_THEMA;
1622 break;
1623 case 18:
1624 nSub = DI_KEYS;
1625 break;
1626 case 19:
1627 nSub = DI_COMMENT;
1628 break;
1629 case 20:
1630 nSub = DI_CHANGE;
1631 nReg = DI_SUB_AUTHOR;
1632 break;
1633 case 21:
1634 nSub = DI_CREATE;
1635 nReg = DI_SUB_DATE;
1636 bDateTime = true;
1637 break;
1638 case 23:
1639 nSub = DI_PRINT;
1640 nReg = DI_SUB_DATE;
1641 bDateTime = true;
1642 break;
1643 case 24:
1644 nSub = DI_DOCNO;
1645 break;
1646 case 22:
1647 nSub = DI_CHANGE;
1648 nReg = DI_SUB_DATE;
1649 bDateTime = true;
1650 break;
1651 case 25:
1652 nSub = DI_CHANGE;
1653 nReg = DI_SUB_TIME;
1654 bDateTime = true;
1655 break;
1656 case 64: // DOCVARIABLE
1657 nSub = DI_CUSTOM;
1658 break;
1661 sal_uInt32 nFormat = 0;
1663 sal_uInt16 nLang(0);
1664 if (bDateTime)
1666 short nDT = GetTimeDatePara(rStr, nFormat, nLang, pF->nId);
1667 switch (nDT)
1669 case css::util::NumberFormat::DATE:
1670 nReg = DI_SUB_DATE;
1671 break;
1672 case css::util::NumberFormat::TIME:
1673 nReg = DI_SUB_TIME;
1674 break;
1675 case css::util::NumberFormat::DATETIME:
1676 nReg = DI_SUB_DATE;
1677 break;
1678 default:
1679 nReg = DI_SUB_DATE;
1680 break;
1684 OUString aData;
1685 // Extract DOCVARIABLE varname
1686 if ( 64 == pF->nId )
1688 WW8ReadFieldParams aReadParam( rStr );
1689 for (;;)
1691 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1692 if ( nRet==-1)
1693 break;
1694 switch( nRet )
1696 case -2:
1697 if( aData.isEmpty() )
1698 aData = aReadParam.GetResult();
1699 break;
1700 case '*':
1701 //Skip over MERGEFORMAT
1702 (void)aReadParam.SkipToNextToken();
1703 break;
1707 aData = aData.replaceAll("\"", "");
1710 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1711 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_DOCINFOFLD )), nSub|nReg, aData, nFormat );
1712 if (bDateTime)
1713 ForceFieldLanguage(aField, nLang);
1714 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1716 return FLD_OK;
1719 eF_ResT SwWW8ImplReader::Read_F_Author( WW8FieldDesc*, OUString& )
1721 // SH: Das SwAuthorField bezeichnet nicht den urspruenglichen
1722 // Autor, sondern den aktuellen Benutzer, also besser ueber DocInfo
1723 SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
1724 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_DOCINFOFLD )),
1725 DI_CREATE|DI_SUB_AUTHOR, OUString() );
1726 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1727 return FLD_OK;
1730 eF_ResT SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc*, OUString& )
1732 SwTemplNameField aField( static_cast<SwTemplNameFieldType*>(
1733 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_TEMPLNAMEFLD )), FF_NAME );
1734 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1735 return FLD_OK;
1738 // Sowohl das Datum- wie auch das Uhrzeit-Feld kann fuer Datum, fuer Uhrzeit
1739 // oder fuer beides benutzt werden.
1740 eF_ResT SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc*pF, OUString& rStr )
1742 bool bHijri = false;
1743 WW8ReadFieldParams aReadParam(rStr);
1744 for (;;)
1746 const sal_Int32 nTok = aReadParam.SkipToNextToken();
1747 if ( nTok==-1 )
1748 break;
1749 switch (nTok)
1751 default:
1752 case 'l':
1753 case -2:
1754 break;
1755 case 'h':
1756 bHijri = true;
1757 break;
1758 case 's':
1759 //Saka Calendar, should we do something with this ?
1760 break;
1764 sal_uInt32 nFormat = 0;
1766 sal_uInt16 nLang(0);
1767 short nDT = GetTimeDatePara(rStr, nFormat, nLang, ww::eDATE, bHijri);
1769 if( css::util::NumberFormat::UNDEFINED == nDT ) // no D/T-Formatstring
1771 if (32 == pF->nId)
1773 nDT = css::util::NumberFormat::TIME;
1774 nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
1775 NF_TIME_START, LANGUAGE_SYSTEM );
1777 else
1779 nDT = css::util::NumberFormat::DATE;
1780 nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
1781 NF_DATE_START, LANGUAGE_SYSTEM );
1785 if (nDT & css::util::NumberFormat::DATE)
1787 SwDateTimeField aField(static_cast<SwDateTimeFieldType*>(
1788 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(RES_DATETIMEFLD )), DATEFLD, nFormat);
1789 ForceFieldLanguage(aField, nLang);
1790 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1792 else if (nDT == css::util::NumberFormat::TIME)
1794 SwDateTimeField aField(static_cast<SwDateTimeFieldType*>(
1795 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(RES_DATETIMEFLD)), TIMEFLD, nFormat);
1796 ForceFieldLanguage(aField, nLang);
1797 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1800 return FLD_OK;
1803 eF_ResT SwWW8ImplReader::Read_F_FileName(WW8FieldDesc*, OUString &rStr)
1805 SwFileNameFormat eType = FF_NAME;
1806 WW8ReadFieldParams aReadParam(rStr);
1807 for (;;)
1809 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1810 if ( nRet==-1 )
1811 break;
1812 switch (nRet)
1814 case 'p':
1815 eType = FF_PATHNAME;
1816 break;
1817 case '*':
1818 //Skip over MERGEFORMAT
1819 (void)aReadParam.SkipToNextToken();
1820 break;
1821 default:
1822 OSL_ENSURE(false, "unknown option in FileName field");
1823 break;
1827 SwFileNameField aField(
1828 static_cast<SwFileNameFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(RES_FILENAMEFLD)), eType);
1829 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
1830 return FLD_OK;
1833 eF_ResT SwWW8ImplReader::Read_F_Num( WW8FieldDesc* pF, OUString& rStr )
1835 sal_uInt16 nSub = DS_PAGE; // page number
1836 switch ( pF->nId ){
1837 case 27: nSub = DS_WORD; break; // number of words
1838 case 28: nSub = DS_CHAR; break; // number of characters
1840 SwDocStatField aField( static_cast<SwDocStatFieldType*>(
1841 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_DOCSTATFLD )), nSub,
1842 GetNumberPara( rStr ) );
1843 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1844 return FLD_OK;
1847 eF_ResT SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc*, OUString& rStr )
1849 // page number
1850 SwPageNumberField aField( static_cast<SwPageNumberFieldType*>(
1851 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_PAGENUMBERFLD )), PG_RANDOM,
1852 GetNumberPara(rStr, true));
1854 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1855 return FLD_OK;
1858 eF_ResT SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc*, OUString& rStr )
1860 //e.g. #i20118#
1861 OUString aQ;
1862 OUString aName;
1863 sal_Int32 nSize = 0;
1864 WW8ReadFieldParams aReadParam( rStr );
1865 for (;;)
1867 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1868 if ( nRet==-1 )
1869 break;
1870 switch( nRet )
1872 case -2:
1873 if( aQ.isEmpty() )
1874 aQ = aReadParam.GetResult();
1875 break;
1876 case 'f':
1877 case 'F':
1878 if ( aReadParam.GoToTokenParam() )
1879 aName = aReadParam.GetResult();
1880 break;
1881 case 's':
1882 case 'S':
1883 if ( aReadParam.GoToTokenParam() )
1885 const OUString aSiz = aReadParam.GetResult();
1886 if ( !aSiz.isEmpty() )
1887 nSize = aSiz.toInt32() * 20; // pT -> twip
1889 break;
1892 if( aQ.isEmpty() )
1893 return FLD_TAGIGN; // -> no 0-char in text
1895 if (sal_Unicode cChar = static_cast<sal_Unicode>(aQ.toInt32()))
1897 if (!aName.isEmpty()) // Font Name set ?
1899 SvxFontItem aFont(FAMILY_DONTKNOW, aName, OUString(),
1900 PITCH_DONTKNOW, RTL_TEXTENCODING_SYMBOL, RES_CHRATR_FONT);
1901 NewAttr(aFont); // new Font
1904 if (nSize > 0) //#i20118#
1906 SvxFontHeightItem aSz(nSize, 100, RES_CHRATR_FONTSIZE);
1907 NewAttr(aSz);
1910 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(cChar));
1912 if (nSize > 0)
1913 m_pCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONTSIZE);
1914 if (!aName.isEmpty())
1915 m_pCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
1917 else
1919 m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, "###");
1922 return FLD_OK;
1925 // "EINBETTEN"
1926 eF_ResT SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc*, OUString& rStr )
1928 OUString sHost;
1930 WW8ReadFieldParams aReadParam( rStr );
1931 for (;;)
1933 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1934 if ( nRet==-1 )
1935 break;
1936 switch( nRet )
1938 case -2:
1939 sHost = aReadParam.GetResult();
1940 break;
1942 case 's':
1943 // use ObjectSize
1944 break;
1948 if( m_bObj && m_nPicLocFc )
1949 m_nObjLocFc = m_nPicLocFc;
1950 m_bEmbeddObj = true;
1951 return FLD_TEXT;
1954 // "SET"
1955 eF_ResT SwWW8ImplReader::Read_F_Set( WW8FieldDesc* pF, OUString& rStr )
1957 OUString sOrigName;
1958 OUString sVal;
1959 WW8ReadFieldParams aReadParam( rStr );
1960 for (;;)
1962 const sal_Int32 nRet = aReadParam.SkipToNextToken();
1963 if ( nRet==-1 )
1964 break;
1965 switch( nRet )
1967 case -2:
1968 if (sOrigName.isEmpty())
1969 sOrigName = aReadParam.GetResult();
1970 else if (sVal.isEmpty())
1971 sVal = aReadParam.GetResult();
1972 break;
1976 const long nNo = MapBookmarkVariables(pF, sOrigName, sVal);
1978 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc, sOrigName,
1979 nsSwGetSetExpType::GSE_STRING ) );
1980 SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), sVal, ULONG_MAX );
1981 aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
1983 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
1985 m_pReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
1987 return FLD_OK;
1990 // "REF"
1991 eF_ResT SwWW8ImplReader::Read_F_Ref( WW8FieldDesc*, OUString& rStr )
1992 { // Reference - Field
1993 OUString sOrigBkmName;
1994 REFERENCEMARK eFormat = REF_CONTENT;
1996 WW8ReadFieldParams aReadParam( rStr );
1997 for (;;)
1999 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2000 if ( nRet==-1 )
2001 break;
2002 switch( nRet )
2004 case -2:
2005 if( sOrigBkmName.isEmpty() ) // get name of bookmark
2006 sOrigBkmName = aReadParam.GetResult();
2007 break;
2009 /* References to numbers in Word could be either to a numbered
2010 paragraph or to a chapter number. However Word does not seem to
2011 have the capability we do, of referring to the chapter number some
2012 other bookmark is in. As a result, cross-references to chapter
2013 numbers in a word document will be cross-references to a numbered
2014 paragraph, being the chapter heading paragraph. As it happens, our
2015 cross-references to numbered paragraphs will do the right thing
2016 when the target is a numbered chapter heading, so there is no need
2017 for us to use the REF_CHAPTER bookmark format on import.
2019 case 'n':
2020 eFormat = REF_NUMBER_NO_CONTEXT;
2021 break;
2022 case 'r':
2023 eFormat = REF_NUMBER;
2024 break;
2025 case 'w':
2026 eFormat = REF_NUMBER_FULL_CONTEXT;
2027 break;
2029 case 'p':
2030 eFormat = REF_UPDOWN;
2031 break;
2032 case 'h':
2033 break;
2034 default:
2035 // unimplemented switch: just do 'nix nought nothing' :-)
2036 break;
2040 OUString sBkmName(GetMappedBookmark(sOrigBkmName));
2042 // #i120879# add cross reference bookmark name prefix, if it
2043 // matches internal TOC bookmark naming convention
2044 if ( IsTOCBookmarkName( sBkmName ) )
2046 sBkmName = EnsureTOCBookmarkName(sBkmName);
2047 // track <sBookmarkName> as referenced TOC bookmark.
2048 m_pReffedStck->aReferencedTOCBookmarks.insert( sBkmName );
2051 SwGetRefField aField(
2052 static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_GETREFFLD )),
2053 sBkmName,REF_BOOKMARK,0,eFormat);
2055 if (eFormat == REF_CONTENT)
2058 If we are just inserting the contents of the bookmark, then it
2059 is possible that the bookmark is actually a variable, so we
2060 must store it until the end of the document to see if it was,
2061 in which case we'll turn it into a show variable
2063 m_pReffingStck->NewAttr( *m_pPaM->GetPoint(), SwFormatField(aField) );
2064 m_pReffingStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2066 else
2068 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
2070 return FLD_OK;
2073 // Note Reference - Field
2074 eF_ResT SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc*, OUString& rStr )
2076 OUString aBkmName;
2077 bool bAboveBelow = false;
2079 WW8ReadFieldParams aReadParam( rStr );
2080 for (;;)
2082 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2083 if ( nRet==-1 )
2084 break;
2085 switch( nRet )
2087 case -2:
2088 if( aBkmName.isEmpty() ) // get name of foot/endnote
2089 aBkmName = aReadParam.GetResult();
2090 break;
2091 case 'r':
2092 // activate flag 'Chapter Number'
2093 break;
2094 case 'p':
2095 bAboveBelow = true;
2096 break;
2097 case 'h':
2098 break;
2099 default:
2100 // unimplemented switch: just do 'nix nought nothing' :-)
2101 break;
2105 // set Sequence No of corresponding Foot-/Endnote to Zero
2106 // (will be corrected in
2107 SwGetRefField aField( static_cast<SwGetRefFieldType*>(
2108 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_GETREFFLD )), aBkmName, REF_FOOTNOTE, 0,
2109 REF_ONLYNUMBER );
2110 m_pReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField));
2111 m_pReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2112 if (bAboveBelow)
2114 SwGetRefField aField2( static_cast<SwGetRefFieldType*>(
2115 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_GETREFFLD )),aBkmName, REF_FOOTNOTE, 0,
2116 REF_UPDOWN );
2117 m_pReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField2));
2118 m_pReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
2120 return FLD_OK;
2123 // "SEITENREF"
2124 eF_ResT SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc*, OUString& rStr )
2126 OUString sOrigName;
2127 WW8ReadFieldParams aReadParam( rStr );
2128 for (;;)
2130 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2131 if ( nRet==-1 )
2132 break;
2133 else if ( nRet == -2 && sOrigName.isEmpty() )
2135 sOrigName = aReadParam.GetResult();
2139 const OUString sName(GetMappedBookmark(sOrigName));
2141 // loading page reference field in TOX
2142 if (m_bLoadingTOXCache)
2144 // insert page ref representation as plain text --> return FLD_TEXT
2145 // if there is no hyperlink settings for current toc and referenced bookmark is available,
2146 // assign link to current ref area
2147 if (!m_bLoadingTOXHyperlink && !sName.isEmpty())
2149 // #i120879# add cross reference bookmark name prefix, if it
2150 // matches internal TOC bookmark naming convention
2151 OUString sBookmarkName;
2152 if ( IsTOCBookmarkName( sName ) )
2154 sBookmarkName = EnsureTOCBookmarkName(sName);
2155 // track <sBookmarkName> as referenced TOC bookmark.
2156 m_pReffedStck->aReferencedTOCBookmarks.insert( sBookmarkName );
2158 else
2160 sBookmarkName = sName;
2162 OUString sURL = "#" + sBookmarkName;
2163 const OUString sTarget;
2164 SwFormatINetFormat aURL( sURL, sTarget );
2165 const OUString sLinkStyle("Index Link");
2166 const sal_uInt16 nPoolId =
2167 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
2168 aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId);
2169 aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
2170 m_pCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
2172 return FLD_TEXT;
2175 // #i120879# add cross reference bookmark name prefix, if it matches
2176 // internal TOC bookmark naming convention
2177 OUString sPageRefBookmarkName;
2178 if ( IsTOCBookmarkName( sName ) )
2180 sPageRefBookmarkName = EnsureTOCBookmarkName(sName);
2181 // track <sPageRefBookmarkName> as referenced TOC bookmark.
2182 m_pReffedStck->aReferencedTOCBookmarks.insert( sPageRefBookmarkName );
2184 else
2186 sPageRefBookmarkName = sName;
2188 SwGetRefField aField( static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_GETREFFLD )),
2189 sPageRefBookmarkName, REF_BOOKMARK, 0, REF_PAGE );
2190 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2192 return FLD_OK;
2195 //helper function
2196 //For MS MacroButton field, the symbol in plain text is always "(" (0x28),
2197 //which should be mapped according to the macro type
2198 bool ConvertMacroSymbol( const OUString& rName, OUString& rReference )
2200 bool bConverted = false;
2201 if( rReference == "(" )
2203 bConverted = true;
2204 sal_Unicode cSymbol = sal_Unicode(); // silence false warning
2205 if (rName == "CheckIt")
2206 cSymbol = 0xF06F;
2207 else if (rName == "UncheckIt")
2208 cSymbol = 0xF0FE;
2209 else if (rName == "ShowExample")
2210 cSymbol = 0xF02A;
2211 //else if... : todo
2212 else
2213 bConverted = false;
2215 if( bConverted )
2216 rReference = OUString(cSymbol);
2218 return bConverted;
2221 // "MACROSCHALTFL"ACHE"
2222 eF_ResT SwWW8ImplReader::Read_F_Macro( WW8FieldDesc*, OUString& rStr)
2224 OUString aName;
2225 OUString aVText;
2226 bool bNewVText = true;
2227 bool bBracket = false;
2228 WW8ReadFieldParams aReadParam( rStr );
2230 sal_Int32 nOffset = 0;
2232 for (;;)
2234 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2235 if ( nRet==-1 )
2236 break;
2237 switch( nRet )
2239 case -2:
2240 if( aName.isEmpty() )
2241 aName = aReadParam.GetResult();
2242 else if( aVText.isEmpty() || bBracket )
2244 nOffset = aReadParam.GetTokenSttPtr() + 1;
2246 if( bBracket )
2247 aVText += " ";
2248 aVText += aReadParam.GetResult();
2249 if (bNewVText)
2251 bBracket = (aVText[0] == '[');
2252 bNewVText = false;
2254 else if( aVText.endsWith("]") )
2255 bBracket = false;
2257 break;
2260 if( aName.isEmpty() )
2261 return FLD_TAGIGN; // makes no sense without Makro-Name
2263 //try converting macro symbol according to macro name
2264 bool bApplyWingdings = ConvertMacroSymbol( aName, aVText );
2265 aName = "StarOffice.Standard.Modul1." + aName;
2267 SwMacroField aField( static_cast<SwMacroFieldType*>(
2268 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_MACROFLD )), aName, aVText );
2270 if( !bApplyWingdings )
2273 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2274 WW8_CP nOldCp = m_pPlcxMan->Where();
2275 WW8_CP nCp = nOldCp + nOffset;
2277 SwPaM aPaM(*m_pPaM, m_pPaM);
2278 aPaM.SetMark();
2279 aPaM.Move(fnMoveBackward);
2280 aPaM.Exchange();
2282 m_pPostProcessAttrsInfo = new WW8PostProcessAttrsInfo(nCp, nCp, aPaM);
2284 else
2286 //set Wingdings font
2287 sal_uInt16 i = 0;
2288 for ( ; i < m_pFonts->GetMax(); i++ )
2290 FontFamily eFamily;
2291 OUString aFontName;
2292 FontPitch ePitch;
2293 rtl_TextEncoding eSrcCharSet;
2294 if( GetFontParams( i, eFamily, aFontName, ePitch, eSrcCharSet )
2295 && aFontName=="Wingdings" )
2297 break;
2301 if ( i < m_pFonts->GetMax() )
2304 SetNewFontAttr( i, true, RES_CHRATR_FONT );
2305 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2306 m_pCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT );
2307 ResetCharSetVars();
2311 return FLD_OK;
2314 WW8PostProcessAttrsInfo::WW8PostProcessAttrsInfo(WW8_CP nCpStart, WW8_CP nCpEnd,
2315 SwPaM & rPaM)
2316 : mbCopy(false)
2317 , mnCpStart(nCpStart)
2318 , mnCpEnd(nCpEnd)
2319 , mPaM(*rPaM.GetMark(), *rPaM.GetPoint())
2320 , mItemSet(rPaM.GetDoc()->GetAttrPool(), RES_CHRATR_BEGIN, RES_PARATR_END - 1)
2324 bool CanUseRemoteLink(const OUString &rGrfName)
2326 bool bUseRemote = false;
2329 // Related: tdf#102499, add a default css::ucb::XCommandEnvironment
2330 // in order to have https protocol manage certificates correctly
2331 uno::Reference< task::XInteractionHandler > xIH(
2332 task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr));
2334 uno::Reference< ucb::XProgressHandler > xProgress;
2335 ::ucbhelper::CommandEnvironment* pCommandEnv =
2336 new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH ), xProgress);
2338 ::ucbhelper::Content aCnt(rGrfName,
2339 static_cast< ucb::XCommandEnvironment* >(pCommandEnv),
2340 comphelper::getProcessComponentContext());
2342 if ( !INetURLObject( rGrfName ).isAnyKnownWebDAVScheme() )
2344 OUString aTitle;
2345 aCnt.getPropertyValue("Title") >>= aTitle;
2346 bUseRemote = !aTitle.isEmpty();
2348 else
2350 // is a link to a WebDAV resource
2351 // need to use MediaType to check for link usability
2352 OUString aMediaType;
2353 aCnt.getPropertyValue("MediaType") >>= aMediaType;
2354 bUseRemote = !aMediaType.isEmpty();
2357 catch ( ... )
2359 // this file did not exist, so we will not set this as graphiclink
2360 bUseRemote = false;
2362 return bUseRemote;
2365 // "EINF"UGENGRAFIK"
2366 eF_ResT SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr )
2368 OUString aGrfName;
2369 bool bEmbedded = true;
2371 WW8ReadFieldParams aReadParam( rStr );
2372 for (;;)
2374 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2375 if ( nRet==-1 )
2376 break;
2377 switch( nRet )
2379 case -2:
2380 if (aGrfName.isEmpty())
2381 aGrfName = ConvertFFileName(aReadParam.GetResult());
2382 break;
2384 case 'd':
2385 bEmbedded = false;
2386 break;
2388 case 'c':// den Converter-Namen ueberlesen
2389 aReadParam.FindNextStringPiece();
2390 break;
2394 if (!bEmbedded)
2395 bEmbedded = !CanUseRemoteLink(aGrfName);
2397 if (!bEmbedded)
2400 Besonderheit:
2402 Wir setzen jetzt den Link ins Doc und merken uns den SwFlyFrameFormat.
2403 Da wir ja unten auf jjeden Fall mit Return-Wert FLD_READ_FSPA enden,
2404 wird der Skip-Wert so bemessen, dass das folgende Char-1 eingelesen
2405 wird.
2406 Wenn wir dann in SwWW8ImplReader::ImportGraf() reinlaufen, wird
2407 erkannt, dass wir soeben einen Grafik-Link inserted haben und
2408 das passende SwAttrSet wird ins Frame-Format eingesetzt.
2410 SfxItemSet aFlySet( m_rDoc.GetAttrPool(), RES_FRMATR_BEGIN,
2411 RES_FRMATR_END-1 );
2412 aFlySet.Put( SwFormatAnchor( FLY_AS_CHAR ) );
2413 aFlySet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
2414 m_pFlyFormatOfJustInsertedGraphic = m_rDoc.getIDocumentContentOperations().Insert( *m_pPaM,
2415 aGrfName,
2416 OUString(),
2417 nullptr, // Graphic*
2418 &aFlySet,
2419 nullptr, nullptr); // SwFrameFormat*
2420 m_aGrfNameGenerator.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic,
2421 INetURLObject(aGrfName).GetBase());
2423 return FLD_READ_FSPA;
2426 OUString wwSectionNamer::UniqueName()
2428 const OUString aName(msFileLinkSeed + OUString::number(++mnFileSectionNo));
2429 return mrDoc.GetUniqueSectionName(&aName);
2432 // "EINFUEGENTEXT"
2433 eF_ResT SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc* /*pF*/, OUString& rStr )
2435 OUString aPara;
2436 OUString aBook;
2437 WW8ReadFieldParams aReadParam( rStr );
2438 for (;;)
2440 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2441 if ( nRet==-1 )
2442 break;
2443 switch( nRet )
2445 case -2:
2446 if( aPara.isEmpty() )
2447 aPara = aReadParam.GetResult();
2448 else if( aBook.isEmpty() )
2449 aBook = aReadParam.GetResult();
2450 break;
2451 case '*':
2452 //Skip over MERGEFORMAT
2453 (void)aReadParam.SkipToNextToken();
2454 break;
2457 aPara = ConvertFFileName(aPara);
2459 if (!aBook.isEmpty() && aBook[ 0 ] != '\\')
2461 // Bereich aus Quelle ( kein Switch ) ?
2462 ConvertUFName(aBook);
2463 aPara += OUStringLiteral1(sfx2::cTokenSeparator)
2464 + OUStringLiteral1(sfx2::cTokenSeparator) + aBook;
2468 ##509##
2469 What we will do is insert a section to be linked to a file, but just in
2470 case the file is not available we will fill in the section with the stored
2471 content of this winword field as a fallback.
2473 SwPosition aTmpPos(*m_pPaM->GetPoint());
2475 SwSectionData aSection(FILE_LINK_SECTION,
2476 m_aSectionNameGenerator.UniqueName());
2477 aSection.SetLinkFileName( aPara );
2478 aSection.SetProtectFlag(true);
2480 SwSection *const pSection =
2481 m_rDoc.InsertSwSection(*m_pPaM, aSection, nullptr, nullptr, false);
2482 OSL_ENSURE(pSection, "no section inserted");
2483 if (!pSection)
2484 return FLD_TEXT;
2485 const SwSectionNode* pSectionNode = pSection->GetFormat()->GetSectionNode();
2486 OSL_ENSURE(pSectionNode, "no section node!");
2487 if (!pSectionNode)
2488 return FLD_TEXT;
2490 m_pPaM->GetPoint()->nNode = pSectionNode->GetIndex()+1;
2491 m_pPaM->GetPoint()->nContent.Assign(m_pPaM->GetContentNode(), 0 );
2493 //we have inserted a section before this point, so adjust pos
2494 //for future page/section segment insertion
2495 m_aSectionManager.PrependedInlineNode(aTmpPos, m_pPaM->GetNode());
2497 return FLD_TEXT;
2500 // "SERIENDRUCKFELD"
2501 eF_ResT SwWW8ImplReader::Read_F_DBField( WW8FieldDesc* pF, OUString& rStr )
2503 #if !HAVE_FEATURE_DBCONNECTIVITY
2504 (void) pF;
2505 (void) rStr;
2506 #else
2507 OUString aName;
2508 WW8ReadFieldParams aReadParam( rStr );
2509 for (;;)
2511 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2512 if ( nRet==-1 )
2513 break;
2514 switch( nRet )
2516 case -2:
2517 if( aName.isEmpty() )
2518 aName = aReadParam.GetResult();
2519 break;
2522 SwDBFieldType aD( &m_rDoc, aName, SwDBData() ); // Datenbank: Nichts
2524 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aD );
2525 SwDBField aField( static_cast<SwDBFieldType*>(pFT) );
2526 aField.SetFieldCode( rStr );
2528 OUString aResult;
2529 m_pSBase->WW8ReadString( *m_pStrm, aResult, m_pPlcxMan->GetCpOfs()+
2530 pF->nSRes, pF->nLRes, m_eTextCharSet );
2532 aResult = aResult.replace( '\xb', '\n' );
2534 aField.InitContent(aResult);
2536 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField( aField ));
2537 #endif
2538 return FLD_OK;
2541 // "N"ACHSTER"
2542 eF_ResT SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc*, OUString& )
2544 #if HAVE_FEATURE_DBCONNECTIVITY
2545 SwDBNextSetFieldType aN;
2546 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
2547 SwDBNextSetField aField( static_cast<SwDBNextSetFieldType*>(pFT), OUString(), OUString(),
2548 SwDBData() ); // Datenbank: Nichts
2549 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2550 #endif
2551 return FLD_OK;
2554 // "DATENSATZ"
2555 eF_ResT SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc*, OUString& )
2557 #if HAVE_FEATURE_DBCONNECTIVITY
2558 SwDBSetNumberFieldType aN;
2559 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
2560 SwDBSetNumberField aField( static_cast<SwDBSetNumberFieldType*>(pFT),
2561 SwDBData() ); // Datenbank: Nichts
2562 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
2563 #endif
2564 return FLD_OK;
2568 EQ , only the usage for
2569 a. Combined Characters supported, must be exactly in the form that word
2570 only accepts as combined characters, i.e.
2571 eq \o(\s\up Y(XXX),\s\do Y(XXX))
2572 b. Ruby Text supported, must be in the form that word recognizes as being
2573 ruby text
2576 eF_ResT SwWW8ImplReader::Read_F_Equation( WW8FieldDesc*, OUString& rStr )
2578 WW8ReadFieldParams aReadParam( rStr );
2579 const sal_Int32 cChar = aReadParam.SkipToNextToken();
2580 if ('o' == cChar || 'O' == cChar)
2582 EquationResult aResult(ParseCombinedChars(rStr));
2584 if (aResult.sType == "Input")
2586 SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( RES_INPUTFLD )),
2587 aResult.sResult, aResult.sResult, INP_TXT, 0 );
2588 m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); // insert input field
2590 else if (aResult.sType == "CombinedCharacters")
2592 SwCombinedCharField aField(static_cast<SwCombinedCharFieldType*>(
2593 m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(RES_COMBINED_CHARS)), aResult.sType);
2594 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
2597 else if ('*' == cChar)
2598 Read_SubF_Ruby(aReadParam);
2600 return FLD_OK;
2603 void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams& rReadParam)
2605 sal_uInt16 nJustificationCode=0;
2606 OUString sFontName;
2607 sal_uInt32 nFontSize=0;
2608 OUString sRuby;
2609 OUString sText;
2610 for (;;)
2612 const sal_Int32 nRet = rReadParam.SkipToNextToken();
2613 if ( nRet==-1 )
2614 break;
2615 switch( nRet )
2617 case -2:
2619 OUString sTemp = rReadParam.GetResult();
2620 if( sTemp.startsWithIgnoreAsciiCase( "jc" ) )
2622 sTemp = sTemp.copy(2);
2623 nJustificationCode = static_cast<sal_uInt16>(sTemp.toInt32());
2625 else if( sTemp.startsWithIgnoreAsciiCase( "hps" ) )
2627 sTemp = sTemp.copy(3);
2628 nFontSize= static_cast<sal_uInt32>(sTemp.toInt32());
2630 else if( sTemp.startsWithIgnoreAsciiCase( "Font:" ) )
2632 sTemp = sTemp.copy(5);
2633 sFontName = sTemp;
2636 break;
2637 case '*':
2638 break;
2639 case 'o':
2640 for (;;)
2642 const sal_Int32 nRes = rReadParam.SkipToNextToken();
2643 if ( nRes==-1 )
2644 break;
2645 if ('u' == nRes)
2647 if (-2 == rReadParam.SkipToNextToken() &&
2648 rReadParam.GetResult().startsWithIgnoreAsciiCase("p"))
2650 if (-2 == rReadParam.SkipToNextToken())
2652 OUString sPart = rReadParam.GetResult();
2653 sal_Int32 nBegin = sPart.indexOf('(');
2655 //Word disallows brackets in this field,
2656 sal_Int32 nEnd = sPart.indexOf(')');
2658 if ((nBegin != -1) &&
2659 (nEnd != -1) && (nBegin < nEnd))
2661 sRuby = sPart.copy(nBegin+1,nEnd-nBegin-1);
2663 if (-1 != nEnd)
2665 if (-1 ==
2666 (nBegin = sPart.indexOf(',',nEnd)))
2668 nBegin = sPart.indexOf(';',nEnd);
2670 nEnd = sPart.lastIndexOf(')');
2672 if ((nBegin != -1) && (nEnd != -1) && (nBegin < nEnd))
2674 sText = sPart.copy(nBegin+1,nEnd-nBegin-1);
2681 break;
2685 //Translate and apply
2686 if (!sRuby.isEmpty() && !sText.isEmpty() && !sFontName.isEmpty() && nFontSize)
2688 switch (nJustificationCode)
2690 case 0:
2691 nJustificationCode=1;
2692 break;
2693 case 1:
2694 nJustificationCode=3;
2695 break;
2696 case 2:
2697 nJustificationCode=4;
2698 break;
2699 default:
2700 case 3:
2701 nJustificationCode=0;
2702 break;
2703 case 4:
2704 nJustificationCode=2;
2705 break;
2708 SwFormatRuby aRuby(sRuby);
2709 const SwCharFormat *pCharFormat=nullptr;
2710 //Make a guess at which of asian of western we should be setting
2711 sal_uInt16 nScript;
2712 if (g_pBreakIt->GetBreakIter().is())
2713 nScript = g_pBreakIt->GetBreakIter()->getScriptType(sRuby, 0);
2714 else
2715 nScript = i18n::ScriptType::ASIAN;
2717 //Check to see if we already have a ruby charstyle that this fits
2718 std::vector<const SwCharFormat*>::const_iterator aEnd =
2719 m_aRubyCharFormats.end();
2720 for(std::vector<const SwCharFormat*>::const_iterator aIter
2721 = m_aRubyCharFormats.begin(); aIter != aEnd; ++aIter)
2723 const SvxFontHeightItem &rFH =
2724 ItemGet<SvxFontHeightItem>(*(*aIter),
2725 GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
2726 if (rFH.GetHeight() == nFontSize*10)
2728 const SvxFontItem &rF = ItemGet<SvxFontItem>(*(*aIter),
2729 GetWhichOfScript(RES_CHRATR_FONT,nScript));
2730 if (rF.GetFamilyName().equals(sFontName))
2732 pCharFormat=*aIter;
2733 break;
2738 //Create a new char style if necessary
2739 if (!pCharFormat)
2741 SwCharFormat *pFormat=nullptr;
2742 OUString aNm;
2743 //Take this as the base name
2744 SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT,aNm);
2745 aNm+=OUString::number(m_aRubyCharFormats.size()+1);
2746 pFormat = m_rDoc.MakeCharFormat(aNm, m_rDoc.GetDfltCharFormat());
2747 SvxFontHeightItem aHeightItem(nFontSize*10, 100, RES_CHRATR_FONTSIZE);
2748 SvxFontItem aFontItem(FAMILY_DONTKNOW,sFontName,
2749 OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
2750 aHeightItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
2751 aFontItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONT,nScript));
2752 pFormat->SetFormatAttr(aHeightItem);
2753 pFormat->SetFormatAttr(aFontItem);
2754 m_aRubyCharFormats.push_back(pFormat);
2755 pCharFormat = pFormat;
2758 //Set the charstyle and justification
2759 aRuby.SetCharFormatName(pCharFormat->GetName());
2760 aRuby.SetCharFormatId(pCharFormat->GetPoolFormatId());
2761 aRuby.SetAdjustment(nJustificationCode);
2763 NewAttr(aRuby);
2764 m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, sText );
2765 m_pCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CJK_RUBY );
2769 // "table of ..." fields
2771 static void lcl_toxMatchACSwitch( SwWW8ImplReader& /*rReader*/,
2772 SwDoc& rDoc,
2773 SwTOXBase& rBase,
2774 WW8ReadFieldParams& rParam,
2775 SwCaptionDisplay eCaptionType)
2777 if ( rParam.GoToTokenParam() )
2779 SwTOXType* pType = const_cast<SwTOXType*>(rDoc.GetTOXType( TOX_ILLUSTRATIONS, 0));
2780 rBase.RegisterToTOXType( *pType );
2781 rBase.SetCaptionDisplay( eCaptionType );
2782 // Read Sequence Name and store in TOXBase
2783 OUString sSeqName( rParam.GetResult() );
2784 lcl_ConvertSequenceName( sSeqName );
2785 rBase.SetSequenceName( sSeqName );
2789 static void EnsureMaxLevelForTemplates(SwTOXBase& rBase)
2791 //If the TOC contains Template entries at levels > the evaluation level
2792 //that was initially taken from the max normal outline level of the word TOC
2793 //then we cannot use that for the evaluation level because writer cuts off
2794 //all styles above that level, while word just cuts off the "standard"
2795 //outline styles, we have no option but to expand to the highest level
2796 //Word included.
2797 if ((rBase.GetLevel() != MAXLEVEL) && (nsSwTOXElement::TOX_TEMPLATE & rBase.GetCreateType()))
2799 for (sal_uInt16 nI = MAXLEVEL; nI > 0; --nI)
2801 if (!rBase.GetStyleNames(nI-1).isEmpty())
2803 rBase.SetLevel(nI);
2804 break;
2810 static void lcl_toxMatchTSwitch(SwWW8ImplReader& rReader, SwTOXBase& rBase,
2811 WW8ReadFieldParams& rParam)
2813 if ( rParam.GoToTokenParam() )
2815 OUString sParams( rParam.GetResult() );
2816 if( !sParams.isEmpty() )
2818 sal_Int32 nIndex = 0;
2820 // Delimiters between styles and style levels appears to allow both ; and ,
2822 OUString sTemplate( sParams.getToken(0, ';', nIndex) );
2823 if( -1 == nIndex )
2825 nIndex=0;
2826 sTemplate = sParams.getToken(0, ',', nIndex);
2828 if( -1 == nIndex )
2830 const SwFormat* pStyle = rReader.GetStyleWithOrgWWName(sTemplate);
2831 if( pStyle )
2832 sTemplate = pStyle->GetName();
2833 // Store Style for Level 0 into TOXBase
2834 rBase.SetStyleNames( sTemplate, 0 );
2836 else while( -1 != nIndex )
2838 sal_Int32 nOldIndex=nIndex;
2839 sal_uInt16 nLevel = static_cast<sal_uInt16>(
2840 sParams.getToken(0, ';', nIndex).toInt32());
2841 if( -1 == nIndex )
2843 nIndex = nOldIndex;
2844 nLevel = static_cast<sal_uInt16>(
2845 sParams.getToken(0, ',', nIndex).toInt32());
2848 if( (0 < nLevel) && (MAXLEVEL >= nLevel) )
2850 nLevel--;
2851 // Store Style and Level into TOXBase
2852 const SwFormat* pStyle
2853 = rReader.GetStyleWithOrgWWName( sTemplate );
2855 if( pStyle )
2856 sTemplate = pStyle->GetName();
2858 OUString sStyles( rBase.GetStyleNames( nLevel ) );
2859 if( !sStyles.isEmpty() )
2860 sStyles += OUStringLiteral1(TOX_STYLE_DELIMITER);
2861 sStyles += sTemplate;
2862 rBase.SetStyleNames( sStyles, nLevel );
2864 // read next style name...
2865 nOldIndex = nIndex;
2866 sTemplate = sParams.getToken(0, ';', nIndex);
2867 if( -1 == nIndex )
2869 nIndex=nOldIndex;
2870 sTemplate = sParams.getToken(0, ',', nIndex);
2877 sal_uInt16 wwSectionManager::CurrentSectionColCount() const
2879 sal_uInt16 nIndexCols = 1;
2880 if (!maSegments.empty())
2881 nIndexCols = maSegments.back().maSep.ccolM1 + 1;
2882 return nIndexCols;
2885 //Will there be a new pagebreak at this position (don't know what type
2886 //until later)
2887 bool wwSectionManager::WillHavePageDescHere(const SwNodeIndex& rIdx) const
2889 bool bRet = false;
2890 if (!maSegments.empty())
2892 if (!maSegments.back().IsContinuous() &&
2893 maSegments.back().maStart == rIdx)
2895 bRet = true;
2898 return bRet;
2901 static sal_uInt16 lcl_GetMaxValidWordTOCLevel(const SwForm &rForm)
2903 // GetFormMax() returns level + 1, hence the -1
2904 sal_uInt16 nRet = rForm.GetFormMax()-1;
2906 // If the max of this type of TOC is greater than the max of a word
2907 // possible toc, then clip to the word max
2908 if (nRet > WW8ListManager::nMaxLevel)
2909 nRet = WW8ListManager::nMaxLevel;
2911 return nRet;
2914 eF_ResT SwWW8ImplReader::Read_F_Tox( WW8FieldDesc* pF, OUString& rStr )
2916 if (!m_bLoadingTOXCache)
2918 m_bLoadingTOXCache = true;
2920 else
2922 // Embedded TOX --> continue reading its content, but no further TOX
2923 // field
2924 ++m_nEmbeddedTOXLevel;
2925 return FLD_TEXT;
2928 if (pF->nLRes < 3)
2929 return FLD_TEXT; // ignore (#i25440#)
2931 TOXTypes eTox; // create a ToxBase
2932 switch( pF->nId )
2934 case 8:
2935 eTox = TOX_INDEX;
2936 break;
2937 case 13:
2938 eTox = TOX_CONTENT;
2939 break;
2940 default:
2941 eTox = TOX_USER;
2942 break;
2945 sal_uInt16 nCreateOf = (eTox == TOX_CONTENT) ? nsSwTOXElement::TOX_OUTLINELEVEL : nsSwTOXElement::TOX_MARK;
2947 sal_uInt16 nIndexCols = 1;
2949 const SwTOXType* pType = m_rDoc.GetTOXType( eTox, 0 );
2950 SwForm aOrigForm(eTox);
2951 SwTOXBase* pBase = new SwTOXBase( pType, aOrigForm, nCreateOf, OUString() );
2952 pBase->SetProtected(m_aSectionManager.CurrentSectionIsProtected());
2953 switch( eTox ){
2954 case TOX_INDEX:
2956 sal_uInt16 eOptions = nsSwTOIOptions::TOI_SAME_ENTRY | nsSwTOIOptions::TOI_CASE_SENSITIVE;
2958 // TOX_OUTLINELEVEL setzen wir genau dann, wenn
2959 // die Parameter \o in 1 bis 9 liegen
2960 // oder der Parameter \f existiert
2961 // oder GARKEINE Switches Parameter angegeben sind.
2962 WW8ReadFieldParams aReadParam( rStr );
2963 for (;;)
2965 const sal_Int32 nRet = aReadParam.SkipToNextToken();
2966 if ( nRet==-1 )
2967 break;
2968 switch( nRet )
2970 case 'c':
2971 if ( aReadParam.GoToTokenParam() )
2973 const OUString sParams( aReadParam.GetResult() );
2974 // if NO OUString just ignore the \c
2975 if( !sParams.isEmpty() )
2977 nIndexCols = static_cast<sal_uInt16>(sParams.toInt32());
2980 break;
2981 case 'e':
2983 if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \e
2985 OUString sDelimiter( aReadParam.GetResult() );
2986 SwForm aForm( pBase->GetTOXForm() );
2988 // Attention: if TOX_CONTENT brave
2989 // GetFormMax() returns MAXLEVEL + 1 !!
2990 sal_uInt16 nEnd = aForm.GetFormMax()-1;
2992 for(sal_uInt16 nLevel = 1;
2993 nLevel <= nEnd;
2994 ++nLevel)
2996 // Levels count from 1
2997 // Level 0 is reserved for CAPTION
2999 // Delimiter statt Tabstop vor der Seitenzahl einsetzen,
3000 // falls es eine Seitenzahl gibt:
3001 FormTokenType ePrevType = TOKEN_END;
3002 FormTokenType eType;
3003 // -> #i21237#
3004 SwFormTokens aPattern =
3005 aForm.GetPattern(nLevel);
3006 SwFormTokens::iterator aIt = aPattern.begin();
3009 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3011 if (eType == TOKEN_PAGE_NUMS)
3013 if (TOKEN_TAB_STOP == ePrevType)
3015 --aIt;
3017 if(0x09 == sDelimiter[0])
3018 aIt->eTabAlign = SvxTabAdjust::End;
3019 else
3021 SwFormToken aToken(TOKEN_TEXT);
3022 aToken.sText = sDelimiter;
3023 *aIt = aToken;
3025 aForm.SetPattern(nLevel, aPattern);
3028 eType = TOKEN_END;
3031 ePrevType = eType;
3033 while (TOKEN_END != eType);
3034 // <- #i21237#
3036 pBase->SetTOXForm( aForm );
3039 break;
3040 case 'h':
3042 eOptions |= nsSwTOIOptions::TOI_ALPHA_DELIMITTER;
3044 break;
3047 pBase->SetOptions( eOptions );
3049 break;
3051 case TOX_CONTENT:
3053 bool bIsHyperlink = false;
3054 bool bShowPage = true;
3055 // TOX_OUTLINELEVEL setzen wir genau dann, wenn
3056 // die Parameter \o in 1 bis 9 liegen
3057 // oder der Parameter \f existiert
3058 // oder GARKEINE Switches Parameter angegeben sind.
3059 sal_uInt16 eCreateFrom = 0;
3060 sal_Int32 nMaxLevel = 0;
3061 WW8ReadFieldParams aReadParam( rStr );
3062 for (;;)
3064 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3065 if ( nRet==-1 )
3066 break;
3067 switch( nRet )
3069 case 'h':
3070 bIsHyperlink = true;
3071 break;
3072 case 'a':
3073 case 'c':
3074 lcl_toxMatchACSwitch(*this, m_rDoc, *pBase, aReadParam,
3075 ('c' == nRet)
3076 ? CAPTION_COMPLETE
3077 : CAPTION_TEXT );
3078 break;
3079 case 'o':
3081 sal_Int32 nVal;
3082 if( !aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
3083 nVal = lcl_GetMaxValidWordTOCLevel(aOrigForm);
3084 if( nMaxLevel < nVal )
3085 nMaxLevel = nVal;
3086 eCreateFrom |= nsSwTOXElement::TOX_OUTLINELEVEL;
3088 break;
3089 case 'f':
3090 eCreateFrom |= nsSwTOXElement::TOX_MARK;
3091 break;
3092 case 'l':
3094 sal_Int32 nVal;
3095 if( aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
3097 if( nMaxLevel < nVal )
3098 nMaxLevel = nVal;
3099 eCreateFrom |= nsSwTOXElement::TOX_MARK;
3102 break;
3103 case 't': // paragraphs using special styles shall
3104 // provide the TOX's content
3105 lcl_toxMatchTSwitch(*this, *pBase, aReadParam);
3106 eCreateFrom |= nsSwTOXElement::TOX_TEMPLATE;
3107 break;
3108 case 'p':
3110 if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \p
3112 OUString sDelimiter( aReadParam.GetResult() );
3113 SwForm aForm( pBase->GetTOXForm() );
3115 // Attention: if TOX_CONTENT brave
3116 // GetFormMax() returns MAXLEVEL + 1 !!
3117 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3119 for(sal_uInt16 nLevel = 1;
3120 nLevel <= nEnd;
3121 ++nLevel)
3123 // Levels count from 1
3124 // Level 0 is reserved for CAPTION
3126 // Delimiter statt Tabstop vor der Seitenzahl einsetzen,
3127 // falls es eine Seitenzahl gibt:
3128 FormTokenType ePrevType = TOKEN_END;
3129 FormTokenType eType;
3131 // -> #i21237#
3132 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3133 SwFormTokens::iterator aIt = aPattern.begin();
3136 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3138 if (eType == TOKEN_PAGE_NUMS)
3140 if (TOKEN_TAB_STOP == ePrevType)
3142 --aIt;
3144 SwFormToken aToken(TOKEN_TEXT);
3145 aToken.sText = sDelimiter;
3147 *aIt = aToken;
3148 aForm.SetPattern(nLevel,
3149 aPattern);
3151 eType = TOKEN_END;
3153 ePrevType = eType;
3155 while( TOKEN_END != eType );
3156 // <- #i21237#
3158 pBase->SetTOXForm( aForm );
3161 break;
3162 case 'n': // don't print page numbers
3164 // read START and END param
3165 sal_Int32 nStart(0);
3166 sal_Int32 nEnd(0);
3167 if( !aReadParam.GetTokenSttFromTo( &nStart, &nEnd,
3168 WW8ListManager::nMaxLevel ) )
3170 nStart = 1;
3171 nEnd = aOrigForm.GetFormMax()-1;
3173 // remove page numbers from this levels
3174 SwForm aForm( pBase->GetTOXForm() );
3175 if (aForm.GetFormMax() <= nEnd)
3176 nEnd = aForm.GetFormMax()-1;
3177 for ( sal_Int32 nLevel = nStart; nLevel<=nEnd; ++nLevel )
3179 // Levels count from 1
3180 // Level 0 is reserved for CAPTION
3182 // Seitenzahl und ggfs. davorstehenden Tabstop
3183 // entfernen:
3184 FormTokenType eType;
3185 // -> #i21237#
3186 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3187 SwFormTokens::iterator aIt = aPattern.begin();
3190 eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
3192 if (eType == TOKEN_PAGE_NUMS)
3194 aIt = aPattern.erase(aIt);
3195 --aIt;
3196 if (
3197 TOKEN_TAB_STOP ==
3198 aIt->eTokenType
3201 aPattern.erase(aIt);
3202 aForm.SetPattern(nLevel, aPattern);
3204 eType = TOKEN_END;
3207 while (TOKEN_END != eType);
3208 // <- #i21237#
3210 pBase->SetTOXForm( aForm );
3212 break;
3215 // the following switches are not (yet) supported
3216 // by good old StarWriter:
3217 case 'b':
3218 case 's':
3219 case 'd':
3220 break;
3225 // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens
3226 // also include the hyperlinks and page references
3227 SwFormToken aLinkStart(TOKEN_LINK_START);
3228 SwFormToken aLinkEnd(TOKEN_LINK_END);
3229 aLinkStart.sCharStyleName = "Index Link";
3230 aLinkEnd.sCharStyleName = "Index Link";
3231 SwForm aForm(pBase->GetTOXForm());
3232 sal_uInt16 nEnd = aForm.GetFormMax()-1;
3234 for(sal_uInt16 nLevel = 1; nLevel <= nEnd; ++nLevel)
3236 SwFormTokens aPattern = aForm.GetPattern(nLevel);
3237 if ( bIsHyperlink )
3239 aPattern.insert(aPattern.begin(), aLinkStart);
3241 else if ( bShowPage )
3243 for (SwFormTokens::iterator aItr = aPattern.begin();aItr!= aPattern.end();++aItr)
3245 if (aItr->eTokenType == TOKEN_PAGE_NUMS)
3247 aPattern.insert(aItr,aLinkStart);
3248 break;
3252 aPattern.push_back(aLinkEnd);
3253 aForm.SetPattern(nLevel, aPattern);
3255 pBase->SetTOXForm(aForm);
3257 if (!nMaxLevel)
3258 nMaxLevel = WW8ListManager::nMaxLevel;
3259 pBase->SetLevel(nMaxLevel);
3261 const TOXTypes eType = pBase->GetTOXType()->GetType();
3262 switch( eType )
3264 case TOX_CONTENT:
3266 //If we would be created from outlines, either explicitly or by default
3267 //then see if we need extra styles added to the outlines
3268 sal_uInt16 eEffectivelyFrom = eCreateFrom ? eCreateFrom : nsSwTOXElement::TOX_OUTLINELEVEL;
3269 if (eEffectivelyFrom & nsSwTOXElement::TOX_OUTLINELEVEL)
3271 // #i19683# Insert a text token " " between the number and entry token.
3272 // In an ideal world we could handle the tab stop between the number and
3273 // the entry correctly, but I currently have no clue how to obtain
3274 // the tab stop position. It is _not_ set at the paragraph style.
3275 SwForm* pForm = nullptr;
3276 for (SwWW8StyInf & rSI : m_vColl)
3278 if (rSI.IsOutlineNumbered())
3280 sal_uInt16 nStyleLevel = rSI.mnWW8OutlineLevel;
3281 const SwNumFormat& rFormat = rSI.GetOutlineNumrule()->Get( nStyleLevel );
3282 if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() )
3284 ++nStyleLevel;
3286 if ( !pForm )
3287 pForm = new SwForm( pBase->GetTOXForm() );
3289 SwFormTokens aPattern = pForm->GetPattern(nStyleLevel);
3290 SwFormTokens::iterator aIt =
3291 find_if(aPattern.begin(), aPattern.end(),
3292 SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
3294 if ( aIt != aPattern.end() )
3296 SwFormToken aNumberEntrySeparator( TOKEN_TEXT );
3297 aNumberEntrySeparator.sText = " ";
3298 aPattern.insert( ++aIt, aNumberEntrySeparator );
3299 pForm->SetPattern( nStyleLevel, aPattern );
3304 if ( pForm )
3306 pBase->SetTOXForm( *pForm );
3307 delete pForm;
3311 if (eCreateFrom)
3312 pBase->SetCreate(eCreateFrom);
3313 EnsureMaxLevelForTemplates(*pBase);
3315 break;
3316 case TOX_ILLUSTRATIONS:
3318 if( !eCreateFrom )
3319 eCreateFrom = nsSwTOXElement::TOX_SEQUENCE;
3320 pBase->SetCreate( eCreateFrom );
3323 We don't know until here if we are an illustration
3324 or not, and so have being used a TOX_CONTENT so far
3325 which has 10 levels, while TOX has only two, this
3326 level is set only in the constructor of SwForm, so
3327 create a new one and copy over anything that could
3328 be set in the old one, and remove entries from the
3329 pattern which do not apply to illustration indices
3331 SwForm aOldForm( pBase->GetTOXForm() );
3332 SwForm aNewForm( eType );
3333 sal_uInt16 nNewEnd = aNewForm.GetFormMax()-1;
3335 // #i21237#
3336 for(sal_uInt16 nLevel = 1; nLevel <= nNewEnd; ++nLevel)
3338 SwFormTokens aPattern = aOldForm.GetPattern(nLevel);
3339 SwFormTokens::iterator new_end =
3340 remove_if(aPattern.begin(), aPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
3341 aPattern.erase(new_end, aPattern.end() ); // table index imported with wrong page number format
3342 aForm.SetPattern( nLevel, aPattern );
3343 aForm.SetTemplate( nLevel, aOldForm.GetTemplate(nLevel) );
3346 pBase->SetTOXForm( aNewForm );
3348 break;
3349 default:
3350 OSL_ENSURE(false, "Unhandled toc options!");
3351 break;
3354 break;
3355 case TOX_USER:
3356 break;
3357 default:
3358 OSL_ENSURE(false, "Unhandled toc options!");
3359 break;
3360 } // ToxBase fertig
3362 // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX
3363 pBase->AdjustTabStops( m_rDoc );
3365 //#i10028# inserting a toc implicltly acts like a parabreak in word and writer
3366 if ( m_pPaM->End() &&
3367 m_pPaM->End()->nNode.GetNode().GetTextNode() &&
3368 m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() != 0 )
3370 m_bCareFirstParaEndInToc = true;
3373 if (m_pPaM->GetPoint()->nContent.GetIndex())
3374 AppendTextNode(*m_pPaM->GetPoint());
3376 const SwPosition* pPos = m_pPaM->GetPoint();
3378 SwFltTOX aFltTOX( pBase );
3380 // test if there is already a break item on this node
3381 if(SwContentNode* pNd = pPos->nNode.GetNode().GetContentNode())
3383 const SfxItemSet* pSet = pNd->GetpSwAttrSet();
3384 if( pSet )
3386 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
3387 aFltTOX.SetHadBreakItem(true);
3388 if (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false))
3389 aFltTOX.SetHadPageDescItem(true);
3393 //Will there be a new pagebreak at this position (don't know what type
3394 //until later)
3395 if (m_aSectionManager.WillHavePageDescHere(pPos->nNode))
3396 aFltTOX.SetHadPageDescItem(true);
3398 // Setze Anfang in Stack
3399 m_pReffedStck->NewAttr( *pPos, aFltTOX );
3401 m_rDoc.InsertTableOf(*m_pPaM->GetPoint(), *aFltTOX.GetBase());
3403 //The TOC field representation contents should be inserted into TOC section, but not after TOC section.
3404 //So we need update the document position when loading TOC representation and after loading TOC;
3405 m_pPosAfterTOC.reset(new SwPaM(*m_pPaM, m_pPaM));
3406 (*m_pPaM).Move(fnMoveBackward);
3407 SwPaM aRegion(*m_pPaM, m_pPaM);
3409 OSL_ENSURE(SwDoc::GetCurTOX(*aRegion.GetPoint()), "Misunderstood how toc works");
3410 if (SwTOXBase* pBase2 = SwDoc::GetCurTOX(*aRegion.GetPoint()))
3412 pBase2->SetMSTOCExpression(rStr);
3414 if ( nIndexCols > 1 )
3416 // Set the column number for index
3417 SfxItemSet aSet( m_rDoc.GetAttrPool(), RES_COL, RES_COL );
3418 SwFormatCol aCol;
3419 aCol.Init( nIndexCols, 708, USHRT_MAX );
3420 aSet.Put( aCol );
3421 pBase2->SetAttrSet( aSet );
3424 // inserting a toc inserts a section before this point, so adjust pos
3425 // for future page/section segment insertion
3426 m_aSectionManager.PrependedInlineNode( *m_pPosAfterTOC->GetPoint(), aRegion.GetNode() );
3429 // Setze Ende in Stack
3430 m_pReffedStck->SetAttr( *pPos, RES_FLTR_TOX );
3432 if (!m_aApos.back()) //a para end in apo doesn't count
3433 m_bWasParaEnd = true;
3435 //Return FLD_TEXT, instead of FLD_OK
3436 //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed
3437 //FLD_OK means the current field loading is finished. The rest part should be ignored.
3438 return FLD_TEXT;
3441 eF_ResT SwWW8ImplReader::Read_F_Shape(WW8FieldDesc* /*pF*/, OUString& /*rStr*/)
3444 #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring
3445 to be ignored followed by a 0x1 with an empty drawing. Detect in inserting
3446 the drawing that we are in the Shape field and respond accordingly
3448 return FLD_TEXT;
3451 eF_ResT SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc* /*pF*/, OUString& rStr )
3453 OUString sURL, sTarget, sMark;
3455 //HYPERLINK "filename" [switches]
3456 rStr = comphelper::string::stripEnd(rStr, 1);
3458 bool bOptions = false;
3459 WW8ReadFieldParams aReadParam( rStr );
3460 for (;;)
3462 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3463 if ( nRet==-1 )
3464 break;
3465 switch( nRet )
3467 case -2:
3468 if (sURL.isEmpty() && !bOptions)
3469 sURL = ConvertFFileName(aReadParam.GetResult());
3470 break;
3472 case 'n':
3473 sTarget = "_blank";
3474 bOptions = true;
3475 break;
3477 case 'l':
3478 bOptions = true;
3479 if ( aReadParam.SkipToNextToken()==-2 )
3481 sMark = aReadParam.GetResult();
3482 if( sMark.endsWith("\""))
3484 sMark = sMark.copy( 0, sMark.getLength() - 1 );
3486 // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention
3487 if ( IsTOCBookmarkName( sMark ) )
3489 sMark = EnsureTOCBookmarkName(sMark);
3490 // track <sMark> as referenced TOC bookmark.
3491 m_pReffedStck->aReferencedTOCBookmarks.insert( sMark );
3494 if (m_bLoadingTOXCache)
3496 m_bLoadingTOXHyperlink = true; //on loading a TOC field nested hyperlink field
3499 break;
3500 case 't':
3501 bOptions = true;
3502 if ( aReadParam.SkipToNextToken()==-2 )
3503 sTarget = aReadParam.GetResult();
3504 break;
3505 case 'h':
3506 case 'm':
3507 OSL_ENSURE( false, "Auswertung fehlt noch - Daten unbekannt" );
3508 SAL_FALLTHROUGH;
3509 case 's': //worthless fake anchor option
3510 bOptions = true;
3511 break;
3515 // das Resultat uebernehmen
3516 OSL_ENSURE(!sURL.isEmpty() || !sMark.isEmpty(), "WW8: Empty URL");
3518 if( !sMark.isEmpty() )
3519 ( sURL += "#" ) += sMark;
3521 SwFormatINetFormat aURL(sURL, sTarget);
3522 // If on loading TOC field, change the default style into the "index link"
3523 if (m_bLoadingTOXCache)
3525 OUString sLinkStyle("Index Link");
3526 sal_uInt16 nPoolId =
3527 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
3528 aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId );
3529 aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
3532 //As an attribute this needs to be closed, and that'll happen from
3533 //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms
3534 //between the start and begin, their hyperlinks will be set at that time
3535 //as well.
3536 m_pCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
3537 return FLD_TEXT;
3540 static void lcl_ImportTox(SwDoc &rDoc, SwPaM &rPaM, const OUString &rStr, bool bIdx)
3542 TOXTypes eTox = ( !bIdx ) ? TOX_CONTENT : TOX_INDEX; // Default
3544 sal_uInt16 nLevel = 1;
3546 OUString sFieldText;
3547 WW8ReadFieldParams aReadParam(rStr);
3548 for (;;)
3550 const sal_Int32 nRet = aReadParam.SkipToNextToken();
3551 if ( nRet==-1 )
3552 break;
3553 switch( nRet )
3555 case -2:
3556 if( sFieldText.isEmpty() )
3558 // PrimaryKey ohne ":", 2nd dahinter
3559 sFieldText = aReadParam.GetResult();
3561 break;
3563 case 'f':
3564 if ( aReadParam.GoToTokenParam() )
3566 const OUString sParams( aReadParam.GetResult() );
3567 if( sParams[0]!='C' && sParams[0]!='c' )
3568 eTox = TOX_USER;
3570 break;
3572 case 'l':
3573 if ( aReadParam.GoToTokenParam() )
3575 const OUString sParams( aReadParam.GetResult() );
3576 // if NO String just ignore the \l
3577 if( !sParams.isEmpty() && sParams[0]>'0' && sParams[0]<='9' )
3579 nLevel = (sal_uInt16)sParams.toInt32();
3582 break;
3586 OSL_ENSURE( rDoc.GetTOXTypeCount( eTox ), "Doc.GetTOXTypeCount() == 0 :-(" );
3588 const SwTOXType* pT = rDoc.GetTOXType( eTox, 0 );
3589 SwTOXMark aM( pT );
3591 if( eTox != TOX_INDEX )
3592 aM.SetLevel( nLevel );
3593 else
3595 sal_Int32 nFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM );
3596 if( -1 != nFnd ) // it exist levels
3598 aM.SetPrimaryKey( sFieldText.copy( 0, nFnd ) );
3599 sal_Int32 nScndFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM, nFnd+1 );
3600 if( -1 != nScndFnd )
3602 aM.SetSecondaryKey( sFieldText.copy( nFnd+1, nScndFnd - nFnd - 1 ));
3603 nFnd = nScndFnd;
3605 sFieldText = sFieldText.copy( nFnd+1 );
3609 if (!sFieldText.isEmpty())
3611 aM.SetAlternativeText( sFieldText );
3612 rDoc.getIDocumentContentOperations().InsertPoolItem( rPaM, aM );
3616 void sw::ms::ImportXE(SwDoc &rDoc, SwPaM &rPaM, const OUString &rStr)
3618 lcl_ImportTox(rDoc, rPaM, rStr, true);
3621 void SwWW8ImplReader::ImportTox( int nFieldId, const OUString& aStr )
3623 bool bIdx = (nFieldId != 9);
3624 lcl_ImportTox(m_rDoc, *m_pPaM, aStr, bIdx);
3627 void SwWW8ImplReader::Read_FieldVanish( sal_uInt16, const sal_uInt8*, short nLen )
3629 //Meaningless in a style
3630 if (m_pAktColl || !m_pPlcxMan)
3631 return;
3633 const int nChunk = 64; //number of characters to read at one time
3635 // Vorsicht: Bei Feldnamen mit Umlauten geht das MEMICMP nicht!
3636 const static sal_Char *aFieldNames[] = { "\x06""INHALT", "\x02""XE", // dt.
3637 "\x02""TC" }; // us
3638 const static sal_uInt8 aFieldId[] = { 9, 4, 9 };
3640 if( nLen < 0 )
3642 m_bIgnoreText = false;
3643 return;
3646 // our methode was called from
3647 // ''Skip attributes of field contents'' loop within ReadTextAttr()
3648 if( m_bIgnoreText )
3649 return;
3651 m_bIgnoreText = true;
3652 long nOldPos = m_pStrm->Tell();
3654 WW8_CP nStartCp = m_pPlcxMan->Where() + m_pPlcxMan->GetCpOfs();
3656 OUString sFieldName;
3657 sal_Int32 nFieldLen = m_pSBase->WW8ReadString( *m_pStrm, sFieldName, nStartCp,
3658 nChunk, m_eStructCharSet );
3659 nStartCp+=nFieldLen;
3661 sal_Int32 nC = 0;
3662 //If the first chunk did not start with a field start then
3663 //reset the stream position and give up
3664 if( !nFieldLen || sFieldName[nC]!=0x13 ) // Field Start Mark
3666 // If Field End Mark found
3667 if( nFieldLen && sFieldName[nC]==0x15 )
3668 m_bIgnoreText = false;
3669 m_pStrm->Seek( nOldPos );
3670 return; // kein Feld zu finden
3673 sal_Int32 nFnd;
3674 //If this chunk does not contain a field end, keep reading chunks
3675 //until we find one, or we run out of text,
3676 for (;;)
3678 nFnd = sFieldName.indexOf(0x15);
3679 //found field end, we can stop now
3680 if (nFnd != -1)
3681 break;
3682 OUString sTemp;
3683 nFieldLen = m_pSBase->WW8ReadString( *m_pStrm, sTemp,
3684 nStartCp, nChunk, m_eStructCharSet );
3685 sFieldName+=sTemp;
3686 nStartCp+=nFieldLen;
3687 if (!nFieldLen)
3688 break;
3691 m_pStrm->Seek( nOldPos );
3693 //if we have no 0x15 give up, otherwise erase everything from the 0x15
3694 //onwards
3695 if (nFnd<0)
3696 return;
3698 sFieldName = sFieldName.copy(0, nFnd);
3700 nC++;
3701 while ( sFieldName[nC]==' ' )
3702 nC++;
3704 for( int i = 0; i < 3; i++ )
3706 const sal_Char* pName = aFieldNames[i];
3707 const sal_Int32 nNameLen = static_cast<sal_Int32>(*pName++);
3708 if( sFieldName.matchIgnoreAsciiCaseAsciiL( pName, nNameLen, nC ) )
3710 ImportTox( aFieldId[i], sFieldName.copy( nC + nNameLen ) );
3711 break; // keine Mehrfachnennungen moeglich
3714 m_bIgnoreText = true;
3715 m_pStrm->Seek( nOldPos );
3718 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */