1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
21 #include <config_fuzzers.h>
23 #include <sal/types.h>
24 #include <tools/solar.h>
25 #include <comphelper/processfactory.hxx>
26 #include <comphelper/string.hxx>
27 #include <comphelper/simplefileaccessinteraction.hxx>
28 #include <com/sun/star/embed/XStorage.hpp>
29 #include <com/sun/star/embed/ElementModes.hpp>
30 #include <com/sun/star/embed/XTransactedObject.hpp>
31 #include <com/sun/star/task/InteractionHandler.hpp>
33 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
34 #include <svl/cintitem.hxx>
35 #include <svl/lngmisc.hxx>
36 #include <svl/urihelper.hxx>
37 #include <svl/numformat.hxx>
38 #include <svl/zforlist.hxx>
39 #include <svl/zformat.hxx>
40 #include <sfx2/linkmgr.hxx>
41 #include <rtl/character.hxx>
42 #include <unotools/charclass.hxx>
44 #include <ucbhelper/content.hxx>
45 #include <ucbhelper/commandenvironment.hxx>
47 #include <com/sun/star/i18n/XBreakIterator.hpp>
48 #include <hintids.hxx>
49 #include <editeng/fontitem.hxx>
50 #include <editeng/fhgtitem.hxx>
51 #include <editeng/langitem.hxx>
53 #include <fmtanchr.hxx>
56 #include <IDocumentFieldsAccess.hxx>
57 #include <IDocumentMarkAccess.hxx>
58 #include <IDocumentState.hxx>
60 #include <docufld.hxx>
67 #include <section.hxx>
69 #include <fmtinfmt.hxx>
71 #include <fmtruby.hxx>
72 #include <charfmt.hxx>
73 #include <breakit.hxx>
74 #include <fmtclds.hxx>
75 #include <poolfmt.hxx>
76 #include <SwStyleNameMapper.hxx>
78 #include "ww8scan.hxx"
80 #include "writerhelper.hxx"
81 #include <o3tl/safeint.hxx>
82 #include <o3tl/string_view.hxx>
83 #include <xmloff/odffields.hxx>
84 #include <osl/diagnose.h>
85 #include <officecfg/Office/Common.hxx>
88 #include <string_view>
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 nsSwDocInfoSubType
;
103 // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention
104 bool IsTOCBookmarkName(std::u16string_view rName
)
106 return o3tl::starts_with(rName
, u
"_Toc") || o3tl::starts_with(rName
, Concat2View(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"));
109 OUString
EnsureTOCBookmarkName(const OUString
& rName
)
111 OUString sTmp
= rName
;
112 if ( IsTOCBookmarkName ( rName
) )
114 if ( ! rName
.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
115 sTmp
= IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName
;
121 tools::Long
SwWW8ImplReader::Read_Book(WW8PLCFManResult
*)
123 // should also work via pRes.nCo2OrIdx
124 WW8PLCFx_Book
* pB
= m_xPlcxMan
->GetBook();
127 OSL_ENSURE( pB
, "WW8PLCFx_Book - Pointer does not exist" );
131 eBookStatus eB
= pB
->GetStatus();
132 if (eB
& BOOK_IGNORE
)
133 return 0; // ignore bookmark
137 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_BOOKMARK
, true,
138 pB
->GetHandle(), (eB
& BOOK_FIELD
)!=0);
142 // "_Hlt*" are unnecessary
143 const OUString
* pName
= pB
->GetName();
144 // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available.
145 // So the hidden bookmarks inside for hyperlink jumping also should be kept.
147 pName
->startsWithIgnoreAsciiCase( "_Hlt" ) )
152 // do NOT call ToUpper as the bookmark name can also be a hyperlink target!
155 if( SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::BOOK_TO_VAR_REF
) )
157 // set variable for translation bookmark
158 tools::Long nLen
= pB
->GetLen();
159 if( nLen
> MAX_FIELDLEN
)
162 sal_uInt64 nOldPos
= m_pStrm
->Tell();
163 m_xSBase
->WW8ReadString( *m_pStrm
, aVal
, pB
->GetStartPos(), nLen
,
165 m_pStrm
->Seek( nOldPos
);
167 // now here the implementation of the old "QuoteString" and
168 // I hope with a better performance as before. It's also only
169 // needed if the filterflags say we will convert bookmarks
170 // to SetExpFields! And this the exception!
173 bool bAllowCr
= SwFltGetFlag(m_nFieldFlags
,
174 SwFltControlStack::ALLOW_FLD_CR
);
176 for( sal_Int32 nI
= 0;
177 nI
< aVal
.getLength() && aVal
.getLength() < (MAX_FIELDLEN
- 4);
180 const sal_Unicode cChar
= aVal
[nI
];
188 aVal
= aVal
.replaceAt( nI
, 1, u
"\n" );
201 bSetAsHex
= 0x20 > cChar
;
207 //all Hex-Numbers with \x before
208 OUString
sTmp( u
"\\x"_ustr
);
211 sTmp
+= OUString::number( cChar
, 16 );
212 aVal
= aVal
.replaceAt( nI
, 1 , sTmp
);
213 nI
+= sTmp
.getLength() - 1;
217 if ( aVal
.getLength() > (MAX_FIELDLEN
- 4))
218 aVal
= aVal
.copy( 0, MAX_FIELDLEN
- 4 );
221 //e.g. inserting bookmark around field result, so we need to put
222 //it around the entire writer field, as we don't have the separation
223 //of field and field result of word, see #i16941#
224 SwPosition
aStart(*m_pPaM
->GetPoint());
225 if (!m_aFieldStack
.empty())
227 const WW8FieldEntry
&rTest
= m_aFieldStack
.back();
228 aStart
= rTest
.maStartPos
;
231 const OUString sOrigName
= BookmarkToWriter(*pName
);
232 m_xReffedStck
->NewAttr( aStart
,
233 SwFltBookmark( EnsureTOCBookmarkName( sOrigName
), aVal
, pB
->GetHandle(), IsTOCBookmarkName( sOrigName
) ));
237 tools::Long
SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult
*)
239 if (WW8PLCFx_AtnBook
* pAtnBook
= m_xPlcxMan
->GetAtnBook())
241 if (pAtnBook
->getIsEnd())
242 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_ANNOTATIONMARK
, true, pAtnBook
->getHandle());
244 m_xReffedStck
->NewAttr(*m_pPaM
->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK
, pAtnBook
->getHandle()));
249 tools::Long
SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult
*)
251 if (WW8PLCFx_FactoidBook
* pFactoidBook
= m_xPlcxMan
->GetFactoidBook())
253 if (pFactoidBook
->getIsEnd())
254 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_RDFMARK
, true, pFactoidBook
->getHandle());
258 aMark
.SetHandle(pFactoidBook
->getHandle());
259 GetSmartTagInfo(aMark
);
260 m_xReffedStck
->NewAttr(*m_pPaM
->GetPoint(), aMark
);
266 // general help methods to separate parameters
268 /// translate FieldParameter names into the system character set and
269 /// at the same time, double backslashes are converted into single ones
270 OUString
SwWW8ImplReader::ConvertFFileName(const OUString
& rOrg
)
272 OUString aName
= rOrg
.replaceAll("\\\\", "\\");
273 aName
= aName
.replaceAll("%20", " ");
275 // remove attached quotation marks
276 if (aName
.endsWith("\""))
277 aName
= aName
.copy(0, aName
.getLength()-1);
279 // Need the more sophisticated url converter.
280 if (!aName
.isEmpty())
281 aName
= URIHelper::SmartRel2Abs(
282 INetURLObject(m_sBaseURL
), aName
, Link
<OUString
*, bool>(), false);
289 /// translate FieldParameter names into the
290 /// system character set and makes them uppercase
291 void ConvertUFName( OUString
& rName
)
293 rName
= GetAppCharClass().uppercase( rName
);
297 static void lcl_ConvertSequenceName(OUString
& rSequenceName
)
299 ConvertUFName(rSequenceName
);
300 if ('0' <= rSequenceName
[0] && '9' >= rSequenceName
[0])
301 rSequenceName
= "_" + rSequenceName
;
304 // FindParaStart() finds 1st Parameter that follows '\' and cToken
305 // and returns start of this parameter or -1
306 static sal_Int32
FindParaStart( std::u16string_view aStr
, sal_Unicode cToken
, sal_Unicode cToken2
)
308 bool bStr
= false; // ignore inside a string
310 for( size_t nBuf
= 0; nBuf
+1 < aStr
.size(); nBuf
++ )
312 if( aStr
[ nBuf
] == '"' )
316 && aStr
[ nBuf
] == '\\'
317 && ( aStr
[ nBuf
+ 1 ] == cToken
318 || aStr
[ nBuf
+ 1 ] == cToken2
) )
321 // skip spaces between cToken and its parameters
322 while( nBuf
< aStr
.size()
323 && aStr
[ nBuf
] == ' ' )
325 // return start of parameters
326 return nBuf
< aStr
.size() ? nBuf
: -1;
332 // FindPara() finds the first parameter including '\' and cToken.
333 // A new String will be allocated (has to be deallocated by the caller)
334 // and everything that is part of the parameter will be returned.
335 static OUString
FindPara( std::u16string_view aStr
, sal_Unicode cToken
, sal_Unicode cToken2
)
338 sal_Int32 n
= FindParaStart( aStr
, cToken
, cToken2
); // start
343 || aStr
[ n
] == 132 )
344 { // Quotationmark in front of parameter
345 n
++; // Skip quotationmark
346 n2
= n
; // search for the end starting from here
347 while( n2
< sal_Int32(aStr
.size())
349 && aStr
[ n2
] != '"' )
350 n2
++; // search end of parameter
353 { // no quotationmarks
354 n2
= n
; // search for the end starting from here
355 while( n2
< sal_Int32(aStr
.size())
356 && aStr
[ n2
] != ' ' )
357 n2
++; // search end of parameter
359 return OUString(aStr
.substr( n
, n2
-n
));
362 static SvxNumType
GetNumTypeFromName(const OUString
& rStr
,
363 bool bAllowPageDesc
= false)
365 SvxNumType 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
;
386 static SvxNumType
GetNumberPara(std::u16string_view aStr
, bool bAllowPageDesc
= false)
388 OUString
s( FindPara( aStr
, '*', '*' ) ); // Type of number
389 SvxNumType aType
= GetNumTypeFromName( s
, bAllowPageDesc
);
393 bool SwWW8ImplReader::ForceFieldLanguage(SwField
&rField
, LanguageType nLang
)
397 const SvxLanguageItem
*pLang
= GetFormatAttr(RES_CHRATR_LANGUAGE
);
398 OSL_ENSURE(pLang
, "impossible");
399 LanguageType nDefault
= pLang
? pLang
->GetValue() : LANGUAGE_ENGLISH_US
;
401 if (nLang
!= nDefault
)
403 rField
.SetAutomaticLanguage(false);
404 rField
.SetLanguage(nLang
);
411 static OUString
GetWordDefaultDateStringAsUS(SvNumberFormatter
* pFormatter
, LanguageType nLang
)
413 //Get the system date in the correct final language layout, convert to
414 //a known language and modify the 2 digit year part to be 4 digit, and
415 //convert back to the correct language layout.
416 const sal_uInt32 nIndex
= pFormatter
->GetFormatIndex(NF_DATE_SYSTEM_SHORT
, nLang
);
418 SvNumberformat aFormat
= *(pFormatter
->GetEntry(nIndex
));
419 aFormat
.ConvertLanguage(*pFormatter
, nLang
, LANGUAGE_ENGLISH_US
);
421 OUString
sParams(aFormat
.GetFormatstring());
423 // Fix provided by mloiseleur@openoffice.org.
424 // A default date can have already 4 year digits, in some case
425 const sal_Int32 pos
= sParams
.indexOf("YYYY");
428 sParams
= sParams
.replaceFirst("YY", "YYYY");
433 SvNumFormatType
SwWW8ImplReader::GetTimeDatePara(std::u16string_view aStr
, sal_uInt32
& rFormat
,
434 LanguageType
&rLang
, int nWhichDefault
, bool bHijri
)
437 if (m_xPlcxMan
&& !m_bVer67
)
439 SprmResult aResult
= m_xPlcxMan
->HasCharSprm(0x85A);
440 if (aResult
.pSprm
&& aResult
.nRemainingData
>= 1 && *aResult
.pSprm
)
443 TypedWhichId
<SvxLanguageItem
> eLang
= bRTL
? RES_CHRATR_CTL_LANGUAGE
: RES_CHRATR_LANGUAGE
;
444 const SvxLanguageItem
*pLang
= GetFormatAttr(eLang
);
445 OSL_ENSURE(pLang
, "impossible");
446 rLang
= pLang
? pLang
->GetValue() : LANGUAGE_ENGLISH_US
;
448 SvNumberFormatter
* pFormatter
= m_rDoc
.GetNumberFormatter();
449 OUString
sParams( FindPara( aStr
, '@', '@' ) );// Date/Time
450 if (sParams
.isEmpty())
452 bool bHasTime
= false;
453 switch (nWhichDefault
)
457 sParams
= GetWordDefaultDateStringAsUS(pFormatter
, rLang
);
458 sParams
+= " HH:MM:SS AM/PM";
461 case ww::eCREATEDATE
:
462 sParams
+= "DD/MM/YYYY HH:MM:SS";
467 sParams
= GetWordDefaultDateStringAsUS(pFormatter
, rLang
);
472 sParams
= "[~hijri]" + sParams
;
474 sal_Int32 nCheckPos
= 0;
475 SvNumFormatType nType
= SvNumFormatType::DEFINED
;
478 pFormatter
->PutandConvertEntry(sParams
, nCheckPos
, nType
, rFormat
,
479 LANGUAGE_ENGLISH_US
, rLang
, false);
481 return bHasTime
? SvNumFormatType::DATETIME
: SvNumFormatType::DATE
;
484 sal_uLong nFormatIdx
=
485 sw::ms::MSDateTimeFormatToSwFormat(sParams
, pFormatter
, rLang
, bHijri
,
487 SvNumFormatType nNumFormatType
= SvNumFormatType::UNDEFINED
;
489 nNumFormatType
= pFormatter
->GetType(nFormatIdx
);
490 rFormat
= nFormatIdx
;
492 return nNumFormatType
;
497 // Update respective fields after loading (currently references)
498 void SwWW8ImplReader::UpdateFields()
500 m_rDoc
.getIDocumentState().SetUpdateExpFieldStat(true);
501 m_rDoc
.SetInitDBFields(true); // Also update fields in the database
504 // Sanity check the PaM to see if it makes sense wrt sw::CalcBreaks
505 static bool SanityCheck(const SwPaM
& rFieldPam
)
507 SwNodeOffset
const nEndNode(rFieldPam
.End()->GetNodeIndex());
508 SwNodes
const& rNodes(rFieldPam
.GetPoint()->GetNodes());
509 SwNode
*const pFinalNode(rNodes
[nEndNode
]);
510 if (pFinalNode
->IsTextNode())
512 SwTextNode
& rTextNode(*pFinalNode
->GetTextNode());
513 return (rTextNode
.Len() >= rFieldPam
.End()->GetContentIndex());
518 sal_uInt16
SwWW8ImplReader::End_Field()
521 WW8PLCFx_FLD
* pF
= m_xPlcxMan
->GetField();
522 OSL_ENSURE(pF
, "WW8PLCFx_FLD - Pointer not available");
524 if (!pF
|| !pF
->EndPosIsFieldEnd(nCP
))
527 bool bUseEnhFields
= officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get();
529 OSL_ENSURE(!m_aFieldStack
.empty(), "Empty field stack");
530 if (!m_aFieldStack
.empty())
533 only hyperlinks currently need to be handled like this, for the other
534 cases we have inserted a field not an attribute with an unknown end
537 nRet
= m_aFieldStack
.back().mnFieldId
;
541 if (bUseEnhFields
&& m_pPaM
!=nullptr && m_pPaM
->GetPoint()!=nullptr) {
542 SwPosition aEndPos
= *m_pPaM
->GetPoint();
543 SwPaM
aFieldPam( m_aFieldStack
.back().GetPtNode().GetNode(), m_aFieldStack
.back().GetPtContent(), aEndPos
.GetNode(), aEndPos
.GetContentIndex());
545 IDocumentMarkAccess
* pMarksAccess
= m_rDoc
.getIDocumentMarkAccess( );
546 Fieldmark
*pFieldmark
= SanityCheck(aFieldPam
) ? pMarksAccess
->makeFieldBookmark(
547 aFieldPam
, m_aFieldStack
.back().GetBookmarkName(), ODF_FORMTEXT
,
548 aFieldPam
.Start() /*same pos as start!*/ ) : nullptr;
549 OSL_ENSURE(pFieldmark
!=nullptr, "hmmm; why was the bookmark not created?");
550 if (pFieldmark
!=nullptr) {
551 // adapt redline positions to inserted field mark start
552 // dummy char (assume not necessary for end dummy char)
553 m_xRedlineStack
->MoveAttrsFieldmarkInserted(*aFieldPam
.Start());
554 const Fieldmark::parameter_map_t
& rParametersToAdd
= m_aFieldStack
.back().getParameters();
555 pFieldmark
->GetParameters()->insert(rParametersToAdd
.begin(), rParametersToAdd
.end());
559 // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field
561 case ww::eINDEX
://index
562 if (m_bLoadingTOXCache
)
564 if (m_nEmbeddedTOXLevel
> 0)
567 --m_nEmbeddedTOXLevel
;
571 m_aTOXEndCps
.insert(nCP
);
572 m_bLoadingTOXCache
= false;
573 if ( m_pPaM
->End() &&
574 m_pPaM
->End()->GetNode().GetTextNode() &&
575 m_pPaM
->End()->GetNode().GetTextNode()->Len() == 0 )
581 m_bCareLastParaEndInToc
= true;
586 *m_pPaM
= *m_oPosAfterTOC
;
587 m_oPosAfterTOC
.reset();
592 case ww::ePAGEREF
: //REF
593 if (m_bLoadingTOXCache
&& !m_bLoadingTOXHyperlink
)
595 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(),RES_TXTATR_INETFMT
);
599 if (m_bLoadingTOXHyperlink
)
600 m_bLoadingTOXHyperlink
= false;
601 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(), RES_TXTATR_INETFMT
);
604 case ww::eINCLUDETEXT
:
606 //Move outside the section associated with this type of field
607 SwPosition
aRestorePos(m_aFieldStack
.back().maStartPos
);
609 SwContentNode
* pNd
= aRestorePos
.GetNode().GetContentNode();
610 sal_Int32 nMaxValidIndex
= pNd
? pNd
->Len() : 0;
611 if (aRestorePos
.GetContentIndex() > nMaxValidIndex
)
613 SAL_WARN("sw.ww8", "Attempt to restore to invalid content position");
614 aRestorePos
.SetContent(nMaxValidIndex
);
617 *m_pPaM
->GetPoint() = std::move(aRestorePos
);
620 case ww::eIF
: // IF-field
622 // conditional field parameters
623 OUString fieldDefinition
= m_aFieldStack
.back().GetBookmarkCode();
625 OUString paramCondition
;
629 // ParseIfFieldDefinition expects: IF <some condition> "true result" "false result"
630 // while many fields include '\* MERGEFORMAT' after that.
631 // So first trim off the switches that are not supported anyway
632 sal_Int32 nLastIndex
= fieldDefinition
.lastIndexOf("\\*");
633 sal_Int32 nOtherIndex
= fieldDefinition
.lastIndexOf("\\#"); //number format
634 if (nOtherIndex
> 0 && (nOtherIndex
< nLastIndex
|| nLastIndex
< 0))
635 nLastIndex
= nOtherIndex
;
636 nOtherIndex
= fieldDefinition
.lastIndexOf("\\@"); //date format
637 if (nOtherIndex
> 0 && (nOtherIndex
< nLastIndex
|| nLastIndex
< 0))
638 nLastIndex
= nOtherIndex
;
639 nOtherIndex
= fieldDefinition
.lastIndexOf("\\!"); //locked result
640 if (nOtherIndex
> 0 && (nOtherIndex
< nLastIndex
|| nLastIndex
< 0))
641 nLastIndex
= nOtherIndex
;
643 fieldDefinition
= fieldDefinition
.copy(0, nLastIndex
);
645 SwHiddenTextField::ParseIfFieldDefinition(fieldDefinition
, paramCondition
, paramTrue
, paramFalse
);
648 SwFieldType
* pFieldType
= m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText
);
649 SwHiddenTextField
aHTField(
650 static_cast<SwHiddenTextFieldType
*>(pFieldType
),
654 SwFieldTypesEnum::ConditionalText
);
656 // insert new field into document
657 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aHTField
));
661 OUString aCode
= m_aFieldStack
.back().GetBookmarkCode();
662 if (!aCode
.isEmpty() && !o3tl::starts_with(o3tl::trim(aCode
), u
"SHAPE"))
664 // Unhandled field with stored code
665 SwPosition aEndPos
= *m_pPaM
->GetPoint();
667 m_aFieldStack
.back().GetPtNode().GetNode(), m_aFieldStack
.back().GetPtContent(),
668 aEndPos
.GetNode(), aEndPos
.GetContentIndex());
670 IDocumentMarkAccess
* pMarksAccess
= m_rDoc
.getIDocumentMarkAccess( );
672 Fieldmark
* pFieldmark
= pMarksAccess
->makeFieldBookmark(
674 m_aFieldStack
.back().GetBookmarkName(),
676 aFieldPam
.Start() /*same pos as start!*/ );
679 // adapt redline positions to inserted field mark start
680 // dummy char (assume not necessary for end dummy char)
681 m_xRedlineStack
->MoveAttrsFieldmarkInserted(*aFieldPam
.Start());
682 const Fieldmark::parameter_map_t
& rParametersToAdd
= m_aFieldStack
.back().getParameters();
683 pFieldmark
->GetParameters()->insert(rParametersToAdd
.begin(), rParametersToAdd
.end());
684 OUString sFieldId
= OUString::number( m_aFieldStack
.back().mnFieldId
);
685 pFieldmark
->GetParameters()->insert(
686 std::pair
< OUString
, uno::Any
> (
688 uno::Any( sFieldId
) ) );
689 pFieldmark
->GetParameters()->insert(
690 std::pair
< OUString
, uno::Any
> (
692 uno::Any( aCode
) ) );
694 if ( m_aFieldStack
.back().mnObjLocFc
> 0 )
696 // Store the OLE object as an internal link
697 OUString sOleId
= "_" +
698 OUString::number( m_aFieldStack
.back().mnObjLocFc
);
700 rtl::Reference
<SotStorage
> xSrc0
= m_pStg
->OpenSotStorage(SL::aObjectPool
);
701 rtl::Reference
<SotStorage
> xSrc1
= xSrc0
->OpenSotStorage( sOleId
, StreamMode::READ
);
704 uno::Reference
< embed::XStorage
> xDocStg
= GetDoc().GetDocStorage();
707 uno::Reference
< embed::XStorage
> xOleStg
= xDocStg
->openStorageElement(
708 u
"OLELinks"_ustr
, embed::ElementModes::WRITE
);
709 rtl::Reference
<SotStorage
> xObjDst
= SotStorage::OpenOLEStorage( xOleStg
, sOleId
);
713 xSrc1
->CopyTo( xObjDst
.get() );
715 if ( !xObjDst
->GetError() )
719 uno::Reference
< embed::XTransactedObject
> xTransact( xOleStg
, uno::UNO_QUERY
);
720 if ( xTransact
.is() )
724 // Store the OLE Id as a parameter
725 pFieldmark
->GetParameters()->insert(
726 std::pair
< OUString
, uno::Any
>(
727 ODF_OLE_PARAM
, uno::Any( sOleId
) ) );
734 m_aFieldStack
.pop_back();
739 static bool AcceptableNestedField(sal_uInt16 nFieldCode
)
743 case ww::eINDEX
: // allow recursive field in TOC...
744 case ww::eTOC
: // allow recursive field in TOC...
746 case ww::eINCLUDETEXT
:
749 // Accept AutoTextList field as nested field.
750 // Thus, the field result is imported as plain text.
751 case ww::eAUTOTEXTLIST
:
752 // tdf#129247 CONTROL contains a nested SHAPE field in the result
760 WW8FieldEntry::WW8FieldEntry(SwPosition
const &rPos
, sal_uInt16 nFieldId
) noexcept
761 : maStartPos(rPos
), mnFieldId(nFieldId
), mnObjLocFc(0)
765 WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry
&rOther
) noexcept
766 : maStartPos(rOther
.maStartPos
), mnFieldId(rOther
.mnFieldId
), mnObjLocFc(rOther
.mnObjLocFc
)
770 void WW8FieldEntry::Swap(WW8FieldEntry
&rOther
) noexcept
772 std::swap(maStartPos
, rOther
.maStartPos
);
773 std::swap(mnFieldId
, rOther
.mnFieldId
);
776 WW8FieldEntry
&WW8FieldEntry::operator=(const WW8FieldEntry
&rOther
) noexcept
778 WW8FieldEntry
aTemp(rOther
);
784 void WW8FieldEntry::SetBookmarkName(const OUString
& bookmarkName
)
786 msBookmarkName
=bookmarkName
;
789 void WW8FieldEntry::SetBookmarkType(const OUString
& bookmarkType
)
791 msMarkType
=bookmarkType
;
794 void WW8FieldEntry::SetBookmarkCode(const OUString
& bookmarkCode
)
796 msMarkCode
= bookmarkCode
;
800 // Read_Field reads a field or returns 0 if the field cannot be read,
801 // so that the calling function reads the field in text format.
802 // Returnvalue: Total length of field
803 tools::Long
SwWW8ImplReader::Read_Field(WW8PLCFManResult
* pRes
)
805 typedef eF_ResT (SwWW8ImplReader::*FNReadField
)( WW8FieldDesc
*, OUString
& );
806 constexpr sal_uInt16 eMax
= 96;
807 static const FNReadField aWW8FieldTab
[eMax
+1] =
810 &SwWW8ImplReader::Read_F_Input
,
812 &SwWW8ImplReader::Read_F_Ref
, // 3
815 &SwWW8ImplReader::Read_F_Set
, // 6
817 &SwWW8ImplReader::Read_F_Tox
, // 8
819 &SwWW8ImplReader::Read_F_Styleref
, // 10
821 &SwWW8ImplReader::Read_F_Seq
, // 12
822 &SwWW8ImplReader::Read_F_Tox
, // 13
823 &SwWW8ImplReader::Read_F_DocInfo
, // 14
824 &SwWW8ImplReader::Read_F_DocInfo
, // 15
825 &SwWW8ImplReader::Read_F_DocInfo
, // 16
826 &SwWW8ImplReader::Read_F_Author
, // 17
827 &SwWW8ImplReader::Read_F_DocInfo
, // 18
828 &SwWW8ImplReader::Read_F_DocInfo
, // 19
829 &SwWW8ImplReader::Read_F_DocInfo
, // 20
830 &SwWW8ImplReader::Read_F_DocInfo
, // 21
831 &SwWW8ImplReader::Read_F_DocInfo
, // 22
832 &SwWW8ImplReader::Read_F_DocInfo
, // 23
833 &SwWW8ImplReader::Read_F_DocInfo
, // 24
834 &SwWW8ImplReader::Read_F_DocInfo
, // 25
835 &SwWW8ImplReader::Read_F_Num
, // 26
836 &SwWW8ImplReader::Read_F_Num
, // 27
837 &SwWW8ImplReader::Read_F_Num
, // 28
838 &SwWW8ImplReader::Read_F_FileName
, // 29
839 &SwWW8ImplReader::Read_F_TemplName
, // 30
840 &SwWW8ImplReader::Read_F_DateTime
, // 31
841 &SwWW8ImplReader::Read_F_DateTime
, // 32
842 &SwWW8ImplReader::Read_F_CurPage
, // 33
845 &SwWW8ImplReader::Read_F_IncludeText
, // 36
846 &SwWW8ImplReader::Read_F_PgRef
, // 37
847 &SwWW8ImplReader::Read_F_InputVar
, // 38
848 &SwWW8ImplReader::Read_F_Input
, // 39
850 &SwWW8ImplReader::Read_F_DBNext
, // 41
853 &SwWW8ImplReader::Read_F_DBNum
, // 44
858 &SwWW8ImplReader::Read_F_Equation
, // 49
860 &SwWW8ImplReader::Read_F_Macro
, // 51
861 &SwWW8ImplReader::Read_F_ANumber
, // 52
862 &SwWW8ImplReader::Read_F_ANumber
, // 53
863 &SwWW8ImplReader::Read_F_ANumber
, // 54
868 &SwWW8ImplReader::Read_F_Symbol
, // 57
869 &SwWW8ImplReader::Read_F_Embedd
, // 58
870 &SwWW8ImplReader::Read_F_DBField
, // 59
875 &SwWW8ImplReader::Read_F_DocInfo
, // 64 - DOCVARIABLE
878 &SwWW8ImplReader::Read_F_IncludePicture
, // 67
879 &SwWW8ImplReader::Read_F_IncludeText
, // 68
881 &SwWW8ImplReader::Read_F_FormTextBox
, // 70
882 &SwWW8ImplReader::Read_F_FormCheckBox
, // 71
883 &SwWW8ImplReader::Read_F_NoteReference
, // 72
884 nullptr, /*&SwWW8ImplReader::Read_F_Tox*/
894 &SwWW8ImplReader::Read_F_FormListBox
, // 83
896 &SwWW8ImplReader::Read_F_DocInfo
, // 85
898 &SwWW8ImplReader::Read_F_OCX
, // 87
899 &SwWW8ImplReader::Read_F_Hyperlink
, // 88
902 &SwWW8ImplReader::Read_F_HTMLControl
, // 91
906 &SwWW8ImplReader::Read_F_Shape
, // 95
907 nullptr // eMax - Dummy empty method
909 OSL_ENSURE( SAL_N_ELEMENTS( aWW8FieldTab
) == eMax
+1, "FieldFunc table not right" );
911 WW8PLCFx_FLD
* pF
= m_xPlcxMan
->GetField();
912 OSL_ENSURE(pF
, "WW8PLCFx_FLD - Pointer not available");
914 if (!pF
|| !pF
->StartPosIsFieldStart())
917 bool bNested
= false;
918 if (!m_aFieldStack
.empty())
920 bNested
= std::any_of(m_aFieldStack
.cbegin(), m_aFieldStack
.cend(),
921 [](const WW8FieldEntry
& aField
) { return !AcceptableNestedField(aField
.mnFieldId
); });
925 bool bOk
= pF
->GetPara(pRes
->nCp2OrIdx
, aF
);
927 OSL_ENSURE(bOk
, "WW8: Bad Field!");
928 if (aF
.nId
== 33) aF
.bCodeNest
=false; // do not recurse into nested page fields
929 bool bCodeNest
= aF
.bCodeNest
;
930 if ( aF
.nId
== 6 ) bCodeNest
= false; // We can handle them and lose the inner data
931 if (aF
.nId
== 70) bCodeNest
= false; // need to import 0x01 in FORMTEXT
933 m_aFieldStack
.emplace_back(*m_pPaM
->GetPoint(), aF
.nId
);
938 sal_uInt16 n
= (aF
.nId
<= eMax
) ? aF
.nId
: eMax
;
939 sal_uInt32 nI
= n
/ 32U; // # of sal_uInt32
941 static_assert(FieldTagSize
== SAL_N_ELEMENTS(m_nFieldTagAlways
) &&
942 FieldTagSize
== SAL_N_ELEMENTS(m_nFieldTagBad
),
943 "m_nFieldTagAlways and m_nFieldTagBad should have the same FieldTagSize num of elements");
945 if (nI
>= FieldTagSize
)
946 { // if indexes larger than 95 are needed, then a new configuration
947 // item has to be added, and nFieldTagAlways/nFieldTagBad expanded!
951 sal_uInt32 nMask
= 1 << ( n
% 32 ); // Mask for bits
953 if( m_nFieldTagAlways
[nI
] & nMask
) // Flag: Tag it
954 return Read_F_Tag( &aF
); // Result not as text
956 if( !bOk
|| !aF
.nId
) // Field corrupted
957 return aF
.nLen
; // -> ignore
959 if( aF
.nId
> eMax
- 1) // WW: Nested Field
961 if( m_nFieldTagBad
[nI
] & nMask
) // Flag: Tag it when bad
962 return Read_F_Tag( &aF
); // Result not as text
967 //Only one type of field (hyperlink) in drawing textboxes exists
968 if (aF
.nId
!= 88 && m_xPlcxMan
->GetDoingDrawTextBox())
971 bool bHasHandler
= aWW8FieldTab
[aF
.nId
] != nullptr;
972 if (aF
.nId
== 10) // STYLEREF
974 bool bHandledByChapter
= false;
975 sal_uInt64 nOldPos
= m_pStrm
->Tell();
977 aF
.nLCode
= m_xSBase
->WW8ReadString(*m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs() + aF
.nSCode
, aF
.nLCode
, m_eTextCharSet
);
978 m_pStrm
->Seek(nOldPos
);
980 WW8ReadFieldParams
aReadParam(aStr
);
981 sal_Int32 nRet
= aReadParam
.SkipToNextToken();
982 if (nRet
== -2 && !aReadParam
.GetResult().isEmpty())
983 // Single numeric argument: this can be handled by SwChapterField.
984 bHandledByChapter
= rtl::isAsciiDigit(aReadParam
.GetResult()[0]);
986 if (bHandledByChapter
)
988 nRet
= aReadParam
.SkipToNextToken();
989 // Handle using SwChapterField only in case there is no \[a-z]
990 // switch after the field argument.
991 bHasHandler
= nRet
< 0 || nRet
== '*';
995 // no routine available
996 if (!bHasHandler
|| bCodeNest
)
998 if( m_nFieldTagBad
[nI
] & nMask
) // Flag: Tag it when bad
999 return Read_F_Tag( &aF
); // Result not as text
1001 if (aF
.bResNest
&& !AcceptableNestedField(aF
.nId
))
1002 return aF
.nLen
; // Result nested -> unusable
1004 sal_uInt64 nOldPos
= m_pStrm
->Tell();
1006 aF
.nLCode
= m_xSBase
->WW8ReadString( *m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs()+
1007 aF
.nSCode
, aF
.nLCode
, m_eTextCharSet
);
1008 m_pStrm
->Seek( nOldPos
);
1010 // field codes which contain '/' or '.' are not displayed in WinWord
1011 // skip if it is formula field or found space before. see #i119446, #i119585.
1012 const sal_Int32 nDotPos
= aStr
.indexOf('.');
1013 const sal_Int32 nSlashPos
= aStr
.indexOf('/');
1014 sal_Int32 nSpacePos
= aStr
.indexOf( ' ', 1 );
1016 nSpacePos
= aStr
.getLength();
1018 if ( ( aStr
.getLength() <= 1 || aStr
[1] != '=') &&
1019 (( nDotPos
>=0 && nDotPos
< nSpacePos
) ||
1020 ( nSlashPos
>=0 && nSlashPos
< nSpacePos
)))
1024 // Link fields aren't supported, but they are bound to an OLE object
1025 // that needs to be roundtripped
1027 m_bEmbeddObj
= true;
1028 // Field not supported: store the field code for later use
1029 m_aFieldStack
.back().SetBookmarkCode( aStr
);
1031 if (aF
.nId
== ww::eIF
)
1033 // In MS Word, the IF field is editable and requires a manual refresh
1034 // so the last, saved result might not match either of the true or false options.
1035 // But in LO the field is automatically updated and not editable,
1036 // so the previous result is of no value to import since it could never be seen.
1040 return aF
.nLen
- aF
.nLRes
- 1; // skipped too many, the resulted field will be read like main text
1045 auto nOldPos
= m_pStrm
->Tell();
1047 if ( aF
.nId
== 6 && aF
.bCodeNest
)
1049 // TODO Extract the whole code string using the nested codes
1050 aF
.nLCode
= m_xSBase
->WW8ReadString( *m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs() +
1051 aF
.nSCode
, aF
.nSRes
- aF
.nSCode
- 1, m_eTextCharSet
);
1055 aF
.nLCode
= m_xSBase
->WW8ReadString( *m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs()+
1056 aF
.nSCode
, aF
.nLCode
, m_eTextCharSet
);
1059 // #i51312# - graphics inside field code not supported by Writer.
1060 // Thus, delete character 0x01, which stands for such a graphic.
1061 if (aF
.nId
==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01.
1063 aStr
= aStr
.replaceAll("\x01", "");
1066 eF_ResT eRes
= (this->*aWW8FieldTab
[aF
.nId
])( &aF
, aStr
);
1067 m_pStrm
->Seek(nOldPos
);
1074 // skipped too many, the resulted field will be read like main text
1075 // attributes can start at char 0x14 so skip one
1076 // char more back == "-2"
1078 return aF
.nLen
- aF
.nLRes
- 2;
1081 case eF_ResT::TAGIGN
:
1082 if ( m_nFieldTagBad
[nI
] & nMask
) // Flag: Tag bad
1083 return Read_F_Tag( &aF
); // Tag it
1084 return aF
.nLen
; // or ignore
1085 case eF_ResT::READ_FSPA
:
1086 return aF
.nLen
- aF
.nLRes
- 2; // position on char 1
1088 return aF
.nLen
; // ignore
1095 // MakeTagString() returns the position of the first CR / end of line / page break
1096 // in pText and converts only up to this point.
1097 // If none of these special characters is found, the function returns 0.
1098 void SwWW8ImplReader::MakeTagString( OUString
& rStr
, const OUString
& rOrg
)
1100 bool bAllowCr
= SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::TAGS_IN_TEXT
)
1101 || SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::ALLOW_FLD_CR
);
1105 for( sal_Int32 nI
= 0;
1106 nI
< rStr
.getLength() && rStr
.getLength() < (MAX_FIELDLEN
- 4); ++nI
)
1108 bool bSetAsHex
= false;
1112 case 132: // Exchange typographical quotation marks for normal ones
1115 rStr
= rStr
.replaceAt( nI
, 1, u
"\"" );
1118 rStr
= rStr
.replaceAt( nI
, 1, u
"{" );
1119 break; // 19..21 to {|}
1121 rStr
= rStr
.replaceAt( nI
, 1, u
"|" );
1124 rStr
= rStr
.replaceAt( nI
, 1, u
"}" );
1126 case '\\': // Tag \{|} with \ ...
1130 rStr
= rStr
.replaceAt( nI
, 0, u
"\\" );
1137 rStr
= rStr
.replaceAt( nI
, 1, u
"\n" );
1146 bSetAsHex
= 0x20 > cChar
;
1152 //all Hex-Numbers with \x before
1153 OUString
sTmp( u
"\\x"_ustr
);
1156 sTmp
+= OUString::number( cChar
, 16 );
1157 rStr
= rStr
.replaceAt( nI
, 1 , sTmp
);
1158 nI
+= sTmp
.getLength() - 1;
1162 if( rStr
.getLength() > (MAX_FIELDLEN
- 4))
1163 rStr
= rStr
.copy( 0, MAX_FIELDLEN
- 4 );
1166 void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId
, const OUString
& rTagText
)
1168 OUString
aName(u
"WwFieldTag"_ustr
);
1169 if( SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::TAGS_DO_ID
) ) // Number?
1170 aName
+= OUString::number( nId
); // return it?
1172 if( SwFltGetFlag(m_nFieldFlags
, SwFltControlStack::TAGS_IN_TEXT
))
1174 aName
+= rTagText
; // tag as text
1175 m_rDoc
.getIDocumentContentOperations().InsertString(*m_pPaM
, aName
,
1176 SwInsertFlags::NOHINTEXPAND
);
1181 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType(
1182 SwSetExpFieldType( &m_rDoc
, aName
, nsSwGetSetExpType::GSE_STRING
) );
1183 SwSetExpField
aField( static_cast<SwSetExpFieldType
*>(pFT
), rTagText
); // SUB_INVISIBLE
1184 sal_uInt16 nSubType
= ( SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::TAGS_VISIBLE
) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE
;
1185 aField
.SetSubType(nSubType
| nsSwGetSetExpType::GSE_STRING
);
1187 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1191 WW8_CP
SwWW8ImplReader::Read_F_Tag( WW8FieldDesc
* pF
)
1193 sal_uInt64 nOldPos
= m_pStrm
->Tell();
1195 WW8_CP nStart
= pF
->nSCode
- 1; // starting with 0x19
1196 WW8_CP nL
= pF
->nLen
; // Total length with result and nest
1197 if( nL
> MAX_FIELDLEN
)
1198 nL
= MAX_FIELDLEN
; // MaxLength, by quoting
1199 // max. 4 times as big
1201 m_xSBase
->WW8ReadString( *m_pStrm
, sFText
,
1202 m_xPlcxMan
->GetCpOfs() + nStart
, nL
, m_eStructCharSet
);
1205 MakeTagString( aTagText
, sFText
);
1206 InsertTagField( pF
->nId
, aTagText
);
1208 m_pStrm
->Seek( nOldPos
);
1214 eF_ResT
SwWW8ImplReader::Read_F_Input( WW8FieldDesc
* pF
, OUString
& rStr
)
1218 WW8ReadFieldParams
aReadParam( rStr
);
1221 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1228 aQ
= aReadParam
.GetResult();
1232 if ( aReadParam
.GoToTokenParam() )
1233 aDef
= aReadParam
.GetResult();
1237 if( aDef
.isEmpty() )
1238 aDef
= GetFieldResult( pF
);
1240 if ( pF
->nId
!= 0x01 ) // 0x01 fields have no result
1242 SwInputField
aField( static_cast<SwInputFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input
)),
1243 aDef
, aQ
, INP_TXT
, 0, false );
1244 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1250 // GetFieldResult allocates a string and reads the resulted field
1251 OUString
SwWW8ImplReader::GetFieldResult( WW8FieldDesc
const * pF
)
1253 sal_uInt64 nOldPos
= m_pStrm
->Tell();
1255 WW8_CP nStart
= pF
->nSRes
; // result start
1256 WW8_CP nL
= pF
->nLRes
; // result length
1258 return OUString(); // no result
1260 if( nL
> MAX_FIELDLEN
)
1261 nL
= MAX_FIELDLEN
; // MaxLength, by quoting
1262 // max. 4 times as big
1265 m_xSBase
->WW8ReadString( *m_pStrm
, sRes
, m_xPlcxMan
->GetCpOfs() + nStart
,
1266 nL
, m_eStructCharSet
);
1268 m_pStrm
->Seek( nOldPos
);
1270 //replace both CR 0x0D and VT 0x0B with LF 0x0A
1271 // at least in the cases where the result is added to an SwInputField
1272 // there must not be control characters in it
1273 OUStringBuffer
buf(sRes
.getLength());
1274 for (sal_Int32 i
= 0; i
< sRes
.getLength(); ++i
)
1276 sal_Unicode
const ch(sRes
[i
]);
1277 if (!linguistic::IsControlChar(ch
))
1294 SAL_INFO("sw.ww8", "GetFieldResult(): filtering control character");
1299 return buf
.makeStringAndClear();
1303 Bookmarks can be set with fields SET and ASK, and they can be referenced with
1304 REF. When set, they behave like variables in writer, otherwise they behave
1305 like normal bookmarks. We can check whether we should use a show variable
1306 instead of a normal bookmark ref by converting to "show variable" at the end
1307 of the document those refs which look for the content of a bookmark but whose
1308 bookmarks were set with SET or ASK. (See SwWW8FltRefStack)
1310 The other piece of the puzzle is that refs that point to the "location" of the
1311 bookmark will in word actually point to the last location where the bookmark
1312 was set with SET or ASK, not the actual bookmark. This is only noticeable when
1313 a document sets the bookmark more than once. This is because word places the
1314 true bookmark at the location of the last set, but the refs will display the
1315 position of the first set before the ref.
1317 So what we will do is
1319 1) keep a list of all bookmarks that were set, any bookmark names mentioned
1320 here that are referred by content will be converted to show variables.
1322 2) create pseudo bookmarks for every position that a bookmark is set with SET
1323 or ASK but has no existing bookmark. We can then keep a map from the original
1324 bookmark name to the new one. As we parse the document new pseudo names will
1325 replace the older ones, so the map always contains the bookmark of the
1326 location that msword itself would use.
1328 3) word's bookmarks are case insensitive, writers are not. So we need to
1329 map case different versions together, regardless of whether they are
1332 4) when a reference is (first) SET or ASK, the bookmark associated with it
1333 is placed around the 0x14 0x15 result part of the field. We will fiddle
1334 the placement to be the writer equivalent of directly before and after
1335 the field, which gives the same effect and meaning, to do so we must
1336 get any bookmarks in the field range, and begin them immediately before
1337 the set/ask field, and end them directly afterwards. MapBookmarkVariables
1338 returns an identifier of the bookmark attribute to close after inserting
1339 the appropriate set/ask field.
1341 tools::Long
SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc
* pF
,
1342 OUString
&rOrigName
, const OUString
&rData
)
1344 OSL_ENSURE(m_xPlcxMan
, "No pPlcxMan");
1347 If there was no bookmark associated with this set field, then we create a
1348 pseudo one and insert it in the document.
1351 m_xPlcxMan
->GetBook()->MapName(rOrigName
);
1352 OUString sName
= m_xPlcxMan
->GetBook()->GetBookmark(
1353 pF
->nSCode
, pF
->nSCode
+ pF
->nLen
, nIndex
);
1354 if (!sName
.isEmpty())
1356 m_xPlcxMan
->GetBook()->SetStatus(nIndex
, BOOK_IGNORE
);
1361 nNo
= m_xReffingStck
->m_aFieldVarNames
.size()+1;
1362 sName
= "WWSetBkmk" + OUString::number(nNo
);
1363 nNo
+= m_xPlcxMan
->GetBook()->GetIMax();
1365 m_xReffedStck
->NewAttr(*m_pPaM
->GetPoint(),
1366 SwFltBookmark( BookmarkToWriter(sName
), rData
, nNo
));
1367 m_xReffingStck
->m_aFieldVarNames
[rOrigName
] = sName
;
1372 Word can set a bookmark with set or with ask, such a bookmark is equivalent to
1373 our variables, but until the end of a document we cannot be sure if a bookmark
1374 is a variable or not, at the end we will have a list of reference names which
1375 were set or asked, all bookmarks using the content of those bookmarks are
1376 converted to show variables, those that reference the position of the field
1377 can be left as references, because a bookmark is also inserted at the position
1378 of a set or ask field, either by word, or in some special cases by the import
1381 SwFltStackEntry
*SwWW8FltRefStack::RefToVar(const SwField
* pField
,
1382 SwFltStackEntry
&rEntry
)
1384 SwFltStackEntry
*pRet
=nullptr;
1385 if (pField
&& SwFieldIds::GetRef
== pField
->Which())
1387 //Get the name of the ref field, and see if actually a variable
1388 const OUString sName
= pField
->GetPar1();
1389 std::map
<OUString
, OUString
, SwWW8::ltstr
>::const_iterator
1390 aResult
= m_aFieldVarNames
.find(sName
);
1392 if (aResult
!= m_aFieldVarNames
.end())
1394 SwGetExpField
aField( static_cast<SwGetExpFieldType
*>(
1395 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp
)), sName
, nsSwGetSetExpType::GSE_STRING
, 0);
1396 SwFormatField
aTmp(aField
);
1397 rEntry
.m_pAttr
.reset( aTmp
.Clone() );
1404 OUString
SwWW8ImplReader::GetMappedBookmark(std::u16string_view rOrigName
)
1406 OUString
sName(BookmarkToWriter(rOrigName
));
1407 OSL_ENSURE(m_xPlcxMan
, "no pPlcxMan");
1408 m_xPlcxMan
->GetBook()->MapName(sName
);
1410 //See if there has been a variable set with this name, if so get
1411 //the pseudo bookmark name that was set with it.
1412 std::map
<OUString
, OUString
, SwWW8::ltstr
>::const_iterator aResult
=
1413 m_xReffingStck
->m_aFieldVarNames
.find(sName
);
1415 return (aResult
== m_xReffingStck
->m_aFieldVarNames
.end())
1416 ? sName
: (*aResult
).second
;
1420 eF_ResT
SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc
* pF
, OUString
& rStr
)
1422 OUString sOrigName
, aQ
;
1424 WW8ReadFieldParams
aReadParam( rStr
);
1427 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1433 if (sOrigName
.isEmpty())
1434 sOrigName
= aReadParam
.GetResult();
1435 else if (aQ
.isEmpty())
1436 aQ
= aReadParam
.GetResult();
1440 if ( aReadParam
.GoToTokenParam() )
1441 aDef
= aReadParam
.GetResult();
1446 if (sOrigName
.isEmpty())
1447 return eF_ResT::TAGIGN
; // does not make sense without textmark
1449 const OUString
aResult(GetFieldResult(pF
));
1451 //#i24377#, munge Default Text into title as we have only one slot
1452 //available for aResult and aDef otherwise
1453 if (!aDef
.isEmpty())
1460 const tools::Long nNo
= MapBookmarkVariables(pF
, sOrigName
, aResult
);
1462 SwSetExpFieldType
* pFT
= static_cast<SwSetExpFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().InsertFieldType(
1463 SwSetExpFieldType(&m_rDoc
, sOrigName
, nsSwGetSetExpType::GSE_STRING
)));
1464 SwSetExpField
aField(pFT
, aResult
);
1465 aField
.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE
| nsSwGetSetExpType::GSE_STRING
);
1466 aField
.SetInputFlag(true);
1467 aField
.SetPromptText( aQ
);
1469 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1471 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_BOOKMARK
, true, nNo
);
1476 eF_ResT
SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc
*, OUString
& rStr
)
1478 if( !m_pNumFieldType
){ // 1st time
1479 SwSetExpFieldType
aT( &m_rDoc
, u
"AutoNr"_ustr
, nsSwGetSetExpType::GSE_SEQ
);
1480 m_pNumFieldType
= static_cast<SwSetExpFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aT
));
1482 SwSetExpField
aField( m_pNumFieldType
, OUString(), GetNumberPara( rStr
) );
1483 aField
.SetValue( ++m_nFieldNum
, nullptr );
1484 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1489 eF_ResT
SwWW8ImplReader::Read_F_Seq( WW8FieldDesc
*, OUString
& rStr
)
1491 OUString aSequenceName
;
1493 bool bHidden
= false;
1494 bool bFormat
= false;
1495 bool bCountOn
= true;
1497 SvxNumType eNumFormat
= SVX_NUM_ARABIC
;
1498 WW8ReadFieldParams
aReadParam( rStr
);
1501 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1507 if( aSequenceName
.isEmpty() )
1508 aSequenceName
= aReadParam
.GetResult();
1509 else if( aBook
.isEmpty() )
1510 aBook
= aReadParam
.GetResult();
1515 bHidden
= true; // activate hidden flag
1519 bFormat
= true; // activate format flag
1520 if ( aReadParam
.SkipToNextToken()!=-2 )
1522 if ( aReadParam
.GetResult()!="MERGEFORMAT" && aReadParam
.GetResult()!="CHARFORMAT" )
1523 eNumFormat
= GetNumTypeFromName( aReadParam
.GetResult() );
1528 if ( aReadParam
.SkipToNextToken()==-2 )
1529 sStart
= aReadParam
.GetResult();
1537 bCountOn
= true; // Increase value by one (default)
1540 case 's': // Outline Level
1541 //#i19682, what I have to do with this value?
1545 if (aSequenceName
.isEmpty() && aBook
.isEmpty())
1546 return eF_ResT::TAGIGN
;
1548 SwSetExpFieldType
* pFT
= static_cast<SwSetExpFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().InsertFieldType(
1549 SwSetExpFieldType( &m_rDoc
, aSequenceName
, nsSwGetSetExpType::GSE_SEQ
) ) );
1550 SwSetExpField
aField( pFT
, OUString(), eNumFormat
);
1552 //#i120654# Add bHidden for /h flag (/h: Hide the field result.)
1554 aField
.SetSubType(aField
.GetSubType() | nsSwExtendedSubType::SUB_INVISIBLE
);
1556 if (!sStart
.isEmpty())
1557 aField
.SetFormula( aSequenceName
+ "=" + sStart
);
1559 aField
.SetFormula(aSequenceName
);
1561 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1565 eF_ResT
SwWW8ImplReader::Read_F_Styleref(WW8FieldDesc
*, OUString
& rString
)
1567 WW8ReadFieldParams
aReadParam(rString
);
1568 sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1570 // \param was found, not normal text.
1571 return eF_ResT::TAGIGN
;
1573 OUString aResult
= aReadParam
.GetResult();
1574 sal_Int32 nResult
= aResult
.toInt32();
1576 return eF_ResT::TAGIGN
;
1578 SwFieldType
* pFieldType
= m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Chapter
);
1579 SwChapterField
aField(static_cast<SwChapterFieldType
*>(pFieldType
), CF_TITLE
);
1580 aField
.SetLevel(nResult
- 1);
1581 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1586 eF_ResT
SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc
* pF
, OUString
& rStr
)
1589 // RegInfoFormat, DefaultFormat for DocInfoFields
1590 sal_uInt16 nReg
= DI_SUB_AUTHOR
;
1591 bool bDateTime
= false;
1592 const sal_uInt16 nFldLock
= (pF
->nOpt
& 0x10) ? DI_SUB_FIXED
: 0;
1596 OUString aDocProperty
;
1597 WW8ReadFieldParams
aReadParam( rStr
);
1600 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1606 if( aDocProperty
.isEmpty() )
1607 aDocProperty
= aReadParam
.GetResult();
1610 //Skip over MERGEFORMAT
1611 (void)aReadParam
.SkipToNextToken();
1616 aDocProperty
= aDocProperty
.replaceAll("\"", "");
1619 There are up to 26 fields that may be meant by 'DocumentProperty'.
1620 Which of them is to be inserted here ?
1621 This Problem can only be solved by implementing a name matching
1622 method that compares the given Parameter String with the four
1623 possible name sets (english, german, french, spanish)
1626 static const char* aName10
= "\x0F"; // SW field code
1627 static const char* aName11
// German
1629 static const char* aName12
// French
1631 static const char* aName13
// English
1633 static const char* aName14
// Spanish
1635 static const char* aName20
= "\x15"; // SW field code
1636 static const char* aName21
// German
1638 static const char* aName22
// French
1640 static const char* aName23
// English
1642 static const char* aName24
// Spanish
1644 static const char* aName30
= "\x16"; // SW field code
1645 static const char* aName31
// German
1646 = "ZULETZTGESPEICHERTZEIT";
1647 static const char* aName32
// French
1648 = "DERNIERENREGISTREMENT";
1649 static const char* aName33
// English
1651 static const char* aName34
// Spanish
1653 static const char* aName40
= "\x17"; // SW field code
1654 static const char* aName41
// German
1655 = "ZULETZTGEDRUCKT";
1656 static const char* aName42
// French
1657 = "DERNI\xC8" "REIMPRESSION";
1658 static const char* aName43
// English
1660 static const char* aName44
// Spanish
1662 static const char* aName50
= "\x18"; // SW field code
1663 static const char* aName51
// German
1664 = "\xDC" "BERARBEITUNGSNUMMER";
1665 static const char* aName52
// French
1666 = "NUM\xC9" "RODEREVISION";
1667 static const char* aName53
// English
1669 static const char* aName54
// Spanish
1671 static const sal_uInt16 nFieldCnt
= 5;
1673 // additional fields are to be coded soon!
1675 static const sal_uInt16 nLangCnt
= 4;
1676 static const char *aNameSet_26
[nFieldCnt
][nLangCnt
+1] =
1678 {aName10
, aName11
, aName12
, aName13
, aName14
},
1679 {aName20
, aName21
, aName22
, aName23
, aName24
},
1680 {aName30
, aName31
, aName32
, aName33
, aName34
},
1681 {aName40
, aName41
, aName42
, aName43
, aName44
},
1682 {aName50
, aName51
, aName52
, aName53
, aName54
}
1685 bool bFieldFound
= false;
1687 for(sal_uInt16 nLIdx
=1; !bFieldFound
&& (nLangCnt
> nLIdx
); ++nLIdx
)
1689 for(nFIdx
= 0; !bFieldFound
&& (nFieldCnt
> nFIdx
); ++nFIdx
)
1691 if( aDocProperty
== OUString( aNameSet_26
[nFIdx
][nLIdx
], strlen(aNameSet_26
[nFIdx
][nLIdx
]),
1692 RTL_TEXTENCODING_MS_1252
) )
1695 pF
->nId
= aNameSet_26
[nFIdx
][0][0];
1702 // LO always automatically updates a DocInfo field from the File-Properties-Custom Prop
1703 // while MS Word requires the user to manually refresh the field (with F9).
1704 // In other words, Word lets the field to be out of sync with the controlling variable.
1705 // Marking as FIXEDFLD solves the automatic replacement problem, but of course prevents
1706 // Writer from making any changes, even on an F9 refresh.
1707 // TODO: Extend LO to allow a linked field that doesn't automatically update.
1708 IDocumentContentOperations
& rIDCO(m_rDoc
.getIDocumentContentOperations());
1709 const auto pType(static_cast<SwDocInfoFieldType
*>(
1710 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo
)));
1711 const OUString sDisplayed
= GetFieldResult(pF
);
1712 SwDocInfoField
aField(pType
, DI_CUSTOM
| nReg
, aDocProperty
);
1714 // If text already matches the DocProperty var, then safe to treat as refreshable field.
1715 OUString sVariable
= aField
.ExpandField(/*bCache=*/false, nullptr);
1716 if (sDisplayed
.getLength() != sVariable
.getLength())
1718 sal_Int32 nLen
= sVariable
.indexOf('\x0');
1720 sVariable
= sVariable
.copy(0, nLen
);
1722 if (sDisplayed
== sVariable
)
1723 rIDCO
.InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1726 // They don't match, so use a fixed field to prevent LO from altering the contents.
1727 SwDocInfoField
aFixedField(pType
, DI_CUSTOM
| DI_SUB_FIXED
| nReg
, aDocProperty
,
1729 rIDCO
.InsertPoolItem(*m_pPaM
, SwFormatField(aFixedField
));
1739 /* supports all INFO variables! */
1755 // MS Word never updates this automatically, so mark as fixed for best compatibility
1756 nSub
= DI_CHANGE
| DI_SUB_FIXED
;
1757 nReg
= DI_SUB_AUTHOR
;
1760 // The real create date can never change, so mark as fixed for best compatibility
1761 nSub
= DI_CREATE
| DI_SUB_FIXED
;
1766 nSub
= DI_PRINT
| nFldLock
;
1774 nSub
= DI_CHANGE
| nFldLock
;
1779 nSub
= DI_CHANGE
| nFldLock
;
1783 case 64: // DOCVARIABLE
1788 sal_uInt32 nFormat
= 0;
1790 LanguageType
nLang(LANGUAGE_SYSTEM
);
1793 SvNumFormatType nDT
= GetTimeDatePara(rStr
, nFormat
, nLang
, pF
->nId
);
1796 case SvNumFormatType::DATE
:
1799 case SvNumFormatType::TIME
:
1802 case SvNumFormatType::DATETIME
:
1812 // Extract DOCVARIABLE varname
1813 if ( 64 == pF
->nId
)
1815 WW8ReadFieldParams
aReadParam( rStr
);
1818 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1824 if( aData
.isEmpty() )
1825 aData
= aReadParam
.GetResult();
1828 //Skip over MERGEFORMAT
1829 (void)aReadParam
.SkipToNextToken();
1834 aData
= aData
.replaceAll("\"", "");
1838 if (DI_CUSTOM
== nSub
)
1840 const auto pType(static_cast<SwUserFieldType
*>(
1841 m_rDoc
.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::User
, aData
, false)));
1844 SwUserField
aField(pType
, 0, nFormat
);
1846 ForceFieldLanguage(aField
, nLang
);
1847 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1853 const auto pType(static_cast<SwDocInfoFieldType
*>(
1854 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo
)));
1855 SwDocInfoField
aField(pType
, nSub
|nReg
, aData
, GetFieldResult(pF
), nFormat
);
1857 ForceFieldLanguage(aField
, nLang
);
1858 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1864 eF_ResT
SwWW8ImplReader::Read_F_Author(WW8FieldDesc
* pF
, OUString
&)
1866 // SH: The SwAuthorField refers not to the original author but to the current user, better use DocInfo
1867 SwDocInfoField
aField( static_cast<SwDocInfoFieldType
*>(
1868 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo
)),
1869 DI_CREATE
|DI_SUB_AUTHOR
|DI_SUB_FIXED
, OUString(), GetFieldResult(pF
));
1870 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1874 eF_ResT
SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc
*, OUString
& )
1876 SwTemplNameField
aField( static_cast<SwTemplNameFieldType
*>(
1877 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName
)), FF_NAME
);
1878 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1882 // Both the date and the time fields can be used for showing a date a time or both.
1883 eF_ResT
SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc
*pF
, OUString
& rStr
)
1885 bool bHijri
= false;
1886 WW8ReadFieldParams
aReadParam(rStr
);
1889 const sal_Int32 nTok
= aReadParam
.SkipToNextToken();
1902 //Saka Calendar, should we do something with this ?
1907 sal_uInt32 nFormat
= 0;
1909 LanguageType
nLang(LANGUAGE_SYSTEM
);
1910 SvNumFormatType nDT
= GetTimeDatePara(rStr
, nFormat
, nLang
, ww::eDATE
, bHijri
);
1912 if( SvNumFormatType::UNDEFINED
== nDT
) // no D/T-Formatstring
1916 nDT
= SvNumFormatType::TIME
;
1917 nFormat
= m_rDoc
.GetNumberFormatter()->GetFormatIndex(
1918 NF_TIME_START
, LANGUAGE_SYSTEM
);
1922 nDT
= SvNumFormatType::DATE
;
1923 nFormat
= m_rDoc
.GetNumberFormatter()->GetFormatIndex(
1924 NF_DATE_START
, LANGUAGE_SYSTEM
);
1928 if (nDT
& SvNumFormatType::DATE
|| nDT
== SvNumFormatType::TIME
)
1930 SwDateTimeField
aField(static_cast<SwDateTimeFieldType
*>(
1931 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime
)),
1932 nDT
& SvNumFormatType::DATE
? DATEFLD
: TIMEFLD
, nFormat
);
1933 if (pF
->nOpt
& 0x10) // Fixed field
1936 if (!m_rDoc
.GetNumberFormatter()->IsNumberFormat(GetFieldResult(pF
), nFormat
, fSerial
,
1937 SvNumInputOptions::LAX_TIME
))
1938 return eF_ResT::TEXT
; // just drop the field and insert the plain text.
1939 aField
.SetSubType(aField
.GetSubType() | FIXEDFLD
);
1940 DateTime
aSetDateTime(m_rDoc
.GetNumberFormatter()->GetNullDate());
1941 aSetDateTime
.AddTime(fSerial
);
1942 aField
.SetDateTime(aSetDateTime
);
1944 ForceFieldLanguage(aField
, nLang
);
1945 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1951 eF_ResT
SwWW8ImplReader::Read_F_FileName(WW8FieldDesc
*, OUString
&rStr
)
1953 SwFileNameFormat eType
= FF_NAME
;
1954 WW8ReadFieldParams
aReadParam(rStr
);
1957 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1963 eType
= FF_PATHNAME
;
1966 //Skip over MERGEFORMAT
1967 (void)aReadParam
.SkipToNextToken();
1970 OSL_ENSURE(false, "unknown option in FileName field");
1975 SwFileNameField
aField(
1976 static_cast<SwFileNameFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename
)), eType
);
1977 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1981 eF_ResT
SwWW8ImplReader::Read_F_Num( WW8FieldDesc
* pF
, OUString
& rStr
)
1983 sal_uInt16 nSub
= DS_PAGE
; // page number
1985 case 27: nSub
= DS_WORD
; break; // number of words
1986 case 28: nSub
= DS_CHAR
; break; // number of characters
1988 SwDocStatField
aField( static_cast<SwDocStatFieldType
*>(
1989 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocStat
)), nSub
,
1990 GetNumberPara( rStr
) );
1991 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1995 eF_ResT
SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc
*, OUString
& rStr
)
1998 SwPageNumberField
aField( static_cast<SwPageNumberFieldType
*>(
1999 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber
)), PG_RANDOM
,
2000 GetNumberPara(rStr
, true));
2002 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2006 eF_ResT
SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc
*, OUString
& rStr
)
2011 sal_Int32 nSize
= 0;
2012 WW8ReadFieldParams
aReadParam( rStr
);
2015 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2022 aQ
= aReadParam
.GetResult();
2026 if ( aReadParam
.GoToTokenParam() )
2027 aName
= aReadParam
.GetResult();
2031 if ( aReadParam
.GoToTokenParam() )
2033 const OUString aSiz
= aReadParam
.GetResult();
2034 if (!aSiz
.isEmpty())
2036 bool bFail
= o3tl::checked_multiply
<sal_Int32
>(aSiz
.toInt32(), 20, nSize
); // pT -> twip
2045 return eF_ResT::TAGIGN
; // -> no 0-char in text
2047 sal_Unicode
const cChar
= static_cast<sal_Unicode
>(aQ
.toInt32());
2048 if (!linguistic::IsControlChar(cChar
) || cChar
== '\r' || cChar
== '\n' || cChar
== '\t')
2050 if (!aName
.isEmpty()) // Font Name set ?
2052 SvxFontItem
aFont(FAMILY_DONTKNOW
, aName
, OUString(),
2053 PITCH_DONTKNOW
, RTL_TEXTENCODING_SYMBOL
, RES_CHRATR_FONT
);
2054 NewAttr(aFont
); // new Font
2057 if (nSize
> 0) //#i20118#
2059 SvxFontHeightItem
aSz(nSize
, 100, RES_CHRATR_FONTSIZE
);
2063 m_rDoc
.getIDocumentContentOperations().InsertString(*m_pPaM
, OUString(cChar
));
2066 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(), RES_CHRATR_FONTSIZE
);
2067 if (!aName
.isEmpty())
2068 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(), RES_CHRATR_FONT
);
2072 m_rDoc
.getIDocumentContentOperations().InsertString(*m_pPaM
, u
"###"_ustr
);
2079 eF_ResT
SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc
*, OUString
& rStr
)
2081 WW8ReadFieldParams
aReadParam( rStr
);
2084 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2099 if( m_bObj
&& m_nPicLocFc
)
2100 m_nObjLocFc
= m_nPicLocFc
;
2101 m_bEmbeddObj
= true;
2102 return eF_ResT::TEXT
;
2106 eF_ResT
SwWW8ImplReader::Read_F_Set( WW8FieldDesc
* pF
, OUString
& rStr
)
2110 WW8ReadFieldParams
aReadParam( rStr
);
2113 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2119 if (sOrigName
.isEmpty())
2120 sOrigName
= aReadParam
.GetResult();
2121 else if (sVal
.isEmpty())
2122 sVal
= aReadParam
.GetResult();
2127 const tools::Long nNo
= MapBookmarkVariables(pF
, sOrigName
, sVal
);
2129 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc
, sOrigName
,
2130 nsSwGetSetExpType::GSE_STRING
) );
2131 SwSetExpField
aField( static_cast<SwSetExpFieldType
*>(pFT
), sVal
, ULONG_MAX
);
2132 aField
.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE
| nsSwGetSetExpType::GSE_STRING
);
2134 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2136 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_BOOKMARK
, true, nNo
);
2142 eF_ResT
SwWW8ImplReader::Read_F_Ref( WW8FieldDesc
*, OUString
& rStr
)
2143 { // Reference - Field
2144 OUString sOrigBkmName
;
2145 REFERENCEMARK eFormat
= REF_CONTENT
;
2147 WW8ReadFieldParams
aReadParam( rStr
);
2150 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2156 if( sOrigBkmName
.isEmpty() ) // get name of bookmark
2157 sOrigBkmName
= aReadParam
.GetResult();
2160 /* References to numbers in Word could be either to a numbered
2161 paragraph or to a chapter number. However Word does not seem to
2162 have the capability we do, of referring to the chapter number some
2163 other bookmark is in. As a result, cross-references to chapter
2164 numbers in a word document will be cross-references to a numbered
2165 paragraph, being the chapter heading paragraph. As it happens, our
2166 cross-references to numbered paragraphs will do the right thing
2167 when the target is a numbered chapter heading, so there is no need
2168 for us to use the REF_CHAPTER bookmark format on import.
2171 eFormat
= REF_NUMBER_NO_CONTEXT
;
2174 eFormat
= REF_NUMBER
;
2177 eFormat
= REF_NUMBER_FULL_CONTEXT
;
2181 eFormat
= REF_UPDOWN
;
2186 // unimplemented switch: just do 'nix nought nothing' :-)
2191 OUString
sBkmName(GetMappedBookmark(sOrigBkmName
));
2193 // #i120879# add cross reference bookmark name prefix, if it
2194 // matches internal TOC bookmark naming convention
2195 if ( IsTOCBookmarkName( sBkmName
) )
2197 sBkmName
= EnsureTOCBookmarkName(sBkmName
);
2198 // track <sBookmarkName> as referenced TOC bookmark.
2199 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sBkmName
);
2202 SwGetRefField
aField(
2203 static_cast<SwGetRefFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)),
2204 sBkmName
,u
""_ustr
,REF_BOOKMARK
,0,0,eFormat
);
2206 if (eFormat
== REF_CONTENT
)
2209 If we are just inserting the contents of the bookmark, then it
2210 is possible that the bookmark is actually a variable, so we
2211 must store it until the end of the document to see if it was,
2212 in which case we'll turn it into a show variable
2214 m_xReffingStck
->NewAttr( *m_pPaM
->GetPoint(), SwFormatField(aField
) );
2215 m_xReffingStck
->SetAttr( *m_pPaM
->GetPoint(), RES_TXTATR_FIELD
);
2219 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
2224 // Note Reference - Field
2225 eF_ResT
SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc
*, OUString
& rStr
)
2228 bool bAboveBelow
= false;
2230 WW8ReadFieldParams
aReadParam( rStr
);
2233 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2239 if( aBkmName
.isEmpty() ) // get name of foot/endnote
2240 aBkmName
= aReadParam
.GetResult();
2243 // activate flag 'Chapter Number'
2251 // unimplemented switch: just do 'nix nought nothing' :-)
2256 // set Sequence No of corresponding Foot-/Endnote to Zero
2257 // (will be corrected in
2258 SwGetRefField
aField( static_cast<SwGetRefFieldType
*>(
2259 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)), aBkmName
, u
""_ustr
, REF_FOOTNOTE
, 0, 0,
2261 m_xReffingStck
->NewAttr(*m_pPaM
->GetPoint(), SwFormatField(aField
));
2262 m_xReffingStck
->SetAttr(*m_pPaM
->GetPoint(), RES_TXTATR_FIELD
);
2265 SwGetRefField
aField2( static_cast<SwGetRefFieldType
*>(
2266 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)),aBkmName
, u
""_ustr
, REF_FOOTNOTE
, 0, 0,
2268 m_xReffingStck
->NewAttr(*m_pPaM
->GetPoint(), SwFormatField(aField2
));
2269 m_xReffingStck
->SetAttr(*m_pPaM
->GetPoint(), RES_TXTATR_FIELD
);
2275 eF_ResT
SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc
*, OUString
& rStr
)
2278 WW8ReadFieldParams
aReadParam( rStr
);
2281 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2284 else if ( nRet
== -2 && sOrigName
.isEmpty() )
2286 sOrigName
= aReadParam
.GetResult();
2290 const OUString
sName(GetMappedBookmark(sOrigName
));
2292 // loading page reference field in TOX
2293 if (m_bLoadingTOXCache
)
2295 // insert page ref representation as plain text --> return FLD_TEXT
2296 // if there is no hyperlink settings for current toc and referenced bookmark is available,
2297 // assign link to current ref area
2298 if (!m_bLoadingTOXHyperlink
&& !sName
.isEmpty())
2300 // #i120879# add cross reference bookmark name prefix, if it
2301 // matches internal TOC bookmark naming convention
2302 OUString sBookmarkName
;
2303 if ( IsTOCBookmarkName( sName
) )
2305 sBookmarkName
= EnsureTOCBookmarkName(sName
);
2306 // track <sBookmarkName> as referenced TOC bookmark.
2307 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sBookmarkName
);
2311 sBookmarkName
= sName
;
2313 OUString sURL
= "#" + sBookmarkName
;
2314 SwFormatINetFormat
aURL( sURL
, u
""_ustr
);
2315 static constexpr OUString
sLinkStyle(u
"Index Link"_ustr
);
2316 const sal_uInt16 nPoolId
=
2317 SwStyleNameMapper::GetPoolIdFromProgName( sLinkStyle
, SwGetPoolIdFromName::ChrFmt
);
2318 aURL
.SetVisitedFormatAndId( sLinkStyle
, nPoolId
);
2319 aURL
.SetINetFormatAndId( sLinkStyle
, nPoolId
);
2320 m_xCtrlStck
->NewAttr( *m_pPaM
->GetPoint(), aURL
);
2322 return eF_ResT::TEXT
;
2325 // #i120879# add cross reference bookmark name prefix, if it matches
2326 // internal TOC bookmark naming convention
2327 OUString sPageRefBookmarkName
;
2328 if ( IsTOCBookmarkName( sName
) )
2330 sPageRefBookmarkName
= EnsureTOCBookmarkName(sName
);
2331 // track <sPageRefBookmarkName> as referenced TOC bookmark.
2332 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sPageRefBookmarkName
);
2336 sPageRefBookmarkName
= sName
;
2338 SwGetRefField
aField( static_cast<SwGetRefFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)),
2339 sPageRefBookmarkName
, u
""_ustr
, REF_BOOKMARK
, 0, 0, REF_PAGE
);
2340 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2346 //For MS MacroButton field, the symbol in plain text is always "(" (0x28),
2347 //which should be mapped according to the macro type
2348 static bool ConvertMacroSymbol( std::u16string_view rName
, OUString
& rReference
)
2350 bool bConverted
= false;
2351 if( rReference
== "(" )
2354 sal_Unicode cSymbol
= sal_Unicode(); // silence false warning
2355 if (rName
== u
"CheckIt")
2357 else if (rName
== u
"UncheckIt")
2359 else if (rName
== u
"ShowExample")
2366 rReference
= OUString(cSymbol
);
2372 eF_ResT
SwWW8ImplReader::Read_F_Macro( WW8FieldDesc
*, OUString
& rStr
)
2376 bool bNewVText
= true;
2377 bool bBracket
= false;
2378 WW8ReadFieldParams
aReadParam( rStr
);
2382 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2388 if( aName
.isEmpty() )
2389 aName
= aReadParam
.GetResult();
2390 else if( aVText
.isEmpty() || bBracket
)
2394 aVText
+= aReadParam
.GetResult();
2397 bBracket
= (aVText
[0] == '[');
2400 else if( aVText
.endsWith("]") )
2406 if( aName
.isEmpty() )
2407 return eF_ResT::TAGIGN
; // makes no sense without Macro-Name
2409 NotifyMacroEventRead();
2411 //try converting macro symbol according to macro name
2412 bool bApplyWingdings
= ConvertMacroSymbol( aName
, aVText
);
2413 aName
= "StarOffice.Standard.Modul1." + aName
;
2415 SwMacroField
aField( static_cast<SwMacroFieldType
*>(
2416 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Macro
)), aName
, aVText
);
2418 if( !bApplyWingdings
)
2419 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2422 //set Wingdings font
2424 for ( ; i
< m_xFonts
->GetMax(); i
++ )
2429 rtl_TextEncoding eSrcCharSet
;
2430 if( GetFontParams( i
, eFamily
, aFontName
, ePitch
, eSrcCharSet
)
2431 && aFontName
=="Wingdings" )
2437 if ( i
< m_xFonts
->GetMax() )
2440 SetNewFontAttr( i
, true, RES_CHRATR_FONT
);
2441 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2442 m_xCtrlStck
->SetAttr( *m_pPaM
->GetPoint(), RES_CHRATR_FONT
);
2450 bool CanUseRemoteLink(const OUString
&rGrfName
)
2452 bool bUseRemote
= false;
2455 // Related: tdf#102499, add a default css::ucb::XCommandEnvironment
2456 // in order to have https protocol manage certificates correctly
2457 uno::Reference
< task::XInteractionHandler
> xIH(
2458 task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr));
2460 uno::Reference
< ucb::XProgressHandler
> xProgress
;
2461 rtl::Reference
<::ucbhelper::CommandEnvironment
> pCommandEnv
=
2462 new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH
), xProgress
);
2464 ::ucbhelper::Content
aCnt(rGrfName
,
2465 static_cast< ucb::XCommandEnvironment
* >(pCommandEnv
.get()),
2466 comphelper::getProcessComponentContext());
2468 if ( !INetURLObject( rGrfName
).isAnyKnownWebDAVScheme() )
2471 aCnt
.getPropertyValue(u
"Title"_ustr
) >>= aTitle
;
2472 bUseRemote
= !aTitle
.isEmpty();
2476 // is a link to a WebDAV resource
2477 // need to use MediaType to check for link usability
2478 OUString aMediaType
;
2479 aCnt
.getPropertyValue(u
"MediaType"_ustr
) >>= aMediaType
;
2480 bUseRemote
= !aMediaType
.isEmpty();
2485 // this file did not exist, so we will not set this as graphiclink
2492 eF_ResT
SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc
*, OUString
& rStr
)
2495 bool bEmbedded
= true;
2497 WW8ReadFieldParams
aReadParam( rStr
);
2500 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2506 if (aGrfName
.isEmpty())
2507 aGrfName
= ConvertFFileName(aReadParam
.GetResult());
2514 case 'c':// skip the converter name
2515 aReadParam
.FindNextStringPiece();
2521 bEmbedded
= !CanUseRemoteLink(aGrfName
);
2528 Now we write the Link into the Doc and remember the SwFlyFrameFormat.
2529 Since we end on return FLD_READ_FSPA below, the skip value will be set
2530 so that Char-1 will still be read.
2531 When we then call SwWW8ImplReader::ImportGraf() it will then recognize
2532 that we have inserted a graphic link and the suiting SwAttrSet will be
2533 inserted into the frame format.
2535 SfxItemSetFixed
<RES_FRMATR_BEGIN
, RES_FRMATR_END
-1> aFlySet( m_rDoc
.GetAttrPool() );
2536 aFlySet
.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR
) );
2537 aFlySet
.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP
, text::RelOrientation::FRAME
));
2538 m_pFlyFormatOfJustInsertedGraphic
=
2539 m_rDoc
.getIDocumentContentOperations().InsertGraphic(*m_pPaM
,
2542 nullptr, // Graphic*
2544 nullptr, nullptr); // SwFrameFormat*
2545 m_aGrfNameGenerator
.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic
,
2546 INetURLObject(aGrfName
).GetBase());
2548 return eF_ResT::READ_FSPA
;
2551 OUString
wwSectionNamer::UniqueName()
2553 const OUString
aName(msFileLinkSeed
+ OUString::number(++mnFileSectionNo
));
2554 return mrDoc
.GetUniqueSectionName(&aName
);
2558 eF_ResT
SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc
* /*pF*/, OUString
& rStr
)
2562 WW8ReadFieldParams
aReadParam( rStr
);
2565 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2571 if( aPara
.isEmpty() )
2572 aPara
= aReadParam
.GetResult();
2573 else if( aBook
.isEmpty() )
2574 aBook
= aReadParam
.GetResult();
2577 //Skip over MERGEFORMAT
2578 (void)aReadParam
.SkipToNextToken();
2582 aPara
= ConvertFFileName(aPara
);
2584 if (!aBook
.isEmpty() && aBook
[ 0 ] != '\\')
2586 // Section from Source (no switch)?
2587 ConvertUFName(aBook
);
2588 aPara
+= OUStringChar(sfx2::cTokenSeparator
)
2589 + OUStringChar(sfx2::cTokenSeparator
) + aBook
;
2594 What we will do is insert a section to be linked to a file, but just in
2595 case the file is not available we will fill in the section with the stored
2596 content of this winword field as a fallback.
2598 SwPosition
aTmpPos(*m_pPaM
->GetPoint());
2600 SwSectionData
aSection(SectionType::FileLink
,
2601 m_aSectionNameGenerator
.UniqueName());
2602 aSection
.SetLinkFileName( aPara
);
2603 aSection
.SetProtectFlag(true);
2605 SwSection
*const pSection
=
2606 m_rDoc
.InsertSwSection(*m_pPaM
, aSection
, nullptr, nullptr, false);
2607 OSL_ENSURE(pSection
, "no section inserted");
2609 return eF_ResT::TEXT
;
2610 const SwSectionNode
* pSectionNode
= pSection
->GetFormat()->GetSectionNode();
2611 OSL_ENSURE(pSectionNode
, "no section node!");
2613 return eF_ResT::TEXT
;
2615 m_pPaM
->GetPoint()->Assign( pSectionNode
->GetIndex()+1 );
2617 //we have inserted a section before this point, so adjust pos
2618 //for future page/section segment insertion
2619 m_aSectionManager
.PrependedInlineNode(aTmpPos
, m_pPaM
->GetPointNode());
2621 return eF_ResT::TEXT
;
2625 eF_ResT
SwWW8ImplReader::Read_F_DBField( WW8FieldDesc
* pF
, OUString
& rStr
)
2627 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
2632 WW8ReadFieldParams
aReadParam( rStr
);
2635 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2641 if( aName
.isEmpty() )
2642 aName
= aReadParam
.GetResult();
2646 SwDBFieldType
aD( &m_rDoc
, aName
, SwDBData() ); // Database: nothing
2648 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aD
);
2649 SwDBField
aField( static_cast<SwDBFieldType
*>(pFT
) );
2650 aField
.SetFieldCode( rStr
);
2653 m_xSBase
->WW8ReadString( *m_pStrm
, aResult
, m_xPlcxMan
->GetCpOfs()+
2654 pF
->nSRes
, pF
->nLRes
, m_eTextCharSet
);
2656 aResult
= aResult
.replace( '\xb', '\n' );
2658 aField
.InitContent(aResult
);
2660 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField( aField
));
2666 eF_ResT
SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc
*, OUString
& )
2668 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
2669 SwDBNextSetFieldType aN
;
2670 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aN
);
2671 SwDBNextSetField
aField( static_cast<SwDBNextSetFieldType
*>(pFT
), OUString(),
2672 SwDBData() ); // Database: nothing
2673 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2679 eF_ResT
SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc
*, OUString
& )
2681 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
2682 SwDBSetNumberFieldType aN
;
2683 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aN
);
2684 SwDBSetNumberField
aField( static_cast<SwDBSetNumberFieldType
*>(pFT
),
2685 SwDBData() ); // Datenbase: nothing
2686 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2692 EQ , only the usage for
2693 a. Combined Characters supported, must be exactly in the form that word
2694 only accepts as combined characters, i.e.
2695 eq \o(\s\up Y(XXX),\s\do Y(XXX))
2696 b. Ruby Text supported, must be in the form that word recognizes as being
2700 eF_ResT
SwWW8ImplReader::Read_F_Equation( WW8FieldDesc
*, OUString
& rStr
)
2702 WW8ReadFieldParams
aReadParam( rStr
);
2703 const sal_Int32 cChar
= aReadParam
.SkipToNextToken();
2704 if ('o' == cChar
|| 'O' == cChar
)
2706 EquationResult
aResult(ParseCombinedChars(rStr
));
2708 if (aResult
.sType
== "Input")
2710 SwInputField
aField( static_cast<SwInputFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input
)),
2711 aResult
.sResult
, aResult
.sResult
, INP_TXT
, 0 );
2712 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) ); // insert input field
2714 else if (aResult
.sType
== "CombinedCharacters")
2716 SwCombinedCharField
aField(static_cast<SwCombinedCharFieldType
*>(
2717 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars
)), aResult
.sType
);
2718 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
2721 else if ('*' == cChar
)
2722 Read_SubF_Ruby(aReadParam
);
2727 void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams
& rReadParam
)
2729 sal_uInt16 nJustificationCode
=0;
2731 sal_uInt32 nFontSize
=0;
2736 const sal_Int32 nRet
= rReadParam
.SkipToNextToken();
2743 OUString sTemp
= rReadParam
.GetResult();
2744 if( sTemp
.startsWithIgnoreAsciiCase( "jc" ) )
2746 sTemp
= sTemp
.copy(2);
2747 nJustificationCode
= o3tl::narrowing
<sal_uInt16
>(sTemp
.toInt32());
2749 else if( sTemp
.startsWithIgnoreAsciiCase( "hps" ) )
2751 sTemp
= sTemp
.copy(3);
2752 nFontSize
= static_cast<sal_uInt32
>(sTemp
.toInt32());
2754 else if( sTemp
.startsWithIgnoreAsciiCase( "Font:" ) )
2756 sTemp
= sTemp
.copy(5);
2766 const sal_Int32 nRes
= rReadParam
.SkipToNextToken();
2771 if (-2 == rReadParam
.SkipToNextToken() &&
2772 rReadParam
.GetResult().startsWithIgnoreAsciiCase("p"))
2774 if (-2 == rReadParam
.SkipToNextToken())
2776 OUString sPart
= rReadParam
.GetResult();
2777 sal_Int32 nBegin
= sPart
.indexOf('(');
2779 //Word disallows brackets in this field,
2780 sal_Int32 nEnd
= sPart
.indexOf(')');
2782 if ((nBegin
!= -1) &&
2783 (nEnd
!= -1) && (nBegin
< nEnd
))
2785 sRuby
= sPart
.copy(nBegin
+1,nEnd
-nBegin
-1);
2789 nBegin
= sPart
.indexOf(',',nEnd
);
2792 nBegin
= sPart
.indexOf(';',nEnd
);
2794 nEnd
= sPart
.lastIndexOf(')');
2796 if ((nBegin
!= -1) && (nEnd
!= -1) && (nBegin
< nEnd
))
2798 sText
= sPart
.copy(nBegin
+1,nEnd
-nBegin
-1);
2799 sText
= sw::FilterControlChars(sText
);
2809 //Translate and apply
2810 if (sRuby
.isEmpty() || sText
.isEmpty() || sFontName
.isEmpty() || !nFontSize
)
2813 css::text::RubyAdjust eRubyAdjust
;
2814 switch (nJustificationCode
)
2817 eRubyAdjust
= css::text::RubyAdjust_CENTER
;
2820 eRubyAdjust
= css::text::RubyAdjust_BLOCK
;
2823 eRubyAdjust
= css::text::RubyAdjust_INDENT_BLOCK
;
2827 eRubyAdjust
= css::text::RubyAdjust_LEFT
;
2830 eRubyAdjust
= css::text::RubyAdjust_RIGHT
;
2834 SwFormatRuby
aRuby(sRuby
);
2835 const SwCharFormat
*pCharFormat
=nullptr;
2836 //Make a guess at which of asian of western we should be setting
2837 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
2838 sal_uInt16 nScript
= g_pBreakIt
->GetBreakIter()->getScriptType(sRuby
, 0);
2840 //Check to see if we already have a ruby charstyle that this fits
2841 for(const auto& rpCharFormat
: m_aRubyCharFormats
)
2843 const SvxFontHeightItem
&rFH
=
2844 rpCharFormat
->GetFormatAttr(
2845 GetWhichOfScript(RES_CHRATR_FONTSIZE
,nScript
));
2846 if (rFH
.GetHeight() == nFontSize
*10)
2848 const SvxFontItem
&rF
= rpCharFormat
->GetFormatAttr(
2849 GetWhichOfScript(RES_CHRATR_FONT
,nScript
));
2850 if (rF
.GetFamilyName() == sFontName
)
2852 pCharFormat
= rpCharFormat
;
2858 //Create a new char style if necessary
2862 //Take this as the base name
2863 SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT
,aNm
);
2864 aNm
+=OUString::number(m_aRubyCharFormats
.size()+1);
2865 SwCharFormat
*pFormat
= m_rDoc
.MakeCharFormat(aNm
, m_rDoc
.GetDfltCharFormat());
2866 SvxFontHeightItem
aHeightItem(nFontSize
*10, 100, RES_CHRATR_FONTSIZE
);
2867 SvxFontItem
aFontItem(FAMILY_DONTKNOW
,sFontName
,
2868 OUString(), PITCH_DONTKNOW
, RTL_TEXTENCODING_DONTKNOW
, RES_CHRATR_FONT
);
2869 aHeightItem
.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE
,nScript
));
2870 aFontItem
.SetWhich(GetWhichOfScript(RES_CHRATR_FONT
,nScript
));
2871 pFormat
->SetFormatAttr(aHeightItem
);
2872 pFormat
->SetFormatAttr(aFontItem
);
2873 m_aRubyCharFormats
.push_back(pFormat
);
2874 pCharFormat
= pFormat
;
2877 //Set the charstyle and justification
2878 aRuby
.SetCharFormatName(pCharFormat
->GetName());
2879 aRuby
.SetCharFormatId(pCharFormat
->GetPoolFormatId());
2880 aRuby
.SetAdjustment(eRubyAdjust
);
2883 m_rDoc
.getIDocumentContentOperations().InsertString( *m_pPaM
, sText
);
2884 m_xCtrlStck
->SetAttr( *m_pPaM
->GetPoint(), RES_TXTATR_CJK_RUBY
);
2888 // "table of ..." fields
2890 static void lcl_toxMatchACSwitch(SwDoc
const & rDoc
,
2892 WW8ReadFieldParams
& rParam
,
2893 SwCaptionDisplay eCaptionType
)
2895 if ( rParam
.GoToTokenParam() )
2897 SwTOXType
* pType
= const_cast<SwTOXType
*>(rDoc
.GetTOXType( TOX_ILLUSTRATIONS
, 0));
2898 rBase
.RegisterToTOXType( *pType
);
2899 rBase
.SetCaptionDisplay( eCaptionType
);
2900 // Read Sequence Name and store in TOXBase
2901 OUString
sSeqName( rParam
.GetResult() );
2902 lcl_ConvertSequenceName( sSeqName
);
2903 rBase
.SetSequenceName( sSeqName
);
2907 static void EnsureMaxLevelForTemplates(SwTOXBase
& rBase
)
2909 //If the TOC contains Template entries at levels > the evaluation level
2910 //that was initially taken from the max normal outline level of the word TOC
2911 //then we cannot use that for the evaluation level because writer cuts off
2912 //all styles above that level, while word just cuts off the "standard"
2913 //outline styles, we have no option but to expand to the highest level
2915 if ((rBase
.GetLevel() != MAXLEVEL
) && (SwTOXElement::Template
& rBase
.GetCreateType()))
2917 for (sal_uInt16 nI
= MAXLEVEL
; nI
> 0; --nI
)
2919 if (!rBase
.GetStyleNames(nI
-1).isEmpty())
2928 static void lcl_toxMatchTSwitch(SwWW8ImplReader
const & rReader
, SwTOXBase
& rBase
,
2929 WW8ReadFieldParams
& rParam
)
2931 if ( !rParam
.GoToTokenParam() )
2934 OUString
sParams( rParam
.GetResult() );
2935 if( sParams
.isEmpty() )
2938 sal_Int32 nIndex
= 0;
2940 // Delimiters between styles and style levels appears to allow both ; and ,
2942 OUString
sTemplate( sParams
.getToken(0, ';', nIndex
) );
2946 sTemplate
= sParams
.getToken(0, ',', nIndex
);
2950 const SwFormat
* pStyle
= rReader
.GetStyleWithOrgWWName(sTemplate
);
2952 sTemplate
= pStyle
->GetName();
2953 // Store Style for Level 0 into TOXBase
2954 rBase
.SetStyleNames( sTemplate
, 0 );
2956 else while( -1 != nIndex
)
2958 sal_Int32 nOldIndex
=nIndex
;
2959 sal_uInt16 nLevel
= o3tl::narrowing
<sal_uInt16
>(
2960 o3tl::toInt32(o3tl::getToken(sParams
, 0, ';', nIndex
)));
2964 nLevel
= o3tl::narrowing
<sal_uInt16
>(
2965 o3tl::toInt32(o3tl::getToken(sParams
, 0, ',', nIndex
)));
2968 if( (0 < nLevel
) && (MAXLEVEL
>= nLevel
) )
2971 // Store Style and Level into TOXBase
2972 const SwFormat
* pStyle
2973 = rReader
.GetStyleWithOrgWWName( sTemplate
);
2976 sTemplate
= pStyle
->GetName();
2978 OUString
sStyles( rBase
.GetStyleNames( nLevel
) );
2979 if( !sStyles
.isEmpty() )
2980 sStyles
+= OUStringChar(TOX_STYLE_DELIMITER
);
2981 sStyles
+= sTemplate
;
2982 rBase
.SetStyleNames( sStyles
, nLevel
);
2984 // read next style name...
2986 sTemplate
= sParams
.getToken(0, ';', nIndex
);
2990 sTemplate
= sParams
.getToken(0, ',', nIndex
);
2995 sal_uInt16
wwSectionManager::CurrentSectionColCount() const
2997 sal_uInt16 nIndexCols
= 1;
2998 if (!maSegments
.empty())
2999 nIndexCols
= maSegments
.back().maSep
.ccolM1
+ 1;
3003 //Will there be a new pagebreak at this position (don't know what type
3005 bool wwSectionManager::WillHavePageDescHere(const SwNode
& rNd
) const
3008 if (!maSegments
.empty())
3010 if (!maSegments
.back().IsContinuous() &&
3011 maSegments
.back().maStart
== rNd
)
3019 static sal_uInt16
lcl_GetMaxValidWordTOCLevel(const SwForm
&rForm
)
3021 // GetFormMax() returns level + 1, hence the -1
3022 sal_uInt16 nRet
= rForm
.GetFormMax()-1;
3024 // If the max of this type of TOC is greater than the max of a word
3025 // possible toc, then clip to the word max
3026 if (nRet
> WW8ListManager::nMaxLevel
)
3027 nRet
= WW8ListManager::nMaxLevel
;
3032 eF_ResT
SwWW8ImplReader::Read_F_Tox( WW8FieldDesc
* pF
, OUString
& rStr
)
3034 if (!m_bLoadingTOXCache
)
3036 m_bLoadingTOXCache
= true;
3040 // Embedded TOX --> continue reading its content, but no further TOX
3042 ++m_nEmbeddedTOXLevel
;
3043 return eF_ResT::TEXT
;
3047 return eF_ResT::TEXT
; // ignore (#i25440#)
3049 TOXTypes eTox
; // create a ToxBase
3063 SwTOXElement nCreateOf
= (eTox
== TOX_CONTENT
) ? SwTOXElement::OutlineLevel
: SwTOXElement::Mark
;
3065 sal_uInt16 nIndexCols
= 1;
3067 const SwTOXType
* pType
= m_rDoc
.GetTOXType( eTox
, 0 );
3068 SwForm
aOrigForm(eTox
);
3069 std::shared_ptr
<SwTOXBase
> pBase
= std::make_shared
<SwTOXBase
>( pType
, aOrigForm
, nCreateOf
, OUString() );
3070 pBase
->SetProtected(m_aSectionManager
.CurrentSectionIsProtected());
3074 SwTOIOptions eOptions
= SwTOIOptions::SameEntry
| SwTOIOptions::CaseSensitive
;
3076 // We set SwTOXElement::OutlineLevel only if
3077 // the parameter \o is within 1 to 9
3078 // or the parameter \f exists
3079 // or NO switch parameter are given at all.
3080 WW8ReadFieldParams
aReadParam( rStr
);
3083 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3089 if ( aReadParam
.GoToTokenParam() )
3091 const OUString
sParams( aReadParam
.GetResult() );
3092 // if NO OUString just ignore the \c
3093 if( !sParams
.isEmpty() )
3095 nIndexCols
= o3tl::narrowing
<sal_uInt16
>(sParams
.toInt32());
3101 if ( aReadParam
.GoToTokenParam() ) // if NO String just ignore the \e
3103 OUString
sDelimiter( aReadParam
.GetResult() );
3104 SwForm
aForm( pBase
->GetTOXForm() );
3106 // Attention: if TOX_CONTENT brave
3107 // GetFormMax() returns MAXLEVEL + 1 !!
3108 sal_uInt16 nEnd
= aForm
.GetFormMax()-1;
3110 for(sal_uInt16 nLevel
= 1;
3114 // Levels count from 1
3115 // Level 0 is reserved for CAPTION
3117 // Insert delimiter instead of tab in front of the page number if there is one:
3118 FormTokenType ePrevType
= TOKEN_END
;
3119 FormTokenType eType
;
3121 SwFormTokens aPattern
=
3122 aForm
.GetPattern(nLevel
);
3123 SwFormTokens::iterator aIt
= aPattern
.begin();
3126 eType
= ++aIt
== aPattern
.end() ? TOKEN_END
: aIt
->eTokenType
;
3128 if (eType
== TOKEN_PAGE_NUMS
)
3130 if (TOKEN_TAB_STOP
== ePrevType
)
3134 if (!sDelimiter
.isEmpty() && sDelimiter
[0] == 0x09)
3135 aIt
->eTabAlign
= SvxTabAdjust::End
;
3138 SwFormToken
aToken(TOKEN_TEXT
);
3139 aToken
.sText
= sDelimiter
;
3140 *aIt
= std::move(aToken
);
3142 aForm
.SetPattern(nLevel
, std::move(aPattern
));
3150 while (TOKEN_END
!= eType
);
3153 pBase
->SetTOXForm( aForm
);
3159 eOptions
|= SwTOIOptions::AlphaDelimiter
;
3164 pBase
->SetOptions( eOptions
);
3170 bool bIsHyperlink
= false;
3171 // We set SwTOXElement::OutlineLevel only if
3172 // the parameter \o is within 1 to 9
3173 // or the parameter \f exists
3174 // or NO switch parameter are given at all.
3175 SwTOXElement eCreateFrom
= SwTOXElement::NONE
;
3176 sal_Int32 nMaxLevel
= 0;
3177 WW8ReadFieldParams
aReadParam( rStr
);
3180 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3186 bIsHyperlink
= true;
3190 lcl_toxMatchACSwitch(m_rDoc
, *pBase
, aReadParam
,
3198 if( !aReadParam
.GetTokenSttFromTo(nullptr, &nVal
, WW8ListManager::nMaxLevel
) )
3199 nVal
= lcl_GetMaxValidWordTOCLevel(aOrigForm
);
3200 if( nMaxLevel
< nVal
)
3202 eCreateFrom
|= SwTOXElement::OutlineLevel
;
3206 eCreateFrom
|= SwTOXElement::Mark
;
3211 if( aReadParam
.GetTokenSttFromTo(nullptr, &nVal
, WW8ListManager::nMaxLevel
) )
3213 if( nMaxLevel
< nVal
)
3215 eCreateFrom
|= SwTOXElement::Mark
;
3219 case 't': // paragraphs using special styles shall
3220 // provide the TOX's content
3221 lcl_toxMatchTSwitch(*this, *pBase
, aReadParam
);
3222 eCreateFrom
|= SwTOXElement::Template
;
3226 if ( aReadParam
.GoToTokenParam() ) // if NO String just ignore the \p
3228 OUString
sDelimiter( aReadParam
.GetResult() );
3229 SwForm
aForm( pBase
->GetTOXForm() );
3231 // Attention: if TOX_CONTENT brave
3232 // GetFormMax() returns MAXLEVEL + 1 !!
3233 sal_uInt16 nEnd
= aForm
.GetFormMax()-1;
3235 for(sal_uInt16 nLevel
= 1;
3239 // Levels count from 1
3240 // Level 0 is reserved for CAPTION
3242 // Insert delimiter instead of tab in front of the pagenumber if there is one:
3243 FormTokenType ePrevType
= TOKEN_END
;
3244 FormTokenType eType
;
3247 SwFormTokens aPattern
= aForm
.GetPattern(nLevel
);
3248 SwFormTokens::iterator aIt
= aPattern
.begin();
3251 eType
= ++aIt
== aPattern
.end() ? TOKEN_END
: aIt
->eTokenType
;
3253 if (eType
== TOKEN_PAGE_NUMS
)
3255 if (TOKEN_TAB_STOP
== ePrevType
)
3259 SwFormToken
aToken(TOKEN_TEXT
);
3260 aToken
.sText
= sDelimiter
;
3262 *aIt
= std::move(aToken
);
3263 aForm
.SetPattern(nLevel
,
3264 std::move(aPattern
));
3270 while( TOKEN_END
!= eType
);
3273 pBase
->SetTOXForm( aForm
);
3277 case 'n': // don't print page numbers
3279 // read START and END param
3280 sal_Int32
nStart(0);
3282 if( !aReadParam
.GetTokenSttFromTo( &nStart
, &nEnd
,
3283 WW8ListManager::nMaxLevel
) )
3286 nEnd
= aOrigForm
.GetFormMax()-1;
3288 // remove page numbers from this levels
3289 SwForm
aForm( pBase
->GetTOXForm() );
3290 if (aForm
.GetFormMax() <= nEnd
)
3291 nEnd
= aForm
.GetFormMax()-1;
3292 for ( sal_Int32 nLevel
= nStart
; nLevel
<=nEnd
; ++nLevel
)
3294 // Levels count from 1
3295 // Level 0 is reserved for CAPTION
3297 // Remove pagenumber and if necessary the tab in front of it:
3298 FormTokenType eType
;
3300 SwFormTokens aPattern
= aForm
.GetPattern(nLevel
);
3301 SwFormTokens::iterator aIt
= aPattern
.begin();
3304 eType
= ++aIt
== aPattern
.end() ? TOKEN_END
: aIt
->eTokenType
;
3306 if (eType
== TOKEN_PAGE_NUMS
)
3308 aIt
= aPattern
.erase(aIt
);
3315 aPattern
.erase(aIt
);
3316 aForm
.SetPattern(nLevel
, std::move(aPattern
));
3321 while (TOKEN_END
!= eType
);
3324 pBase
->SetTOXForm( aForm
);
3329 // the following switches are not (yet) supported
3330 // by good old StarWriter:
3339 // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens
3340 // also include the hyperlinks and page references
3341 SwFormToken
aLinkStart(TOKEN_LINK_START
);
3342 SwFormToken
aLinkEnd(TOKEN_LINK_END
);
3343 aLinkStart
.sCharStyleName
= "Index Link";
3344 aLinkEnd
.sCharStyleName
= "Index Link";
3345 SwForm
aForm(pBase
->GetTOXForm());
3346 sal_uInt16 nEnd
= aForm
.GetFormMax()-1;
3348 for(sal_uInt16 nLevel
= 1; nLevel
<= nEnd
; ++nLevel
)
3350 SwFormTokens aPattern
= aForm
.GetPattern(nLevel
);
3353 aPattern
.insert(aPattern
.begin(), aLinkStart
);
3357 auto aItr
= std::find_if(aPattern
.begin(), aPattern
.end(),
3358 [](const SwFormToken
& rToken
) { return rToken
.eTokenType
== TOKEN_PAGE_NUMS
; });
3359 if (aItr
!= aPattern
.end())
3360 aPattern
.insert(aItr
, aLinkStart
);
3362 aPattern
.push_back(aLinkEnd
);
3363 aForm
.SetPattern(nLevel
, std::move(aPattern
));
3365 pBase
->SetTOXForm(aForm
);
3368 nMaxLevel
= WW8ListManager::nMaxLevel
;
3369 pBase
->SetLevel(nMaxLevel
);
3371 const TOXTypes eType
= pBase
->GetTOXType()->GetType();
3376 //If we would be created from outlines, either explicitly or by default
3377 //then see if we need extra styles added to the outlines
3378 SwTOXElement eEffectivelyFrom
= eCreateFrom
!= SwTOXElement::NONE
? eCreateFrom
: SwTOXElement::OutlineLevel
;
3379 if (eEffectivelyFrom
& SwTOXElement::OutlineLevel
)
3381 // #i19683# Insert a text token " " between the number and entry token.
3382 // In an ideal world we could handle the tab stop between the number and
3383 // the entry correctly, but I currently have no clue how to obtain
3384 // the tab stop position. It is _not_ set at the paragraph style.
3385 std::unique_ptr
<SwForm
> pForm
;
3386 for (const SwWW8StyInf
& rSI
: m_vColl
)
3388 if (rSI
.IsOutlineNumbered())
3390 sal_uInt16 nStyleLevel
= rSI
.mnWW8OutlineLevel
;
3391 const SwNumFormat
& rFormat
= rSI
.GetOutlineNumrule()->Get( nStyleLevel
);
3392 if ( SVX_NUM_NUMBER_NONE
!= rFormat
.GetNumberingType() )
3397 pForm
.reset(new SwForm( pBase
->GetTOXForm() ));
3399 SwFormTokens aPattern
= pForm
->GetPattern(nStyleLevel
);
3400 SwFormTokens::iterator aIt
=
3401 find_if(aPattern
.begin(), aPattern
.end(),
3402 SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO
));
3404 if ( aIt
!= aPattern
.end() )
3406 SwFormToken
aNumberEntrySeparator( TOKEN_TEXT
);
3407 aNumberEntrySeparator
.sText
= " ";
3408 aPattern
.insert( ++aIt
, aNumberEntrySeparator
);
3409 pForm
->SetPattern( nStyleLevel
, std::move(aPattern
) );
3416 pBase
->SetTOXForm( *pForm
);
3420 if (eCreateFrom
!= SwTOXElement::NONE
)
3421 pBase
->SetCreate(eCreateFrom
);
3422 EnsureMaxLevelForTemplates(*pBase
);
3425 case TOX_ILLUSTRATIONS
:
3427 if( eCreateFrom
== SwTOXElement::NONE
)
3428 eCreateFrom
= SwTOXElement::Sequence
;
3429 pBase
->SetCreate( eCreateFrom
);
3432 We don't know until here if we are an illustration
3433 or not, and so have being used a TOX_CONTENT so far
3434 which has 10 levels, while TOX has only two, this
3435 level is set only in the constructor of SwForm, so
3436 create a new one and copy over anything that could
3437 be set in the old one, and remove entries from the
3438 pattern which do not apply to illustration indices
3440 SwForm
aOldForm( pBase
->GetTOXForm() );
3441 SwForm
aNewForm( eType
);
3442 sal_uInt16 nNewEnd
= aNewForm
.GetFormMax()-1;
3445 for(sal_uInt16 nLevel
= 1; nLevel
<= nNewEnd
; ++nLevel
)
3447 SwFormTokens aPattern
= aOldForm
.GetPattern(nLevel
);
3448 SwFormTokens::iterator new_end
=
3449 remove_if(aPattern
.begin(), aPattern
.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO
));
3450 aPattern
.erase(new_end
, aPattern
.end() ); // table index imported with wrong page number format
3451 aForm
.SetPattern( nLevel
, std::move(aPattern
) );
3452 aForm
.SetTemplate( nLevel
, aOldForm
.GetTemplate(nLevel
) );
3455 pBase
->SetTOXForm( aNewForm
);
3459 OSL_ENSURE(false, "Unhandled toc options!");
3467 OSL_ENSURE(false, "Unhandled toc options!");
3471 // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX
3472 pBase
->AdjustTabStops( m_rDoc
);
3474 //#i10028# inserting a toc implicitly acts like a parabreak in word and writer
3475 if ( m_pPaM
->End() &&
3476 m_pPaM
->End()->GetNode().GetTextNode() &&
3477 m_pPaM
->End()->GetNode().GetTextNode()->Len() != 0 )
3479 m_bCareFirstParaEndInToc
= true;
3482 if (m_pPaM
->GetPoint()->GetContentIndex())
3483 FinalizeTextNode(*m_pPaM
->GetPoint());
3485 const SwPosition
* pPos
= m_pPaM
->GetPoint();
3487 SwFltTOX
aFltTOX(std::move(pBase
));
3489 // test if there is already a break item on this node
3490 if(SwContentNode
* pNd
= pPos
->GetNode().GetContentNode())
3492 const SfxItemSet
* pSet
= pNd
->GetpSwAttrSet();
3495 if (SfxItemState::SET
== pSet
->GetItemState(RES_BREAK
, false))
3496 aFltTOX
.SetHadBreakItem(true);
3497 if (SfxItemState::SET
== pSet
->GetItemState(RES_PAGEDESC
, false))
3498 aFltTOX
.SetHadPageDescItem(true);
3502 //Will there be a new pagebreak at this position (don't know what type
3504 if (m_aSectionManager
.WillHavePageDescHere(pPos
->GetNode()))
3505 aFltTOX
.SetHadPageDescItem(true);
3507 // Set start in stack
3508 m_xReffedStck
->NewAttr( *pPos
, aFltTOX
);
3510 m_rDoc
.InsertTableOf(*m_pPaM
->GetPoint(), aFltTOX
.GetBase());
3512 //The TOC field representation contents should be inserted into TOC section, but not after TOC section.
3513 //So we need update the document position when loading TOC representation and after loading TOC;
3514 m_oPosAfterTOC
.emplace(*m_pPaM
, m_pPaM
);
3515 (*m_pPaM
).Move(fnMoveBackward
);
3516 SwPaM
aRegion(*m_pPaM
, m_pPaM
);
3518 OSL_ENSURE(SwDoc::GetCurTOX(*aRegion
.GetPoint()), "Misunderstood how toc works");
3519 if (SwTOXBase
* pBase2
= SwDoc::GetCurTOX(*aRegion
.GetPoint()))
3521 pBase2
->SetMSTOCExpression(rStr
);
3523 if ( nIndexCols
> 1 )
3525 // Set the column number for index
3526 SfxItemSetFixed
<RES_COL
, RES_COL
> aSet( m_rDoc
.GetAttrPool() );
3528 aCol
.Init( nIndexCols
, 708, USHRT_MAX
);
3530 pBase2
->SetAttrSet( aSet
);
3533 // inserting a toc inserts a section before this point, so adjust pos
3534 // for future page/section segment insertion
3535 m_aSectionManager
.PrependedInlineNode( *m_oPosAfterTOC
->GetPoint(), aRegion
.GetPointNode() );
3539 m_xReffedStck
->SetAttr( *pPos
, RES_FLTR_TOX
);
3541 if (!m_aApos
.back()) //a para end in apo doesn't count
3542 m_bWasParaEnd
= true;
3544 //Return FLD_TEXT, instead of FLD_OK
3545 //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed
3546 //FLD_OK means the current field loading is finished. The rest part should be ignored.
3547 return eF_ResT::TEXT
;
3550 eF_ResT
SwWW8ImplReader::Read_F_Shape(WW8FieldDesc
* /*pF*/, OUString
& /*rStr*/)
3553 #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring
3554 to be ignored followed by a 0x1 with an empty drawing. Detect in inserting
3555 the drawing that we are in the Shape field and respond accordingly
3557 return eF_ResT::TEXT
;
3560 eF_ResT
SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc
* /*pF*/, OUString
& rStr
)
3562 OUString sURL
, sTarget
, sMark
;
3564 //HYPERLINK "filename" [switches]
3565 rStr
= comphelper::string::stripEnd(rStr
, 1);
3567 bool bOptions
= false;
3568 WW8ReadFieldParams
aReadParam( rStr
);
3571 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3577 if (sURL
.isEmpty() && !bOptions
)
3578 sURL
= ConvertFFileName(aReadParam
.GetResult());
3588 if ( aReadParam
.SkipToNextToken()==-2 )
3590 sMark
= aReadParam
.GetResult();
3591 if( sMark
.endsWith("\""))
3593 sMark
= sMark
.copy( 0, sMark
.getLength() - 1 );
3595 // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention
3596 if ( IsTOCBookmarkName( sMark
) )
3598 sMark
= EnsureTOCBookmarkName(sMark
);
3599 // track <sMark> as referenced TOC bookmark.
3600 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sMark
);
3603 if (m_bLoadingTOXCache
)
3605 m_bLoadingTOXHyperlink
= true; //on loading a TOC field nested hyperlink field
3611 if ( aReadParam
.SkipToNextToken()==-2 )
3612 sTarget
= aReadParam
.GetResult();
3616 OSL_ENSURE( false, "Analysis still missing - unknown data" );
3618 case 's': //worthless fake anchor option
3625 OSL_ENSURE(!sURL
.isEmpty() || !sMark
.isEmpty(), "WW8: Empty URL");
3627 if( !sMark
.isEmpty() )
3628 sURL
+= "#" + sMark
;
3630 SwFormatINetFormat
aURL(sURL
, sTarget
);
3631 // If on loading TOC field, change the default style into the "index link"
3632 if (m_bLoadingTOXCache
)
3634 OUString
sLinkStyle(u
"Index Link"_ustr
);
3635 sal_uInt16 nPoolId
=
3636 SwStyleNameMapper::GetPoolIdFromProgName( sLinkStyle
, SwGetPoolIdFromName::ChrFmt
);
3637 aURL
.SetVisitedFormatAndId( sLinkStyle
, nPoolId
);
3638 aURL
.SetINetFormatAndId( sLinkStyle
, nPoolId
);
3641 //As an attribute this needs to be closed, and that'll happen from
3642 //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms
3643 //between the start and begin, their hyperlinks will be set at that time
3645 m_xCtrlStck
->NewAttr( *m_pPaM
->GetPoint(), aURL
);
3646 return eF_ResT::TEXT
;
3649 static void lcl_ImportTox(SwDoc
&rDoc
, SwPaM
const &rPaM
, const OUString
&rStr
, bool bIdx
)
3651 TOXTypes eTox
= ( !bIdx
) ? TOX_CONTENT
: TOX_INDEX
; // Default
3653 sal_uInt16 nLevel
= 1;
3655 OUString sFieldText
;
3656 WW8ReadFieldParams
aReadParam(rStr
);
3659 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3665 if( sFieldText
.isEmpty() )
3667 // PrimaryKey without ":", 2nd after
3668 sFieldText
= aReadParam
.GetResult();
3673 if ( aReadParam
.GoToTokenParam() )
3675 const OUString
sParams( aReadParam
.GetResult() );
3676 if( sParams
[0]!='C' && sParams
[0]!='c' )
3682 if ( aReadParam
.GoToTokenParam() )
3684 const OUString
sParams( aReadParam
.GetResult() );
3685 // if NO String just ignore the \l
3686 if( !sParams
.isEmpty() && sParams
[0]>'0' && sParams
[0]<='9' )
3688 nLevel
= o3tl::narrowing
<sal_uInt16
>(sParams
.toInt32());
3695 OSL_ENSURE( rDoc
.GetTOXTypeCount( eTox
), "Doc.GetTOXTypeCount() == 0 :-(" );
3697 const SwTOXType
* pT
= rDoc
.GetTOXType( eTox
, 0 );
3700 if( eTox
!= TOX_INDEX
)
3701 aM
.SetLevel( nLevel
);
3704 sal_Int32 nFnd
= sFieldText
.indexOf( WW8_TOX_LEVEL_DELIM
);
3705 if( -1 != nFnd
) // it exist levels
3707 aM
.SetPrimaryKey( sFieldText
.copy( 0, nFnd
) );
3708 sal_Int32 nScndFnd
= sFieldText
.indexOf( WW8_TOX_LEVEL_DELIM
, nFnd
+1 );
3709 if( -1 != nScndFnd
)
3711 aM
.SetSecondaryKey( sFieldText
.copy( nFnd
+1, nScndFnd
- nFnd
- 1 ));
3714 sFieldText
= sFieldText
.copy( nFnd
+1 );
3718 if (!sFieldText
.isEmpty())
3720 aM
.SetAlternativeText( sFieldText
);
3721 rDoc
.getIDocumentContentOperations().InsertPoolItem( rPaM
, aM
);
3725 void SwWW8ImplReader::ImportTox( int nFieldId
, const OUString
& aStr
)
3727 bool bIdx
= (nFieldId
!= 9);
3728 lcl_ImportTox(m_rDoc
, *m_pPaM
, aStr
, bIdx
);
3731 void SwWW8ImplReader::Read_FieldVanish( sal_uInt16
, const sal_uInt8
*, short nLen
)
3733 //Meaningless in a style
3734 if (m_pCurrentColl
|| !m_xPlcxMan
)
3737 const int nChunk
= 64; //number of characters to read at one time
3739 // Careful: MEMICMP doesn't work with fieldnames including umlauts!
3740 const static char *aFieldNames
[] = { "\x06""INHALT", "\x02""XE", // dt.
3742 const static sal_uInt8 aFieldId
[] = { 9, 4, 9 };
3746 m_bIgnoreText
= false;
3750 // our method was called from
3751 // ''Skip attributes of field contents'' loop within ReadTextAttr()
3755 m_bIgnoreText
= true;
3756 sal_uInt64 nOldPos
= m_pStrm
->Tell();
3758 WW8_CP nStartCp
= m_xPlcxMan
->Where() + m_xPlcxMan
->GetCpOfs();
3760 OUString sFieldName
;
3761 sal_Int32 nFieldLen
= m_xSBase
->WW8ReadString( *m_pStrm
, sFieldName
, nStartCp
,
3762 nChunk
, m_eStructCharSet
);
3763 nStartCp
+=nFieldLen
;
3766 //If the first chunk did not start with a field start then
3767 //reset the stream position and give up
3768 if( !nFieldLen
|| sFieldName
[nC
]!=0x13 ) // Field Start Mark
3770 // If Field End Mark found
3771 if( nFieldLen
&& sFieldName
[nC
]==0x15 )
3772 m_bIgnoreText
= false;
3773 m_pStrm
->Seek( nOldPos
);
3774 return; // no field found
3778 //If this chunk does not contain a field end, keep reading chunks
3779 //until we find one, or we run out of text,
3782 nFnd
= sFieldName
.indexOf(0x15);
3783 //found field end, we can stop now
3787 nFieldLen
= m_xSBase
->WW8ReadString( *m_pStrm
, sTemp
,
3788 nStartCp
, nChunk
, m_eStructCharSet
);
3790 nStartCp
+=nFieldLen
;
3795 m_pStrm
->Seek( nOldPos
);
3797 //if we have no 0x15 give up, otherwise erase everything from the 0x15
3802 sFieldName
= sFieldName
.copy(0, nFnd
);
3805 while ( sFieldName
[nC
]==' ' )
3808 for( int i
= 0; i
< 3; i
++ )
3810 const char* pName
= aFieldNames
[i
];
3811 const sal_Int32 nNameLen
= static_cast<sal_Int32
>(*pName
++);
3812 if( sFieldName
.matchIgnoreAsciiCaseAsciiL( pName
, nNameLen
, nC
) )
3814 ImportTox( aFieldId
[i
], sFieldName
.copy( nC
+ nNameLen
) );
3815 break; // no duplicates allowed
3818 m_bIgnoreText
= true;
3819 m_pStrm
->Seek( nOldPos
);
3822 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */