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>
66 #include <section.hxx>
68 #include <fmtinfmt.hxx>
70 #include <fmtruby.hxx>
71 #include <charfmt.hxx>
72 #include <breakit.hxx>
73 #include <fmtclds.hxx>
74 #include <poolfmt.hxx>
75 #include <SwStyleNameMapper.hxx>
77 #include "ww8scan.hxx"
79 #include "writerhelper.hxx"
80 #include <o3tl/safeint.hxx>
81 #include <o3tl/string_view.hxx>
82 #include <unotools/fltrcfg.hxx>
83 #include <xmloff/odffields.hxx>
84 #include <osl/diagnose.h>
87 #include <string_view>
89 #define MAX_FIELDLEN 64000
91 #define WW8_TOX_LEVEL_DELIM ':'
93 using namespace ::com::sun::star
;
94 using namespace msfilter::util
;
95 using namespace sw::util
;
96 using namespace sw::mark
;
97 using namespace nsSwDocInfoSubType
;
102 // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention
103 bool IsTOCBookmarkName(std::u16string_view rName
)
105 return o3tl::starts_with(rName
, u
"_Toc") || o3tl::starts_with(rName
, Concat2View(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"));
108 OUString
EnsureTOCBookmarkName(const OUString
& rName
)
110 OUString sTmp
= rName
;
111 if ( IsTOCBookmarkName ( rName
) )
113 if ( ! rName
.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
114 sTmp
= IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName
;
120 tools::Long
SwWW8ImplReader::Read_Book(WW8PLCFManResult
*)
122 // should also work via pRes.nCo2OrIdx
123 WW8PLCFx_Book
* pB
= m_xPlcxMan
->GetBook();
126 OSL_ENSURE( pB
, "WW8PLCFx_Book - Pointer does not exist" );
130 eBookStatus eB
= pB
->GetStatus();
131 if (eB
& BOOK_IGNORE
)
132 return 0; // ignore bookmark
136 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_BOOKMARK
, true,
137 pB
->GetHandle(), (eB
& BOOK_FIELD
)!=0);
141 // "_Hlt*" are unnecessary
142 const OUString
* pName
= pB
->GetName();
143 // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available.
144 // So the hidden bookmarks inside for hyperlink jumping also should be kept.
146 pName
->startsWithIgnoreAsciiCase( "_Hlt" ) )
151 // do NOT call ToUpper as the bookmark name can also be a hyperlink target!
154 if( SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::BOOK_TO_VAR_REF
) )
156 // set variable for translation bookmark
157 tools::Long nLen
= pB
->GetLen();
158 if( nLen
> MAX_FIELDLEN
)
161 sal_uInt64 nOldPos
= m_pStrm
->Tell();
162 m_xSBase
->WW8ReadString( *m_pStrm
, aVal
, pB
->GetStartPos(), nLen
,
164 m_pStrm
->Seek( nOldPos
);
166 // now here the implementation of the old "QuoteString" and
167 // I hope with a better performance as before. It's also only
168 // needed if the filterflags say we will convert bookmarks
169 // to SetExpFields! And this the exception!
172 bool bAllowCr
= SwFltGetFlag(m_nFieldFlags
,
173 SwFltControlStack::ALLOW_FLD_CR
);
175 for( sal_Int32 nI
= 0;
176 nI
< aVal
.getLength() && aVal
.getLength() < (MAX_FIELDLEN
- 4);
179 const sal_Unicode cChar
= aVal
[nI
];
187 aVal
= aVal
.replaceAt( nI
, 1, u
"\n" );
200 bSetAsHex
= 0x20 > cChar
;
206 //all Hex-Numbers with \x before
207 OUString
sTmp( "\\x" );
210 sTmp
+= OUString::number( cChar
, 16 );
211 aVal
= aVal
.replaceAt( nI
, 1 , sTmp
);
212 nI
+= sTmp
.getLength() - 1;
216 if ( aVal
.getLength() > (MAX_FIELDLEN
- 4))
217 aVal
= aVal
.copy( 0, MAX_FIELDLEN
- 4 );
220 //e.g. inserting bookmark around field result, so we need to put
221 //it around the entire writer field, as we don't have the separation
222 //of field and field result of word, see #i16941#
223 SwPosition
aStart(*m_pPaM
->GetPoint());
224 if (!m_aFieldStack
.empty())
226 const WW8FieldEntry
&rTest
= m_aFieldStack
.back();
227 aStart
= rTest
.maStartPos
;
230 const OUString sOrigName
= BookmarkToWriter(*pName
);
231 m_xReffedStck
->NewAttr( aStart
,
232 SwFltBookmark( EnsureTOCBookmarkName( sOrigName
), aVal
, pB
->GetHandle(), IsTOCBookmarkName( sOrigName
) ));
236 tools::Long
SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult
*)
238 if (WW8PLCFx_AtnBook
* pAtnBook
= m_xPlcxMan
->GetAtnBook())
240 if (pAtnBook
->getIsEnd())
241 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_ANNOTATIONMARK
, true, pAtnBook
->getHandle());
243 m_xReffedStck
->NewAttr(*m_pPaM
->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK
, pAtnBook
->getHandle()));
248 tools::Long
SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult
*)
250 if (WW8PLCFx_FactoidBook
* pFactoidBook
= m_xPlcxMan
->GetFactoidBook())
252 if (pFactoidBook
->getIsEnd())
253 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_RDFMARK
, true, pFactoidBook
->getHandle());
257 aMark
.SetHandle(pFactoidBook
->getHandle());
258 GetSmartTagInfo(aMark
);
259 m_xReffedStck
->NewAttr(*m_pPaM
->GetPoint(), aMark
);
265 // general help methods to separate parameters
267 /// translate FieldParameter names into the system character set and
268 /// at the same time, double backslashes are converted into single ones
269 OUString
SwWW8ImplReader::ConvertFFileName(const OUString
& rOrg
)
271 OUString aName
= rOrg
.replaceAll("\\\\", "\\");
272 aName
= aName
.replaceAll("%20", " ");
274 // remove attached quotation marks
275 if (aName
.endsWith("\""))
276 aName
= aName
.copy(0, aName
.getLength()-1);
278 // Need the more sophisticated url converter.
279 if (!aName
.isEmpty())
280 aName
= URIHelper::SmartRel2Abs(
281 INetURLObject(m_sBaseURL
), aName
, Link
<OUString
*, bool>(), false);
288 /// translate FieldParameter names into the
289 /// system character set and makes them uppercase
290 void ConvertUFName( OUString
& rName
)
292 rName
= GetAppCharClass().uppercase( rName
);
296 static void lcl_ConvertSequenceName(OUString
& rSequenceName
)
298 ConvertUFName(rSequenceName
);
299 if ('0' <= rSequenceName
[0] && '9' >= rSequenceName
[0])
300 rSequenceName
= "_" + rSequenceName
;
303 // FindParaStart() finds 1st Parameter that follows '\' and cToken
304 // and returns start of this parameter or -1
305 static sal_Int32
FindParaStart( std::u16string_view aStr
, sal_Unicode cToken
, sal_Unicode cToken2
)
307 bool bStr
= false; // ignore inside a string
309 for( size_t nBuf
= 0; nBuf
+1 < aStr
.size(); nBuf
++ )
311 if( aStr
[ nBuf
] == '"' )
315 && aStr
[ nBuf
] == '\\'
316 && ( aStr
[ nBuf
+ 1 ] == cToken
317 || aStr
[ nBuf
+ 1 ] == cToken2
) )
320 // skip spaces between cToken and its parameters
321 while( nBuf
< aStr
.size()
322 && aStr
[ nBuf
] == ' ' )
324 // return start of parameters
325 return nBuf
< aStr
.size() ? nBuf
: -1;
331 // FindPara() finds the first parameter including '\' and cToken.
332 // A new String will be allocated (has to be deallocated by the caller)
333 // and everything that is part of the parameter will be returned.
334 static OUString
FindPara( std::u16string_view aStr
, sal_Unicode cToken
, sal_Unicode cToken2
)
337 sal_Int32 n
= FindParaStart( aStr
, cToken
, cToken2
); // start
342 || aStr
[ n
] == 132 )
343 { // Quotationmark in front of parameter
344 n
++; // Skip quotationmark
345 n2
= n
; // search for the end starting from here
346 while( n2
< sal_Int32(aStr
.size())
348 && aStr
[ n2
] != '"' )
349 n2
++; // search end of parameter
352 { // no quotationmarks
353 n2
= n
; // search for the end starting from here
354 while( n2
< sal_Int32(aStr
.size())
355 && aStr
[ n2
] != ' ' )
356 n2
++; // search end of parameter
358 return OUString(aStr
.substr( n
, n2
-n
));
361 static SvxNumType
GetNumTypeFromName(const OUString
& rStr
,
362 bool bAllowPageDesc
= false)
364 SvxNumType eTyp
= bAllowPageDesc
? SVX_NUM_PAGEDESC
: SVX_NUM_ARABIC
;
368 if( rStr
.startsWithIgnoreAsciiCase( "Arabi" ) ) // Arabisch, Arabic
369 eTyp
= SVX_NUM_ARABIC
;
370 else if( rStr
.startsWith( "misch" ) ) // r"omisch
371 eTyp
= SVX_NUM_ROMAN_LOWER
;
372 else if( rStr
.startsWith( "MISCH" ) ) // R"OMISCH
373 eTyp
= SVX_NUM_ROMAN_UPPER
;
374 else if( rStr
.startsWithIgnoreAsciiCase( "alphabeti" ) )// alphabetisch, alphabetic
375 eTyp
= ( rStr
[0] == 'A' )
376 ? SVX_NUM_CHARS_UPPER_LETTER_N
377 : SVX_NUM_CHARS_LOWER_LETTER_N
;
378 else if( rStr
.startsWithIgnoreAsciiCase( "roman" ) ) // us
379 eTyp
= ( rStr
[0] == 'R' )
380 ? SVX_NUM_ROMAN_UPPER
381 : SVX_NUM_ROMAN_LOWER
;
385 static SvxNumType
GetNumberPara(std::u16string_view aStr
, bool bAllowPageDesc
= false)
387 OUString
s( FindPara( aStr
, '*', '*' ) ); // Type of number
388 SvxNumType aType
= GetNumTypeFromName( s
, bAllowPageDesc
);
392 bool SwWW8ImplReader::ForceFieldLanguage(SwField
&rField
, LanguageType nLang
)
396 const SvxLanguageItem
*pLang
=
397 static_cast<const SvxLanguageItem
*>(GetFormatAttr(RES_CHRATR_LANGUAGE
));
398 OSL_ENSURE(pLang
, "impossible");
399 LanguageType nDefault
= pLang
? pLang
->GetValue() : LANGUAGE_ENGLISH_US
;
401 if (nLang
!= nDefault
)
403 rField
.SetAutomaticLanguage(false);
404 rField
.SetLanguage(nLang
);
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 sal_uInt16 eLang
= bRTL
? RES_CHRATR_CTL_LANGUAGE
: RES_CHRATR_LANGUAGE
;
444 const SvxLanguageItem
*pLang
= static_cast<const SvxLanguageItem
*>(GetFormatAttr(eLang
));
445 OSL_ENSURE(pLang
, "impossible");
446 rLang
= pLang
? pLang
->GetValue() : LANGUAGE_ENGLISH_US
;
448 SvNumberFormatter
* pFormatter
= m_rDoc
.GetNumberFormatter();
449 OUString
sParams( FindPara( aStr
, '@', '@' ) );// Date/Time
450 if (sParams
.isEmpty())
452 bool bHasTime
= false;
453 switch (nWhichDefault
)
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 OUString
sTemp(sParams
);
479 pFormatter
->PutandConvertEntry(sTemp
, nCheckPos
, nType
, rFormat
,
480 LANGUAGE_ENGLISH_US
, rLang
, false);
483 return bHasTime
? SvNumFormatType::DATETIME
: SvNumFormatType::DATE
;
486 sal_uLong nFormatIdx
=
487 sw::ms::MSDateTimeFormatToSwFormat(sParams
, pFormatter
, rLang
, bHijri
,
489 SvNumFormatType nNumFormatType
= SvNumFormatType::UNDEFINED
;
491 nNumFormatType
= pFormatter
->GetType(nFormatIdx
);
492 rFormat
= nFormatIdx
;
494 return nNumFormatType
;
499 // Update respective fields after loading (currently references)
500 void SwWW8ImplReader::UpdateFields()
502 m_rDoc
.getIDocumentState().SetUpdateExpFieldStat(true);
503 m_rDoc
.SetInitDBFields(true); // Also update fields in the database
506 // Sanity check the PaM to see if it makes sense wrt sw::CalcBreaks
507 static bool SanityCheck(const SwPaM
& rFieldPam
)
509 SwNodeOffset
const nEndNode(rFieldPam
.End()->GetNodeIndex());
510 SwNodes
const& rNodes(rFieldPam
.GetPoint()->GetNodes());
511 SwNode
*const pFinalNode(rNodes
[nEndNode
]);
512 if (pFinalNode
->IsTextNode())
514 SwTextNode
& rTextNode(*pFinalNode
->GetTextNode());
515 return (rTextNode
.Len() >= rFieldPam
.End()->GetContentIndex());
520 sal_uInt16
SwWW8ImplReader::End_Field()
523 WW8PLCFx_FLD
* pF
= m_xPlcxMan
->GetField();
524 OSL_ENSURE(pF
, "WW8PLCFx_FLD - Pointer not available");
526 if (!pF
|| !pF
->EndPosIsFieldEnd(nCP
))
529 const SvtFilterOptions
&rOpt
= SvtFilterOptions::Get();
530 bool bUseEnhFields
= rOpt
.IsUseEnhancedFields();
532 OSL_ENSURE(!m_aFieldStack
.empty(), "Empty field stack");
533 if (!m_aFieldStack
.empty())
536 only hyperlinks currently need to be handled like this, for the other
537 cases we have inserted a field not an attribute with an unknown end
540 nRet
= m_aFieldStack
.back().mnFieldId
;
544 if (bUseEnhFields
&& m_pPaM
!=nullptr && m_pPaM
->GetPoint()!=nullptr) {
545 SwPosition aEndPos
= *m_pPaM
->GetPoint();
546 SwPaM
aFieldPam( m_aFieldStack
.back().GetPtNode().GetNode(), m_aFieldStack
.back().GetPtContent(), aEndPos
.GetNode(), aEndPos
.GetContentIndex());
548 IDocumentMarkAccess
* pMarksAccess
= m_rDoc
.getIDocumentMarkAccess( );
549 IFieldmark
*pFieldmark
= SanityCheck(aFieldPam
) ? pMarksAccess
->makeFieldBookmark(
550 aFieldPam
, m_aFieldStack
.back().GetBookmarkName(), ODF_FORMTEXT
,
551 aFieldPam
.Start() /*same pos as start!*/ ) : nullptr;
552 OSL_ENSURE(pFieldmark
!=nullptr, "hmmm; why was the bookmark not created?");
553 if (pFieldmark
!=nullptr) {
554 // adapt redline positions to inserted field mark start
555 // dummy char (assume not necessary for end dummy char)
556 m_xRedlineStack
->MoveAttrsFieldmarkInserted(*aFieldPam
.Start());
557 const IFieldmark::parameter_map_t
& rParametersToAdd
= m_aFieldStack
.back().getParameters();
558 pFieldmark
->GetParameters()->insert(rParametersToAdd
.begin(), rParametersToAdd
.end());
562 // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field
564 case ww::eINDEX
://index
565 if (m_bLoadingTOXCache
)
567 if (m_nEmbeddedTOXLevel
> 0)
570 --m_nEmbeddedTOXLevel
;
574 m_aTOXEndCps
.insert(nCP
);
575 m_bLoadingTOXCache
= false;
576 if ( m_pPaM
->End() &&
577 m_pPaM
->End()->GetNode().GetTextNode() &&
578 m_pPaM
->End()->GetNode().GetTextNode()->Len() == 0 )
584 m_bCareLastParaEndInToc
= true;
589 *m_pPaM
= *m_oPosAfterTOC
;
590 m_oPosAfterTOC
.reset();
595 case ww::ePAGEREF
: //REF
596 if (m_bLoadingTOXCache
&& !m_bLoadingTOXHyperlink
)
598 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(),RES_TXTATR_INETFMT
);
602 if (m_bLoadingTOXHyperlink
)
603 m_bLoadingTOXHyperlink
= false;
604 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(), RES_TXTATR_INETFMT
);
607 case ww::eINCLUDETEXT
:
609 //Move outside the section associated with this type of field
610 SwPosition
aRestorePos(m_aFieldStack
.back().maStartPos
);
612 SwContentNode
* pNd
= aRestorePos
.GetNode().GetContentNode();
613 sal_Int32 nMaxValidIndex
= pNd
? pNd
->Len() : 0;
614 if (aRestorePos
.GetContentIndex() > nMaxValidIndex
)
616 SAL_WARN("sw.ww8", "Attempt to restore to invalid content position");
617 aRestorePos
.SetContent(nMaxValidIndex
);
620 *m_pPaM
->GetPoint() = aRestorePos
;
623 case ww::eIF
: // IF-field
625 // conditional field parameters
626 OUString fieldDefinition
= m_aFieldStack
.back().GetBookmarkCode();
628 OUString paramCondition
;
632 // ParseIfFieldDefinition expects: IF <some condition> "true result" "false result"
633 // while many fields include '\* MERGEFORMAT' after that.
634 // So first trim off the switches that are not supported anyway
635 sal_Int32 nLastIndex
= fieldDefinition
.lastIndexOf("\\*");
636 sal_Int32 nOtherIndex
= fieldDefinition
.lastIndexOf("\\#"); //number format
637 if (nOtherIndex
> 0 && (nOtherIndex
< nLastIndex
|| nLastIndex
< 0))
638 nLastIndex
= nOtherIndex
;
639 nOtherIndex
= fieldDefinition
.lastIndexOf("\\@"); //date format
640 if (nOtherIndex
> 0 && (nOtherIndex
< nLastIndex
|| nLastIndex
< 0))
641 nLastIndex
= nOtherIndex
;
642 nOtherIndex
= fieldDefinition
.lastIndexOf("\\!"); //locked result
643 if (nOtherIndex
> 0 && (nOtherIndex
< nLastIndex
|| nLastIndex
< 0))
644 nLastIndex
= nOtherIndex
;
646 fieldDefinition
= fieldDefinition
.copy(0, nLastIndex
);
648 SwHiddenTextField::ParseIfFieldDefinition(fieldDefinition
, paramCondition
, paramTrue
, paramFalse
);
651 SwFieldType
* pFieldType
= m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText
);
652 SwHiddenTextField
aHTField(
653 static_cast<SwHiddenTextFieldType
*>(pFieldType
),
657 SwFieldTypesEnum::ConditionalText
);
659 // insert new field into document
660 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aHTField
));
664 OUString aCode
= m_aFieldStack
.back().GetBookmarkCode();
665 if (!aCode
.isEmpty() && !o3tl::starts_with(o3tl::trim(aCode
), u
"SHAPE"))
667 // Unhandled field with stored code
668 SwPosition aEndPos
= *m_pPaM
->GetPoint();
670 m_aFieldStack
.back().GetPtNode().GetNode(), m_aFieldStack
.back().GetPtContent(),
671 aEndPos
.GetNode(), aEndPos
.GetContentIndex());
673 IDocumentMarkAccess
* pMarksAccess
= m_rDoc
.getIDocumentMarkAccess( );
675 IFieldmark
* pFieldmark
= pMarksAccess
->makeFieldBookmark(
677 m_aFieldStack
.back().GetBookmarkName(),
679 aFieldPam
.Start() /*same pos as start!*/ );
682 // adapt redline positions to inserted field mark start
683 // dummy char (assume not necessary for end dummy char)
684 m_xRedlineStack
->MoveAttrsFieldmarkInserted(*aFieldPam
.Start());
685 const IFieldmark::parameter_map_t
& rParametersToAdd
= m_aFieldStack
.back().getParameters();
686 pFieldmark
->GetParameters()->insert(rParametersToAdd
.begin(), rParametersToAdd
.end());
687 OUString sFieldId
= OUString::number( m_aFieldStack
.back().mnFieldId
);
688 pFieldmark
->GetParameters()->insert(
689 std::pair
< OUString
, uno::Any
> (
691 uno::Any( sFieldId
) ) );
692 pFieldmark
->GetParameters()->insert(
693 std::pair
< OUString
, uno::Any
> (
695 uno::Any( aCode
) ) );
697 if ( m_aFieldStack
.back().mnObjLocFc
> 0 )
699 // Store the OLE object as an internal link
700 OUString sOleId
= "_" +
701 OUString::number( m_aFieldStack
.back().mnObjLocFc
);
703 tools::SvRef
<SotStorage
> xSrc0
= m_pStg
->OpenSotStorage(SL::aObjectPool
);
704 tools::SvRef
<SotStorage
> xSrc1
= xSrc0
->OpenSotStorage( sOleId
, StreamMode::READ
);
707 uno::Reference
< embed::XStorage
> xDocStg
= GetDoc().GetDocStorage();
710 uno::Reference
< embed::XStorage
> xOleStg
= xDocStg
->openStorageElement(
711 "OLELinks", embed::ElementModes::WRITE
);
712 tools::SvRef
<SotStorage
> xObjDst
= SotStorage::OpenOLEStorage( xOleStg
, sOleId
);
716 xSrc1
->CopyTo( xObjDst
.get() );
718 if ( !xObjDst
->GetError() )
722 uno::Reference
< embed::XTransactedObject
> xTransact( xOleStg
, uno::UNO_QUERY
);
723 if ( xTransact
.is() )
727 // Store the OLE Id as a parameter
728 pFieldmark
->GetParameters()->insert(
729 std::pair
< OUString
, uno::Any
>(
730 ODF_OLE_PARAM
, uno::Any( sOleId
) ) );
737 m_aFieldStack
.pop_back();
742 static bool AcceptableNestedField(sal_uInt16 nFieldCode
)
746 case ww::eINDEX
: // allow recursive field in TOC...
747 case ww::eTOC
: // allow recursive field in TOC...
749 case ww::eINCLUDETEXT
:
752 // Accept AutoTextList field as nested field.
753 // Thus, the field result is imported as plain text.
754 case ww::eAUTOTEXTLIST
:
755 // tdf#129247 CONTROL contains a nested SHAPE field in the result
763 WW8FieldEntry::WW8FieldEntry(SwPosition
const &rPos
, sal_uInt16 nFieldId
) noexcept
764 : maStartPos(rPos
), mnFieldId(nFieldId
), mnObjLocFc(0)
768 WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry
&rOther
) noexcept
769 : maStartPos(rOther
.maStartPos
), mnFieldId(rOther
.mnFieldId
), mnObjLocFc(rOther
.mnObjLocFc
)
773 void WW8FieldEntry::Swap(WW8FieldEntry
&rOther
) noexcept
775 std::swap(maStartPos
, rOther
.maStartPos
);
776 std::swap(mnFieldId
, rOther
.mnFieldId
);
779 WW8FieldEntry
&WW8FieldEntry::operator=(const WW8FieldEntry
&rOther
) noexcept
781 WW8FieldEntry
aTemp(rOther
);
787 void WW8FieldEntry::SetBookmarkName(const OUString
& bookmarkName
)
789 msBookmarkName
=bookmarkName
;
792 void WW8FieldEntry::SetBookmarkType(const OUString
& bookmarkType
)
794 msMarkType
=bookmarkType
;
797 void WW8FieldEntry::SetBookmarkCode(const OUString
& bookmarkCode
)
799 msMarkCode
= bookmarkCode
;
803 // Read_Field reads a field or returns 0 if the field cannot be read,
804 // so that the calling function reads the field in text format.
805 // Returnvalue: Total length of field
806 tools::Long
SwWW8ImplReader::Read_Field(WW8PLCFManResult
* pRes
)
808 typedef eF_ResT (SwWW8ImplReader::*FNReadField
)( WW8FieldDesc
*, OUString
& );
809 enum Limits
{eMax
= 96};
810 static const FNReadField aWW8FieldTab
[eMax
+1] =
813 &SwWW8ImplReader::Read_F_Input
,
815 &SwWW8ImplReader::Read_F_Ref
, // 3
818 &SwWW8ImplReader::Read_F_Set
, // 6
820 &SwWW8ImplReader::Read_F_Tox
, // 8
822 &SwWW8ImplReader::Read_F_Styleref
, // 10
824 &SwWW8ImplReader::Read_F_Seq
, // 12
825 &SwWW8ImplReader::Read_F_Tox
, // 13
826 &SwWW8ImplReader::Read_F_DocInfo
, // 14
827 &SwWW8ImplReader::Read_F_DocInfo
, // 15
828 &SwWW8ImplReader::Read_F_DocInfo
, // 16
829 &SwWW8ImplReader::Read_F_Author
, // 17
830 &SwWW8ImplReader::Read_F_DocInfo
, // 18
831 &SwWW8ImplReader::Read_F_DocInfo
, // 19
832 &SwWW8ImplReader::Read_F_DocInfo
, // 20
833 &SwWW8ImplReader::Read_F_DocInfo
, // 21
834 &SwWW8ImplReader::Read_F_DocInfo
, // 22
835 &SwWW8ImplReader::Read_F_DocInfo
, // 23
836 &SwWW8ImplReader::Read_F_DocInfo
, // 24
837 &SwWW8ImplReader::Read_F_DocInfo
, // 25
838 &SwWW8ImplReader::Read_F_Num
, // 26
839 &SwWW8ImplReader::Read_F_Num
, // 27
840 &SwWW8ImplReader::Read_F_Num
, // 28
841 &SwWW8ImplReader::Read_F_FileName
, // 29
842 &SwWW8ImplReader::Read_F_TemplName
, // 30
843 &SwWW8ImplReader::Read_F_DateTime
, // 31
844 &SwWW8ImplReader::Read_F_DateTime
, // 32
845 &SwWW8ImplReader::Read_F_CurPage
, // 33
848 &SwWW8ImplReader::Read_F_IncludeText
, // 36
849 &SwWW8ImplReader::Read_F_PgRef
, // 37
850 &SwWW8ImplReader::Read_F_InputVar
, // 38
851 &SwWW8ImplReader::Read_F_Input
, // 39
853 &SwWW8ImplReader::Read_F_DBNext
, // 41
856 &SwWW8ImplReader::Read_F_DBNum
, // 44
861 &SwWW8ImplReader::Read_F_Equation
, // 49
863 &SwWW8ImplReader::Read_F_Macro
, // 51
864 &SwWW8ImplReader::Read_F_ANumber
, // 52
865 &SwWW8ImplReader::Read_F_ANumber
, // 53
866 &SwWW8ImplReader::Read_F_ANumber
, // 54
871 &SwWW8ImplReader::Read_F_Symbol
, // 57
872 &SwWW8ImplReader::Read_F_Embedd
, // 58
873 &SwWW8ImplReader::Read_F_DBField
, // 59
878 &SwWW8ImplReader::Read_F_DocInfo
, // 64 - DOCVARIABLE
881 &SwWW8ImplReader::Read_F_IncludePicture
, // 67
882 &SwWW8ImplReader::Read_F_IncludeText
, // 68
884 &SwWW8ImplReader::Read_F_FormTextBox
, // 70
885 &SwWW8ImplReader::Read_F_FormCheckBox
, // 71
886 &SwWW8ImplReader::Read_F_NoteReference
, // 72
887 nullptr, /*&SwWW8ImplReader::Read_F_Tox*/
897 &SwWW8ImplReader::Read_F_FormListBox
, // 83
899 &SwWW8ImplReader::Read_F_DocInfo
, // 85
901 &SwWW8ImplReader::Read_F_OCX
, // 87
902 &SwWW8ImplReader::Read_F_Hyperlink
, // 88
905 &SwWW8ImplReader::Read_F_HTMLControl
, // 91
909 &SwWW8ImplReader::Read_F_Shape
, // 95
910 nullptr // eMax - Dummy empty method
912 OSL_ENSURE( SAL_N_ELEMENTS( aWW8FieldTab
) == eMax
+1, "FieldFunc table not right" );
914 WW8PLCFx_FLD
* pF
= m_xPlcxMan
->GetField();
915 OSL_ENSURE(pF
, "WW8PLCFx_FLD - Pointer not available");
917 if (!pF
|| !pF
->StartPosIsFieldStart())
920 bool bNested
= false;
921 if (!m_aFieldStack
.empty())
923 bNested
= std::any_of(m_aFieldStack
.cbegin(), m_aFieldStack
.cend(),
924 [](const WW8FieldEntry
& aField
) { return !AcceptableNestedField(aField
.mnFieldId
); });
928 bool bOk
= pF
->GetPara(pRes
->nCp2OrIdx
, aF
);
930 OSL_ENSURE(bOk
, "WW8: Bad Field!");
931 if (aF
.nId
== 33) aF
.bCodeNest
=false; // do not recurse into nested page fields
932 bool bCodeNest
= aF
.bCodeNest
;
933 if ( aF
.nId
== 6 ) bCodeNest
= false; // We can handle them and lose the inner data
934 if (aF
.nId
== 70) bCodeNest
= false; // need to import 0x01 in FORMTEXT
936 m_aFieldStack
.emplace_back(*m_pPaM
->GetPoint(), aF
.nId
);
941 sal_uInt16 n
= (aF
.nId
<= eMax
) ? aF
.nId
: o3tl::narrowing
<sal_uInt16
>(eMax
);
942 sal_uInt16 nI
= n
/ 32; // # of sal_uInt32
943 sal_uInt32 nMask
= 1 << ( n
% 32 ); // Mask for bits
945 if (SAL_N_ELEMENTS(m_nFieldTagAlways
) <= nI
)
946 { // if indexes larger than 95 are needed, then a new configuration
947 // item has to be added, and nFieldTagAlways/nFieldTagBad expanded!
951 if( m_nFieldTagAlways
[nI
] & nMask
) // Flag: Tag it
952 return Read_F_Tag( &aF
); // Result not as text
954 if( !bOk
|| !aF
.nId
) // Field corrupted
955 return aF
.nLen
; // -> ignore
957 if( aF
.nId
> eMax
- 1) // WW: Nested Field
959 if( m_nFieldTagBad
[nI
] & nMask
) // Flag: Tag it when bad
960 return Read_F_Tag( &aF
); // Result not as text
965 //Only one type of field (hyperlink) in drawing textboxes exists
966 if (aF
.nId
!= 88 && m_xPlcxMan
->GetDoingDrawTextBox())
969 bool bHasHandler
= aWW8FieldTab
[aF
.nId
] != nullptr;
970 if (aF
.nId
== 10) // STYLEREF
972 // STYLEREF, by default these are not handled.
974 sal_uInt64 nOldPos
= m_pStrm
->Tell();
976 aF
.nLCode
= m_xSBase
->WW8ReadString(*m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs() + aF
.nSCode
, aF
.nLCode
, m_eTextCharSet
);
977 m_pStrm
->Seek(nOldPos
);
979 WW8ReadFieldParams
aReadParam(aStr
);
980 sal_Int32 nRet
= aReadParam
.SkipToNextToken();
981 if (nRet
== -2 && !aReadParam
.GetResult().isEmpty())
982 // Single numeric argument: this can be handled by SwChapterField.
983 bHasHandler
= rtl::isAsciiDigit(aReadParam
.GetResult()[0]);
987 nRet
= aReadParam
.SkipToNextToken();
988 // Handle using SwChapterField only in case there is no \[a-z]
989 // switch after the field argument.
990 bHasHandler
= nRet
< 0 || nRet
== '*';
994 // no routine available
995 if (!bHasHandler
|| bCodeNest
)
997 if( m_nFieldTagBad
[nI
] & nMask
) // Flag: Tag it when bad
998 return Read_F_Tag( &aF
); // Result not as text
1000 if (aF
.bResNest
&& !AcceptableNestedField(aF
.nId
))
1001 return aF
.nLen
; // Result nested -> unusable
1003 sal_uInt64 nOldPos
= m_pStrm
->Tell();
1005 aF
.nLCode
= m_xSBase
->WW8ReadString( *m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs()+
1006 aF
.nSCode
, aF
.nLCode
, m_eTextCharSet
);
1007 m_pStrm
->Seek( nOldPos
);
1009 // field codes which contain '/' or '.' are not displayed in WinWord
1010 // skip if it is formula field or found space before. see #i119446, #i119585.
1011 const sal_Int32 nDotPos
= aStr
.indexOf('.');
1012 const sal_Int32 nSlashPos
= aStr
.indexOf('/');
1013 sal_Int32 nSpacePos
= aStr
.indexOf( ' ', 1 );
1015 nSpacePos
= aStr
.getLength();
1017 if ( ( aStr
.getLength() <= 1 || aStr
[1] != '=') &&
1018 (( nDotPos
>=0 && nDotPos
< nSpacePos
) ||
1019 ( nSlashPos
>=0 && nSlashPos
< nSpacePos
)))
1023 // Link fields aren't supported, but they are bound to an OLE object
1024 // that needs to be roundtripped
1026 m_bEmbeddObj
= true;
1027 // Field not supported: store the field code for later use
1028 m_aFieldStack
.back().SetBookmarkCode( aStr
);
1030 if (aF
.nId
== ww::eIF
)
1032 // In MS Word, the IF field is editable and requires a manual refresh
1033 // so the last, saved result might not match either of the true or false options.
1034 // But in LO the field is automatically updated and not editable,
1035 // so the previous result is of no value to import since it could never be seen.
1039 return aF
.nLen
- aF
.nLRes
- 1; // skipped too many, the resulted field will be read like main text
1044 auto nOldPos
= m_pStrm
->Tell();
1046 if ( aF
.nId
== 6 && aF
.bCodeNest
)
1048 // TODO Extract the whole code string using the nested codes
1049 aF
.nLCode
= m_xSBase
->WW8ReadString( *m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs() +
1050 aF
.nSCode
, aF
.nSRes
- aF
.nSCode
- 1, m_eTextCharSet
);
1054 aF
.nLCode
= m_xSBase
->WW8ReadString( *m_pStrm
, aStr
, m_xPlcxMan
->GetCpOfs()+
1055 aF
.nSCode
, aF
.nLCode
, m_eTextCharSet
);
1058 // #i51312# - graphics inside field code not supported by Writer.
1059 // Thus, delete character 0x01, which stands for such a graphic.
1060 if (aF
.nId
==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01.
1062 aStr
= aStr
.replaceAll("\x01", "");
1065 eF_ResT eRes
= (this->*aWW8FieldTab
[aF
.nId
])( &aF
, aStr
);
1066 m_pStrm
->Seek(nOldPos
);
1073 // skipped too many, the resulted field will be read like main text
1074 // attributes can start at char 0x14 so skip one
1075 // char more back == "-2"
1077 return aF
.nLen
- aF
.nLRes
- 2;
1080 case eF_ResT::TAGIGN
:
1081 if ( m_nFieldTagBad
[nI
] & nMask
) // Flag: Tag bad
1082 return Read_F_Tag( &aF
); // Tag it
1083 return aF
.nLen
; // or ignore
1084 case eF_ResT::READ_FSPA
:
1085 return aF
.nLen
- aF
.nLRes
- 2; // position on char 1
1087 return aF
.nLen
; // ignore
1094 // MakeTagString() returns the position of the first CR / end of line / page break
1095 // in pText and converts only up to this point.
1096 // If none of these special characters is found, the function returns 0.
1097 void SwWW8ImplReader::MakeTagString( OUString
& rStr
, const OUString
& rOrg
)
1099 bool bAllowCr
= SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::TAGS_IN_TEXT
)
1100 || SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::ALLOW_FLD_CR
);
1104 for( sal_Int32 nI
= 0;
1105 nI
< rStr
.getLength() && rStr
.getLength() < (MAX_FIELDLEN
- 4); ++nI
)
1107 bool bSetAsHex
= false;
1111 case 132: // Exchange typographical quotation marks for normal ones
1114 rStr
= rStr
.replaceAt( nI
, 1, u
"\"" );
1117 rStr
= rStr
.replaceAt( nI
, 1, u
"{" );
1118 break; // 19..21 to {|}
1120 rStr
= rStr
.replaceAt( nI
, 1, u
"|" );
1123 rStr
= rStr
.replaceAt( nI
, 1, u
"}" );
1125 case '\\': // Tag \{|} with \ ...
1129 rStr
= rStr
.replaceAt( nI
, 0, u
"\\" );
1136 rStr
= rStr
.replaceAt( nI
, 1, u
"\n" );
1145 bSetAsHex
= 0x20 > cChar
;
1151 //all Hex-Numbers with \x before
1152 OUString
sTmp( "\\x" );
1155 sTmp
+= OUString::number( cChar
, 16 );
1156 rStr
= rStr
.replaceAt( nI
, 1 , sTmp
);
1157 nI
+= sTmp
.getLength() - 1;
1161 if( rStr
.getLength() > (MAX_FIELDLEN
- 4))
1162 rStr
= rStr
.copy( 0, MAX_FIELDLEN
- 4 );
1165 void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId
, const OUString
& rTagText
)
1167 OUString
aName("WwFieldTag");
1168 if( SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::TAGS_DO_ID
) ) // Number?
1169 aName
+= OUString::number( nId
); // return it?
1171 if( SwFltGetFlag(m_nFieldFlags
, SwFltControlStack::TAGS_IN_TEXT
))
1173 aName
+= rTagText
; // tag as text
1174 m_rDoc
.getIDocumentContentOperations().InsertString(*m_pPaM
, aName
,
1175 SwInsertFlags::NOHINTEXPAND
);
1180 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType(
1181 SwSetExpFieldType( &m_rDoc
, aName
, nsSwGetSetExpType::GSE_STRING
) );
1182 SwSetExpField
aField( static_cast<SwSetExpFieldType
*>(pFT
), rTagText
); // SUB_INVISIBLE
1183 sal_uInt16 nSubType
= ( SwFltGetFlag( m_nFieldFlags
, SwFltControlStack::TAGS_VISIBLE
) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE
;
1184 aField
.SetSubType(nSubType
| nsSwGetSetExpType::GSE_STRING
);
1186 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1190 WW8_CP
SwWW8ImplReader::Read_F_Tag( WW8FieldDesc
* pF
)
1192 sal_uInt64 nOldPos
= m_pStrm
->Tell();
1194 WW8_CP nStart
= pF
->nSCode
- 1; // starting with 0x19
1195 WW8_CP nL
= pF
->nLen
; // Total length with result and nest
1196 if( nL
> MAX_FIELDLEN
)
1197 nL
= MAX_FIELDLEN
; // MaxLength, by quoting
1198 // max. 4 times as big
1200 m_xSBase
->WW8ReadString( *m_pStrm
, sFText
,
1201 m_xPlcxMan
->GetCpOfs() + nStart
, nL
, m_eStructCharSet
);
1204 MakeTagString( aTagText
, sFText
);
1205 InsertTagField( pF
->nId
, aTagText
);
1207 m_pStrm
->Seek( nOldPos
);
1213 eF_ResT
SwWW8ImplReader::Read_F_Input( WW8FieldDesc
* pF
, OUString
& rStr
)
1217 WW8ReadFieldParams
aReadParam( rStr
);
1220 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1227 aQ
= aReadParam
.GetResult();
1231 if ( aReadParam
.GoToTokenParam() )
1232 aDef
= aReadParam
.GetResult();
1236 if( aDef
.isEmpty() )
1237 aDef
= GetFieldResult( pF
);
1239 if ( pF
->nId
!= 0x01 ) // 0x01 fields have no result
1241 SwInputField
aField( static_cast<SwInputFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input
)),
1242 aDef
, aQ
, INP_TXT
, 0, false );
1243 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1249 // GetFieldResult allocates a string and reads the resulted field
1250 OUString
SwWW8ImplReader::GetFieldResult( WW8FieldDesc
const * pF
)
1252 sal_uInt64 nOldPos
= m_pStrm
->Tell();
1254 WW8_CP nStart
= pF
->nSRes
; // result start
1255 WW8_CP nL
= pF
->nLRes
; // result length
1257 return OUString(); // no result
1259 if( nL
> MAX_FIELDLEN
)
1260 nL
= MAX_FIELDLEN
; // MaxLength, by quoting
1261 // max. 4 times as big
1264 m_xSBase
->WW8ReadString( *m_pStrm
, sRes
, m_xPlcxMan
->GetCpOfs() + nStart
,
1265 nL
, m_eStructCharSet
);
1267 m_pStrm
->Seek( nOldPos
);
1269 //replace both CR 0x0D and VT 0x0B with LF 0x0A
1270 // at least in the cases where the result is added to an SwInputField
1271 // there must not be control characters in it
1272 OUStringBuffer
buf(sRes
.getLength());
1273 for (sal_Int32 i
= 0; i
< sRes
.getLength(); ++i
)
1275 sal_Unicode
const ch(sRes
[i
]);
1276 if (!linguistic::IsControlChar(ch
))
1293 SAL_INFO("sw.ww8", "GetFieldResult(): filtering control character");
1298 return buf
.makeStringAndClear();
1302 Bookmarks can be set with fields SET and ASK, and they can be referenced with
1303 REF. When set, they behave like variables in writer, otherwise they behave
1304 like normal bookmarks. We can check whether we should use a show variable
1305 instead of a normal bookmark ref by converting to "show variable" at the end
1306 of the document those refs which look for the content of a bookmark but whose
1307 bookmarks were set with SET or ASK. (See SwWW8FltRefStack)
1309 The other piece of the puzzle is that refs that point to the "location" of the
1310 bookmark will in word actually point to the last location where the bookmark
1311 was set with SET or ASK, not the actual bookmark. This is only noticeable when
1312 a document sets the bookmark more than once. This is because word places the
1313 true bookmark at the location of the last set, but the refs will display the
1314 position of the first set before the ref.
1316 So what we will do is
1318 1) keep a list of all bookmarks that were set, any bookmark names mentioned
1319 here that are referred by content will be converted to show variables.
1321 2) create pseudo bookmarks for every position that a bookmark is set with SET
1322 or ASK but has no existing bookmark. We can then keep a map from the original
1323 bookmark name to the new one. As we parse the document new pseudo names will
1324 replace the older ones, so the map always contains the bookmark of the
1325 location that msword itself would use.
1327 3) word's bookmarks are case insensitive, writers are not. So we need to
1328 map case different versions together, regardless of whether they are
1331 4) when a reference is (first) SET or ASK, the bookmark associated with it
1332 is placed around the 0x14 0x15 result part of the field. We will fiddle
1333 the placement to be the writer equivalent of directly before and after
1334 the field, which gives the same effect and meaning, to do so we must
1335 get any bookmarks in the field range, and begin them immediately before
1336 the set/ask field, and end them directly afterwards. MapBookmarkVariables
1337 returns an identifier of the bookmark attribute to close after inserting
1338 the appropriate set/ask field.
1340 tools::Long
SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc
* pF
,
1341 OUString
&rOrigName
, const OUString
&rData
)
1343 OSL_ENSURE(m_xPlcxMan
, "No pPlcxMan");
1346 If there was no bookmark associated with this set field, then we create a
1347 pseudo one and insert it in the document.
1350 m_xPlcxMan
->GetBook()->MapName(rOrigName
);
1351 OUString sName
= m_xPlcxMan
->GetBook()->GetBookmark(
1352 pF
->nSCode
, pF
->nSCode
+ pF
->nLen
, nIndex
);
1353 if (!sName
.isEmpty())
1355 m_xPlcxMan
->GetBook()->SetStatus(nIndex
, BOOK_IGNORE
);
1360 nNo
= m_xReffingStck
->m_aFieldVarNames
.size()+1;
1361 sName
= "WWSetBkmk" + OUString::number(nNo
);
1362 nNo
+= m_xPlcxMan
->GetBook()->GetIMax();
1364 m_xReffedStck
->NewAttr(*m_pPaM
->GetPoint(),
1365 SwFltBookmark( BookmarkToWriter(sName
), rData
, nNo
));
1366 m_xReffingStck
->m_aFieldVarNames
[rOrigName
] = sName
;
1371 Word can set a bookmark with set or with ask, such a bookmark is equivalent to
1372 our variables, but until the end of a document we cannot be sure if a bookmark
1373 is a variable or not, at the end we will have a list of reference names which
1374 were set or asked, all bookmarks using the content of those bookmarks are
1375 converted to show variables, those that reference the position of the field
1376 can be left as references, because a bookmark is also inserted at the position
1377 of a set or ask field, either by word, or in some special cases by the import
1380 SwFltStackEntry
*SwWW8FltRefStack::RefToVar(const SwField
* pField
,
1381 SwFltStackEntry
&rEntry
)
1383 SwFltStackEntry
*pRet
=nullptr;
1384 if (pField
&& SwFieldIds::GetRef
== pField
->Which())
1386 //Get the name of the ref field, and see if actually a variable
1387 const OUString sName
= pField
->GetPar1();
1388 std::map
<OUString
, OUString
, SwWW8::ltstr
>::const_iterator
1389 aResult
= m_aFieldVarNames
.find(sName
);
1391 if (aResult
!= m_aFieldVarNames
.end())
1393 SwGetExpField
aField( static_cast<SwGetExpFieldType
*>(
1394 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp
)), sName
, nsSwGetSetExpType::GSE_STRING
, 0);
1395 SwFormatField
aTmp(aField
);
1396 rEntry
.m_pAttr
.reset( aTmp
.Clone() );
1403 OUString
SwWW8ImplReader::GetMappedBookmark(std::u16string_view rOrigName
)
1405 OUString
sName(BookmarkToWriter(rOrigName
));
1406 OSL_ENSURE(m_xPlcxMan
, "no pPlcxMan");
1407 m_xPlcxMan
->GetBook()->MapName(sName
);
1409 //See if there has been a variable set with this name, if so get
1410 //the pseudo bookmark name that was set with it.
1411 std::map
<OUString
, OUString
, SwWW8::ltstr
>::const_iterator aResult
=
1412 m_xReffingStck
->m_aFieldVarNames
.find(sName
);
1414 return (aResult
== m_xReffingStck
->m_aFieldVarNames
.end())
1415 ? sName
: (*aResult
).second
;
1419 eF_ResT
SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc
* pF
, OUString
& rStr
)
1421 OUString sOrigName
, aQ
;
1423 WW8ReadFieldParams
aReadParam( rStr
);
1426 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1432 if (sOrigName
.isEmpty())
1433 sOrigName
= aReadParam
.GetResult();
1434 else if (aQ
.isEmpty())
1435 aQ
= aReadParam
.GetResult();
1439 if ( aReadParam
.GoToTokenParam() )
1440 aDef
= aReadParam
.GetResult();
1445 if (sOrigName
.isEmpty())
1446 return eF_ResT::TAGIGN
; // does not make sense without textmark
1448 const OUString
aResult(GetFieldResult(pF
));
1450 //#i24377#, munge Default Text into title as we have only one slot
1451 //available for aResult and aDef otherwise
1452 if (!aDef
.isEmpty())
1459 const tools::Long nNo
= MapBookmarkVariables(pF
, sOrigName
, aResult
);
1461 SwSetExpFieldType
* pFT
= static_cast<SwSetExpFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().InsertFieldType(
1462 SwSetExpFieldType(&m_rDoc
, sOrigName
, nsSwGetSetExpType::GSE_STRING
)));
1463 SwSetExpField
aField(pFT
, aResult
);
1464 aField
.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE
| nsSwGetSetExpType::GSE_STRING
);
1465 aField
.SetInputFlag(true);
1466 aField
.SetPromptText( aQ
);
1468 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1470 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_BOOKMARK
, true, nNo
);
1475 eF_ResT
SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc
*, OUString
& rStr
)
1477 if( !m_pNumFieldType
){ // 1st time
1478 SwSetExpFieldType
aT( &m_rDoc
, "AutoNr", nsSwGetSetExpType::GSE_SEQ
);
1479 m_pNumFieldType
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aT
);
1481 SwSetExpField
aField( static_cast<SwSetExpFieldType
*>(m_pNumFieldType
), OUString(),
1482 GetNumberPara( rStr
) );
1483 aField
.SetValue( ++m_nFieldNum
, nullptr );
1484 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
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("\"", "");
1837 const auto pType(static_cast<SwDocInfoFieldType
*>(
1838 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo
)));
1839 SwDocInfoField
aField(pType
, nSub
|nReg
, aData
, GetFieldResult(pF
), nFormat
);
1841 ForceFieldLanguage(aField
, nLang
);
1842 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1847 eF_ResT
SwWW8ImplReader::Read_F_Author(WW8FieldDesc
* pF
, OUString
&)
1849 // SH: The SwAuthorField refers not to the original author but to the current user, better use DocInfo
1850 SwDocInfoField
aField( static_cast<SwDocInfoFieldType
*>(
1851 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo
)),
1852 DI_CREATE
|DI_SUB_AUTHOR
|DI_SUB_FIXED
, OUString(), GetFieldResult(pF
));
1853 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1857 eF_ResT
SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc
*, OUString
& )
1859 SwTemplNameField
aField( static_cast<SwTemplNameFieldType
*>(
1860 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName
)), FF_NAME
);
1861 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1865 // Both the date and the time fields can be used for showing a date a time or both.
1866 eF_ResT
SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc
*pF
, OUString
& rStr
)
1868 bool bHijri
= false;
1869 WW8ReadFieldParams
aReadParam(rStr
);
1872 const sal_Int32 nTok
= aReadParam
.SkipToNextToken();
1885 //Saka Calendar, should we do something with this ?
1890 sal_uInt32 nFormat
= 0;
1892 LanguageType
nLang(LANGUAGE_SYSTEM
);
1893 SvNumFormatType nDT
= GetTimeDatePara(rStr
, nFormat
, nLang
, ww::eDATE
, bHijri
);
1895 if( SvNumFormatType::UNDEFINED
== nDT
) // no D/T-Formatstring
1899 nDT
= SvNumFormatType::TIME
;
1900 nFormat
= m_rDoc
.GetNumberFormatter()->GetFormatIndex(
1901 NF_TIME_START
, LANGUAGE_SYSTEM
);
1905 nDT
= SvNumFormatType::DATE
;
1906 nFormat
= m_rDoc
.GetNumberFormatter()->GetFormatIndex(
1907 NF_DATE_START
, LANGUAGE_SYSTEM
);
1911 if (nDT
& SvNumFormatType::DATE
|| nDT
== SvNumFormatType::TIME
)
1913 SwDateTimeField
aField(static_cast<SwDateTimeFieldType
*>(
1914 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime
)),
1915 nDT
& SvNumFormatType::DATE
? DATEFLD
: TIMEFLD
, nFormat
);
1916 if (pF
->nOpt
& 0x10) // Fixed field
1919 if (!m_rDoc
.GetNumberFormatter()->IsNumberFormat(GetFieldResult(pF
), nFormat
, fSerial
,
1920 SvNumInputOptions::LAX_TIME
))
1921 return eF_ResT::TEXT
; // just drop the field and insert the plain text.
1922 aField
.SetSubType(aField
.GetSubType() | FIXEDFLD
);
1923 DateTime
aSetDateTime(m_rDoc
.GetNumberFormatter()->GetNullDate());
1924 aSetDateTime
.AddTime(fSerial
);
1925 aField
.SetDateTime(aSetDateTime
);
1927 ForceFieldLanguage(aField
, nLang
);
1928 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1934 eF_ResT
SwWW8ImplReader::Read_F_FileName(WW8FieldDesc
*, OUString
&rStr
)
1936 SwFileNameFormat eType
= FF_NAME
;
1937 WW8ReadFieldParams
aReadParam(rStr
);
1940 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
1946 eType
= FF_PATHNAME
;
1949 //Skip over MERGEFORMAT
1950 (void)aReadParam
.SkipToNextToken();
1953 OSL_ENSURE(false, "unknown option in FileName field");
1958 SwFileNameField
aField(
1959 static_cast<SwFileNameFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename
)), eType
);
1960 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
1964 eF_ResT
SwWW8ImplReader::Read_F_Num( WW8FieldDesc
* pF
, OUString
& rStr
)
1966 sal_uInt16 nSub
= DS_PAGE
; // page number
1968 case 27: nSub
= DS_WORD
; break; // number of words
1969 case 28: nSub
= DS_CHAR
; break; // number of characters
1971 SwDocStatField
aField( static_cast<SwDocStatFieldType
*>(
1972 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocStat
)), nSub
,
1973 GetNumberPara( rStr
) );
1974 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1978 eF_ResT
SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc
*, OUString
& rStr
)
1981 SwPageNumberField
aField( static_cast<SwPageNumberFieldType
*>(
1982 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber
)), PG_RANDOM
,
1983 GetNumberPara(rStr
, true));
1985 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
1989 eF_ResT
SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc
*, OUString
& rStr
)
1994 sal_Int32 nSize
= 0;
1995 WW8ReadFieldParams
aReadParam( rStr
);
1998 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2005 aQ
= aReadParam
.GetResult();
2009 if ( aReadParam
.GoToTokenParam() )
2010 aName
= aReadParam
.GetResult();
2014 if ( aReadParam
.GoToTokenParam() )
2016 const OUString aSiz
= aReadParam
.GetResult();
2017 if (!aSiz
.isEmpty())
2019 bool bFail
= o3tl::checked_multiply
<sal_Int32
>(aSiz
.toInt32(), 20, nSize
); // pT -> twip
2028 return eF_ResT::TAGIGN
; // -> no 0-char in text
2030 sal_Unicode
const cChar
= static_cast<sal_Unicode
>(aQ
.toInt32());
2031 if (!linguistic::IsControlChar(cChar
) || cChar
== '\r' || cChar
== '\n' || cChar
== '\t')
2033 if (!aName
.isEmpty()) // Font Name set ?
2035 SvxFontItem
aFont(FAMILY_DONTKNOW
, aName
, OUString(),
2036 PITCH_DONTKNOW
, RTL_TEXTENCODING_SYMBOL
, RES_CHRATR_FONT
);
2037 NewAttr(aFont
); // new Font
2040 if (nSize
> 0) //#i20118#
2042 SvxFontHeightItem
aSz(nSize
, 100, RES_CHRATR_FONTSIZE
);
2046 m_rDoc
.getIDocumentContentOperations().InsertString(*m_pPaM
, OUString(cChar
));
2049 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(), RES_CHRATR_FONTSIZE
);
2050 if (!aName
.isEmpty())
2051 m_xCtrlStck
->SetAttr(*m_pPaM
->GetPoint(), RES_CHRATR_FONT
);
2055 m_rDoc
.getIDocumentContentOperations().InsertString(*m_pPaM
, "###");
2062 eF_ResT
SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc
*, OUString
& rStr
)
2064 WW8ReadFieldParams
aReadParam( rStr
);
2067 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2082 if( m_bObj
&& m_nPicLocFc
)
2083 m_nObjLocFc
= m_nPicLocFc
;
2084 m_bEmbeddObj
= true;
2085 return eF_ResT::TEXT
;
2089 eF_ResT
SwWW8ImplReader::Read_F_Set( WW8FieldDesc
* pF
, OUString
& rStr
)
2093 WW8ReadFieldParams
aReadParam( rStr
);
2096 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2102 if (sOrigName
.isEmpty())
2103 sOrigName
= aReadParam
.GetResult();
2104 else if (sVal
.isEmpty())
2105 sVal
= aReadParam
.GetResult();
2110 const tools::Long nNo
= MapBookmarkVariables(pF
, sOrigName
, sVal
);
2112 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc
, sOrigName
,
2113 nsSwGetSetExpType::GSE_STRING
) );
2114 SwSetExpField
aField( static_cast<SwSetExpFieldType
*>(pFT
), sVal
, ULONG_MAX
);
2115 aField
.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE
| nsSwGetSetExpType::GSE_STRING
);
2117 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2119 m_xReffedStck
->SetAttr(*m_pPaM
->GetPoint(), RES_FLTR_BOOKMARK
, true, nNo
);
2125 eF_ResT
SwWW8ImplReader::Read_F_Ref( WW8FieldDesc
*, OUString
& rStr
)
2126 { // Reference - Field
2127 OUString sOrigBkmName
;
2128 REFERENCEMARK eFormat
= REF_CONTENT
;
2130 WW8ReadFieldParams
aReadParam( rStr
);
2133 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2139 if( sOrigBkmName
.isEmpty() ) // get name of bookmark
2140 sOrigBkmName
= aReadParam
.GetResult();
2143 /* References to numbers in Word could be either to a numbered
2144 paragraph or to a chapter number. However Word does not seem to
2145 have the capability we do, of referring to the chapter number some
2146 other bookmark is in. As a result, cross-references to chapter
2147 numbers in a word document will be cross-references to a numbered
2148 paragraph, being the chapter heading paragraph. As it happens, our
2149 cross-references to numbered paragraphs will do the right thing
2150 when the target is a numbered chapter heading, so there is no need
2151 for us to use the REF_CHAPTER bookmark format on import.
2154 eFormat
= REF_NUMBER_NO_CONTEXT
;
2157 eFormat
= REF_NUMBER
;
2160 eFormat
= REF_NUMBER_FULL_CONTEXT
;
2164 eFormat
= REF_UPDOWN
;
2169 // unimplemented switch: just do 'nix nought nothing' :-)
2174 OUString
sBkmName(GetMappedBookmark(sOrigBkmName
));
2176 // #i120879# add cross reference bookmark name prefix, if it
2177 // matches internal TOC bookmark naming convention
2178 if ( IsTOCBookmarkName( sBkmName
) )
2180 sBkmName
= EnsureTOCBookmarkName(sBkmName
);
2181 // track <sBookmarkName> as referenced TOC bookmark.
2182 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sBkmName
);
2185 SwGetRefField
aField(
2186 static_cast<SwGetRefFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)),
2187 sBkmName
,"",REF_BOOKMARK
,0,eFormat
);
2189 if (eFormat
== REF_CONTENT
)
2192 If we are just inserting the contents of the bookmark, then it
2193 is possible that the bookmark is actually a variable, so we
2194 must store it until the end of the document to see if it was,
2195 in which case we'll turn it into a show variable
2197 m_xReffingStck
->NewAttr( *m_pPaM
->GetPoint(), SwFormatField(aField
) );
2198 m_xReffingStck
->SetAttr( *m_pPaM
->GetPoint(), RES_TXTATR_FIELD
);
2202 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
2207 // Note Reference - Field
2208 eF_ResT
SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc
*, OUString
& rStr
)
2211 bool bAboveBelow
= false;
2213 WW8ReadFieldParams
aReadParam( rStr
);
2216 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2222 if( aBkmName
.isEmpty() ) // get name of foot/endnote
2223 aBkmName
= aReadParam
.GetResult();
2226 // activate flag 'Chapter Number'
2234 // unimplemented switch: just do 'nix nought nothing' :-)
2239 // set Sequence No of corresponding Foot-/Endnote to Zero
2240 // (will be corrected in
2241 SwGetRefField
aField( static_cast<SwGetRefFieldType
*>(
2242 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)), aBkmName
, "", REF_FOOTNOTE
, 0,
2244 m_xReffingStck
->NewAttr(*m_pPaM
->GetPoint(), SwFormatField(aField
));
2245 m_xReffingStck
->SetAttr(*m_pPaM
->GetPoint(), RES_TXTATR_FIELD
);
2248 SwGetRefField
aField2( static_cast<SwGetRefFieldType
*>(
2249 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)),aBkmName
, "", REF_FOOTNOTE
, 0,
2251 m_xReffingStck
->NewAttr(*m_pPaM
->GetPoint(), SwFormatField(aField2
));
2252 m_xReffingStck
->SetAttr(*m_pPaM
->GetPoint(), RES_TXTATR_FIELD
);
2258 eF_ResT
SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc
*, OUString
& rStr
)
2261 WW8ReadFieldParams
aReadParam( rStr
);
2264 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2267 else if ( nRet
== -2 && sOrigName
.isEmpty() )
2269 sOrigName
= aReadParam
.GetResult();
2273 const OUString
sName(GetMappedBookmark(sOrigName
));
2275 // loading page reference field in TOX
2276 if (m_bLoadingTOXCache
)
2278 // insert page ref representation as plain text --> return FLD_TEXT
2279 // if there is no hyperlink settings for current toc and referenced bookmark is available,
2280 // assign link to current ref area
2281 if (!m_bLoadingTOXHyperlink
&& !sName
.isEmpty())
2283 // #i120879# add cross reference bookmark name prefix, if it
2284 // matches internal TOC bookmark naming convention
2285 OUString sBookmarkName
;
2286 if ( IsTOCBookmarkName( sName
) )
2288 sBookmarkName
= EnsureTOCBookmarkName(sName
);
2289 // track <sBookmarkName> as referenced TOC bookmark.
2290 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sBookmarkName
);
2294 sBookmarkName
= sName
;
2296 OUString sURL
= "#" + sBookmarkName
;
2297 SwFormatINetFormat
aURL( sURL
, "" );
2298 static const OUStringLiteral
sLinkStyle(u
"Index Link");
2299 const sal_uInt16 nPoolId
=
2300 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle
, SwGetPoolIdFromName::ChrFmt
);
2301 aURL
.SetVisitedFormatAndId( sLinkStyle
, nPoolId
);
2302 aURL
.SetINetFormatAndId( sLinkStyle
, nPoolId
);
2303 m_xCtrlStck
->NewAttr( *m_pPaM
->GetPoint(), aURL
);
2305 return eF_ResT::TEXT
;
2308 // #i120879# add cross reference bookmark name prefix, if it matches
2309 // internal TOC bookmark naming convention
2310 OUString sPageRefBookmarkName
;
2311 if ( IsTOCBookmarkName( sName
) )
2313 sPageRefBookmarkName
= EnsureTOCBookmarkName(sName
);
2314 // track <sPageRefBookmarkName> as referenced TOC bookmark.
2315 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sPageRefBookmarkName
);
2319 sPageRefBookmarkName
= sName
;
2321 SwGetRefField
aField( static_cast<SwGetRefFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef
)),
2322 sPageRefBookmarkName
, "", REF_BOOKMARK
, 0, REF_PAGE
);
2323 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2329 //For MS MacroButton field, the symbol in plain text is always "(" (0x28),
2330 //which should be mapped according to the macro type
2331 static bool ConvertMacroSymbol( std::u16string_view rName
, OUString
& rReference
)
2333 bool bConverted
= false;
2334 if( rReference
== "(" )
2337 sal_Unicode cSymbol
= sal_Unicode(); // silence false warning
2338 if (rName
== u
"CheckIt")
2340 else if (rName
== u
"UncheckIt")
2342 else if (rName
== u
"ShowExample")
2349 rReference
= OUString(cSymbol
);
2355 eF_ResT
SwWW8ImplReader::Read_F_Macro( WW8FieldDesc
*, OUString
& rStr
)
2359 bool bNewVText
= true;
2360 bool bBracket
= false;
2361 WW8ReadFieldParams
aReadParam( rStr
);
2365 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2371 if( aName
.isEmpty() )
2372 aName
= aReadParam
.GetResult();
2373 else if( aVText
.isEmpty() || bBracket
)
2377 aVText
+= aReadParam
.GetResult();
2380 bBracket
= (aVText
[0] == '[');
2383 else if( aVText
.endsWith("]") )
2389 if( aName
.isEmpty() )
2390 return eF_ResT::TAGIGN
; // makes no sense without Macro-Name
2392 NotifyMacroEventRead();
2394 //try converting macro symbol according to macro name
2395 bool bApplyWingdings
= ConvertMacroSymbol( aName
, aVText
);
2396 aName
= "StarOffice.Standard.Modul1." + aName
;
2398 SwMacroField
aField( static_cast<SwMacroFieldType
*>(
2399 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Macro
)), aName
, aVText
);
2401 if( !bApplyWingdings
)
2402 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2405 //set Wingdings font
2407 for ( ; i
< m_xFonts
->GetMax(); i
++ )
2412 rtl_TextEncoding eSrcCharSet
;
2413 if( GetFontParams( i
, eFamily
, aFontName
, ePitch
, eSrcCharSet
)
2414 && aFontName
=="Wingdings" )
2420 if ( i
< m_xFonts
->GetMax() )
2423 SetNewFontAttr( i
, true, RES_CHRATR_FONT
);
2424 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2425 m_xCtrlStck
->SetAttr( *m_pPaM
->GetPoint(), RES_CHRATR_FONT
);
2433 bool CanUseRemoteLink(const OUString
&rGrfName
)
2435 bool bUseRemote
= false;
2438 // Related: tdf#102499, add a default css::ucb::XCommandEnvironment
2439 // in order to have https protocol manage certificates correctly
2440 uno::Reference
< task::XInteractionHandler
> xIH(
2441 task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr));
2443 uno::Reference
< ucb::XProgressHandler
> xProgress
;
2444 rtl::Reference
<::ucbhelper::CommandEnvironment
> pCommandEnv
=
2445 new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH
), xProgress
);
2447 ::ucbhelper::Content
aCnt(rGrfName
,
2448 static_cast< ucb::XCommandEnvironment
* >(pCommandEnv
.get()),
2449 comphelper::getProcessComponentContext());
2451 if ( !INetURLObject( rGrfName
).isAnyKnownWebDAVScheme() )
2454 aCnt
.getPropertyValue("Title") >>= aTitle
;
2455 bUseRemote
= !aTitle
.isEmpty();
2459 // is a link to a WebDAV resource
2460 // need to use MediaType to check for link usability
2461 OUString aMediaType
;
2462 aCnt
.getPropertyValue("MediaType") >>= aMediaType
;
2463 bUseRemote
= !aMediaType
.isEmpty();
2468 // this file did not exist, so we will not set this as graphiclink
2475 eF_ResT
SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc
*, OUString
& rStr
)
2478 bool bEmbedded
= true;
2480 WW8ReadFieldParams
aReadParam( rStr
);
2483 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2489 if (aGrfName
.isEmpty())
2490 aGrfName
= ConvertFFileName(aReadParam
.GetResult());
2497 case 'c':// skip the converter name
2498 aReadParam
.FindNextStringPiece();
2504 bEmbedded
= !CanUseRemoteLink(aGrfName
);
2511 Now we write the Link into the Doc and remember the SwFlyFrameFormat.
2512 Since we end on return FLD_READ_FSPA below, the skip value will be set
2513 so that Char-1 will still be read.
2514 When we then call SwWW8ImplReader::ImportGraf() it will then recognize
2515 that we have inserted a graphic link and the suiting SwAttrSet will be
2516 inserted into the frame format.
2518 SfxItemSetFixed
<RES_FRMATR_BEGIN
, RES_FRMATR_END
-1> aFlySet( m_rDoc
.GetAttrPool() );
2519 aFlySet
.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR
) );
2520 aFlySet
.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP
, text::RelOrientation::FRAME
));
2521 m_pFlyFormatOfJustInsertedGraphic
=
2522 m_rDoc
.getIDocumentContentOperations().InsertGraphic(*m_pPaM
,
2525 nullptr, // Graphic*
2527 nullptr, nullptr); // SwFrameFormat*
2528 m_aGrfNameGenerator
.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic
,
2529 INetURLObject(aGrfName
).GetBase());
2531 return eF_ResT::READ_FSPA
;
2534 OUString
wwSectionNamer::UniqueName()
2536 const OUString
aName(msFileLinkSeed
+ OUString::number(++mnFileSectionNo
));
2537 return mrDoc
.GetUniqueSectionName(&aName
);
2541 eF_ResT
SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc
* /*pF*/, OUString
& rStr
)
2545 WW8ReadFieldParams
aReadParam( rStr
);
2548 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2554 if( aPara
.isEmpty() )
2555 aPara
= aReadParam
.GetResult();
2556 else if( aBook
.isEmpty() )
2557 aBook
= aReadParam
.GetResult();
2560 //Skip over MERGEFORMAT
2561 (void)aReadParam
.SkipToNextToken();
2565 aPara
= ConvertFFileName(aPara
);
2567 if (!aBook
.isEmpty() && aBook
[ 0 ] != '\\')
2569 // Section from Source (no switch)?
2570 ConvertUFName(aBook
);
2571 aPara
+= OUStringChar(sfx2::cTokenSeparator
)
2572 + OUStringChar(sfx2::cTokenSeparator
) + aBook
;
2577 What we will do is insert a section to be linked to a file, but just in
2578 case the file is not available we will fill in the section with the stored
2579 content of this winword field as a fallback.
2581 SwPosition
aTmpPos(*m_pPaM
->GetPoint());
2583 SwSectionData
aSection(SectionType::FileLink
,
2584 m_aSectionNameGenerator
.UniqueName());
2585 aSection
.SetLinkFileName( aPara
);
2586 aSection
.SetProtectFlag(true);
2588 SwSection
*const pSection
=
2589 m_rDoc
.InsertSwSection(*m_pPaM
, aSection
, nullptr, nullptr, false);
2590 OSL_ENSURE(pSection
, "no section inserted");
2592 return eF_ResT::TEXT
;
2593 const SwSectionNode
* pSectionNode
= pSection
->GetFormat()->GetSectionNode();
2594 OSL_ENSURE(pSectionNode
, "no section node!");
2596 return eF_ResT::TEXT
;
2598 m_pPaM
->GetPoint()->Assign( pSectionNode
->GetIndex()+1 );
2600 //we have inserted a section before this point, so adjust pos
2601 //for future page/section segment insertion
2602 m_aSectionManager
.PrependedInlineNode(aTmpPos
, m_pPaM
->GetPointNode());
2604 return eF_ResT::TEXT
;
2608 eF_ResT
SwWW8ImplReader::Read_F_DBField( WW8FieldDesc
* pF
, OUString
& rStr
)
2610 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
2615 WW8ReadFieldParams
aReadParam( rStr
);
2618 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
2624 if( aName
.isEmpty() )
2625 aName
= aReadParam
.GetResult();
2629 SwDBFieldType
aD( &m_rDoc
, aName
, SwDBData() ); // Database: nothing
2631 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aD
);
2632 SwDBField
aField( static_cast<SwDBFieldType
*>(pFT
) );
2633 aField
.SetFieldCode( rStr
);
2636 m_xSBase
->WW8ReadString( *m_pStrm
, aResult
, m_xPlcxMan
->GetCpOfs()+
2637 pF
->nSRes
, pF
->nLRes
, m_eTextCharSet
);
2639 aResult
= aResult
.replace( '\xb', '\n' );
2641 aField
.InitContent(aResult
);
2643 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField( aField
));
2649 eF_ResT
SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc
*, OUString
& )
2651 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
2652 SwDBNextSetFieldType aN
;
2653 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aN
);
2654 SwDBNextSetField
aField( static_cast<SwDBNextSetFieldType
*>(pFT
), OUString(),
2655 SwDBData() ); // Database: nothing
2656 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2662 eF_ResT
SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc
*, OUString
& )
2664 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
2665 SwDBSetNumberFieldType aN
;
2666 SwFieldType
* pFT
= m_rDoc
.getIDocumentFieldsAccess().InsertFieldType( aN
);
2667 SwDBSetNumberField
aField( static_cast<SwDBSetNumberFieldType
*>(pFT
),
2668 SwDBData() ); // Datenbase: nothing
2669 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) );
2675 EQ , only the usage for
2676 a. Combined Characters supported, must be exactly in the form that word
2677 only accepts as combined characters, i.e.
2678 eq \o(\s\up Y(XXX),\s\do Y(XXX))
2679 b. Ruby Text supported, must be in the form that word recognizes as being
2683 eF_ResT
SwWW8ImplReader::Read_F_Equation( WW8FieldDesc
*, OUString
& rStr
)
2685 WW8ReadFieldParams
aReadParam( rStr
);
2686 const sal_Int32 cChar
= aReadParam
.SkipToNextToken();
2687 if ('o' == cChar
|| 'O' == cChar
)
2689 EquationResult
aResult(ParseCombinedChars(rStr
));
2691 if (aResult
.sType
== "Input")
2693 SwInputField
aField( static_cast<SwInputFieldType
*>(m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input
)),
2694 aResult
.sResult
, aResult
.sResult
, INP_TXT
, 0 );
2695 m_rDoc
.getIDocumentContentOperations().InsertPoolItem( *m_pPaM
, SwFormatField( aField
) ); // insert input field
2697 else if (aResult
.sType
== "CombinedCharacters")
2699 SwCombinedCharField
aField(static_cast<SwCombinedCharFieldType
*>(
2700 m_rDoc
.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars
)), aResult
.sType
);
2701 m_rDoc
.getIDocumentContentOperations().InsertPoolItem(*m_pPaM
, SwFormatField(aField
));
2704 else if ('*' == cChar
)
2705 Read_SubF_Ruby(aReadParam
);
2710 void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams
& rReadParam
)
2712 sal_uInt16 nJustificationCode
=0;
2714 sal_uInt32 nFontSize
=0;
2719 const sal_Int32 nRet
= rReadParam
.SkipToNextToken();
2726 OUString sTemp
= rReadParam
.GetResult();
2727 if( sTemp
.startsWithIgnoreAsciiCase( "jc" ) )
2729 sTemp
= sTemp
.copy(2);
2730 nJustificationCode
= o3tl::narrowing
<sal_uInt16
>(sTemp
.toInt32());
2732 else if( sTemp
.startsWithIgnoreAsciiCase( "hps" ) )
2734 sTemp
= sTemp
.copy(3);
2735 nFontSize
= static_cast<sal_uInt32
>(sTemp
.toInt32());
2737 else if( sTemp
.startsWithIgnoreAsciiCase( "Font:" ) )
2739 sTemp
= sTemp
.copy(5);
2749 const sal_Int32 nRes
= rReadParam
.SkipToNextToken();
2754 if (-2 == rReadParam
.SkipToNextToken() &&
2755 rReadParam
.GetResult().startsWithIgnoreAsciiCase("p"))
2757 if (-2 == rReadParam
.SkipToNextToken())
2759 OUString sPart
= rReadParam
.GetResult();
2760 sal_Int32 nBegin
= sPart
.indexOf('(');
2762 //Word disallows brackets in this field,
2763 sal_Int32 nEnd
= sPart
.indexOf(')');
2765 if ((nBegin
!= -1) &&
2766 (nEnd
!= -1) && (nBegin
< nEnd
))
2768 sRuby
= sPart
.copy(nBegin
+1,nEnd
-nBegin
-1);
2772 nBegin
= sPart
.indexOf(',',nEnd
);
2775 nBegin
= sPart
.indexOf(';',nEnd
);
2777 nEnd
= sPart
.lastIndexOf(')');
2779 if ((nBegin
!= -1) && (nEnd
!= -1) && (nBegin
< nEnd
))
2781 sText
= sPart
.copy(nBegin
+1,nEnd
-nBegin
-1);
2782 sText
= sw::FilterControlChars(sText
);
2792 //Translate and apply
2793 if (sRuby
.isEmpty() || sText
.isEmpty() || sFontName
.isEmpty() || !nFontSize
)
2796 css::text::RubyAdjust eRubyAdjust
;
2797 switch (nJustificationCode
)
2800 eRubyAdjust
= css::text::RubyAdjust_CENTER
;
2803 eRubyAdjust
= css::text::RubyAdjust_BLOCK
;
2806 eRubyAdjust
= css::text::RubyAdjust_INDENT_BLOCK
;
2810 eRubyAdjust
= css::text::RubyAdjust_LEFT
;
2813 eRubyAdjust
= css::text::RubyAdjust_RIGHT
;
2817 SwFormatRuby
aRuby(sRuby
);
2818 const SwCharFormat
*pCharFormat
=nullptr;
2819 //Make a guess at which of asian of western we should be setting
2820 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
2821 sal_uInt16 nScript
= g_pBreakIt
->GetBreakIter()->getScriptType(sRuby
, 0);
2823 //Check to see if we already have a ruby charstyle that this fits
2824 for(const auto& rpCharFormat
: m_aRubyCharFormats
)
2826 const SvxFontHeightItem
&rFH
=
2827 rpCharFormat
->GetFormatAttr(
2828 GetWhichOfScript(RES_CHRATR_FONTSIZE
,nScript
));
2829 if (rFH
.GetHeight() == nFontSize
*10)
2831 const SvxFontItem
&rF
= rpCharFormat
->GetFormatAttr(
2832 GetWhichOfScript(RES_CHRATR_FONT
,nScript
));
2833 if (rF
.GetFamilyName() == sFontName
)
2835 pCharFormat
= rpCharFormat
;
2841 //Create a new char style if necessary
2845 //Take this as the base name
2846 SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT
,aNm
);
2847 aNm
+=OUString::number(m_aRubyCharFormats
.size()+1);
2848 SwCharFormat
*pFormat
= m_rDoc
.MakeCharFormat(aNm
, m_rDoc
.GetDfltCharFormat());
2849 SvxFontHeightItem
aHeightItem(nFontSize
*10, 100, RES_CHRATR_FONTSIZE
);
2850 SvxFontItem
aFontItem(FAMILY_DONTKNOW
,sFontName
,
2851 OUString(), PITCH_DONTKNOW
, RTL_TEXTENCODING_DONTKNOW
, RES_CHRATR_FONT
);
2852 aHeightItem
.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE
,nScript
));
2853 aFontItem
.SetWhich(GetWhichOfScript(RES_CHRATR_FONT
,nScript
));
2854 pFormat
->SetFormatAttr(aHeightItem
);
2855 pFormat
->SetFormatAttr(aFontItem
);
2856 m_aRubyCharFormats
.push_back(pFormat
);
2857 pCharFormat
= pFormat
;
2860 //Set the charstyle and justification
2861 aRuby
.SetCharFormatName(pCharFormat
->GetName());
2862 aRuby
.SetCharFormatId(pCharFormat
->GetPoolFormatId());
2863 aRuby
.SetAdjustment(eRubyAdjust
);
2866 m_rDoc
.getIDocumentContentOperations().InsertString( *m_pPaM
, sText
);
2867 m_xCtrlStck
->SetAttr( *m_pPaM
->GetPoint(), RES_TXTATR_CJK_RUBY
);
2871 // "table of ..." fields
2873 static void lcl_toxMatchACSwitch(SwDoc
const & rDoc
,
2875 WW8ReadFieldParams
& rParam
,
2876 SwCaptionDisplay eCaptionType
)
2878 if ( rParam
.GoToTokenParam() )
2880 SwTOXType
* pType
= const_cast<SwTOXType
*>(rDoc
.GetTOXType( TOX_ILLUSTRATIONS
, 0));
2881 rBase
.RegisterToTOXType( *pType
);
2882 rBase
.SetCaptionDisplay( eCaptionType
);
2883 // Read Sequence Name and store in TOXBase
2884 OUString
sSeqName( rParam
.GetResult() );
2885 lcl_ConvertSequenceName( sSeqName
);
2886 rBase
.SetSequenceName( sSeqName
);
2890 static void EnsureMaxLevelForTemplates(SwTOXBase
& rBase
)
2892 //If the TOC contains Template entries at levels > the evaluation level
2893 //that was initially taken from the max normal outline level of the word TOC
2894 //then we cannot use that for the evaluation level because writer cuts off
2895 //all styles above that level, while word just cuts off the "standard"
2896 //outline styles, we have no option but to expand to the highest level
2898 if ((rBase
.GetLevel() != MAXLEVEL
) && (SwTOXElement::Template
& rBase
.GetCreateType()))
2900 for (sal_uInt16 nI
= MAXLEVEL
; nI
> 0; --nI
)
2902 if (!rBase
.GetStyleNames(nI
-1).isEmpty())
2911 static void lcl_toxMatchTSwitch(SwWW8ImplReader
const & rReader
, SwTOXBase
& rBase
,
2912 WW8ReadFieldParams
& rParam
)
2914 if ( !rParam
.GoToTokenParam() )
2917 OUString
sParams( rParam
.GetResult() );
2918 if( sParams
.isEmpty() )
2921 sal_Int32 nIndex
= 0;
2923 // Delimiters between styles and style levels appears to allow both ; and ,
2925 OUString
sTemplate( sParams
.getToken(0, ';', nIndex
) );
2929 sTemplate
= sParams
.getToken(0, ',', nIndex
);
2933 const SwFormat
* pStyle
= rReader
.GetStyleWithOrgWWName(sTemplate
);
2935 sTemplate
= pStyle
->GetName();
2936 // Store Style for Level 0 into TOXBase
2937 rBase
.SetStyleNames( sTemplate
, 0 );
2939 else while( -1 != nIndex
)
2941 sal_Int32 nOldIndex
=nIndex
;
2942 sal_uInt16 nLevel
= o3tl::narrowing
<sal_uInt16
>(
2943 o3tl::toInt32(o3tl::getToken(sParams
, 0, ';', nIndex
)));
2947 nLevel
= o3tl::narrowing
<sal_uInt16
>(
2948 o3tl::toInt32(o3tl::getToken(sParams
, 0, ',', nIndex
)));
2951 if( (0 < nLevel
) && (MAXLEVEL
>= nLevel
) )
2954 // Store Style and Level into TOXBase
2955 const SwFormat
* pStyle
2956 = rReader
.GetStyleWithOrgWWName( sTemplate
);
2959 sTemplate
= pStyle
->GetName();
2961 OUString
sStyles( rBase
.GetStyleNames( nLevel
) );
2962 if( !sStyles
.isEmpty() )
2963 sStyles
+= OUStringChar(TOX_STYLE_DELIMITER
);
2964 sStyles
+= sTemplate
;
2965 rBase
.SetStyleNames( sStyles
, nLevel
);
2967 // read next style name...
2969 sTemplate
= sParams
.getToken(0, ';', nIndex
);
2973 sTemplate
= sParams
.getToken(0, ',', nIndex
);
2978 sal_uInt16
wwSectionManager::CurrentSectionColCount() const
2980 sal_uInt16 nIndexCols
= 1;
2981 if (!maSegments
.empty())
2982 nIndexCols
= maSegments
.back().maSep
.ccolM1
+ 1;
2986 //Will there be a new pagebreak at this position (don't know what type
2988 bool wwSectionManager::WillHavePageDescHere(const SwNode
& rNd
) const
2991 if (!maSegments
.empty())
2993 if (!maSegments
.back().IsContinuous() &&
2994 maSegments
.back().maStart
== rNd
)
3002 static sal_uInt16
lcl_GetMaxValidWordTOCLevel(const SwForm
&rForm
)
3004 // GetFormMax() returns level + 1, hence the -1
3005 sal_uInt16 nRet
= rForm
.GetFormMax()-1;
3007 // If the max of this type of TOC is greater than the max of a word
3008 // possible toc, then clip to the word max
3009 if (nRet
> WW8ListManager::nMaxLevel
)
3010 nRet
= WW8ListManager::nMaxLevel
;
3015 eF_ResT
SwWW8ImplReader::Read_F_Tox( WW8FieldDesc
* pF
, OUString
& rStr
)
3017 if (!m_bLoadingTOXCache
)
3019 m_bLoadingTOXCache
= true;
3023 // Embedded TOX --> continue reading its content, but no further TOX
3025 ++m_nEmbeddedTOXLevel
;
3026 return eF_ResT::TEXT
;
3030 return eF_ResT::TEXT
; // ignore (#i25440#)
3032 TOXTypes eTox
; // create a ToxBase
3046 SwTOXElement nCreateOf
= (eTox
== TOX_CONTENT
) ? SwTOXElement::OutlineLevel
: SwTOXElement::Mark
;
3048 sal_uInt16 nIndexCols
= 1;
3050 const SwTOXType
* pType
= m_rDoc
.GetTOXType( eTox
, 0 );
3051 SwForm
aOrigForm(eTox
);
3052 std::shared_ptr
<SwTOXBase
> pBase
= std::make_shared
<SwTOXBase
>( pType
, aOrigForm
, nCreateOf
, OUString() );
3053 pBase
->SetProtected(m_aSectionManager
.CurrentSectionIsProtected());
3057 SwTOIOptions eOptions
= SwTOIOptions::SameEntry
| SwTOIOptions::CaseSensitive
;
3059 // We set SwTOXElement::OutlineLevel only if
3060 // the parameter \o is within 1 to 9
3061 // or the parameter \f exists
3062 // or NO switch parameter are given at all.
3063 WW8ReadFieldParams
aReadParam( rStr
);
3066 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3072 if ( aReadParam
.GoToTokenParam() )
3074 const OUString
sParams( aReadParam
.GetResult() );
3075 // if NO OUString just ignore the \c
3076 if( !sParams
.isEmpty() )
3078 nIndexCols
= o3tl::narrowing
<sal_uInt16
>(sParams
.toInt32());
3084 if ( aReadParam
.GoToTokenParam() ) // if NO String just ignore the \e
3086 OUString
sDelimiter( aReadParam
.GetResult() );
3087 SwForm
aForm( pBase
->GetTOXForm() );
3089 // Attention: if TOX_CONTENT brave
3090 // GetFormMax() returns MAXLEVEL + 1 !!
3091 sal_uInt16 nEnd
= aForm
.GetFormMax()-1;
3093 for(sal_uInt16 nLevel
= 1;
3097 // Levels count from 1
3098 // Level 0 is reserved for CAPTION
3100 // Insert delimiter instead of tab in front of the page number if there is one:
3101 FormTokenType ePrevType
= TOKEN_END
;
3102 FormTokenType eType
;
3104 SwFormTokens aPattern
=
3105 aForm
.GetPattern(nLevel
);
3106 SwFormTokens::iterator aIt
= aPattern
.begin();
3109 eType
= ++aIt
== aPattern
.end() ? TOKEN_END
: aIt
->eTokenType
;
3111 if (eType
== TOKEN_PAGE_NUMS
)
3113 if (TOKEN_TAB_STOP
== ePrevType
)
3117 if(0x09 == sDelimiter
[0])
3118 aIt
->eTabAlign
= SvxTabAdjust::End
;
3121 SwFormToken
aToken(TOKEN_TEXT
);
3122 aToken
.sText
= sDelimiter
;
3125 aForm
.SetPattern(nLevel
, std::move(aPattern
));
3133 while (TOKEN_END
!= eType
);
3136 pBase
->SetTOXForm( aForm
);
3142 eOptions
|= SwTOIOptions::AlphaDelimiter
;
3147 pBase
->SetOptions( eOptions
);
3153 bool bIsHyperlink
= false;
3154 // We set SwTOXElement::OutlineLevel only if
3155 // the parameter \o is within 1 to 9
3156 // or the parameter \f exists
3157 // or NO switch parameter are given at all.
3158 SwTOXElement eCreateFrom
= SwTOXElement::NONE
;
3159 sal_Int32 nMaxLevel
= 0;
3160 WW8ReadFieldParams
aReadParam( rStr
);
3163 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3169 bIsHyperlink
= true;
3173 lcl_toxMatchACSwitch(m_rDoc
, *pBase
, aReadParam
,
3181 if( !aReadParam
.GetTokenSttFromTo(nullptr, &nVal
, WW8ListManager::nMaxLevel
) )
3182 nVal
= lcl_GetMaxValidWordTOCLevel(aOrigForm
);
3183 if( nMaxLevel
< nVal
)
3185 eCreateFrom
|= SwTOXElement::OutlineLevel
;
3189 eCreateFrom
|= SwTOXElement::Mark
;
3194 if( aReadParam
.GetTokenSttFromTo(nullptr, &nVal
, WW8ListManager::nMaxLevel
) )
3196 if( nMaxLevel
< nVal
)
3198 eCreateFrom
|= SwTOXElement::Mark
;
3202 case 't': // paragraphs using special styles shall
3203 // provide the TOX's content
3204 lcl_toxMatchTSwitch(*this, *pBase
, aReadParam
);
3205 eCreateFrom
|= SwTOXElement::Template
;
3209 if ( aReadParam
.GoToTokenParam() ) // if NO String just ignore the \p
3211 OUString
sDelimiter( aReadParam
.GetResult() );
3212 SwForm
aForm( pBase
->GetTOXForm() );
3214 // Attention: if TOX_CONTENT brave
3215 // GetFormMax() returns MAXLEVEL + 1 !!
3216 sal_uInt16 nEnd
= aForm
.GetFormMax()-1;
3218 for(sal_uInt16 nLevel
= 1;
3222 // Levels count from 1
3223 // Level 0 is reserved for CAPTION
3225 // Insert delimiter instead of tab in front of the pagenumber if there is one:
3226 FormTokenType ePrevType
= TOKEN_END
;
3227 FormTokenType eType
;
3230 SwFormTokens aPattern
= aForm
.GetPattern(nLevel
);
3231 SwFormTokens::iterator aIt
= aPattern
.begin();
3234 eType
= ++aIt
== aPattern
.end() ? TOKEN_END
: aIt
->eTokenType
;
3236 if (eType
== TOKEN_PAGE_NUMS
)
3238 if (TOKEN_TAB_STOP
== ePrevType
)
3242 SwFormToken
aToken(TOKEN_TEXT
);
3243 aToken
.sText
= sDelimiter
;
3246 aForm
.SetPattern(nLevel
,
3247 std::move(aPattern
));
3253 while( TOKEN_END
!= eType
);
3256 pBase
->SetTOXForm( aForm
);
3260 case 'n': // don't print page numbers
3262 // read START and END param
3263 sal_Int32
nStart(0);
3265 if( !aReadParam
.GetTokenSttFromTo( &nStart
, &nEnd
,
3266 WW8ListManager::nMaxLevel
) )
3269 nEnd
= aOrigForm
.GetFormMax()-1;
3271 // remove page numbers from this levels
3272 SwForm
aForm( pBase
->GetTOXForm() );
3273 if (aForm
.GetFormMax() <= nEnd
)
3274 nEnd
= aForm
.GetFormMax()-1;
3275 for ( sal_Int32 nLevel
= nStart
; nLevel
<=nEnd
; ++nLevel
)
3277 // Levels count from 1
3278 // Level 0 is reserved for CAPTION
3280 // Remove pagenumber and if necessary the tab in front of it:
3281 FormTokenType eType
;
3283 SwFormTokens aPattern
= aForm
.GetPattern(nLevel
);
3284 SwFormTokens::iterator aIt
= aPattern
.begin();
3287 eType
= ++aIt
== aPattern
.end() ? TOKEN_END
: aIt
->eTokenType
;
3289 if (eType
== TOKEN_PAGE_NUMS
)
3291 aIt
= aPattern
.erase(aIt
);
3298 aPattern
.erase(aIt
);
3299 aForm
.SetPattern(nLevel
, std::move(aPattern
));
3304 while (TOKEN_END
!= eType
);
3307 pBase
->SetTOXForm( aForm
);
3312 // the following switches are not (yet) supported
3313 // by good old StarWriter:
3322 // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens
3323 // also include the hyperlinks and page references
3324 SwFormToken
aLinkStart(TOKEN_LINK_START
);
3325 SwFormToken
aLinkEnd(TOKEN_LINK_END
);
3326 aLinkStart
.sCharStyleName
= "Index Link";
3327 aLinkEnd
.sCharStyleName
= "Index Link";
3328 SwForm
aForm(pBase
->GetTOXForm());
3329 sal_uInt16 nEnd
= aForm
.GetFormMax()-1;
3331 for(sal_uInt16 nLevel
= 1; nLevel
<= nEnd
; ++nLevel
)
3333 SwFormTokens aPattern
= aForm
.GetPattern(nLevel
);
3336 aPattern
.insert(aPattern
.begin(), aLinkStart
);
3340 auto aItr
= std::find_if(aPattern
.begin(), aPattern
.end(),
3341 [](const SwFormToken
& rToken
) { return rToken
.eTokenType
== TOKEN_PAGE_NUMS
; });
3342 if (aItr
!= aPattern
.end())
3343 aPattern
.insert(aItr
, aLinkStart
);
3345 aPattern
.push_back(aLinkEnd
);
3346 aForm
.SetPattern(nLevel
, std::move(aPattern
));
3348 pBase
->SetTOXForm(aForm
);
3351 nMaxLevel
= WW8ListManager::nMaxLevel
;
3352 pBase
->SetLevel(nMaxLevel
);
3354 const TOXTypes eType
= pBase
->GetTOXType()->GetType();
3359 //If we would be created from outlines, either explicitly or by default
3360 //then see if we need extra styles added to the outlines
3361 SwTOXElement eEffectivelyFrom
= eCreateFrom
!= SwTOXElement::NONE
? eCreateFrom
: SwTOXElement::OutlineLevel
;
3362 if (eEffectivelyFrom
& SwTOXElement::OutlineLevel
)
3364 // #i19683# Insert a text token " " between the number and entry token.
3365 // In an ideal world we could handle the tab stop between the number and
3366 // the entry correctly, but I currently have no clue how to obtain
3367 // the tab stop position. It is _not_ set at the paragraph style.
3368 std::unique_ptr
<SwForm
> pForm
;
3369 for (const SwWW8StyInf
& rSI
: m_vColl
)
3371 if (rSI
.IsOutlineNumbered())
3373 sal_uInt16 nStyleLevel
= rSI
.mnWW8OutlineLevel
;
3374 const SwNumFormat
& rFormat
= rSI
.GetOutlineNumrule()->Get( nStyleLevel
);
3375 if ( SVX_NUM_NUMBER_NONE
!= rFormat
.GetNumberingType() )
3380 pForm
.reset(new SwForm( pBase
->GetTOXForm() ));
3382 SwFormTokens aPattern
= pForm
->GetPattern(nStyleLevel
);
3383 SwFormTokens::iterator aIt
=
3384 find_if(aPattern
.begin(), aPattern
.end(),
3385 SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO
));
3387 if ( aIt
!= aPattern
.end() )
3389 SwFormToken
aNumberEntrySeparator( TOKEN_TEXT
);
3390 aNumberEntrySeparator
.sText
= " ";
3391 aPattern
.insert( ++aIt
, aNumberEntrySeparator
);
3392 pForm
->SetPattern( nStyleLevel
, std::move(aPattern
) );
3399 pBase
->SetTOXForm( *pForm
);
3403 if (eCreateFrom
!= SwTOXElement::NONE
)
3404 pBase
->SetCreate(eCreateFrom
);
3405 EnsureMaxLevelForTemplates(*pBase
);
3408 case TOX_ILLUSTRATIONS
:
3410 if( eCreateFrom
== SwTOXElement::NONE
)
3411 eCreateFrom
= SwTOXElement::Sequence
;
3412 pBase
->SetCreate( eCreateFrom
);
3415 We don't know until here if we are an illustration
3416 or not, and so have being used a TOX_CONTENT so far
3417 which has 10 levels, while TOX has only two, this
3418 level is set only in the constructor of SwForm, so
3419 create a new one and copy over anything that could
3420 be set in the old one, and remove entries from the
3421 pattern which do not apply to illustration indices
3423 SwForm
aOldForm( pBase
->GetTOXForm() );
3424 SwForm
aNewForm( eType
);
3425 sal_uInt16 nNewEnd
= aNewForm
.GetFormMax()-1;
3428 for(sal_uInt16 nLevel
= 1; nLevel
<= nNewEnd
; ++nLevel
)
3430 SwFormTokens aPattern
= aOldForm
.GetPattern(nLevel
);
3431 SwFormTokens::iterator new_end
=
3432 remove_if(aPattern
.begin(), aPattern
.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO
));
3433 aPattern
.erase(new_end
, aPattern
.end() ); // table index imported with wrong page number format
3434 aForm
.SetPattern( nLevel
, std::move(aPattern
) );
3435 aForm
.SetTemplate( nLevel
, aOldForm
.GetTemplate(nLevel
) );
3438 pBase
->SetTOXForm( aNewForm
);
3442 OSL_ENSURE(false, "Unhandled toc options!");
3450 OSL_ENSURE(false, "Unhandled toc options!");
3454 // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX
3455 pBase
->AdjustTabStops( m_rDoc
);
3457 //#i10028# inserting a toc implicitly acts like a parabreak in word and writer
3458 if ( m_pPaM
->End() &&
3459 m_pPaM
->End()->GetNode().GetTextNode() &&
3460 m_pPaM
->End()->GetNode().GetTextNode()->Len() != 0 )
3462 m_bCareFirstParaEndInToc
= true;
3465 if (m_pPaM
->GetPoint()->GetContentIndex())
3466 AppendTextNode(*m_pPaM
->GetPoint());
3468 const SwPosition
* pPos
= m_pPaM
->GetPoint();
3470 SwFltTOX
aFltTOX( pBase
);
3472 // test if there is already a break item on this node
3473 if(SwContentNode
* pNd
= pPos
->GetNode().GetContentNode())
3475 const SfxItemSet
* pSet
= pNd
->GetpSwAttrSet();
3478 if (SfxItemState::SET
== pSet
->GetItemState(RES_BREAK
, false))
3479 aFltTOX
.SetHadBreakItem(true);
3480 if (SfxItemState::SET
== pSet
->GetItemState(RES_PAGEDESC
, false))
3481 aFltTOX
.SetHadPageDescItem(true);
3485 //Will there be a new pagebreak at this position (don't know what type
3487 if (m_aSectionManager
.WillHavePageDescHere(pPos
->GetNode()))
3488 aFltTOX
.SetHadPageDescItem(true);
3490 // Set start in stack
3491 m_xReffedStck
->NewAttr( *pPos
, aFltTOX
);
3493 m_rDoc
.InsertTableOf(*m_pPaM
->GetPoint(), aFltTOX
.GetBase());
3495 //The TOC field representation contents should be inserted into TOC section, but not after TOC section.
3496 //So we need update the document position when loading TOC representation and after loading TOC;
3497 m_oPosAfterTOC
.emplace(*m_pPaM
, m_pPaM
);
3498 (*m_pPaM
).Move(fnMoveBackward
);
3499 SwPaM
aRegion(*m_pPaM
, m_pPaM
);
3501 OSL_ENSURE(SwDoc::GetCurTOX(*aRegion
.GetPoint()), "Misunderstood how toc works");
3502 if (SwTOXBase
* pBase2
= SwDoc::GetCurTOX(*aRegion
.GetPoint()))
3504 pBase2
->SetMSTOCExpression(rStr
);
3506 if ( nIndexCols
> 1 )
3508 // Set the column number for index
3509 SfxItemSetFixed
<RES_COL
, RES_COL
> aSet( m_rDoc
.GetAttrPool() );
3511 aCol
.Init( nIndexCols
, 708, USHRT_MAX
);
3513 pBase2
->SetAttrSet( aSet
);
3516 // inserting a toc inserts a section before this point, so adjust pos
3517 // for future page/section segment insertion
3518 m_aSectionManager
.PrependedInlineNode( *m_oPosAfterTOC
->GetPoint(), aRegion
.GetPointNode() );
3522 m_xReffedStck
->SetAttr( *pPos
, RES_FLTR_TOX
);
3524 if (!m_aApos
.back()) //a para end in apo doesn't count
3525 m_bWasParaEnd
= true;
3527 //Return FLD_TEXT, instead of FLD_OK
3528 //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed
3529 //FLD_OK means the current field loading is finished. The rest part should be ignored.
3530 return eF_ResT::TEXT
;
3533 eF_ResT
SwWW8ImplReader::Read_F_Shape(WW8FieldDesc
* /*pF*/, OUString
& /*rStr*/)
3536 #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring
3537 to be ignored followed by a 0x1 with an empty drawing. Detect in inserting
3538 the drawing that we are in the Shape field and respond accordingly
3540 return eF_ResT::TEXT
;
3543 eF_ResT
SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc
* /*pF*/, OUString
& rStr
)
3545 OUString sURL
, sTarget
, sMark
;
3547 //HYPERLINK "filename" [switches]
3548 rStr
= comphelper::string::stripEnd(rStr
, 1);
3550 bool bOptions
= false;
3551 WW8ReadFieldParams
aReadParam( rStr
);
3554 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3560 if (sURL
.isEmpty() && !bOptions
)
3561 sURL
= ConvertFFileName(aReadParam
.GetResult());
3571 if ( aReadParam
.SkipToNextToken()==-2 )
3573 sMark
= aReadParam
.GetResult();
3574 if( sMark
.endsWith("\""))
3576 sMark
= sMark
.copy( 0, sMark
.getLength() - 1 );
3578 // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention
3579 if ( IsTOCBookmarkName( sMark
) )
3581 sMark
= EnsureTOCBookmarkName(sMark
);
3582 // track <sMark> as referenced TOC bookmark.
3583 m_xReffedStck
->m_aReferencedTOCBookmarks
.insert( sMark
);
3586 if (m_bLoadingTOXCache
)
3588 m_bLoadingTOXHyperlink
= true; //on loading a TOC field nested hyperlink field
3594 if ( aReadParam
.SkipToNextToken()==-2 )
3595 sTarget
= aReadParam
.GetResult();
3599 OSL_ENSURE( false, "Analysis still missing - unknown data" );
3601 case 's': //worthless fake anchor option
3608 OSL_ENSURE(!sURL
.isEmpty() || !sMark
.isEmpty(), "WW8: Empty URL");
3610 if( !sMark
.isEmpty() )
3611 sURL
+= "#" + sMark
;
3613 SwFormatINetFormat
aURL(sURL
, sTarget
);
3614 // If on loading TOC field, change the default style into the "index link"
3615 if (m_bLoadingTOXCache
)
3617 OUString
sLinkStyle("Index Link");
3618 sal_uInt16 nPoolId
=
3619 SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle
, SwGetPoolIdFromName::ChrFmt
);
3620 aURL
.SetVisitedFormatAndId( sLinkStyle
, nPoolId
);
3621 aURL
.SetINetFormatAndId( sLinkStyle
, nPoolId
);
3624 //As an attribute this needs to be closed, and that'll happen from
3625 //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms
3626 //between the start and begin, their hyperlinks will be set at that time
3628 m_xCtrlStck
->NewAttr( *m_pPaM
->GetPoint(), aURL
);
3629 return eF_ResT::TEXT
;
3632 static void lcl_ImportTox(SwDoc
&rDoc
, SwPaM
const &rPaM
, const OUString
&rStr
, bool bIdx
)
3634 TOXTypes eTox
= ( !bIdx
) ? TOX_CONTENT
: TOX_INDEX
; // Default
3636 sal_uInt16 nLevel
= 1;
3638 OUString sFieldText
;
3639 WW8ReadFieldParams
aReadParam(rStr
);
3642 const sal_Int32 nRet
= aReadParam
.SkipToNextToken();
3648 if( sFieldText
.isEmpty() )
3650 // PrimaryKey without ":", 2nd after
3651 sFieldText
= aReadParam
.GetResult();
3656 if ( aReadParam
.GoToTokenParam() )
3658 const OUString
sParams( aReadParam
.GetResult() );
3659 if( sParams
[0]!='C' && sParams
[0]!='c' )
3665 if ( aReadParam
.GoToTokenParam() )
3667 const OUString
sParams( aReadParam
.GetResult() );
3668 // if NO String just ignore the \l
3669 if( !sParams
.isEmpty() && sParams
[0]>'0' && sParams
[0]<='9' )
3671 nLevel
= o3tl::narrowing
<sal_uInt16
>(sParams
.toInt32());
3678 OSL_ENSURE( rDoc
.GetTOXTypeCount( eTox
), "Doc.GetTOXTypeCount() == 0 :-(" );
3680 const SwTOXType
* pT
= rDoc
.GetTOXType( eTox
, 0 );
3683 if( eTox
!= TOX_INDEX
)
3684 aM
.SetLevel( nLevel
);
3687 sal_Int32 nFnd
= sFieldText
.indexOf( WW8_TOX_LEVEL_DELIM
);
3688 if( -1 != nFnd
) // it exist levels
3690 aM
.SetPrimaryKey( sFieldText
.copy( 0, nFnd
) );
3691 sal_Int32 nScndFnd
= sFieldText
.indexOf( WW8_TOX_LEVEL_DELIM
, nFnd
+1 );
3692 if( -1 != nScndFnd
)
3694 aM
.SetSecondaryKey( sFieldText
.copy( nFnd
+1, nScndFnd
- nFnd
- 1 ));
3697 sFieldText
= sFieldText
.copy( nFnd
+1 );
3701 if (!sFieldText
.isEmpty())
3703 aM
.SetAlternativeText( sFieldText
);
3704 rDoc
.getIDocumentContentOperations().InsertPoolItem( rPaM
, aM
);
3708 void SwWW8ImplReader::ImportTox( int nFieldId
, const OUString
& aStr
)
3710 bool bIdx
= (nFieldId
!= 9);
3711 lcl_ImportTox(m_rDoc
, *m_pPaM
, aStr
, bIdx
);
3714 void SwWW8ImplReader::Read_FieldVanish( sal_uInt16
, const sal_uInt8
*, short nLen
)
3716 //Meaningless in a style
3717 if (m_pCurrentColl
|| !m_xPlcxMan
)
3720 const int nChunk
= 64; //number of characters to read at one time
3722 // Careful: MEMICMP doesn't work with fieldnames including umlauts!
3723 const static char *aFieldNames
[] = { "\x06""INHALT", "\x02""XE", // dt.
3725 const static sal_uInt8 aFieldId
[] = { 9, 4, 9 };
3729 m_bIgnoreText
= false;
3733 // our method was called from
3734 // ''Skip attributes of field contents'' loop within ReadTextAttr()
3738 m_bIgnoreText
= true;
3739 sal_uInt64 nOldPos
= m_pStrm
->Tell();
3741 WW8_CP nStartCp
= m_xPlcxMan
->Where() + m_xPlcxMan
->GetCpOfs();
3743 OUString sFieldName
;
3744 sal_Int32 nFieldLen
= m_xSBase
->WW8ReadString( *m_pStrm
, sFieldName
, nStartCp
,
3745 nChunk
, m_eStructCharSet
);
3746 nStartCp
+=nFieldLen
;
3749 //If the first chunk did not start with a field start then
3750 //reset the stream position and give up
3751 if( !nFieldLen
|| sFieldName
[nC
]!=0x13 ) // Field Start Mark
3753 // If Field End Mark found
3754 if( nFieldLen
&& sFieldName
[nC
]==0x15 )
3755 m_bIgnoreText
= false;
3756 m_pStrm
->Seek( nOldPos
);
3757 return; // no field found
3761 //If this chunk does not contain a field end, keep reading chunks
3762 //until we find one, or we run out of text,
3765 nFnd
= sFieldName
.indexOf(0x15);
3766 //found field end, we can stop now
3770 nFieldLen
= m_xSBase
->WW8ReadString( *m_pStrm
, sTemp
,
3771 nStartCp
, nChunk
, m_eStructCharSet
);
3773 nStartCp
+=nFieldLen
;
3778 m_pStrm
->Seek( nOldPos
);
3780 //if we have no 0x15 give up, otherwise erase everything from the 0x15
3785 sFieldName
= sFieldName
.copy(0, nFnd
);
3788 while ( sFieldName
[nC
]==' ' )
3791 for( int i
= 0; i
< 3; i
++ )
3793 const char* pName
= aFieldNames
[i
];
3794 const sal_Int32 nNameLen
= static_cast<sal_Int32
>(*pName
++);
3795 if( sFieldName
.matchIgnoreAsciiCaseAsciiL( pName
, nNameLen
, nC
) )
3797 ImportTox( aFieldId
[i
], sFieldName
.copy( nC
+ nNameLen
) );
3798 break; // no duplicates allowed
3801 m_bIgnoreText
= true;
3802 m_pStrm
->Seek( nOldPos
);
3805 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */