summaryrefslogtreecommitdiffstats
path: root/sw/source/filter
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/filter')
-rw-r--r--sw/source/filter/ascii/ascatr.cxx388
-rw-r--r--sw/source/filter/ascii/parasc.cxx521
-rw-r--r--sw/source/filter/ascii/wrtasc.cxx230
-rw-r--r--sw/source/filter/ascii/wrtasc.hxx45
-rw-r--r--sw/source/filter/basflt/docfact.cxx40
-rw-r--r--sw/source/filter/basflt/fltini.cxx725
-rw-r--r--sw/source/filter/basflt/fltshell.cxx1064
-rw-r--r--sw/source/filter/basflt/iodetect.cxx433
-rw-r--r--sw/source/filter/basflt/shellio.cxx940
-rw-r--r--sw/source/filter/docx/swdocxreader.cxx251
-rw-r--r--sw/source/filter/docx/swdocxreader.hxx42
-rw-r--r--sw/source/filter/html/README42
-rw-r--r--sw/source/filter/html/SwAppletImpl.cxx194
-rw-r--r--sw/source/filter/html/css1atr.cxx3671
-rw-r--r--sw/source/filter/html/css1atr.hxx29
-rw-r--r--sw/source/filter/html/css1kywd.hxx224
-rw-r--r--sw/source/filter/html/htmlatr.cxx3467
-rw-r--r--sw/source/filter/html/htmlatr.hxx21
-rw-r--r--sw/source/filter/html/htmlbas.cxx330
-rw-r--r--sw/source/filter/html/htmlcss1.cxx2331
-rw-r--r--sw/source/filter/html/htmlctxt.cxx806
-rw-r--r--sw/source/filter/html/htmldrawreader.cxx572
-rw-r--r--sw/source/filter/html/htmldrawwriter.cxx284
-rw-r--r--sw/source/filter/html/htmlfld.cxx651
-rw-r--r--sw/source/filter/html/htmlfld.hxx82
-rw-r--r--sw/source/filter/html/htmlfldw.cxx582
-rw-r--r--sw/source/filter/html/htmlfly.cxx84
-rw-r--r--sw/source/filter/html/htmlfly.hxx128
-rw-r--r--sw/source/filter/html/htmlflyt.cxx487
-rw-r--r--sw/source/filter/html/htmlflywriter.cxx2257
-rw-r--r--sw/source/filter/html/htmlform.cxx2455
-rw-r--r--sw/source/filter/html/htmlform.hxx30
-rw-r--r--sw/source/filter/html/htmlforw.cxx1337
-rw-r--r--sw/source/filter/html/htmlftn.cxx598
-rw-r--r--sw/source/filter/html/htmlgrin.cxx1574
-rw-r--r--sw/source/filter/html/htmlnum.cxx93
-rw-r--r--sw/source/filter/html/htmlnum.hxx125
-rw-r--r--sw/source/filter/html/htmlnumreader.cxx620
-rw-r--r--sw/source/filter/html/htmlnumwriter.cxx336
-rw-r--r--sw/source/filter/html/htmlplug.cxx1765
-rw-r--r--sw/source/filter/html/htmlreqifreader.cxx652
-rw-r--r--sw/source/filter/html/htmlreqifreader.hxx39
-rw-r--r--sw/source/filter/html/htmlsect.cxx825
-rw-r--r--sw/source/filter/html/htmltab.cxx5220
-rw-r--r--sw/source/filter/html/htmltabw.cxx1156
-rw-r--r--sw/source/filter/html/parcss1.cxx1388
-rw-r--r--sw/source/filter/html/parcss1.hxx264
-rw-r--r--sw/source/filter/html/svxcss1.cxx3153
-rw-r--r--sw/source/filter/html/svxcss1.hxx314
-rw-r--r--sw/source/filter/html/swcss1.hxx210
-rw-r--r--sw/source/filter/html/swhtml.cxx5648
-rw-r--r--sw/source/filter/html/swhtml.hxx1069
-rw-r--r--sw/source/filter/html/wrthtml.cxx1694
-rw-r--r--sw/source/filter/html/wrthtml.hxx748
-rw-r--r--sw/source/filter/inc/IndexingExport.hxx32
-rw-r--r--sw/source/filter/inc/IndexingExportFilter.hxx68
-rw-r--r--sw/source/filter/inc/fltini.hxx72
-rw-r--r--sw/source/filter/inc/fltshell.hxx338
-rw-r--r--sw/source/filter/inc/msfilter.hxx409
-rw-r--r--sw/source/filter/inc/rtf.hxx49
-rw-r--r--sw/source/filter/inc/wrt_fn.hxx57
-rw-r--r--sw/source/filter/inc/wrtswtbl.hxx308
-rw-r--r--sw/source/filter/inc/wwstyles.hxx177
-rw-r--r--sw/source/filter/indexing/IndexingExport.cxx203
-rw-r--r--sw/source/filter/indexing/IndexingExportFilter.cxx61
-rw-r--r--sw/source/filter/rtf/swparrtf.cxx212
-rw-r--r--sw/source/filter/writer/writer.cxx505
-rw-r--r--sw/source/filter/writer/wrt_fn.cxx158
-rw-r--r--sw/source/filter/writer/wrtswtbl.cxx866
-rw-r--r--sw/source/filter/ww8/README-rtf.txt244
-rw-r--r--sw/source/filter/ww8/WW8FFData.cxx158
-rw-r--r--sw/source/filter/ww8/WW8FFData.hxx88
-rw-r--r--sw/source/filter/ww8/WW8FibData.cxx43
-rw-r--r--sw/source/filter/ww8/WW8FibData.hxx45
-rw-r--r--sw/source/filter/ww8/WW8Sttbf.cxx97
-rw-r--r--sw/source/filter/ww8/WW8Sttbf.hxx124
-rw-r--r--sw/source/filter/ww8/WW8TableInfo.cxx1460
-rw-r--r--sw/source/filter/ww8/WW8TableInfo.hxx352
-rw-r--r--sw/source/filter/ww8/attributeoutputbase.hxx711
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx10247
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.hxx1200
-rw-r--r--sw/source/filter/ww8/docxexport.cxx2179
-rw-r--r--sw/source/filter/ww8/docxexport.hxx333
-rw-r--r--sw/source/filter/ww8/docxexportfilter.cxx139
-rw-r--r--sw/source/filter/ww8/docxexportfilter.hxx56
-rw-r--r--sw/source/filter/ww8/docxfootnotes.hxx91
-rw-r--r--sw/source/filter/ww8/docxhelper.hxx29
-rw-r--r--sw/source/filter/ww8/docxsdrexport.cxx2198
-rw-r--r--sw/source/filter/ww8/docxsdrexport.hxx109
-rw-r--r--sw/source/filter/ww8/docxtableexport.cxx894
-rw-r--r--sw/source/filter/ww8/docxtablestyleexport.cxx736
-rw-r--r--sw/source/filter/ww8/docxtablestyleexport.hxx42
-rw-r--r--sw/source/filter/ww8/escher.hxx182
-rw-r--r--sw/source/filter/ww8/fields.cxx142
-rw-r--r--sw/source/filter/ww8/fields.hxx48
-rw-r--r--sw/source/filter/ww8/needed_cast.hxx54
-rw-r--r--sw/source/filter/ww8/rtfattributeoutput.cxx4622
-rw-r--r--sw/source/filter/ww8/rtfattributeoutput.hxx696
-rw-r--r--sw/source/filter/ww8/rtfexport.cxx1541
-rw-r--r--sw/source/filter/ww8/rtfexport.hxx236
-rw-r--r--sw/source/filter/ww8/rtfexportfilter.cxx120
-rw-r--r--sw/source/filter/ww8/rtfexportfilter.hxx72
-rw-r--r--sw/source/filter/ww8/rtfsdrexport.cxx737
-rw-r--r--sw/source/filter/ww8/rtfsdrexport.hxx110
-rw-r--r--sw/source/filter/ww8/rtfstringbuffer.cxx88
-rw-r--r--sw/source/filter/ww8/rtfstringbuffer.hxx69
-rw-r--r--sw/source/filter/ww8/sortedarray.hxx65
-rw-r--r--sw/source/filter/ww8/sprmids.hxx628
-rw-r--r--sw/source/filter/ww8/styles.cxx179
-rw-r--r--sw/source/filter/ww8/types.hxx45
-rw-r--r--sw/source/filter/ww8/writerhelper.cxx913
-rw-r--r--sw/source/filter/ww8/writerhelper.hxx620
-rw-r--r--sw/source/filter/ww8/writerwordglue.cxx1087
-rw-r--r--sw/source/filter/ww8/writerwordglue.hxx150
-rw-r--r--sw/source/filter/ww8/wrtw8esh.cxx3063
-rw-r--r--sw/source/filter/ww8/wrtw8nds.cxx3804
-rw-r--r--sw/source/filter/ww8/wrtw8num.cxx645
-rw-r--r--sw/source/filter/ww8/wrtw8sty.cxx2725
-rw-r--r--sw/source/filter/ww8/wrtww8.cxx4626
-rw-r--r--sw/source/filter/ww8/wrtww8.hxx1683
-rw-r--r--sw/source/filter/ww8/wrtww8gr.cxx872
-rw-r--r--sw/source/filter/ww8/ww8atr.cxx6020
-rw-r--r--sw/source/filter/ww8/ww8attributeoutput.hxx518
-rw-r--r--sw/source/filter/ww8/ww8glsy.cxx247
-rw-r--r--sw/source/filter/ww8/ww8glsy.hxx91
-rw-r--r--sw/source/filter/ww8/ww8graf.cxx3244
-rw-r--r--sw/source/filter/ww8/ww8graf.hxx101
-rw-r--r--sw/source/filter/ww8/ww8graf2.cxx781
-rw-r--r--sw/source/filter/ww8/ww8par.cxx6759
-rw-r--r--sw/source/filter/ww8/ww8par.hxx1974
-rw-r--r--sw/source/filter/ww8/ww8par2.cxx4606
-rw-r--r--sw/source/filter/ww8/ww8par2.hxx305
-rw-r--r--sw/source/filter/ww8/ww8par3.cxx2589
-rw-r--r--sw/source/filter/ww8/ww8par4.cxx547
-rw-r--r--sw/source/filter/ww8/ww8par5.cxx3802
-rw-r--r--sw/source/filter/ww8/ww8par6.cxx6282
-rw-r--r--sw/source/filter/ww8/ww8scan.cxx8540
-rw-r--r--sw/source/filter/ww8/ww8scan.hxx1888
-rw-r--r--sw/source/filter/ww8/ww8struc.hxx1159
-rw-r--r--sw/source/filter/ww8/ww8toolbar.cxx1004
-rw-r--r--sw/source/filter/ww8/ww8toolbar.hxx342
-rw-r--r--sw/source/filter/xml/XMLRedlineImportHelper.cxx842
-rw-r--r--sw/source/filter/xml/XMLRedlineImportHelper.hxx134
-rw-r--r--sw/source/filter/xml/swxml.cxx1017
-rw-r--r--sw/source/filter/xml/wrtxml.cxx586
-rw-r--r--sw/source/filter/xml/wrtxml.hxx87
-rw-r--r--sw/source/filter/xml/xmlbrsh.cxx194
-rw-r--r--sw/source/filter/xml/xmlbrshe.hxx41
-rw-r--r--sw/source/filter/xml/xmlbrshi.hxx77
-rw-r--r--sw/source/filter/xml/xmlexp.cxx623
-rw-r--r--sw/source/filter/xml/xmlexp.hxx146
-rw-r--r--sw/source/filter/xml/xmlexpit.cxx1137
-rw-r--r--sw/source/filter/xml/xmlexpit.hxx100
-rw-r--r--sw/source/filter/xml/xmlfmt.cxx1079
-rw-r--r--sw/source/filter/xml/xmlfmte.cxx389
-rw-r--r--sw/source/filter/xml/xmlfonte.cxx143
-rw-r--r--sw/source/filter/xml/xmlimp.cxx1924
-rw-r--r--sw/source/filter/xml/xmlimp.hxx187
-rw-r--r--sw/source/filter/xml/xmlimpit.cxx1056
-rw-r--r--sw/source/filter/xml/xmlimpit.hxx93
-rw-r--r--sw/source/filter/xml/xmlitem.cxx104
-rw-r--r--sw/source/filter/xml/xmlitem.hxx64
-rw-r--r--sw/source/filter/xml/xmliteme.cxx246
-rw-r--r--sw/source/filter/xml/xmlitemi.cxx278
-rw-r--r--sw/source/filter/xml/xmlitemm.cxx286
-rw-r--r--sw/source/filter/xml/xmlithlp.cxx333
-rw-r--r--sw/source/filter/xml/xmlithlp.hxx70
-rw-r--r--sw/source/filter/xml/xmlitmap.hxx87
-rw-r--r--sw/source/filter/xml/xmlitmpr.cxx39
-rw-r--r--sw/source/filter/xml/xmlmeta.cxx175
-rw-r--r--sw/source/filter/xml/xmlscript.cxx37
-rw-r--r--sw/source/filter/xml/xmltble.cxx1266
-rw-r--r--sw/source/filter/xml/xmltbli.cxx2757
-rw-r--r--sw/source/filter/xml/xmltbli.hxx214
-rw-r--r--sw/source/filter/xml/xmltext.cxx77
-rw-r--r--sw/source/filter/xml/xmltexte.cxx549
-rw-r--r--sw/source/filter/xml/xmltexte.hxx84
-rw-r--r--sw/source/filter/xml/xmltexti.cxx997
-rw-r--r--sw/source/filter/xml/xmltexti.hxx111
-rw-r--r--sw/source/filter/xml/zorder.hxx78
180 files changed, 175652 insertions, 0 deletions
diff --git a/sw/source/filter/ascii/ascatr.cxx b/sw/source/filter/ascii/ascatr.cxx
new file mode 100644
index 0000000000..c4f4d12490
--- /dev/null
+++ b/sw/source/filter/ascii/ascatr.cxx
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <tools/stream.hxx>
+#include <comphelper/string.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include "wrtasc.hxx"
+#include <txatbase.hxx>
+#include <txtfld.hxx>
+#include <fmtftn.hxx>
+#include <fmtfld.hxx>
+#include <fldbas.hxx>
+#include <ftninfo.hxx>
+#include <numrule.hxx>
+
+#include <algorithm>
+
+/*
+ * This file contains all output functions of the ASCII-Writer;
+ * For all nodes, attributes, formats and chars.
+ */
+
+namespace {
+
+class SwASC_AttrIter
+{
+ SwASCWriter& m_rWrt;
+ const SwTextNode& m_rNd;
+ sal_Int32 m_nCurrentSwPos;
+
+ sal_Int32 SearchNext( sal_Int32 nStartPos );
+
+public:
+ SwASC_AttrIter( SwASCWriter& rWrt, const SwTextNode& rNd, sal_Int32 nStt );
+
+ void NextPos() { m_nCurrentSwPos = SearchNext(m_nCurrentSwPos + 1); }
+
+ sal_Int32 WhereNext() const { return m_nCurrentSwPos; }
+
+ bool OutAttr( sal_Int32 nSwPos );
+};
+
+}
+
+SwASC_AttrIter::SwASC_AttrIter(SwASCWriter& rWr, const SwTextNode& rTextNd, sal_Int32 nStt)
+ : m_rWrt(rWr)
+ , m_rNd(rTextNd)
+ , m_nCurrentSwPos(0)
+{
+ m_nCurrentSwPos = SearchNext(nStt + 1);
+}
+
+sal_Int32 SwASC_AttrIter::SearchNext( sal_Int32 nStartPos )
+{
+ sal_Int32 nMinPos = SAL_MAX_INT32;
+ const SwpHints* pTextAttrs = m_rNd.GetpSwpHints();
+ if( pTextAttrs )
+ {
+ // TODO: This can be optimized, if we make use of the fact that the TextAttrs
+ // are sorted by starting position. We would need to remember two indices, however.
+ for ( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ if ( pHt->HasDummyChar() )
+ {
+ sal_Int32 nPos = pHt->GetStart();
+
+ if( nPos >= nStartPos && nPos <= nMinPos )
+ nMinPos = nPos;
+
+ if( ( ++nPos ) >= nStartPos && nPos < nMinPos )
+ nMinPos = nPos;
+ }
+ else if ( pHt->HasContent() )
+ {
+ const sal_Int32 nHintStart = pHt->GetStart();
+ if ( nHintStart >= nStartPos && nHintStart <= nMinPos )
+ {
+ nMinPos = nHintStart;
+ }
+
+ const sal_Int32 nHintEnd = pHt->End() ? *pHt->End() : COMPLETE_STRING;
+ if ( nHintEnd >= nStartPos && nHintEnd < nMinPos )
+ {
+ nMinPos = nHintEnd;
+ }
+ }
+ }
+ }
+ return nMinPos;
+}
+
+bool SwASC_AttrIter::OutAttr( sal_Int32 nSwPos )
+{
+ bool bRet = false;
+ const SwpHints* pTextAttrs = m_rNd.GetpSwpHints();
+ if( pTextAttrs )
+ {
+ for( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ if ( ( pHt->HasDummyChar()
+ || pHt->HasContent() )
+ && nSwPos == pHt->GetStart() )
+ {
+ bRet = true;
+ OUString sOut;
+ switch( pHt->Which() )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ sOut = static_txtattr_cast<SwTextField const*>(pHt)
+ ->GetFormatField().GetField()->ExpandField(true, nullptr);
+ break;
+
+ case RES_TXTATR_FTN:
+ {
+ const SwFormatFootnote& rFootnote = pHt->GetFootnote();
+ if( !rFootnote.GetNumStr().isEmpty() )
+ sOut = rFootnote.GetNumStr();
+ else if( rFootnote.IsEndNote() )
+ sOut = m_rWrt.m_pDoc->GetEndNoteInfo().m_aFormat.GetNumStr(
+ rFootnote.GetNumber());
+ else
+ sOut = m_rWrt.m_pDoc->GetFootnoteInfo().m_aFormat.GetNumStr(
+ rFootnote.GetNumber());
+ }
+ break;
+ case RES_TXTATR_LINEBREAK:
+ {
+ // Downgrade the clearing break to a simple linebreak.
+ sOut = OUStringChar(GetCharOfTextAttr(*pHt));
+ break;
+ }
+ }
+ if( !sOut.isEmpty() )
+ m_rWrt.Strm().WriteUnicodeOrByteText(sOut);
+ }
+ else if( nSwPos < pHt->GetStart() )
+ break;
+ }
+ }
+ return bRet;
+}
+
+namespace {
+
+class SwASC_RedlineIter
+{
+private:
+ SwTextNode const& m_rNode;
+ IDocumentRedlineAccess const& m_rIDRA;
+ SwRedlineTable::size_type m_nextRedline;
+
+public:
+ SwASC_RedlineIter(SwASCWriter const& rWriter, SwTextNode const& rNode)
+ : m_rNode(rNode)
+ , m_rIDRA(rNode.GetDoc().getIDocumentRedlineAccess())
+ , m_nextRedline(rWriter.m_bHideDeleteRedlines
+ ? m_rIDRA.GetRedlinePos(m_rNode, RedlineType::Delete)
+ : SwRedlineTable::npos)
+ {
+ }
+
+ bool CheckNodeDeleted()
+ {
+ if (m_nextRedline == SwRedlineTable::npos)
+ {
+ return false;
+ }
+ SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]);
+ return pRedline->Start()->GetNodeIndex() < m_rNode.GetIndex()
+ && m_rNode.GetIndex() < pRedline->End()->GetNodeIndex();
+ }
+
+ std::pair<sal_Int32, sal_Int32> GetNextRedlineSkip()
+ {
+ sal_Int32 nRedlineStart(COMPLETE_STRING);
+ sal_Int32 nRedlineEnd(COMPLETE_STRING);
+ for ( ; m_nextRedline < m_rIDRA.GetRedlineTable().size(); ++m_nextRedline)
+ {
+ SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]);
+ if (pRedline->GetType() != RedlineType::Delete)
+ {
+ continue;
+ }
+ auto [pStart, pEnd] = pRedline->StartEnd(); // SwPosition*
+ if (m_rNode.GetIndex() < pStart->GetNodeIndex())
+ {
+ m_nextRedline = SwRedlineTable::npos;
+ break; // done
+ }
+ if (nRedlineStart == COMPLETE_STRING)
+ {
+ nRedlineStart = pStart->GetNodeIndex() == m_rNode.GetIndex()
+ ? pStart->GetContentIndex()
+ : 0;
+ }
+ else
+ {
+ if (pStart->GetContentIndex() != nRedlineEnd)
+ {
+ assert(nRedlineEnd < pStart->GetContentIndex());
+ break; // no increment, revisit it next call
+ }
+ }
+ nRedlineEnd = pEnd->GetNodeIndex() == m_rNode.GetIndex()
+ ? pEnd->GetContentIndex()
+ : COMPLETE_STRING;
+ }
+ return std::make_pair(nRedlineStart, nRedlineEnd);
+ }
+};
+
+}
+
+// Output of the node
+
+static Writer& OutASC_SwTextNode( Writer& rWrt, SwContentNode& rNode )
+{
+ const SwTextNode& rNd = static_cast<SwTextNode&>(rNode);
+
+ sal_Int32 nStrPos = rWrt.m_pCurrentPam->GetPoint()->GetContentIndex();
+ const sal_Int32 nNodeEnd = rNd.Len();
+ sal_Int32 nEnd = nNodeEnd;
+ bool bLastNd = rWrt.m_pCurrentPam->GetPoint()->GetNode() == rWrt.m_pCurrentPam->GetMark()->GetNode();
+ if( bLastNd )
+ nEnd = rWrt.m_pCurrentPam->GetMark()->GetContentIndex();
+
+ bool bIsOneParagraph = rWrt.m_pOrigPam->Start()->GetNode() == rWrt.m_pOrigPam->End()->GetNode() && !getenv("SW_ASCII_COPY_NUMBERING");
+
+ SwASC_AttrIter aAttrIter( static_cast<SwASCWriter&>(rWrt), rNd, nStrPos );
+ SwASC_RedlineIter redlineIter(static_cast<SwASCWriter&>(rWrt), rNd);
+
+ if (redlineIter.CheckNodeDeleted())
+ {
+ return rWrt;
+ }
+
+ const SwNumRule* pNumRule = rNd.GetNumRule();
+ if (pNumRule && !nStrPos && rWrt.m_bExportParagraphNumbering && !bIsOneParagraph)
+ {
+ bool bIsOutlineNumRule = pNumRule == rNd.GetDoc().GetOutlineNumRule();
+
+ // indent each numbering level by 4 spaces
+ OUString level;
+ if (!bIsOutlineNumRule)
+ {
+ for (int i = 0; i <= rNd.GetActualListLevel(); ++i)
+ level += " ";
+ }
+
+ // set up bullets or numbering
+ OUString numString(rNd.GetNumString());
+ if (numString.isEmpty() && !bIsOutlineNumRule)
+ {
+ if (rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet())
+ numString = " ";
+ else if (rNd.HasBullet())
+ numString = OUString(numfunc::GetBulletChar(rNd.GetActualListLevel()));
+ else if (!rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet())
+ numString = " ";
+ }
+
+ if (!level.isEmpty() || !numString.isEmpty())
+ rWrt.Strm().WriteUnicodeOrByteText(Concat2View(level + numString + " "));
+ }
+
+ OUString aStr( rNd.GetText() );
+ if( rWrt.m_bASCII_ParaAsBlank )
+ aStr = aStr.replace(0x0A, ' ');
+
+ const bool bExportSoftHyphens = RTL_TEXTENCODING_UCS2 == rWrt.GetAsciiOptions().GetCharSet() ||
+ RTL_TEXTENCODING_UTF8 == rWrt.GetAsciiOptions().GetCharSet();
+
+ std::pair<sal_Int32, sal_Int32> curRedline(redlineIter.GetNextRedlineSkip());
+ for (;;) {
+ const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
+
+ bool isOutAttr(false);
+ if (nStrPos < curRedline.first || curRedline.second <= nStrPos)
+ {
+ isOutAttr = aAttrIter.OutAttr(nStrPos);
+ }
+
+ if (!isOutAttr)
+ {
+ OUStringBuffer buf;
+ while (true)
+ {
+ if (nNextAttr <= curRedline.first)
+ {
+ buf.append(aStr.subView(nStrPos, nNextAttr - nStrPos));
+ break;
+ }
+ else if (nStrPos < curRedline.second)
+ {
+ if (nStrPos < curRedline.first)
+ {
+ buf.append(aStr.subView(nStrPos, curRedline.first - nStrPos));
+ }
+ if (curRedline.second <= nNextAttr)
+ {
+ nStrPos = curRedline.second;
+ curRedline = redlineIter.GetNextRedlineSkip();
+ }
+ else
+ {
+ nStrPos = nNextAttr;
+ break;
+ }
+ }
+ else
+ {
+ curRedline = redlineIter.GetNextRedlineSkip();
+ }
+ }
+ OUString aOutStr(buf.makeStringAndClear());
+ if ( !bExportSoftHyphens )
+ aOutStr = aOutStr.replaceAll(OUStringChar(CHAR_SOFTHYPHEN), "");
+
+ // all INWORD should be already removed by OutAttr
+ // but the field-marks are not attributes so filter those
+ static sal_Unicode const forbidden [] = {
+ CH_TXT_ATR_INPUTFIELDSTART,
+ CH_TXT_ATR_INPUTFIELDEND,
+ CH_TXT_ATR_FORMELEMENT,
+ CH_TXT_ATR_FIELDSTART,
+ CH_TXT_ATR_FIELDSEP,
+ CH_TXT_ATR_FIELDEND,
+ CH_TXTATR_BREAKWORD,
+ 0
+ };
+ aOutStr = comphelper::string::removeAny(aOutStr, forbidden);
+
+ rWrt.Strm().WriteUnicodeOrByteText( aOutStr );
+ }
+ nStrPos = nNextAttr;
+ if (nStrPos >= nEnd)
+ {
+ break;
+ }
+ aAttrIter.NextPos();
+ }
+
+ if( !bLastNd ||
+ ( ( !rWrt.m_bWriteClipboardDoc && !rWrt.m_bASCII_NoLastLineEnd )
+ && !nStrPos && nEnd == nNodeEnd ) )
+ rWrt.Strm().WriteUnicodeOrByteText( static_cast<SwASCWriter&>(rWrt).GetLineEnd());
+
+ return rWrt;
+}
+
+/*
+ * Create the table for the ASCII function pointers to the output
+ * function.
+ * There are local structures that only need to be known to the ASCII DLL.
+ */
+
+SwNodeFnTab aASCNodeFnTab = {
+/* RES_TXTNODE */ OutASC_SwTextNode,
+/* RES_GRFNODE */ nullptr,
+/* RES_OLENODE */ nullptr
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ascii/parasc.cxx b/sw/source/filter/ascii/parasc.cxx
new file mode 100644
index 0000000000..b4e191df62
--- /dev/null
+++ b/sw/source/filter/ascii/parasc.cxx
@@ -0,0 +1,521 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+
+#include <tools/stream.hxx>
+#include <hintids.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <svl/languageoptions.hxx>
+#include <shellio.hxx>
+#include <doc.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <breakit.hxx>
+#include <swerror.h>
+#include <strings.hrc>
+#include <mdiexp.hxx>
+#include <poolfmt.hxx>
+#include <iodetect.hxx>
+
+#include <vcl/metric.hxx>
+#include <osl/diagnose.h>
+
+#define ASC_BUFFLEN 4096
+
+namespace {
+
+class SwASCIIParser
+{
+ SwDoc& m_rDoc;
+ std::optional<SwPaM> m_oPam;
+ SvStream& m_rInput;
+ std::unique_ptr<char[]> m_pArr;
+ const SwAsciiOptions& m_rOpt;
+ SwAsciiOptions m_usedAsciiOptions;
+ std::optional<SfxItemSet> m_oItemSet;
+ tools::Long m_nFileSize;
+ SvtScriptType m_nScript;
+ bool m_bNewDoc;
+
+ ErrCode ReadChars();
+ void InsertText( const OUString& rStr );
+
+ SwASCIIParser(const SwASCIIParser&) = delete;
+ SwASCIIParser& operator=(const SwASCIIParser&) = delete;
+
+public:
+ SwASCIIParser( SwDoc& rD, const SwPaM& rCursor, SvStream& rIn,
+ bool bReadNewDoc, const SwAsciiOptions& rOpts );
+
+ ErrCode CallParser();
+ const SwAsciiOptions& GetUsedAsciiOptions() const { return m_usedAsciiOptions; }
+};
+
+}
+
+// Call for the general reader interface
+ErrCodeMsg AsciiReader::Read( SwDoc& rDoc, const OUString&, SwPaM &rPam, const OUString & )
+{
+ if( !m_pStream )
+ {
+ OSL_ENSURE( false, "ASCII read without a stream" );
+ return ERR_SWG_READ_ERROR;
+ }
+
+ ErrCode nRet;
+ {
+ SwASCIIParser aParser( rDoc, rPam, *m_pStream,
+ !m_bInsertMode, m_aOption.GetASCIIOpts() );
+ nRet = aParser.CallParser();
+
+ OUString optionsString;
+ aParser.GetUsedAsciiOptions().WriteUserData(optionsString);
+
+ if(m_pMedium != nullptr)
+ m_pMedium->GetItemSet().Put(SfxStringItem(SID_FILE_FILTEROPTIONS, optionsString));
+ }
+ // after Read reset the options
+ m_aOption.ResetASCIIOpts();
+ return nRet;
+}
+
+SwASCIIParser::SwASCIIParser(SwDoc& rD, const SwPaM& rCursor, SvStream& rIn, bool bReadNewDoc,
+ const SwAsciiOptions& rOpts)
+ : m_rDoc(rD)
+ , m_rInput(rIn)
+ , m_rOpt(rOpts)
+ , m_usedAsciiOptions(rOpts)
+ , m_nFileSize(0)
+ , m_nScript(SvtScriptType::NONE)
+ , m_bNewDoc(bReadNewDoc)
+{
+ m_oPam.emplace(*rCursor.GetPoint());
+ m_pArr.reset(new char[ASC_BUFFLEN + 2]);
+
+ m_oItemSet.emplace(
+ m_rDoc.GetAttrPool(),
+ svl::Items<RES_CHRATR_FONT, RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_LANGUAGE>);
+
+ // set defaults from the options
+ if (m_rOpt.GetLanguage())
+ {
+ SvxLanguageItem aLang(m_rOpt.GetLanguage(), RES_CHRATR_LANGUAGE);
+ m_oItemSet->Put(aLang);
+ aLang.SetWhich(RES_CHRATR_CJK_LANGUAGE);
+ m_oItemSet->Put(aLang);
+ aLang.SetWhich(RES_CHRATR_CTL_LANGUAGE);
+ m_oItemSet->Put(aLang);
+ }
+ if (m_rOpt.GetFontName().isEmpty())
+ return;
+
+ vcl::Font aTextFont(m_rOpt.GetFontName(), Size(0, 10));
+ if (m_rDoc.getIDocumentDeviceAccess().getPrinter(false))
+ aTextFont = m_rDoc.getIDocumentDeviceAccess().getPrinter(false)->GetFontMetric(aTextFont);
+ SvxFontItem aFont( aTextFont.GetFamilyType(), aTextFont.GetFamilyName(),
+ OUString(), aTextFont.GetPitch(), aTextFont.GetCharSet(), RES_CHRATR_FONT );
+ m_oItemSet->Put(aFont);
+ aFont.SetWhich(RES_CHRATR_CJK_FONT);
+ m_oItemSet->Put(aFont);
+ aFont.SetWhich(RES_CHRATR_CTL_FONT);
+ m_oItemSet->Put(aFont);
+}
+
+// Calling the parser
+ErrCode SwASCIIParser::CallParser()
+{
+ m_rInput.ResetError();
+ m_nFileSize = m_rInput.TellEnd();
+ m_rInput.Seek(STREAM_SEEK_TO_BEGIN);
+ m_rInput.ResetError();
+
+ ::StartProgress(STR_STATSTR_W4WREAD, 0, m_nFileSize, m_rDoc.GetDocShell());
+
+ std::optional<SwPaM> pInsPam;
+ sal_Int32 nSttContent = 0;
+ if (!m_bNewDoc)
+ {
+ const SwNode& rTmp = m_oPam->GetPoint()->GetNode();
+ pInsPam.emplace( rTmp, rTmp, SwNodeOffset(0), SwNodeOffset(-1) );
+ nSttContent = m_oPam->GetPoint()->GetContentIndex();
+ }
+
+ SwTextFormatColl *pColl = nullptr;
+
+ if (m_bNewDoc)
+ {
+ pColl = m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_PRE,
+ false);
+ if (!pColl)
+ pColl = m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,
+ false);
+ if (pColl)
+ m_rDoc.SetTextFormatColl(*m_oPam, pColl);
+ }
+
+ ErrCode nError = ReadChars();
+
+ if (m_oItemSet)
+ {
+ // set only the attribute, for scanned scripts.
+ if (!(SvtScriptType::LATIN & m_nScript))
+ {
+ m_oItemSet->ClearItem(RES_CHRATR_FONT);
+ m_oItemSet->ClearItem(RES_CHRATR_LANGUAGE);
+ }
+ if (!(SvtScriptType::ASIAN & m_nScript))
+ {
+ m_oItemSet->ClearItem(RES_CHRATR_CJK_FONT);
+ m_oItemSet->ClearItem(RES_CHRATR_CJK_LANGUAGE);
+ }
+ if (!(SvtScriptType::COMPLEX & m_nScript))
+ {
+ m_oItemSet->ClearItem(RES_CHRATR_CTL_FONT);
+ m_oItemSet->ClearItem(RES_CHRATR_CTL_LANGUAGE);
+ }
+ if (m_oItemSet->Count())
+ {
+ if (m_bNewDoc)
+ {
+ if (pColl)
+ {
+ // Using the pool defaults for the font causes significant
+ // trouble for the HTML filter, because it is not able
+ // to export the pool defaults (or to be more precise:
+ // the HTML filter is not able to detect whether a pool
+ // default has changed or not. Even a comparison with the
+ // HTML template does not work, because the defaults are
+ // not copied when a new doc is created. The result of
+ // comparing pool defaults therefore would be that the
+ // defaults are exported always if the have changed for
+ // text documents in general. That's not sensible, as well
+ // as it is not sensible to export them always.
+ sal_uInt16 aWhichIds[4] =
+ {
+ RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CTL_FONT, 0
+ };
+ sal_uInt16 *pWhichIds = aWhichIds;
+ while (*pWhichIds)
+ {
+ const SfxPoolItem *pItem;
+ if (SfxItemState::SET
+ == m_oItemSet->GetItemState(*pWhichIds, false, &pItem))
+ {
+ pColl->SetFormatAttr( *pItem );
+ m_oItemSet->ClearItem(*pWhichIds);
+ }
+ ++pWhichIds;
+ }
+ }
+ if (m_oItemSet->Count())
+ m_rDoc.SetDefault(*m_oItemSet);
+ }
+ else if( pInsPam )
+ {
+ // then set over the insert range the defined attributes
+ *pInsPam->GetMark() = *m_oPam->GetPoint();
+ pInsPam->GetPoint()->Assign(pInsPam->GetPoint()->GetNode(), SwNodeOffset(1),
+ nSttContent );
+
+ // !!!!!
+ OSL_ENSURE( false, "Have to change - hard attr. to para. style" );
+ m_rDoc.getIDocumentContentOperations().InsertItemSet(*pInsPam, *m_oItemSet);
+ }
+ }
+ m_oItemSet.reset();
+ }
+
+ pInsPam.reset();
+
+ ::EndProgress(m_rDoc.GetDocShell());
+ return nError;
+}
+
+ErrCode SwASCIIParser::ReadChars()
+{
+ sal_Unicode *pStt = nullptr, *pEnd = nullptr, *pLastStt = nullptr;
+ tools::Long nReadCnt = 0, nLineLen = 0;
+ sal_Unicode cLastCR = 0;
+ bool bSwapUnicode = false;
+
+ const SwAsciiOptions* pUseMe = &m_rOpt;
+ SwAsciiOptions aEmpty;
+ if (m_nFileSize >= 2 && aEmpty.GetFontName() == m_rOpt.GetFontName()
+ && aEmpty.GetCharSet() == m_rOpt.GetCharSet()
+ && aEmpty.GetLanguage() == m_rOpt.GetLanguage()
+ && aEmpty.GetParaFlags() == m_rOpt.GetParaFlags())
+ {
+ sal_Size nLen, nOrig;
+ nOrig = nLen = m_rInput.ReadBytes(m_pArr.get(), ASC_BUFFLEN);
+ rtl_TextEncoding eCharSet;
+ LineEnd eLineEnd;
+ bool bHasBom;
+ const bool bRet
+ = SwIoSystem::IsDetectableText(m_pArr.get(), nLen, &eCharSet,
+ &bSwapUnicode, &eLineEnd, &bHasBom);
+ if (!bRet)
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ OSL_ENSURE(bRet, "Autodetect of text import without nag dialog must have failed");
+ if (bRet && eCharSet != RTL_TEXTENCODING_DONTKNOW)
+ {
+ aEmpty.SetCharSet(eCharSet);
+ aEmpty.SetParaFlags(eLineEnd);
+ aEmpty.SetIncludeBOM(bHasBom);
+ m_rInput.SeekRel(-(tools::Long(nLen)));
+ }
+ else
+ m_rInput.SeekRel(-(tools::Long(nOrig)));
+ pUseMe=&aEmpty;
+ }
+ m_usedAsciiOptions = *pUseMe;
+
+ rtl_TextToUnicodeConverter hConverter=nullptr;
+ rtl_TextToUnicodeContext hContext=nullptr;
+ rtl_TextEncoding currentCharSet = pUseMe->GetCharSet();
+ if (RTL_TEXTENCODING_UCS2 != currentCharSet)
+ {
+ if( currentCharSet == RTL_TEXTENCODING_DONTKNOW )
+ currentCharSet = RTL_TEXTENCODING_ASCII_US;
+ hConverter = rtl_createTextToUnicodeConverter( currentCharSet );
+ OSL_ENSURE( hConverter, "no string convert available" );
+ if (!hConverter)
+ return ErrCode(ErrCodeArea::Sw, ErrCodeClass::Read, 0);
+ bSwapUnicode = false;
+ hContext = rtl_createTextToUnicodeContext( hConverter );
+ }
+ else if (pUseMe != &aEmpty) //Already successfully figured out type
+ {
+ m_rInput.StartReadingUnicodeText(currentCharSet);
+ bSwapUnicode = m_rInput.IsEndianSwap();
+ }
+
+ std::unique_ptr<sal_Unicode[]> aWork;
+ sal_Size nArrOffset = 0;
+
+ do {
+ if( pStt >= pEnd )
+ {
+ if( pLastStt != pStt )
+ InsertText( OUString( pLastStt ));
+
+ // Read a new block
+ sal_Size lGCount;
+ if (ERRCODE_NONE != m_rInput.GetError()
+ || 0
+ == (lGCount = m_rInput.ReadBytes(m_pArr.get() + nArrOffset,
+ ASC_BUFFLEN - nArrOffset)))
+ break; // break from the while loop
+
+ /*
+ If there was some unconverted bytes on the last cycle then they
+ were put at the beginning of the array, so total bytes available
+ to convert this cycle includes them. If we found 0 following bytes
+ then we ignore the previous partial character.
+ */
+ lGCount += nArrOffset;
+
+ if( hConverter )
+ {
+ sal_uInt32 nInfo;
+ sal_Size nNewLen = lGCount, nCntBytes;
+ aWork.reset(new sal_Unicode[nNewLen + 1]); // add 1 for '\0'
+ sal_Unicode* pBuf = aWork.get();
+ pBuf[nNewLen] = 0; // ensure '\0'
+
+ nNewLen = rtl_convertTextToUnicode(hConverter, hContext, m_pArr.get(), lGCount,
+ pBuf, nNewLen,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT
+ | RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE),
+ &nInfo, &nCntBytes);
+ nArrOffset = lGCount - nCntBytes;
+ if( 0 != nArrOffset )
+ memmove(m_pArr.get(), m_pArr.get() + nCntBytes, nArrOffset);
+
+ pStt = pLastStt = aWork.get();
+ pEnd = pStt + nNewLen;
+ }
+ else
+ {
+ pStt = pLastStt = reinterpret_cast<sal_Unicode*>(m_pArr.get());
+ auto nChars = lGCount / 2;
+ pEnd = pStt + nChars;
+
+ if( bSwapUnicode )
+ {
+ char *pF = m_pArr.get(), *pN = m_pArr.get() + 1;
+ for (sal_Size n = 0; n < nChars; ++n, pF += 2, pN += 2)
+ {
+ char c = *pF;
+ *pF = *pN;
+ *pN = c;
+ }
+ }
+ }
+
+ *pEnd = 0;
+ nReadCnt += lGCount;
+
+ ::SetProgressState(nReadCnt, m_rDoc.GetDocShell());
+
+ if( cLastCR )
+ {
+ if( 0x0a == *pStt && 0x0d == cLastCR )
+ pLastStt = ++pStt;
+ cLastCR = 0;
+ nLineLen = 0;
+ // We skip the last one at the end
+ if (!m_rInput.eof() || !(pEnd == pStt || (!*pEnd && pEnd == pStt + 1)))
+ m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), false);
+ }
+ }
+
+ bool bIns = true, bSplitNode = false;
+ switch( *pStt )
+ {
+
+ case 0x0a: if( LINEEND_LF == pUseMe->GetParaFlags() )
+ {
+ bIns = false;
+ *pStt = 0;
+ ++pStt;
+
+ // We skip the last one at the end
+ if (!m_rInput.eof() || pEnd != pStt)
+ bSplitNode = true;
+ }
+ break;
+
+ case 0x0d: if( LINEEND_LF != pUseMe->GetParaFlags() )
+ {
+ bIns = false;
+ *pStt = 0;
+ ++pStt;
+
+ bool bChkSplit = true;
+ if( LINEEND_CRLF == pUseMe->GetParaFlags() )
+ {
+ if( pStt == pEnd )
+ {
+ cLastCR = 0x0d;
+ bChkSplit = false;
+ }
+ else if( 0x0a == *pStt )
+ ++pStt;
+ }
+
+ // We skip the last one at the end
+ if (bChkSplit && (!m_rInput.eof() || pEnd != pStt))
+ bSplitNode = true;
+ }
+ break;
+
+ case 0x0c:
+ {
+ // Insert a hard page break
+ *pStt++ = 0;
+ if( nLineLen )
+ {
+ InsertText( OUString( pLastStt ));
+ }
+ m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(),
+ false);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(
+ *m_oPam, SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
+ pLastStt = pStt;
+ nLineLen = 0;
+ bIns = false;
+ }
+ break;
+
+ case 0x1a:
+ if (nReadCnt == m_nFileSize && pStt + 1 == pEnd)
+ *pStt = 0;
+ else
+ *pStt = '#'; // Replacement visualisation
+ break;
+
+ case '\t': break;
+
+ default:
+ if( ' ' > *pStt )
+ // Found control char, replace with '#'
+ *pStt = '#';
+ break;
+ }
+
+ if( bIns )
+ {
+ if( ( nLineLen >= MAX_ASCII_PARA - 100 ) &&
+ ( ( *pStt == ' ' ) || ( nLineLen >= MAX_ASCII_PARA - 1 ) ) )
+ {
+ sal_Unicode c = *pStt;
+ *pStt = 0;
+ InsertText( OUString( pLastStt ));
+ m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), false);
+ pLastStt = pStt;
+ nLineLen = 0;
+ *pStt = c;
+ }
+ ++pStt;
+ ++nLineLen;
+ }
+ else if( bSplitNode )
+ {
+ // We found a CR/LF, thus save the text
+ InsertText( OUString( pLastStt ));
+ if (m_bNewDoc)
+ m_rDoc.getIDocumentContentOperations().AppendTextNode(*m_oPam->GetPoint());
+ else
+ m_rDoc.getIDocumentContentOperations().SplitNode(*m_oPam->GetPoint(), false);
+ pLastStt = pStt;
+ nLineLen = 0;
+ }
+ } while(true);
+
+ if( hConverter )
+ {
+ rtl_destroyTextToUnicodeContext( hConverter, hContext );
+ rtl_destroyTextToUnicodeConverter( hConverter );
+ }
+ return ERRCODE_NONE;
+}
+
+void SwASCIIParser::InsertText( const OUString& rStr )
+{
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_oPam, rStr);
+
+ if (m_oItemSet && g_pBreakIt
+ && m_nScript != (SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX))
+ m_nScript |= g_pBreakIt->GetAllScriptsOfText(rStr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ascii/wrtasc.cxx b/sw/source/filter/ascii/wrtasc.cxx
new file mode 100644
index 0000000000..0f1e368b92
--- /dev/null
+++ b/sw/source/filter/ascii/wrtasc.cxx
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/endian.h>
+#include <tools/stream.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <mdiexp.hxx>
+#include <fmtcntnt.hxx>
+#include <frmfmt.hxx>
+#include "wrtasc.hxx"
+#include <frameformats.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <o3tl/string_view.hxx>
+
+#include <strings.hrc>
+
+SwASCWriter::SwASCWriter( std::u16string_view rFltNm )
+{
+ SwAsciiOptions aNewOpts;
+
+ switch( 5 <= rFltNm.size() ? rFltNm[4] : 0 )
+ {
+ case 'D':
+ aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_850 );
+ aNewOpts.SetParaFlags( LINEEND_CRLF );
+ if( 5 < rFltNm.size() )
+ {
+ std::u16string_view aFilterNum = rFltNm.substr( 5 );
+ switch( o3tl::toInt32(aFilterNum) )
+ {
+ case 437: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_437 ); break;
+ case 850: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_850 ); break;
+ case 860: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_860 ); break;
+ case 861: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_861 ); break;
+ case 863: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_863 ); break;
+ case 865: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_865 ); break;
+ }
+ }
+ break;
+
+ case 'A':
+#ifndef _WIN32
+ aNewOpts.SetCharSet( RTL_TEXTENCODING_MS_1252 );
+ aNewOpts.SetParaFlags( LINEEND_CRLF );
+#endif
+ break;
+
+ case 'M':
+ aNewOpts.SetCharSet( RTL_TEXTENCODING_APPLE_ROMAN );
+ aNewOpts.SetParaFlags( LINEEND_CR );
+ break;
+
+ case 'X':
+#ifdef _WIN32
+ aNewOpts.SetCharSet( RTL_TEXTENCODING_MS_1252 );
+ aNewOpts.SetParaFlags( LINEEND_LF );
+#endif
+ break;
+
+ default:
+ if( rFltNm.size() >= 4 && rFltNm.substr( 4 )==u"_DLG" )
+ {
+ // use the options
+ aNewOpts = GetAsciiOptions();
+ }
+ }
+ SetAsciiOptions( aNewOpts );
+}
+
+SwASCWriter::~SwASCWriter() {}
+
+ErrCode SwASCWriter::WriteStream()
+{
+ static constexpr OUString STR_CR = u"\015"_ustr;
+ static constexpr OUStringLiteral STR_LF = u"\012";
+ static constexpr OUStringLiteral STR_CRLF = u"\015\012";
+ static constexpr OUStringLiteral STR_BLANK = u" ";
+ bool bIncludeBOM = GetAsciiOptions().GetIncludeBOM();
+ bool bIncludeHidden = GetAsciiOptions().GetIncludeHidden();
+
+ if( m_bASCII_ParaAsCR ) // If predefined
+ m_sLineEnd = STR_CR;
+ else if( m_bASCII_ParaAsBlank )
+ m_sLineEnd = STR_BLANK;
+ else
+ switch( GetAsciiOptions().GetParaFlags() )
+ {
+ case LINEEND_CR: m_sLineEnd = STR_CR; break;
+ case LINEEND_LF: m_sLineEnd = STR_LF; break;
+ case LINEEND_CRLF: m_sLineEnd = STR_CRLF; break;
+ }
+
+ SwNodeOffset nMaxNode = m_pDoc->GetNodes().Count();
+
+ if( m_bShowProgress )
+ ::StartProgress( STR_STATSTR_W4WWRITE, 0, sal_Int32(nMaxNode), m_pDoc->GetDocShell() );
+
+ SwPaM* pPam = m_pOrigPam;
+
+ bool bWriteSttTag = m_bUCS2_WithStartChar &&
+ (RTL_TEXTENCODING_UCS2 == GetAsciiOptions().GetCharSet() ||
+ RTL_TEXTENCODING_UTF8 == GetAsciiOptions().GetCharSet());
+
+ rtl_TextEncoding eOld = Strm().GetStreamCharSet();
+ Strm().SetStreamCharSet( GetAsciiOptions().GetCharSet() );
+
+ // Output all areas of the pam into the ASC file
+ do {
+ bool bTstFly = true;
+ while( m_pCurrentPam->GetPoint()->GetNodeIndex() < m_pCurrentPam->GetMark()->GetNodeIndex() ||
+ (m_pCurrentPam->GetPoint()->GetNodeIndex() == m_pCurrentPam->GetMark()->GetNodeIndex() &&
+ m_pCurrentPam->GetPoint()->GetContentIndex() <= m_pCurrentPam->GetMark()->GetContentIndex()) )
+ {
+ SwTextNode* pNd = m_pCurrentPam->GetPoint()->GetNode().GetTextNode();
+ if( pNd )
+ {
+ // Should we have frames only?
+ // That's possible, if we put a frame selection into the clipboard
+ if( bTstFly && m_bWriteAll &&
+ pNd->GetText().isEmpty() &&
+ // Frame exists
+ !m_pDoc->GetSpzFrameFormats()->empty() &&
+ // Only one node in the array
+ m_pDoc->GetNodes().GetEndOfExtras().GetIndex() + 3 ==
+ m_pDoc->GetNodes().GetEndOfContent().GetIndex() &&
+ // And exactly this one is selected
+ m_pDoc->GetNodes().GetEndOfContent().GetIndex() - 1 ==
+ m_pCurrentPam->GetPoint()->GetNodeIndex() )
+ {
+ // Print the frame's content.
+ // It is always at position 0!
+ const SwFrameFormat* pFormat = (*m_pDoc->GetSpzFrameFormats())[ 0 ];
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx )
+ {
+ m_pCurrentPam = NewUnoCursor(*m_pDoc, pIdx->GetIndex(),
+ pIdx->GetNode().EndOfSectionIndex() );
+ m_pCurrentPam->Exchange();
+ continue; // reset while loop!
+ }
+ }
+ else if (!pNd->IsHidden() || bIncludeHidden)
+ {
+ if (bWriteSttTag)
+ {
+ switch(GetAsciiOptions().GetCharSet())
+ {
+ case RTL_TEXTENCODING_UTF8:
+ if( bIncludeBOM )
+ {
+ Strm().WriteUChar( 0xEF ).WriteUChar( 0xBB ).WriteUChar( 0xBF );
+ }
+
+ break;
+ case RTL_TEXTENCODING_UCS2:
+#ifdef OSL_LITENDIAN
+ Strm().SetEndian(SvStreamEndian::LITTLE);
+#else
+ Strm().SetEndian(SvStreamEndian::BIG);
+#endif
+ if( bIncludeBOM )
+ {
+ Strm().StartWritingUnicodeText();
+ }
+ break;
+
+ }
+ bWriteSttTag = false;
+ }
+ Out( aASCNodeFnTab, *pNd, *this );
+ }
+ bTstFly = false; // Testing once is enough
+ }
+
+ if( !m_pCurrentPam->Move( fnMoveForward, GoInNode ) )
+ break;
+
+ if( m_bShowProgress )
+ ::SetProgressState( sal_Int32(m_pCurrentPam->GetPoint()->GetNodeIndex()),
+ m_pDoc->GetDocShell() ); // How far?
+
+ }
+ } while( CopyNextPam( &pPam ) ); // Until all pams are processed
+
+ Strm().SetStreamCharSet( eOld );
+
+ if( m_bShowProgress )
+ ::EndProgress( m_pDoc->GetDocShell() );
+
+ return ERRCODE_NONE;
+}
+
+void SwASCWriter::SetupFilterOptions(SfxMedium& rMedium)
+{
+ if( const SfxStringItem* pItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ SwAsciiOptions aOpt;
+ OUString sItemOpt;
+ sItemOpt = pItem->GetValue();
+ aOpt.ReadUserData(sItemOpt);
+ SetAsciiOptions(aOpt);
+ }
+}
+
+void GetASCWriter(
+ std::u16string_view rFltNm, [[maybe_unused]] const OUString& /*rBaseURL*/, WriterRef& xRet )
+{
+ xRet = new SwASCWriter( rFltNm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ascii/wrtasc.hxx b/sw/source/filter/ascii/wrtasc.hxx
new file mode 100644
index 0000000000..c2e3dfa9a2
--- /dev/null
+++ b/sw/source/filter/ascii/wrtasc.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX
+#define INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX
+
+#include <shellio.hxx>
+#include <wrt_fn.hxx>
+
+extern SwNodeFnTab aASCNodeFnTab;
+
+// The ASC writer
+
+class SwASCWriter : public Writer
+{
+ OUString m_sLineEnd;
+
+ virtual ErrCode WriteStream() override;
+
+public:
+ SwASCWriter(std::u16string_view rFilterName);
+ virtual ~SwASCWriter() override;
+
+ void SetupFilterOptions(SfxMedium& rMedium) override;
+ const OUString& GetLineEnd() const { return m_sLineEnd; }
+};
+
+#endif // _ INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/basflt/docfact.cxx b/sw/source/filter/basflt/docfact.cxx
new file mode 100644
index 0000000000..731f29913b
--- /dev/null
+++ b/sw/source/filter/basflt/docfact.cxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <docfac.hxx>
+
+
+SwDocFac::SwDocFac( SwDoc *pDc )
+ : mxDoc( pDc )
+{
+}
+
+SwDocFac::~SwDocFac() COVERITY_NOEXCEPT_FALSE
+{
+}
+
+SwDoc& SwDocFac::GetDoc()
+{
+ if(!mxDoc.is())
+ mxDoc = new SwDoc;
+ return *mxDoc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/basflt/fltini.cxx b/sw/source/filter/basflt/fltini.cxx
new file mode 100644
index 0000000000..9a5b6f0e2c
--- /dev/null
+++ b/sw/source/filter/basflt/fltini.cxx
@@ -0,0 +1,725 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <i18nlangtag/lang.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/any.hxx>
+#include <tools/svlibrary.h>
+#include <sot/storage.hxx>
+#include <shellio.hxx>
+#include <fltini.hxx>
+#include <init.hxx>
+#include <fmtfsize.hxx>
+#include <swtable.hxx>
+#include <fmtcntnt.hxx>
+#include <editeng/boxitem.hxx>
+#include <ndtxt.hxx>
+#include <swfltopt.hxx>
+#include <swdll.hxx>
+#include <iodetect.hxx>
+#include <osl/module.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star;
+
+Reader *ReadAscii = nullptr, *ReadHTML = nullptr, *ReadXML = nullptr;
+
+static Reader* GetRTFReader();
+static Reader* GetWW8Reader();
+static Reader* GetDOCXReader();
+
+// Note: if editing, please don't forget to modify also the enum
+// ReaderWriterEnum and aFilterDetect in iodetect.hxx & iodetect.cxx
+static SwReaderWriterEntry aReaderWriter[] =
+{
+ SwReaderWriterEntry( &::GetRTFReader, &::GetRTFWriter, true ),
+ SwReaderWriterEntry( nullptr, &::GetASCWriter, false ),
+ SwReaderWriterEntry( &::GetWW8Reader, nullptr, true ),
+ SwReaderWriterEntry( &::GetWW8Reader, &::GetWW8Writer, true ),
+ SwReaderWriterEntry( &::GetRTFReader, &::GetRTFWriter, true ),
+ SwReaderWriterEntry( nullptr, &::GetHTMLWriter, true ),
+ SwReaderWriterEntry( &::GetWW8Reader, nullptr, true ),
+ SwReaderWriterEntry( nullptr, &::GetXMLWriter, true ),
+ SwReaderWriterEntry( nullptr, &::GetASCWriter, false ),
+ SwReaderWriterEntry( nullptr, &::GetASCWriter, true ),
+ SwReaderWriterEntry( &::GetDOCXReader, nullptr, true )
+};
+
+Reader* SwReaderWriterEntry::GetReader()
+{
+ if ( pReader )
+ return pReader;
+ else if ( fnGetReader )
+ {
+ pReader = (*fnGetReader)();
+ return pReader;
+ }
+ return nullptr;
+}
+
+void SwReaderWriterEntry::GetWriter( std::u16string_view rNm, const OUString& rBaseURL, WriterRef& xWrt ) const
+{
+ if ( fnGetWriter )
+ (*fnGetWriter)( rNm, rBaseURL, xWrt );
+ else
+ xWrt = WriterRef(nullptr);
+}
+
+Reader* SwGetReaderXML() // SW_DLLPUBLIC
+{
+ return ReadXML;
+}
+
+static void SetFltPtr( sal_uInt16 rPos, Reader* pReader )
+{
+ aReaderWriter[ rPos ].pReader = pReader;
+}
+
+namespace sw {
+
+Filters::Filters()
+{
+ ReadAscii = new AsciiReader;
+ ReadHTML = new HTMLReader;
+ ReadXML = new XMLReader;
+ SetFltPtr( READER_WRITER_BAS, ReadAscii );
+ SetFltPtr( READER_WRITER_HTML, ReadHTML );
+ SetFltPtr( READER_WRITER_XML, ReadXML );
+ SetFltPtr( READER_WRITER_TEXT_DLG, ReadAscii );
+ SetFltPtr( READER_WRITER_TEXT, ReadAscii );
+}
+
+Filters::~Filters()
+{
+ // kill Readers
+ for(SwReaderWriterEntry & rEntry : aReaderWriter)
+ {
+ if( rEntry.bDelReader && rEntry.pReader )
+ {
+ delete rEntry.pReader;
+ rEntry.pReader = nullptr;
+ }
+ }
+ msword_.release();
+}
+
+#ifndef DISABLE_DYNLOADING
+
+oslGenericFunction Filters::GetMswordLibSymbol( const char *pSymbol )
+{
+ if (!msword_.is())
+ {
+ OUString url("$LO_LIB_DIR/" SVLIBRARY("msword"));
+ rtl::Bootstrap::expandMacros(url);
+ bool ok = msword_.load( url, SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY );
+ SAL_WARN_IF(!ok, "sw", "failed to load msword library");
+ }
+ if (msword_.is())
+ return msword_.getFunctionSymbol( OUString::createFromAscii( pSymbol ) );
+ return nullptr;
+}
+
+#endif
+
+}
+
+namespace SwReaderWriter {
+
+Reader* GetRtfReader()
+{
+ return aReaderWriter[READER_WRITER_RTF].GetReader();
+}
+
+Reader* GetDOCXReader()
+{
+ return aReaderWriter[READER_WRITER_DOCX].GetReader();
+}
+
+void GetWriter( std::u16string_view rFltName, const OUString& rBaseURL, WriterRef& xRet )
+{
+ for( int n = 0; n < MAXFILTER; ++n )
+ if ( aFilterDetect[n].IsFilter( rFltName ) )
+ {
+ aReaderWriter[n].GetWriter( rFltName, rBaseURL, xRet );
+ break;
+ }
+}
+
+Reader* GetReader( const OUString& rFltName )
+{
+ Reader* pRead = nullptr;
+ for( int n = 0; n < MAXFILTER; ++n )
+ {
+ if ( aFilterDetect[n].IsFilter( rFltName ) )
+ {
+ pRead = aReaderWriter[n].GetReader();
+ // add special treatment for some readers
+ if ( pRead )
+ pRead->SetFltName( rFltName );
+ break;
+ }
+ }
+ return pRead;
+}
+
+} // namespace SwReaderWriter
+
+bool Writer::IsStgWriter() const { return false; }
+
+bool StgWriter::IsStgWriter() const { return true; }
+
+// Read Filter Flags; used by WW8 / W4W / EXCEL / LOTUS
+
+/*
+<FilterFlags>
+ <WinWord>
+ <WW1F cfg:type="long">0</WW1F>
+ <WW cfg:type="long">0</WW>
+ <WW8 cfg:type="long">0</WW8>
+ <WWF cfg:type="long">0</WWF>
+ <WWFA0 cfg:type="long">0</WWFA0>
+ <WWFA1 cfg:type="long">0</WWFA1>
+ <WWFA2 cfg:type="long">0</WWFA2>
+ <WWFB0 cfg:type="long">0</WWFB0>
+ <WWFB1 cfg:type="long">0</WWFB1>
+ <WWFB2 cfg:type="long">0</WWFB2>
+ <WWFLX cfg:type="long">0</WWFLX>
+ <WWFLY cfg:type="long">0</WWFLY>
+ <WWFT cfg:type="long">0</WWFT>
+ <WWWR cfg:type="long">0</WWWR>
+ </WinWord>
+</FilterFlags>
+*/
+
+SwFilterOptions::SwFilterOptions( sal_uInt16 nCnt, const char** ppNames,
+ sal_uInt64* pValues )
+ : ConfigItem( "Office.Writer/FilterFlags" )
+{
+ GetValues( nCnt, ppNames, pValues );
+}
+
+void SwFilterOptions::GetValues( sal_uInt16 nCnt, const char** ppNames,
+ sal_uInt64* pValues )
+{
+ Sequence<OUString> aNames( nCnt );
+ OUString* pNames = aNames.getArray();
+ sal_uInt16 n;
+
+ for( n = 0; n < nCnt; ++n )
+ pNames[ n ] = OUString::createFromAscii( ppNames[ n ] );
+ Sequence<Any> aValues = GetProperties( aNames );
+
+ if( nCnt == aValues.getLength() )
+ {
+ const Any* pAnyValues = aValues.getConstArray();
+ for( n = 0; n < nCnt; ++n )
+ pValues[ n ] = pAnyValues[ n ].hasValue()
+ ? *o3tl::doAccess<sal_uInt64>(pAnyValues[ n ])
+ : 0;
+ }
+ else
+ {
+ for( n = 0; n < nCnt; ++n )
+ pValues[ n ] = 0;
+ }
+}
+
+void SwFilterOptions::ImplCommit() {}
+void SwFilterOptions::Notify( const css::uno::Sequence< OUString >& ) {}
+
+void StgReader::SetFltName( const OUString& rFltNm )
+{
+ if( SwReaderType::Storage & GetReaderType() )
+ m_aFltName = rFltNm;
+}
+
+void CalculateFlySize(SfxItemSet& rFlySet, const SwNode& rAnchor,
+ SwTwips nPageWidth)
+{
+ const SwFormatFrameSize* pFrameSizeItem = rFlySet.GetItemIfSet( RES_FRM_SIZE );
+ if( !pFrameSizeItem || MINFLY > pFrameSizeItem->GetWidth() )
+ {
+ std::unique_ptr<SwFormatFrameSize> aSz(rFlySet.Get(RES_FRM_SIZE).Clone());
+ if (pFrameSizeItem)
+ aSz.reset(pFrameSizeItem->Clone());
+
+ SwTwips nWidth;
+ // determine the width; if there is a table use the width of the table;
+ // otherwise use the width of the page
+ const SwTableNode* pTableNd = rAnchor.FindTableNode();
+ if( pTableNd )
+ nWidth = pTableNd->GetTable().GetFrameFormat()->GetFrameSize().GetWidth();
+ else
+ nWidth = nPageWidth;
+
+ const SwNodeIndex* pSttNd = rFlySet.Get( RES_CNTNT ).GetContentIdx();
+ if( pSttNd )
+ {
+ bool bOnlyOneNode = true;
+ sal_uLong nMinFrame = 0;
+ sal_uLong nMaxFrame = 0;
+ SwTextNode* pFirstTextNd = nullptr;
+ SwNodeIndex aIdx( *pSttNd, 1 );
+ SwNodeIndex aEnd( *pSttNd->GetNode().EndOfSectionNode() );
+ while( aIdx < aEnd )
+ {
+ SwTextNode *pTextNd = aIdx.GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ if( !pFirstTextNd )
+ pFirstTextNd = pTextNd;
+ else if( pFirstTextNd != pTextNd )
+ {
+ // forget it
+ bOnlyOneNode = false;
+ break;
+ }
+
+ sal_uLong nAbsMinCnts;
+ pTextNd->GetMinMaxSize( aIdx.GetIndex(), nMinFrame, nMaxFrame, nAbsMinCnts );
+ }
+ ++aIdx;
+ }
+
+ if( bOnlyOneNode )
+ {
+ if( nMinFrame < MINLAY && pFirstTextNd )
+ {
+ // if the first node don't contained any content, then
+ // insert one char in it calc again and delete once again
+ SwContentIndex aNdIdx( pFirstTextNd );
+ pFirstTextNd->InsertText("MM", aNdIdx);
+ sal_uLong nAbsMinCnts;
+ pFirstTextNd->GetMinMaxSize( pFirstTextNd->GetIndex(),
+ nMinFrame, nMaxFrame, nAbsMinCnts );
+ aNdIdx -= 2;
+ pFirstTextNd->EraseText( aNdIdx, 2 );
+ }
+
+ // consider border and distance to content
+ const SvxBoxItem& rBoxItem = rFlySet.Get( RES_BOX );
+ SvxBoxItemLine nLine = SvxBoxItemLine::LEFT;
+ for( int i = 0; i < 2; ++i )
+ {
+ const editeng::SvxBorderLine* pLn = rBoxItem.GetLine( nLine );
+ if( pLn )
+ {
+ sal_uInt16 nWidthTmp = pLn->GetOutWidth() + pLn->GetInWidth();
+ nWidthTmp = nWidthTmp + rBoxItem.GetDistance( nLine );
+ nMinFrame += nWidthTmp;
+ nMaxFrame += nWidthTmp;
+ }
+ nLine = SvxBoxItemLine::RIGHT;
+ }
+
+ // enforce minimum width for contents
+ if( nMinFrame < MINLAY )
+ nMinFrame = MINLAY;
+ if( nMaxFrame < MINLAY )
+ nMaxFrame = MINLAY;
+
+ if( nWidth > o3tl::narrowing<sal_uInt16>(nMaxFrame) )
+ nWidth = nMaxFrame;
+ else if( nWidth > o3tl::narrowing<sal_uInt16>(nMinFrame) )
+ nWidth = nMinFrame;
+ }
+ }
+
+ if( MINFLY > nWidth )
+ nWidth = MINFLY;
+
+ aSz->SetWidth( nWidth );
+ if( MINFLY > aSz->GetHeight() )
+ aSz->SetHeight( MINFLY );
+ rFlySet.Put( std::move(aSz) );
+ }
+ else if( MINFLY > pFrameSizeItem->GetHeight() )
+ {
+ std::unique_ptr<SwFormatFrameSize> aSz(pFrameSizeItem->Clone());
+ aSz->SetHeight( MINFLY );
+ rFlySet.Put( std::move(aSz) );
+ }
+}
+
+namespace
+{
+
+struct CharSetNameMap
+{
+ rtl_TextEncoding eCode;
+ const char* pName;
+};
+
+const CharSetNameMap *GetCharSetNameMap()
+{
+ static const CharSetNameMap aMapArr[] =
+ {
+# define IMPLENTRY(X) { RTL_TEXTENCODING_##X, #X }
+ IMPLENTRY(DONTKNOW),
+ IMPLENTRY(MS_1252),
+ IMPLENTRY(APPLE_ROMAN),
+ IMPLENTRY(IBM_437),
+ IMPLENTRY(IBM_850),
+ IMPLENTRY(IBM_860),
+ IMPLENTRY(IBM_861),
+ IMPLENTRY(IBM_863),
+ IMPLENTRY(IBM_865),
+ IMPLENTRY(SYMBOL),
+ IMPLENTRY(ASCII_US),
+ IMPLENTRY(ISO_8859_1),
+ IMPLENTRY(ISO_8859_2),
+ IMPLENTRY(ISO_8859_3),
+ IMPLENTRY(ISO_8859_4),
+ IMPLENTRY(ISO_8859_5),
+ IMPLENTRY(ISO_8859_6),
+ IMPLENTRY(ISO_8859_7),
+ IMPLENTRY(ISO_8859_8),
+ IMPLENTRY(ISO_8859_9),
+ IMPLENTRY(ISO_8859_14),
+ IMPLENTRY(ISO_8859_15),
+ IMPLENTRY(IBM_737),
+ IMPLENTRY(IBM_775),
+ IMPLENTRY(IBM_852),
+ IMPLENTRY(IBM_855),
+ IMPLENTRY(IBM_857),
+ IMPLENTRY(IBM_862),
+ IMPLENTRY(IBM_864),
+ IMPLENTRY(IBM_866),
+ IMPLENTRY(IBM_869),
+ IMPLENTRY(MS_874),
+ IMPLENTRY(MS_1250),
+ IMPLENTRY(MS_1251),
+ IMPLENTRY(MS_1253),
+ IMPLENTRY(MS_1254),
+ IMPLENTRY(MS_1255),
+ IMPLENTRY(MS_1256),
+ IMPLENTRY(MS_1257),
+ IMPLENTRY(MS_1258),
+ IMPLENTRY(APPLE_ARABIC),
+ IMPLENTRY(APPLE_CENTEURO),
+ IMPLENTRY(APPLE_CROATIAN),
+ IMPLENTRY(APPLE_CYRILLIC),
+ IMPLENTRY(APPLE_DEVANAGARI),
+ IMPLENTRY(APPLE_FARSI),
+ IMPLENTRY(APPLE_GREEK),
+ IMPLENTRY(APPLE_GUJARATI),
+ IMPLENTRY(APPLE_GURMUKHI),
+ IMPLENTRY(APPLE_HEBREW),
+ IMPLENTRY(APPLE_ICELAND),
+ IMPLENTRY(APPLE_ROMANIAN),
+ IMPLENTRY(APPLE_THAI),
+ IMPLENTRY(APPLE_TURKISH),
+ IMPLENTRY(APPLE_UKRAINIAN),
+ IMPLENTRY(APPLE_CHINSIMP),
+ IMPLENTRY(APPLE_CHINTRAD),
+ IMPLENTRY(APPLE_JAPANESE),
+ IMPLENTRY(APPLE_KOREAN),
+ IMPLENTRY(MS_932),
+ IMPLENTRY(MS_936),
+ IMPLENTRY(MS_949),
+ IMPLENTRY(MS_950),
+ IMPLENTRY(SHIFT_JIS),
+ IMPLENTRY(GB_2312),
+ IMPLENTRY(GBT_12345),
+ IMPLENTRY(GBK),
+ IMPLENTRY(BIG5),
+ IMPLENTRY(EUC_JP),
+ IMPLENTRY(EUC_CN),
+ IMPLENTRY(EUC_TW),
+ IMPLENTRY(ISO_2022_JP),
+ IMPLENTRY(ISO_2022_CN),
+ IMPLENTRY(KOI8_R),
+ IMPLENTRY(KOI8_U),
+ IMPLENTRY(UTF7),
+ IMPLENTRY(UTF8),
+ IMPLENTRY(ISO_8859_10),
+ IMPLENTRY(ISO_8859_13),
+ IMPLENTRY(EUC_KR),
+ IMPLENTRY(ISO_2022_KR),
+ IMPLENTRY(JIS_X_0201),
+ IMPLENTRY(JIS_X_0208),
+ IMPLENTRY(JIS_X_0212),
+ IMPLENTRY(MS_1361),
+ IMPLENTRY(GB_18030),
+ IMPLENTRY(BIG5_HKSCS),
+ IMPLENTRY(TIS_620),
+ IMPLENTRY(PT154),
+ IMPLENTRY(UCS4),
+ IMPLENTRY(UCS2),
+ IMPLENTRY(UNICODE),
+ {0,nullptr} //Last
+ };
+ return &aMapArr[0];
+}
+
+/*
+ Get a rtl_TextEncoding from its name
+ */
+rtl_TextEncoding CharSetFromName(std::u16string_view rChrSetStr)
+{
+ const CharSetNameMap *pStart = GetCharSetNameMap();
+ rtl_TextEncoding nRet = pStart->eCode;
+
+ for(const CharSetNameMap *pMap = pStart; pMap->pName; ++pMap)
+ {
+ if(o3tl::equalsIgnoreAsciiCase(rChrSetStr, pMap->pName))
+ {
+ nRet = pMap->eCode;
+ break;
+ }
+ }
+
+ OSL_ENSURE(nRet != pStart->eCode, "TXT: That was an unknown language!");
+
+ return nRet;
+}
+
+/*
+ Get the String name of an rtl_TextEncoding
+ */
+OUString NameFromCharSet(rtl_TextEncoding nChrSet)
+{
+ const CharSetNameMap *pStart = GetCharSetNameMap();
+ const char *pRet = pStart->pName;
+
+ for(const CharSetNameMap *pMap = pStart; pMap->pName; ++pMap)
+ {
+ if (nChrSet == pMap->eCode)
+ {
+ pRet = pMap->pName;
+ break;
+ }
+ }
+
+ OSL_ENSURE(pRet != pStart->pName, "TXT: That was an unknown language!");
+
+ return OUString::createFromAscii(pRet);
+}
+
+}
+
+// for the automatic conversion (mail/news/...)
+// The user data contains the options for the ascii import/export filter.
+// The format is:
+// 1. CharSet - as ascii chars
+// 2. LineEnd - as CR/LF/CRLF
+// 3. Fontname
+// 4. Language
+// 5. Whether to include byte-order-mark - as true/false
+// 6. Whether to include hidden paragraphs and text - as true/false
+// the delimiter character is ","
+
+void SwAsciiOptions::ReadUserData( std::u16string_view rStr )
+{
+ sal_Int32 nToken = 0;
+ std::u16string_view sToken = o3tl::getToken(rStr, 0, ',', nToken); // 1. Charset name
+ if (!sToken.empty())
+ m_eCharSet = CharSetFromName(sToken);
+ if (nToken >= 0 && !(sToken = o3tl::getToken(rStr, 0, ',', nToken)).empty()) // 2. Line ending type
+ {
+ if (o3tl::equalsIgnoreAsciiCase(sToken, u"CRLF"))
+ m_eCRLF_Flag = LINEEND_CRLF;
+ else if (o3tl::equalsIgnoreAsciiCase(sToken, u"LF"))
+ m_eCRLF_Flag = LINEEND_LF;
+ else
+ m_eCRLF_Flag = LINEEND_CR;
+ }
+ if (nToken >= 0 && !(sToken = o3tl::getToken(rStr, 0, ',', nToken)).empty()) // 3. Font name
+ m_sFont = sToken;
+ if (nToken >= 0 && !(sToken = o3tl::getToken(rStr, 0, ',', nToken)).empty()) // 4. Language tag
+ m_nLanguage = LanguageTag::convertToLanguageTypeWithFallback(OUString(sToken));
+ if (nToken >= 0 && !(sToken = o3tl::getToken(rStr, 0, ',', nToken)).empty()) // 5. Include BOM?
+ m_bIncludeBOM = !(o3tl::equalsIgnoreAsciiCase(sToken, u"FALSE"));
+ // 6. Include hidden text
+ if (nToken >= 0 && !(sToken = o3tl::getToken(rStr, 0, ',', nToken)).empty())
+ m_bIncludeHidden = !(o3tl::equalsIgnoreAsciiCase(sToken, u"FALSE"));
+}
+
+void SwAsciiOptions::WriteUserData(OUString& rStr) const
+{
+ // 1. charset
+ rStr = NameFromCharSet(m_eCharSet) + ",";
+
+ // 2. LineEnd
+ switch(m_eCRLF_Flag)
+ {
+ case LINEEND_CRLF:
+ rStr += "CRLF";
+ break;
+ case LINEEND_CR:
+ rStr += "CR";
+ break;
+ case LINEEND_LF:
+ rStr += "LF";
+ break;
+ }
+ rStr += ",";
+
+ // 3. Fontname
+ rStr += m_sFont + ",";
+
+ // 4. Language
+ if (m_nLanguage)
+ {
+ rStr += LanguageTag::convertToBcp47(m_nLanguage);
+ }
+ rStr += ",";
+
+ // 5. Whether to include byte-order-mark
+ if(m_bIncludeBOM)
+ {
+ rStr += "true";
+ }
+ else
+ {
+ rStr += "false";
+ }
+ rStr += ",";
+
+ // 6. Whether to include hidden paragraphs and text
+ if(m_bIncludeHidden)
+ {
+ rStr += "true";
+ }
+ else
+ {
+ rStr += "false";
+ }
+ rStr += ",";
+}
+
+#ifdef DISABLE_DYNLOADING
+
+extern "C" {
+ Reader *ImportRTF();
+ void ExportRTF( std::u16string_view, const OUString& rBaseURL, WriterRef& );
+ Reader *ImportDOC();
+ void ExportDOC( std::u16string_view, const OUString& rBaseURL, WriterRef& );
+ Reader *ImportDOCX();
+ sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell&, SotStorage&, sal_Bool, const OUString& );
+ sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8( SfxObjectShell& );
+}
+
+#endif
+
+Reader* GetRTFReader()
+{
+#ifndef DISABLE_DYNLOADING
+
+ FnGetReader pFunction = reinterpret_cast<FnGetReader>( SwGlobals::getFilters().GetMswordLibSymbol( "ImportRTF" ) );
+
+ if ( pFunction )
+ return (*pFunction)();
+
+ return nullptr;
+#else
+ return ImportRTF();
+#endif
+
+}
+
+void GetRTFWriter( std::u16string_view rFltName, const OUString& rBaseURL, WriterRef& xRet )
+{
+#ifndef DISABLE_DYNLOADING
+ FnGetWriter pFunction = reinterpret_cast<FnGetWriter>( SwGlobals::getFilters().GetMswordLibSymbol( "ExportRTF" ) );
+
+ if ( pFunction )
+ (*pFunction)( rFltName, rBaseURL, xRet );
+ else
+ xRet = WriterRef(nullptr);
+#else
+ ExportRTF( rFltName, rBaseURL, xRet );
+#endif
+}
+
+Reader* GetWW8Reader()
+{
+#ifndef DISABLE_DYNLOADING
+ FnGetReader pFunction = reinterpret_cast<FnGetReader>( SwGlobals::getFilters().GetMswordLibSymbol( "ImportDOC" ) );
+
+ if ( pFunction )
+ return (*pFunction)();
+
+ return nullptr;
+#else
+ return ImportDOC();
+#endif
+}
+
+void GetWW8Writer( std::u16string_view rFltName, const OUString& rBaseURL, WriterRef& xRet )
+{
+#ifndef DISABLE_DYNLOADING
+ FnGetWriter pFunction = reinterpret_cast<FnGetWriter>( SwGlobals::getFilters().GetMswordLibSymbol( "ExportDOC" ) );
+
+ if ( pFunction )
+ (*pFunction)( rFltName, rBaseURL, xRet );
+ else
+ xRet = WriterRef(nullptr);
+#else
+ ExportDOC( rFltName, rBaseURL, xRet );
+#endif
+}
+
+Reader* GetDOCXReader()
+{
+#ifndef DISABLE_DYNLOADING
+ FnGetReader pFunction = reinterpret_cast<FnGetReader>( SwGlobals::getFilters().GetMswordLibSymbol( "ImportDOCX" ) );
+
+ if ( pFunction )
+ return (*pFunction)();
+
+ return nullptr;
+#else
+ return ImportDOCX();
+#endif
+}
+
+typedef sal_uInt32 ( *SaveOrDel )( SfxObjectShell&, SotStorage&, sal_Bool, const OUString& );
+typedef sal_uInt32 ( *GetSaveWarning )( SfxObjectShell& );
+
+ErrCode SaveOrDelMSVBAStorage( SfxObjectShell& rDoc, SotStorage& rStor, bool bSaveInto, const OUString& rStorageName )
+{
+#ifndef DISABLE_DYNLOADING
+ SaveOrDel pFunction = reinterpret_cast<SaveOrDel>( SwGlobals::getFilters().GetMswordLibSymbol( "SaveOrDelMSVBAStorage_ww8" ) );
+ if( pFunction )
+ return ErrCode(pFunction( rDoc, rStor, bSaveInto, rStorageName ));
+ return ERRCODE_NONE;
+#else
+ return ErrCode(SaveOrDelMSVBAStorage_ww8( rDoc, rStor, bSaveInto, rStorageName ));
+#endif
+}
+
+ErrCode GetSaveWarningOfMSVBAStorage( SfxObjectShell &rDocS )
+{
+#ifndef DISABLE_DYNLOADING
+ GetSaveWarning pFunction = reinterpret_cast<GetSaveWarning>( SwGlobals::getFilters().GetMswordLibSymbol( "GetSaveWarningOfMSVBAStorage_ww8" ) );
+ if( pFunction )
+ return ErrCode(pFunction( rDocS ));
+ return ERRCODE_NONE;
+#else
+ return ErrCode(GetSaveWarningOfMSVBAStorage_ww8( rDocS ));
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/basflt/fltshell.cxx b/sw/source/filter/basflt/fltshell.cxx
new file mode 100644
index 0000000000..901614897f
--- /dev/null
+++ b/sw/source/filter/basflt/fltshell.cxx
@@ -0,0 +1,1064 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <cstddef>
+
+#include <hintids.hxx>
+#include <hints.hxx>
+
+#include <svl/cintitem.hxx>
+#include <svl/stritem.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfld.hxx>
+#include <redline.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <ndtxt.hxx>
+#include <fldbas.hxx>
+#include <docufld.hxx>
+#include <txtfld.hxx>
+#include <tox.hxx>
+#include <expfld.hxx>
+#include <bookmark.hxx>
+#include <fltshell.hxx>
+#include <rdfhelper.hxx>
+#include <utility>
+
+using namespace com::sun::star;
+
+static SwContentNode* GetContentNode(SwDoc& rDoc, SwPosition& rPos, bool bNext)
+{
+ SwContentNode * pCNd = rPos.GetNode().GetContentNode();
+ if(!pCNd && nullptr == (pCNd = bNext ? rDoc.GetNodes().GoNext(&rPos)
+ : SwNodes::GoPrevious(&rPos)))
+ {
+ pCNd = bNext ? SwNodes::GoPrevious(&rPos)
+ : rDoc.GetNodes().GoNext(&rPos);
+ OSL_ENSURE(pCNd, "no ContentNode found");
+ }
+ return pCNd;
+}
+
+static OUString lcl_getTypePath(OUString& rType)
+{
+ OUString aRet;
+ if (rType.startsWith("urn:bails"))
+ {
+ rType = "urn:bails";
+ aRet = "tscp/bails.rdf";
+ }
+ return aRet;
+}
+
+// Stack entry for all text attributes
+SwFltStackEntry::SwFltStackEntry(const SwPosition& rStartPos, std::unique_ptr<SfxPoolItem> pHt)
+ : m_aMkPos(rStartPos)
+ , m_aPtPos(rStartPos)
+ , m_pAttr( std::move(pHt) )
+ , m_isAnnotationOnEnd(false)
+{
+ m_bOld = false; // used for marking Attributes *before* skipping field results
+ m_bOpen = true; // lock the attribute --> may first
+ m_bConsumedByField = false;
+}
+
+SwFltStackEntry::~SwFltStackEntry()
+{
+ // Although attribute got passed as pointer, it gets deleted here
+}
+
+void SwFltStackEntry::SetEndPos(const SwPosition& rEndPos)
+{
+ // Release attribute and keep track of end
+ // Everything with sal_uInt16s, because otherwise the inserting of new text at
+ // the cursor position moves the attribute's range
+ // That's not the desired behavior!
+ m_bOpen = false; // release and remember END
+ m_aPtPos.FromSwPosition(rEndPos);
+}
+
+bool SwFltStackEntry::MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode const eCheck,
+ const SwFltPosition &rMkPos, const SwFltPosition &rPtPos,
+ sal_uInt16 nWhich)
+{
+ // does this range actually contain something?
+ // empty range is allowed if at start of empty paragraph
+ // fields are special: never have range, so leave them
+ SwNodeOffset nMk = rMkPos.m_nNode.GetIndex() + 1;
+ const SwNodes& rMkNodes = rMkPos.m_nNode.GetNodes();
+ if (nMk >= rMkNodes.Count())
+ return false;
+ SwContentNode *const pContentNode(rMkNodes[nMk]->GetContentNode());
+ if (rMkPos == rPtPos &&
+ ((0 != rPtPos.m_nContent) || (pContentNode && (0 != pContentNode->Len())))
+ && ( RES_TXTATR_FIELD != nWhich
+ && RES_TXTATR_ANNOTATION != nWhich
+ && RES_TXTATR_INPUTFIELD != nWhich ))
+ {
+ return false;
+ }
+ // The content indices always apply to the node!
+ rRegion.GetPoint()->Assign( rMkPos.m_nNode.GetIndex() + 1 );
+ SwContentNode* pCNd = GetContentNode(rDoc, *rRegion.GetPoint(), true);
+
+ SAL_WARN_IF(pCNd->Len() < rMkPos.m_nContent, "sw.ww8",
+ "invalid content index " << rMkPos.m_nContent << " but text node has only " << pCNd->Len());
+ rRegion.GetPoint()->SetContent( std::min<sal_Int32>(rMkPos.m_nContent, pCNd->Len()) );
+ rRegion.SetMark();
+ if (rMkPos.m_nNode != rPtPos.m_nNode)
+ {
+ SwNodeOffset n = rPtPos.m_nNode.GetIndex() + 1;
+ SwNodes& rNodes = rRegion.GetPoint()->GetNodes();
+ if (n >= rNodes.Count())
+ return false;
+ rRegion.GetPoint()->Assign(n);
+ pCNd = GetContentNode(rDoc, *rRegion.GetPoint(), false);
+ }
+ SAL_WARN_IF(pCNd->Len() < rPtPos.m_nContent, "sw.ww8",
+ "invalid content index " << rPtPos.m_nContent << " but text node has only " << pCNd->Len());
+ rRegion.GetPoint()->SetContent( std::min<sal_Int32>(rPtPos.m_nContent, pCNd->Len()) );
+ OSL_ENSURE( CheckNodesRange( rRegion.Start()->GetNode(),
+ rRegion.End()->GetNode(), true ),
+ "attribute or similar crosses section-boundaries" );
+ bool bRet = true;
+ if (eCheck & RegionMode::CheckNodes)
+ {
+ bRet &= CheckNodesRange(rRegion.Start()->GetNode(),
+ rRegion.End()->GetNode(), true);
+ }
+ if (eCheck & RegionMode::CheckFieldmark)
+ {
+ bRet &= !sw::mark::IsFieldmarkOverlap(rRegion);
+ }
+ return bRet;
+}
+
+bool SwFltStackEntry::MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode eCheck) const
+{
+ return MakeRegion(rDoc, rRegion, eCheck, m_aMkPos, m_aPtPos, m_pAttr->Which());
+}
+
+SwFltControlStack::SwFltControlStack(SwDoc& rDo, sal_uLong nFieldFl)
+ : m_nFieldFlags(nFieldFl), m_rDoc(rDo), m_bIsEndStack(false)
+{
+}
+
+SwFltControlStack::~SwFltControlStack()
+{
+ OSL_ENSURE(m_Entries.empty(), "There are still Attributes on the stack");
+}
+
+// MoveAttrs() is meant to address the following problem:
+// When a field like "set variable" is set through the stack, the text
+// is shifted by one \xff character, which makes all subsequent
+// attribute positions invalid.
+// After setting the attribute in the doc, MoveAttrs() needs to be
+// called in order to push all attribute positions to the right in the
+// same paragraph further out by one character.
+void SwFltControlStack::MoveAttrs(const SwPosition& rPos, MoveAttrsMode eMode)
+{
+ SwNodeOffset nPosNd = rPos.GetNodeIndex();
+ sal_uInt16 nPosCt = rPos.GetContentIndex() - 1;
+
+ for (size_t i = 0, nCnt = m_Entries.size(); i < nCnt; ++i)
+ {
+ SwFltStackEntry& rEntry = *m_Entries[i];
+ if (
+ (rEntry.m_aMkPos.m_nNode.GetIndex()+1 == nPosNd) &&
+ (rEntry.m_aMkPos.m_nContent >= nPosCt)
+ )
+ {
+ rEntry.m_aMkPos.m_nContent++;
+ OSL_ENSURE( rEntry.m_aMkPos.m_nContent
+ <= m_rDoc.GetNodes()[nPosNd]->GetContentNode()->Len(),
+ "Attribute ends after end of line" );
+ }
+ if (
+ (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == nPosNd) &&
+ (rEntry.m_aPtPos.m_nContent >= nPosCt)
+ )
+ {
+ if ( !rEntry.m_isAnnotationOnEnd
+ || rEntry.m_aPtPos.m_nContent > nPosCt)
+ {
+ assert(!(rEntry.m_isAnnotationOnEnd && rEntry.m_aPtPos.m_nContent > nPosCt));
+ if ( eMode == MoveAttrsMode::POSTIT_INSERTED
+ && rEntry.m_aPtPos.m_nContent == nPosCt
+ && rEntry.m_pAttr->Which() == RES_FLTR_ANNOTATIONMARK)
+ {
+ rEntry.m_isAnnotationOnEnd = true;
+ eMode = MoveAttrsMode::DEFAULT; // only set 1 flag
+ }
+ rEntry.m_aPtPos.m_nContent++;
+ OSL_ENSURE( rEntry.m_aPtPos.m_nContent
+ <= m_rDoc.GetNodes()[nPosNd]->GetContentNode()->Len(),
+ "Attribute ends after end of line" );
+ }
+ }
+ }
+}
+
+void SwFltControlStack::MarkAllAttrsOld()
+{
+ size_t nCnt = m_Entries.size();
+ for (size_t i=0; i < nCnt; ++i)
+ m_Entries[i]->m_bOld = true;
+}
+
+namespace
+{
+ bool couldExtendEntry(const SwFltStackEntry *pExtendCandidate,
+ const SfxPoolItem& rAttr)
+ {
+ return (pExtendCandidate &&
+ !pExtendCandidate->m_bConsumedByField &&
+ //if we bring character attributes into the fold we need to both
+ //a) consider RES_CHRATR_FONTSIZE and RES_CHRATR_FONT wrt Word's CJK/CTL variants
+ //b) consider crossing table cell boundaries (tdf#102334)
+ isPARATR_LIST(rAttr.Which()) &&
+ *(pExtendCandidate->m_pAttr) == rAttr);
+ }
+}
+
+void SwFltControlStack::NewAttr(const SwPosition& rPos, const SfxPoolItem& rAttr)
+{
+ sal_uInt16 nWhich = rAttr.Which();
+ // Set end position of potentially equal attributes on stack, so
+ // as to avoid having them accumulate
+ SwFltStackEntry *pExtendCandidate = SetAttr(rPos, nWhich);
+ if (couldExtendEntry(pExtendCandidate, rAttr))
+ {
+ //Here we optimize by seeing if there is an attribute uncommitted
+ //to the document which
+
+ //a) has the same value as this attribute
+ //b) is already open, or ends at the same place as where we're starting
+ //from. If so we merge it with this one and elide adding another
+ //to the stack
+ pExtendCandidate->SetEndPos(rPos);
+ pExtendCandidate->m_bOpen=true;
+ }
+ else
+ {
+ SwFltStackEntry *pTmp = new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone()) );
+ m_Entries.push_back(std::unique_ptr<SwFltStackEntry>(pTmp));
+ }
+}
+
+void SwFltControlStack::DeleteAndDestroy(Entries::size_type nCnt)
+{
+ OSL_ENSURE(nCnt < m_Entries.size(), "Out of range!");
+ if (nCnt < m_Entries.size())
+ {
+ auto aElement = m_Entries.begin() + nCnt;
+ m_Entries.erase(aElement);
+ }
+}
+
+// SwFltControlStack::StealAttr() removes attributes of the given type
+// from the stack. Allowed as nAttrId: 0 meaning any, or a specific
+// type. This makes them disappear from the doc structure. Only
+// attributes from the same paragraph as rPos are removed. Used for
+// graphic apos -> images.
+void SwFltControlStack::StealAttr(const SwNode& rNode)
+{
+ size_t nCnt = m_Entries.size();
+
+ while (nCnt)
+ {
+ nCnt --;
+ SwFltStackEntry& rEntry = *m_Entries[nCnt];
+ if (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == rNode.GetIndex())
+ {
+ DeleteAndDestroy(nCnt); // delete from the stack
+ }
+ }
+}
+
+// SwFltControlStack::KillUnlockedAttr() removes all attributes from
+// the stack, which are assigned to an rPos. This makes them disappear
+// from the doc structure. Used in WW import for ignoring attributes
+// assigned to the 0x0c section break symbol.
+void SwFltControlStack::KillUnlockedAttrs(const SwPosition& rPos)
+{
+ SwFltPosition aFltPos(rPos);
+
+ size_t nCnt = m_Entries.size();
+ while( nCnt )
+ {
+ nCnt --;
+ SwFltStackEntry& rEntry = *m_Entries[nCnt];
+ if( !rEntry.m_bOld
+ && !rEntry.m_bOpen
+ && (rEntry.m_aMkPos == aFltPos)
+ && (rEntry.m_aPtPos == aFltPos))
+ {
+ DeleteAndDestroy( nCnt ); // remove from stack
+ }
+ }
+}
+
+// Unlock all locked attributes and move to the end, all others will
+// be applied to the document and removed from the stack.
+// Returns if there were any selected attributes on the stack
+SwFltStackEntry* SwFltControlStack::SetAttr(const SwPosition& rPos,
+ sal_uInt16 nAttrId, bool bTstEnd, tools::Long nHand,
+ bool consumedByField)
+{
+ SwFltStackEntry *pRet = nullptr;
+
+ SwFltPosition aFltPos(rPos);
+
+ OSL_ENSURE(!nAttrId ||
+ (POOLATTR_BEGIN <= nAttrId && POOLATTR_END > nAttrId) ||
+ (RES_FLTRATTR_BEGIN <= nAttrId && RES_FLTRATTR_END > nAttrId),
+ "Wrong id for attribute");
+
+ auto aI = m_Entries.begin();
+ while (aI != m_Entries.end())
+ {
+ bool bLastEntry = aI == m_Entries.end() - 1;
+
+ SwFltStackEntry& rEntry = **aI;
+ if (rEntry.m_bOpen)
+ {
+ // set end of attribute
+ bool bF = false;
+ if (!nAttrId )
+ {
+ bF = true;
+ }
+ else if (nAttrId == rEntry.m_pAttr->Which())
+ {
+ if( nAttrId != RES_FLTR_BOOKMARK && nAttrId != RES_FLTR_ANNOTATIONMARK && nAttrId != RES_FLTR_RDFMARK )
+ {
+ // query handle
+ bF = true;
+ }
+ else if (nAttrId == RES_FLTR_BOOKMARK && nHand == static_cast<SwFltBookmark*>(rEntry.m_pAttr.get())->GetHandle())
+ {
+ bF = true;
+ }
+ else if (nAttrId == RES_FLTR_ANNOTATIONMARK && nHand == static_cast<CntUInt16Item*>(rEntry.m_pAttr.get())->GetValue())
+ {
+ bF = true;
+ }
+ else if (nAttrId == RES_FLTR_RDFMARK && nHand == static_cast<SwFltRDFMark*>(rEntry.m_pAttr.get())->GetHandle())
+ {
+ bF = true;
+ }
+ }
+ if (bF)
+ {
+ rEntry.m_bConsumedByField = consumedByField;
+ rEntry.SetEndPos(rPos);
+ if (bLastEntry && nAttrId == rEntry.m_pAttr->Which())
+ {
+ //potential candidate for merging with an identical
+ //property beginning at rPos
+ pRet = &rEntry;
+ }
+ }
+ ++aI;
+ continue;
+ }
+
+ // if the end position is equal to the cursor position, then
+ // refrain from applying it; there needs to be following text,
+ // except at the very end. (attribute expansion !!)
+ // Never apply end stack except at document ending
+ if (bTstEnd)
+ {
+ if (m_bIsEndStack)
+ {
+ ++aI;
+ continue;
+ }
+
+ //defer inserting this attribute into the document until
+ //we advance to the next node, or finish processing the document
+ if (rEntry.m_aPtPos.m_nNode.GetIndex() == aFltPos.m_nNode.GetIndex())
+ {
+ if (bLastEntry && nAttrId == rEntry.m_pAttr->Which() &&
+ rEntry.m_aPtPos.m_nContent == aFltPos.m_nContent)
+ {
+ //potential candidate for merging with an identical
+ //property beginning at rPos
+ pRet = &rEntry;
+ }
+
+ ++aI;
+ continue;
+ }
+ }
+ SetAttrInDoc(rPos, rEntry);
+ aI = m_Entries.erase(aI);
+ }
+
+ return pRet;
+}
+
+static bool MakePoint(const SwFltStackEntry& rEntry, SwDoc& rDoc,
+ SwPaM& rRegion)
+{
+ // the anchor is the Pam's Point. It's modified when inserting
+ // text, etc.; therefore it is kept on the stack. Only the
+ // attribute's format needs to be set.
+ rRegion.DeleteMark();
+
+ SwNodeOffset nMk = rEntry.m_aMkPos.m_nNode.GetIndex() + 1;
+ const SwNodes& rMkNodes = rEntry.m_aMkPos.m_nNode.GetNodes();
+ if (nMk >= rMkNodes.Count())
+ return false;
+
+ rRegion.GetPoint()->Assign(nMk);
+ GetContentNode(rDoc, *rRegion.GetPoint(), true);
+ rRegion.GetPoint()->SetContent(rEntry.m_aMkPos.m_nContent);
+ return true;
+}
+
+// MakeBookRegionOrPoint() behaves like MakeRegionOrPoint, except that
+// it adheres to certain restrictions on bookmarks in tables (cannot
+// span more than one cell)
+static bool MakeBookRegionOrPoint(const SwFltStackEntry& rEntry, SwDoc& rDoc,
+ SwPaM& rRegion )
+{
+ if (rEntry.MakeRegion(rDoc, rRegion, SwFltStackEntry::RegionMode::CheckNodes))
+ {
+ if (rRegion.GetPoint()->GetNode().FindTableBoxStartNode()
+ != rRegion.GetMark()->GetNode().FindTableBoxStartNode())
+ {
+ rRegion.Exchange(); // invalid range
+ rRegion.DeleteMark(); // -> both to mark
+ }
+ return true;
+ }
+ return MakePoint(rEntry, rDoc, rRegion);
+}
+
+// IterateNumrulePiece() looks for the first range valid for Numrules
+// between rTmpStart and rEnd.
+
+// rNds denotes the doc nodes
+// rEnd denotes the range end,
+// rTmpStart is an in/out parameter: in: start of range to be searched,
+// out: start of valid range
+// rTmpEnd is an out parameter
+// Returns true for valid range
+static bool IterateNumrulePiece( const SwPosition& rEnd,
+ SwNodeIndex& rTmpStart, SwNodeIndex& rTmpEnd )
+{
+ while( ( rTmpStart <= rEnd.GetNode() )
+ && !( rTmpStart.GetNode().IsTextNode() ) ) // look for valid start
+ ++rTmpStart;
+
+ rTmpEnd = rTmpStart;
+ while( ( rTmpEnd <= rEnd.GetNode() )
+ && ( rTmpEnd.GetNode().IsTextNode() ) ) // look for valid end + 1
+ ++rTmpEnd;
+
+ --rTmpEnd; // valid end
+
+ return rTmpStart <= rTmpEnd; // valid ?
+}
+
+void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry)
+{
+ SwPaM aRegion( rTmpPos );
+
+ switch(rEntry.m_pAttr->Which())
+ {
+ case RES_FLTR_ANCHOR:
+ {
+ SwFrameFormat* pFormat = static_cast<SwFltAnchor*>(rEntry.m_pAttr.get())->GetFrameFormat();
+ if (pFormat != nullptr)
+ {
+ MakePoint(rEntry, m_rDoc, aRegion);
+ SwFormatAnchor aAnchor(pFormat->GetAnchor());
+ aAnchor.SetAnchor(aRegion.GetPoint());
+ pFormat->SetFormatAttr(aAnchor);
+ // So the frames will be created when inserting into
+ // existing doc (after setting the anchor!):
+ if (m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()
+ && (RndStdIds::FLY_AT_PARA == pFormat->GetAnchor().GetAnchorId()))
+ {
+ pFormat->MakeFrames();
+ }
+ }
+ }
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ break;
+
+ case RES_TXTATR_TOXMARK:
+ break;
+
+ case RES_FLTR_NUMRULE: // insert Numrule
+ {
+ const OUString& rNumNm = static_cast<SfxStringItem*>(rEntry.m_pAttr.get())->GetValue();
+ SwNumRule* pNumRule = m_rDoc.FindNumRulePtr( rNumNm );
+ if( pNumRule )
+ {
+ if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::CheckNodes))
+ {
+ SwNodeIndex aTmpStart( aRegion.Start()->GetNode() );
+ SwNodeIndex aTmpEnd( aTmpStart );
+ SwPosition& rRegEndNd = *aRegion.End();
+ while( IterateNumrulePiece( rRegEndNd,
+ aTmpStart, aTmpEnd ) )
+ {
+ SwPaM aTmpPam( aTmpStart, aTmpEnd );
+ // no start of a new list
+ m_rDoc.SetNumRule( aTmpPam, *pNumRule, false );
+
+ aTmpStart = aTmpEnd; // here starts the next range
+ ++aTmpStart;
+ }
+ }
+ else
+ m_rDoc.DelNumRule( rNumNm );
+ }
+ }
+ break;
+
+ case RES_FLTR_BOOKMARK:
+ {
+ SwFltBookmark* pB = static_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
+ const OUString& rName = static_cast<SwFltBookmark*>(rEntry.m_pAttr.get())->GetName();
+
+ if (IsFlagSet(BOOK_TO_VAR_REF))
+ {
+ SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rName, false);
+ if (!pFT)
+ {
+ SwSetExpFieldType aS(&m_rDoc, rName, nsSwGetSetExpType::GSE_STRING);
+ pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType(aS);
+ }
+ SwSetExpField aField(static_cast<SwSetExpFieldType*>(pFT), pB->GetValSys());
+ aField.SetSubType( nsSwExtendedSubType::SUB_INVISIBLE );
+ MakePoint(rEntry, m_rDoc, aRegion);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, SwFormatField(aField));
+ MoveAttrs( *(aRegion.GetPoint()) );
+ }
+ if ( ( !IsFlagSet(HYPO) || IsFlagSet(BOOK_AND_REF) ) &&
+ !rEntry.m_bConsumedByField )
+ {
+ MakeBookRegionOrPoint(rEntry, m_rDoc, aRegion);
+ // #i120879# - create a cross reference heading bookmark if appropriate.
+ const IDocumentMarkAccess::MarkType eBookmarkType =
+ ( pB->IsTOCBookmark() &&
+ IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( aRegion ) )
+ ? IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
+ : IDocumentMarkAccess::MarkType::BOOKMARK;
+ m_rDoc.getIDocumentMarkAccess()->makeMark(aRegion, rName, eBookmarkType, sw::mark::InsertMode::New);
+ }
+ }
+ break;
+ case RES_FLTR_ANNOTATIONMARK:
+ {
+ if (MakeBookRegionOrPoint(rEntry, m_rDoc, aRegion))
+ {
+ SwTextNode const*const pTextNode(
+ aRegion.End()->GetNode().GetTextNode());
+ SwTextField const*const pField = pTextNode ? pTextNode->GetFieldTextAttrAt(
+ aRegion.End()->GetContentIndex() - 1, ::sw::GetTextAttrMode::Default) : nullptr;
+ if (pField)
+ {
+ SwPostItField const*const pPostIt(
+ dynamic_cast<SwPostItField const*>(pField->GetFormatField().GetField()));
+ if (pPostIt)
+ {
+ assert(pPostIt->GetName().isEmpty());
+
+ if (!aRegion.HasMark())
+ {
+ // Annotation range was found in the file, but start/end is the same,
+ // pointing after the postit placeholder (see assert above).
+ // Adjust the start of the range to actually cover the comment, similar
+ // to what the UI and the UNO API does.
+ aRegion.SetMark();
+ aRegion.Start()->AdjustContent(-1);
+ }
+
+ m_rDoc.getIDocumentMarkAccess()->makeAnnotationMark(aRegion, OUString());
+ }
+ else
+ {
+ SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: unexpected field");
+ }
+ }
+ else
+ {
+ SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: missing field");
+ }
+ }
+ else
+ SAL_WARN("sw", "failed to make book region or point");
+ }
+ break;
+ case RES_FLTR_RDFMARK:
+ {
+ if (MakeBookRegionOrPoint(rEntry, m_rDoc, aRegion))
+ {
+ SwFltRDFMark* pMark = static_cast<SwFltRDFMark*>(rEntry.m_pAttr.get());
+ if (aRegion.GetPointNode().IsTextNode())
+ {
+ SwTextNode& rTextNode = *aRegion.GetPointNode().GetTextNode();
+
+ for (const std::pair<OUString, OUString>& rAttribute : pMark->GetAttributes())
+ {
+ OUString aTypeNS = rAttribute.first;
+ OUString aMetadataFilePath = lcl_getTypePath(aTypeNS);
+ if (aMetadataFilePath.isEmpty())
+ continue;
+
+ SwRDFHelper::addTextNodeStatement(aTypeNS, aMetadataFilePath, rTextNode, rAttribute.first, rAttribute.second);
+ }
+ }
+ }
+ else
+ SAL_WARN("sw", "failed to make book region or point");
+ }
+ break;
+ case RES_FLTR_TOX:
+ {
+ MakePoint(rEntry, m_rDoc, aRegion);
+
+ SwPosition* pPoint = aRegion.GetPoint();
+
+ SwFltTOX* pTOXAttr = static_cast<SwFltTOX*>(rEntry.m_pAttr.get());
+
+ // test if on this node there had been a pagebreak BEFORE the
+ // tox attribute was put on the stack
+ SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> aBkSet( m_rDoc.GetAttrPool() );
+ SwContentNode* pNd = nullptr;
+ if( !pTOXAttr->HadBreakItem() || !pTOXAttr->HadPageDescItem() )
+ {
+ pNd = pPoint->GetNode().GetContentNode();
+ if( pNd )
+ {
+ const SfxItemSet* pSet = pNd->GetpSwAttrSet();
+ const SfxPoolItem* pItem;
+ if( pSet )
+ {
+ if( !pTOXAttr->HadBreakItem()
+ && SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) )
+ {
+ aBkSet.Put( *pItem );
+ pNd->ResetAttr( RES_BREAK );
+ }
+ if( !pTOXAttr->HadPageDescItem()
+ && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) )
+ {
+ aBkSet.Put( *pItem );
+ pNd->ResetAttr( RES_PAGEDESC );
+ }
+ }
+ }
+ }
+
+ // set (above saved and removed) the break item at the node following the TOX
+ if (pNd && aBkSet.Count())
+ pNd->SetAttr(aBkSet);
+ }
+ break;
+ case RES_FLTR_REDLINE:
+ {
+ if (rEntry.MakeRegion(m_rDoc, aRegion,
+ SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark))
+ {
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On
+ | RedlineFlags::ShowInsert
+ | RedlineFlags::ShowDelete );
+ SwFltRedline& rFltRedline = *static_cast<SwFltRedline*>(rEntry.m_pAttr.get());
+
+ SwRedlineData aData(rFltRedline.m_eType,
+ rFltRedline.m_nAutorNo,
+ rFltRedline.m_aStamp,
+ 0,
+ OUString(),
+ nullptr
+ );
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(aData, aRegion), true );
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::NONE
+ | RedlineFlags::ShowInsert
+ | RedlineFlags::ShowDelete );
+ }
+ }
+ break;
+ default:
+ {
+ if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck))
+ {
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr);
+ }
+ }
+ break;
+ }
+}
+
+SfxPoolItem* SwFltControlStack::GetFormatStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos)
+{
+ size_t nSize = m_Entries.size();
+
+ while (nSize)
+ {
+ // is it the looked-for attribute ? (only applies to locked, meaning
+ // currently set attributes!!)
+ SwFltStackEntry &rEntry = *m_Entries[--nSize];
+ if (rEntry.m_bOpen && rEntry.m_pAttr->Which() == nWhich)
+ {
+ if (pPos)
+ *pPos = nSize;
+ return rEntry.m_pAttr.get(); // Ok, so stop
+ }
+ }
+ return nullptr;
+}
+
+const SfxPoolItem* SwFltControlStack::GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich)
+{
+ SwFltPosition aFltPos(rPos);
+
+ size_t nSize = m_Entries.size();
+
+ while (nSize)
+ {
+ SwFltStackEntry &rEntry = *m_Entries[--nSize];
+ if (rEntry.m_bOpen && rEntry.m_pAttr->Which() == nWhich && rEntry.m_aMkPos == aFltPos)
+ {
+ return rEntry.m_pAttr.get();
+ }
+ }
+ return nullptr;
+}
+
+void SwFltControlStack::Delete(const SwPaM &rPam)
+{
+ auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition*
+
+ if( !rPam.HasMark() || *pStt >= *pEnd )
+ return;
+
+ SwNodeIndex aStartNode(pStt->GetNode(), -1);
+ const sal_Int32 nStartIdx = pStt->GetContentIndex();
+ SwNodeIndex aEndNode(pEnd->GetNode(), -1);
+ const sal_Int32 nEndIdx = pEnd->GetContentIndex();
+
+ // We don't support deleting content that is over one node, or removing a node.
+ OSL_ENSURE(aEndNode == aStartNode, "nodes must be the same, or this method extended");
+ if (aEndNode != aStartNode)
+ return;
+
+ for (size_t nSize = m_Entries.size(); nSize > 0;)
+ {
+ SwFltStackEntry& rEntry = *m_Entries[--nSize];
+
+ bool bEntryStartAfterSelStart =
+ (rEntry.m_aMkPos.m_nNode == aStartNode &&
+ rEntry.m_aMkPos.m_nContent >= nStartIdx);
+
+ bool bEntryStartBeforeSelEnd =
+ (rEntry.m_aMkPos.m_nNode == aEndNode &&
+ rEntry.m_aMkPos.m_nContent <= nEndIdx);
+
+ bool bEntryEndAfterSelStart = false;
+ bool bEntryEndBeforeSelEnd = false;
+ if (!rEntry.m_bOpen)
+ {
+ bEntryEndAfterSelStart =
+ (rEntry.m_aPtPos.m_nNode == aStartNode &&
+ rEntry.m_aPtPos.m_nContent >= nStartIdx);
+
+ bEntryEndBeforeSelEnd =
+ (rEntry.m_aPtPos.m_nNode == aEndNode &&
+ rEntry.m_aPtPos.m_nContent <= nEndIdx);
+ }
+
+ bool bTotallyContained = false;
+ if (
+ bEntryStartAfterSelStart && bEntryStartBeforeSelEnd &&
+ bEntryEndAfterSelStart && bEntryEndBeforeSelEnd
+ )
+ {
+ bTotallyContained = true;
+ }
+
+ if (bTotallyContained)
+ {
+ // after start, before end, delete
+ DeleteAndDestroy(nSize);
+ continue;
+ }
+
+ const sal_Int32 nContentDiff = nEndIdx - nStartIdx;
+
+ // to be adjusted
+ if (bEntryStartAfterSelStart)
+ {
+ if (bEntryStartBeforeSelEnd)
+ {
+ // move start to new start
+ rEntry.m_aMkPos.SetPos(aStartNode, nStartIdx);
+ }
+ else
+ rEntry.m_aMkPos.m_nContent -= nContentDiff;
+ }
+
+ if (bEntryEndAfterSelStart)
+ {
+ if (bEntryEndBeforeSelEnd)
+ rEntry.m_aPtPos.SetPos(aStartNode, nStartIdx);
+ else
+ rEntry.m_aPtPos.m_nContent -= nContentDiff;
+ }
+
+ //That's what Open is, end equal to start, and nPtContent is invalid
+ if (rEntry.m_bOpen)
+ rEntry.m_aPtPos = rEntry.m_aMkPos;
+ }
+}
+
+// methods of SwFltAnchor follow
+SwFltAnchor::SwFltAnchor(SwFrameFormat* pFormat) :
+ SfxPoolItem(RES_FLTR_ANCHOR), m_pFrameFormat(pFormat)
+{
+ m_pListener.reset(new SwFltAnchorListener(this));
+ m_pListener->StartListening(m_pFrameFormat->GetNotifier());
+}
+
+SwFltAnchor::SwFltAnchor(const SwFltAnchor& rCpy) :
+ SfxPoolItem(RES_FLTR_ANCHOR), m_pFrameFormat(rCpy.m_pFrameFormat)
+{
+ m_pListener.reset(new SwFltAnchorListener(this));
+ m_pListener->StartListening(m_pFrameFormat->GetNotifier());
+}
+
+SwFltAnchor::~SwFltAnchor()
+{
+}
+
+void SwFltAnchor::SetFrameFormat(SwFrameFormat * _pFrameFormat)
+{
+ m_pFrameFormat = _pFrameFormat;
+}
+
+
+bool SwFltAnchor::operator==(const SfxPoolItem& rItem) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ m_pFrameFormat == static_cast<const SwFltAnchor&>(rItem).m_pFrameFormat;
+}
+
+SwFltAnchor* SwFltAnchor::Clone(SfxItemPool*) const
+{
+ return new SwFltAnchor(*this);
+}
+
+SwFltAnchorListener::SwFltAnchorListener(SwFltAnchor* pFltAnchor)
+ : m_pFltAnchor(pFltAnchor)
+{ }
+
+void SwFltAnchorListener::Notify(const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ m_pFltAnchor->SetFrameFormat(nullptr);
+ else if (rHint.GetId() == SfxHintId::SwDrawFrameFormat)
+ {
+ auto pDrawFrameFormatHint = static_cast<const sw::DrawFrameFormatHint*>(&rHint);
+ if (pDrawFrameFormatHint->m_eId != sw::DrawFrameFormatHintId::DYING)
+ return;
+ m_pFltAnchor->SetFrameFormat(nullptr);
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(pLegacyHint->m_pNew->Which() != RES_FMT_CHG)
+ return;
+ auto pFormatChg = dynamic_cast<const SwFormatChg*>(pLegacyHint->m_pNew);
+ auto pFrameFormat = pFormatChg ? dynamic_cast<SwFrameFormat*>(pFormatChg->pChangedFormat) : nullptr;
+ if(pFrameFormat)
+ m_pFltAnchor->SetFrameFormat(pFrameFormat);
+ }
+}
+
+// methods of SwFltRedline follow
+bool SwFltRedline::operator==(const SfxPoolItem& rItem) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ SfxPoolItem::areSame(*this, rItem);
+}
+
+SwFltRedline* SwFltRedline::Clone( SfxItemPool* ) const
+{
+ return new SwFltRedline(*this);
+}
+
+// methods of SwFltBookmark follow
+SwFltBookmark::SwFltBookmark( const OUString& rNa, OUString aVa,
+ tools::Long nHand, const bool bIsTOCBookmark )
+ : SfxPoolItem( RES_FLTR_BOOKMARK )
+ , mnHandle( nHand )
+ , maName( rNa )
+ , maVal(std::move( aVa ))
+ , mbIsTOCBookmark( bIsTOCBookmark )
+{
+ // eSrc: CHARSET_DONTKNOW for no transform at operator <<
+ // Upcase is always done.
+ // Transform is never done at XXXStack.NewAttr(...).
+ // otherwise: Src Charset from argument for aName
+ // Src Charset from filter for aVal ( Text )
+
+ if ( IsTOCBookmark() && ! rNa.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
+ {
+ maName = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix();
+ maName += rNa;
+ }
+}
+
+bool SwFltBookmark::operator==(const SfxPoolItem& rItem) const
+{
+ return SfxPoolItem::operator==(rItem)
+ && maName == static_cast<const SwFltBookmark&>(rItem).maName
+ && mnHandle == static_cast<const SwFltBookmark&>(rItem).mnHandle;
+}
+
+SwFltBookmark* SwFltBookmark::Clone(SfxItemPool*) const
+{
+ return new SwFltBookmark(*this);
+}
+
+SwFltRDFMark::SwFltRDFMark()
+ : SfxPoolItem(RES_FLTR_RDFMARK),
+ m_nHandle(0)
+{
+}
+
+bool SwFltRDFMark::operator==(const SfxPoolItem& rItem) const
+{
+ if (!SfxPoolItem::operator==(rItem))
+ return false;
+
+ const SwFltRDFMark& rMark = static_cast<const SwFltRDFMark&>(rItem);
+
+ return m_nHandle == rMark.m_nHandle && m_aAttributes == rMark.m_aAttributes;
+}
+
+SwFltRDFMark* SwFltRDFMark::Clone(SfxItemPool*) const
+{
+ return new SwFltRDFMark(*this);
+}
+
+void SwFltRDFMark::SetHandle(tools::Long nHandle)
+{
+ m_nHandle = nHandle;
+}
+
+tools::Long SwFltRDFMark::GetHandle() const
+{
+ return m_nHandle;
+}
+
+void SwFltRDFMark::SetAttributes( std::vector< std::pair<OUString, OUString> >&& rAttributes)
+{
+ m_aAttributes = std::move(rAttributes);
+}
+
+const std::vector< std::pair<OUString, OUString> >& SwFltRDFMark::GetAttributes() const
+{
+ return m_aAttributes;
+}
+
+// methods of SwFltTOX follow
+SwFltTOX::SwFltTOX(std::shared_ptr<SwTOXBase> xBase)
+ : SfxPoolItem(RES_FLTR_TOX), m_xTOXBase(std::move(xBase)),
+ m_bHadBreakItem( false ), m_bHadPageDescItem( false )
+{
+}
+
+bool SwFltTOX::operator==(const SfxPoolItem& rItem) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ m_xTOXBase.get() == static_cast<const SwFltTOX&>(rItem).m_xTOXBase.get();
+}
+
+SwFltTOX* SwFltTOX::Clone(SfxItemPool*) const
+{
+ return new SwFltTOX(*this);
+}
+
+// UpdatePageDescs needs to be called at end of parsing to make Writer actually
+// accept Pagedescs contents
+void UpdatePageDescs(SwDoc &rDoc, size_t nInPageDescOffset)
+{
+ // Update document page descriptors (only this way also left pages
+ // get adjusted)
+
+ // PageDesc "Standard"
+ rDoc.ChgPageDesc(0, rDoc.GetPageDesc(0));
+
+ // PageDescs "Convert..."
+ for (size_t i = nInPageDescOffset; i < rDoc.GetPageDescCnt(); ++i)
+ rDoc.ChgPageDesc(i, rDoc.GetPageDesc(i));
+}
+
+FrameDeleteWatch::FrameDeleteWatch(SwFrameFormat* pFormat)
+ : m_pFormat(pFormat)
+{
+ if(m_pFormat)
+ StartListening(pFormat->GetNotifier());
+}
+
+void FrameDeleteWatch::Notify(const SfxHint& rHint)
+{
+ bool bDying = false;
+ if (rHint.GetId() == SfxHintId::Dying)
+ bDying = true;
+ else if (rHint.GetId() == SfxHintId::SwDrawFrameFormat)
+ {
+ auto pDrawFrameFormatHint = static_cast<const sw::DrawFrameFormatHint*>(&rHint);
+ bDying = pDrawFrameFormatHint->m_eId == sw::DrawFrameFormatHintId::DYING;
+ }
+ if (bDying)
+ {
+ m_pFormat = nullptr;
+ EndListeningAll();
+ }
+}
+
+FrameDeleteWatch::~FrameDeleteWatch()
+{
+ m_pFormat = nullptr;
+ EndListeningAll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/basflt/iodetect.cxx b/sw/source/filter/basflt/iodetect.cxx
new file mode 100644
index 0000000000..8639510050
--- /dev/null
+++ b/sw/source/filter/basflt/iodetect.cxx
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <iodetect.hxx>
+#include <memory>
+#include <osl/endian.h>
+#include <sot/storage.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfile.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <unicode/ucsdet.h>
+
+using namespace ::com::sun::star;
+
+static bool IsDocShellRegistered()
+{
+ return SvtModuleOptions().IsWriter();
+}
+
+SwIoDetect aFilterDetect[] =
+{
+ SwIoDetect( FILTER_RTF ),
+ SwIoDetect( FILTER_BAS ),
+ SwIoDetect( sWW6 ),
+ SwIoDetect( FILTER_WW8 ),
+ SwIoDetect( sRtfWH ),
+ SwIoDetect( sHTML ),
+ SwIoDetect( sWW5 ),
+ SwIoDetect( FILTER_XML ),
+ SwIoDetect( FILTER_TEXT_DLG ),
+ SwIoDetect( FILTER_TEXT ),
+ SwIoDetect( FILTER_DOCX )
+};
+
+OUString SwIoSystem::GetSubStorageName( const SfxFilter& rFltr )
+{
+ // for StorageFilters also set the SubStorageName
+ const OUString& rUserData = rFltr.GetUserData();
+ if (rUserData == FILTER_XML ||
+ rUserData == FILTER_XMLV ||
+ rUserData == FILTER_XMLVW)
+ return "content.xml";
+ if (rUserData == sWW6 || rUserData == FILTER_WW8)
+ return "WordDocument";
+ return OUString();
+}
+
+std::shared_ptr<const SfxFilter> SwIoSystem::GetFilterOfFormat(std::u16string_view rFormatNm,
+ const SfxFilterContainer* pCnt)
+{
+ SfxFilterContainer aCntSw( sSWRITER );
+ SfxFilterContainer aCntSwWeb( sSWRITERWEB );
+ const SfxFilterContainer* pFltCnt = pCnt ? pCnt : ( IsDocShellRegistered() ? &aCntSw : &aCntSwWeb );
+
+ do {
+ if( pFltCnt )
+ {
+ SfxFilterMatcher aMatcher( pFltCnt->GetName() );
+ SfxFilterMatcherIter aIter( aMatcher );
+ std::shared_ptr<const SfxFilter> pFilter = aIter.First();
+ while ( pFilter )
+ {
+ if( pFilter->GetUserData() == rFormatNm )
+ return pFilter;
+ pFilter = aIter.Next();
+ }
+ }
+ if( pCnt || pFltCnt == &aCntSwWeb )
+ break;
+ pFltCnt = &aCntSwWeb;
+ } while( true );
+ return nullptr;
+}
+
+bool SwIoSystem::IsValidStgFilter( const css::uno::Reference < css::embed::XStorage >& rStg, const SfxFilter& rFilter)
+{
+ bool bRet = false;
+ try
+ {
+ SotClipboardFormatId nStgFormatId = SotStorage::GetFormatID( rStg );
+ bRet = rStg->isStreamElement( "content.xml" );
+ if ( bRet )
+ bRet = ( nStgFormatId != SotClipboardFormatId::NONE && ( rFilter.GetFormat() == nStgFormatId ) );
+ }
+ catch (const css::uno::Exception& )
+ {
+ }
+
+ return bRet;
+}
+
+bool SwIoSystem::IsValidStgFilter(SotStorage& rStg, const SfxFilter& rFilter)
+{
+ SotClipboardFormatId nStgFormatId = rStg.GetFormat();
+ /*#i8409# We cannot trust the clipboard id anymore :-(*/
+ if (rFilter.GetUserData() == FILTER_WW8 || rFilter.GetUserData() == sWW6)
+ nStgFormatId = SotClipboardFormatId::NONE;
+
+ bool bRet = ERRCODE_NONE == rStg.GetError() &&
+ ( nStgFormatId == SotClipboardFormatId::NONE || rFilter.GetFormat() == nStgFormatId ) &&
+ ( rStg.IsContained( SwIoSystem::GetSubStorageName( rFilter )) );
+ if( bRet )
+ {
+ /* Bug 53445 - there are Excel Docs w/o ClipBoardId! */
+ /* Bug 62703 - and also WinWord Docs w/o ClipBoardId! */
+ if (rFilter.GetUserData() == FILTER_WW8 || rFilter.GetUserData() == sWW6)
+ {
+ bRet = (rStg.IsContained("0Table")
+ || rStg.IsContained("1Table"))
+ == (rFilter.GetUserData() == FILTER_WW8);
+ if (bRet && !rFilter.IsAllowedAsTemplate())
+ {
+ tools::SvRef<SotStorageStream> xRef =
+ rStg.OpenSotStream("WordDocument",
+ StreamMode::STD_READ );
+ xRef->Seek(10);
+ sal_uInt8 nByte;
+ xRef->ReadUChar( nByte );
+ bRet = !(nByte & 1);
+ }
+ }
+ }
+ return bRet;
+}
+
+// Check the type of the stream (file) by searching for corresponding set of bytes.
+// If no known type is found, return ASCII for now!
+// Returns the internal FilterName.
+std::shared_ptr<const SfxFilter> SwIoSystem::GetFileFilter(const OUString& rFileName)
+{
+ SfxFilterContainer aCntSw( sSWRITER );
+ SfxFilterContainer aCntSwWeb( sSWRITERWEB );
+ const SfxFilterContainer* pFCntnr = IsDocShellRegistered() ? &aCntSw : &aCntSwWeb;
+
+ SfxFilterMatcher aMatcher( pFCntnr->GetName() );
+ SfxFilterMatcherIter aIter( aMatcher );
+ std::shared_ptr<const SfxFilter> pFilter = aIter.First();
+ if ( !pFilter )
+ return nullptr;
+
+ if (SotStorage::IsStorageFile(rFileName))
+ {
+ // package storage or OLEStorage based format
+ tools::SvRef<SotStorage> xStg;
+ INetURLObject aObj;
+ aObj.SetSmartProtocol( INetProtocol::File );
+ aObj.SetSmartURL( rFileName );
+ SfxMedium aMedium(aObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::STD_READ);
+
+ // templates should not get precedence over "normal" filters (#i35508, #i33168)
+ std::shared_ptr<const SfxFilter> pTemplateFilter;
+ if (aMedium.IsStorage())
+ {
+ uno::Reference<embed::XStorage> const xStor = aMedium.GetStorage();
+ if ( xStor.is() )
+ {
+ while ( pFilter )
+ {
+ if (pFilter->GetUserData().startsWith("C") && IsValidStgFilter(xStor, *pFilter ))
+ {
+ if (pFilter->IsOwnTemplateFormat())
+ {
+ // found template filter; maybe there's a "normal" one also
+ pTemplateFilter = pFilter;
+ }
+ else
+ return pFilter;
+ }
+
+ pFilter = aIter.Next();
+ }
+
+ // there's only a template filter that could be found
+ if ( pTemplateFilter )
+ pFilter = pTemplateFilter;
+ }
+ }
+ else
+ {
+ try
+ {
+ SvStream *const pStream = aMedium.GetInStream();
+ if ( pStream && SotStorage::IsStorageFile(pStream) )
+ xStg = new SotStorage( pStream, false );
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ }
+
+ if( xStg.is() && ( xStg->GetError() == ERRCODE_NONE ) )
+ {
+ while ( pFilter )
+ {
+ if (pFilter->GetUserData().startsWith("C") && IsValidStgFilter(*xStg, *pFilter))
+ {
+ if (pFilter->IsOwnTemplateFormat())
+ {
+ // found template filter; maybe there's a "normal" one also
+ pTemplateFilter = pFilter;
+ }
+ else
+ return pFilter;
+ }
+
+ pFilter = aIter.Next();
+ }
+
+ // there's only a template filter that could be found
+ if ( pTemplateFilter )
+ pFilter = pTemplateFilter;
+
+ }
+ }
+
+ return pFilter;
+ }
+
+ return SwIoSystem::GetFilterOfFormat(FILTER_TEXT);
+}
+
+rtl_TextEncoding SwIoSystem::GetTextEncoding(SvStream& rStrm)
+{
+ sal_Size nLen, nOrig;
+ char aBuf[4096];
+ nOrig = nLen = rStrm.ReadBytes(aBuf, sizeof(aBuf));
+
+ rtl_TextEncoding eCharSet;
+ const bool bRet = SwIoSystem::IsDetectableText(aBuf, nLen, &eCharSet, nullptr, nullptr, nullptr);
+ if (bRet && eCharSet != RTL_TEXTENCODING_DONTKNOW)
+ rStrm.SeekRel(-(tools::Long(nLen)));
+ else
+ rStrm.SeekRel(-(tools::Long(nOrig)));
+
+ return eCharSet;
+}
+
+bool SwIoSystem::IsDetectableText(const char* pBuf, sal_uLong &rLen,
+ rtl_TextEncoding *pCharSet, bool *pSwap, LineEnd *pLineEnd, bool *pBom)
+{
+ bool bSwap = false;
+ rtl_TextEncoding eCharSet = RTL_TEXTENCODING_DONTKNOW;
+ bool bLE = true;
+ bool bBom = false;
+ /*See if it's a known unicode type*/
+ if (rLen >= 2)
+ {
+ sal_uLong nHead=0;
+ if (rLen > 2 && sal_uInt8(pBuf[0]) == 0xEF && sal_uInt8(pBuf[1]) == 0xBB &&
+ sal_uInt8(pBuf[2]) == 0xBF)
+ {
+ eCharSet = RTL_TEXTENCODING_UTF8;
+ nHead = 3;
+ bBom = true;
+ }
+ else if (sal_uInt8(pBuf[0]) == 0xFE && sal_uInt8(pBuf[1]) == 0xFF)
+ {
+ eCharSet = RTL_TEXTENCODING_UCS2;
+ bLE = false;
+ nHead = 2;
+ bBom = true;
+ }
+ else if (sal_uInt8(pBuf[1]) == 0xFE && sal_uInt8(pBuf[0]) == 0xFF)
+ {
+ eCharSet = RTL_TEXTENCODING_UCS2;
+ nHead = 2;
+ bBom = true;
+ }
+ pBuf+=nHead;
+ rLen-=nHead;
+ }
+ /*See unicode type again without BOM*/
+ if (rLen >= 1 && eCharSet == RTL_TEXTENCODING_DONTKNOW)
+ {
+ UErrorCode uerr = U_ZERO_ERROR;
+ UCharsetDetector* ucd = ucsdet_open(&uerr);
+ ucsdet_setText(ucd, pBuf, rLen, &uerr);
+ if (const UCharsetMatch* match = ucsdet_detect(ucd, &uerr))
+ {
+ const char* pEncodingName = ucsdet_getName(match, &uerr);
+
+ if (U_SUCCESS(uerr) && !strcmp("UTF-8", pEncodingName))
+ {
+ eCharSet = RTL_TEXTENCODING_UTF8; // UTF-8
+ }
+ else if (U_SUCCESS(uerr) && !strcmp("UTF-16BE", pEncodingName))
+ {
+ eCharSet = RTL_TEXTENCODING_UCS2; // UTF-16BE
+ bLE = false;
+ }
+ else if (U_SUCCESS(uerr) && !strcmp("UTF-16LE", pEncodingName))
+ {
+ eCharSet = RTL_TEXTENCODING_UCS2; // UTF-16LE
+ }
+ else if (U_SUCCESS(uerr) && !strcmp("GB18030", pEncodingName))
+ {
+ eCharSet = RTL_TEXTENCODING_GB_18030;
+ }
+ }
+
+ ucsdet_close(ucd);
+ }
+
+ bool bCR = false, bLF = false, bIsBareUnicode = false;
+
+ if (eCharSet != RTL_TEXTENCODING_DONTKNOW)
+ {
+ std::unique_ptr<sal_Unicode[]> aWork(new sal_Unicode[rLen+1]);
+ sal_Unicode *pNewBuf = aWork.get();
+ std::size_t nNewLen;
+ if (eCharSet != RTL_TEXTENCODING_UCS2)
+ {
+ nNewLen = rLen;
+ rtl_TextToUnicodeConverter hConverter =
+ rtl_createTextToUnicodeConverter(eCharSet);
+ rtl_TextToUnicodeContext hContext =
+ rtl_createTextToUnicodeContext(hConverter);
+
+ sal_Size nCntBytes;
+ sal_uInt32 nInfo;
+ nNewLen = rtl_convertTextToUnicode( hConverter, hContext, pBuf,
+ rLen, pNewBuf, nNewLen,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
+ RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT), &nInfo, &nCntBytes);
+
+ rtl_destroyTextToUnicodeContext(hConverter, hContext);
+ rtl_destroyTextToUnicodeConverter(hConverter);
+ }
+ else
+ {
+ nNewLen = rLen/2;
+ memcpy(pNewBuf, pBuf, rLen);
+#ifdef OSL_LITENDIAN
+ bool const bNativeLE = true;
+#else
+ bool const bNativeLE = false;
+#endif
+ if (bLE != bNativeLE)
+ {
+ bSwap = true;
+ char* pF = reinterpret_cast<char*>(pNewBuf);
+ char* pN = pF+1;
+ for(sal_uLong n = 0; n < nNewLen; ++n, pF+=2, pN+=2 )
+ {
+ char c = *pF;
+ *pF = *pN;
+ *pN = c;
+ }
+ }
+ }
+
+ for (sal_uLong nCnt = 0; nCnt < nNewLen; ++nCnt, ++pNewBuf)
+ {
+ switch (*pNewBuf)
+ {
+ case 0xA:
+ bLF = true;
+ break;
+ case 0xD:
+ bCR = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ for( sal_uLong nCnt = 0; nCnt < rLen; ++nCnt, ++pBuf )
+ {
+ switch (*pBuf)
+ {
+ case 0x0:
+ if( nCnt + 1 < rLen && !*(pBuf+1) )
+ return false;
+ bIsBareUnicode = true;
+ break;
+ case 0xA:
+ bLF = true;
+ break;
+ case 0xD:
+ bCR = true;
+ break;
+ case 0xC:
+ case 0x1A:
+ case 0x9:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ LineEnd eSysLE = GetSystemLineEnd();
+ LineEnd eLineEnd;
+ if (!bCR && !bLF)
+ eLineEnd = eSysLE;
+ else
+ eLineEnd = bCR ? ( bLF ? LINEEND_CRLF : LINEEND_CR ) : LINEEND_LF;
+
+ if (pCharSet)
+ *pCharSet = eCharSet;
+ if (pSwap)
+ *pSwap = bSwap;
+ if (pLineEnd)
+ *pLineEnd = eLineEnd;
+ if (pBom)
+ *pBom = bBom;
+
+ return !bIsBareUnicode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/basflt/shellio.cxx b/sw/source/filter/basflt/shellio.cxx
new file mode 100644
index 0000000000..888213aa9d
--- /dev/null
+++ b/sw/source/filter/basflt/shellio.cxx
@@ -0,0 +1,940 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <svl/fstathelper.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <o3tl/deleter.hxx>
+#include <officecfg/Office/Writer.hxx>
+#include <node.hxx>
+#include <docary.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtpdsc.hxx>
+#include <shellio.hxx>
+#include <iodetect.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <editsh.hxx>
+#include <undobj.hxx>
+#include <swundo.hxx>
+#include <swtable.hxx>
+#include <tblafmt.hxx>
+#include <tblsel.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <fltini.hxx>
+#include <docsh.hxx>
+#include <ndole.hxx>
+#include <ndtxt.hxx>
+#include <redline.hxx>
+#include <swerror.h>
+#include <pausethreadstarting.hxx>
+#include <frameformats.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+static bool sw_MergePortions(SwNode* pNode, void *)
+{
+ if (pNode->IsTextNode())
+ {
+ pNode->GetTextNode()->FileLoadedInitHints();
+ }
+ return true;
+}
+
+void SwAsciiOptions::Reset()
+{
+ m_sFont.clear();
+ m_eCRLF_Flag = GetSystemLineEnd();
+ m_eCharSet = ::osl_getThreadTextEncoding();
+ m_nLanguage = LANGUAGE_SYSTEM;
+ m_bIncludeBOM = true;
+ m_bIncludeHidden = !utl::ConfigManager::IsFuzzing() && officecfg::Office::Writer::FilterFlags::ASCII::IncludeHiddenText::get();
+}
+
+ErrCodeMsg SwReader::Read( const Reader& rOptions )
+{
+ // copy variables
+ Reader* po = const_cast<Reader*>(&rOptions);
+ po->m_pStream = mpStrm;
+ po->m_pStorage = mpStg;
+ po->m_xStorage = mxStg;
+ po->m_bInsertMode = nullptr != mpCursor;
+ po->m_bSkipImages = mbSkipImages;
+
+ // if a Medium is selected, get its Stream
+ if( nullptr != (po->m_pMedium = mpMedium ) &&
+ !po->SetStrmStgPtr() )
+ {
+ po->SetReadUTF8( false );
+ po->SetBlockMode( false );
+ po->SetOrganizerMode( false );
+ po->SetIgnoreHTMLComments( false );
+ return ERR_SWG_FILE_FORMAT_ERROR;
+ }
+
+ ErrCodeMsg nError = ERRCODE_NONE;
+
+ GetDoc();
+
+ // while reading, do not call OLE-Modified
+ Link<bool,void> aOLELink( mxDoc->GetOle2Link() );
+ mxDoc->SetOle2Link( Link<bool,void>() );
+
+ mxDoc->SetInReading( true );
+ mxDoc->SetInXMLImport( dynamic_cast< XMLReader* >(po) != nullptr );
+ mxDoc->SetInWriterfilterImport(mpMedium && mpMedium->GetFilter()
+ && (mpMedium->GetFilter()->GetUserData() == FILTER_RTF
+ || mpMedium->GetFilter()->GetUserData() == sRtfWH
+ || mpMedium->GetFilter()->GetUserData() == FILTER_DOCX));
+
+ SwPaM *pPam;
+ if( mpCursor )
+ pPam = mpCursor;
+ else
+ {
+ // if the Reader was not called by a Shell, create a PaM ourselves
+ pPam = new SwPaM( mxDoc->GetNodes().GetEndOfContent(), SwNodeOffset(-1) );
+ // For Web documents the default template was set already by InitNew,
+ // unless the filter is not HTML,
+ // or a SetTemplateName was called in ConvertFrom.
+ if( !mxDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) || ReadHTML != po || !po->mxTemplate.is() )
+ po->SetTemplate( *mxDoc );
+ }
+
+ // Pams are connected like rings; stop when we return to the 1st element
+ SwPaM *pEnd = pPam;
+
+ bool bReadPageDescs = false;
+ bool const bDocUndo = mxDoc->GetIDocumentUndoRedo().DoesUndo();
+ bool bSaveUndo = bDocUndo && mpCursor;
+ if( bSaveUndo )
+ {
+ // the reading of the page template cannot be undone!
+ bReadPageDescs = po->m_aOption.IsPageDescs();
+ if( bReadPageDescs )
+ {
+ bSaveUndo = false;
+ mxDoc->GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+ else
+ {
+ mxDoc->GetIDocumentUndoRedo().ClearRedo();
+ mxDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSDOKUMENT, nullptr );
+ }
+ }
+ mxDoc->GetIDocumentUndoRedo().DoUndo(false);
+
+ RedlineFlags eOld = mxDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ RedlineFlags ePostReadRedlineFlags( RedlineFlags::Ignore );
+
+ // Array of FlyFormats
+ SwFrameFormatsV aFlyFrameArr;
+ // only read templates? then ignore multi selection!
+ bool bFormatsOnly = po->m_aOption.IsFormatsOnly();
+
+ while( true )
+ {
+ std::unique_ptr<SwUndoInsDoc> pUndo;
+ if( bSaveUndo )
+ pUndo.reset(new SwUndoInsDoc( *pPam ));
+
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
+
+ std::optional<SwPaM> pUndoPam;
+ if( bDocUndo || mpCursor )
+ {
+ // set Pam to the previous node, so that it is not also moved
+ const SwNode& rTmp = pPam->GetPoint()->GetNode();
+ pUndoPam.emplace( rTmp, rTmp, SwNodeOffset(0), SwNodeOffset(-1) );
+ }
+
+ // store for now all Fly's
+ if( mpCursor )
+ {
+ aFlyFrameArr.insert( aFlyFrameArr.end(), mxDoc->GetSpzFrameFormats()->begin(),
+ mxDoc->GetSpzFrameFormats()->end() );
+ }
+
+ const sal_Int32 nSttContent = pPam->GetPoint()->GetContentIndex();
+
+ // make sure the End position is correct for all Readers
+ SwContentNode* pCNd = pPam->GetPointContentNode();
+ sal_Int32 nEndContent = pCNd ? pCNd->Len() - nSttContent : 0;
+ SwNodeIndex aEndPos( pPam->GetPoint()->GetNode(), 1 );
+
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ nError = po->Read( *mxDoc, msBaseURL, *pPam, maFileName );
+
+ // an ODF document may contain redline mode in settings.xml; save it!
+ ePostReadRedlineFlags = mxDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
+
+ if( ! nError.IsError() ) // set the End position already
+ {
+ --aEndPos;
+ pCNd = aEndPos.GetNode().GetContentNode();
+ if( !pCNd && nullptr == ( pCNd = SwNodes::GoPrevious( &aEndPos ) ))
+ pCNd = mxDoc->GetNodes().GoNext( &aEndPos );
+
+ const sal_Int32 nLen = pCNd->Len();
+ if( nLen < nEndContent )
+ nEndContent = 0;
+ else
+ nEndContent = nLen - nEndContent;
+ pPam->GetPoint()->Assign( *pCNd, nEndContent );
+
+ const SwStartNode* pTableBoxStart = pCNd->FindTableBoxStartNode();
+ if ( pTableBoxStart )
+ {
+ SwTableBox* pBox = pTableBoxStart->GetTableBox();
+ if ( pBox )
+ {
+ mxDoc->ChkBoxNumFormat( *pBox, true );
+ }
+ }
+ }
+
+ if( mpCursor )
+ {
+ *pUndoPam->GetMark() = *pPam->GetPoint();
+ pUndoPam->GetPoint()->Adjust(SwNodeOffset(1));
+ SwNode& rNd = pUndoPam->GetPointNode();
+ if( rNd.IsContentNode() )
+ pUndoPam->GetPoint()->SetContent( nSttContent );
+
+ bool bChkHeaderFooter = rNd.FindHeaderStartNode() ||
+ rNd.FindFooterStartNode();
+
+ // search all new Fly's, and store them as individual Undo Objects
+ for( sw::FrameFormats<sw::SpzFrameFormat*>::size_type n = 0; n < mxDoc->GetSpzFrameFormats()->size(); ++n )
+ {
+ SwFrameFormat* pFrameFormat = (*mxDoc->GetSpzFrameFormats())[ n ];
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ // ok, here IsAlive is a misnomer...
+ if (!aFlyFrameArr.IsAlive(pFrameFormat))
+ {
+ if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ // TODO: why is this not handled via SetInsertRange?
+ || SwUndoInserts::IsCreateUndoForNewFly(rAnchor,
+ pUndoPam->GetPoint()->GetNodeIndex(),
+ pUndoPam->GetMark()->GetNodeIndex()))
+ {
+ if( bChkHeaderFooter &&
+ (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) &&
+ RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ // DrawObjects are not allowed in Headers/Footers!
+ pFrameFormat->DelFrames();
+ mxDoc->DelFrameFormat( pFrameFormat );
+ --n;
+ }
+ else
+ {
+ if( bSaveUndo )
+ {
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ // UGLY: temp. enable undo
+ mxDoc->GetIDocumentUndoRedo().DoUndo(true);
+ mxDoc->GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsLayFormat>( pFrameFormat, SwNodeOffset(0), 0 ) );
+ mxDoc->GetIDocumentUndoRedo().DoUndo(false);
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
+ }
+ if( pFrameFormat->HasWriterListeners() )
+ {
+ // Draw-Objects create a Frame when being inserted; thus delete them
+ pFrameFormat->DelFrames();
+ }
+
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ {
+ if( !rAnchor.GetAnchorNode() )
+ {
+ pFrameFormat->MakeFrames();
+ }
+ else if( mpCursor )
+ {
+ mxDoc->SetContainsAtPageObjWithContentAnchor( true );
+ }
+ }
+ else
+ pFrameFormat->MakeFrames();
+ }
+ }
+ }
+ }
+ aFlyFrameArr.clear();
+
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ if( mxDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ mxDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pUndoPam ), true);
+ else
+ mxDoc->getIDocumentRedlineAccess().SplitRedline( *pUndoPam );
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
+ }
+ if( bSaveUndo )
+ {
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ pUndo->SetInsertRange( *pUndoPam, false );
+ // UGLY: temp. enable undo
+ mxDoc->GetIDocumentUndoRedo().DoUndo(true);
+ mxDoc->GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ mxDoc->GetIDocumentUndoRedo().DoUndo(false);
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
+ }
+
+ pUndoPam.reset();
+
+ pPam = pPam->GetNext();
+ if( pPam == pEnd )
+ break;
+
+ // only read templates? then ignore multi selection! Bug 68593
+ if( bFormatsOnly )
+ break;
+
+ /*
+ * !!! The Status of the Stream has to be reset directly. !!!
+ * When Seeking, the current Status-, EOF- and bad-Bit is set;
+ * nobody knows why
+ */
+ if( mpStrm )
+ {
+ mpStrm->Seek(0);
+ mpStrm->ResetError();
+ }
+ }
+
+ // fdo#52028: ODF file import does not result in MergePortions being called
+ // for every attribute, since that would be inefficient. So call it here.
+ // This is only necessary for formats that may contain RSIDs (ODF,MSO).
+ // It's too hard to figure out which nodes were inserted in Insert->File
+ // case (redlines, flys, footnotes, header/footer) so just do every node.
+ mxDoc->GetNodes().ForEach(&sw_MergePortions);
+
+ mxDoc->SetInReading( false );
+ mxDoc->SetInXMLImport( false );
+ mxDoc->SetInWriterfilterImport(false);
+
+ mxDoc->InvalidateNumRules();
+ mxDoc->UpdateNumRule();
+ mxDoc->ChkCondColls();
+ mxDoc->SetAllUniqueFlyNames();
+ // Clear unassigned cell styles, because they aren't needed anymore.
+ mxDoc->GetCellStyles().clear();
+
+ mxDoc->GetIDocumentUndoRedo().DoUndo(bDocUndo);
+ if (!bReadPageDescs && bSaveUndo )
+ {
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ mxDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSDOKUMENT, nullptr );
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore );
+ }
+
+ // delete Pam if it was created only for reading
+ if( !mpCursor )
+ {
+ delete pPam; // open a new one
+
+ // #i42634# Moved common code of SwReader::Read() and
+ // SwDocShell::UpdateLinks() to new SwDoc::UpdateLinks():
+ // ATM still with Update
+ mxDoc->getIDocumentLinksAdministration().UpdateLinks();
+
+ // not insert: set the redline mode read from settings.xml
+ eOld = ePostReadRedlineFlags & ~RedlineFlags::Ignore;
+
+ mxDoc->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
+ }
+
+ mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ mxDoc->SetOle2Link( aOLELink );
+
+ if( mpCursor ) // the document is now modified
+ mxDoc->getIDocumentState().SetModified();
+ // #i38810# - If links have been updated, the document
+ // have to be modified. During update of links the OLE link at the document
+ // isn't set. Thus, the document's modified state has to be set again after
+ // the OLE link is restored - see above <mxDoc->SetOle2Link( aOLELink )>.
+ if ( mxDoc->getIDocumentLinksAdministration().LinksUpdated() )
+ {
+ mxDoc->getIDocumentState().SetModified();
+ }
+
+ po->SetReadUTF8( false );
+ po->SetBlockMode( false );
+ po->SetOrganizerMode( false );
+ po->SetIgnoreHTMLComments( false );
+
+ return nError;
+}
+
+
+SwReader::SwReader(SfxMedium& rMedium, OUString aFileName, SwDoc *pDocument)
+ : SwDocFac(pDocument), mpStrm(nullptr), mpMedium(&rMedium), mpCursor(nullptr),
+ maFileName(std::move(aFileName)), mbSkipImages(false)
+{
+ SetBaseURL( rMedium.GetBaseURL() );
+ SetSkipImages( rMedium.IsSkipImages() );
+}
+
+
+// Read into an existing document
+SwReader::SwReader(SvStream& rStrm, OUString aFileName, const OUString& rBaseURL, SwPaM& rPam)
+ : SwDocFac(&rPam.GetDoc()), mpStrm(&rStrm), mpMedium(nullptr), mpCursor(&rPam),
+ maFileName(std::move(aFileName)), mbSkipImages(false)
+{
+ SetBaseURL( rBaseURL );
+}
+
+SwReader::SwReader(SfxMedium& rMedium, OUString aFileName, SwPaM& rPam)
+ : SwDocFac(&rPam.GetDoc()), mpStrm(nullptr), mpMedium(&rMedium),
+ mpCursor(&rPam), maFileName(std::move(aFileName)), mbSkipImages(false)
+{
+ SetBaseURL( rMedium.GetBaseURL() );
+}
+
+SwReader::SwReader( uno::Reference < embed::XStorage > xStg, OUString aFilename, SwPaM &rPam )
+ : SwDocFac(&rPam.GetDoc()), mpStrm(nullptr), mxStg( std::move(xStg) ), mpMedium(nullptr), mpCursor(&rPam), maFileName(std::move(aFilename)), mbSkipImages(false)
+{
+}
+
+Reader::Reader()
+ : m_aDateStamp( Date::EMPTY ),
+ m_aTimeStamp( tools::Time::EMPTY ),
+ m_aCheckDateTime( DateTime::EMPTY ),
+ m_pStream(nullptr), m_pMedium(nullptr), m_bInsertMode(false),
+ m_bTemplateBrowseMode(false), m_bReadUTF8(false), m_bBlockMode(false), m_bOrganizerMode(false),
+ m_bHasAskTemplateName(false), m_bIgnoreHTMLComments(false), m_bSkipImages(false)
+{
+}
+
+Reader::~Reader()
+{
+}
+
+OUString Reader::GetTemplateName(SwDoc& /*rDoc*/) const
+{
+ return OUString();
+}
+
+// load the Filter template, set and release
+SwDoc* Reader::GetTemplateDoc(SwDoc& rDoc)
+{
+ if( !m_bHasAskTemplateName )
+ {
+ SetTemplateName( GetTemplateName(rDoc) );
+ m_bHasAskTemplateName = true;
+ }
+
+ if( m_aTemplateName.isEmpty() )
+ ClearTemplate();
+ else
+ {
+ INetURLObject aTDir( m_aTemplateName );
+ const OUString aFileName = aTDir.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ OSL_ENSURE( !aTDir.HasError(), "No absolute path for template name!" );
+ DateTime aCurrDateTime( DateTime::SYSTEM );
+ bool bLoad = false;
+
+ // if the template is already loaded, check once-a-minute if it has changed
+ if( !mxTemplate.is() || aCurrDateTime >= m_aCheckDateTime )
+ {
+ Date aTstDate( Date::EMPTY );
+ tools::Time aTstTime( tools::Time::EMPTY );
+ if( FStatHelper::GetModifiedDateTimeOfFile(
+ aTDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ &aTstDate, &aTstTime ) &&
+ ( !mxTemplate.is() || m_aDateStamp != aTstDate || m_aTimeStamp != aTstTime ))
+ {
+ bLoad = true;
+ m_aDateStamp = aTstDate;
+ m_aTimeStamp = aTstTime;
+ }
+
+ // only one minute later check if it has changed
+ m_aCheckDateTime = aCurrDateTime;
+ m_aCheckDateTime += tools::Time( 0, 1 );
+ }
+
+ if (bLoad)
+ {
+ ClearTemplate();
+ OSL_ENSURE( !mxTemplate.is(), "Who holds the template doc?" );
+
+ // If the writer module is not installed,
+ // we cannot create a SwDocShell. We could create a
+ // SwWebDocShell however, because this exists always
+ // for the help.
+ SvtModuleOptions aModuleOptions;
+ if (aModuleOptions.IsWriter())
+ {
+ SwDocShell *pDocSh = new SwDocShell(SfxObjectCreateMode::INTERNAL);
+ SfxObjectShellLock xDocSh = pDocSh;
+ if (pDocSh->DoInitNew())
+ {
+ mxTemplate = pDocSh->GetDoc();
+ mxTemplate->SetOle2Link( Link<bool,void>() );
+ // always FALSE
+ mxTemplate->GetIDocumentUndoRedo().DoUndo( false );
+ mxTemplate->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, m_bTemplateBrowseMode );
+ mxTemplate->RemoveAllFormatLanguageDependencies();
+
+ ReadXML->SetOrganizerMode( true );
+ SfxMedium aMedium( aFileName, StreamMode::NONE );
+ SwReader aRdr( aMedium, OUString(), mxTemplate.get() );
+ aRdr.Read( *ReadXML );
+ ReadXML->SetOrganizerMode( false );
+ }
+ }
+ }
+
+ OSL_ENSURE( !mxTemplate.is() || FStatHelper::IsDocument( aFileName ) || m_aTemplateName=="$$Dummy$$",
+ "TemplatePtr but no template exist!" );
+ }
+
+ return mxTemplate.get();
+}
+
+bool Reader::SetTemplate( SwDoc& rDoc )
+{
+ bool bRet = false;
+
+ GetTemplateDoc(rDoc);
+ if( mxTemplate.is() )
+ {
+ rDoc.RemoveAllFormatLanguageDependencies();
+ rDoc.ReplaceStyles( *mxTemplate );
+ rDoc.getIDocumentFieldsAccess().SetFixFields(nullptr);
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void Reader::ClearTemplate()
+{
+ mxTemplate.clear();
+}
+
+void Reader::SetTemplateName( const OUString& rDir )
+{
+ if( !rDir.isEmpty() && m_aTemplateName != rDir )
+ {
+ ClearTemplate();
+ m_aTemplateName = rDir;
+ }
+}
+
+void Reader::MakeHTMLDummyTemplateDoc()
+{
+ ClearTemplate();
+ mxTemplate = new SwDoc;
+ mxTemplate->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, m_bTemplateBrowseMode );
+ mxTemplate->getIDocumentDeviceAccess().getPrinter( true );
+ mxTemplate->RemoveAllFormatLanguageDependencies();
+ m_aCheckDateTime = Date( 1, 1, 2300 ); // year 2300 should be sufficient
+ m_aTemplateName = "$$Dummy$$";
+}
+
+// Users that do not need to open these Streams / Storages,
+// have to override this method
+bool Reader::SetStrmStgPtr()
+{
+ OSL_ENSURE( m_pMedium, "Where is the Media??" );
+
+ if( m_pMedium->IsStorage() )
+ {
+ if( SwReaderType::Storage & GetReaderType() )
+ {
+ m_xStorage = m_pMedium->GetStorage();
+ return true;
+ }
+ }
+ else
+ {
+ m_pStream = m_pMedium->GetInStream();
+ if ( m_pStream && SotStorage::IsStorageFile(m_pStream) && (SwReaderType::Storage & GetReaderType()) )
+ {
+ m_pStorage = new SotStorage( *m_pStream );
+ m_pStream = nullptr;
+ }
+ else if ( !(SwReaderType::Stream & GetReaderType()) )
+ {
+ m_pStream = nullptr;
+ return false;
+ }
+
+ return true;
+ }
+ return false;
+}
+
+SwReaderType Reader::GetReaderType()
+{
+ return SwReaderType::Stream;
+}
+
+void Reader::SetFltName( const OUString& )
+{
+}
+
+void Reader::ResetFrameFormatAttrs( SfxItemSet &rFrameSet )
+{
+ rFrameSet.Put( SvxLRSpaceItem(RES_LR_SPACE) );
+ rFrameSet.Put( SvxULSpaceItem(RES_UL_SPACE) );
+ rFrameSet.Put( SvxBoxItem(RES_BOX) );
+}
+
+void Reader::ResetFrameFormats( SwDoc& rDoc )
+{
+ sal_uInt16 const s_ids[3] = {
+ RES_POOLFRM_FRAME, RES_POOLFRM_GRAPHIC, RES_POOLFRM_OLE
+ };
+ for (sal_uInt16 i : s_ids)
+ {
+ SwFrameFormat *const pFrameFormat = rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( i );
+
+ pFrameFormat->ResetFormatAttr( RES_LR_SPACE );
+ pFrameFormat->ResetFormatAttr( RES_UL_SPACE );
+ pFrameFormat->ResetFormatAttr( RES_BOX );
+ }
+}
+
+ // read the sections of the document, which is equal to the medium.
+ // returns the count of it
+size_t Reader::GetSectionList(SfxMedium&, std::vector<OUString>&) const
+{
+ return 0;
+}
+
+bool SwReader::HasGlossaries( const Reader& rOptions )
+{
+ // copy variables
+ Reader* po = const_cast<Reader*>(&rOptions);
+ po->m_pStream = mpStrm;
+ po->m_pStorage = mpStg;
+ po->m_bInsertMode = false;
+
+ // if a Medium is selected, get its Stream
+ bool bRet = false;
+ if( nullptr == (po->m_pMedium = mpMedium ) || po->SetStrmStgPtr() )
+ bRet = po->HasGlossaries();
+ return bRet;
+}
+
+bool SwReader::ReadGlossaries( const Reader& rOptions,
+ SwTextBlocks& rBlocks, bool bSaveRelFiles )
+{
+ // copy variables
+ Reader* po = const_cast<Reader*>(&rOptions);
+ po->m_pStream = mpStrm;
+ po->m_pStorage = mpStg;
+ po->m_bInsertMode = false;
+
+ // if a Medium is selected, get its Stream
+ bool bRet = false;
+ if( nullptr == (po->m_pMedium = mpMedium ) || po->SetStrmStgPtr() )
+ bRet = po->ReadGlossaries( rBlocks, bSaveRelFiles );
+ return bRet;
+}
+
+bool Reader::HasGlossaries() const
+{
+ return false;
+}
+
+bool Reader::ReadGlossaries( SwTextBlocks&, bool ) const
+{
+ return false;
+}
+
+SwReaderType StgReader::GetReaderType()
+{
+ return SwReaderType::Storage;
+}
+
+/*
+ * Writer
+ */
+
+/*
+ * Constructors, Destructors are inline (inc/shellio.hxx).
+ */
+
+SwWriter::SwWriter(SvStream& rStrm, SwCursorShell &rShell, bool bInWriteAll)
+ : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(&rShell),
+ m_rDoc(*rShell.GetDoc()), m_bWriteAll(bInWriteAll)
+{
+}
+
+SwWriter::SwWriter(SvStream& rStrm,SwDoc &rDocument)
+ : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument),
+ m_bWriteAll(true)
+{
+}
+
+SwWriter::SwWriter(SvStream& rStrm, SwPaM& rPam, bool bInWriteAll)
+ : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(&rPam), m_pShell(nullptr),
+ m_rDoc(rPam.GetDoc()), m_bWriteAll(bInWriteAll)
+{
+}
+
+SwWriter::SwWriter( uno::Reference < embed::XStorage > xStg, SwDoc &rDocument)
+ : m_pStrm(nullptr), m_xStg( std::move(xStg) ), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument), m_bWriteAll(true)
+{
+}
+
+SwWriter::SwWriter(SfxMedium& rMedium, SwCursorShell &rShell, bool bInWriteAll)
+ : m_pStrm(nullptr), m_pMedium(&rMedium), m_pOutPam(nullptr), m_pShell(&rShell),
+ m_rDoc(*rShell.GetDoc()), m_bWriteAll(bInWriteAll)
+{
+}
+
+SwWriter::SwWriter(SfxMedium& rMedium, SwDoc &rDocument)
+ : m_pStrm(nullptr), m_pMedium(&rMedium), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument),
+ m_bWriteAll(true)
+{
+}
+
+static bool isFlyNode(const SwPaM& pam)
+{
+ return *pam.GetPoint() == *pam.GetMark()
+ && (pam.GetPoint()->GetNode().IsOLENode() || pam.GetPoint()->GetNode().IsGrfNode());
+}
+
+ErrCodeMsg SwWriter::Write( WriterRef const & rxWriter, const OUString* pRealFileName )
+{
+ // #i73788#
+ SwPauseThreadStarting aPauseThreadStarting;
+
+ bool bHasMark = false;
+ std::shared_ptr<SwUnoCursor> pTempCursor;
+ SwPaM * pPam;
+
+ rtl::Reference<SwDoc> xDoc;
+
+ if ( m_pShell && !m_bWriteAll && m_pShell->IsTableMode() )
+ {
+ m_bWriteAll = true;
+ xDoc = new SwDoc;
+
+ // Copy parts of a table:
+ // Create a table with the width of the original and copy the selected cells.
+ // The sizes are corrected by ratio.
+
+ // search the layout for cells
+ SwSelBoxes aBoxes;
+ GetTableSel( *m_pShell, aBoxes );
+ const SwTableNode* pTableNd = static_cast<const SwTableNode*>(aBoxes[0]->GetSttNd()->StartOfSectionNode());
+ SwNodeIndex aIdx( xDoc->GetNodes().GetEndOfExtras(), 2 );
+ SwContentNode *pNd = aIdx.GetNode().GetContentNode();
+ OSL_ENSURE( pNd, "Node not found" );
+ SwPosition aPos( aIdx, pNd, 0 );
+ pTableNd->GetTable().MakeCopy( *xDoc, aPos, aBoxes );
+ }
+
+ if( !m_bWriteAll && ( m_pShell || m_pOutPam ))
+ {
+ if( m_pShell )
+ pPam = m_pShell->GetCursor();
+ else
+ pPam = m_pOutPam;
+
+ SwPaM *pEnd = pPam;
+
+ // 1st round: Check if there is a selection
+ do
+ {
+ bHasMark = pPam->HasMark() || isFlyNode(*pPam);
+ pPam = pPam->GetNext();
+ } while (!bHasMark && pPam != pEnd);
+
+ // if there is no selection, select the whole document
+ if(!bHasMark)
+ {
+ if( m_pShell )
+ {
+ m_pShell->Push();
+ m_pShell->SttEndDoc(true);
+ m_pShell->SetMark();
+ m_pShell->SttEndDoc(false);
+ }
+ else
+ {
+ pPam = new SwPaM( *pPam, pPam );
+ pPam->Move( fnMoveBackward, GoInDoc );
+ pPam->SetMark();
+ pPam->Move( fnMoveForward, GoInDoc );
+ }
+ }
+ // pPam is still the current Cursor !!
+ }
+ else
+ {
+ // no Shell or write-everything -> create a Pam
+ SwDoc* pOutDoc = xDoc.is() ? xDoc.get() : &m_rDoc;
+ pTempCursor = pOutDoc->CreateUnoCursor(
+ SwPosition(pOutDoc->GetNodes().GetEndOfContent()), false);
+ pPam = pTempCursor.get();
+ if( pOutDoc->IsClipBoard() )
+ {
+ pPam->Move( fnMoveBackward, GoInDoc );
+ pPam->SetMark();
+ pPam->Move( fnMoveForward, GoInDoc );
+ }
+ else
+ {
+ pPam->SetMark();
+ pPam->Move( fnMoveBackward, GoInDoc );
+ }
+ }
+
+ rxWriter->m_bWriteAll = m_bWriteAll;
+ SwDoc* pOutDoc = xDoc.is() ? xDoc.get() : &m_rDoc;
+
+ // If the default PageDesc has still the initial value,
+ // (e.g. if no printer was set) then set it to DIN A4.
+ // #i37248# - Modifications are only allowed at a new document.
+ // <pOutDoc> contains a new document, if <xDoc> is set - see above.
+ if ( xDoc.is() && !pOutDoc->getIDocumentDeviceAccess().getPrinter( false ) )
+ {
+ const SwPageDesc& rPgDsc = pOutDoc->GetPageDesc( 0 );
+ //const SwPageDesc& rPgDsc = *pOutDoc->GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ const SwFormatFrameSize& rSz = rPgDsc.GetMaster().GetFrameSize();
+ // Clipboard-Document is always created w/o printer; thus the
+ // default PageDesc is always aug LONG_MAX !! Set then to DIN A4
+ if( LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth() )
+ {
+ SwPageDesc aNew( rPgDsc );
+ SwFormatFrameSize aNewSz( rSz );
+ Size a4(SvxPaperInfo::GetPaperSize( PAPER_A4 ));
+ aNewSz.SetHeight( a4.Width() );
+ aNewSz.SetWidth( a4.Height() );
+ aNew.GetMaster().SetFormatAttr( aNewSz );
+ pOutDoc->ChgPageDesc( 0, aNew );
+ }
+ }
+
+ bool bLockedView(false);
+ SwEditShell* pESh = pOutDoc->GetEditShell();
+ if( pESh )
+ {
+ bLockedView = pESh->IsViewLocked();
+ pESh->LockView( true ); //lock visible section
+ pESh->StartAllAction();
+ }
+
+ std::unique_ptr<PurgeGuard, o3tl::default_delete<PurgeGuard>> xGuard(new PurgeGuard(*pOutDoc));
+
+ pOutDoc->SetInWriting(true);
+ ErrCodeMsg nError = ERRCODE_NONE;
+ if( m_pMedium )
+ nError = rxWriter->Write( *pPam, *m_pMedium, pRealFileName );
+ else if( m_pStrm )
+ nError = rxWriter->Write( *pPam, *m_pStrm, pRealFileName );
+ else if( m_xStg.is() )
+ nError = rxWriter->Write( *pPam, m_xStg, pRealFileName );
+ pOutDoc->SetInWriting(false);
+
+ xGuard.reset();
+
+ if( pESh )
+ {
+ pESh->EndAllAction();
+ pESh->LockView( bLockedView );
+ }
+
+ // If the selection was only created for printing, reset the old cursor before returning
+ if( !m_bWriteAll && ( m_pShell || m_pOutPam ))
+ {
+ if(!bHasMark)
+ {
+ if( m_pShell )
+ m_pShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ else
+ delete pPam;
+ }
+ }
+ else
+ {
+ // Everything was written successfully? Tell the document!
+ if ( !nError.IsError() && !xDoc.is() )
+ {
+ m_rDoc.getIDocumentState().ResetModified();
+ // #i38810# - reset also flag, that indicates updated links
+ m_rDoc.getIDocumentLinksAdministration().SetLinksUpdated( false );
+ }
+ }
+
+ if ( xDoc.is() )
+ {
+ pTempCursor.reset();
+ xDoc.clear();
+ m_bWriteAll = false;
+ }
+
+ return nError;
+}
+
+bool SetHTMLTemplate( SwDoc & rDoc )
+{
+ // get template name of the Sfx-HTML-Filter !!!
+ if( !ReadHTML->GetTemplateDoc(rDoc) )
+ ReadHTML->MakeHTMLDummyTemplateDoc();
+
+ bool bRet = ReadHTML->SetTemplate( rDoc );
+
+ SwNodes& rNds = rDoc.GetNodes();
+ SwNodeIndex aIdx( rNds.GetEndOfExtras(), 1 );
+ SwContentNode* pCNd = rNds.GoNext( &aIdx );
+ if( pCNd )
+ {
+ pCNd->SetAttr
+ ( SwFormatPageDesc(rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_HTML, false) ) );
+ pCNd->ChgFormatColl( rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false ));
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/docx/swdocxreader.cxx b/sw/source/filter/docx/swdocxreader.cxx
new file mode 100644
index 0000000000..c77c4417f7
--- /dev/null
+++ b/sw/source/filter/docx/swdocxreader.cxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "swdocxreader.hxx"
+
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <swerror.h>
+#include <unotools/streamwrap.hxx>
+#include <unotextrange.hxx>
+#include <sfx2/docfile.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+constexpr OUString AUTOTEXT_GALLERY = u"autoTxt"_ustr;
+
+using namespace css;
+
+extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOCX()
+{
+ return new SwDOCXReader;
+}
+
+ErrCodeMsg SwDOCXReader::Read(SwDoc& rDoc, const OUString& /* rBaseURL */, SwPaM& rPam, const OUString& /* FileName */ )
+{
+ if (!m_pMedium->GetInStream())
+ return ERR_SWG_READ_ERROR;
+
+ // We want to work in an empty paragraph.
+ const SwPosition* pPos = rPam.GetPoint();
+ rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
+ rDoc.SetTextFormatColl(rPam, rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false));
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter"), uno::UNO_SET_THROW);
+
+ SwDocShell* pDocShell(rDoc.GetDocShell());
+ uno::Reference<lang::XComponent> xDstDoc(pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ xImporter->setTargetDocument(xDstDoc);
+
+ const rtl::Reference<SwXTextRange> xInsertTextRange = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
+ uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*m_pMedium->GetInStream()));
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ pDocShell->SetLoading(SfxLoadedFlags::NONE);
+
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "InsertMode", uno::Any(true) },
+ { "TextInsertModeRange", uno::Any(uno::Reference<text::XTextRange>(xInsertTextRange)) }
+ }));
+
+ ErrCode ret = ERRCODE_NONE;
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ try
+ {
+ xFilter->filter(aDescriptor);
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.docx", "SwDOCXReader::Read()");
+ ret = ERR_SWG_READ_ERROR;
+ }
+ pDocShell->SetLoading(SfxLoadedFlags::ALL);
+
+ return ret;
+}
+
+SwReaderType SwDOCXReader::GetReaderType()
+{
+ return SwReaderType::Storage | SwReaderType::Stream;
+}
+
+bool SwDOCXReader::HasGlossaries() const
+{
+ // TODO
+ return true;
+}
+
+bool SwDOCXReader::ReadGlossaries( SwTextBlocks& rBlocks, bool /* bSaveRelFiles */ ) const
+{
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+ comphelper::getProcessServiceFactory() );
+
+ uno::Reference<uno::XInterface> xInterface(
+ xMultiServiceFactory->createInstance( "com.sun.star.comp.Writer.WriterFilter" ),
+ uno::UNO_SET_THROW );
+
+ uno::Reference<document::XFilter> xFilter( xInterface, uno::UNO_QUERY_THROW );
+ uno::Reference<document::XImporter> xImporter( xFilter, uno::UNO_QUERY_THROW );
+
+ SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::INTERNAL ) );
+ if( xDocSh->DoInitNew() )
+ {
+ uno::Reference<lang::XComponent> xDstDoc( xDocSh->GetModel(), uno::UNO_QUERY_THROW );
+ xImporter->setTargetDocument( xDstDoc );
+
+ uno::Reference<io::XStream> xStream( new utl::OStreamWrapper( *m_pMedium->GetInStream() ) );
+
+ uno::Sequence<beans::PropertyValue> aDescriptor( comphelper::InitPropertySequence({
+ { "InputStream", uno::Any(xStream) },
+ { "ReadGlossaries", uno::Any(true) }
+ }));
+
+ if( xFilter->filter( aDescriptor ) )
+ {
+ if (rBlocks.StartPutMuchBlockEntries())
+ {
+ bool bRet = MakeEntries(static_cast<SwDocShell*>(&xDocSh)->GetDoc(), rBlocks);
+ rBlocks.EndPutMuchBlockEntries();
+ return bRet;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SwDOCXReader::MakeEntries( SwDoc *pD, SwTextBlocks &rBlocks )
+{
+ const OUString aOldURL( rBlocks.GetBaseURL() );
+ rBlocks.SetBaseURL( OUString() );
+
+ bool bRet = false;
+
+ SwNodeIndex aDocEnd( pD->GetNodes().GetEndOfContent() );
+ SwNodeIndex aStart( *aDocEnd.GetNode().StartOfSectionNode(), 1 );
+ bool bIsAutoText = false;
+
+ if( aStart < aDocEnd && ( aDocEnd.GetIndex() - aStart.GetIndex() > SwNodeOffset(2) ) )
+ {
+ SwTextFormatColl* pColl = pD->getIDocumentStylePoolAccess().GetTextCollFromPool
+ (RES_POOLCOLL_STANDARD, false);
+ SwContentNode* pCNd = nullptr;
+ bRet = true;
+ do {
+ // Get name - first paragraph
+ OUString aLNm;
+ {
+ SwPaM aPam( aStart );
+ aPam.GetPoint()->Adjust(SwNodeOffset(1));
+ aLNm = aPam.GetPointNode().GetTextNode()->GetText();
+
+ // is AutoText?
+ bIsAutoText = aLNm.startsWith(AUTOTEXT_GALLERY);
+ aLNm = aLNm.copy(AUTOTEXT_GALLERY.getLength() + 1);
+ }
+
+ // Do not copy name
+ ++aStart;
+
+ // Get content
+ SwPaM aPam( aStart );
+ {
+ SwNodeIndex aIdx( aPam.GetPoint()->GetNode(), SwNodeOffset(1) );
+ pCNd = aIdx.GetNode().GetTextNode();
+ if( nullptr == pCNd )
+ {
+ pCNd = pD->GetNodes().MakeTextNode( aIdx.GetNode(), pColl );
+ }
+ }
+
+ aPam.GetPoint()->Assign( *pCNd );
+ aPam.SetMark();
+ {
+ SwNodeIndex aIdx( *aStart.GetNode().EndOfSectionNode(), SwNodeOffset(-1) );
+ // don't add extra empty text node if exist (.dotx but not .dotm)
+ if( aIdx.GetNode().GetTextNode() &&
+ aIdx.GetNode().GetTextNode()->GetText().isEmpty() )
+ aIdx = aStart.GetNode().EndOfSectionIndex() - 2;
+ pCNd = aIdx.GetNode().GetContentNode();
+ if( nullptr == pCNd )
+ {
+ ++aIdx;
+ pCNd = pD->GetNodes().MakeTextNode( aIdx.GetNode(), pColl );
+ }
+ }
+ aPam.GetPoint()->Assign( *pCNd, pCNd->Len() );
+
+ if( bIsAutoText )
+ {
+ // Now we have the right selection for one entry
+ rBlocks.ClearDoc();
+
+ OUString sShortcut = aLNm;
+
+ // Need to check make sure the shortcut is not already being used
+ sal_Int32 nStart = 0;
+ sal_uInt16 nCurPos = rBlocks.GetIndex( sShortcut );
+
+ while( sal_uInt16(-1) != nCurPos )
+ {
+ // add a Number to it
+ sShortcut = aLNm + OUString::number( ++nStart );
+ nCurPos = rBlocks.GetIndex( sShortcut );
+ }
+
+ if( rBlocks.BeginPutDoc( sShortcut, sShortcut ) )
+ {
+ SwDoc* pGlDoc = rBlocks.GetDoc();
+ SwNodeIndex aIdx( pGlDoc->GetNodes().GetEndOfContent(), -1 );
+ pCNd = aIdx.GetNode().GetContentNode();
+ SwPosition aPos( aIdx, pCNd, pCNd ? pCNd->Len() : 0 );
+ pD->getIDocumentContentOperations().CopyRange(aPam, aPos, SwCopyFlags::CheckPosInFly);
+ rBlocks.PutDoc();
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+
+ aStart = aStart.GetNode().EndOfSectionIndex() + 1;
+ } while( aStart < aDocEnd && aStart.GetNode().IsStartNode() );
+ }
+
+ rBlocks.SetBaseURL( aOldURL );
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/docx/swdocxreader.hxx b/sw/source/filter/docx/swdocxreader.hxx
new file mode 100644
index 0000000000..0a8b8a91c4
--- /dev/null
+++ b/sw/source/filter/docx/swdocxreader.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_DOCX_SWDOCXREADER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_DOCX_SWDOCXREADER_HXX
+
+#include <shellio.hxx>
+
+/// Wrapper for the UNO DOCX import filter (in writerfilter) for autotext purposes.
+class SwDOCXReader : public StgReader
+{
+public:
+ virtual SwReaderType GetReaderType() override;
+
+ virtual bool HasGlossaries() const override;
+ virtual bool ReadGlossaries(SwTextBlocks& rBlocks, bool bSaveRelFiles) const override;
+
+private:
+ virtual ErrCodeMsg Read(SwDoc&, const OUString&, SwPaM&, const OUString&) override;
+
+ static bool MakeEntries(SwDoc* pD, SwTextBlocks& rBlocks);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/README b/sw/source/filter/html/README
new file mode 100644
index 0000000000..7b16fb0838
--- /dev/null
+++ b/sw/source/filter/html/README
@@ -0,0 +1,42 @@
+This filter is used when the "HTML (StarWriter)" filter is invoked.
+
+Import options:
+
+- FilterOptions: string
+
+ - xhtmlns=reqif-xhtml: actives the ReqIF mode
+
+- AllowedRTFOLEMimeTypes: sequence<string>
+
+ In case an (UNO) client wants to limit the accepted set of MIME types for
+ OLE objects in ReqIF mode, that's possible via this parameter.
+
+ Any MIME type is accepted by default.
+
+Export options:
+
+- FilterOptions: string
+
+ - SkipImages: skips images
+
+ - SkipHeaderFooter: skips the header and footer
+
+ - EmbedImages: inlines images
+
+ - XHTML: activates the XHTML mode
+
+ - xhtmlns=reqif-xhtml: actives the ReqIF mode
+
+- RTFOLEMimeType: string
+
+ Defines what MIME type to use for OLE objects in ReqIF mode, defaults to text/rtf.
+
+- ExportImagesAsOLE: boolean
+
+ Defines if images should be exported as OLE objects or bitmaps, defaults to
+ false.
+
+- ShapeDPI: long (32bit signed int)
+
+ Defines a custom DPI when converting vector shapes to bitmaps, defaults to
+ the system DPI (96 when not on HiDPI).
diff --git a/sw/source/filter/html/SwAppletImpl.cxx b/sw/source/filter/html/SwAppletImpl.cxx
new file mode 100644
index 0000000000..9dbca27483
--- /dev/null
+++ b/sw/source/filter/html/SwAppletImpl.cxx
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <SwAppletImpl.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <comphelper/classids.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <o3tl/string_view.hxx>
+#include <svtools/embedhlp.hxx>
+#include <tools/globname.hxx>
+#include <tools/urlobj.hxx>
+#include <hintids.hxx>
+
+using namespace com::sun::star;
+
+SwHtmlOptType SwApplet_Impl::GetOptionType( std::u16string_view rName, bool bApplet )
+{
+ SwHtmlOptType nType = bApplet ? SwHtmlOptType::PARAM : SwHtmlOptType::TAG;
+
+ switch( rName[0] )
+ {
+ case 'A':
+ case 'a':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_align ) ||
+ o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_alt ) )
+ nType = SwHtmlOptType::IGNORE;
+ else if( bApplet &&
+ (rName == u"ARCHIVE" || rName == u"ARCHIVES" ) )
+ nType = SwHtmlOptType::TAG;
+ break;
+ case 'C':
+ case 'c':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_class ) ||
+ (bApplet && (o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_code ) ||
+ o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_codebase ))) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'H':
+ case 'h':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_height ) )
+ nType = SwHtmlOptType::SIZE;
+ else if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_hspace ) ||
+ (!bApplet && o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SW_HTML_O_Hidden )) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'I':
+ case 'i':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_id ) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'M':
+ case 'm':
+ if( bApplet && o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_mayscript ) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'N':
+ case 'n':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_name ) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'O':
+ case 'o':
+ if( bApplet && rName == u"OBJECT" )
+ nType = SwHtmlOptType::TAG;
+ break;
+ case 'S':
+ case 's':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_style ) ||
+ (!bApplet && o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_src )) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'T':
+ case 't':
+ if( !bApplet && o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_type ) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'V':
+ case 'v':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_vspace ) )
+ nType = SwHtmlOptType::IGNORE;
+ break;
+ case 'W':
+ case 'w':
+ if( o3tl::equalsIgnoreAsciiCase( rName, OOO_STRING_SVTOOLS_HTML_O_width ) )
+ nType = SwHtmlOptType::SIZE;
+ break;
+ }
+
+ return nType;
+}
+SwApplet_Impl::SwApplet_Impl( SfxItemPool& rPool ) :
+ m_aItemSet( rPool, svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1> )
+{
+}
+
+void SwApplet_Impl::CreateApplet( const OUString& rCode, const OUString& rName,
+ bool bMayScript, const OUString& rCodeBase,
+ std::u16string_view rDocumentBaseURL )
+{
+ comphelper::EmbeddedObjectContainer aCnt;
+ OUString aName;
+
+ // create Applet; it will be in running state
+ m_xApplet = aCnt.CreateEmbeddedObject( SvGlobalName( SO3_APPLET_CLASSID ).GetByteSequence(), aName );
+ (void)::svt::EmbeddedObjectRef::TryRunningState( m_xApplet );
+
+ INetURLObject aUrlBase(rDocumentBaseURL);
+ aUrlBase.removeSegment();
+
+ OUString sDocBase = aUrlBase.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ uno::Reference < beans::XPropertySet > xSet( m_xApplet->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ xSet->setPropertyValue("AppletCode", uno::Any( rCode ) );
+ xSet->setPropertyValue("AppletName", uno::Any( rName ) );
+ xSet->setPropertyValue("AppletIsScript", uno::Any( bMayScript ) );
+ xSet->setPropertyValue("AppletDocBase", uno::Any( sDocBase ) );
+ if ( !rCodeBase.isEmpty() )
+ xSet->setPropertyValue("AppletCodeBase", uno::Any( rCodeBase ) );
+ else
+ xSet->setPropertyValue("AppletCodeBase", uno::Any( sDocBase ) );
+ }
+}
+#if HAVE_FEATURE_JAVA
+bool SwApplet_Impl::CreateApplet( std::u16string_view rBaseURL )
+{
+ OUString aCode, aName, aCodeBase;
+ bool bMayScript = false;
+
+ size_t nArgCount = m_aCommandList.size();
+ for( size_t i = 0; i < nArgCount; i++ )
+ {
+ const SvCommand& rArg = m_aCommandList[i];
+ const OUString& rName = rArg.GetCommand();
+ if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_code ) )
+ aCode = rArg.GetArgument();
+ else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_codebase ) )
+ aCodeBase = INetURLObject::GetAbsURL( rBaseURL, rArg.GetArgument() );
+ else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_name ) )
+ aName = rArg.GetArgument();
+ else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_mayscript ) )
+ bMayScript = true;
+ }
+
+ if( aCode.isEmpty() )
+ return false;
+ CreateApplet( aCode, aName, bMayScript, aCodeBase, rBaseURL );
+ return true;
+}
+#endif
+
+SwApplet_Impl::~SwApplet_Impl()
+{
+}
+void SwApplet_Impl::FinishApplet()
+{
+ uno::Reference < beans::XPropertySet > xSet( m_xApplet->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ uno::Sequence < beans::PropertyValue > aProps;
+ m_aCommandList.FillSequence( aProps );
+ xSet->setPropertyValue("AppletCommands", uno::Any( aProps ) );
+ }
+}
+
+#if HAVE_FEATURE_JAVA
+void SwApplet_Impl::AppendParam( const OUString& rName, const OUString& rValue )
+{
+ m_aCommandList.Append( rName, rValue );
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx
new file mode 100644
index 0000000000..30ba8d3c0a
--- /dev/null
+++ b/sw/source/filter/html/css1atr.cxx
@@ -0,0 +1,3671 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <hintids.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/xmlencode.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/whiter.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <svx/xoutbmp.hxx>
+#include <svx/svdobj.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svl/urihelper.hxx>
+#include <unotools/charclass.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <charfmt.hxx>
+#include <fmtclds.hxx>
+#include <fmtcol.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtlsplt.hxx>
+#include <pagedesc.hxx>
+#include <fmtanchr.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <swtable.hxx>
+// NOTES
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+// FOOTNOTES
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swerror.h>
+#include <paratr.hxx>
+#include <frmatr.hxx>
+#include <poolfmt.hxx>
+#include "css1kywd.hxx"
+#include "wrthtml.hxx"
+#include "htmlnum.hxx"
+#include "css1atr.hxx"
+
+#include <IDocumentStylePoolAccess.hxx>
+#include <numrule.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <osl/diagnose.h>
+
+using namespace css;
+using editeng::SvxBorderLine;
+
+#define HTML_HEADSPACE (12*20)
+
+namespace {
+
+enum class Css1FrameSize {
+ NONE = 0x00,
+ Width = 0x01,
+ MinHeight = 0x02,
+ FixHeight = 0x04,
+ Pixel = 0x10,
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<Css1FrameSize> : is_typed_flags<Css1FrameSize, 0x17> {};
+}
+
+#define DOT_LEADERS_MAX_WIDTH 18
+
+static SwHTMLWriter& OutCSS1_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat,
+ IDocumentStylePoolAccess /*SwDoc*/ *pDoc, SwDoc *pTemplate );
+static SwHTMLWriter& OutCSS1_SwPageDesc( SwHTMLWriter& rWrt, const SwPageDesc& rFormat,
+ IDocumentStylePoolAccess /*SwDoc*/ *pDoc, SwDoc *pTemplate,
+ sal_uInt16 nRefPoolId, bool bExtRef,
+ bool bPseudo=true );
+static SwHTMLWriter& OutCSS1_SwFootnoteInfo( SwHTMLWriter& rWrt, const SwEndNoteInfo& rInfo,
+ SwDoc *pDoc, bool bHasNotes, bool bEndNote );
+static void OutCSS1_SwFormatDropAttrs( SwHTMLWriter& rHWrt,
+ const SwFormatDrop& rDrop,
+ const SfxItemSet *pCharFormatItemSet=nullptr );
+static SwHTMLWriter& OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( SwHTMLWriter& rWrt,
+ const SvxUnderlineItem *pUItem,
+ const SvxOverlineItem *pOItem,
+ const SvxCrossedOutItem *pCOItem,
+ const SvxBlinkItem *pBItem );
+static SwHTMLWriter& OutCSS1_SvxFontWeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+static SwHTMLWriter& OutCSS1_SvxPosture( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+static SwHTMLWriter& OutCSS1_SvxULSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+static SwHTMLWriter& OutCSS1_SvxFirstLineIndent(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
+static SwHTMLWriter& OutCSS1_SvxTextLeftMargin(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
+static SwHTMLWriter& OutCSS1_SvxRightMargin(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
+static SwHTMLWriter& OutCSS1_SvxLRSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
+ const SvxULSpaceItem *pULSpace,
+ const SvxLRSpaceItem *pLRSpace );
+static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
+ const SfxItemSet& rItemSet );
+static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
+ sw::Css1Background nMode,
+ const OUString *pGraphicName );
+static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+static SwHTMLWriter& OutCSS1_SwFormatFrameSize( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
+ Css1FrameSize nMode );
+static SwHTMLWriter& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( SwHTMLWriter& rWrt,
+ const SfxItemSet& rItemSet,
+ bool bDeep );
+static SwHTMLWriter& OutCSS1_SwFormatLayoutSplit( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+
+namespace
+{
+
+const char sCSS1_rule_end[] = " }";
+const char sCSS1_span_tag_end[] = "\">";
+const char cCSS1_style_opt_end = '\"';
+
+const char* const sHTML_FTN_fontheight = "57%";
+
+OString lclConvToHex(sal_uInt16 nHex)
+{
+ char aNToABuf[] = "00";
+
+ // set pointer to end of buffer
+ char *pStr = aNToABuf + (sizeof(aNToABuf)-1);
+ for( sal_uInt8 n = 0; n < 2; ++n )
+ {
+ *(--pStr) = static_cast<char>(nHex & 0xf ) + 48;
+ if( *pStr > '9' )
+ *pStr += 39;
+ nHex >>= 4;
+ }
+
+ return OString(aNToABuf, 2);
+}
+}
+
+bool IgnorePropertyForReqIF(bool bReqIF, std::string_view rProperty, std::string_view rValue,
+ std::optional<sw::Css1Background> oMode)
+{
+ if (!bReqIF)
+ return false;
+
+ if (oMode.has_value() && *oMode != sw::Css1Background::TableCell)
+ {
+ // Table or row.
+ if (rProperty == sCSS1_P_background && rValue == "transparent")
+ {
+ // This is the default already.
+ return true;
+ }
+
+ return false;
+ }
+
+ // Only allow these two keys, nothing else in ReqIF mode.
+ if (rProperty == sCSS1_P_text_decoration)
+ {
+ // Deny other text-decoration values (e.g. "none").
+ if (rValue == "underline" || rValue == "line-through")
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (rProperty == sCSS1_P_color)
+ return false;
+
+ return true;
+}
+
+OString GetCSS1_Color(const Color& rColor)
+{
+ return "#" + lclConvToHex(rColor.GetRed()) + lclConvToHex(rColor.GetGreen()) + lclConvToHex(rColor.GetBlue());
+}
+
+namespace {
+
+class SwCSS1OutMode
+{
+ SwHTMLWriter& rWrt;
+ sal_uInt16 nOldMode;
+
+public:
+
+ SwCSS1OutMode( SwHTMLWriter& rHWrt, sal_uInt16 nMode,
+ const OUString *pSelector ) :
+ rWrt( rHWrt ),
+ nOldMode( rHWrt.m_nCSS1OutMode )
+ {
+ rWrt.m_nCSS1OutMode = nMode;
+ rWrt.m_bFirstCSS1Property = true;
+ if( pSelector )
+ rWrt.m_aCSS1Selector = *pSelector;
+ }
+
+ ~SwCSS1OutMode()
+ {
+ rWrt.m_nCSS1OutMode = nOldMode;
+ }
+};
+
+}
+
+void SwHTMLWriter::OutCSS1_Property( std::string_view pProp,
+ std::string_view sVal,
+ const OUString *pSVal,
+ std::optional<sw::Css1Background> oMode )
+{
+ OString aPropertyValue(sVal);
+ if (aPropertyValue.isEmpty() && pSVal)
+ {
+ aPropertyValue = OUStringToOString(*pSVal, RTL_TEXTENCODING_UTF8);
+ }
+ if (IgnorePropertyForReqIF(mbReqIF, pProp, aPropertyValue, oMode))
+ return;
+
+ OStringBuffer sOut;
+
+ if( m_bFirstCSS1Rule && (m_nCSS1OutMode & CSS1_OUTMODE_RULE_ON)!=0 )
+ {
+ m_bFirstCSS1Rule = false;
+ OutNewLine();
+ sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_style " "
+ OOO_STRING_SVTOOLS_HTML_O_type "=\"text/css\">");
+ // Optional CSS2 code for dot leaders (dotted line between the Table of Contents titles and page numbers):
+ // (More information: http://www.w3.org/Style/Examples/007/leaders.en.html)
+ //
+ // p.leaders {
+ // /* FIXME:
+ // (1) dots line up vertically only in the paragraphs with the same alignment/level
+ // (2) max-width = 18 cm instead of 80em; possible improvement with the new CSS3 calc() */
+ // max-width: 18cm; /* note: need to overwrite max-width with max-width - border-left_of_the_actual_paragraph */
+ // padding: 0;
+ // overflow-x: hidden;
+ // line-height: 120%; /* note: avoid HTML scrollbars and missing descenders of the letters */
+ // }
+ // p.leaders:after {
+ // float: left;
+ // width: 0;
+ // white-space: nowrap;
+ // content: ". . . . . . . . . . . . . . . . . . ...";
+ // }
+ // p.leaders span:first-child {
+ // padding-right: 0.33em;
+ // background: white;
+ // }
+ // p.leaders span + span {
+ // float: right;
+ // padding-left: 0.33em;
+ // background: white;
+ // position: relative;
+ // z-index: 1
+ // }
+
+ if (m_bCfgPrintLayout) {
+ sOut.append(
+ "p." sCSS2_P_CLASS_leaders "{max-width:" + OString::number(DOT_LEADERS_MAX_WIDTH) +
+ "cm;padding:0;overflow-x:hidden;line-height:120%}"
+ "p." sCSS2_P_CLASS_leaders ":after{float:left;width:0;white-space:nowrap;content:\"");
+ for (int i = 0; i < 100; i++ )
+ sOut.append(". ");
+ sOut.append(
+ "\"}p." sCSS2_P_CLASS_leaders " span:first-child{padding-right:0.33em;background:white}"
+ "p." sCSS2_P_CLASS_leaders " span+span{float:right;padding-left:0.33em;"
+ "background:white;position:relative;z-index:1}");
+ }
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ IncIndentLevel();
+ }
+
+ if( m_bFirstCSS1Property )
+ {
+ switch( m_nCSS1OutMode & CSS1_OUTMODE_ANY_ON )
+ {
+ case CSS1_OUTMODE_SPAN_TAG_ON:
+ case CSS1_OUTMODE_SPAN_TAG1_ON:
+ if( m_bTagOn )
+ {
+ sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
+ " " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
+ }
+ else
+ {
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
+ return;
+ }
+ break;
+
+ case CSS1_OUTMODE_RULE_ON:
+ {
+ OutNewLine();
+ sOut.append(OUStringToOString(m_aCSS1Selector, RTL_TEXTENCODING_UTF8) + " { ");
+ }
+ break;
+
+ case CSS1_OUTMODE_STYLE_OPT_ON:
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
+ break;
+ }
+ m_bFirstCSS1Property = false;
+ }
+ else
+ {
+ sOut.append("; ");
+ }
+
+ sOut.append(pProp + OString::Concat(": "));
+ if( m_nCSS1OutMode & CSS1_OUTMODE_ENCODE )
+ {
+ // for STYLE-Option encode string
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ if( !sVal.empty() )
+ HTMLOutFuncs::Out_String( Strm(), OUString::createFromAscii(sVal) );
+ else if( pSVal )
+ HTMLOutFuncs::Out_String( Strm(), *pSVal );
+ }
+ else
+ {
+ // for STYLE-Tag print string directly
+ sOut.append(aPropertyValue);
+ }
+
+ if (!sOut.isEmpty())
+ Strm().WriteOString( sOut );
+}
+
+static void AddUnitPropertyValue(OStringBuffer &rOut, tools::Long nVal,
+ FieldUnit eUnit)
+{
+ if( nVal < 0 )
+ {
+ // special-case sign symbol
+ nVal = -nVal;
+ rOut.append('-');
+ }
+
+ o3tl::Length eTo;
+ int nFac; // used to get specific number of decimals
+ std::string_view pUnit;
+ switch( eUnit )
+ {
+ case FieldUnit::MM_100TH:
+ OSL_ENSURE( FieldUnit::MM == eUnit, "Measuring unit not supported" );
+ [[fallthrough]];
+ case FieldUnit::MM:
+ eTo = o3tl::Length::mm;
+ nFac = 100;
+ pUnit = sCSS1_UNIT_mm;
+ break;
+
+ case FieldUnit::M:
+ case FieldUnit::KM:
+ OSL_ENSURE( FieldUnit::CM == eUnit, "Measuring unit not supported" );
+ [[fallthrough]];
+ case FieldUnit::CM:
+ eTo = o3tl::Length::cm;
+ nFac = 100;
+ pUnit = sCSS1_UNIT_cm;
+ break;
+
+ case FieldUnit::TWIP:
+ OSL_ENSURE( FieldUnit::POINT == eUnit, "Measuring unit not supported" );
+ [[fallthrough]];
+ case FieldUnit::POINT:
+ eTo = o3tl::Length::pt;
+ nFac = 10;
+ pUnit = sCSS1_UNIT_pt;
+ break;
+
+ case FieldUnit::PICA:
+ eTo = o3tl::Length::pc;
+ nFac = 100;
+ pUnit = sCSS1_UNIT_pc;
+ break;
+
+ case FieldUnit::NONE:
+ case FieldUnit::FOOT:
+ case FieldUnit::MILE:
+ case FieldUnit::CUSTOM:
+ case FieldUnit::PERCENT:
+ case FieldUnit::INCH:
+ default:
+ OSL_ENSURE( FieldUnit::INCH == eUnit, "Measuring unit not supported" );
+ eTo = o3tl::Length::in;
+ nFac = 100;
+ pUnit = sCSS1_UNIT_inch;
+ break;
+ }
+
+ sal_Int64 nResult = o3tl::convert(nVal * nFac, o3tl::Length::twip, eTo);
+ rOut.append(nResult/nFac);
+ if ((nResult % nFac) != 0)
+ {
+ rOut.append('.');
+ while (nFac > 1 && (nResult % nFac) != 0)
+ {
+ nFac /= 10;
+ rOut.append((nResult / nFac) % 10);
+ }
+ }
+
+ rOut.append(pUnit);
+}
+
+void SwHTMLWriter::OutCSS1_UnitProperty( std::string_view pProp, tools::Long nVal )
+{
+ OStringBuffer sOut;
+ AddUnitPropertyValue(sOut, nVal, m_eCSS1Unit);
+ OutCSS1_PropertyAscii(pProp, sOut);
+}
+
+void SwHTMLWriter::OutCSS1_PixelProperty( std::string_view pProp, tools::Long nTwips )
+{
+ OString sOut(OString::number(ToPixel(nTwips)) + sCSS1_UNIT_px);
+ OutCSS1_PropertyAscii(pProp, sOut);
+}
+
+void SwHTMLWriter::OutStyleSheet( const SwPageDesc& rPageDesc )
+{
+ m_bFirstCSS1Rule = true;
+
+// Feature: PrintExt
+ if( IsHTMLMode(HTMLMODE_PRINT_EXT) )
+ {
+ const SwPageDesc *pFirstPageDesc = nullptr;
+ sal_uInt16 nFirstRefPoolId = RES_POOLPAGE_HTML;
+ m_bCSS1IgnoreFirstPageDesc = true;
+
+ // First we try to guess how the document is constructed.
+ // Allowed are only the templates: HTML, 1st page, left page, and right page.
+ // A first page is only exported, if it matches the template "1st page".
+ // Left and right pages are only exported, if their templates are linked.
+ // If other templates are used, only very simple cases are exported.
+ const SwPageDesc *pPageDesc = &rPageDesc;
+ const SwPageDesc *pFollow = rPageDesc.GetFollow();
+ if( RES_POOLPAGE_FIRST == pPageDesc->GetPoolFormatId() &&
+ pFollow != pPageDesc &&
+ !IsPoolUserFormat( pFollow->GetPoolFormatId() ) )
+ {
+ // the document has a first page
+ pFirstPageDesc = pPageDesc;
+ pPageDesc = pFollow;
+ pFollow = pPageDesc->GetFollow();
+ }
+
+ IDocumentStylePoolAccess* pStylePoolAccess = &getIDocumentStylePoolAccess();
+ if( pPageDesc == pFollow )
+ {
+ // The document is one-sided; no matter what page, we do not create a 2-sided doc.
+ // The attribute is exported relative to the HTML page template.
+ OutCSS1_SwPageDesc( *this, *pPageDesc, pStylePoolAccess, m_xTemplate.get(),
+ RES_POOLPAGE_HTML, true, false );
+ nFirstRefPoolId = pFollow->GetPoolFormatId();
+ }
+ else if( (RES_POOLPAGE_LEFT == pPageDesc->GetPoolFormatId() &&
+ RES_POOLPAGE_RIGHT == pFollow->GetPoolFormatId()) ||
+ (RES_POOLPAGE_RIGHT == pPageDesc->GetPoolFormatId() &&
+ RES_POOLPAGE_LEFT == pFollow->GetPoolFormatId()) )
+ {
+ // the document is double-sided
+ OutCSS1_SwPageDesc( *this, *pPageDesc, pStylePoolAccess, m_xTemplate.get(),
+ RES_POOLPAGE_HTML, true );
+ OutCSS1_SwPageDesc( *this, *pFollow, pStylePoolAccess, m_xTemplate.get(),
+ RES_POOLPAGE_HTML, true );
+ nFirstRefPoolId = RES_POOLPAGE_RIGHT;
+ m_bCSS1IgnoreFirstPageDesc = false;
+ }
+ // other cases we miss
+
+ if( pFirstPageDesc )
+ OutCSS1_SwPageDesc( *this, *pFirstPageDesc, pStylePoolAccess, m_xTemplate.get(),
+ nFirstRefPoolId, false );
+ }
+
+ // The text body style has to be exported always (if it is changed compared
+ // to the template), because it is used as reference for any style
+ // that maps to <P>, and that's especially the standard style
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
+
+ // the Default-TextStyle is not also exported !!
+ // 0-Style is the Default; is never exported !!
+ const size_t nTextFormats = m_pDoc->GetTextFormatColls()->size();
+ for( size_t i = 1; i < nTextFormats; ++i )
+ {
+ const SwTextFormatColl* pColl = (*m_pDoc->GetTextFormatColls())[i];
+ sal_uInt16 nPoolId = pColl->GetPoolFormatId();
+ if( nPoolId == RES_POOLCOLL_TEXT || m_pDoc->IsUsed( *pColl ) )
+ OutCSS1_SwFormat( *this, *pColl, &m_pDoc->getIDocumentStylePoolAccess(), m_xTemplate.get() );
+ }
+
+ // the Default-TextStyle is not also exported !!
+ const size_t nCharFormats = m_pDoc->GetCharFormats()->size();
+ for( size_t i = 1; i < nCharFormats; ++i )
+ {
+ const SwCharFormat *pCFormat = (*m_pDoc->GetCharFormats())[i];
+ sal_uInt16 nPoolId = pCFormat->GetPoolFormatId();
+ if( nPoolId == RES_POOLCHR_INET_NORMAL ||
+ nPoolId == RES_POOLCHR_INET_VISIT ||
+ m_pDoc->IsUsed( *pCFormat ) )
+ OutCSS1_SwFormat( *this, *pCFormat, &m_pDoc->getIDocumentStylePoolAccess(), m_xTemplate.get() );
+ }
+
+ bool bHasEndNotes {false};
+ bool bHasFootNotes {false};
+ const SwFootnoteIdxs& rIdxs = m_pDoc->GetFootnoteIdxs();
+ for( auto pIdx : rIdxs )
+ {
+ if( pIdx->GetFootnote().IsEndNote() )
+ {
+ bHasEndNotes = true;
+ if (bHasFootNotes)
+ break;
+ }
+ else
+ {
+ bHasFootNotes = true;
+ if (bHasEndNotes)
+ break;
+ }
+ }
+ OutCSS1_SwFootnoteInfo( *this, m_pDoc->GetFootnoteInfo(), m_pDoc, bHasFootNotes, false );
+ OutCSS1_SwFootnoteInfo( *this, m_pDoc->GetEndNoteInfo(), m_pDoc, bHasEndNotes, true );
+
+ if( !m_bFirstCSS1Rule )
+ {
+ DecIndentLevel();
+
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_style), false );
+ }
+ else
+ {
+ m_bFirstCSS1Rule = false;
+ }
+
+ m_nDfltTopMargin = 0;
+ m_nDfltBottomMargin = 0;
+}
+
+// if pPseudo is set, Styles-Sheets will be exported;
+// otherwise we only search for Token and Class for a Format
+sal_uInt16 SwHTMLWriter::GetCSS1Selector( const SwFormat *pFormat, OString& rToken,
+ OUString& rClass, sal_uInt16& rRefPoolId,
+ OUString *pPseudo )
+{
+ sal_uInt16 nDeep = 0;
+ rToken.clear();
+ rClass.clear();
+ rRefPoolId = 0;
+ if( pPseudo )
+ pPseudo->clear();
+
+ bool bChrFormat = RES_CHRFMT==pFormat->Which();
+
+ // search formats above for the nearest standard or HTML-Tag template
+ const SwFormat *pPFormat = pFormat;
+ while( pPFormat && !pPFormat->IsDefault() )
+ {
+ bool bStop = false;
+ sal_uInt16 nPoolId = pPFormat->GetPoolFormatId();
+ if( USER_FMT & nPoolId )
+ {
+ // user templates
+ const OUString& aNm(pPFormat->GetName());
+
+ if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_blockquote)
+ {
+ rRefPoolId = RES_POOLCOLL_HTML_BLOCKQUOTE;
+ rToken = OOO_STRING_SVTOOLS_HTML_blockquote ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_citation)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_CITATION;
+ rToken = OOO_STRING_SVTOOLS_HTML_citation ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_code)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_CODE;
+ rToken = OOO_STRING_SVTOOLS_HTML_code ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_definstance)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_DEFINSTANCE;
+ rToken = OOO_STRING_SVTOOLS_HTML_definstance ""_ostr;
+ }
+ else if (!bChrFormat && (aNm == OOO_STRING_SVTOOLS_HTML_dd ||
+ aNm == OOO_STRING_SVTOOLS_HTML_dt))
+ {
+ sal_uInt16 nDefListLvl = GetDefListLvl(aNm, nPoolId);
+ // Export the templates DD 1/DT 1,
+ // but none of their derived templates,
+ // also not DD 2/DT 2 etc.
+ if (nDefListLvl)
+ {
+ if (pPseudo && (nDeep || (nDefListLvl & 0x0fff) > 1))
+ {
+ bStop = true;
+ }
+ else if (nDefListLvl & HTML_DLCOLL_DD)
+ {
+ rRefPoolId = RES_POOLCOLL_HTML_DD;
+ rToken = OOO_STRING_SVTOOLS_HTML_dd ""_ostr;
+ }
+ else
+ {
+ rRefPoolId = RES_POOLCOLL_HTML_DT;
+ rToken = OOO_STRING_SVTOOLS_HTML_dt ""_ostr;
+ }
+ }
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_emphasis)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_EMPHASIS;
+ rToken = OOO_STRING_SVTOOLS_HTML_emphasis ""_ostr;
+ }
+ else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_horzrule)
+ {
+ // do not export HR !
+ bStop = (nDeep==0);
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_keyboard)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_KEYBOARD;
+ rToken = OOO_STRING_SVTOOLS_HTML_keyboard ""_ostr;
+ }
+ else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_listing)
+ {
+ // Export Listings as PRE or PRE-derived template
+ rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
+ rRefPoolId = RES_POOLCOLL_HTML_PRE;
+ nDeep = CSS1_FMT_CMPREF;
+ }
+ else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_preformtxt)
+ {
+ rRefPoolId = RES_POOLCOLL_HTML_PRE;
+ rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_sample)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_SAMPLE;
+ rToken = OOO_STRING_SVTOOLS_HTML_sample ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_strong)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_STRONG;
+ rToken = OOO_STRING_SVTOOLS_HTML_strong ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_teletype)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_TELETYPE;
+ rToken = OOO_STRING_SVTOOLS_HTML_teletype ""_ostr;
+ }
+ else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_variable)
+ {
+ rRefPoolId = RES_POOLCHR_HTML_VARIABLE;
+ rToken = OOO_STRING_SVTOOLS_HTML_variable ""_ostr;
+ }
+ else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_xmp)
+ {
+ // export XMP as PRE (but not the template as Style)
+ rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
+ rRefPoolId = RES_POOLCOLL_HTML_PRE;
+ nDeep = CSS1_FMT_CMPREF;
+ }
+
+ // if a PoolId is set, the Name of the template is that of the related Token
+ OSL_ENSURE( (rRefPoolId != 0) == (!rToken.isEmpty()),
+ "Token missing" );
+ }
+ else
+ {
+ // Pool templates
+ switch( nPoolId )
+ {
+ // paragraph templates
+ case RES_POOLCOLL_HEADLINE_BASE:
+ case RES_POOLCOLL_STANDARD:
+ // do not export this template
+ bStop = (nDeep==0);
+ break;
+ case RES_POOLCOLL_TEXT:
+ rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ break;
+ case RES_POOLCOLL_HEADLINE1:
+ rToken = OOO_STRING_SVTOOLS_HTML_head1 ""_ostr;
+ break;
+ case RES_POOLCOLL_HEADLINE2:
+ rToken = OOO_STRING_SVTOOLS_HTML_head2 ""_ostr;
+ break;
+ case RES_POOLCOLL_HEADLINE3:
+ rToken = OOO_STRING_SVTOOLS_HTML_head3 ""_ostr;
+ break;
+ case RES_POOLCOLL_HEADLINE4:
+ rToken = OOO_STRING_SVTOOLS_HTML_head4 ""_ostr;
+ break;
+ case RES_POOLCOLL_HEADLINE5:
+ rToken = OOO_STRING_SVTOOLS_HTML_head5 ""_ostr;
+ break;
+ case RES_POOLCOLL_HEADLINE6:
+ rToken = OOO_STRING_SVTOOLS_HTML_head6 ""_ostr;
+ break;
+ case RES_POOLCOLL_SEND_ADDRESS:
+ rToken = OOO_STRING_SVTOOLS_HTML_address ""_ostr;
+ break;
+ case RES_POOLCOLL_HTML_BLOCKQUOTE:
+ rToken = OOO_STRING_SVTOOLS_HTML_blockquote ""_ostr;
+ break;
+ case RES_POOLCOLL_HTML_PRE:
+ rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
+ break;
+
+ case RES_POOLCOLL_HTML_DD:
+ rToken = OOO_STRING_SVTOOLS_HTML_dd ""_ostr;
+ break;
+ case RES_POOLCOLL_HTML_DT:
+ rToken = OOO_STRING_SVTOOLS_HTML_dt ""_ostr;
+ break;
+
+ case RES_POOLCOLL_TABLE:
+ if( pPseudo )
+ {
+ rToken = OOO_STRING_SVTOOLS_HTML_tabledata " "
+ OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ }
+ else
+ rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ break;
+ case RES_POOLCOLL_TABLE_HDLN:
+ if( pPseudo )
+ {
+ rToken = OOO_STRING_SVTOOLS_HTML_tableheader " "
+ OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ }
+ else
+ rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ break;
+ case RES_POOLCOLL_HTML_HR:
+ // do not export HR !
+ bStop = (nDeep==0);
+ break;
+ case RES_POOLCOLL_FOOTNOTE:
+ if( !nDeep )
+ {
+ rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ rClass = OOO_STRING_SVTOOLS_HTML_sdfootnote;
+ rRefPoolId = RES_POOLCOLL_TEXT;
+ nDeep = CSS1_FMT_CMPREF;
+ }
+ break;
+ case RES_POOLCOLL_ENDNOTE:
+ if( !nDeep )
+ {
+ rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ rClass = OOO_STRING_SVTOOLS_HTML_sdendnote;
+ rRefPoolId = RES_POOLCOLL_TEXT;
+ nDeep = CSS1_FMT_CMPREF;
+ }
+ break;
+
+ // character templates
+ case RES_POOLCHR_HTML_EMPHASIS:
+ rToken = OOO_STRING_SVTOOLS_HTML_emphasis ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_CITATION:
+ rToken = OOO_STRING_SVTOOLS_HTML_citation ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_STRONG:
+ rToken = OOO_STRING_SVTOOLS_HTML_strong ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_CODE:
+ rToken = OOO_STRING_SVTOOLS_HTML_code ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_SAMPLE:
+ rToken = OOO_STRING_SVTOOLS_HTML_sample ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_KEYBOARD:
+ rToken = OOO_STRING_SVTOOLS_HTML_keyboard ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_VARIABLE:
+ rToken = OOO_STRING_SVTOOLS_HTML_variable ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_DEFINSTANCE:
+ rToken = OOO_STRING_SVTOOLS_HTML_definstance ""_ostr;
+ break;
+ case RES_POOLCHR_HTML_TELETYPE:
+ rToken = OOO_STRING_SVTOOLS_HTML_teletype ""_ostr;
+ break;
+
+ case RES_POOLCHR_INET_NORMAL:
+ if( pPseudo )
+ {
+ rToken = OOO_STRING_SVTOOLS_HTML_anchor ""_ostr;
+ *pPseudo = OStringToOUString( sCSS1_link, RTL_TEXTENCODING_ASCII_US );
+ }
+ break;
+ case RES_POOLCHR_INET_VISIT:
+ if( pPseudo )
+ {
+ rToken = OOO_STRING_SVTOOLS_HTML_anchor ""_ostr;
+ *pPseudo = OStringToOUString( sCSS1_visited, RTL_TEXTENCODING_ASCII_US );
+ }
+ break;
+ }
+
+ // if a token is set, PoolId contains the related template
+ if( !rToken.isEmpty() && !rRefPoolId )
+ rRefPoolId = nPoolId;
+ }
+
+ if( !rToken.isEmpty() || bStop )
+ {
+ // stop if a HTML-Tag template was found
+ break;
+ }
+ else
+ {
+ // continue otherwise
+ nDeep++;
+ pPFormat = pPFormat->DerivedFrom();
+ }
+ }
+
+ if( !rToken.isEmpty() )
+ {
+ // this is a HTML-Tag template
+ if( !nDeep )
+ nDeep = CSS1_FMT_ISTAG;
+ }
+ else
+ {
+ // this is not a HTML-Tag template nor derived from one
+ nDeep = 0;
+ }
+ if( nDeep > 0 && nDeep < CSS1_FMT_SPECIAL )
+ {
+ // If the template is derived from a HTML template,
+ // we export it as <TOKEN>.<CLASS>, otherwise as .<CLASS>.
+ // <CLASS> is the name of the template after removing all characters
+ // before and including the first '.'
+ rClass = pFormat->GetName();
+ sal_Int32 nPos = rClass.indexOf( '.' );
+ if( nPos >= 0 && rClass.getLength() > nPos+1 )
+ {
+ rClass = rClass.replaceAt( 0, nPos+1, u"" );
+ }
+
+ rClass = GetAppCharClass().lowercase( rClass );
+ rClass = rClass.replaceAll( ".", "-" );
+ rClass = rClass.replaceAll( " ", "-" );
+ rClass = rClass.replaceAll( "_", "-" );
+ }
+
+ return nDeep;
+}
+
+static sal_uInt16 GetCSS1Selector( const SwFormat *pFormat, OUString& rSelector,
+ sal_uInt16& rRefPoolId )
+{
+ OString aToken;
+ OUString aClass;
+ OUString aPseudo;
+
+ sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass,
+ rRefPoolId, &aPseudo );
+ if( nDeep )
+ {
+ if( !aToken.isEmpty() )
+ rSelector = OStringToOUString(aToken, RTL_TEXTENCODING_ASCII_US);
+ else
+ rSelector.clear();
+
+ if( !aClass.isEmpty() )
+ rSelector += "." + aClass;
+ if( !aPseudo.isEmpty() )
+ rSelector += ":" + aPseudo;
+ }
+
+ return nDeep;
+}
+
+const SwFormat *SwHTMLWriter::GetTemplateFormat( sal_uInt16 nPoolFormatId,
+ IDocumentStylePoolAccess* pTemplate /*SwDoc *pTemplate*/)
+{
+ const SwFormat *pRefFormat = nullptr;
+
+ if( pTemplate )
+ {
+ OSL_ENSURE( !(USER_FMT & nPoolFormatId),
+ "No user templates found" );
+ if( POOLGRP_NOCOLLID & nPoolFormatId )
+ pRefFormat = pTemplate->GetCharFormatFromPool( nPoolFormatId );
+ else
+ pRefFormat = pTemplate->GetTextCollFromPool( nPoolFormatId, false );
+ }
+
+ return pRefFormat;
+}
+
+const SwFormat *SwHTMLWriter::GetParentFormat( const SwFormat& rFormat, sal_uInt16 nDeep )
+{
+ OSL_ENSURE( nDeep != USHRT_MAX, "Called GetParent for HTML-template!" );
+ const SwFormat *pRefFormat = nullptr;
+
+ if( nDeep > 0 )
+ {
+ // get the pointer for the HTML-Tag template, from which the template is derived
+ pRefFormat = &rFormat;
+ for( sal_uInt16 i=nDeep; i>0; i-- )
+ pRefFormat = pRefFormat->DerivedFrom();
+
+ if( pRefFormat && pRefFormat->IsDefault() )
+ pRefFormat = nullptr;
+ }
+
+ return pRefFormat;
+}
+
+bool swhtml_css1atr_equalFontItems( const SfxPoolItem& r1, const SfxPoolItem& r2 )
+{
+ return static_cast<const SvxFontItem &>(r1).GetFamilyName() ==
+ static_cast<const SvxFontItem &>(r2).GetFamilyName() &&
+ static_cast<const SvxFontItem &>(r1).GetFamily() ==
+ static_cast<const SvxFontItem &>(r2).GetFamily();
+}
+
+void SwHTMLWriter::SubtractItemSet( SfxItemSet& rItemSet,
+ const SfxItemSet& rRefItemSet,
+ bool bSetDefaults,
+ bool bClearSame,
+ const SfxItemSet *pRefScriptItemSet )
+{
+ OSL_ENSURE( bSetDefaults || bClearSame,
+ "SwHTMLWriter::SubtractItemSet: No action for this Flag" );
+ SfxItemSet aRefItemSet( *rRefItemSet.GetPool(), rRefItemSet.GetRanges() );
+ aRefItemSet.Set( rRefItemSet );
+
+ // compare with the Attr-Set of the template
+ SfxWhichIter aIter( rItemSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ const SfxPoolItem *pRefItem, *pItem;
+ bool bItemSet = ( SfxItemState::SET ==
+ aIter.GetItemState( false, &pItem) );
+ bool bRefItemSet;
+
+ if( pRefScriptItemSet )
+ {
+ switch( nWhich )
+ {
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_LANGUAGE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_LANGUAGE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ case RES_CHRATR_CTL_FONT:
+ case RES_CHRATR_CTL_FONTSIZE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ case RES_CHRATR_CTL_POSTURE:
+ case RES_CHRATR_CTL_WEIGHT:
+ bRefItemSet = ( SfxItemState::SET ==
+ pRefScriptItemSet->GetItemState( nWhich, true, &pRefItem) );
+ break;
+ default:
+ bRefItemSet = ( SfxItemState::SET ==
+ aRefItemSet.GetItemState( nWhich, false, &pRefItem) );
+ break;
+ }
+ }
+ else
+ {
+ bRefItemSet = ( SfxItemState::SET ==
+ aRefItemSet.GetItemState( nWhich, false, &pRefItem) );
+ }
+
+ if( bItemSet )
+ {
+ if( (bClearSame || pRefScriptItemSet) && bRefItemSet &&
+ ( *pItem == *pRefItem ||
+ ((RES_CHRATR_FONT == nWhich ||
+ RES_CHRATR_CJK_FONT == nWhich ||
+ RES_CHRATR_CTL_FONT == nWhich) &&
+ swhtml_css1atr_equalFontItems( *pItem, *pRefItem )) ) )
+ {
+ // the Attribute is in both templates with the same value
+ // and does not need to be exported
+ rItemSet.ClearItem( nWhich );
+ }
+ }
+ else
+ {
+ if( (bSetDefaults || pRefScriptItemSet) && bRefItemSet )
+ {
+ // the Attribute exists only in the reference; the default
+ // might have to be exported
+ rItemSet.Put( rItemSet.GetPool()->GetDefaultItem(nWhich) );
+ }
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void SwHTMLWriter::PrepareFontList( const SvxFontItem& rFontItem,
+ OUString& rNames,
+ sal_Unicode cQuote, bool bGeneric )
+{
+ rNames.clear();
+ const OUString& rName = rFontItem.GetFamilyName();
+ bool bContainsKeyword = false;
+ if( !rName.isEmpty() )
+ {
+ sal_Int32 nStrPos = 0;
+ while( nStrPos != -1 )
+ {
+ OUString aName = rName.getToken( 0, ';', nStrPos );
+ aName = comphelper::string::encodeForXml(comphelper::string::strip(aName, ' '));
+ if( aName.isEmpty() )
+ continue;
+
+ bool bIsKeyword = false;
+ switch( aName[0] )
+ {
+ case 'c':
+ case 'C':
+ bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_cursive );
+ break;
+
+ case 'f':
+ case 'F':
+ bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_fantasy );
+ break;
+
+ case 'm':
+ case 'M':
+ bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_monospace );
+ break;
+
+ case 's':
+ case 'S':
+ bIsKeyword =
+ aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_serif ) ||
+ aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_sans_serif );
+ break;
+ }
+
+ bContainsKeyword |= bIsKeyword;
+
+ if( !rNames.isEmpty() )
+ rNames += ", ";
+ if( cQuote && !bIsKeyword )
+ rNames += OUStringChar( cQuote );
+ rNames += aName;
+ if( cQuote && !bIsKeyword )
+ rNames += OUStringChar( cQuote );
+ }
+ }
+
+ if( bContainsKeyword || !bGeneric )
+ return;
+
+ std::string_view pStr;
+ switch( rFontItem.GetFamily() )
+ {
+ case FAMILY_ROMAN: pStr = sCSS1_PV_serif; break;
+ case FAMILY_SWISS: pStr = sCSS1_PV_sans_serif; break;
+ case FAMILY_SCRIPT: pStr = sCSS1_PV_cursive; break;
+ case FAMILY_DECORATIVE: pStr = sCSS1_PV_fantasy; break;
+ case FAMILY_MODERN: pStr = sCSS1_PV_monospace; break;
+ default:
+ ;
+ }
+
+ if( !pStr.empty() )
+ {
+ if( !rNames.isEmpty() )
+ rNames += ", ";
+ rNames += OStringToOUString( pStr, RTL_TEXTENCODING_ASCII_US );
+ }
+}
+
+bool SwHTMLWriter::HasScriptDependentItems( const SfxItemSet& rItemSet,
+ bool bCheckDropCap )
+{
+ static const sal_uInt16 aWhichIds[] =
+ {
+ RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT,
+ RES_CHRATR_FONTSIZE, RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CTL_FONTSIZE,
+ RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE,
+ RES_CHRATR_POSTURE, RES_CHRATR_CJK_POSTURE, RES_CHRATR_CTL_POSTURE,
+ RES_CHRATR_WEIGHT, RES_CHRATR_CJK_WEIGHT, RES_CHRATR_CTL_WEIGHT,
+ 0, 0, 0
+ };
+
+ for( int i=0; aWhichIds[i]; i += 3 )
+ {
+ const SfxPoolItem *pItem = nullptr, *pItemCJK = nullptr, *pItemCTL = nullptr, *pTmp;
+ int nItemCount = 0;
+ if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i], false,
+ &pTmp ) )
+ {
+ pItem = pTmp;
+ nItemCount++;
+ }
+ if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i+1], false,
+ &pTmp ) )
+ {
+ pItemCJK = pTmp;
+ nItemCount++;
+ }
+ if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i+2], false,
+ &pTmp ) )
+ {
+ pItemCTL = pTmp;
+ nItemCount++;
+ }
+
+ // If some of the items are set, but not all, we need script dependent
+ // styles
+ if( nItemCount > 0 && nItemCount < 3 )
+ return true;
+
+ if( 3 == nItemCount )
+ {
+ // If all items are set, but some of them have different values,
+ // we need script dependent styles, too. For font items, we have
+ // to take care about their special HTML/CSS1 representation.
+ if( RES_CHRATR_FONT == aWhichIds[i] )
+ {
+ if( !swhtml_css1atr_equalFontItems( *pItem, *pItemCJK ) ||
+ !swhtml_css1atr_equalFontItems( *pItem, *pItemCTL ) ||
+ !swhtml_css1atr_equalFontItems( *pItemCJK, *pItemCTL ) )
+ return true;
+ }
+ else
+ {
+ if( *pItem != *pItemCJK ||
+ *pItem != *pItemCTL ||
+ *pItemCJK != *pItemCTL )
+ return true;
+ }
+ }
+ }
+
+ const SwFormatDrop *pDrop;
+ if( bCheckDropCap &&
+ (pDrop = rItemSet.GetItemIfSet( RES_PARATR_DROP )) )
+ {
+ const SwCharFormat *pDCCharFormat = pDrop->GetCharFormat();
+ if( pDCCharFormat )
+ {
+ //sequence of (start, end) property ranges we want to
+ //query
+ SfxItemSetFixed<
+ RES_CHRATR_FONT, RES_CHRATR_FONT,
+ RES_CHRATR_POSTURE, RES_CHRATR_POSTURE,
+ RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CJK_POSTURE, RES_CHRATR_CTL_FONT,
+ RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT>
+ aTstItemSet(*pDCCharFormat->GetAttrSet().GetPool());
+ aTstItemSet.Set( pDCCharFormat->GetAttrSet() );
+ return HasScriptDependentItems( aTstItemSet, false );
+ }
+ }
+
+ return false;
+}
+
+static bool OutCSS1Rule( SwHTMLWriter& rWrt, const OUString& rSelector,
+ const SfxItemSet& rItemSet, bool bHasClass,
+ bool bCheckForPseudo )
+{
+ bool bScriptDependent = false;
+ if( SwHTMLWriter::HasScriptDependentItems( rItemSet, bHasClass ) )
+ {
+ bScriptDependent = true;
+ std::u16string_view aSelector( rSelector );
+
+ std::u16string_view aPseudo;
+ if( bCheckForPseudo )
+ {
+ size_t nPos = aSelector.rfind( ':' );
+ if( nPos != std::u16string_view::npos )
+ {
+ aPseudo = aSelector.substr( nPos );
+ aSelector =aSelector.substr( 0, nPos );
+ }
+ }
+
+ if( !bHasClass )
+ {
+ // If we are exporting styles for a tag we have to export a tag
+ // rule for all properties that aren't style dependent and
+ // some class rule for the additional style dependen properties
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_NO_SCRIPT|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &rSelector );
+ rWrt.OutCSS1_SfxItemSet( rItemSet, false );
+ }
+
+ //sequence of (start, end) property ranges we want to
+ //query
+ SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_LANGUAGE, RES_CHRATR_POSTURE,
+ RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>
+ aScriptItemSet( *rItemSet.GetPool() );
+ aScriptItemSet.Put( rItemSet );
+
+ OUString aNewSelector = OUString::Concat(aSelector) + ".western" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aNewSelector );
+ rWrt.OutCSS1_SfxItemSet( aScriptItemSet, false );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + ".cjk" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aNewSelector );
+ rWrt.OutCSS1_SfxItemSet( aScriptItemSet, false );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + ".ctl" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aNewSelector );
+ rWrt.OutCSS1_SfxItemSet( aScriptItemSet, false );
+ }
+ }
+ else
+ {
+ // If there are script dependencies and we are derived from a tag,
+ // when we have to export a style dependent class for all
+ // scripts
+ OUString aNewSelector = OUString::Concat(aSelector) + "-western" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aNewSelector );
+ rWrt.OutCSS1_SfxItemSet( rItemSet, false );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + "-cjk" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aNewSelector );
+ rWrt.OutCSS1_SfxItemSet( rItemSet, false );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + "-ctl" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aNewSelector );
+ rWrt.OutCSS1_SfxItemSet( rItemSet, false );
+ }
+ }
+ }
+ else
+ {
+ // If there are no script dependencies, when all items are
+ // exported in one step. For hyperlinks only, a script information
+ // must be there, because these two chr formats don't support
+ // script dependencies by now.
+ SwCSS1OutMode aMode( rWrt,
+ rWrt.m_nCSS1Script|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &rSelector );
+ rWrt.OutCSS1_SfxItemSet( rItemSet, false );
+ }
+
+ return bScriptDependent;
+}
+
+static void OutCSS1DropCapRule(
+ SwHTMLWriter& rWrt, const OUString& rSelector,
+ const SwFormatDrop& rDrop, bool bHasClass,
+ bool bHasScriptDependencies )
+{
+ const SwCharFormat *pDCCharFormat = rDrop.GetCharFormat();
+ if( (bHasScriptDependencies && bHasClass) ||
+ (pDCCharFormat && SwHTMLWriter::HasScriptDependentItems( pDCCharFormat->GetAttrSet(), false ) ) )
+ {
+ std::u16string_view aSelector( rSelector );
+
+ std::u16string_view aPseudo;
+ size_t nPos = aSelector.rfind( ':' );
+ if( nPos != std::u16string_view::npos )
+ {
+ aPseudo = aSelector.substr( nPos );
+ aSelector = aSelector.substr( 0, nPos );
+ }
+
+ if( !bHasClass )
+ {
+ // If we are exporting styles for a tag we have to export a tag
+ // rule for all properties that aren't style dependent and
+ // some class rule for the additional style dependen properties
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_NO_SCRIPT|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &rSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
+ }
+
+ SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_LANGUAGE, RES_CHRATR_POSTURE,
+ RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>
+ aScriptItemSet( rWrt.m_pDoc->GetAttrPool() );
+ if( pDCCharFormat )
+ aScriptItemSet.Set( pDCCharFormat->GetAttrSet() );
+
+ OUString aNewSelector = OUString::Concat(aSelector) + ".western" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &aNewSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop, &aScriptItemSet );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + ".cjk" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &aNewSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop, &aScriptItemSet );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + ".ctl" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &aNewSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop, &aScriptItemSet );
+ }
+ }
+ else
+ {
+ // If there are script dependencies and we are derived from a tag,
+ // when we have to export a style dependent class for all
+ // scripts
+ OUString aNewSelector = OUString::Concat(aSelector) + "-western" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &aNewSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + "-cjk" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &aNewSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
+ }
+
+ aNewSelector = OUString::Concat(aSelector) + "-ctl" + aPseudo;
+ {
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &aNewSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
+ }
+ }
+ }
+ else
+ {
+ // If there are no script dependencies, when all items are
+ // exported in one step. For hyperlinks only, a script information
+ // must be there, because these two chr formats don't support
+ // script dependencies by now.
+ SwCSS1OutMode aMode( rWrt,
+ rWrt.m_nCSS1Script|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
+ &rSelector );
+ OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
+ }
+}
+
+static SwHTMLWriter& OutCSS1_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat,
+ IDocumentStylePoolAccess/*SwDoc*/ *pDoc, SwDoc *pTemplate )
+{
+ bool bCharFormat = false;
+ switch( rFormat.Which() )
+ {
+ case RES_CHRFMT:
+ bCharFormat = true;
+ break;
+
+ case RES_TXTFMTCOLL:
+ case RES_CONDTXTFMTCOLL:
+ // these template-types can be exported
+ break;
+
+ default:
+ // but not these
+ return rWrt;
+ }
+
+ // determine Selector and the to-be-exported Attr-Set-depth
+ OUString aSelector;
+ sal_uInt16 nRefPoolId = 0;
+ sal_uInt16 nDeep = GetCSS1Selector( &rFormat, aSelector, nRefPoolId );
+ if( !nDeep )
+ return rWrt; // not derived from a HTML-template
+
+ sal_uInt16 nPoolFormatId = rFormat.GetPoolFormatId();
+
+ // Determine the to-be-exported Attr-Set. We have to distinguish 3 cases:
+ // - HTML-Tag templates (nDeep==USHRT_MAX):
+ // Export Attrs...
+ // - that are set in the template, but not in the original of the HTML template
+ // - the Default-Attrs for the Attrs, that are set in the Original of the
+ // HTML template, but not in the current template.
+ // - templates directly derived from HTML templates (nDeep==1):
+ // Export only Attributes of the template Item-Set w/o its parents.
+ // - templates in-directly derived from HTML templates (nDeep>1):
+ // Export Attributes of the template Item-Set incl. its Parents,
+ // but w/o Attributes that are set in the HTML-Tag template.
+
+ // create Item-Set with all Attributes from the template
+ // (all but for nDeep==1)
+ const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet();
+ SfxItemSet aItemSet( *rFormatItemSet.GetPool(), rFormatItemSet.GetRanges() );
+ aItemSet.Set( rFormatItemSet ); // Was nDeep!=1 that is not working
+ // for script dependent items but should
+ // not make a difference for any other
+
+ bool bSetDefaults = true, bClearSame = true;
+ const SwFormat *pRefFormat = nullptr;
+ const SwFormat *pRefFormatScript = nullptr;
+ switch( nDeep )
+ {
+ case CSS1_FMT_ISTAG:
+ pRefFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() );
+ break;
+ case CSS1_FMT_CMPREF:
+ pRefFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pDoc );
+ pRefFormatScript = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() );
+ bClearSame = false;
+ break;
+ default:
+ pRefFormat = SwHTMLWriter::GetParentFormat( rFormat, nDeep );
+ pRefFormatScript = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() );
+ bSetDefaults = false;
+ break;
+ }
+
+ if( pRefFormat )
+ {
+ // subtract Item-Set of the Reference template (incl. its Parents)
+ SwHTMLWriter::SubtractItemSet( aItemSet, pRefFormat->GetAttrSet(),
+ bSetDefaults, bClearSame,
+ pRefFormatScript
+ ? &pRefFormatScript->GetAttrSet()
+ : nullptr );
+
+ if( !bCharFormat )
+ {
+ const SvxULSpaceItem& rULItem = pRefFormat->GetULSpace();
+ rWrt.m_nDfltTopMargin = rULItem.GetUpper();
+ rWrt.m_nDfltBottomMargin = rULItem.GetLower();
+ }
+ }
+ else if( CSS1_FMT_ISTAG==nDeep && !bCharFormat )
+ {
+ // set Default-distance above and below (for the
+ // case that there is no reference template)
+ rWrt.m_nDfltTopMargin = 0;
+ rWrt.m_nDfltBottomMargin = HTML_PARSPACE;
+ if( USER_FMT & nPoolFormatId )
+ {
+ // user templates
+ const OUString& aNm(rFormat.GetName());
+
+ if (aNm == "DD 1" || aNm == "DT 1")
+ rWrt.m_nDfltBottomMargin = 0;
+ else if (aNm == OOO_STRING_SVTOOLS_HTML_listing)
+ rWrt.m_nDfltBottomMargin = 0;
+ else if (aNm == OOO_STRING_SVTOOLS_HTML_preformtxt)
+ rWrt.m_nDfltBottomMargin = 0;
+ else if (aNm == OOO_STRING_SVTOOLS_HTML_xmp)
+ rWrt.m_nDfltBottomMargin = 0;
+ }
+ else
+ {
+ // Pool templates
+ switch( nPoolFormatId )
+ {
+ case RES_POOLCOLL_HEADLINE1:
+ case RES_POOLCOLL_HEADLINE2:
+ case RES_POOLCOLL_HEADLINE3:
+ case RES_POOLCOLL_HEADLINE4:
+ case RES_POOLCOLL_HEADLINE5:
+ case RES_POOLCOLL_HEADLINE6:
+ rWrt.m_nDfltTopMargin = HTML_HEADSPACE;
+ break;
+ case RES_POOLCOLL_SEND_ADDRESS:
+ case RES_POOLCOLL_HTML_DT:
+ case RES_POOLCOLL_HTML_DD:
+ case RES_POOLCOLL_HTML_PRE:
+ rWrt.m_nDfltBottomMargin = 0;
+ break;
+ }
+ }
+ }
+
+ // if nothing is to be exported ...
+ if( !aItemSet.Count() )
+ return rWrt;
+
+ // There is no support for script dependent hyperlinks by now.
+ bool bCheckForPseudo = false;
+ if( bCharFormat &&
+ (RES_POOLCHR_INET_NORMAL==nRefPoolId ||
+ RES_POOLCHR_INET_VISIT==nRefPoolId) )
+ bCheckForPseudo = true;
+
+ // export now the Attributes (incl. selector)
+ bool bHasScriptDependencies = false;
+ if( OutCSS1Rule( rWrt, aSelector, aItemSet, CSS1_FMT_ISTAG != nDeep,
+ bCheckForPseudo ) )
+ {
+ if( bCharFormat )
+ rWrt.m_aScriptTextStyles.insert( rFormat.GetName() );
+ else
+ {
+ if( nPoolFormatId==RES_POOLCOLL_TEXT )
+ rWrt.m_aScriptParaStyles.insert( pDoc->GetTextCollFromPool( RES_POOLCOLL_STANDARD, false )->GetName() );
+ rWrt.m_aScriptParaStyles.insert( rFormat.GetName() );
+ }
+ bHasScriptDependencies = true;
+ }
+
+ // export Drop-Caps
+ if( const SwFormatDrop *pDrop = aItemSet.GetItemIfSet( RES_PARATR_DROP, false ) )
+ {
+ OUString sOut = aSelector +
+ ":" + OStringToOUString( sCSS1_first_letter, RTL_TEXTENCODING_ASCII_US );
+ OutCSS1DropCapRule( rWrt, sOut, *pDrop, CSS1_FMT_ISTAG != nDeep, bHasScriptDependencies );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SwPageDesc( SwHTMLWriter& rWrt, const SwPageDesc& rPageDesc,
+ IDocumentStylePoolAccess/*SwDoc*/ *pDoc, SwDoc *pTemplate,
+ sal_uInt16 nRefPoolId, bool bExtRef,
+ bool bPseudo )
+{
+ const SwPageDesc* pRefPageDesc = nullptr;
+ if( !bExtRef )
+ pRefPageDesc = pDoc->GetPageDescFromPool( nRefPoolId, false );
+ else if( pTemplate )
+ pRefPageDesc = pTemplate->getIDocumentStylePoolAccess().GetPageDescFromPool( nRefPoolId, false );
+
+ OUString aSelector = "@" + OStringToOUString( sCSS1_page, RTL_TEXTENCODING_ASCII_US );
+
+ if( bPseudo )
+ {
+ std::string_view pPseudo;
+ switch( rPageDesc.GetPoolFormatId() )
+ {
+ case RES_POOLPAGE_FIRST: pPseudo = sCSS1_first; break;
+ case RES_POOLPAGE_LEFT: pPseudo = sCSS1_left; break;
+ case RES_POOLPAGE_RIGHT: pPseudo = sCSS1_right; break;
+ }
+ if( !pPseudo.empty() )
+ aSelector += ":" + OStringToOUString( pPseudo, RTL_TEXTENCODING_ASCII_US );
+ }
+
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_RULE_ON|CSS1_OUTMODE_TEMPLATE,
+ &aSelector );
+
+ // Size: If the only difference is the Landscape-Flag,
+ // only export Portrait or Landscape. Otherwise export size.
+ bool bRefLandscape = pRefPageDesc && pRefPageDesc->GetLandscape();
+ Size aRefSz;
+ const Size& rSz = rPageDesc.GetMaster().GetFrameSize().GetSize();
+ if( pRefPageDesc )
+ {
+ aRefSz = pRefPageDesc->GetMaster().GetFrameSize().GetSize();
+ if( bRefLandscape != rPageDesc.GetLandscape() )
+ {
+ tools::Long nTmp = aRefSz.Height();
+ aRefSz.setHeight( aRefSz.Width() );
+ aRefSz.setWidth( nTmp );
+ }
+ }
+
+ // TODO: Bad Hack: On the Page-Tabpage there are small rounding errors
+ // for the page size. Partially because of bug 25535, we stupidly still
+ // use the Size-Item from Dialog, even if nothing changed.
+ // Thus: once one visited the Page-Dialog and left it with OK, we get a
+ // new page size that then gets exported here. To avoid that, we allow
+ // here small deviations.
+ if( std::abs( rSz.Width() - aRefSz.Width() ) <= 2 &&
+ std::abs( rSz.Height() - aRefSz.Height() ) <= 2 )
+ {
+ if( bRefLandscape != rPageDesc.GetLandscape() )
+ {
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_size,
+ rPageDesc.GetLandscape() ? sCSS1_PV_landscape
+ : sCSS1_PV_portrait );
+ }
+ }
+ else
+ {
+ OStringBuffer sVal;
+ AddUnitPropertyValue(sVal, rSz.Width(), rWrt.GetCSS1Unit());
+ sVal.append(' ');
+ AddUnitPropertyValue(sVal, rSz.Height(), rWrt.GetCSS1Unit());
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_size, sVal);
+ }
+
+ // Export the distance-Attributes as normally
+ const SwFrameFormat &rMaster = rPageDesc.GetMaster();
+ SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE> aItemSet( *rMaster.GetAttrSet().GetPool() );
+ aItemSet.Set( rMaster.GetAttrSet() );
+
+ if( pRefPageDesc )
+ {
+ SwHTMLWriter::SubtractItemSet( aItemSet,
+ pRefPageDesc->GetMaster().GetAttrSet(),
+ true );
+ }
+
+ OutCSS1_SvxULSpace_SvxLRSpace( rWrt, aItemSet );
+
+ // If for a Pseudo-Selector no Property had been set, we still
+ // have to export something, so that the corresponding template is
+ // created on the next import.
+ if( rWrt.m_bFirstCSS1Property && bPseudo )
+ {
+ rWrt.OutNewLine();
+ OString sTmp(OUStringToOString(aSelector, RTL_TEXTENCODING_UTF8));
+ rWrt.Strm().WriteOString( sTmp ).WriteOString( " {" );
+ rWrt.m_bFirstCSS1Property = false;
+ }
+
+ if( !rWrt.m_bFirstCSS1Property )
+ rWrt.Strm().WriteOString( sCSS1_rule_end );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SwFootnoteInfo( SwHTMLWriter& rWrt, const SwEndNoteInfo& rInfo,
+ SwDoc *pDoc, bool bHasNotes, bool bEndNote )
+{
+ OUString aSelector;
+
+ if( bHasNotes )
+ {
+ aSelector = OUString::Concat(OOO_STRING_SVTOOLS_HTML_anchor ".") +
+ ( bEndNote ? std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_sdendnote_anc)
+ : std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_sdfootnote_anc) );
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
+ &aSelector );
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_size,
+ sHTML_FTN_fontheight );
+ rWrt.Strm().WriteOString( sCSS1_rule_end );
+ }
+
+ const SwCharFormat *pSymCharFormat = rInfo.GetCharFormat( *pDoc );
+ if( pSymCharFormat )
+ {
+ const SfxItemSet& rFormatItemSet = pSymCharFormat->GetAttrSet();
+ SfxItemSet aItemSet( *rFormatItemSet.GetPool(), rFormatItemSet.GetRanges() );
+ aItemSet.Set( rFormatItemSet );
+
+ // If there are footnotes or endnotes, then all Attributes have to be
+ // exported, so that Netscape displays the document correctly.
+ // Otherwise it is sufficient, to export the differences to the
+ // footnote and endnote template.
+ if( !bHasNotes && rWrt.m_xTemplate.is() )
+ {
+ SwFormat *pRefFormat = rWrt.m_xTemplate->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ static_cast< sal_uInt16 >(bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE) );
+ if( pRefFormat )
+ SwHTMLWriter::SubtractItemSet( aItemSet, pRefFormat->GetAttrSet(),
+ true );
+ }
+ if( aItemSet.Count() )
+ {
+ aSelector = OUString::Concat(OOO_STRING_SVTOOLS_HTML_anchor ".") +
+ ( bEndNote ? std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_sdendnote_sym)
+ : std::u16string_view(
+ u"" OOO_STRING_SVTOOLS_HTML_sdfootnote_sym));
+ if( OutCSS1Rule( rWrt, aSelector, aItemSet, true, false ))
+ rWrt.m_aScriptTextStyles.insert( pSymCharFormat->GetName() );
+ }
+ }
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutCSS1_BodyTagStyleOpt( SwHTMLWriter& rWrt, const SfxItemSet& rItemSet )
+{
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT_ON |
+ CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_BODY, nullptr );
+
+ // Only export the attributes of the page template.
+ // The attributes of the default paragraph template were
+ // considered already when exporting the paragraph template.
+
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false,
+ &pItem ) )
+ {
+ OUString rEmbeddedGraphicName;
+ OutCSS1_SvxBrush( rWrt, *pItem, sw::Css1Background::Page, &rEmbeddedGraphicName );
+ }
+
+ if( SfxItemState::SET == rItemSet.GetItemState( RES_BOX, false,
+ &pItem ))
+ {
+ OutCSS1_SvxBox( rWrt, *pItem );
+ }
+
+ if( !rWrt.m_bFirstCSS1Property )
+ {
+ // if a Property was exported as part of a Style-Option,
+ // the Option still needs to be finished
+ rWrt.Strm().WriteChar( '\"' );
+ }
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutCSS1_ParaTagStyleOpt( SwHTMLWriter& rWrt, const SfxItemSet& rItemSet, std::string_view rAdd )
+{
+ SwCSS1OutMode aMode( rWrt, rWrt.m_nCSS1Script|CSS1_OUTMODE_STYLE_OPT |
+ CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr );
+ rWrt.OutCSS1_SfxItemSet( rItemSet, false, rAdd );
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutCSS1_TableBGStyleOpt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT_ON |
+ CSS1_OUTMODE_ENCODE|
+ CSS1_OUTMODE_TABLEBOX, nullptr );
+ OutCSS1_SvxBrush( rWrt, rHt, sw::Css1Background::TableRow, nullptr );
+
+ if (!rWrt.m_bFirstCSS1Property)
+ rWrt.Strm().WriteChar(cCSS1_style_opt_end);
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutCSS1_NumberBulletListStyleOpt( SwHTMLWriter& rWrt, const SwNumRule& rNumRule,
+ sal_uInt8 nLevel )
+{
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT |
+ CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr );
+
+ const SwNumFormat& rNumFormat = rNumRule.Get( nLevel );
+
+ tools::Long nLSpace = rNumFormat.GetAbsLSpace();
+ tools::Long nFirstLineOffset = rNumFormat.GetFirstLineOffset();
+ tools::Long nDfltFirstLineOffset = HTML_NUMBER_BULLET_INDENT;
+ if( nLevel > 0 )
+ {
+ const SwNumFormat& rPrevNumFormat = rNumRule.Get( nLevel-1 );
+ nLSpace -= rPrevNumFormat.GetAbsLSpace();
+ nDfltFirstLineOffset = rPrevNumFormat.GetFirstLineOffset();
+ }
+
+ if( rWrt.IsHTMLMode(HTMLMODE_LSPACE_IN_NUMBER_BULLET) &&
+ nLSpace != HTML_NUMBER_BULLET_MARGINLEFT )
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_left, nLSpace );
+
+ if( rWrt.IsHTMLMode(HTMLMODE_FRSTLINE_IN_NUMBER_BULLET) &&
+ nFirstLineOffset != nDfltFirstLineOffset )
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_text_indent, nFirstLineOffset );
+
+ if( !rWrt.m_bFirstCSS1Property )
+ rWrt.Strm().WriteChar( '\"' );
+
+ return rWrt;
+}
+
+void SwHTMLWriter::OutCSS1_FrameFormatOptions( const SwFrameFormat& rFrameFormat,
+ HtmlFrmOpts nFrameOpts,
+ const SdrObject *pSdrObj,
+ const SfxItemSet *pItemSet )
+{
+ SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON |
+ CSS1_OUTMODE_ENCODE|
+ CSS1_OUTMODE_FRAME, nullptr );
+
+ const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
+ SvxLRSpaceItem aLRItem( rFrameFormat.GetLRSpace() );
+ SvxULSpaceItem aULItem( rFrameFormat.GetULSpace() );
+ if( nFrameOpts & HtmlFrmOpts::SAlign )
+ {
+ const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
+ switch( rAnchor.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ if( text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
+ text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
+ {
+ if( !(nFrameOpts & HtmlFrmOpts::Align) )
+ {
+ // float
+ std::string_view pStr = text::HoriOrientation::RIGHT==rHoriOri.GetHoriOrient()
+ ? sCSS1_PV_right
+ : sCSS1_PV_left;
+ OutCSS1_PropertyAscii( sCSS1_P_float, pStr );
+ }
+ break;
+ }
+ [[fallthrough]];
+
+ case RndStdIds::FLY_AT_PAGE:
+ case RndStdIds::FLY_AT_FLY:
+ {
+ // position
+ OutCSS1_PropertyAscii( sCSS1_P_position, sCSS1_PV_absolute );
+
+ // For top/left we need to subtract the distance to the frame
+ // from the position, as in CSS1 it is added to the position.
+ // This works also for automatically aligned frames, even that
+ // in this case Writer also adds the distance; because in this
+ // case the Orient-Attribute contains the correct position.
+
+ // top
+ tools::Long nXPos=0, nYPos=0;
+ bool bOutXPos = false, bOutYPos = false;
+ if( RES_DRAWFRMFMT == rFrameFormat.Which() )
+ {
+ OSL_ENSURE( pSdrObj, "Do not pass a SdrObject. Inefficient" );
+ if( !pSdrObj )
+ pSdrObj = rFrameFormat.FindSdrObject();
+ OSL_ENSURE( pSdrObj, "Where is the SdrObject" );
+ if( pSdrObj )
+ {
+ Point aPos( pSdrObj->GetRelativePos() );
+ nXPos = aPos.X();
+ nYPos = aPos.Y();
+ }
+ bOutXPos = bOutYPos = true;
+ }
+ else
+ {
+ bOutXPos = text::RelOrientation::CHAR != rHoriOri.GetRelationOrient();
+ nXPos = text::HoriOrientation::NONE == rHoriOri.GetHoriOrient()
+ ? rHoriOri.GetPos() : 0;
+
+ const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
+ bOutYPos = text::RelOrientation::CHAR != rVertOri.GetRelationOrient();
+ nYPos = text::VertOrientation::NONE == rVertOri.GetVertOrient()
+ ? rVertOri.GetPos() : 0;
+ }
+
+ if( bOutYPos )
+ {
+ if( IsHTMLMode( HTMLMODE_FLY_MARGINS) )
+ {
+ nYPos -= aULItem.GetUpper();
+ if( nYPos < 0 )
+ {
+ aULItem.SetUpper( o3tl::narrowing<sal_uInt16>(aULItem.GetUpper() + nYPos) );
+ nYPos = 0;
+ }
+ }
+
+ OutCSS1_UnitProperty( sCSS1_P_top, nYPos );
+ }
+
+ if( bOutXPos )
+ {
+ // left
+ if( IsHTMLMode( HTMLMODE_FLY_MARGINS) )
+ {
+ nXPos -= aLRItem.GetLeft();
+ if( nXPos < 0 )
+ {
+ aLRItem.SetLeft( o3tl::narrowing<sal_uInt16>(aLRItem.GetLeft() + nXPos) );
+ nXPos = 0;
+ }
+ }
+
+ OutCSS1_UnitProperty( sCSS1_P_left, nXPos );
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ // width/height
+ if( nFrameOpts & HtmlFrmOpts::SSize )
+ {
+ if( RES_DRAWFRMFMT == rFrameFormat.Which() )
+ {
+ OSL_ENSURE( pSdrObj, "Do not pass a SdrObject. Inefficient" );
+ if( !pSdrObj )
+ pSdrObj = rFrameFormat.FindSdrObject();
+ OSL_ENSURE( pSdrObj, "Where is the SdrObject" );
+ if( pSdrObj )
+ {
+ Size aTwipSz( pSdrObj->GetLogicRect().GetSize() );
+ if( nFrameOpts & HtmlFrmOpts::SWidth )
+ {
+ if( nFrameOpts & HtmlFrmOpts::SPixSize )
+ OutCSS1_PixelProperty( sCSS1_P_width, aTwipSz.Width() );
+ else
+ OutCSS1_UnitProperty( sCSS1_P_width, aTwipSz.Width() );
+ }
+ if( nFrameOpts & HtmlFrmOpts::SHeight )
+ {
+ if( nFrameOpts & HtmlFrmOpts::SPixSize )
+ OutCSS1_PixelProperty( sCSS1_P_height, aTwipSz.Height() );
+ else
+ OutCSS1_UnitProperty( sCSS1_P_height, aTwipSz.Height() );
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( HtmlFrmOpts::AbsSize & nFrameOpts,
+ "Export absolute size" );
+ OSL_ENSURE( HtmlFrmOpts::AnySize & nFrameOpts,
+ "Export every size" );
+ Css1FrameSize nMode = Css1FrameSize::NONE;
+ if( nFrameOpts & HtmlFrmOpts::SWidth )
+ nMode |= Css1FrameSize::Width;
+ if( nFrameOpts & HtmlFrmOpts::SHeight )
+ nMode |= Css1FrameSize::MinHeight|Css1FrameSize::FixHeight;
+ if( nFrameOpts & HtmlFrmOpts::SPixSize )
+ nMode |= Css1FrameSize::Pixel;
+
+ OutCSS1_SwFormatFrameSize( *this, rFrameFormat.GetFrameSize(), nMode );
+ }
+ }
+
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+ // margin-*
+ if( (nFrameOpts & HtmlFrmOpts::SSpace) &&
+ IsHTMLMode( HTMLMODE_FLY_MARGINS) )
+ {
+ const SvxLRSpaceItem *pLRItem = nullptr;
+ const SvxULSpaceItem *pULItem = nullptr;
+ if( SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE ) )
+ pLRItem = &aLRItem;
+ if( SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE ) )
+ pULItem = &aULItem;
+ if( pLRItem || pULItem )
+ OutCSS1_SvxULSpace_SvxLRSpace( *this, pULItem, pLRItem );
+ }
+
+ // border
+ if( nFrameOpts & HtmlFrmOpts::SBorder )
+ {
+ const SfxPoolItem* pItem;
+ if( nFrameOpts & HtmlFrmOpts::SNoBorder )
+ OutCSS1_SvxBox( *this, rFrameFormat.GetBox() );
+ else if( SfxItemState::SET==rItemSet.GetItemState( RES_BOX, true, &pItem ) )
+ OutCSS1_SvxBox( *this, *pItem );
+ }
+
+ // background (if, then the color must be set also)
+ if( nFrameOpts & HtmlFrmOpts::SBackground )
+ OutCSS1_FrameFormatBackground( rFrameFormat );
+
+ if( pItemSet )
+ OutCSS1_SfxItemSet( *pItemSet, false );
+
+ if( !m_bFirstCSS1Property )
+ Strm().WriteChar( '\"' );
+}
+
+void SwHTMLWriter::OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameFormat )
+{
+ SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON |
+ CSS1_OUTMODE_ENCODE|
+ CSS1_OUTMODE_TABLE, nullptr );
+
+ const SfxPoolItem *pItem;
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+ if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
+ OutCSS1_SvxBrush( *this, *pItem, sw::Css1Background::Table, nullptr );
+
+ if( IsHTMLMode( HTMLMODE_PRINT_EXT ) )
+ OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, false );
+
+ if( SfxItemState::SET==rItemSet.GetItemState( RES_LAYOUT_SPLIT, false, &pItem ) )
+ OutCSS1_SwFormatLayoutSplit( *this, *pItem );
+
+ if (mbXHTML)
+ {
+ sal_Int16 eTabHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
+ if (eTabHoriOri == text::HoriOrientation::CENTER)
+ {
+ // Emit XHTML's center using inline CSS.
+ OutCSS1_Property(sCSS1_P_margin_left, "auto", nullptr, sw::Css1Background::Table);
+ OutCSS1_Property(sCSS1_P_margin_right, "auto", nullptr, sw::Css1Background::Table);
+ }
+ }
+
+ if( !m_bFirstCSS1Property )
+ Strm().WriteChar( '\"' );
+}
+
+void SwHTMLWriter::OutCSS1_TableCellBordersAndBG(SwFrameFormat const& rFrameFormat, const SvxBrushItem *pBrushItem)
+{
+ SwCSS1OutMode const aMode( *this,
+ CSS1_OUTMODE_STYLE_OPT_ON|CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_TABLEBOX, nullptr );
+ if (pBrushItem)
+ OutCSS1_SvxBrush(*this, *pBrushItem, sw::Css1Background::TableCell, nullptr);
+ OutCSS1_SvxBox(*this, rFrameFormat.GetBox());
+ if (!m_bFirstCSS1Property)
+ Strm().WriteChar(cCSS1_style_opt_end);
+}
+
+void SwHTMLWriter::OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameFormat, const SwFormatCol *pCol )
+{
+ SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON |
+ CSS1_OUTMODE_ENCODE|
+ CSS1_OUTMODE_SECTION, nullptr );
+
+ const SfxPoolItem *pItem;
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+ if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
+ OutCSS1_SvxBrush( *this, *pItem, sw::Css1Background::Section, nullptr );
+
+ if (mbXHTML)
+ {
+ SvxFrameDirection nDir = GetHTMLDirection(rFrameFormat.GetAttrSet());
+ OString sConvertedDirection = convertDirection(nDir);
+ if (!sConvertedDirection.isEmpty())
+ {
+ OutCSS1_Property(sCSS1_P_dir, sConvertedDirection, nullptr,
+ sw::Css1Background::Section);
+ }
+ }
+
+ if (pCol)
+ {
+ OString sColumnCount(OString::number(static_cast<sal_Int32>(pCol->GetNumCols())));
+ OutCSS1_PropertyAscii(sCSS1_P_column_count, sColumnCount);
+ }
+
+ if( !m_bFirstCSS1Property )
+ Strm().WriteChar( '\"' );
+}
+
+static bool OutCSS1_FrameFormatBrush( SwHTMLWriter& rWrt,
+ const SvxBrushItem& rBrushItem )
+{
+ bool bWritten = false;
+ /// output brush of frame format, if its background color is not "no fill"/"auto fill"
+ /// or it has a background graphic.
+ if( rBrushItem.GetColor() != COL_TRANSPARENT ||
+ !rBrushItem.GetGraphicLink().isEmpty() ||
+ 0 != rBrushItem.GetGraphicPos() )
+ {
+ OutCSS1_SvxBrush( rWrt, rBrushItem, sw::Css1Background::Fly, nullptr );
+ bWritten = true;
+ }
+ return bWritten;
+}
+
+void SwHTMLWriter::OutCSS1_FrameFormatBackground( const SwFrameFormat& rFrameFormat )
+{
+ // If the frame itself has a background, then export.
+ if( OutCSS1_FrameFormatBrush( *this, *rFrameFormat.makeBackgroundBrushItem() ) )
+ return;
+
+ // If the frame is not linked to a page, we use the background of the anchor.
+ const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
+ RndStdIds eAnchorId = rAnchor.GetAnchorId();
+ const SwNode *pAnchorNode = rAnchor.GetAnchorNode();
+ if (RndStdIds::FLY_AT_PAGE != eAnchorId && pAnchorNode)
+ {
+ if( pAnchorNode->IsContentNode() )
+ {
+ // If the frame is linked to a content-node,
+ // we take the background of the content-node, if it has one.
+ if( OutCSS1_FrameFormatBrush( *this,
+ pAnchorNode->GetContentNode()->GetSwAttrSet().GetBackground()) )
+ return;
+
+ // Otherwise we also could be in a table
+ const SwTableNode *pTableNd = pAnchorNode->FindTableNode();
+ if( pTableNd )
+ {
+ const SwStartNode *pBoxSttNd = pAnchorNode->FindTableBoxStartNode();
+ const SwTableBox *pBox =
+ pTableNd->GetTable().GetTableBox( pBoxSttNd->GetIndex() );
+
+ // If the box has a background, we take it.
+ if( OutCSS1_FrameFormatBrush( *this,
+ *pBox->GetFrameFormat()->makeBackgroundBrushItem() ) )
+ return;
+
+ // Otherwise we use that of the lines
+ const SwTableLine *pLine = pBox->GetUpper();
+ while( pLine )
+ {
+ if( OutCSS1_FrameFormatBrush( *this,
+ *pLine->GetFrameFormat()->makeBackgroundBrushItem() ) )
+ return;
+ pBox = pLine->GetUpper();
+ pLine = pBox ? pBox->GetUpper() : nullptr;
+ }
+
+ // If there was none either, we use the background of the table.
+ if( OutCSS1_FrameFormatBrush( *this,
+ *pTableNd->GetTable().GetFrameFormat()->makeBackgroundBrushItem() ) )
+ return;
+ }
+
+ }
+
+ // If the anchor is again in a Fly-Frame, use the background of the Fly-Frame.
+ const SwFrameFormat *pFrameFormat = pAnchorNode->GetFlyFormat();
+ if( pFrameFormat )
+ {
+ OutCSS1_FrameFormatBackground( *pFrameFormat );
+ return;
+ }
+ }
+
+ // At last there is the background of the page, and as the final rescue
+ // the value of the Config.
+ OSL_ENSURE( m_pCurrPageDesc, "no page template found" );
+ if( OutCSS1_FrameFormatBrush( *this,
+ *m_pCurrPageDesc->GetMaster().makeBackgroundBrushItem() ) )
+ return;
+
+ Color aColor( COL_WHITE );
+
+ // The background color is normally only used in Browse-Mode.
+ // We always use it for a HTML document, but for a text document
+ // only if viewed in Browse-Mode.
+ if( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) ||
+ m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))
+ {
+ SwViewShell *pVSh = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pVSh &&
+ COL_TRANSPARENT != pVSh->GetViewOptions()->GetRetoucheColor())
+ aColor = pVSh->GetViewOptions()->GetRetoucheColor();
+ }
+
+ OutCSS1_PropertyAscii(sCSS1_P_background, GetCSS1_Color(aColor));
+}
+
+static SwHTMLWriter& OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( SwHTMLWriter& rWrt,
+ const SvxUnderlineItem *pUItem,
+ const SvxOverlineItem *pOItem,
+ const SvxCrossedOutItem *pCOItem,
+ const SvxBlinkItem *pBItem )
+{
+ bool bNone = false;
+ OStringBuffer sOut;
+
+ if( pUItem )
+ {
+ switch( pUItem->GetLineStyle() )
+ {
+ case LINESTYLE_NONE:
+ bNone = true;
+ break;
+ case LINESTYLE_DONTKNOW:
+ break;
+ default:
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ {
+ // this also works in HTML does not need to be written as
+ // a STYLE-Options, and must not be written as Hint
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) || rWrt.mbReqIF,
+ "write underline as Hint?" );
+ sOut.append(sCSS1_PV_underline);
+ }
+ break;
+ }
+ }
+
+ if( pOItem )
+ {
+ switch( pOItem->GetLineStyle() )
+ {
+ case LINESTYLE_NONE:
+ bNone = true;
+ break;
+ case LINESTYLE_DONTKNOW:
+ break;
+ default:
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ {
+ // this also works in HTML does not need to be written as
+ // a STYLE-Options, and must not be written as Hint
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write overline as Hint?" );
+ if (!sOut.isEmpty())
+ sOut.append(' ');
+ sOut.append(sCSS1_PV_overline);
+ }
+ break;
+ }
+ }
+
+ if( pCOItem )
+ {
+ switch( pCOItem->GetStrikeout() )
+ {
+ case STRIKEOUT_NONE:
+ bNone = true;
+ break;
+ case STRIKEOUT_DONTKNOW:
+ break;
+ default:
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ {
+ // this also works in HTML does not need to be written as
+ // a STYLE-Options, and must not be written as Hint
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) || rWrt.mbReqIF,
+ "write crossedOut as Hint?" );
+ if (!sOut.isEmpty())
+ sOut.append(' ');
+ sOut.append(sCSS1_PV_line_through);
+ }
+ break;
+ }
+ }
+
+ if( pBItem )
+ {
+ if( !pBItem->GetValue() )
+ {
+ bNone = true;
+ }
+ else if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ {
+ // this also works in HTML does not need to be written as
+ // a STYLE-Options, and must not be written as Hint
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write blink as Hint?" );
+ if (!sOut.isEmpty())
+ sOut.append(' ');
+ sOut.append(sCSS1_PV_blink);
+ }
+ }
+
+ if (!sOut.isEmpty())
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_decoration, sOut );
+ else if( bNone )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_decoration, sCSS1_PV_none );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxCaseMap( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ switch( static_cast<const SvxCaseMapItem&>(rHt).GetCaseMap() )
+ {
+ case SvxCaseMap::NotMapped:
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_variant, sCSS1_PV_normal );
+ break;
+ case SvxCaseMap::SmallCaps:
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_variant, sCSS1_PV_small_caps );
+ break;
+ case SvxCaseMap::Uppercase:
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_uppercase );
+ break;
+ case SvxCaseMap::Lowercase:
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_lowercase );
+ break;
+ case SvxCaseMap::Capitalize:
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_capitalize );
+ break;
+ default:
+ ;
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxColor( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Colors do not need to be exported for Style-Option.
+ if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) &&
+ !rWrt.m_bCfgPreferStyles )
+ return rWrt;
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write color as Hint?" );
+
+ Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() );
+ if( COL_AUTO == aColor )
+ aColor = COL_BLACK;
+
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_color, GetCSS1_Color(aColor));
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxCrossedOut( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // This function only exports Hints!
+ // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
+
+ if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
+ OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
+ nullptr, nullptr, static_cast<const SvxCrossedOutItem *>(&rHt), nullptr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFont( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // No need to export Fonts for the Style-Option.
+ if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ return rWrt;
+
+ sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
+ switch( rHt.Which() )
+ {
+ case RES_CHRATR_CJK_FONT: nScript = CSS1_OUTMODE_CJK; break;
+ case RES_CHRATR_CTL_FONT: nScript = CSS1_OUTMODE_CTL; break;
+ }
+ if( !rWrt.IsCSS1Script( nScript ) )
+ return rWrt;
+
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write Font as Hint?" );
+
+ OUString sOut;
+ // MS IE3b1 has problems with single quotes
+ sal_uInt16 nMode = rWrt.m_nCSS1OutMode & CSS1_OUTMODE_ANY_ON;
+ sal_Unicode cQuote = nMode == CSS1_OUTMODE_RULE_ON ? '\"' : '\'';
+ SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), sOut, cQuote,
+ true );
+
+ rWrt.OutCSS1_Property( sCSS1_P_font_family, sOut );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFontHeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Font-Height need not be exported in the Style-Option.
+ // For Drop-Caps another Font-Size is exported.
+ if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ||
+ rWrt.IsCSS1Source( CSS1_OUTMODE_DROPCAP ) )
+ return rWrt;
+
+ sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
+ switch( rHt.Which() )
+ {
+ case RES_CHRATR_CJK_FONTSIZE: nScript = CSS1_OUTMODE_CJK; break;
+ case RES_CHRATR_CTL_FONTSIZE: nScript = CSS1_OUTMODE_CTL; break;
+ }
+ if( !rWrt.IsCSS1Script( nScript ) )
+ return rWrt;
+
+ sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
+ OString sHeight(OString::number(nHeight/20) + sCSS1_UNIT_pt);
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_font_size, sHeight);
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxPosture( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
+ switch( rHt.Which() )
+ {
+ case RES_CHRATR_CJK_POSTURE: nScript = CSS1_OUTMODE_CJK; break;
+ case RES_CHRATR_CTL_POSTURE: nScript = CSS1_OUTMODE_CTL; break;
+ }
+ if( !rWrt.IsCSS1Script( nScript ) )
+ return rWrt;
+
+ std::string_view pStr;
+ switch( static_cast<const SvxPostureItem&>(rHt).GetPosture() )
+ {
+ case ITALIC_NONE: pStr = sCSS1_PV_normal; break;
+ case ITALIC_OBLIQUE: pStr = sCSS1_PV_oblique; break;
+ case ITALIC_NORMAL:
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ {
+ // this also works in HTML does not need to be written as
+ // a STYLE-Options, and must not be written as Hint
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write italic as Hint?" );
+ pStr = sCSS1_PV_italic;
+ }
+ break;
+ default:
+ ;
+ }
+
+ if( !pStr.empty() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_style, pStr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxKerning( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ sal_Int16 nValue = static_cast<const SvxKerningItem&>(rHt).GetValue();
+ if( nValue )
+ {
+ OStringBuffer sOut;
+ if( nValue < 0 )
+ {
+ sOut.append('-');
+ nValue = -nValue;
+ }
+
+ // Width as n.n pt
+ nValue = (nValue + 1) / 2; // 1/10pt
+ sOut.append(OString::number(nValue / 10) + "." + OString::number(nValue % 10) +
+ sCSS1_UNIT_pt);
+
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_letter_spacing, sOut);
+ sOut.setLength(0);
+ }
+ else
+ {
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_letter_spacing,
+ sCSS1_PV_normal );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxLanguage( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Only export Language rules
+ if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ return rWrt;
+
+ sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
+ switch( rHt.Which() )
+ {
+ case RES_CHRATR_CJK_LANGUAGE: nScript = CSS1_OUTMODE_CJK; break;
+ case RES_CHRATR_CTL_LANGUAGE: nScript = CSS1_OUTMODE_CTL; break;
+ }
+ if( !rWrt.IsCSS1Script( nScript ) )
+ return rWrt;
+
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write Language as Hint?" );
+
+ LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage();
+ if( LANGUAGE_DONTKNOW == eLang )
+ return rWrt;
+
+ OUString sOut = LanguageTag::convertToBcp47( eLang );
+
+ rWrt.OutCSS1_Property( sCSS1_P_so_language, sOut );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxUnderline( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // This function only exports Hints!
+ // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
+
+ if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
+ OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
+ static_cast<const SvxUnderlineItem *>(&rHt), nullptr, nullptr, nullptr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxOverline( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // This function only exports Hints!
+ // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
+
+ if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
+ OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
+ nullptr, static_cast<const SvxOverlineItem *>(&rHt), nullptr, nullptr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxHidden( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if ( static_cast<const SvxCharHiddenItem&>(rHt).GetValue() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_display, sCSS1_PV_none );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFontWeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
+ switch( rHt.Which() )
+ {
+ case RES_CHRATR_CJK_WEIGHT: nScript = CSS1_OUTMODE_CJK; break;
+ case RES_CHRATR_CTL_WEIGHT: nScript = CSS1_OUTMODE_CTL; break;
+ }
+ if( !rWrt.IsCSS1Script( nScript ) )
+ return rWrt;
+
+ std::string_view pStr;
+ switch( static_cast<const SvxWeightItem&>(rHt).GetWeight() )
+ {
+ case WEIGHT_ULTRALIGHT: pStr = sCSS1_PV_extra_light; break;
+ case WEIGHT_LIGHT: pStr = sCSS1_PV_light; break;
+ case WEIGHT_SEMILIGHT: pStr = sCSS1_PV_demi_light; break;
+ case WEIGHT_NORMAL: pStr = sCSS1_PV_normal; break;
+ case WEIGHT_SEMIBOLD: pStr = sCSS1_PV_demi_bold; break;
+ case WEIGHT_BOLD:
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ {
+ // this also works in HTML does not need to be written as
+ // a STYLE-Options, and must not be written as Hint
+ OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
+ "write bold as Hint?" );
+ pStr = sCSS1_PV_bold;
+ }
+ break;
+ case WEIGHT_ULTRABOLD: pStr = sCSS1_PV_extra_bold; break;
+ default:
+ pStr = sCSS1_PV_normal;
+ }
+
+ if( !pStr.empty() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_weight, pStr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxBlink( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // This function only exports Hints!
+ // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
+
+ if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
+ OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
+ nullptr, nullptr, nullptr, static_cast<const SvxBlinkItem *>(&rHt) );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxLineSpacing( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Netscape4 has big problems with cell heights if the line spacing is
+ // changed within a table and the width of the table is not calculated
+ // automatically (== if there is a WIDTH-Option)
+ if( rWrt.m_bOutTable && rWrt.m_bCfgNetscape4 )
+ return rWrt;
+
+ const SvxLineSpacingItem& rLSItem = static_cast<const SvxLineSpacingItem&>(rHt);
+
+ sal_uInt16 nHeight = 0;
+ sal_uInt16 nPercentHeight = 0;
+ SvxLineSpaceRule eLineSpace = rLSItem.GetLineSpaceRule();
+ switch( rLSItem.GetInterLineSpaceRule() )
+ {
+ case SvxInterLineSpaceRule::Off:
+ case SvxInterLineSpaceRule::Fix:
+ {
+ switch( eLineSpace )
+ {
+ case SvxLineSpaceRule::Min:
+ case SvxLineSpaceRule::Fix:
+ nHeight = rLSItem.GetLineHeight();
+ break;
+ case SvxLineSpaceRule::Auto:
+ nPercentHeight = 100;
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+ case SvxInterLineSpaceRule::Prop:
+ nPercentHeight = rLSItem.GetPropLineSpace();
+ break;
+
+ default:
+ ;
+ }
+
+ if( nHeight )
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_line_height, static_cast<tools::Long>(nHeight) );
+ else if( nPercentHeight &&
+ !(nPercentHeight < 115 && rWrt.m_bParaDotLeaders )) // avoid HTML scrollbars and missing descenders
+ {
+ OString sHeight(OString::number(nPercentHeight) + "%");
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_line_height, sHeight);
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxAdjust( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Export Alignment in Style-Option only if the Tag does not allow ALIGN=xxx
+ if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) &&
+ !rWrt.m_bNoAlign)
+ return rWrt;
+
+ std::string_view pStr;
+ switch( static_cast<const SvxAdjustItem&>(rHt).GetAdjust() )
+ {
+ case SvxAdjust::Left: pStr = sCSS1_PV_left; break;
+ case SvxAdjust::Right: pStr = sCSS1_PV_right; break;
+ case SvxAdjust::Block: pStr = sCSS1_PV_justify; break;
+ case SvxAdjust::Center: pStr = sCSS1_PV_center; break;
+ default:
+ ;
+ }
+
+ if( !pStr.empty() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_align, pStr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFormatSplit( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ std::string_view pStr = static_cast<const SvxFormatSplitItem&>(rHt).GetValue()
+ ? sCSS1_PV_auto
+ : sCSS1_PV_avoid;
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_inside, pStr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SwFormatLayoutSplit( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ std::string_view pStr = static_cast<const SwFormatLayoutSplit&>(rHt).GetValue()
+ ? sCSS1_PV_auto
+ : sCSS1_PV_avoid;
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_inside, pStr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxWidows( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ OString aStr(OString::number(static_cast<const SvxWidowsItem&>(rHt).GetValue()));
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_widows, aStr );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxOrphans( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ OString aStr(OString::number(static_cast<const SvxOrphansItem&>(rHt).GetValue()));
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_orphans, aStr );
+
+ return rWrt;
+}
+
+static void OutCSS1_SwFormatDropAttrs( SwHTMLWriter& rHWrt,
+ const SwFormatDrop& rDrop,
+ const SfxItemSet *pCharFormatItemSet )
+{
+ // Text flows around on right side
+ rHWrt.OutCSS1_PropertyAscii( sCSS1_P_float, sCSS1_PV_left );
+
+ // number of lines -> use % for Font-Height!
+ OString sOut(OString::number(rDrop.GetLines()*100) + "%");
+ rHWrt.OutCSS1_PropertyAscii(sCSS1_P_font_size, sOut);
+
+ // distance to Text = right margin
+ sal_uInt16 nDistance = rDrop.GetDistance();
+ if( nDistance > 0 )
+ rHWrt.OutCSS1_UnitProperty( sCSS1_P_margin_right, nDistance );
+
+ const SwCharFormat *pDCCharFormat = rDrop.GetCharFormat();
+ if( pCharFormatItemSet )
+ rHWrt.OutCSS1_SfxItemSet( *pCharFormatItemSet );
+ else if( pDCCharFormat )
+ rHWrt.OutCSS1_SfxItemSet( pDCCharFormat->GetAttrSet() );
+ else if( (rHWrt.m_nCSS1OutMode & CSS1_OUTMODE_ANY_OFF) == CSS1_OUTMODE_RULE_OFF )
+ rHWrt.Strm().WriteOString( sCSS1_rule_end );
+
+}
+
+static SwHTMLWriter& OutCSS1_SwFormatDrop( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // never export as an Option of a paragraph, but only as Hints
+ if( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
+ return rWrt;
+
+ if( rWrt.m_bTagOn )
+ {
+ SwCSS1OutMode aMode( rWrt,
+ rWrt.m_nCSS1Script|CSS1_OUTMODE_SPAN_TAG1_ON|CSS1_OUTMODE_ENCODE|
+ CSS1_OUTMODE_DROPCAP, nullptr );
+
+ OutCSS1_SwFormatDropAttrs( rWrt, static_cast<const SwFormatDrop&>(rHt) );
+ // A "> is already printed by the calling OutCSS1_HintAsSpanTag.
+ }
+ else
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SwFormatFrameSize( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
+ Css1FrameSize nMode )
+{
+ const SwFormatFrameSize& rFSItem = static_cast<const SwFormatFrameSize&>(rHt);
+
+ if( nMode & Css1FrameSize::Width )
+ {
+ sal_uInt8 nPercentWidth = rFSItem.GetWidthPercent();
+ if( nPercentWidth )
+ {
+ OString sOut(OString::number(nPercentWidth) + "%");
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_width, sOut);
+ }
+ else if( nMode & Css1FrameSize::Pixel )
+ {
+ rWrt.OutCSS1_PixelProperty( sCSS1_P_width,
+ rFSItem.GetSize().Width() );
+ }
+ else
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_width,
+ rFSItem.GetSize().Width() );
+ }
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFirstLineIndent(SwHTMLWriter & rWrt, SfxPoolItem const& rHt)
+{
+ const SvxFirstLineIndentItem & rFirstLine(static_cast<const SvxFirstLineIndentItem&>(rHt));
+
+ // No Export of a firm attribute is needed if the new values
+ // match that of the current template
+
+ // The LineIndent of the first line might contain the room for numbering
+ tools::Long nFirstLineIndent = static_cast<tools::Long>(rFirstLine.GetTextFirstLineOffset())
+ - rWrt.m_nFirstLineIndent;
+ if (rWrt.m_nDfltFirstLineIndent != nFirstLineIndent)
+ {
+ rWrt.OutCSS1_UnitProperty(sCSS1_P_text_indent, nFirstLineIndent);
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxTextLeftMargin(SwHTMLWriter & rWrt, SfxPoolItem const& rHt)
+{
+ const SvxTextLeftMarginItem& rLeftMargin(static_cast<const SvxTextLeftMarginItem&>(rHt));
+
+ // No Export of a firm attribute is needed if the new values
+ // match that of the current template
+
+ // A left margin can exist because of a list nearby
+ tools::Long nLeftMargin = rLeftMargin.GetTextLeft() - rWrt.m_nLeftMargin;
+ if (rWrt.m_nDfltLeftMargin != nLeftMargin)
+ {
+ rWrt.OutCSS1_UnitProperty(sCSS1_P_margin_left, nLeftMargin);
+
+ // max-width = max-width - margin-left for TOC paragraphs with dot leaders
+ if (rWrt.m_bParaDotLeaders)
+ rWrt.OutCSS1_UnitProperty(sCSS1_P_max_width, tools::Long(DOT_LEADERS_MAX_WIDTH/2.54*72*20) - nLeftMargin);
+
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxRightMargin(SwHTMLWriter & rWrt, SfxPoolItem const& rHt)
+{
+ const SvxRightMarginItem& rRightMargin(static_cast<const SvxRightMarginItem&>(rHt));
+
+ // No Export of a firm attribute is needed if the new values
+ // match that of the current template
+
+ if (rWrt.m_nDfltRightMargin != rRightMargin.GetRight())
+ {
+ rWrt.OutCSS1_UnitProperty(sCSS1_P_margin_right, rRightMargin.GetRight());
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxLRSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ const SvxLRSpaceItem& rLRItem = static_cast<const SvxLRSpaceItem&>(rHt);
+
+ // No Export of a firm attribute is needed if the new values
+ // match that of the current template
+
+ // A left margin can exist because of a list nearby
+ tools::Long nLeftMargin = rLRItem.GetTextLeft() - rWrt.m_nLeftMargin;
+ if( rWrt.m_nDfltLeftMargin != nLeftMargin )
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_left, nLeftMargin );
+
+ // max-width = max-width - margin-left for TOC paragraphs with dot leaders
+ if( rWrt.m_bParaDotLeaders )
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_max_width, tools::Long(DOT_LEADERS_MAX_WIDTH/2.54*72*20) - nLeftMargin );
+
+ }
+
+ if( rWrt.m_nDfltRightMargin != rLRItem.GetRight() )
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_right, rLRItem.GetRight() );
+ }
+
+ // The LineIndent of the first line might contain the room for numbering
+ tools::Long nFirstLineIndent = static_cast<tools::Long>(rLRItem.GetTextFirstLineOffset()) -
+ rWrt.m_nFirstLineIndent;
+ if( rWrt.m_nDfltFirstLineIndent != nFirstLineIndent )
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_text_indent,
+ nFirstLineIndent );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxULSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ const SvxULSpaceItem& rULItem = static_cast<const SvxULSpaceItem&>(rHt);
+
+ if( rWrt.m_nDfltTopMargin != rULItem.GetUpper() )
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_top,
+ static_cast<tools::Long>(rULItem.GetUpper()) );
+ }
+
+ if( rWrt.m_nDfltBottomMargin != rULItem.GetLower() )
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_bottom,
+ static_cast<tools::Long>(rULItem.GetLower()) );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
+ const SvxULSpaceItem *pULItem,
+ const SvxLRSpaceItem *pLRItem )
+{
+ if( pLRItem && pULItem &&
+ pLRItem->GetLeft() == pLRItem->GetRight() &&
+ pLRItem->GetLeft() == pULItem->GetUpper() &&
+ pLRItem->GetLeft() == pULItem->GetLower() &&
+ pLRItem->GetLeft() != rWrt.m_nDfltLeftMargin &&
+ pLRItem->GetRight() != rWrt.m_nDfltRightMargin &&
+ pULItem->GetUpper() != rWrt.m_nDfltTopMargin &&
+ pULItem->GetLower() != rWrt.m_nDfltBottomMargin )
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_margin, pLRItem->GetLeft() );
+ }
+ else
+ {
+ if( pLRItem )
+ OutCSS1_SvxLRSpace( rWrt, *pLRItem );
+ if( pULItem )
+ OutCSS1_SvxULSpace( rWrt, *pULItem );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
+ const SfxItemSet& rItemSet )
+{
+ const SvxLRSpaceItem *pLRSpace = rItemSet.GetItemIfSet( RES_LR_SPACE, false/*bDeep*/ );
+ const SvxULSpaceItem *pULSpace = rItemSet.GetItemIfSet( RES_UL_SPACE, false/*bDeep*/ );
+
+ if( pLRSpace || pULSpace )
+ OutCSS1_SvxULSpace_SvxLRSpace( rWrt, pULSpace, pLRSpace );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( SwHTMLWriter& rWrt,
+ const SvxFormatBreakItem *pBreakItem,
+ const SwFormatPageDesc *pPDescItem,
+ const SvxFormatKeepItem *pKeepItem )
+{
+ if( !rWrt.IsHTMLMode(HTMLMODE_PRINT_EXT) )
+ return rWrt;
+
+ std::string_view pBreakBefore;
+ std::string_view pBreakAfter;
+
+ if( pKeepItem )
+ {
+ pBreakAfter = pKeepItem->GetValue() ? sCSS1_PV_avoid : sCSS1_PV_auto;
+ }
+ if( pBreakItem )
+ {
+ switch( pBreakItem->GetBreak() )
+ {
+ case SvxBreak::NONE:
+ pBreakBefore = sCSS1_PV_auto;
+ if( pBreakAfter.empty() )
+ pBreakAfter = sCSS1_PV_auto;
+ break;
+
+ case SvxBreak::PageBefore:
+ pBreakBefore = sCSS1_PV_always;
+ break;
+
+ case SvxBreak::PageAfter:
+ pBreakAfter= sCSS1_PV_always;
+ break;
+
+ default:
+ ;
+ }
+ }
+ if( pPDescItem )
+ {
+ const SwPageDesc *pPDesc = pPDescItem->GetPageDesc();
+ if( pPDesc )
+ {
+ switch( pPDesc->GetPoolFormatId() )
+ {
+ case RES_POOLPAGE_LEFT: pBreakBefore = sCSS1_PV_left; break;
+ case RES_POOLPAGE_RIGHT: pBreakBefore = sCSS1_PV_right; break;
+ default: pBreakBefore = sCSS1_PV_always; break;
+ }
+ }
+ else if( pBreakBefore.empty() )
+ {
+ pBreakBefore = sCSS1_PV_auto;
+ }
+ }
+
+ if (rWrt.mbSkipHeaderFooter)
+ // No page break when writing only a fragment.
+ return rWrt;
+
+ if( !pBreakBefore.empty() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_before,
+ pBreakBefore );
+ if( !pBreakAfter.empty() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_after,
+ pBreakAfter );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( SwHTMLWriter& rWrt,
+ const SfxItemSet& rItemSet,
+ bool bDeep )
+{
+ const SvxFormatBreakItem *pBreakItem = rItemSet.GetItemIfSet( RES_BREAK, bDeep );
+
+ const SwFormatPageDesc *pPDescItem = nullptr;
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ||
+ !rWrt.m_bCSS1IgnoreFirstPageDesc ||
+ rWrt.m_pStartNdIdx->GetIndex() !=
+ rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() )
+ pPDescItem = rItemSet.GetItemIfSet( RES_PAGEDESC, bDeep );
+
+ const SvxFormatKeepItem *pKeepItem = rItemSet.GetItemIfSet( RES_KEEP, bDeep );
+
+ if( pBreakItem || pPDescItem || pKeepItem )
+ OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( rWrt, pBreakItem,
+ pPDescItem, pKeepItem );
+
+ return rWrt;
+}
+
+// Wrapper for OutCSS1_SfxItemSet etc.
+static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ OutCSS1_SvxBrush( rWrt, rHt, sw::Css1Background::Attr, nullptr );
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
+ sw::Css1Background nMode,
+ const OUString* pGraphicName)
+{
+ // The Character-Attribute is skipped, if we are about to
+ // exporting options
+ if( rHt.Which() < RES_CHRATR_END &&
+ rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ return rWrt;
+
+ // start getting a few values
+// const Brush &rBrush = static_cast<const SvxBrushItem &>(rHt).GetBrush();
+ const Color & rColor = static_cast<const SvxBrushItem &>(rHt).GetColor();
+ OUString aLink = pGraphicName ? *pGraphicName
+ : static_cast<const SvxBrushItem &>(rHt).GetGraphicLink();
+ SvxGraphicPosition ePos = static_cast<const SvxBrushItem &>(rHt).GetGraphicPos();
+ if( sw::Css1Background::Page == nMode && !rWrt.mbEmbedImages )
+ {
+ // page style images are exported if not tiled
+ if( aLink.isEmpty() || GPOS_TILED==ePos )
+ return rWrt;
+ }
+
+ // get the color
+ bool bColor = false;
+ /// set <bTransparent> to true, if color is "no fill"/"auto fill"
+ bool bTransparent = (rColor == COL_TRANSPARENT);
+ Color aColor;
+ if( !bTransparent )
+ {
+ aColor = rColor;
+ bColor = true;
+ }
+
+ // and now the Graphic
+ OUString aGraphicInBase64;
+
+ // Embedded Graphic -> export WriteEmbedded
+ const Graphic* pGrf = nullptr;
+ if( rWrt.mbEmbedImages || aLink.isEmpty())
+ {
+ pGrf = static_cast<const SvxBrushItem &>(rHt).GetGraphic();
+ if( pGrf )
+ {
+ if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
+ {
+ rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ }
+ }
+ aLink.clear();
+ }
+ else if( !pGraphicName && rWrt.m_bCfgCpyLinkedGrfs )
+ {
+ OUString aGraphicAsLink = aLink;
+ rWrt.CopyLocalFileToINet( aGraphicAsLink );
+ aLink = aGraphicAsLink;
+ }
+ // In tables we only export something if there is a Graphic
+ if( (nMode == sw::Css1Background::Table || nMode == sw::Css1Background::TableRow) && !pGrf && !aLink.isEmpty())
+ return rWrt;
+
+ // if necessary, add the orientation of the Graphic
+ std::string_view pRepeat, pHori, pVert;
+ if( pGrf || !aLink.isEmpty() )
+ {
+ if( GPOS_TILED==ePos )
+ {
+ pRepeat = sCSS1_PV_repeat;
+ }
+ else
+ {
+ switch( ePos )
+ {
+ case GPOS_LT:
+ case GPOS_MT:
+ case GPOS_RT:
+ pHori = sCSS1_PV_top;
+ break;
+
+ case GPOS_LM:
+ case GPOS_MM:
+ case GPOS_RM:
+ pHori = sCSS1_PV_middle;
+ break;
+
+ case GPOS_LB:
+ case GPOS_MB:
+ case GPOS_RB:
+ pHori = sCSS1_PV_bottom;
+ break;
+
+ default:
+ ;
+ }
+
+ switch( ePos )
+ {
+ case GPOS_LT:
+ case GPOS_LM:
+ case GPOS_LB:
+ pVert = sCSS1_PV_left;
+ break;
+
+ case GPOS_MT:
+ case GPOS_MM:
+ case GPOS_MB:
+ pVert = sCSS1_PV_center;
+ break;
+
+ case GPOS_RT:
+ case GPOS_RM:
+ case GPOS_RB:
+ pVert = sCSS1_PV_right;
+ break;
+
+ default:
+ ;
+ }
+
+ if( !pHori.empty() || !pVert.empty() )
+ pRepeat = sCSS1_PV_no_repeat;
+ }
+ }
+
+ // now build the string
+ OUString sOut;
+ if( !pGrf && aLink.isEmpty() && !bColor )
+ {
+ // no color and no Link, but a transparent Brush
+ if( bTransparent && sw::Css1Background::Fly != nMode )
+ sOut += OStringToOUString(sCSS1_PV_transparent, RTL_TEXTENCODING_ASCII_US);
+ }
+ else
+ {
+ if( bColor )
+ {
+ OString sTmp(GetCSS1_Color(aColor));
+ sOut += OStringToOUString(sTmp, RTL_TEXTENCODING_ASCII_US);
+ }
+
+ if( pGrf || !aLink.isEmpty() )
+ {
+ if( bColor )
+ sOut += " ";
+
+ if(pGrf)
+ {
+ sOut += OStringToOUString(sCSS1_url, RTL_TEXTENCODING_ASCII_US) +
+ "(\'" OOO_STRING_SVTOOLS_HTML_O_data ":" + aGraphicInBase64 + "\')";
+ }
+ else
+ {
+ sOut += OStringToOUString(sCSS1_url, RTL_TEXTENCODING_ASCII_US)+
+ "(" + URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(),
+ aLink) + ")";
+ }
+
+ if( !pRepeat.empty() )
+ {
+ sOut += " " + OStringToOUString(pRepeat, RTL_TEXTENCODING_ASCII_US);
+ }
+
+ if( !pHori.empty() )
+ {
+ sOut += " " + OStringToOUString(pHori, RTL_TEXTENCODING_ASCII_US);
+ }
+ if( !pVert.empty() )
+ {
+ sOut += " " + OStringToOUString(pVert, RTL_TEXTENCODING_ASCII_US);
+ }
+
+ sOut += " " + OStringToOUString(sCSS1_PV_scroll, RTL_TEXTENCODING_ASCII_US) + " ";
+ }
+ }
+
+ if( !sOut.isEmpty() )
+ {
+ rWrt.OutCSS1_Property(sCSS1_P_background, std::string_view(), &sOut,
+ nMode);
+ }
+
+ return rWrt;
+}
+
+static void OutCSS1_SvxBorderLine( SwHTMLWriter& rWrt,
+ std::string_view pProperty,
+ const SvxBorderLine *pLine )
+{
+ if( !pLine || pLine->isEmpty() )
+ {
+ rWrt.OutCSS1_PropertyAscii( pProperty, sCSS1_PV_none );
+ return;
+ }
+
+ sal_Int32 nWidth = pLine->GetWidth();
+
+ OStringBuffer sOut;
+ if( nWidth <= o3tl::convert(1, o3tl::Length::px, o3tl::Length::twip) )
+ {
+ // If the width is smaller than one pixel, then export as 1px
+ // so that Netscape and IE show the line.
+ sOut.append("1px");
+ }
+ else
+ {
+ nWidth *= 5; // 1/100pt
+
+ // width in n.nn pt
+ sOut.append(OString::number(nWidth / 100) + "." + OString::number((nWidth/10) % 10) +
+ OString::number(nWidth % 10) + sCSS1_UNIT_pt);
+ }
+
+ // Line-Style: solid or double
+ sOut.append(' ');
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ sOut.append(sCSS1_PV_solid);
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ sOut.append(sCSS1_PV_dotted);
+ break;
+ case SvxBorderLineStyle::DASHED:
+ sOut.append(sCSS1_PV_dashed);
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ sOut.append(sCSS1_PV_double);
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ sOut.append(sCSS1_PV_ridge);
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ sOut.append(sCSS1_PV_groove);
+ break;
+ case SvxBorderLineStyle::INSET:
+ sOut.append(sCSS1_PV_inset);
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ sOut.append(sCSS1_PV_outset);
+ break;
+ default:
+ sOut.append(sCSS1_PV_none);
+ }
+ sOut.append(' ');
+
+ // and also the color
+ sOut.append(GetCSS1_Color(pLine->GetColor()));
+
+ rWrt.OutCSS1_PropertyAscii(pProperty, sOut);
+}
+
+SwHTMLWriter& OutCSS1_SvxBox( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Avoid interference between character and paragraph attributes
+ if( rHt.Which() < RES_CHRATR_END &&
+ rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
+ return rWrt;
+
+ if( rHt.Which() == RES_CHRATR_BOX )
+ {
+ if( rWrt.m_bTagOn )
+ {
+ // Inline-block to make the line height changing correspond to the character border
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, "inline-block");
+ }
+ else
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
+ return rWrt;
+ }
+ }
+
+ const SvxBoxItem& rBoxItem = static_cast<const SvxBoxItem&>(rHt);
+ const SvxBorderLine *pTop = rBoxItem.GetTop();
+ const SvxBorderLine *pBottom = rBoxItem.GetBottom();
+ const SvxBorderLine *pLeft = rBoxItem.GetLeft();
+ const SvxBorderLine *pRight = rBoxItem.GetRight();
+
+ if( (pTop && pBottom && pLeft && pRight &&
+ *pTop == *pBottom && *pTop == *pLeft && *pTop == *pRight) ||
+ (!pTop && !pBottom && !pLeft && !pRight) )
+ {
+ // all Lines are set and equal, or all Lines are not set
+ // => border : ...
+ OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border, pTop );
+ }
+ else
+ {
+ // otherwise export all Lines separately
+ OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_top, pTop );
+ OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_bottom, pBottom );
+ OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_left, pLeft );
+ OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_right, pRight );
+ }
+
+ tools::Long nTopDist = pTop ? rBoxItem.GetDistance( SvxBoxItemLine::TOP ) : 0;
+ tools::Long nBottomDist = pBottom ? rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) : 0;
+ tools::Long nLeftDist = pLeft ? rBoxItem.GetDistance( SvxBoxItemLine::LEFT ) : 0;
+ tools::Long nRightDist = pRight ? rBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) : 0;
+
+ if( nTopDist == nBottomDist && nLeftDist == nRightDist )
+ {
+ OStringBuffer sVal;
+ AddUnitPropertyValue(sVal, nTopDist, rWrt.GetCSS1Unit());
+ if( nTopDist != nLeftDist )
+ {
+ sVal.append(' ');
+ AddUnitPropertyValue(sVal, nLeftDist, rWrt.GetCSS1Unit());
+ }
+ rWrt.OutCSS1_PropertyAscii(sCSS1_P_padding, sVal);
+ }
+ else
+ {
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_top, nTopDist );
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_bottom, nBottomDist );
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_left, nLeftDist );
+ rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_right, nRightDist );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutCSS1_SvxFrameDirection( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // Language will be exported rules only
+ if( !rWrt.IsCSS1Source( CSS1_OUTMODE_TEMPLATE ) )
+ return rWrt;
+
+ SvxFrameDirection nDir =
+ static_cast< const SvxFrameDirectionItem& >( rHt ).GetValue();
+ std::string_view pStr;
+ switch( nDir )
+ {
+ case SvxFrameDirection::Horizontal_LR_TB:
+ case SvxFrameDirection::Vertical_LR_TB:
+ pStr = sCSS1_PV_ltr;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ case SvxFrameDirection::Vertical_RL_TB:
+ pStr = sCSS1_PV_rtl;
+ break;
+ case SvxFrameDirection::Environment:
+ pStr = sCSS1_PV_inherit;
+ break;
+ default: break;
+ }
+
+ if( !pStr.empty() )
+ rWrt.OutCSS1_PropertyAscii( sCSS1_P_direction, pStr );
+
+ return rWrt;
+}
+
+/*
+ * Place here the table for the HTML-Function-Pointer to the
+ * Export-Functions.
+ * They are local structures, only needed within the HTML-DLL.
+ */
+
+SwAttrFnTab const aCSS1AttrFnTab = {
+/* RES_CHRATR_CASEMAP */ OutCSS1_SvxCaseMap,
+/* RES_CHRATR_CHARSETCOLOR */ nullptr,
+/* RES_CHRATR_COLOR */ OutCSS1_SvxColor,
+/* RES_CHRATR_CONTOUR */ nullptr,
+/* RES_CHRATR_CROSSEDOUT */ OutCSS1_SvxCrossedOut,
+/* RES_CHRATR_ESCAPEMENT */ nullptr,
+/* RES_CHRATR_FONT */ OutCSS1_SvxFont,
+/* RES_CHRATR_FONTSIZE */ OutCSS1_SvxFontHeight,
+/* RES_CHRATR_KERNING */ OutCSS1_SvxKerning,
+/* RES_CHRATR_LANGUAGE */ OutCSS1_SvxLanguage,
+/* RES_CHRATR_POSTURE */ OutCSS1_SvxPosture,
+/* RES_CHRATR_UNUSED1*/ nullptr,
+/* RES_CHRATR_SHADOWED */ nullptr,
+/* RES_CHRATR_UNDERLINE */ OutCSS1_SvxUnderline,
+/* RES_CHRATR_WEIGHT */ OutCSS1_SvxFontWeight,
+/* RES_CHRATR_WORDLINEMODE */ nullptr,
+/* RES_CHRATR_AUTOKERN */ nullptr,
+/* RES_CHRATR_BLINK */ OutCSS1_SvxBlink,
+/* RES_CHRATR_NOHYPHEN */ nullptr, // new: don't separate
+/* RES_CHRATR_UNUSED2 */ nullptr,
+/* RES_CHRATR_BACKGROUND */ OutCSS1_SvxBrush, // new: character background
+/* RES_CHRATR_CJK_FONT */ OutCSS1_SvxFont,
+/* RES_CHRATR_CJK_FONTSIZE */ OutCSS1_SvxFontHeight,
+/* RES_CHRATR_CJK_LANGUAGE */ OutCSS1_SvxLanguage,
+/* RES_CHRATR_CJK_POSTURE */ OutCSS1_SvxPosture,
+/* RES_CHRATR_CJK_WEIGHT */ OutCSS1_SvxFontWeight,
+/* RES_CHRATR_CTL_FONT */ OutCSS1_SvxFont,
+/* RES_CHRATR_CTL_FONTSIZE */ OutCSS1_SvxFontHeight,
+/* RES_CHRATR_CTL_LANGUAGE */ OutCSS1_SvxLanguage,
+/* RES_CHRATR_CTL_POSTURE */ OutCSS1_SvxPosture,
+/* RES_CHRATR_CTL_WEIGHT */ OutCSS1_SvxFontWeight,
+/* RES_CHRATR_ROTATE */ nullptr,
+/* RES_CHRATR_EMPHASIS_MARK */ nullptr,
+/* RES_CHRATR_TWO_LINES */ nullptr,
+/* RES_CHRATR_SCALEW */ nullptr,
+/* RES_CHRATR_RELIEF */ nullptr,
+/* RES_CHRATR_HIDDEN */ OutCSS1_SvxHidden,
+/* RES_CHRATR_OVERLINE */ OutCSS1_SvxOverline,
+/* RES_CHRATR_RSID */ nullptr,
+/* RES_CHRATR_BOX */ OutCSS1_SvxBox,
+/* RES_CHRATR_SHADOW */ nullptr,
+/* RES_CHRATR_HIGHLIGHT */ nullptr,
+/* RES_CHRATR_GRABBAG */ nullptr,
+/* RES_CHRATR_BIDIRTL */ nullptr,
+/* RES_CHRATR_IDCTHINT */ nullptr,
+
+/* RES_TXTATR_REFMARK */ nullptr,
+/* RES_TXTATR_TOXMARK */ nullptr,
+/* RES_TXTATR_META */ nullptr,
+/* RES_TXTATR_METAFIELD */ nullptr,
+/* RES_TXTATR_AUTOFMT */ nullptr,
+/* RES_TXTATR_INETFMT */ nullptr,
+/* RES_TXTATR_CHARFMT */ nullptr,
+/* RES_TXTATR_CJK_RUBY */ nullptr,
+/* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr,
+/* RES_TXTATR_INPUTFIELD */ nullptr,
+/* RES_TXTATR_CONTENTCONTROL */ nullptr,
+
+/* RES_TXTATR_FIELD */ nullptr,
+/* RES_TXTATR_FLYCNT */ nullptr,
+/* RES_TXTATR_FTN */ nullptr,
+/* RES_TXTATR_ANNOTATION */ nullptr,
+/* RES_TXTATR_LINEBREAK */ nullptr,
+/* RES_TXTATR_DUMMY1 */ nullptr, // Dummy:
+
+/* RES_PARATR_LINESPACING */ OutCSS1_SvxLineSpacing,
+/* RES_PARATR_ADJUST */ OutCSS1_SvxAdjust,
+/* RES_PARATR_SPLIT */ OutCSS1_SvxFormatSplit,
+/* RES_PARATR_ORPHANS */ OutCSS1_SvxOrphans,
+/* RES_PARATR_WIDOWS */ OutCSS1_SvxWidows,
+/* RES_PARATR_TABSTOP */ nullptr,
+/* RES_PARATR_HYPHENZONE*/ nullptr,
+/* RES_PARATR_DROP */ OutCSS1_SwFormatDrop,
+/* RES_PARATR_REGISTER */ nullptr, // new: register-true
+/* RES_PARATR_NUMRULE */ nullptr,
+/* RES_PARATR_SCRIPTSPACE */ nullptr,
+/* RES_PARATR_HANGINGPUNCTUATION */ nullptr,
+/* RES_PARATR_FORBIDDEN_RULES */ nullptr, // new
+/* RES_PARATR_VERTALIGN */ nullptr, // new
+/* RES_PARATR_SNAPTOGRID*/ nullptr, // new
+/* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new
+/* RES_PARATR_OUTLINELEVEL */ nullptr, // new since cws outlinelevel
+/* RES_PARATR_RSID */ nullptr, // new
+/* RES_PARATR_GRABBAG */ nullptr,
+
+/* RES_PARATR_LIST_ID */ nullptr, // new
+/* RES_PARATR_LIST_LEVEL */ nullptr, // new
+/* RES_PARATR_LIST_ISRESTART */ nullptr, // new
+/* RES_PARATR_LIST_RESTARTVALUE */ nullptr, // new
+/* RES_PARATR_LIST_ISCOUNTED */ nullptr, // new
+/* RES_PARATR_LIST_AUTOFMT */ nullptr, // new
+
+/* RES_FILL_ORDER */ nullptr,
+/* RES_FRM_SIZE */ nullptr,
+/* RES_PAPER_BIN */ nullptr,
+/* RES_MARGIN_FIRSTLINE */ OutCSS1_SvxFirstLineIndent,
+/* RES_MARGIN_TEXTLEFT */ OutCSS1_SvxTextLeftMargin,
+/* RES_MARGIN_RIGHT */ OutCSS1_SvxRightMargin,
+/* RES_MARGIN_LEFT */ nullptr,
+/* RES_MARGIN_GUTTER */ nullptr,
+/* RES_MARGIN_GUTTER_RIGHT */ nullptr,
+/* RES_LR_SPACE */ OutCSS1_SvxLRSpace,
+/* RES_UL_SPACE */ OutCSS1_SvxULSpace,
+/* RES_PAGEDESC */ nullptr,
+/* RES_BREAK */ nullptr,
+/* RES_CNTNT */ nullptr,
+/* RES_HEADER */ nullptr,
+/* RES_FOOTER */ nullptr,
+/* RES_PRINT */ nullptr,
+/* RES_OPAQUE */ nullptr,
+/* RES_PROTECT */ nullptr,
+/* RES_SURROUND */ nullptr,
+/* RES_VERT_ORIENT */ nullptr,
+/* RES_HORI_ORIENT */ nullptr,
+/* RES_ANCHOR */ nullptr,
+/* RES_BACKGROUND */ OutCSS1_SvxBrush,
+/* RES_BOX */ OutCSS1_SvxBox,
+/* RES_SHADOW */ nullptr,
+/* RES_FRMMACRO */ nullptr,
+/* RES_COL */ nullptr,
+/* RES_KEEP */ nullptr,
+/* RES_URL */ nullptr,
+/* RES_EDIT_IN_READONLY */ nullptr,
+/* RES_LAYOUT_SPLIT */ nullptr,
+/* RES_CHAIN */ nullptr,
+/* RES_TEXTGRID */ nullptr,
+/* RES_LINENUMBER */ nullptr,
+/* RES_FTN_AT_TXTEND */ nullptr,
+/* RES_END_AT_TXTEND */ nullptr,
+/* RES_COLUMNBALANCE */ nullptr,
+/* RES_FRAMEDIR */ OutCSS1_SvxFrameDirection,
+/* RES_HEADER_FOOTER_EAT_SPACING */ nullptr,
+/* RES_ROW_SPLIT */ nullptr,
+/* RES_FLY_SPLIT */ nullptr,
+/* RES_FOLLOW_TEXT_FLOW */ nullptr,
+/* RES_COLLAPSING_BORDERS */ nullptr,
+/* RES_WRAP_INFLUENCE_ON_OBJPOS */ nullptr,
+/* RES_AUTO_STYLE */ nullptr,
+/* RES_FRMATR_STYLE_NAME */ nullptr,
+/* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr,
+/* RES_FRMATR_GRABBAG */ nullptr,
+/* RES_TEXT_VERT_ADJUST */ nullptr,
+/* RES_BACKGROUND_FULL_SIZE */ nullptr,
+/* RES_RTL_GUTTER */ nullptr,
+/* RES_DECORATIVE */ nullptr,
+
+/* RES_GRFATR_MIRRORGRF */ nullptr,
+/* RES_GRFATR_CROPGRF */ nullptr,
+/* RES_GRFATR_ROTATION */ nullptr,
+/* RES_GRFATR_LUMINANCE */ nullptr,
+/* RES_GRFATR_CONTRAST */ nullptr,
+/* RES_GRFATR_CHANNELR */ nullptr,
+/* RES_GRFATR_CHANNELG */ nullptr,
+/* RES_GRFATR_CHANNELB */ nullptr,
+/* RES_GRFATR_GAMMA */ nullptr,
+/* RES_GRFATR_INVERT */ nullptr,
+/* RES_GRFATR_TRANSPARENCY */ nullptr,
+/* RES_GRFATR_DRWAMODE */ nullptr,
+/* RES_GRFATR_DUMMY3 */ nullptr,
+/* RES_GRFATR_DUMMY4 */ nullptr,
+/* RES_GRFATR_DUMMY5 */ nullptr,
+
+/* RES_BOXATR_FORMAT */ nullptr,
+/* RES_BOXATR_FORMULA */ nullptr,
+/* RES_BOXATR_VALUE */ nullptr
+};
+
+static_assert(SAL_N_ELEMENTS(aCSS1AttrFnTab) == RES_BOXATR_END);
+
+void SwHTMLWriter::OutCSS1_SfxItemSet( const SfxItemSet& rItemSet,
+ bool bDeep, std::string_view rAdd )
+{
+ // print ItemSet, including all attributes
+ Out_SfxItemSet( aCSS1AttrFnTab, *this, rItemSet, bDeep );
+
+ // some Attributes require special treatment
+
+ // Underline, Overline, CrossedOut and Blink form together a CSS1-Property
+ // (doesn't work of course for Hints)
+ if( !IsCSS1Source(CSS1_OUTMODE_HINT) )
+ {
+ const SvxUnderlineItem *pUnderlineItem =
+ rItemSet.GetItemIfSet( RES_CHRATR_UNDERLINE, bDeep );
+
+ const SvxOverlineItem *pOverlineItem =
+ rItemSet.GetItemIfSet( RES_CHRATR_OVERLINE, bDeep );
+
+ const SvxCrossedOutItem *pCrossedOutItem =
+ rItemSet.GetItemIfSet( RES_CHRATR_CROSSEDOUT, bDeep );
+
+ const SvxBlinkItem *pBlinkItem =
+ rItemSet.GetItemIfSet( RES_CHRATR_BLINK, bDeep );
+
+ if( pUnderlineItem || pOverlineItem || pCrossedOutItem || pBlinkItem )
+ OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( *this, pUnderlineItem,
+ pOverlineItem,
+ pCrossedOutItem,
+ pBlinkItem );
+
+ OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, bDeep );
+ }
+
+ if (!rAdd.empty())
+ {
+ for (std::size_t index = 0; index != std::string_view::npos;)
+ {
+ std::string_view attr = o3tl::trim(o3tl::getToken(rAdd, ':', index));
+ assert(!attr.empty());
+ assert(index != std::string_view::npos);
+
+ std::string_view val = o3tl::trim(o3tl::getToken(rAdd, ':', index));
+ assert(!val.empty());
+ OutCSS1_PropertyAscii(attr, val);
+ }
+ }
+
+ if( m_bFirstCSS1Property )
+ return;
+
+ // if a Property was exported as part of a Style-Option,
+ // the Option still needs to be finished
+ OStringBuffer sOut;
+ switch( m_nCSS1OutMode & CSS1_OUTMODE_ANY_OFF )
+ {
+ case CSS1_OUTMODE_SPAN_TAG_OFF:
+ sOut.append(sCSS1_span_tag_end);
+ break;
+
+ case CSS1_OUTMODE_STYLE_OPT_OFF:
+ sOut.append(cCSS1_style_opt_end);
+ break;
+
+ case CSS1_OUTMODE_RULE_OFF:
+ sOut.append(sCSS1_rule_end);
+ break;
+ }
+ if (!sOut.isEmpty())
+ Strm().WriteOString( sOut );
+}
+
+SwHTMLWriter& OutCSS1_HintSpanTag( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_SPAN_TAG |
+ CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_HINT, nullptr );
+
+ Out( aCSS1AttrFnTab, rHt, rWrt );
+
+ if( !rWrt.m_bFirstCSS1Property && rWrt.m_bTagOn )
+ rWrt.Strm().WriteOString( sCSS1_span_tag_end );
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutCSS1_HintStyleOpt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT_ON |
+ CSS1_OUTMODE_ENCODE|
+ CSS1_OUTMODE_HINT, nullptr );
+
+ Out( aCSS1AttrFnTab, rHt, rWrt );
+
+ if( !rWrt.m_bFirstCSS1Property )
+ rWrt.Strm().WriteChar( '\"' );
+
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/css1atr.hxx b/sw/source/filter/html/css1atr.hxx
new file mode 100644
index 0000000000..42b925587f
--- /dev/null
+++ b/sw/source/filter/html/css1atr.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_CSS1ATR_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_CSS1ATR_HXX
+
+class SfxPoolItem;
+
+bool swhtml_css1atr_equalFontItems(const SfxPoolItem& r1, const SfxPoolItem& r2);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/css1kywd.hxx b/sw/source/filter/html/css1kywd.hxx
new file mode 100644
index 0000000000..42ad28da92
--- /dev/null
+++ b/sw/source/filter/html/css1kywd.hxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_CSS1KYWD_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_CSS1KYWD_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+constexpr inline std::string_view sCSS_mimetype = "text/css";
+
+constexpr inline std::string_view sCSS1_page = "page";
+//constexpr inline std::string_view sCSS1_media = "media";
+
+constexpr inline std::string_view sCSS1_link = "link";
+constexpr inline std::string_view sCSS1_visited = "visited";
+constexpr inline std::string_view sCSS1_first_letter = "first-letter";
+
+constexpr inline std::string_view sCSS1_left = "left";
+constexpr inline std::string_view sCSS1_right = "right";
+constexpr inline std::string_view sCSS1_first = "first";
+
+constexpr inline std::string_view sCSS1_url = "url";
+
+constexpr inline std::string_view sCSS1_UNIT_pt = "pt";
+constexpr inline std::string_view sCSS1_UNIT_mm = "mm";
+constexpr inline std::string_view sCSS1_UNIT_cm = "cm";
+constexpr inline std::string_view sCSS1_UNIT_pc = "pc";
+constexpr inline std::string_view sCSS1_UNIT_inch = "in";
+constexpr inline std::string_view sCSS1_UNIT_px = "px";
+
+// Strings for font properties
+
+constexpr inline std::string_view sCSS1_P_font_family = "font-family";
+
+constexpr inline std::string_view sCSS1_PV_serif = "serif";
+constexpr inline std::string_view sCSS1_PV_sans_serif = "sans-serif";
+constexpr inline std::string_view sCSS1_PV_cursive = "cursive";
+constexpr inline std::string_view sCSS1_PV_fantasy = "fantasy";
+constexpr inline std::string_view sCSS1_PV_monospace = "monospace";
+
+constexpr inline std::string_view sCSS1_P_font_style = "font-style";
+
+constexpr inline std::string_view sCSS1_PV_normal = "normal";
+constexpr inline std::string_view sCSS1_PV_italic = "italic";
+constexpr inline std::string_view sCSS1_PV_oblique = "oblique";
+
+constexpr inline std::string_view sCSS1_P_font_variant = "font-variant";
+
+//constexpr inline std::string_view sCSS1_PV_normal = "normal";
+constexpr inline std::string_view sCSS1_PV_small_caps = "small-caps";
+
+constexpr inline std::string_view sCSS1_P_text_transform = "text-transform";
+
+constexpr inline std::string_view sCSS1_PV_capitalize = "capitalize";
+constexpr inline std::string_view sCSS1_PV_uppercase = "uppercase";
+constexpr inline std::string_view sCSS1_PV_lowercase = "lowercase";
+
+constexpr inline std::string_view sCSS1_P_font_weight = "font-weight";
+
+constexpr inline std::string_view sCSS1_PV_extra_light = "extra-light";
+constexpr inline std::string_view sCSS1_PV_light = "light";
+constexpr inline std::string_view sCSS1_PV_demi_light = "demi-light";
+//constexpr inline std::string_view sCSS1_PV_medium = "medium";
+constexpr inline std::string_view sCSS1_PV_demi_bold = "demi-bold";
+constexpr inline std::string_view sCSS1_PV_bold = "bold";
+constexpr inline std::string_view sCSS1_PV_extra_bold = "extra-bold";
+
+constexpr inline std::string_view sCSS1_P_font_size = "font-size";
+
+constexpr inline std::string_view sCSS1_P_font = "font";
+
+// Strings for color and background properties
+
+constexpr inline std::string_view sCSS1_P_color = "color";
+
+constexpr inline std::string_view sCSS1_P_background = "background";
+constexpr inline std::string_view sCSS1_P_background_color = "background-color";
+
+constexpr inline std::string_view sCSS1_PV_transparent = "transparent";
+
+constexpr inline std::string_view sCSS1_PV_repeat = "repeat";
+constexpr inline std::string_view sCSS1_PV_no_repeat = "no-repeat";
+
+constexpr inline std::string_view sCSS1_PV_top = "top";
+constexpr inline std::string_view sCSS1_PV_middle = "middle";
+constexpr inline std::string_view sCSS1_PV_bottom = "bottom";
+
+constexpr inline std::string_view sCSS1_PV_scroll = "scroll";
+
+// Strings for text properties
+
+constexpr inline std::string_view sCSS1_P_letter_spacing = "letter-spacing";
+
+constexpr inline std::string_view sCSS1_P_text_decoration = "text-decoration";
+
+constexpr inline std::string_view sCSS1_PV_none = "none";
+constexpr inline std::string_view sCSS1_PV_underline = "underline";
+constexpr inline std::string_view sCSS1_PV_overline = "overline";
+constexpr inline std::string_view sCSS1_PV_line_through = "line-through";
+constexpr inline std::string_view sCSS1_PV_blink = "blink";
+
+constexpr inline std::string_view sCSS1_P_text_align = "text-align";
+
+constexpr inline std::string_view sCSS1_PV_left = "left";
+constexpr inline std::string_view sCSS1_PV_center = "center";
+constexpr inline std::string_view sCSS1_PV_right = "right";
+constexpr inline std::string_view sCSS1_PV_justify = "justify";
+
+constexpr inline std::string_view sCSS1_P_text_indent = "text-indent";
+
+constexpr inline std::string_view sCSS1_P_line_height = "line-height";
+
+constexpr inline std::string_view sCSS1_P_list_style_type = "list-style-type";
+
+// Strings for box properties
+
+constexpr inline std::string_view sCSS1_P_margin_left = "margin-left";
+constexpr inline std::string_view sCSS1_P_margin_right = "margin-right";
+constexpr inline std::string_view sCSS1_P_margin_top = "margin-top";
+constexpr inline std::string_view sCSS1_P_margin_bottom = "margin-bottom";
+constexpr inline std::string_view sCSS1_P_margin = "margin";
+
+constexpr inline std::string_view sCSS1_P_padding_top = "padding-top";
+constexpr inline std::string_view sCSS1_P_padding_bottom = "padding-bottom";
+constexpr inline std::string_view sCSS1_P_padding_left = "padding-left";
+constexpr inline std::string_view sCSS1_P_padding_right = "padding-right";
+constexpr inline std::string_view sCSS1_P_padding = "padding";
+
+constexpr inline std::string_view sCSS1_PV_auto = "auto";
+
+constexpr inline std::string_view sCSS1_P_border_left_width = "border-left-width";
+constexpr inline std::string_view sCSS1_P_border_right_width = "border-right-width";
+constexpr inline std::string_view sCSS1_P_border_top_width = "border-top-width";
+constexpr inline std::string_view sCSS1_P_border_bottom_width = "border-bottom-width";
+constexpr inline std::string_view sCSS1_P_border_width = "border-width";
+constexpr inline std::string_view sCSS1_P_border_color = "border-color";
+constexpr inline std::string_view sCSS1_P_border_style = "border-style";
+constexpr inline std::string_view sCSS1_P_border_left = "border-left";
+constexpr inline std::string_view sCSS1_P_border_right = "border-right";
+constexpr inline std::string_view sCSS1_P_border_top = "border-top";
+constexpr inline std::string_view sCSS1_P_border_bottom = "border-bottom";
+constexpr inline std::string_view sCSS1_P_border = "border";
+
+//constexpr inline std::string_view sCSS1_PV_none = "none";
+constexpr inline std::string_view sCSS1_PV_dotted = "dotted";
+constexpr inline std::string_view sCSS1_PV_dashed = "dashed";
+constexpr inline std::string_view sCSS1_PV_solid = "solid";
+constexpr inline std::string_view sCSS1_PV_double = "double";
+constexpr inline std::string_view sCSS1_PV_groove = "groove";
+constexpr inline std::string_view sCSS1_PV_ridge = "ridge";
+constexpr inline std::string_view sCSS1_PV_inset = "inset";
+constexpr inline std::string_view sCSS1_PV_outset = "outset";
+
+constexpr inline std::string_view sCSS1_P_width = "width";
+constexpr inline std::string_view sCSS1_P_max_width = "max-width";
+
+constexpr inline std::string_view sCSS1_P_height = "height";
+
+constexpr inline std::string_view sCSS1_P_float = "float";
+
+constexpr inline std::string_view sCSS1_P_column_count = "column-count";
+constexpr inline std::string_view sCSS1_P_dir = "dir";
+
+// Strings for positioning
+
+constexpr inline std::string_view sCSS1_P_position = "position";
+
+constexpr inline std::string_view sCSS1_PV_absolute = "absolute";
+
+constexpr inline std::string_view sCSS1_P_left = "left";
+
+constexpr inline std::string_view sCSS1_P_top = "top";
+
+// Strings for printing extensions
+
+constexpr inline std::string_view sCSS1_P_page_break_before = "page-break-before";
+constexpr inline std::string_view sCSS1_P_page_break_after = "page-break-after";
+constexpr inline std::string_view sCSS1_P_page_break_inside = "page-break-inside";
+constexpr inline std::string_view sCSS1_P_size = "size";
+constexpr inline std::string_view sCSS1_P_widows = "widows";
+constexpr inline std::string_view sCSS1_P_visibility = "visibility";
+constexpr inline std::string_view sCSS1_P_orphans = "orphans";
+//constexpr inline std::string_view sCSS1_P_marks = "marks";
+
+constexpr inline std::string_view sCSS1_PV_always = "always";
+constexpr inline std::string_view sCSS1_PV_avoid = "avoid";
+
+constexpr inline std::string_view sCSS1_PV_portrait = "portrait";
+constexpr inline std::string_view sCSS1_PV_landscape = "landscape";
+
+//constexpr inline std::string_view sCSS1_PV_crop = "crop";
+//constexpr inline std::string_view sCSS1_PV_cross = "cross";
+
+constexpr inline std::string_view sCSS1_P_so_language = "so-language";
+constexpr inline std::string_view sCSS1_P_direction = "direction";
+constexpr inline std::string_view sCSS1_PV_ltr = "ltr";
+constexpr inline std::string_view sCSS1_PV_rtl = "rtl";
+constexpr inline std::string_view sCSS1_PV_inherit = "inherit";
+
+constexpr inline std::string_view sCSS1_P_display = "display";
+
+constexpr inline std::string_view sCSS1_white_space = "white-space";
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx
new file mode 100644
index 0000000000..9f67d1ee03
--- /dev/null
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -0,0 +1,3467 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <comphelper/string.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmltokn.h>
+#include <svl/whiter.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/htmlmode.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtclds.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtinfmt.hxx>
+#include <txatbase.hxx>
+#include <frmatr.hxx>
+#include <charfmt.hxx>
+#include <fmtfld.hxx>
+#include <doc.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <swtable.hxx>
+#include <fldbas.hxx>
+#include <breakit.hxx>
+#include "htmlatr.hxx"
+#include "htmlnum.hxx"
+#include "wrthtml.hxx"
+#include "htmlfly.hxx"
+#include <numrule.hxx>
+#include <rtl/character.hxx>
+#include <osl/diagnose.h>
+#include <deque>
+
+#include <svtools/HtmlWriter.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <memory>
+#include <algorithm>
+
+using namespace css;
+
+HTMLOutEvent const aAnchorEventTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_O_SDonclick, OOO_STRING_SVTOOLS_HTML_O_onclick, SvMacroItemId::OnClick },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut },
+ { nullptr, nullptr, SvMacroItemId::NONE }
+};
+
+static SwHTMLWriter& OutHTML_SvxAdjust( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+
+sal_uInt16 SwHTMLWriter::GetDefListLvl( std::u16string_view rNm, sal_uInt16 nPoolId )
+{
+ if( nPoolId == RES_POOLCOLL_HTML_DD )
+ {
+ return 1 | HTML_DLCOLL_DD;
+ }
+ else if( nPoolId == RES_POOLCOLL_HTML_DT )
+ {
+ return 1 | HTML_DLCOLL_DT;
+ }
+
+ OUString sDTDD = OOO_STRING_SVTOOLS_HTML_dt " ";
+ if( o3tl::starts_with(rNm, sDTDD) )
+ // DefinitionList - term
+ return o3tl::narrowing<sal_uInt16>(o3tl::toInt32(rNm.substr( sDTDD.getLength() ))) | HTML_DLCOLL_DT;
+
+ sDTDD = OOO_STRING_SVTOOLS_HTML_dd " ";
+ if( o3tl::starts_with(rNm, sDTDD) )
+ // DefinitionList - definition
+ return o3tl::narrowing<sal_uInt16>(o3tl::toInt32(rNm.substr( sDTDD.getLength() ))) | HTML_DLCOLL_DD;
+
+ return 0;
+}
+
+void SwHTMLWriter::OutAndSetDefList( sal_uInt16 nNewLvl )
+{
+ // possibly, we first need to start a new list
+ if( m_nDefListLvl < nNewLvl )
+ {
+ // output </pre> for the previous(!) paragraph, if required.
+ // Preferable, the <pre> is exported by OutHTML_SwFormatOff for the
+ // previous paragraph already, but that's not possible, because a very
+ // deep look at the next paragraph (this one) is required to figure
+ // out that a def list starts here.
+
+ ChangeParaToken( HtmlTokenId::NONE );
+
+ // write according to the level difference
+ for( sal_uInt16 i=m_nDefListLvl; i<nNewLvl; i++ )
+ {
+ if (IsLFPossible())
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist) );
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd) );
+ IncIndentLevel();
+ SetLFPossible(true);
+ }
+ }
+ else if( m_nDefListLvl > nNewLvl )
+ {
+ for( sal_uInt16 i=nNewLvl ; i < m_nDefListLvl; i++ )
+ {
+ DecIndentLevel();
+ if (IsLFPossible())
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd), false );
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist), false );
+ SetLFPossible(true);
+ }
+ }
+
+ m_nDefListLvl = nNewLvl;
+}
+
+void SwHTMLWriter::ChangeParaToken( HtmlTokenId nNew )
+{
+ if( nNew != m_nLastParaToken && HtmlTokenId::PREFORMTXT_ON == m_nLastParaToken )
+ {
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_preformtxt), false );
+ SetLFPossible(true);
+ }
+ m_nLastParaToken = nNew;
+}
+
+sal_uInt16 SwHTMLWriter::GetCSS1ScriptForScriptType( sal_uInt16 nScriptType )
+{
+ sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT;
+
+ switch( nScriptType )
+ {
+ case i18n::ScriptType::LATIN:
+ nRet = CSS1_OUTMODE_WESTERN;
+ break;
+ case i18n::ScriptType::ASIAN:
+ nRet = CSS1_OUTMODE_CJK;
+ break;
+ case i18n::ScriptType::COMPLEX:
+ nRet = CSS1_OUTMODE_CTL;
+ break;
+ }
+
+ return nRet;
+}
+
+// a single output function should be enough for all formats
+/*
+ * Output the formats as follows
+ * - output the tag for formats for which a corresponding HTML tag exist
+ * - for all the other formats, output a paragraph tag <P> and set bUserFormat
+ * - if a paragraph alignment is set for the supplied ItemSet of the node or
+ * for the ItemSet of the format, output an ALIGN=xxx if HTML allows it
+ * - In all cases, hard attribute is written as STYLE option.
+ * If bUserFormat is not set, only the supplied ItemSet is considered.
+ * Otherwise, attributes of the format are output as well.
+ */
+
+namespace {
+
+struct SwHTMLTextCollOutputInfo
+{
+ OString aToken; // End token to be output
+ std::optional<SfxItemSet> moItemSet; // hard attribute
+
+ bool bInNumberBulletList; // in an enumerated list;
+ bool bParaPossible; // a </P> may be output additionally
+ bool bOutPara; // a </P> is supposed to be output
+ bool bOutDiv; // write a </DIV>
+
+ SwHTMLTextCollOutputInfo() :
+ bInNumberBulletList( false ),
+ bParaPossible( false ),
+ bOutPara( false ),
+ bOutDiv( false )
+ {}
+
+ bool HasParaToken() const { return aToken.getLength()==1 && aToken[0]=='P'; }
+ bool ShouldOutputToken() const { return bOutPara || !HasParaToken(); }
+};
+
+}
+
+SwHTMLFormatInfo::SwHTMLFormatInfo( const SwFormat *pF, SwDoc *pDoc, SwDoc *pTemplate,
+ bool bOutStyles,
+ LanguageType eDfltLang,
+ sal_uInt16 nCSS1Script )
+ : pFormat(pF)
+ , nLeftMargin(0)
+ , nRightMargin(0)
+ , nFirstLineIndent(0)
+ , nTopMargin(0)
+ , nBottomMargin(0)
+ , bScriptDependent( false )
+{
+ sal_uInt16 nRefPoolId = 0;
+ // Get the selector of the format
+ sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass,
+ nRefPoolId );
+ OSL_ENSURE( nDeep ? !aToken.isEmpty() : aToken.isEmpty(),
+ "Something seems to be wrong with this token!" );
+ OSL_ENSURE( nDeep ? nRefPoolId != 0 : nRefPoolId == 0,
+ "Something seems to be wrong with the comparison style!" );
+
+ bool bTextColl = pFormat->Which() == RES_TXTFMTCOLL ||
+ pFormat->Which() == RES_CONDTXTFMTCOLL;
+
+ const SwFormat *pReferenceFormat = nullptr; // Comparison format
+ if( nDeep != 0 )
+ {
+ // It's an HTML-tag style or this style is derived from such
+ // a style.
+ if( !bOutStyles )
+ {
+ // if no styles are exported, it may be necessary to additionally
+ // write hard attribute
+ switch( nDeep )
+ {
+ case CSS1_FMT_ISTAG:
+ case CSS1_FMT_CMPREF:
+ // for HTML-tag styles the differences to the original
+ // (if available)
+ pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId,
+ &pTemplate->getIDocumentStylePoolAccess() );
+ break;
+
+ default:
+ // otherwise, the differences to the HTML-tag style of the
+ // original or the ones to the current document, if it the
+ // HTML-tag style is not available
+ if( pTemplate )
+ pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId,
+ &pTemplate->getIDocumentStylePoolAccess() );
+ else
+ pReferenceFormat = SwHTMLWriter::GetParentFormat( *pFormat, nDeep );
+ break;
+ }
+ }
+ }
+ else if( bTextColl )
+ {
+ // HTML-tag styles that are not derived from a paragraph style
+ // must be exported as hard attribute relative to the text-body
+ // style. For a 'not-styles' export, the one of the HTML style
+ // should be used as a reference
+ if( !bOutStyles && pTemplate )
+ pReferenceFormat = pTemplate->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
+ else
+ pReferenceFormat = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
+ }
+
+ if( pReferenceFormat || nDeep==0 )
+ {
+ moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
+ pFormat->GetAttrSet().GetRanges() );
+ // if the differences to a different style are supposed to be
+ // written, hard attribute is necessary. This is always true
+ // for styles that are not derived from HTML-tag styles.
+
+ moItemSet->Set( pFormat->GetAttrSet() );
+
+ if( pReferenceFormat )
+ SwHTMLWriter::SubtractItemSet( *moItemSet, pReferenceFormat->GetAttrSet(), true );
+
+ // delete ItemSet that is empty straight away. This will save work
+ // later on
+ if( !moItemSet->Count() )
+ {
+ moItemSet.reset();
+ }
+ }
+
+ if( !bTextColl )
+ return;
+
+ if( bOutStyles )
+ {
+ // We have to add hard attributes for any script dependent
+ // item that is not accessed by the style
+ static const sal_uInt16 aWhichIds[3][4] =
+ {
+ { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT },
+ { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT },
+ { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE,
+ RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT }
+ };
+
+ sal_uInt16 nRef = 0;
+ sal_uInt16 aSets[2] = {0,0};
+ switch( nCSS1Script )
+ {
+ case CSS1_OUTMODE_WESTERN:
+ nRef = 0;
+ aSets[0] = 1;
+ aSets[1] = 2;
+ break;
+ case CSS1_OUTMODE_CJK:
+ nRef = 1;
+ aSets[0] = 0;
+ aSets[1] = 2;
+ break;
+ case CSS1_OUTMODE_CTL:
+ nRef = 2;
+ aSets[0] = 0;
+ aSets[1] = 1;
+ break;
+ }
+ for( int i=0; i<4; ++i )
+ {
+ const SfxPoolItem& rRef = pFormat->GetFormatAttr( aWhichIds[nRef][i] );
+ for(sal_uInt16 nSet : aSets)
+ {
+ const SfxPoolItem& rSet = pFormat->GetFormatAttr( aWhichIds[nSet][i] );
+ if( rSet != rRef )
+ {
+ if( !moItemSet )
+ moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
+ pFormat->GetAttrSet().GetRanges() );
+ moItemSet->Put( rSet );
+ }
+ }
+ }
+ }
+
+ // remember all the different default spacings from the style or
+ // the comparison style.
+ SvxFirstLineIndentItem const& rFirstLine(
+ (pReferenceFormat ? pReferenceFormat : pFormat)->GetFirstLineIndent());
+ SvxTextLeftMarginItem const& rTextLeftMargin(
+ (pReferenceFormat ? pReferenceFormat : pFormat)->GetTextLeftMargin());
+ SvxRightMarginItem const& rRightMargin(
+ (pReferenceFormat ? pReferenceFormat : pFormat)->GetRightMargin());
+ nLeftMargin = rTextLeftMargin.GetTextLeft();
+ nRightMargin = rRightMargin.GetRight();
+ nFirstLineIndent = rFirstLine.GetTextFirstLineOffset();
+
+ const SvxULSpaceItem &rULSpace =
+ (pReferenceFormat ? pReferenceFormat : pFormat)->GetULSpace();
+ nTopMargin = rULSpace.GetUpper();
+ nBottomMargin = rULSpace.GetLower();
+
+ // export language if it differs from the default language
+ TypedWhichId<SvxLanguageItem> nWhichId =
+ SwHTMLWriter::GetLangWhichIdFromScript( nCSS1Script );
+ const SvxLanguageItem& rLang = pFormat->GetFormatAttr( nWhichId );
+ LanguageType eLang = rLang.GetLanguage();
+ if( eLang != eDfltLang )
+ {
+ if( !moItemSet )
+ moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
+ pFormat->GetAttrSet().GetRanges() );
+ moItemSet->Put( rLang );
+ }
+
+ static const TypedWhichId<SvxLanguageItem> aWhichIds[3] =
+ { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE,
+ RES_CHRATR_CTL_LANGUAGE };
+ for(const TypedWhichId<SvxLanguageItem>& i : aWhichIds)
+ {
+ if( i != nWhichId )
+ {
+ const SvxLanguageItem& rTmpLang = pFormat->GetFormatAttr(i);
+ if( rTmpLang.GetLanguage() != eLang )
+ {
+ if( !moItemSet )
+ moItemSet.emplace( *pFormat->GetAttrSet().GetPool(),
+ pFormat->GetAttrSet().GetRanges() );
+ moItemSet->Put( rTmpLang );
+ }
+ }
+ }
+
+}
+
+SwHTMLFormatInfo::~SwHTMLFormatInfo()
+{
+}
+
+static void OutHTML_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat,
+ const SfxItemSet *pNodeItemSet,
+ SwHTMLTextCollOutputInfo& rInfo )
+{
+ OSL_ENSURE( RES_CONDTXTFMTCOLL==rFormat.Which() || RES_TXTFMTCOLL==rFormat.Which(),
+ "not a paragraph style" );
+
+ // First, some flags
+ sal_uInt16 nNewDefListLvl = 0;
+ sal_uInt16 nNumStart = USHRT_MAX;
+ bool bForceDL = false;
+ bool bDT = false;
+ rInfo.bInNumberBulletList = false; // Are we in a list?
+ bool bNumbered = false; // The current paragraph is numbered
+ bool bPara = false; // the current token is <P>
+ rInfo.bParaPossible = false; // a <P> may be additionally output
+ bool bNoEndTag = false; // don't output an end tag
+
+ rWrt.m_bNoAlign = false; // no ALIGN=... possible
+
+ if (rWrt.mbXHTML)
+ {
+ rWrt.m_bNoAlign = true;
+ }
+
+ sal_uInt8 nBulletGrfLvl = 255; // The bullet graphic we want to output
+
+ // Are we in a bulleted or numbered list?
+ const SwTextNode* pTextNd = rWrt.m_pCurrentPam->GetPointNode().GetTextNode();
+
+ SwHTMLNumRuleInfo aNumInfo;
+ if( rWrt.GetNextNumInfo() )
+ {
+ aNumInfo = *rWrt.GetNextNumInfo();
+ rWrt.ClearNextNumInfo();
+ }
+ else
+ {
+ aNumInfo.Set( *pTextNd );
+ }
+
+ if( aNumInfo.GetNumRule() )
+ {
+ rInfo.bInNumberBulletList = true;
+ nNewDefListLvl = 0;
+
+ // is the current paragraph numbered?
+ bNumbered = aNumInfo.IsNumbered();
+ sal_uInt8 nLvl = aNumInfo.GetLevel();
+
+ OSL_ENSURE( pTextNd->GetActualListLevel() == nLvl,
+ "Remembered Num level is wrong" );
+ OSL_ENSURE( bNumbered == pTextNd->IsCountedInList(),
+ "Remembered numbering state is wrong" );
+
+ if( bNumbered )
+ {
+ nBulletGrfLvl = nLvl; // only temporarily!!!
+ // #i57919#
+ // correction of re-factoring done by cws swnumtree:
+ // - <nNumStart> has to contain the restart value, if the
+ // numbering is restarted at this text node. Value <USHRT_MAX>
+ // indicates, that no additional restart value has to be written.
+ if ( pTextNd->IsListRestart() )
+ {
+ nNumStart = static_cast< sal_uInt16 >(pTextNd->GetActualListStartValue());
+ }
+ OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE,
+ "<PRE> was not closed before <LI>." );
+ }
+ }
+
+ // Now, we're getting the token and, if necessary, the class
+ std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat));
+ SwHTMLFormatInfo *pFormatInfo;
+ SwHTMLFormatInfos::iterator it = rWrt.m_TextCollInfos.find( pTmpInfo );
+ if (it != rWrt.m_TextCollInfos.end())
+ {
+ pFormatInfo = it->get();
+ }
+ else
+ {
+ pFormatInfo = new SwHTMLFormatInfo( &rFormat, rWrt.m_pDoc, rWrt.m_xTemplate.get(),
+ rWrt.m_bCfgOutStyles, rWrt.m_eLang,
+ rWrt.m_nCSS1Script );
+ rWrt.m_TextCollInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo));
+ if( rWrt.m_aScriptParaStyles.count( rFormat.GetName() ) )
+ pFormatInfo->bScriptDependent = true;
+ }
+
+ // Now, we define what is possible due to the token
+ HtmlTokenId nToken = HtmlTokenId::NONE; // token for tag change
+ bool bOutNewLine = false; // only output a single LF?
+ if( !pFormatInfo->aToken.isEmpty() )
+ {
+ // It is an HTML-tag style or the style is derived from such a
+ // style.
+ rInfo.aToken = pFormatInfo->aToken;
+
+ if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_address)
+ {
+ rInfo.bParaPossible = true;
+ rWrt.m_bNoAlign = true;
+ }
+ else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote)
+ {
+ rInfo.bParaPossible = true;
+ rWrt.m_bNoAlign = true;
+ }
+ else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_parabreak)
+ {
+ bPara = true;
+ }
+ else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_preformtxt)
+ {
+ if (HtmlTokenId::PREFORMTXT_ON == rWrt.m_nLastParaToken)
+ {
+ bOutNewLine = true;
+ }
+ else
+ {
+ nToken = HtmlTokenId::PREFORMTXT_ON;
+ rWrt.m_bNoAlign = true;
+ bNoEndTag = true;
+ }
+ }
+ else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt || rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dd)
+ {
+ bDT = rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt;
+ rInfo.bParaPossible = !bDT;
+ rWrt.m_bNoAlign = true;
+ bForceDL = true;
+ }
+ }
+ else
+ {
+ // all styles that do not correspond to an HTML tag, or that are
+ // not derived from it, are exported as <P>
+
+ rInfo.aToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ bPara = true;
+ }
+
+ // If necessary, take the hard attribute from the style
+ if( pFormatInfo->moItemSet )
+ {
+ OSL_ENSURE(!rInfo.moItemSet, "Where does this ItemSet come from?");
+ rInfo.moItemSet.emplace( *pFormatInfo->moItemSet );
+ }
+
+ // additionally, add the hard attribute from the paragraph
+ if( pNodeItemSet )
+ {
+ if (rInfo.moItemSet)
+ rInfo.moItemSet->Put( *pNodeItemSet );
+ else
+ rInfo.moItemSet.emplace( *pNodeItemSet );
+ }
+
+ // we will need the lower spacing of the paragraph later on
+ const SvxULSpaceItem& rULSpace =
+ pNodeItemSet ? pNodeItemSet->Get(RES_UL_SPACE)
+ : rFormat.GetULSpace();
+
+ if( (rWrt.m_bOutHeader &&
+ rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() ==
+ rWrt.m_pCurrentPam->GetMark()->GetNodeIndex()) ||
+ rWrt.m_bOutFooter )
+ {
+ if( rWrt.m_bCfgOutStyles )
+ {
+ SvxULSpaceItem aULSpaceItem( rULSpace );
+ if( rWrt.m_bOutHeader )
+ aULSpaceItem.SetLower( rWrt.m_nHeaderFooterSpace );
+ else
+ aULSpaceItem.SetUpper( rWrt.m_nHeaderFooterSpace );
+
+ if (!rInfo.moItemSet)
+ {
+ rInfo.moItemSet.emplace(*rFormat.GetAttrSet().GetPool(), svl::Items<RES_UL_SPACE, RES_UL_SPACE>);
+ }
+ rInfo.moItemSet->Put( aULSpaceItem );
+ }
+ rWrt.m_bOutHeader = false;
+ rWrt.m_bOutFooter = false;
+ }
+
+ if( bOutNewLine )
+ {
+ // output a line break (without indentation) at the beginning of the
+ // paragraph, only
+ rInfo.aToken.clear(); // don't output an end tag
+ rWrt.Strm().WriteOString( SAL_NEWLINE_STRING );
+
+ return;
+ }
+
+ // should an ALIGN=... be written?
+ const SvxAdjustItem* pAdjItem = nullptr;
+
+ if( rInfo.moItemSet )
+ pAdjItem = rInfo.moItemSet->GetItemIfSet( RES_PARATR_ADJUST, false );
+
+ // Consider the lower spacing of the paragraph? (never in the last
+ // paragraph of tables)
+ bool bUseParSpace = !rWrt.m_bOutTable ||
+ (rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() !=
+ rWrt.m_pCurrentPam->GetMark()->GetNodeIndex());
+ // If styles are exported, indented paragraphs become definition lists
+ SvxFirstLineIndentItem const& rFirstLine(
+ pNodeItemSet ? pNodeItemSet->Get(RES_MARGIN_FIRSTLINE)
+ : rFormat.GetFirstLineIndent());
+ SvxTextLeftMarginItem const& rTextLeftMargin(
+ pNodeItemSet ? pNodeItemSet->Get(RES_MARGIN_TEXTLEFT)
+ : rFormat.GetTextLeftMargin());
+ if( (!rWrt.m_bCfgOutStyles || bForceDL) && !rInfo.bInNumberBulletList )
+ {
+ sal_Int32 nLeftMargin;
+ if( bForceDL )
+ nLeftMargin = rTextLeftMargin.GetTextLeft();
+ else
+ nLeftMargin = rTextLeftMargin.GetTextLeft() > pFormatInfo->nLeftMargin
+ ? rTextLeftMargin.GetTextLeft() - pFormatInfo->nLeftMargin
+ : 0;
+
+ if( nLeftMargin > 0 && rWrt.m_nDefListMargin > 0 )
+ {
+ nNewDefListLvl = static_cast< sal_uInt16 >((nLeftMargin + (rWrt.m_nDefListMargin/2)) /
+ rWrt.m_nDefListMargin);
+ if( nNewDefListLvl == 0 && bForceDL && !bDT )
+ nNewDefListLvl = 1;
+ }
+ else
+ {
+ // If the left margin is 0 or negative, emulating indent
+ // with <dd> does not work. We then set a def list only if
+ // the dd style is used.
+ nNewDefListLvl = (bForceDL&& !bDT) ? 1 : 0;
+ }
+
+ bool bIsNextTextNode =
+ rWrt.m_pDoc->GetNodes()[rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex()+1]
+ ->IsTextNode();
+
+ if( bForceDL && bDT )
+ {
+ // Instead of a DD we must use a DT from the level above this one.
+ nNewDefListLvl++;
+ }
+ else if( !nNewDefListLvl && !rWrt.m_bCfgOutStyles && bPara &&
+ rULSpace.GetLower()==0 &&
+ ((bUseParSpace && bIsNextTextNode) || rWrt.m_nDefListLvl==1) &&
+ (!pAdjItem || SvxAdjust::Left==pAdjItem->GetAdjust()) )
+ {
+ // Export paragraphs without a lower spacing as DT
+ nNewDefListLvl = 1;
+ bDT = true;
+ rInfo.bParaPossible = false;
+ rWrt.m_bNoAlign = true;
+ }
+ }
+
+ if( nNewDefListLvl != rWrt.m_nDefListLvl )
+ rWrt.OutAndSetDefList( nNewDefListLvl );
+
+ // if necessary, start a bulleted or numbered list
+ if( rInfo.bInNumberBulletList )
+ {
+ OSL_ENSURE( !rWrt.m_nDefListLvl, "DL cannot be inside OL!" );
+ OutHTML_NumberBulletListStart( rWrt, aNumInfo );
+
+ if( bNumbered )
+ {
+ if( !rWrt.m_aBulletGrfs[nBulletGrfLvl].isEmpty() )
+ bNumbered = false;
+ else
+ nBulletGrfLvl = 255;
+ }
+ }
+
+ // Take the defaults of the style, because they don't need to be
+ // exported
+ rWrt.m_nDfltLeftMargin = pFormatInfo->nLeftMargin;
+ rWrt.m_nDfltRightMargin = pFormatInfo->nRightMargin;
+ rWrt.m_nDfltFirstLineIndent = pFormatInfo->nFirstLineIndent;
+
+ if( rInfo.bInNumberBulletList )
+ {
+ if( !rWrt.IsHTMLMode( HTMLMODE_LSPACE_IN_NUMBER_BULLET ) )
+ rWrt.m_nDfltLeftMargin = rTextLeftMargin.GetTextLeft();
+
+ // In numbered lists, don't output a first line indent.
+ rWrt.m_nFirstLineIndent = rFirstLine.GetTextFirstLineOffset();
+ }
+
+ if( rInfo.bInNumberBulletList && bNumbered && bPara && !rWrt.m_bCfgOutStyles )
+ {
+ // a single LI doesn't have spacing
+ rWrt.m_nDfltTopMargin = 0;
+ rWrt.m_nDfltBottomMargin = 0;
+ }
+ else if( rWrt.m_nDefListLvl && bPara )
+ {
+ // a single DD doesn't have spacing, as well
+ rWrt.m_nDfltTopMargin = 0;
+ rWrt.m_nDfltBottomMargin = 0;
+ }
+ else
+ {
+ rWrt.m_nDfltTopMargin = pFormatInfo->nTopMargin;
+ // if in the last paragraph of a table the lower paragraph spacing
+ // is changed, Netscape doesn't get it. That's why we don't
+ // export anything here for now, by setting this spacing to the
+ // default value.
+ if( rWrt.m_bCfgNetscape4 && !bUseParSpace )
+ rWrt.m_nDfltBottomMargin = rULSpace.GetLower();
+ else
+ rWrt.m_nDfltBottomMargin = pFormatInfo->nBottomMargin;
+ }
+
+ if( rWrt.m_nDefListLvl )
+ {
+ rWrt.m_nLeftMargin =
+ (rWrt.m_nDefListLvl-1) * rWrt.m_nDefListMargin;
+ }
+
+ if (rWrt.IsLFPossible() && !rWrt.m_bFirstLine)
+ rWrt.OutNewLine(); // paragraph tag on a new line
+ rInfo.bOutPara = false;
+
+ // this is now our new token
+ rWrt.ChangeParaToken( nToken );
+
+ bool bHasParSpace = bUseParSpace && rULSpace.GetLower() > 0;
+ // XHTML doesn't allow character children for <blockquote>.
+ bool bXhtmlBlockQuote = rWrt.mbXHTML && rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote;
+
+ // if necessary, start a new list item
+ bool bNumberedForListItem = bNumbered;
+ if (!bNumberedForListItem)
+ {
+ // Open a list also for the leading unnumbered nodes (= list headers in ODF terminology);
+ // to do that, detect if this unnumbered node is the first in this list
+ const auto& rPrevListInfo = rWrt.GetNumInfo();
+ if (rPrevListInfo.GetNumRule() != aNumInfo.GetNumRule() || aNumInfo.IsRestart(rPrevListInfo)
+ || rPrevListInfo.GetDepth() < aNumInfo.GetDepth())
+ bNumberedForListItem = true;
+ }
+ if( rInfo.bInNumberBulletList && bNumberedForListItem )
+ {
+ OStringBuffer sOut(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li);
+ if (!bNumbered)
+ {
+ // Handles list headers (<text:list-header> ODF element)
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\"display: block\"");
+ }
+ else if (USHRT_MAX != nNumStart)
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_value "=\"" + OString::number(nNumStart)
+ + "\"");
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), sOut);
+ }
+
+ if( rWrt.m_nDefListLvl > 0 && !bForceDL )
+ {
+ OString aTag = bDT ? OOO_STRING_SVTOOLS_HTML_dt : OOO_STRING_SVTOOLS_HTML_dd;
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag) );
+ }
+
+ if( pAdjItem &&
+ rWrt.IsHTMLMode( HTMLMODE_NO_CONTROL_CENTERING ) &&
+ rWrt.HasControls() )
+ {
+ // The align=... attribute does behave strange in netscape
+ // if there are controls in a paragraph, because the control and
+ // all text behind the control does not recognize this attribute.
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division;
+ rWrt.Strm().WriteOString( sOut );
+
+ rWrt.m_bTextAttr = false;
+ rWrt.m_bOutOpts = true;
+ OutHTML_SvxAdjust( rWrt, *pAdjItem );
+ rWrt.Strm().WriteChar( '>' );
+ pAdjItem = nullptr;
+ rWrt.m_bNoAlign = false;
+ rInfo.bOutDiv = true;
+ rWrt.IncIndentLevel();
+ rWrt.SetLFPossible(true);
+ rWrt.OutNewLine();
+ }
+
+ // for BLOCKQUOTE, ADDRESS and DD we output another paragraph token, if
+ // - no styles are written and
+ // - a lower spacing or a paragraph alignment exists
+ // Also, XHTML does not allow character children in this context.
+ OString aToken = rInfo.aToken;
+ if( (!rWrt.m_bCfgOutStyles || rWrt.mbXHTML) && rInfo.bParaPossible && !bPara &&
+ (bHasParSpace || bXhtmlBlockQuote || pAdjItem) )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + rInfo.aToken) );
+ aToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
+ bPara = true;
+ rWrt.m_bNoAlign = false;
+ }
+
+ LanguageType eLang;
+ if (rInfo.moItemSet)
+ {
+ const SvxLanguageItem& rLangItem = rInfo.moItemSet->Get(SwHTMLWriter::GetLangWhichIdFromScript(rWrt.m_nCSS1Script));
+ eLang = rLangItem.GetLanguage();
+ }
+ else
+ eLang = rWrt.m_eLang;
+
+ if( rInfo.moItemSet )
+ {
+ static const TypedWhichId<SvxLanguageItem> aWhichIds[3] = { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE };
+
+ for(auto const & i : aWhichIds)
+ {
+ // export language if it differs from the default language only.
+ const SvxLanguageItem* pTmpItem = rInfo.moItemSet->GetItemIfSet( i );
+ if( pTmpItem && pTmpItem->GetLanguage() == eLang )
+ rInfo.moItemSet->ClearItem( i );
+ }
+ }
+
+ // and the text direction
+ SvxFrameDirection nDir = rWrt.GetHTMLDirection(
+ (pNodeItemSet ? pNodeItemSet->Get( RES_FRAMEDIR )
+ : rFormat.GetFrameDir() ).GetValue() );
+
+ // We only write a <P>, if
+ // - we are not inside OL/UL/DL, or
+ // - the paragraph of an OL/UL is not numbered or
+ // - styles are not exported and
+ // - a lower spacing, or
+ // - a paragraph alignment exists, or
+ // - styles are exported and
+ // - the text body style was changed, or
+ // - a user format is exported, or
+ // - a paragraph attribute exists
+ if( !bPara ||
+ (!rInfo.bInNumberBulletList && !rWrt.m_nDefListLvl) ||
+ (rInfo.bInNumberBulletList && !bNumbered) ||
+ (!rWrt.m_bCfgOutStyles &&
+ (bHasParSpace || bXhtmlBlockQuote || pAdjItem ||
+ (eLang != LANGUAGE_DONTKNOW && eLang != rWrt.m_eLang))) ||
+ nDir != rWrt.m_nDirection ||
+ rWrt.m_bCfgOutStyles )
+ {
+ // now, options are output
+ rWrt.m_bTextAttr = false;
+ rWrt.m_bOutOpts = true;
+
+ OString sOut = "<" + rWrt.GetNamespace() + aToken;
+
+ if( eLang != LANGUAGE_DONTKNOW && eLang != rWrt.m_eLang )
+ {
+ rWrt.Strm().WriteOString( sOut );
+ sOut = ""_ostr;
+ rWrt.OutLanguage( eLang );
+ }
+
+ if( nDir != rWrt.m_nDirection )
+ {
+ if( !sOut.isEmpty() )
+ {
+ rWrt.Strm().WriteOString( sOut );
+ sOut = ""_ostr;
+ }
+ rWrt.OutDirection( nDir );
+ }
+
+ if( rWrt.m_bCfgOutStyles &&
+ (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ sOut = ""_ostr;
+ OUString aClass( pFormatInfo->aClass );
+ if( pFormatInfo->bScriptDependent )
+ {
+ if( !aClass.isEmpty() )
+ aClass += "-";
+ switch( rWrt.m_nCSS1Script )
+ {
+ case CSS1_OUTMODE_WESTERN:
+ aClass += "western";
+ break;
+ case CSS1_OUTMODE_CJK:
+ aClass += "cjk";
+ break;
+ case CSS1_OUTMODE_CTL:
+ aClass += "ctl";
+ break;
+ }
+ }
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aClass );
+ sOut += "\"";
+ }
+ rWrt.Strm().WriteOString( sOut );
+ sOut = ""_ostr;
+
+ std::string_view sStyle;
+ if (rWrt.IsSpacePreserve())
+ {
+ if (rWrt.mbXHTML)
+ rWrt.Strm().WriteOString(" xml:space=\"preserve\"");
+ else
+ sStyle = "white-space: pre-wrap";
+ }
+
+ // if necessary, output alignment
+ if( !rWrt.m_bNoAlign && pAdjItem )
+ OutHTML_SvxAdjust( rWrt, *pAdjItem );
+
+ rWrt.m_bParaDotLeaders = bPara && rWrt.m_bCfgPrintLayout && rWrt.indexOfDotLeaders(
+ pTextNd->GetAnyFormatColl().GetPoolFormatId(), pTextNd->GetText()) > -1;
+
+ // and now, if necessary, the STYLE options
+ if (rWrt.m_bCfgOutStyles && rInfo.moItemSet)
+ {
+ OutCSS1_ParaTagStyleOpt(rWrt, *rInfo.moItemSet, sStyle);
+ }
+
+ if (rWrt.m_bParaDotLeaders) {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\""
+ sCSS2_P_CLASS_leaders "\"><"
+ OOO_STRING_SVTOOLS_HTML_O_span;
+ rWrt.Strm().WriteOString( sOut );
+ sOut = ""_ostr;
+ }
+
+ rWrt.Strm().WriteChar( '>' );
+
+ // is a </P> supposed to be written?
+ rInfo.bOutPara =
+ bPara &&
+ ( rWrt.m_bCfgOutStyles || bHasParSpace );
+
+ // if no end tag is supposed to be written, delete it
+ if( bNoEndTag )
+ rInfo.aToken.clear();
+ }
+
+ if( nBulletGrfLvl != 255 )
+ {
+ OSL_ENSURE( aNumInfo.GetNumRule(), "Where is the numbering gone???" );
+ OSL_ENSURE( nBulletGrfLvl < MAXLEVEL, "There are not this many layers." );
+ const SwNumFormat& rNumFormat = aNumInfo.GetNumRule()->Get(nBulletGrfLvl);
+ OutHTML_BulletImage( rWrt, OOO_STRING_SVTOOLS_HTML_image, rNumFormat.GetBrush(),
+ rWrt.m_aBulletGrfs[nBulletGrfLvl]);
+ }
+
+ rWrt.GetNumInfo() = aNumInfo;
+
+ // reset the defaults
+ rWrt.m_nDfltLeftMargin = 0;
+ rWrt.m_nDfltRightMargin = 0;
+ rWrt.m_nDfltFirstLineIndent = 0;
+ rWrt.m_nDfltTopMargin = 0;
+ rWrt.m_nDfltBottomMargin = 0;
+ rWrt.m_nLeftMargin = 0;
+ rWrt.m_nFirstLineIndent = 0;
+}
+
+static void OutHTML_SwFormatOff( SwHTMLWriter& rWrt, const SwHTMLTextCollOutputInfo& rInfo )
+{
+ // if there is no token, we don't need to output anything
+ if( rInfo.aToken.isEmpty() )
+ {
+ rWrt.FillNextNumInfo();
+ const SwHTMLNumRuleInfo& rNextInfo = *rWrt.GetNextNumInfo();
+ // a bulleted list must be closed in PRE as well
+ if( rInfo.bInNumberBulletList )
+ {
+
+ const SwHTMLNumRuleInfo& rNRInfo = rWrt.GetNumInfo();
+ if( rNextInfo.GetNumRule() != rNRInfo.GetNumRule() ||
+ rNextInfo.GetDepth() != rNRInfo.GetDepth() ||
+ rNextInfo.IsNumbered() || rNextInfo.IsRestart(rNRInfo) )
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+ OutHTML_NumberBulletListEnd( rWrt, rNextInfo );
+ }
+ else if( rNextInfo.GetNumRule() != nullptr )
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+
+ return;
+ }
+
+ if( rInfo.ShouldOutputToken() )
+ {
+ if (rWrt.IsPrettyPrint() && rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+
+ // if necessary, for BLOCKQUOTE, ADDRESS and DD another paragraph token
+ // is output, if
+ // - no styles are written and
+ // - a lower spacing exists
+ if( rInfo.bParaPossible && rInfo.bOutPara )
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_parabreak), false );
+
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + rInfo.aToken), false );
+ rWrt.SetLFPossible(
+ rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dt &&
+ rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dd &&
+ rInfo.aToken != OOO_STRING_SVTOOLS_HTML_li);
+ }
+ if( rInfo.bOutDiv )
+ {
+ rWrt.DecIndentLevel();
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
+ rWrt.SetLFPossible(true);
+ }
+
+ // if necessary, close the list item, then close a bulleted or numbered list
+ if( rInfo.bInNumberBulletList )
+ {
+ rWrt.FillNextNumInfo();
+ OutHTML_NumberBulletListEnd( rWrt, *rWrt.GetNextNumInfo() );
+ }
+}
+
+namespace {
+
+class HTMLStartEndPos
+{
+ sal_Int32 m_nStart;
+ sal_Int32 m_nEnd;
+ std::unique_ptr<SfxPoolItem> m_pItem;
+
+public:
+
+ HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE );
+
+ const SfxPoolItem* GetItem() const { return m_pItem.get(); }
+
+ void SetStart(sal_Int32 nStt) { m_nStart = nStt; }
+ sal_Int32 GetStart() const { return m_nStart; }
+
+ sal_Int32 GetEnd() const { return m_nEnd; }
+ void SetEnd(sal_Int32 nE) { m_nEnd = nE; }
+};
+
+}
+
+HTMLStartEndPos::HTMLStartEndPos(const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE)
+ : m_nStart(nStt)
+ , m_nEnd(nE)
+ , m_pItem(rItem.Clone())
+{}
+
+typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions;
+
+namespace {
+
+enum HTMLOnOffState { HTML_NOT_SUPPORTED, // unsupported Attribute
+ HTML_REAL_VALUE, // Attribute with value
+ HTML_ON_VALUE, // Attribute is On-Tag
+ HTML_OFF_VALUE, // Attribute is Off-Tag
+ HTML_CHRFMT_VALUE, // Attribute for character format
+ HTML_COLOR_VALUE, // Attribute for foreground color
+ HTML_STYLE_VALUE, // Attribute must be exported as style
+ HTML_DROPCAP_VALUE, // DropCap-Attribute
+ HTML_AUTOFMT_VALUE }; // Attribute for automatic character styles
+
+class HTMLEndPosLst
+{
+ HTMLStartEndPositions m_aStartLst; // list, sorted for start positions
+ HTMLStartEndPositions m_aEndLst; // list, sorted for end positions
+ std::deque<sal_Int32> m_aScriptChgLst; // positions where script changes
+ // 0 is not contained in this list,
+ // but the text length
+ // the script that is valid up to the position
+ // contained in aScriptChgList at the same index
+ std::vector<sal_uInt16> m_aScriptLst;
+
+ SwDoc* m_pDoc; // the current document
+ SwDoc* m_pTemplate; // the HTML template (or 0)
+ std::optional<Color> m_xDefaultColor; // the default foreground colors
+ std::set<OUString>& m_rScriptTextStyles;
+
+ sal_uLong m_nHTMLMode;
+ bool m_bOutStyles : 1; // are styles exported
+
+ // Insert/remove a SttEndPos in/from the Start and End lists.
+ // The end position is known.
+ void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos );
+ void RemoveItem_( HTMLStartEndPositions::size_type nEndPos );
+
+ // determine the 'type' of the attribute
+ HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem );
+
+ // does a specific OnTag item exist
+ bool ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos );
+
+ // does an item exist that can be used to disable an attribute that
+ // is exported the same way as the supplied item in the same range?
+ bool ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
+ sal_Int32 nEndPos );
+
+ // adapt the end of a split item
+ void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
+ HTMLStartEndPositions::size_type nStartPos );
+
+ // insert an attribute in the lists and, if necessary, split it
+ void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
+ sal_Int32 nEnd );
+
+ // split an already existing attribute
+ void SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
+ sal_Int32 nEnd );
+
+ // Insert without taking care of script
+ void InsertNoScript( const SfxPoolItem& rItem, sal_Int32 nStart,
+ sal_Int32 nEnd, SwHTMLFormatInfos& rFormatInfos,
+ bool bParaAttrs );
+
+ const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat,
+ SwHTMLFormatInfos& rFormatInfos );
+
+public:
+
+ HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> xDfltColor,
+ bool bOutStyles, sal_uLong nHTMLMode,
+ const OUString& rText, std::set<OUString>& rStyles );
+ ~HTMLEndPosLst();
+
+ // insert an attribute
+ void Insert( const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd,
+ SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs=false );
+ void Insert( const SfxItemSet& rItemSet, sal_Int32 nStart, sal_Int32 nEnd,
+ SwHTMLFormatInfos& rFormatInfos, bool bDeep,
+ bool bParaAttrs=false );
+ void Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos,
+ SwHTMLFormatInfos& rFormatInfos );
+
+ sal_uInt16 GetScriptAtPos( sal_Int32 nPos,
+ sal_uInt16 nWeak );
+
+ void OutStartAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos );
+ void OutEndAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos );
+
+ bool IsHTMLMode(sal_uLong nMode) const { return (m_nHTMLMode & nMode) != 0; }
+};
+
+}
+
+void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos )
+{
+ // Insert the attribute in the Start list behind all attributes that
+ // were started before, or at the same position.
+ sal_Int32 nStart = pPos->GetStart();
+ HTMLStartEndPositions::size_type i {0};
+
+ while (i < m_aStartLst.size() && m_aStartLst[i]->GetStart() <= nStart)
+ ++i;
+ m_aStartLst.insert(m_aStartLst.begin() + i, pPos);
+
+ // the position in the End list was supplied
+ m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
+}
+
+void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos )
+{
+ HTMLStartEndPos* pPos = m_aEndLst[nEndPos];
+
+ // now, we are looking for it in the Start list
+ HTMLStartEndPositions::iterator it = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
+ OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
+ if (it != m_aStartLst.end())
+ m_aStartLst.erase(it);
+
+ m_aEndLst.erase(m_aEndLst.begin() + nEndPos);
+
+ delete pPos;
+}
+
+HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem )
+{
+ HTMLOnOffState eState = HTML_NOT_SUPPORTED;
+ switch( rItem.Which() )
+ {
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CTL_POSTURE:
+ switch( static_cast<const SvxPostureItem&>(rItem).GetPosture() )
+ {
+ case ITALIC_NORMAL:
+ eState = HTML_ON_VALUE;
+ break;
+ case ITALIC_NONE:
+ eState = HTML_OFF_VALUE;
+ break;
+ default:
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
+ eState = HTML_STYLE_VALUE;
+ break;
+ }
+ break;
+
+ case RES_CHRATR_CROSSEDOUT:
+ switch( rItem.StaticWhichCast(RES_CHRATR_CROSSEDOUT).GetStrikeout() )
+ {
+ case STRIKEOUT_SINGLE:
+ case STRIKEOUT_DOUBLE:
+ eState = HTML_ON_VALUE;
+ break;
+ case STRIKEOUT_NONE:
+ eState = HTML_OFF_VALUE;
+ break;
+ default:
+ ;
+ }
+ break;
+
+ case RES_CHRATR_ESCAPEMENT:
+ switch( static_cast<SvxEscapement>(rItem.StaticWhichCast(RES_CHRATR_ESCAPEMENT).GetEnumValue()) )
+ {
+ case SvxEscapement::Superscript:
+ case SvxEscapement::Subscript:
+ eState = HTML_ON_VALUE;
+ break;
+ case SvxEscapement::Off:
+ eState = HTML_OFF_VALUE;
+ break;
+ default:
+ ;
+ }
+ break;
+
+ case RES_CHRATR_UNDERLINE:
+ switch( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetLineStyle() )
+ {
+ case LINESTYLE_SINGLE:
+ eState = HTML_ON_VALUE;
+ break;
+ case LINESTYLE_NONE:
+ eState = HTML_OFF_VALUE;
+ break;
+ default:
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
+ eState = HTML_STYLE_VALUE;
+ break;
+ }
+ break;
+
+ case RES_CHRATR_OVERLINE:
+ case RES_CHRATR_HIDDEN:
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
+ eState = HTML_STYLE_VALUE;
+ break;
+
+ case RES_CHRATR_WEIGHT:
+ case RES_CHRATR_CJK_WEIGHT:
+ case RES_CHRATR_CTL_WEIGHT:
+ switch( static_cast<const SvxWeightItem&>(rItem).GetWeight() )
+ {
+ case WEIGHT_BOLD:
+ eState = HTML_ON_VALUE;
+ break;
+ case WEIGHT_NORMAL:
+ eState = HTML_OFF_VALUE;
+ break;
+ default:
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
+ eState = HTML_STYLE_VALUE;
+ break;
+ }
+ break;
+
+ case RES_CHRATR_BLINK:
+ eState = rItem.StaticWhichCast(RES_CHRATR_BLINK).GetValue() ? HTML_ON_VALUE
+ : HTML_OFF_VALUE;
+ break;
+
+ case RES_CHRATR_COLOR:
+ eState = HTML_COLOR_VALUE;
+ break;
+
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_LANGUAGE:
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_LANGUAGE:
+ case RES_CHRATR_CTL_FONT:
+ case RES_CHRATR_CTL_FONTSIZE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ case RES_TXTATR_INETFMT:
+ eState = HTML_REAL_VALUE;
+ break;
+
+ case RES_TXTATR_CHARFMT:
+ eState = HTML_CHRFMT_VALUE;
+ break;
+
+ case RES_TXTATR_AUTOFMT:
+ eState = HTML_AUTOFMT_VALUE;
+ break;
+
+ case RES_CHRATR_CASEMAP:
+ eState = HTML_STYLE_VALUE;
+ break;
+
+ case RES_CHRATR_KERNING:
+ eState = HTML_STYLE_VALUE;
+ break;
+
+ case RES_CHRATR_BACKGROUND:
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
+ eState = HTML_STYLE_VALUE;
+ break;
+
+ case RES_PARATR_DROP:
+ eState = HTML_DROPCAP_VALUE;
+ break;
+
+ case RES_CHRATR_BOX:
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES) )
+ eState = HTML_STYLE_VALUE;
+ break;
+ }
+
+ return eState;
+}
+
+bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos )
+{
+ for (auto pTest : m_aStartLst)
+ {
+ if( pTest->GetStart() > nPos )
+ {
+ // this attribute, and all attributes that follow, start later
+ break;
+ }
+ else if( pTest->GetEnd() > nPos )
+ {
+ // the attribute starts before, or at, the current position and
+ // ends after it
+ const SfxPoolItem *pItem = pTest->GetItem();
+ if( pItem->Which() == nWhich &&
+ HTML_ON_VALUE == GetHTMLItemState(*pItem) )
+ {
+ // an OnTag attribute was found
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
+ sal_Int32 nEndPos )
+{
+ if( nWhich != RES_CHRATR_CROSSEDOUT &&
+ nWhich != RES_CHRATR_UNDERLINE &&
+ nWhich != RES_CHRATR_BLINK )
+ {
+ return false;
+ }
+
+ for (auto pTest : m_aStartLst)
+ {
+ if( pTest->GetStart() > nStartPos )
+ {
+ // this attribute, and all attributes that follow, start later
+ break;
+ }
+ else if( pTest->GetStart()==nStartPos &&
+ pTest->GetEnd()==nEndPos )
+ {
+ // the attribute starts before or at the current position and
+ // ends after it
+ const SfxPoolItem *pItem = pTest->GetItem();
+ sal_uInt16 nTstWhich = pItem->Which();
+ if( (nTstWhich == RES_CHRATR_CROSSEDOUT ||
+ nTstWhich == RES_CHRATR_UNDERLINE ||
+ nTstWhich == RES_CHRATR_BLINK) &&
+ HTML_OFF_VALUE == GetHTMLItemState(*pItem) )
+ {
+ // an OffTag attribute was found that is exported the same
+ // way as the current item
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
+ HTMLStartEndPositions::size_type nStartPos )
+{
+ // fix the end position accordingly
+ pPos->SetEnd( nNewEnd );
+
+ // remove the item from the End list
+ HTMLStartEndPositions::iterator it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pPos);
+ OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
+ if (it != m_aEndLst.end())
+ m_aEndLst.erase(it);
+
+ // from now on, it is closed as the last one at the corresponding position
+ HTMLStartEndPositions::size_type nEndPos {0};
+ while (nEndPos < m_aEndLst.size() && m_aEndLst[nEndPos]->GetEnd() <= nNewEnd)
+ ++nEndPos;
+ m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
+
+ // now, adjust the attributes that got started afterwards
+ for (HTMLStartEndPositions::size_type i = nStartPos + 1; i < m_aStartLst.size(); ++i)
+ {
+ HTMLStartEndPos* pTest = m_aStartLst[i];
+ sal_Int32 nTestEnd = pTest->GetEnd();
+ if( pTest->GetStart() >= nNewEnd )
+ {
+ // the Test attribute and all the following ones start, after the
+ // split attribute ends
+ break;
+ }
+ else if( nTestEnd > nNewEnd )
+ {
+ // the Test attribute starts before the split attribute
+ // ends, and ends afterwards, i.e., it must be split, as well
+
+ // set the new end
+ pTest->SetEnd( nNewEnd );
+
+ // remove the attribute from the End list
+ it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
+ OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
+ if (it != m_aEndLst.end())
+ m_aEndLst.erase(it);
+
+ // it now ends as the first attribute in the respective position.
+ // We already know this position in the End list.
+ m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pTest);
+
+ // insert the 'rest' of the attribute
+ InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd );
+ }
+ }
+}
+
+void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
+ sal_Int32 nEnd )
+{
+ HTMLStartEndPositions::size_type i;
+ for (i = 0; i < m_aEndLst.size(); i++)
+ {
+ HTMLStartEndPos* pTest = m_aEndLst[i];
+ sal_Int32 nTestEnd = pTest->GetEnd();
+ if( nTestEnd <= nStart )
+ {
+ // the Test attribute ends, before the new one starts
+ continue;
+ }
+ else if( nTestEnd < nEnd )
+ {
+ if( pTest->GetStart() < nStart )
+ {
+ // the Test attribute ends, before the new one ends. Thus, the
+ // new attribute must be split.
+ InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i );
+ nStart = nTestEnd;
+ }
+ }
+ else
+ {
+ // the Test attribute (and all that follow) ends, before the new
+ // one ends
+ break;
+ }
+ }
+
+ // one attribute must still be inserted
+ InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i );
+}
+
+void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
+ sal_Int32 nEnd )
+{
+ sal_uInt16 nWhich = rItem.Which();
+
+ // first, we must search for the old items by using the start list and
+ // determine the new item range
+
+ for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
+ {
+ HTMLStartEndPos* pTest = m_aStartLst[i];
+ sal_Int32 nTestStart = pTest->GetStart();
+ sal_Int32 nTestEnd = pTest->GetEnd();
+
+ if( nTestStart >= nEnd )
+ {
+ // this attribute, and all that follow, start later
+ break;
+ }
+ else if( nTestEnd > nStart )
+ {
+ // the Test attribute ends in the range that must be deleted
+ const SfxPoolItem *pItem = pTest->GetItem();
+
+ // only the corresponding OnTag attributes have to be considered
+ if( pItem->Which() == nWhich &&
+ HTML_ON_VALUE == GetHTMLItemState( *pItem ) )
+ {
+ bool bDelete = true;
+
+ if( nTestStart < nStart )
+ {
+ // the start of the new attribute corresponds to the new
+ // end of the attribute
+ FixSplittedItem( pTest, nStart, i );
+ bDelete = false;
+ }
+ else
+ {
+ // the Test item only starts after the new end of the
+ // attribute. Therefore, it can be completely erased.
+ m_aStartLst.erase(m_aStartLst.begin() + i);
+ i--;
+
+ HTMLStartEndPositions::iterator it
+ = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
+ OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
+ if (it != m_aEndLst.end())
+ m_aEndLst.erase(it);
+ }
+
+ // if necessary, insert the second part of the split
+ // attribute
+ if( nTestEnd > nEnd )
+ {
+ InsertItem( *pTest->GetItem(), nEnd, nTestEnd );
+ }
+
+ if( bDelete )
+ delete pTest;
+ }
+ }
+ }
+}
+
+const SwHTMLFormatInfo *HTMLEndPosLst::GetFormatInfo( const SwFormat& rFormat,
+ SwHTMLFormatInfos& rFormatInfos )
+{
+ SwHTMLFormatInfo *pFormatInfo;
+ std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat));
+ SwHTMLFormatInfos::iterator it = rFormatInfos.find( pTmpInfo );
+ if (it != rFormatInfos.end())
+ {
+ pFormatInfo = it->get();
+ }
+ else
+ {
+ pFormatInfo = new SwHTMLFormatInfo(&rFormat, m_pDoc, m_pTemplate, m_bOutStyles);
+ rFormatInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo));
+ if (m_rScriptTextStyles.count(rFormat.GetName()))
+ pFormatInfo->bScriptDependent = true;
+ }
+
+ return pFormatInfo;
+}
+
+HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, std::optional<Color> xDfltCol, bool bStyles,
+ sal_uLong nMode, const OUString& rText, std::set<OUString>& rStyles)
+ : m_pDoc(pD)
+ , m_pTemplate(pTempl)
+ , m_xDefaultColor(std::move(xDfltCol))
+ , m_rScriptTextStyles(rStyles)
+ , m_nHTMLMode(nMode)
+ , m_bOutStyles(bStyles)
+{
+ sal_Int32 nEndPos = rText.getLength();
+ sal_Int32 nPos = 0;
+ while( nPos < nEndPos )
+ {
+ sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( rText, nPos );
+ nPos = g_pBreakIt->GetBreakIter()->endOfScript( rText, nPos, nScript );
+ m_aScriptChgLst.push_back(nPos);
+ m_aScriptLst.push_back(nScript);
+ }
+}
+
+HTMLEndPosLst::~HTMLEndPosLst()
+{
+ OSL_ENSURE(m_aStartLst.empty(), "Start List not empty in destructor");
+ OSL_ENSURE(m_aEndLst.empty(), "End List not empty in destructor");
+}
+
+void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem,
+ sal_Int32 nStart, sal_Int32 nEnd,
+ SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs )
+{
+ // no range ?? in that case, don't take it, it will never take effect !!
+ if( nStart == nEnd )
+ return;
+
+ bool bSet = false, bSplit = false;
+ switch( GetHTMLItemState(rItem) )
+ {
+ case HTML_ON_VALUE:
+ // output the attribute, if it isn't 'on', already
+ if( !ExistsOnTagItem( rItem.Which(), nStart ) )
+ bSet = true;
+ break;
+
+ case HTML_OFF_VALUE:
+ // If the corresponding attribute is 'on', split it.
+ // Additionally, output it as Style, if it is not set for the
+ // whole paragraph, because in that case it was already output
+ // together with the paragraph tag.
+ if( ExistsOnTagItem( rItem.Which(), nStart ) )
+ bSplit = true;
+ bSet = m_bOutStyles && !bParaAttrs && !ExistsOffTagItem(rItem.Which(), nStart, nEnd);
+ break;
+
+ case HTML_REAL_VALUE:
+ // we can always output the attribute
+ bSet = true;
+ break;
+
+ case HTML_STYLE_VALUE:
+ // We can only output the attribute as CSS1. If it is set for
+ // the paragraph, it was already output with the paragraph tag.
+ // The only exception is the character-background attribute. This
+ // attribute must always be handled like a Hint.
+ bSet = m_bOutStyles
+ && (!bParaAttrs || rItem.Which() == RES_CHRATR_BACKGROUND
+ || rItem.Which() == RES_CHRATR_BOX || rItem.Which() == RES_CHRATR_OVERLINE);
+ break;
+
+ case HTML_CHRFMT_VALUE:
+ {
+ OSL_ENSURE( RES_TXTATR_CHARFMT == rItem.Which(),
+ "Not a character style after all" );
+ const SwFormatCharFormat& rChrFormat = rItem.StaticWhichCast(RES_TXTATR_CHARFMT);
+ const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
+
+ const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos );
+ if( !pFormatInfo->aToken.isEmpty() )
+ {
+ // output the character style tag before the hard
+ // attributes
+ InsertItem( rItem, nStart, nEnd );
+ }
+ if( pFormatInfo->moItemSet )
+ {
+ Insert( *pFormatInfo->moItemSet, nStart, nEnd,
+ rFormatInfos, true, bParaAttrs );
+ }
+ }
+ break;
+
+ case HTML_AUTOFMT_VALUE:
+ {
+ OSL_ENSURE( RES_TXTATR_AUTOFMT == rItem.Which(),
+ "Not an automatic style, after all" );
+ const SwFormatAutoFormat& rAutoFormat = rItem.StaticWhichCast(RES_TXTATR_AUTOFMT);
+ const std::shared_ptr<SfxItemSet>& pSet = rAutoFormat.GetStyleHandle();
+ if( pSet )
+ Insert( *pSet, nStart, nEnd, rFormatInfos, true, bParaAttrs );
+ }
+ break;
+
+ case HTML_COLOR_VALUE:
+ // A foreground color as a paragraph attribute is only exported if
+ // it is not the same as the default color.
+ {
+ OSL_ENSURE( RES_CHRATR_COLOR == rItem.Which(),
+ "Not a foreground color, after all" );
+ Color aColor( rItem.StaticWhichCast(RES_CHRATR_COLOR).GetValue() );
+ if( COL_AUTO == aColor )
+ aColor = COL_BLACK;
+ bSet = !bParaAttrs || !m_xDefaultColor || !m_xDefaultColor->IsRGBEqual(aColor);
+ }
+ break;
+
+ case HTML_DROPCAP_VALUE:
+ {
+ OSL_ENSURE( RES_PARATR_DROP == rItem.Which(),
+ "Not a drop cap, after all" );
+ const SwFormatDrop& rDrop = rItem.StaticWhichCast(RES_PARATR_DROP);
+ nEnd = nStart + rDrop.GetChars();
+ if (!m_bOutStyles)
+ {
+ // At least use the attributes of the character style
+ const SwCharFormat *pCharFormat = rDrop.GetCharFormat();
+ if( pCharFormat )
+ {
+ Insert( pCharFormat->GetAttrSet(), nStart, nEnd,
+ rFormatInfos, true, bParaAttrs );
+ }
+ }
+ else
+ {
+ bSet = true;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+
+ if( bSet )
+ InsertItem( rItem, nStart, nEnd );
+ if( bSplit )
+ SplitItem( rItem, nStart, nEnd );
+}
+
+void HTMLEndPosLst::Insert( const SfxPoolItem& rItem,
+ sal_Int32 nStart, sal_Int32 nEnd,
+ SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs )
+{
+ bool bDependsOnScript = false, bDependsOnAnyScript = false;
+ sal_uInt16 nScript = i18n::ScriptType::LATIN;
+ switch( rItem.Which() )
+ {
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_LANGUAGE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ bDependsOnScript = true;
+ nScript = i18n::ScriptType::LATIN;
+ break;
+
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_LANGUAGE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ bDependsOnScript = true;
+ nScript = i18n::ScriptType::ASIAN;
+ break;
+
+ case RES_CHRATR_CTL_FONT:
+ case RES_CHRATR_CTL_FONTSIZE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ case RES_CHRATR_CTL_POSTURE:
+ case RES_CHRATR_CTL_WEIGHT:
+ bDependsOnScript = true;
+ nScript = i18n::ScriptType::COMPLEX;
+ break;
+ case RES_TXTATR_CHARFMT:
+ {
+ const SwFormatCharFormat& rChrFormat = rItem.StaticWhichCast(RES_TXTATR_CHARFMT);
+ const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
+ const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos );
+ if( pFormatInfo->bScriptDependent )
+ {
+ bDependsOnScript = true;
+ bDependsOnAnyScript = true;
+ }
+ }
+ break;
+ case RES_TXTATR_INETFMT:
+ {
+ if (GetFormatInfo(*m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_INET_NORMAL),
+ rFormatInfos)
+ ->bScriptDependent
+ || GetFormatInfo(*m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_INET_VISIT),
+ rFormatInfos)
+ ->bScriptDependent)
+ {
+ bDependsOnScript = true;
+ bDependsOnAnyScript = true;
+ }
+ }
+ break;
+ }
+
+ if( bDependsOnScript )
+ {
+ sal_Int32 nPos = nStart;
+ for (size_t i = 0; i < m_aScriptChgLst.size(); i++)
+ {
+ sal_Int32 nChgPos = m_aScriptChgLst[i];
+ if( nPos >= nChgPos )
+ {
+ // the hint starts behind or at the next script change,
+ // so we may continue with this position.
+ continue;
+ }
+ if( nEnd <= nChgPos )
+ {
+ // the (rest of) the hint ends before or at the next script
+ // change, so we can insert it, but only if it belongs
+ // to the current script.
+ if (bDependsOnAnyScript || nScript == m_aScriptLst[i])
+ InsertNoScript( rItem, nPos, nEnd, rFormatInfos,
+ bParaAttrs );
+ break;
+ }
+
+ // the hint starts before the next script change and ends behind
+ // it, so we can insert a hint up to the next script change and
+ // continue with the rest of the hint.
+ if (bDependsOnAnyScript || nScript == m_aScriptLst[i])
+ InsertNoScript( rItem, nPos, nChgPos, rFormatInfos, bParaAttrs );
+ nPos = nChgPos;
+ }
+ }
+ else
+ {
+ InsertNoScript( rItem, nStart, nEnd, rFormatInfos, bParaAttrs );
+ }
+}
+
+void HTMLEndPosLst::Insert( const SfxItemSet& rItemSet,
+ sal_Int32 nStart, sal_Int32 nEnd,
+ SwHTMLFormatInfos& rFormatInfos,
+ bool bDeep, bool bParaAttrs )
+{
+ SfxWhichIter aIter( rItemSet );
+
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == aIter.GetItemState( bDeep, &pItem ) )
+ {
+ Insert( *pItem, nStart, nEnd, rFormatInfos, bParaAttrs );
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void HTMLEndPosLst::Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos,
+ SwHTMLFormatInfos& rFormatInfos )
+{
+ const SdrObject* pTextObj = SwHTMLWriter::GetMarqueeTextObj( rFormat );
+
+ if( !pTextObj )
+ return;
+
+ // get the edit engine attributes of the object as SW attributes and
+ // insert them as hints. Because of the amount of Hints the styles
+ // are not considered!
+ const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( *rFormatItemSet.GetPool() );
+ SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, pTextObj );
+ bool bOutStylesOld = m_bOutStyles;
+ m_bOutStyles = false;
+ Insert( aItemSet, nPos, nPos+1, rFormatInfos, false );
+ m_bOutStyles = bOutStylesOld;
+}
+
+sal_uInt16 HTMLEndPosLst::GetScriptAtPos( sal_Int32 nPos, sal_uInt16 nWeak )
+{
+ sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT;
+
+ size_t nScriptChgs = m_aScriptChgLst.size();
+ size_t i=0;
+ while (i < nScriptChgs && nPos >= m_aScriptChgLst[i])
+ i++;
+ OSL_ENSURE( i < nScriptChgs, "script list is too short" );
+ if( i < nScriptChgs )
+ {
+ if (i18n::ScriptType::WEAK == m_aScriptLst[i])
+ nRet = nWeak;
+ else
+ nRet = SwHTMLWriter::GetCSS1ScriptForScriptType(m_aScriptLst[i]);
+ }
+
+ return nRet;
+}
+
+void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos )
+{
+ rWrt.m_bTagOn = true;
+
+ // Character border attribute must be the first which is written out
+ // because of border merge.
+ HTMLStartEndPositions::size_type nCharBoxIndex = 0;
+ while (nCharBoxIndex < m_aStartLst.size()
+ && m_aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX)
+ {
+ ++nCharBoxIndex;
+ }
+
+ // the attributes of the start list are sorted in ascending order
+ for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
+ {
+ HTMLStartEndPos *pPos = nullptr;
+ if (nCharBoxIndex < m_aStartLst.size())
+ {
+ if( i == 0 )
+ pPos = m_aStartLst[nCharBoxIndex];
+ else if( i == nCharBoxIndex )
+ pPos = m_aStartLst[0];
+ else
+ pPos = m_aStartLst[i];
+ }
+ else
+ pPos = m_aStartLst[i];
+
+ sal_Int32 nStart = pPos->GetStart();
+ if( nStart > nPos )
+ {
+ // this attribute, and all that follow, will be opened later on
+ break;
+ }
+ else if( nStart == nPos )
+ {
+ // output the attribute
+ sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
+ sal_uInt16 nWhich = pPos->GetItem()->Which();
+ if( RES_TXTATR_CHARFMT == nWhich ||
+ RES_TXTATR_INETFMT == nWhich ||
+ RES_PARATR_DROP == nWhich )
+ {
+ rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
+ }
+ HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
+ Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
+ rWrt.maStartedAttributes[pPos->GetItem()->Which()]++;
+ rWrt.m_nCSS1Script = nCSS1Script;
+ }
+ }
+}
+
+void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos )
+{
+ rWrt.m_bTagOn = false;
+
+ // the attributes in the End list are sorted in ascending order
+ HTMLStartEndPositions::size_type i {0};
+ while (i < m_aEndLst.size())
+ {
+ HTMLStartEndPos* pPos = m_aEndLst[i];
+ sal_Int32 nEnd = pPos->GetEnd();
+
+ if( SAL_MAX_INT32 == nPos || nEnd == nPos )
+ {
+ HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
+ // Skip closing span if next character span has the same border (border merge)
+ bool bSkipOut = false;
+ if( pPos->GetItem()->Which() == RES_CHRATR_BOX )
+ {
+ HTMLStartEndPositions::iterator it
+ = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
+ OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
+ if (it != m_aStartLst.end())
+ ++it;
+ while (it != m_aStartLst.end())
+ {
+ HTMLStartEndPos *pEndPos = *it;
+ if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX &&
+ *static_cast<const SvxBoxItem*>(pEndPos->GetItem()) ==
+ *static_cast<const SvxBoxItem*>(pPos->GetItem()) )
+ {
+ pEndPos->SetStart(pPos->GetStart());
+ bSkipOut = true;
+ break;
+ }
+ ++it;
+ }
+ }
+ if( !bSkipOut )
+ {
+ Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
+ rWrt.maStartedAttributes[pPos->GetItem()->Which()]--;
+ }
+ RemoveItem_( i );
+ }
+ else if( nEnd > nPos )
+ {
+ // this attribute, and all that follow, are closed later on
+ break;
+ }
+ else
+ {
+ // The attribute is closed before the current position. This
+ // is not allowed, but we can handle it anyway.
+ OSL_ENSURE( nEnd >= nPos,
+ "The attribute should've been closed a long time ago" );
+ i++;
+ }
+ }
+}
+
+static constexpr bool IsLF(sal_Unicode ch) { return ch == '\n'; }
+
+static constexpr bool IsWhitespaceExcludingLF(sal_Unicode ch)
+{
+ return ch == ' ' || ch == '\t' || ch == '\r';
+}
+
+static constexpr bool IsWhitespaceIncludingLF(sal_Unicode ch)
+{
+ return IsWhitespaceExcludingLF(ch) || IsLF(ch);
+}
+
+static bool NeedPreserveWhitespace(std::u16string_view str, bool xml)
+{
+ if (str.empty())
+ return false;
+ // leading / trailing spaces
+ // A leading / trailing \n would turn into a leading / trailing <br/>,
+ // and will not disappear, even without space preserving option
+ if (IsWhitespaceExcludingLF(str.front()) || IsWhitespaceExcludingLF(str.back()))
+ return true;
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ if (xml)
+ {
+ // No need to consider \n, which convert to <br/>, when it's after a space
+ // (but handle it *before* a space)
+ if (IsWhitespaceIncludingLF(str[i]))
+ {
+ do
+ {
+ ++i;
+ if (i == str.size())
+ return false;
+ } while (IsLF(str[i]));
+ if (IsWhitespaceExcludingLF(str[i]))
+ return true; // Second whitespace in a row
+ }
+ }
+ else // html
+ {
+ // Only consider \n, when an adjacent space is not \n - which would be eaten
+ // without a space preserving option
+ if (IsWhitespaceExcludingLF(str[i]))
+ {
+ ++i;
+ if (i == str.size())
+ return false;
+ if (IsWhitespaceIncludingLF(str[i]))
+ return true; // Any whitespace after a non-LF whitespace
+ }
+ else if (IsLF(str[i]))
+ {
+ do
+ {
+ ++i;
+ if (i == str.size())
+ return false;
+ }
+ while (IsLF(str[i]));
+ if (IsWhitespaceExcludingLF(str[i]))
+ return true; // A non-LF whitespace after a LF
+ }
+ }
+ }
+ return false;
+}
+
+/* Output of the nodes*/
+SwHTMLWriter& OutHTML_SwTextNode( SwHTMLWriter& rWrt, const SwContentNode& rNode )
+{
+ const SwTextNode * pNd = &static_cast<const SwTextNode&>(rNode);
+
+ const OUString& rStr = pNd->GetText();
+ sal_Int32 nEnd = rStr.getLength();
+
+ // special case: empty node and HR style (horizontal rule)
+ // output a <HR>, only
+ sal_uInt16 nPoolId = pNd->GetAnyFormatColl().GetPoolFormatId();
+
+ // Handle horizontal rule <hr>
+ if (!nEnd &&
+ (RES_POOLCOLL_HTML_HR==nPoolId || pNd->GetAnyFormatColl().GetName() == OOO_STRING_SVTOOLS_HTML_horzrule))
+ {
+ // then, the paragraph-anchored graphics/OLE objects in the paragraph
+ // MIB 8.7.97: We enclose the line in a <PRE>. This means that the
+ // spacings are wrong, but otherwise we get an empty paragraph
+ // after the <HR> which is even uglier.
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+
+ // Output all the nodes that are anchored to a frame
+ rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any );
+
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine(); // paragraph tag on a new line
+
+ rWrt.SetLFPossible(true);
+
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ aHtml.prettyPrint(rWrt.IsPrettyPrint());
+ aHtml.start(OOO_STRING_SVTOOLS_HTML_horzrule ""_ostr);
+
+ const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
+ if( !pItemSet )
+ {
+ aHtml.end();
+ return rWrt;
+ }
+ if (pItemSet->GetItemIfSet(RES_MARGIN_FIRSTLINE, false)
+ || pItemSet->GetItemIfSet(RES_MARGIN_TEXTLEFT, false)
+ || pItemSet->GetItemIfSet(RES_MARGIN_RIGHT, false))
+ {
+ SvxFirstLineIndentItem const& rFirstLine(pItemSet->Get(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem const& rTextLeftMargin(pItemSet->Get(RES_MARGIN_TEXTLEFT));
+ SvxRightMarginItem const& rRightMargin(pItemSet->Get(RES_MARGIN_RIGHT));
+ sal_Int32 const nLeft(rTextLeftMargin.GetLeft(rFirstLine));
+ sal_Int32 const nRight(rRightMargin.GetRight());
+ if( nLeft || nRight )
+ {
+ const SwFrameFormat& rPgFormat =
+ rWrt.m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool
+ ( RES_POOLPAGE_HTML, false )->GetMaster();
+ const SwFormatFrameSize& rSz = rPgFormat.GetFrameSize();
+ const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace();
+ const SwFormatCol& rCol = rPgFormat.GetCol();
+
+ tools::Long nPageWidth = rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight();
+
+ if( 1 < rCol.GetNumCols() )
+ nPageWidth /= rCol.GetNumCols();
+
+ const SwTableNode* pTableNd = pNd->FindTableNode();
+ if( pTableNd )
+ {
+ const SwTableBox* pBox = pTableNd->GetTable().GetTableBox(
+ pNd->StartOfSectionIndex() );
+ if( pBox )
+ nPageWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+
+ OString sWidth = OString::number(SwHTMLWriter::ToPixel(nPageWidth - nLeft - nRight));
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
+
+ if( !nLeft )
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_left);
+ else if( !nRight )
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_right);
+ else
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_center);
+ }
+ }
+
+ if( const SvxBoxItem* pBoxItem = pItemSet->GetItemIfSet( RES_BOX, false ))
+ {
+ const editeng::SvxBorderLine* pBorderLine = pBoxItem->GetBottom();
+ if( pBorderLine )
+ {
+ sal_uInt16 nWidth = pBorderLine->GetScaledWidth();
+ OString sWidth = OString::number(SwHTMLWriter::ToPixel(nWidth));
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_size, sWidth);
+
+ const Color& rBorderColor = pBorderLine->GetColor();
+ if( !rBorderColor.IsRGBEqual( COL_GRAY ) )
+ {
+ HtmlWriterHelper::applyColor(aHtml, OOO_STRING_SVTOOLS_HTML_O_color, rBorderColor);
+ }
+
+ if( !pBorderLine->GetInWidth() )
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_noshade, OOO_STRING_SVTOOLS_HTML_O_noshade);
+ }
+ }
+ }
+ aHtml.end();
+ return rWrt;
+ }
+
+ // Do not export the empty nodes with 2pt fonts and standard style that
+ // are inserted before tables and sections, but do export bookmarks
+ // and paragraph anchored frames.
+ if( !nEnd && (nPoolId == RES_POOLCOLL_STANDARD ||
+ nPoolId == RES_POOLCOLL_TABLE ||
+ nPoolId == RES_POOLCOLL_TABLE_HDLN) )
+ {
+ // The current node is empty and contains the standard style ...
+ const SvxFontHeightItem* pFontHeightItem;
+ const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
+ if( pItemSet && pItemSet->Count() &&
+ (pFontHeightItem = pItemSet->GetItemIfSet( RES_CHRATR_FONTSIZE, false )) &&
+ 40 == pFontHeightItem->GetHeight() )
+ {
+ // ... moreover, the 2pt font is set ...
+ SwNodeOffset nNdPos = rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex();
+ const SwNode *pNextNd = rWrt.m_pDoc->GetNodes()[nNdPos+1];
+ const SwNode *pPrevNd = rWrt.m_pDoc->GetNodes()[nNdPos-1];
+ bool bStdColl = nPoolId == RES_POOLCOLL_STANDARD;
+ if( ( bStdColl && (pNextNd->IsTableNode() || pNextNd->IsSectionNode()) ) ||
+ ( !bStdColl &&
+ pNextNd->IsEndNode() &&
+ pPrevNd->IsStartNode() &&
+ SwTableBoxStartNode == pPrevNd->GetStartNode()->GetStartNodeType() ) )
+ {
+ // ... and it is located before a table or a section
+ rWrt.OutBookmarks();
+ rWrt.SetLFPossible(rWrt.m_nLastParaToken == HtmlTokenId::NONE);
+
+ // Output all frames that are anchored to this node
+ rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any );
+ rWrt.SetLFPossible(false);
+
+ return rWrt;
+ }
+ }
+ }
+
+ // catch PageBreaks and PageDescs
+ bool bPageBreakBehind = false;
+ if( rWrt.m_bCfgFormFeed &&
+ !(rWrt.m_bOutTable || rWrt.m_bOutFlyFrame) &&
+ rWrt.m_pStartNdIdx->GetIndex() != rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() )
+ {
+ bool bPageBreakBefore = false;
+ const SfxItemSet* pItemSet = pNd->GetpSwAttrSet();
+
+ if( pItemSet )
+ {
+ const SwFormatPageDesc* pPageDescItem = pItemSet->GetItemIfSet( RES_PAGEDESC );
+ if( pPageDescItem && pPageDescItem->GetPageDesc() )
+ {
+ bPageBreakBefore = true;
+ }
+ else if( const SvxFormatBreakItem* pItem = pItemSet->GetItemIfSet( RES_BREAK ) )
+ {
+ switch( pItem->GetBreak() )
+ {
+ case SvxBreak::PageBefore:
+ bPageBreakBefore = true;
+ break;
+ case SvxBreak::PageAfter:
+ bPageBreakBehind = true;
+ break;
+ case SvxBreak::PageBoth:
+ bPageBreakBefore = true;
+ bPageBreakBehind = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if( bPageBreakBefore )
+ rWrt.Strm().WriteChar( '\f' );
+ }
+
+ // if necessary, open a form
+ rWrt.OutForm();
+
+ // Output the page-anchored frames that are 'anchored' to this node
+ bool bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Prefix );
+
+ // Output all frames that are anchored to this node that are supposed to
+ // be written before the paragraph tag.
+ if( bFlysLeft )
+ {
+ bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Before );
+ }
+
+ if( rWrt.m_pCurrentPam->GetPoint()->GetNode() == rWrt.m_pCurrentPam->GetMark()->GetNode() )
+ {
+ nEnd = rWrt.m_pCurrentPam->GetMark()->GetContentIndex();
+ }
+
+ // are there any hard attributes that must be written as options?
+ rWrt.m_bTagOn = true;
+
+ // now, output the tag of the paragraph
+ const SwFormat& rFormat = pNd->GetAnyFormatColl();
+ SwHTMLTextCollOutputInfo aFormatInfo;
+ bool bOldLFPossible = rWrt.IsLFPossible();
+ bool bOldSpacePreserve = rWrt.IsSpacePreserve();
+ if (rWrt.IsPreserveSpacesOnWritePrefSet())
+ rWrt.SetSpacePreserve(NeedPreserveWhitespace(rStr, rWrt.mbReqIF));
+ OutHTML_SwFormat( rWrt, rFormat, pNd->GetpSwAttrSet(), aFormatInfo );
+
+ // If we didn't open a new line before the paragraph tag, we do that now
+ rWrt.SetLFPossible(rWrt.m_nLastParaToken == HtmlTokenId::NONE);
+ if (!bOldLFPossible && rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+
+ // then, the bookmarks (including end tag)
+ rWrt.m_bOutOpts = false;
+ rWrt.OutBookmarks();
+
+ // now it's a good opportunity again for an LF - if it is still allowed
+ // FIXME: for LOK case we set rWrt.m_nWishLineLen as -1, for now keep old flow
+ // when LOK side will be fixed - don't insert new line at the beginning
+ if( rWrt.IsLFPossible() && rWrt.IsPrettyPrint() && rWrt.m_nWishLineLen >= 0 &&
+ rWrt.GetLineLen() >= rWrt.m_nWishLineLen )
+ {
+ rWrt.OutNewLine();
+ }
+ rWrt.SetLFPossible(false);
+
+ // find text that originates from an outline numbering
+ sal_Int32 nOffset = 0;
+ OUString aOutlineText;
+ OUString aFullText;
+
+ // export numbering string as plain text only for the outline numbering,
+ // because the outline numbering isn't exported as a numbering - see <SwHTMLNumRuleInfo::Set(..)>
+ if ( pNd->IsOutline() &&
+ pNd->GetNumRule() == pNd->GetDoc().GetOutlineNumRule() )
+ {
+ aOutlineText = pNd->GetNumString();
+ nOffset = nOffset + aOutlineText.getLength();
+ aFullText = aOutlineText;
+ }
+ OUString aFootEndNoteSym;
+ if( rWrt.m_pFormatFootnote )
+ {
+ aFootEndNoteSym = rWrt.GetFootEndNoteSym( *rWrt.m_pFormatFootnote );
+ nOffset = nOffset + aFootEndNoteSym.getLength();
+ aFullText += aFootEndNoteSym;
+ }
+
+ // Table of Contents or other paragraph with dot leaders?
+ sal_Int32 nIndexTab = rWrt.indexOfDotLeaders( nPoolId, rStr );
+ if (nIndexTab > -1)
+ // skip part after the tabulator (page number)
+ nEnd = nIndexTab;
+
+ // are there any hard attributes that must be written as tags?
+ aFullText += rStr;
+ HTMLEndPosLst aEndPosLst( rWrt.m_pDoc, rWrt.m_xTemplate.get(),
+ rWrt.m_xDfltColor, rWrt.m_bCfgOutStyles,
+ rWrt.GetHTMLMode(), aFullText,
+ rWrt.m_aScriptTextStyles );
+ if( aFormatInfo.moItemSet )
+ {
+ aEndPosLst.Insert( *aFormatInfo.moItemSet, 0, nEnd + nOffset,
+ rWrt.m_CharFormatInfos, false, true );
+ }
+
+ if( !aOutlineText.isEmpty() || rWrt.m_pFormatFootnote )
+ {
+ // output paragraph attributes, so that the text gets the attributes of
+ // the paragraph.
+ aEndPosLst.OutStartAttrs( rWrt, 0 );
+
+ // Theoretically, we would have to consider the character style of
+ // the numbering. Because it cannot be set via the UI, let's ignore
+ // it for now.
+
+ if( !aOutlineText.isEmpty() )
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aOutlineText );
+
+ if( rWrt.m_pFormatFootnote )
+ {
+ rWrt.OutFootEndNoteSym( *rWrt.m_pFormatFootnote, aFootEndNoteSym,
+ aEndPosLst.GetScriptAtPos( aOutlineText.getLength(), rWrt.m_nCSS1Script ) );
+ rWrt.m_pFormatFootnote = nullptr;
+ }
+ }
+
+ // for now, correct the start. I.e., if we only output part of the sentence,
+ // the attributes must be correct there, as well!!
+ rWrt.m_bTextAttr = true;
+
+ size_t nAttrPos = 0;
+ sal_Int32 nStrPos = rWrt.m_pCurrentPam->GetPoint()->GetContentIndex();
+ const SwTextAttr * pHt = nullptr;
+ const size_t nCntAttr = pNd->HasHints() ? pNd->GetSwpHints().Count() : 0;
+ if( nCntAttr && nStrPos > ( pHt = pNd->GetSwpHints().Get(0) )->GetStart() )
+ {
+ // Ok, there are earlier attributes that we must output
+ do {
+ aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
+
+ nAttrPos++;
+ if( pHt->Which() == RES_TXTATR_FIELD
+ || pHt->Which() == RES_TXTATR_ANNOTATION )
+ continue;
+
+ if ( pHt->End() && !pHt->HasDummyChar() )
+ {
+ const sal_Int32 nHtEnd = *pHt->End(),
+ nHtStt = pHt->GetStart();
+ if( !rWrt.m_bWriteAll && nHtEnd <= nStrPos )
+ continue;
+
+ // don't consider empty hints at the beginning - or should we ??
+ if( nHtEnd == nHtStt )
+ continue;
+
+ // add attribute to the list
+ if( rWrt.m_bWriteAll )
+ aEndPosLst.Insert( pHt->GetAttr(), nHtStt + nOffset,
+ nHtEnd + nOffset,
+ rWrt.m_CharFormatInfos );
+ else
+ {
+ sal_Int32 nTmpStt = nHtStt < nStrPos ? nStrPos : nHtStt;
+ sal_Int32 nTmpEnd = std::min(nHtEnd, nEnd);
+ aEndPosLst.Insert( pHt->GetAttr(), nTmpStt + nOffset,
+ nTmpEnd + nOffset,
+ rWrt.m_CharFormatInfos );
+ }
+ continue;
+ // but don't output it, that will be done later !!
+ }
+
+ } while( nAttrPos < nCntAttr && nStrPos >
+ ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() );
+
+ // so, let's output all collected attributes from the string pos on
+ aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
+ aEndPosLst.OutStartAttrs( rWrt, nStrPos + nOffset );
+ }
+
+ bool bWriteBreak = (HtmlTokenId::PREFORMTXT_ON != rWrt.m_nLastParaToken);
+ if (bWriteBreak && (pNd->GetNumRule() || rWrt.mbReqIF))
+ {
+ // One line-break is exactly one <br> in the ReqIF case.
+ bWriteBreak = false;
+ }
+
+ {
+ // Tabs are leading till there is a non-tab since the start of the paragraph.
+ bool bLeadingTab = true;
+ for( ; nStrPos < nEnd; nStrPos++ )
+ {
+ // output the frames that are anchored to the current position
+ if( bFlysLeft )
+ {
+ aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
+ bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(),
+ nStrPos, HtmlPosition::Inside );
+ }
+
+ bool bOutChar = true;
+ const SwTextAttr * pTextHt = nullptr;
+ if (nAttrPos < nCntAttr && pHt->GetStart() == nStrPos)
+ {
+ do {
+ if ( pHt->End() && !pHt->HasDummyChar() )
+ {
+ if( *pHt->End() != nStrPos )
+ {
+ // insert hints with end, if they don't start
+ // an empty range (hints that don't start a range
+ // are ignored)
+ aEndPosLst.Insert( pHt->GetAttr(), nStrPos + nOffset,
+ *pHt->End() + nOffset,
+ rWrt.m_CharFormatInfos );
+ }
+ }
+ else
+ {
+ // hints without an end are output last
+ OSL_ENSURE( !pTextHt, "Why is there already an attribute without an end?" );
+ if( rWrt.m_nTextAttrsToIgnore>0 )
+ {
+ rWrt.m_nTextAttrsToIgnore--;
+ }
+ else
+ {
+ pTextHt = pHt;
+ SwFieldIds nFieldWhich;
+ if( RES_TXTATR_FIELD != pHt->Which()
+ || ( SwFieldIds::Postit != (nFieldWhich = static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->Which())
+ && SwFieldIds::Script != nFieldWhich ) )
+ {
+ bWriteBreak = false;
+ }
+ }
+ bOutChar = false; // don't output 255
+ }
+ } while( ++nAttrPos < nCntAttr && nStrPos ==
+ ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() );
+ }
+
+ // Additionally, some draw formats can bring attributes
+ if( pTextHt && RES_TXTATR_FLYCNT == pTextHt->Which() )
+ {
+ const SwFrameFormat* pFrameFormat =
+ pTextHt->GetAttr().StaticWhichCast(RES_TXTATR_FLYCNT).GetFrameFormat();
+
+ if( RES_DRAWFRMFMT == pFrameFormat->Which() )
+ aEndPosLst.Insert( *static_cast<const SwDrawFrameFormat *>(pFrameFormat),
+ nStrPos + nOffset,
+ rWrt.m_CharFormatInfos );
+ }
+
+ aEndPosLst.OutEndAttrs( rWrt, nStrPos + nOffset );
+ aEndPosLst.OutStartAttrs( rWrt, nStrPos + nOffset );
+
+ if( pTextHt )
+ {
+ rWrt.SetLFPossible(rWrt.m_nLastParaToken == HtmlTokenId::NONE &&
+ nStrPos > 0 &&
+ rStr[nStrPos-1] == ' ');
+ sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
+ rWrt.m_nCSS1Script = aEndPosLst.GetScriptAtPos(
+ nStrPos + nOffset, nCSS1Script );
+ HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
+ Out( aHTMLAttrFnTab, pTextHt->GetAttr(), rWrt );
+ rWrt.m_nCSS1Script = nCSS1Script;
+ rWrt.SetLFPossible(false);
+ }
+
+ if( bOutChar )
+ {
+ sal_uInt32 c = rStr[nStrPos];
+ if( rtl::isHighSurrogate(c) && nStrPos < nEnd - 1 )
+ {
+ const sal_Unicode d = rStr[nStrPos + 1];
+ if( rtl::isLowSurrogate(d) )
+ {
+ c = rtl::combineSurrogates(c, d);
+ nStrPos++;
+ }
+ }
+
+ // try to split a line after about 255 characters
+ // at a space character unless in a PRE-context
+ if( ' ' == c && rWrt.m_nLastParaToken == HtmlTokenId::NONE && !rWrt.IsSpacePreserve() )
+ {
+ sal_Int32 nLineLen;
+ nLineLen = rWrt.GetLineLen();
+
+ sal_Int32 nWordLen = rStr.indexOf( ' ', nStrPos+1 );
+ if( nWordLen == -1 )
+ nWordLen = nEnd;
+ nWordLen -= nStrPos;
+
+ if( rWrt.IsPrettyPrint() && rWrt.m_nWishLineLen >= 0 &&
+ (nLineLen >= rWrt.m_nWishLineLen ||
+ (nLineLen+nWordLen) >= rWrt.m_nWishLineLen ) )
+ {
+ HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
+ rWrt.OutNewLine();
+ bOutChar = false;
+ }
+ }
+
+ if( bOutChar )
+ {
+ if( 0x0a == c )
+ {
+ HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ aHtml.prettyPrint(rWrt.IsPrettyPrint());
+ aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
+ }
+ else if (c == CH_TXT_ATR_FORMELEMENT)
+ {
+ // Placeholder for a single-point fieldmark.
+
+ SwPosition aMarkPos = *rWrt.m_pCurrentPam->GetPoint();
+ aMarkPos.AdjustContent( nStrPos - aMarkPos.GetContentIndex() );
+ rWrt.OutPointFieldmarks(aMarkPos);
+ }
+ else
+ {
+ bool bConsumed = false;
+ if (c == '\t')
+ {
+ if (bLeadingTab && rWrt.m_nLeadingTabWidth.has_value())
+ {
+ // Consume a tab if it's leading and we know the number of NBSPs to
+ // be used as a replacement.
+ for (sal_Int32 i = 0; i < *rWrt.m_nLeadingTabWidth; ++i)
+ {
+ rWrt.Strm().WriteOString("&#160;");
+ }
+ bConsumed = true;
+ }
+ }
+ else
+ {
+ // Not a tab -> later tabs are no longer leading.
+ bLeadingTab = false;
+ }
+
+ if (!bConsumed)
+ {
+ HTMLOutFuncs::Out_Char(rWrt.Strm(), c);
+ }
+ }
+
+ if (!rWrt.mbReqIF)
+ {
+ // if a paragraph's last character is a hard line break
+ // then we need to add an extra <br>
+ // because browsers like Mozilla wouldn't add a line for the next paragraph
+ bWriteBreak = (0x0a == c) &&
+ (HtmlTokenId::PREFORMTXT_ON != rWrt.m_nLastParaToken);
+ }
+ }
+ }
+ }
+ HTMLOutFuncs::FlushToAscii( rWrt.Strm() );
+ }
+
+ aEndPosLst.OutEndAttrs( rWrt, SAL_MAX_INT32 );
+
+ // Output the frames that are anchored to the last position
+ if( bFlysLeft )
+ bFlysLeft = rWrt.OutFlyFrame( rNode.GetIndex(),
+ nEnd, HtmlPosition::Inside );
+ OSL_ENSURE( !bFlysLeft, "Not all frames were saved!" );
+
+ rWrt.m_bTextAttr = false;
+
+ if( bWriteBreak )
+ {
+ bool bEndOfCell = rWrt.m_bOutTable &&
+ rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() ==
+ rWrt.m_pCurrentPam->GetMark()->GetNodeIndex();
+
+ if( bEndOfCell && !nEnd &&
+ rWrt.IsHTMLMode(HTMLMODE_NBSP_IN_TABLES) )
+ {
+ // If the last paragraph of a table cell is empty and we export
+ // for the MS-IE, we write a &nbsp; instead of a <BR>
+ rWrt.Strm().WriteChar( '&' ).WriteOString( OOO_STRING_SVTOOLS_HTML_S_nbsp ).WriteChar( ';' );
+ }
+ else
+ {
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ aHtml.prettyPrint(rWrt.IsPrettyPrint());
+ aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
+ const SvxULSpaceItem& rULSpace = pNd->GetSwAttrSet().Get(RES_UL_SPACE);
+ if (rULSpace.GetLower() > 0 && !bEndOfCell)
+ {
+ aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
+ }
+ rWrt.SetLFPossible(true);
+ }
+ }
+
+ if( rWrt.m_bClearLeft || rWrt.m_bClearRight )
+ {
+ const char* pString;
+ if( rWrt.m_bClearLeft )
+ {
+ if( rWrt.m_bClearRight )
+ pString = OOO_STRING_SVTOOLS_HTML_AL_all;
+ else
+ pString = OOO_STRING_SVTOOLS_HTML_AL_left;
+ }
+ else
+ {
+ pString = OOO_STRING_SVTOOLS_HTML_AL_right;
+ }
+
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ aHtml.prettyPrint(rWrt.IsPrettyPrint());
+ aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pString);
+ aHtml.end();
+
+ rWrt.m_bClearLeft = false;
+ rWrt.m_bClearRight = false;
+
+ rWrt.SetLFPossible(true);
+ }
+
+ // if an LF is not allowed already, it is allowed once the paragraphs
+ // ends with a ' '
+ if (!rWrt.IsLFPossible() &&
+ rWrt.m_nLastParaToken == HtmlTokenId::NONE &&
+ nEnd > 0 && ' ' == rStr[nEnd-1] )
+ rWrt.SetLFPossible(true);
+
+ // dot leaders: print the skipped page number in a different span element
+ if (nIndexTab > -1) {
+ OString sOut = OUStringToOString(rStr.subView(nIndexTab + 1), RTL_TEXTENCODING_ASCII_US);
+ rWrt.Strm().WriteOString( Concat2View("</span><span>" + sOut + "</span>") );
+ }
+
+ rWrt.m_bTagOn = false;
+ OutHTML_SwFormatOff( rWrt, aFormatInfo );
+ rWrt.SetSpacePreserve(bOldSpacePreserve);
+
+ // if necessary, close a form
+ rWrt.OutForm( false );
+
+ if( bPageBreakBehind )
+ rWrt.Strm().WriteChar( '\f' );
+
+ return rWrt;
+}
+
+// In CSS, "px" is 1/96 of inch: https://www.w3.org/TR/css3-values/#absolute-lengths
+sal_uInt32 SwHTMLWriter::ToPixel(sal_uInt32 nTwips)
+{
+ // if there is a Twip, there should be a pixel as well
+ return nTwips
+ ? std::max(o3tl::convert(nTwips, o3tl::Length::twip, o3tl::Length::px), sal_Int64(1))
+ : 0;
+}
+
+Size SwHTMLWriter::ToPixel(Size aTwips)
+{
+ return Size(ToPixel(aTwips.Width()), ToPixel(aTwips.Height()));
+}
+
+static SwHTMLWriter& OutHTML_CSS1Attr( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ // if hints are currently written, we try to write the hint as an
+ // CSS1 attribute
+
+ if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ OutCSS1_HintSpanTag( rWrt, rHt );
+
+ return rWrt;
+}
+
+/* File CHRATR.HXX: */
+
+static SwHTMLWriter& OutHTML_SvxColor( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ if( !rWrt.m_bTextAttr && rWrt.m_bCfgOutStyles && rWrt.m_bCfgPreferStyles )
+ {
+ // don't write the font color as a tag, if styles are preferred to
+ // normal tags
+ return rWrt;
+ }
+
+ if( rWrt.m_bTagOn )
+ {
+ Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() );
+ if( COL_AUTO == aColor )
+ aColor = COL_BLACK;
+
+ if (rWrt.mbXHTML)
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
+ " " OOO_STRING_SVTOOLS_HTML_O_style "=";
+ rWrt.Strm().WriteOString(sOut);
+ HTMLOutFuncs::Out_Color(rWrt.Strm(), aColor, /*bXHTML=*/true).WriteChar('>');
+ }
+ else
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " "
+ OOO_STRING_SVTOOLS_HTML_O_color "=";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_Color( rWrt.Strm(), aColor ).WriteChar( '>' );
+ }
+ }
+ else
+ {
+ if (rWrt.mbXHTML)
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
+ else
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SwPosture( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ const FontItalic nPosture = static_cast<const SvxPostureItem&>(rHt).GetPosture();
+ if( ITALIC_NORMAL == nPosture )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_italic), rWrt.m_bTagOn );
+ }
+ else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // maybe as CSS1 attribute?
+ OutCSS1_HintSpanTag( rWrt, rHt );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SvxFont( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ if (IgnorePropertyForReqIF(rWrt.mbReqIF, "font-family", ""))
+ {
+ return rWrt;
+ }
+
+ if( rWrt.m_bTagOn )
+ {
+ OUString aNames;
+ SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), aNames, 0,
+ rWrt.IsHTMLMode(HTMLMODE_FONT_GENERIC) );
+ if (rWrt.mbXHTML)
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
+ " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-family: ";
+ rWrt.Strm().WriteOString(sOut);
+ HTMLOutFuncs::Out_String(rWrt.Strm(), aNames)
+ .WriteOString("\">");
+ }
+ else
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " "
+ OOO_STRING_SVTOOLS_HTML_O_face "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aNames )
+ .WriteOString( "\">" );
+ }
+ }
+ else
+ {
+ if (rWrt.mbXHTML)
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
+ else
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SvxFontHeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ if (IgnorePropertyForReqIF(rWrt.mbReqIF, "font-size", ""))
+ {
+ return rWrt;
+ }
+
+ if( rWrt.m_bTagOn )
+ {
+ if (rWrt.mbXHTML)
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span;
+
+ sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
+ // Twips -> points.
+ sal_uInt16 nSize = nHeight / 20;
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-size: "
+ + OString::number(static_cast<sal_Int32>(nSize)) + "pt\"";
+ rWrt.Strm().WriteOString(sOut);
+ }
+ else
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font;
+
+ sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
+ sal_uInt16 nSize = rWrt.GetHTMLFontSize( nHeight );
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" +
+ OString::number(static_cast<sal_Int32>(nSize)) + "\"";
+ rWrt.Strm().WriteOString( sOut );
+
+ if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // always export font size as CSS option, too
+ OutCSS1_HintStyleOpt( rWrt, rHt );
+ }
+ }
+ rWrt.Strm().WriteChar( '>' );
+ }
+ else
+ {
+ if (rWrt.mbXHTML)
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
+ else
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font), false );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SvxLanguage( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage();
+ if( LANGUAGE_DONTKNOW == eLang )
+ return rWrt;
+
+ if( rWrt.m_bTagOn )
+ {
+ OString sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span;
+ rWrt.Strm().WriteOString( sOut );
+ rWrt.OutLanguage( static_cast<const SvxLanguageItem &>(rHt).GetLanguage() );
+ rWrt.Strm().WriteChar( '>' );
+ }
+ else
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
+ }
+
+ return rWrt;
+}
+static SwHTMLWriter& OutHTML_SwWeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ const FontWeight nBold = static_cast<const SvxWeightItem&>(rHt).GetWeight();
+ if( WEIGHT_BOLD == nBold )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_bold), rWrt.m_bTagOn );
+ }
+ else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // maybe as CSS1 attribute ?
+ OutCSS1_HintSpanTag( rWrt, rHt );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SwCrossedOut( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ // Because of Netscape, we output STRIKE and not S!
+ const FontStrikeout nStrike = static_cast<const SvxCrossedOutItem&>(rHt).GetStrikeout();
+ if( STRIKEOUT_NONE != nStrike && !rWrt.mbReqIF )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_strike), rWrt.m_bTagOn );
+ }
+ else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // maybe as CSS1 attribute?
+ OutCSS1_HintSpanTag( rWrt, rHt );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SvxEscapement( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ const SvxEscapement eEscape =
+ static_cast<SvxEscapement>(static_cast<const SvxEscapementItem&>(rHt).GetEnumValue());
+ OString aTag;
+ switch( eEscape )
+ {
+ case SvxEscapement::Superscript: aTag = OOO_STRING_SVTOOLS_HTML_superscript ""_ostr; break;
+ case SvxEscapement::Subscript: aTag = OOO_STRING_SVTOOLS_HTML_subscript ""_ostr; break;
+ default:
+ ;
+ }
+
+ if( !aTag.isEmpty() )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), rWrt.m_bTagOn );
+ }
+ else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // maybe as CSS1 attribute?
+ OutCSS1_HintSpanTag( rWrt, rHt );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SwUnderline( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ const FontLineStyle eUnder = static_cast<const SvxUnderlineItem&>(rHt).GetLineStyle();
+ if( LINESTYLE_NONE != eUnder && !rWrt.mbReqIF )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_underline), rWrt.m_bTagOn );
+ }
+ else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // maybe as CSS1 attribute?
+ OutCSS1_HintSpanTag( rWrt, rHt );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SwFlyCnt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ const SwFormatFlyCnt& rFlyCnt = static_cast<const SwFormatFlyCnt&>(rHt);
+
+ const SwFrameFormat& rFormat = *rFlyCnt.GetFrameFormat();
+ const SdrObject *pSdrObj = nullptr;
+
+ SwHTMLFrameType eType = rWrt.GuessFrameType( rFormat, pSdrObj );
+ AllHtmlFlags nMode = getHTMLOutFrameAsCharTable(eType, rWrt.m_nExportMode);
+ rWrt.OutFrameFormat( nMode, rFormat, pSdrObj );
+ return rWrt;
+}
+
+// This is now our Blink item. Blinking is activated by setting the item to
+// true!
+static SwHTMLWriter& OutHTML_SwBlink( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ if( static_cast<const SvxBlinkItem&>(rHt).GetValue() )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_blink), rWrt.m_bTagOn );
+ }
+ else if( rWrt.m_bCfgOutStyles && rWrt.m_bTextAttr )
+ {
+ // maybe as CSS1 attribute?
+ OutCSS1_HintSpanTag( rWrt, rHt );
+ }
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_INetFormat( SwHTMLWriter& rWrt, const SwFormatINetFormat& rINetFormat, bool bOn )
+{
+ OUString aURL( rINetFormat.GetValue() );
+ const SvxMacroTableDtor *pMacTable = rINetFormat.GetMacroTable();
+ bool bEvents = pMacTable != nullptr && !pMacTable->empty();
+
+ // Anything to output at all?
+ if( aURL.isEmpty() && !bEvents && rINetFormat.GetName().isEmpty() )
+ return rWrt;
+
+ // bOn controls if we are writing the opening or closing tag
+ if( !bOn )
+ {
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
+ return rWrt;
+ }
+
+ OString sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor);
+
+ bool bScriptDependent = false;
+ {
+ const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_INET_NORMAL );
+ std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat));
+ auto const it = rWrt.m_CharFormatInfos.find( pFormatInfo );
+ if (it != rWrt.m_CharFormatInfos.end())
+ {
+ bScriptDependent = (*it)->bScriptDependent;
+ }
+ }
+ if( !bScriptDependent )
+ {
+ const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_INET_VISIT );
+ std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat));
+ auto const it = rWrt.m_CharFormatInfos.find( pFormatInfo );
+ if (it != rWrt.m_CharFormatInfos.end())
+ {
+ bScriptDependent = (*it)->bScriptDependent;
+ }
+ }
+
+ if( bScriptDependent )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
+ const char* pStr = nullptr;
+ switch( rWrt.m_nCSS1Script )
+ {
+ case CSS1_OUTMODE_WESTERN:
+ pStr = "western";
+ break;
+ case CSS1_OUTMODE_CJK:
+ pStr = "cjk";
+ break;
+ case CSS1_OUTMODE_CTL:
+ pStr = "ctl";
+ break;
+ }
+ sOut += pStr + OString::Concat("\"");
+ }
+
+ rWrt.Strm().WriteOString( sOut );
+ sOut = ""_ostr;
+
+ OUString sRel;
+
+ if( !aURL.isEmpty() || bEvents )
+ {
+ OUString sTmp( aURL.toAsciiUpperCase() );
+ sal_Int32 nPos = sTmp.indexOf( "\" REL=" );
+ if( nPos >= 0 )
+ {
+ sRel = aURL.copy( nPos+1 );
+ aURL = aURL.copy( 0, nPos);
+ }
+ aURL = comphelper::string::strip(aURL, ' ');
+
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_href "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ rWrt.OutHyperlinkHRefValue( aURL );
+ sOut = "\""_ostr;
+ }
+
+ if( !rINetFormat.GetName().isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rINetFormat.GetName() );
+ sOut = "\""_ostr;
+ }
+
+ const OUString& rTarget = rINetFormat.GetTargetFrame();
+ if( !rTarget.isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_target "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rTarget );
+ sOut = "\""_ostr;
+ }
+
+ if( !sRel.isEmpty() )
+ sOut += OUStringToOString(sRel, RTL_TEXTENCODING_ASCII_US);
+
+ if( !sOut.isEmpty() )
+ rWrt.Strm().WriteOString( sOut );
+
+ if( bEvents )
+ HTMLOutFuncs::Out_Events( rWrt.Strm(), *pMacTable, aAnchorEventTable,
+ rWrt.m_bCfgStarBasic );
+ rWrt.Strm().WriteOString( ">" );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SwFormatINetFormat( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ const SwFormatINetFormat& rINetFormat = static_cast<const SwFormatINetFormat&>(rHt);
+
+ if( rWrt.m_bTagOn )
+ {
+ // if necessary, temporarily close an attribute that is still open
+ if( !rWrt.m_aINetFormats.empty() )
+ {
+ SwFormatINetFormat *pINetFormat =
+ rWrt.m_aINetFormats.back();
+ OutHTML_INetFormat( rWrt, *pINetFormat, false );
+ }
+
+ // now, open the new one
+ OutHTML_INetFormat( rWrt, rINetFormat, true );
+
+ // and remember it
+ SwFormatINetFormat *pINetFormat = new SwFormatINetFormat( rINetFormat );
+ rWrt.m_aINetFormats.push_back( pINetFormat );
+ }
+ else
+ {
+ OutHTML_INetFormat( rWrt, rINetFormat, false );
+
+ OSL_ENSURE( rWrt.m_aINetFormats.size(), "there must be a URL attribute missing" );
+ if( !rWrt.m_aINetFormats.empty() )
+ {
+ // get its own attribute from the stack
+ SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
+ rWrt.m_aINetFormats.pop_back();
+ delete pINetFormat;
+ }
+
+ if( !rWrt.m_aINetFormats.empty() )
+ {
+ // there is still an attribute on the stack that must be reopened
+ SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
+ OutHTML_INetFormat( rWrt, *pINetFormat, true );
+ }
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SwTextCharFormat( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( rWrt.m_bOutOpts )
+ return rWrt;
+
+ const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rHt);
+ const SwCharFormat* pFormat = rChrFormat.GetCharFormat();
+
+ if( !pFormat )
+ {
+ return rWrt;
+ }
+
+ std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(pFormat));
+ SwHTMLFormatInfos::const_iterator it = rWrt.m_CharFormatInfos.find(pTmpInfo);
+ if (it == rWrt.m_CharFormatInfos.end())
+ return rWrt;
+
+ const SwHTMLFormatInfo *pFormatInfo = it->get();
+ OSL_ENSURE( pFormatInfo, "Why is there no information about the character style?" );
+
+ if( rWrt.m_bTagOn )
+ {
+ OString sOut = "<" + rWrt.GetNamespace();
+ if( !pFormatInfo->aToken.isEmpty() )
+ sOut += pFormatInfo->aToken;
+ else
+ sOut += OOO_STRING_SVTOOLS_HTML_span;
+
+ if( rWrt.m_bCfgOutStyles &&
+ (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ OUString aClass( pFormatInfo->aClass );
+ if( pFormatInfo->bScriptDependent )
+ {
+ if( !aClass.isEmpty() )
+ aClass += "-";
+ switch( rWrt.m_nCSS1Script )
+ {
+ case CSS1_OUTMODE_WESTERN:
+ aClass += "western";
+ break;
+ case CSS1_OUTMODE_CJK:
+ aClass += "cjk";
+ break;
+ case CSS1_OUTMODE_CTL:
+ aClass += "ctl";
+ break;
+ }
+ }
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aClass );
+ sOut = "\""_ostr;
+ }
+ sOut += ">";
+ rWrt.Strm().WriteOString( sOut );
+ }
+ else
+ {
+ OString aTag = !pFormatInfo->aToken.isEmpty() ? pFormatInfo->aToken.getStr()
+ : OOO_STRING_SVTOOLS_HTML_span;
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_SvxAdjust( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ if( !rWrt.m_bOutOpts || !rWrt.m_bTagOn )
+ return rWrt;
+
+ const SvxAdjustItem& rAdjust = static_cast<const SvxAdjustItem&>(rHt);
+ const char* pStr = nullptr;
+ switch( rAdjust.GetAdjust() )
+ {
+ case SvxAdjust::Center: pStr = OOO_STRING_SVTOOLS_HTML_AL_center; break;
+ case SvxAdjust::Left: pStr = OOO_STRING_SVTOOLS_HTML_AL_left; break;
+ case SvxAdjust::Right: pStr = OOO_STRING_SVTOOLS_HTML_AL_right; break;
+ case SvxAdjust::Block: pStr = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
+ default:
+ ;
+ }
+ if( pStr )
+ {
+ OString sOut = OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
+ pStr + "\"";
+ rWrt.Strm().WriteOString( sOut );
+ }
+
+ return rWrt;
+}
+
+/*
+ * here, define the table for the HTML function pointers to the output
+ * functions.
+ */
+
+SwAttrFnTab aHTMLAttrFnTab = {
+/* RES_CHRATR_CASEMAP */ OutHTML_CSS1Attr,
+/* RES_CHRATR_CHARSETCOLOR */ nullptr,
+/* RES_CHRATR_COLOR */ OutHTML_SvxColor,
+/* RES_CHRATR_CONTOUR */ nullptr,
+/* RES_CHRATR_CROSSEDOUT */ OutHTML_SwCrossedOut,
+/* RES_CHRATR_ESCAPEMENT */ OutHTML_SvxEscapement,
+/* RES_CHRATR_FONT */ OutHTML_SvxFont,
+/* RES_CHRATR_FONTSIZE */ OutHTML_SvxFontHeight,
+/* RES_CHRATR_KERNING */ OutHTML_CSS1Attr,
+/* RES_CHRATR_LANGUAGE */ OutHTML_SvxLanguage,
+/* RES_CHRATR_POSTURE */ OutHTML_SwPosture,
+/* RES_CHRATR_UNUSED1*/ nullptr,
+/* RES_CHRATR_SHADOWED */ nullptr,
+/* RES_CHRATR_UNDERLINE */ OutHTML_SwUnderline,
+/* RES_CHRATR_WEIGHT */ OutHTML_SwWeight,
+/* RES_CHRATR_WORDLINEMODE */ nullptr,
+/* RES_CHRATR_AUTOKERN */ nullptr,
+/* RES_CHRATR_BLINK */ OutHTML_SwBlink,
+/* RES_CHRATR_NOHYPHEN */ nullptr, // New: don't hyphenate
+/* RES_CHRATR_UNUSED2 */ nullptr,
+/* RES_CHRATR_BACKGROUND */ OutHTML_CSS1Attr, // New: character background
+/* RES_CHRATR_CJK_FONT */ OutHTML_SvxFont,
+/* RES_CHRATR_CJK_FONTSIZE */ OutHTML_SvxFontHeight,
+/* RES_CHRATR_CJK_LANGUAGE */ OutHTML_SvxLanguage,
+/* RES_CHRATR_CJK_POSTURE */ OutHTML_SwPosture,
+/* RES_CHRATR_CJK_WEIGHT */ OutHTML_SwWeight,
+/* RES_CHRATR_CTL_FONT */ OutHTML_SvxFont,
+/* RES_CHRATR_CTL_FONTSIZE */ OutHTML_SvxFontHeight,
+/* RES_CHRATR_CTL_LANGUAGE */ OutHTML_SvxLanguage,
+/* RES_CHRATR_CTL_POSTURE */ OutHTML_SwPosture,
+/* RES_CHRATR_CTL_WEIGHT */ OutHTML_SwWeight,
+/* RES_CHRATR_ROTATE */ nullptr,
+/* RES_CHRATR_EMPHASIS_MARK */ nullptr,
+/* RES_CHRATR_TWO_LINES */ nullptr,
+/* RES_CHRATR_SCALEW */ nullptr,
+/* RES_CHRATR_RELIEF */ nullptr,
+/* RES_CHRATR_HIDDEN */ OutHTML_CSS1Attr,
+/* RES_CHRATR_OVERLINE */ OutHTML_CSS1Attr,
+/* RES_CHRATR_RSID */ nullptr,
+/* RES_CHRATR_BOX */ OutHTML_CSS1Attr,
+/* RES_CHRATR_SHADOW */ nullptr,
+/* RES_CHRATR_HIGHLIGHT */ nullptr,
+/* RES_CHRATR_GRABBAG */ nullptr,
+/* RES_CHRATR_BIDIRTL */ nullptr,
+/* RES_CHRATR_IDCTHINT */ nullptr,
+
+/* RES_TXTATR_REFMARK */ nullptr,
+/* RES_TXTATR_TOXMARK */ nullptr,
+/* RES_TXTATR_META */ nullptr,
+/* RES_TXTATR_METAFIELD */ nullptr,
+/* RES_TXTATR_AUTOFMT */ nullptr,
+/* RES_TXTATR_INETFMT */ OutHTML_SwFormatINetFormat,
+/* RES_TXTATR_CHARFMT */ OutHTML_SwTextCharFormat,
+/* RES_TXTATR_CJK_RUBY */ nullptr,
+/* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr,
+/* RES_TXTATR_INPUTFIELD */ OutHTML_SwFormatField,
+/* RES_TXTATR_CONTENTCONTROL */ nullptr,
+
+/* RES_TXTATR_FIELD */ OutHTML_SwFormatField,
+/* RES_TXTATR_FLYCNT */ OutHTML_SwFlyCnt,
+/* RES_TXTATR_FTN */ OutHTML_SwFormatFootnote,
+/* RES_TXTATR_ANNOTATION */ OutHTML_SwFormatField,
+/* RES_TXTATR_LINEBREAK */ OutHTML_SwFormatLineBreak,
+/* RES_TXTATR_DUMMY1 */ nullptr, // Dummy:
+
+/* RES_PARATR_LINESPACING */ nullptr,
+/* RES_PARATR_ADJUST */ OutHTML_SvxAdjust,
+/* RES_PARATR_SPLIT */ nullptr,
+/* RES_PARATR_ORPHANS */ nullptr,
+/* RES_PARATR_WIDOWS */ nullptr,
+/* RES_PARATR_TABSTOP */ nullptr,
+/* RES_PARATR_HYPHENZONE*/ nullptr,
+/* RES_PARATR_DROP */ OutHTML_CSS1Attr,
+/* RES_PARATR_REGISTER */ nullptr, // new: register-true
+/* RES_PARATR_NUMRULE */ nullptr, // Dummy:
+/* RES_PARATR_SCRIPTSPACE */ nullptr, // Dummy:
+/* RES_PARATR_HANGINGPUNCTUATION */ nullptr, // Dummy:
+/* RES_PARATR_FORBIDDEN_RULES */ nullptr, // new
+/* RES_PARATR_VERTALIGN */ nullptr, // new
+/* RES_PARATR_SNAPTOGRID*/ nullptr, // new
+/* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new
+/* RES_PARATR_OUTLINELEVEL */ nullptr,
+/* RES_PARATR_RSID */ nullptr,
+/* RES_PARATR_GRABBAG */ nullptr,
+
+/* RES_PARATR_LIST_ID */ nullptr, // new
+/* RES_PARATR_LIST_LEVEL */ nullptr, // new
+/* RES_PARATR_LIST_ISRESTART */ nullptr, // new
+/* RES_PARATR_LIST_RESTARTVALUE */ nullptr, // new
+/* RES_PARATR_LIST_ISCOUNTED */ nullptr, // new
+
+/* RES_FILL_ORDER */ nullptr,
+/* RES_FRM_SIZE */ nullptr,
+/* RES_PAPER_BIN */ nullptr,
+/* RES_MARGIN_FIRSTLINE */ nullptr,
+/* RES_MARGIN_TEXTLEFT */ nullptr,
+/* RES_MARGIN_RIGHT */ nullptr,
+/* RES_MARGIN_LEFT */ nullptr,
+/* RES_MARGIN_GUTTER */ nullptr,
+/* RES_MARGIN_GUTTER_RIGHT */ nullptr,
+/* RES_LR_SPACE */ nullptr,
+/* RES_UL_SPACE */ nullptr,
+/* RES_PAGEDESC */ nullptr,
+/* RES_BREAK */ nullptr,
+/* RES_CNTNT */ nullptr,
+/* RES_HEADER */ nullptr,
+/* RES_FOOTER */ nullptr,
+/* RES_PRINT */ nullptr,
+/* RES_OPAQUE */ nullptr,
+/* RES_PROTECT */ nullptr,
+/* RES_SURROUND */ nullptr,
+/* RES_VERT_ORIENT */ nullptr,
+/* RES_HORI_ORIENT */ nullptr,
+/* RES_ANCHOR */ nullptr,
+/* RES_BACKGROUND */ nullptr,
+/* RES_BOX */ nullptr,
+/* RES_SHADOW */ nullptr,
+/* RES_FRMMACRO */ nullptr,
+/* RES_COL */ nullptr,
+/* RES_KEEP */ nullptr,
+/* RES_URL */ nullptr,
+/* RES_EDIT_IN_READONLY */ nullptr,
+/* RES_LAYOUT_SPLIT */ nullptr,
+/* RES_CHAIN */ nullptr,
+/* RES_TEXTGRID */ nullptr,
+/* RES_LINENUMBER */ nullptr,
+/* RES_FTN_AT_TXTEND */ nullptr,
+/* RES_END_AT_TXTEND */ nullptr,
+/* RES_COLUMNBALANCE */ nullptr,
+/* RES_FRAMEDIR */ nullptr,
+/* RES_HEADER_FOOTER_EAT_SPACING */ nullptr,
+/* RES_ROW_SPLIT */ nullptr,
+/* RES_FLY_SPLIT */ nullptr,
+/* RES_FOLLOW_TEXT_FLOW */ nullptr,
+/* RES_COLLAPSING_BORDERS */ nullptr,
+/* RES_WRAP_INFLUENCE_ON_OBJPOS */ nullptr,
+/* RES_AUTO_STYLE */ nullptr,
+/* RES_FRMATR_STYLE_NAME */ nullptr,
+/* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr,
+/* RES_FRMATR_GRABBAG */ nullptr,
+/* RES_TEXT_VERT_ADJUST */ nullptr,
+/* RES_BACKGROUND_FULL_SIZE */ nullptr,
+/* RES_RTL_GUTTER */ nullptr,
+/* RES_DECORATIVE */ nullptr,
+
+/* RES_GRFATR_MIRRORGRF */ nullptr,
+/* RES_GRFATR_CROPGRF */ nullptr,
+/* RES_GRFATR_ROTATION */ nullptr,
+/* RES_GRFATR_LUMINANCE */ nullptr,
+/* RES_GRFATR_CONTRAST */ nullptr,
+/* RES_GRFATR_CHANNELR */ nullptr,
+/* RES_GRFATR_CHANNELG */ nullptr,
+/* RES_GRFATR_CHANNELB */ nullptr,
+/* RES_GRFATR_GAMMA */ nullptr,
+/* RES_GRFATR_INVERT */ nullptr,
+/* RES_GRFATR_TRANSPARENCY */ nullptr,
+/* RES_GRFATR_DRWAMODE */ nullptr,
+/* RES_GRFATR_DUMMY3 */ nullptr,
+/* RES_GRFATR_DUMMY4 */ nullptr,
+/* RES_GRFATR_DUMMY5 */ nullptr,
+
+/* RES_BOXATR_FORMAT */ nullptr,
+/* RES_BOXATR_FORMULA */ nullptr,
+/* RES_BOXATR_VALUE */ nullptr
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlatr.hxx b/sw/source/filter/html/htmlatr.hxx
new file mode 100644
index 0000000000..642a7c048e
--- /dev/null
+++ b/sw/source/filter/html/htmlatr.hxx
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLATR_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLATR_HXX
+
+#include <sal/config.h>
+
+struct HTMLOutEvent;
+
+extern HTMLOutEvent const aAnchorEventTable[];
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/filter/html/htmlbas.cxx b/sw/source/filter/html/htmlbas.cxx
new file mode 100644
index 0000000000..3500a631f1
--- /dev/null
+++ b/sw/source/filter/html/htmlbas.cxx
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <basic/basmgr.hxx>
+#include <basic/sbmod.hxx>
+#include <sfx2/evntconf.hxx>
+#include <sfx2/app.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+
+#include <com/sun/star/document/XEventsSupplier.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+#include <fmtfld.hxx>
+
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docsh.hxx>
+#include <docufld.hxx>
+#include "wrthtml.hxx"
+#include "swhtml.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+
+HTMLOutEvent const aBodyEventTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_O_SDonload, OOO_STRING_SVTOOLS_HTML_O_onload, SvMacroItemId::OpenDoc },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonunload, OOO_STRING_SVTOOLS_HTML_O_onunload, SvMacroItemId::PrepareCloseDoc },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonfocus, OOO_STRING_SVTOOLS_HTML_O_onfocus, SvMacroItemId::ActivateDoc },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonblur, OOO_STRING_SVTOOLS_HTML_O_onblur, SvMacroItemId::DeactivateDoc },
+ { nullptr, nullptr, SvMacroItemId::NONE }
+};
+
+void SwHTMLParser::NewScript()
+{
+ ParseScriptOptions( m_aScriptType, m_sBaseURL, m_eScriptLang, m_aScriptURL,
+ m_aBasicLib, m_aBasicModule );
+
+ if( !m_aScriptURL.isEmpty() )
+ {
+ // Ignore the script tag
+ m_bIgnoreRawData = true;
+ }
+}
+
+void SwHTMLParser::EndScript()
+{
+ bool bInsIntoBasic = false,
+ bInsSrcIntoField = false;
+
+ switch( m_eScriptLang )
+ {
+ case HTMLScriptLanguage::StarBasic:
+ bInsIntoBasic = true;
+ break;
+ default:
+ bInsSrcIntoField = true;
+ break;
+ }
+
+ m_bIgnoreRawData = false;
+ m_aScriptSource = convertLineEnd(m_aScriptSource, GetSystemLineEnd());
+
+ // Except for StarBasic and unused JavaScript, save each script or module name in a field
+ if( bInsSrcIntoField && !m_bIgnoreHTMLComments )
+ {
+ SwScriptFieldType *pType =
+ static_cast<SwScriptFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Script ));
+
+ SwScriptField aField( pType, m_aScriptType,
+ !m_aScriptURL.isEmpty() ? m_aScriptURL : m_aScriptSource,
+ !m_aScriptURL.isEmpty() );
+ InsertAttr( SwFormatField( aField ), false );
+ }
+
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ if( !m_aScriptSource.isEmpty() && pDocSh &&
+ bInsIntoBasic && IsNewDoc() )
+ {
+ // Create a Basic module for javascript and StarBasic.
+
+ // The Basic does still not remove SGML comments
+ RemoveSGMLComment( m_aScriptSource );
+
+ // get library name
+ OUString aLibName;
+ if( !m_aBasicLib.isEmpty() )
+ aLibName = m_aBasicLib;
+ else
+ aLibName = "Standard";
+
+ // get module library container
+ Reference< script::XLibraryContainer > xModLibContainer = pDocSh->GetBasicContainer();
+
+ if ( xModLibContainer.is() )
+ {
+ Reference< container::XNameContainer > xModLib;
+ if ( xModLibContainer->hasByName( aLibName ) )
+ {
+ // get module library
+ Any aElement = xModLibContainer->getByName( aLibName );
+ aElement >>= xModLib;
+ }
+ else
+ {
+ // create module library
+ xModLib = xModLibContainer->createLibrary( aLibName );
+ }
+
+ if ( xModLib.is() )
+ {
+ if( m_aBasicModule.isEmpty() )
+ {
+ // create module name
+ bool bFound = true;
+ while( bFound )
+ {
+ m_aBasicModule = "Modul" + OUString::number( static_cast<sal_Int32>(++m_nSBModuleCnt) );
+ bFound = xModLib->hasByName( m_aBasicModule );
+ }
+ }
+
+ // create module
+ OUString aModName( m_aBasicModule );
+ if ( !xModLib->hasByName( aModName ) )
+ {
+ Any aElement;
+ aElement <<= m_aScriptSource;
+ xModLib->insertByName( aModName , aElement );
+ }
+ }
+ }
+
+ // get dialog library container
+ Reference< script::XLibraryContainer > xDlgLibContainer = pDocSh->GetDialogContainer();
+
+ if ( xDlgLibContainer.is() )
+ {
+ if ( !xDlgLibContainer->hasByName( aLibName ) )
+ {
+ // create dialog library
+ xDlgLibContainer->createLibrary( aLibName );
+ }
+ }
+ }
+
+ m_aScriptSource.clear();
+ m_aScriptType.clear();
+ m_aScriptURL.clear();
+
+ m_aBasicLib.clear();
+ m_aBasicModule.clear();
+}
+
+void SwHTMLParser::AddScriptSource()
+{
+ // We'll just remember a few strings here
+ if( aToken.getLength() > 2 &&
+ (HTMLScriptLanguage::StarBasic==m_eScriptLang && aToken[ 0 ] == '\'') )
+ {
+ sal_Int32 nPos = -1;
+ if( m_aBasicLib.isEmpty() )
+ {
+ nPos = aToken.indexOf( OOO_STRING_SVTOOLS_HTML_SB_library );
+ if( nPos != -1 )
+ {
+ m_aBasicLib =
+ aToken.subView( nPos + sizeof(OOO_STRING_SVTOOLS_HTML_SB_library) - 1 );
+ m_aBasicLib = comphelper::string::strip(m_aBasicLib, ' ');
+ }
+ }
+
+ if( m_aBasicModule.isEmpty() && nPos == -1 )
+ {
+ nPos = aToken.indexOf( OOO_STRING_SVTOOLS_HTML_SB_module );
+ if( nPos != -1 )
+ {
+ m_aBasicModule =
+ aToken.subView( nPos + sizeof(OOO_STRING_SVTOOLS_HTML_SB_module) - 1 );
+ m_aBasicModule = comphelper::string::strip(m_aBasicModule, ' ');
+ }
+ }
+
+ if( nPos == -1 )
+ {
+ if( !m_aScriptSource.isEmpty() )
+ m_aScriptSource += "\n";
+ m_aScriptSource += aToken;
+ }
+ }
+ else if( !m_aScriptSource.isEmpty() || !aToken.isEmpty() )
+ {
+ // Empty lines are ignored on the beginning
+ if( !m_aScriptSource.isEmpty() )
+ {
+ m_aScriptSource += "\n";
+ }
+ m_aScriptSource += aToken;
+ }
+}
+
+void SwHTMLParser::InsertBasicDocEvent( const OUString& aEvent, const OUString& rName,
+ ScriptType eScrType,
+ const OUString& rScrType )
+{
+ OSL_ENSURE( !rName.isEmpty(), "InsertBasicDocEvent() called without macro" );
+ if( rName.isEmpty() )
+ return;
+
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ OSL_ENSURE( pDocSh, "Where is the DocShell?" );
+ if( !pDocSh )
+ return;
+
+ OUString sEvent(convertLineEnd(rName, GetSystemLineEnd()));
+ OUString sScriptType;
+ if( EXTENDED_STYPE == eScrType )
+ sScriptType = rScrType;
+
+ SfxEventConfiguration::ConfigureEvent( aEvent, SvxMacro( sEvent, sScriptType, eScrType ),
+ pDocSh );
+}
+
+void SwHTMLWriter::OutBasic(const SwHTMLWriter & rHTMLWrt)
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) rHTMLWrt;
+#else
+ if( !m_bCfgStarBasic )
+ return;
+
+ BasicManager *pBasicMan = m_pDoc->GetDocShell()->GetBasicManager();
+ OSL_ENSURE( pBasicMan, "Where is the Basic-Manager?" );
+ // Only write DocumentBasic
+ if( !pBasicMan || pBasicMan == SfxApplication::GetBasicManager() )
+ {
+ return;
+ }
+
+ bool bFirst=true;
+ // Now write all StarBasic and unused Javascript modules
+ for( sal_uInt16 i=0; i<pBasicMan->GetLibCount(); i++ )
+ {
+ StarBASIC *pBasic = pBasicMan->GetLib( i );
+ const OUString& rLibName = pBasic->GetName();
+ for( const auto& pModule: pBasic->GetModules() )
+ {
+ OUString sLang(SVX_MACRO_LANGUAGE_STARBASIC);
+
+ if( bFirst )
+ {
+ bFirst = false;
+ OutNewLine();
+ OString sOut =
+ "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_meta
+ " " OOO_STRING_SVTOOLS_HTML_O_httpequiv
+ "=\""
+ OOO_STRING_SVTOOLS_HTML_META_content_script_type
+ "\" " OOO_STRING_SVTOOLS_HTML_O_content
+ "=\"text/x-";
+ Strm().WriteOString( sOut );
+ // Entities aren't welcome here
+ Strm().WriteOString( OUStringToOString(sLang, RTL_TEXTENCODING_UTF8) )
+ .WriteOString( "\">" );
+ }
+
+ const OUString& rModName = pModule->GetName();
+ Strm().WriteOString( SAL_NEWLINE_STRING ); // don't indent!
+ HTMLOutFuncs::OutScript( Strm(), GetBaseURL(), pModule->GetSource32(),
+ sLang, STARBASIC, OUString(),
+ &rLibName, &rModName );
+ }
+ }
+#endif
+}
+
+static const char* aEventNames[] =
+{
+ "OnLoad", "OnPrepareUnload", "OnFocus", "OnUnfocus"
+};
+
+void SwHTMLWriter::OutBasicBodyEvents()
+{
+ SwDocShell *pDocSh = m_pDoc->GetDocShell();
+ if( !pDocSh )
+ return;
+
+ SvxMacroTableDtor aDocTable;
+
+ uno::Reference< document::XEventsSupplier > xSup( pDocSh->GetModel(), uno::UNO_QUERY );
+ uno::Reference < container::XNameReplace > xEvents = xSup->getEvents();
+ for ( sal_Int32 i=0; i<4; i++ )
+ {
+ std::unique_ptr<SvxMacro> pMacro = SfxEventConfiguration::ConvertToMacro( xEvents->getByName( OUString::createFromAscii(aEventNames[i]) ), pDocSh );
+ if ( pMacro )
+ {
+ aDocTable.Insert( aBodyEventTable[i].nEvent, *pMacro );
+ }
+ }
+
+ if( !aDocTable.empty() )
+ HTMLOutFuncs::Out_Events( Strm(), aDocTable, aBodyEventTable,
+ m_bCfgStarBasic );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlcss1.cxx b/sw/source/filter/html/htmlcss1.cxx
new file mode 100644
index 0000000000..92e5d0d94d
--- /dev/null
+++ b/sw/source/filter/html/htmlcss1.cxx
@@ -0,0 +1,2331 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/urihelper.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sfx2/docfile.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <o3tl/string_view.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfsize.hxx>
+#include <frmatr.hxx>
+#include <charfmt.hxx>
+#include <docary.hxx>
+#include <osl/diagnose.h>
+
+#include <doc.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <poolfmt.hxx>
+#include <docsh.hxx>
+#include <paratr.hxx>
+#include <pagedesc.hxx>
+#include "css1kywd.hxx"
+#include "swcss1.hxx"
+#include "htmlnum.hxx"
+#include "swhtml.hxx"
+#include <numrule.hxx>
+#include "css1atr.hxx"
+
+using namespace ::com::sun::star;
+
+// How many rows/characters are allowed for DropCaps?
+// (Are there maybe somewhere else corresponding values?)
+#define MAX_DROPCAP_LINES 9
+#define MAX_DROPCAP_CHARS 9
+
+static void lcl_swcss1_setEncoding( SwFormat& rFormat, rtl_TextEncoding eEnc );
+
+// Implementation of SwCSS1Parsers (actually swcss1.cxx)
+const sal_uInt16 aItemIds[] =
+{
+ RES_BREAK,
+ RES_PAGEDESC,
+ RES_KEEP,
+};
+
+void SwCSS1Parser::ChgPageDesc( const SwPageDesc *pPageDesc,
+ const SwPageDesc& rNewPageDesc )
+{
+ size_t pos;
+ bool found = m_pDoc->ContainsPageDesc( pPageDesc, &pos );
+ OSL_ENSURE( found, "style not found" );
+ if (found)
+ m_pDoc->ChgPageDesc( pos, rNewPageDesc );
+}
+
+SwCSS1Parser::SwCSS1Parser(SwDoc *const pDoc, SwHTMLParser const& rParser,
+ const sal_uInt32 aFHeights[7], const OUString& rBaseURL, bool const bNewDoc)
+ : SvxCSS1Parser(pDoc->GetAttrPool(), rBaseURL,
+ aItemIds, SAL_N_ELEMENTS(aItemIds))
+ , m_pDoc( pDoc )
+ , m_rHTMLParser(rParser)
+ , m_nDropCapCnt( 0 ),
+ m_bIsNewDoc( bNewDoc ),
+ m_bBodyBGColorSet( false ),
+ m_bBodyBackgroundSet( false ),
+ m_bBodyTextSet( false ),
+ m_bBodyLinkSet( false ),
+ m_bBodyVLinkSet( false ),
+ m_bSetFirstPageDesc( false ),
+ m_bSetRightPageDesc( false ),
+ m_bTableHeaderTextCollSet( false ),
+ m_bTableTextCollSet( false ),
+ m_bLinkCharFormatsSet( false )
+{
+ m_aFontHeights[0] = aFHeights[0];
+ m_aFontHeights[1] = aFHeights[1];
+ m_aFontHeights[2] = aFHeights[2];
+ m_aFontHeights[3] = aFHeights[3];
+ m_aFontHeights[4] = aFHeights[4];
+ m_aFontHeights[5] = aFHeights[5];
+ m_aFontHeights[6] = aFHeights[6];
+}
+
+SwCSS1Parser::~SwCSS1Parser()
+{
+}
+
+// Feature: PrintExt
+bool SwCSS1Parser::SetFormatBreak( SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rPropInfo )
+{
+ SvxBreak eBreak = SvxBreak::NONE;
+ bool bKeep = false;
+ bool bSetKeep = false, bSetBreak = false, bSetPageDesc = false;
+ const SwPageDesc *pPageDesc = nullptr;
+ switch( rPropInfo.m_ePageBreakBefore )
+ {
+ case SVX_CSS1_PBREAK_ALWAYS:
+ eBreak = SvxBreak::PageBefore;
+ bSetBreak = true;
+ break;
+ case SVX_CSS1_PBREAK_LEFT:
+ pPageDesc = GetLeftPageDesc( true );
+ bSetPageDesc = true;
+ break;
+ case SVX_CSS1_PBREAK_RIGHT:
+ pPageDesc = GetRightPageDesc( true );
+ bSetPageDesc = true;
+ break;
+ case SVX_CSS1_PBREAK_AUTO:
+ bSetBreak = bSetPageDesc = true;
+ break;
+ default:
+ ;
+ }
+ switch( rPropInfo.m_ePageBreakAfter )
+ {
+ case SVX_CSS1_PBREAK_ALWAYS:
+ case SVX_CSS1_PBREAK_LEFT:
+ case SVX_CSS1_PBREAK_RIGHT:
+ // LEFT/RIGHT also could be set in the previous paragraph
+ eBreak = SvxBreak::PageAfter;
+ bSetBreak = true;
+ break;
+ case SVX_CSS1_PBREAK_AUTO:
+ bSetBreak = bSetKeep = bSetPageDesc = true;
+ break;
+ case SVX_CSS1_PBREAK_AVOID:
+ bKeep = bSetKeep = true;
+ break;
+ default:
+ ;
+ }
+
+ if( bSetBreak )
+ rItemSet.Put( SvxFormatBreakItem( eBreak, RES_BREAK ) );
+ if( bSetPageDesc )
+ rItemSet.Put( SwFormatPageDesc( pPageDesc ) );
+ if( bSetKeep )
+ rItemSet.Put( SvxFormatKeepItem( bKeep, RES_KEEP ) );
+
+ return bSetBreak;
+}
+
+static void SetCharFormatAttrs( SwCharFormat *pCharFormat, SfxItemSet& rItemSet )
+{
+ static const TypedWhichId<SvxFontHeightItem> aWhichIds[3] = { RES_CHRATR_FONTSIZE,RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CTL_FONTSIZE };
+ for(auto const & i : aWhichIds)
+ {
+ const SvxFontHeightItem* pItem = rItemSet.GetItemIfSet( i, false );
+ if( pItem && pItem->GetProp() != 100)
+ {
+ // percentage values at the FontHeight item aren't supported
+ rItemSet.ClearItem( i );
+ }
+ }
+
+ pCharFormat->SetFormatAttr( rItemSet );
+
+ if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ // A Brush-Item with RES_BACKGROUND must be converted to one
+ // with RES_CHRATR_BACKGROUND
+
+ SvxBrushItem aBrushItem( *pItem );
+ aBrushItem.SetWhich( RES_CHRATR_BACKGROUND );
+ pCharFormat->SetFormatAttr( aBrushItem );
+ }
+
+ if( const SvxBoxItem* pItem = rItemSet.GetItemIfSet( RES_BOX, false ) )
+ {
+ SvxBoxItem aBoxItem( *pItem );
+ aBoxItem.SetWhich( RES_CHRATR_BOX );
+ pCharFormat->SetFormatAttr( aBoxItem );
+ }
+}
+
+void SwCSS1Parser::SetLinkCharFormats()
+{
+ OSL_ENSURE( !m_bLinkCharFormatsSet, "Call SetLinkCharFormats unnecessary" );
+
+ SvxCSS1MapEntry *pStyleEntry =
+ GetTag( OOO_STRING_SVTOOLS_HTML_anchor );
+ SwCharFormat *pUnvisited = nullptr, *pVisited = nullptr;
+ if( pStyleEntry )
+ {
+ SfxItemSet& rItemSet = pStyleEntry->GetItemSet();
+ bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,
+ false));
+ pUnvisited = GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL );
+ SetCharFormatAttrs( pUnvisited, rItemSet );
+ m_bBodyLinkSet |= bColorSet;
+
+ pVisited = GetCharFormatFromPool( RES_POOLCHR_INET_VISIT );
+ SetCharFormatAttrs( pVisited, rItemSet );
+ m_bBodyVLinkSet |= bColorSet;
+ }
+
+ OUString sTmp = OOO_STRING_SVTOOLS_HTML_anchor ":link";
+
+ pStyleEntry = GetTag( sTmp );
+ if( pStyleEntry )
+ {
+ SfxItemSet& rItemSet = pStyleEntry->GetItemSet();
+ bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,
+ false));
+ if( !pUnvisited )
+ pUnvisited = GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL );
+ SetCharFormatAttrs( pUnvisited, rItemSet );
+ m_bBodyLinkSet |= bColorSet;
+ }
+
+ sTmp = OOO_STRING_SVTOOLS_HTML_anchor ":visited";
+
+ pStyleEntry = GetTag( sTmp );
+ if( pStyleEntry )
+ {
+ SfxItemSet& rItemSet = pStyleEntry->GetItemSet();
+ bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,
+ false));
+ if( !pVisited )
+ pVisited = GetCharFormatFromPool( RES_POOLCHR_INET_VISIT );
+ SetCharFormatAttrs( pVisited, rItemSet );
+ m_bBodyVLinkSet |= bColorSet;
+ }
+
+ m_bLinkCharFormatsSet = true;
+}
+
+static void SetTextCollAttrs( SwTextFormatColl *pColl, SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo const & rPropInfo,
+ SwCSS1Parser *pCSS1Parser )
+{
+ const SfxItemSet& rCollItemSet = pColl->GetAttrSet();
+
+ // note: there was some SvxLRSpaceItem code here that was nonobvious
+ // but it looks like the only cases in which it would be required
+ // with split items are if some nProp != 100 or if SetAutoFirst() had
+ // been called (on the pColl items) but it looks like none of these are
+ // possible in HTML import.
+
+ // top and bottom border
+ const SvxULSpaceItem* pCollULItem;
+ const SvxULSpaceItem* pULItem;
+ if( (rPropInfo.m_bTopMargin || rPropInfo.m_bBottomMargin) &&
+ (!rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin) &&
+ (pCollULItem = rCollItemSet.GetItemIfSet(RES_UL_SPACE)) &&
+ (pULItem = rItemSet.GetItemIfSet(RES_UL_SPACE,false)) )
+ {
+ SvxULSpaceItem aULItem( *pCollULItem );
+ if( rPropInfo.m_bTopMargin )
+ aULItem.SetUpper( pULItem->GetUpper() );
+ if( rPropInfo.m_bBottomMargin )
+ aULItem.SetLower( pULItem->GetLower() );
+
+ rItemSet.Put( aULItem );
+ }
+
+ static const TypedWhichId<SvxFontHeightItem> aWhichIds[3] = { RES_CHRATR_FONTSIZE,RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CTL_FONTSIZE };
+ for(auto const & i : aWhichIds)
+ {
+ const SvxFontHeightItem* pItem = rItemSet.GetItemIfSet( i, false );
+ if( pItem && pItem->GetProp() != 100)
+ {
+ // percentage values at the FontHeight item aren't supported
+ rItemSet.ClearItem( i );
+ }
+ }
+
+ pCSS1Parser->SetFormatBreak( rItemSet, rPropInfo );
+
+ pColl->SetFormatAttr( rItemSet );
+}
+
+void SwCSS1Parser::SetTableTextColl( bool bHeader )
+{
+ OSL_ENSURE( !(bHeader ? m_bTableHeaderTextCollSet : m_bTableTextCollSet),
+ "Call SetTableTextColl unnecessary" );
+
+ sal_uInt16 nPoolId;
+ OUString sTag;
+ if( bHeader )
+ {
+ nPoolId = RES_POOLCOLL_TABLE_HDLN;
+ sTag = OOO_STRING_SVTOOLS_HTML_tableheader;
+ }
+ else
+ {
+ nPoolId = RES_POOLCOLL_TABLE;
+ sTag = OOO_STRING_SVTOOLS_HTML_tabledata;
+ }
+
+ SwTextFormatColl *pColl = nullptr;
+
+ // The following entries will never be used again and may be changed.
+ SvxCSS1MapEntry *pStyleEntry = GetTag( sTag );
+ if( pStyleEntry )
+ {
+ pColl = GetTextFormatColl(nPoolId, OUString());
+ SetTextCollAttrs( pColl, pStyleEntry->GetItemSet(),
+ pStyleEntry->GetPropertyInfo(), this );
+ }
+
+ OUString sTmp = sTag + " " OOO_STRING_SVTOOLS_HTML_parabreak;
+ pStyleEntry = GetTag( sTmp );
+ if( pStyleEntry )
+ {
+ if( !pColl )
+ pColl = GetTextFormatColl(nPoolId, OUString());
+ SetTextCollAttrs( pColl, pStyleEntry->GetItemSet(),
+ pStyleEntry->GetPropertyInfo(), this );
+ }
+
+ if( bHeader )
+ m_bTableHeaderTextCollSet = true;
+ else
+ m_bTableTextCollSet = true;
+}
+
+void SwCSS1Parser::SetPageDescAttrs( const SvxBrushItem *pBrush,
+ SfxItemSet *pItemSet2 )
+{
+ std::shared_ptr<SvxBrushItem> aBrushItem(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
+ std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(RES_BOX));
+ std::shared_ptr<SvxFrameDirectionItem> aFrameDirItem(std::make_shared<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR));
+ bool bSetBrush = pBrush!=nullptr, bSetBox = false, bSetFrameDir = false;
+ if( pBrush )
+ aBrushItem.reset(pBrush->Clone());
+
+ if( pItemSet2 )
+ {
+ if( const SvxBrushItem* pItem = pItemSet2->GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ // set a background
+ aBrushItem.reset(pItem->Clone());
+ pItemSet2->ClearItem( RES_BACKGROUND );
+ bSetBrush = true;
+ }
+
+ if( const SvxBoxItem* pItem = pItemSet2->GetItemIfSet( RES_BOX, false ) )
+ {
+ // set a border
+ aBoxItem.reset(pItem->Clone());
+ pItemSet2->ClearItem( RES_BOX );
+ bSetBox = true;
+ }
+
+ if( const SvxFrameDirectionItem* pItem = pItemSet2->GetItemIfSet( RES_FRAMEDIR, false ) )
+ {
+ // set a frame
+ aFrameDirItem.reset(pItem->Clone());
+ pItemSet2->ClearItem( RES_FRAMEDIR );
+ bSetFrameDir = true;
+ }
+ }
+
+ if( !(bSetBrush || bSetBox || bSetFrameDir) )
+ return;
+
+ static sal_uInt16 aPoolIds[] = { RES_POOLPAGE_HTML, RES_POOLPAGE_FIRST,
+ RES_POOLPAGE_LEFT, RES_POOLPAGE_RIGHT };
+ for(sal_uInt16 i : aPoolIds)
+ {
+ const SwPageDesc *pPageDesc = GetPageDesc( i, false );
+ if( pPageDesc )
+ {
+ SwPageDesc aNewPageDesc( *pPageDesc );
+ SwFrameFormat &rMaster = aNewPageDesc.GetMaster();
+ if( bSetBrush )
+ rMaster.SetFormatAttr( *aBrushItem );
+ if( bSetBox )
+ rMaster.SetFormatAttr( *aBoxItem );
+ if( bSetFrameDir )
+ rMaster.SetFormatAttr( *aFrameDirItem );
+
+ ChgPageDesc( pPageDesc, aNewPageDesc );
+ }
+ }
+}
+
+// Feature: PrintExt
+void SwCSS1Parser::SetPageDescAttrs( const SwPageDesc *pPageDesc,
+ SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rPropInfo )
+{
+ if( !pPageDesc )
+ return;
+
+ SwPageDesc aNewPageDesc( *pPageDesc );
+ SwFrameFormat &rMaster = aNewPageDesc.GetMaster();
+ const SfxItemSet& rPageItemSet = rMaster.GetAttrSet();
+ bool bChanged = false;
+
+ // left, right border and first line indentation
+ ::std::optional<SvxLRSpaceItem> oLRSpace;
+ assert(!rItemSet.GetItemIfSet(RES_LR_SPACE,false));
+ if (rPropInfo.m_bLeftMargin)
+ {
+ // note: parser never creates SvxLeftMarginItem! must be converted
+ if (SvxTextLeftMarginItem const*const pLeft = rItemSet.GetItemIfSet(RES_MARGIN_TEXTLEFT, false))
+ {
+ if (!oLRSpace)
+ {
+ if (const SvxLRSpaceItem* pPageItem = rPageItemSet.GetItemIfSet(RES_LR_SPACE))
+ {
+ oLRSpace.emplace(*pPageItem);
+ }
+ else
+ {
+ oLRSpace.emplace(RES_LR_SPACE);
+ }
+ }
+ oLRSpace->SetLeft(pLeft->GetTextLeft());
+ }
+ }
+ if (rPropInfo.m_bRightMargin)
+ {
+ // note: parser never creates SvxLeftMarginItem! must be converted
+ if (SvxRightMarginItem const*const pRight = rItemSet.GetItemIfSet(RES_MARGIN_RIGHT, false))
+ {
+ if (!oLRSpace)
+ {
+ if (const SvxLRSpaceItem* pPageItem = rPageItemSet.GetItemIfSet(RES_LR_SPACE))
+ {
+ oLRSpace.emplace(*pPageItem);
+ }
+ else
+ {
+ oLRSpace.emplace(RES_LR_SPACE);
+ }
+ }
+ oLRSpace->SetRight(pRight->GetRight());
+ }
+ }
+ if (oLRSpace)
+ {
+ rMaster.SetFormatAttr(*oLRSpace);
+ bChanged = true;
+ }
+
+ // top and bottom border
+ const SvxULSpaceItem *pULItem;
+ if( (rPropInfo.m_bTopMargin || rPropInfo.m_bBottomMargin) &&
+ (pULItem = rItemSet.GetItemIfSet(RES_UL_SPACE,false)) )
+ {
+ const SvxULSpaceItem* pPageItem;
+ if( (!rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin) &&
+ (pPageItem = rPageItemSet.GetItemIfSet(RES_UL_SPACE) ) )
+ {
+ SvxULSpaceItem aULItem( *pPageItem );
+ if( rPropInfo.m_bTopMargin )
+ aULItem.SetUpper( pULItem->GetUpper() );
+ if( rPropInfo.m_bBottomMargin )
+ aULItem.SetLower( pULItem->GetLower() );
+
+ rMaster.SetFormatAttr( aULItem );
+ }
+ else
+ {
+ rMaster.SetFormatAttr( *pULItem );
+ }
+ bChanged = true;
+ }
+
+ // the size
+ if( rPropInfo.m_eSizeType != SVX_CSS1_STYPE_NONE )
+ {
+ if( rPropInfo.m_eSizeType == SVX_CSS1_STYPE_TWIP )
+ {
+ rMaster.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Fixed, rPropInfo.m_nWidth,
+ rPropInfo.m_nHeight ) );
+ bChanged = true;
+ }
+ else
+ {
+ // With "size: auto|portrait|landscape" the current size
+ // of the style remains. If "landscape" and "portrait" then
+ // the landscape flag will be set and maybe the width/height
+ // are swapped.
+ SwFormatFrameSize aFrameSz( rMaster.GetFrameSize() );
+ bool bLandscape = aNewPageDesc.GetLandscape();
+ if( ( bLandscape &&
+ rPropInfo.m_eSizeType == SVX_CSS1_STYPE_PORTRAIT ) ||
+ ( !bLandscape &&
+ rPropInfo.m_eSizeType == SVX_CSS1_STYPE_LANDSCAPE ) )
+ {
+ SwTwips nTmp = aFrameSz.GetHeight();
+ aFrameSz.SetHeight( aFrameSz.GetWidth() );
+ aFrameSz.SetWidth( nTmp );
+ rMaster.SetFormatAttr( aFrameSz );
+ aNewPageDesc.SetLandscape( !bLandscape );
+ bChanged = true;
+ }
+ }
+ }
+
+ // Is that possible?
+ if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ // set a background
+ rMaster.SetFormatAttr( *pItem );
+ rItemSet.ClearItem( RES_BACKGROUND );
+ bChanged = true;
+ }
+
+ if( bChanged )
+ ChgPageDesc( pPageDesc, aNewPageDesc );
+}
+
+std::unique_ptr<SvxBrushItem> SwCSS1Parser::makePageDescBackground() const
+{
+ return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false )
+ ->GetMaster().makeBackgroundBrushItem();
+}
+
+Css1ScriptFlags SwCSS1Parser::GetScriptFromClass( OUString& rClass,
+ bool bSubClassOnly )
+{
+ Css1ScriptFlags nScriptFlags = Css1ScriptFlags::AllMask;
+ sal_Int32 nLen = rClass.getLength();
+ sal_Int32 nPos = nLen > 4 ? rClass.lastIndexOf( '-' ) : -1;
+
+ if( nPos == -1 )
+ {
+ if( bSubClassOnly )
+ return nScriptFlags;
+ nPos = 0;
+ }
+ else
+ {
+ nPos++;
+ nLen = nLen - nPos;
+ }
+
+ switch( nLen )
+ {
+ case 3:
+ if( rClass.matchIgnoreAsciiCase( "cjk", nPos ) )
+ {
+ nScriptFlags = Css1ScriptFlags::CJK;
+ }
+ else if( rClass.matchIgnoreAsciiCase( "ctl", nPos ) )
+ {
+ nScriptFlags = Css1ScriptFlags::CTL;
+ }
+ break;
+ case 7:
+ if( rClass.matchIgnoreAsciiCase( "western", nPos ) )
+ {
+ nScriptFlags = Css1ScriptFlags::Western;
+ }
+ break;
+ }
+ if( Css1ScriptFlags::AllMask != nScriptFlags )
+ {
+ if( nPos )
+ {
+ rClass = rClass.copy( 0, nPos-1 );
+ }
+ else
+ {
+ rClass.clear();
+ }
+ }
+
+ return nScriptFlags;
+}
+
+static CSS1SelectorType GetTokenAndClass( const CSS1Selector *pSelector,
+ OUString& rToken, OUString& rClass,
+ Css1ScriptFlags& rScriptFlags )
+{
+ rToken = pSelector->GetString();
+ rClass.clear();
+ rScriptFlags = Css1ScriptFlags::AllMask;
+
+ CSS1SelectorType eType = pSelector->GetType();
+ if( CSS1_SELTYPE_ELEM_CLASS==eType )
+ {
+ sal_Int32 nPos = rToken.indexOf( '.' );
+ OSL_ENSURE( nPos >= 0, "No dot in Class-Selector???" );
+ if( nPos >= 0 )
+ {
+ rClass = rToken.copy( nPos+1 );
+ rToken = rToken.copy( 0, nPos );
+
+ rScriptFlags = SwCSS1Parser::GetScriptFromClass( rClass, false );
+ if( rClass.isEmpty() )
+ eType = CSS1_SELTYPE_ELEMENT;
+ }
+ }
+
+ rToken = rToken.toAsciiLowerCase();
+ return eType;
+}
+
+static void RemoveScriptItems( SfxItemSet& rItemSet, Css1ScriptFlags nScript,
+ const SfxItemSet *pParentItemSet = nullptr )
+{
+ static const sal_uInt16 aWhichIds[3][5] =
+ {
+ { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, RES_CHRATR_LANGUAGE,
+ RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT },
+ { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CJK_LANGUAGE,
+ RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT },
+ { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, RES_CHRATR_CTL_LANGUAGE,
+ RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT }
+ };
+
+ bool aClearItems[3] = { false, false, false };
+ switch( nScript )
+ {
+ case Css1ScriptFlags::Western:
+ aClearItems[1] = aClearItems[2] = true;
+ break;
+ case Css1ScriptFlags::CJK:
+ aClearItems[0] = aClearItems[2] = true;
+ break;
+ case Css1ScriptFlags::CTL:
+ aClearItems[0] = aClearItems[1] = true;
+ break;
+ case Css1ScriptFlags::AllMask:
+ break;
+ default:
+ OSL_ENSURE( aClearItems[0], "unknown script type" );
+ break;
+ }
+
+ for( size_t j=0; j < SAL_N_ELEMENTS(aWhichIds); ++j )
+ {
+ for( size_t i=0; i < SAL_N_ELEMENTS(aWhichIds[0]); ++i )
+ {
+ sal_uInt16 nWhich = aWhichIds[j][i];
+ const SfxPoolItem *pItem;
+ if( aClearItems[j] ||
+ (pParentItemSet &&
+ SfxItemState::SET == rItemSet.GetItemState( nWhich, false, &pItem ) &&
+ (0==i ? swhtml_css1atr_equalFontItems( *pItem, pParentItemSet->Get(nWhich ) )
+ : *pItem == pParentItemSet->Get(nWhich ) ) ) )
+ {
+ rItemSet.ClearItem( nWhich );
+ }
+ }
+ }
+}
+
+void SwCSS1Parser::StyleParsed( const CSS1Selector *pSelector,
+ SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo )
+{
+ if( !m_bIsNewDoc )
+ return;
+
+ CSS1SelectorType eSelType = pSelector->GetType();
+ const CSS1Selector *pNext = pSelector->GetNext();
+
+ if( CSS1_SELTYPE_ID==eSelType && !pNext )
+ {
+ InsertId( pSelector->GetString(), rItemSet, rPropInfo );
+ }
+ else if( CSS1_SELTYPE_CLASS==eSelType && !pNext )
+ {
+ OUString aClass( pSelector->GetString() );
+ Css1ScriptFlags nScript = GetScriptFromClass( aClass );
+ if( Css1ScriptFlags::AllMask != nScript )
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ RemoveScriptItems( aScriptItemSet, nScript );
+ InsertClass( aClass, aScriptItemSet, rPropInfo );
+ }
+ else
+ {
+ InsertClass( aClass, rItemSet, rPropInfo );
+ }
+ }
+ else if( CSS1_SELTYPE_PAGE==eSelType )
+ {
+ if( !pNext ||
+ (CSS1_SELTYPE_PSEUDO == pNext->GetType() &&
+ (pNext->GetString().equalsIgnoreAsciiCase( "left" ) ||
+ pNext->GetString().equalsIgnoreAsciiCase( "right" ) ||
+ pNext->GetString().equalsIgnoreAsciiCase( "first" ) ) ) )
+ {
+ OUString aName;
+ if( pNext )
+ aName = pNext->GetString();
+ InsertPage( aName,
+ pNext != nullptr,
+ rItemSet, rPropInfo );
+ }
+ }
+
+ if( CSS1_SELTYPE_ELEMENT != eSelType &&
+ CSS1_SELTYPE_ELEM_CLASS != eSelType)
+ return;
+
+ // get token and class of selector
+ OUString aToken2;
+ OUString aClass;
+ Css1ScriptFlags nScript;
+ eSelType = GetTokenAndClass( pSelector, aToken2, aClass, nScript );
+ HtmlTokenId nToken2 = GetHTMLToken( aToken2 );
+
+ // and also some information of the next element
+ CSS1SelectorType eNextType = pNext ? pNext->GetType()
+ : CSS1_SELTYPE_ELEMENT;
+
+ // first some special cases
+ if( CSS1_SELTYPE_ELEMENT==eSelType )
+ {
+ switch( nToken2 )
+ {
+ case HtmlTokenId::ANCHOR_ON:
+ if( !pNext )
+ {
+ InsertTag( aToken2, rItemSet, rPropInfo );
+ return;
+ }
+ else if (CSS1_SELTYPE_PSEUDO == eNextType)
+ {
+ // maybe A:visited or A:link
+
+ OUString aPseudo( pNext->GetString() );
+ aPseudo = aPseudo.toAsciiLowerCase();
+ bool bInsert = false;
+ switch( aPseudo[0] )
+ {
+ case 'l':
+ if( aPseudo == "link" )
+ {
+ bInsert = true;
+ }
+ break;
+ case 'v':
+ if( aPseudo == "visited" )
+ {
+ bInsert = true;
+ }
+ break;
+ }
+ if( bInsert )
+ {
+ OUString sTmp = aToken2 + ":" + aPseudo;
+ if( Css1ScriptFlags::AllMask != nScript )
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ RemoveScriptItems( aScriptItemSet, nScript );
+ InsertTag( sTmp, aScriptItemSet, rPropInfo );
+ }
+ else
+ {
+ InsertTag( sTmp, rItemSet, rPropInfo );
+ }
+ return;
+ }
+ }
+ break;
+ case HtmlTokenId::BODY_ON:
+ if( !pNext )
+ {
+ // BODY
+
+ // We must test the background before setting, because
+ // in SetPageDescAttrs it will be deleted.
+ if( const SvxBrushItem *pBrushItem = rItemSet.GetItemIfSet(RES_BACKGROUND,false) )
+ {
+ /// Body has a background color, if it is not "no fill"/"auto fill"
+ if( pBrushItem->GetColor() != COL_TRANSPARENT )
+ m_bBodyBGColorSet = true;
+ if( GPOS_NONE != pBrushItem->GetGraphicPos() )
+ m_bBodyBackgroundSet = true;
+ }
+
+ // Border and Padding
+ rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST );
+
+ // Some attributes must be set at the style, the ones which
+ // aren't inherited
+ SetPageDescAttrs( nullptr, &rItemSet );
+
+ // all remaining options can be set at the default style,
+ // then they're the default
+ if( SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,false) )
+ m_bBodyTextSet = true;
+ SetTextCollAttrs(
+ GetTextCollFromPool( RES_POOLCOLL_STANDARD ),
+ rItemSet, rPropInfo, this );
+
+ return;
+ }
+ break;
+ default: break;
+ }
+ }
+ else if( CSS1_SELTYPE_ELEM_CLASS==eSelType && HtmlTokenId::ANCHOR_ON==nToken2 &&
+ !pNext && aClass.getLength() >= 9 &&
+ ('s' == aClass[0] || 'S' == aClass[0]) )
+ {
+ sal_uInt16 nPoolFormatId = 0;
+ if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote_sym) )
+ nPoolFormatId = RES_POOLCHR_ENDNOTE;
+ else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote_sym) )
+ nPoolFormatId = RES_POOLCHR_FOOTNOTE;
+ if( nPoolFormatId )
+ {
+ if( Css1ScriptFlags::AllMask == nScript )
+ {
+ SetCharFormatAttrs( GetCharFormatFromPool(nPoolFormatId), rItemSet );
+ }
+ else
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ RemoveScriptItems( aScriptItemSet, nScript );
+ SetCharFormatAttrs( GetCharFormatFromPool(nPoolFormatId),
+ aScriptItemSet);
+ }
+ return;
+ }
+ }
+
+ // Now the selectors are processed which belong to a paragraph style
+ sal_uInt16 nPoolCollId = 0;
+ switch( nToken2 )
+ {
+ case HtmlTokenId::HEAD1_ON:
+ nPoolCollId = RES_POOLCOLL_HEADLINE1;
+ break;
+ case HtmlTokenId::HEAD2_ON:
+ nPoolCollId = RES_POOLCOLL_HEADLINE2;
+ break;
+ case HtmlTokenId::HEAD3_ON:
+ nPoolCollId = RES_POOLCOLL_HEADLINE3;
+ break;
+ case HtmlTokenId::HEAD4_ON:
+ nPoolCollId = RES_POOLCOLL_HEADLINE4;
+ break;
+ case HtmlTokenId::HEAD5_ON:
+ nPoolCollId = RES_POOLCOLL_HEADLINE5;
+ break;
+ case HtmlTokenId::HEAD6_ON:
+ nPoolCollId = RES_POOLCOLL_HEADLINE6;
+ break;
+ case HtmlTokenId::PARABREAK_ON:
+ if( aClass.getLength() >= 9 &&
+ ('s' == aClass[0] || 'S' == aClass[0]) )
+ {
+ if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote) )
+ nPoolCollId = RES_POOLCOLL_ENDNOTE;
+ else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote) )
+ nPoolCollId = RES_POOLCOLL_FOOTNOTE;
+
+ if( nPoolCollId )
+ aClass.clear();
+ else
+ nPoolCollId = RES_POOLCOLL_TEXT;
+ }
+ else
+ {
+ nPoolCollId = RES_POOLCOLL_TEXT;
+ }
+ break;
+ case HtmlTokenId::ADDRESS_ON:
+ nPoolCollId = RES_POOLCOLL_SEND_ADDRESS;
+ break;
+ case HtmlTokenId::BLOCKQUOTE_ON:
+ nPoolCollId = RES_POOLCOLL_HTML_BLOCKQUOTE;
+ break;
+ case HtmlTokenId::DT_ON:
+ nPoolCollId = RES_POOLCOLL_HTML_DT;
+ break;
+ case HtmlTokenId::DD_ON:
+ nPoolCollId = RES_POOLCOLL_HTML_DD;
+ break;
+ case HtmlTokenId::PREFORMTXT_ON:
+ nPoolCollId = RES_POOLCOLL_HTML_PRE;
+ break;
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ if( CSS1_SELTYPE_ELEMENT==eSelType && !pNext )
+ {
+ InsertTag( aToken2, rItemSet, rPropInfo );
+ return;
+ }
+ else if( CSS1_SELTYPE_ELEMENT==eSelType && pNext &&
+ (CSS1_SELTYPE_ELEMENT==eNextType ||
+ CSS1_SELTYPE_ELEM_CLASS==eNextType) )
+ {
+ // not TH and TD, but TH P and TD P
+ OUString aSubToken, aSubClass;
+ GetTokenAndClass( pNext, aSubToken, aSubClass, nScript );
+ if( HtmlTokenId::PARABREAK_ON == GetHTMLToken( aSubToken ) )
+ {
+ aClass = aSubClass;
+ pNext = pNext->GetNext();
+ eNextType = pNext ? pNext->GetType() : CSS1_SELTYPE_ELEMENT;
+
+ if( !aClass.isEmpty() || pNext )
+ {
+ nPoolCollId = static_cast< sal_uInt16 >(
+ HtmlTokenId::TABLEHEADER_ON == nToken2 ? RES_POOLCOLL_TABLE_HDLN
+ : RES_POOLCOLL_TABLE );
+ }
+ else
+ {
+ OUString sTmp = aToken2 + " " OOO_STRING_SVTOOLS_HTML_parabreak;
+
+ if( Css1ScriptFlags::AllMask == nScript )
+ {
+ InsertTag( sTmp, rItemSet, rPropInfo );
+ }
+ else
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ RemoveScriptItems( aScriptItemSet, nScript );
+ InsertTag( sTmp, aScriptItemSet, rPropInfo );
+ }
+
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ if( nPoolCollId )
+ {
+ if( !pNext ||
+ (CSS1_SELTYPE_PSEUDO==eNextType &&
+ pNext->GetString().equalsIgnoreAsciiCase( "first-letter" ) &&
+ SvxAdjust::Left == rPropInfo.m_eFloat) )
+ {
+ // either not a composed selector or a X:first-line { float: left; ... }
+
+ // search resp. create the style
+ SwTextFormatColl* pColl = GetTextFormatColl(nPoolCollId, OUString());
+ SwTextFormatColl* pParentColl = nullptr;
+ if( !aClass.isEmpty() )
+ {
+ OUString aName( pColl->GetName() );
+ AddClassName( aName, aClass );
+
+ pParentColl = pColl;
+ pColl = m_pDoc->FindTextFormatCollByName( aName );
+ if( !pColl )
+ pColl = m_pDoc->MakeTextFormatColl( aName, pParentColl );
+ }
+ if( !pNext )
+ {
+ // set only the attributes at the style
+ const SvxBoxItem *pBoxItem =
+ pColl->GetAttrSet().GetItemIfSet(RES_BOX);
+ rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST, pBoxItem );
+ if( Css1ScriptFlags::AllMask == nScript && !pParentColl )
+ {
+ SetTextCollAttrs( pColl, rItemSet, rPropInfo, this );
+ }
+ else
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ RemoveScriptItems( aScriptItemSet, nScript,
+ pParentColl ? &pParentColl->GetAttrSet() : nullptr );
+ SetTextCollAttrs( pColl, aScriptItemSet, rPropInfo, this );
+ }
+ }
+ else
+ {
+ // create a DropCap attribute
+ SwFormatDrop aDrop( pColl->GetDrop() );
+ aDrop.GetChars() = 1;
+
+ // set the attributes of the DropCap attribute
+ if( Css1ScriptFlags::AllMask == nScript )
+ {
+ OUString sName(pColl->GetName());
+ FillDropCap( aDrop, rItemSet, &sName );
+ }
+ else
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ if( Css1ScriptFlags::Western != nScript )
+ {
+ aScriptItemSet.ClearItem( RES_CHRATR_FONT );
+ aScriptItemSet.ClearItem( RES_CHRATR_LANGUAGE );
+ aScriptItemSet.ClearItem( RES_CHRATR_POSTURE );
+ aScriptItemSet.ClearItem( RES_CHRATR_WEIGHT );
+ }
+ if( Css1ScriptFlags::CJK != nScript )
+ {
+ aScriptItemSet.ClearItem( RES_CHRATR_CJK_FONT );
+ aScriptItemSet.ClearItem( RES_CHRATR_CJK_LANGUAGE );
+ aScriptItemSet.ClearItem( RES_CHRATR_CJK_POSTURE );
+ aScriptItemSet.ClearItem( RES_CHRATR_CJK_WEIGHT );
+ }
+ if( Css1ScriptFlags::CTL != nScript )
+ {
+ aScriptItemSet.ClearItem( RES_CHRATR_CTL_FONT );
+ aScriptItemSet.ClearItem( RES_CHRATR_CTL_LANGUAGE );
+ aScriptItemSet.ClearItem( RES_CHRATR_CTL_POSTURE );
+ aScriptItemSet.ClearItem( RES_CHRATR_CTL_WEIGHT );
+ }
+ OUString sName(pColl->GetName());
+ FillDropCap( aDrop, aScriptItemSet, &sName );
+ }
+
+ // Only set the attribute if "float: left" is specified and
+ // the Initial is over several lines. Otherwise the maybe
+ // created character style will be later searched and set
+ // via name.
+ if( aDrop.GetLines() > 1 &&
+ (SvxAdjust::Left == rPropInfo.m_eFloat ||
+ Css1ScriptFlags::AllMask == nScript) )
+ {
+ pColl->SetFormatAttr( aDrop );
+ }
+ }
+ }
+
+ return;
+ }
+
+ // Now the selectors are processed which are belonging to the character
+ // template. There are no composed ones here.
+ if( pNext )
+ return;
+
+ SwCharFormat* pCFormat = GetChrFormat(nToken2, OUString());
+ if( !pCFormat )
+ return;
+
+ SwCharFormat *pParentCFormat = nullptr;
+ if( !aClass.isEmpty() )
+ {
+ OUString aName( pCFormat->GetName() );
+ AddClassName( aName, aClass );
+ pParentCFormat = pCFormat;
+
+ pCFormat = m_pDoc->FindCharFormatByName( aName );
+ if( !pCFormat )
+ {
+ pCFormat = m_pDoc->MakeCharFormat( aName, pParentCFormat );
+ pCFormat->SetAuto(false);
+ }
+ }
+
+ if( Css1ScriptFlags::AllMask == nScript && !pParentCFormat )
+ {
+ SetCharFormatAttrs( pCFormat, rItemSet );
+ }
+ else
+ {
+ SfxItemSet aScriptItemSet( rItemSet );
+ RemoveScriptItems( aScriptItemSet, nScript,
+ pParentCFormat ? &pParentCFormat->GetAttrSet() : nullptr );
+ SetCharFormatAttrs( pCFormat, aScriptItemSet );
+ }
+}
+
+sal_uInt32 SwCSS1Parser::GetFontHeight( sal_uInt16 nSize ) const
+{
+ return m_aFontHeights[ std::min<sal_uInt16>(nSize,6) ];
+}
+
+const FontList *SwCSS1Parser::GetFontList() const
+{
+ const FontList *pFList = nullptr;
+ SwDocShell *pDocSh = m_pDoc->GetDocShell();
+ if( pDocSh )
+ {
+ const SvxFontListItem *pFListItem =
+ static_cast<const SvxFontListItem *>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
+ if( pFListItem )
+ pFList = pFListItem->GetFontList();
+ }
+
+ return pFList;
+}
+
+SwCharFormat* SwCSS1Parser::GetChrFormat( HtmlTokenId nToken2, const OUString& rClass ) const
+{
+ // search the corresponding style
+ sal_uInt16 nPoolId = 0;
+ const char* sName = nullptr;
+ switch( nToken2 )
+ {
+ case HtmlTokenId::EMPHASIS_ON: nPoolId = RES_POOLCHR_HTML_EMPHASIS; break;
+ case HtmlTokenId::CITATION_ON: nPoolId = RES_POOLCHR_HTML_CITATION; break;
+ case HtmlTokenId::STRONG_ON: nPoolId = RES_POOLCHR_HTML_STRONG; break;
+ case HtmlTokenId::CODE_ON: nPoolId = RES_POOLCHR_HTML_CODE; break;
+ case HtmlTokenId::SAMPLE_ON: nPoolId = RES_POOLCHR_HTML_SAMPLE; break;
+ case HtmlTokenId::KEYBOARD_ON: nPoolId = RES_POOLCHR_HTML_KEYBOARD; break;
+ case HtmlTokenId::VARIABLE_ON: nPoolId = RES_POOLCHR_HTML_VARIABLE; break;
+ case HtmlTokenId::DEFINSTANCE_ON: nPoolId = RES_POOLCHR_HTML_DEFINSTANCE; break;
+ case HtmlTokenId::TELETYPE_ON: nPoolId = RES_POOLCHR_HTML_TELETYPE; break;
+
+ case HtmlTokenId::SHORTQUOTE_ON: sName = OOO_STRING_SVTOOLS_HTML_shortquote; break;
+ case HtmlTokenId::LANGUAGE_ON: sName = OOO_STRING_SVTOOLS_HTML_language; break;
+ case HtmlTokenId::AUTHOR_ON: sName = OOO_STRING_SVTOOLS_HTML_author; break;
+ case HtmlTokenId::PERSON_ON: sName = OOO_STRING_SVTOOLS_HTML_person; break;
+ case HtmlTokenId::ACRONYM_ON: sName = OOO_STRING_SVTOOLS_HTML_acronym; break;
+ case HtmlTokenId::ABBREVIATION_ON: sName = OOO_STRING_SVTOOLS_HTML_abbreviation; break;
+ case HtmlTokenId::INSERTEDTEXT_ON: sName = OOO_STRING_SVTOOLS_HTML_insertedtext; break;
+ case HtmlTokenId::DELETEDTEXT_ON: sName = OOO_STRING_SVTOOLS_HTML_deletedtext; break;
+ default: break;
+ }
+
+ // search or create the style (only possible with name)
+ if( !nPoolId && !sName )
+ return nullptr;
+
+ // search or create style (without class)
+ SwCharFormat *pCFormat = nullptr;
+ if( nPoolId )
+ {
+ pCFormat = GetCharFormatFromPool( nPoolId );
+ }
+ else
+ {
+ OUString sCName( OUString::createFromAscii(sName) );
+ pCFormat = m_pDoc->FindCharFormatByName( sCName );
+ if( !pCFormat )
+ {
+ pCFormat = m_pDoc->MakeCharFormat( sCName, m_pDoc->GetDfltCharFormat() );
+ pCFormat->SetAuto(false);
+ }
+ }
+
+ OSL_ENSURE( pCFormat, "No character style???" );
+
+ // If a class exists, then search for the class style but don't
+ // create one.
+ OUString aClass( rClass );
+ GetScriptFromClass( aClass, false );
+ if( !aClass.isEmpty() )
+ {
+ OUString aTmp( pCFormat->GetName() );
+ AddClassName( aTmp, aClass );
+ SwCharFormat *pClassCFormat = m_pDoc->FindCharFormatByName( aTmp );
+ if( pClassCFormat )
+ {
+ pCFormat = pClassCFormat;
+ }
+ else
+ {
+ const SvxCSS1MapEntry *pClass = GetClass( aClass );
+ if( pClass )
+ {
+ pCFormat = m_pDoc->MakeCharFormat( aTmp, pCFormat );
+ pCFormat->SetAuto(false);
+ SfxItemSet aItemSet( pClass->GetItemSet() );
+ SetCharFormatAttrs( pCFormat, aItemSet );
+ }
+ }
+ }
+
+ return pCFormat;
+}
+
+SwTextFormatColl *SwCSS1Parser::GetTextCollFromPool( sal_uInt16 nPoolId ) const
+{
+ const SwTextFormatColls::size_type nOldArrLen = m_pDoc->GetTextFormatColls()->size();
+
+ SwTextFormatColl *pColl = m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId, false );
+
+ if( m_bIsNewDoc )
+ {
+ const SwTextFormatColls::size_type nArrLen = m_pDoc->GetTextFormatColls()->size();
+ for( SwTextFormatColls::size_type i=nOldArrLen; i<nArrLen; ++i )
+ lcl_swcss1_setEncoding( *(*m_pDoc->GetTextFormatColls())[i],
+ GetDfltEncoding() );
+ }
+
+ return pColl;
+}
+
+SwCharFormat *SwCSS1Parser::GetCharFormatFromPool( sal_uInt16 nPoolId ) const
+{
+ const SwCharFormats::size_type nOldArrLen = m_pDoc->GetCharFormats()->size();
+
+ SwCharFormat *pCharFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nPoolId );
+
+ if( m_bIsNewDoc )
+ {
+ const SwCharFormats::size_type nArrLen = m_pDoc->GetCharFormats()->size();
+
+ for( SwCharFormats::size_type i=nOldArrLen; i<nArrLen; i++ )
+ lcl_swcss1_setEncoding( *(*m_pDoc->GetCharFormats())[i],
+ GetDfltEncoding() );
+ }
+
+ return pCharFormat;
+}
+
+SwTextFormatColl *SwCSS1Parser::GetTextFormatColl( sal_uInt16 nTextColl,
+ const OUString& rClass )
+{
+ SwTextFormatColl* pColl = nullptr;
+
+ OUString aClass( rClass );
+ GetScriptFromClass( aClass, false );
+ if( RES_POOLCOLL_TEXT == nTextColl && aClass.getLength() >= 9 &&
+ ('s' == aClass[0] || 'S' == aClass[0] ) )
+ {
+ if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote) )
+ {
+ nTextColl = RES_POOLCOLL_ENDNOTE;
+ aClass.clear();
+ }
+ else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote) )
+ {
+ nTextColl = RES_POOLCOLL_FOOTNOTE;
+ aClass.clear();
+ }
+ }
+
+ if( USER_FMT & nTextColl ) // one created by Reader
+ {
+ OSL_ENSURE( false, "Where does the user style comes from?" );
+ pColl = GetTextCollFromPool( RES_POOLCOLL_STANDARD );
+ }
+ else
+ {
+ pColl = GetTextCollFromPool( nTextColl );
+ }
+
+ OSL_ENSURE( pColl, "No paragraph style???" );
+ if( !aClass.isEmpty() )
+ {
+ OUString aTmp( pColl->GetName() );
+ AddClassName( aTmp, aClass );
+ SwTextFormatColl* pClassColl = m_pDoc->FindTextFormatCollByName( aTmp );
+
+ if( !pClassColl &&
+ (nTextColl==RES_POOLCOLL_TABLE ||
+ nTextColl==RES_POOLCOLL_TABLE_HDLN) )
+ {
+ // In this case there was a <TD><P CLASS=foo>, but no TD.foo
+ // style was found. The we must use P.foo, if available.
+ SwTextFormatColl* pCollText =
+ GetTextCollFromPool( RES_POOLCOLL_TEXT );
+ aTmp = pCollText->GetName();
+ AddClassName( aTmp, aClass );
+ pClassColl = m_pDoc->FindTextFormatCollByName( aTmp );
+ }
+
+ if( pClassColl )
+ {
+ pColl = pClassColl;
+ }
+ else
+ {
+ const SvxCSS1MapEntry *pClass = GetClass( aClass );
+ if( pClass )
+ {
+ pColl = m_pDoc->MakeTextFormatColl( aTmp, pColl );
+ SfxItemSet aItemSet( pClass->GetItemSet() );
+ SvxCSS1PropertyInfo aPropInfo( pClass->GetPropertyInfo() );
+ aPropInfo.SetBoxItem( aItemSet, MIN_BORDER_DIST );
+ bool bPositioned = MayBePositioned( pClass->GetPropertyInfo() );
+ if( bPositioned )
+ aItemSet.ClearItem( RES_BACKGROUND );
+ SetTextCollAttrs( pColl, aItemSet, aPropInfo,
+ this );
+ }
+ }
+
+ }
+
+ if( pColl )
+ lcl_swcss1_setEncoding( *pColl, GetDfltEncoding() );
+
+ return pColl;
+}
+
+SwPageDesc *SwCSS1Parser::GetMasterPageDesc()
+{
+ return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );
+}
+
+static SwPageDesc *FindPageDesc(SwDoc *pDoc, sal_uInt16 nPoolId)
+{
+ size_t nPageDescs = pDoc->GetPageDescCnt();
+ size_t nPage;
+ for (nPage=0; nPage < nPageDescs &&
+ pDoc->GetPageDesc(nPage).GetPoolFormatId() != nPoolId; ++nPage)
+ ;
+
+ return nPage < nPageDescs ? &pDoc->GetPageDesc(nPage) : nullptr;
+}
+
+const SwPageDesc *SwCSS1Parser::GetPageDesc( sal_uInt16 nPoolId, bool bCreate )
+{
+ if( RES_POOLPAGE_HTML == nPoolId )
+ return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );
+
+ const SwPageDesc *pPageDesc = FindPageDesc(m_pDoc, nPoolId);
+ if( !pPageDesc && bCreate )
+ {
+ if (m_rHTMLParser.IsReadingHeaderOrFooter())
+ { // (there should be only one definition of header/footer in HTML)
+ SAL_WARN("sw.html", "no creating PageDesc while reading header/footer");
+ return nullptr;
+ }
+
+ // The first page is created from the right page, if there is one.
+ SwPageDesc *pMasterPageDesc = nullptr;
+ if( RES_POOLPAGE_FIRST == nPoolId )
+ pMasterPageDesc = FindPageDesc(m_pDoc, RES_POOLPAGE_RIGHT);
+ if( !pMasterPageDesc )
+ pMasterPageDesc = m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );
+
+ // The new page style is created by copying from master
+ SwPageDesc *pNewPageDesc = m_pDoc->
+ getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId, false );
+
+ // therefore we also need the number of the new style
+ OSL_ENSURE(pNewPageDesc == FindPageDesc(m_pDoc, nPoolId), "page style not found");
+
+ m_pDoc->CopyPageDesc( *pMasterPageDesc, *pNewPageDesc, false );
+
+ // Modify the styles for their new purpose.
+ const SwPageDesc *pFollow = nullptr;
+ bool bSetFollowFollow = false;
+ switch( nPoolId )
+ {
+ case RES_POOLPAGE_FIRST:
+ // If there is already a left page, then is it the follow-up
+ // style, else it is the HTML style.
+ pFollow = GetLeftPageDesc();
+ if( !pFollow )
+ pFollow = pMasterPageDesc;
+ break;
+
+ case RES_POOLPAGE_RIGHT:
+ // If the left style is already created, nothing will happen here.
+ // Otherwise the left style is created and ensures the link with
+ // the right style.
+ GetLeftPageDesc( true );
+ break;
+
+ case RES_POOLPAGE_LEFT:
+ // The right style is created if none exists. No links are created.
+ // If there is already a first page style, then the left style becomes
+ // follow-up style of the first page.
+ pFollow = GetRightPageDesc( true );
+ bSetFollowFollow = true;
+ {
+ const SwPageDesc *pFirstPageDesc = GetFirstPageDesc();
+ if( pFirstPageDesc )
+ {
+ SwPageDesc aNewFirstPageDesc( *pFirstPageDesc );
+ aNewFirstPageDesc.SetFollow( pNewPageDesc );
+ ChgPageDesc( pFirstPageDesc, aNewFirstPageDesc );
+ }
+ }
+ break;
+ }
+
+ if( pFollow )
+ {
+ SwPageDesc aNewPageDesc( *pNewPageDesc );
+ aNewPageDesc.SetFollow( pFollow );
+ ChgPageDesc( pNewPageDesc, aNewPageDesc );
+
+ if( bSetFollowFollow )
+ {
+ SwPageDesc aNewFollowPageDesc( *pFollow );
+ aNewFollowPageDesc.SetFollow( pNewPageDesc );
+ ChgPageDesc( pFollow, aNewFollowPageDesc );
+ }
+ }
+ pPageDesc = pNewPageDesc;
+ }
+
+ return pPageDesc;
+}
+
+bool SwCSS1Parser::MayBePositioned( const SvxCSS1PropertyInfo& rPropInfo,
+ bool bAutoWidth )
+{
+ if (!rPropInfo.m_bVisible)
+ {
+ // Don't create a textframe for this div if it's hidden.
+ return false;
+ }
+
+ // abs-pos
+ // left/top none auto twip perc
+
+ // none Z Z - -
+ // auto Z Z - -
+ // twip Z Z S/R -
+ // perc - - - -
+
+ // - the tag will be positioned absolutely and left/top are both
+ // present and don't contain a percentage value, or
+ // - the tag should flow, and
+ // - a width was specified (needed in both cases)
+ return ( ( SVX_CSS1_POS_ABSOLUTE == rPropInfo.m_ePosition &&
+ SVX_CSS1_LTYPE_PERCENTAGE != rPropInfo.m_eLeftType &&
+ SVX_CSS1_LTYPE_PERCENTAGE != rPropInfo.m_eTopType &&
+ (SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType ||
+ SVX_CSS1_LTYPE_TWIP != rPropInfo.m_eTopType) ) ||
+ ( SvxAdjust::End != rPropInfo.m_eFloat ) ) &&
+ ( bAutoWidth ||
+ SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eWidthType ||
+ SVX_CSS1_LTYPE_PERCENTAGE == rPropInfo.m_eWidthType );
+}
+
+void SwCSS1Parser::AddClassName( OUString& rFormatName, std::u16string_view rClass )
+{
+ OSL_ENSURE( !rClass.empty(), "Style class without length?" );
+
+ rFormatName += OUString::Concat(".") + rClass;
+}
+
+void SwCSS1Parser::FillDropCap( SwFormatDrop& rDrop,
+ SfxItemSet& rItemSet,
+ const OUString *pName )
+{
+ // the number of lines matches somehow a percentage value
+ // for the height (what happens with absolute heights???)
+ sal_uInt8 nLines = rDrop.GetLines();
+ if( const SvxFontHeightItem* pFontHeightItem = rItemSet.GetItemIfSet( RES_CHRATR_FONTSIZE, false ) )
+ {
+ sal_uInt16 nProp = pFontHeightItem->GetProp();
+ nLines = static_cast<sal_uInt8>((nProp + 50) / 100);
+ if( nLines < 1 )
+ nLines = 1;
+ else if( nLines > MAX_DROPCAP_LINES )
+ nLines = MAX_DROPCAP_LINES;
+
+ // Only when nLines>1, then the attribute also is set. Then
+ // we don't need the font height in the character style.
+ if( nLines > 1 )
+ {
+ rItemSet.ClearItem( RES_CHRATR_FONTSIZE );
+ rItemSet.ClearItem( RES_CHRATR_CJK_FONTSIZE );
+ rItemSet.ClearItem( RES_CHRATR_CTL_FONTSIZE );
+ }
+ }
+
+ // In case of hard attribution (pName==0) we can stop, if the Initial is
+ // only one line.
+ if( nLines<=1 )
+ return;
+
+ rDrop.GetLines() = nLines;
+
+ // a right border becomes the spacing to text!
+ if (const SvxRightMarginItem *const pRightMargin = rItemSet.GetItemIfSet(RES_MARGIN_RIGHT, false))
+ {
+ rDrop.GetDistance() = static_cast<sal_uInt16>(pRightMargin->GetRight());
+ rItemSet.ClearItem(RES_MARGIN_RIGHT);
+ }
+ rItemSet.ClearItem(RES_MARGIN_FIRSTLINE);
+ rItemSet.ClearItem(RES_MARGIN_TEXTLEFT);
+
+ // for every other attribute create a character style
+ if( !rItemSet.Count() )
+ return;
+
+ SwCharFormat *pCFormat = nullptr;
+ OUString aName;
+ if( pName )
+ {
+ aName = *pName + ".FL"; // first letter
+ pCFormat = m_pDoc->FindCharFormatByName( aName );
+ }
+ else
+ {
+ do
+ {
+ aName = "first-letter " + OUString::number( static_cast<sal_Int32>(++m_nDropCapCnt) );
+ }
+ while( m_pDoc->FindCharFormatByName(aName) );
+ }
+
+ if( !pCFormat )
+ {
+ pCFormat = m_pDoc->MakeCharFormat( aName, m_pDoc->GetDfltCharFormat() );
+ pCFormat->SetAuto(false);
+ }
+ SetCharFormatAttrs( pCFormat, rItemSet );
+
+ // The character style needs only be set in the attribute, when
+ // the attribute also is set.
+ if( nLines > 1 )
+ rDrop.SetCharFormat( pCFormat );
+}
+
+// specific CSS1 of SwHTMLParsers
+
+HTMLAttr **SwHTMLParser::GetAttrTabEntry( sal_uInt16 nWhich )
+{
+ // find the table entry of the item ...
+ HTMLAttr **ppAttr = nullptr;
+ switch( nWhich )
+ {
+ case RES_CHRATR_BLINK:
+ ppAttr = &m_xAttrTab->pBlink;
+ break;
+ case RES_CHRATR_CASEMAP:
+ ppAttr = &m_xAttrTab->pCaseMap;
+ break;
+ case RES_CHRATR_COLOR:
+ ppAttr = &m_xAttrTab->pFontColor;
+ break;
+ case RES_CHRATR_CROSSEDOUT:
+ ppAttr = &m_xAttrTab->pStrike;
+ break;
+ case RES_CHRATR_ESCAPEMENT:
+ ppAttr = &m_xAttrTab->pEscapement;
+ break;
+ case RES_CHRATR_FONT:
+ ppAttr = &m_xAttrTab->pFont;
+ break;
+ case RES_CHRATR_CJK_FONT:
+ ppAttr = &m_xAttrTab->pFontCJK;
+ break;
+ case RES_CHRATR_CTL_FONT:
+ ppAttr = &m_xAttrTab->pFontCTL;
+ break;
+ case RES_CHRATR_FONTSIZE:
+ ppAttr = &m_xAttrTab->pFontHeight;
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ ppAttr = &m_xAttrTab->pFontHeightCJK;
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ ppAttr = &m_xAttrTab->pFontHeightCTL;
+ break;
+ case RES_CHRATR_KERNING:
+ ppAttr = &m_xAttrTab->pKerning;
+ break;
+ case RES_CHRATR_POSTURE:
+ ppAttr = &m_xAttrTab->pItalic;
+ break;
+ case RES_CHRATR_CJK_POSTURE:
+ ppAttr = &m_xAttrTab->pItalicCJK;
+ break;
+ case RES_CHRATR_CTL_POSTURE:
+ ppAttr = &m_xAttrTab->pItalicCTL;
+ break;
+ case RES_CHRATR_UNDERLINE:
+ ppAttr = &m_xAttrTab->pUnderline;
+ break;
+ case RES_CHRATR_WEIGHT:
+ ppAttr = &m_xAttrTab->pBold;
+ break;
+ case RES_CHRATR_CJK_WEIGHT:
+ ppAttr = &m_xAttrTab->pBoldCJK;
+ break;
+ case RES_CHRATR_CTL_WEIGHT:
+ ppAttr = &m_xAttrTab->pBoldCTL;
+ break;
+ case RES_CHRATR_BACKGROUND:
+ ppAttr = &m_xAttrTab->pCharBrush;
+ break;
+ case RES_CHRATR_BOX:
+ ppAttr = &m_xAttrTab->pCharBox;
+ break;
+
+ case RES_PARATR_LINESPACING:
+ ppAttr = &m_xAttrTab->pLineSpacing;
+ break;
+ case RES_PARATR_ADJUST:
+ ppAttr = &m_xAttrTab->pAdjust;
+ break;
+
+ case RES_MARGIN_FIRSTLINE:
+ ppAttr = &m_xAttrTab->pFirstLineIndent;
+ break;
+ case RES_MARGIN_TEXTLEFT:
+ ppAttr = &m_xAttrTab->pTextLeftMargin;
+ break;
+ case RES_MARGIN_RIGHT:
+ ppAttr = &m_xAttrTab->pRightMargin;
+ break;
+ case RES_UL_SPACE:
+ ppAttr = &m_xAttrTab->pULSpace;
+ break;
+ case RES_BOX:
+ ppAttr = &m_xAttrTab->pBox;
+ break;
+ case RES_BACKGROUND:
+ ppAttr = &m_xAttrTab->pBrush;
+ break;
+ case RES_BREAK:
+ ppAttr = &m_xAttrTab->pBreak;
+ break;
+ case RES_PAGEDESC:
+ ppAttr = &m_xAttrTab->pPageDesc;
+ break;
+ case RES_PARATR_SPLIT:
+ ppAttr = &m_xAttrTab->pSplit;
+ break;
+ case RES_PARATR_WIDOWS:
+ ppAttr = &m_xAttrTab->pWidows;
+ break;
+ case RES_PARATR_ORPHANS:
+ ppAttr = &m_xAttrTab->pOrphans;
+ break;
+ case RES_KEEP:
+ ppAttr = &m_xAttrTab->pKeep;
+ break;
+
+ case RES_CHRATR_LANGUAGE:
+ ppAttr = &m_xAttrTab->pLanguage;
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ ppAttr = &m_xAttrTab->pLanguageCJK;
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ ppAttr = &m_xAttrTab->pLanguageCTL;
+ break;
+
+ case RES_FRAMEDIR:
+ ppAttr = &m_xAttrTab->pDirection;
+ break;
+ }
+
+ return ppAttr;
+}
+
+void SwHTMLParser::NewStyle()
+{
+ OUString sType;
+
+ const HTMLOptions& rOptions2 = GetOptions();
+ for (size_t i = rOptions2.size(); i; )
+ {
+ const HTMLOption& rOption = rOptions2[--i];
+ if( HtmlOptionId::TYPE == rOption.GetToken() )
+ sType = rOption.GetString();
+ }
+
+ m_bIgnoreRawData = sType.getLength() &&
+ !o3tl::equalsAscii(o3tl::getToken(sType, 0,';'), sCSS_mimetype);
+}
+
+void SwHTMLParser::EndStyle()
+{
+ m_bIgnoreRawData = false;
+
+ if( !m_aStyleSource.isEmpty() )
+ {
+ m_pCSS1Parser->ParseStyleSheet( m_aStyleSource );
+ m_aStyleSource.clear();
+ }
+}
+
+bool SwHTMLParser::FileDownload( const OUString& rURL,
+ OUString& rStr )
+{
+ // depose view (because of reschedule)
+ SwViewShell *pOldVSh = CallEndAction();
+
+ SfxMedium aDLMedium( rURL, StreamMode::READ | StreamMode::SHARE_DENYWRITE );
+
+ SvStream* pStream = aDLMedium.GetInStream();
+ if( pStream )
+ {
+ SvMemoryStream aStream;
+ aStream.WriteStream( *pStream );
+
+ rStr = OUString(static_cast<const char *>(aStream.GetData()), aStream.TellEnd(),
+ GetSrcEncoding());
+ }
+
+ // was aborted?
+ if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
+ || 1 == m_xDoc->getReferenceCount() )
+ {
+ // was the import aborted from SFX?
+ eState = SvParserState::Error;
+ pStream = nullptr;
+ }
+
+ // recreate View
+ SwViewShell *const pVSh = CallStartAction( pOldVSh );
+ OSL_ENSURE( pOldVSh == pVSh, "FileDownload: SwViewShell changed on us" );
+
+ return pStream!=nullptr;
+}
+
+void SwHTMLParser::InsertLink()
+{
+ bool bFinishDownload = false;
+ if( !m_vPendingStack.empty() )
+ {
+ OSL_ENSURE( ShouldFinishFileDownload(),
+ "Pending-Stack without File-Download?" );
+
+ m_vPendingStack.pop_back();
+ assert( m_vPendingStack.empty() && "Where does the Pending-Stack come from?" );
+
+ bFinishDownload = true;
+ }
+ else
+ {
+ OUString sRel, sHRef, sType;
+
+ const HTMLOptions& rOptions2 = GetOptions();
+ for (size_t i = rOptions2.size(); i; )
+ {
+ const HTMLOption& rOption = rOptions2[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::REL:
+ sRel = rOption.GetString();
+ break;
+ case HtmlOptionId::HREF:
+ sHRef = URIHelper::SmartRel2Abs( INetURLObject( m_sBaseURL ), rOption.GetString(), Link<OUString *, bool>(), false );
+ break;
+ case HtmlOptionId::TYPE:
+ sType = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ if( !sHRef.isEmpty() && sRel.equalsIgnoreAsciiCase( "STYLESHEET" ) &&
+ ( sType.isEmpty() ||
+ o3tl::equalsAscii(o3tl::getToken(sType, 0,';'), sCSS_mimetype) ) )
+ {
+ if( GetMedium() )
+ {
+ // start download of style source
+ StartFileDownload(sHRef);
+ if( IsParserWorking() )
+ {
+ // The style was loaded synchronously and we can call it directly.
+ bFinishDownload = true;
+ }
+ else
+ {
+ // The style was load asynchronously and is only available
+ // on the next continue call. Therefore we must create a
+ // Pending stack, so that we will return to here.
+ m_vPendingStack.emplace_back( HtmlTokenId::LINK );
+ }
+ }
+ else
+ {
+ // load file synchronous
+ OUString sSource;
+ if( FileDownload( sHRef, sSource ) )
+ m_pCSS1Parser->ParseStyleSheet( sSource );
+ }
+ }
+ }
+
+ if( bFinishDownload )
+ {
+ OUString sSource;
+ if( FinishFileDownload( sSource ) && !sSource.isEmpty() )
+ m_pCSS1Parser->ParseStyleSheet( sSource );
+ }
+}
+
+bool SwCSS1Parser::ParseStyleSheet( const OUString& rIn )
+{
+ if( !SvxCSS1Parser::ParseStyleSheet( rIn ) )
+ return false;
+
+ SwPageDesc *pMasterPageDesc =
+ m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );
+
+ SvxCSS1MapEntry* pPageEntry = GetPage(OUString(), false);
+ if( pPageEntry )
+ {
+ // @page (affects all already existing pages)
+
+ SetPageDescAttrs( pMasterPageDesc, pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+
+ // For all other already existing page styles the attributes
+ // must also be set
+
+ SetPageDescAttrs( GetFirstPageDesc(), pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+ SetPageDescAttrs( GetLeftPageDesc(), pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+ SetPageDescAttrs( GetRightPageDesc(), pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+
+ }
+
+ pPageEntry = GetPage( "first", true );
+ if( pPageEntry )
+ {
+ SetPageDescAttrs( GetFirstPageDesc(true), pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+ m_bSetFirstPageDesc = true;
+ }
+
+ pPageEntry = GetPage( "right", true );
+ if( pPageEntry )
+ {
+ SetPageDescAttrs( GetRightPageDesc(true), pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+ m_bSetRightPageDesc = true;
+ }
+
+ pPageEntry = GetPage( "left", true );
+ if( pPageEntry )
+ SetPageDescAttrs( GetLeftPageDesc(true), pPageEntry->GetItemSet(),
+ pPageEntry->GetPropertyInfo() );
+
+ return true;
+}
+
+bool SwHTMLParser::ParseStyleOptions( const OUString &rStyle,
+ const OUString &rId,
+ const OUString &rClass,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo,
+ const OUString *pLang,
+ const OUString *pDir )
+{
+ bool bRet = false;
+
+ if( !rClass.isEmpty() )
+ {
+ OUString aClass( rClass );
+ SwCSS1Parser::GetScriptFromClass( aClass );
+ const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass );
+ if( pClass )
+ {
+ SvxCSS1Parser::MergeStyles( pClass->GetItemSet(),
+ pClass->GetPropertyInfo(),
+ rItemSet, rPropInfo, false );
+ bRet = true;
+ }
+ }
+
+ if( !rId.isEmpty() )
+ {
+ const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId );
+ if( pId )
+ SvxCSS1Parser::MergeStyles( pId->GetItemSet(),
+ pId->GetPropertyInfo(),
+ rItemSet, rPropInfo, !rClass.isEmpty() );
+ rPropInfo.m_aId = rId;
+ bRet = true;
+ }
+
+ if( !rStyle.isEmpty() )
+ {
+ m_pCSS1Parser->ParseStyleOption( rStyle, rItemSet, rPropInfo );
+ bRet = true;
+ }
+
+ if( bRet )
+ rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST );
+
+ if( pLang && !pLang->isEmpty() )
+ {
+ LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( *pLang );
+ if( LANGUAGE_DONTKNOW != eLang )
+ {
+ SvxLanguageItem aLang( eLang, RES_CHRATR_LANGUAGE );
+ rItemSet.Put( aLang );
+ aLang.SetWhich( RES_CHRATR_CJK_LANGUAGE );
+ rItemSet.Put( aLang );
+ aLang.SetWhich( RES_CHRATR_CTL_LANGUAGE );
+ rItemSet.Put( aLang );
+
+ bRet = true;
+ }
+ }
+ if( pDir && !pDir->isEmpty() )
+ {
+ OUString aValue( *pDir );
+ SvxFrameDirection eDir = SvxFrameDirection::Environment;
+ if (aValue.equalsIgnoreAsciiCase("LTR"))
+ eDir = SvxFrameDirection::Horizontal_LR_TB;
+ else if (aValue.equalsIgnoreAsciiCase("RTL"))
+ eDir = SvxFrameDirection::Horizontal_RL_TB;
+
+ if( SvxFrameDirection::Environment != eDir )
+ {
+ SvxFrameDirectionItem aDir( eDir, RES_FRAMEDIR );
+ rItemSet.Put( aDir );
+
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+void SwHTMLParser::SetAnchorAndAdjustment( const SvxCSS1PropertyInfo &rPropInfo,
+ SfxItemSet &rFrameItemSet )
+{
+ SwFormatAnchor aAnchor;
+
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+ sal_Int16 eVertOri = text::VertOrientation::NONE;
+ sal_Int16 eHoriRel = text::RelOrientation::FRAME;
+ sal_Int16 eVertRel = text::RelOrientation::FRAME;
+ SwTwips nHoriPos = 0, nVertPos = 0;
+ css::text::WrapTextMode eSurround = css::text::WrapTextMode_THROUGH;
+ if( SVX_CSS1_POS_ABSOLUTE == rPropInfo.m_ePosition )
+ {
+ if( SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType &&
+ SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eTopType )
+ {
+ // Absolute positioned objects are page-bound, when they
+ // aren't in a frame and otherwise frame-bound.
+ const SwStartNode *pFlySttNd =
+ m_pPam->GetPoint()->GetNode().FindFlyStartNode();
+ if( pFlySttNd )
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_FLY );
+ SwPosition aPos( *pFlySttNd );
+ aAnchor.SetAnchor( &aPos );
+ }
+ else
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_PAGE );
+ aAnchor.SetPageNum( 1 );
+ }
+ nHoriPos = rPropInfo.m_nLeft;
+ nVertPos = rPropInfo.m_nTop;
+ }
+ else
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_PARA );
+ aAnchor.SetAnchor( m_pPam->GetPoint() );
+ eVertOri = text::VertOrientation::TOP;
+ eVertRel = text::RelOrientation::CHAR;
+ if( SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType )
+ {
+ eHoriOri = text::HoriOrientation::NONE;
+ eHoriRel = text::RelOrientation::PAGE_FRAME;
+ nHoriPos = rPropInfo.m_nLeft;
+ }
+ else
+ {
+ eHoriOri = text::HoriOrientation::LEFT;
+ eHoriRel = text::RelOrientation::FRAME; // to be changed later
+ }
+ }
+ }
+ else
+ {
+ // Flowing object are inserted as paragraph-bound, when the paragraph is
+ // still empty and otherwise auto-bound.
+ // Auto-bound frames for the time being inserted at the previous position
+ // and later moved.
+ const sal_Int32 nContent = m_pPam->GetPoint()->GetContentIndex();
+ if( nContent )
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_CHAR );
+ m_pPam->Move( fnMoveBackward );
+ eVertOri = text::VertOrientation::CHAR_BOTTOM;
+ eVertRel = text::RelOrientation::CHAR;
+ }
+ else
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_PARA );
+ eVertOri = text::VertOrientation::TOP;
+ eVertRel = text::RelOrientation::PRINT_AREA;
+ }
+
+ aAnchor.SetAnchor( m_pPam->GetPoint() );
+
+ if( nContent )
+ m_pPam->Move( fnMoveForward );
+
+ sal_uInt16 nLeftSpace = 0, nRightSpace = 0;
+ short nIndent = 0;
+ GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent );
+
+ if( SvxAdjust::Right==rPropInfo.m_eFloat )
+ {
+ eHoriOri = text::HoriOrientation::RIGHT;
+ eHoriRel = nRightSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME;
+ eSurround = css::text::WrapTextMode_LEFT;
+ }
+ else
+ {
+ eHoriOri = text::HoriOrientation::LEFT;
+ eHoriRel = nLeftSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME;
+ eSurround = css::text::WrapTextMode_RIGHT;
+ }
+ }
+ rFrameItemSet.Put( aAnchor );
+
+ // positioned absolutely with wrap
+ rFrameItemSet.Put( SwFormatHoriOrient( nHoriPos, eHoriOri, eHoriRel ) );
+ rFrameItemSet.Put( SwFormatVertOrient( nVertPos, eVertOri, eVertRel ) );
+ rFrameItemSet.Put( SwFormatSurround( eSurround ) );
+}
+
+void SwHTMLParser::SetVarSize( SvxCSS1PropertyInfo const &rPropInfo,
+ SfxItemSet &rFrameItemSet,
+ SwTwips nDfltWidth, sal_uInt8 nDfltPrcWidth )
+{
+ SwTwips nWidth = nDfltWidth, nHeight = MINFLY;
+ sal_uInt8 nPercentWidth = nDfltPrcWidth, nPercentHeight = 0;
+ switch( rPropInfo.m_eWidthType )
+ {
+ case SVX_CSS1_LTYPE_PERCENTAGE:
+ nPercentWidth = rPropInfo.m_nWidth > 0 ? static_cast<sal_uInt8>(rPropInfo.m_nWidth) : 1;
+ nWidth = MINFLY;
+ break;
+ case SVX_CSS1_LTYPE_TWIP:
+ nWidth = std::max<tools::Long>(rPropInfo.m_nWidth, MINFLY);
+ nPercentWidth = 0;
+ break;
+ default:
+ ;
+ }
+ switch( rPropInfo.m_eHeightType )
+ {
+ case SVX_CSS1_LTYPE_PERCENTAGE:
+ nPercentHeight = rPropInfo.m_nHeight > 0 ? static_cast<sal_uInt8>(rPropInfo.m_nHeight) : 1;
+ break;
+ case SVX_CSS1_LTYPE_TWIP:
+ // Netscape and MS-IE interpreting the height incorrectly as minimum height,
+ // therefore we are doing the same.
+ nHeight = std::max<tools::Long>(rPropInfo.m_nHeight, MINFLY);
+ break;
+ default:
+ ;
+ }
+
+ SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, nWidth, nHeight );
+ aFrameSize.SetWidthPercent( nPercentWidth );
+ aFrameSize.SetHeightPercent( nPercentHeight );
+ rFrameItemSet.Put( aFrameSize );
+}
+
+void SwHTMLParser::SetFrameFormatAttrs( SfxItemSet &rItemSet,
+ HtmlFrameFormatFlags nFlags,
+ SfxItemSet &rFrameItemSet )
+{
+ const SvxBoxItem *pBoxItem;
+ if( (nFlags & HtmlFrameFormatFlags::Box) &&
+ (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
+ {
+ if( nFlags & HtmlFrameFormatFlags::Padding )
+ {
+ SvxBoxItem aBoxItem( *pBoxItem );
+ // reset all 4 sides to 0
+ aBoxItem.SetAllDistances(0);
+ rFrameItemSet.Put( aBoxItem );
+ }
+ else
+ {
+ rFrameItemSet.Put( *pBoxItem );
+ }
+ rItemSet.ClearItem( RES_BOX );
+ }
+
+ const SvxBrushItem* pBrushItem;
+ if( (nFlags & HtmlFrameFormatFlags::Background) &&
+ (pBrushItem = rItemSet.GetItemIfSet( RES_BACKGROUND )) )
+ {
+ rFrameItemSet.Put( *pBrushItem );
+ rItemSet.ClearItem( RES_BACKGROUND );
+ }
+
+ const SvxFrameDirectionItem* pFrameDirectionItem;
+ if( (nFlags & HtmlFrameFormatFlags::Direction) &&
+ (pFrameDirectionItem = rItemSet.GetItemIfSet( RES_FRAMEDIR )) )
+ {
+ rFrameItemSet.Put( *pFrameDirectionItem );
+ rItemSet.ClearItem( RES_FRAMEDIR );
+ }
+}
+
+std::unique_ptr<HTMLAttrContext> SwHTMLParser::PopContext( HtmlTokenId nToken )
+{
+ std::unique_ptr<HTMLAttrContext> xCntxt;
+
+ HTMLAttrContexts::size_type nPos = m_aContexts.size();
+ if( nPos <= m_nContextStMin )
+ return nullptr;
+
+ bool bFound = HtmlTokenId::NONE == nToken;
+ if( nToken != HtmlTokenId::NONE )
+ {
+ // search for stack entry of token ...
+ while( nPos > m_nContextStMin )
+ {
+ HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
+ if( nCntxtToken == nToken )
+ {
+ bFound = true;
+ break;
+ }
+ else if( nCntxtToken == HtmlTokenId::NONE ) // zero as token doesn't occur
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ nPos--;
+ }
+
+ if( bFound )
+ {
+ xCntxt = std::move(m_aContexts[nPos]);
+ m_aContexts.erase( m_aContexts.begin() + nPos );
+ }
+
+ return xCntxt;
+}
+
+void SwHTMLParser::GetMarginsFromContext( sal_uInt16& nLeft,
+ sal_uInt16& nRight,
+ short& nIndent,
+ bool bIgnoreTopContext ) const
+{
+ HTMLAttrContexts::size_type nPos = m_aContexts.size();
+ if( bIgnoreTopContext )
+ {
+ if( !nPos )
+ return;
+ else
+ nPos--;
+ }
+
+ while( nPos > m_nContextStAttrMin )
+ {
+ const HTMLAttrContext *pCntxt = m_aContexts[--nPos].get();
+ if( pCntxt->IsLRSpaceChanged() )
+ {
+ pCntxt->GetMargins( nLeft, nRight, nIndent );
+ return;
+ }
+ }
+}
+
+void SwHTMLParser::GetMarginsFromContextWithNumberBullet( sal_uInt16& nLeft,
+ sal_uInt16& nRight,
+ short& nIndent ) const
+{
+ GetMarginsFromContext( nLeft, nRight, nIndent );
+ const SwHTMLNumRuleInfo& rInfo = const_cast<SwHTMLParser*>(this)->GetNumInfo();
+ if( rInfo.GetDepth() )
+ {
+ sal_uInt8 nLevel = static_cast<sal_uInt8>( (rInfo.GetDepth() <= MAXLEVEL ? rInfo.GetDepth()
+ : MAXLEVEL) - 1 );
+ const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get(nLevel);
+ nLeft = nLeft + rNumFormat.GetAbsLSpace(); //TODO: overflow
+ nIndent = rNumFormat.GetFirstLineOffset(); //TODO: overflow
+ }
+}
+
+void SwHTMLParser::GetULSpaceFromContext( sal_uInt16& nUpper,
+ sal_uInt16& nLower ) const
+{
+ sal_uInt16 nDefaultColl = 0;
+ OUString aDefaultClass;
+
+ HTMLAttrContexts::size_type nPos = m_aContexts.size();
+ while( nPos > m_nContextStAttrMin )
+ {
+ const HTMLAttrContext *pCntxt = m_aContexts[--nPos].get();
+ if( pCntxt->IsULSpaceChanged() )
+ {
+ pCntxt->GetULSpace( nUpper, nLower );
+ return;
+ }
+ else if (!nDefaultColl)
+ {
+ nDefaultColl = pCntxt->GetDefaultTextFormatColl();
+ if (nDefaultColl)
+ aDefaultClass = pCntxt->GetClass();
+ }
+ }
+
+ if (!nDefaultColl)
+ nDefaultColl = RES_POOLCOLL_TEXT;
+
+ const SwTextFormatColl *pColl =
+ m_pCSS1Parser->GetTextFormatColl(nDefaultColl, aDefaultClass);
+ const SvxULSpaceItem& rULSpace = pColl->GetULSpace();
+ nUpper = rULSpace.GetUpper();
+ nLower = rULSpace.GetLower();
+}
+
+void SwHTMLParser::EndContextAttrs( HTMLAttrContext *pContext )
+{
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ for( auto pAttr : rAttrs )
+ {
+ if( RES_PARATR_DROP==pAttr->GetItem().Which() )
+ {
+ // Set the number of characters for DropCaps. If it's zero at the
+ // end, the attribute is set to invalid and then isn't set from SetAttr.
+ sal_Int32 nChars = m_pPam->GetPoint()->GetContentIndex();
+ if( nChars < 1 )
+ pAttr->Invalidate();
+ else if( nChars > MAX_DROPCAP_CHARS )
+ nChars = MAX_DROPCAP_CHARS;
+ static_cast<SwFormatDrop&>(pAttr->GetItem()).GetChars() = static_cast<sal_uInt8>(nChars);
+ }
+
+ EndAttr( pAttr );
+ }
+}
+
+void SwHTMLParser::InsertParaAttrs( const SfxItemSet& rItemSet )
+{
+ SfxItemIter aIter( rItemSet );
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ // search for the table entry of the item...
+ sal_uInt16 nWhich = pItem->Which();
+ HTMLAttr **ppAttr = GetAttrTabEntry( nWhich );
+
+ if( ppAttr )
+ {
+ NewAttr(m_xAttrTab, ppAttr, *pItem);
+ if( RES_PARATR_BEGIN > nWhich )
+ (*ppAttr)->SetLikePara();
+ m_aParaAttrs.push_back( *ppAttr );
+ bool bSuccess = EndAttr( *ppAttr, false );
+ if (!bSuccess)
+ m_aParaAttrs.pop_back();
+ }
+ }
+}
+
+static void lcl_swcss1_setEncoding( SwFormat& rFormat, rtl_TextEncoding eEnc )
+{
+ if( RTL_TEXTENCODING_DONTKNOW == eEnc )
+ return;
+
+ const SfxItemSet& rItemSet = rFormat.GetAttrSet();
+ static const TypedWhichId<SvxFontItem> aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CTL_FONT };
+ for (auto const & i : aWhichIds)
+ {
+ const SvxFontItem *pFontItem = rItemSet.GetItemIfSet(i, false);
+ if (!pFontItem)
+ continue;
+ if (RTL_TEXTENCODING_SYMBOL == pFontItem->GetCharSet())
+ continue;
+ if (eEnc == pFontItem->GetCharSet())
+ continue;
+ SvxFontItem aFont(pFontItem->GetFamily(), pFontItem->GetFamilyName(),
+ pFontItem->GetStyleName(), pFontItem->GetPitch(),
+ eEnc, i);
+ rFormat.SetFormatAttr(aFont);
+ }
+}
+
+void SwCSS1Parser::SetDfltEncoding( rtl_TextEncoding eEnc )
+{
+ if( eEnc == GetDfltEncoding() )
+ return;
+
+ if( m_bIsNewDoc )
+ {
+ // Set new encoding as pool default
+ static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CTL_FONT };
+ for(sal_uInt16 i : aWhichIds)
+ {
+ const SvxFontItem& rDfltFont =
+ static_cast<const SvxFontItem&>(m_pDoc->GetDefault( i));
+ SvxFontItem aFont( rDfltFont.GetFamily(),
+ rDfltFont.GetFamilyName(),
+ rDfltFont.GetStyleName(),
+ rDfltFont.GetPitch(),
+ eEnc, i );
+ m_pDoc->SetDefault( aFont );
+ }
+
+ // Change all paragraph styles that do specify a font.
+ for( auto pTextFormatColl : *m_pDoc->GetTextFormatColls() )
+ lcl_swcss1_setEncoding( *pTextFormatColl, eEnc );
+
+ // Change all character styles that do specify a font.
+ for( auto pCharFormat : *m_pDoc->GetCharFormats() )
+ lcl_swcss1_setEncoding( *pCharFormat, eEnc );
+ }
+
+ SvxCSS1Parser::SetDfltEncoding( eEnc );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlctxt.cxx b/sw/source/filter/html/htmlctxt.cxx
new file mode 100644
index 0000000000..80245ba2ea
--- /dev/null
+++ b/sw/source/filter/html/htmlctxt.cxx
@@ -0,0 +1,806 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svtools/htmltokn.h>
+#include <editeng/boxitem.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <doc.hxx>
+#include <pam.hxx>
+#include <shellio.hxx>
+#include <paratr.hxx>
+#include "htmlnum.hxx"
+#include "swcss1.hxx"
+#include "swhtml.hxx"
+
+#include <memory>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+class HTMLAttrContext_SaveDoc
+{
+ SwHTMLNumRuleInfo m_aNumRuleInfo; // Numbering for this environment
+ std::unique_ptr<SwPosition>
+ m_pPos; // Jump back to here when leaving context
+ std::shared_ptr<HTMLAttrTable>
+ m_xAttrTab; // Valid attributes for the environment,
+ // if attributes shouldn't be preserved
+
+ size_t m_nContextStMin; // Stack lower bound for the environment
+ // if stack needs to be protected
+ size_t m_nContextStAttrMin; // Stack lower bound for the environment
+ // if the attributes shouldn't be preserved
+ bool m_bStripTrailingPara : 1;
+ bool m_bKeepNumRules : 1;
+ bool m_bFixHeaderDist : 1;
+ bool m_bFixFooterDist : 1;
+
+public:
+
+ HTMLAttrContext_SaveDoc() :
+ m_nContextStMin( SIZE_MAX ), m_nContextStAttrMin( SIZE_MAX ),
+ m_bStripTrailingPara( false ), m_bKeepNumRules( false ),
+ m_bFixHeaderDist( false ), m_bFixFooterDist( false )
+ {}
+
+ // The position is ours, so we need to create and delete it
+ void SetPos( const SwPosition& rPos ) { m_pPos.reset( new SwPosition(rPos) ); }
+ const SwPosition *GetPos() const { return m_pPos.get(); }
+
+ // The index isn't ours. So no creation or deletion
+ void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { m_aNumRuleInfo.Set(rInf); }
+ const SwHTMLNumRuleInfo& GetNumInfo() const { return m_aNumRuleInfo; }
+
+ std::shared_ptr<HTMLAttrTable> const & GetAttrTab(bool bCreate = false);
+
+ void SetContextStMin( size_t nMin ) { m_nContextStMin = nMin; }
+ size_t GetContextStMin() const { return m_nContextStMin; }
+
+ void SetContextStAttrMin( size_t nMin ) { m_nContextStAttrMin = nMin; }
+ size_t GetContextStAttrMin() const { return m_nContextStAttrMin; }
+
+ void SetStripTrailingPara( bool bSet ) { m_bStripTrailingPara = bSet; }
+ bool GetStripTrailingPara() const { return m_bStripTrailingPara; }
+
+ void SetKeepNumRules( bool bSet ) { m_bKeepNumRules = bSet; }
+ bool GetKeepNumRules() const { return m_bKeepNumRules; }
+
+ void SetFixHeaderDist( bool bSet ) { m_bFixHeaderDist = bSet; }
+ bool GetFixHeaderDist() const { return m_bFixHeaderDist; }
+
+ void SetFixFooterDist( bool bSet ) { m_bFixFooterDist = bSet; }
+ bool GetFixFooterDist() const { return m_bFixFooterDist; }
+};
+
+std::shared_ptr<HTMLAttrTable> const & HTMLAttrContext_SaveDoc::GetAttrTab( bool bCreate )
+{
+ if (!m_xAttrTab && bCreate)
+ {
+ m_xAttrTab = std::make_shared<HTMLAttrTable>();
+ memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable));
+ }
+ return m_xAttrTab;
+}
+
+HTMLAttrContext_SaveDoc *HTMLAttrContext::GetSaveDocContext( bool bCreate )
+{
+ if( !m_pSaveDocContext && bCreate )
+ m_pSaveDocContext.reset(new HTMLAttrContext_SaveDoc);
+
+ return m_pSaveDocContext.get();
+}
+
+HTMLAttrContext::HTMLAttrContext( HtmlTokenId nTokn, sal_uInt16 nPoolId, OUString aClass,
+ bool bDfltColl ) :
+ m_aClass(std::move( aClass )),
+ m_nToken( nTokn ),
+ m_nTextFormatColl( nPoolId ),
+ m_nLeftMargin( 0 ),
+ m_nRightMargin( 0 ),
+ m_nFirstLineIndent( 0 ),
+ m_nUpperSpace( 0 ),
+ m_nLowerSpace( 0 ),
+ m_eAppend( AM_NONE ),
+ m_bLRSpaceChanged( false ),
+ m_bULSpaceChanged( false ),
+ m_bDefaultTextFormatColl( bDfltColl ),
+ m_bSpansSection( false ),
+ m_bPopStack( false ),
+ m_bFinishPREListingXMP( false ),
+ m_bRestartPRE( false ),
+ m_bRestartXMP( false ),
+ m_bRestartListing( false ),
+ m_bHeaderOrFooter( false )
+{}
+
+HTMLAttrContext::HTMLAttrContext( HtmlTokenId nTokn ) :
+ m_nToken( nTokn ),
+ m_nTextFormatColl( 0 ),
+ m_nLeftMargin( 0 ),
+ m_nRightMargin( 0 ),
+ m_nFirstLineIndent( 0 ),
+ m_nUpperSpace( 0 ),
+ m_nLowerSpace( 0 ),
+ m_eAppend( AM_NONE ),
+ m_bLRSpaceChanged( false ),
+ m_bULSpaceChanged( false ),
+ m_bDefaultTextFormatColl( false ),
+ m_bSpansSection( false ),
+ m_bPopStack( false ),
+ m_bFinishPREListingXMP( false ),
+ m_bRestartPRE( false ),
+ m_bRestartXMP( false ),
+ m_bRestartListing( false ),
+ m_bHeaderOrFooter( false )
+{}
+
+HTMLAttrContext::~HTMLAttrContext()
+{
+ m_pSaveDocContext.reset();
+}
+
+void HTMLAttrContext::ClearSaveDocContext()
+{
+ m_pSaveDocContext.reset();
+}
+
+void SwHTMLParser::SplitAttrTab( const SwPosition& rNewPos )
+{
+ // preliminary paragraph attributes are not allowed here, they could
+ // be set here and then the pointers become invalid!
+ OSL_ENSURE(m_aParaAttrs.empty(),
+ "Danger: there are non-final paragraph attributes");
+ m_aParaAttrs.clear();
+
+ const SwPosition* pOldEndPara = m_pPam->GetPoint();
+#ifndef NDEBUG
+ auto const nOld(pOldEndPara->GetNodeIndex());
+#endif
+ sal_Int32 nOldEndCnt = m_pPam->GetPoint()->GetContentIndex();
+
+ const SwPosition& rNewSttPara = rNewPos;
+ sal_Int32 nNewSttCnt = rNewPos.GetContentIndex();
+
+ bool bMoveBack = false;
+
+ // close all open attributes and re-open them after the table
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
+ {
+ HTMLAttr *pAttr = *pHTMLAttributes;
+ while( pAttr )
+ {
+ HTMLAttr *pNext = pAttr->GetNext();
+ HTMLAttr *pPrev = pAttr->GetPrev();
+
+ sal_uInt16 nWhich = pAttr->m_pItem->Which();
+ if( !nOldEndCnt && RES_PARATR_BEGIN <= nWhich &&
+ pAttr->GetStartParagraphIdx() < pOldEndPara->GetNodeIndex() )
+ {
+ // The attribute needs to be closed one content position beforehand
+ if( !bMoveBack )
+ {
+ bMoveBack = m_pPam->Move( fnMoveBackward );
+ nOldEndCnt = m_pPam->GetPoint()->GetContentIndex();
+ }
+ }
+ else if( bMoveBack )
+ {
+ m_pPam->Move( fnMoveForward );
+ nOldEndCnt = m_pPam->GetPoint()->GetContentIndex();
+ bMoveBack = false;
+ }
+
+ if( (RES_PARATR_BEGIN <= nWhich && bMoveBack) ||
+ pAttr->GetStartParagraphIdx() < pOldEndPara->GetNodeIndex() ||
+ (pAttr->GetStartParagraph() == pOldEndPara->GetNode() &&
+ pAttr->GetStartContent() != nOldEndCnt) )
+ {
+ // The attribute needs to be set. Because we still need the original, since
+ // pointers to the attribute still exists in the contexts, we need to clone it.
+ // The next-list gets lost but the previous-list is preserved
+ HTMLAttr *pSetAttr = pAttr->Clone( pOldEndPara->GetNode(), nOldEndCnt );
+
+ if( pNext )
+ pNext->InsertPrev( pSetAttr );
+ else
+ {
+ if (pSetAttr->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pSetAttr );
+ else
+ m_aSetAttrTab.push_back( pSetAttr );
+ }
+ }
+ else if( pPrev )
+ {
+ // The previous attributes still need to be set, even if the current attribute
+ // doesn't need to be set before the table
+ if( pNext )
+ pNext->InsertPrev( pPrev );
+ else
+ {
+ if (pPrev->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pPrev );
+ else
+ m_aSetAttrTab.push_back( pPrev );
+ }
+ }
+
+ // Set the start of the attribute
+ pAttr->m_nStartPara = rNewSttPara.GetNode();
+ pAttr->m_nEndPara = rNewSttPara.GetNode();
+ pAttr->m_nStartContent = nNewSttCnt;
+ pAttr->m_nEndContent = nNewSttCnt;
+ pAttr->m_pPrev = nullptr;
+
+ pAttr = pNext;
+ }
+ }
+
+ if( bMoveBack )
+ m_pPam->Move( fnMoveForward );
+
+ assert(m_pPam->GetPoint()->GetNodeIndex() == nOld);
+}
+
+void SwHTMLParser::SaveDocContext( HTMLAttrContext *pCntxt,
+ HtmlContextFlags nFlags,
+ const SwPosition *pNewPos )
+{
+ HTMLAttrContext_SaveDoc *pSave = pCntxt->GetSaveDocContext( true );
+ pSave->SetStripTrailingPara( bool(HtmlContextFlags::StripPara & nFlags) );
+ pSave->SetKeepNumRules( bool(HtmlContextFlags::KeepNumrule & nFlags) );
+ pSave->SetFixHeaderDist( bool(HtmlContextFlags::HeaderDist & nFlags) );
+ pSave->SetFixFooterDist( bool(HtmlContextFlags::FooterDist & nFlags) );
+
+ if( pNewPos )
+ {
+ // If the PaM needs to be set to a different position, we need to preserve numbering
+ if( !pSave->GetKeepNumRules() )
+ {
+ // Numbering shall not be preserved. So we need to preserve the current state
+ // and turn off numbering afterwards
+ pSave->SetNumInfo( GetNumInfo() );
+ GetNumInfo().Clear();
+ }
+
+ if( HtmlContextFlags::KeepAttrs & nFlags )
+ {
+ // Close attribute on current position and start on new one
+ SplitAttrTab( *pNewPos );
+ }
+ else
+ {
+ std::shared_ptr<HTMLAttrTable> xSaveAttrTab = pSave->GetAttrTab(true);
+ SaveAttrTab(xSaveAttrTab);
+ }
+
+ pSave->SetPos( *m_pPam->GetPoint() );
+ *m_pPam->GetPoint() = *pNewPos;
+ }
+
+ // Settings nContextStMin automatically means, that no
+ // currently open lists (DL/OL/UL) can be closed
+ if( HtmlContextFlags::ProtectStack & nFlags )
+ {
+ pSave->SetContextStMin( m_nContextStMin );
+ m_nContextStMin = m_aContexts.size();
+
+ if( HtmlContextFlags::KeepAttrs & nFlags )
+ {
+ pSave->SetContextStAttrMin( m_nContextStAttrMin );
+ m_nContextStAttrMin = m_aContexts.size();
+ }
+ }
+}
+
+void SwHTMLParser::RestoreDocContext( HTMLAttrContext *pCntxt )
+{
+ HTMLAttrContext_SaveDoc *pSave = pCntxt->GetSaveDocContext();
+ if( !pSave )
+ return;
+
+ if( pSave->GetStripTrailingPara() )
+ StripTrailingPara();
+
+ if( pSave->GetPos() )
+ {
+ if( pSave->GetFixHeaderDist() || pSave->GetFixFooterDist() )
+ FixHeaderFooterDistance( pSave->GetFixHeaderDist(),
+ pSave->GetPos() );
+
+ std::shared_ptr<HTMLAttrTable> xSaveAttrTab = pSave->GetAttrTab();
+ if (!xSaveAttrTab)
+ {
+ // Close attribute on current position and start on the old one
+ SplitAttrTab( *pSave->GetPos() );
+ }
+ else
+ {
+ RestoreAttrTab(xSaveAttrTab);
+ }
+
+ *m_pPam->GetPoint() = *pSave->GetPos();
+
+ // We can already set the attributes so far
+ SetAttr();
+ }
+
+ if( SIZE_MAX != pSave->GetContextStMin() )
+ {
+ m_nContextStMin = pSave->GetContextStMin();
+ if( SIZE_MAX != pSave->GetContextStAttrMin() )
+ m_nContextStAttrMin = pSave->GetContextStAttrMin();
+ }
+
+ if( !pSave->GetKeepNumRules() )
+ {
+ // Set the preserved numbering back
+ GetNumInfo().Set( pSave->GetNumInfo() );
+ }
+
+ pCntxt->ClearSaveDocContext();
+}
+
+void SwHTMLParser::EndContext( HTMLAttrContext *pContext )
+{
+ if( pContext->GetPopStack() )
+ {
+ // Close all still open contexts. Our own context needs to be deleted already!
+ while( m_aContexts.size() > m_nContextStMin )
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ OSL_ENSURE(xCntxt.get() != pContext,
+ "Context still on the stack" );
+ if (xCntxt.get() == pContext)
+ break;
+
+ EndContext(xCntxt.get());
+ }
+ }
+
+ // Close all still open attributes
+ if( pContext->HasAttrs() )
+ EndContextAttrs( pContext );
+
+ // If a section has been opened, end it. Since sections can be part of absolute-positioned
+ // objects, this needs to be done before restoring document context
+ if( pContext->GetSpansSection() )
+ EndSection();
+
+ // Leave borders and other special sections
+ if( pContext->HasSaveDocContext() )
+ RestoreDocContext( pContext );
+
+ // Add a paragraph break if needed
+ if( AM_NONE != pContext->GetAppendMode() &&
+ m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( pContext->GetAppendMode() );
+
+ // Restart PRE, LISTING and XMP environments
+ if( pContext->IsFinishPREListingXMP() )
+ FinishPREListingXMP();
+
+ if( pContext->IsRestartPRE() )
+ StartPRE();
+
+ if( pContext->IsRestartXMP() )
+ StartXMP();
+
+ if( pContext->IsRestartListing() )
+ StartListing();
+}
+
+void SwHTMLParser::ClearContext( HTMLAttrContext *pContext )
+{
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ for( auto pAttr : rAttrs )
+ {
+ // Simple deletion doesn't to the job, since the attribute
+ // needs to be deregistered with its list.
+ // In theory, you could delete the list and its attributes separately
+ // but if you get that wrong, quite a lot is messed up
+ DeleteAttr( pAttr );
+ }
+ rAttrs.clear();
+
+ OSL_ENSURE( !pContext->GetSpansSection(),
+ "Area can no longer be exited" );
+
+ OSL_ENSURE( !pContext->HasSaveDocContext(),
+ "Frame can no longer be exited" );
+
+ // like RestoreDocContext reset enough of this to not catastrophically
+ // fail if we still have a SaveDocContext here
+ if (HTMLAttrContext_SaveDoc *pSave = pContext->GetSaveDocContext())
+ {
+ if (SIZE_MAX != pSave->GetContextStMin())
+ {
+ m_nContextStMin = pSave->GetContextStMin();
+ if (SIZE_MAX != pSave->GetContextStAttrMin())
+ m_nContextStAttrMin = pSave->GetContextStAttrMin();
+ }
+
+ pContext->ClearSaveDocContext();
+ }
+
+ // Restart PRE/LISTING/XMP environments
+ if( pContext->IsFinishPREListingXMP() )
+ FinishPREListingXMP();
+
+ if( pContext->IsRestartPRE() )
+ StartPRE();
+
+ if( pContext->IsRestartXMP() )
+ StartXMP();
+
+ if( pContext->IsRestartListing() )
+ StartListing();
+}
+
+bool SwHTMLParser::DoPositioning( SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo,
+ HTMLAttrContext *pContext )
+{
+ bool bRet = false;
+
+ // A border is opened on the following conditions
+ // - the tag is absolute-positioned AND left/top are both known AND don't contain a % property
+ // OR
+ // - the tag should be floating AND
+ // - there's a given width
+ if( SwCSS1Parser::MayBePositioned( rPropInfo ) )
+ {
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameItemSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs(aFrameItemSet );
+
+ SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE, rPropInfo,
+ aFrameItemSet );
+
+ SetVarSize( rPropInfo, aFrameItemSet );
+
+ SetSpace( Size(0,0), rItemSet, rPropInfo, aFrameItemSet );
+
+ SetFrameFormatAttrs( rItemSet,
+ HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Padding|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Direction,
+ aFrameItemSet );
+
+ InsertFlyFrame(aFrameItemSet, pContext, rPropInfo.m_aId);
+ pContext->SetPopStack( true );
+ rPropInfo.m_aId.clear();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool SwHTMLParser::CreateContainer( std::u16string_view rClass,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo,
+ HTMLAttrContext *pContext )
+{
+ bool bRet = false;
+ if( o3tl::equalsIgnoreAsciiCase( rClass, u"sd-abs-pos" ) &&
+ SwCSS1Parser::MayBePositioned( rPropInfo ) )
+ {
+ // Container class
+ SfxItemSet *pFrameItemSet = pContext->GetFrameItemSet( m_xDoc.get() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( *pFrameItemSet );
+
+ SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE,
+ rPropInfo, *pFrameItemSet );
+ Size aDummy(0,0);
+ SetFixSize( aDummy, aDummy, false, false, rPropInfo, *pFrameItemSet );
+ SetSpace( aDummy, rItemSet, rPropInfo, *pFrameItemSet );
+ SetFrameFormatAttrs( rItemSet, HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Direction,
+ *pFrameItemSet );
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void SwHTMLParser::InsertAttrs( SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo const &rPropInfo,
+ HTMLAttrContext *pContext,
+ bool bCharLvl )
+{
+ // Put together a DropCap attribute, if a "float:left" is before the first character
+ if( bCharLvl && !m_pPam->GetPoint()->GetContentIndex() &&
+ SvxAdjust::Left == rPropInfo.m_eFloat )
+ {
+ SwFormatDrop aDrop;
+ aDrop.GetChars() = 1;
+
+ m_pCSS1Parser->FillDropCap( aDrop, rItemSet );
+
+ // We only set the DropCap attribute if the initial spans multiple lines
+ if( aDrop.GetLines() > 1 )
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pDropCap, aDrop);
+
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ rAttrs.push_back( m_xAttrTab->pDropCap );
+
+ return;
+ }
+ }
+
+ if( !bCharLvl )
+ m_pCSS1Parser->SetFormatBreak( rItemSet, rPropInfo );
+
+ OSL_ENSURE(m_aContexts.size() <= m_nContextStAttrMin ||
+ m_aContexts.back().get() != pContext,
+ "SwHTMLParser::InsertAttrs: Context already on the Stack");
+
+ SfxItemIter aIter( rItemSet );
+
+ const SvxFirstLineIndentItem * pFirstLineItem(nullptr);
+ const SvxTextLeftMarginItem * pTextLeftMargin(nullptr);
+ const SvxRightMarginItem * pRightMargin(nullptr);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ switch( pItem->Which() )
+ {
+ case RES_MARGIN_FIRSTLINE:
+ {
+ pFirstLineItem = static_cast<const SvxFirstLineIndentItem*>(pItem);
+ }
+ break;
+ case RES_MARGIN_TEXTLEFT:
+ {
+ pTextLeftMargin = static_cast<const SvxTextLeftMarginItem*>(pItem);
+ }
+ break;
+ case RES_MARGIN_RIGHT:
+ {
+ pRightMargin = static_cast<const SvxRightMarginItem*>(pItem);
+ }
+ break;
+ }
+ }
+
+#if 1
+ {
+ // Paragraph indents need to be added and are generated for each paragraphs
+ // (here for the first paragraph only, all the following in SetTextCollAttrs)
+
+ // Get old paragraph indents without the top context (that's the one we're editing)
+ sal_uInt16 nOldLeft = 0, nOldRight = 0;
+ short nOldIndent = 0;
+ bool bIgnoreTop = m_aContexts.size() > m_nContextStMin &&
+ m_aContexts.back().get() == pContext;
+ GetMarginsFromContext( nOldLeft, nOldRight, nOldIndent,
+ bIgnoreTop );
+
+ // ... and the currently valid ones
+ sal_uInt16 nLeft = nOldLeft, nRight = nOldRight;
+ short nIndent = nOldIndent;
+ pContext->GetMargins( nLeft, nRight, nIndent );
+
+ // ... and add the new indents to the old ones
+ // Here, we don't get the ones from the item but the separately remembered ones,
+ // since they could be negative. Accessing those via the item still works, since
+ // the item (with value 0) will be added
+ if( rPropInfo.m_bLeftMargin )
+ {
+ OSL_ENSURE( rPropInfo.m_nLeftMargin < 0 ||
+ !pTextLeftMargin ||
+ rPropInfo.m_nLeftMargin == pTextLeftMargin->GetTextLeft(),
+ "left margin does not match with item" );
+ if( rPropInfo.m_nLeftMargin < 0 &&
+ -rPropInfo.m_nLeftMargin > nOldLeft )
+ nLeft = 0;
+ else
+ nLeft = nOldLeft + static_cast< sal_uInt16 >(rPropInfo.m_nLeftMargin);
+ }
+ if( rPropInfo.m_bRightMargin )
+ {
+ OSL_ENSURE( rPropInfo.m_nRightMargin < 0 ||
+ !pRightMargin ||
+ rPropInfo.m_nRightMargin == pRightMargin->GetRight(),
+ "right margin does not match with item" );
+ if( rPropInfo.m_nRightMargin < 0 &&
+ -rPropInfo.m_nRightMargin > nOldRight )
+ nRight = 0;
+ else
+ nRight = nOldRight + static_cast< sal_uInt16 >(rPropInfo.m_nRightMargin);
+ }
+ if (rPropInfo.m_bTextIndent && pFirstLineItem)
+ nIndent = pFirstLineItem->GetTextFirstLineOffset();
+
+ // Remember the value for the following paragraphs
+ pContext->SetMargins( nLeft, nRight, nIndent );
+
+ // Set the attribute on the current paragraph
+ SvxFirstLineIndentItem const firstLine(nIndent, RES_MARGIN_FIRSTLINE);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pFirstLineIndent, firstLine);
+ EndAttr(m_xAttrTab->pFirstLineIndent, false);
+ SvxTextLeftMarginItem const leftMargin(nLeft, RES_MARGIN_TEXTLEFT);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pTextLeftMargin, leftMargin);
+ EndAttr(m_xAttrTab->pTextLeftMargin, false);
+ SvxRightMarginItem const rightMargin(nRight, RES_MARGIN_RIGHT);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pRightMargin, rightMargin);
+ EndAttr(m_xAttrTab->pRightMargin, false);
+ }
+#endif
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ HTMLAttr **ppAttr = nullptr;
+
+ switch( pItem->Which() )
+ {
+
+ case RES_UL_SPACE:
+ if( !rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin )
+ {
+ sal_uInt16 nUpper = 0, nLower = 0;
+ GetULSpaceFromContext( nUpper, nLower );
+ SvxULSpaceItem aULSpace( *static_cast<const SvxULSpaceItem *>(pItem) );
+ if( !rPropInfo.m_bTopMargin )
+ aULSpace.SetUpper( nUpper );
+ if( !rPropInfo.m_bBottomMargin )
+ aULSpace.SetLower( nLower );
+
+ NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, aULSpace);
+
+ // save context information
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ rAttrs.push_back( m_xAttrTab->pULSpace );
+
+ pContext->SetULSpace( aULSpace.GetUpper(), aULSpace.GetLower() );
+ }
+ else
+ {
+ ppAttr = &m_xAttrTab->pULSpace;
+ }
+ break;
+ case RES_CHRATR_FONTSIZE:
+ // don't set attributes with a % property
+ if( static_cast<const SvxFontHeightItem *>(pItem)->GetProp() == 100 )
+ ppAttr = &m_xAttrTab->pFontHeight;
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ // don't set attributes with a % property
+ if( static_cast<const SvxFontHeightItem *>(pItem)->GetProp() == 100 )
+ ppAttr = &m_xAttrTab->pFontHeightCJK;
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ // don't set attributes with a % property
+ if( static_cast<const SvxFontHeightItem *>(pItem)->GetProp() == 100 )
+ ppAttr = &m_xAttrTab->pFontHeightCTL;
+ break;
+
+ case RES_BACKGROUND:
+ if( bCharLvl )
+ {
+ // Convert the Frame attribute to a Char attribute (if needed)
+ SvxBrushItem aBrushItem( *static_cast<const SvxBrushItem *>(pItem) );
+ aBrushItem.SetWhich( RES_CHRATR_BACKGROUND );
+
+ // Set the attribute
+ NewAttr(m_xAttrTab, &m_xAttrTab->pCharBrush, aBrushItem);
+
+ // and save context information
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ rAttrs.push_back( m_xAttrTab->pCharBrush );
+ }
+ else if( pContext->GetToken() != HtmlTokenId::TABLEHEADER_ON &&
+ pContext->GetToken() != HtmlTokenId::TABLEDATA_ON )
+ {
+ ppAttr = &m_xAttrTab->pBrush;
+ }
+ break;
+
+ case RES_BOX:
+ if( bCharLvl )
+ {
+ SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem *>(pItem) );
+ aBoxItem.SetWhich( RES_CHRATR_BOX );
+
+ NewAttr(m_xAttrTab, &m_xAttrTab->pCharBox, aBoxItem);
+
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ rAttrs.push_back( m_xAttrTab->pCharBox );
+ }
+ else
+ {
+ ppAttr = &m_xAttrTab->pBox;
+ }
+ break;
+
+ default:
+ ppAttr = GetAttrTabEntry( pItem->Which() );
+ break;
+ }
+
+ if( ppAttr )
+ {
+ // Set the attribute
+ NewAttr(m_xAttrTab, ppAttr, *pItem);
+
+ // and save context information
+ HTMLAttrs &rAttrs = pContext->GetAttrs();
+ rAttrs.push_back( *ppAttr );
+ }
+ }
+
+ if( !rPropInfo.m_aId.isEmpty() )
+ InsertBookmark( rPropInfo.m_aId );
+}
+
+void SwHTMLParser::InsertAttr( HTMLAttr **ppAttr, const SfxPoolItem & rItem,
+ HTMLAttrContext *pCntxt )
+{
+ if( !ppAttr )
+ {
+ ppAttr = GetAttrTabEntry( rItem.Which() );
+ if( !ppAttr )
+ return;
+ }
+
+ // Set the attribute
+ NewAttr(m_xAttrTab, ppAttr, rItem);
+
+ // save context information
+ HTMLAttrs &rAttrs = pCntxt->GetAttrs();
+ rAttrs.push_back( *ppAttr );
+}
+
+void SwHTMLParser::SplitPREListingXMP( HTMLAttrContext *pCntxt )
+{
+ // PRE/Listing/XMP need to be finished when finishing context
+ pCntxt->SetFinishPREListingXMP( true );
+
+ // And set all now valid flags
+ if( IsReadPRE() )
+ pCntxt->SetRestartPRE( true );
+ if( IsReadXMP() )
+ pCntxt->SetRestartXMP( true );
+ if( IsReadListing() )
+ pCntxt->SetRestartListing( true );
+
+ FinishPREListingXMP();
+}
+
+SfxItemSet *HTMLAttrContext::GetFrameItemSet( SwDoc *pCreateDoc )
+{
+ if( !m_pFrameItemSet && pCreateDoc )
+ m_pFrameItemSet = std::make_unique<SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1>>
+ ( pCreateDoc->GetAttrPool() );
+ return m_pFrameItemSet.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmldrawreader.cxx b/sw/source/filter/html/htmldrawreader.cxx
new file mode 100644
index 0000000000..fdbc187348
--- /dev/null
+++ b/sw/source/filter/html/htmldrawreader.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtayitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <svx/sdtaiitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <svl/itemiter.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <osl/diagnose.h>
+
+#include <charatr.hxx>
+#include <drawdoc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <poolfmt.hxx>
+#include "swcss1.hxx"
+#include "swhtml.hxx"
+#include <shellio.hxx>
+
+using namespace css;
+
+HTMLOptionEnum<SdrTextAniKind> const aHTMLMarqBehaviorTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_BEHAV_scroll, SdrTextAniKind::Scroll },
+ { OOO_STRING_SVTOOLS_HTML_BEHAV_alternate, SdrTextAniKind::Alternate },
+ { OOO_STRING_SVTOOLS_HTML_BEHAV_slide, SdrTextAniKind::Slide },
+ { nullptr, SdrTextAniKind(0) }
+};
+
+HTMLOptionEnum<SdrTextAniDirection> const aHTMLMarqDirectionTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_AL_left, SdrTextAniDirection::Left },
+ { OOO_STRING_SVTOOLS_HTML_AL_right, SdrTextAniDirection::Right },
+ { nullptr, SdrTextAniDirection(0) }
+};
+
+void SwHTMLParser::InsertDrawObject( SdrObject* pNewDrawObj,
+ const Size& rPixSpace,
+ sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ SfxItemSet& rCSS1ItemSet,
+ SvxCSS1PropertyInfo& rCSS1PropInfo )
+{
+ // always on top of text.
+ // but in invisible layer. <ConnectToLayout> will move the object
+ // to the visible layer.
+ pNewDrawObj->SetLayer( m_xDoc->getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( aFrameSet );
+
+ sal_uInt16 nLeftSpace = 0, nRightSpace = 0, nUpperSpace = 0, nLowerSpace = 0;
+ if( rPixSpace.Width() || rPixSpace.Height() )
+ {
+ nLeftSpace = nRightSpace = o3tl::convert(rPixSpace.Width(), o3tl::Length::px, o3tl::Length::twip);
+ nUpperSpace = nLowerSpace = o3tl::convert(rPixSpace.Height(), o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ // set left/right border
+ // note: parser never creates SvxLeftMarginItem! must be converted
+ if (const SvxTextLeftMarginItem *const pLeft = rCSS1ItemSet.GetItemIfSet(RES_MARGIN_TEXTLEFT))
+ {
+ if( rCSS1PropInfo.m_bLeftMargin )
+ {
+ // should be SvxLeftMarginItem... "cast" it
+ nLeftSpace = static_cast<sal_uInt16>(pLeft->GetTextLeft());
+ rCSS1PropInfo.m_bLeftMargin = false;
+ }
+ rCSS1ItemSet.ClearItem(RES_MARGIN_TEXTLEFT);
+ }
+ if (const SvxRightMarginItem *const pRight = rCSS1ItemSet.GetItemIfSet(RES_MARGIN_RIGHT))
+ {
+ if( rCSS1PropInfo.m_bRightMargin )
+ {
+ nRightSpace = static_cast< sal_uInt16 >(pRight->GetRight());
+ rCSS1PropInfo.m_bRightMargin = false;
+ }
+ rCSS1ItemSet.ClearItem(RES_MARGIN_RIGHT);
+ }
+ if( nLeftSpace || nRightSpace )
+ {
+ SvxLRSpaceItem aLRItem( RES_LR_SPACE );
+ aLRItem.SetLeft( nLeftSpace );
+ aLRItem.SetRight( nRightSpace );
+ aFrameSet.Put( aLRItem );
+ }
+
+ // set top/bottom border
+ if( const SvxULSpaceItem* pULItem = rCSS1ItemSet.GetItemIfSet( RES_UL_SPACE ) )
+ {
+ // maybe flatten the first line indentation
+ if( rCSS1PropInfo.m_bTopMargin )
+ {
+ nUpperSpace = pULItem->GetUpper();
+ rCSS1PropInfo.m_bTopMargin = false;
+ }
+ if( rCSS1PropInfo.m_bBottomMargin )
+ {
+ nLowerSpace = pULItem->GetLower();
+ rCSS1PropInfo.m_bBottomMargin = false;
+ }
+
+ rCSS1ItemSet.ClearItem( RES_UL_SPACE );
+ }
+ if( nUpperSpace || nLowerSpace )
+ {
+ SvxULSpaceItem aULItem( RES_UL_SPACE );
+ aULItem.SetUpper( nUpperSpace );
+ aULItem.SetLower( nLowerSpace );
+ aFrameSet.Put( aULItem );
+ }
+
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR );
+ if( SVX_CSS1_POS_ABSOLUTE == rCSS1PropInfo.m_ePosition &&
+ SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eLeftType &&
+ SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eTopType )
+ {
+ const SwStartNode *pFlySttNd =
+ m_pPam->GetPoint()->GetNode().FindFlyStartNode();
+
+ if( pFlySttNd )
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_FLY );
+ SwPosition aPos( *pFlySttNd );
+ aAnchor.SetAnchor( &aPos );
+ }
+ else
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_PAGE );
+ }
+ // #i26791# - direct positioning for <SwDoc::Insert(..)>
+ pNewDrawObj->SetRelativePos( Point(rCSS1PropInfo.m_nLeft + nLeftSpace,
+ rCSS1PropInfo.m_nTop + nUpperSpace) );
+ aFrameSet.Put( SwFormatSurround(css::text::WrapTextMode_THROUGH) );
+ }
+ else if( SvxAdjust::Left == rCSS1PropInfo.m_eFloat ||
+ text::HoriOrientation::LEFT == eHoriOri )
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_PARA );
+ aFrameSet.Put( SwFormatSurround(css::text::WrapTextMode_RIGHT) );
+ // #i26791# - direct positioning for <SwDoc::Insert(..)>
+ pNewDrawObj->SetRelativePos( Point(nLeftSpace, nUpperSpace) );
+ }
+ else if( text::VertOrientation::NONE != eVertOri )
+ {
+ aFrameSet.Put( SwFormatVertOrient( 0, eVertOri ) );
+ }
+
+ if (RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId())
+ {
+ aAnchor.SetPageNum( 1 );
+ }
+ else if( RndStdIds::FLY_AT_FLY != aAnchor.GetAnchorId() )
+ {
+ aAnchor.SetAnchor( m_pPam->GetPoint() );
+ }
+ aFrameSet.Put( aAnchor );
+
+ m_xDoc->getIDocumentContentOperations().InsertDrawObj( *m_pPam, *pNewDrawObj, aFrameSet );
+}
+
+static void PutEEPoolItem( SfxItemSet &rEEItemSet,
+ const SfxPoolItem& rSwItem )
+{
+
+ sal_uInt16 nEEWhich = 0;
+
+ switch( rSwItem.Which() )
+ {
+ case RES_CHRATR_COLOR: nEEWhich = EE_CHAR_COLOR; break;
+ case RES_CHRATR_CROSSEDOUT: nEEWhich = EE_CHAR_STRIKEOUT; break;
+ case RES_CHRATR_ESCAPEMENT: nEEWhich = EE_CHAR_ESCAPEMENT; break;
+ case RES_CHRATR_FONT: nEEWhich = EE_CHAR_FONTINFO; break;
+ case RES_CHRATR_CJK_FONT: nEEWhich = EE_CHAR_FONTINFO_CJK; break;
+ case RES_CHRATR_CTL_FONT: nEEWhich = EE_CHAR_FONTINFO_CTL; break;
+ case RES_CHRATR_FONTSIZE: nEEWhich = EE_CHAR_FONTHEIGHT; break;
+ case RES_CHRATR_CJK_FONTSIZE: nEEWhich = EE_CHAR_FONTHEIGHT_CJK; break;
+ case RES_CHRATR_CTL_FONTSIZE: nEEWhich = EE_CHAR_FONTHEIGHT_CTL; break;
+ case RES_CHRATR_KERNING: nEEWhich = EE_CHAR_KERNING; break;
+ case RES_CHRATR_POSTURE: nEEWhich = EE_CHAR_ITALIC; break;
+ case RES_CHRATR_CJK_POSTURE: nEEWhich = EE_CHAR_ITALIC_CJK; break;
+ case RES_CHRATR_CTL_POSTURE: nEEWhich = EE_CHAR_ITALIC_CTL; break;
+ case RES_CHRATR_UNDERLINE: nEEWhich = EE_CHAR_UNDERLINE; break;
+ case RES_CHRATR_WEIGHT: nEEWhich = EE_CHAR_WEIGHT; break;
+ case RES_CHRATR_CJK_WEIGHT: nEEWhich = EE_CHAR_WEIGHT_CJK; break;
+ case RES_CHRATR_CTL_WEIGHT: nEEWhich = EE_CHAR_WEIGHT_CTL; break;
+ case RES_BACKGROUND:
+ case RES_CHRATR_BACKGROUND:
+ {
+ const SvxBrushItem& rBrushItem = static_cast<const SvxBrushItem&>(rSwItem);
+ rEEItemSet.Put( XFillStyleItem(drawing::FillStyle_SOLID) );
+ rEEItemSet.Put(XFillColorItem(OUString(),
+ rBrushItem.GetColor()) );
+ }
+ break;
+ }
+
+ if( nEEWhich )
+ rEEItemSet.Put( rSwItem.CloneSetWhich(nEEWhich) );
+}
+
+void SwHTMLParser::NewMarquee( HTMLTable *pCurTable )
+{
+
+ OSL_ENSURE( !m_pMarquee, "Marquee in Marquee???" );
+ m_aContents.clear();
+
+ OUString aId, aStyle, aClass;
+
+ tools::Long nWidth=0, nHeight=0;
+ bool bPercentWidth = false, bDirection = false, bBGColor = false;
+ Size aSpace( 0, 0 );
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+ SdrTextAniKind eAniKind = SdrTextAniKind::Scroll;
+ SdrTextAniDirection eAniDir = SdrTextAniDirection::Left;
+ sal_uInt16 nCount = 0, nDelay = 60;
+ sal_Int16 nAmount = -6;
+ Color aBGColor;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (const auto & rOption : rHTMLOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+
+ case HtmlOptionId::BEHAVIOR:
+ eAniKind = rOption.GetEnum( aHTMLMarqBehaviorTable, eAniKind );
+ break;
+
+ case HtmlOptionId::BGCOLOR:
+ rOption.GetColor( aBGColor );
+ bBGColor = true;
+ break;
+
+ case HtmlOptionId::DIRECTION:
+ eAniDir = rOption.GetEnum( aHTMLMarqDirectionTable, eAniDir );
+ bDirection = true;
+ break;
+
+ case HtmlOptionId::LOOP:
+ if (rOption.GetString().
+ equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_LOOP_infinite))
+ {
+ nCount = 0;
+ }
+ else
+ {
+ const sal_Int32 nLoop = rOption.GetSNumber();
+ nCount = std::max<sal_Int32>(nLoop, 0);
+ }
+ break;
+
+ case HtmlOptionId::SCROLLAMOUNT:
+ nAmount = - static_cast<sal_Int16>(rOption.GetNumber());
+ break;
+
+ case HtmlOptionId::SCROLLDELAY:
+ nDelay = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+
+ case HtmlOptionId::WIDTH:
+ // first only save as pixel value!
+ nWidth = rOption.GetNumber();
+ bPercentWidth = rOption.GetString().indexOf('%') != -1;
+ if( bPercentWidth && nWidth>100 )
+ nWidth = 100;
+ break;
+
+ case HtmlOptionId::HEIGHT:
+ // first only save as pixel value!
+ nHeight = rOption.GetNumber();
+ if( rOption.GetString().indexOf('%') != -1 )
+ nHeight = 0;
+ break;
+
+ case HtmlOptionId::HSPACE:
+ // first only save as pixel value!
+ aSpace.setHeight( rOption.GetNumber() );
+ break;
+
+ case HtmlOptionId::VSPACE:
+ // first only save as pixel value!
+ aSpace.setWidth( rOption.GetNumber() );
+ break;
+
+ case HtmlOptionId::ALIGN:
+ eVertOri =
+ rOption.GetEnum( aHTMLImgVAlignTable,
+ text::VertOrientation::TOP );
+ eHoriOri =
+ rOption.GetEnum( aHTMLImgHAlignTable );
+ break;
+ default: break;
+ }
+ }
+
+ // create a DrawTextobj
+ // #i52858# - method name changed
+ SwDrawModel* pModel = m_xDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ SdrPage* pPg = pModel->GetPage( 0 );
+ m_pMarquee = static_cast<SdrTextObj*>(SdrObjFactory::MakeNewObject(
+ *pModel,
+ SdrInventor::Default,
+ SdrObjKind::Text).get());
+
+ if( !m_pMarquee )
+ return;
+
+ pPg->InsertObject( m_pMarquee.get() );
+
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+
+ // (only) Alternate runs from left to right as default
+ if( SdrTextAniKind::Alternate==eAniKind && !bDirection )
+ eAniDir = SdrTextAniDirection::Right;
+
+ // re set the attributes needed for scrolling
+ SfxItemSetFixed<
+ XATTR_FILL_FIRST, XATTR_FILL_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ EE_CHAR_START, EE_CHAR_END>
+ aItemSet( pModel->GetItemPool() );
+ aItemSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
+ aItemSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
+ aItemSet.Put( SdrTextAniKindItem( eAniKind ) );
+ aItemSet.Put( SdrTextAniDirectionItem( eAniDir ) );
+ aItemSet.Put( SdrTextAniCountItem( nCount ) );
+ aItemSet.Put( SdrTextAniDelayItem( nDelay ) );
+ aItemSet.Put( SdrTextAniAmountItem( nAmount ) );
+ if( SdrTextAniKind::Alternate==eAniKind )
+ {
+ // (only) Alternate starts and ends Inside as default
+ aItemSet.Put( SdrTextAniStartInsideItem(true) );
+ aItemSet.Put( SdrTextAniStopInsideItem(true) );
+ if( SdrTextAniDirection::Left==eAniDir )
+ aItemSet.Put( SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT) );
+ }
+
+ // set the default colour (from the default template), so that a meaningful
+ // colour is set at all
+ const Color& rDfltColor =
+ m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD )
+ ->GetColor().GetValue();
+ aItemSet.Put( SvxColorItem( rDfltColor, EE_CHAR_COLOR ) );
+
+ // set the attributes of the current paragraph style
+ sal_uInt16 nWhichIds[] =
+ {
+ RES_CHRATR_COLOR, RES_CHRATR_CROSSEDOUT, RES_CHRATR_ESCAPEMENT,
+ RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, RES_CHRATR_KERNING,
+ RES_CHRATR_POSTURE, RES_CHRATR_UNDERLINE, RES_CHRATR_WEIGHT,
+ RES_CHRATR_BACKGROUND,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT,
+ RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE,
+ RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT,
+ 0
+ };
+ SwTextNode const*const pTextNd =
+ m_pPam->GetPoint()->GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ const SfxItemSet& rItemSet = pTextNd->GetAnyFormatColl().GetAttrSet();
+ const SfxPoolItem *pItem;
+ for( int i=0; nWhichIds[i]; ++i )
+ {
+ if( SfxItemState::SET == rItemSet.GetItemState( nWhichIds[i], true, &pItem ) )
+ PutEEPoolItem( aItemSet, *pItem );
+ }
+ }
+
+ // set attribute of environment at the Draw object
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
+ {
+ HTMLAttr *pAttr = *pHTMLAttributes;
+ if( pAttr )
+ PutEEPoolItem( aItemSet, pAttr->GetItem() );
+ }
+
+ if( bBGColor )
+ {
+ aItemSet.Put( XFillStyleItem(drawing::FillStyle_SOLID) );
+ aItemSet.Put(XFillColorItem(OUString(), aBGColor));
+ }
+
+ // parse styles (is here only possible for attributes, which also
+ // can be set at character object)
+ SfxItemSet aStyleItemSet( m_xDoc->GetAttrPool(),
+ m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) &&
+ ParseStyleOptions( aStyle, aId, aClass, aStyleItemSet, aPropInfo ) )
+ {
+ SfxItemIter aIter( aStyleItemSet );
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ PutEEPoolItem( aItemSet, *pItem );
+ }
+ }
+
+ // now set the size
+ Size aTwipSz( bPercentWidth ? 0 : nWidth, nHeight );
+ if( aTwipSz.Width() || aTwipSz.Height() )
+ {
+ aTwipSz = o3tl::convert(aTwipSz, o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ if( SVX_CSS1_LTYPE_TWIP== aPropInfo.m_eWidthType )
+ {
+ aTwipSz.setWidth( aPropInfo.m_nWidth );
+ nWidth = 1; // != 0;
+ bPercentWidth = false;
+ }
+ if( SVX_CSS1_LTYPE_TWIP== aPropInfo.m_eHeightType )
+ aTwipSz.setHeight( aPropInfo.m_nHeight );
+
+ m_bFixMarqueeWidth = false;
+ if( !nWidth || bPercentWidth )
+ {
+ if( m_xTable )
+ {
+ if( !pCurTable )
+ {
+ // The marquee is in a table, but not in a cell. Since now no
+ // reasonable mapping to a cell is possible, we adjust here the
+ // width to the content of the marquee.
+ m_bFixMarqueeWidth = true;
+ }
+ else if( !nWidth )
+ {
+ // Because we know in which cell the marquee is, we also can
+ // adjust the width. No width specification is treated as
+ // 100 percent.
+ nWidth = 100;
+ bPercentWidth = true;
+ }
+ aTwipSz.setWidth( MINLAY );
+ }
+ else
+ {
+ tools::Long nBrowseWidth = GetCurrentBrowseWidth();
+ aTwipSz.setWidth( !nWidth ? nBrowseWidth
+ : (nWidth*nBrowseWidth) / 100 );
+ }
+ }
+
+ // The height is only minimum height
+ if( aTwipSz.Height() < MINFLY )
+ aTwipSz.setHeight( MINFLY );
+ aItemSet.Put( makeSdrTextMinFrameHeightItem( aTwipSz.Height() ) );
+
+ m_pMarquee->SetMergedItemSetAndBroadcast(aItemSet);
+
+ if( aTwipSz.Width() < MINFLY )
+ aTwipSz.setWidth( MINFLY );
+ m_pMarquee->SetLogicRect( tools::Rectangle( 0, 0, aTwipSz.Width(), aTwipSz.Height() ) );
+
+ // and insert the object into the document
+ InsertDrawObject( m_pMarquee.get(), aSpace, eVertOri, eHoriOri, aStyleItemSet,
+ aPropInfo );
+
+ // Register the drawing object at the table. Is a little bit complicated,
+ // because it is done via the parser, although the table is known, but
+ // otherwise the table would have to be public and that also isn't pretty.
+ // The global pTable also can't be used, because the marquee can also be
+ // in a sub-table.
+ if( pCurTable && bPercentWidth)
+ RegisterDrawObjectToTable( pCurTable, m_pMarquee.get(), static_cast<sal_uInt8>(nWidth) );
+}
+
+void SwHTMLParser::EndMarquee()
+{
+ OSL_ENSURE( m_pMarquee && SdrObjKind::Text==m_pMarquee->GetObjIdentifier(),
+ "no marquee or wrong type" );
+
+ if( m_bFixMarqueeWidth )
+ {
+ // Because there is no fixed height make the text object wider then
+ // the text, so that there is no line break.
+ const tools::Rectangle& rOldRect = m_pMarquee->GetLogicRect();
+ m_pMarquee->SetLogicRect( tools::Rectangle( rOldRect.TopLeft(),
+ Size( USHRT_MAX, 240 ) ) );
+ }
+
+ // insert the collected text
+ m_pMarquee->SetText( m_aContents );
+ m_pMarquee->SetMergedItemSetAndBroadcast( m_pMarquee->GetMergedItemSet() );
+
+ if (m_bFixMarqueeWidth && !bFuzzing)
+ {
+ // adjust the size to the text
+ m_pMarquee->FitFrameToTextSize();
+ }
+
+ m_aContents.clear();
+ m_pMarquee = nullptr;
+}
+
+void SwHTMLParser::InsertMarqueeText()
+{
+ OSL_ENSURE( m_pMarquee && SdrObjKind::Text==m_pMarquee->GetObjIdentifier(),
+ "no marquee or wrong type" );
+
+ // append the current text part to the text
+ m_aContents += aToken;
+}
+
+void SwHTMLParser::ResizeDrawObject( SdrObject* pObj, SwTwips nWidth )
+{
+ OSL_ENSURE( SdrObjKind::Text==pObj->GetObjIdentifier(),
+ "no marquee or wrong type" );
+
+ if( SdrObjKind::Text!=pObj->GetObjIdentifier() )
+ return;
+
+ // the old size
+ const tools::Rectangle& rOldRect = pObj->GetLogicRect();
+ Size aNewSz( nWidth, rOldRect.GetSize().Height() );
+ pObj->SetLogicRect( tools::Rectangle( rOldRect.TopLeft(), aNewSz ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmldrawwriter.cxx b/sw/source/filter/html/htmldrawwriter.cxx
new file mode 100644
index 0000000000..cf37c1948b
--- /dev/null
+++ b/sw/source/filter/html/htmldrawwriter.cxx
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtayitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outliner.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <osl/diagnose.h>
+
+#include <rtl/strbuf.hxx>
+
+#include <IDocumentDrawModelAccess.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <dcontact.hxx>
+
+#include "wrthtml.hxx"
+
+
+using namespace css;
+
+const HtmlFrmOpts HTML_FRMOPTS_MARQUEE =
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::Space;
+
+const HtmlFrmOpts HTML_FRMOPTS_MARQUEE_CSS1 =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSpace;
+
+const SdrObject *SwHTMLWriter::GetMarqueeTextObj( const SwDrawFrameFormat& rFormat )
+{
+ const SdrObject* pObj = rFormat.FindSdrObject();
+ return (pObj && ::IsMarqueeTextObj( *pObj )) ? pObj : nullptr;
+}
+
+void SwHTMLWriter::GetEEAttrsFromDrwObj( SfxItemSet& rItemSet,
+ const SdrObject *pObj )
+{
+ // get the edit script::Engine attributes from object
+ const SfxItemSet& rObjItemSet = pObj->GetMergedItemSet();
+
+ // iterate over Edit script::Engine attributes and convert them
+ // into SW-Attrs resp. set default
+ SfxWhichIter aIter( rObjItemSet );
+ sal_uInt16 nEEWhich = aIter.FirstWhich();
+ while( nEEWhich )
+ {
+ const SfxPoolItem *pEEItem;
+ bool bSet = SfxItemState::SET == aIter.GetItemState( false, &pEEItem );
+
+ sal_uInt16 nSwWhich = 0;
+ switch( nEEWhich )
+ {
+ case EE_CHAR_COLOR: nSwWhich = RES_CHRATR_COLOR; break;
+ case EE_CHAR_STRIKEOUT: nSwWhich = RES_CHRATR_CROSSEDOUT; break;
+ case EE_CHAR_ESCAPEMENT: nSwWhich = RES_CHRATR_ESCAPEMENT; break;
+ case EE_CHAR_FONTINFO: nSwWhich = RES_CHRATR_FONT; break;
+ case EE_CHAR_FONTINFO_CJK: nSwWhich = RES_CHRATR_CJK_FONT; break;
+ case EE_CHAR_FONTINFO_CTL: nSwWhich = RES_CHRATR_CTL_FONT; break;
+ case EE_CHAR_FONTHEIGHT: nSwWhich = RES_CHRATR_FONTSIZE; break;
+ case EE_CHAR_FONTHEIGHT_CJK:nSwWhich = RES_CHRATR_CJK_FONTSIZE; break;
+ case EE_CHAR_FONTHEIGHT_CTL:nSwWhich = RES_CHRATR_CTL_FONTSIZE; break;
+ case EE_CHAR_KERNING: nSwWhich = RES_CHRATR_KERNING; break;
+ case EE_CHAR_ITALIC: nSwWhich = RES_CHRATR_POSTURE; break;
+ case EE_CHAR_ITALIC_CJK: nSwWhich = RES_CHRATR_CJK_POSTURE; break;
+ case EE_CHAR_ITALIC_CTL: nSwWhich = RES_CHRATR_CTL_POSTURE; break;
+ case EE_CHAR_UNDERLINE: nSwWhich = RES_CHRATR_UNDERLINE; break;
+ case EE_CHAR_WEIGHT: nSwWhich = RES_CHRATR_WEIGHT; break;
+ case EE_CHAR_WEIGHT_CJK: nSwWhich = RES_CHRATR_CJK_WEIGHT; break;
+ case EE_CHAR_WEIGHT_CTL: nSwWhich = RES_CHRATR_CTL_WEIGHT; break;
+ }
+
+ if( nSwWhich )
+ {
+ // if the item isn't set we maybe take the default item
+ if( !bSet )
+ pEEItem = &rObjItemSet.GetPool()->GetDefaultItem(nEEWhich);
+
+ // now we clone the item with the which id of the writer
+ rItemSet.Put( pEEItem->CloneSetWhich(nSwWhich) );
+ }
+
+ nEEWhich = aIter.NextWhich();
+ }
+}
+
+SwHTMLWriter& OutHTML_DrawFrameFormatAsMarquee( SwHTMLWriter& rWrt,
+ const SwDrawFrameFormat& rFormat,
+ const SdrObject& rSdrObject )
+{
+ OSL_ENSURE( rWrt.m_pDoc->getIDocumentDrawModelAccess().GetDrawModel(),
+ "There is a Draw-Obj with no Draw-Model?" );
+ const SdrTextObj *pTextObj = static_cast<const SdrTextObj *>(&rSdrObject);
+
+ // Is there text to output
+ const OutlinerParaObject *pOutlinerParaObj =
+ pTextObj->GetOutlinerParaObject();
+ if( !pOutlinerParaObj )
+ return rWrt;
+
+ OStringBuffer sOut("<" OOO_STRING_SVTOOLS_HTML_marquee);
+
+ // get attributes of the object
+ const SfxItemSet& rItemSet = pTextObj->GetMergedItemSet();
+
+ // BEHAVIOUR
+ SdrTextAniKind eAniKind = pTextObj->GetTextAniKind();
+ OSL_ENSURE( SdrTextAniKind::Scroll==eAniKind ||
+ SdrTextAniKind::Alternate==eAniKind ||
+ SdrTextAniKind::Slide==eAniKind,
+ "Text-Draw-Object not suitable for marquee" );
+
+ const char *pStr = nullptr;
+ switch( eAniKind )
+ {
+ case SdrTextAniKind::Scroll: pStr = OOO_STRING_SVTOOLS_HTML_BEHAV_scroll; break;
+ case SdrTextAniKind::Slide: pStr = OOO_STRING_SVTOOLS_HTML_BEHAV_slide; break;
+ case SdrTextAniKind::Alternate: pStr = OOO_STRING_SVTOOLS_HTML_BEHAV_alternate; break;
+ default:
+ ;
+ }
+
+ if( pStr )
+ {
+ sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_behavior "=\"") +
+ pStr + "\"");
+ }
+
+ // DIRECTION
+ pStr = nullptr;
+ SdrTextAniDirection eAniDir = pTextObj->GetTextAniDirection();
+ switch( eAniDir )
+ {
+ case SdrTextAniDirection::Left: pStr = OOO_STRING_SVTOOLS_HTML_AL_left; break;
+ case SdrTextAniDirection::Right: pStr = OOO_STRING_SVTOOLS_HTML_AL_right; break;
+ default:
+ ;
+ }
+
+ if( pStr )
+ {
+ sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_direction
+ "=\"") + pStr + "\"");
+ }
+
+ // LOOP
+ sal_Int32 nCount = rItemSet.Get( SDRATTR_TEXT_ANICOUNT ).GetValue();
+ if( 0==nCount )
+ nCount = SdrTextAniKind::Slide==eAniKind ? 1 : -1;
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_loop "=\"" +
+ OString::number(nCount) + "\"");
+
+ // SCROLLDELAY
+ sal_uInt16 nDelay = rItemSet.Get( SDRATTR_TEXT_ANIDELAY ).GetValue();
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_scrolldelay
+ "=\"" + OString::number(nDelay) + "\"");
+
+ // SCROLLAMOUNT
+ sal_Int16 nAmount = rItemSet.Get( SDRATTR_TEXT_ANIAMOUNT ).GetValue();
+ if( nAmount < 0 )
+ {
+ nAmount = -nAmount;
+ }
+ else
+ {
+ nAmount = SwHTMLWriter::ToPixel(nAmount);
+ }
+ if( nAmount )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_scrollamount
+ "=\"" + OString::number(nAmount) + "\"");
+ }
+
+ Size aTwipSz( pTextObj->GetLogicRect().GetSize() );
+ if( pTextObj->IsAutoGrowWidth() )
+ aTwipSz.setWidth( 0 );
+ // The height is at MS a minimum height, therefore we output the minimum
+ // height, if they exist. Because a minimum height MINFLY is coming with
+ // high probability from import, we aren't outputting it. You can't
+ // do anything wrong, because every font is higher.
+ if( pTextObj->IsAutoGrowHeight() )
+ {
+ aTwipSz.setHeight( pTextObj->GetMinTextFrameHeight() );
+ if( MINFLY==aTwipSz.Height() )
+ aTwipSz.setHeight( 0 );
+ }
+
+ if( (aTwipSz.Width() || aTwipSz.Height()) &&
+ Application::GetDefaultDevice() )
+ {
+ Size aPixelSz =
+ Application::GetDefaultDevice()->LogicToPixel( aTwipSz,
+ MapMode(MapUnit::MapTwip) );
+ if( !aPixelSz.Width() && aTwipSz.Width() )
+ aPixelSz.setWidth( 1 );
+ if( !aPixelSz.Height() && aTwipSz.Height() )
+ aPixelSz.setHeight( 1 );
+
+ if( aPixelSz.Width() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width
+ "=\"" + OString::number(aPixelSz.Width()) + "\"");
+ }
+
+ if( aPixelSz.Height() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height
+ "=\"" + OString::number(aPixelSz.Height()) + "\"");
+ }
+ }
+
+ // BGCOLOR
+ drawing::FillStyle eFillStyle =
+ rItemSet.Get(XATTR_FILLSTYLE).GetValue();
+ if( drawing::FillStyle_SOLID==eFillStyle )
+ {
+ const Color& rFillColor =
+ rItemSet.Get(XATTR_FILLCOLOR).GetColorValue();
+
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_bgcolor "=");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_Color( rWrt.Strm(), rFillColor );
+ }
+
+ if (!sOut.isEmpty())
+ {
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ }
+
+ // and now ALIGN, HSPACE and VSPACE
+ HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MARQUEE;
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_DRAW ) )
+ nFrameFlags |= HTML_FRMOPTS_MARQUEE_CSS1;
+ OString aEndTags = rWrt.OutFrameFormatOptions(rFormat, OUString(), nFrameFlags);
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_DRAW ) )
+ rWrt.OutCSS1_FrameFormatOptions( rFormat, nFrameFlags, &rSdrObject );
+
+ rWrt.Strm().WriteChar( '>' );
+
+ // What follows now is the counterpart of SdrTextObject::SetText()
+ Outliner aOutliner(nullptr, OutlinerMode::TextObject);
+ aOutliner.SetUpdateLayout( false );
+ aOutliner.SetText( *pOutlinerParaObj );
+ OUString aText( aOutliner.GetText( aOutliner.GetParagraph(0),
+ aOutliner.GetParagraphCount() ) );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aText );
+
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_marquee), false );
+
+ if( !aEndTags.isEmpty() )
+ rWrt.Strm().WriteOString( aEndTags );
+
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlfld.cxx b/sw/source/filter/html/htmlfld.cxx
new file mode 100644
index 0000000000..c1eff7ef7c
--- /dev/null
+++ b/sw/source/filter/html/htmlfld.cxx
@@ -0,0 +1,651 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <docsh.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <svtools/htmltokn.h>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <unotools/useroptions.hxx>
+#include <fmtfld.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <fldbas.hxx>
+#include <docufld.hxx>
+#include <flddat.hxx>
+#include "htmlfld.hxx"
+#include "swhtml.hxx"
+
+using namespace nsSwDocInfoSubType;
+using namespace ::com::sun::star;
+
+namespace {
+
+struct HTMLNumFormatTableEntry
+{
+ std::string_view pName;
+ NfIndexTableOffset eFormat;
+};
+
+}
+
+HTMLOptionEnum<SwFieldIds> const aHTMLFieldTypeTable[] =
+{
+ { OOO_STRING_SW_HTML_FT_author, SwFieldIds::Author },
+ { OOO_STRING_SW_HTML_FT_sender, SwFieldIds::ExtUser },
+ { "DATE", SwFieldIds::Date },
+ { "TIME", SwFieldIds::Time },
+ { OOO_STRING_SW_HTML_FT_datetime, SwFieldIds::DateTime },
+ { OOO_STRING_SW_HTML_FT_page, SwFieldIds::PageNumber },
+ { OOO_STRING_SW_HTML_FT_docinfo, SwFieldIds::DocInfo },
+ { OOO_STRING_SW_HTML_FT_docstat, SwFieldIds::DocStat },
+ { OOO_STRING_SW_HTML_FT_filename, SwFieldIds::Filename },
+ { nullptr, SwFieldIds(0) }
+};
+
+HTMLNumFormatTableEntry const aHTMLDateFieldFormatTable[] =
+{
+ { "SSYS", NF_DATE_SYSTEM_SHORT },
+ { "LSYS", NF_DATE_SYSTEM_LONG },
+ { "DMY", NF_DATE_SYS_DDMMYY, },
+ { "DMYY", NF_DATE_SYS_DDMMYYYY, },
+ { "DMMY", NF_DATE_SYS_DMMMYY, },
+ { "DMMYY", NF_DATE_SYS_DMMMYYYY, },
+ { "DMMMY", NF_DATE_DIN_DMMMMYYYY },
+ { "DMMMYY", NF_DATE_DIN_DMMMMYYYY },
+ { "DDMMY", NF_DATE_SYS_NNDMMMYY },
+ { "DDMMMY", NF_DATE_SYS_NNDMMMMYYYY },
+ { "DDMMMYY", NF_DATE_SYS_NNDMMMMYYYY },
+ { "DDDMMMY", NF_DATE_SYS_NNNNDMMMMYYYY },
+ { "DDDMMMYY", NF_DATE_SYS_NNNNDMMMMYYYY },
+ { "MY", NF_DATE_SYS_MMYY },
+ { "MD", NF_DATE_DIN_MMDD },
+ { "YMD", NF_DATE_DIN_YYMMDD },
+ { "YYMD", NF_DATE_DIN_YYYYMMDD },
+ { {}, NF_NUMERIC_START }
+};
+
+HTMLNumFormatTableEntry const aHTMLTimeFieldFormatTable[] =
+{
+ { "SYS", NF_TIME_HHMMSS },
+ { "SSMM24", NF_TIME_HHMM },
+ { "SSMM12", NF_TIME_HHMMAMPM },
+ { {}, NF_NUMERIC_START }
+};
+
+HTMLOptionEnum<SvxNumType> const aHTMLPageNumFieldFormatTable[] =
+{
+ { OOO_STRING_SW_HTML_FF_uletter, SVX_NUM_CHARS_UPPER_LETTER },
+ { OOO_STRING_SW_HTML_FF_lletter, SVX_NUM_CHARS_LOWER_LETTER },
+ { OOO_STRING_SW_HTML_FF_uroman, SVX_NUM_ROMAN_UPPER },
+ { OOO_STRING_SW_HTML_FF_lroman, SVX_NUM_ROMAN_LOWER },
+ { OOO_STRING_SW_HTML_FF_arabic, SVX_NUM_ARABIC },
+ { OOO_STRING_SW_HTML_FF_none, SVX_NUM_NUMBER_NONE },
+ { OOO_STRING_SW_HTML_FF_char, SVX_NUM_CHAR_SPECIAL },
+ { OOO_STRING_SW_HTML_FF_page, SVX_NUM_PAGEDESC },
+ { OOO_STRING_SW_HTML_FF_ulettern, SVX_NUM_CHARS_UPPER_LETTER_N },
+ { OOO_STRING_SW_HTML_FF_llettern, SVX_NUM_CHARS_LOWER_LETTER_N },
+ { nullptr, SvxNumType(0) }
+};
+
+HTMLOptionEnum<SwExtUserSubType> const aHTMLExtUsrFieldSubTable[] =
+{
+ { OOO_STRING_SW_HTML_FS_company, EU_COMPANY },
+ { OOO_STRING_SW_HTML_FS_firstname, EU_FIRSTNAME },
+ { OOO_STRING_SW_HTML_FS_name, EU_NAME },
+ { OOO_STRING_SW_HTML_FS_shortcut, EU_SHORTCUT },
+ { OOO_STRING_SW_HTML_FS_street, EU_STREET },
+ { OOO_STRING_SW_HTML_FS_country, EU_COUNTRY },
+ { OOO_STRING_SW_HTML_FS_zip, EU_ZIP },
+ { OOO_STRING_SW_HTML_FS_city, EU_CITY },
+ { OOO_STRING_SW_HTML_FS_title, EU_TITLE },
+ { OOO_STRING_SW_HTML_FS_position, EU_POSITION },
+ { OOO_STRING_SW_HTML_FS_pphone, EU_PHONE_PRIVATE },
+ { OOO_STRING_SW_HTML_FS_cphone, EU_PHONE_COMPANY },
+ { OOO_STRING_SW_HTML_FS_fax, EU_FAX },
+ { OOO_STRING_SW_HTML_FS_email, EU_EMAIL },
+ { OOO_STRING_SW_HTML_FS_state, EU_STATE },
+ { nullptr, SwExtUserSubType(0) }
+};
+
+HTMLOptionEnum<SwAuthorFormat> const aHTMLAuthorFieldFormatTable[] =
+{
+ { OOO_STRING_SW_HTML_FF_name, AF_NAME },
+ { OOO_STRING_SW_HTML_FF_shortcut, AF_SHORTCUT },
+ { nullptr, SwAuthorFormat(0) }
+};
+
+HTMLOptionEnum<SwPageNumSubType> const aHTMLPageNumFieldSubTable[] =
+{
+ { OOO_STRING_SW_HTML_FS_random, PG_RANDOM },
+ { OOO_STRING_SW_HTML_FS_next, PG_NEXT },
+ { OOO_STRING_SW_HTML_FS_prev, PG_PREV },
+ { nullptr, SwPageNumSubType(0) }
+};
+
+// UGLY: these are extensions of nsSwDocInfoSubType (in inc/docufld.hxx)
+// these are necessary for importing document info fields written by
+// older versions of OOo (< 3.0) which did not have DI_CUSTOM fields
+ const SwDocInfoSubType DI_INFO1 = DI_SUBTYPE_END + 1;
+ const SwDocInfoSubType DI_INFO2 = DI_SUBTYPE_END + 2;
+ const SwDocInfoSubType DI_INFO3 = DI_SUBTYPE_END + 3;
+ const SwDocInfoSubType DI_INFO4 = DI_SUBTYPE_END + 4;
+
+HTMLOptionEnum<sal_uInt16> const aHTMLDocInfoFieldSubTable[] =
+{
+ { OOO_STRING_SW_HTML_FS_title, DI_TITLE },
+ { OOO_STRING_SW_HTML_FS_theme, DI_SUBJECT },
+ { OOO_STRING_SW_HTML_FS_keys, DI_KEYS },
+ { OOO_STRING_SW_HTML_FS_comment, DI_COMMENT },
+ { "INFO1", DI_INFO1 },
+ { "INFO2", DI_INFO2 },
+ { "INFO3", DI_INFO3 },
+ { "INFO4", DI_INFO4 },
+ { OOO_STRING_SW_HTML_FS_custom, DI_CUSTOM },
+ { OOO_STRING_SW_HTML_FS_create, DI_CREATE },
+ { OOO_STRING_SW_HTML_FS_change, DI_CHANGE },
+ { nullptr, 0 }
+};
+
+HTMLOptionEnum<sal_uInt16> const aHTMLDocInfoFieldFormatTable[] =
+{
+ { OOO_STRING_SW_HTML_FF_author, DI_SUB_AUTHOR },
+ { OOO_STRING_SW_HTML_FF_time, DI_SUB_TIME },
+ { OOO_STRING_SW_HTML_FF_date, DI_SUB_DATE },
+ { nullptr, 0 }
+};
+
+HTMLOptionEnum<SwDocStatSubType> const aHTMLDocStatFieldSubTable[] =
+{
+ { OOO_STRING_SW_HTML_FS_page, DS_PAGE },
+ { OOO_STRING_SW_HTML_FS_para, DS_PARA },
+ { OOO_STRING_SW_HTML_FS_word, DS_WORD },
+ { OOO_STRING_SW_HTML_FS_char, DS_CHAR },
+ { OOO_STRING_SW_HTML_FS_tbl, DS_TBL },
+ { OOO_STRING_SW_HTML_FS_grf, DS_GRF },
+ { OOO_STRING_SW_HTML_FS_ole, DS_OLE },
+ { nullptr, SwDocStatSubType(0) }
+};
+
+HTMLOptionEnum<SwFileNameFormat> const aHTMLFileNameFieldFormatTable[] =
+{
+ { OOO_STRING_SW_HTML_FF_name, FF_NAME },
+ { OOO_STRING_SW_HTML_FF_pathname, FF_PATHNAME },
+ { OOO_STRING_SW_HTML_FF_path, FF_PATH },
+ { OOO_STRING_SW_HTML_FF_name_noext, FF_NAME_NOEXT },
+ { nullptr, SwFileNameFormat(0) }
+};
+
+SvxNumType SwHTMLParser::GetNumType( std::u16string_view rStr, SvxNumType nDfltType )
+{
+ const HTMLOptionEnum<SvxNumType> *pOptEnums = aHTMLPageNumFieldFormatTable;
+ while( pOptEnums->pName )
+ {
+ if( o3tl::equalsIgnoreAsciiCase( rStr, pOptEnums->pName ) )
+ return pOptEnums->nValue;
+ pOptEnums++;
+ }
+ return nDfltType;
+}
+
+void SwHTMLParser::NewField()
+{
+ bool bKnownType = false, bFixed = false,
+ bHasNumFormat = false, bHasNumValue = false;
+ SwFieldIds nType = SwFieldIds::Database;
+ OUString aValue, aNumFormat, aNumValue, aName;
+ const HTMLOption *pSubOption=nullptr, *pFormatOption=nullptr;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ size_t i;
+
+ for ( i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::TYPE:
+ bKnownType = rOption.GetEnum( nType, aHTMLFieldTypeTable );
+ break;
+ case HtmlOptionId::SUBTYPE:
+ pSubOption = &rOption;
+ break;
+ case HtmlOptionId::FORMAT:
+ pFormatOption = &rOption;
+ break;
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ break;
+ case HtmlOptionId::VALUE:
+ aValue = rOption.GetString();
+ break;
+ case HtmlOptionId::SDNUM:
+ aNumFormat = rOption.GetString();
+ bHasNumFormat = true;
+ break;
+ case HtmlOptionId::SDVAL:
+ aNumValue = rOption.GetString();
+ bHasNumValue = true;
+ break;
+ case HtmlOptionId::SDFIXED:
+ bFixed = true;
+ break;
+ default: break;
+ }
+ }
+
+ if( !bKnownType )
+ return;
+
+ // Author and sender are only inserted as a variable field if the document
+ // was last changed by ourself or nobody changed it and it was created
+ // by ourself. Otherwise it will be a fixed field.
+ if( !bFixed &&
+ (SwFieldIds::ExtUser == nType ||
+ SwFieldIds::Author == nType) )
+ {
+ SvtUserOptions aOpt;
+ const OUString& rUser = aOpt.GetFullName();
+ SwDocShell *pDocShell(m_xDoc->GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell) {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+ const OUString& rChanged = xDocProps->getModifiedBy();
+ const OUString& rCreated = xDocProps->getAuthor();
+ if( rUser.isEmpty() ||
+ (!rChanged.isEmpty() ? rUser != rChanged : rUser != rCreated) )
+ bFixed = true;
+ }
+ }
+
+ SwFieldIds nWhich = nType;
+ if( SwFieldIds::Date==nType || SwFieldIds::Time==nType )
+ nWhich = SwFieldIds::DateTime;
+
+ SwFieldType* pType = m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( nWhich );
+ std::unique_ptr<SwField> xNewField;
+ bool bInsOnEndTag = false;
+
+ switch( nType )
+ {
+ case SwFieldIds::ExtUser:
+ if( pSubOption )
+ {
+ SwExtUserSubType nSub;
+ sal_uLong nFormat = 0;
+ if( bFixed )
+ {
+ nFormat |= AF_FIXED;
+ bInsOnEndTag = true;
+ }
+ if( pSubOption->GetEnum( nSub, aHTMLExtUsrFieldSubTable ) )
+ xNewField.reset(new SwExtUserField(static_cast<SwExtUserFieldType*>(pType), nSub, nFormat));
+ }
+ break;
+
+ case SwFieldIds::Author:
+ {
+ SwAuthorFormat nFormat = AF_NAME;
+ if( pFormatOption )
+ pFormatOption->GetEnum( nFormat, aHTMLAuthorFieldFormatTable );
+ if( bFixed )
+ {
+ nFormat = static_cast<SwAuthorFormat>(static_cast<int>(nFormat) | AF_FIXED);
+ bInsOnEndTag = true;
+ }
+
+ xNewField.reset(new SwAuthorField(static_cast<SwAuthorFieldType*>(pType), nFormat));
+ }
+ break;
+
+ case SwFieldIds::Date:
+ case SwFieldIds::Time:
+ {
+ sal_uInt32 nNumFormat = 0;
+ DateTime aDateTime( DateTime::SYSTEM );
+ sal_Int64 nTime = aDateTime.GetTime();
+ sal_Int32 nDate = aDateTime.GetDate();
+ sal_uInt16 nSub = 0;
+ bool bValidFormat = false;
+ HTMLNumFormatTableEntry const * pFormatTable;
+
+ if( SwFieldIds::Date==nType )
+ {
+ nSub = DATEFLD;
+ pFormatTable = aHTMLDateFieldFormatTable;
+ if( !aValue.isEmpty() )
+ nDate = aValue.toInt32();
+ }
+ else
+ {
+ nSub = TIMEFLD;
+ pFormatTable = aHTMLTimeFieldFormatTable;
+ if( !aValue.isEmpty() )
+ nTime = static_cast<sal_uLong>(aValue.toInt32());
+ }
+ if( !aValue.isEmpty() )
+ nSub |= FIXEDFLD;
+
+ SvNumberFormatter *pFormatter = m_xDoc->GetNumberFormatter();
+ if( pFormatOption )
+ {
+ const OUString& rFormat = pFormatOption->GetString();
+ for( int k = 0; !pFormatTable[k].pName.empty(); ++k )
+ {
+ if( rFormat.equalsIgnoreAsciiCaseAscii( pFormatTable[k].pName ) )
+ {
+ nNumFormat = pFormatter->GetFormatIndex(
+ pFormatTable[k].eFormat, LANGUAGE_SYSTEM);
+ bValidFormat = true;
+ break;
+ }
+ }
+ }
+ if( !bValidFormat )
+ nNumFormat = pFormatter->GetFormatIndex( pFormatTable[i].eFormat,
+ LANGUAGE_SYSTEM);
+
+ xNewField.reset(new SwDateTimeField(static_cast<SwDateTimeFieldType *>(pType), nSub, nNumFormat));
+
+ if (nSub & FIXEDFLD)
+ static_cast<SwDateTimeField *>(xNewField.get())->SetDateTime(DateTime(Date(nDate), tools::Time(nTime)));
+ }
+ break;
+
+ case SwFieldIds::DateTime:
+ if( bHasNumFormat )
+ {
+ sal_uInt16 nSub = 0;
+
+ SvNumberFormatter *pFormatter = m_xDoc->GetNumberFormatter();
+ sal_uInt32 nNumFormat;
+ LanguageType eLang;
+ double dValue = GetTableDataOptionsValNum(
+ nNumFormat, eLang, aNumValue, aNumFormat,
+ *m_xDoc->GetNumberFormatter() );
+ SvNumFormatType nFormatType = pFormatter->GetType( nNumFormat );
+ switch( nFormatType )
+ {
+ case SvNumFormatType::DATE: nSub = DATEFLD; break;
+ case SvNumFormatType::TIME: nSub = TIMEFLD; break;
+ default: break;
+ }
+
+ if( nSub )
+ {
+ if( bHasNumValue )
+ nSub |= FIXEDFLD;
+
+ xNewField.reset(new SwDateTimeField(static_cast<SwDateTimeFieldType *>(pType), nSub, nNumFormat));
+ if (bHasNumValue)
+ static_cast<SwDateTimeField*>(xNewField.get())->SetValue(dValue);
+ }
+ }
+ break;
+
+ case SwFieldIds::PageNumber:
+ if( pSubOption )
+ {
+ SwPageNumSubType nSub;
+ if( pSubOption->GetEnum( nSub, aHTMLPageNumFieldSubTable ) )
+ {
+ SvxNumType nFormat = SVX_NUM_PAGEDESC;
+ if( pFormatOption )
+ pFormatOption->GetEnum( nFormat, aHTMLPageNumFieldFormatTable );
+
+ short nOff = 0;
+
+ if( nFormat!=SVX_NUM_CHAR_SPECIAL && !aValue.isEmpty() )
+ nOff = static_cast<short>(aValue.toInt32());
+ else if( nSub == PG_NEXT )
+ nOff = 1;
+ else if( nSub == PG_PREV )
+ nOff = -1;
+
+ if( nFormat==SVX_NUM_CHAR_SPECIAL &&
+ nSub==PG_RANDOM )
+ nFormat = SVX_NUM_PAGEDESC;
+
+ xNewField.reset(new SwPageNumberField(static_cast<SwPageNumberFieldType*>(pType), nSub, nFormat, nOff));
+ if (nFormat == SVX_NUM_CHAR_SPECIAL)
+ static_cast<SwPageNumberField*>(xNewField.get())->SetUserString(aValue);
+ }
+ }
+ break;
+
+ case SwFieldIds::DocInfo:
+ if( pSubOption )
+ {
+ sal_uInt16 nSub;
+ if( pSubOption->GetEnum( nSub, aHTMLDocInfoFieldSubTable ) )
+ {
+ sal_uInt16 nExtSub = 0;
+ if( DI_CREATE==static_cast<SwDocInfoSubType>(nSub) ||
+ DI_CHANGE==static_cast<SwDocInfoSubType>(nSub) )
+ {
+ nExtSub = DI_SUB_AUTHOR;
+ if( pFormatOption )
+ pFormatOption->GetEnum( nExtSub, aHTMLDocInfoFieldFormatTable );
+ nSub |= nExtSub;
+ }
+
+ sal_uInt32 nNumFormat = 0;
+ double dValue = 0;
+ if( bHasNumFormat && (DI_SUB_DATE==nExtSub || DI_SUB_TIME==nExtSub) )
+ {
+ LanguageType eLang;
+ dValue = GetTableDataOptionsValNum(
+ nNumFormat, eLang, aNumValue, aNumFormat,
+ *m_xDoc->GetNumberFormatter() );
+ bFixed &= bHasNumValue;
+ }
+ else
+ bHasNumValue = false;
+
+ if( nSub >= DI_INFO1 && nSub <= DI_INFO4 && aName.isEmpty() )
+ {
+ // backward compatibility for OOo 2:
+ // map to names stored in AddMetaUserDefined
+ aName = m_InfoNames[nSub - DI_INFO1];
+ nSub = DI_CUSTOM;
+ }
+
+ if( bFixed )
+ {
+ nSub |= DI_SUB_FIXED;
+ bInsOnEndTag = true;
+ }
+
+ xNewField.reset(new SwDocInfoField(static_cast<SwDocInfoFieldType *>(pType), nSub, aName, nNumFormat));
+ if (bHasNumValue)
+ static_cast<SwDocInfoField*>(xNewField.get())->SetValue(dValue);
+ }
+ }
+ break;
+
+ case SwFieldIds::DocStat:
+ if( pSubOption )
+ {
+ SwDocStatSubType nSub;
+ if( pSubOption->GetEnum( nSub, aHTMLDocStatFieldSubTable ) )
+ {
+ SvxNumType nFormat = SVX_NUM_ARABIC;
+ if( pFormatOption )
+ pFormatOption->GetEnum( nFormat, aHTMLPageNumFieldFormatTable );
+ xNewField.reset(new SwDocStatField(static_cast<SwDocStatFieldType*>(pType), nSub, nFormat));
+ m_bUpdateDocStat |= (DS_PAGE != nSub);
+ }
+ }
+ break;
+
+ case SwFieldIds::Filename:
+ {
+ SwFileNameFormat nFormat = FF_NAME;
+ if( pFormatOption )
+ pFormatOption->GetEnum( nFormat, aHTMLFileNameFieldFormatTable );
+ if( bFixed )
+ {
+ nFormat = static_cast<SwFileNameFormat>(static_cast<int>(nFormat) | FF_FIXED);
+ bInsOnEndTag = true;
+ }
+
+ xNewField.reset(new SwFileNameField(static_cast<SwFileNameFieldType*>(pType), nFormat));
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (!xNewField)
+ return;
+
+ if (bInsOnEndTag)
+ {
+ m_xField = std::move(xNewField);
+ }
+ else
+ {
+ m_xDoc->getIDocumentContentOperations().InsertPoolItem(*m_pPam, SwFormatField(*xNewField));
+ xNewField.reset();
+ }
+ m_bInField = true;
+}
+
+void SwHTMLParser::EndField()
+{
+ if (m_xField)
+ {
+ switch (m_xField->Which())
+ {
+ case SwFieldIds::DocInfo:
+ OSL_ENSURE( static_cast<SwDocInfoField*>(m_xField.get())->IsFixed(),
+ "Field DocInfo should not have been saved" );
+ static_cast<SwDocInfoField*>(m_xField.get())->SetExpansion( m_aContents );
+ break;
+
+ case SwFieldIds::ExtUser:
+ OSL_ENSURE( static_cast<SwExtUserField*>(m_xField.get())->IsFixed(),
+ "Field ExtUser should not have been saved" );
+ static_cast<SwExtUserField*>(m_xField.get())->SetExpansion( m_aContents );
+ break;
+
+ case SwFieldIds::Author:
+ OSL_ENSURE( static_cast<SwAuthorField*>(m_xField.get())->IsFixed(),
+ "Field Author should not have been saved" );
+ static_cast<SwAuthorField*>(m_xField.get())->SetExpansion( m_aContents );
+ break;
+
+ case SwFieldIds::Filename:
+ OSL_ENSURE( static_cast<SwFileNameField*>(m_xField.get())->IsFixed(),
+ "Field FileName should not have been saved" );
+ static_cast<SwFileNameField*>(m_xField.get())->SetExpansion( m_aContents );
+ break;
+ default: break;
+ }
+
+ m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, SwFormatField(*m_xField) );
+ m_xField.reset();
+ }
+
+ m_bInField = false;
+ m_aContents.clear();
+}
+
+void SwHTMLParser::InsertFieldText()
+{
+ if (m_xField)
+ {
+ // append the current text part to the text
+ m_aContents += aToken;
+ }
+}
+
+void SwHTMLParser::InsertCommentText( std::string_view pTag )
+{
+ bool bEmpty = m_aContents.isEmpty();
+ if( !bEmpty )
+ m_aContents += "\n";
+
+ m_aContents += aToken;
+ if( bEmpty && !pTag.empty() )
+ {
+ m_aContents = OUString::Concat("HTML: <") + OUString::createFromAscii(pTag) + ">" + m_aContents;
+ }
+}
+
+void SwHTMLParser::InsertComment( const OUString& rComment, std::string_view pTag )
+{
+ OUString aComment( rComment );
+ if( !pTag.empty() )
+ {
+ aComment += "</" +
+ OUString::createFromAscii(pTag) +
+ ">";
+ }
+
+ // MIB 24.06.97: If a PostIt should be insert after a space, we
+ // will insert before the space. Then there are less problems
+ // during formatting. (bug #40483#)
+ const sal_Int32 nPos = m_pPam->GetPoint()->GetContentIndex();
+ SwTextNode *pTextNd = m_pPam->GetPointNode().GetTextNode();
+ bool bMoveFwd = false;
+ if (nPos>0 && pTextNd && (' ' == pTextNd->GetText()[nPos-1]))
+ {
+ bMoveFwd = true;
+
+ SwNodeOffset nNodeIdx = m_pPam->GetPoint()->GetNodeIndex();
+ const sal_Int32 nIdx = m_pPam->GetPoint()->GetContentIndex();
+ for( auto i = m_aSetAttrTab.size(); i > 0; )
+ {
+ HTMLAttr *pAttr = m_aSetAttrTab[--i];
+ if( pAttr->GetStartParagraphIdx() != nNodeIdx ||
+ pAttr->GetStartContent() != nIdx )
+ break;
+
+ if( RES_TXTATR_FIELD == pAttr->m_pItem->Which() &&
+ SwFieldIds::Script == static_cast<const SwFormatField *>(pAttr->m_pItem.get())->GetField()->GetTyp()->Which() )
+ {
+ bMoveFwd = false;
+ break;
+ }
+ }
+
+ if( bMoveFwd )
+ m_pPam->Move( fnMoveBackward );
+ }
+
+ SwPostItField aPostItField(
+ static_cast<SwPostItFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Postit )),
+ OUString(), aComment, OUString(), OUString(), DateTime(DateTime::SYSTEM));
+ InsertAttr( SwFormatField( aPostItField ), false );
+
+ if( bMoveFwd )
+ m_pPam->Move( fnMoveForward );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlfld.hxx b/sw/source/filter/html/htmlfld.hxx
new file mode 100644
index 0000000000..80c50959e5
--- /dev/null
+++ b/sw/source/filter/html/htmlfld.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFLD_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFLD_HXX
+
+#define OOO_STRING_SW_HTML_FT_author "AUTHOR"
+#define OOO_STRING_SW_HTML_FT_sender "SENDER"
+#define OOO_STRING_SW_HTML_FT_datetime "DATETIME"
+#define OOO_STRING_SW_HTML_FT_page "PAGE"
+#define OOO_STRING_SW_HTML_FT_docinfo "DOCINFO"
+#define OOO_STRING_SW_HTML_FT_docstat "DOCSTAT"
+#define OOO_STRING_SW_HTML_FT_filename "FILENAME"
+#define OOO_STRING_SW_HTML_FS_company "COMPANY"
+#define OOO_STRING_SW_HTML_FS_firstname "FIRSTNAME"
+#define OOO_STRING_SW_HTML_FS_name "NAME"
+#define OOO_STRING_SW_HTML_FS_shortcut "SHORTCUT"
+#define OOO_STRING_SW_HTML_FS_street "STREET"
+#define OOO_STRING_SW_HTML_FS_country "COUNTRY"
+#define OOO_STRING_SW_HTML_FS_zip "ZIP"
+#define OOO_STRING_SW_HTML_FS_city "CITY"
+#define OOO_STRING_SW_HTML_FS_title "TITLE"
+#define OOO_STRING_SW_HTML_FS_position "POSITION"
+#define OOO_STRING_SW_HTML_FS_pphone "PPHONE"
+#define OOO_STRING_SW_HTML_FS_cphone "CPHONE"
+#define OOO_STRING_SW_HTML_FS_fax "FAX"
+#define OOO_STRING_SW_HTML_FS_email "EMAIL"
+#define OOO_STRING_SW_HTML_FS_state "STATE"
+#define OOO_STRING_SW_HTML_FS_random "RANDOM"
+#define OOO_STRING_SW_HTML_FS_next "NEXT"
+#define OOO_STRING_SW_HTML_FS_prev "PREV"
+#define OOO_STRING_SW_HTML_FS_theme "THEME"
+#define OOO_STRING_SW_HTML_FS_keys "KEYS"
+#define OOO_STRING_SW_HTML_FS_comment "COMMENT"
+#define OOO_STRING_SW_HTML_FS_custom "CUSTOM"
+#define OOO_STRING_SW_HTML_FS_create "CREATE"
+#define OOO_STRING_SW_HTML_FS_change "CHANGE"
+#define OOO_STRING_SW_HTML_FS_page "PAGE"
+#define OOO_STRING_SW_HTML_FS_para "PARAGRAPH"
+#define OOO_STRING_SW_HTML_FS_word "WORD"
+#define OOO_STRING_SW_HTML_FS_char "CHAR"
+#define OOO_STRING_SW_HTML_FS_tbl "TABLE"
+#define OOO_STRING_SW_HTML_FS_grf "GRAPHIC"
+#define OOO_STRING_SW_HTML_FS_ole "OLE"
+#define OOO_STRING_SW_HTML_FF_name "NAME"
+#define OOO_STRING_SW_HTML_FF_shortcut "SHORTCUT"
+#define OOO_STRING_SW_HTML_FF_uletter "ULETTER"
+#define OOO_STRING_SW_HTML_FF_lletter "LLETTER"
+#define OOO_STRING_SW_HTML_FF_uroman "UROMAN"
+#define OOO_STRING_SW_HTML_FF_lroman "LROMAN"
+#define OOO_STRING_SW_HTML_FF_arabic "ARABIC"
+#define OOO_STRING_SW_HTML_FF_none "NONE"
+#define OOO_STRING_SW_HTML_FF_char "CHAR"
+#define OOO_STRING_SW_HTML_FF_page "PAGE"
+#define OOO_STRING_SW_HTML_FF_ulettern "ULETTERN"
+#define OOO_STRING_SW_HTML_FF_llettern "LLETTERN"
+#define OOO_STRING_SW_HTML_FF_author "AUTHOR"
+#define OOO_STRING_SW_HTML_FF_time "TIME"
+#define OOO_STRING_SW_HTML_FF_date "DATE"
+#define OOO_STRING_SW_HTML_FF_pathname "PATHNAME"
+#define OOO_STRING_SW_HTML_FF_path "PATH"
+#define OOO_STRING_SW_HTML_FF_name_noext "NAME-NOEXT"
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlfldw.cxx b/sw/source/filter/html/htmlfldw.cxx
new file mode 100644
index 0000000000..e8d38608b4
--- /dev/null
+++ b/sw/source/filter/html/htmlfldw.cxx
@@ -0,0 +1,582 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <comphelper/string.hxx>
+#include <comphelper/xmlencode.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <fmtfld.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <breakit.hxx>
+#include <ndtxt.hxx>
+#include <txtfld.hxx>
+#include <fldbas.hxx>
+#include <docufld.hxx>
+#include <flddat.hxx>
+#include <viewopt.hxx>
+#include "htmlfld.hxx"
+#include "wrthtml.hxx"
+#include <rtl/strbuf.hxx>
+#include "css1atr.hxx"
+#include "css1kywd.hxx"
+
+using namespace nsSwDocInfoSubType;
+
+const char *SwHTMLWriter::GetNumFormat( sal_uInt16 nFormat )
+{
+ const char *pFormatStr = nullptr;
+
+ switch( static_cast<SvxNumType>(nFormat) )
+ {
+ case SVX_NUM_CHARS_UPPER_LETTER: pFormatStr = OOO_STRING_SW_HTML_FF_uletter; break;
+ case SVX_NUM_CHARS_LOWER_LETTER: pFormatStr = OOO_STRING_SW_HTML_FF_lletter; break;
+ case SVX_NUM_ROMAN_UPPER: pFormatStr = OOO_STRING_SW_HTML_FF_uroman; break;
+ case SVX_NUM_ROMAN_LOWER: pFormatStr = OOO_STRING_SW_HTML_FF_lroman; break;
+ case SVX_NUM_ARABIC: pFormatStr = OOO_STRING_SW_HTML_FF_arabic; break;
+ case SVX_NUM_NUMBER_NONE: pFormatStr = OOO_STRING_SW_HTML_FF_none; break;
+ case SVX_NUM_CHAR_SPECIAL: pFormatStr = OOO_STRING_SW_HTML_FF_char; break;
+ case SVX_NUM_PAGEDESC: pFormatStr = OOO_STRING_SW_HTML_FF_page; break;
+ case SVX_NUM_CHARS_UPPER_LETTER_N: pFormatStr = OOO_STRING_SW_HTML_FF_ulettern; break;
+ case SVX_NUM_CHARS_LOWER_LETTER_N: pFormatStr = OOO_STRING_SW_HTML_FF_llettern; break;
+ default:
+ ;
+ }
+
+ return pFormatStr;
+}
+
+static SwHTMLWriter& OutHTML_SwField( SwHTMLWriter& rWrt, const SwField* pField,
+ const SwTextNode& rTextNd, sal_Int32 nFieldPos )
+{
+ const SwFieldType* pFieldTyp = pField->GetTyp();
+ SwFieldIds nField = pFieldTyp->Which();
+ sal_uLong nFormat = pField->GetFormat();
+
+ const char *pTypeStr=nullptr, // TYPE
+ *pSubStr=nullptr, // SUBTYPE
+ *pFormatStr=nullptr; // FORMAT (SW)
+ OUString aValue; // VALUE (SW)
+ bool bNumFormat=false; // SDNUM (Number-Formatter-Format)
+ bool bNumValue=false; // SDVAL (Number-Formatter-Value)
+ double dNumValue = 0.0; // SDVAL (Number-Formatter-Value)
+ bool bFixed=false; // SDFIXED
+ OUString aName; // NAME (CUSTOM)
+
+ switch( nField )
+ {
+ case SwFieldIds::ExtUser:
+ pTypeStr = OOO_STRING_SW_HTML_FT_sender;
+ switch( static_cast<SwExtUserSubType>(pField->GetSubType()) )
+ {
+ case EU_COMPANY: pSubStr = OOO_STRING_SW_HTML_FS_company; break;
+ case EU_FIRSTNAME: pSubStr = OOO_STRING_SW_HTML_FS_firstname; break;
+ case EU_NAME: pSubStr = OOO_STRING_SW_HTML_FS_name; break;
+ case EU_SHORTCUT: pSubStr = OOO_STRING_SW_HTML_FS_shortcut; break;
+ case EU_STREET: pSubStr = OOO_STRING_SW_HTML_FS_street; break;
+ case EU_COUNTRY: pSubStr = OOO_STRING_SW_HTML_FS_country; break;
+ case EU_ZIP: pSubStr = OOO_STRING_SW_HTML_FS_zip; break;
+ case EU_CITY: pSubStr = OOO_STRING_SW_HTML_FS_city; break;
+ case EU_TITLE: pSubStr = OOO_STRING_SW_HTML_FS_title; break;
+ case EU_POSITION: pSubStr = OOO_STRING_SW_HTML_FS_position; break;
+ case EU_PHONE_PRIVATE: pSubStr = OOO_STRING_SW_HTML_FS_pphone; break;
+ case EU_PHONE_COMPANY: pSubStr = OOO_STRING_SW_HTML_FS_cphone; break;
+ case EU_FAX: pSubStr = OOO_STRING_SW_HTML_FS_fax; break;
+ case EU_EMAIL: pSubStr = OOO_STRING_SW_HTML_FS_email; break;
+ case EU_STATE: pSubStr = OOO_STRING_SW_HTML_FS_state; break;
+ default:
+ ;
+ }
+ OSL_ENSURE( pSubStr, "unknown sub type for SwExtUserField" );
+ bFixed = static_cast<const SwExtUserField*>(pField)->IsFixed();
+ break;
+
+ case SwFieldIds::Author:
+ pTypeStr = OOO_STRING_SW_HTML_FT_author;
+ switch( static_cast<SwAuthorFormat>(nFormat) & 0xff)
+ {
+ case AF_NAME: pFormatStr = OOO_STRING_SW_HTML_FF_name; break;
+ case AF_SHORTCUT: pFormatStr = OOO_STRING_SW_HTML_FF_shortcut; break;
+ }
+ OSL_ENSURE( pFormatStr, "unknown format for SwAuthorField" );
+ bFixed = static_cast<const SwAuthorField*>(pField)->IsFixed();
+ break;
+
+ case SwFieldIds::DateTime:
+ pTypeStr = OOO_STRING_SW_HTML_FT_datetime;
+ bNumFormat = true;
+ if( static_cast<const SwDateTimeField*>(pField)->IsFixed() )
+ {
+ bNumValue = true;
+ dNumValue = static_cast<const SwDateTimeField*>(pField)->GetValue();
+ }
+ break;
+
+ case SwFieldIds::PageNumber:
+ {
+ pTypeStr = OOO_STRING_SW_HTML_FT_page;
+ SwPageNumSubType eSubType = static_cast<SwPageNumSubType>(pField->GetSubType());
+ switch( eSubType )
+ {
+ case PG_RANDOM: pSubStr = OOO_STRING_SW_HTML_FS_random; break;
+ case PG_NEXT: pSubStr = OOO_STRING_SW_HTML_FS_next; break;
+ case PG_PREV: pSubStr = OOO_STRING_SW_HTML_FS_prev; break;
+ }
+ OSL_ENSURE( pSubStr, "unknown sub type for SwPageNumberField" );
+ pFormatStr = SwHTMLWriter::GetNumFormat( static_cast< sal_uInt16 >(nFormat) );
+
+ if( static_cast<SvxNumType>(nFormat)==SVX_NUM_CHAR_SPECIAL )
+ {
+ aValue = static_cast<const SwPageNumberField *>(pField)->GetUserString();
+ }
+ else
+ {
+ const OUString& rValue = pField->GetPar2();
+ short nValue = static_cast<short>(rValue.toInt32());
+ if( (eSubType == PG_NEXT && nValue!=1) ||
+ (eSubType == PG_PREV && nValue!=-1) ||
+ (eSubType == PG_RANDOM && nValue!=0) )
+ {
+ aValue = rValue;
+ }
+ }
+ }
+ break;
+ case SwFieldIds::DocInfo:
+ {
+ sal_uInt16 nSubType = pField->GetSubType();
+ pTypeStr = OOO_STRING_SW_HTML_FT_docinfo;
+ sal_uInt16 nExtSubType = nSubType & 0x0f00;
+ nSubType &= 0x00ff;
+
+ switch( nSubType )
+ {
+ case DI_TITLE: pSubStr = OOO_STRING_SW_HTML_FS_title; break;
+ case DI_SUBJECT: pSubStr = OOO_STRING_SW_HTML_FS_theme; break;
+ case DI_KEYS: pSubStr = OOO_STRING_SW_HTML_FS_keys; break;
+ case DI_COMMENT: pSubStr = OOO_STRING_SW_HTML_FS_comment; break;
+ case DI_CREATE: pSubStr = OOO_STRING_SW_HTML_FS_create; break;
+ case DI_CHANGE: pSubStr = OOO_STRING_SW_HTML_FS_change; break;
+ case DI_CUSTOM: pSubStr = OOO_STRING_SW_HTML_FS_custom; break;
+ default: pTypeStr = nullptr; break;
+ }
+
+ if( DI_CUSTOM == nSubType ) {
+ aName = static_cast<const SwDocInfoField*>(pField)->GetName();
+ }
+
+ if( DI_CREATE == nSubType || DI_CHANGE == nSubType )
+ {
+ switch( nExtSubType )
+ {
+ case DI_SUB_AUTHOR:
+ pFormatStr = OOO_STRING_SW_HTML_FF_author;
+ break;
+ case DI_SUB_TIME:
+ pFormatStr = OOO_STRING_SW_HTML_FF_time;
+ bNumFormat = true;
+ break;
+ case DI_SUB_DATE:
+ pFormatStr = OOO_STRING_SW_HTML_FF_date;
+ bNumFormat = true;
+ break;
+ }
+ }
+ bFixed = static_cast<const SwDocInfoField*>(pField)->IsFixed();
+ if( bNumFormat )
+ {
+ if( bFixed )
+ {
+ // For a fixed field output the num value too.
+ // Fixed fields without number format shouldn't
+ // exist. See below for OSL_ENSURE().
+ dNumValue = static_cast<const SwDocInfoField*>(pField)->GetValue();
+ bNumValue = true;
+ }
+ else if( !nFormat )
+ {
+ // Non-fixed fields may not have a number format, when
+ // they come from a 4.0-document.
+ bNumFormat = false;
+ }
+ }
+ }
+ break;
+
+ case SwFieldIds::DocStat:
+ {
+ pTypeStr = OOO_STRING_SW_HTML_FT_docstat;
+ sal_uInt16 nSubType = pField->GetSubType();
+ switch( nSubType )
+ {
+ case DS_PAGE: pSubStr = OOO_STRING_SW_HTML_FS_page; break;
+ case DS_PARA: pSubStr = OOO_STRING_SW_HTML_FS_para; break;
+ case DS_WORD: pSubStr = OOO_STRING_SW_HTML_FS_word; break;
+ case DS_CHAR: pSubStr = OOO_STRING_SW_HTML_FS_char; break;
+ case DS_TBL: pSubStr = OOO_STRING_SW_HTML_FS_tbl; break;
+ case DS_GRF: pSubStr = OOO_STRING_SW_HTML_FS_grf; break;
+ case DS_OLE: pSubStr = OOO_STRING_SW_HTML_FS_ole; break;
+ default: pTypeStr = nullptr; break;
+ }
+ pFormatStr = SwHTMLWriter::GetNumFormat( static_cast< sal_uInt16 >(nFormat) );
+ }
+ break;
+
+ case SwFieldIds::Filename:
+ pTypeStr = OOO_STRING_SW_HTML_FT_filename;
+ switch( static_cast<SwFileNameFormat>(nFormat & ~FF_FIXED) )
+ {
+ case FF_NAME: pFormatStr = OOO_STRING_SW_HTML_FF_name; break;
+ case FF_PATHNAME: pFormatStr = OOO_STRING_SW_HTML_FF_pathname; break;
+ case FF_PATH: pFormatStr = OOO_STRING_SW_HTML_FF_path; break;
+ case FF_NAME_NOEXT: pFormatStr = OOO_STRING_SW_HTML_FF_name_noext; break;
+ default:
+ ;
+ }
+ bFixed = static_cast<const SwFileNameField*>(pField)->IsFixed();
+ OSL_ENSURE( pFormatStr, "unknown format for SwFileNameField" );
+ break;
+ default: break;
+ }
+
+ // ReqIF-XHTML doesn't allow <sdfield>.
+ if (rWrt.mbReqIF && pTypeStr)
+ {
+ pTypeStr = nullptr;
+ }
+
+ // Output the <sdfield> tag.
+ if( pTypeStr )
+ {
+ OStringBuffer sOut("<"
+ + rWrt.GetNamespace()
+ + OOO_STRING_SVTOOLS_HTML_sdfield
+ " "
+ OOO_STRING_SVTOOLS_HTML_O_type
+ "="
+ + pTypeStr);
+ if( pSubStr )
+ {
+ sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_subtype "=")
+ + pSubStr);
+ }
+ if( pFormatStr )
+ {
+ sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_format "=")
+ + pFormatStr);
+ }
+ if( !aName.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aName );
+ sOut.append('\"');
+ }
+ if( !aValue.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_value "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aValue );
+ sOut.append('\"');
+ }
+ if( bNumFormat )
+ {
+ OSL_ENSURE( nFormat, "number format is 0" );
+ sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum(
+ bNumValue, dNumValue, nFormat,
+ *rWrt.m_pDoc->GetNumberFormatter()));
+ }
+ if( bFixed )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_sdfixed);
+ }
+ sOut.append('>');
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ }
+
+ // output content of the field
+ OUString const sExpand( pField->ExpandField(true, nullptr) );
+ bool bNeedsCJKProcessing = false;
+ if( !sExpand.isEmpty() )
+ {
+ sal_uInt16 nScriptType = g_pBreakIt->GetBreakIter()->getScriptType( sExpand, 0 );
+ sal_Int32 nPos = g_pBreakIt->GetBreakIter()->endOfScript( sExpand, 0,
+ nScriptType );
+
+ sal_uInt16 nScript =
+ SwHTMLWriter::GetCSS1ScriptForScriptType( nScriptType );
+ if( (nPos < sExpand.getLength() && nPos >= 0) || nScript != rWrt.m_nCSS1Script )
+ {
+ bNeedsCJKProcessing = true;
+ }
+ }
+
+ if( bNeedsCJKProcessing )
+ {
+ //sequence of (start, end) property ranges we want to
+ //query
+ SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_POSTURE, RES_CHRATR_POSTURE,
+ RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>
+ aScriptItemSet( rWrt.m_pDoc->GetAttrPool() );
+ rTextNd.GetParaAttr(aScriptItemSet, nFieldPos, nFieldPos+1);
+
+ sal_uInt16 aWesternWhichIds[4] =
+ { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT };
+ sal_uInt16 aCJKWhichIds[4] =
+ { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT };
+ sal_uInt16 aCTLWhichIds[4] =
+ { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE,
+ RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT };
+
+ sal_uInt16 *pRefWhichIds = nullptr;
+ switch( rWrt.m_nCSS1Script )
+ {
+ case CSS1_OUTMODE_WESTERN:
+ pRefWhichIds = aWesternWhichIds;
+ break;
+ case CSS1_OUTMODE_CJK:
+ pRefWhichIds = aCJKWhichIds;
+ break;
+ case CSS1_OUTMODE_CTL:
+ pRefWhichIds = aCTLWhichIds;
+ break;
+ }
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ sal_uInt16 nScriptType = g_pBreakIt->GetBreakIter()->getScriptType( sExpand, nPos );
+ sal_uInt16 nScript =
+ SwHTMLWriter::GetCSS1ScriptForScriptType( nScriptType );
+ sal_Int32 nEndPos = g_pBreakIt->GetBreakIter()->endOfScript(
+ sExpand, nPos, nScriptType );
+ sal_Int32 nChunkLen = nEndPos - nPos;
+ if( nScript != CSS1_OUTMODE_ANY_SCRIPT &&
+ /* #108791# */ nScript != rWrt.m_nCSS1Script )
+ {
+ sal_uInt16 *pWhichIds = nullptr;
+ switch( nScript )
+ {
+ case CSS1_OUTMODE_WESTERN: pWhichIds = aWesternWhichIds; break;
+ case CSS1_OUTMODE_CJK: pWhichIds = aCJKWhichIds; break;
+ case CSS1_OUTMODE_CTL: pWhichIds = aCTLWhichIds; break;
+ }
+
+ rWrt.m_bTagOn = true;
+
+ const SfxPoolItem *aItems[5];
+ int nItems = 0;
+
+ assert(pWhichIds && pRefWhichIds);
+ if (pWhichIds && pRefWhichIds)
+ {
+ for( int i=0; i<4; i++ )
+ {
+ const SfxPoolItem *pRefItem =
+ aScriptItemSet.GetItem( pRefWhichIds[i] );
+ const SfxPoolItem *pItem =
+ aScriptItemSet.GetItem( pWhichIds[i] );
+ if( pRefItem && pItem &&
+ !(0==i ? swhtml_css1atr_equalFontItems( *pRefItem, *pItem )
+ : *pRefItem == *pItem) )
+ {
+ Out( aHTMLAttrFnTab, *pItem, rWrt );
+ aItems[nItems++] = pItem;
+ }
+ }
+ }
+
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sExpand.copy( nPos, nChunkLen ) );
+
+ rWrt.m_bTagOn = false;
+ while( nItems )
+ Out( aHTMLAttrFnTab, *aItems[--nItems], rWrt );
+
+ }
+ else
+ {
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sExpand.copy( nPos, nChunkLen ) );
+ }
+ nPos = nEndPos;
+ }
+ while( nPos < sExpand.getLength() );
+ }
+ else
+ {
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sExpand );
+ }
+
+ // Output the closing tag.
+ if( pTypeStr )
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_sdfield), false );
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_SwFormatField( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ const SwFormatField & rField = static_cast<const SwFormatField&>(rHt);
+ const SwField* pField = rField.GetField();
+ const SwFieldType* pFieldTyp = pField->GetTyp();
+
+ if( SwFieldIds::SetExp == pFieldTyp->Which() &&
+ (nsSwGetSetExpType::GSE_STRING & pField->GetSubType()) )
+ {
+ const bool bOn = pFieldTyp->GetName() == "HTML_ON";
+ if (!bOn && pFieldTyp->GetName() != "HTML_OFF")
+ return rWrt;
+
+ OUString rText(comphelper::string::strip(pField->GetPar2(), ' '));
+ rWrt.Strm().WriteChar( '<' );
+ if( !bOn )
+ rWrt.Strm().WriteChar( '/' );
+ // TODO: HTML-Tags are written without entities, that for, characters
+ // not contained in the destination encoding are lost!
+ OString sTmp(OUStringToOString(rText,
+ RTL_TEXTENCODING_UTF8));
+ rWrt.Strm().WriteOString( sTmp ).WriteChar( '>' );
+ }
+ else if( SwFieldIds::Postit == pFieldTyp->Which() )
+ {
+ // Comments will be written in ANSI character set, but with system
+ // line breaks.
+ const OUString& rComment = pField->GetPar2();
+ bool bWritten = false;
+
+ if( (rComment.getLength() >= 6 && rComment.startsWith("<") && rComment.endsWith(">") &&
+ o3tl::equalsIgnoreAsciiCase(rComment.subView( 1, 4 ), u"" OOO_STRING_SVTOOLS_HTML_meta) ) ||
+ (rComment.getLength() >= 7 &&
+ rComment.startsWith( "<!--" ) &&
+ rComment.endsWith( "-->" )) )
+ {
+ // directly output META tags
+ OUString sComment(convertLineEnd(rComment, GetSystemLineEnd()));
+ // TODO: HTML-Tags are written without entities, that for,
+ // characters not contained in the destination encoding are lost!
+ OString sTmp(OUStringToOString(sComment,
+ RTL_TEXTENCODING_UTF8));
+ rWrt.Strm().WriteOString( sTmp );
+ bWritten = true;
+ }
+ else if( rComment.getLength() >= 7 &&
+ rComment.endsWith(">") &&
+ rComment.startsWithIgnoreAsciiCase( "HTML:" ) )
+ {
+ OUString sComment(comphelper::string::stripStart(rComment.subView(5), ' '));
+ if( '<' == sComment[0] )
+ {
+ sComment = convertLineEnd(sComment, GetSystemLineEnd());
+ // TODO: HTML-Tags are written without entities, that for,
+ // characters not contained in the destination encoding are
+ // lost!
+ OString sTmp(OUStringToOString(sComment,
+ RTL_TEXTENCODING_UTF8));
+ rWrt.Strm().WriteOString( sTmp );
+ bWritten = true;
+ }
+
+ }
+
+ if( !bWritten )
+ {
+ OUString sComment(convertLineEnd(rComment, GetSystemLineEnd()));
+ // TODO: ???
+ OString sOut =
+ "<" OOO_STRING_SVTOOLS_HTML_comment
+ " " +
+ OUStringToOString(comphelper::string::encodeForXml(sComment), RTL_TEXTENCODING_UTF8) +
+ " -->";
+ rWrt.Strm().WriteOString( sOut );
+ }
+ }
+ else if( SwFieldIds::Script == pFieldTyp->Which() )
+ {
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+
+ bool bURL = static_cast<const SwScriptField *>(pField)->IsCodeURL();
+ const OUString& rType = pField->GetPar1();
+ OUString aContents, aURL;
+ if(bURL)
+ aURL = pField->GetPar2();
+ else
+ aContents = pField->GetPar2();
+
+ // otherwise is the script content itself. Since only JavaScript
+ // is in fields, it must be JavaScript ...:)
+ HTMLOutFuncs::OutScript( rWrt.Strm(), rWrt.GetBaseURL(), aContents, rType, JAVASCRIPT,
+ aURL, nullptr, nullptr );
+
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+ }
+ else
+ {
+ const SwTextField *pTextField = rField.GetTextField();
+ OSL_ENSURE( pTextField, "Where is the txt fld?" );
+ if (pTextField && rWrt.m_pDoc->GetDocShell() && rWrt.m_pDoc->GetDocShell()->GetView())
+ {
+ // ReqIF-XHTML doesn't allow specifying a background color.
+ const SwViewOption* pViewOptions = rWrt.m_pDoc->GetDocShell()->GetView()->GetWrtShell().GetViewOptions();
+ bool bFieldShadings = pViewOptions->IsFieldShadings() && !rWrt.mbReqIF;
+ if (bFieldShadings)
+ {
+ // If there is a text portion background started already, that should have priority.
+ auto it = rWrt.maStartedAttributes.find(RES_CHRATR_BACKGROUND);
+ if (it != rWrt.maStartedAttributes.end())
+ bFieldShadings = it->second <= 0;
+ }
+
+ if (bFieldShadings)
+ {
+ const Color& rColor = pViewOptions->GetFieldShadingsColor();
+ OString sOut(
+ "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
+ " " OOO_STRING_SVTOOLS_HTML_O_style "=\""
+ + sCSS1_P_background
+ + ": "
+ + GetCSS1_Color(rColor)
+ + "\">");
+ rWrt.Strm().WriteOString(sOut);
+ }
+
+ OutHTML_SwField( rWrt, pField, pTextField->GetTextNode(),
+ pTextField->GetStart() );
+
+ if (bFieldShadings)
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
+ }
+ }
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlfly.cxx b/sw/source/filter/html/htmlfly.cxx
new file mode 100644
index 0000000000..c4fe7dfa10
--- /dev/null
+++ b/sw/source/filter/html/htmlfly.cxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "htmlfly.hxx"
+
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <flypos.hxx>
+
+#include <frmfmt.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <osl/diagnose.h>
+
+using namespace css;
+
+SwHTMLPosFlyFrame::SwHTMLPosFlyFrame( const SwPosFlyFrame& rPosFly,
+ const SdrObject *pSdrObj,
+ AllHtmlFlags nFlags ) :
+ m_pFrameFormat( &rPosFly.GetFormat() ),
+ m_pSdrObject( pSdrObj ),
+ m_aNodeIndex( rPosFly.GetNode() ),
+ m_nOrdNum( rPosFly.GetOrdNum() ),
+ m_nContentIndex( 0 ),
+ m_nAllFlags( nFlags )
+{
+ const SwFormatAnchor& rAnchor = rPosFly.GetFormat().GetAnchor();
+ if ((RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId()) ||
+ HtmlPosition::Inside != GetOutPos())
+ return;
+
+ // Output of auto-bound frames will be a character farther back,
+ // because then the position aligns with Netscape.
+ OSL_ENSURE( rAnchor.GetAnchorNode(), "No anchor position?" );
+ if( !rAnchor.GetAnchorNode() )
+ return;
+
+ m_nContentIndex = rAnchor.GetAnchorContentOffset();
+ sal_Int16 eHoriRel = rPosFly.GetFormat().GetHoriOrient().
+ GetRelationOrient();
+ if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel )
+ {
+ const SwContentNode *pCNd = m_aNodeIndex.GetNode().GetContentNode();
+ OSL_ENSURE( pCNd, "No Content-Node at PaM position" );
+ if( pCNd && m_nContentIndex < pCNd->Len() )
+ m_nContentIndex++;
+ }
+}
+
+bool SwHTMLPosFlyFrame::operator<( const SwHTMLPosFlyFrame& rFrame ) const
+{
+ if( m_aNodeIndex.GetIndex() == rFrame.m_aNodeIndex.GetIndex() )
+ {
+ if( m_nContentIndex == rFrame.m_nContentIndex )
+ {
+ if( GetOutPos() == rFrame.GetOutPos() )
+ return m_nOrdNum < rFrame.m_nOrdNum;
+ else
+ return GetOutPos() < rFrame.GetOutPos();
+ }
+ else
+ return m_nContentIndex < rFrame.m_nContentIndex;
+ }
+ else
+ return m_aNodeIndex.GetIndex() < rFrame.m_aNodeIndex.GetIndex();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlfly.hxx b/sw/source/filter/html/htmlfly.hxx
new file mode 100644
index 0000000000..666f7730ec
--- /dev/null
+++ b/sw/source/filter/html/htmlfly.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <o3tl/sorted_vector.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <sal/types.h>
+#include <ndindex.hxx>
+#include <memory>
+
+class SdrObject;
+class SwFrameFormat;
+class SwPosFlyFrame;
+
+// ATTENTION: The values of this enum are used directly in the output table!!!
+enum SwHTMLFrameType
+{
+ HTML_FRMTYPE_TABLE,
+ HTML_FRMTYPE_TABLE_CAP,
+ HTML_FRMTYPE_MULTICOL,
+ HTML_FRMTYPE_EMPTY,
+ HTML_FRMTYPE_TEXT,
+ HTML_FRMTYPE_GRF,
+ HTML_FRMTYPE_PLUGIN,
+ HTML_FRMTYPE_APPLET,
+ HTML_FRMTYPE_IFRAME,
+ HTML_FRMTYPE_OLE,
+ HTML_FRMTYPE_MARQUEE,
+ HTML_FRMTYPE_CONTROL,
+ HTML_FRMTYPE_DRAW,
+ HTML_FRMTYPE_END
+};
+
+enum class HtmlOut {
+ TableNode,
+ GraphicNode,
+ OleNode,
+ Div,
+ MultiCol,
+ Spacer,
+ Control,
+ AMarquee,
+ Marquee,
+ GraphicFrame,
+ OleGraphic,
+ Span
+};
+
+enum class HtmlPosition {
+ Prefix,
+ Before,
+ Inside,
+ Any
+};
+
+enum class HtmlContainerFlags {
+ NONE = 0x00,
+ Span = 0x01,
+ Div = 0x02,
+};
+namespace o3tl {
+ template<> struct typed_flags<HtmlContainerFlags> : is_typed_flags<HtmlContainerFlags, 0x03> {};
+}
+
+struct AllHtmlFlags {
+ HtmlOut nOut;
+ HtmlPosition nPosition;
+ HtmlContainerFlags nContainer;
+};
+
+AllHtmlFlags getHTMLOutFramePageFlyTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode);
+AllHtmlFlags getHTMLOutFrameParaFrameTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode);
+AllHtmlFlags getHTMLOutFrameParaPrtAreaTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode);
+AllHtmlFlags getHTMLOutFrameParaOtherTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode);
+AllHtmlFlags getHTMLOutFrameAsCharTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode);
+
+class SwHTMLPosFlyFrame
+{
+ const SwFrameFormat *m_pFrameFormat; // the frame
+ const SdrObject *m_pSdrObject; // maybe Sdr-Object
+ SwNodeIndex m_aNodeIndex; // Node-Index
+ sal_uInt32 m_nOrdNum; // from SwPosFlyFrame
+ sal_Int32 m_nContentIndex; // its position in content
+ AllHtmlFlags m_nAllFlags;
+
+ SwHTMLPosFlyFrame(const SwHTMLPosFlyFrame&) = delete;
+ SwHTMLPosFlyFrame& operator=(const SwHTMLPosFlyFrame&) = delete;
+
+public:
+
+ SwHTMLPosFlyFrame( const SwPosFlyFrame& rPosFly,
+ const SdrObject *pSdrObj, AllHtmlFlags nAllFlags );
+
+ bool operator<( const SwHTMLPosFlyFrame& ) const;
+
+ const SwFrameFormat& GetFormat() const { return *m_pFrameFormat; }
+ const SdrObject* GetSdrObject() const { return m_pSdrObject; }
+ const SwNodeIndex& GetNdIndex() const { return m_aNodeIndex; }
+ sal_Int32 GetContentIndex() const { return m_nContentIndex; }
+ AllHtmlFlags const & GetOutMode() const { return m_nAllFlags; }
+ HtmlOut GetOutFn() const { return m_nAllFlags.nOut; }
+ HtmlPosition GetOutPos() const { return m_nAllFlags.nPosition; }
+};
+
+class SwHTMLPosFlyFrames
+ : public o3tl::sorted_vector<std::unique_ptr<SwHTMLPosFlyFrame>,
+ o3tl::less_uniqueptr_to<SwHTMLPosFlyFrame>,
+ o3tl::find_partialorder_ptrequals>
+{};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflyt.cxx b/sw/source/filter/html/htmlflyt.cxx
new file mode 100644
index 0000000000..9653c0bb06
--- /dev/null
+++ b/sw/source/filter/html/htmlflyt.cxx
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "htmlfly.hxx"
+#include <svtools/htmlcfg.hxx>
+
+constexpr sal_uInt16 MAX_FRMTYPES = HTML_FRMTYPE_END;
+constexpr sal_uInt16 MAX_BROWSERS = HTML_CFG_MAX + 1;
+
+constexpr AllHtmlFlags aHTMLOutFramePageFlyTable[][MAX_BROWSERS] =
+{
+ {
+ // text frame with table
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE } // Netscape 4!
+ },
+ {
+ // text frame with table and headline
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // column frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::MultiCol, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::MultiCol, HtmlPosition::Prefix, HtmlContainerFlags::Div } // Netscape 4
+ },
+ {
+ // empty text frame
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other text frame
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Div, HtmlPosition::Prefix, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // graphic node
+ { HtmlOut::GraphicNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicNode, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // plug-in
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // applet
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // floating frame
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // other OLE objects
+ { HtmlOut::OleGraphic, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleGraphic, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // marquee
+ { HtmlOut::AMarquee, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::AMarquee, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // controls
+ { HtmlOut::Control, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Control, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ // Netscape disables FROM at controls in absolute position span.
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other character objects
+ { HtmlOut::GraphicFrame, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Prefix, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Prefix, HtmlContainerFlags::Span } // Netscape 4
+ }
+};
+
+AllHtmlFlags getHTMLOutFramePageFlyTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode)
+{
+ static_assert(std::size(aHTMLOutFramePageFlyTable) == MAX_FRMTYPES);
+ assert(eFrameType < HTML_FRMTYPE_END);
+ assert(nExportMode <= HTML_CFG_MAX);
+
+ return aHTMLOutFramePageFlyTable[eFrameType][nExportMode];
+}
+
+constexpr AllHtmlFlags aHTMLOutFrameParaFrameTable[][MAX_BROWSERS] =
+{
+ {
+ // text frame with table
+ { HtmlOut::TableNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::TableNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::TableNode, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // text frame with table and headline
+ { HtmlOut::Div, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Div, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::TableNode, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // column frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::MultiCol, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::MultiCol, HtmlPosition::Before, HtmlContainerFlags::Div } // Netscape 4
+ },
+ {
+ // empty text frame
+ { HtmlOut::Div, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Spacer, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Spacer, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other text frame
+ { HtmlOut::Div, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Div, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Div, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // graphic node
+ { HtmlOut::GraphicNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicNode, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // plug-in
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // applet
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // floating frame
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other OLE objects
+ { HtmlOut::OleGraphic, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleGraphic, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // marquee (for Netscape 4 in container, so that
+ // the marquee appears at the right spot)
+ { HtmlOut::AMarquee, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::AMarquee, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // controls
+ { HtmlOut::Control, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Control, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ // here you could make container out if it (import is missing)
+ { HtmlOut::Control, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other character objects
+ { HtmlOut::GraphicFrame, HtmlPosition::Before, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Before, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Before, HtmlContainerFlags::NONE } // Netscape 4
+ }
+};
+
+AllHtmlFlags getHTMLOutFrameParaFrameTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode)
+{
+ static_assert(std::size(aHTMLOutFrameParaFrameTable) == MAX_FRMTYPES);
+ assert(eFrameType < HTML_FRMTYPE_END);
+ assert(nExportMode <= HTML_CFG_MAX);
+
+ return aHTMLOutFrameParaFrameTable[eFrameType][nExportMode];
+}
+
+constexpr AllHtmlFlags aHTMLOutFrameParaPrtAreaTable[][MAX_BROWSERS] =
+{
+ {
+ // text frame with table
+ { HtmlOut::TableNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::TableNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::TableNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // text frame with table and headline
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // column frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::MultiCol, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::MultiCol, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // empty text frame
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Spacer, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Spacer, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other text frame
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // graphic node
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // plug-in
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // applet
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // floating frame
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other OLE objects
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // marquee
+ { HtmlOut::AMarquee, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::AMarquee, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // controls
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ // here you could make container out if it (import is missing)
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other character objects
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ }
+};
+
+AllHtmlFlags getHTMLOutFrameParaPrtAreaTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode)
+{
+ static_assert(std::size(aHTMLOutFrameParaPrtAreaTable) == MAX_FRMTYPES);
+ assert(eFrameType < HTML_FRMTYPE_END);
+ assert(nExportMode <= HTML_CFG_MAX);
+
+ return aHTMLOutFrameParaPrtAreaTable[eFrameType][nExportMode];
+}
+
+constexpr AllHtmlFlags aHTMLOutFrameParaOtherTable[][MAX_BROWSERS] =
+{
+ {
+ // text frame with table
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // text frame with table and headline
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // column frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::MultiCol, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::MultiCol, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // empty text frame
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other text frame
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Span, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // graphic node
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // plug-in
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // applet
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // floating frame
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // other OLE objects
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // marquee
+ { HtmlOut::AMarquee, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::AMarquee, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ },
+ {
+ // controls
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ // Netscape disables FROM at controls in absolute position span.
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other character objects
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::Span } // Netscape 4
+ }
+};
+
+AllHtmlFlags getHTMLOutFrameParaOtherTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode)
+{
+ static_assert(std::size(aHTMLOutFrameParaOtherTable) == MAX_FRMTYPES);
+ assert(eFrameType < HTML_FRMTYPE_END);
+ assert(nExportMode <= HTML_CFG_MAX);
+
+ return aHTMLOutFrameParaOtherTable[eFrameType][nExportMode];
+}
+
+constexpr AllHtmlFlags aHTMLOutFrameAsCharTable[][MAX_BROWSERS] =
+{
+ {
+ // text frame with table
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // text frame with table and headline
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // column frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::MultiCol, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::MultiCol, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // empty text frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Spacer, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Spacer, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other text frame
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // graphic node
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // plug-in
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // applet
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // floating frame
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleNode, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other OLE objects
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::OleGraphic, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // marquee (can always exported as marquee, because
+ // the content shows up at the right spot
+ { HtmlOut::Marquee, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Marquee, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Marquee, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // controls
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::Control, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ },
+ {
+ // other character objects
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // IE 4
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE }, // SW
+ { HtmlOut::GraphicFrame, HtmlPosition::Inside, HtmlContainerFlags::NONE } // Netscape 4
+ }
+};
+
+AllHtmlFlags getHTMLOutFrameAsCharTable(SwHTMLFrameType eFrameType, sal_uInt16 nExportMode)
+{
+ static_assert(std::size(aHTMLOutFrameAsCharTable) == MAX_FRMTYPES);
+ assert(eFrameType < HTML_FRMTYPE_END);
+ assert(nExportMode <= HTML_CFG_MAX);
+
+ return aHTMLOutFrameAsCharTable[eFrameType][nExportMode];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx
new file mode 100644
index 0000000000..ef3e4b19c3
--- /dev/null
+++ b/sw/source/filter/html/htmlflywriter.cxx
@@ -0,0 +1,2257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <hintids.hxx>
+#include <tools/fract.hxx>
+#include <svl/urihelper.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/event.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmltokn.h>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <svtools/htmlcfg.hxx>
+#include <svtools/HtmlWriter.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/xoutbmp.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdograf.hxx>
+#include <comphelper/xmlencode.hxx>
+
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmturl.hxx>
+#include <fmtfsize.hxx>
+#include <fmtclds.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtinfmt.hxx>
+#include <txtinet.hxx>
+#include <frmatr.hxx>
+#include <grfatr.hxx>
+#include <flypos.hxx>
+#include <ndgrf.hxx>
+
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <swerror.h>
+#include <frmfmt.hxx>
+#include "wrthtml.hxx"
+#include "htmlatr.hxx"
+#include "htmlfly.hxx"
+#include "htmlreqifreader.hxx"
+
+using namespace css;
+
+const HtmlFrmOpts HTML_FRMOPTS_IMG_ALL =
+ HtmlFrmOpts::Alt |
+ HtmlFrmOpts::Size |
+ HtmlFrmOpts::AnySize |
+ HtmlFrmOpts::Border |
+ HtmlFrmOpts::Name;
+const HtmlFrmOpts HTML_FRMOPTS_IMG_CNTNR =
+ HTML_FRMOPTS_IMG_ALL |
+ HtmlFrmOpts::AbsSize;
+const HtmlFrmOpts HTML_FRMOPTS_IMG =
+ HTML_FRMOPTS_IMG_ALL |
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::Space |
+ HtmlFrmOpts::BrClear;
+const HtmlFrmOpts HTML_FRMOPTS_IMG_CSS1 =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSpace;
+
+const HtmlFrmOpts HTML_FRMOPTS_DIV =
+ HtmlFrmOpts::Id |
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSize |
+ HtmlFrmOpts::AnySize |
+ HtmlFrmOpts::AbsSize |
+ HtmlFrmOpts::SSpace |
+ HtmlFrmOpts::SBorder |
+ HtmlFrmOpts::SBackground |
+ HtmlFrmOpts::BrClear |
+ HtmlFrmOpts::Dir;
+
+const HtmlFrmOpts HTML_FRMOPTS_MULTICOL =
+ HtmlFrmOpts::Id |
+ HtmlFrmOpts::Width |
+ HtmlFrmOpts::AnySize |
+ HtmlFrmOpts::AbsSize |
+ HtmlFrmOpts::Dir;
+
+const HtmlFrmOpts HTML_FRMOPTS_MULTICOL_CSS1 =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSize |
+ HtmlFrmOpts::SSpace |
+ HtmlFrmOpts::SBorder|
+ HtmlFrmOpts::SBackground;
+
+const HtmlFrmOpts HTML_FRMOPTS_SPACER =
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::Size |
+ HtmlFrmOpts::AnySize |
+ HtmlFrmOpts::BrClear |
+ HtmlFrmOpts::MarginSize |
+ HtmlFrmOpts::AbsSize;
+
+const HtmlFrmOpts HTML_FRMOPTS_CNTNR =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSpace |
+ HtmlFrmOpts::SWidth |
+ HtmlFrmOpts::AnySize |
+ HtmlFrmOpts::AbsSize |
+ HtmlFrmOpts::SPixSize;
+
+static SwHTMLWriter& OutHTML_FrameFormatTableNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat );
+static SwHTMLWriter& OutHTML_FrameFormatAsMulticol( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
+ bool bInCntnr );
+static SwHTMLWriter& OutHTML_FrameFormatAsSpacer( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat );
+static SwHTMLWriter& OutHTML_FrameFormatAsDivOrSpan( SwHTMLWriter& rWrt,
+ const SwFrameFormat& rFrameFormat, bool bSpan );
+static SwHTMLWriter& OutHTML_FrameFormatAsImage( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat, bool bPNGFallback );
+
+static SwHTMLWriter& OutHTML_FrameFormatGrfNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
+ bool bInCntnr, bool bPNGFallback );
+
+static SwHTMLWriter& OutHTML_FrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ const SdrObject& rSdrObj );
+
+HTMLOutEvent const aImageEventTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_O_SDonload, OOO_STRING_SVTOOLS_HTML_O_onload, SvMacroItemId::OnImageLoadDone },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonabort, OOO_STRING_SVTOOLS_HTML_O_onabort, SvMacroItemId::OnImageLoadCancel },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonerror, OOO_STRING_SVTOOLS_HTML_O_onerror, SvMacroItemId::OnImageLoadError },
+ { nullptr, nullptr, SvMacroItemId::NONE }
+};
+
+HTMLOutEvent const aIMapEventTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver },
+ { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut },
+ { nullptr, nullptr, SvMacroItemId::NONE }
+};
+
+SwHTMLFrameType SwHTMLWriter::GuessFrameType( const SwFrameFormat& rFrameFormat,
+ const SdrObject*& rpSdrObj )
+{
+ SwHTMLFrameType eType;
+
+ if( RES_DRAWFRMFMT == rFrameFormat.Which() )
+ {
+ // use an arbitrary draw object as the default value
+ eType = HTML_FRMTYPE_DRAW;
+
+ const SdrObject *pObj =
+ SwHTMLWriter::GetMarqueeTextObj( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
+ if( pObj )
+ {
+ // scrolling text
+ rpSdrObj = pObj;
+ eType = HTML_FRMTYPE_MARQUEE;
+ }
+ else
+ {
+ pObj = GetHTMLControl( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
+
+ if( pObj )
+ {
+ // Form control
+ rpSdrObj = pObj;
+ eType = HTML_FRMTYPE_CONTROL;
+ }
+ }
+ }
+ else
+ {
+ // use a text frame as the default value
+ eType = HTML_FRMTYPE_TEXT;
+
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
+ const SwNode* pNd = m_pDoc->GetNodes()[ nStt ];
+
+ if( pNd->IsGrfNode() )
+ {
+ // graphic node
+ eType = HTML_FRMTYPE_GRF;
+ }
+ else if( pNd->IsOLENode() )
+ {
+ // applet, plugin, floating frame
+ eType = GuessOLENodeFrameType( *pNd );
+ }
+ else
+ {
+ SwNodeOffset nEnd = m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
+
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+ const SwFormatCol* pFormatCol = rItemSet.GetItemIfSet( RES_COL );
+ if( pFormatCol && pFormatCol->GetNumCols() > 1 )
+ {
+ // frame with columns
+ eType = HTML_FRMTYPE_MULTICOL;
+ }
+ else if( pNd->IsTableNode() )
+ {
+ const SwTableNode *pTableNd = pNd->GetTableNode();
+ SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex();
+
+ if( nTableEnd+1 == nEnd )
+ {
+ // table
+ eType = HTML_FRMTYPE_TABLE;
+ }
+ else if( nTableEnd+2 == nEnd )
+ {
+ // table with caption
+ eType = HTML_FRMTYPE_TABLE_CAP;
+ }
+ }
+ else if( pNd->IsTextNode() )
+ {
+ const SwTextNode *pTextNd = pNd->GetTextNode();
+
+ bool bEmpty = false;
+ if( nStt==nEnd-1 && !pTextNd->Len() )
+ {
+ // empty frame? Only if no frame is
+ // anchored to the text or start node.
+ bEmpty = true;
+ for( auto & pHTMLPosFlyFrame : m_aHTMLPosFlyFrames )
+ {
+ SwNodeOffset nIdx = pHTMLPosFlyFrame->GetNdIndex().GetIndex();
+ bEmpty = (nIdx != nStt) && (nIdx != nStt-1);
+ if( !bEmpty || nIdx > nStt )
+ break;
+ }
+ }
+ if( bEmpty )
+ {
+ std::unique_ptr<SvxBrushItem> aBrush = rFrameFormat.makeBackgroundBrushItem();
+ /// background is not empty, if it has a background graphic
+ /// or its background color is not "no fill"/"auto fill".
+ if( GPOS_NONE != aBrush->GetGraphicPos() ||
+ aBrush->GetColor() != COL_TRANSPARENT )
+ {
+ bEmpty = false;
+ }
+ }
+ if( bEmpty )
+ {
+ // empty frame
+ eType = HTML_FRMTYPE_EMPTY;
+ }
+ else if( m_pDoc->GetNodes()[nStt+1]->IsTableNode() )
+ {
+ const SwTableNode *pTableNd =
+ m_pDoc->GetNodes()[nStt+1]->GetTableNode();
+ if( pTableNd->EndOfSectionIndex()+1 == nEnd )
+ {
+ // table with heading
+ eType = HTML_FRMTYPE_TABLE_CAP;
+ }
+ }
+ }
+ }
+ }
+
+ return eType;
+}
+
+void SwHTMLWriter::CollectFlyFrames()
+{
+ SwPosFlyFrames aFlyPos(
+ m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), true));
+
+ for(const SwPosFlyFrame& rItem : aFlyPos)
+ {
+ const SwFrameFormat& rFrameFormat = rItem.GetFormat();
+ const SdrObject *pSdrObj = nullptr;
+ const SwNode *pAnchorNode;
+ const SwContentNode *pACNd;
+ SwHTMLFrameType eType = GuessFrameType( rFrameFormat, pSdrObj );
+
+ AllHtmlFlags nMode;
+ const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
+ sal_Int16 eHoriRel = rFrameFormat.GetHoriOrient().GetRelationOrient();
+ switch( rAnchor.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ case RndStdIds::FLY_AT_FLY:
+ nMode = getHTMLOutFramePageFlyTable(eType, m_nExportMode);
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ // frames that are anchored to a paragraph are only placed
+ // before the paragraph, if the paragraph has a
+ // spacing.
+ if( text::RelOrientation::FRAME == eHoriRel &&
+ (pAnchorNode = rAnchor.GetAnchorNode()) != nullptr &&
+ (pACNd = pAnchorNode->GetContentNode()) != nullptr )
+ {
+ const SvxTextLeftMarginItem& rTextLeftMargin =
+ pACNd->GetAttr(RES_MARGIN_TEXTLEFT);
+ const SvxRightMarginItem& rRightMargin =
+ pACNd->GetAttr(RES_MARGIN_RIGHT);
+ if (rTextLeftMargin.GetTextLeft() || rRightMargin.GetRight())
+ {
+ nMode = getHTMLOutFrameParaFrameTable(eType, m_nExportMode);
+ break;
+ }
+ }
+ nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode);
+ break;
+
+ case RndStdIds::FLY_AT_CHAR:
+ if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel )
+ nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode);
+ else
+ nMode = getHTMLOutFrameParaOtherTable(eType, m_nExportMode);
+ break;
+
+ default:
+ nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode);
+ break;
+ }
+
+ m_aHTMLPosFlyFrames.insert( std::make_unique<SwHTMLPosFlyFrame>(rItem, pSdrObj, nMode) );
+ }
+}
+
+bool SwHTMLWriter::OutFlyFrame( SwNodeOffset nNdIdx, sal_Int32 nContentIdx, HtmlPosition nPos )
+{
+ bool bFlysLeft = false; // Are there still Flys left at the current node position?
+
+ // OutFlyFrame can be called recursively. Thus, sometimes it is
+ // necessary to start over after a Fly was returned.
+ bool bRestart = true;
+ while( !m_aHTMLPosFlyFrames.empty() && bRestart )
+ {
+ bFlysLeft = bRestart = false;
+
+ // search for the beginning of the FlyFrames
+ size_t i {0};
+
+ for( ; i < m_aHTMLPosFlyFrames.size() &&
+ m_aHTMLPosFlyFrames[i]->GetNdIndex().GetIndex() < nNdIdx; i++ )
+ ;
+ for( ; !bRestart && i < m_aHTMLPosFlyFrames.size() &&
+ m_aHTMLPosFlyFrames[i]->GetNdIndex().GetIndex() == nNdIdx; i++ )
+ {
+ SwHTMLPosFlyFrame *pPosFly = m_aHTMLPosFlyFrames[i].get();
+ if( ( HtmlPosition::Any == nPos ||
+ pPosFly->GetOutPos() == nPos ) &&
+ pPosFly->GetContentIndex() == nContentIdx )
+ {
+ // It is important to remove it first, because additional
+ // elements or the whole array could be deleted on
+ // deeper recursion levels.
+ std::unique_ptr<SwHTMLPosFlyFrame> flyHolder = m_aHTMLPosFlyFrames.erase_extract(i);
+ i--;
+ if( m_aHTMLPosFlyFrames.empty() )
+ {
+ bRestart = true; // not really, only exit the loop
+ }
+
+ HTMLOutFuncs::FlushToAscii(Strm()); // it was one time only; do we still need it?
+
+ OutFrameFormat( pPosFly->GetOutMode(), pPosFly->GetFormat(),
+ pPosFly->GetSdrObject() );
+ switch( pPosFly->GetOutFn() )
+ {
+ case HtmlOut::Div:
+ case HtmlOut::Span:
+ case HtmlOut::MultiCol:
+ case HtmlOut::TableNode:
+ bRestart = true; // It could become recursive here
+ break;
+ default: break;
+ }
+ }
+ else
+ {
+ bFlysLeft = true;
+ }
+ }
+ }
+
+ return bFlysLeft;
+}
+
+void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFrameFormat,
+ const SdrObject *pSdrObject )
+{
+ HtmlContainerFlags nCntnrMode = nMode.nContainer;
+ HtmlOut nOutMode = nMode.nOut;
+ OString aContainerStr;
+ if( HtmlContainerFlags::NONE != nCntnrMode )
+ {
+
+ if (IsLFPossible() && HtmlContainerFlags::Div == nCntnrMode)
+ OutNewLine();
+
+ OStringBuffer sOut;
+ aContainerStr = (HtmlContainerFlags::Div == nCntnrMode)
+ ? OOO_STRING_SVTOOLS_HTML_division
+ : OOO_STRING_SVTOOLS_HTML_span;
+ sOut.append("<" + GetNamespace() + aContainerStr + " "
+ OOO_STRING_SVTOOLS_HTML_O_class "=\""
+ "sd-abs-pos\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ // Output a width for non-draw objects
+ HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_CNTNR;
+
+ // For frames with columns we can also output the background
+ if( HtmlOut::MultiCol == nOutMode )
+ nFrameFlags |= HtmlFrmOpts::SBackground|HtmlFrmOpts::SBorder;
+
+ if( IsHTMLMode( HTMLMODE_BORDER_NONE ) )
+ nFrameFlags |= HtmlFrmOpts::SNoBorder;
+ OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags, pSdrObject );
+ Strm().WriteChar( '>' );
+
+ if( HtmlContainerFlags::Div == nCntnrMode )
+ {
+ IncIndentLevel();
+ SetLFPossible(true);
+ }
+ }
+
+ switch( nOutMode )
+ {
+ case HtmlOut::TableNode: // OK
+ OSL_ENSURE( aContainerStr.isEmpty(), "Table: Container is not supposed to be here" );
+ OutHTML_FrameFormatTableNode( *this, rFrameFormat );
+ break;
+ case HtmlOut::GraphicNode: // OK
+ OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty(), /*bPNGFallback=*/true );
+ break;
+ case HtmlOut::OleNode: // OK
+ OutHTML_FrameFormatOLENode( *this, rFrameFormat, !aContainerStr.isEmpty() );
+ break;
+ case HtmlOut::OleGraphic: // OK
+ OutHTML_FrameFormatOLENodeGrf( *this, rFrameFormat, !aContainerStr.isEmpty() );
+ break;
+ case HtmlOut::Div:
+ case HtmlOut::Span:
+ OSL_ENSURE( aContainerStr.isEmpty(), "Div: Container is not supposed to be here" );
+ OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Span==nOutMode );
+ break;
+ case HtmlOut::MultiCol: // OK
+ OutHTML_FrameFormatAsMulticol( *this, rFrameFormat, !aContainerStr.isEmpty() );
+ break;
+ case HtmlOut::Spacer: // OK
+ OSL_ENSURE( aContainerStr.isEmpty(), "Spacer: Container is not supposed to be here" );
+ OutHTML_FrameFormatAsSpacer( *this, rFrameFormat );
+ break;
+ case HtmlOut::Control: // OK
+ OutHTML_DrawFrameFormatAsControl( *this,
+ static_cast<const SwDrawFrameFormat &>(rFrameFormat), dynamic_cast<const SdrUnoObj&>(*pSdrObject),
+ !aContainerStr.isEmpty() );
+ break;
+ case HtmlOut::AMarquee:
+ OutHTML_FrameFormatAsMarquee( *this, rFrameFormat, *pSdrObject );
+ break;
+ case HtmlOut::Marquee:
+ OSL_ENSURE( aContainerStr.isEmpty(), "Marquee: Container is not supposed to be here" );
+ OutHTML_DrawFrameFormatAsMarquee( *this,
+ static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject );
+ break;
+ case HtmlOut::GraphicFrame:
+ OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true );
+ break;
+ }
+
+ if( HtmlContainerFlags::Div == nCntnrMode )
+ {
+ DecIndentLevel();
+ if (IsLFPossible())
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
+ SetLFPossible(true);
+ }
+ else if( HtmlContainerFlags::Span == nCntnrMode )
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
+}
+
+OString SwHTMLWriter::OutFrameFormatOptions( const SwFrameFormat &rFrameFormat,
+ const OUString& rAlternateText,
+ HtmlFrmOpts nFrameOpts )
+{
+ OString sRetEndTags;
+ OStringBuffer sOut;
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+
+ // Name
+ if( (nFrameOpts & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
+ !rFrameFormat.GetName().isEmpty() )
+ {
+ const char *pStr =
+ (nFrameOpts & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
+ sOut.append(OString::Concat(" ") + pStr + "=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), rFrameFormat.GetName() );
+ sOut.append('\"');
+ }
+
+ // Name
+ if( nFrameOpts & HtmlFrmOpts::Dir )
+ {
+ SvxFrameDirection nDir = GetHTMLDirection( rItemSet );
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ OutDirection( nDir );
+ }
+
+ // ALT
+ if( (nFrameOpts & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_alt "=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), rAlternateText );
+ sOut.append('\"');
+ }
+
+ // ALIGN
+ const char *pStr = nullptr;
+ RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
+ if( (nFrameOpts & HtmlFrmOpts::Align) &&
+ ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) )
+ {
+ // MIB 12.3.98: Wouldn't it be more clever to left-align frames that
+ // are anchored to a paragraph if necessary, instead of inserting them
+ // as being anchored to characters?
+ const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
+ if( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
+ text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
+ text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
+ {
+ pStr = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
+ ? OOO_STRING_SVTOOLS_HTML_AL_right
+ : OOO_STRING_SVTOOLS_HTML_AL_left;
+ }
+ }
+ const SwFormatVertOrient* pVertOrient;
+ if( (nFrameOpts & HtmlFrmOpts::Align) && !pStr &&
+ ( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
+ (RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
+ (pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT )) )
+ {
+ switch( pVertOrient->GetVertOrient() )
+ {
+ case text::VertOrientation::LINE_TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_top; break;
+ case text::VertOrientation::CHAR_TOP:
+ case text::VertOrientation::BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_texttop; break; // not possible
+ case text::VertOrientation::LINE_CENTER:
+ case text::VertOrientation::CHAR_CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break; // not possible
+ case text::VertOrientation::CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
+ case text::VertOrientation::LINE_BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break; // not possible
+ case text::VertOrientation::TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
+ case text::VertOrientation::NONE: break;
+ }
+ }
+ if( pStr )
+ {
+ sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
+ pStr + "\"");
+ }
+
+ // HSPACE and VSPACE
+ Size aTwipSpc( 0, 0 );
+ const SvxLRSpaceItem* pLRSpaceItem;
+ if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
+ (pLRSpaceItem = rItemSet.GetItemIfSet( RES_LR_SPACE )) )
+ {
+ aTwipSpc.setWidth(
+ ( pLRSpaceItem->GetLeft() + pLRSpaceItem->GetRight() ) / 2 );
+ m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
+ }
+ const SvxULSpaceItem* pULSpaceItem;
+ if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
+ (pULSpaceItem = rItemSet.GetItemIfSet( RES_UL_SPACE )) )
+ {
+ aTwipSpc.setHeight(
+ ( pULSpaceItem->GetUpper() + pULSpaceItem->GetLower() ) / 2 );
+ m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
+ }
+
+ if( (nFrameOpts & HtmlFrmOpts::Space) &&
+ (aTwipSpc.Width() || aTwipSpc.Height()) &&
+ !mbReqIF )
+ {
+ Size aPixelSpc = SwHTMLWriter::ToPixel(aTwipSpc);
+
+ if( aPixelSpc.Width() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
+ "=\"" + OString::number(aPixelSpc.Width()) + "\"");
+ }
+
+ if( aPixelSpc.Height() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
+ "=\"" + OString::number(aPixelSpc.Height()) + "\"");
+ }
+ }
+
+ // The spacing must be considered for the size, if the corresponding flag
+ // is set.
+ if( nFrameOpts & HtmlFrmOpts::MarginSize )
+ {
+ aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
+ aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
+ }
+ else
+ {
+ aTwipSpc.setWidth( 0 );
+ aTwipSpc.setHeight( 0 );
+ }
+
+ const SvxBoxItem* pBoxItem;
+ if( !(nFrameOpts & HtmlFrmOpts::AbsSize) &&
+ (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
+ {
+ aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
+ aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
+ aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
+ aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
+ }
+
+ // WIDTH and/or HEIGHT
+ // Output SwFrameSize::Variable/SwFrameSize::Minimum only, if ANYSIZE is set
+ const SwFormatFrameSize *pFSItem;
+ if( (nFrameOpts & HtmlFrmOpts::Size) &&
+ (pFSItem = rItemSet.GetItemIfSet( RES_FRM_SIZE )) &&
+ ( (nFrameOpts & HtmlFrmOpts::AnySize) ||
+ SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
+ {
+ sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
+ sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
+
+ // Size of the object in Twips without margins
+ Size aTwipSz( (nPercentWidth ? 0
+ : pFSItem->GetWidth()-aTwipSpc.Width()),
+ (nPercentHeight ? 0
+ : pFSItem->GetHeight()-aTwipSpc.Height()) );
+
+ OSL_ENSURE( aTwipSz.Width() >= 0 && aTwipSz.Height() >= 0, "Frame size minus spacing < 0!!!???" );
+ if( aTwipSz.Width() < 0 )
+ aTwipSz.setWidth( 0 );
+ if( aTwipSz.Height() < 0 )
+ aTwipSz.setHeight( 0 );
+
+ Size aPixelSz(SwHTMLWriter::ToPixel(aTwipSz));
+
+ if( (nFrameOpts & HtmlFrmOpts::Width) &&
+ ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
+ if( nPercentWidth )
+ sOut.append(OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%");
+ else
+ sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
+ sOut.append("\"");
+ }
+
+ if( (nFrameOpts & HtmlFrmOpts::Height) &&
+ ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"");
+ if( nPercentHeight )
+ sOut.append(OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%");
+ else
+ sOut.append(static_cast<sal_Int32>(aPixelSz.Height()));
+ sOut.append("\"");
+ }
+ }
+
+ if (!sOut.isEmpty())
+ {
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ }
+
+ if (!mbReqIF)
+ {
+ // Insert wrap for graphics that are anchored to a paragraph as
+ // <BR CLEAR=...> in the string
+ const SwFormatSurround* pSurround;
+ if( (nFrameOpts & HtmlFrmOpts::BrClear) &&
+ ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) &&
+ (pSurround = rItemSet.GetItemIfSet( RES_SURROUND )) )
+ {
+ sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
+ pStr = nullptr;
+ css::text::WrapTextMode eSurround = pSurround->GetSurround();
+ bool bAnchorOnly = pSurround->IsAnchorOnly();
+ switch( eHoriOri )
+ {
+ case text::HoriOrientation::RIGHT:
+ {
+ switch( eSurround )
+ {
+ case css::text::WrapTextMode_NONE:
+ case css::text::WrapTextMode_RIGHT:
+ pStr = OOO_STRING_SVTOOLS_HTML_AL_right;
+ break;
+ case css::text::WrapTextMode_LEFT:
+ case css::text::WrapTextMode_PARALLEL:
+ if( bAnchorOnly )
+ m_bClearRight = true;
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+
+ default:
+ // If a frame is centered, it gets left aligned. This
+ // should be taken into account here, too.
+ {
+ switch( eSurround )
+ {
+ case css::text::WrapTextMode_NONE:
+ case css::text::WrapTextMode_LEFT:
+ pStr = OOO_STRING_SVTOOLS_HTML_AL_left;
+ break;
+ case css::text::WrapTextMode_RIGHT:
+ case css::text::WrapTextMode_PARALLEL:
+ if( bAnchorOnly )
+ m_bClearLeft = true;
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+
+ }
+
+ if( pStr )
+ {
+ sOut.append("<" OOO_STRING_SVTOOLS_HTML_linebreak
+ " " OOO_STRING_SVTOOLS_HTML_O_clear
+ "=\"" + OString::Concat(pStr) + "\">");
+ sRetEndTags = sOut.makeStringAndClear();
+ }
+ }
+ }
+ return sRetEndTags;
+}
+
+void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, const OUString& rAlternateText, HtmlFrmOpts nFrameOptions)
+{
+ bool bReplacement = (nFrameOptions & HtmlFrmOpts::Replacement) || mbReqIF;
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+
+ // Name
+ if( (nFrameOptions & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
+ !rFrameFormat.GetName().isEmpty() && !bReplacement)
+ {
+ const char* pAttributeName = (nFrameOptions & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
+ aHtml.attribute(pAttributeName, rFrameFormat.GetName());
+ }
+
+ // Name
+ if (nFrameOptions & HtmlFrmOpts::Dir)
+ {
+ SvxFrameDirection nCurrentDirection = GetHTMLDirection(rItemSet);
+ OString sDirection = convertDirection(nCurrentDirection);
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_dir, sDirection);
+ }
+
+ // alt
+ if( (nFrameOptions & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() && !bReplacement )
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_alt, rAlternateText);
+ }
+
+ // align
+ std::string_view pAlignString;
+ RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
+ if( (nFrameOptions & HtmlFrmOpts::Align) &&
+ ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) && !bReplacement)
+ {
+ const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
+ if( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
+ text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
+ text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
+ {
+ pAlignString = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
+ ? std::string_view(OOO_STRING_SVTOOLS_HTML_AL_right)
+ : std::string_view(OOO_STRING_SVTOOLS_HTML_AL_left);
+ }
+ }
+ const SwFormatVertOrient* pVertOrient;
+ if( (nFrameOptions & HtmlFrmOpts::Align) && pAlignString.empty() &&
+ ( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
+ (RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
+ (pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT )) )
+ {
+ switch( pVertOrient->GetVertOrient() )
+ {
+ case text::VertOrientation::LINE_TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_top; break;
+ case text::VertOrientation::CHAR_TOP:
+ case text::VertOrientation::BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_texttop; break;
+ case text::VertOrientation::LINE_CENTER:
+ case text::VertOrientation::CHAR_CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break;
+ case text::VertOrientation::CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
+ case text::VertOrientation::LINE_BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break;
+ case text::VertOrientation::TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
+ case text::VertOrientation::NONE: break;
+ }
+ }
+ if (!pAlignString.empty() && !bReplacement)
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, pAlignString);
+ }
+
+ // hspace and vspace
+ Size aTwipSpc( 0, 0 );
+ const SvxLRSpaceItem* pLRSpaceItem;
+ if( (nFrameOptions & (HtmlFrmOpts::Space | HtmlFrmOpts::MarginSize)) &&
+ (pLRSpaceItem = rItemSet.GetItemIfSet( RES_LR_SPACE )) )
+ {
+ aTwipSpc.setWidth(
+ ( pLRSpaceItem->GetLeft() + pLRSpaceItem->GetRight() ) / 2 );
+ m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
+ }
+ const SvxULSpaceItem* pULSpaceItem;
+ if( (nFrameOptions & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
+ (pULSpaceItem = rItemSet.GetItemIfSet( RES_UL_SPACE )) )
+ {
+ aTwipSpc.setHeight(
+ ( pULSpaceItem->GetUpper() + pULSpaceItem->GetLower() ) / 2 );
+ m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
+ }
+
+ if( (nFrameOptions & HtmlFrmOpts::Space) &&
+ (aTwipSpc.Width() || aTwipSpc.Height()) &&
+ !mbReqIF )
+ {
+ Size aPixelSpc = SwHTMLWriter::ToPixel(aTwipSpc);
+
+ if (aPixelSpc.Width())
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_hspace, static_cast<sal_Int32>(aPixelSpc.Width()));
+ }
+
+ if (aPixelSpc.Height())
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_vspace, static_cast<sal_Int32>(aPixelSpc.Height()));
+ }
+ }
+
+ // The spacing must be considered for the size, if the corresponding flag
+ // is set.
+ if( nFrameOptions & HtmlFrmOpts::MarginSize )
+ {
+ aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
+ aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
+ }
+ else
+ {
+ aTwipSpc.setWidth( 0 );
+ aTwipSpc.setHeight( 0 );
+ }
+
+ const SvxBoxItem* pBoxItem;
+ if( !(nFrameOptions & HtmlFrmOpts::AbsSize) &&
+ (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
+ {
+ aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
+ aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
+ aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
+ aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
+ }
+
+ // "width" and/or "height"
+ // Only output SwFrameSize::Variable/SwFrameSize::Minimum if ANYSIZE is set
+ std::optional<SwFormatFrameSize> aFrameSize;
+ const SwFormatFrameSize* pFSItem = rItemSet.GetItemIfSet( RES_FRM_SIZE );
+ const SdrObject* pObject;
+ if (!pFSItem && (pObject = rFrameFormat.FindSdrObject()))
+ {
+ // Write size for Draw shapes as well.
+ const tools::Rectangle& rSnapRect = pObject->GetSnapRect();
+ aFrameSize.emplace();
+ aFrameSize->SetWidthSizeType(SwFrameSize::Fixed);
+ aFrameSize->SetWidth(rSnapRect.getOpenWidth());
+ aFrameSize->SetHeightSizeType(SwFrameSize::Fixed);
+ aFrameSize->SetHeight(rSnapRect.getOpenHeight());
+ pFSItem = &*aFrameSize;
+ }
+ if( (nFrameOptions & HtmlFrmOpts::Size) &&
+ pFSItem &&
+ ( (nFrameOptions & HtmlFrmOpts::AnySize) ||
+ SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
+ {
+ sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
+ sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
+
+ // Size of the object in Twips without margins
+ Size aTwipSz( (nPercentWidth && nPercentWidth != 255 ? 0
+ : pFSItem->GetWidth()-aTwipSpc.Width()),
+ (nPercentHeight && nPercentHeight != 255 ? 0
+ : pFSItem->GetHeight()-aTwipSpc.Height()) );
+
+ OSL_ENSURE( aTwipSz.Width() >= 0 && aTwipSz.Height() >= 0, "Frame size minus spacing < 0!!!???" );
+ if( aTwipSz.Width() < 0 )
+ aTwipSz.setWidth( 0 );
+ if( aTwipSz.Height() < 0 )
+ aTwipSz.setHeight( 0 );
+
+ Size aPixelSz(SwHTMLWriter::ToPixel(aTwipSz));
+
+ if( (nFrameOptions & HtmlFrmOpts::Width) &&
+ ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
+ {
+ OString sWidth;
+ if (nPercentWidth)
+ {
+ if (nPercentWidth == 255)
+ {
+ if (nPercentHeight)
+ {
+ sWidth = "auto"_ostr;
+ }
+ else
+ {
+ sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
+ }
+ }
+ else
+ {
+ sWidth = OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%";
+ }
+ }
+ else
+ sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
+ if (!mbXHTML || sWidth != "auto")
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
+ }
+ }
+
+ if( (nFrameOptions & HtmlFrmOpts::Height) &&
+ ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
+ {
+ OString sHeight;
+ if (nPercentHeight)
+ {
+ if (nPercentHeight == 255)
+ {
+ if (nPercentWidth)
+ {
+ sHeight = "auto"_ostr;
+ }
+ else
+ {
+ sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
+ }
+ }
+ else
+ {
+ sHeight = OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%";
+ }
+ }
+ else
+ sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
+ if (!mbXHTML || sHeight != "auto")
+ {
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_height, sHeight);
+ }
+ }
+ }
+
+ if (mbReqIF)
+ return;
+
+ // Insert wrap for graphics that are anchored to a paragraph as
+ // <BR CLEAR=...> in the string
+
+ if( !(nFrameOptions & HtmlFrmOpts::BrClear) )
+ return;
+ RndStdIds nAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
+ if (RndStdIds::FLY_AT_PARA != nAnchorId && RndStdIds::FLY_AT_CHAR != nAnchorId)
+ return;
+ const SwFormatSurround* pSurround = rItemSet.GetItemIfSet( RES_SURROUND );
+ if (!pSurround)
+ return;
+
+ std::string_view pSurroundString;
+
+ sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
+ css::text::WrapTextMode eSurround = pSurround->GetSurround();
+ bool bAnchorOnly = pSurround->IsAnchorOnly();
+ switch( eHoriOri )
+ {
+ case text::HoriOrientation::RIGHT:
+ {
+ switch( eSurround )
+ {
+ case css::text::WrapTextMode_NONE:
+ case css::text::WrapTextMode_RIGHT:
+ pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_right;
+ break;
+ case css::text::WrapTextMode_LEFT:
+ case css::text::WrapTextMode_PARALLEL:
+ if( bAnchorOnly )
+ m_bClearRight = true;
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+
+ default:
+ // If a frame is centered, it gets left aligned. This
+ // should be taken into account here, too.
+ {
+ switch( eSurround )
+ {
+ case css::text::WrapTextMode_NONE:
+ case css::text::WrapTextMode_LEFT:
+ pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_left;
+ break;
+ case css::text::WrapTextMode_RIGHT:
+ case css::text::WrapTextMode_PARALLEL:
+ if( bAnchorOnly )
+ m_bClearLeft = true;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ if (!pSurroundString.empty())
+ {
+ aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
+ aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pSurroundString);
+ aHtml.end();
+ }
+}
+
+namespace
+{
+
+OUString lclWriteOutImap(SwHTMLWriter& rWrt, const SfxItemSet& rItemSet, const SwFrameFormat& rFrameFormat,
+ const Size& rRealSize, const ImageMap* pAltImgMap, const SwFormatURL*& pURLItem)
+{
+ OUString aIMapName;
+
+ // Only consider the URL attribute if no ImageMap was supplied
+ if (!pAltImgMap)
+ pURLItem = rItemSet.GetItemIfSet( RES_URL );
+
+ // write ImageMap
+ const ImageMap* pIMap = pAltImgMap;
+ if( !pIMap && pURLItem )
+ {
+ pIMap = pURLItem->GetMap();
+ }
+
+ if (pIMap)
+ {
+ // make the name unique
+ aIMapName = pIMap->GetName();
+ OUString aNameBase;
+ if (!aIMapName.isEmpty())
+ aNameBase = aIMapName;
+ else
+ aNameBase = OOO_STRING_SVTOOLS_HTML_map;
+
+ if (aIMapName.isEmpty())
+ aIMapName = aNameBase + OUString::number(rWrt.m_nImgMapCnt);
+
+ bool bFound;
+ do
+ {
+ bFound = false;
+ for (const OUString & rImgMapName : rWrt.m_aImgMapNames)
+ {
+ // TODO: Unicode: Comparison is case insensitive for ASCII
+ // characters only now!
+ if (aIMapName.equalsIgnoreAsciiCase(rImgMapName))
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if (bFound)
+ {
+ rWrt.m_nImgMapCnt++;
+ aIMapName = aNameBase + OUString::number( rWrt.m_nImgMapCnt );
+ }
+ } while (bFound);
+
+ bool bScale = false;
+ Fraction aScaleX(1, 1);
+ Fraction aScaleY(1, 1);
+
+ const SwFormatFrameSize& rFrameSize = rFrameFormat.GetFrameSize();
+ const SvxBoxItem& rBox = rFrameFormat.GetBox();
+
+ if (!rFrameSize.GetWidthPercent() && rRealSize.Width())
+ {
+ SwTwips nWidth = rFrameSize.GetWidth();
+ nWidth -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
+
+ OSL_ENSURE( nWidth > 0, "Are there any graphics that are 0 twip wide!?" );
+ if (nWidth <= 0) // should not happen
+ nWidth = 1;
+
+ if (rRealSize.Width() != nWidth)
+ {
+ aScaleX = Fraction(nWidth, rRealSize.Width());
+ bScale = true;
+ }
+ }
+
+ if (!rFrameSize.GetHeightPercent() && rRealSize.Height())
+ {
+ SwTwips nHeight = rFrameSize.GetHeight();
+
+ nHeight -= rBox.CalcLineSpace(SvxBoxItemLine::TOP) + rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM);
+
+ OSL_ENSURE( nHeight > 0, "Are there any graphics that are 0 twip high!?" );
+ if (nHeight <= 0)
+ nHeight = 1;
+
+ if (rRealSize.Height() != nHeight)
+ {
+ aScaleY = Fraction(nHeight, rRealSize.Height());
+ bScale = true;
+ }
+ }
+
+ rWrt.m_aImgMapNames.push_back(aIMapName);
+
+ OString aIndMap, aIndArea;
+ const char *pIndArea = nullptr, *pIndMap = nullptr;
+
+ if (rWrt.IsLFPossible())
+ {
+ rWrt.OutNewLine( true );
+ aIndMap = rWrt.GetIndentString();
+ aIndArea = rWrt.GetIndentString(1);
+ pIndArea = aIndArea.getStr();
+ pIndMap = aIndMap.getStr();
+ }
+
+ if (bScale)
+ {
+ ImageMap aScaledIMap(*pIMap);
+ aScaledIMap.Scale(aScaleX, aScaleY);
+ HTMLOutFuncs::Out_ImageMap( rWrt.Strm(), rWrt.GetBaseURL(), aScaledIMap, aIMapName,
+ aIMapEventTable,
+ rWrt.m_bCfgStarBasic,
+ SAL_NEWLINE_STRING, pIndArea, pIndMap );
+ }
+ else
+ {
+ HTMLOutFuncs::Out_ImageMap( rWrt.Strm(), rWrt.GetBaseURL(), *pIMap, aIMapName,
+ aIMapEventTable,
+ rWrt.m_bCfgStarBasic,
+ SAL_NEWLINE_STRING, pIndArea, pIndMap );
+ }
+ }
+ return aIMapName;
+}
+
+OUString getFrameFormatText(const SwFrameFormat& rFrameFormat)
+{
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ const SwNodeIndex* pSttIx = rFlyContent.GetContentIdx();
+ if (!pSttIx)
+ return {};
+
+ const SwNodeOffset nStt = pSttIx->GetIndex();
+ const auto& nodes = rFrameFormat.GetDoc()->GetNodes();
+ const SwNodeOffset nEnd = nodes[nStt]->EndOfSectionIndex();
+
+ OUStringBuffer result;
+ for (SwNodeOffset i = nStt + 1; i < nEnd; ++i)
+ {
+ if (const auto* pTextNd = nodes[i]->GetTextNode())
+ {
+ if (!result.isEmpty())
+ result.append("\n");
+ result.append(comphelper::string::encodeForXml(pTextNd->GetExpandText(
+ nullptr, 0, -1, true, true, false,
+ ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::HideDeletions
+ | ExpandMode::HideFieldmarkCommands)));
+ }
+ }
+
+ return result.makeStringAndClear();
+}
+
+}
+
+SwHTMLWriter& OutHTML_ImageStart( HtmlWriter& rHtml, SwHTMLWriter& rWrt, const SwFrameFormat &rFrameFormat,
+ const OUString& rGraphicURL,
+ Graphic const & rGraphic, const OUString& rAlternateText,
+ const Size &rRealSize, HtmlFrmOpts nFrameOpts,
+ const char *pMarkType,
+ const ImageMap *pAltImgMap,
+ const OUString& rMimeType )
+{
+ // <object data="..."> instead of <img src="...">
+ bool bReplacement = (nFrameOpts & HtmlFrmOpts::Replacement) || rWrt.mbReqIF;
+
+ if (rWrt.mbSkipImages)
+ return rWrt;
+
+ // if necessary, temporarily close an open attribute
+ if( !rWrt.m_aINetFormats.empty() )
+ {
+ SwFormatINetFormat* pINetFormat = rWrt.m_aINetFormats.back();
+ OutHTML_INetFormat( rWrt, *pINetFormat, false );
+ }
+
+ OUString aGraphicURL( rGraphicURL );
+ if( !rWrt.mbEmbedImages && !HTMLOutFuncs::PrivateURLToInternalImg(aGraphicURL) && !rWrt.mpTempBaseURL )
+ aGraphicURL = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aGraphicURL);
+
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+
+ const SwFormatURL* pURLItem = nullptr;
+ OUString aIMapName = lclWriteOutImap(rWrt, rItemSet, rFrameFormat, rRealSize, pAltImgMap, pURLItem);
+
+ // put img into new line
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+
+ // <a name=...></a>...<img ...>
+ if( pMarkType && !rFrameFormat.GetName().isEmpty() )
+ {
+ rWrt.OutImplicitMark( rFrameFormat.GetName(), pMarkType );
+ }
+
+ // URL -> <a>...<img ... >...</a>
+ const SvxMacroItem *pMacItem = rItemSet.GetItemIfSet(RES_FRMMACRO);
+
+ if (pURLItem || pMacItem)
+ {
+ OUString aMapURL;
+ OUString aName;
+ OUString aTarget;
+
+ if(pURLItem)
+ {
+ aMapURL = pURLItem->GetURL();
+ aName = pURLItem->GetName();
+ aTarget = pURLItem->GetTargetFrameName();
+ }
+
+ bool bEvents = pMacItem && !pMacItem->GetMacroTable().empty();
+
+ if( !aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty() || bEvents )
+ {
+ rHtml.start(OOO_STRING_SVTOOLS_HTML_anchor ""_ostr);
+
+ // Output "href" element if a link or macro exists
+ if( !aMapURL.isEmpty() || bEvents )
+ {
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, rWrt.convertHyperlinkHRefValue(aMapURL));
+ }
+
+ if( !aName.isEmpty() )
+ {
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, aName);
+ }
+
+ if( !aTarget.isEmpty() )
+ {
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, aTarget);
+ }
+
+ if( pMacItem )
+ {
+ const SvxMacroTableDtor& rMacTable = pMacItem->GetMacroTable();
+ if (!rMacTable.empty())
+ {
+ HtmlWriterHelper::applyEvents(rHtml, rMacTable, aAnchorEventTable, rWrt.m_bCfgStarBasic);
+ }
+ }
+ }
+ }
+
+ // <font color = ...>...<img ... >...</font>
+ sal_uInt16 nBorderWidth = 0;
+ const SvxBoxItem* pBoxItem;
+ if( (nFrameOpts & HtmlFrmOpts::Border) &&
+ (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
+ {
+ Size aTwipBorder( 0, 0 );
+ const ::editeng::SvxBorderLine *pColBorderLine = nullptr;
+ const ::editeng::SvxBorderLine *pBorderLine = pBoxItem->GetLeft();
+ if( pBorderLine )
+ {
+ pColBorderLine = pBorderLine;
+ aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
+ }
+
+ pBorderLine = pBoxItem->GetRight();
+ if( pBorderLine )
+ {
+ pColBorderLine = pBorderLine;
+ aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
+ }
+
+ pBorderLine = pBoxItem->GetTop();
+ if( pBorderLine )
+ {
+ pColBorderLine = pBorderLine;
+ aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
+ }
+
+ pBorderLine = pBoxItem->GetBottom();
+ if( pBorderLine )
+ {
+ pColBorderLine = pBorderLine;
+ aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
+ }
+
+ aTwipBorder.setWidth( aTwipBorder.Width() / 2 );
+ aTwipBorder.setHeight( aTwipBorder.Height() / 2 );
+
+ if( (aTwipBorder.Width() || aTwipBorder.Height()) &&
+ Application::GetDefaultDevice() )
+ {
+ Size aPixelBorder = SwHTMLWriter::ToPixel(aTwipBorder);
+
+ if( aPixelBorder.Width() )
+ aPixelBorder.setHeight( 0 );
+
+ nBorderWidth =
+ o3tl::narrowing<sal_uInt16>(aPixelBorder.Width() + aPixelBorder.Height());
+ }
+
+ if( pColBorderLine )
+ {
+ rHtml.start(OOO_STRING_SVTOOLS_HTML_font ""_ostr);
+ HtmlWriterHelper::applyColor(rHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor());
+ }
+ }
+
+ OString aTag(OOO_STRING_SVTOOLS_HTML_image ""_ostr);
+ if (bReplacement)
+ // Write replacement graphic of OLE object as <object>.
+ aTag = OOO_STRING_SVTOOLS_HTML_object ""_ostr;
+ rHtml.start(aTag);
+
+ if(rWrt.mbEmbedImages)
+ {
+ OUString aGraphicInBase64;
+ if (XOutBitmap::GraphicToBase64(rGraphic, aGraphicInBase64))
+ {
+ OString sBuffer(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_data)
+ + ":"
+ + OUStringToOString(aGraphicInBase64, RTL_TEXTENCODING_UTF8));
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer);
+ }
+ else
+ rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ }
+ else
+ {
+ OString sBuffer(OUStringToOString(aGraphicURL, RTL_TEXTENCODING_UTF8));
+ OString aAttribute(OOO_STRING_SVTOOLS_HTML_O_src ""_ostr);
+ if (bReplacement)
+ aAttribute = OOO_STRING_SVTOOLS_HTML_O_data ""_ostr;
+ rHtml.attribute(aAttribute, sBuffer);
+ }
+
+ if (bReplacement)
+ {
+ // Handle XHTML type attribute for OLE replacement images.
+ if (!rMimeType.isEmpty())
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType);
+ }
+
+ // Events
+ if (const SvxMacroItem* pMacroItem = rItemSet.GetItemIfSet(RES_FRMMACRO))
+ {
+ const SvxMacroTableDtor& rMacTable = pMacroItem->GetMacroTable();
+ if (!rMacTable.empty())
+ {
+ HtmlWriterHelper::applyEvents(rHtml, rMacTable, aImageEventTable, rWrt.m_bCfgStarBasic);
+ }
+ }
+
+ // alt, align, width, height, hspace, vspace
+ rWrt.writeFrameFormatOptions(rHtml, rFrameFormat, rAlternateText, nFrameOpts);
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) )
+ rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts );
+
+ if ((nFrameOpts & HtmlFrmOpts::Border) && !bReplacement)
+ {
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth);
+ }
+
+ if( pURLItem && pURLItem->IsServerMap() )
+ {
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap);
+ }
+
+ if( !aIMapName.isEmpty() )
+ {
+ rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, "#" + aIMapName);
+ }
+
+ if (bReplacement)
+ {
+ OUString aAltText = rAlternateText;
+ // In ReqIF mode, output text from the frame instead
+ if (rWrt.mbReqIF)
+ if (OUString aFrameText = getFrameFormatText(rFrameFormat); !aFrameText.isEmpty())
+ aAltText = aFrameText;
+
+ // XHTML object replacement image's alternate text doesn't use the
+ // "alt" attribute.
+ if (aAltText.isEmpty())
+ // Empty alternate text is not valid.
+ rHtml.characters(" ");
+ else
+ rHtml.characters(aAltText.toUtf8());
+ }
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_ImageEnd( HtmlWriter& rHtml, SwHTMLWriter& rWrt )
+{
+ rHtml.flushStack();
+
+ if( !rWrt.m_aINetFormats.empty() )
+ {
+ // There is still an attribute on the stack that has to be reopened
+ SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
+ OutHTML_INetFormat( rWrt, *pINetFormat, true );
+ }
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_BulletImage( SwHTMLWriter& rWrt,
+ const char *pTag,
+ const SvxBrushItem* pBrush,
+ const OUString &rGraphicURL)
+{
+ OUString aGraphicInBase64;
+ OUString aLink;
+ if( pBrush )
+ {
+ aLink = pBrush->GetGraphicLink();
+ if(rWrt.mbEmbedImages || aLink.isEmpty())
+ {
+ const Graphic* pGrf = pBrush->GetGraphic();
+ if( pGrf )
+ {
+ if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
+ {
+ rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ }
+ }
+ }
+ else if(!aLink.isEmpty())
+ {
+ if( rWrt.m_bCfgCpyLinkedGrfs )
+ {
+ rWrt.CopyLocalFileToINet( aLink );
+ }
+
+ }
+ }
+ else if(!rWrt.mbEmbedImages)
+ {
+ aLink = rGraphicURL;
+ }
+ if(!aLink.isEmpty())
+ {
+ if( !HTMLOutFuncs::PrivateURLToInternalImg(aLink) )
+ aLink = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aLink);
+ }
+
+ OStringBuffer sOut;
+ if( pTag )
+ sOut.append(OString::Concat("<") + pTag);
+
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
+ if(!aLink.isEmpty())
+ {
+ sOut.append(OOO_STRING_SVTOOLS_HTML_O_src "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aLink );
+ }
+ else
+ {
+ sOut.append("list-style-image: url("
+ OOO_STRING_SVTOOLS_HTML_O_data ":");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aGraphicInBase64 );
+ sOut.append(");");
+ }
+ sOut.append('\"');
+
+ if (pTag)
+ sOut.append('>');
+ rWrt.Strm().WriteOString( sOut );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_FrameFormatTableNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat )
+{
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
+ SwNodeOffset nEnd = rWrt.m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
+
+ OUString aCaption;
+ bool bTopCaption = false;
+
+ // Not const, because GetTable won't be const sometime later
+ SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ];
+ SwTableNode *pTableNd = pNd->GetTableNode();
+ const SwTextNode *pTextNd = pNd->GetTextNode();
+ if( !pTableNd && pTextNd )
+ {
+ // Table with heading
+ bTopCaption = true;
+ pTableNd = rWrt.m_pDoc->GetNodes()[nStt+1]->GetTableNode();
+ }
+ OSL_ENSURE( pTableNd, "Frame does not contain a table" );
+ if( pTableNd )
+ {
+ SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex();
+ OSL_ENSURE( nTableEnd == nEnd - 1 ||
+ (nTableEnd == nEnd - 2 && !bTopCaption),
+ "Invalid frame content for a table" );
+
+ if( nTableEnd == nEnd - 2 )
+ pTextNd = rWrt.m_pDoc->GetNodes()[nTableEnd+1]->GetTextNode();
+ }
+ if( pTextNd )
+ aCaption = pTextNd->GetText();
+
+ if( pTableNd )
+ {
+ HTMLSaveData aSaveData( rWrt, pTableNd->GetIndex()+1,
+ pTableNd->EndOfSectionIndex(),
+ true, &rFrameFormat );
+ rWrt.m_bOutFlyFrame = true;
+ OutHTML_SwTableNode( rWrt, *pTableNd, &rFrameFormat, &aCaption,
+ bTopCaption );
+ }
+
+ return rWrt;
+}
+
+static SwHTMLWriter & OutHTML_FrameFormatAsMulticol( SwHTMLWriter& rWrt,
+ const SwFrameFormat& rFrameFormat,
+ bool bInCntnr )
+{
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+
+ // Close the current <DL>!
+ rWrt.OutAndSetDefList( 0 );
+
+ // output as Multicol
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+
+ OStringBuffer sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol);
+
+ const SwFormatCol& rFormatCol = rFrameFormat.GetCol();
+
+ // output the number of columns as COLS
+ sal_uInt16 nCols = rFormatCol.GetNumCols();
+ if( nCols )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cols
+ "=\"" + OString::number(nCols) + "\"");
+ }
+
+ // the Gutter width (minimum value) as GUTTER
+ sal_uInt16 nGutter = rFormatCol.GetGutterWidth( true );
+ if( nGutter!=USHRT_MAX )
+ {
+ nGutter = SwHTMLWriter::ToPixel(nGutter);
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter
+ "=\"" + OString::number(nGutter) + "\"");
+ }
+
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ // WIDTH
+ HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MULTICOL;
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
+ nFrameFlags |= HTML_FRMOPTS_MULTICOL_CSS1;
+ rWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags);
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
+ rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
+
+ rWrt.Strm().WriteChar( '>' );
+
+ rWrt.SetLFPossible(true);
+ rWrt.IncIndentLevel(); // indent the content of Multicol
+
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
+ const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
+ OSL_ENSURE( pSttNd, "Where is the start node" );
+
+ {
+ // in a block, so that the old state can be restored in time
+ // before the end
+ HTMLSaveData aSaveData( rWrt, nStt+1,
+ pSttNd->EndOfSectionIndex(),
+ true, &rFrameFormat );
+ rWrt.m_bOutFlyFrame = true;
+ rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
+ }
+
+ rWrt.DecIndentLevel(); // indent the content of Multicol;
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol), false );
+ rWrt.SetLFPossible(true);
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_FrameFormatAsSpacer( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat )
+{
+ // if possible, output a line break before the graphic
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+
+ OString sOut =
+ "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_spacer " "
+ OOO_STRING_SVTOOLS_HTML_O_type "=\""
+ OOO_STRING_SVTOOLS_HTML_SPTYPE_block "\"";
+ rWrt.Strm().WriteOString( sOut );
+
+ // ALIGN, WIDTH, HEIGHT
+ OString aEndTags = rWrt.OutFrameFormatOptions(rFrameFormat, OUString(), HTML_FRMOPTS_SPACER);
+
+ rWrt.Strm().WriteChar( '>' );
+ if( !aEndTags.isEmpty() )
+ rWrt.Strm().WriteOString( aEndTags );
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_FrameFormatAsDivOrSpan( SwHTMLWriter& rWrt,
+ const SwFrameFormat& rFrameFormat, bool bSpan)
+{
+ OString aTag;
+ if( !bSpan )
+ {
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+
+ // Close the current <DL>!
+ rWrt.OutAndSetDefList( 0 );
+ aTag = OOO_STRING_SVTOOLS_HTML_division ""_ostr;
+ }
+ else
+ aTag = OOO_STRING_SVTOOLS_HTML_span ""_ostr;
+
+ // output as DIV
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+
+ OStringBuffer sOut("<" + rWrt.GetNamespace() + aTag);
+
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_DIV;
+ if( rWrt.IsHTMLMode( HTMLMODE_BORDER_NONE ) )
+ nFrameFlags |= HtmlFrmOpts::SNoBorder;
+ OString aEndTags = rWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags);
+ rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
+ rWrt.Strm().WriteChar( '>' );
+
+ rWrt.IncIndentLevel(); // indent the content
+ rWrt.SetLFPossible(true);
+
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
+
+ // Output frame-anchored frames that are anchored to the start node
+ rWrt.OutFlyFrame( nStt, 0, HtmlPosition::Any );
+
+ const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
+ OSL_ENSURE( pSttNd, "Where is the start node" );
+
+ {
+ // in a block, so that the old state can be restored in time
+ // before the end
+ HTMLSaveData aSaveData( rWrt, nStt+1,
+ pSttNd->EndOfSectionIndex(),
+ true, &rFrameFormat );
+ rWrt.m_bOutFlyFrame = true;
+ rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
+ }
+
+ rWrt.DecIndentLevel(); // indent the content of Multicol;
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false );
+
+ if( !aEndTags.isEmpty() )
+ rWrt.Strm().WriteOString( aEndTags );
+
+ return rWrt;
+}
+
+/// Starts the OLE version of an image in the ReqIF + OLE case.
+static void OutHTML_ImageOLEStart(SwHTMLWriter& rWrt, const Graphic& rGraphic,
+ const SwFrameFormat& rFrameFormat)
+{
+ if (!rWrt.mbReqIF || !rWrt.m_bExportImagesAsOLE)
+ return;
+
+ // Write the original image as an RTF fragment.
+ OUString aFileName;
+ if (rWrt.GetOrigFileName())
+ aFileName = *rWrt.GetOrigFileName();
+ INetURLObject aURL(aFileName);
+ OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_"
+ + OUString::number(rGraphic.GetChecksum(), 16);
+ aURL.setBase(aName);
+ aURL.setExtension(u"ole");
+ aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ SvFileStream aOutStream(aFileName, StreamMode::WRITE);
+ if (!SwReqIfReader::WrapGraphicInRtf(rGraphic, rFrameFormat, aOutStream))
+ SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed");
+
+ // Refer to this data.
+ aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName);
+ rWrt.Strm().WriteOString(
+ Concat2View("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object));
+ rWrt.Strm().WriteOString(Concat2View(" data=\"" + aFileName.toUtf8() + "\""));
+ rWrt.Strm().WriteOString(" type=\"text/rtf\"");
+ rWrt.Strm().WriteOString(">");
+ rWrt.OutNewLine();
+}
+
+/// Ends the OLE version of an image in the ReqIF + OLE case.
+static void OutHTML_ImageOLEEnd(SwHTMLWriter& rWrt)
+{
+ if (rWrt.mbReqIF && rWrt.m_bExportImagesAsOLE)
+ {
+ rWrt.Strm().WriteOString(
+ Concat2View("</" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
+ }
+}
+
+static SwHTMLWriter & OutHTML_FrameFormatAsImage( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat, bool bPNGFallback)
+{
+ bool bWritePNGFallback = rWrt.mbReqIF && !rWrt.m_bExportImagesAsOLE && bPNGFallback;
+
+ if (rWrt.mbSkipImages)
+ return rWrt;
+
+ ImageMap aIMap;
+ std::optional<Size> aDPI;
+ if (rWrt.m_nShapeDPI.has_value())
+ {
+ aDPI.emplace(*rWrt.m_nShapeDPI, *rWrt.m_nShapeDPI);
+ }
+ Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap, /*nMaximumQuadraticPixels=*/2100000, aDPI ) );
+
+ if (rWrt.mbReqIF)
+ {
+ // ImageMap doesn't seem to be allowed in reqif.
+ if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(rFrameFormat.FindSdrObject()))
+ {
+ aGraphic = pGrafObj->GetGraphic();
+ }
+ else
+ {
+ // We only have a bitmap, write that as PNG without any fallback.
+ bWritePNGFallback = false;
+ }
+ }
+
+ Size aSz( 0, 0 );
+ OUString GraphicURL;
+ OUString aMimeType("image/jpeg");
+ if(!rWrt.mbEmbedImages)
+ {
+ if( rWrt.GetOrigFileName() )
+ GraphicURL = *rWrt.GetOrigFileName();
+
+ OUString aFilterName("JPG");
+ XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
+
+ if (rWrt.mbReqIF && !bWritePNGFallback)
+ {
+ // Writing image without fallback PNG in ReqIF mode: force PNG output.
+ aFilterName = "PNG";
+ nFlags = XOutFlags::NONE;
+ aMimeType = "image/png";
+ }
+ else if (rWrt.mbReqIF)
+ {
+ // Original format is wanted, don't force JPG.
+ aFilterName.clear();
+ aMimeType.clear();
+ }
+
+ if( aGraphic.GetType() == GraphicType::NONE ||
+ XOutBitmap::WriteGraphic( aGraphic, GraphicURL,
+ aFilterName,
+ nFlags ) != ERRCODE_NONE )
+ {
+ // empty or incorrect, because there is nothing to output
+ rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ return rWrt;
+ }
+
+ GraphicURL = URIHelper::SmartRel2Abs(
+ INetURLObject(rWrt.GetBaseURL()), GraphicURL,
+ URIHelper::GetMaybeFileHdl() );
+
+ }
+ uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
+ if (xGraphic.is() && aMimeType.isEmpty())
+ xGraphic->getPropertyValue("MimeType") >>= aMimeType;
+
+ OutHTML_ImageOLEStart(rWrt, aGraphic, rFrameFormat);
+
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
+ HtmlFrmOpts::GenImgMask, "frame",
+ aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType );
+
+ GfxLink aLink = aGraphic.GetGfxLink();
+ if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
+ {
+ OutHTML_FrameFormatAsImage( rWrt, rFrameFormat, /*bPNGFallback=*/false);
+ }
+
+ OutHTML_ImageEnd(aHtml, rWrt);
+
+ OutHTML_ImageOLEEnd(rWrt);
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_FrameFormatGrfNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ bool bInCntnr, bool bPNGFallback )
+{
+ bool bWritePNGFallback = rWrt.mbReqIF && !rWrt.m_bExportImagesAsOLE && bPNGFallback;
+
+ if (rWrt.mbSkipImages)
+ return rWrt;
+
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
+ SwGrfNode *pGrfNd = rWrt.m_pDoc->GetNodes()[ nStt ]->GetGrfNode();
+ OSL_ENSURE( pGrfNd, "Grf node expected" );
+ if( !pGrfNd )
+ return rWrt;
+
+ HtmlFrmOpts nFrameFlags = bInCntnr ? HTML_FRMOPTS_IMG_CNTNR : HTML_FRMOPTS_IMG;
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
+ nFrameFlags |= HTML_FRMOPTS_IMG_CSS1;
+
+ Graphic aGraphic = pGrfNd->GetGraphic();
+
+ if (aGraphic.GetType() == GraphicType::GdiMetafile)
+ {
+ // We only have a metafile, write that as PNG without any fallback.
+ bWritePNGFallback = false;
+ }
+
+ OUString aGraphicURL;
+ OUString aMimeType;
+ if(!rWrt.mbEmbedImages)
+ {
+ const SwMirrorGrf& rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
+
+ if( !pGrfNd->IsLinkedFile() || MirrorGraph::Dont != rMirror.GetValue() )
+ {
+ // create a (mirrored) jpeg file
+ if( rWrt.GetOrigFileName() )
+ aGraphicURL = *rWrt.GetOrigFileName();
+ else
+ aGraphicURL = rWrt.GetBaseURL();
+ pGrfNd->GetGrf( true );
+
+ XOutFlags nFlags = XOutFlags::UseGifIfSensible |
+ XOutFlags::UseNativeIfPossible;
+ switch( rMirror.GetValue() )
+ {
+ case MirrorGraph::Vertical: nFlags = XOutFlags::MirrorHorz; break;
+ case MirrorGraph::Horizontal: nFlags = XOutFlags::MirrorVert; break;
+ case MirrorGraph::Both:
+ nFlags = XOutFlags::MirrorVert | XOutFlags::MirrorHorz;
+ break;
+ default: break;
+ }
+
+ const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
+ Size aMM100Size = o3tl::convert( rSize.GetSize(),
+ o3tl::Length::twip, o3tl::Length::mm100 );
+
+ OUString aFilterName;
+
+ if (rWrt.mbReqIF)
+ {
+ // In ReqIF mode, do not try to write GIF for other image types
+ nFlags &= ~XOutFlags::UseGifIfSensible;
+ if (!bWritePNGFallback)
+ {
+ // Writing image without fallback PNG in ReqIF mode: force PNG
+ // output.
+ // But don't force it when writing the original format and we'll write PNG inside
+ // that.
+ aFilterName = "PNG";
+ nFlags &= ~XOutFlags::UseNativeIfPossible;
+ }
+ }
+
+ const Graphic& rGraphic = pGrfNd->GetGrf();
+
+ // So that Graphic::IsTransparent() can report true.
+ if (!rGraphic.isAvailable())
+ const_cast<Graphic&>(rGraphic).makeAvailable();
+
+ if (rWrt.mbReqIF && bWritePNGFallback)
+ {
+ // ReqIF: force native data if possible.
+ const std::shared_ptr<VectorGraphicData>& pVectorGraphicData = rGraphic.getVectorGraphicData();
+ if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Svg)
+ {
+ aFilterName = "svg";
+ }
+ else if (rGraphic.GetGfxLink().IsEMF())
+ {
+ aFilterName = "emf";
+ }
+ else if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Wmf)
+ {
+ aFilterName = "wmf";
+ }
+ else if (rGraphic.GetGfxLink().GetType() == GfxLinkType::NativeTif)
+ {
+ aFilterName = "tif";
+ }
+ }
+
+ ErrCode nErr = XOutBitmap::WriteGraphic( rGraphic, aGraphicURL,
+ aFilterName, nFlags, &aMM100Size, nullptr, &aMimeType );
+ if( nErr )
+ {
+ rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ return rWrt;
+ }
+ aGraphicURL = URIHelper::SmartRel2Abs(
+ INetURLObject(rWrt.GetBaseURL()), aGraphicURL,
+ URIHelper::GetMaybeFileHdl() );
+ }
+ else
+ {
+ pGrfNd->GetFileFilterNms( &aGraphicURL, nullptr );
+ if( rWrt.m_bCfgCpyLinkedGrfs )
+ rWrt.CopyLocalFileToINet( aGraphicURL );
+ }
+
+ }
+ uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
+ if (xGraphic.is() && aMimeType.isEmpty())
+ xGraphic->getPropertyValue("MimeType") >>= aMimeType;
+
+ OutHTML_ImageOLEStart(rWrt, aGraphic, rFrameFormat);
+
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
+ pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType );
+
+ GfxLink aLink = aGraphic.GetGfxLink();
+ if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
+ {
+ // Not OLE mode, outer format is not PNG: write inner PNG.
+ OutHTML_FrameFormatGrfNode( rWrt, rFrameFormat,
+ bInCntnr, /*bPNGFallback=*/false );
+ }
+
+ OutHTML_ImageEnd(aHtml, rWrt);
+
+ OutHTML_ImageOLEEnd(rWrt);
+
+ return rWrt;
+}
+
+static SwHTMLWriter& OutHTML_FrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ const SdrObject& rSdrObj )
+{
+ // get the edit engine attributes of the object as SW attributes and
+ // sort them as Hints
+ const SfxItemSet& rFormatItemSet = rFrameFormat.GetAttrSet();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( *rFormatItemSet.GetPool() );
+ SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, &rSdrObj );
+ bool bCfgOutStylesOld = rWrt.m_bCfgOutStyles;
+ rWrt.m_bCfgOutStyles = false;
+ rWrt.m_bTextAttr = true;
+ rWrt.m_bTagOn = true;
+ Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
+ rWrt.m_bTextAttr = false;
+
+ OutHTML_DrawFrameFormatAsMarquee( rWrt,
+ static_cast<const SwDrawFrameFormat &>(rFrameFormat),
+ rSdrObj );
+ rWrt.m_bTextAttr = true;
+ rWrt.m_bTagOn = false;
+ Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
+ rWrt.m_bTextAttr = false;
+ rWrt.m_bCfgOutStyles = bCfgOutStylesOld;
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_HeaderFooter( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ bool bHeader )
+{
+ // output as Multicol
+ rWrt.OutNewLine();
+ OStringBuffer sOut;
+ sOut.append(OOO_STRING_SVTOOLS_HTML_division " "
+ OOO_STRING_SVTOOLS_HTML_O_title "=\"")
+ .append( bHeader ? "header" : "footer" ).append("\"");
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut) );
+
+ rWrt.IncIndentLevel(); // indent the content of Multicol;
+
+ // Piece a spacer for the spacing together. Because the
+ // <DL> or </DL> always produces a space between paragraphs, it is
+ // subtracted if necessary.
+ const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace();
+ sal_uInt16 nSize = bHeader ? rULSpace.GetLower() : rULSpace.GetUpper();
+ rWrt.m_nHeaderFooterSpace = nSize;
+
+ OString aSpacer;
+ if( rWrt.IsHTMLMode(HTMLMODE_VERT_SPACER) &&
+ nSize > HTML_PARSPACE )
+ {
+ nSize -= HTML_PARSPACE;
+ nSize = SwHTMLWriter::ToPixel(nSize);
+
+ aSpacer = OOO_STRING_SVTOOLS_HTML_spacer
+ " " OOO_STRING_SVTOOLS_HTML_O_type
+ "=\"" OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical "\""
+ " " OOO_STRING_SVTOOLS_HTML_O_size
+ "=\"" + OString::number(nSize) + "\"";
+ }
+
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
+ const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
+ OSL_ENSURE( pSttNd, "Where is the start node" );
+
+ if( !bHeader && !aSpacer.isEmpty() )
+ {
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aSpacer) );
+ }
+
+ {
+ // in a block, so that the old state can be restored in time
+ // before the end. pFlyFormat doesn't need to be set here, because
+ // PageDesc attributes cannot occur here
+ HTMLSaveData aSaveData( rWrt, nStt+1,
+ pSttNd->EndOfSectionIndex() );
+
+ if( bHeader )
+ rWrt.m_bOutHeader = true;
+ else
+ rWrt.m_bOutFooter = true;
+
+ rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
+ }
+
+ if( bHeader && !aSpacer.isEmpty() )
+ {
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aSpacer) );
+ }
+
+ rWrt.DecIndentLevel(); // indent the content of Multicol;
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
+
+ rWrt.m_nHeaderFooterSpace = 0;
+
+ return rWrt;
+}
+
+void SwHTMLWriter::AddLinkTarget( std::u16string_view aURL )
+{
+ if( aURL.empty() || aURL[0] != '#' )
+ return;
+
+ // There might be a '|' as delimiter (if the link has been inserted
+ // freshly) or a '%7c' or a '%7C' if the document has been saved and
+ // loaded already.
+ sal_Int32 nPos = aURL.size();
+ bool bFound = false, bEncoded = false;
+ while( !bFound && nPos > 0 )
+ {
+ sal_Unicode c = aURL[ --nPos ];
+ switch( c )
+ {
+ case cMarkSeparator:
+ bFound = true;
+ break;
+ case '%':
+ bFound = (aURL.size() - nPos) >=3 && aURL[ nPos+1 ] == '7';
+ if(bFound)
+ {
+ c = aURL[ nPos+2 ];
+ bFound = (c == 'C' || c == 'c');
+ }
+ if( bFound )
+ bEncoded = true;
+ }
+ }
+ if( !bFound || nPos < 2 ) // at least "#a|..."
+ return;
+
+ aURL = aURL.substr( 1 );
+
+ // nPos-1+1/3 (-1 because of Erase)
+ OUString sCmp = OUString(aURL.substr(bEncoded ? nPos+2 : nPos)).replaceAll(" ","");
+ if( sCmp.isEmpty() )
+ return;
+
+ sCmp = sCmp.toAsciiLowerCase();
+
+ if( sCmp == "region" ||
+ sCmp == "frame" ||
+ sCmp == "graphic" ||
+ sCmp == "ole" ||
+ sCmp == "table" )
+ {
+ // Just remember it in a sorted array
+ OUString aURL2(aURL);
+ if( bEncoded )
+ {
+ aURL2 = aURL2.replaceAt( nPos - 1, 3, rtl::OUStringChar(cMarkSeparator) );
+ }
+ m_aImplicitMarks.insert( aURL2 );
+ }
+ else if( sCmp == "outline" )
+ {
+ // Here, we need position and name. That's why we sort a
+ // sal_uInt16 and a string array ourselves.
+ OUString aOutline( aURL.substr( 0, nPos-1 ) );
+ SwPosition aPos( *m_pCurrentPam->GetPoint() );
+ if( m_pDoc->GotoOutline( aPos, aOutline ) )
+ {
+ SwNodeOffset nIdx = aPos.GetNodeIndex();
+
+ decltype(m_aOutlineMarkPoss)::size_type nIns=0;
+ while( nIns < m_aOutlineMarkPoss.size() &&
+ m_aOutlineMarkPoss[nIns] < nIdx )
+ nIns++;
+
+ m_aOutlineMarkPoss.insert( m_aOutlineMarkPoss.begin()+nIns, nIdx );
+ OUString aURL2(aURL);
+ if( bEncoded )
+ {
+ aURL2 = aURL2.replaceAt( nPos - 1, 3, rtl::OUStringChar(cMarkSeparator) );
+ }
+ m_aOutlineMarks.insert( m_aOutlineMarks.begin()+nIns, aURL2 );
+ }
+ }
+}
+
+void SwHTMLWriter::CollectLinkTargets()
+{
+ const SwTextINetFormat* pTextAttr;
+
+ for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ const SwTextNode* pTextNd;
+
+ if( pINetFormat &&
+ nullptr != ( pTextAttr = pINetFormat->GetTextINetFormat()) &&
+ nullptr != ( pTextNd = pTextAttr->GetpTextNode() ) &&
+ pTextNd->GetNodes().IsDocNodes() )
+ {
+ AddLinkTarget( pINetFormat->GetValue() );
+ }
+ }
+
+ for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_URL))
+ {
+ auto pURL = dynamic_cast<const SwFormatURL*>(pItem);
+ if( pURL )
+ {
+ AddLinkTarget( pURL->GetURL() );
+ const ImageMap *pIMap = pURL->GetMap();
+ if( pIMap )
+ {
+ for( size_t i=0; i<pIMap->GetIMapObjectCount(); ++i )
+ {
+ const IMapObject* pObj = pIMap->GetIMapObject( i );
+ if( pObj )
+ {
+ AddLinkTarget( pObj->GetURL() );
+ }
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlform.cxx b/sw/source/filter/html/htmlform.cxx
new file mode 100644
index 0000000000..d37abddc68
--- /dev/null
+++ b/sw/source/filter/html/htmlform.cxx
@@ -0,0 +1,2455 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <hintids.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <comphelper/string.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/unohelp.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmltokn.h>
+#include <svl/urihelper.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <svx/svdouno.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/form/FormSubmitEncoding.hpp>
+#include <com/sun/star/form/FormSubmitMethod.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/awt/XTextLayoutConstrains.hpp>
+#include <com/sun/star/awt/XLayoutConstrains.hpp>
+#include <com/sun/star/awt/XImageConsumer.hpp>
+#include <com/sun/star/awt/ImageStatus.hpp>
+#include <com/sun/star/form/XImageProducerSupplier.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <fmtanchr.hxx>
+#include <htmltbl.hxx>
+#include <docsh.hxx>
+#include <viewsh.hxx>
+#include <unodraw.hxx>
+#include <unotextrange.hxx>
+
+#include "swcss1.hxx"
+#include "swhtml.hxx"
+#include "htmlform.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+
+const sal_uInt16 TABINDEX_MIN = 0;
+const sal_uInt16 TABINDEX_MAX = 32767;
+
+HTMLOptionEnum<FormSubmitMethod> const aHTMLFormMethodTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_METHOD_get, FormSubmitMethod_GET },
+ { OOO_STRING_SVTOOLS_HTML_METHOD_post, FormSubmitMethod_POST },
+ { nullptr, FormSubmitMethod(0) }
+};
+
+HTMLOptionEnum<FormSubmitEncoding> const aHTMLFormEncTypeTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_ET_url, FormSubmitEncoding_URL },
+ { OOO_STRING_SVTOOLS_HTML_ET_multipart, FormSubmitEncoding_MULTIPART },
+ { OOO_STRING_SVTOOLS_HTML_ET_text, FormSubmitEncoding_TEXT },
+ { nullptr, FormSubmitEncoding(0) }
+};
+
+namespace {
+
+enum HTMLWordWrapMode { HTML_WM_OFF, HTML_WM_HARD, HTML_WM_SOFT };
+
+}
+
+HTMLOptionEnum<HTMLWordWrapMode> const aHTMLTextAreaWrapTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_WW_off, HTML_WM_OFF },
+ { OOO_STRING_SVTOOLS_HTML_WW_hard, HTML_WM_HARD },
+ { OOO_STRING_SVTOOLS_HTML_WW_soft, HTML_WM_SOFT },
+ { OOO_STRING_SVTOOLS_HTML_WW_physical, HTML_WM_HARD },
+ { OOO_STRING_SVTOOLS_HTML_WW_virtual, HTML_WM_SOFT },
+ { nullptr, HTMLWordWrapMode(0) }
+};
+
+static SvMacroItemId aEventTypeTable[] =
+{
+ SvMacroItemId::HtmlOnSubmitForm,
+ SvMacroItemId::HtmlOnResetForm,
+ SvMacroItemId::HtmlOnGetFocus,
+ SvMacroItemId::HtmlOnLoseFocus,
+ SvMacroItemId::HtmlOnClick,
+ SvMacroItemId::HtmlOnClickItem,
+ SvMacroItemId::HtmlOnChange,
+ SvMacroItemId::HtmlOnSelect,
+ SvMacroItemId::NONE
+};
+
+const char * aEventListenerTable[] =
+{
+ "XSubmitListener",
+ "XResetListener",
+ "XFocusListener",
+ "XFocusListener",
+ "XApproveActionListener",
+ "XItemListener",
+ "XChangeListener",
+ ""
+};
+
+const char * aEventMethodTable[] =
+{
+ "approveSubmit",
+ "approveReset",
+ "focusGained",
+ "focusLost",
+ "approveAction",
+ "itemStateChanged",
+ "changed",
+ ""
+};
+
+const char * aEventSDOptionTable[] =
+{
+ OOO_STRING_SVTOOLS_HTML_O_SDonsubmit,
+ OOO_STRING_SVTOOLS_HTML_O_SDonreset,
+ OOO_STRING_SVTOOLS_HTML_O_SDonfocus,
+ OOO_STRING_SVTOOLS_HTML_O_SDonblur,
+ OOO_STRING_SVTOOLS_HTML_O_SDonclick,
+ OOO_STRING_SVTOOLS_HTML_O_SDonclick,
+ OOO_STRING_SVTOOLS_HTML_O_SDonchange,
+ nullptr
+};
+
+const char * aEventOptionTable[] =
+{
+ OOO_STRING_SVTOOLS_HTML_O_onsubmit,
+ OOO_STRING_SVTOOLS_HTML_O_onreset,
+ OOO_STRING_SVTOOLS_HTML_O_onfocus,
+ OOO_STRING_SVTOOLS_HTML_O_onblur,
+ OOO_STRING_SVTOOLS_HTML_O_onclick,
+ OOO_STRING_SVTOOLS_HTML_O_onclick,
+ OOO_STRING_SVTOOLS_HTML_O_onchange,
+ nullptr
+};
+
+class SwHTMLForm_Impl
+{
+ SwDocShell *m_pDocShell;
+
+ SvKeyValueIterator *m_pHeaderAttrs;
+
+ // Cached interfaces
+ uno::Reference< drawing::XDrawPage > m_xDrawPage;
+ uno::Reference< container::XIndexContainer > m_xForms;
+ uno::Reference< drawing::XShapes > m_xShapes;
+ uno::Reference< XMultiServiceFactory > m_xServiceFactory;
+
+ uno::Reference< script::XEventAttacherManager > m_xControlEventManager;
+ uno::Reference< script::XEventAttacherManager > m_xFormEventManager;
+
+ // Context information
+ uno::Reference< container::XIndexContainer > m_xFormComps;
+ uno::Reference< beans::XPropertySet > m_xFCompPropertySet;
+ uno::Reference< drawing::XShape > m_xShape;
+
+ OUString m_sText;
+ std::vector<OUString> m_aStringList;
+ std::vector<OUString> m_aValueList;
+ std::vector<sal_uInt16> m_aSelectedList;
+
+public:
+ explicit SwHTMLForm_Impl( SwDocShell *pDSh ) :
+ m_pDocShell( pDSh ),
+ m_pHeaderAttrs( pDSh ? pDSh->GetHeaderAttributes() : nullptr )
+ {
+ OSL_ENSURE( m_pDocShell, "No DocShell, no Controls" );
+ }
+
+ const uno::Reference< XMultiServiceFactory >& GetServiceFactory();
+ void GetDrawPage();
+ const uno::Reference< drawing::XShapes >& GetShapes();
+ const uno::Reference< script::XEventAttacherManager >& GetControlEventManager();
+ const uno::Reference< script::XEventAttacherManager >& GetFormEventManager();
+ const uno::Reference< container::XIndexContainer >& GetForms();
+
+ const uno::Reference< container::XIndexContainer >& GetFormComps() const
+ {
+ return m_xFormComps;
+ }
+
+ void SetFormComps( const uno::Reference< container::XIndexContainer >& r )
+ {
+ m_xFormComps = r;
+ }
+
+ void ReleaseFormComps() { m_xFormComps = nullptr; m_xControlEventManager = nullptr; }
+
+ const uno::Reference< beans::XPropertySet >& GetFCompPropSet() const
+ {
+ return m_xFCompPropertySet;
+ }
+
+ void SetFCompPropSet( const uno::Reference< beans::XPropertySet >& r )
+ {
+ m_xFCompPropertySet = r;
+ }
+
+ void ReleaseFCompPropSet() { m_xFCompPropertySet = nullptr; }
+
+ const uno::Reference< drawing::XShape >& GetShape() const { return m_xShape; }
+ void SetShape( const uno::Reference< drawing::XShape >& r ) { m_xShape = r; }
+
+ OUString& GetText() { return m_sText; }
+ void EraseText() { m_sText.clear(); }
+
+ std::vector<OUString>& GetStringList() { return m_aStringList; }
+ void EraseStringList()
+ {
+ m_aStringList.clear();
+ }
+
+ std::vector<OUString>& GetValueList() { return m_aValueList; }
+ void EraseValueList()
+ {
+ m_aValueList.clear();
+ }
+
+ std::vector<sal_uInt16>& GetSelectedList() { return m_aSelectedList; }
+ void EraseSelectedList()
+ {
+ m_aSelectedList.clear();
+ }
+
+ SvKeyValueIterator *GetHeaderAttrs() const { return m_pHeaderAttrs; }
+};
+
+const uno::Reference< XMultiServiceFactory >& SwHTMLForm_Impl::GetServiceFactory()
+{
+ if( !m_xServiceFactory.is() && m_pDocShell )
+ {
+ m_xServiceFactory =
+ uno::Reference< XMultiServiceFactory >( m_pDocShell->GetBaseModel(),
+ UNO_QUERY );
+ OSL_ENSURE( m_xServiceFactory.is(),
+ "XServiceFactory not received from model" );
+ }
+ return m_xServiceFactory;
+}
+
+void SwHTMLForm_Impl::GetDrawPage()
+{
+ if( !m_xDrawPage.is() && m_pDocShell )
+ {
+ uno::Reference< drawing::XDrawPageSupplier > xTextDoc( m_pDocShell->GetBaseModel(),
+ UNO_QUERY );
+ OSL_ENSURE( xTextDoc.is(),
+ "drawing::XDrawPageSupplier not received from model" );
+ m_xDrawPage = xTextDoc->getDrawPage();
+ OSL_ENSURE( m_xDrawPage.is(), "drawing::XDrawPage not received" );
+ }
+}
+
+const uno::Reference< container::XIndexContainer >& SwHTMLForm_Impl::GetForms()
+{
+ if( !m_xForms.is() )
+ {
+ GetDrawPage();
+ if( m_xDrawPage.is() )
+ {
+ uno::Reference< XFormsSupplier > xFormsSupplier( m_xDrawPage, UNO_QUERY );
+ OSL_ENSURE( xFormsSupplier.is(),
+ "XFormsSupplier not received from drawing::XDrawPage" );
+
+ uno::Reference< container::XNameContainer > xNameCont =
+ xFormsSupplier->getForms();
+ m_xForms.set( xNameCont, UNO_QUERY );
+
+ OSL_ENSURE( m_xForms.is(), "XForms not received" );
+ }
+ }
+ return m_xForms;
+}
+
+const uno::Reference< drawing::XShapes > & SwHTMLForm_Impl::GetShapes()
+{
+ if( !m_xShapes.is() )
+ {
+ GetDrawPage();
+ if( m_xDrawPage.is() )
+ {
+ m_xShapes = m_xDrawPage;
+ OSL_ENSURE( m_xShapes.is(),
+ "XShapes not received from drawing::XDrawPage" );
+ }
+ }
+ return m_xShapes;
+}
+
+const uno::Reference< script::XEventAttacherManager >&
+ SwHTMLForm_Impl::GetControlEventManager()
+{
+ if( !m_xControlEventManager.is() && m_xFormComps.is() )
+ {
+ m_xControlEventManager =
+ uno::Reference< script::XEventAttacherManager >( m_xFormComps, UNO_QUERY );
+ OSL_ENSURE( m_xControlEventManager.is(),
+ "uno::Reference< XEventAttacherManager > not received from xFormComps" );
+ }
+
+ return m_xControlEventManager;
+}
+
+const uno::Reference< script::XEventAttacherManager >&
+ SwHTMLForm_Impl::GetFormEventManager()
+{
+ if( !m_xFormEventManager.is() )
+ {
+ GetForms();
+ if( m_xForms.is() )
+ {
+ m_xFormEventManager =
+ uno::Reference< script::XEventAttacherManager >( m_xForms, UNO_QUERY );
+ OSL_ENSURE( m_xFormEventManager.is(),
+ "uno::Reference< XEventAttacherManager > not received from xForms" );
+ }
+ }
+
+ return m_xFormEventManager;
+}
+
+namespace {
+
+class SwHTMLImageWatcher :
+ public cppu::WeakImplHelper< awt::XImageConsumer, XEventListener >
+{
+ uno::Reference< drawing::XShape > m_xShape; // the control
+ uno::Reference< XImageProducerSupplier > m_xSrc;
+ uno::Reference< awt::XImageConsumer > m_xThis; // reference to self
+ bool m_bSetWidth;
+ bool m_bSetHeight;
+
+ void clear();
+
+public:
+ SwHTMLImageWatcher( uno::Reference< drawing::XShape > xShape,
+ bool bWidth, bool bHeight );
+
+ // startProduction can not be called in the constructor because it can
+ // destruct itself, hence a separate method.
+ void start() { m_xSrc->getImageProducer()->startProduction(); }
+
+ // UNO binding
+
+ // XImageConsumer
+ virtual void SAL_CALL init( sal_Int32 Width, sal_Int32 Height) override;
+ virtual void SAL_CALL setColorModel(
+ sal_Int16 BitCount, const uno::Sequence< sal_Int32 >& RGBAPal,
+ sal_Int32 RedMask, sal_Int32 GreenMask, sal_Int32 BlueMask,
+ sal_Int32 AlphaMask) override;
+ virtual void SAL_CALL setPixelsByBytes(
+ sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height,
+ const uno::Sequence< sal_Int8 >& ProducerData,
+ sal_Int32 Offset, sal_Int32 Scansize) override;
+ virtual void SAL_CALL setPixelsByLongs(
+ sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height,
+ const uno::Sequence< sal_Int32 >& ProducerData,
+ sal_Int32 Offset, sal_Int32 Scansize) override;
+ virtual void SAL_CALL complete(
+ sal_Int32 Status,
+ const uno::Reference< awt::XImageProducer > & Producer) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+};
+
+}
+
+SwHTMLImageWatcher::SwHTMLImageWatcher(
+ uno::Reference< drawing::XShape > xShape,
+ bool bWidth, bool bHeight ) :
+ m_xShape(std::move( xShape )),
+ m_bSetWidth( bWidth ), m_bSetHeight( bHeight )
+{
+ // Remember the source of the image
+ uno::Reference< drawing::XControlShape > xControlShape( m_xShape, UNO_QUERY );
+ uno::Reference< awt::XControlModel > xControlModel(
+ xControlShape->getControl() );
+ m_xSrc.set( xControlModel, UNO_QUERY );
+ OSL_ENSURE( m_xSrc.is(), "No XImageProducerSupplier" );
+
+ // Register as Event-Listener on the shape to be able to release it on dispose.
+ uno::Reference< XEventListener > xEvtLstnr = static_cast<XEventListener *>(this);
+ uno::Reference< XComponent > xComp( m_xShape, UNO_QUERY );
+ xComp->addEventListener( xEvtLstnr );
+
+ // Lastly we keep a reference to ourselves so we are not destroyed
+ // (should not be necessary since we're still registered elsewhere)
+ m_xThis = static_cast<awt::XImageConsumer *>(this);
+
+ // Register at ImageProducer to retrieve the size...
+ m_xSrc->getImageProducer()->addConsumer( m_xThis );
+}
+
+void SwHTMLImageWatcher::clear()
+{
+ // Unregister on Shape
+ uno::Reference< XEventListener > xEvtLstnr = static_cast<XEventListener *>(this);
+ uno::Reference< XComponent > xComp( m_xShape, UNO_QUERY );
+ xComp->removeEventListener( xEvtLstnr );
+
+ // Unregister on ImageProducer
+ uno::Reference<awt::XImageProducer> xProd = m_xSrc->getImageProducer();
+ if( xProd.is() )
+ xProd->removeConsumer( m_xThis );
+}
+
+void SwHTMLImageWatcher::init( sal_Int32 Width, sal_Int32 Height )
+{
+ OSL_ENSURE( m_bSetWidth || m_bSetHeight,
+ "Width or height has to be adjusted" );
+
+ // If no width or height is given, it is initialized to those of
+ // the empty graphic that is available before the stream of a graphic
+ // that is to be displayed asynchronous is available.
+ if( !Width && !Height )
+ return;
+
+ awt::Size aNewSz;
+ aNewSz.Width = o3tl::convert(Width, o3tl::Length::px, o3tl::Length::mm100);
+ aNewSz.Height = o3tl::convert(Height, o3tl::Length::px, o3tl::Length::mm100);
+
+ if( !m_bSetWidth || !m_bSetHeight )
+ {
+ awt::Size aSz( m_xShape->getSize() );
+ if( m_bSetWidth && aNewSz.Height )
+ {
+ aNewSz.Width *= aSz.Height;
+ aNewSz.Width /= aNewSz.Height;
+ aNewSz.Height = aSz.Height;
+ }
+ if( m_bSetHeight && aNewSz.Width )
+ {
+ aNewSz.Height *= aSz.Width;
+ aNewSz.Height /= aNewSz.Width;
+ aNewSz.Width = aSz.Width;
+ }
+ }
+ if( aNewSz.Width < MINFLY )
+ aNewSz.Width = MINFLY;
+ if( aNewSz.Height < MINFLY )
+ aNewSz.Height = MINFLY;
+
+ m_xShape->setSize( aNewSz );
+ if( m_bSetWidth )
+ {
+ // If the control is anchored to a table, the column have to be recalculated
+
+ // To get to the SwXShape* we need an interface that is implemented by SwXShape
+
+ uno::Reference< beans::XPropertySet > xPropSet( m_xShape, UNO_QUERY );
+ SwXShape *pSwShape = comphelper::getFromUnoTunnel<SwXShape>(xPropSet);
+
+ OSL_ENSURE( pSwShape, "Where is SW-Shape?" );
+ if( pSwShape )
+ {
+ SwFrameFormat *pFrameFormat = pSwShape->GetFrameFormat();
+
+ const SwDoc *pDoc = pFrameFormat->GetDoc();
+ SwNode* pAnchorNode = pFrameFormat->GetAnchor().GetAnchorNode();
+ SwTableNode *pTableNd;
+ if (pAnchorNode && nullptr != (pTableNd = pAnchorNode->FindTableNode()))
+ {
+ const bool bLastGrf = !pTableNd->GetTable().DecGrfsThatResize();
+ SwHTMLTableLayout *pLayout =
+ pTableNd->GetTable().GetHTMLTableLayout();
+ if( pLayout )
+ {
+ const sal_uInt16 nBrowseWidth =
+ pLayout->GetBrowseWidthByTable( *pDoc );
+
+ if ( nBrowseWidth )
+ {
+ pLayout->Resize( nBrowseWidth, true, true,
+ bLastGrf ? HTMLTABLE_RESIZE_NOW
+ : 500 );
+ }
+ }
+ }
+ }
+ }
+
+ // unregister and delete self
+ clear();
+ m_xThis = nullptr;
+}
+
+void SwHTMLImageWatcher::setColorModel(
+ sal_Int16, const Sequence< sal_Int32 >&, sal_Int32, sal_Int32,
+ sal_Int32, sal_Int32 )
+{
+}
+
+void SwHTMLImageWatcher::setPixelsByBytes(
+ sal_Int32, sal_Int32, sal_Int32, sal_Int32,
+ const Sequence< sal_Int8 >&, sal_Int32, sal_Int32 )
+{
+}
+
+void SwHTMLImageWatcher::setPixelsByLongs(
+ sal_Int32, sal_Int32, sal_Int32, sal_Int32,
+ const Sequence< sal_Int32 >&, sal_Int32, sal_Int32 )
+{
+}
+
+void SwHTMLImageWatcher::complete( sal_Int32 Status,
+ const uno::Reference< awt::XImageProducer >& )
+{
+ if( awt::ImageStatus::IMAGESTATUS_ERROR == Status || awt::ImageStatus::IMAGESTATUS_ABORTED == Status )
+ {
+ // unregister and delete self
+ clear();
+ m_xThis = nullptr;
+ }
+}
+
+void SwHTMLImageWatcher::disposing(const lang::EventObject& evt)
+{
+ uno::Reference< awt::XImageConsumer > xTmp;
+
+ // We need to release the shape if it is disposed of
+ if( evt.Source == m_xShape )
+ {
+ clear();
+ xTmp = static_cast<awt::XImageConsumer*>(this);
+ m_xThis = nullptr;
+ }
+}
+
+void SwHTMLParser::DeleteFormImpl()
+{
+ delete m_pFormImpl;
+ m_pFormImpl = nullptr;
+}
+
+static void lcl_html_setFixedFontProperty(
+ const uno::Reference< beans::XPropertySet >& rPropSet )
+{
+ vcl::Font aFixedFont( OutputDevice::GetDefaultFont(
+ DefaultFontType::FIXED, LANGUAGE_ENGLISH_US,
+ GetDefaultFontFlags::OnlyOne ) );
+ Any aTmp;
+ aTmp <<= aFixedFont.GetFamilyName();
+ rPropSet->setPropertyValue("FontName", aTmp );
+
+ aTmp <<= aFixedFont.GetStyleName();
+ rPropSet->setPropertyValue("FontStyleName",
+ aTmp );
+
+ aTmp <<= static_cast<sal_Int16>(aFixedFont.GetFamilyType());
+ rPropSet->setPropertyValue("FontFamily", aTmp );
+
+ aTmp <<= static_cast<sal_Int16>(aFixedFont.GetCharSet());
+ rPropSet->setPropertyValue("FontCharset",
+ aTmp );
+
+ aTmp <<= static_cast<sal_Int16>(aFixedFont.GetPitch());
+ rPropSet->setPropertyValue("FontPitch", aTmp );
+
+ aTmp <<= float(10.0);
+ rPropSet->setPropertyValue("FontHeight", aTmp );
+}
+
+void SwHTMLParser::SetControlSize( const uno::Reference< drawing::XShape >& rShape,
+ const Size& rTextSz,
+ bool bMinWidth,
+ bool bMinHeight )
+{
+ if( !rTextSz.Width() && !rTextSz.Height() && !bMinWidth && !bMinHeight )
+ return;
+
+ // To get to SwXShape* we need an interface that is implemented by SwXShape
+
+ uno::Reference< beans::XPropertySet > xPropSet( rShape, UNO_QUERY );
+
+ SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( !pVSh && !m_nEventId )
+ {
+ // If there is no view shell by now and the doc shell is an internal
+ // one, no view shell will be created. That for, we have to do that of
+ // our own. This happens if a linked section is inserted or refreshed.
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ if( pDocSh )
+ {
+ if ( pDocSh->GetMedium() )
+ {
+ // if there is no hidden property in the MediaDescriptor it should be removed after loading
+ const SfxBoolItem* pHiddenItem = pDocSh->GetMedium()->GetItemSet().GetItem(SID_HIDDEN, false);
+ m_bRemoveHidden = ( pHiddenItem == nullptr || !pHiddenItem->GetValue() );
+ }
+
+ m_pTempViewFrame = SfxViewFrame::LoadHiddenDocument( *pDocSh, SFX_INTERFACE_NONE );
+ CallStartAction();
+ pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ // this ridiculous hack also enables Undo, so turn it off again
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
+ }
+ }
+
+ SwXShape *pSwShape = comphelper::getFromUnoTunnel<SwXShape>(xPropSet);
+
+ OSL_ENSURE( pSwShape, "Where is SW-Shape?" );
+
+ // has to be a Draw-Format
+ SwFrameFormat *pFrameFormat = pSwShape ? pSwShape->GetFrameFormat() : nullptr ;
+ OSL_ENSURE( pFrameFormat && RES_DRAWFRMFMT == pFrameFormat->Which(), "No DrawFrameFormat" );
+
+ // look if a SdrObject exists for it
+ const SdrObject *pObj = pFrameFormat ? pFrameFormat->FindSdrObject() : nullptr;
+ OSL_ENSURE( pObj, "SdrObject not found" );
+ OSL_ENSURE( pObj && SdrInventor::FmForm == pObj->GetObjInventor(), "wrong Inventor" );
+
+ const SdrView* pDrawView = pVSh ? pVSh->GetDrawView() : nullptr;
+
+ const SdrUnoObj *pFormObj = dynamic_cast<const SdrUnoObj*>( pObj );
+ uno::Reference< awt::XControl > xControl;
+ if ( pDrawView && pVSh->GetWin() && pFormObj )
+ xControl = pFormObj->GetUnoControl( *pDrawView, *pVSh->GetWin()->GetOutDev() );
+
+ awt::Size aSz( rShape->getSize() );
+ awt::Size aNewSz( 0, 0 );
+
+ // #i71248# ensure we got a XControl before applying corrections
+ if(xControl.is())
+ {
+ if( bMinWidth || bMinHeight )
+ {
+ uno::Reference< awt::XLayoutConstrains > xLC( xControl, UNO_QUERY );
+ awt::Size aTmpSz( xLC->getPreferredSize() );
+ if( bMinWidth )
+ aNewSz.Width = aTmpSz.Width;
+ if( bMinHeight )
+ aNewSz.Height = aTmpSz.Height;
+ }
+ if( rTextSz.Width() || rTextSz.Height())
+ {
+ uno::Reference< awt::XTextLayoutConstrains > xLC( xControl, UNO_QUERY );
+ OSL_ENSURE( xLC.is(), "no XTextLayoutConstrains" );
+ if( xLC.is() )
+ {
+ awt::Size aTmpSz( rTextSz.Width(), rTextSz.Height() );
+ if( -1 == rTextSz.Width() )
+ {
+ aTmpSz.Width = 0;
+ aTmpSz.Height = m_nSelectEntryCnt;
+ }
+ aTmpSz = xLC->getMinimumSize( static_cast< sal_Int16 >(aTmpSz.Width), static_cast< sal_Int16 >(aTmpSz.Height) );
+ if( rTextSz.Width() )
+ aNewSz.Width = aTmpSz.Width;
+ if( rTextSz.Height() )
+ aNewSz.Height = aTmpSz.Height;
+ }
+ }
+ }
+
+ aNewSz.Width = o3tl::convert(aNewSz.Width, o3tl::Length::px, o3tl::Length::mm100);
+ aNewSz.Height = o3tl::convert(aNewSz.Height, o3tl::Length::px, o3tl::Length::mm100);
+ if( aNewSz.Width )
+ {
+ if( aNewSz.Width < MINLAY )
+ aNewSz.Width = MINLAY;
+ aSz.Width = aNewSz.Width;
+ }
+ if( aNewSz.Height )
+ {
+ if( aNewSz.Height < MINLAY )
+ aNewSz.Height = MINLAY;
+ aSz.Height = aNewSz.Height;
+ }
+
+ rShape->setSize( aSz );
+}
+
+static bool lcl_html_setEvents(
+ const uno::Reference< script::XEventAttacherManager > & rEvtMn,
+ sal_uInt32 nPos, const SvxMacroTableDtor& rMacroTable,
+ const std::vector<OUString>& rUnoMacroTable,
+ const std::vector<OUString>& rUnoMacroParamTable,
+ const OUString& rType )
+{
+ // First the number of events has to be determined
+ sal_Int32 nEvents = 0;
+
+ for( int i = 0; SvMacroItemId::NONE != aEventTypeTable[i]; ++i )
+ {
+ const SvxMacro *pMacro = rMacroTable.Get( aEventTypeTable[i] );
+ // As long as not all events are implemented the table also holds empty strings
+ if( pMacro && aEventListenerTable[i] )
+ nEvents++;
+ }
+ for( const auto &rStr : rUnoMacroTable )
+ {
+ sal_Int32 nIndex = 0;
+ if( o3tl::getToken(rStr, 0, '-', nIndex ).empty() || -1 == nIndex )
+ continue;
+ if( o3tl::getToken(rStr, 0, '-', nIndex ).empty() || -1 == nIndex )
+ continue;
+ if( nIndex < rStr.getLength() )
+ nEvents++;
+ }
+
+ if( 0==nEvents )
+ return false;
+
+ Sequence<script::ScriptEventDescriptor> aDescs( nEvents );
+ script::ScriptEventDescriptor* pDescs = aDescs.getArray();
+ sal_Int32 nEvent = 0;
+
+ for( int i=0; SvMacroItemId::NONE != aEventTypeTable[i]; ++i )
+ {
+ const SvxMacro *pMacro = rMacroTable.Get( aEventTypeTable[i] );
+ if( pMacro && aEventListenerTable[i] )
+ {
+ script::ScriptEventDescriptor& rDesc = pDescs[nEvent++];
+ rDesc.ListenerType =
+ OUString::createFromAscii(aEventListenerTable[i]);
+ rDesc.EventMethod = OUString::createFromAscii(aEventMethodTable[i]);
+ rDesc.ScriptType = pMacro->GetLanguage();
+ rDesc.ScriptCode = pMacro->GetMacName();
+ }
+ }
+
+ for( const auto &rStr : rUnoMacroTable )
+ {
+ sal_Int32 nIndex = 0;
+ OUString sListener( rStr.getToken( 0, '-', nIndex ) );
+ if( sListener.isEmpty() || -1 == nIndex )
+ continue;
+
+ OUString sMethod( rStr.getToken( 0, '-', nIndex ) );
+ if( sMethod.isEmpty() || -1 == nIndex )
+ continue;
+
+ OUString sCode( rStr.copy( nIndex ) );
+ if( sCode.isEmpty() )
+ continue;
+
+ script::ScriptEventDescriptor& rDesc = pDescs[nEvent++];
+ rDesc.ListenerType = sListener;
+ rDesc.EventMethod = sMethod;
+ rDesc.ScriptType = rType;
+ rDesc.ScriptCode = sCode;
+ rDesc.AddListenerParam.clear();
+
+ if(!rUnoMacroParamTable.empty())
+ {
+ OUString sSearch = sListener + "-" +sMethod + "-";
+ sal_Int32 nLen = sSearch.getLength();
+ for(const auto & rParam : rUnoMacroParamTable)
+ {
+ if( rParam.startsWith( sSearch ) && rParam.getLength() > nLen )
+ {
+ rDesc.AddListenerParam = rParam.copy(nLen);
+ break;
+ }
+ }
+ }
+ }
+ rEvtMn->registerScriptEvents( nPos, aDescs );
+ return true;
+}
+
+static void lcl_html_getEvents( const OUString& rOption, std::u16string_view rValue,
+ std::vector<OUString>& rUnoMacroTable,
+ std::vector<OUString>& rUnoMacroParamTable )
+{
+ if( rOption.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_sdevent ) )
+ {
+ OUString aEvent = OUString::Concat(rOption.subView( strlen( OOO_STRING_SVTOOLS_HTML_O_sdevent ) )) +
+ "-" + rValue;
+ rUnoMacroTable.push_back(aEvent);
+ }
+ else if( rOption.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_sdaddparam ) )
+ {
+ OUString aParam = OUString::Concat(rOption.subView( strlen( OOO_STRING_SVTOOLS_HTML_O_sdaddparam ) )) +
+ "-" + rValue;
+ rUnoMacroParamTable.push_back(aParam);
+ }
+}
+
+uno::Reference< drawing::XShape > SwHTMLParser::InsertControl(
+ const uno::Reference< XFormComponent > & rFComp,
+ const uno::Reference< beans::XPropertySet > & rFCompPropSet,
+ const Size& rSize, sal_Int16 eVertOri, sal_Int16 eHoriOri,
+ SfxItemSet& rCSS1ItemSet, SvxCSS1PropertyInfo& rCSS1PropInfo,
+ const SvxMacroTableDtor& rMacroTable, const std::vector<OUString>& rUnoMacroTable,
+ const std::vector<OUString>& rUnoMacroParamTable, bool bSetFCompPropSet,
+ bool bHidden )
+{
+ uno::Reference< drawing::XShape > xShape;
+
+ const uno::Reference< container::XIndexContainer > & rFormComps =
+ m_pFormImpl->GetFormComps();
+ Any aAny( &rFComp, cppu::UnoType<XFormComponent>::get());
+ rFormComps->insertByIndex( rFormComps->getCount(), aAny );
+
+ if( !bHidden )
+ {
+ Any aTmp;
+ sal_Int32 nLeftSpace = 0;
+ sal_Int32 nRightSpace = 0;
+ sal_Int32 nUpperSpace = 0;
+ sal_Int32 nLowerSpace = 0;
+
+ const uno::Reference< XMultiServiceFactory > & rServiceFactory =
+ m_pFormImpl->GetServiceFactory();
+ if( !rServiceFactory.is() )
+ return xShape;
+
+ uno::Reference< XInterface > xCreate = rServiceFactory->createInstance( "com.sun.star.drawing.ControlShape" );
+ if( !xCreate.is() )
+ return xShape;
+
+ xShape.set( xCreate, UNO_QUERY );
+
+ OSL_ENSURE( xShape.is(), "XShape not received" );
+ awt::Size aTmpSz;
+ aTmpSz.Width = rSize.Width();
+ aTmpSz.Height = rSize.Height();
+ xShape->setSize( aTmpSz );
+
+ uno::Reference< beans::XPropertySet > xShapePropSet( xCreate, UNO_QUERY );
+
+ // set left/right border
+ // note: parser never creates SvxLeftMarginItem! must be converted
+ if (const SvxTextLeftMarginItem *const pLeft = rCSS1ItemSet.GetItemIfSet(RES_MARGIN_TEXTLEFT))
+ {
+ if( rCSS1PropInfo.m_bLeftMargin )
+ {
+ // should be SvxLeftMarginItem... "cast" it
+ nLeftSpace = convertTwipToMm100(pLeft->GetTextLeft());
+ rCSS1PropInfo.m_bLeftMargin = false;
+ }
+ rCSS1ItemSet.ClearItem(RES_MARGIN_TEXTLEFT);
+ }
+ if (const SvxRightMarginItem *const pRight = rCSS1ItemSet.GetItemIfSet(RES_MARGIN_RIGHT))
+ {
+ if( rCSS1PropInfo.m_bRightMargin )
+ {
+ nRightSpace = convertTwipToMm100(pRight->GetRight());
+ rCSS1PropInfo.m_bRightMargin = false;
+ }
+ rCSS1ItemSet.ClearItem(RES_MARGIN_RIGHT);
+ }
+ if( nLeftSpace || nRightSpace )
+ {
+ Any aAny2;
+ aAny2 <<= nLeftSpace;
+ xShapePropSet->setPropertyValue("LeftMargin", aAny2 );
+
+ aAny2 <<= nRightSpace;
+ xShapePropSet->setPropertyValue("RightMargin", aAny2 );
+ }
+
+ // set upper/lower border
+ if( const SvxULSpaceItem *pULItem = rCSS1ItemSet.GetItemIfSet( RES_UL_SPACE ) )
+ {
+ // Flatten first line indent
+ if( rCSS1PropInfo.m_bTopMargin )
+ {
+ nUpperSpace = convertTwipToMm100( pULItem->GetUpper() );
+ rCSS1PropInfo.m_bTopMargin = false;
+ }
+ if( rCSS1PropInfo.m_bBottomMargin )
+ {
+ nLowerSpace = convertTwipToMm100( pULItem->GetLower() );
+ rCSS1PropInfo.m_bBottomMargin = false;
+ }
+
+ rCSS1ItemSet.ClearItem( RES_UL_SPACE );
+ }
+ if( nUpperSpace || nLowerSpace )
+ {
+ uno::Any aAny2;
+ aAny2 <<= nUpperSpace;
+ xShapePropSet->setPropertyValue("TopMargin", aAny2 );
+
+ aAny2 <<= nLowerSpace;
+ xShapePropSet->setPropertyValue("BottomMargin", aAny2 );
+ }
+
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
+ rFCompPropSet->getPropertySetInfo();
+ OUString sPropName = "BackgroundColor";
+ const SvxBrushItem* pBrushItem = rCSS1ItemSet.GetItemIfSet( RES_BACKGROUND );
+ if( pBrushItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ const Color &rColor = pBrushItem->GetColor();
+ /// copy color, if color is not "no fill"/"auto fill"
+ if( rColor != COL_TRANSPARENT )
+ {
+ /// copy complete color with transparency
+ aTmp <<= rColor;
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ }
+
+ sPropName = "TextColor";
+ const SvxColorItem* pColorItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_COLOR );
+ if( pColorItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int32>(pColorItem->GetValue().GetRGBColor());
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ sPropName = "FontHeight";
+ const SvxFontHeightItem* pFontHeightItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_FONTSIZE );
+ if( pFontHeightItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ float fVal = static_cast< float >( pFontHeightItem->GetHeight() / 20.0 );
+ aTmp <<= fVal;
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ if( const SvxFontItem* pFontItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_FONT ) )
+ {
+ sPropName = "FontName";
+ if( xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= pFontItem->GetFamilyName();
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+ sPropName = "FontStyleName";
+ if( xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= pFontItem->GetStyleName();
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+ sPropName = "FontFamily";
+ if( xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int16>(pFontItem->GetFamily()) ;
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+ sPropName = "FontCharset";
+ if( xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int16>(pFontItem->GetCharSet()) ;
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+ sPropName = "FontPitch";
+ if( xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int16>(pFontItem->GetPitch()) ;
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+ }
+
+ sPropName = "FontWeight";
+ const SvxWeightItem* pWeightItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_WEIGHT );
+ if( pWeightItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ float fVal = vcl::unohelper::ConvertFontWeight(
+ pWeightItem->GetWeight() );
+ aTmp <<= fVal;
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ sPropName = "FontSlant";
+ const SvxPostureItem* pPostureItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_POSTURE );
+ if( pPostureItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int16>(pPostureItem->GetPosture());
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ sPropName = "FontUnderline";
+ const SvxUnderlineItem* pUnderlineItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_UNDERLINE );
+ if( pUnderlineItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int16>(pUnderlineItem->GetLineStyle());
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ sPropName = "FontStrikeout";
+ const SvxCrossedOutItem* pCrossedOutItem = rCSS1ItemSet.GetItemIfSet( RES_CHRATR_CROSSEDOUT );
+ if( pCrossedOutItem && xPropSetInfo->hasPropertyByName( sPropName ) )
+ {
+ aTmp <<= static_cast<sal_Int16>(pCrossedOutItem->GetStrikeout());
+ rFCompPropSet->setPropertyValue( sPropName, aTmp );
+ }
+
+ uno::Reference< text::XTextRange > xTextRg;
+ text::TextContentAnchorType nAnchorType = text::TextContentAnchorType_AS_CHARACTER;
+ bool bSetPos = false, bSetSurround = false;
+ sal_Int32 nXPos = 0, nYPos = 0;
+ text::WrapTextMode nSurround = text::WrapTextMode_NONE;
+ if( SVX_CSS1_POS_ABSOLUTE == rCSS1PropInfo.m_ePosition &&
+ SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eLeftType &&
+ SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eTopType )
+ {
+ const SwStartNode *pFlySttNd =
+ m_pPam->GetPoint()->GetNode().FindFlyStartNode();
+
+ if( pFlySttNd )
+ {
+ nAnchorType = text::TextContentAnchorType_AT_FRAME;
+ SwPaM aPaM( *pFlySttNd );
+
+ uno::Reference< text::XText > xDummyTextRef; // dirty, but works according to OS...
+ xTextRg = new SwXTextRange( aPaM, xDummyTextRef );
+ }
+ else
+ {
+ nAnchorType = text::TextContentAnchorType_AT_PAGE;
+ }
+ nXPos = convertTwipToMm100( rCSS1PropInfo.m_nLeft ) + nLeftSpace;
+ nYPos = convertTwipToMm100( rCSS1PropInfo.m_nTop ) + nUpperSpace;
+ bSetPos = true;
+
+ nSurround = text::WrapTextMode_THROUGH;
+ bSetSurround = true;
+ }
+ else if( SvxAdjust::Left == rCSS1PropInfo.m_eFloat ||
+ text::HoriOrientation::LEFT == eHoriOri )
+ {
+ nAnchorType = text::TextContentAnchorType_AT_PARAGRAPH;
+ nXPos = nLeftSpace;
+ nYPos = nUpperSpace;
+ bSetPos = true;
+ nSurround = text::WrapTextMode_RIGHT;
+ bSetSurround = true;
+ }
+ else if( text::VertOrientation::NONE != eVertOri )
+ {
+ sal_Int16 nVertOri = text::VertOrientation::NONE;
+ switch( eVertOri )
+ {
+ case text::VertOrientation::TOP:
+ nVertOri = text::VertOrientation::TOP;
+ break;
+ case text::VertOrientation::CENTER:
+ nVertOri = text::VertOrientation::CENTER;
+ break;
+ case text::VertOrientation::BOTTOM:
+ nVertOri = text::VertOrientation::BOTTOM;
+ break;
+ case text::VertOrientation::CHAR_TOP:
+ nVertOri = text::VertOrientation::CHAR_TOP;
+ break;
+ case text::VertOrientation::CHAR_CENTER:
+ nVertOri = text::VertOrientation::CHAR_CENTER;
+ break;
+ case text::VertOrientation::CHAR_BOTTOM:
+ nVertOri = text::VertOrientation::CHAR_BOTTOM;
+ break;
+ case text::VertOrientation::LINE_TOP:
+ nVertOri = text::VertOrientation::LINE_TOP;
+ break;
+ case text::VertOrientation::LINE_CENTER:
+ nVertOri = text::VertOrientation::LINE_CENTER;
+ break;
+ case text::VertOrientation::LINE_BOTTOM:
+ nVertOri = text::VertOrientation::LINE_BOTTOM;
+ break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case text::VertOrientation::NONE:
+ nVertOri = text::VertOrientation::NONE;
+ break;
+ }
+ aTmp <<= nVertOri ;
+ xShapePropSet->setPropertyValue("VertOrient", aTmp );
+ }
+
+ aTmp <<= nAnchorType ;
+ xShapePropSet->setPropertyValue("AnchorType", aTmp );
+
+ if( text::TextContentAnchorType_AT_PAGE == nAnchorType )
+ {
+ aTmp <<= sal_Int16(1) ;
+ xShapePropSet->setPropertyValue("AnchorPageNo", aTmp );
+ }
+ else
+ {
+ if( !xTextRg.is() )
+ {
+ uno::Reference< text::XText > xDummyTextRef; // dirty but works according to OS...
+ xTextRg = new SwXTextRange( *m_pPam, xDummyTextRef );
+ }
+
+ aTmp <<= xTextRg;
+ xShapePropSet->setPropertyValue("TextRange", aTmp );
+ }
+
+ if( bSetPos )
+ {
+ aTmp <<= sal_Int16(text::HoriOrientation::NONE);
+ xShapePropSet->setPropertyValue("HoriOrient", aTmp );
+ aTmp <<= nXPos ;
+ xShapePropSet->setPropertyValue("HoriOrientPosition", aTmp );
+
+ aTmp <<= sal_Int16(text::VertOrientation::NONE);
+ xShapePropSet->setPropertyValue("VertOrient", aTmp );
+ aTmp <<= nYPos ;
+ xShapePropSet->setPropertyValue("VertOrientPosition", aTmp );
+ }
+ if( bSetSurround )
+ {
+ aTmp <<= nSurround ;
+ xShapePropSet->setPropertyValue("Surround", aTmp );
+ }
+
+ m_pFormImpl->GetShapes()->add(xShape);
+
+ // Set ControlModel to ControlShape
+ uno::Reference< drawing::XControlShape > xControlShape( xShape, UNO_QUERY );
+ uno::Reference< awt::XControlModel > xControlModel( rFComp, UNO_QUERY );
+ xControlShape->setControl( xControlModel );
+ }
+
+ // Since the focus is set at insertion of the controls, focus events will be sent
+ // To prevent previous JavaScript-Events from being called, these events will only be set retroactively
+ if( !rMacroTable.empty() || !rUnoMacroTable.empty() )
+ {
+ bool bHasEvents = lcl_html_setEvents( m_pFormImpl->GetControlEventManager(),
+ rFormComps->getCount() - 1,
+ rMacroTable, rUnoMacroTable, rUnoMacroParamTable,
+ GetScriptTypeString(m_pFormImpl->GetHeaderAttrs()) );
+ if (bHasEvents)
+ NotifyMacroEventRead();
+ }
+
+ if( bSetFCompPropSet )
+ {
+ m_pFormImpl->SetFCompPropSet( rFCompPropSet );
+ }
+
+ return xShape;
+}
+
+void SwHTMLParser::NewForm( bool bAppend )
+{
+ // Does a form already exist?
+ if( m_pFormImpl && m_pFormImpl->GetFormComps().is() )
+ return;
+
+ if( bAppend )
+ {
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_SPACE );
+ else
+ AddParSpace();
+ }
+
+ if( !m_pFormImpl )
+ m_pFormImpl = new SwHTMLForm_Impl( m_xDoc->GetDocShell() );
+
+ OUString aAction( m_sBaseURL );
+ OUString sName, sTarget;
+ FormSubmitEncoding nEncType = FormSubmitEncoding_URL;
+ FormSubmitMethod nMethod = FormSubmitMethod_GET;
+ SvxMacroTableDtor aMacroTable;
+ std::vector<OUString> aUnoMacroTable;
+ std::vector<OUString> aUnoMacroParamTable;
+ SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs();
+ ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs );
+ const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ ScriptType eScriptType2 = eDfltScriptType;
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ bool bSetEvent = false;
+
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ACTION:
+ aAction = rOption.GetString();
+ break;
+ case HtmlOptionId::METHOD:
+ nMethod = rOption.GetEnum( aHTMLFormMethodTable, nMethod );
+ break;
+ case HtmlOptionId::ENCTYPE:
+ nEncType = rOption.GetEnum( aHTMLFormEncTypeTable, nEncType );
+ break;
+ case HtmlOptionId::TARGET:
+ sTarget = rOption.GetString();
+ break;
+ case HtmlOptionId::NAME:
+ sName = rOption.GetString();
+ break;
+
+ case HtmlOptionId::SDONSUBMIT:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONSUBMIT:
+ nEvent = SvMacroItemId::HtmlOnSubmitForm;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONRESET:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONRESET:
+ nEvent = SvMacroItemId::HtmlOnResetForm;
+ bSetEvent = true;
+ break;
+
+ default:
+ lcl_html_getEvents( rOption.GetTokenString(),
+ rOption.GetString(),
+ aUnoMacroTable, aUnoMacroParamTable );
+ break;
+ }
+
+ if( bSetEvent )
+ {
+ OUString sEvent( rOption.GetString() );
+ if( !sEvent.isEmpty() )
+ {
+ sEvent = convertLineEnd(sEvent, GetSystemLineEnd());
+ OUString aScriptType2;
+ if( EXTENDED_STYPE==eScriptType2 )
+ aScriptType2 = rDfltScriptType;
+ aMacroTable.Insert( nEvent, SvxMacro( sEvent, aScriptType2, eScriptType2 ) );
+ }
+ }
+ }
+
+ const uno::Reference< XMultiServiceFactory > & rSrvcMgr =
+ m_pFormImpl->GetServiceFactory();
+ if( !rSrvcMgr.is() )
+ return;
+
+ uno::Reference< XInterface > xInt;
+ uno::Reference<XForm> xForm;
+ try
+ {
+ xInt = rSrvcMgr->createInstance("com.sun.star.form.component.Form");
+ if (!xInt.is())
+ return;
+ xForm.set(xInt, UNO_QUERY);
+ SAL_WARN_IF(!xForm.is(), "sw", "no XForm for com.sun.star.form.component.Form?");
+ if (!xForm.is())
+ return;
+ }
+ catch (...)
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ return;
+ }
+
+ uno::Reference< container::XIndexContainer > xFormComps( xForm, UNO_QUERY );
+ m_pFormImpl->SetFormComps( xFormComps );
+
+ uno::Reference< beans::XPropertySet > xFormPropSet( xForm, UNO_QUERY );
+
+ Any aTmp;
+ aTmp <<= sName;
+ xFormPropSet->setPropertyValue("Name", aTmp );
+
+ if( !aAction.isEmpty() )
+ {
+ aAction = URIHelper::SmartRel2Abs(INetURLObject(m_sBaseURL), aAction, Link<OUString *, bool>(), false);
+ }
+ else
+ {
+ // use directory at empty URL
+ INetURLObject aURLObj( m_aPathToFile );
+ aAction = aURLObj.GetPartBeforeLastName();
+ }
+ aTmp <<= aAction;
+ xFormPropSet->setPropertyValue("TargetURL",
+ aTmp );
+
+ aTmp <<= nMethod;
+ xFormPropSet->setPropertyValue("SubmitMethod",
+ aTmp );
+
+ aTmp <<= nEncType;
+ xFormPropSet->setPropertyValue("SubmitEncoding", aTmp );
+
+ if( !sTarget.isEmpty() )
+ {
+ aTmp <<= sTarget;
+ xFormPropSet->setPropertyValue( "TargetFrame", aTmp );
+ }
+
+ const uno::Reference< container::XIndexContainer > & rForms =
+ m_pFormImpl->GetForms();
+ Any aAny( &xForm, cppu::UnoType<XForm>::get());
+ rForms->insertByIndex( rForms->getCount(), aAny );
+ if( !aMacroTable.empty() )
+ {
+ bool bHasEvents = lcl_html_setEvents( m_pFormImpl->GetFormEventManager(),
+ rForms->getCount() - 1,
+ aMacroTable, aUnoMacroTable, aUnoMacroParamTable,
+ rDfltScriptType );
+ if (bHasEvents)
+ NotifyMacroEventRead();
+ }
+}
+
+void SwHTMLParser::EndForm( bool bAppend )
+{
+ if( m_pFormImpl && m_pFormImpl->GetFormComps().is() )
+ {
+ if( bAppend )
+ {
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_SPACE );
+ else
+ AddParSpace();
+ }
+
+ m_pFormImpl->ReleaseFormComps();
+ }
+}
+
+void SwHTMLParser::InsertInput()
+{
+ assert(m_vPendingStack.empty());
+
+ if( !m_pFormImpl || !m_pFormImpl->GetFormComps().is() )
+ return;
+
+ OUString sImgSrc, aId, aClass, aStyle, sName;
+ OUString sText;
+ SvxMacroTableDtor aMacroTable;
+ std::vector<OUString> aUnoMacroTable;
+ std::vector<OUString> aUnoMacroParamTable;
+ sal_uInt16 nSize = 0;
+ sal_Int16 nMaxLen = 0;
+ sal_Int16 nChecked = TRISTATE_FALSE;
+ sal_Int32 nTabIndex = TABINDEX_MAX + 1;
+ HTMLInputType eType = HTMLInputType::Text;
+ bool bDisabled = false, bValue = false;
+ bool bSetGrfWidth = false, bSetGrfHeight = false;
+ bool bHidden = false;
+ tools::Long nWidth=0, nHeight=0;
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+ SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs();
+ ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs );
+ const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs );
+
+ HtmlOptionId nKeepCRLFToken = HtmlOptionId::VALUE;
+ const HTMLOptions& rHTMLOptions = GetOptions( &nKeepCRLFToken );
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ ScriptType eScriptType2 = eDfltScriptType;
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ bool bSetEvent = false;
+
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::TYPE:
+ eType = rOption.GetInputType();
+ break;
+ case HtmlOptionId::NAME:
+ sName = rOption.GetString();
+ break;
+ case HtmlOptionId::VALUE:
+ sText = rOption.GetString();
+ bValue = true;
+ break;
+ case HtmlOptionId::CHECKED:
+ nChecked = TRISTATE_TRUE;
+ break;
+ case HtmlOptionId::DISABLED:
+ bDisabled = true;
+ break;
+ case HtmlOptionId::MAXLENGTH:
+ nMaxLen = static_cast<sal_Int16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::SIZE:
+ nSize = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::SRC:
+ sImgSrc = rOption.GetString();
+ break;
+ case HtmlOptionId::WIDTH:
+ // only save pixel values at first!
+ nWidth = rOption.GetNumber();
+ break;
+ case HtmlOptionId::HEIGHT:
+ // only save pixel values at first!
+ nHeight = rOption.GetNumber();
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri =
+ rOption.GetEnum( aHTMLImgVAlignTable, eVertOri );
+ eHoriOri =
+ rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri );
+ break;
+ case HtmlOptionId::TABINDEX:
+ // only save pixel values at first!
+ nTabIndex = rOption.GetNumber();
+ break;
+
+ case HtmlOptionId::SDONFOCUS:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONFOCUS:
+ nEvent = SvMacroItemId::HtmlOnGetFocus;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONBLUR: // actually only EDIT
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONBLUR:
+ nEvent = SvMacroItemId::HtmlOnLoseFocus;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONCLICK:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCLICK:
+ nEvent = SvMacroItemId::HtmlOnClick;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONCHANGE: // actually only EDIT
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCHANGE:
+ nEvent = SvMacroItemId::HtmlOnChange;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONSELECT: // actually only EDIT
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONSELECT:
+ nEvent = SvMacroItemId::HtmlOnSelect;
+ bSetEvent = true;
+ break;
+
+ default:
+ lcl_html_getEvents( rOption.GetTokenString(),
+ rOption.GetString(),
+ aUnoMacroTable, aUnoMacroParamTable );
+ break;
+ }
+
+ if( bSetEvent )
+ {
+ OUString sEvent( rOption.GetString() );
+ if( !sEvent.isEmpty() )
+ {
+ sEvent = convertLineEnd(sEvent, GetSystemLineEnd());
+ OUString aScriptType2;
+ if( EXTENDED_STYPE==eScriptType2 )
+ aScriptType2 = rDfltScriptType;
+ aMacroTable.Insert( nEvent, SvxMacro( sEvent, aScriptType2, eScriptType2 ) );
+ }
+ }
+ }
+
+ if( HTMLInputType::Image==eType )
+ {
+ // Image controls without image URL are ignored (same as MS)
+ if( sImgSrc.isEmpty() )
+ return;
+ }
+ else
+ {
+ // evaluation of ALIGN for all controls is not a good idea as long as
+ // paragraph bound controls do not influence the height of the cells of a table
+ eVertOri = text::VertOrientation::TOP;
+ eHoriOri = text::HoriOrientation::NONE;
+ }
+
+ // Default is HTMLInputType::Text
+ const char *pType = "TextField";
+ bool bKeepCRLFInValue = false;
+ switch( eType )
+ {
+ case HTMLInputType::Checkbox:
+ pType = "CheckBox";
+ bKeepCRLFInValue = true;
+ break;
+
+ case HTMLInputType::Radio:
+ pType = "RadioButton";
+ bKeepCRLFInValue = true;
+ break;
+
+ case HTMLInputType::Password:
+ bKeepCRLFInValue = true;
+ break;
+
+ case HTMLInputType::Button:
+ bKeepCRLFInValue = true;
+ [[fallthrough]];
+ case HTMLInputType::Submit:
+ case HTMLInputType::Reset:
+ pType = "CommandButton";
+ break;
+
+ case HTMLInputType::Image:
+ pType = "ImageButton";
+ break;
+
+ case HTMLInputType::File:
+ pType = "FileControl";
+ break;
+
+ case HTMLInputType::Hidden:
+ pType = "HiddenControl";
+ bKeepCRLFInValue = true;
+ break;
+ default:
+ ;
+ }
+
+ // For some controls CR/LF has to be deleted from VALUE
+ if( !bKeepCRLFInValue )
+ {
+ sText = sText.replaceAll("\r", "").replaceAll("\n", "");
+ }
+
+ const uno::Reference< XMultiServiceFactory > & rServiceFactory =
+ m_pFormImpl->GetServiceFactory();
+ if( !rServiceFactory.is() )
+ return;
+
+ OUString sServiceName = "com.sun.star.form.component." +
+ OUString::createFromAscii(pType);
+ uno::Reference< XInterface > xInt =
+ rServiceFactory->createInstance( sServiceName );
+ if( !xInt.is() )
+ return;
+
+ uno::Reference< XFormComponent > xFComp( xInt, UNO_QUERY );
+ if( !xFComp.is() )
+ return;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFComp, UNO_QUERY );
+
+ Any aTmp;
+ aTmp <<= sName;
+ xPropSet->setPropertyValue("Name", aTmp );
+
+ if( HTMLInputType::Hidden != eType )
+ {
+ if( nTabIndex >= TABINDEX_MIN && nTabIndex <= TABINDEX_MAX )
+ {
+ aTmp <<= static_cast<sal_Int16>(nTabIndex) ;
+ xPropSet->setPropertyValue("TabIndex", aTmp );
+ }
+
+ if( bDisabled )
+ {
+ xPropSet->setPropertyValue("Enabled", Any(false) );
+ }
+ }
+
+ aTmp <<= sText;
+
+ Size aSz( 0, 0 ); // defaults
+ Size aTextSz( 0, 0 ); // Text size
+ bool bMinWidth = false, bMinHeight = false;
+ bool bUseSize = false;
+ switch( eType )
+ {
+ case HTMLInputType::Checkbox:
+ case HTMLInputType::Radio:
+ {
+ if( !bValue )
+ aTmp <<= OUString( OOO_STRING_SVTOOLS_HTML_on );
+ xPropSet->setPropertyValue("RefValue",
+ aTmp );
+ aTmp <<= OUString();
+ xPropSet->setPropertyValue("Label",
+ aTmp );
+ // RadioButton: The DefaultChecked property should only be set
+ // if the control has been created and activateTabOrder has been called
+ // because otherwise it would still belong to the previous group.
+ if( HTMLInputType::Checkbox == eType )
+ {
+ aTmp <<= nChecked ;
+ xPropSet->setPropertyValue("DefaultState", aTmp );
+ }
+
+ const SvxMacro* pMacro = aMacroTable.Get( SvMacroItemId::HtmlOnClick );
+ if( pMacro )
+ {
+ aMacroTable.Insert( SvMacroItemId::HtmlOnClickItem, *pMacro );
+ aMacroTable.Erase( SvMacroItemId::HtmlOnClick );
+ }
+ // evaluating SIZE shouldn't be necessary here?
+ bMinWidth = bMinHeight = true;
+ }
+ break;
+
+ case HTMLInputType::Image:
+ {
+ // SIZE = WIDTH
+ aSz.setWidth(o3tl::convert(nWidth, o3tl::Length::px, o3tl::Length::mm100));
+ aSz.setHeight(o3tl::convert(nHeight, o3tl::Length::px, o3tl::Length::mm100));
+ aTmp <<= FormButtonType_SUBMIT;
+ xPropSet->setPropertyValue("ButtonType", aTmp );
+
+ aTmp <<= sal_Int16(0) ;
+ xPropSet->setPropertyValue("Border",
+ aTmp );
+ }
+ break;
+
+ case HTMLInputType::Button:
+ case HTMLInputType::Submit:
+ case HTMLInputType::Reset:
+ {
+ FormButtonType eButtonType;
+ switch( eType )
+ {
+ case HTMLInputType::Button:
+ eButtonType = FormButtonType_PUSH;
+ break;
+ case HTMLInputType::Submit:
+ eButtonType = FormButtonType_SUBMIT;
+ if (sText.isEmpty())
+ sText = OOO_STRING_SVTOOLS_HTML_IT_submit;
+ break;
+ case HTMLInputType::Reset:
+ eButtonType = FormButtonType_RESET;
+ if (sText.isEmpty())
+ sText = OOO_STRING_SVTOOLS_HTML_IT_reset;
+ break;
+ default:
+ ;
+ }
+ aTmp <<= sText;
+ xPropSet->setPropertyValue("Label",
+ aTmp );
+
+ aTmp <<= eButtonType;
+ xPropSet->setPropertyValue("ButtonType", aTmp );
+
+ bMinWidth = bMinHeight = true;
+ bUseSize = true;
+ }
+ break;
+
+ case HTMLInputType::Password:
+ case HTMLInputType::Text:
+ case HTMLInputType::File:
+ if( HTMLInputType::File != eType )
+ {
+ // The VALUE of file control will be ignored for security reasons
+ xPropSet->setPropertyValue("DefaultText", aTmp );
+ if( nMaxLen != 0 )
+ {
+ aTmp <<= nMaxLen ;
+ xPropSet->setPropertyValue("MaxTextLen", aTmp );
+ }
+ }
+
+ if( HTMLInputType::Password == eType )
+ {
+ aTmp <<= sal_Int16('*') ;
+ xPropSet->setPropertyValue("EchoChar", aTmp );
+ }
+
+ lcl_html_setFixedFontProperty( xPropSet );
+
+ if( !nSize )
+ nSize = 20;
+ aTextSz.setWidth( nSize );
+ bMinHeight = true;
+ break;
+
+ case HTMLInputType::Hidden:
+ xPropSet->setPropertyValue("HiddenValue", aTmp );
+ bHidden = true;
+ break;
+ default:
+ ;
+ }
+
+ if( bUseSize && nSize>0 )
+ {
+ aSz.setWidth(o3tl::convert(nSize, o3tl::Length::px, o3tl::Length::mm100));
+ OSL_ENSURE( !aTextSz.Width(), "text width is present" );
+ bMinWidth = false;
+ }
+
+ SfxItemSet aCSS1ItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aCSS1PropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ {
+ ParseStyleOptions( aStyle, aId, aClass, aCSS1ItemSet, aCSS1PropInfo );
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+ }
+
+ if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eWidthType )
+ {
+ aSz.setWidth( convertTwipToMm100( aCSS1PropInfo.m_nWidth ) );
+ aTextSz.setWidth( 0 );
+ bMinWidth = false;
+ }
+ if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eHeightType )
+ {
+ aSz.setHeight( convertTwipToMm100( aCSS1PropInfo.m_nHeight ) );
+ aTextSz.setHeight( 0 );
+ bMinHeight = false;
+ }
+
+ // Set sensible default values if the image button has no valid size
+ if( HTMLInputType::Image== eType )
+ {
+ if( !aSz.Width() )
+ {
+ aSz.setWidth( HTML_DFLT_IMG_WIDTH );
+ bSetGrfWidth = true;
+ if (m_xTable)
+ IncGrfsThatResizeTable();
+ }
+ if( !aSz.Height() )
+ {
+ aSz.setHeight( HTML_DFLT_IMG_HEIGHT );
+ bSetGrfHeight = true;
+ }
+ }
+ if( aSz.Width() < MINFLY )
+ aSz.setWidth( MINFLY );
+ if( aSz.Height() < MINFLY )
+ aSz.setHeight( MINFLY );
+
+ uno::Reference< drawing::XShape > xShape = InsertControl(
+ xFComp, xPropSet, aSz,
+ eVertOri, eHoriOri,
+ aCSS1ItemSet, aCSS1PropInfo,
+ aMacroTable, aUnoMacroTable,
+ aUnoMacroParamTable, false,
+ bHidden );
+ if( aTextSz.Width() || aTextSz.Height() || bMinWidth || bMinHeight )
+ {
+ OSL_ENSURE( !(bSetGrfWidth || bSetGrfHeight), "Adjust graphic size???" );
+ SetControlSize( xShape, aTextSz, bMinWidth, bMinHeight );
+ }
+
+ if( HTMLInputType::Radio == eType )
+ {
+ aTmp <<= nChecked ;
+ xPropSet->setPropertyValue("DefaultState", aTmp );
+ }
+
+ if( HTMLInputType::Image == eType )
+ {
+ // Set the URL after inserting the graphic because the Download can
+ // only register with XModel after the control has been inserted.
+ aTmp <<= URIHelper::SmartRel2Abs(INetURLObject(m_sBaseURL), sImgSrc, Link<OUString *, bool>(), false);
+ xPropSet->setPropertyValue("ImageURL",
+ aTmp );
+ }
+
+ if( bSetGrfWidth || bSetGrfHeight )
+ {
+ rtl::Reference<SwHTMLImageWatcher> pWatcher =
+ new SwHTMLImageWatcher( xShape, bSetGrfWidth, bSetGrfHeight );
+ pWatcher->start();
+ }
+}
+
+void SwHTMLParser::NewTextArea()
+{
+ assert(m_vPendingStack.empty());
+
+ OSL_ENSURE( !m_bTextArea, "TextArea in TextArea?" );
+ OSL_ENSURE( !m_pFormImpl || !m_pFormImpl->GetFCompPropSet().is(),
+ "TextArea in Control?" );
+
+ if( !m_pFormImpl || !m_pFormImpl->GetFormComps().is() )
+ {
+ // Close special treatment for TextArea in the parser
+ FinishTextArea();
+ return;
+ }
+
+ OUString aId, aClass, aStyle;
+ OUString sName;
+ sal_Int32 nTabIndex = TABINDEX_MAX + 1;
+ SvxMacroTableDtor aMacroTable;
+ std::vector<OUString> aUnoMacroTable;
+ std::vector<OUString> aUnoMacroParamTable;
+ sal_uInt16 nRows = 0, nCols = 0;
+ HTMLWordWrapMode nWrap = HTML_WM_OFF;
+ bool bDisabled = false;
+ SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs();
+ ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs );
+ const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ ScriptType eScriptType2 = eDfltScriptType;
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ bool bSetEvent = false;
+
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::NAME:
+ sName = rOption.GetString();
+ break;
+ case HtmlOptionId::DISABLED:
+ bDisabled = true;
+ break;
+ case HtmlOptionId::ROWS:
+ nRows = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::COLS:
+ nCols = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::WRAP:
+ nWrap = rOption.GetEnum( aHTMLTextAreaWrapTable, nWrap );
+ break;
+
+ case HtmlOptionId::TABINDEX:
+ nTabIndex = rOption.GetSNumber();
+ break;
+
+ case HtmlOptionId::SDONFOCUS:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONFOCUS:
+ nEvent = SvMacroItemId::HtmlOnGetFocus;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONBLUR:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONBLUR:
+ nEvent = SvMacroItemId::HtmlOnLoseFocus;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONCLICK:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCLICK:
+ nEvent = SvMacroItemId::HtmlOnClick;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONCHANGE:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCHANGE:
+ nEvent = SvMacroItemId::HtmlOnChange;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONSELECT:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONSELECT:
+ nEvent = SvMacroItemId::HtmlOnSelect;
+ bSetEvent = true;
+ break;
+
+ default:
+ lcl_html_getEvents( rOption.GetTokenString(),
+ rOption.GetString(),
+ aUnoMacroTable, aUnoMacroParamTable );
+ break;
+ }
+
+ if( bSetEvent )
+ {
+ OUString sEvent( rOption.GetString() );
+ if( !sEvent.isEmpty() )
+ {
+ sEvent = convertLineEnd(sEvent, GetSystemLineEnd());
+ if( EXTENDED_STYPE==eScriptType2 )
+ m_aScriptType = rDfltScriptType;
+ aMacroTable.Insert( nEvent, SvxMacro( sEvent, m_aScriptType, eScriptType2 ) );
+ }
+ }
+ }
+
+ const uno::Reference< lang::XMultiServiceFactory > & rSrvcMgr =
+ m_pFormImpl->GetServiceFactory();
+ if( !rSrvcMgr.is() )
+ {
+ FinishTextArea();
+ return;
+ }
+ uno::Reference< uno::XInterface > xInt = rSrvcMgr->createInstance(
+ "com.sun.star.form.component.TextField" );
+ if( !xInt.is() )
+ {
+ FinishTextArea();
+ return;
+ }
+
+ uno::Reference< XFormComponent > xFComp( xInt, UNO_QUERY );
+ OSL_ENSURE( xFComp.is(), "no FormComponent?" );
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFComp, UNO_QUERY );
+
+ Any aTmp;
+ aTmp <<= sName;
+ xPropSet->setPropertyValue("Name", aTmp );
+
+ aTmp <<= true;
+ xPropSet->setPropertyValue("MultiLine", aTmp );
+ xPropSet->setPropertyValue("VScroll", aTmp );
+ if( HTML_WM_OFF == nWrap )
+ xPropSet->setPropertyValue("HScroll", aTmp );
+ if( HTML_WM_HARD == nWrap )
+ xPropSet->setPropertyValue("HardLineBreaks", aTmp );
+
+ if( nTabIndex >= TABINDEX_MIN && nTabIndex <= TABINDEX_MAX )
+ {
+ aTmp <<= static_cast<sal_Int16>(nTabIndex) ;
+ xPropSet->setPropertyValue("TabIndex", aTmp );
+ }
+
+ lcl_html_setFixedFontProperty( xPropSet );
+
+ if( bDisabled )
+ {
+ xPropSet->setPropertyValue("Enabled", Any(false) );
+ }
+
+ OSL_ENSURE( m_pFormImpl->GetText().isEmpty(), "Text is not empty!" );
+
+ if( !nCols )
+ nCols = 20;
+ if( !nRows )
+ nRows = 1;
+
+ Size aTextSz( nCols, nRows );
+
+ SfxItemSet aCSS1ItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aCSS1PropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ {
+ ParseStyleOptions( aStyle, aId, aClass, aCSS1ItemSet, aCSS1PropInfo );
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+ }
+
+ Size aSz( MINFLY, MINFLY );
+ if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eWidthType )
+ {
+ aSz.setWidth( convertTwipToMm100( aCSS1PropInfo.m_nWidth ) );
+ aTextSz.setWidth( 0 );
+ }
+ if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eHeightType )
+ {
+ aSz.setHeight( convertTwipToMm100( aCSS1PropInfo.m_nHeight ) );
+ aTextSz.setHeight( 0 );
+ }
+ if( aSz.Width() < MINFLY )
+ aSz.setWidth( MINFLY );
+ if( aSz.Height() < MINFLY )
+ aSz.setHeight( MINFLY );
+
+ uno::Reference< drawing::XShape > xShape = InsertControl( xFComp, xPropSet, aSz,
+ text::VertOrientation::TOP, text::HoriOrientation::NONE,
+ aCSS1ItemSet, aCSS1PropInfo,
+ aMacroTable, aUnoMacroTable,
+ aUnoMacroParamTable );
+ if( aTextSz.Width() || aTextSz.Height() )
+ SetControlSize( xShape, aTextSz, false, false );
+
+ // create new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::TEXTAREA_ON));
+
+ // temporarily disable PRE/Listing/XMP
+ SplitPREListingXMP(xCntxt.get());
+ PushContext(xCntxt);
+
+ m_bTextArea = true;
+ m_bTAIgnoreNewPara = true;
+}
+
+void SwHTMLParser::EndTextArea()
+{
+ OSL_ENSURE( m_bTextArea, "no TextArea or wrong type" );
+ OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(),
+ "TextArea missing" );
+
+ const uno::Reference< beans::XPropertySet > & rPropSet =
+ m_pFormImpl->GetFCompPropSet();
+
+ Any aTmp;
+ aTmp <<= m_pFormImpl->GetText();
+ rPropSet->setPropertyValue("DefaultText", aTmp );
+ m_pFormImpl->EraseText();
+
+ m_pFormImpl->ReleaseFCompPropSet();
+
+ // get context
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::TEXTAREA_ON));
+ if (xCntxt)
+ {
+ // end attributes
+ EndContext(xCntxt.get());
+ }
+
+ m_bTextArea = false;
+}
+
+void SwHTMLParser::InsertTextAreaText( HtmlTokenId nToken )
+{
+ OSL_ENSURE( m_bTextArea, "no TextArea or wrong type" );
+ OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(),
+ "TextArea missing" );
+
+ OUString& rText = m_pFormImpl->GetText();
+ switch( nToken)
+ {
+ case HtmlTokenId::TEXTTOKEN:
+ rText += aToken;
+ break;
+ case HtmlTokenId::NEWPARA:
+ if( !m_bTAIgnoreNewPara )
+ rText += "\n";
+ break;
+ default:
+ rText += "<";
+ rText += sSaveToken;
+ if( !aToken.isEmpty() )
+ {
+ rText += " ";
+ rText += aToken;
+ }
+ rText += ">";
+ }
+
+ m_bTAIgnoreNewPara = false;
+}
+
+void SwHTMLParser::NewSelect()
+{
+ assert(m_vPendingStack.empty());
+
+ OSL_ENSURE( !m_bSelect, "Select in Select?" );
+ OSL_ENSURE( !m_pFormImpl || !m_pFormImpl->GetFCompPropSet().is(),
+ "Select in Control?" );
+
+ if( !m_pFormImpl || !m_pFormImpl->GetFormComps().is() )
+ return;
+
+ OUString aId, aClass, aStyle;
+ OUString sName;
+ sal_Int32 nTabIndex = TABINDEX_MAX + 1;
+ SvxMacroTableDtor aMacroTable;
+ std::vector<OUString> aUnoMacroTable;
+ std::vector<OUString> aUnoMacroParamTable;
+ bool bMultiple = false;
+ bool bDisabled = false;
+ m_nSelectEntryCnt = 1;
+ SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs();
+ ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs );
+ const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ ScriptType eScriptType2 = eDfltScriptType;
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ bool bSetEvent = false;
+
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::NAME:
+ sName = rOption.GetString();
+ break;
+ case HtmlOptionId::MULTIPLE:
+ bMultiple = true;
+ break;
+ case HtmlOptionId::DISABLED:
+ bDisabled = true;
+ break;
+ case HtmlOptionId::SIZE:
+ m_nSelectEntryCnt = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+
+ case HtmlOptionId::TABINDEX:
+ nTabIndex = rOption.GetSNumber();
+ break;
+
+ case HtmlOptionId::SDONFOCUS:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONFOCUS:
+ nEvent = SvMacroItemId::HtmlOnGetFocus;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONBLUR:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONBLUR:
+ nEvent = SvMacroItemId::HtmlOnLoseFocus;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONCLICK:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCLICK:
+ nEvent = SvMacroItemId::HtmlOnClick;
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONCHANGE:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCHANGE:
+ nEvent = SvMacroItemId::HtmlOnChange;
+ bSetEvent = true;
+ break;
+
+ default:
+ lcl_html_getEvents( rOption.GetTokenString(),
+ rOption.GetString(),
+ aUnoMacroTable, aUnoMacroParamTable );
+ break;
+ }
+
+ if( bSetEvent )
+ {
+ OUString sEvent( rOption.GetString() );
+ if( !sEvent.isEmpty() )
+ {
+ sEvent = convertLineEnd(sEvent, GetSystemLineEnd());
+ if( EXTENDED_STYPE==eScriptType2 )
+ m_aScriptType = rDfltScriptType;
+ aMacroTable.Insert( nEvent, SvxMacro( sEvent, m_aScriptType, eScriptType2 ) );
+ }
+ }
+ }
+
+ const uno::Reference< lang::XMultiServiceFactory > & rSrvcMgr =
+ m_pFormImpl->GetServiceFactory();
+ if( !rSrvcMgr.is() )
+ {
+ FinishTextArea();
+ return;
+ }
+ uno::Reference< uno::XInterface > xInt = rSrvcMgr->createInstance(
+ "com.sun.star.form.component.ListBox" );
+ if( !xInt.is() )
+ {
+ FinishTextArea();
+ return;
+ }
+
+ uno::Reference< XFormComponent > xFComp( xInt, UNO_QUERY );
+ OSL_ENSURE(xFComp.is(), "no FormComponent?");
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFComp, UNO_QUERY );
+
+ Any aTmp;
+ aTmp <<= sName;
+ xPropSet->setPropertyValue("Name", aTmp );
+
+ if( nTabIndex >= TABINDEX_MIN && nTabIndex <= TABINDEX_MAX )
+ {
+ aTmp <<= static_cast<sal_Int16>(nTabIndex) ;
+ xPropSet->setPropertyValue("TabIndex", aTmp );
+ }
+
+ if( bDisabled )
+ {
+ xPropSet->setPropertyValue("Enabled", Any(false) );
+ }
+
+ Size aTextSz( 0, 0 );
+ bool bMinWidth = true, bMinHeight = true;
+ if( !bMultiple && 1==m_nSelectEntryCnt )
+ {
+ xPropSet->setPropertyValue("Dropdown", Any(true) );
+ }
+ else
+ {
+ if( m_nSelectEntryCnt <= 1 ) // 4 lines is default
+ m_nSelectEntryCnt = 4;
+
+ if( bMultiple )
+ {
+ xPropSet->setPropertyValue("MultiSelection", Any(true) );
+ }
+ aTextSz.setHeight( m_nSelectEntryCnt );
+ bMinHeight = false;
+ }
+
+ SfxItemSet aCSS1ItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aCSS1PropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ {
+ ParseStyleOptions( aStyle, aId, aClass, aCSS1ItemSet, aCSS1PropInfo );
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+ }
+
+ Size aSz( MINFLY, MINFLY );
+ m_bFixSelectWidth = true;
+ if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eWidthType )
+ {
+ aSz.setWidth( convertTwipToMm100( aCSS1PropInfo.m_nWidth ) );
+ m_bFixSelectWidth = false;
+ bMinWidth = false;
+ }
+ if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eHeightType )
+ {
+ aSz.setHeight( convertTwipToMm100( aCSS1PropInfo.m_nHeight ) );
+ aTextSz.setHeight( 0 );
+ bMinHeight = false;
+ }
+ if( aSz.Width() < MINFLY )
+ aSz.setWidth( MINFLY );
+ if( aSz.Height() < MINFLY )
+ aSz.setHeight( MINFLY );
+
+ uno::Reference< drawing::XShape > xShape = InsertControl( xFComp, xPropSet, aSz,
+ text::VertOrientation::TOP, text::HoriOrientation::NONE,
+ aCSS1ItemSet, aCSS1PropInfo,
+ aMacroTable, aUnoMacroTable,
+ aUnoMacroParamTable );
+ if( m_bFixSelectWidth )
+ m_pFormImpl->SetShape( xShape );
+ if( aTextSz.Height() || bMinWidth || bMinHeight )
+ SetControlSize( xShape, aTextSz, bMinWidth, bMinHeight );
+
+ // create new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::SELECT_ON));
+
+ // temporarily disable PRE/Listing/XMP
+ SplitPREListingXMP(xCntxt.get());
+ PushContext(xCntxt);
+
+ m_bSelect = true;
+}
+
+void SwHTMLParser::EndSelect()
+{
+ assert(m_vPendingStack.empty());
+
+ OSL_ENSURE( m_bSelect, "no Select" );
+ OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(),
+ "no select control" );
+
+ const uno::Reference< beans::XPropertySet > & rPropSet =
+ m_pFormImpl->GetFCompPropSet();
+
+ size_t nEntryCnt = m_pFormImpl->GetStringList().size();
+ if(!m_pFormImpl->GetStringList().empty())
+ {
+ Sequence<OUString> aList( static_cast<sal_Int32>(nEntryCnt) );
+ Sequence<OUString> aValueList( static_cast<sal_Int32>(nEntryCnt) );
+ OUString *pStrings = aList.getArray();
+ OUString *pValues = aValueList.getArray();
+
+ for(size_t i = 0; i < nEntryCnt; ++i)
+ {
+ OUString sText(m_pFormImpl->GetStringList()[i]);
+ sText = comphelper::string::stripEnd(sText, ' ');
+ pStrings[i] = sText;
+
+ sText = m_pFormImpl->GetValueList()[i];
+ pValues[i] = sText;
+ }
+
+ rPropSet->setPropertyValue("StringItemList", Any(aList) );
+
+ rPropSet->setPropertyValue("ListSourceType", Any(ListSourceType_VALUELIST) );
+
+ rPropSet->setPropertyValue("ListSource", Any(aValueList) );
+
+ size_t nSelCnt = m_pFormImpl->GetSelectedList().size();
+ if( !nSelCnt && 1 == m_nSelectEntryCnt && nEntryCnt )
+ {
+ // In a dropdown list an entry should always be selected.
+ m_pFormImpl->GetSelectedList().insert( m_pFormImpl->GetSelectedList().begin(), 0 );
+ nSelCnt = 1;
+ }
+ Sequence<sal_Int16> aSelList( static_cast<sal_Int32>(nSelCnt) );
+ sal_Int16 *pSels = aSelList.getArray();
+ for(size_t i = 0; i < nSelCnt; ++i)
+ {
+ pSels[i] = static_cast<sal_Int16>(m_pFormImpl->GetSelectedList()[i]);
+ }
+ rPropSet->setPropertyValue("DefaultSelection", Any(aSelList) );
+
+ m_pFormImpl->EraseStringList();
+ m_pFormImpl->EraseValueList();
+ }
+
+ m_pFormImpl->EraseSelectedList();
+
+ if( m_bFixSelectWidth )
+ {
+ OSL_ENSURE( m_pFormImpl->GetShape().is(), "Shape not saved" );
+ Size aTextSz( -1, 0 );
+ SetControlSize( m_pFormImpl->GetShape(), aTextSz, false, false );
+ }
+
+ m_pFormImpl->ReleaseFCompPropSet();
+
+ // get context
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::SELECT_ON));
+ if (xCntxt)
+ {
+ // close attributes
+ EndContext(xCntxt.get());
+ }
+
+ m_bSelect = false;
+}
+
+void SwHTMLParser::InsertSelectOption()
+{
+ OSL_ENSURE( m_bSelect, "no Select" );
+ OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(),
+ "no Select-Control" );
+
+ m_bLBEntrySelected = false;
+ OUString aValue;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ // leave out for now
+ break;
+ case HtmlOptionId::SELECTED:
+ m_bLBEntrySelected = true;
+ break;
+ case HtmlOptionId::VALUE:
+ aValue = rOption.GetString();
+ if( aValue.isEmpty() )
+ aValue = "$$$empty$$$";
+ break;
+ default: break;
+ }
+ }
+
+ sal_uInt16 nEntryCnt = m_pFormImpl->GetStringList().size();
+ m_pFormImpl->GetStringList().push_back(OUString());
+ m_pFormImpl->GetValueList().push_back(aValue);
+ if( m_bLBEntrySelected )
+ {
+ m_pFormImpl->GetSelectedList().push_back( nEntryCnt );
+ }
+}
+
+void SwHTMLParser::InsertSelectText()
+{
+ OSL_ENSURE( m_bSelect, "no select" );
+ OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(),
+ "no select control" );
+
+ if(m_pFormImpl->GetStringList().empty())
+ return;
+
+ OUString& rText = m_pFormImpl->GetStringList().back();
+
+ if( !aToken.isEmpty() && ' '==aToken[ 0 ] )
+ {
+ sal_Int32 nLen = rText.getLength();
+ if( !nLen || ' '==rText[nLen-1])
+ aToken.remove( 0, 1 );
+ }
+ if( !aToken.isEmpty() )
+ rText += aToken;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlform.hxx b/sw/source/filter/html/htmlform.hxx
new file mode 100644
index 0000000000..d1acce1faf
--- /dev/null
+++ b/sw/source/filter/html/htmlform.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFORM_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFORM_HXX
+
+extern const char* aEventListenerTable[];
+extern const char* aEventMethodTable[];
+extern const char* aEventSDOptionTable[];
+extern const char* aEventOptionTable[];
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlforw.cxx b/sw/source/filter/html/htmlforw.cxx
new file mode 100644
index 0000000000..98423fc96a
--- /dev/null
+++ b/sw/source/filter/html/htmlforw.cxx
@@ -0,0 +1,1337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/form/FormSubmitEncoding.hpp>
+#include <com/sun/star/form/FormSubmitMethod.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/XTextLayoutConstrains.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <hintids.hxx>
+#include <o3tl/any.hxx>
+#include <rtl/math.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <svl/macitem.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svl/urihelper.hxx>
+#include <vcl/unohelp.hxx>
+#include <svx/svdouno.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <osl/diagnose.h>
+#include <docsh.hxx>
+#include <fmtanchr.hxx>
+#include <viewsh.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include "wrthtml.hxx"
+#include "htmlfly.hxx"
+#include "htmlform.hxx"
+#include <frmfmt.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+const HtmlFrmOpts HTML_FRMOPTS_CONTROL =
+ HtmlFrmOpts::NONE;
+const HtmlFrmOpts HTML_FRMOPTS_CONTROL_CSS1 =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSize |
+ HtmlFrmOpts::SSpace |
+ HtmlFrmOpts::BrClear;
+const HtmlFrmOpts HTML_FRMOPTS_IMG_CONTROL =
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::BrClear;
+const HtmlFrmOpts HTML_FRMOPTS_IMG_CONTROL_CSS1 =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSpace;
+
+static void lcl_html_outEvents( SvStream& rStrm,
+ const uno::Reference< form::XFormComponent >& rFormComp,
+ bool bCfgStarBasic )
+{
+ uno::Reference< uno::XInterface > xParentIfc = rFormComp->getParent();
+ OSL_ENSURE( xParentIfc.is(), "lcl_html_outEvents: no parent interface" );
+ if( !xParentIfc.is() )
+ return;
+ uno::Reference< container::XIndexAccess > xIndexAcc( xParentIfc, uno::UNO_QUERY );
+ uno::Reference< script::XEventAttacherManager > xEventManager( xParentIfc,
+ uno::UNO_QUERY );
+ if( !xIndexAcc.is() || !xEventManager.is() )
+ return;
+
+ // and search for the position of the ControlModel within
+ sal_Int32 nCount = xIndexAcc->getCount(), nPos;
+ for( nPos = 0 ; nPos < nCount; nPos++ )
+ {
+ uno::Any aTmp = xIndexAcc->getByIndex(nPos);
+ if( auto x1 = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(aTmp) )
+
+ {
+ if( rFormComp == *x1 )
+ break;
+ }
+ else if( auto x2 = o3tl::tryAccess<uno::Reference<form::XForm>>(aTmp) )
+ {
+ if( rFormComp == *x2 )
+ break;
+ }
+ else
+ {
+ OSL_ENSURE( false, "lcl_html_outEvents: wrong reflection" );
+ }
+ }
+
+ if( nPos == nCount )
+ return;
+
+ const uno::Sequence< script::ScriptEventDescriptor > aDescs =
+ xEventManager->getScriptEvents( nPos );
+ if( !aDescs.hasElements() )
+ return;
+
+ for( const script::ScriptEventDescriptor& rDesc : aDescs )
+ {
+ ScriptType eScriptType = EXTENDED_STYPE;
+ OUString aScriptType( rDesc.ScriptType );
+ if( aScriptType.equalsIgnoreAsciiCase(SVX_MACRO_LANGUAGE_JAVASCRIPT) )
+ eScriptType = JAVASCRIPT;
+ else if( aScriptType.equalsIgnoreAsciiCase(SVX_MACRO_LANGUAGE_STARBASIC ) )
+ eScriptType = STARBASIC;
+ if( JAVASCRIPT != eScriptType && !bCfgStarBasic )
+ continue;
+
+ OUString sListener( rDesc.ListenerType );
+ if (!sListener.isEmpty())
+ {
+ const sal_Int32 nIdx { sListener.lastIndexOf('.')+1 };
+ if (nIdx>0)
+ {
+ if (nIdx<sListener.getLength())
+ {
+ sListener = sListener.copy(nIdx);
+ }
+ else
+ {
+ sListener.clear();
+ }
+ }
+ }
+ OUString sMethod( rDesc.EventMethod );
+
+ const char *pOpt = nullptr;
+ for( int j=0; aEventListenerTable[j]; j++ )
+ {
+ if( sListener.equalsAscii( aEventListenerTable[j] ) &&
+ sMethod.equalsAscii( aEventMethodTable[j] ) )
+ {
+ pOpt = (STARBASIC==eScriptType ? aEventSDOptionTable
+ : aEventOptionTable)[j];
+ break;
+ }
+ }
+
+ OString sOut = " "_ostr;
+ if( pOpt && (EXTENDED_STYPE != eScriptType ||
+ rDesc.AddListenerParam.isEmpty()) )
+ sOut += pOpt;
+ else
+ {
+ sOut += OOO_STRING_SVTOOLS_HTML_O_sdevent +
+ OUStringToOString(sListener, RTL_TEXTENCODING_ASCII_US) + "-" +
+ OUStringToOString(sMethod, RTL_TEXTENCODING_ASCII_US);
+ }
+ sOut += "=\"";
+ rStrm.WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rStrm, rDesc.ScriptCode );
+ rStrm.WriteChar( '\"' );
+ if( EXTENDED_STYPE == eScriptType &&
+ !rDesc.AddListenerParam.isEmpty() )
+ {
+ sOut = " " OOO_STRING_SVTOOLS_HTML_O_sdaddparam +
+ OUStringToOString(sListener, RTL_TEXTENCODING_ASCII_US) + "-" +
+ OUStringToOString(sMethod, RTL_TEXTENCODING_ASCII_US) + "=\"";
+ rStrm.WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rStrm, rDesc.AddListenerParam );
+ rStrm.WriteChar( '\"' );
+ }
+ }
+}
+
+static bool lcl_html_isHTMLControl( sal_Int16 nClassId )
+{
+ bool bRet = false;
+
+ switch( nClassId )
+ {
+ case form::FormComponentType::TEXTFIELD:
+ case form::FormComponentType::COMMANDBUTTON:
+ case form::FormComponentType::RADIOBUTTON:
+ case form::FormComponentType::CHECKBOX:
+ case form::FormComponentType::LISTBOX:
+ case form::FormComponentType::IMAGEBUTTON:
+ case form::FormComponentType::FILECONTROL:
+ bRet = true;
+ break;
+ }
+
+ return bRet;
+}
+
+bool SwHTMLWriter::HasControls() const
+{
+ SwNodeOffset nStartIdx = m_pCurrentPam->GetPoint()->GetNodeIndex();
+ size_t i = 0;
+
+ // Skip all controls in front of the current paragraph
+ while ( i < m_aHTMLControls.size() && m_aHTMLControls[i]->nNdIdx < nStartIdx )
+ ++i;
+
+ return i < m_aHTMLControls.size() && m_aHTMLControls[i]->nNdIdx == nStartIdx;
+}
+
+void SwHTMLWriter::OutForm( bool bTag_On, const SwStartNode *pStartNd )
+{
+ if( m_bPreserveForm ) // we are in a table or an area with form spanned over it
+ return;
+
+ if( !bTag_On )
+ {
+ // end the form when all controls are output
+ if( mxFormComps.is() &&
+ mxFormComps->getCount() == m_nFormCntrlCnt )
+ {
+ OutForm( false, mxFormComps );
+ mxFormComps.clear();
+ }
+ return;
+ }
+
+ uno::Reference< container::XIndexContainer > xNewFormComps;
+ SwNodeOffset nStartIdx = pStartNd ? pStartNd->GetIndex()
+ : m_pCurrentPam->GetPoint()->GetNodeIndex();
+
+ // skip controls before the interesting area
+ size_t i = 0;
+ while ( i < m_aHTMLControls.size() && m_aHTMLControls[i]->nNdIdx < nStartIdx )
+ ++i;
+
+ if( !pStartNd )
+ {
+ // Check for a single node: there it's only interesting, if there is
+ // a control for the node and to which form it belongs.
+ if( i < m_aHTMLControls.size() &&
+ m_aHTMLControls[i]->nNdIdx == nStartIdx )
+ xNewFormComps = m_aHTMLControls[i]->xFormComps;
+ }
+ else
+ {
+ // we iterate over a table/an area: we're interested in:
+ // - if there are controls with different start nodes
+ // - if there is a form, with controls which aren't all in the table/area
+
+ uno::Reference< container::XIndexContainer > xCurrentFormComps;// current form in table
+ const SwStartNode *pCurrentStNd = nullptr; // and the start node of a Control
+ sal_Int32 nCurrentCtrls = 0; // and the found controls in it
+ SwNodeOffset nEndIdx = pStartNd->EndOfSectionIndex();
+ for( ; i < m_aHTMLControls.size() &&
+ m_aHTMLControls[i]->nNdIdx <= nEndIdx; i++ )
+ {
+ const SwStartNode *pCntrlStNd =
+ m_pDoc->GetNodes()[m_aHTMLControls[i]->nNdIdx]->StartOfSectionNode();
+
+ if( xCurrentFormComps.is() )
+ {
+ // already inside a form ...
+ if( xCurrentFormComps==m_aHTMLControls[i]->xFormComps )
+ {
+ // ... and the control is also inside ...
+ if( pCurrentStNd!=pCntrlStNd )
+ {
+ // ... but it's inside another cell:
+ // Then open a form above the table
+ xNewFormComps = xCurrentFormComps;
+ break;
+ }
+ nCurrentCtrls = nCurrentCtrls + m_aHTMLControls[i]->nCount;
+ }
+ else
+ {
+ // ... but the Control is in another cell:
+ // There we act as if we open a new from and continue searching.
+ xCurrentFormComps = m_aHTMLControls[i]->xFormComps;
+ pCurrentStNd = pCntrlStNd;
+ nCurrentCtrls = m_aHTMLControls[i]->nCount;
+ }
+ }
+ else
+ {
+ // We aren't in a form:
+ // There we act as if we open a form.
+ xCurrentFormComps = m_aHTMLControls[i]->xFormComps;
+ pCurrentStNd = pCntrlStNd;
+ nCurrentCtrls = m_aHTMLControls[i]->nCount;
+ }
+ }
+ if( !xNewFormComps.is() && xCurrentFormComps.is() &&
+ nCurrentCtrls != xCurrentFormComps->getCount() )
+ {
+ // A form should be opened in the table/area which isn't completely
+ // inside the table. Then we must also now open the form.
+ xNewFormComps = xCurrentFormComps;
+ }
+ }
+
+ if( !(xNewFormComps.is() &&
+ (!mxFormComps.is() || xNewFormComps != mxFormComps)) )
+ return;
+
+ // A form should be opened ...
+ if( mxFormComps.is() )
+ {
+ // ... but a form is still open: That is in every case an error,
+ // but we'll close the old form nevertheless.
+ OutForm( false, mxFormComps );
+
+ //!!!nWarn = 1; // Control will be assigned to wrong form
+ }
+
+ mxFormComps = xNewFormComps;
+
+ OutForm( true, mxFormComps );
+ uno::Reference< beans::XPropertySet > xTmp;
+ OutHiddenControls( mxFormComps, xTmp );
+}
+
+void SwHTMLWriter::OutHiddenForms()
+{
+ // Without DrawModel there can't be controls. Then you also can't access the
+ // document via UNO, because otherwise a DrawModel would be created.
+ if( !m_pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ return;
+
+ SwDocShell *pDocSh = m_pDoc->GetDocShell();
+ if( !pDocSh )
+ return;
+
+ uno::Reference< drawing::XDrawPageSupplier > xDPSupp( pDocSh->GetBaseModel(),
+ uno::UNO_QUERY );
+ OSL_ENSURE( xDPSupp.is(), "XTextDocument not received from XModel" );
+ uno::Reference< drawing::XDrawPage > xDrawPage = xDPSupp->getDrawPage();
+
+ OSL_ENSURE( xDrawPage.is(), "XDrawPage not received" );
+ if( !xDrawPage.is() )
+ return;
+
+ uno::Reference< form::XFormsSupplier > xFormsSupplier( xDrawPage, uno::UNO_QUERY );
+ OSL_ENSURE( xFormsSupplier.is(),
+ "XFormsSupplier not received from XDrawPage" );
+
+ uno::Reference< container::XNameContainer > xTmp = xFormsSupplier->getForms();
+ OSL_ENSURE( xTmp.is(), "XForms not received" );
+ uno::Reference< container::XIndexContainer > xForms( xTmp, uno::UNO_QUERY );
+ OSL_ENSURE( xForms.is(), "XForms without container::XIndexContainer?" );
+
+ sal_Int32 nCount = xForms->getCount();
+ for( sal_Int32 i=0; i<nCount; i++)
+ {
+ uno::Any aTmp = xForms->getByIndex( i );
+ if( auto x = o3tl::tryAccess<uno::Reference<form::XForm>>(aTmp) )
+ OutHiddenForm( *x );
+ else
+ {
+ OSL_ENSURE( false, "OutHiddenForms: wrong reflection" );
+ }
+ }
+}
+
+void SwHTMLWriter::OutHiddenForm( const uno::Reference< form::XForm > & rForm )
+{
+ uno::Reference< container::XIndexContainer > xFormComps( rForm, uno::UNO_QUERY );
+ if( !xFormComps.is() )
+ return;
+
+ sal_Int32 nCount = xFormComps->getCount();
+ bool bHiddenOnly = nCount > 0, bHidden = false;
+ for( sal_Int32 i=0; i<nCount; i++ )
+ {
+ uno::Any aTmp = xFormComps->getByIndex( i );
+ auto xFormComp = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(
+ aTmp);
+ OSL_ENSURE( xFormComp, "OutHiddenForm: wrong reflection" );
+ if( !xFormComp )
+ continue;
+
+ uno::Reference< form::XForm > xForm( *xFormComp, uno::UNO_QUERY );
+ if( xForm.is() )
+ OutHiddenForm( xForm );
+
+ if( bHiddenOnly )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( *xFormComp, uno::UNO_QUERY );
+ OUString sPropName("ClassId");
+ if( xPropSet->getPropertySetInfo()->hasPropertyByName( sPropName ) )
+ {
+ uno::Any aAny2 = xPropSet->getPropertyValue( sPropName );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aAny2) )
+ {
+ if( form::FormComponentType::HIDDENCONTROL == *n )
+ bHidden = true;
+ else if( lcl_html_isHTMLControl( *n ) )
+ bHiddenOnly = false;
+ }
+ }
+ }
+ }
+
+ if( bHidden && bHiddenOnly )
+ {
+ OutForm( true, xFormComps );
+ uno::Reference< beans::XPropertySet > xTmp;
+ OutHiddenControls( xFormComps, xTmp );
+ OutForm( false, xFormComps );
+ }
+}
+
+void SwHTMLWriter::OutForm( bool bOn,
+ const uno::Reference< container::XIndexContainer > & rFormComps )
+{
+ m_nFormCntrlCnt = 0;
+
+ if( !bOn )
+ {
+ DecIndentLevel(); // indent content of form
+ if (IsLFPossible())
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_form), false );
+ SetLFPossible(true);
+
+ return;
+ }
+
+ // the new form is opened
+ if (IsLFPossible())
+ OutNewLine();
+ OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_form;
+
+ uno::Reference< beans::XPropertySet > xFormPropSet( rFormComps, uno::UNO_QUERY );
+
+ uno::Any aTmp = xFormPropSet->getPropertyValue( "Name" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\"";
+ Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( Strm(), *s );
+ sOut = "\""_ostr;
+ }
+ }
+
+ aTmp = xFormPropSet->getPropertyValue( "TargetURL" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if ( !s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_action "=\"";
+ Strm().WriteOString( sOut );
+ OUString aURL
+ = URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), *s);
+ HTMLOutFuncs::Out_String( Strm(), aURL );
+ sOut = "\""_ostr;
+ }
+ }
+
+ aTmp = xFormPropSet->getPropertyValue( "SubmitMethod" );
+ if( auto eMethod = o3tl::tryAccess<form::FormSubmitMethod>(aTmp) )
+ {
+ if( form::FormSubmitMethod_POST==*eMethod )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_method "=\""
+ OOO_STRING_SVTOOLS_HTML_METHOD_post "\"";
+ }
+ }
+ aTmp = xFormPropSet->getPropertyValue( "SubmitEncoding" );
+ if( auto eEncType = o3tl::tryAccess<form::FormSubmitEncoding>(aTmp) )
+ {
+ const char *pStr = nullptr;
+ switch( *eEncType )
+ {
+ case form::FormSubmitEncoding_MULTIPART:
+ pStr = OOO_STRING_SVTOOLS_HTML_ET_multipart;
+ break;
+ case form::FormSubmitEncoding_TEXT:
+ pStr = OOO_STRING_SVTOOLS_HTML_ET_text;
+ break;
+ default:
+ ;
+ }
+
+ if( pStr )
+ {
+ sOut += OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_enctype "=\"") +
+ pStr + "\"";
+ }
+ }
+
+ aTmp = xFormPropSet->getPropertyValue( "TargetFrame" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if (!s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_target "=\"";
+ Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( Strm(), *s );
+ sOut = "\""_ostr;
+ }
+ }
+
+ Strm().WriteOString( sOut );
+ uno::Reference< form::XFormComponent > xFormComp( rFormComps, uno::UNO_QUERY );
+ lcl_html_outEvents( Strm(), xFormComp, m_bCfgStarBasic );
+ Strm().WriteChar( '>' );
+
+ IncIndentLevel(); // indent content of form
+ SetLFPossible(true);
+}
+
+void SwHTMLWriter::OutHiddenControls(
+ const uno::Reference< container::XIndexContainer > & rFormComps,
+ const uno::Reference< beans::XPropertySet > & rPropSet )
+{
+ sal_Int32 nCount = rFormComps->getCount();
+ sal_Int32 nPos = 0;
+ if( rPropSet.is() )
+ {
+ bool bDone = false;
+
+ uno::Reference< form::XFormComponent > xFC( rPropSet, uno::UNO_QUERY );
+ for( nPos=0; !bDone && nPos < nCount; nPos++ )
+ {
+ uno::Any aTmp = rFormComps->getByIndex( nPos );
+ auto x = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(aTmp);
+ OSL_ENSURE( x,
+ "OutHiddenControls: wrong reflection" );
+ bDone = x && *x == xFC;
+ }
+ }
+
+ for( ; nPos < nCount; nPos++ )
+ {
+ uno::Any aTmp = rFormComps->getByIndex( nPos );
+ auto xFC = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(aTmp);
+ OSL_ENSURE( xFC,
+ "OutHiddenControls: wrong reflection" );
+ if( !xFC )
+ continue;
+ uno::Reference< beans::XPropertySet > xPropSet( *xFC, uno::UNO_QUERY );
+
+ OUString sPropName = "ClassId";
+ if( !xPropSet->getPropertySetInfo()->hasPropertyByName( sPropName ) )
+ continue;
+
+ aTmp = xPropSet->getPropertyValue( sPropName );
+ auto n = o3tl::tryAccess<sal_Int16>(aTmp);
+ if( !n )
+ continue;
+
+ if( form::FormComponentType::HIDDENCONTROL == *n )
+ {
+ if (IsLFPossible())
+ OutNewLine( true );
+ OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_input " "
+ OOO_STRING_SVTOOLS_HTML_O_type "=\""
+ OOO_STRING_SVTOOLS_HTML_IT_hidden "\"";
+
+ aTmp = xPropSet->getPropertyValue( "Name" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\"";
+ Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( Strm(), *s );
+ sOut = "\""_ostr;
+ }
+ }
+ aTmp = xPropSet->getPropertyValue( "HiddenValue" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_value "=\"";
+ Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( Strm(), *s );
+ sOut = "\""_ostr;
+ }
+ }
+ sOut += ">";
+ Strm().WriteOString( sOut );
+
+ m_nFormCntrlCnt++;
+ }
+ else if( lcl_html_isHTMLControl( *n ) )
+ {
+ break;
+ }
+ }
+}
+
+// here are the output routines, thus the form::Forms are bundled:
+
+const SdrObject *SwHTMLWriter::GetHTMLControl( const SwDrawFrameFormat& rFormat )
+{
+ // it must be a Draw-Format
+ OSL_ENSURE( RES_DRAWFRMFMT == rFormat.Which(),
+ "GetHTMLControl only allow for Draw-Formats" );
+
+ // Look if a SdrObject exists for it
+ const SdrObject *pObj = rFormat.FindSdrObject();
+ if( !pObj || SdrInventor::FmForm != pObj->GetObjInventor() )
+ return nullptr;
+
+ const SdrUnoObj& rFormObj = dynamic_cast<const SdrUnoObj&>(*pObj);
+ const uno::Reference< awt::XControlModel >& xControlModel =
+ rFormObj.GetUnoControlModel();
+
+ OSL_ENSURE( xControlModel.is(), "UNO-Control without model" );
+ if( !xControlModel.is() )
+ return nullptr;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+
+ OUString sPropName("ClassId");
+ if( !xPropSet->getPropertySetInfo()->hasPropertyByName( sPropName ) )
+ return nullptr;
+
+ uno::Any aTmp = xPropSet->getPropertyValue( sPropName );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ if( lcl_html_isHTMLControl( *n ) )
+ {
+ return pObj;
+ }
+ }
+
+ return nullptr;
+}
+
+static void GetControlSize(const SdrUnoObj& rFormObj, Size& rSz, SwDoc *pDoc)
+{
+ SwViewShell *pVSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( !pVSh )
+ return;
+
+ uno::Reference< awt::XControl > xControl;
+ SdrView* pDrawView = pVSh->GetDrawView();
+ OSL_ENSURE( pDrawView && pVSh->GetWin(), "no DrawView or window!" );
+ if ( pDrawView && pVSh->GetWin() )
+ xControl = rFormObj.GetUnoControl( *pDrawView, *pVSh->GetWin()->GetOutDev() );
+ uno::Reference< awt::XTextLayoutConstrains > xLC( xControl, uno::UNO_QUERY );
+ OSL_ENSURE( xLC.is(), "no XTextLayoutConstrains" );
+ if( !xLC.is() )
+ return;
+
+ sal_Int16 nCols=0, nLines=0;
+ xLC->getColumnsAndLines( nCols, nLines );
+ rSz.setWidth( nCols );
+ rSz.setHeight( nLines );
+}
+
+SwHTMLWriter& OutHTML_DrawFrameFormatAsControl( SwHTMLWriter& rWrt,
+ const SwDrawFrameFormat& rFormat,
+ const SdrUnoObj& rFormObj,
+ bool bInCntnr )
+{
+ const uno::Reference< awt::XControlModel >& xControlModel =
+ rFormObj.GetUnoControlModel();
+
+ OSL_ENSURE( xControlModel.is(), "UNO-Control without model" );
+ if( !xControlModel.is() )
+ return rWrt;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
+ xPropSet->getPropertySetInfo();
+
+ rWrt.m_nFormCntrlCnt++;
+
+ enum Tag { TAG_INPUT, TAG_SELECT, TAG_TEXTAREA, TAG_NONE };
+ static char const * const TagNames[] = {
+ OOO_STRING_SVTOOLS_HTML_input, OOO_STRING_SVTOOLS_HTML_select,
+ OOO_STRING_SVTOOLS_HTML_textarea };
+ Tag eTag = TAG_INPUT;
+ enum Type {
+ TYPE_TEXT, TYPE_PASSWORD, TYPE_CHECKBOX, TYPE_RADIO, TYPE_FILE,
+ TYPE_SUBMIT, TYPE_IMAGE, TYPE_RESET, TYPE_BUTTON, TYPE_NONE };
+ static char const * const TypeNames[] = {
+ OOO_STRING_SVTOOLS_HTML_IT_text, OOO_STRING_SVTOOLS_HTML_IT_password,
+ OOO_STRING_SVTOOLS_HTML_IT_checkbox, OOO_STRING_SVTOOLS_HTML_IT_radio,
+ OOO_STRING_SVTOOLS_HTML_IT_file, OOO_STRING_SVTOOLS_HTML_IT_submit,
+ OOO_STRING_SVTOOLS_HTML_IT_image, OOO_STRING_SVTOOLS_HTML_IT_reset,
+ OOO_STRING_SVTOOLS_HTML_IT_button };
+ Type eType = TYPE_NONE;
+ OUString sValue;
+ OString sOptions;
+ bool bEmptyValue = false;
+ uno::Any aTmp = xPropSet->getPropertyValue( "ClassId" );
+ sal_Int16 nClassId = *o3tl::doAccess<sal_Int16>(aTmp);
+ HtmlFrmOpts nFrameOpts = HTML_FRMOPTS_CONTROL;
+ switch( nClassId )
+ {
+ case form::FormComponentType::CHECKBOX:
+ case form::FormComponentType::RADIOBUTTON:
+ eType = (form::FormComponentType::CHECKBOX == nClassId
+ ? TYPE_CHECKBOX : TYPE_RADIO);
+ aTmp = xPropSet->getPropertyValue( "DefaultState" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ if ( TRISTATE_FALSE != *n )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_checked "=\""
+ OOO_STRING_SVTOOLS_HTML_O_checked
+ "\"";
+ }
+ }
+
+ aTmp = xPropSet->getPropertyValue( "RefValue" );
+ if( auto rVal = o3tl::tryAccess<OUString>(aTmp) )
+
+ {
+ if( rVal->isEmpty() )
+ bEmptyValue = true;
+ else if( *rVal != OOO_STRING_SVTOOLS_HTML_on )
+ sValue = *rVal;
+ }
+ break;
+
+ case form::FormComponentType::COMMANDBUTTON:
+ {
+ form::FormButtonType eButtonType = form::FormButtonType_PUSH;
+ aTmp = xPropSet->getPropertyValue( "ButtonType" );
+ if( auto t = o3tl::tryAccess<form::FormButtonType>(aTmp) )
+ eButtonType = *t;
+
+ switch( eButtonType )
+ {
+ case form::FormButtonType_RESET:
+ eType = TYPE_RESET;
+ break;
+ case form::FormButtonType_SUBMIT:
+ eType = TYPE_SUBMIT;
+ break;
+ case form::FormButtonType_PUSH:
+ default:
+ eType = TYPE_BUTTON;
+ }
+
+ aTmp = xPropSet->getPropertyValue( "Label" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sValue = *s;
+ }
+ }
+ }
+ break;
+
+ case form::FormComponentType::LISTBOX:
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+ eTag = TAG_SELECT;
+ aTmp = xPropSet->getPropertyValue( "Dropdown" );
+ if( auto b1 = o3tl::tryAccess<bool>(aTmp) )
+ {
+ if( !*b1 )
+ {
+ Size aSz( 0, 0 );
+ GetControlSize( rFormObj, aSz, rWrt.m_pDoc );
+
+ // How many are visible ??
+ if( aSz.Height() )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" +
+ OString::number(static_cast<sal_Int32>(aSz.Height())) + "\"";
+ }
+
+ auto aTmp2 = xPropSet->getPropertyValue( "MultiSelection" );
+ if( auto b2 = o3tl::tryAccess<bool>(aTmp2) )
+ {
+ if ( *b2 )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_multiple;
+ }
+ }
+ }
+ }
+ break;
+
+ case form::FormComponentType::TEXTFIELD:
+ {
+ Size aSz( 0, 0 );
+ GetControlSize( rFormObj, aSz, rWrt.m_pDoc );
+
+ bool bMultiLine = false;
+ OUString sMultiLine("MultiLine");
+ if( xPropSetInfo->hasPropertyByName( sMultiLine ) )
+ {
+ aTmp = xPropSet->getPropertyValue( sMultiLine );
+ std::optional<const bool> b = o3tl::tryAccess<bool>(aTmp);
+ bMultiLine = b.has_value() && *b;
+ }
+
+ if( bMultiLine )
+ {
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+ eTag = TAG_TEXTAREA;
+
+ if( aSz.Height() )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_rows "=\"" +
+ OString::number(static_cast<sal_Int32>(aSz.Height())) + "\"";
+ }
+ if( aSz.Width() )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_cols "=\"" +
+ OString::number(static_cast<sal_Int32>(aSz.Width())) + "\"";
+ }
+
+ aTmp = xPropSet->getPropertyValue( "HScroll" );
+ if( aTmp.getValueType() == cppu::UnoType<void>::get() ||
+ (aTmp.getValueType() == cppu::UnoType<bool>::get() &&
+ !*o3tl::forceAccess<bool>(aTmp)) )
+ {
+ const char *pWrapStr = nullptr;
+ auto aTmp2 = xPropSet->getPropertyValue( "HardLineBreaks" );
+ std::optional<const bool> b = o3tl::tryAccess<bool>(aTmp2);
+ pWrapStr = (b.has_value() && *b) ? OOO_STRING_SVTOOLS_HTML_WW_hard
+ : OOO_STRING_SVTOOLS_HTML_WW_soft;
+ sOptions += OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_wrap "=\"") +
+ pWrapStr + "\"";
+ }
+ }
+ else
+ {
+ eType = TYPE_TEXT;
+ OUString sEchoChar("EchoChar");
+ if( xPropSetInfo->hasPropertyByName( sEchoChar ) )
+ {
+ aTmp = xPropSet->getPropertyValue( sEchoChar );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ if( *n != 0 )
+ eType = TYPE_PASSWORD;
+ }
+ }
+
+ if( aSz.Width() )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" +
+ OString::number(static_cast<sal_Int32>(aSz.Width())) + "\"";
+ }
+
+ aTmp = xPropSet->getPropertyValue( "MaxTextLen" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ if( *n != 0 )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_maxlength "=\"" +
+ OString::number(static_cast<sal_Int32>(*n)) + "\"";
+ }
+ }
+
+ if( xPropSetInfo->hasPropertyByName( "DefaultText" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "DefaultText" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sValue = *s;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case form::FormComponentType::FILECONTROL:
+ {
+ Size aSz( 0, 0 );
+ GetControlSize( rFormObj, aSz, rWrt.m_pDoc );
+ eType = TYPE_FILE;
+
+ if( aSz.Width() )
+ {
+ sOptions += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" +
+ OString::number(static_cast<sal_Int32>(aSz.Width())) + "\"";
+ }
+
+ // VALUE vim form: don't export because of security reasons
+ }
+ break;
+
+ case form::FormComponentType::IMAGEBUTTON:
+ eType = TYPE_IMAGE;
+ nFrameOpts = HTML_FRMOPTS_IMG_CONTROL;
+ break;
+
+ default: // doesn't know HTML
+ eTag = TAG_NONE; // therefore skip it
+ break;
+ }
+
+ if( eTag == TAG_NONE )
+ return rWrt;
+
+ OString sOut = OString::Concat("<") + TagNames[eTag];
+ if( eType != TYPE_NONE )
+ {
+ sOut += OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_type "=\"") +
+ TypeNames[eType] + "\"";
+ }
+
+ aTmp = xPropSet->getPropertyValue("Name");
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), *s );
+ sOut = "\""_ostr;
+ }
+ }
+
+ aTmp = xPropSet->getPropertyValue("Enabled");
+ if( auto b = o3tl::tryAccess<bool>(aTmp) )
+ {
+ if( !*b )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_disabled;
+ }
+ }
+
+ if( !sValue.isEmpty() || bEmptyValue )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_value "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sValue );
+ sOut = "\""_ostr;
+ }
+
+ sOut += " " + sOptions;
+
+ if( TYPE_IMAGE == eType )
+ {
+ aTmp = xPropSet->getPropertyValue( "ImageURL" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_src "=\"";
+ rWrt.Strm().WriteOString( sOut );
+
+ HTMLOutFuncs::Out_String( rWrt.Strm(),
+ URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), *s) );
+ sOut = "\""_ostr;
+ }
+ }
+
+ Size aPixelSz(SwHTMLWriter::ToPixel(rFormObj.GetLogicRect().GetSize()));
+
+ if( aPixelSz.Width() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_width "=\"" +
+ OString::number(static_cast<sal_Int32>(aPixelSz.Width())) + "\"";
+ }
+
+ if( aPixelSz.Height() )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_height "=\"" +
+ OString::number(static_cast<sal_Int32>(aPixelSz.Height())) + "\"";
+ }
+ }
+
+ aTmp = xPropSet->getPropertyValue( "TabIndex" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ sal_Int16 nTabIndex = *n;
+ if( nTabIndex > 0 )
+ {
+ if( nTabIndex >= 32767 )
+ nTabIndex = 32767;
+
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_tabindex "=\"" +
+ OString::number(static_cast<sal_Int32>(nTabIndex)) + "\"";
+ }
+ }
+
+ if( !sOut.isEmpty() )
+ rWrt.Strm().WriteOString( sOut );
+
+ OSL_ENSURE( !bInCntnr, "Container is not supported for Controls" );
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_DRAW ) && !bInCntnr )
+ {
+ // If Character-Objects can't be positioned absolutely,
+ // then delete the corresponding flag.
+ nFrameOpts |= (TYPE_IMAGE == eType
+ ? HTML_FRMOPTS_IMG_CONTROL_CSS1
+ : HTML_FRMOPTS_CONTROL_CSS1);
+ }
+ OString aEndTags;
+ if( nFrameOpts != HtmlFrmOpts::NONE )
+ aEndTags = rWrt.OutFrameFormatOptions(rFormat, OUString(), nFrameOpts);
+
+ if( rWrt.m_bCfgOutStyles )
+ {
+ bool bEdit = TAG_TEXTAREA == eTag || TYPE_FILE == eType ||
+ TYPE_TEXT == eType;
+
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( rWrt.m_pDoc->GetAttrPool() );
+ if( xPropSetInfo->hasPropertyByName( "BackgroundColor" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "BackgroundColor" );
+ if( auto n = o3tl::tryAccess<sal_Int32>(aTmp) )
+ {
+ Color aCol(ColorTransparency, *n);
+ aItemSet.Put( SvxBrushItem( aCol, RES_CHRATR_BACKGROUND ) );
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "TextColor" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "TextColor" );
+ if( auto n = o3tl::tryAccess<sal_Int32>(aTmp) )
+ {
+ Color aColor( ColorTransparency, *n );
+ aItemSet.Put( SvxColorItem( aColor, RES_CHRATR_COLOR ) );
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "FontHeight" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "FontHeight" );
+ if( auto nHeight = o3tl::tryAccess<float>(aTmp) )
+
+ {
+ if( *nHeight > 0 && (!bEdit || !rtl::math::approxEqual(*nHeight, 10.0)) )
+ aItemSet.Put( SvxFontHeightItem( sal_Int16(*nHeight * 20.), 100, RES_CHRATR_FONTSIZE ) );
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "FontName" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "FontName" );
+ if( auto aFName = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !aFName->isEmpty() )
+ {
+ vcl::Font aFixedFont( OutputDevice::GetDefaultFont(
+ DefaultFontType::FIXED, LANGUAGE_ENGLISH_US,
+ GetDefaultFontFlags::OnlyOne ) );
+ if( !bEdit || *aFName != aFixedFont.GetFamilyName() )
+ {
+ FontFamily eFamily = FAMILY_DONTKNOW;
+ if( xPropSetInfo->hasPropertyByName( "FontFamily" ) )
+ {
+ auto aTmp2 = xPropSet->getPropertyValue( "FontFamily" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp2) )
+ eFamily = static_cast<FontFamily>(*n);
+ }
+ SvxFontItem aItem(eFamily, *aFName, OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
+ aItemSet.Put( aItem );
+ }
+ }
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "FontWeight" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "FontWeight" );
+ if( auto x = o3tl::tryAccess<float>(aTmp) )
+ {
+ FontWeight eWeight =
+ vcl::unohelper::ConvertFontWeight( *x );
+ if( eWeight != WEIGHT_DONTKNOW && eWeight != WEIGHT_NORMAL )
+ aItemSet.Put( SvxWeightItem( eWeight, RES_CHRATR_WEIGHT ) );
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "FontSlant" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "FontSlant" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ FontItalic eItalic = static_cast<FontItalic>(*n);
+ if( eItalic != ITALIC_DONTKNOW && eItalic != ITALIC_NONE )
+ aItemSet.Put( SvxPostureItem( eItalic, RES_CHRATR_POSTURE ) );
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "FontLineStyle" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "FontLineStyle" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ FontLineStyle eUnderline = static_cast<FontLineStyle>(*n);
+ if( eUnderline != LINESTYLE_DONTKNOW &&
+ eUnderline != LINESTYLE_NONE )
+ aItemSet.Put( SvxUnderlineItem( eUnderline, RES_CHRATR_UNDERLINE ) );
+ }
+ }
+ if( xPropSetInfo->hasPropertyByName( "FontStrikeout" ) )
+ {
+ aTmp = xPropSet->getPropertyValue( "FontStrikeout" );
+ if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) )
+ {
+ FontStrikeout eStrikeout = static_cast<FontStrikeout>(*n);
+ if( eStrikeout != STRIKEOUT_DONTKNOW &&
+ eStrikeout != STRIKEOUT_NONE )
+ aItemSet.Put( SvxCrossedOutItem( eStrikeout, RES_CHRATR_CROSSEDOUT ) );
+ }
+ }
+
+ rWrt.OutCSS1_FrameFormatOptions( rFormat, nFrameOpts, &rFormObj,
+ &aItemSet );
+ }
+
+ uno::Reference< form::XFormComponent > xFormComp( xControlModel, uno::UNO_QUERY );
+ lcl_html_outEvents( rWrt.Strm(), xFormComp, rWrt.m_bCfgStarBasic );
+
+ rWrt.Strm().WriteChar( '>' );
+
+ if( TAG_SELECT == eTag )
+ {
+ aTmp = xPropSet->getPropertyValue( "StringItemList" );
+ if( auto aList = o3tl::tryAccess<uno::Sequence<OUString>>(aTmp) )
+ {
+ rWrt.IncIndentLevel(); // the content of Select can be indented
+ sal_Int32 nCnt = aList->getLength();
+ const OUString *pStrings = aList->getConstArray();
+
+ const OUString *pValues = nullptr;
+ sal_Int32 nValCnt = 0;
+ auto aTmp2 = xPropSet->getPropertyValue( "ListSource" );
+ uno::Sequence<OUString> aValList;
+ if( auto s = o3tl::tryAccess<uno::Sequence<OUString>>(aTmp2) )
+ {
+ aValList = *s;
+ nValCnt = aValList.getLength();
+ pValues = aValList.getConstArray();
+ }
+
+ uno::Any aSelTmp = xPropSet->getPropertyValue( "DefaultSelection" );
+ const sal_Int16 *pSels = nullptr;
+ sal_Int32 nSel = 0;
+ sal_Int32 nSelCnt = 0;
+ uno::Sequence<sal_Int16> aSelList;
+ if( auto s = o3tl::tryAccess<uno::Sequence<sal_Int16>>(aSelTmp) )
+ {
+ aSelList = *s;
+ nSelCnt = aSelList.getLength();
+ pSels = aSelList.getConstArray();
+ }
+
+ for( sal_Int32 i = 0; i < nCnt; i++ )
+ {
+ OUString sVal;
+ bool bSelected = false, bEmptyVal = false;
+ if( i < nValCnt )
+ {
+ const OUString& rVal = pValues[i];
+ if( rVal == "$$$empty$$$" )
+ bEmptyVal = true;
+ else
+ sVal = rVal;
+ }
+
+ bSelected = (nSel < nSelCnt) && pSels[nSel] == i;
+ if( bSelected )
+ nSel++;
+
+ rWrt.OutNewLine(); // every Option gets its own line
+ sOut = "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_option;
+ if( !sVal.isEmpty() || bEmptyVal )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_value "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sVal );
+ sOut = "\""_ostr;
+ }
+ if( bSelected )
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_selected;
+
+ sOut += ">";
+ rWrt.Strm().WriteOString( sOut );
+
+ HTMLOutFuncs::Out_String( rWrt.Strm(), pStrings[i] );
+ }
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_option), false );
+
+ rWrt.DecIndentLevel();
+ rWrt.OutNewLine();// the </SELECT> gets its own line
+ }
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_select), false );
+ }
+ else if( TAG_TEXTAREA == eTag )
+ {
+ // In TextAreas no additional spaces or LF may be exported!
+ OUString sVal;
+ aTmp = xPropSet->getPropertyValue( "DefaultText" );
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ sVal = *s;
+ }
+ }
+ if( !sVal.isEmpty() )
+ {
+ sVal = convertLineEnd(sVal, LINEEND_LF);
+ sal_Int32 nPos = 0;
+ while ( nPos != -1 )
+ {
+ if( nPos )
+ rWrt.Strm().WriteOString( SAL_NEWLINE_STRING );
+ OUString aLine = sVal.getToken( 0, 0x0A, nPos );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aLine );
+ }
+ }
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_textarea), false );
+ }
+ else if( TYPE_CHECKBOX == eType || TYPE_RADIO == eType )
+ {
+ aTmp = xPropSet->getPropertyValue("Label");
+ if( auto s = o3tl::tryAccess<OUString>(aTmp) )
+ {
+ if( !s->isEmpty() )
+ {
+ HTMLOutFuncs::Out_String( rWrt.Strm(), *s ).WriteChar( ' ' );
+ }
+ }
+ }
+
+ if( !aEndTags.isEmpty() )
+ rWrt.Strm().WriteOString( aEndTags );
+
+ // Controls aren't bound to a paragraph, therefore don't output LF anymore!
+ rWrt.SetLFPossible(false);
+
+ if( rWrt.mxFormComps.is() )
+ rWrt.OutHiddenControls( rWrt.mxFormComps, xPropSet );
+ return rWrt;
+}
+
+/**
+ * Find out if a format belongs to a control and if yes return its form.
+ */
+static void AddControl( HTMLControls& rControls,
+ const SdrUnoObj& rFormObj,
+ SwNodeOffset nNodeIdx )
+{
+ const uno::Reference< awt::XControlModel >& xControlModel =
+ rFormObj.GetUnoControlModel();
+ if( !xControlModel.is() )
+ return;
+
+ uno::Reference< form::XFormComponent > xFormComp( xControlModel, uno::UNO_QUERY );
+ uno::Reference< uno::XInterface > xIfc = xFormComp->getParent();
+ uno::Reference< form::XForm > xForm(xIfc, uno::UNO_QUERY);
+
+ OSL_ENSURE( xForm.is(), "Where is the form?" );
+ if( xForm.is() )
+ {
+ uno::Reference< container::XIndexContainer > xFormComps( xForm, uno::UNO_QUERY );
+ std::unique_ptr<HTMLControl> pHCntrl(new HTMLControl( xFormComps, nNodeIdx ));
+ auto itPair = rControls.insert( std::move(pHCntrl) );
+ if (!itPair.second )
+ {
+ if( (*itPair.first)->xFormComps==xFormComps )
+ (*itPair.first)->nCount++;
+ }
+ }
+}
+
+void SwHTMLWriter::GetControls()
+{
+ // Idea: first off collect the paragraph- and character-bound controls.
+ // In the process for every control the paragraph position and VCForm are
+ // saved in an array.
+ // With that array it's possible to find out where form::Forms must be
+ // opened and closed.
+
+ // collect the paragraph-bound controls
+ for( size_t i=0; i<m_aHTMLPosFlyFrames.size(); i++ )
+ {
+ const SwHTMLPosFlyFrame* pPosFlyFrame = m_aHTMLPosFlyFrames[ i ].get();
+ if( HtmlOut::Control != pPosFlyFrame->GetOutFn() )
+ continue;
+
+ const SdrObject *pSdrObj = pPosFlyFrame->GetSdrObject();
+ OSL_ENSURE( pSdrObj, "Where is the SdrObject?" );
+ if( !pSdrObj )
+ continue;
+
+ AddControl( m_aHTMLControls, dynamic_cast<const SdrUnoObj&>(*pSdrObj),
+ pPosFlyFrame->GetNdIndex().GetIndex() );
+ }
+
+ // and now the ones in a character-bound frame
+ for(sw::SpzFrameFormat* pSpz: *m_pDoc->GetSpzFrameFormats())
+ {
+ if( RES_DRAWFRMFMT != pSpz->Which() )
+ continue;
+
+ const SwFormatAnchor& rAnchor = pSpz->GetAnchor();
+ const SwNode *pAnchorNode = rAnchor.GetAnchorNode();
+ if ((RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId()) || !pAnchorNode)
+ continue;
+
+ const SdrObject *pSdrObj =
+ SwHTMLWriter::GetHTMLControl(*static_cast<SwDrawFrameFormat*>(pSpz) );
+ if( !pSdrObj )
+ continue;
+
+ AddControl( m_aHTMLControls, dynamic_cast<const SdrUnoObj&>(*pSdrObj), pAnchorNode->GetIndex() );
+ }
+}
+
+HTMLControl::HTMLControl(
+ uno::Reference< container::XIndexContainer > _xFormComps,
+ SwNodeOffset nIdx ) :
+ xFormComps(std::move( _xFormComps )), nNdIdx( nIdx ), nCount( 1 )
+{}
+
+HTMLControl::~HTMLControl()
+{}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlftn.cxx b/sw/source/filter/html/htmlftn.cxx
new file mode 100644
index 0000000000..db24a32bb7
--- /dev/null
+++ b/sw/source/filter/html/htmlftn.cxx
@@ -0,0 +1,598 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <osl/diagnose.h>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/HtmlWriter.hxx>
+#include <rtl/strbuf.hxx>
+#include <ndindex.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <ftninfo.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <charfmt.hxx>
+
+#include "swhtml.hxx"
+#include "wrthtml.hxx"
+
+static sal_Int32 lcl_html_getNextPart( OUString& rPart, std::u16string_view aContent,
+ sal_Int32 nPos )
+{
+ rPart.clear();
+ sal_Int32 nLen = aContent.size();
+ if( nPos >= nLen )
+ {
+ nPos = -1;
+ }
+ else
+ {
+ bool bQuoted = false, bDone = false;
+ for( ; nPos < nLen && !bDone; nPos++ )
+ {
+ sal_Unicode c = aContent[nPos];
+ switch( c )
+ {
+ case '\\':
+ if( bQuoted )
+ rPart += OUStringChar( c );
+ bQuoted = !bQuoted;
+ break;
+
+ case ';':
+ if( bQuoted )
+ rPart += OUStringChar( c );
+ else
+ bDone = true;
+ bQuoted = false;
+ break;
+
+ default:
+ rPart += OUStringChar( c );
+ bQuoted = false;
+ break;
+ }
+ }
+ }
+
+ return nPos;
+}
+
+static sal_Int32 lcl_html_getEndNoteInfo( SwEndNoteInfo& rInfo,
+ std::u16string_view aContent,
+ bool bEndNote )
+{
+ sal_Int32 nStrPos = 0;
+ for( int nPart = 0; nPart < 4; ++nPart )
+ {
+ OUString aPart;
+ if( -1 != nStrPos )
+ nStrPos = lcl_html_getNextPart( aPart, aContent, nStrPos );
+
+ switch( nPart )
+ {
+ case 0:
+ rInfo.m_aFormat.SetNumberingType( bEndNote ? SVX_NUM_ROMAN_LOWER : SVX_NUM_ARABIC );
+ if( !aPart.isEmpty() )
+ rInfo.m_aFormat.SetNumberingType(SwHTMLParser::GetNumType( aPart,
+ rInfo.m_aFormat.GetNumberingType() ));
+ break;
+
+ case 1:
+ rInfo.m_nFootnoteOffset = aPart.isEmpty() ? 0 : o3tl::narrowing<sal_uInt16>(aPart.toInt32());
+ break;
+
+ case 2:
+ rInfo.SetPrefix( aPart );
+ break;
+
+ case 3:
+ rInfo.SetSuffix( aPart );
+ break;
+ }
+ }
+
+ return nStrPos;
+}
+
+void SwHTMLParser::FillEndNoteInfo( std::u16string_view aContent )
+{
+ SwEndNoteInfo aInfo( m_xDoc->GetEndNoteInfo() );
+ lcl_html_getEndNoteInfo( aInfo, aContent, true );
+ m_xDoc->SetEndNoteInfo( aInfo );
+}
+
+void SwHTMLParser::FillFootNoteInfo( std::u16string_view aContent )
+{
+ SwFootnoteInfo aInfo( m_xDoc->GetFootnoteInfo() );
+
+ sal_Int32 nStrPos = lcl_html_getEndNoteInfo( aInfo, aContent, false );
+
+ for( int nPart = 4; nPart < 8; ++nPart )
+ {
+ OUString aPart;
+ if( -1 != nStrPos )
+ nStrPos = lcl_html_getNextPart( aPart, aContent, nStrPos );
+
+ switch( nPart )
+ {
+ case 4:
+ aInfo.m_eNum = FTNNUM_DOC;
+ if( !aPart.isEmpty() )
+ {
+ switch( aPart[0] )
+ {
+ case 'D': aInfo.m_eNum = FTNNUM_DOC; break;
+ case 'C': aInfo.m_eNum = FTNNUM_CHAPTER; break;
+ case 'P': aInfo.m_eNum = FTNNUM_PAGE; break;
+ }
+ }
+ break;
+
+ case 5:
+ aInfo.m_ePos = FTNPOS_PAGE;
+ if( !aPart.isEmpty() )
+ {
+ switch( aPart[0] )
+ {
+ case 'C': aInfo.m_ePos = FTNPOS_CHAPTER; break;
+ case 'P': aInfo.m_ePos = FTNPOS_PAGE; break;
+ }
+ }
+ break;
+
+ case 6:
+ aInfo.m_aQuoVadis = aPart;
+ break;
+
+ case 7:
+ aInfo.m_aErgoSum = aPart;
+ break;
+ }
+ }
+
+ m_xDoc->SetFootnoteInfo( aInfo );
+}
+
+void SwHTMLParser::InsertFootEndNote( const OUString& rName, bool bEndNote,
+ bool bFixed )
+{
+ if( !m_pFootEndNoteImpl )
+ m_pFootEndNoteImpl.reset(new SwHTMLFootEndNote_Impl);
+
+ m_pFootEndNoteImpl->sName = rName;
+ if( m_pFootEndNoteImpl->sName.getLength() > 3 )
+ m_pFootEndNoteImpl->sName = m_pFootEndNoteImpl->sName.copy( 0, m_pFootEndNoteImpl->sName.getLength() - 3 );
+ m_pFootEndNoteImpl->sName = m_pFootEndNoteImpl->sName.toAsciiUpperCase();
+ m_pFootEndNoteImpl->bEndNote = bEndNote;
+ m_pFootEndNoteImpl->bFixed = bFixed;
+ m_pFootEndNoteImpl->sContent.clear();
+}
+
+void SwHTMLParser::FinishFootEndNote()
+{
+ if( !m_pFootEndNoteImpl )
+ return;
+
+ SwFormatFootnote aFootnote( m_pFootEndNoteImpl->bEndNote );
+ if( m_pFootEndNoteImpl->bFixed )
+ aFootnote.SetNumStr( m_pFootEndNoteImpl->sContent );
+
+ m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, aFootnote );
+ SwTextFootnote * const pTextFootnote = static_cast<SwTextFootnote *>(
+ m_pPam->GetPointNode().GetTextNode()->GetTextAttrForCharAt(
+ m_pPam->GetPoint()->GetContentIndex() - 1, RES_TXTATR_FTN ) );
+ // In header and footer no footnotes can be inserted.
+ if (pTextFootnote)
+ m_pFootEndNoteImpl->aTextFootnotes.push_back(SwHTMLTextFootnote(m_pFootEndNoteImpl->sName,pTextFootnote));
+ m_pFootEndNoteImpl->sName.clear();
+ m_pFootEndNoteImpl->sContent.clear();
+ m_pFootEndNoteImpl->bFixed = false;
+}
+
+void SwHTMLParser::InsertFootEndNoteText()
+{
+ if( m_pFootEndNoteImpl && m_pFootEndNoteImpl->bFixed )
+ m_pFootEndNoteImpl->sContent += aToken;
+}
+
+SwNodeIndex *SwHTMLParser::GetFootEndNoteSection( const OUString& rName )
+{
+ SwNodeIndex *pStartNodeIdx = nullptr;
+
+ if (m_pFootEndNoteImpl)
+ {
+ OUString aName(rName.toAsciiUpperCase());
+
+ size_t nCount = m_pFootEndNoteImpl->aTextFootnotes.size();
+ for(size_t i = 0; i < nCount; ++i)
+ {
+ if (m_pFootEndNoteImpl->aTextFootnotes[i].GetName() == aName)
+ {
+ pStartNodeIdx = const_cast<SwNodeIndex*>(m_pFootEndNoteImpl->aTextFootnotes[i].GetStartNode());
+ m_pFootEndNoteImpl->aTextFootnotes.erase( m_pFootEndNoteImpl->aTextFootnotes.begin() + i );
+ if (m_pFootEndNoteImpl->aTextFootnotes.empty())
+ {
+ m_pFootEndNoteImpl.reset();
+ }
+
+ break;
+ }
+ }
+ }
+
+ return pStartNodeIdx;
+}
+
+SwHTMLWriter& OutHTML_SwFormatLineBreak(SwHTMLWriter& rWrt, const SfxPoolItem& rHt)
+{
+ const auto& rLineBreak = static_cast<const SwFormatLineBreak&>(rHt);
+
+ HtmlWriter aWriter(rWrt.Strm(), rWrt.maNamespace);
+ aWriter.start(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
+ switch (rLineBreak.GetValue())
+ {
+ case SwLineBreakClear::NONE:
+ aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "none");
+ break;
+ case SwLineBreakClear::LEFT:
+ aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "left");
+ break;
+ case SwLineBreakClear::RIGHT:
+ aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "right");
+ break;
+ case SwLineBreakClear::ALL:
+ aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "all");
+ break;
+ }
+ aWriter.end();
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_SwFormatFootnote( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
+{
+ SwFormatFootnote& rFormatFootnote = const_cast<SwFormatFootnote&>(static_cast<const SwFormatFootnote&>(rHt));
+ SwTextFootnote *pTextFootnote = rFormatFootnote.GetTextFootnote();
+ if( !pTextFootnote )
+ return rWrt;
+
+ OUString sFootnoteName, sClass;
+ size_t nPos;
+ if( rFormatFootnote.IsEndNote() )
+ {
+ nPos = rWrt.m_xFootEndNotes ? rWrt.m_xFootEndNotes->size() : 0;
+ OSL_ENSURE( nPos == static_cast<size_t>(rWrt.m_nFootNote + rWrt.m_nEndNote),
+ "OutHTML_SwFormatFootnote: wrong position" );
+ sClass = OOO_STRING_SVTOOLS_HTML_sdendnote_anc;
+ sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + OUString::number( static_cast<sal_Int32>(++rWrt.m_nEndNote) );
+ }
+ else
+ {
+ nPos = rWrt.m_nFootNote;
+ sClass = OOO_STRING_SVTOOLS_HTML_sdfootnote_anc;
+ sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number( static_cast<sal_Int32>(++rWrt.m_nFootNote));
+ }
+
+ if( !rWrt.m_xFootEndNotes )
+ rWrt.m_xFootEndNotes.emplace();
+ rWrt.m_xFootEndNotes->insert( rWrt.m_xFootEndNotes->begin() + nPos, pTextFootnote );
+
+ OStringBuffer sOut;
+ OString aTag = rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor;
+ sOut.append("<" + aTag + " " OOO_STRING_SVTOOLS_HTML_O_class "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sClass );
+ sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sFootnoteName );
+ sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_anchor "\" "
+ OOO_STRING_SVTOOLS_HTML_O_href "=\"#");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sFootnoteName );
+ sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_symbol "\"");
+ if( !rFormatFootnote.GetNumStr().isEmpty() )
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_sdfixed);
+ sOut.append(">");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_superscript ));
+
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rFormatFootnote.GetViewNumStr(*rWrt.m_pDoc, nullptr) );
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_superscript), false );
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
+
+ return rWrt;
+}
+
+void SwHTMLWriter::OutFootEndNotes()
+{
+ OSL_ENSURE( m_xFootEndNotes,
+ "SwHTMLWriter::OutFootEndNotes(): unnecessary call" );
+ if( !m_xFootEndNotes )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt16 nFootnote = m_nFootNote, nEn = m_nEndNote;
+#endif
+ m_nFootNote = 0;
+ m_nEndNote = 0;
+
+ for( auto *pTextFootnote : *m_xFootEndNotes )
+ {
+ m_pFormatFootnote = &pTextFootnote->GetFootnote();
+
+ OUString sFootnoteName;
+ if( m_pFormatFootnote->IsEndNote() )
+ {
+ sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + OUString::number(static_cast<sal_Int32>(++m_nEndNote));
+ }
+ else
+ {
+ sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number(static_cast<sal_Int32>(++m_nFootNote));
+ }
+
+ if (IsLFPossible())
+ OutNewLine();
+ OString sOut =
+ "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
+ " " OOO_STRING_SVTOOLS_HTML_O_id "=\"";
+ Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( Strm(), sFootnoteName );
+ Strm().WriteOString( "\">" );
+
+ SetLFPossible(true);
+ IncIndentLevel(); // indent content of <DIV>
+
+ OSL_ENSURE( pTextFootnote, "SwHTMLWriter::OutFootEndNotes: SwTextFootnote is missing" );
+ const SwNodeIndex *pSttNdIdx = pTextFootnote->GetStartNode();
+ OSL_ENSURE( pSttNdIdx,
+ "SwHTMLWriter::OutFootEndNotes: StartNode-Index is missing" );
+ if( pSttNdIdx )
+ {
+ HTMLSaveData aSaveData( *this, pSttNdIdx->GetIndex()+1,
+ pSttNdIdx->GetNode().EndOfSectionIndex(), false );
+ Out_SwDoc( m_pCurrentPam.get() );
+ }
+
+ DecIndentLevel(); // indent content of <DIV>
+ if (IsLFPossible())
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
+ SetLFPossible(true);
+
+ OSL_ENSURE( !m_pFormatFootnote,
+ "SwHTMLWriter::OutFootEndNotes: Footnote was not output" );
+ if( m_pFormatFootnote )
+ {
+ if( m_pFormatFootnote->IsEndNote() )
+ m_nEndNote++;
+ else
+ m_nFootNote++;
+
+ m_pFormatFootnote = nullptr;
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( nFootnote == m_nFootNote,
+ "SwHTMLWriter::OutFootEndNotes: Number of footnotes does not match" );
+ OSL_ENSURE( nEn == m_nEndNote,
+ "SwHTMLWriter::OutFootEndNotes: Number of endnotes does not match" );
+#endif
+
+ m_xFootEndNotes.reset();
+ m_nFootNote = m_nEndNote = 0;
+}
+
+OUString SwHTMLWriter::GetFootEndNoteSym( const SwFormatFootnote& rFormatFootnote )
+{
+ const SwEndNoteInfo * pInfo = nullptr;
+ if( rFormatFootnote.GetNumStr().isEmpty() )
+ pInfo = rFormatFootnote.IsEndNote() ? &m_pDoc->GetEndNoteInfo()
+ : &m_pDoc->GetFootnoteInfo();
+
+ OUString sRet;
+ if( pInfo )
+ sRet = pInfo->GetPrefix();
+ sRet += rFormatFootnote.GetViewNumStr(*m_pDoc, nullptr);
+ if( pInfo )
+ sRet += pInfo->GetSuffix();
+
+ return sRet;
+}
+
+void SwHTMLWriter::OutFootEndNoteSym( const SwFormatFootnote& rFormatFootnote,
+ const OUString& rNum,
+ sal_uInt16 nScript )
+{
+ const SwEndNoteInfo *pInfo;
+
+ OUString sFootnoteName, sClass;
+ if( rFormatFootnote.IsEndNote() )
+ {
+ sClass = OOO_STRING_SVTOOLS_HTML_sdendnote_sym;
+ sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote +
+ OUString::number(static_cast<sal_Int32>(m_nEndNote));
+ pInfo = &m_pDoc->GetEndNoteInfo();
+ }
+ else
+ {
+ sClass = OOO_STRING_SVTOOLS_HTML_sdfootnote_sym;
+ sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote +
+ OUString::number(static_cast<sal_Int32>(m_nFootNote));
+ pInfo = &m_pDoc->GetFootnoteInfo();
+ }
+
+ const SwCharFormat *pSymCharFormat = pInfo->GetCharFormat( *m_pDoc );
+ if( pSymCharFormat && 0 != m_aScriptTextStyles.count( pSymCharFormat->GetName() ) )
+ {
+ switch( nScript )
+ {
+ case CSS1_OUTMODE_WESTERN:
+ sClass += "-western";
+ break;
+ case CSS1_OUTMODE_CJK:
+ sClass += "-cjk";
+ break;
+ case CSS1_OUTMODE_CTL:
+ sClass += "-ctl";
+ break;
+ }
+ }
+
+ OStringBuffer sOut("<"
+ + GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor " "
+ OOO_STRING_SVTOOLS_HTML_O_class "=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), sClass );
+ sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), sFootnoteName );
+ sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_symbol "\" "
+ OOO_STRING_SVTOOLS_HTML_O_href "=\"#");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), sFootnoteName );
+ sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_anchor "\">");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ HTMLOutFuncs::Out_String( Strm(), rNum );
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
+}
+
+static int lcl_html_fillEndNoteInfo( const SwEndNoteInfo& rInfo,
+ OUString *pParts,
+ bool bEndNote )
+{
+ int nParts = 0;
+ sal_Int16 eFormat = rInfo.m_aFormat.GetNumberingType();
+ if( (bEndNote ? SVX_NUM_ROMAN_LOWER : SVX_NUM_ARABIC) != eFormat )
+ {
+ const char *pStr = SwHTMLWriter::GetNumFormat( eFormat );
+ if( pStr )
+ {
+ pParts[0] = OUString::createFromAscii( pStr );
+ nParts = 1;
+ }
+ }
+ if( rInfo.m_nFootnoteOffset > 0 )
+ {
+ pParts[1] = OUString::number(rInfo.m_nFootnoteOffset);
+ nParts = 2;
+ }
+ if( !rInfo.GetPrefix().isEmpty() )
+ {
+ pParts[2] = rInfo.GetPrefix();
+ nParts = 3;
+ }
+ if( !rInfo.GetSuffix().isEmpty() )
+ {
+ pParts[3] = rInfo.GetSuffix();
+ nParts = 4;
+ }
+
+ return nParts;
+}
+
+static void lcl_html_outFootEndNoteInfo( SwHTMLWriter& rWrt, OUString const *pParts,
+ int nParts, const char *pName )
+{
+ OUStringBuffer aContent;
+ for( int i=0; i<nParts; ++i )
+ {
+ OUString aTmp( pParts[i] );
+ aTmp = aTmp.replaceAll( "\\", "\\\\" );
+ aTmp = aTmp.replaceAll( ";", "\\;" );
+ if( i > 0 )
+ aContent.append(";");
+ aContent.append(aTmp);
+ }
+
+ rWrt.OutNewLine();
+ OString sOut =
+ "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_meta " "
+ OOO_STRING_SVTOOLS_HTML_O_name "=\"" + pName +
+ "\" " OOO_STRING_SVTOOLS_HTML_O_content "=\"";
+ rWrt.Strm().WriteOString( sOut );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aContent.makeStringAndClear() );
+ rWrt.Strm().WriteOString( "\">" );
+}
+
+void SwHTMLWriter::OutFootEndNoteInfo()
+{
+ // Number type (1 or i)
+ // Offset (0)
+ // Before it
+ // Behind it
+ // Doc/Page/Chap (D)
+ // Position (S)
+ // Next page
+ // Beginning
+
+ {
+ const SwFootnoteInfo& rInfo = m_pDoc->GetFootnoteInfo();
+ OUString aParts[8];
+ int nParts = lcl_html_fillEndNoteInfo( rInfo, aParts, false );
+ if( rInfo.m_eNum != FTNNUM_DOC )
+ {
+ aParts[4] = rInfo.m_eNum == FTNNUM_CHAPTER ? std::u16string_view( u"C" ) : std::u16string_view( u"P" );
+ nParts = 5;
+ }
+ if( rInfo.m_ePos != FTNPOS_PAGE)
+ {
+ aParts[5] = "C";
+ nParts = 6;
+ }
+ if( !rInfo.m_aQuoVadis.isEmpty() )
+ {
+ aParts[6] = rInfo.m_aQuoVadis;
+ nParts = 7;
+ }
+ if( !rInfo.m_aErgoSum.isEmpty() )
+ {
+ aParts[7] = rInfo.m_aErgoSum;
+ nParts = 8;
+ }
+ if( nParts > 0 )
+ lcl_html_outFootEndNoteInfo( *this, aParts, nParts,
+ OOO_STRING_SVTOOLS_HTML_META_sdfootnote );
+ }
+
+ {
+ const SwEndNoteInfo& rInfo = m_pDoc->GetEndNoteInfo();
+ OUString aParts[4];
+ const int nParts = lcl_html_fillEndNoteInfo( rInfo, aParts, true );
+ if( nParts > 0 )
+ lcl_html_outFootEndNoteInfo( *this, aParts, nParts,
+ OOO_STRING_SVTOOLS_HTML_META_sdendnote );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlgrin.cxx b/sw/source/filter/html/htmlgrin.cxx
new file mode 100644
index 0000000000..072b8945d2
--- /dev/null
+++ b/sw/source/filter/html/htmlgrin.cxx
@@ -0,0 +1,1574 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svl/stritem.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/languageoptions.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/langitem.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/event.hxx>
+#include <vcl/imap.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <unotools/eventcfg.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <fmtornt.hxx>
+#include <fmturl.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <charatr.hxx>
+#include <frmfmt.hxx>
+#include <charfmt.hxx>
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <poolfmt.hxx>
+#include <IMark.hxx>
+#include <ndgrf.hxx>
+#include "htmlnum.hxx"
+#include "swcss1.hxx"
+#include "swhtml.hxx"
+#include <numrule.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <frameformats.hxx>
+
+#include <vcl/graphicfilter.hxx>
+#include <tools/UnitConversion.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/securityoptions.hxx>
+
+using namespace ::com::sun::star;
+
+HTMLOptionEnum<sal_Int16> const aHTMLImgHAlignTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_AL_left, text::HoriOrientation::LEFT },
+ { OOO_STRING_SVTOOLS_HTML_AL_right, text::HoriOrientation::RIGHT },
+ { nullptr, 0 }
+};
+
+HTMLOptionEnum<sal_Int16> const aHTMLImgVAlignTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_VA_top, text::VertOrientation::LINE_TOP },
+ { OOO_STRING_SVTOOLS_HTML_VA_texttop, text::VertOrientation::CHAR_TOP },
+ { OOO_STRING_SVTOOLS_HTML_VA_middle, text::VertOrientation::CENTER },
+ { OOO_STRING_SVTOOLS_HTML_AL_center, text::VertOrientation::CENTER },
+ { OOO_STRING_SVTOOLS_HTML_VA_absmiddle, text::VertOrientation::LINE_CENTER },
+ { OOO_STRING_SVTOOLS_HTML_VA_bottom, text::VertOrientation::TOP },
+ { OOO_STRING_SVTOOLS_HTML_VA_baseline, text::VertOrientation::TOP },
+ { OOO_STRING_SVTOOLS_HTML_VA_absbottom, text::VertOrientation::LINE_BOTTOM },
+ { nullptr, 0 }
+};
+
+ImageMap *SwHTMLParser::FindImageMap( std::u16string_view rName ) const
+{
+ OSL_ENSURE( rName[0] != '#', "FindImageMap: name begins with '#'!" );
+
+ if (m_pImageMaps)
+ {
+ for (const auto &rpIMap : *m_pImageMaps)
+ {
+ if (o3tl::equalsIgnoreAsciiCase(rName, rpIMap->GetName()))
+ {
+ return rpIMap.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SwHTMLParser::ConnectImageMaps()
+{
+ SwNodes& rNds = m_xDoc->GetNodes();
+ // on the first node of section #1
+ SwNodeOffset nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + 1;
+ SwNodeOffset nEndIdx = rNds.GetEndOfAutotext().GetIndex();
+
+ SwGrfNode* pGrfNd;
+ while( m_nMissingImgMaps > 0 && nIdx < nEndIdx )
+ {
+ SwNode *pNd = rNds[nIdx + 1];
+ pGrfNd = pNd->GetGrfNode();
+ if( nullptr != pGrfNd )
+ {
+ SwFrameFormat *pFormat = pGrfNd->GetFlyFormat();
+ SwFormatURL aURL( pFormat->GetURL() );
+ const ImageMap *pIMap = aURL.GetMap();
+ if( pIMap && pIMap->GetIMapObjectCount()==0 )
+ {
+ // The (empty) image map of the node will be either
+ // replaced with found image map or deleted.
+ ImageMap *pNewIMap =
+ FindImageMap( pIMap->GetName() );
+ aURL.SetMap( pNewIMap );
+ pFormat->SetFormatAttr( aURL );
+ if( !pGrfNd->IsScaleImageMap() )
+ {
+ // meanwhile the graphic size is known or the
+ // graphic don't need scaling
+ pGrfNd->ScaleImageMap();
+ }
+ m_nMissingImgMaps--; // search a map less
+ }
+ }
+ nIdx = rNds[nIdx]->EndOfSectionIndex() + 1;
+ }
+}
+
+void SwHTMLParser::SetAnchorAndAdjustment( sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ const SvxCSS1PropertyInfo &rCSS1PropInfo,
+ SfxItemSet& rFrameItemSet )
+{
+ const SfxItemSet *pCntnrItemSet = nullptr;
+ auto i = m_aContexts.size();
+ while( !pCntnrItemSet && i > m_nContextStMin )
+ pCntnrItemSet = m_aContexts[--i]->GetFrameItemSet();
+
+ if( pCntnrItemSet )
+ {
+ // If we are in a container then the anchoring of the container is used.
+ rFrameItemSet.Put( *pCntnrItemSet );
+ }
+ else if( SwCSS1Parser::MayBePositioned( rCSS1PropInfo, true ) )
+ {
+ // If the alignment can be set via CSS1 options we use them.
+ SetAnchorAndAdjustment( rCSS1PropInfo, rFrameItemSet );
+ }
+ else
+ {
+ // Otherwise the alignment is set correspondingly the normal HTML options.
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, rFrameItemSet );
+ }
+}
+
+void SwHTMLParser::SetAnchorAndAdjustment( sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ SfxItemSet& rFrameSet,
+ bool bDontAppend )
+{
+ bool bMoveBackward = false;
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR );
+ sal_Int16 eVertRel = text::RelOrientation::FRAME;
+
+ if( text::HoriOrientation::NONE != eHoriOri )
+ {
+ // determine paragraph indent
+ sal_uInt16 nLeftSpace = 0, nRightSpace = 0;
+ short nIndent = 0;
+ GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent );
+
+ // determine horizontal alignment and wrapping
+ sal_Int16 eHoriRel;
+ css::text::WrapTextMode eSurround;
+ switch( eHoriOri )
+ {
+ case text::HoriOrientation::LEFT:
+ eHoriRel = nLeftSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME;
+ eSurround = css::text::WrapTextMode_RIGHT;
+ break;
+ case text::HoriOrientation::RIGHT:
+ eHoriRel = nRightSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME;
+ eSurround = css::text::WrapTextMode_LEFT;
+ break;
+ case text::HoriOrientation::CENTER: // for tables
+ eHoriRel = text::RelOrientation::FRAME;
+ eSurround = css::text::WrapTextMode_NONE;
+ break;
+ default:
+ eHoriRel = text::RelOrientation::FRAME;
+ eSurround = css::text::WrapTextMode_PARALLEL;
+ break;
+ }
+
+ // Create a new paragraph, if the current one has frames
+ // anchored at paragraph/at char without wrapping.
+ if( !bDontAppend && HasCurrentParaFlys( true ) )
+ {
+ // When the paragraph only contains graphics then there
+ // is no need for bottom margin. Since here also with use of
+ // styles no margin should be created, set attributes to
+ // override!
+ sal_uInt16 nUpper=0, nLower=0;
+ GetULSpaceFromContext( nUpper, nLower );
+ InsertAttr( SvxULSpaceItem( nUpper, 0, RES_UL_SPACE ), true );
+
+ AppendTextNode( AM_NOSPACE );
+
+ if( nUpper )
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, SvxULSpaceItem(0, nLower, RES_UL_SPACE));
+ m_aParaAttrs.push_back( m_xAttrTab->pULSpace );
+ EndAttr( m_xAttrTab->pULSpace, false );
+ }
+ }
+
+ // determine vertical alignment and anchoring
+ const sal_Int32 nContent = m_pPam->GetPoint()->GetContentIndex();
+ if( nContent )
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_CHAR );
+ bMoveBackward = true;
+ eVertOri = text::VertOrientation::CHAR_BOTTOM;
+ eVertRel = text::RelOrientation::CHAR;
+ }
+ else
+ {
+ aAnchor.SetType( RndStdIds::FLY_AT_PARA );
+ eVertOri = text::VertOrientation::TOP;
+ eVertRel = text::RelOrientation::PRINT_AREA;
+ }
+
+ rFrameSet.Put( SwFormatHoriOrient( 0, eHoriOri, eHoriRel) );
+
+ rFrameSet.Put( SwFormatSurround( eSurround ) );
+ }
+ rFrameSet.Put( SwFormatVertOrient( 0, eVertOri, eVertRel) );
+
+ if( bMoveBackward )
+ m_pPam->Move( fnMoveBackward );
+
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR && !m_pPam->GetPointNode().GetTextNode())
+ {
+ eState = SvParserState::Error;
+ return;
+ }
+
+ aAnchor.SetAnchor( m_pPam->GetPoint() );
+
+ if( bMoveBackward )
+ m_pPam->Move( fnMoveForward );
+
+ rFrameSet.Put( aAnchor );
+}
+
+void SwHTMLParser::RegisterFlyFrame( SwFrameFormat *pFlyFormat )
+{
+ // automatically anchored frames must be moved forward by one position
+ if( RES_DRAWFRMFMT != pFlyFormat->Which() &&
+ (RndStdIds::FLY_AT_PARA == pFlyFormat->GetAnchor().GetAnchorId()) &&
+ css::text::WrapTextMode_THROUGH == pFlyFormat->GetSurround().GetSurround() )
+ {
+ m_aMoveFlyFrames.emplace_back(std::make_unique<SwHTMLFrameFormatListener>(pFlyFormat));
+ m_aMoveFlyCnts.push_back( m_pPam->GetPoint()->GetContentIndex() );
+ }
+}
+
+/* */
+
+void SwHTMLParser::GetDefaultScriptType( ScriptType& rType,
+ OUString& rTypeStr ) const
+{
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ SvKeyValueIterator* pHeaderAttrs = pDocSh ? pDocSh->GetHeaderAttributes()
+ : nullptr;
+ rType = GetScriptType( pHeaderAttrs );
+ rTypeStr = GetScriptTypeString( pHeaderAttrs );
+}
+
+namespace
+{
+ bool allowAccessLink(const SwDoc& rDoc)
+ {
+ OUString sReferer;
+ SfxObjectShell * sh = rDoc.GetPersist();
+ if (sh != nullptr && sh->HasName())
+ {
+ sReferer = sh->GetMedium()->GetName();
+ }
+ return !SvtSecurityOptions::isUntrustedReferer(sReferer);
+ }
+}
+
+/* */
+
+void SwHTMLParser::InsertImage()
+{
+ // and now analyze
+ OUString sAltNm, aId, aClass, aStyle, aMap, sHTMLGrfName;
+ OUString sGrfNm;
+ OUString aGraphicData;
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+ bool bWidthProvided=false, bHeightProvided=false;
+ tools::Long nWidth=0, nHeight=0;
+ tools::Long nVSpace=0, nHSpace=0;
+
+ sal_uInt16 nBorder = (m_xAttrTab->pINetFormat ? 1 : 0);
+ bool bIsMap = false;
+ bool bPercentWidth = false;
+ bool bPercentHeight = false;
+ OUString sWidthAsString, sHeightAsString;
+ SvxMacroItem aMacroItem(RES_FRMMACRO);
+
+ ScriptType eDfltScriptType;
+ OUString sDfltScriptType;
+ GetDefaultScriptType( eDfltScriptType, sDfltScriptType );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ ScriptType eScriptType2 = eDfltScriptType;
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::SRC:
+ sGrfNm = rOption.GetString();
+ if( !InternalImgToPrivateURL(sGrfNm) )
+ sGrfNm = INetURLObject::GetAbsURL( m_sBaseURL, sGrfNm );
+ break;
+ case HtmlOptionId::DATA:
+ aGraphicData = rOption.GetString();
+ if (!InternalImgToPrivateURL(aGraphicData))
+ aGraphicData = INetURLObject::GetAbsURL(
+ m_sBaseURL, SwHTMLParser::StripQueryFromPath(m_sBaseURL, aGraphicData));
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri =
+ rOption.GetEnum( aHTMLImgVAlignTable,
+ text::VertOrientation::TOP );
+ eHoriOri =
+ rOption.GetEnum( aHTMLImgHAlignTable );
+ break;
+ case HtmlOptionId::WIDTH:
+ // for now only store as pixel value!
+ nWidth = rOption.GetNumber();
+ sWidthAsString = rOption.GetString();
+ bPercentWidth = (sWidthAsString.indexOf('%') != -1);
+ if( bPercentWidth && nWidth>100 )
+ nWidth = 100;
+ // width|height = "auto" means viewing app decides the size
+ // i.e. proceed as if no particular size was provided
+ bWidthProvided = (sWidthAsString != "auto");
+ break;
+ case HtmlOptionId::HEIGHT:
+ // for now only store as pixel value!
+ nHeight = rOption.GetNumber();
+ sHeightAsString = rOption.GetString();
+ bPercentHeight = (sHeightAsString.indexOf('%') != -1);
+ if( bPercentHeight && nHeight>100 )
+ nHeight = 100;
+ // the same as above w/ HtmlOptionId::WIDTH
+ bHeightProvided = (sHeightAsString != "auto");
+ break;
+ case HtmlOptionId::VSPACE:
+ nVSpace = rOption.GetNumber();
+ break;
+ case HtmlOptionId::HSPACE:
+ nHSpace = rOption.GetNumber();
+ break;
+ case HtmlOptionId::ALT:
+ sAltNm = rOption.GetString();
+ break;
+ case HtmlOptionId::BORDER:
+ nBorder = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::ISMAP:
+ bIsMap = true;
+ break;
+ case HtmlOptionId::USEMAP:
+ aMap = rOption.GetString();
+ break;
+ case HtmlOptionId::NAME:
+ sHTMLGrfName = rOption.GetString();
+ break;
+
+ case HtmlOptionId::SDONLOAD:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONLOAD:
+ nEvent = SvMacroItemId::OnImageLoadDone;
+ goto IMAGE_SETEVENT;
+
+ case HtmlOptionId::SDONABORT:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONABORT:
+ nEvent = SvMacroItemId::OnImageLoadCancel;
+ goto IMAGE_SETEVENT;
+
+ case HtmlOptionId::SDONERROR:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONERROR:
+ nEvent = SvMacroItemId::OnImageLoadError;
+ goto IMAGE_SETEVENT;
+IMAGE_SETEVENT:
+ {
+ OUString sTmp( rOption.GetString() );
+ if( !sTmp.isEmpty() )
+ {
+ sTmp = convertLineEnd(sTmp, GetSystemLineEnd());
+ OUString sScriptType;
+ if( EXTENDED_STYPE == eScriptType2 )
+ sScriptType = sDfltScriptType;
+ aMacroItem.SetMacro( nEvent,
+ SvxMacro( sTmp, sScriptType, eScriptType2 ));
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ if (sGrfNm.isEmpty() && !aGraphicData.isEmpty())
+ sGrfNm = aGraphicData;
+
+ if( sGrfNm.isEmpty() )
+ return;
+
+ // When we are in an ordered list and the paragraph is still empty and not
+ // numbered, it may be a graphic for a bullet list.
+ if( !m_pPam->GetPoint()->GetContentIndex() &&
+ GetNumInfo().GetDepth() > 0 && GetNumInfo().GetDepth() <= MAXLEVEL &&
+ !m_aBulletGrfs[GetNumInfo().GetDepth()-1].isEmpty() &&
+ m_aBulletGrfs[GetNumInfo().GetDepth()-1]==sGrfNm )
+ {
+ SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+
+ if( pTextNode && ! pTextNode->IsCountedInList())
+ {
+ OSL_ENSURE( pTextNode->GetActualListLevel() == GetNumInfo().GetLevel(),
+ "Numbering level is wrong" );
+
+ pTextNode->SetCountedInList( true );
+
+ // It's necessary to invalidate the rule, because between the reading
+ // of LI and the graphic an EndAction could be called.
+ if( GetNumInfo().GetNumRule() )
+ GetNumInfo().GetNumRule()->SetInvalidRule( true );
+
+ // Set the style again, so that indent of the first line is correct.
+ SetTextCollAttrs();
+
+ return;
+ }
+ }
+
+ Graphic aGraphic;
+ INetURLObject aGraphicURL( sGrfNm );
+ if( aGraphicURL.GetProtocol() == INetProtocol::Data )
+ {
+ std::unique_ptr<SvMemoryStream> const pStream(aGraphicURL.getData());
+ if (pStream)
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ aGraphic = rFilter.ImportUnloadedGraphic(*pStream);
+ sGrfNm.clear();
+
+ if (!sGrfNm.isEmpty())
+ {
+ if (ERRCODE_NONE == rFilter.ImportGraphic(aGraphic, u"", *pStream))
+ sGrfNm.clear();
+ }
+ }
+ }
+ else if (m_sBaseURL.isEmpty() || !aGraphicData.isEmpty())
+ {
+ // sBaseURL is empty if the source is clipboard
+ // aGraphicData is non-empty for <object data="..."> -> not a linked graphic.
+ if (ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, aGraphicURL))
+ sGrfNm.clear();
+ }
+
+ if (!sGrfNm.isEmpty())
+ {
+ aGraphic.SetDefaultType();
+ }
+
+ if (!nHeight || !nWidth)
+ {
+ Size aPixelSize = aGraphic.GetSizePixel(Application::GetDefaultDevice());
+ if (!bWidthProvided)
+ nWidth = aPixelSize.Width();
+ if (!bHeightProvided)
+ nHeight = aPixelSize.Height();
+ // tdf#142781 - calculate the width/height keeping the aspect ratio
+ if (bWidthProvided && !bHeightProvided && aPixelSize.Width())
+ {
+ if (bPercentWidth)
+ {
+ nHeight = SwFormatFrameSize::SYNCED;
+ bPercentHeight = true;
+ }
+ else
+ {
+ nHeight = nWidth * aPixelSize.Height() / aPixelSize.Width();
+ }
+ }
+ else if (!bWidthProvided && bHeightProvided && aPixelSize.Height())
+ {
+ if (bPercentHeight)
+ {
+ nWidth = SwFormatFrameSize::SYNCED;
+ bPercentWidth = true;
+ }
+ else
+ {
+ nWidth = nHeight * aPixelSize.Width() / aPixelSize.Height();
+ }
+ }
+ }
+
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo );
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( aFrameSet );
+
+ // set the border
+ tools::Long nHBorderWidth = 0, nVBorderWidth = 0;
+ if( nBorder )
+ {
+ nHBorderWidth = static_cast<tools::Long>(nBorder);
+ nVBorderWidth = static_cast<tools::Long>(nBorder);
+ SvxCSS1Parser::PixelToTwip( nVBorderWidth, nHBorderWidth );
+
+ ::editeng::SvxBorderLine aHBorderLine( nullptr, nHBorderWidth );
+ ::editeng::SvxBorderLine aVBorderLine( nullptr, nVBorderWidth );
+
+ if( m_xAttrTab->pINetFormat )
+ {
+ const OUString& rURL =
+ static_cast<const SwFormatINetFormat&>(m_xAttrTab->pINetFormat->GetItem()).GetValue();
+
+ m_pCSS1Parser->SetATagStyles();
+ sal_uInt16 nPoolId = static_cast< sal_uInt16 >(m_xDoc->IsVisitedURL( rURL )
+ ? RES_POOLCHR_INET_VISIT
+ : RES_POOLCHR_INET_NORMAL);
+ const SwCharFormat *pCharFormat = m_pCSS1Parser->GetCharFormatFromPool( nPoolId );
+ aHBorderLine.SetColor( pCharFormat->GetColor().GetValue() );
+ aVBorderLine.SetColor( aHBorderLine.GetColor() );
+ }
+ else
+ {
+ const SvxColorItem& rColorItem = m_xAttrTab->pFontColor ?
+ static_cast<const SvxColorItem &>(m_xAttrTab->pFontColor->GetItem()) :
+ m_xDoc->GetDefault(RES_CHRATR_COLOR);
+ aHBorderLine.SetColor( rColorItem.GetValue() );
+ aVBorderLine.SetColor( aHBorderLine.GetColor() );
+ }
+
+ SvxBoxItem aBoxItem( RES_BOX );
+ aBoxItem.SetLine( &aHBorderLine, SvxBoxItemLine::TOP );
+ aBoxItem.SetLine( &aHBorderLine, SvxBoxItemLine::BOTTOM );
+ aBoxItem.SetLine( &aVBorderLine, SvxBoxItemLine::LEFT );
+ aBoxItem.SetLine( &aVBorderLine, SvxBoxItemLine::RIGHT );
+ aFrameSet.Put( aBoxItem );
+ }
+
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, aFrameSet );
+
+ SetSpace( Size( nHSpace, nVSpace), aItemSet, aPropInfo, aFrameSet );
+
+ // set other CSS1 attributes
+ SetFrameFormatAttrs( aItemSet, HtmlFrameFormatFlags::Box, aFrameSet );
+
+ Size aTwipSz( bPercentWidth ? 0 : nWidth, bPercentHeight ? 0 : nHeight );
+ if( aTwipSz.Width() || aTwipSz.Height() )
+ {
+ if (bWidthProvided || bHeightProvided || // attributes imply pixel!
+ aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+ {
+ aTwipSz = o3tl::convert(aTwipSz, o3tl::Length::px, o3tl::Length::twip);
+ }
+ else
+ { // some bitmaps may have a size in metric units (e.g. PNG); use that
+ assert(aGraphic.GetPrefMapMode().GetMapUnit() < MapUnit::MapPixel);
+ aTwipSz = o3tl::convert(aGraphic.GetPrefSize(),
+ MapToO3tlLength(aGraphic.GetPrefMapMode().GetMapUnit()),
+ o3tl::Length::twip);
+ }
+ }
+
+ // convert CSS1 size to "normal" size
+ switch( aPropInfo.m_eWidthType )
+ {
+ case SVX_CSS1_LTYPE_TWIP:
+ aTwipSz.setWidth( aPropInfo.m_nWidth );
+ nWidth = 1; // != 0
+ bPercentWidth = false;
+ break;
+ case SVX_CSS1_LTYPE_PERCENTAGE:
+ aTwipSz.setWidth( 0 );
+ nWidth = aPropInfo.m_nWidth;
+ bPercentWidth = true;
+ break;
+ default:
+ ;
+ }
+ switch( aPropInfo.m_eHeightType )
+ {
+ case SVX_CSS1_LTYPE_TWIP:
+ aTwipSz.setHeight( aPropInfo.m_nHeight );
+ nHeight = 1; // != 0
+ bPercentHeight = false;
+ break;
+ case SVX_CSS1_LTYPE_PERCENTAGE:
+ aTwipSz.setHeight( 0 );
+ nHeight = aPropInfo.m_nHeight;
+ bPercentHeight = true;
+ break;
+ default:
+ ;
+ }
+
+ Size aGrfSz( 0, 0 );
+ bool bSetTwipSize = true; // Set Twip-Size on Node?
+ bool bChangeFrameSize = false; // Change frame format later?
+ bool bRequestGrfNow = false;
+ bool bSetScaleImageMap = false;
+ sal_uInt8 nPercentWidth = 0, nPercentHeight = 0;
+
+ // bPercentWidth / bPercentHeight means we have a percent size. If that's not the case and we have no
+ // size from nWidth / nHeight either, then inspect the image header.
+ bool bRelWidthScale = bPercentWidth && nWidth == SwFormatFrameSize::SYNCED;
+ bool bNeedWidth = (!bPercentWidth && !nWidth) || bRelWidthScale;
+ bool bRelHeightScale = bPercentHeight && nHeight == SwFormatFrameSize::SYNCED;
+ bool bNeedHeight = (!bPercentHeight && !nHeight) || bRelHeightScale;
+ if ((bNeedWidth || bNeedHeight) && !bFuzzing && allowAccessLink(*m_xDoc))
+ {
+ GraphicDescriptor aDescriptor(aGraphicURL);
+ if (aDescriptor.Detect(/*bExtendedInfo=*/true))
+ {
+ // Try to use size info from the image header before defaulting to
+ // HTML_DFLT_IMG_WIDTH/HEIGHT.
+ aTwipSz
+ = o3tl::convert(aDescriptor.GetSizePixel(), o3tl::Length::px, o3tl::Length::twip);
+ if (!bPercentWidth && !nWidth)
+ {
+ nWidth = aTwipSz.getWidth();
+ }
+ if (!bPercentHeight && !nHeight)
+ {
+ nHeight = aTwipSz.getHeight();
+ }
+ }
+ }
+
+ if( !(nWidth && !bRelWidthScale) || !(nHeight && !bRelHeightScale) )
+ {
+ // When the graphic is in a table, it will be requested immediately,
+ // so that it is available before the table is layouted.
+ if (m_xTable && !nWidth)
+ {
+ bRequestGrfNow = true;
+ IncGrfsThatResizeTable();
+ }
+
+ // The frame size is set later
+ bChangeFrameSize = true;
+ aGrfSz = aTwipSz;
+ if( !nWidth && !nHeight )
+ {
+ aTwipSz.setWidth( HTML_DFLT_IMG_WIDTH );
+ aTwipSz.setHeight( HTML_DFLT_IMG_HEIGHT );
+ }
+ else if( nWidth )
+ {
+ // a percentage value
+ if( bPercentWidth )
+ {
+ nPercentWidth = static_cast<sal_uInt8>(nWidth);
+ nPercentHeight = 255;
+ }
+ else
+ {
+ aTwipSz.setHeight( HTML_DFLT_IMG_HEIGHT );
+ }
+ }
+ else if( nHeight )
+ {
+ if( bPercentHeight )
+ {
+ nPercentHeight = static_cast<sal_uInt8>(nHeight);
+ nPercentWidth = 255;
+ }
+ else
+ {
+ aTwipSz.setWidth( HTML_DFLT_IMG_WIDTH );
+ }
+ }
+ }
+ else
+ {
+ // Width and height were given and don't need to be set
+ bSetTwipSize = false;
+
+ if( bPercentWidth )
+ nPercentWidth = static_cast<sal_uInt8>(nWidth);
+
+ if( bPercentHeight )
+ nPercentHeight = static_cast<sal_uInt8>(nHeight);
+ }
+
+ // set image map
+ aMap = comphelper::string::stripEnd(aMap, ' ');
+ if( !aMap.isEmpty() )
+ {
+ // Since we only know local image maps we just use everything
+ // after # as name
+ sal_Int32 nPos = aMap.indexOf( '#' );
+ OUString aName;
+ if ( -1 == nPos )
+ aName = aMap ;
+ else
+ aName = aMap.copy(nPos+1);
+
+ ImageMap *pImgMap = FindImageMap( aName );
+ if( pImgMap )
+ {
+ SwFormatURL aURL; aURL.SetMap( pImgMap );// is copied
+
+ bSetScaleImageMap = !nPercentWidth || !nPercentHeight;
+ aFrameSet.Put( aURL );
+ }
+ else
+ {
+ ImageMap aEmptyImgMap( aName );
+ SwFormatURL aURL; aURL.SetMap( &aEmptyImgMap );// is copied
+ aFrameSet.Put( aURL );
+ m_nMissingImgMaps++; // image maps are missing
+
+ // the graphic has to scaled during SetTwipSize, if we didn't
+ // set a size on the node or the size doesn't match the graphic size.
+ bSetScaleImageMap = true;
+ }
+ }
+
+ // observe minimum values !!
+ bool bRelSizeScale = bRelWidthScale || bRelHeightScale;
+ if( nPercentWidth )
+ {
+ OSL_ENSURE( !aTwipSz.Width() || bRelSizeScale,
+ "Why is a width set if we already have percentage value?" );
+ aTwipSz.setWidth( aGrfSz.Width() ? aGrfSz.Width()
+ : HTML_DFLT_IMG_WIDTH );
+ }
+ else
+ {
+ aTwipSz.AdjustWidth(2*nVBorderWidth );
+ if( aTwipSz.Width() < MINFLY )
+ aTwipSz.setWidth( MINFLY );
+ }
+ if( nPercentHeight )
+ {
+ OSL_ENSURE( !aTwipSz.Height() || bRelSizeScale,
+ "Why is a height set if we already have percentage value?" );
+ aTwipSz.setHeight( aGrfSz.Height() ? aGrfSz.Height()
+ : HTML_DFLT_IMG_HEIGHT );
+ }
+ else
+ {
+ aTwipSz.AdjustHeight(2*nHBorderWidth );
+ if( aTwipSz.Height() < MINFLY )
+ aTwipSz.setHeight( MINFLY );
+ }
+
+ SwFormatFrameSize aFrameSize( SwFrameSize::Fixed, aTwipSz.Width(), aTwipSz.Height() );
+ aFrameSize.SetWidthPercent( nPercentWidth );
+ aFrameSize.SetHeightPercent( nPercentHeight );
+ aFrameSet.Put( aFrameSize );
+
+ const SwNodeType eNodeType = m_pPam->GetPointNode().GetNodeType();
+ if (eNodeType != SwNodeType::Text && eNodeType != SwNodeType::Table)
+ return;
+
+ // passing empty sGrfNm here, means we don't want the graphic to be linked
+ SwFrameFormat *const pFlyFormat =
+ m_xDoc->getIDocumentContentOperations().InsertGraphic(
+ *m_pPam, sGrfNm, OUString(), &aGraphic,
+ &aFrameSet, nullptr, nullptr);
+ SwGrfNode *pGrfNd = m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx()
+ ->GetIndex()+1 ]->GetGrfNode();
+
+ if( !sHTMLGrfName.isEmpty() )
+ {
+ pFlyFormat->SetFormatName( sHTMLGrfName );
+
+ // maybe jump to graphic
+ if( JumpToMarks::Graphic == m_eJumpTo && sHTMLGrfName == m_sJmpMark )
+ {
+ m_bChkJumpMark = true;
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+ }
+
+ if (pGrfNd)
+ {
+ if( !sAltNm.isEmpty() )
+ pGrfNd->SetTitle( sAltNm );
+
+ if( bSetTwipSize )
+ pGrfNd->SetTwipSize( aGrfSz );
+
+ pGrfNd->SetChgTwipSize( bChangeFrameSize );
+
+ if( bSetScaleImageMap )
+ pGrfNd->SetScaleImageMap( true );
+ }
+
+ if( m_xAttrTab->pINetFormat )
+ {
+ const SwFormatINetFormat &rINetFormat =
+ static_cast<const SwFormatINetFormat&>(m_xAttrTab->pINetFormat->GetItem());
+
+ SwFormatURL aURL( pFlyFormat->GetURL() );
+
+ aURL.SetURL( rINetFormat.GetValue(), bIsMap );
+ aURL.SetTargetFrameName( rINetFormat.GetTargetFrame() );
+ aURL.SetName( rINetFormat.GetName() );
+ pFlyFormat->SetFormatAttr( aURL );
+
+ {
+ static const SvMacroItemId aEvents[] = {
+ SvMacroItemId::OnMouseOver,
+ SvMacroItemId::OnClick,
+ SvMacroItemId::OnMouseOut };
+
+ for( SvMacroItemId id : aEvents )
+ {
+ const SvxMacro *pMacro = rINetFormat.GetMacro( id );
+ if( nullptr != pMacro )
+ aMacroItem.SetMacro( id, *pMacro );
+ }
+ }
+
+ if ((RndStdIds::FLY_AS_CHAR == pFlyFormat->GetAnchor().GetAnchorId()) &&
+ m_xAttrTab->pINetFormat->GetStartParagraph() ==
+ m_pPam->GetPoint()->GetNode() &&
+ m_xAttrTab->pINetFormat->GetStartContent() ==
+ m_pPam->GetPoint()->GetContentIndex() - 1 )
+ {
+ // the attribute was insert right before as-character anchored
+ // graphic, therefore we move it
+ m_xAttrTab->pINetFormat->SetStart( *m_pPam->GetPoint() );
+
+ // When the attribute is also an anchor, we'll insert
+ // a bookmark before the graphic, because SwFormatURL
+ // isn't an anchor.
+ if( !rINetFormat.GetName().isEmpty() )
+ {
+ m_pPam->Move( fnMoveBackward );
+ InsertBookmark( rINetFormat.GetName() );
+ m_pPam->Move( fnMoveForward );
+ }
+ }
+
+ }
+ else if (!m_aEmbedURL.isEmpty())
+ {
+ // This is an inner <object> image and the outer <object> has a URL for us. Set that on the
+ // image.
+ SwFormatURL aURL(pFlyFormat->GetURL());
+ aURL.SetURL(m_aEmbedURL, bIsMap);
+ m_aEmbedURL.clear();
+ pFlyFormat->SetFormatAttr(aURL);
+ }
+
+ if( !aMacroItem.GetMacroTable().empty() )
+ {
+ NotifyMacroEventRead();
+ pFlyFormat->SetFormatAttr( aMacroItem );
+ }
+
+ // tdf#87083 If the graphic has not been loaded yet, then load it now.
+ // Otherwise it may be loaded during the first paint of the object and it
+ // will be too late to adapt the size of the graphic at that point.
+ if (bRequestGrfNow && pGrfNd)
+ {
+ Size aUpdatedSize = pGrfNd->GetTwipSize(); //trigger a swap-in
+ SAL_WARN_IF(!aUpdatedSize.Width() || !aUpdatedSize.Height(), "sw.html", "html image with no width or height");
+ }
+
+ // maybe create frames and register auto bound frames
+ RegisterFlyFrame( pFlyFormat );
+
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+}
+
+/* */
+
+void SwHTMLParser::InsertBodyOptions()
+{
+ m_xDoc->SetTextFormatColl( *m_pPam,
+ m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+
+ OUString aBackGround, aId, aStyle, aLang, aDir;
+ Color aBGColor, aTextColor, aLinkColor, aVLinkColor;
+ bool bBGColor=false, bTextColor=false;
+ bool bLinkColor=false, bVLinkColor=false;
+
+ ScriptType eDfltScriptType;
+ OUString sDfltScriptType;
+ GetDefaultScriptType( eDfltScriptType, sDfltScriptType );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ ScriptType eScriptType2 = eDfltScriptType;
+ OUString aEvent;
+ bool bSetEvent = false;
+
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::BACKGROUND:
+ aBackGround = rOption.GetString();
+ break;
+ case HtmlOptionId::BGCOLOR:
+ rOption.GetColor( aBGColor );
+ bBGColor = true;
+ break;
+ case HtmlOptionId::TEXT:
+ rOption.GetColor( aTextColor );
+ bTextColor = true;
+ break;
+ case HtmlOptionId::LINK:
+ rOption.GetColor( aLinkColor );
+ bLinkColor = true;
+ break;
+ case HtmlOptionId::VLINK:
+ rOption.GetColor( aVLinkColor );
+ bVLinkColor = true;
+ break;
+
+ case HtmlOptionId::SDONLOAD:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONLOAD:
+ aEvent = GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC );
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONUNLOAD:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONUNLOAD:
+ aEvent = GlobalEventConfig::GetEventName( GlobalEventId::PREPARECLOSEDOC );
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONFOCUS:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONFOCUS:
+ aEvent = GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC );
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::SDONBLUR:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONBLUR:
+ aEvent = GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC );
+ bSetEvent = true;
+ break;
+
+ case HtmlOptionId::ONERROR:
+ break;
+
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ bTextColor = true;
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+
+ if( bSetEvent )
+ {
+ const OUString& rEvent = rOption.GetString();
+ if( !rEvent.isEmpty() )
+ InsertBasicDocEvent( aEvent, rEvent, eScriptType2,
+ sDfltScriptType );
+ }
+ }
+
+ if( bTextColor && !m_pCSS1Parser->IsBodyTextSet() )
+ {
+ // The font colour is set in the default style
+ m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD )
+ ->SetFormatAttr( SvxColorItem(aTextColor, RES_CHRATR_COLOR) );
+ m_pCSS1Parser->SetBodyTextSet();
+ }
+
+ // Prepare the items for the page style (background, frame)
+ // If BrushItem already set values must remain!
+ std::unique_ptr<SvxBrushItem> aBrushItem( m_pCSS1Parser->makePageDescBackground() );
+ bool bSetBrush = false;
+
+ if( bBGColor && !m_pCSS1Parser->IsBodyBGColorSet() )
+ {
+ // background colour from "BGCOLOR"
+ OUString aLink;
+ if( !aBrushItem->GetGraphicLink().isEmpty() )
+ aLink = aBrushItem->GetGraphicLink();
+ SvxGraphicPosition ePos = aBrushItem->GetGraphicPos();
+
+ aBrushItem->SetColor( aBGColor );
+
+ if( !aLink.isEmpty() )
+ {
+ aBrushItem->SetGraphicLink( aLink );
+ aBrushItem->SetGraphicPos( ePos );
+ }
+ bSetBrush = true;
+ m_pCSS1Parser->SetBodyBGColorSet();
+ }
+
+ if( !aBackGround.isEmpty() && !m_pCSS1Parser->IsBodyBackgroundSet() )
+ {
+ // background graphic from "BACKGROUND"
+ aBrushItem->SetGraphicLink( INetURLObject::GetAbsURL( m_sBaseURL, aBackGround ) );
+ aBrushItem->SetGraphicPos( GPOS_TILED );
+ bSetBrush = true;
+ m_pCSS1Parser->SetBodyBackgroundSet();
+ }
+
+ if( !aStyle.isEmpty() || !aDir.isEmpty() )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ OUString aDummy;
+ ParseStyleOptions( aStyle, aDummy, aDummy, aItemSet, aPropInfo, nullptr, &aDir );
+
+ // Some attributes have to set on the page style, in fact the ones
+ // which aren't inherited
+ m_pCSS1Parser->SetPageDescAttrs( bSetBrush ? aBrushItem.get() : nullptr,
+ &aItemSet );
+
+ static const TypedWhichId<SvxFontHeightItem> aWhichIds[3] = { RES_CHRATR_FONTSIZE,
+ RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CTL_FONTSIZE };
+ for(auto const & i : aWhichIds)
+ {
+ const SvxFontHeightItem *pItem = aItemSet.GetItemIfSet( i, false );
+ if( pItem && pItem->GetProp() != 100)
+ {
+ sal_uInt32 nHeight =
+ ( m_aFontHeights[2] * pItem->GetProp() ) / 100;
+ SvxFontHeightItem aNewItem( nHeight, 100, i );
+ aItemSet.Put( aNewItem );
+ }
+ }
+
+ // all remaining options can be set on the default style
+ m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD )
+ ->SetFormatAttr( aItemSet );
+ }
+ else if( bSetBrush )
+ {
+ m_pCSS1Parser->SetPageDescAttrs( aBrushItem.get() );
+ }
+
+ if( bLinkColor && !m_pCSS1Parser->IsBodyLinkSet() )
+ {
+ SwCharFormat *pCharFormat =
+ m_pCSS1Parser->GetCharFormatFromPool(RES_POOLCHR_INET_NORMAL);
+ pCharFormat->SetFormatAttr( SvxColorItem(aLinkColor, RES_CHRATR_COLOR) );
+ m_pCSS1Parser->SetBodyLinkSet();
+ }
+ if( bVLinkColor && !m_pCSS1Parser->IsBodyVLinkSet() )
+ {
+ SwCharFormat *pCharFormat =
+ m_pCSS1Parser->GetCharFormatFromPool(RES_POOLCHR_INET_VISIT);
+ pCharFormat->SetFormatAttr( SvxColorItem(aVLinkColor, RES_CHRATR_COLOR) );
+ m_pCSS1Parser->SetBodyVLinkSet();
+ }
+ if( !aLang.isEmpty() )
+ {
+ LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( aLang );
+ if( LANGUAGE_DONTKNOW != eLang )
+ {
+ TypedWhichId<SvxLanguageItem> nWhich(0);
+ switch( SvtLanguageOptions::GetScriptTypeOfLanguage( eLang ) )
+ {
+ case SvtScriptType::LATIN:
+ nWhich = RES_CHRATR_LANGUAGE;
+ break;
+ case SvtScriptType::ASIAN:
+ nWhich = RES_CHRATR_CJK_LANGUAGE;
+ break;
+ case SvtScriptType::COMPLEX:
+ nWhich = RES_CHRATR_CTL_LANGUAGE;
+ break;
+ default: break;
+ }
+ if( nWhich )
+ {
+ SvxLanguageItem aLanguage( eLang, nWhich );
+ aLanguage.SetWhich( nWhich );
+ m_xDoc->SetDefault( aLanguage );
+ }
+ }
+ }
+
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+}
+
+/* */
+
+void SwHTMLParser::NewAnchor()
+{
+ // end previous link if there was one
+ std::unique_ptr<HTMLAttrContext> xOldCntxt(PopContext(HtmlTokenId::ANCHOR_ON));
+ if (xOldCntxt)
+ {
+ // and maybe end attributes
+ EndContext(xOldCntxt.get());
+ }
+
+ SvxMacroTableDtor aMacroTable;
+ OUString sHRef, aName, sTarget;
+ OUString aId, aStyle, aClass, aLang, aDir;
+ bool bHasHRef = false, bFixed = false;
+
+ ScriptType eDfltScriptType;
+ OUString sDfltScriptType;
+ GetDefaultScriptType( eDfltScriptType, sDfltScriptType );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ ScriptType eScriptType2 = eDfltScriptType;
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ break;
+
+ case HtmlOptionId::HREF:
+ sHRef = rOption.GetString();
+ bHasHRef = true;
+ break;
+ case HtmlOptionId::TARGET:
+ sTarget = rOption.GetString();
+ break;
+
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::SDFIXED:
+ bFixed = true;
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+
+ case HtmlOptionId::SDONCLICK:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONCLICK:
+ nEvent = SvMacroItemId::OnClick;
+ goto ANCHOR_SETEVENT;
+
+ case HtmlOptionId::SDONMOUSEOVER:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONMOUSEOVER:
+ nEvent = SvMacroItemId::OnMouseOver;
+ goto ANCHOR_SETEVENT;
+
+ case HtmlOptionId::SDONMOUSEOUT:
+ eScriptType2 = STARBASIC;
+ [[fallthrough]];
+ case HtmlOptionId::ONMOUSEOUT:
+ nEvent = SvMacroItemId::OnMouseOut;
+ goto ANCHOR_SETEVENT;
+ANCHOR_SETEVENT:
+ {
+ OUString sTmp( rOption.GetString() );
+ if( !sTmp.isEmpty() )
+ {
+ sTmp = convertLineEnd(sTmp, GetSystemLineEnd());
+ OUString sScriptType;
+ if( EXTENDED_STYPE == eScriptType2 )
+ sScriptType = sDfltScriptType;
+ aMacroTable.Insert( nEvent, SvxMacro( sTmp, sScriptType, eScriptType2 ));
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ // Jump targets, which match our implicit targets,
+ // here we throw out rigorously.
+ if( !aName.isEmpty() )
+ {
+ OUString sDecoded( INetURLObject::decode( aName,
+ INetURLObject::DecodeMechanism::Unambiguous ));
+ sal_Int32 nPos = sDecoded.lastIndexOf( cMarkSeparator );
+ if( nPos != -1 )
+ {
+ OUString sCmp= sDecoded.copy(nPos+1).replaceAll(" ","");
+ if( !sCmp.isEmpty() )
+ {
+ sCmp = sCmp.toAsciiLowerCase();
+ if( sCmp == "region" ||
+ sCmp == "frame" ||
+ sCmp == "graphic" ||
+ sCmp == "ole" ||
+ sCmp == "table" ||
+ sCmp == "outline" ||
+ sCmp == "text" )
+ {
+ aName.clear();
+ }
+ }
+ }
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::ANCHOR_ON));
+
+ bool bEnAnchor = false, bFootnoteAnchor = false, bFootnoteEnSymbol = false;
+ OUString aFootnoteName;
+ OUString aStrippedClass( aClass );
+ SwCSS1Parser::GetScriptFromClass( aStrippedClass, false );
+ if( aStrippedClass.getLength() >=9 && bHasHRef && sHRef.getLength() > 1 &&
+ ('s' == aStrippedClass[0] || 'S' == aStrippedClass[0]) &&
+ ('d' == aStrippedClass[1] || 'D' == aStrippedClass[1]) )
+ {
+ if( aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote_anc ) )
+ bEnAnchor = true;
+ else if( aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote_anc ) )
+ bFootnoteAnchor = true;
+ else if( aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote_sym ) ||
+ aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote_sym ) )
+ bFootnoteEnSymbol = true;
+ if( bEnAnchor || bFootnoteAnchor || bFootnoteEnSymbol )
+ {
+ aFootnoteName = sHRef.copy( 1 );
+ aClass.clear();
+ aStrippedClass.clear();
+ aName.clear();
+ bHasHRef = false;
+ }
+ }
+
+ // Styles parsen
+ if( HasStyleOptions( aStyle, aId, aStrippedClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ {
+ DoPositioning(aItemSet, aPropInfo, xCntxt.get());
+ InsertAttrs(aItemSet, aPropInfo, xCntxt.get(), true);
+ }
+ }
+
+ if( bHasHRef )
+ {
+ if( !sHRef.isEmpty() )
+ {
+ sHRef = URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), sHRef, Link<OUString *, bool>(), false );
+ }
+ else
+ {
+ // use directory if empty URL
+ INetURLObject aURLObj( m_aPathToFile );
+ sHRef = aURLObj.GetPartBeforeLastName();
+ }
+
+ m_pCSS1Parser->SetATagStyles();
+ SwFormatINetFormat aINetFormat( sHRef, sTarget );
+ aINetFormat.SetName( aName );
+
+ if( !aMacroTable.empty() )
+ {
+ NotifyMacroEventRead();
+ aINetFormat.SetMacroTable( &aMacroTable );
+ }
+
+ // set the default attribute
+ InsertAttr(&m_xAttrTab->pINetFormat, aINetFormat, xCntxt.get());
+ }
+ else if( !aName.isEmpty() )
+ {
+ InsertBookmark( aName );
+ }
+
+ if( bEnAnchor || bFootnoteAnchor )
+ {
+ InsertFootEndNote( aFootnoteName, bEnAnchor, bFixed );
+ m_bInFootEndNoteAnchor = m_bCallNextToken = true;
+ }
+ else if( bFootnoteEnSymbol )
+ {
+ m_bInFootEndNoteSymbol = m_bCallNextToken = true;
+ }
+
+ // save context
+ PushContext(xCntxt);
+}
+
+void SwHTMLParser::EndAnchor()
+{
+ if( m_bInFootEndNoteAnchor )
+ {
+ FinishFootEndNote();
+ m_bInFootEndNoteAnchor = false;
+ }
+ else if( m_bInFootEndNoteSymbol )
+ {
+ m_bInFootEndNoteSymbol = false;
+ }
+
+ EndTag( HtmlTokenId::ANCHOR_OFF );
+}
+
+/* */
+
+void SwHTMLParser::InsertBookmark( const OUString& rName )
+{
+ HTMLAttr* pTmp = new HTMLAttr( *m_pPam->GetPoint(),
+ SfxStringItem(RES_FLTR_BOOKMARK, rName), nullptr, std::shared_ptr<HTMLAttrTable>());
+ m_aSetAttrTab.push_back( pTmp );
+}
+
+bool SwHTMLParser::HasCurrentParaBookmarks( bool bIgnoreStack ) const
+{
+ bool bHasMarks = false;
+ SwNodeOffset nNodeIdx = m_pPam->GetPoint()->GetNodeIndex();
+
+ // first step: are there still bookmark in the attribute-stack?
+ // bookmarks are added to the end of the stack - thus we only have
+ // to check the last bookmark
+ if( !bIgnoreStack )
+ {
+ for( auto i = m_aSetAttrTab.size(); i; )
+ {
+ HTMLAttr* pAttr = m_aSetAttrTab[ --i ];
+ if( RES_FLTR_BOOKMARK == pAttr->m_pItem->Which() )
+ {
+ if( pAttr->GetStartParagraphIdx() == nNodeIdx )
+ bHasMarks = true;
+ break;
+ }
+ }
+ }
+
+ if( !bHasMarks )
+ {
+ // second step: when we didn't find a bookmark, check if there is one set already
+ IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess();
+ for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin();
+ ppMark != pMarkAccess->getAllMarksEnd();
+ ++ppMark)
+ {
+ const ::sw::mark::IMark* pBookmark = *ppMark;
+
+ const SwNodeOffset nBookNdIdx = pBookmark->GetMarkPos().GetNodeIndex();
+ if( nBookNdIdx==nNodeIdx )
+ {
+ bHasMarks = true;
+ break;
+ }
+ else if( nBookNdIdx > nNodeIdx )
+ break;
+ }
+ }
+
+ return bHasMarks;
+}
+
+/* */
+
+void SwHTMLParser::StripTrailingPara()
+{
+ bool bSetSmallFont = false;
+
+ SwContentNode* pCNd = m_pPam->GetPointContentNode();
+ SwNodeOffset nNodeIdx = m_pPam->GetPoint()->GetNodeIndex();
+ if( !m_pPam->GetPoint()->GetContentIndex() )
+ {
+ if( pCNd && pCNd->StartOfSectionIndex() + 2 <
+ pCNd->EndOfSectionIndex() && CanRemoveNode(nNodeIdx))
+ {
+
+ for(sw::SpzFrameFormat* pSpz: *m_xDoc->GetSpzFrameFormats())
+ {
+ SwFormatAnchor const*const pAnchor = &pSpz->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ pAnchorNode->GetIndex() == nNodeIdx )
+
+ return; // we can't delete the node
+ }
+
+ SetAttr( false ); // the still open attributes must be
+ // closed before the node is deleted,
+ // otherwise the last index is dangling
+
+ if( pCNd->Len() && pCNd->IsTextNode() )
+ {
+ // fields were inserted into the node, now they have
+ // to be moved
+ SwTextNode *pPrvNd = m_xDoc->GetNodes()[nNodeIdx-1]->GetTextNode();
+ if( pPrvNd )
+ {
+ SwContentIndex aSrc( pCNd, 0 );
+ pCNd->GetTextNode()->CutText( pPrvNd, aSrc, pCNd->Len() );
+ }
+ }
+
+ // now we have to move maybe existing bookmarks
+ IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess();
+ for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin();
+ ppMark != pMarkAccess->getAllMarksEnd();
+ ++ppMark)
+ {
+ ::sw::mark::IMark* pMark = *ppMark;
+
+ SwNodeOffset nBookNdIdx = pMark->GetMarkPos().GetNodeIndex();
+ if(nBookNdIdx==nNodeIdx)
+ {
+ SwNodeIndex nNewNdIdx(m_pPam->GetPoint()->GetNode());
+ SwContentNode* pNd = SwNodes::GoPrevious(&nNewNdIdx);
+ if(!pNd)
+ {
+ OSL_ENSURE(false, "Oops, where is my predecessor node?");
+ return;
+ }
+ // #i81002# - refactoring
+ // Do not directly manipulate member of <SwBookmark>
+ {
+ const SwPaM aPaM(*pNd, pNd->Len());
+ pMarkAccess->repositionMark(*ppMark, aPaM);
+ }
+ }
+ else if( nBookNdIdx > nNodeIdx )
+ break;
+ }
+
+ SwNode& rDelNode = m_pPam->GetPoint()->GetNode();
+ m_pPam->Move( fnMoveBackward, GoInNode );
+ m_pPam->SetMark();
+ m_pPam->DeleteMark();
+ m_xDoc->GetNodes().Delete( rDelNode );
+ }
+ else if (pCNd && pCNd->IsTextNode() && m_xTable)
+ {
+ // In empty cells we set a small font, so that the cell doesn't
+ // get higher than the graphic resp. as low as possible.
+ bSetSmallFont = true;
+ }
+ }
+ else if( pCNd && pCNd->IsTextNode() && m_xTable &&
+ pCNd->StartOfSectionIndex()+2 ==
+ pCNd->EndOfSectionIndex() )
+ {
+ // When the cell contains only as-character anchored graphics/frames,
+ // then we also set a small font.
+ bSetSmallFont = true;
+ SwTextNode* pTextNd = pCNd->GetTextNode();
+
+ sal_Int32 nPos = m_pPam->GetPoint()->GetContentIndex();
+ while( bSetSmallFont && nPos>0 )
+ {
+ --nPos;
+ bSetSmallFont =
+ (CH_TXTATR_BREAKWORD == pTextNd->GetText()[nPos]) &&
+ (nullptr != pTextNd->GetTextAttrForCharAt( nPos, RES_TXTATR_FLYCNT ));
+ }
+ }
+
+ if( bSetSmallFont )
+ {
+ // Added default to CJK and CTL
+ SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
+ pCNd->SetAttr( aFontHeight );
+ SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
+ pCNd->SetAttr( aFontHeightCJK );
+ SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
+ pCNd->SetAttr( aFontHeightCTL );
+ }
+}
+
+void SwHTMLParser::NotifyMacroEventRead()
+{
+ if (m_bNotifyMacroEventRead)
+ return;
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ if (!pDocSh)
+ return;
+ uno::Reference<frame::XModel> const xModel(pDocSh->GetBaseModel());
+ comphelper::DocumentInfo::notifyMacroEventRead(xModel);
+ m_bNotifyMacroEventRead = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlnum.cxx b/sw/source/filter/html/htmlnum.cxx
new file mode 100644
index 0000000000..8fa120a630
--- /dev/null
+++ b/sw/source/filter/html/htmlnum.cxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "htmlnum.hxx"
+#include <ndtxt.hxx>
+#include <doc.hxx>
+
+void SwHTMLNumRuleInfo::Set(const SwTextNode& rTextNd)
+{
+ const SwNumRule* pTextNdNumRule(rTextNd.GetNumRule());
+ if (pTextNdNumRule && pTextNdNumRule != rTextNd.GetDoc().GetOutlineNumRule())
+ {
+ m_pNumRule = const_cast<SwNumRule*>(pTextNdNumRule);
+ m_nDeep = o3tl::narrowing<sal_uInt16>(m_pNumRule ? rTextNd.GetActualListLevel() + 1 : 0);
+ m_bNumbered = rTextNd.IsCountedInList();
+ // #i57919# - correction of refactoring done by cws swnumtree:
+ // <bRestart> has to be set to <true>, if numbering is restarted at this
+ // text node and the start value equals <USHRT_MAX>.
+ // Start value <USHRT_MAX> indicates, that at this text node the numbering
+ // is restarted with the value given at the corresponding level.
+ m_bRestart = rTextNd.IsListRestart() && !rTextNd.HasAttrListRestartValue();
+ }
+ else
+ {
+ m_pNumRule = nullptr;
+ m_nDeep = 0;
+ m_bNumbered = m_bRestart = false;
+ }
+}
+
+// Restart flag is only effective when this level is not below the previous
+bool SwHTMLNumRuleInfo::IsRestart(const SwHTMLNumRuleInfo& rPrev) const
+{
+ // calling this, when the rules are different, makes no sense
+ assert(rPrev.GetNumRule() == GetNumRule());
+
+ // An example ODF when the restart flag is set, but has no effect:
+ // <text:list text:style-name="L1">
+ // <text:list-item>
+ // <text:p>l1</text:p>
+ // <text:list>
+ // <text:list-item>
+ // <text:p>l2</text:p>
+ // </text:list-item>
+ // <text:list-item>
+ // <text:p>l2</text:p>
+ // </text:list-item>
+ // </text:list>
+ // <text:list>
+ // <text:list-item>
+ // <text:list>
+ // <text:list-item>
+ // <text:p>l3</text:p>
+ // </text:list-item>
+ // </text:list>
+ // </text:list-item>
+ // </text:list>
+ // </text:list-item>
+ // </text:list>
+ // In this case, "l3" is in a separate sublist than "l2", and so the "l3" node gets the
+ // "list restart" property. But the document rendering would be
+ // 1. l1
+ // 1.1. l2
+ // 1.2. l2
+ // 1.2.1. l3
+ // and the second-level numbering will not actually restart at the "l3" node.
+ //
+ // TODO/LATER: note that restarting may happen at different levels. In the code using this
+ // function, the level is reset to 0 whenever a restart is detected. And also, there is no
+ // code to actually descend to that new level (close corresponding li/ul/ol elements).
+
+ if (rPrev.GetDepth() < GetDepth())
+ return false; // No matter if the restart flag is set, it is not effective for subitems
+ return m_bRestart;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlnum.hxx b/sw/source/filter/html/htmlnum.hxx
new file mode 100644
index 0000000000..670f08229d
--- /dev/null
+++ b/sw/source/filter/html/htmlnum.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLNUM_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLNUM_HXX
+
+#include <swtypes.hxx>
+#include <string.h>
+
+#define HTML_NUMBER_BULLET_MARGINLEFT (o3tl::toTwips(125, o3tl::Length::mm10))
+#define HTML_NUMBER_BULLET_INDENT (-o3tl::toTwips(5, o3tl::Length::mm))
+
+class SwTextNode;
+class SwNumRule;
+
+// TODO: Unicode: Are these characters the correct ones?
+#define HTML_BULLETCHAR_DISC (0xe008)
+#define HTML_BULLETCHAR_CIRCLE (0xe009)
+#define HTML_BULLETCHAR_SQUARE (0xe00b)
+
+class SwHTMLNumRuleInfo
+{
+ sal_uInt16 m_aNumStarts[MAXLEVEL];
+ SwNumRule * m_pNumRule; // current numbering
+ sal_uInt16 m_nDeep; // current numbering depth (1, 2, 3, ...)
+ bool m_bRestart; // Export: restart numbering
+ bool m_bNumbered; // Export: paragraph is numbered
+
+public:
+
+ inline void Set( const SwHTMLNumRuleInfo& rInf );
+ void Set( const SwTextNode& rTextNd );
+
+ SwHTMLNumRuleInfo() :
+ m_pNumRule( nullptr ), m_nDeep( 0 ),
+ m_bRestart( false ), m_bNumbered( false )
+ {
+ memset( &m_aNumStarts, 0xff, sizeof( m_aNumStarts ) );
+ }
+
+ SwHTMLNumRuleInfo( const SwHTMLNumRuleInfo& rInf ) :
+ m_pNumRule( rInf.m_pNumRule ), m_nDeep( rInf.m_nDeep ),
+ m_bRestart( rInf.m_bRestart ), m_bNumbered( rInf.m_bNumbered )
+ {
+ memcpy( &m_aNumStarts, &rInf.m_aNumStarts, sizeof( m_aNumStarts ) );
+ }
+
+ explicit SwHTMLNumRuleInfo( const SwTextNode& rTextNd ) { Set( rTextNd ); }
+ inline SwHTMLNumRuleInfo& operator=( const SwHTMLNumRuleInfo& rInf );
+
+ inline void Clear();
+
+ void SetNumRule( const SwNumRule *pRule ) { m_pNumRule = const_cast<SwNumRule *>(pRule); }
+ SwNumRule *GetNumRule() { return m_pNumRule; }
+ const SwNumRule *GetNumRule() const { return m_pNumRule; }
+
+ void SetDepth( sal_uInt16 nDepth ) { m_nDeep = nDepth; }
+ sal_uInt16 GetDepth() const { return m_nDeep; }
+ void IncDepth() { ++m_nDeep; }
+ void DecDepth() { if (m_nDeep!=0) --m_nDeep; }
+ inline sal_uInt8 GetLevel() const;
+
+ bool IsRestart(const SwHTMLNumRuleInfo& rPrev) const;
+
+ bool IsNumbered() const { return m_bNumbered; }
+
+ inline void SetNodeStartValue( sal_uInt8 nLvl, sal_uInt16 nVal=USHRT_MAX );
+ sal_uInt16 GetNodeStartValue( sal_uInt8 nLvl ) const { return m_aNumStarts[nLvl]; }
+};
+
+inline SwHTMLNumRuleInfo& SwHTMLNumRuleInfo::operator=(
+ const SwHTMLNumRuleInfo& rInf )
+{
+ Set( rInf );
+ return *this;
+}
+
+inline void SwHTMLNumRuleInfo::Set( const SwHTMLNumRuleInfo& rInf )
+{
+ m_pNumRule = rInf.m_pNumRule;
+ m_nDeep = rInf.m_nDeep;
+ m_bRestart = rInf.m_bRestart;
+ m_bNumbered = rInf.m_bNumbered;
+ memcpy( &m_aNumStarts, &rInf.m_aNumStarts, sizeof( m_aNumStarts ) );
+}
+
+inline void SwHTMLNumRuleInfo::Clear()
+{
+ m_pNumRule = nullptr;
+ m_nDeep = 0;
+ m_bRestart = m_bNumbered = false;
+ memset( &m_aNumStarts, 0xff, sizeof( m_aNumStarts ) );
+}
+
+inline sal_uInt8 SwHTMLNumRuleInfo::GetLevel() const
+{
+ return
+ static_cast<sal_uInt8>( m_pNumRule!=nullptr && m_nDeep != 0
+ ? ( m_nDeep<=MAXLEVEL ? m_nDeep-1 : MAXLEVEL - 1 )
+ : 0 );
+}
+
+inline void SwHTMLNumRuleInfo::SetNodeStartValue( sal_uInt8 nLvl, sal_uInt16 nVal )
+{
+ m_aNumStarts[nLvl] = nVal;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlnumreader.cxx b/sw/source/filter/html/htmlnumreader.cxx
new file mode 100644
index 0000000000..c21a45e877
--- /dev/null
+++ b/sw/source/filter/html/htmlnumreader.cxx
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <hintids.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <svl/urihelper.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <numrule.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <poolfmt.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+
+#include "htmlnum.hxx"
+#include "swcss1.hxx"
+#include "swhtml.hxx"
+
+using namespace css;
+
+// <UL TYPE=...>
+HTMLOptionEnum<sal_UCS4> const aHTMLULTypeTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_ULTYPE_disc, HTML_BULLETCHAR_DISC },
+ { OOO_STRING_SVTOOLS_HTML_ULTYPE_circle, HTML_BULLETCHAR_CIRCLE },
+ { OOO_STRING_SVTOOLS_HTML_ULTYPE_square, HTML_BULLETCHAR_SQUARE },
+ { nullptr, 0 }
+};
+
+
+void SwHTMLParser::NewNumberBulletList( HtmlTokenId nToken )
+{
+ SwHTMLNumRuleInfo& rInfo = GetNumInfo();
+
+ // Create a new paragraph
+ bool bSpace = (rInfo.GetDepth() + m_nDefListDeep) == 0;
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( bSpace ? AM_SPACE : AM_NOSPACE, false );
+ else if( bSpace )
+ AddParSpace();
+
+ // Increment the numbering depth
+ rInfo.IncDepth();
+ sal_uInt8 nLevel = static_cast<sal_uInt8>( (rInfo.GetDepth() <= MAXLEVEL ? rInfo.GetDepth()
+ : MAXLEVEL) - 1 );
+
+ // Create rules if needed
+ if( !rInfo.GetNumRule() )
+ {
+ sal_uInt16 nPos = m_xDoc->MakeNumRule( m_xDoc->GetUniqueNumRuleName() );
+ rInfo.SetNumRule( m_xDoc->GetNumRuleTable()[nPos] );
+ }
+
+ // Change the format for this level if that hasn't happened yet for this level
+ bool bNewNumFormat = rInfo.GetNumRule()->GetNumFormat( nLevel ) == nullptr;
+ bool bChangeNumFormat = false;
+
+ // Create the default numbering format
+ SwNumFormat aNumFormat( rInfo.GetNumRule()->Get(nLevel) );
+ rInfo.SetNodeStartValue( nLevel );
+ if( bNewNumFormat )
+ {
+ sal_uInt16 nChrFormatPoolId = 0;
+ if( HtmlTokenId::ORDERLIST_ON == nToken )
+ {
+ aNumFormat.SetNumberingType(SVX_NUM_ARABIC);
+ nChrFormatPoolId = RES_POOLCHR_NUM_LEVEL;
+ }
+ else
+ {
+ // We'll set a default style because the UI does the same. This meant a 9pt font, which
+ // was not the case in Netscape. That didn't bother anyone so far
+ // #i63395# - Only apply user defined default bullet font
+ if ( numfunc::IsDefBulletFontUserDefined() )
+ {
+ aNumFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ }
+ aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aNumFormat.SetBulletChar( cBulletChar );
+ nChrFormatPoolId = RES_POOLCHR_BULLET_LEVEL;
+ }
+
+ sal_Int32 nAbsLSpace = HTML_NUMBER_BULLET_MARGINLEFT;
+
+ sal_Int32 nFirstLineIndent = HTML_NUMBER_BULLET_INDENT;
+ if( nLevel > 0 )
+ {
+ const SwNumFormat& rPrevNumFormat = rInfo.GetNumRule()->Get( nLevel-1 );
+ nAbsLSpace = nAbsLSpace + rPrevNumFormat.GetAbsLSpace();
+ nFirstLineIndent = rPrevNumFormat.GetFirstLineOffset();
+ }
+ aNumFormat.SetAbsLSpace( nAbsLSpace );
+ aNumFormat.SetFirstLineOffset( nFirstLineIndent );
+ aNumFormat.SetCharFormat( m_pCSS1Parser->GetCharFormatFromPool(nChrFormatPoolId) );
+
+ bChangeNumFormat = true;
+ }
+ else if( 1 != aNumFormat.GetStart() )
+ {
+ // If the layer has already been used, the start value may need to be set hard to the paragraph.
+ rInfo.SetNodeStartValue( nLevel, 1 );
+ }
+
+ // and set that in the options
+ OUString aId, aStyle, aClass, aLang, aDir;
+ OUString aBulletSrc;
+ sal_Int16 eVertOri = text::VertOrientation::NONE;
+ sal_uInt16 nWidth=USHRT_MAX, nHeight=USHRT_MAX;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::TYPE:
+ if( bNewNumFormat && !rOption.GetString().isEmpty() )
+ {
+ switch( nToken )
+ {
+ case HtmlTokenId::ORDERLIST_ON:
+ bChangeNumFormat = true;
+ switch( rOption.GetString()[0] )
+ {
+ case 'A': aNumFormat.SetNumberingType(SVX_NUM_CHARS_UPPER_LETTER); break;
+ case 'a': aNumFormat.SetNumberingType(SVX_NUM_CHARS_LOWER_LETTER); break;
+ case 'I': aNumFormat.SetNumberingType(SVX_NUM_ROMAN_UPPER); break;
+ case 'i': aNumFormat.SetNumberingType(SVX_NUM_ROMAN_LOWER); break;
+ default: bChangeNumFormat = false;
+ }
+ break;
+
+ case HtmlTokenId::UNORDERLIST_ON:
+ aNumFormat.SetBulletChar( rOption.GetEnum(
+ aHTMLULTypeTable,aNumFormat.GetBulletChar() ) );
+ bChangeNumFormat = true;
+ break;
+ default: break;
+ }
+ }
+ break;
+ case HtmlOptionId::START:
+ {
+ sal_uInt16 nStart = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if( bNewNumFormat )
+ {
+ aNumFormat.SetStart( nStart );
+ bChangeNumFormat = true;
+ }
+ else
+ {
+ rInfo.SetNodeStartValue( nLevel, nStart );
+ }
+ }
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ case HtmlOptionId::SRC:
+ if( bNewNumFormat )
+ {
+ aBulletSrc = rOption.GetString();
+ if( !InternalImgToPrivateURL(aBulletSrc) )
+ aBulletSrc = URIHelper::SmartRel2Abs( INetURLObject( m_sBaseURL ), aBulletSrc, Link<OUString *, bool>(), false );
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::HEIGHT:
+ nHeight = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri );
+ break;
+ default: break;
+ }
+ }
+
+ if( !aBulletSrc.isEmpty() )
+ {
+ // A bullet list with graphics
+ aNumFormat.SetNumberingType(SVX_NUM_BITMAP);
+
+ // Create the graphic as a brush
+ SvxBrushItem aBrushItem( RES_BACKGROUND );
+ aBrushItem.SetGraphicLink( aBulletSrc );
+ aBrushItem.SetGraphicPos( GPOS_AREA );
+
+ // Only set size if given a width and a height
+ Size aTwipSz( nWidth, nHeight), *pTwipSz=nullptr;
+ if( nWidth!=USHRT_MAX && nHeight!=USHRT_MAX )
+ {
+ aTwipSz = o3tl::convert(aTwipSz, o3tl::Length::px, o3tl::Length::twip);
+ pTwipSz = &aTwipSz;
+ }
+
+ // Only set orientation if given one
+ aNumFormat.SetGraphicBrush( &aBrushItem, pTwipSz,
+ text::VertOrientation::NONE!=eVertOri ? &eVertOri : nullptr);
+
+ // Remember the graphic to not put it into the paragraph
+ m_aBulletGrfs[nLevel] = aBulletSrc;
+ bChangeNumFormat = true;
+ }
+ else
+ m_aBulletGrfs[nLevel].clear();
+
+ // don't number the current paragraph (for now)
+ {
+ sal_uInt8 nLvl = nLevel;
+ SetNodeNum( nLvl );
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ // Parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ {
+ if( bNewNumFormat )
+ {
+ if( aPropInfo.m_bLeftMargin )
+ {
+ // Default indent has already been added
+ tools::Long nAbsLSpace =
+ aNumFormat.GetAbsLSpace() - HTML_NUMBER_BULLET_MARGINLEFT;
+ if( aPropInfo.m_nLeftMargin < 0 &&
+ nAbsLSpace < -aPropInfo.m_nLeftMargin )
+ nAbsLSpace = 0U;
+ else if( aPropInfo.m_nLeftMargin > SHRT_MAX ||
+ nAbsLSpace + aPropInfo.m_nLeftMargin > SHRT_MAX )
+ nAbsLSpace = SHRT_MAX;
+ else
+ nAbsLSpace = nAbsLSpace + aPropInfo.m_nLeftMargin;
+
+ aNumFormat.SetAbsLSpace( nAbsLSpace );
+ bChangeNumFormat = true;
+ }
+ if( aPropInfo.m_bTextIndent )
+ {
+ short nTextIndent =
+ aItemSet.Get(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset();
+ aNumFormat.SetFirstLineOffset( nTextIndent );
+ bChangeNumFormat = true;
+ }
+ if( aPropInfo.m_bNumbering )
+ {
+ aNumFormat.SetNumberingType(aPropInfo.m_nNumberingType);
+ bChangeNumFormat = true;
+ }
+ if( aPropInfo.m_bBullet )
+ {
+ aNumFormat.SetBulletChar( aPropInfo.m_cBulletChar );
+ bChangeNumFormat = true;
+ }
+ }
+ aPropInfo.m_bLeftMargin = aPropInfo.m_bTextIndent = false;
+ if( !aPropInfo.m_bRightMargin )
+ aItemSet.ClearItem(RES_MARGIN_RIGHT); // superfluous?
+
+ // #i89812# - Perform change to list style before calling <DoPositioning(..)>,
+ // because <DoPositioning(..)> may open a new context and thus may
+ // clear the <SwHTMLNumRuleInfo> instance hold by local variable <rInfo>.
+ if( bChangeNumFormat )
+ {
+ rInfo.GetNumRule()->Set( nLevel, aNumFormat );
+ m_xDoc->ChgNumRuleFormats( *rInfo.GetNumRule() );
+ bChangeNumFormat = false;
+ }
+
+ DoPositioning(aItemSet, aPropInfo, xCntxt.get());
+
+ InsertAttrs(aItemSet, aPropInfo, xCntxt.get());
+ }
+ }
+
+ if( bChangeNumFormat )
+ {
+ rInfo.GetNumRule()->Set( nLevel, aNumFormat );
+ m_xDoc->ChgNumRuleFormats( *rInfo.GetNumRule() );
+ }
+
+ PushContext(xCntxt);
+
+ // set attributes to the current template
+ SetTextCollAttrs(m_aContexts.back().get());
+}
+
+void SwHTMLParser::EndNumberBulletList( HtmlTokenId nToken )
+{
+ SwHTMLNumRuleInfo& rInfo = GetNumInfo();
+
+ // A new paragraph needs to be created, when
+ // - the current one isn't empty (it contains text or paragraph-bound objects)
+ // - the current one is numbered
+ bool bAppend = m_pPam->GetPoint()->GetContentIndex() > 0;
+ if( !bAppend )
+ {
+ SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+
+ bAppend = (pTextNode && ! pTextNode->IsOutline() && pTextNode->IsCountedInList()) ||
+
+ HasCurrentParaFlys();
+ }
+
+ bool bSpace = (rInfo.GetDepth() + m_nDefListDeep) == 1;
+ if( bAppend )
+ AppendTextNode( bSpace ? AM_SPACE : AM_NOSPACE, false );
+ else if( bSpace )
+ AddParSpace();
+
+ // get current context from stack
+ std::unique_ptr<HTMLAttrContext> xCntxt(nToken != HtmlTokenId::NONE ? PopContext(getOnToken(nToken)) : nullptr);
+
+ // Don't end a list because of a token, if the context wasn't created or mustn't be ended
+ if( rInfo.GetDepth()>0 && (nToken == HtmlTokenId::NONE || xCntxt) )
+ {
+ rInfo.DecDepth();
+ if( !rInfo.GetDepth() ) // was that the last level?
+ {
+ // The formats not yet modified are now modified, to ease editing
+ const SwNumFormat *pRefNumFormat = nullptr;
+ bool bChanged = false;
+ for( sal_uInt16 i=0; i<MAXLEVEL; i++ )
+ {
+ const SwNumFormat *pNumFormat = rInfo.GetNumRule()->GetNumFormat(i);
+ if( pNumFormat )
+ {
+ pRefNumFormat = pNumFormat;
+ }
+ else if( pRefNumFormat )
+ {
+ SwNumFormat aNumFormat( rInfo.GetNumRule()->Get(i) );
+ aNumFormat.SetNumberingType(pRefNumFormat->GetNumberingType() != SVX_NUM_BITMAP
+ ? pRefNumFormat->GetNumberingType() : SVX_NUM_CHAR_SPECIAL);
+ if( SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType() )
+ {
+ // #i63395# - Only apply user defined default bullet font
+ if ( numfunc::IsDefBulletFontUserDefined() )
+ {
+ aNumFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ }
+ aNumFormat.SetBulletChar( cBulletChar );
+ }
+ aNumFormat.SetAbsLSpace( (i+1) * HTML_NUMBER_BULLET_MARGINLEFT );
+ aNumFormat.SetFirstLineOffset( HTML_NUMBER_BULLET_INDENT );
+ aNumFormat.SetCharFormat( pRefNumFormat->GetCharFormat() );
+ rInfo.GetNumRule()->Set( i, aNumFormat );
+ bChanged = true;
+ }
+ }
+ if( bChanged )
+ m_xDoc->ChgNumRuleFormats( *rInfo.GetNumRule() );
+
+ // On the last append, the NumRule item and NodeNum object were copied.
+ // Now we need to delete them. ResetAttr deletes the NodeNum object as well
+ if (SwTextNode *pTextNode = m_pPam->GetPointNode().GetTextNode())
+ pTextNode->ResetAttr(RES_PARATR_NUMRULE);
+
+ rInfo.Clear();
+ }
+ else
+ {
+ // the next paragraph not numbered first
+ SetNodeNum( rInfo.GetLevel() );
+ }
+ }
+
+ // end attributes
+ bool bSetAttrs = false;
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ xCntxt.reset();
+ bSetAttrs = true;
+ }
+
+ if( nToken != HtmlTokenId::NONE )
+ SetTextCollAttrs();
+
+ if( bSetAttrs )
+ SetAttr(); // Set paragraph attributes asap because of Javascript
+
+}
+
+void SwHTMLParser::NewNumberBulletListItem( HtmlTokenId nToken )
+{
+ sal_uInt8 nLevel = GetNumInfo().GetLevel();
+ OUString aId, aStyle, aClass, aLang, aDir;
+ sal_uInt16 nStart = HtmlTokenId::LISTHEADER_ON != nToken
+ ? GetNumInfo().GetNodeStartValue( nLevel )
+ : USHRT_MAX;
+ if( USHRT_MAX != nStart )
+ GetNumInfo().SetNodeStartValue( nLevel );
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::VALUE:
+ nStart = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // create a new paragraph
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_NOSPACE, false );
+ m_bNoParSpace = false; // no space in <LI>!
+
+ SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+ if (!pTextNode)
+ {
+ SAL_WARN("sw.html", "No Text-Node at PaM-Position");
+ return;
+ }
+
+ const bool bCountedInList = nToken != HtmlTokenId::LISTHEADER_ON;
+
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ OUString aNumRuleName;
+ if( GetNumInfo().GetNumRule() )
+ {
+ aNumRuleName = GetNumInfo().GetNumRule()->GetName();
+ }
+ else
+ {
+ aNumRuleName = m_xDoc->GetUniqueNumRuleName();
+ SwNumRule aNumRule( aNumRuleName,
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION );
+ SwNumFormat aNumFormat( aNumRule.Get( 0 ) );
+ // #i63395# - Only apply user defined default bullet font
+ if ( numfunc::IsDefBulletFontUserDefined() )
+ {
+ aNumFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ }
+ aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aNumFormat.SetBulletChar( cBulletChar ); // the bullet character !!
+ aNumFormat.SetCharFormat( m_pCSS1Parser->GetCharFormatFromPool(RES_POOLCHR_BULLET_LEVEL) );
+ aNumFormat.SetFirstLineOffset( HTML_NUMBER_BULLET_INDENT );
+ aNumRule.Set( 0, aNumFormat );
+
+ m_xDoc->MakeNumRule( aNumRuleName, &aNumRule );
+
+ OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE,
+ "Now an open paragraph element is lost" );
+ // We'll act like we're in a paragraph. On the next paragraph, at least numbering is gone,
+ // that's gonna be taken over by the next AppendTextNode
+ m_nOpenParaToken = nToken;
+ }
+
+ static_cast<SwContentNode *>(pTextNode)->SetAttr( SwNumRuleItem(aNumRuleName) );
+ pTextNode->SetAttrListLevel(nLevel);
+ // #i57656# - <IsCounted()> state of text node has to be adjusted accordingly.
+ if ( nLevel < MAXLEVEL )
+ {
+ pTextNode->SetCountedInList( bCountedInList );
+ }
+ // #i57919#
+ // correction of refactoring done by cws swnumtree
+ // - <nStart> contains the start value, if the numbering has to be restarted
+ // at this text node. Value <USHRT_MAX> indicates, that numbering isn't
+ // restarted at this text node
+ if ( nStart != USHRT_MAX )
+ {
+ pTextNode->SetListRestart( true );
+ pTextNode->SetAttrListRestartValue( nStart );
+ }
+
+ if( GetNumInfo().GetNumRule() )
+ GetNumInfo().GetNumRule()->SetInvalidRule( true );
+
+ // parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ {
+ DoPositioning(aItemSet, aPropInfo, xCntxt.get());
+ InsertAttrs(aItemSet, aPropInfo, xCntxt.get());
+ }
+ }
+
+ PushContext(xCntxt);
+
+ // set the new template
+ SetTextCollAttrs(m_aContexts.back().get());
+
+ // Refresh scroll bar
+ ShowStatline();
+}
+
+void SwHTMLParser::EndNumberBulletListItem( HtmlTokenId nToken, bool bSetColl )
+{
+ // Create a new paragraph
+ if( nToken == HtmlTokenId::NONE && m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_NOSPACE );
+
+ // Get context to that token and pop it from stack
+ std::unique_ptr<HTMLAttrContext> xCntxt;
+ auto nPos = m_aContexts.size();
+ nToken = getOnToken(nToken);
+ while (!xCntxt && nPos>m_nContextStMin)
+ {
+ HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
+ switch( nCntxtToken )
+ {
+ case HtmlTokenId::LI_ON:
+ case HtmlTokenId::LISTHEADER_ON:
+ if( nToken == HtmlTokenId::NONE || nToken == nCntxtToken )
+ {
+ xCntxt = std::move(m_aContexts[nPos]);
+ m_aContexts.erase( m_aContexts.begin() + nPos );
+ }
+ break;
+ case HtmlTokenId::ORDERLIST_ON:
+ case HtmlTokenId::UNORDERLIST_ON:
+ case HtmlTokenId::MENULIST_ON:
+ case HtmlTokenId::DIRLIST_ON:
+ // Don't care about LI/LH outside the current list
+ nPos = m_nContextStMin;
+ break;
+ default: break;
+ }
+ }
+
+ // end attributes
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ SetAttr(); // set paragraph attributes asap because of Javascript
+ xCntxt.reset();
+ }
+
+ // set current template
+ if( bSetColl )
+ SetTextCollAttrs();
+}
+
+void SwHTMLParser::SetNodeNum( sal_uInt8 nLevel )
+{
+ SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+ if (!pTextNode)
+ {
+ SAL_WARN("sw.html", "No Text-Node at PaM-Position");
+ return;
+ }
+
+ OSL_ENSURE( GetNumInfo().GetNumRule(), "No numbering rule" );
+ const OUString& rName = GetNumInfo().GetNumRule()->GetName();
+ static_cast<SwContentNode *>(pTextNode)->SetAttr( SwNumRuleItem(rName) );
+
+ pTextNode->SetAttrListLevel( nLevel );
+ pTextNode->SetCountedInList( false );
+
+ // Invalidate NumRule, it may have been set valid because of an EndAction
+ GetNumInfo().GetNumRule()->SetInvalidRule( false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlnumwriter.cxx b/sw/source/filter/html/htmlnumwriter.cxx
new file mode 100644
index 0000000000..046747754a
--- /dev/null
+++ b/sw/source/filter/html/htmlnumwriter.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <numrule.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+
+#include "htmlnum.hxx"
+#include "wrthtml.hxx"
+
+#include <osl/diagnose.h>
+
+using namespace css;
+
+
+void SwHTMLWriter::FillNextNumInfo()
+{
+ m_pNextNumRuleInfo = nullptr;
+
+ SwNodeOffset nPos = m_pCurrentPam->GetPoint()->GetNodeIndex() + 1;
+
+ bool bTable = false;
+ do
+ {
+ const SwNode* pNd = m_pDoc->GetNodes()[nPos];
+ if( pNd->IsTextNode() )
+ {
+ m_pNextNumRuleInfo.reset( new SwHTMLNumRuleInfo( *pNd->GetTextNode() ) );
+
+ // Before a table we keep the old level if the same numbering is
+ // continued after the table and no new numbering is started.
+ // The table will get the indentation that corresponds to its
+ // numbering level during import.
+ if( bTable &&
+ m_pNextNumRuleInfo->GetNumRule()==GetNumInfo().GetNumRule() &&
+ !m_pNextNumRuleInfo->IsRestart(GetNumInfo()) )
+ {
+ m_pNextNumRuleInfo->SetDepth( GetNumInfo().GetDepth() );
+ }
+ }
+ else if( pNd->IsTableNode() )
+ {
+ // A table is skipped so the node after table is viewed.
+ nPos = pNd->EndOfSectionIndex() + 1;
+ bTable = true;
+ }
+ else
+ {
+ // In all other case the numbering is over.
+ m_pNextNumRuleInfo.reset(new SwHTMLNumRuleInfo);
+ }
+ }
+ while( !m_pNextNumRuleInfo );
+}
+
+void SwHTMLWriter::ClearNextNumInfo()
+{
+ m_pNextNumRuleInfo.reset();
+}
+
+void SwHTMLWriter::SetNextNumInfo( std::unique_ptr<SwHTMLNumRuleInfo> pNxt )
+{
+ m_pNextNumRuleInfo = std::move(pNxt);
+}
+
+SwHTMLWriter& OutHTML_NumberBulletListStart( SwHTMLWriter& rWrt,
+ const SwHTMLNumRuleInfo& rInfo )
+{
+ SwHTMLNumRuleInfo& rPrevInfo = rWrt.GetNumInfo();
+ bool bSameRule = rPrevInfo.GetNumRule() == rInfo.GetNumRule();
+ if( bSameRule && rPrevInfo.GetDepth() >= rInfo.GetDepth() &&
+ !rInfo.IsRestart(rPrevInfo) )
+ {
+ return rWrt;
+ }
+
+ bool bStartValue = false;
+ if( !bSameRule && rInfo.GetDepth() )
+ {
+ OUString aName( rInfo.GetNumRule()->GetName() );
+ if( 0 != rWrt.m_aNumRuleNames.count( aName ) )
+ {
+ // The rule has been applied before
+ sal_Int16 eType = rInfo.GetNumRule()
+ ->Get( rInfo.GetDepth()-1 ).GetNumberingType();
+ if( SVX_NUM_CHAR_SPECIAL != eType && SVX_NUM_BITMAP != eType )
+ {
+ // If it's a numbering rule, the current number should be
+ // exported as start value, but only if there are no nodes
+ // within the numbering that have a lower level
+ bStartValue = true;
+ if( rInfo.GetDepth() > 1 )
+ {
+ SwNodeOffset nPos =
+ rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() + 1;
+ do
+ {
+ const SwNode* pNd = rWrt.m_pDoc->GetNodes()[nPos];
+ if( pNd->IsTextNode() )
+ {
+ const SwTextNode *pTextNd = pNd->GetTextNode();
+ if( !pTextNd->GetNumRule() )
+ {
+ // node isn't numbered => check completed
+ break;
+ }
+
+ OSL_ENSURE(! pTextNd->IsOutline(),
+ "outline not expected");
+
+ if( pTextNd->GetActualListLevel() + 1 <
+ rInfo.GetDepth() )
+ {
+ if (rPrevInfo.GetDepth() == 0)
+ // previous node had no numbering => write start value
+ bStartValue = true;
+ else
+ // node is numbered, but level is lower
+ bStartValue = false;
+ // => check completed
+ break;
+ }
+ nPos++;
+ }
+ else if( pNd->IsTableNode() )
+ {
+ // skip table
+ nPos = pNd->EndOfSectionIndex() + 1;
+ }
+ else
+ {
+ // end node or sections start node -> check
+ // completed
+ break;
+ }
+ }
+ while( true );
+ }
+ }
+ }
+ else
+ {
+ rWrt.m_aNumRuleNames.insert( aName );
+ }
+ }
+
+ OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE,
+ "<PRE> was not closed before <OL>." );
+ sal_uInt16 nPrevDepth =
+ (bSameRule && !rInfo.IsRestart(rPrevInfo)) ? rPrevInfo.GetDepth() : 0;
+
+ for( sal_uInt16 i=nPrevDepth; i<rInfo.GetDepth(); i++ )
+ {
+ rWrt.OutNewLine(); // <OL>/<UL> in a new row
+
+ rWrt.m_aBulletGrfs[i].clear();
+ OString sOut = "<" + rWrt.GetNamespace();
+ if (rWrt.mbXHTML && i != nPrevDepth)
+ {
+ // for all skipped sublevels, add a li
+ sOut += OOO_STRING_SVTOOLS_HTML_li "><" + rWrt.GetNamespace();
+ }
+ const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get( i );
+ sal_Int16 eType = rNumFormat.GetNumberingType();
+ if( SVX_NUM_CHAR_SPECIAL == eType )
+ {
+ // unordered list: <UL>
+ sOut += OOO_STRING_SVTOOLS_HTML_unorderlist;
+
+ // determine the type by the bullet character
+ const char *pStr = nullptr;
+ switch( rNumFormat.GetBulletChar() )
+ {
+ case HTML_BULLETCHAR_DISC:
+ pStr = OOO_STRING_SVTOOLS_HTML_ULTYPE_disc;
+ break;
+ case HTML_BULLETCHAR_CIRCLE:
+ pStr = OOO_STRING_SVTOOLS_HTML_ULTYPE_circle;
+ break;
+ case HTML_BULLETCHAR_SQUARE:
+ pStr = OOO_STRING_SVTOOLS_HTML_ULTYPE_square;
+ break;
+ }
+
+ if( pStr )
+ {
+ sOut += OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_type "=\"") + pStr + "\"";
+ }
+ }
+ else if( SVX_NUM_BITMAP == eType )
+ {
+ // Unordered list: <UL>
+ sOut += OOO_STRING_SVTOOLS_HTML_unorderlist;
+ }
+ else
+ {
+ // Ordered list: <OL>
+ sOut += OOO_STRING_SVTOOLS_HTML_orderlist;
+
+ if (!rWrt.mbReqIF) // No 'type' nor 'start' attribute in ReqIF
+ {
+ // determine the type by the format
+ char cType = 0;
+ switch (eType)
+ {
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ cType = 'A';
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ cType = 'a';
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ cType = 'I';
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ cType = 'i';
+ break;
+ }
+ if( cType )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_type "=\"" + OStringChar(cType) + "\"";
+ }
+
+ sal_uInt16 nStartVal = rNumFormat.GetStart();
+ if( bStartValue && 1 == nStartVal && i == rInfo.GetDepth()-1 )
+ {
+ if ( rWrt.m_pCurrentPam->GetPointNode().GetTextNode()->GetNum() )
+ {
+ nStartVal = static_cast< sal_uInt16 >( rWrt.m_pCurrentPam->GetPointNode()
+ .GetTextNode()->GetNumberVector()[i] );
+ }
+ else
+ {
+ OSL_FAIL( "<OutHTML_NumberBulletListStart(..) - text node has no number." );
+ }
+ }
+ if( nStartVal != 1 )
+ {
+ sOut += " " OOO_STRING_SVTOOLS_HTML_O_start "=\"" + OString::number(static_cast<sal_Int32>(nStartVal)) + "\"";
+ }
+ }
+ }
+
+ rWrt.Strm().WriteOString(sOut);
+
+ if (eType == SVX_NUM_BITMAP)
+ OutHTML_BulletImage(rWrt, nullptr, rNumFormat.GetBrush(), rWrt.m_aBulletGrfs[i]);
+
+ if( rWrt.m_bCfgOutStyles )
+ OutCSS1_NumberBulletListStyleOpt( rWrt, *rInfo.GetNumRule(), static_cast<sal_uInt8>(i) );
+
+ rWrt.Strm().WriteChar( '>' );
+
+ rWrt.IncIndentLevel(); // indent content of <OL>
+ }
+
+ return rWrt;
+}
+
+SwHTMLWriter& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
+ const SwHTMLNumRuleInfo& rNextInfo )
+{
+ SwHTMLNumRuleInfo& rInfo = rWrt.GetNumInfo();
+ bool bSameRule = rNextInfo.GetNumRule() == rInfo.GetNumRule();
+ bool bListEnd = !bSameRule || rNextInfo.GetDepth() < rInfo.GetDepth() || rNextInfo.IsRestart(rInfo);
+ bool bNextIsSubitem = !bListEnd && rNextInfo.GetDepth() > rInfo.GetDepth();
+
+ // XHTML </li> for the list item content, if there is an open <li>.
+ if (bListEnd || (!bNextIsSubitem && rNextInfo.IsNumbered()))
+ {
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li),
+ false);
+ }
+
+ if (!bListEnd)
+ {
+ return rWrt;
+ }
+
+ OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE,
+ "<PRE> was not closed before </OL>." );
+ sal_uInt16 nNextDepth =
+ (bSameRule && !rNextInfo.IsRestart(rInfo)) ? rNextInfo.GetDepth() : 0;
+
+ // MIB 23.7.97: We must loop backwards, to get the right order of </OL>/</UL>
+ for( sal_uInt16 i=rInfo.GetDepth(); i>nNextDepth; i-- )
+ {
+ rWrt.DecIndentLevel(); // indent content of <OL>
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine(); // </OL>/</UL> in a new line
+
+ // a list is started or ended:
+ sal_Int16 eType = rInfo.GetNumRule()->Get( i-1 ).GetNumberingType();
+ OString aTag;
+ if( SVX_NUM_CHAR_SPECIAL == eType || SVX_NUM_BITMAP == eType)
+ aTag = OOO_STRING_SVTOOLS_HTML_unorderlist ""_ostr;
+ else
+ aTag = OOO_STRING_SVTOOLS_HTML_orderlist ""_ostr;
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false );
+ if (rWrt.mbXHTML && (i != nNextDepth + 1 || (i != 1 && rNextInfo.IsNumbered())))
+ {
+ // for all skipped sublevels, close a li
+ HTMLOutFuncs::Out_AsciiTag(
+ rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li),
+ /*bOn=*/false);
+ }
+ rWrt.SetLFPossible(true);
+ }
+
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx
new file mode 100644
index 0000000000..8b2ff71cd3
--- /dev/null
+++ b/sw/source/filter/html/htmlplug.cxx
@@ -0,0 +1,1765 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_java.h>
+
+#include <hintids.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <svl/urihelper.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/frmhtml.hxx>
+#include <sfx2/frmhtmlw.hxx>
+#include <sfx2/frmdescr.hxx>
+#include <sot/storage.hxx>
+#include <svx/xoutbmp.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmltokn.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <SwAppletImpl.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <frmfmt.hxx>
+
+#include <svl/ownlist.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/streamwrap.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <swerror.h>
+#include <ndole.hxx>
+#include <docsh.hxx>
+#include "swhtml.hxx"
+#include "wrthtml.hxx"
+#include "htmlfly.hxx"
+#include "swcss1.hxx"
+#include "htmlreqifreader.hxx"
+#include <unoframe.hxx>
+#include <com/sun/star/embed/XClassifiedObject.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/embed/XEmbedPersist2.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <comphelper/classids.hxx>
+#include <rtl/uri.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <filter/msfilter/msoleexp.hxx>
+#include <comphelper/fileurl.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <svtools/HtmlWriter.hxx>
+
+using namespace com::sun::star;
+
+
+#define HTML_DFLT_EMBED_WIDTH (o3tl::toTwips(125, o3tl::Length::mm10))
+#define HTML_DFLT_EMBED_HEIGHT (o3tl::toTwips(125, o3tl::Length::mm10))
+
+#define HTML_DFLT_APPLET_WIDTH (o3tl::toTwips(125, o3tl::Length::mm10))
+#define HTML_DFLT_APPLET_HEIGHT (o3tl::toTwips(125, o3tl::Length::mm10))
+
+
+const HtmlFrmOpts HTML_FRMOPTS_EMBED_ALL =
+ HtmlFrmOpts::Alt |
+ HtmlFrmOpts::Size |
+ HtmlFrmOpts::Name;
+const HtmlFrmOpts HTML_FRMOPTS_EMBED_CNTNR =
+ HTML_FRMOPTS_EMBED_ALL |
+ HtmlFrmOpts::AbsSize;
+const HtmlFrmOpts HTML_FRMOPTS_EMBED =
+ HTML_FRMOPTS_EMBED_ALL |
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::Space |
+ HtmlFrmOpts::BrClear |
+ HtmlFrmOpts::Name;
+const HtmlFrmOpts HTML_FRMOPTS_HIDDEN_EMBED =
+ HtmlFrmOpts::Alt |
+ HtmlFrmOpts::Name;
+
+const HtmlFrmOpts HTML_FRMOPTS_APPLET_ALL =
+ HtmlFrmOpts::Alt |
+ HtmlFrmOpts::Size;
+const HtmlFrmOpts HTML_FRMOPTS_APPLET_CNTNR =
+ HTML_FRMOPTS_APPLET_ALL |
+ HtmlFrmOpts::AbsSize;
+const HtmlFrmOpts HTML_FRMOPTS_APPLET =
+ HTML_FRMOPTS_APPLET_ALL |
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::Space |
+ HtmlFrmOpts::BrClear;
+
+const HtmlFrmOpts HTML_FRMOPTS_IFRAME_ALL =
+ HtmlFrmOpts::Alt |
+ HtmlFrmOpts::Size;
+const HtmlFrmOpts HTML_FRMOPTS_IFRAME_CNTNR =
+ HTML_FRMOPTS_IFRAME_ALL |
+ HtmlFrmOpts::AbsSize;
+const HtmlFrmOpts HTML_FRMOPTS_IFRAME =
+ HTML_FRMOPTS_IFRAME_ALL |
+ HtmlFrmOpts::Align |
+ HtmlFrmOpts::Space |
+ HtmlFrmOpts::Border |
+ HtmlFrmOpts::BrClear;
+
+const HtmlFrmOpts HTML_FRMOPTS_OLE_CSS1 =
+ HtmlFrmOpts::SAlign |
+ HtmlFrmOpts::SSpace;
+
+namespace
+{
+/**
+ * Calculates a filename for an image, provided the HTML file name, the image
+ * itself and a wanted extension.
+ */
+OUString lcl_CalculateFileName(const OUString* pOrigFileName, const Graphic& rGraphic,
+ std::u16string_view rExtension)
+{
+ OUString aFileName;
+
+ if (pOrigFileName)
+ aFileName = *pOrigFileName;
+ INetURLObject aURL(aFileName);
+ OUString aName = aURL.getBase() + "_" +
+ aURL.getExtension() + "_" +
+ OUString::number(rGraphic.GetChecksum(), 16);
+ aURL.setBase(aName);
+ aURL.setExtension(rExtension);
+ aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ return aFileName;
+}
+}
+
+void SwHTMLParser::SetFixSize( const Size& rPixSize,
+ const Size& rTwipDfltSize,
+ bool bPercentWidth, bool bPercentHeight,
+ SvxCSS1PropertyInfo const & rCSS1PropInfo,
+ SfxItemSet& rFlyItemSet )
+{
+ // convert absolute size values into Twip
+ sal_uInt8 nPercentWidth = 0, nPercentHeight = 0;
+ Size aTwipSz( bPercentWidth || USHRT_MAX==rPixSize.Width() ? 0 : rPixSize.Width(),
+ bPercentHeight || USHRT_MAX==rPixSize.Height() ? 0 : rPixSize.Height() );
+ if( aTwipSz.Width() || aTwipSz.Height() )
+ {
+ aTwipSz = o3tl::convert(aTwipSz, o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ // process width
+ if( SVX_CSS1_LTYPE_PERCENTAGE == rCSS1PropInfo.m_eWidthType )
+ {
+ nPercentWidth = static_cast<sal_uInt8>(rCSS1PropInfo.m_nWidth);
+ aTwipSz.setWidth( rTwipDfltSize.Width() );
+ }
+ else if( SVX_CSS1_LTYPE_TWIP== rCSS1PropInfo.m_eWidthType )
+ {
+ aTwipSz.setWidth( rCSS1PropInfo.m_nWidth );
+ }
+ else if( bPercentWidth && rPixSize.Width() )
+ {
+ nPercentWidth = static_cast<sal_uInt8>(rPixSize.Width());
+ if (nPercentWidth > 100 && nPercentWidth != SwFormatFrameSize::SYNCED)
+ nPercentWidth = 100;
+
+ aTwipSz.setWidth( rTwipDfltSize.Width() );
+ }
+ else if( USHRT_MAX==rPixSize.Width() )
+ {
+ aTwipSz.setWidth( rTwipDfltSize.Width() );
+ }
+ if( aTwipSz.Width() < MINFLY )
+ {
+ aTwipSz.setWidth( MINFLY );
+ }
+
+ // process height
+ if( SVX_CSS1_LTYPE_PERCENTAGE == rCSS1PropInfo.m_eHeightType )
+ {
+ nPercentHeight = static_cast<sal_uInt8>(rCSS1PropInfo.m_nHeight);
+ aTwipSz.setHeight( rTwipDfltSize.Height() );
+ }
+ else if( SVX_CSS1_LTYPE_TWIP== rCSS1PropInfo.m_eHeightType )
+ {
+ aTwipSz.setHeight( rCSS1PropInfo.m_nHeight );
+ }
+ else if( bPercentHeight && rPixSize.Height() )
+ {
+ nPercentHeight = static_cast<sal_uInt8>(rPixSize.Height());
+ if (nPercentHeight > 100 && nPercentHeight != SwFormatFrameSize::SYNCED)
+ nPercentHeight = 100;
+
+ aTwipSz.setHeight( rTwipDfltSize.Height() );
+ }
+ else if( USHRT_MAX==rPixSize.Height() )
+ {
+ aTwipSz.setHeight( rTwipDfltSize.Height() );
+ }
+ if( aTwipSz.Height() < MINFLY )
+ {
+ aTwipSz.setHeight( MINFLY );
+ }
+
+ // set size
+ SwFormatFrameSize aFrameSize( SwFrameSize::Fixed, aTwipSz.Width(), aTwipSz.Height() );
+ aFrameSize.SetWidthPercent( nPercentWidth );
+ aFrameSize.SetHeightPercent( nPercentHeight );
+ rFlyItemSet.Put( aFrameSize );
+}
+
+void SwHTMLParser::SetSpace( const Size& rPixSpace,
+ SfxItemSet& rCSS1ItemSet,
+ SvxCSS1PropertyInfo& rCSS1PropInfo,
+ SfxItemSet& rFlyItemSet )
+{
+ sal_Int32 nLeftSpace = 0, nRightSpace = 0;
+ sal_uInt16 nUpperSpace = 0, nLowerSpace = 0;
+ if( rPixSpace.Width() || rPixSpace.Height() )
+ {
+ nLeftSpace = nRightSpace = o3tl::convert(rPixSpace.Width(), o3tl::Length::px, o3tl::Length::twip);
+ nUpperSpace = nLowerSpace = o3tl::convert(rPixSpace.Height(), o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ // set left/right margin
+ // note: parser never creates SvxLeftMarginItem! must be converted
+ if (const SvxTextLeftMarginItem *const pLeft = rCSS1ItemSet.GetItemIfSet(RES_MARGIN_TEXTLEFT))
+ {
+ if( rCSS1PropInfo.m_bLeftMargin )
+ {
+ // should be SvxLeftMarginItem... "cast" it
+ nLeftSpace = pLeft->GetTextLeft();
+ rCSS1PropInfo.m_bLeftMargin = false;
+ }
+ rCSS1ItemSet.ClearItem(RES_MARGIN_TEXTLEFT);
+ }
+ if (const SvxRightMarginItem *const pRight = rCSS1ItemSet.GetItemIfSet(RES_MARGIN_RIGHT))
+ {
+ if( rCSS1PropInfo.m_bRightMargin )
+ {
+ nRightSpace = pRight->GetRight();
+ rCSS1PropInfo.m_bRightMargin = false;
+ }
+ rCSS1ItemSet.ClearItem(RES_MARGIN_RIGHT);
+ }
+ if( nLeftSpace > 0 || nRightSpace > 0 )
+ {
+ SvxLRSpaceItem aLRItem( RES_LR_SPACE );
+ aLRItem.SetLeft( std::max<sal_Int32>(nLeftSpace, 0) );
+ aLRItem.SetRight( std::max<sal_Int32>(nRightSpace, 0) );
+ rFlyItemSet.Put( aLRItem );
+ if( nLeftSpace )
+ {
+ const SwFormatHoriOrient& rHoriOri =
+ rFlyItemSet.Get( RES_HORI_ORIENT );
+ if( text::HoriOrientation::NONE == rHoriOri.GetHoriOrient() )
+ {
+ SwFormatHoriOrient aHoriOri( rHoriOri );
+ aHoriOri.SetPos( aHoriOri.GetPos() + nLeftSpace );
+ rFlyItemSet.Put( aHoriOri );
+ }
+ }
+ }
+
+ // set top/bottom margin
+ if( const SvxULSpaceItem *pULItem = rCSS1ItemSet.GetItemIfSet( RES_UL_SPACE ) )
+ {
+ // if applicable remove the first line indent
+ if( rCSS1PropInfo.m_bTopMargin )
+ {
+ nUpperSpace = pULItem->GetUpper();
+ rCSS1PropInfo.m_bTopMargin = false;
+ }
+ if( rCSS1PropInfo.m_bBottomMargin )
+ {
+ nLowerSpace = pULItem->GetLower();
+ rCSS1PropInfo.m_bBottomMargin = false;
+ }
+ rCSS1ItemSet.ClearItem( RES_UL_SPACE );
+ }
+ if( !(nUpperSpace || nLowerSpace) )
+ return;
+
+ SvxULSpaceItem aULItem( RES_UL_SPACE );
+ aULItem.SetUpper( nUpperSpace );
+ aULItem.SetLower( nLowerSpace );
+ rFlyItemSet.Put( aULItem );
+ if( nUpperSpace )
+ {
+ const SwFormatVertOrient& rVertOri =
+ rFlyItemSet.Get( RES_VERT_ORIENT );
+ if( text::VertOrientation::NONE == rVertOri.GetVertOrient() )
+ {
+ SwFormatVertOrient aVertOri( rVertOri );
+ aVertOri.SetPos( aVertOri.GetPos() + nUpperSpace );
+ rFlyItemSet.Put( aVertOri );
+ }
+ }
+}
+
+OUString SwHTMLParser::StripQueryFromPath(std::u16string_view rBase, const OUString& rPath)
+{
+ if (!comphelper::isFileUrl(rBase))
+ return rPath;
+
+ sal_Int32 nIndex = rPath.indexOf('?');
+
+ if (nIndex != -1)
+ return rPath.copy(0, nIndex);
+
+ return rPath;
+}
+
+bool SwHTMLParser::InsertEmbed()
+{
+ OUString aURL, aType, aName, aAlt, aId, aStyle, aClass;
+ OUString aData;
+ Size aSize( USHRT_MAX, USHRT_MAX );
+ Size aSpace( USHRT_MAX, USHRT_MAX );
+ bool bPercentWidth = false, bPercentHeight = false, bHidden = false;
+ sal_Int16 eVertOri = text::VertOrientation::NONE;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+ SvCommandList aCmdLst;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+
+ // The options are read forwards, because the plug-ins expect them in this
+ // order. Still only the first value of an option may be regarded.
+ for (const auto & rOption : rHTMLOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ break;
+ case HtmlOptionId::SRC:
+ if( aURL.isEmpty() )
+ aURL = rOption.GetString();
+ break;
+ case HtmlOptionId::ALT:
+ aAlt = rOption.GetString();
+ break;
+ case HtmlOptionId::TYPE:
+ if( aType.isEmpty() )
+ aType = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ if( eVertOri==text::VertOrientation::NONE && eHoriOri==text::HoriOrientation::NONE )
+ {
+ eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri );
+ eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri );
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ if( USHRT_MAX==aSize.Width() )
+ {
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::HEIGHT:
+ if( USHRT_MAX==aSize.Height() )
+ {
+ bPercentHeight = (rOption.GetString().indexOf('%') != -1);
+ aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::HSPACE:
+ if( USHRT_MAX==aSpace.Width() )
+ aSpace.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::VSPACE:
+ if( USHRT_MAX==aSpace.Height() )
+ aSpace.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::DATA:
+ if (m_bXHTML && aURL.isEmpty())
+ aData = rOption.GetString();
+ break;
+ case HtmlOptionId::UNKNOWN:
+ if (rOption.GetTokenString().equalsIgnoreAsciiCase(
+ OOO_STRING_SW_HTML_O_Hidden))
+ {
+ bHidden = !rOption.GetString().equalsIgnoreAsciiCase(
+ "FALSE");
+ }
+ break;
+ default: break;
+ }
+
+ // All parameters are passed to the plug-in.
+ aCmdLst.Append( rOption.GetTokenString(), rOption.GetString() );
+ }
+
+ static const std::set<std::u16string_view> vAllowlist = {
+ u"image/png",
+ u"image/gif",
+ u"image/x-MS-bmp",
+ u"image/jpeg",
+ u"image/x-wmf",
+ u"image/svg+xml",
+ u"image/tiff",
+ u"image/x-emf",
+ u"image/bmp",
+ u"image/tif",
+ u"image/wmf",
+ };
+
+ if (vAllowlist.find(aType) != vAllowlist.end() && m_aEmbeds.empty())
+ {
+ // Toplevel <object> for an image format -> that's an image, not an OLE object.
+ m_aEmbeds.push(nullptr);
+ return false;
+ }
+
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo );
+
+ // Convert the default values (except height/width, which is done by SetFrameSize())
+ if( eVertOri==text::VertOrientation::NONE && eHoriOri==text::HoriOrientation::NONE )
+ eVertOri = text::VertOrientation::TOP;
+ if( USHRT_MAX==aSpace.Width() )
+ aSpace.setWidth( 0 );
+ if( USHRT_MAX==aSpace.Height() )
+ aSpace.setHeight( 0 );
+ if( bHidden )
+ {
+ // Size (0,0) will be changed to (MINFLY, MINFLY) in SetFrameSize()
+ aSize.setWidth( 0 ); aSize.setHeight( 0 );
+ aSpace.setWidth( 0 ); aSpace.setHeight( 0 );
+ bPercentWidth = bPercentHeight = false;
+ }
+
+ // prepare the URL
+ INetURLObject aURLObj;
+ bool bHasURL = !aURL.isEmpty() &&
+ aURLObj.SetURL(
+ URIHelper::SmartRel2Abs(
+ INetURLObject(m_sBaseURL), aURL,
+ URIHelper::GetMaybeFileHdl()) );
+ bool bHasData = !aData.isEmpty();
+
+ try
+ {
+ // Strip query and everything after that for file:// URLs, browsers do
+ // the same.
+ aURLObj.SetURL(rtl::Uri::convertRelToAbs(
+ m_sBaseURL, SwHTMLParser::StripQueryFromPath(m_sBaseURL, aData)));
+ }
+ catch (const rtl::MalformedUriException& /*rException*/)
+ {
+ bHasData = false;
+ }
+
+ // do not insert plugin if it has neither URL nor type
+ bool bHasType = !aType.isEmpty();
+ if( !bHasURL && !bHasType && !bHasData )
+ return true;
+
+ if (!m_aEmbeds.empty())
+ {
+ // Nested XHTML <object> element: points to replacement graphic.
+ SwOLENode* pOLENode = m_aEmbeds.top();
+ if (!pOLENode)
+ {
+ // <object> is mapped to an image -> ignore replacement graphic.
+ return true;
+ }
+
+ svt::EmbeddedObjectRef& rObj = pOLENode->GetOLEObj().GetObject();
+ Graphic aGraphic;
+ if (GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, aURLObj) != ERRCODE_NONE)
+ return true;
+
+ rObj.SetGraphic(aGraphic, aType);
+
+ // Set the size of the OLE frame to the size of the graphic.
+ SwFrameFormat* pFormat = pOLENode->GetFlyFormat();
+ if (!pFormat)
+ return true;
+ SwAttrSet aAttrSet(pFormat->GetAttrSet());
+ aAttrSet.ClearItem(RES_CNTNT);
+ Size aDefaultTwipSize(o3tl::convert(aGraphic.GetSizePixel(), o3tl::Length::px, o3tl::Length::twip));
+
+ if (aSize.Width() == USHRT_MAX && bPercentHeight)
+ {
+ // Height is relative, width is not set: keep aspect ratio.
+ aSize.setWidth(SwFormatFrameSize::SYNCED);
+ bPercentWidth = true;
+ }
+ if (aSize.Height() == USHRT_MAX && bPercentWidth)
+ {
+ // Width is relative, height is not set: keep aspect ratio.
+ aSize.setHeight(SwFormatFrameSize::SYNCED);
+ bPercentHeight = true;
+ }
+
+ SetFixSize(aSize, aDefaultTwipSize, bPercentWidth, bPercentHeight, aPropInfo, aAttrSet);
+ pOLENode->GetDoc().SetFlyFrameAttr(*pFormat, aAttrSet);
+ return true;
+ }
+
+ // create the plug-in
+ comphelper::EmbeddedObjectContainer aCnt;
+ OUString aObjName;
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ if (!bHasData)
+ {
+ xObj = aCnt.CreateEmbeddedObject( SvGlobalName( SO3_PLUGIN_CLASSID ).GetByteSequence(), aObjName );
+ if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ if( bHasURL )
+ xSet->setPropertyValue("PluginURL", uno::Any( aURL ) );
+ if( bHasType )
+ xSet->setPropertyValue("PluginMimeType", uno::Any( aType ) );
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ aCmdLst.FillSequence( aProps );
+ xSet->setPropertyValue("PluginCommands", uno::Any( aProps ) );
+
+ }
+ }
+ }
+ else if (SwDocShell* pDocSh = m_xDoc->GetDocShell())
+ {
+ // Has non-empty data attribute in XHTML: map that to an OLE object.
+ uno::Reference<embed::XStorage> xStorage = pDocSh->GetStorage();
+ aCnt.SwitchPersistence(xStorage);
+ aObjName = aCnt.CreateUniqueObjectName();
+ {
+ OUString aEmbedURL = aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ SvFileStream aFileStream(aEmbedURL, StreamMode::READ);
+ uno::Reference<io::XInputStream> xInStream;
+ SvMemoryStream aMemoryStream;
+
+ // Allow any MIME type that starts with magic, unless a set of allowed types are
+ // specified.
+ auto it = m_aAllowedRTFOLEMimeTypes.find(aType);
+ if (m_aAllowedRTFOLEMimeTypes.empty() || it != m_aAllowedRTFOLEMimeTypes.end())
+ {
+ OString aMagic("{\\object"_ostr);
+ OString aHeader(read_uInt8s_ToOString(aFileStream, aMagic.getLength()));
+ aFileStream.Seek(0);
+ if (aHeader == aMagic)
+ {
+ // OLE2 wrapped in RTF: either own format or real OLE2 embedding.
+ bool bOwnFormat = false;
+ if (SwReqIfReader::ExtractOleFromRtf(aFileStream, aMemoryStream, bOwnFormat))
+ {
+ xInStream.set(new utl::OStreamWrapper(aMemoryStream));
+ }
+
+ if (bOwnFormat)
+ {
+ uno::Sequence<beans::PropertyValue> aMedium = comphelper::InitPropertySequence(
+ { { "InputStream", uno::Any(xInStream) },
+ { "URL", uno::Any(OUString("private:stream")) },
+ { "DocumentBaseURL", uno::Any(m_sBaseURL) } });
+ xObj = aCnt.InsertEmbeddedObject(aMedium, aName, &m_sBaseURL);
+ }
+ else
+ {
+ // The type is now an OLE2 container, not the original XHTML type.
+ aType = "application/vnd.sun.star.oleobject";
+ }
+ }
+ }
+
+ if (!xInStream.is())
+ {
+ // Object data is neither OLE2 in RTF, nor an image. Then map this to an URL that
+ // will be set on the inner image.
+ m_aEmbedURL = aEmbedURL;
+ // Signal success, so the outer object won't fall back to the image handler.
+ return true;
+ }
+
+ if (!xObj.is())
+ {
+ uno::Reference<io::XStream> xOutStream
+ = xStorage->openStreamElement(aObjName, embed::ElementModes::READWRITE);
+ if (aFileStream.IsOpen())
+ comphelper::OStorageHelper::CopyInputToOutput(xInStream,
+ xOutStream->getOutputStream());
+
+ if (!aType.isEmpty())
+ {
+ // Set media type of the native data.
+ uno::Reference<beans::XPropertySet> xOutStreamProps(xOutStream, uno::UNO_QUERY);
+ if (xOutStreamProps.is())
+ xOutStreamProps->setPropertyValue("MediaType", uno::Any(aType));
+ }
+ }
+ xObj = aCnt.GetEmbeddedObject(aObjName);
+ }
+ }
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( aFrameSet );
+
+ // set the anchor
+ if( !bHidden )
+ {
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, aFrameSet );
+ }
+ else
+ {
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AT_PARA );
+ aAnchor.SetAnchor( m_pPam->GetPoint() );
+ aFrameSet.Put( aAnchor );
+ aFrameSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT, text::RelOrientation::FRAME) );
+ aFrameSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) );
+ aFrameSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::PRINT_AREA ) );
+ }
+
+ // and the size of the frame
+ Size aDfltSz( HTML_DFLT_EMBED_WIDTH, HTML_DFLT_EMBED_HEIGHT );
+ SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, aFrameSet );
+ SetSpace( aSpace, aItemSet, aPropInfo, aFrameSet );
+
+ // and insert into the document
+ uno::Reference<lang::XInitialization> xObjInitialization(xObj, uno::UNO_QUERY);
+ if (xObjInitialization.is())
+ {
+ // Request that the native data of the embedded object is not modified
+ // during parsing.
+ uno::Sequence<beans::PropertyValue> aValues{ comphelper::makePropertyValue("StreamReadOnly",
+ true) };
+ uno::Sequence<uno::Any> aArguments{ uno::Any(aValues) };
+ xObjInitialization->initialize(aArguments);
+ }
+ SwFrameFormat* pFlyFormat =
+ m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam,
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ &aFrameSet);
+ if (xObjInitialization.is())
+ {
+ uno::Sequence<beans::PropertyValue> aValues{ comphelper::makePropertyValue("StreamReadOnly",
+ false) };
+ uno::Sequence<uno::Any> aArguments{ uno::Any(aValues) };
+ xObjInitialization->initialize(aArguments);
+ }
+
+ // set name at FrameFormat
+ if( !aName.isEmpty() )
+ pFlyFormat->SetFormatName( aName );
+
+ // set the alternative text
+ SwNoTextNode *pNoTextNd =
+ m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx()
+ ->GetIndex()+1 ]->GetNoTextNode();
+ pNoTextNd->SetTitle( aAlt );
+
+ // if applicable create frames and register auto-bound frames
+ if( !bHidden )
+ {
+ // HIDDEN plug-ins should stay paragraph-bound. Since RegisterFlyFrame()
+ // will change paragraph-bound frames with wrap-through into a
+ // character-bound frame, here we must create the frames by hand.
+ RegisterFlyFrame( pFlyFormat );
+ }
+
+ if (!bHasData)
+ return true;
+
+ SwOLENode* pOLENode = pNoTextNd->GetOLENode();
+ if (!pOLENode)
+ return true;
+
+ m_aEmbeds.push(pOLENode);
+
+ return true;
+}
+
+#if HAVE_FEATURE_JAVA
+void SwHTMLParser::NewObject()
+{
+ OUString aClassID;
+ OUString aStandBy, aId, aStyle, aClass;
+ Size aSize( USHRT_MAX, USHRT_MAX );
+ Size aSpace( 0, 0 );
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+
+ bool bPercentWidth = false, bPercentHeight = false,
+ bDeclare = false;
+ // create a new Command list
+ m_pAppletImpl.reset(new SwApplet_Impl( m_xDoc->GetAttrPool() ));
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::DECLARE:
+ bDeclare = true;
+ break;
+ case HtmlOptionId::CLASSID:
+ aClassID = rOption.GetString();
+ break;
+ case HtmlOptionId::CODEBASE:
+ break;
+ case HtmlOptionId::DATA:
+ break;
+ case HtmlOptionId::TYPE:
+ break;
+ case HtmlOptionId::CODETYPE:
+ break;
+ case HtmlOptionId::ARCHIVE:
+ case HtmlOptionId::UNKNOWN:
+ break;
+ case HtmlOptionId::STANDBY:
+ aStandBy = rOption.GetString();
+ break;
+ case HtmlOptionId::WIDTH:
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::HEIGHT:
+ bPercentHeight = (rOption.GetString().indexOf('%') != -1);
+ aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri );
+ eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri );
+ break;
+ case HtmlOptionId::USEMAP:
+ break;
+ case HtmlOptionId::NAME:
+ break;
+ case HtmlOptionId::HSPACE:
+ aSpace.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::VSPACE:
+ aSpace.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::BORDER:
+ break;
+
+ case HtmlOptionId::SDONCLICK:
+ case HtmlOptionId::ONCLICK:
+ case HtmlOptionId::SDONMOUSEOVER:
+ case HtmlOptionId::ONMOUSEOVER:
+ case HtmlOptionId::SDONMOUSEOUT:
+ case HtmlOptionId::ONMOUSEOUT:
+ break;
+ default: break;
+ }
+ // All parameters are passed to the applet.
+ m_pAppletImpl->AppendParam( rOption.GetTokenString(),
+ rOption.GetString() );
+
+ }
+
+ // Objects that are declared only are not evaluated. Moreover, only
+ // Java applets are supported.
+ bool bIsApplet = false;
+
+ if( !bDeclare && aClassID.getLength() == 42 &&
+ aClassID.startsWith("clsid:") )
+ {
+ aClassID = aClassID.copy(6);
+ SvGlobalName aCID;
+ if( aCID.MakeId( aClassID ) )
+ {
+ SvGlobalName aJavaCID( 0x8AD9C840UL, 0x044EU, 0x11D1U, 0xB3U, 0xE9U,
+ 0x00U, 0x80U, 0x5FU, 0x49U, 0x9DU, 0x93U );
+
+ bIsApplet = aJavaCID == aCID;
+ }
+ }
+
+ if( !bIsApplet )
+ {
+ m_pAppletImpl.reset();
+ return;
+ }
+
+ m_pAppletImpl->SetAltText( aStandBy );
+
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo );
+
+ SfxItemSet& rFrameSet = m_pAppletImpl->GetItemSet();
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( rFrameSet );
+
+ // set the anchor and the adjustment
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, rFrameSet );
+
+ // and still the size of the frame
+ Size aDfltSz( HTML_DFLT_APPLET_WIDTH, HTML_DFLT_APPLET_HEIGHT );
+ SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, rFrameSet );
+ SetSpace( aSpace, aItemSet, aPropInfo, rFrameSet );
+}
+#endif
+
+void SwHTMLParser::EndObject()
+{
+#if HAVE_FEATURE_JAVA
+ if( !m_pAppletImpl )
+ return;
+ if( !m_pAppletImpl->CreateApplet( m_sBaseURL ) )
+ return;
+
+ m_pAppletImpl->FinishApplet();
+
+ // and insert into the document
+ SwFrameFormat* pFlyFormat =
+ m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam,
+ ::svt::EmbeddedObjectRef( m_pAppletImpl->GetApplet(), embed::Aspects::MSOLE_CONTENT ),
+ &m_pAppletImpl->GetItemSet() );
+
+ // set the alternative name
+ SwNoTextNode *pNoTextNd =
+ m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx()
+ ->GetIndex()+1 ]->GetNoTextNode();
+ pNoTextNd->SetTitle( m_pAppletImpl->GetAltText() );
+
+ // if applicable create frames and register auto-bound frames
+ RegisterFlyFrame( pFlyFormat );
+
+ m_pAppletImpl.reset();
+#else
+ (void) this; // Silence loplugin:staticmethods
+#endif
+}
+
+#if HAVE_FEATURE_JAVA
+void SwHTMLParser::InsertApplet()
+{
+ OUString aCodeBase, aCode, aName, aAlt, aId, aStyle, aClass;
+ Size aSize( USHRT_MAX, USHRT_MAX );
+ Size aSpace( 0, 0 );
+ bool bPercentWidth = false, bPercentHeight = false, bMayScript = false;
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+
+ // create a new Command list
+ m_pAppletImpl.reset(new SwApplet_Impl( m_xDoc->GetAttrPool() ));
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::CODEBASE:
+ aCodeBase = rOption.GetString();
+ break;
+ case HtmlOptionId::CODE:
+ aCode = rOption.GetString();
+ break;
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ break;
+ case HtmlOptionId::ALT:
+ aAlt = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri );
+ eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri );
+ break;
+ case HtmlOptionId::WIDTH:
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::HEIGHT:
+ bPercentHeight = (rOption.GetString().indexOf('%') != -1);
+ aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::HSPACE:
+ aSpace.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::VSPACE:
+ aSpace.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::MAYSCRIPT:
+ bMayScript = true;
+ break;
+ default: break;
+ }
+
+ // All parameters are passed to the applet.
+ m_pAppletImpl->AppendParam( rOption.GetTokenString(),
+ rOption.GetString() );
+ }
+
+ if( aCode.isEmpty() )
+ {
+ m_pAppletImpl.reset();
+ return;
+ }
+
+ if ( !aCodeBase.isEmpty() )
+ aCodeBase = INetURLObject::GetAbsURL( m_sBaseURL, aCodeBase );
+ m_pAppletImpl->CreateApplet( aCode, aName, bMayScript, aCodeBase, m_sBaseURL );//, aAlt );
+ m_pAppletImpl->SetAltText( aAlt );
+
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo );
+
+ SfxItemSet& rFrameSet = m_pAppletImpl->GetItemSet();
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( rFrameSet );
+
+ // set the anchor and the adjustment
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, rFrameSet );
+
+ // and still the size or the frame
+ Size aDfltSz( HTML_DFLT_APPLET_WIDTH, HTML_DFLT_APPLET_HEIGHT );
+ SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, rFrameSet );
+ SetSpace( aSpace, aItemSet, aPropInfo, rFrameSet );
+}
+#endif
+
+void SwHTMLParser::EndApplet()
+{
+#if HAVE_FEATURE_JAVA
+ if( !m_pAppletImpl )
+ return;
+
+ m_pAppletImpl->FinishApplet();
+
+ // and insert into the document
+ SwFrameFormat* pFlyFormat =
+ m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam,
+ ::svt::EmbeddedObjectRef( m_pAppletImpl->GetApplet(), embed::Aspects::MSOLE_CONTENT ),
+ &m_pAppletImpl->GetItemSet());
+
+ // set the alternative name
+ SwNoTextNode *pNoTextNd =
+ m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx()
+ ->GetIndex()+1 ]->GetNoTextNode();
+ pNoTextNd->SetTitle( m_pAppletImpl->GetAltText() );
+
+ // if applicable create frames and register auto-bound frames
+ RegisterFlyFrame( pFlyFormat );
+
+ m_pAppletImpl.reset();
+#else
+ (void) this;
+#endif
+}
+
+void SwHTMLParser::InsertParam()
+{
+#if HAVE_FEATURE_JAVA
+ if( !m_pAppletImpl )
+ return;
+
+ OUString aName, aValue;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ break;
+ case HtmlOptionId::VALUE:
+ aValue = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ if( aName.isEmpty() )
+ return;
+
+ m_pAppletImpl->AppendParam( aName, aValue );
+#else
+ (void) this;
+#endif
+}
+
+void SwHTMLParser::InsertFloatingFrame()
+{
+ OUString aAlt, aId, aStyle, aClass;
+ Size aSize( USHRT_MAX, USHRT_MAX );
+ Size aSpace( 0, 0 );
+ bool bPercentWidth = false, bPercentHeight = false;
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+
+ // First fetch the options of the Writer-Frame-Format
+ for (const auto & rOption : rHTMLOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::ALT:
+ aAlt = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri );
+ eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri );
+ break;
+ case HtmlOptionId::WIDTH:
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::HEIGHT:
+ bPercentHeight = (rOption.GetString().indexOf('%') != -1);
+ aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::HSPACE:
+ aSpace.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::VSPACE:
+ aSpace.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ default: break;
+ }
+ }
+
+ // and now the ones for the SfxFrame
+ SfxFrameDescriptor aFrameDesc;
+
+ SfxFrameHTMLParser::ParseFrameOptions( &aFrameDesc, rHTMLOptions, m_sBaseURL );
+
+ // create a floating frame
+ comphelper::EmbeddedObjectContainer aCnt;
+ OUString aObjName;
+ uno::Reference < embed::XEmbeddedObject > xObj = aCnt.CreateEmbeddedObject( SvGlobalName( SO3_IFRAME_CLASSID ).GetByteSequence(), aObjName );
+
+ try
+ {
+ // TODO/MBA: testing
+ if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ const OUString& aName = aFrameDesc.GetName();
+ ScrollingMode eScroll = aFrameDesc.GetScrollingMode();
+ bool bHasBorder = aFrameDesc.HasFrameBorder();
+ Size aMargin = aFrameDesc.GetMargin();
+
+ OUString sHRef = aFrameDesc.GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if (INetURLObject(sHRef).IsExoticProtocol())
+ NotifyMacroEventRead();
+
+ xSet->setPropertyValue("FrameURL", uno::Any( sHRef ) );
+ xSet->setPropertyValue("FrameName", uno::Any( aName ) );
+
+ if ( eScroll == ScrollingMode::Auto )
+ xSet->setPropertyValue("FrameIsAutoScroll",
+ uno::Any( true ) );
+ else
+ xSet->setPropertyValue("FrameIsScrollingMode",
+ uno::Any( eScroll == ScrollingMode::Yes ) );
+
+ xSet->setPropertyValue("FrameIsBorder",
+ uno::Any( bHasBorder ) );
+
+ xSet->setPropertyValue("FrameMarginWidth",
+ uno::Any( sal_Int32( aMargin.Width() ) ) );
+
+ xSet->setPropertyValue("FrameMarginHeight",
+ uno::Any( sal_Int32( aMargin.Height() ) ) );
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo );
+
+ // fetch the ItemSet
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( aFrameSet );
+
+ // set the anchor and the adjustment
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, aFrameSet );
+
+ // and still the size of the frame
+ Size aDfltSz( HTML_DFLT_APPLET_WIDTH, HTML_DFLT_APPLET_HEIGHT );
+ SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, aFrameSet );
+ SetSpace( aSpace, aItemSet, aPropInfo, aFrameSet );
+
+ // and insert into the document
+ SwFrameFormat* pFlyFormat =
+ m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam,
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ &aFrameSet);
+
+ // set the alternative name
+ SwNoTextNode *pNoTextNd =
+ m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx()
+ ->GetIndex()+1 ]->GetNoTextNode();
+ pNoTextNd->SetTitle( aAlt );
+
+ // if applicable create frames and register auto-bound frames
+ RegisterFlyFrame( pFlyFormat );
+
+ m_bInFloatingFrame = true;
+
+ ++m_nFloatingFrames;
+}
+
+SwHTMLFrameType SwHTMLWriter::GuessOLENodeFrameType( const SwNode& rNode )
+{
+ SwHTMLFrameType eType = HTML_FRMTYPE_OLE;
+
+ SwOLENode* pOLENode = const_cast<SwOLENode*>(rNode.GetOLENode());
+ assert(pOLENode && "must exist");
+ SwOLEObj& rObj = pOLENode->GetOLEObj();
+
+ uno::Reference < embed::XClassifiedObject > xClass = rObj.GetOleRef();
+ SvGlobalName aClass( xClass->getClassID() );
+ if( aClass == SvGlobalName( SO3_PLUGIN_CLASSID ) )
+ {
+ eType = HTML_FRMTYPE_PLUGIN;
+ }
+ else if( aClass == SvGlobalName( SO3_IFRAME_CLASSID ) )
+ {
+ eType = HTML_FRMTYPE_IFRAME;
+ }
+#if HAVE_FEATURE_JAVA
+ else if( aClass == SvGlobalName( SO3_APPLET_CLASSID ) )
+ {
+ eType = HTML_FRMTYPE_APPLET;
+ }
+#endif
+
+ return eType;
+}
+
+SwHTMLWriter& OutHTML_FrameFormatOLENode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ bool bInCntnr )
+{
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
+ SwOLENode *pOLENd = rWrt.m_pDoc->GetNodes()[ nStt ]->GetOLENode();
+
+ OSL_ENSURE( pOLENd, "OLE-Node expected" );
+ if( !pOLENd )
+ return rWrt;
+
+ SwOLEObj &rObj = pOLENd->GetOLEObj();
+
+ uno::Reference < embed::XEmbeddedObject > xObj( rObj.GetOleRef() );
+ if ( !svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ return rWrt;
+
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ bool bHiddenEmbed = false;
+
+ if( !xSet.is() )
+ {
+ OSL_FAIL("Unknown Object" );
+ return rWrt;
+ }
+
+ HtmlFrmOpts nFrameOpts;
+
+ // if possible output a line break before the "object"
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine( true );
+
+ if( !rFrameFormat.GetName().isEmpty() )
+ rWrt.OutImplicitMark( rFrameFormat.GetName(),
+ "ole" );
+ uno::Any aAny;
+ SvGlobalName aGlobName( xObj->getClassID() );
+ OStringBuffer sOut("<");
+ if( aGlobName == SvGlobalName( SO3_PLUGIN_CLASSID ) )
+ {
+ // first the plug-in specifics
+ sOut.append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_embed);
+
+ OUString aStr;
+ OUString aURL;
+ aAny = xSet->getPropertyValue("PluginURL");
+ if( (aAny >>= aStr) && !aStr.isEmpty() )
+ {
+ aURL = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(),
+ aStr);
+ }
+
+ if( !aURL.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_src "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aURL );
+ sOut.append('\"');
+ }
+
+ OUString aType;
+ aAny = xSet->getPropertyValue("PluginMimeType");
+ if( (aAny >>= aType) && !aType.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_type "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aType );
+ sOut.append('\"');
+ }
+
+ if ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) &&
+ css::text::WrapTextMode_THROUGH == rFrameFormat.GetSurround().GetSurround() )
+ {
+ // A HIDDEN plug-in
+ sOut.append(" " OOO_STRING_SW_HTML_O_Hidden);
+ nFrameOpts = HTML_FRMOPTS_HIDDEN_EMBED;
+ bHiddenEmbed = true;
+ }
+ else
+ {
+ nFrameOpts = bInCntnr ? HTML_FRMOPTS_EMBED_CNTNR
+ : HTML_FRMOPTS_EMBED;
+ }
+ }
+ else if( aGlobName == SvGlobalName( SO3_APPLET_CLASSID ) )
+ {
+ // or the applet specifics
+
+ sOut.append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_applet);
+
+ // CODEBASE
+ OUString aCd;
+ aAny = xSet->getPropertyValue("AppletCodeBase");
+ if( (aAny >>= aCd) && !aCd.isEmpty() )
+ {
+ OUString sCodeBase( URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aCd) );
+ if( !sCodeBase.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_codebase "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), sCodeBase );
+ sOut.append('\"');
+ }
+ }
+
+ // CODE
+ OUString aClass;
+ aAny = xSet->getPropertyValue("AppletCode");
+ aAny >>= aClass;
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_code "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aClass );
+ sOut.append('\"');
+
+ // NAME
+ OUString aAppletName;
+ aAny = xSet->getPropertyValue("AppletName");
+ aAny >>= aAppletName;
+ if( !aAppletName.isEmpty() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), aAppletName );
+ sOut.append('\"');
+ }
+
+ bool bScript = false;
+ aAny = xSet->getPropertyValue("AppletIsScript");
+ aAny >>= bScript;
+ if( bScript )
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_mayscript);
+
+ nFrameOpts = bInCntnr ? HTML_FRMOPTS_APPLET_CNTNR
+ : HTML_FRMOPTS_APPLET;
+ }
+ else
+ {
+ // or the Floating-Frame specifics
+
+ sOut.append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_iframe);
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ SfxFrameHTMLWriter::Out_FrameDescriptor( rWrt.Strm(), rWrt.GetBaseURL(),
+ xSet );
+
+ nFrameOpts = bInCntnr ? HTML_FRMOPTS_IFRAME_CNTNR
+ : HTML_FRMOPTS_IFRAME;
+ }
+
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ // ALT, WIDTH, HEIGHT, HSPACE, VSPACE, ALIGN
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bHiddenEmbed )
+ nFrameOpts |= HTML_FRMOPTS_OLE_CSS1;
+ OString aEndTags = rWrt.OutFrameFormatOptions( rFrameFormat, pOLENd->GetTitle(), nFrameOpts );
+ if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bHiddenEmbed )
+ rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts );
+
+ if( aGlobName == SvGlobalName( SO3_APPLET_CLASSID ) )
+ {
+ // output the parameters of applets as separate tags
+ // and write a </APPLET>
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ aAny = xSet->getPropertyValue("AppletCommands");
+ aAny >>= aProps;
+
+ SvCommandList aCommands;
+ aCommands.FillFromSequence( aProps );
+ std::vector<sal_uLong> aParams;
+ size_t i = aCommands.size();
+ while( i > 0 )
+ {
+ const SvCommand& rCommand = aCommands[ --i ];
+ const OUString& rName = rCommand.GetCommand();
+ SwHtmlOptType nType = SwApplet_Impl::GetOptionType( rName, true );
+ if( SwHtmlOptType::TAG == nType )
+ {
+ const OUString& rValue = rCommand.GetArgument();
+ rWrt.Strm().WriteChar( ' ' );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rName );
+ rWrt.Strm().WriteOString( "=\"" );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rValue ).WriteChar( '\"' );
+ }
+ else if( SwHtmlOptType::PARAM == nType )
+ {
+ aParams.push_back( i );
+ }
+ }
+
+ rWrt.Strm().WriteChar( '>' );
+
+ rWrt.IncIndentLevel(); // indent the applet content
+
+ size_t ii = aParams.size();
+ while( ii > 0 )
+ {
+ const SvCommand& rCommand = aCommands[ aParams[--ii] ];
+ const OUString& rName = rCommand.GetCommand();
+ const OUString& rValue = rCommand.GetArgument();
+ rWrt.OutNewLine();
+ sOut.append(
+ "<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_param
+ " " OOO_STRING_SVTOOLS_HTML_O_name
+ "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rName );
+ sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_value "=\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rValue ).WriteOString( "\">" );
+ }
+
+ rWrt.DecIndentLevel(); // indent the applet content
+ if( aCommands.size() )
+ rWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_applet), false );
+ }
+ else if( aGlobName == SvGlobalName( SO3_PLUGIN_CLASSID ) )
+ {
+ // write plug-ins parameters as options
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ aAny = xSet->getPropertyValue("PluginCommands");
+ aAny >>= aProps;
+
+ SvCommandList aCommands;
+ aCommands.FillFromSequence( aProps );
+ for( size_t i = 0; i < aCommands.size(); i++ )
+ {
+ const SvCommand& rCommand = aCommands[ i ];
+ const OUString& rName = rCommand.GetCommand();
+
+ if( SwApplet_Impl::GetOptionType( rName, false ) == SwHtmlOptType::TAG )
+ {
+ const OUString& rValue = rCommand.GetArgument();
+ rWrt.Strm().WriteChar( ' ' );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rName );
+ rWrt.Strm().WriteOString( "=\"" );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), rValue ).WriteChar( '\"' );
+ }
+ }
+ rWrt.Strm().WriteChar( '>' );
+ }
+ else
+ {
+ // and for Floating-Frames just output another </IFRAME>
+
+ rWrt.Strm().WriteChar( '>' );
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_iframe), false );
+ }
+
+ if( !aEndTags.isEmpty() )
+ rWrt.Strm().WriteOString( aEndTags );
+
+ return rWrt;
+}
+
+static void OutHTMLGraphic(SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat, SwOLENode* pOLENd,
+ const Graphic& rGraphic, bool bObjectOpened, bool bInCntnr)
+{
+ OUString aGraphicURL;
+ OUString aMimeType;
+ if (!rWrt.mbEmbedImages)
+ {
+ const OUString* pTempFileName = rWrt.GetOrigFileName();
+ if (pTempFileName)
+ aGraphicURL = *pTempFileName;
+
+ OUString aFilterName(u"JPG"_ustr);
+ XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
+
+ if (bObjectOpened)
+ {
+ aFilterName = u"PNG"_ustr;
+ nFlags = XOutFlags::NONE;
+ aMimeType = u"image/png"_ustr;
+
+ if (rGraphic.GetType() == GraphicType::NONE)
+ {
+ // The OLE Object has no replacement image, write a stub.
+ aGraphicURL = lcl_CalculateFileName(rWrt.GetOrigFileName(), rGraphic, u"png");
+ osl::File aFile(aGraphicURL);
+ aFile.open(osl_File_OpenFlag_Create);
+ aFile.close();
+ }
+ }
+
+ ErrCode nErr = XOutBitmap::WriteGraphic(rGraphic, aGraphicURL, aFilterName, nFlags);
+ if (nErr) // error, don't write anything
+ {
+ rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
+ if (bObjectOpened) // Still at least close the tag.
+ rWrt.Strm().WriteOString(
+ Concat2View("</" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
+ return;
+ }
+ aGraphicURL = URIHelper::SmartRel2Abs(INetURLObject(rWrt.GetBaseURL()), aGraphicURL,
+ URIHelper::GetMaybeFileHdl());
+ }
+ HtmlFrmOpts nFlags = bInCntnr ? HtmlFrmOpts::GenImgAllMask : HtmlFrmOpts::GenImgMask;
+ if (bObjectOpened)
+ nFlags |= HtmlFrmOpts::Replacement;
+ HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
+ OutHTML_ImageStart(aHtml, rWrt, rFrameFormat, aGraphicURL, rGraphic, pOLENd->GetTitle(),
+ pOLENd->GetTwipSize(), nFlags, "ole", nullptr, aMimeType);
+ OutHTML_ImageEnd(aHtml, rWrt);
+}
+
+static void OutHTMLStartObject(SwHTMLWriter& rWrt, const OUString& rFileName, const OUString& rFileType)
+{
+ OUString aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), rFileName);
+
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+ rWrt.Strm().WriteOString(
+ Concat2View("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object));
+ rWrt.Strm().WriteOString(Concat2View(" data=\"" + aFileName.toUtf8() + "\""));
+ if (!rFileType.isEmpty())
+ rWrt.Strm().WriteOString(Concat2View(" type=\"" + rFileType.toUtf8() + "\""));
+ rWrt.Strm().WriteOString(">");
+ rWrt.SetLFPossible(true);
+}
+
+static void OutHTMLEndObject(SwHTMLWriter& rWrt)
+{
+ rWrt.Strm().WriteOString(
+ Concat2View("</" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
+}
+
+static bool TrySaveFormulaAsPDF(SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ SwOLENode* pOLENd, bool bWriteReplacementGraphic, bool bInCntnr)
+{
+ if (!rWrt.mbReqIF)
+ return false;
+ if (!rWrt.m_bExportFormulasAsPDF)
+ return false;
+
+ auto xTextContent = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *rWrt.m_pDoc, const_cast<SwFrameFormat*>(&rFrameFormat));
+ uno::Reference<frame::XStorable> xStorable(xTextContent->getEmbeddedObject(), uno::UNO_QUERY);
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xStorable, uno::UNO_QUERY);
+ if (!xServiceInfo)
+ return false;
+ if (!xServiceInfo->supportsService(u"com.sun.star.formula.FormulaProperties"_ustr))
+ return false;
+
+ Graphic aGraphic(xTextContent->getReplacementGraphic());
+ OUString aFileName = lcl_CalculateFileName(rWrt.GetOrigFileName(), aGraphic, u"pdf");
+
+ utl::MediaDescriptor aDescr;
+ aDescr[u"FilterName"_ustr] <<= u"math_pdf_Export"_ustr;
+ // Properties from starmath/inc/unomodel.hxx
+ aDescr[u"FilterData"_ustr] <<= comphelper::InitPropertySequence({
+ { u"TitleRow"_ustr, css::uno::Any(false) },
+ { u"FormulaText"_ustr, css::uno::Any(false) },
+ { u"Border"_ustr, css::uno::Any(false) },
+ { u"PrintFormat"_ustr, css::uno::Any(sal_Int32(1)) }, // PRINT_SIZE_SCALED
+ });
+ xStorable->storeToURL(aFileName, aDescr.getAsConstPropertyValueList());
+
+ OutHTMLStartObject(rWrt, aFileName, u"application/pdf"_ustr);
+
+ if (bWriteReplacementGraphic)
+ OutHTMLGraphic(rWrt, rFrameFormat, pOLENd, aGraphic, true, bInCntnr);
+
+ OutHTMLEndObject(rWrt);
+
+ return true;
+}
+
+SwHTMLWriter& OutHTML_FrameFormatOLENodeGrf( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ bool bInCntnr, bool bWriteReplacementGraphic )
+{
+ const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
+ SwOLENode *pOLENd = rWrt.m_pDoc->GetNodes()[ nStt ]->GetOLENode();
+
+ OSL_ENSURE( pOLENd, "OLE-Node expected" );
+ if( !pOLENd )
+ return rWrt;
+
+ if (rWrt.mbSkipImages)
+ {
+ // If we skip images, embedded objects would be completely lost.
+ // Instead, try to use the HTML export of the embedded object.
+ auto xTextContent = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(*rWrt.m_pDoc, const_cast<SwFrameFormat*>(&rFrameFormat));
+ uno::Reference<frame::XStorable> xStorable(xTextContent->getEmbeddedObject(), uno::UNO_QUERY);
+ SAL_WARN_IF(!xStorable.is(), "sw.html", "OutHTML_FrameFormatOLENodeGrf: no embedded object");
+
+ // Figure out what is the filter name of the embedded object.
+ OUString aFilter;
+ if (uno::Reference<lang::XServiceInfo> xServiceInfo{ xStorable, uno::UNO_QUERY })
+ {
+ if (xServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+ aFilter = "HTML (StarCalc)";
+ else if (xServiceInfo->supportsService("com.sun.star.text.TextDocument"))
+ aFilter = "HTML (StarWriter)";
+ }
+
+ if (!aFilter.isEmpty())
+ {
+ try
+ {
+ // FIXME: exception for the simplest test document, too
+ SvMemoryStream aStream;
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream));
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= aFilter;
+ aMediaDescriptor["FilterOptions"] <<= OUString("SkipHeaderFooter");
+ aMediaDescriptor["OutputStream"] <<= xOutputStream;
+ xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
+ SAL_WARN_IF(aStream.GetSize()>=o3tl::make_unsigned(SAL_MAX_INT32), "sw.html", "Stream can't fit in OString");
+ OString aData(static_cast<const char*>(aStream.GetData()), static_cast<sal_Int32>(aStream.GetSize()));
+ // Wrap output in a <span> tag to avoid 'HTML parser error: Unexpected end tag: p'
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span));
+ rWrt.Strm().WriteOString(aData);
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false);
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ return rWrt;
+ }
+
+ if (TrySaveFormulaAsPDF(rWrt, rFrameFormat, pOLENd, bWriteReplacementGraphic, bInCntnr))
+ return rWrt;
+
+ if ( !pOLENd->GetGraphic() )
+ {
+ SAL_WARN("sw.html", "Unexpected missing OLE fallback graphic");
+ return rWrt;
+ }
+
+ Graphic aGraphic( *pOLENd->GetGraphic() );
+
+ SwDocShell* pDocSh = rWrt.m_pDoc->GetDocShell();
+ bool bObjectOpened = false;
+ OUString aRTFType = "text/rtf";
+ if (!rWrt.m_aRTFOLEMimeType.isEmpty())
+ {
+ aRTFType = rWrt.m_aRTFOLEMimeType;
+ }
+
+ if (rWrt.mbXHTML && pDocSh)
+ {
+ // Map native data to an outer <object> element.
+
+ // Calculate the file name, which is meant to be the same as the
+ // replacement image, just with a .ole extension.
+ OUString aFileName = lcl_CalculateFileName(rWrt.GetOrigFileName(), aGraphic, u"ole");
+
+ // Write the data.
+ SwOLEObj& rOLEObj = pOLENd->GetOLEObj();
+ uno::Reference<embed::XEmbeddedObject> xEmbeddedObject = rOLEObj.GetOleRef();
+ OUString aFileType;
+ SvFileStream aOutStream(aFileName, StreamMode::WRITE);
+ uno::Reference<io::XActiveDataStreamer> xStreamProvider;
+ uno::Reference<embed::XEmbedPersist2> xOwnEmbedded;
+ if (xEmbeddedObject.is())
+ {
+ xStreamProvider.set(xEmbeddedObject, uno::UNO_QUERY);
+ xOwnEmbedded.set(xEmbeddedObject, uno::UNO_QUERY);
+ }
+ if (xStreamProvider.is())
+ {
+ // Real OLE2 case: OleEmbeddedObject.
+ uno::Reference<io::XInputStream> xStream(xStreamProvider->getStream(), uno::UNO_QUERY);
+ if (xStream.is())
+ {
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xStream));
+ if (SwReqIfReader::WrapOleInRtf(*pStream, aOutStream, *pOLENd, rFrameFormat))
+ {
+ // Data always wrapped in RTF.
+ aFileType = aRTFType;
+ }
+ }
+ }
+ else if (xOwnEmbedded.is())
+ {
+ // Our own embedded object: OCommonEmbeddedObject.
+ SvxMSExportOLEObjects aOLEExp(0);
+ // Trigger the load of the OLE object if needed, otherwise we can't
+ // export it.
+ pOLENd->GetTwipSize();
+ SvMemoryStream aMemory;
+ tools::SvRef<SotStorage> pStorage = new SotStorage(aMemory);
+ aOLEExp.ExportOLEObject(rOLEObj.GetObject(), *pStorage);
+ pStorage->Commit();
+ aMemory.Seek(0);
+ if (SwReqIfReader::WrapOleInRtf(aMemory, aOutStream, *pOLENd, rFrameFormat))
+ {
+ // Data always wrapped in RTF.
+ aFileType = aRTFType;
+ }
+ }
+ else
+ {
+ // Otherwise the native data is just a grab-bag: ODummyEmbeddedObject.
+ const OUString& aStreamName = rOLEObj.GetCurrentPersistName();
+ uno::Reference<embed::XStorage> xStorage = pDocSh->GetStorage();
+ uno::Reference<io::XStream> xInStream;
+ try
+ {
+ // Even the native data may be missing.
+ xInStream = xStorage->openStreamElement(aStreamName, embed::ElementModes::READ);
+ } catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.html", "OutHTML_FrameFormatOLENodeGrf: failed to open stream element");
+ }
+ if (xInStream.is())
+ {
+ uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream));
+ comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(),
+ xOutStream->getOutputStream());
+ }
+
+ uno::Reference<beans::XPropertySet> xOutStreamProps(xInStream, uno::UNO_QUERY);
+ if (xOutStreamProps.is())
+ xOutStreamProps->getPropertyValue("MediaType") >>= aFileType;
+ if (!aRTFType.isEmpty())
+ {
+ aFileType = aRTFType;
+ }
+ }
+
+ // Refer to this data.
+ OutHTMLStartObject(rWrt, aFileName, aFileType);
+ bObjectOpened = true;
+ }
+
+ if (!bObjectOpened || bWriteReplacementGraphic)
+ OutHTMLGraphic(rWrt, rFrameFormat, pOLENd, aGraphic, bObjectOpened, bInCntnr);
+
+ if (bObjectOpened)
+ // Close native data.
+ OutHTMLEndObject(rWrt);
+
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlreqifreader.cxx b/sw/source/filter/html/htmlreqifreader.cxx
new file mode 100644
index 0000000000..8df0d5b483
--- /dev/null
+++ b/sw/source/filter/html/htmlreqifreader.cxx
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "htmlreqifreader.hxx"
+
+#include <comphelper/scopeguard.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+#include <rtl/strbuf.hxx>
+#include <sot/storage.hxx>
+#include <svtools/parrtf.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <svtools/rtftoken.h>
+#include <tools/stream.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <ndole.hxx>
+#include <sal/log.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <vcl/wmf.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <fmtfsize.hxx>
+#include <frmfmt.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/// RTF parser that just extracts a single OLE2 object from a file.
+class ReqIfRtfReader : public SvRTFParser
+{
+public:
+ ReqIfRtfReader(SvStream& rStream);
+ void NextToken(int nToken) override;
+ bool WriteObjectData(SvStream& rOLE);
+
+private:
+ bool m_bInObjData = false;
+ OStringBuffer m_aHex;
+};
+
+ReqIfRtfReader::ReqIfRtfReader(SvStream& rStream)
+ : SvRTFParser(rStream)
+{
+}
+
+void ReqIfRtfReader::NextToken(int nToken)
+{
+ switch (nToken)
+ {
+ case '}':
+ m_bInObjData = false;
+ break;
+ case RTF_TEXTTOKEN:
+ if (m_bInObjData)
+ m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US));
+ break;
+ case RTF_OBJDATA:
+ m_bInObjData = true;
+ break;
+ }
+}
+
+bool ReqIfRtfReader::WriteObjectData(SvStream& rOLE)
+{
+ return msfilter::rtfutil::ExtractOLE2FromObjdata(m_aHex.makeStringAndClear(), rOLE);
+}
+
+/// Looks up what OLE1 calls the ClassName, see [MS-OLEDS] 2.3.8 CompObjStream.
+OString ExtractOLEClassName(const tools::SvRef<SotStorage>& xStorage)
+{
+ OString aRet;
+
+ tools::SvRef<SotStorageStream> pCompObj = xStorage->OpenSotStream("\1CompObj");
+ if (!pCompObj)
+ return aRet;
+
+ pCompObj->Seek(0);
+ pCompObj->SeekRel(28); // Header
+ if (!pCompObj->good())
+ return aRet;
+
+ sal_uInt32 nData;
+ pCompObj->ReadUInt32(nData); // AnsiUserType
+ pCompObj->SeekRel(nData);
+ if (!pCompObj->good())
+ return aRet;
+
+ pCompObj->ReadUInt32(nData); // AnsiClipboardFormat
+ pCompObj->SeekRel(nData);
+ if (!pCompObj->good())
+ return aRet;
+
+ pCompObj->ReadUInt32(nData); // Reserved1
+ return read_uInt8s_ToOString(*pCompObj, nData - 1); // -1 because it is null-terminated
+}
+
+/// Parses the presentation stream of an OLE2 storage.
+bool ParseOLE2Presentation(SvStream& rOle2, sal_uInt32& nWidth, sal_uInt32& nHeight,
+ SvStream& rPresentationData)
+{
+ // See [MS-OLEDS] 2.3.4, OLEPresentationStream
+ rOle2.Seek(0);
+ tools::SvRef<SotStorage> pStorage = new SotStorage(rOle2);
+ tools::SvRef<SotStorageStream> xOle2Presentation
+ = pStorage->OpenSotStream("\002OlePres000", StreamMode::STD_READ);
+
+ // Read AnsiClipboardFormat.
+ sal_uInt32 nMarkerOrLength = 0;
+ xOle2Presentation->ReadUInt32(nMarkerOrLength);
+ if (nMarkerOrLength != 0xffffffff)
+ // FormatOrAnsiString is not present
+ return false;
+ sal_uInt32 nFormatOrAnsiLength = 0;
+ xOle2Presentation->ReadUInt32(nFormatOrAnsiLength);
+ if (nFormatOrAnsiLength != 0x00000003) // CF_METAFILEPICT
+ return false;
+
+ // Read TargetDeviceSize.
+ sal_uInt32 nTargetDeviceSize = 0;
+ xOle2Presentation->ReadUInt32(nTargetDeviceSize);
+ if (nTargetDeviceSize != 0x00000004)
+ // TargetDevice is present
+ return false;
+
+ sal_uInt32 nAspect = 0;
+ xOle2Presentation->ReadUInt32(nAspect);
+ sal_uInt32 nLindex = 0;
+ xOle2Presentation->ReadUInt32(nLindex);
+ sal_uInt32 nAdvf = 0;
+ xOle2Presentation->ReadUInt32(nAdvf);
+ sal_uInt32 nReserved1 = 0;
+ xOle2Presentation->ReadUInt32(nReserved1);
+ xOle2Presentation->ReadUInt32(nWidth);
+ xOle2Presentation->ReadUInt32(nHeight);
+ sal_uInt32 nSize = 0;
+ xOle2Presentation->ReadUInt32(nSize);
+
+ // Read Data.
+ if (nSize > xOle2Presentation->remainingSize())
+ return false;
+
+ if (nSize <= 64)
+ {
+ SAL_WARN("sw.html",
+ "ParseOLE2Presentation: ignoring potentially broken small preview: size is "
+ << nSize);
+ return false;
+ }
+
+ std::vector<char> aBuffer(nSize);
+ xOle2Presentation->ReadBytes(aBuffer.data(), aBuffer.size());
+ rPresentationData.WriteBytes(aBuffer.data(), aBuffer.size());
+
+ return true;
+}
+
+/**
+ * Inserts an OLE1 header before an OLE2 storage, assuming that the storage has an Ole10Native
+ * stream.
+ */
+OString InsertOLE1HeaderFromOle10NativeStream(const tools::SvRef<SotStorage>& xStorage,
+ SwOLENode& rOLENode, SvStream& rOle1)
+{
+ tools::SvRef<SotStorageStream> xOle1Stream
+ = xStorage->OpenSotStream("\1Ole10Native", StreamMode::STD_READ);
+ sal_uInt32 nOle1Size = 0;
+ xOle1Stream->ReadUInt32(nOle1Size);
+
+ OString aClassName;
+ if (xStorage->GetClassName() == SvGlobalName(0x0003000A, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46))
+ {
+ aClassName = "PBrush"_ostr;
+ }
+ else
+ {
+ if (xStorage->GetClassName()
+ != SvGlobalName(0x0003000C, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46))
+ {
+ SAL_WARN("sw.html", "InsertOLE1HeaderFromOle10NativeStream: unexpected class id: "
+ << xStorage->GetClassName().GetHexName());
+ }
+ aClassName = "Package"_ostr;
+ }
+
+ // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
+ rOle1.Seek(0);
+ // OLEVersion.
+ rOle1.WriteUInt32(0x00000501);
+
+ // FormatID is EmbeddedObject.
+ rOle1.WriteUInt32(0x00000002);
+
+ // ClassName
+ rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1);
+ if (!aClassName.isEmpty())
+ {
+ rOle1.WriteOString(aClassName);
+ // Null terminated pascal string.
+ rOle1.WriteChar(0);
+ }
+
+ // TopicName.
+ rOle1.WriteUInt32(0);
+
+ // ItemName.
+ rOle1.WriteUInt32(0);
+
+ // NativeDataSize
+ rOle1.WriteUInt32(nOle1Size);
+
+ // Write the actual native data.
+ rOle1.WriteStream(*xOle1Stream, nOle1Size);
+
+ // Write Presentation.
+ if (!rOLENode.GetGraphic())
+ {
+ return aClassName;
+ }
+
+ const Graphic& rGraphic = *rOLENode.GetGraphic();
+ Size aSize = rOLENode.GetTwipSize();
+ SvMemoryStream aGraphicStream;
+ if (GraphicConverter::Export(aGraphicStream, rGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
+ {
+ return aClassName;
+ }
+
+ auto pGraphicAry = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
+ sal_uInt64 nPresentationData = aGraphicStream.TellEnd();
+ msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nPresentationData);
+
+ // OLEVersion.
+ rOle1.WriteUInt32(0x00000501);
+ // FormatID: constant means the ClassName field is present.
+ rOle1.WriteUInt32(0x00000005);
+ // ClassName: null terminated pascal string.
+ OString aPresentationClassName("METAFILEPICT"_ostr);
+ rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
+ rOle1.WriteOString(aPresentationClassName);
+ rOle1.WriteChar(0);
+ // Width.
+ rOle1.WriteUInt32(aSize.getWidth());
+ // Height.
+ rOle1.WriteUInt32(aSize.getHeight() * -1);
+ // PresentationDataSize
+ rOle1.WriteUInt32(8 + nPresentationData);
+ // Reserved1-4.
+ rOle1.WriteUInt16(0x0008);
+ rOle1.WriteUInt16(0x31b1);
+ rOle1.WriteUInt16(0x1dd9);
+ rOle1.WriteUInt16(0x0000);
+ rOle1.WriteBytes(pGraphicAry, nPresentationData);
+
+ return aClassName;
+}
+
+/**
+ * Writes an OLE1 header and data from rOle2 to rOle1.
+ *
+ * In case rOle2 has presentation data, then its size is written to nWidth/nHeight. Otherwise
+ * nWidth/nHeight/pPresentationData/nPresentationData is used for the presentation data.
+ */
+OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1, sal_uInt32& nWidth, sal_uInt32& nHeight,
+ SwOLENode& rOLENode, const sal_uInt8* pPresentationData,
+ sal_uInt64 nPresentationData)
+{
+ rOle2.Seek(0);
+ tools::SvRef<SotStorage> xStorage(new SotStorage(rOle2));
+ if (xStorage->GetError() != ERRCODE_NONE)
+ return {};
+
+ if (xStorage->IsStream("\1Ole10Native"))
+ {
+ return InsertOLE1HeaderFromOle10NativeStream(xStorage, rOLENode, rOle1);
+ }
+
+ OString aClassName = ExtractOLEClassName(xStorage);
+
+ // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
+ rOle1.Seek(0);
+ // OLEVersion.
+ rOle1.WriteUInt32(0x00000501);
+
+ // FormatID is EmbeddedObject.
+ rOle1.WriteUInt32(0x00000002);
+
+ // ClassName
+ rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1);
+ if (!aClassName.isEmpty())
+ {
+ rOle1.WriteOString(aClassName);
+ // Null terminated pascal string.
+ rOle1.WriteChar(0);
+ }
+
+ // TopicName.
+ rOle1.WriteUInt32(0);
+
+ // ItemName.
+ rOle1.WriteUInt32(0);
+
+ // NativeDataSize
+ rOle1.WriteUInt32(rOle2.TellEnd());
+
+ // Write the actual native data.
+ rOle2.Seek(0);
+ rOle1.WriteStream(rOle2);
+
+ // Write Presentation.
+ SvMemoryStream aPresentationData;
+ // OLEVersion.
+ rOle1.WriteUInt32(0x00000501);
+ // FormatID: constant means the ClassName field is present.
+ rOle1.WriteUInt32(0x00000005);
+ // ClassName: null terminated pascal string.
+ OString aPresentationClassName("METAFILEPICT"_ostr);
+ rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
+ rOle1.WriteOString(aPresentationClassName);
+ rOle1.WriteChar(0);
+ const sal_uInt8* pBytes = nullptr;
+ sal_uInt64 nBytes = 0;
+ if (ParseOLE2Presentation(rOle2, nWidth, nHeight, aPresentationData))
+ {
+ // Take presentation data for OLE1 from OLE2.
+ pBytes = static_cast<const sal_uInt8*>(aPresentationData.GetData());
+ nBytes = aPresentationData.Tell();
+ }
+ else
+ {
+ // Take presentation data for OLE1 from RTF.
+ pBytes = pPresentationData;
+ nBytes = nPresentationData;
+ }
+ // Width.
+ rOle1.WriteUInt32(nWidth);
+ // Height.
+ rOle1.WriteUInt32(nHeight * -1);
+ // PresentationDataSize: size of (reserved fields + pBytes).
+ rOle1.WriteUInt32(8 + nBytes);
+ // Reserved1-4.
+ rOle1.WriteUInt16(0x0008);
+ rOle1.WriteUInt16(0x31b1);
+ rOle1.WriteUInt16(0x1dd9);
+ rOle1.WriteUInt16(0x0000);
+ rOle1.WriteBytes(pBytes, nBytes);
+
+ return aClassName;
+}
+
+/// Writes presentation data with the specified size to rRtf as an RTF hexdump.
+void WrapOleGraphicInRtf(SvStream& rRtf, sal_uInt32 nWidth, sal_uInt32 nHeight,
+ const sal_uInt8* pPresentationData, sal_uInt64 nPresentationData)
+{
+ // Start result.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_RESULT);
+
+ // Start pict.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT);
+
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_WMETAFILE "8");
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICW);
+ rRtf.WriteOString(OString::number(nWidth));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICH);
+ rRtf.WriteOString(OString::number(nHeight));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
+ rRtf.WriteOString(OString::number(nWidth));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
+ rRtf.WriteOString(OString::number(nHeight));
+ if (pPresentationData)
+ {
+ rRtf.WriteOString(SAL_NEWLINE_STRING);
+ msfilter::rtfutil::WriteHex(pPresentationData, nPresentationData, &rRtf);
+ }
+
+ // End pict.
+ rRtf.WriteOString("}");
+
+ // End result.
+ rRtf.WriteOString("}");
+}
+}
+
+namespace SwReqIfReader
+{
+bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle, bool& bOwnFormat)
+{
+ // Add missing header/footer.
+ SvMemoryStream aRtf;
+ aRtf.WriteOString("{\\rtf1");
+ aRtf.WriteStream(rRtf);
+ aRtf.WriteOString("}");
+ aRtf.Seek(0);
+
+ // Read the RTF markup.
+ tools::SvRef<ReqIfRtfReader> xReader(new ReqIfRtfReader(aRtf));
+ SvParserState eState = xReader->CallParser();
+ if (eState == SvParserState::Error)
+ return false;
+
+ // Write the OLE2 data.
+ if (!xReader->WriteObjectData(rOle))
+ return false;
+
+ tools::SvRef<SotStorage> pStorage = new SotStorage(rOle);
+ OUString aFilterName = SvxMSDffManager::GetFilterNameFromClassID(pStorage->GetClassName());
+ bOwnFormat = !aFilterName.isEmpty();
+ if (!bOwnFormat)
+ {
+ // Real OLE2 data, we're done.
+ rOle.Seek(0);
+ return true;
+ }
+
+ // ODF-in-OLE2 case, extract actual data.
+ SvMemoryStream aMemory;
+ SvxMSDffManager::ExtractOwnStream(*pStorage, aMemory);
+ rOle.Seek(0);
+ aMemory.Seek(0);
+ rOle.WriteStream(aMemory);
+ // Stream length is current position + 1.
+ rOle.SetStreamSize(aMemory.GetSize() + 1);
+ rOle.Seek(0);
+ return true;
+}
+
+bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf, SwOLENode& rOLENode,
+ const SwFrameFormat& rFormat)
+{
+ sal_uInt64 nPos = rOle2.Tell();
+ comphelper::ScopeGuard g([&rOle2, nPos] { rOle2.Seek(nPos); });
+
+ // Write OLE1 header, then the RTF wrapper.
+ SvMemoryStream aOLE1;
+
+ // Prepare presentation data early, so it's available to both OLE1 and RTF.
+ Size aSize = rFormat.GetFrameSize().GetSize();
+ sal_uInt32 nWidth = aSize.getWidth();
+ sal_uInt32 nHeight = aSize.getHeight();
+ const Graphic* pGraphic = rOLENode.GetGraphic();
+ const sal_uInt8* pPresentationData = nullptr;
+ sal_uInt64 nPresentationData = 0;
+ SvMemoryStream aGraphicStream;
+ if (pGraphic)
+ {
+ uno::Sequence<beans::PropertyValue> aFilterData
+ = { comphelper::makePropertyValue("EmbedEMF", false) };
+ FilterConfigItem aConfigItem(&aFilterData);
+ if (ConvertGraphicToWMF(*pGraphic, aGraphicStream, &aConfigItem))
+ {
+ pPresentationData = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
+ nPresentationData = aGraphicStream.TellEnd();
+ msfilter::rtfutil::StripMetafileHeader(pPresentationData, nPresentationData);
+ }
+ }
+ OString aClassName = InsertOLE1Header(rOle2, aOLE1, nWidth, nHeight, rOLENode,
+ pPresentationData, nPresentationData);
+
+ // Start object.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_OBJECT);
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJEMB);
+
+ // Start objclass.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS " ");
+ rRtf.WriteOString(aClassName);
+ // End objclass.
+ rRtf.WriteOString("}");
+
+ // Object size.
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJW);
+ rRtf.WriteOString(OString::number(nWidth));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJH);
+ rRtf.WriteOString(OString::number(nHeight));
+
+ // Start objdata.
+ rRtf.WriteOString(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA SAL_NEWLINE_STRING);
+ msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8*>(aOLE1.GetData()), aOLE1.GetSize(),
+ &rRtf);
+ // End objdata.
+ rRtf.WriteOString("}");
+
+ if (pPresentationData)
+ {
+ WrapOleGraphicInRtf(rRtf, nWidth, nHeight, pPresentationData, nPresentationData);
+ }
+
+ // End object.
+ rRtf.WriteOString("}");
+
+ return true;
+}
+
+bool WrapGraphicInRtf(const Graphic& rGraphic, const SwFrameFormat& rFormat, SvStream& rRtf)
+{
+ // Start object.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_OBJECT);
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJEMB);
+
+ // Object size: as used in the document model (not pixel size)
+ Size aSize = rFormat.GetFrameSize().GetSize();
+ sal_uInt32 nWidth = aSize.getWidth();
+ sal_uInt32 nHeight = aSize.getHeight();
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJW);
+ rRtf.WriteOString(OString::number(nWidth));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJH);
+ rRtf.WriteOString(OString::number(nHeight));
+ rRtf.WriteOString(SAL_NEWLINE_STRING);
+
+ // Start objclass.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS " ");
+ OString aClassName("PBrush"_ostr);
+ rRtf.WriteOString(aClassName);
+ // End objclass.
+ rRtf.WriteOString("}");
+ rRtf.WriteOString(SAL_NEWLINE_STRING);
+
+ // Start objdata.
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA " ");
+
+ SvMemoryStream aOle1;
+ // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
+ // OLEVersion.
+ aOle1.WriteUInt32(0x00000501);
+
+ // FormatID is EmbeddedObject.
+ aOle1.WriteUInt32(0x00000002);
+
+ // ClassName
+ aOle1.WriteUInt32(aClassName.getLength() + 1);
+ aOle1.WriteOString(aClassName);
+ // Null terminated pascal string.
+ aOle1.WriteChar(0);
+
+ // TopicName.
+ aOle1.WriteUInt32(0);
+
+ // ItemName.
+ aOle1.WriteUInt32(0);
+
+ // NativeDataSize
+ SvMemoryStream aNativeData;
+
+ // Set white background for the semi-transparent pixels.
+ BitmapEx aBitmapEx = rGraphic.GetBitmapEx();
+ Bitmap aBitmap = aBitmapEx.GetBitmap(/*aTransparentReplaceColor=*/COL_WHITE);
+
+ if (aBitmap.getPixelFormat() != vcl::PixelFormat::N24_BPP)
+ {
+ // More exotic pixel formats cause trouble for ms paint.
+ aBitmap.Convert(BmpConversion::N24Bit);
+ }
+
+ if (GraphicConverter::Export(aNativeData, BitmapEx(aBitmap), ConvertDataFormat::BMP)
+ != ERRCODE_NONE)
+ {
+ SAL_WARN("sw.html", "WrapGraphicInRtf: bmp conversion failed");
+ }
+ aOle1.WriteUInt32(aNativeData.TellEnd());
+
+ // Write the actual native data.
+ aNativeData.Seek(0);
+ aOle1.WriteStream(aNativeData);
+
+ // Prepare presentation data.
+ const sal_uInt8* pPresentationData = nullptr;
+ sal_uInt64 nPresentationData = 0;
+ SvMemoryStream aGraphicStream;
+ uno::Sequence<beans::PropertyValue> aFilterData
+ = { comphelper::makePropertyValue("EmbedEMF", false) };
+ FilterConfigItem aConfigItem(&aFilterData);
+ if (ConvertGraphicToWMF(rGraphic, aGraphicStream, &aConfigItem))
+ {
+ pPresentationData = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
+ nPresentationData = aGraphicStream.TellEnd();
+ msfilter::rtfutil::StripMetafileHeader(pPresentationData, nPresentationData);
+ }
+
+ // Write Presentation.
+ // OLEVersion.
+ aOle1.WriteUInt32(0x00000501);
+ // FormatID: constant means the ClassName field is present.
+ aOle1.WriteUInt32(0x00000005);
+ // ClassName: null terminated pascal string.
+ OString aPresentationClassName("METAFILEPICT"_ostr);
+ aOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
+ aOle1.WriteOString(aPresentationClassName);
+ aOle1.WriteChar(0);
+ const sal_uInt8* pBytes = nullptr;
+ sal_uInt64 nBytes = 0;
+ // Take presentation data for OLE1 from RTF.
+ pBytes = pPresentationData;
+ nBytes = nPresentationData;
+ // Width.
+ aOle1.WriteUInt32(nWidth);
+ // Height.
+ aOle1.WriteUInt32(nHeight * -1);
+ // PresentationDataSize: size of (reserved fields + pBytes).
+ aOle1.WriteUInt32(8 + nBytes);
+ // Reserved1-4.
+ aOle1.WriteUInt16(0x0008);
+ aOle1.WriteUInt16(0x31b1);
+ aOle1.WriteUInt16(0x1dd9);
+ aOle1.WriteUInt16(0x0000);
+ aOle1.WriteBytes(pBytes, nBytes);
+
+ // End objdata.
+ msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8*>(aOle1.GetData()), aOle1.GetSize(),
+ &rRtf);
+ rRtf.WriteOString("}");
+ rRtf.WriteOString(SAL_NEWLINE_STRING);
+
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_RESULT);
+ rRtf.WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT);
+
+ Size aMapped(rGraphic.GetPrefSize());
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICW);
+ rRtf.WriteOString(OString::number(aMapped.Width()));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICH);
+ rRtf.WriteOString(OString::number(aMapped.Height()));
+
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
+ rRtf.WriteOString(OString::number(nWidth));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
+ rRtf.WriteOString(OString::number(nHeight));
+ rRtf.WriteOString(OOO_STRING_SVTOOLS_RTF_WMETAFILE "8");
+ rRtf.WriteOString(SAL_NEWLINE_STRING);
+
+ if (pPresentationData)
+ {
+ msfilter::rtfutil::WriteHex(pPresentationData, nPresentationData, &rRtf);
+ rRtf.WriteOString(SAL_NEWLINE_STRING);
+ }
+
+ // End pict.
+ rRtf.WriteOString("}");
+
+ // End result.
+ rRtf.WriteOString("}");
+
+ // End object.
+ rRtf.WriteOString("}");
+ return true;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlreqifreader.hxx b/sw/source/filter/html/htmlreqifreader.hxx
new file mode 100644
index 0000000000..84169bb7c0
--- /dev/null
+++ b/sw/source/filter/html/htmlreqifreader.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX
+
+class Graphic;
+class Size;
+class SvStream;
+class SwOLENode;
+class SwFrameFormat;
+
+namespace SwReqIfReader
+{
+/**
+ * Extracts an OLE2 container binary from an RTF fragment.
+ *
+ * @param bOwnFormat if the extracted data has an ODF class ID or not.
+ */
+bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle, bool& bOwnFormat);
+
+/// Wraps an OLE2 container binary in an RTF fragment.
+bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf, SwOLENode& rOLENode,
+ const SwFrameFormat& rFormat);
+
+/**
+ * Wraps an image in an RTF fragment.
+ */
+bool WrapGraphicInRtf(const Graphic& rGraphic, const SwFrameFormat& rFormat, SvStream& rRtf);
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmlsect.cxx b/sw/source/filter/html/htmlsect.cxx
new file mode 100644
index 0000000000..26a1ec8d0e
--- /dev/null
+++ b/sw/source/filter/html/htmlsect.cxx
@@ -0,0 +1,825 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <rtl/uri.hxx>
+
+#include <svl/urihelper.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <osl/diagnose.h>
+
+#include <hintids.hxx>
+#include <fmthdft.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtclds.hxx>
+#include <fmtanchr.hxx>
+#include <fmtpdsc.hxx>
+#include <frmatr.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <section.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <swtable.hxx>
+#include "swcss1.hxx"
+#include "swhtml.hxx"
+
+
+using namespace ::com::sun::star;
+
+void SwHTMLParser::NewDivision( HtmlTokenId nToken )
+{
+ OUString aId, aHRef;
+ OUString aStyle, aLang, aDir;
+ OUString aClass;
+ SvxAdjust eAdjust = HtmlTokenId::CENTER_ON==nToken ? SvxAdjust::Center
+ : SvxAdjust::End;
+
+ bool bHeader=false, bFooter=false;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ if( HtmlTokenId::DIVISION_ON==nToken )
+ eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ case HtmlOptionId::HREF:
+ aHRef = rOption.GetString();
+ break;
+ case HtmlOptionId::TITLE:
+ {
+ const OUString& rType = rOption.GetString();
+ if( rType.equalsIgnoreAsciiCase("header") )
+ bHeader = true;
+ else if( rType.equalsIgnoreAsciiCase("footer") )
+ bFooter = true;
+ }
+ break;
+ default: break;
+ }
+ }
+
+ bool bAppended = false;
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ {
+ AppendTextNode( bHeader||bFooter||!aId.isEmpty()|| !aHRef.isEmpty() ? AM_NORMAL
+ : AM_NOSPACE );
+ bAppended = true;
+ }
+
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ bool bStyleParsed = false, bPositioned = false;
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ bStyleParsed = ParseStyleOptions( aStyle, aId, aClass,
+ aItemSet, aPropInfo, &aLang, &aDir );
+ if( bStyleParsed )
+ {
+ if ( aPropInfo.m_nColumnCount >= 2 )
+ {
+ xCntxt.reset();
+ NewMultiCol( aPropInfo.m_nColumnCount );
+ return;
+ }
+ bPositioned = HtmlTokenId::DIVISION_ON == nToken && !aClass.isEmpty() &&
+ CreateContainer(aClass, aItemSet, aPropInfo,
+ xCntxt.get());
+ if( !bPositioned )
+ {
+ if (aPropInfo.m_bVisible && m_aContexts.size())
+ {
+ const std::unique_ptr<HTMLAttrContext>& pParent
+ = m_aContexts[m_aContexts.size() - 1];
+ if (!pParent->IsVisible())
+ {
+ // If the parent context is hidden, we are not visible, either.
+ aPropInfo.m_bVisible = false;
+ }
+ }
+ bPositioned = DoPositioning(aItemSet, aPropInfo, xCntxt.get());
+ }
+ }
+ }
+
+ if (!bPositioned && (bHeader || bFooter) && IsNewDoc() && !m_bReadingHeaderOrFooter)
+ {
+ m_bReadingHeaderOrFooter = true;
+ xCntxt->SetHeaderOrFooter(true);
+
+ SwPageDesc *pPageDesc = m_pCSS1Parser->GetMasterPageDesc();
+ SwFrameFormat& rPageFormat = pPageDesc->GetMaster();
+
+ SwFrameFormat *pHdFtFormat;
+ bool bNew = false;
+ HtmlContextFlags nFlags = HtmlContextFlags::MultiColMask;
+ if( bHeader )
+ {
+ pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat());
+ if( !pHdFtFormat )
+ {
+ // still no header, then create one
+ rPageFormat.SetFormatAttr( SwFormatHeader( true ));
+ pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat());
+ bNew = true;
+ }
+ nFlags |= HtmlContextFlags::HeaderDist;
+ }
+ else
+ {
+ pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat());
+ if( !pHdFtFormat )
+ {
+ // still no footer, then create one
+ rPageFormat.SetFormatAttr( SwFormatFooter( true ));
+ pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat());
+ bNew = true;
+ }
+ nFlags |= HtmlContextFlags::FooterDist;
+ }
+
+ const SwFormatContent& rFlyContent = pHdFtFormat->GetContent();
+ const SwNodeIndex& rContentStIdx = *rFlyContent.GetContentIdx();
+
+ if( !bNew )
+ {
+ // Our own html export only exports one "header" at most (and one "footer")
+
+ // Create a new node at the beginning of the section if a duplicate arises
+ // and hide the original header/footers content by putting it into a hidden
+ // document-level section
+ SwNodeIndex aSttIdx( rContentStIdx, 1 );
+ m_xDoc->GetNodes().MakeTextNode( aSttIdx.GetNode(),
+ m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_TEXT));
+
+ // delete the current content of the section
+ SwPaM aDelPam( aSttIdx );
+ aDelPam.SetMark();
+
+ const SwStartNode *pStNd =
+ static_cast<const SwStartNode *>( &rContentStIdx.GetNode() );
+ aDelPam.GetPoint()->Assign( pStNd->EndOfSectionIndex() );
+
+ SwSectionData aSection(SectionType::Content, m_xDoc->GetUniqueSectionName());
+ if (SwSection* pOldContent = m_xDoc->InsertSwSection(aDelPam, aSection, nullptr, nullptr, false))
+ pOldContent->SetHidden(true);
+
+ // update page style
+ for( size_t i=0; i < m_xDoc->GetPageDescCnt(); i++ )
+ {
+ if( RES_POOLPAGE_HTML == m_xDoc->GetPageDesc(i).GetPoolFormatId() )
+ {
+ m_xDoc->ChgPageDesc( i, *pPageDesc );
+ break;
+ }
+ }
+ }
+
+ SwPosition aNewPos( rContentStIdx, SwNodeOffset(1) );
+ SaveDocContext(xCntxt.get(), nFlags, &aNewPos);
+ }
+ else if( !bPositioned && aId.getLength() > 9 &&
+ (aId[0] == 's' || aId[0] == 'S' ) &&
+ (aId[1] == 'd' || aId[1] == 'D' ) )
+ {
+ bool bEndNote = false, bFootNote = false;
+ if( aId.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote ) )
+ bEndNote = true;
+ else if( aId.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote ) )
+ bFootNote = true;
+ if( bFootNote || bEndNote )
+ {
+ SwNodeIndex *pStartNdIdx = GetFootEndNoteSection( aId );
+ if( pStartNdIdx )
+ {
+ SwContentNode *pCNd =
+ m_xDoc->GetNodes()[pStartNdIdx->GetIndex()+1]->GetContentNode();
+ SwNodeIndex aTmpSwNodeIndex(*pCNd);
+ SwPosition aNewPos( aTmpSwNodeIndex, pCNd, 0 );
+ SaveDocContext(xCntxt.get(), HtmlContextFlags::MultiColMask, &aNewPos);
+ aId.clear();
+ aPropInfo.m_aId.clear();
+ }
+ }
+ }
+
+ // We only insert sections into frames if the section is linked.
+ if( (!aId.isEmpty() && !bPositioned) || !aHRef.isEmpty() )
+ {
+ // Insert section (has to be done before setting of attributes,
+ // because the section is inserted before the PaM position.
+
+ // If we are in the first node of a section, we insert the section
+ // before the current section and not in the current section.
+ // Therefore we have to add a node and delete it again!
+ if( !bAppended )
+ {
+ SwNodeIndex aPrvNdIdx( m_pPam->GetPoint()->GetNode(), -1 );
+ if (aPrvNdIdx.GetNode().IsSectionNode())
+ {
+ AppendTextNode();
+ bAppended = true;
+ }
+ }
+ std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts(bAppended ? nullptr : new std::deque<std::unique_ptr<HTMLAttr>>);
+ SetAttr( true, true, pPostIts.get() );
+
+ // make name of section unique
+ const OUString aName( m_xDoc->GetUniqueSectionName( !aId.isEmpty() ? &aId : nullptr ) );
+
+ if( !aHRef.isEmpty() )
+ {
+ sal_Unicode cDelim = 255U;
+ sal_Int32 nPos = aHRef.lastIndexOf( cDelim );
+ sal_Int32 nPos2 = -1;
+ if( nPos != -1 )
+ {
+ nPos2 = aHRef.lastIndexOf( cDelim, nPos );
+ if( nPos2 != -1 )
+ std::swap( nPos, nPos2 );
+ }
+ OUString aURL;
+ if( nPos == -1 )
+ {
+ aURL = URIHelper::SmartRel2Abs(INetURLObject( m_sBaseURL ), aHRef, Link<OUString *, bool>(), false);
+ }
+ else
+ {
+ aURL = URIHelper::SmartRel2Abs(INetURLObject( m_sBaseURL ), aHRef.copy( 0, nPos ), Link<OUString *, bool>(), false )
+ + OUStringChar(sfx2::cTokenSeparator);
+ if( nPos2 == -1 )
+ {
+ aURL += aHRef.subView( nPos+1 );
+ }
+ else
+ {
+ aURL += aHRef.subView( nPos+1, nPos2 - (nPos+1) )
+ + OUStringChar(sfx2::cTokenSeparator)
+ + rtl::Uri::decode( aHRef.copy( nPos2+1 ),
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_ISO_8859_1 );
+ }
+ }
+ aHRef = aURL;
+ }
+
+ SwSectionData aSection( (!aHRef.isEmpty()) ? SectionType::FileLink
+ : SectionType::Content, aName );
+ if( !aHRef.isEmpty() )
+ {
+ aSection.SetLinkFileName( aHRef );
+ aSection.SetProtectFlag(true);
+ }
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameItemSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs(aFrameItemSet );
+
+ if( const SvxBrushItem* pItem = aItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ aFrameItemSet.Put( *pItem );
+ aItemSet.ClearItem( RES_BACKGROUND );
+ }
+ if( const SvxFrameDirectionItem* pItem = aItemSet.GetItemIfSet( RES_FRAMEDIR, false ) )
+ {
+ aFrameItemSet.Put( *pItem );
+ aItemSet.ClearItem( RES_FRAMEDIR );
+ }
+
+ m_xDoc->InsertSwSection( *m_pPam, aSection, nullptr, &aFrameItemSet, false );
+
+ // maybe jump to section
+ if( JumpToMarks::Region == m_eJumpTo && aName == m_sJmpMark )
+ {
+ m_bChkJumpMark = true;
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+
+ SwTextNode* pOldTextNd =
+ bAppended ? nullptr : m_pPam->GetPoint()->GetNode().GetTextNode();
+
+ m_pPam->Move( fnMoveBackward );
+
+ // move PageDesc and SwFormatBreak attribute from current node into
+ // (first) node of the section
+ if( pOldTextNd )
+ MovePageDescAttrs( pOldTextNd, m_pPam->GetPoint()->GetNodeIndex(),
+ true );
+
+ if( pPostIts )
+ {
+ // move still existing PostIts in the first paragraph of the table
+ InsertAttrs( std::move(*pPostIts) );
+ pPostIts.reset();
+ }
+
+ xCntxt->SetSpansSection( true );
+
+ // don't insert Bookmarks with same name as sections
+ if( !aPropInfo.m_aId.isEmpty() && aPropInfo.m_aId==aName )
+ aPropInfo.m_aId.clear();
+ }
+ else
+ {
+ xCntxt->SetAppendMode( AM_NOSPACE );
+ }
+
+ if( SvxAdjust::End != eAdjust )
+ {
+ InsertAttr(&m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST), xCntxt.get());
+ }
+
+ // parse style
+ if( bStyleParsed )
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+
+ xCntxt->SetVisible(aPropInfo.m_bVisible);
+ PushContext(xCntxt);
+}
+
+void SwHTMLParser::EndDivision()
+{
+ // search for the stack entry of the token (because we still have the div stack
+ // we don't make a difference between DIV and CENTER)
+ std::unique_ptr<HTMLAttrContext> xCntxt;
+ auto nPos = m_aContexts.size();
+ while (!xCntxt && nPos>m_nContextStMin)
+ {
+ switch( m_aContexts[--nPos]->GetToken() )
+ {
+ case HtmlTokenId::CENTER_ON:
+ case HtmlTokenId::DIVISION_ON:
+ xCntxt = std::move(m_aContexts[nPos]);
+ m_aContexts.erase( m_aContexts.begin() + nPos );
+ break;
+ default: break;
+ }
+ }
+
+ if (xCntxt)
+ {
+ // close attribute
+ EndContext(xCntxt.get());
+ SetAttr(); // set paragraph attributes really fast because of JavaScript
+ if (xCntxt->IsHeaderOrFooter())
+ m_bReadingHeaderOrFooter = false;
+ }
+}
+
+void SwHTMLParser::FixHeaderFooterDistance( bool bHeader,
+ const SwPosition *pOldPos )
+{
+ SwPageDesc *pPageDesc = m_pCSS1Parser->GetMasterPageDesc();
+ SwFrameFormat& rPageFormat = pPageDesc->GetMaster();
+
+ SwFrameFormat *pHdFtFormat =
+ bHeader ? const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat())
+ : const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat());
+ OSL_ENSURE( pHdFtFormat, "No header or footer" );
+
+ const SwFormatContent& rFlyContent = pHdFtFormat->GetContent();
+ const SwNodeIndex& rContentStIdx = *rFlyContent.GetContentIdx();
+
+ SwNodeOffset nPrvNxtIdx;
+ if( bHeader )
+ {
+ nPrvNxtIdx = rContentStIdx.GetNode().EndOfSectionIndex()-1;
+ }
+ else
+ {
+ nPrvNxtIdx = pOldPos->GetNodeIndex() - 1;
+ }
+
+ sal_uInt16 nSpace = 0;
+ SwTextNode *pTextNode = m_xDoc->GetNodes()[nPrvNxtIdx]->GetTextNode();
+ if( pTextNode )
+ {
+ const SvxULSpaceItem& rULSpace =
+ pTextNode->SwContentNode::GetAttr( RES_UL_SPACE );
+
+ // The bottom paragraph padding becomes the padding
+ // to header or footer
+ nSpace = rULSpace.GetLower();
+
+ // and afterwards set to a valid value
+ const SvxULSpaceItem& rCollULSpace =
+ pTextNode->GetAnyFormatColl().GetULSpace();
+ if( rCollULSpace.GetUpper() == rULSpace.GetUpper() )
+ pTextNode->ResetAttr( RES_UL_SPACE );
+ else
+ pTextNode->SetAttr(
+ SvxULSpaceItem( rULSpace.GetUpper(),
+ rCollULSpace.GetLower(), RES_UL_SPACE ) );
+ }
+
+ if( bHeader )
+ {
+ nPrvNxtIdx = pOldPos->GetNodeIndex();
+ }
+ else
+ {
+ nPrvNxtIdx = rContentStIdx.GetIndex() + 1;
+ }
+
+ pTextNode = m_xDoc->GetNodes()[nPrvNxtIdx]
+ ->GetTextNode();
+ if( pTextNode )
+ {
+ const SvxULSpaceItem& rULSpace =
+ pTextNode->SwContentNode::GetAttr( RES_UL_SPACE );
+
+ // The top paragraph padding becomes the padding
+ // to headline or footer if it is greater than the
+ // bottom padding of the paragraph beforehand
+ if( rULSpace.GetUpper() > nSpace )
+ nSpace = rULSpace.GetUpper();
+
+ // and afterwards set to a valid value
+ const SvxULSpaceItem& rCollULSpace =
+ pTextNode->GetAnyFormatColl().GetULSpace();
+ if( rCollULSpace.GetLower() == rULSpace.GetLower() )
+ pTextNode->ResetAttr( RES_UL_SPACE );
+ else
+ pTextNode->SetAttr(
+ SvxULSpaceItem( rCollULSpace.GetUpper(),
+ rULSpace.GetLower(), RES_UL_SPACE ) );
+ }
+
+ SvxULSpaceItem aULSpace( RES_UL_SPACE );
+ if( bHeader )
+ aULSpace.SetLower( nSpace );
+ else
+ aULSpace.SetUpper( nSpace );
+
+ pHdFtFormat->SetFormatAttr( aULSpace );
+}
+
+bool SwHTMLParser::EndSection( bool bLFStripped )
+{
+ SwEndNode *pEndNd = m_xDoc->GetNodes()[m_pPam->GetPoint()->GetNodeIndex()+1]
+ ->GetEndNode();
+ if( pEndNd && pEndNd->StartOfSectionNode()->IsSectionNode() )
+ {
+ // close the section
+ if( !bLFStripped )
+ StripTrailingPara();
+ m_pPam->Move( fnMoveForward );
+ return true;
+ }
+
+ OSL_ENSURE( false, "Wrong PaM position at end of section" );
+
+ return false;
+}
+
+bool SwHTMLParser::EndSections( bool bLFStripped )
+{
+ bool bSectionClosed = false;
+ auto nPos = m_aContexts.size();
+ while( nPos>m_nContextStMin )
+ {
+ HTMLAttrContext *pCntxt = m_aContexts[--nPos].get();
+ if( pCntxt->GetSpansSection() && EndSection( bLFStripped ) )
+ {
+ bSectionClosed = true;
+ pCntxt->SetSpansSection( false );
+ bLFStripped = false;
+ }
+ }
+
+ return bSectionClosed;
+}
+
+void SwHTMLParser::NewMultiCol( sal_uInt16 columnsFromCss )
+{
+ OUString aId;
+ OUString aStyle, aClass, aLang, aDir;
+ tools::Long nWidth = 100;
+ sal_uInt16 nCols = columnsFromCss, nGutter = 10;
+ bool bPercentWidth = true;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ case HtmlOptionId::COLS:
+ nCols = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::WIDTH:
+ nWidth = rOption.GetNumber();
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ if( bPercentWidth && nWidth>100 )
+ nWidth = 100;
+ break;
+ case HtmlOptionId::GUTTER:
+ nGutter = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ default: break;
+ }
+ }
+
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::MULTICOL_ON));
+
+ //.is the multicol element contained in a container? That may be the
+ // case for 5.0 documents.
+ bool bInCntnr = false;
+ auto i = m_aContexts.size();
+ while( !bInCntnr && i > m_nContextStMin )
+ bInCntnr = nullptr != m_aContexts[--i]->GetFrameItemSet();
+
+ // Parse style sheets, but don't position anything by now.
+ bool bStyleParsed = false;
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ bStyleParsed = ParseStyleOptions( aStyle, aId, aClass,
+ aItemSet, aPropInfo, &aLang, &aDir );
+
+ // Calculate width.
+ sal_uInt8 nPercentWidth = bPercentWidth ? static_cast<sal_uInt8>(nWidth) : 0;
+ SwTwips nTwipWidth = 0;
+ if( !bPercentWidth && nWidth )
+ {
+ nTwipWidth = o3tl::convert(nWidth, o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ if( !nPercentWidth && nTwipWidth < MINFLY )
+ nTwipWidth = MINFLY;
+
+ // Do positioning.
+ bool bPositioned = false;
+ if( bInCntnr || SwCSS1Parser::MayBePositioned( aPropInfo, true ) )
+ {
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameItemSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs(aFrameItemSet );
+
+ SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE, aPropInfo,
+ aFrameItemSet );
+
+ // The width is either the WIDTH attribute's value or contained
+ // in some style option.
+ SetVarSize( aPropInfo, aFrameItemSet, nTwipWidth, nPercentWidth );
+
+ SetSpace( Size(0,0), aItemSet, aPropInfo, aFrameItemSet );
+
+ // Set some other frame attributes. If the background is set, its
+ // it will be cleared here. That for, it won't be set at the section,
+ // too.
+ SetFrameFormatAttrs( aItemSet,
+ HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Padding|HtmlFrameFormatFlags::Direction,
+ aFrameItemSet );
+
+ // Insert fly frame. If the are columns, the fly frame's name is not
+ // the sections name but a generated one.
+ OUString aFlyName;
+ if( nCols < 2 )
+ {
+ aFlyName = aId;
+ aPropInfo.m_aId.clear();
+ }
+
+ InsertFlyFrame(aFrameItemSet, xCntxt.get(), aFlyName);
+
+ xCntxt->SetPopStack( true );
+ bPositioned = true;
+ }
+
+ bool bAppended = false;
+ if( !bPositioned )
+ {
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ {
+ AppendTextNode( AM_SPACE );
+ bAppended = true;
+ }
+ else
+ {
+ AddParSpace();
+ }
+ }
+
+ // If there are less than 2 columns, no section is inserted.
+ if( nCols >= 2 )
+ {
+ if( !bAppended )
+ {
+ // If the pam is at the start of a section, an additional text
+ // node must be inserted. Otherwise, the new section will be
+ // inserted in front of the old one.
+ SwNodeIndex aPrvNdIdx( m_pPam->GetPoint()->GetNode(), -1 );
+ if (aPrvNdIdx.GetNode().IsSectionNode())
+ {
+ AppendTextNode();
+ bAppended = true;
+ }
+ }
+ std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts(bAppended ? nullptr : new std::deque<std::unique_ptr<HTMLAttr>>);
+ SetAttr( true, true, pPostIts.get() );
+
+ // Make section name unique.
+ OUString aName( m_xDoc->GetUniqueSectionName( !aId.isEmpty() ? &aId : nullptr ) );
+ SwSectionData aSection( SectionType::Content, aName );
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameItemSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs(aFrameItemSet );
+
+ if( nGutter )
+ {
+ nGutter = o3tl::convert(nGutter, o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ SwFormatCol aFormatCol;
+
+ aFormatCol.Init( nCols, nGutter, USHRT_MAX );
+ aFrameItemSet.Put( aFormatCol );
+
+ if( const SvxBrushItem* pItem = aItemSet.GetItemIfSet( RES_BACKGROUND, false) )
+ {
+ aFrameItemSet.Put( *pItem );
+ aItemSet.ClearItem( RES_BACKGROUND );
+ }
+ if( const SvxFrameDirectionItem* pItem = aItemSet.GetItemIfSet( RES_FRAMEDIR, false ) )
+ {
+ aFrameItemSet.Put( *pItem );
+ aItemSet.ClearItem( RES_FRAMEDIR );
+ }
+ m_xDoc->InsertSwSection( *m_pPam, aSection, nullptr, &aFrameItemSet, false );
+
+ // Jump to section, if this is requested.
+ if( JumpToMarks::Region == m_eJumpTo && aName == m_sJmpMark )
+ {
+ m_bChkJumpMark = true;
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+
+ SwTextNode* pOldTextNd =
+ bAppended ? nullptr : m_pPam->GetPoint()->GetNode().GetTextNode();
+
+ m_pPam->Move( fnMoveBackward );
+
+ // Move PageDesc and SwFormatBreak attributes of the current node
+ // to the section's first node.
+ if( pOldTextNd )
+ MovePageDescAttrs( pOldTextNd, m_pPam->GetPoint()->GetNodeIndex(),
+ true );
+
+ if( pPostIts )
+ {
+ // Move pending PostIts into the section.
+ InsertAttrs( std::move(*pPostIts) );
+ pPostIts.reset();
+ }
+
+ xCntxt->SetSpansSection( true );
+
+ // Insert a bookmark if its name differs from the section's name only.
+ if( !aPropInfo.m_aId.isEmpty() && aPropInfo.m_aId==aName )
+ aPropInfo.m_aId.clear();
+ }
+
+ // Additional attributes must be set as hard ones.
+ if( bStyleParsed )
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+
+ PushContext(xCntxt);
+}
+
+void SwHTMLParser::InsertFlyFrame( const SfxItemSet& rItemSet,
+ HTMLAttrContext *pCntxt,
+ const OUString& rName )
+{
+ RndStdIds eAnchorId =
+ rItemSet.Get( RES_ANCHOR ).GetAnchorId();
+
+ // create frame
+ SwFlyFrameFormat* pFlyFormat = m_xDoc->MakeFlySection( eAnchorId, m_pPam->GetPoint(),
+ &rItemSet );
+ if( !rName.isEmpty() )
+ pFlyFormat->SetFormatName( rName );
+
+ RegisterFlyFrame( pFlyFormat );
+
+ const SwFormatContent& rFlyContent = pFlyFormat->GetContent();
+ const SwNodeIndex& rFlyCntIdx = *rFlyContent.GetContentIdx();
+
+ SwPosition aNewPos( rFlyCntIdx, SwNodeOffset(1) );
+ const HtmlContextFlags nFlags = HtmlContextFlags::ProtectStack|HtmlContextFlags::StripPara;
+ SaveDocContext( pCntxt, nFlags, &aNewPos );
+}
+
+void SwHTMLParser::MovePageDescAttrs( SwNode *pSrcNd,
+ SwNodeOffset nDestIdx,
+ bool bFormatBreak )
+{
+ SwContentNode* pDestContentNd =
+ m_xDoc->GetNodes()[nDestIdx]->GetContentNode();
+
+ OSL_ENSURE( pDestContentNd, "Why is the target not a Content-Node?" );
+
+ if( pSrcNd->IsContentNode() )
+ {
+ SwContentNode* pSrcContentNd = pSrcNd->GetContentNode();
+
+ const SwFormatPageDesc* pFormatPageDesc =
+ pSrcContentNd->GetSwAttrSet().GetItemIfSet( RES_PAGEDESC, false );
+ if( pFormatPageDesc && pFormatPageDesc->GetPageDesc() )
+ {
+ pDestContentNd->SetAttr( *pFormatPageDesc );
+ pSrcContentNd->ResetAttr( RES_PAGEDESC );
+ }
+ if( const SvxFormatBreakItem* pItem = pSrcContentNd->GetSwAttrSet()
+ .GetItemIfSet( RES_BREAK, false ) )
+ {
+ switch( pItem->GetBreak() )
+ {
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ if( bFormatBreak )
+ pDestContentNd->SetAttr( *pItem );
+ pSrcContentNd->ResetAttr( RES_BREAK );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if( pSrcNd->IsTableNode() )
+ {
+ SwFrameFormat *pFrameFormat = pSrcNd->GetTableNode()->GetTable().GetFrameFormat();
+
+ if( const SwFormatPageDesc* pItem = pFrameFormat->GetAttrSet().
+ GetItemIfSet( RES_PAGEDESC, false ) )
+ {
+ if (pDestContentNd)
+ pDestContentNd->SetAttr(*pItem);
+ pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmltab.cxx b/sw/source/filter/html/htmltab.cxx
new file mode 100644
index 0000000000..424644378b
--- /dev/null
+++ b/sw/source/filter/html/htmltab.cxx
@@ -0,0 +1,5220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+#include <comphelper/flagguard.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <svl/numformat.hxx>
+#include <svl/urihelper.hxx>
+#include <svx/sdrobjectuser.hxx>
+#include <svx/svdotext.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <dcontact.hxx>
+#include <fmtornt.hxx>
+#include <frmfmt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtlsplt.hxx>
+#include <frmatr.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <poolfmt.hxx>
+#include <swtable.hxx>
+#include <cellatr.hxx>
+#include <htmltbl.hxx>
+#include <swtblfmt.hxx>
+#include "htmlnum.hxx"
+#include "swhtml.hxx"
+#include "swcss1.hxx"
+#include <txtftn.hxx>
+#include <itabenum.hxx>
+#include <tblafmt.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <frameformats.hxx>
+
+#define NETSCAPE_DFLT_BORDER 1
+#define NETSCAPE_DFLT_CELLSPACING 2
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+HTMLOptionEnum<sal_Int16> const aHTMLTableVAlignTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_VA_top, text::VertOrientation::NONE },
+ { OOO_STRING_SVTOOLS_HTML_VA_middle, text::VertOrientation::CENTER },
+ { OOO_STRING_SVTOOLS_HTML_VA_bottom, text::VertOrientation::BOTTOM },
+ { nullptr, 0 }
+};
+
+// table tags options
+
+namespace {
+
+struct HTMLTableOptions
+{
+ sal_uInt16 nCols;
+ sal_uInt16 nWidth;
+ sal_uInt16 nHeight;
+ sal_uInt16 nCellPadding;
+ sal_uInt16 nCellSpacing;
+ sal_uInt16 nBorder;
+ sal_uInt16 nHSpace;
+ sal_uInt16 nVSpace;
+
+ SvxAdjust eAdjust;
+ sal_Int16 eVertOri;
+ HTMLTableFrame eFrame;
+ HTMLTableRules eRules;
+
+ bool bPercentWidth : 1;
+ bool bTableAdjust : 1;
+ bool bBGColor : 1;
+
+ Color aBorderColor;
+ Color aBGColor;
+
+ OUString aBGImage, aStyle, aId, aClass, aDir;
+
+ HTMLTableOptions( const HTMLOptions& rOptions, SvxAdjust eParentAdjust );
+};
+
+class HTMLTableContext
+{
+ SwHTMLNumRuleInfo m_aNumRuleInfo; // Numbering valid before the table
+
+ SwTableNode *m_pTableNd; // table node
+ SwFrameFormat *m_pFrameFormat; // the Fly frame::Frame, containing the table
+ std::unique_ptr<SwPosition> m_pPos; // position behind the table
+
+ size_t m_nContextStAttrMin;
+ size_t m_nContextStMin;
+
+ bool m_bRestartPRE : 1;
+ bool m_bRestartXMP : 1;
+ bool m_bRestartListing : 1;
+
+ HTMLTableContext(const HTMLTableContext&) = delete;
+ HTMLTableContext& operator=(const HTMLTableContext&) = delete;
+
+public:
+
+ std::shared_ptr<HTMLAttrTable> m_xAttrTab; // attributes
+
+ HTMLTableContext( SwPosition *pPs, size_t nCntxtStMin,
+ size_t nCntxtStAttrMin ) :
+ m_pTableNd( nullptr ),
+ m_pFrameFormat( nullptr ),
+ m_pPos( pPs ),
+ m_nContextStAttrMin( nCntxtStAttrMin ),
+ m_nContextStMin( nCntxtStMin ),
+ m_bRestartPRE( false ),
+ m_bRestartXMP( false ),
+ m_bRestartListing( false ),
+ m_xAttrTab(std::make_shared<HTMLAttrTable>())
+ {
+ memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable));
+ }
+
+ void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { m_aNumRuleInfo.Set(rInf); }
+ const SwHTMLNumRuleInfo& GetNumInfo() const { return m_aNumRuleInfo; };
+
+ void SavePREListingXMP( SwHTMLParser& rParser );
+ void RestorePREListingXMP( SwHTMLParser& rParser );
+
+ SwPosition *GetPos() const { return m_pPos.get(); }
+
+ void SetTableNode( SwTableNode *pNd ) { m_pTableNd = pNd; }
+ SwTableNode *GetTableNode() const { return m_pTableNd; }
+
+ void SetFrameFormat( SwFrameFormat *pFormat ) { m_pFrameFormat = pFormat; }
+ SwFrameFormat *GetFrameFormat() const { return m_pFrameFormat; }
+
+ size_t GetContextStMin() const { return m_nContextStMin; }
+ size_t GetContextStAttrMin() const { return m_nContextStAttrMin; }
+};
+
+}
+
+// Cell content is a linked list with SwStartNodes and
+// HTMLTables.
+
+class HTMLTableCnts
+{
+ std::unique_ptr<HTMLTableCnts> m_pNext; // next content
+
+ // Only one of the next two pointers must be set!
+ const SwStartNode *m_pStartNode; // a paragraph
+ std::shared_ptr<HTMLTable> m_xTable; // a table
+
+ std::shared_ptr<SwHTMLTableLayoutCnts> m_xLayoutInfo;
+
+ bool m_bNoBreak;
+
+ void InitCtor();
+
+public:
+
+ explicit HTMLTableCnts(const SwStartNode* pStNd);
+ explicit HTMLTableCnts(std::shared_ptr<HTMLTable> xTab);
+
+ ~HTMLTableCnts(); // only allowed in ~HTMLTableCell
+
+ // Determine SwStartNode and HTMLTable respectively
+ const SwStartNode *GetStartNode() const { return m_pStartNode; }
+ const std::shared_ptr<HTMLTable>& GetTable() const { return m_xTable; }
+ std::shared_ptr<HTMLTable>& GetTable() { return m_xTable; }
+
+ // Add a new node at the end of the list
+ void Add( std::unique_ptr<HTMLTableCnts> pNewCnts );
+
+ // Determine next node
+ const HTMLTableCnts *Next() const { return m_pNext.get(); }
+ HTMLTableCnts *Next() { return m_pNext.get(); }
+
+ inline void SetTableBox( SwTableBox *pBox );
+
+ void SetNoBreak() { m_bNoBreak = true; }
+
+ const std::shared_ptr<SwHTMLTableLayoutCnts>& CreateLayoutInfo();
+};
+
+namespace {
+
+// Cell of a HTML table
+class HTMLTableCell
+{
+ std::shared_ptr<HTMLTableCnts> m_xContents; // cell content
+ std::shared_ptr<SvxBrushItem> m_xBGBrush; // cell background
+ std::shared_ptr<SvxBoxItem> m_xBoxItem;
+
+ double m_nValue;
+ sal_uInt32 m_nNumFormat;
+ sal_uInt16 m_nRowSpan; // cell ROWSPAN
+ sal_uInt16 m_nColSpan; // cell COLSPAN
+ sal_uInt16 m_nWidth; // cell WIDTH
+ sal_Int16 m_eVertOrient; // vertical alignment of the cell
+ bool m_bProtected : 1; // cell must not filled
+ bool m_bRelWidth : 1; // nWidth is given in %
+ bool m_bHasNumFormat : 1;
+ bool m_bHasValue : 1;
+ bool m_bNoWrap : 1;
+ bool mbCovered : 1;
+
+public:
+
+ HTMLTableCell(); // new cells always empty
+
+ // Fill a not empty cell
+ void Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan,
+ sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrush,
+ std::shared_ptr<SvxBoxItem> const& rBoxItem,
+ bool bHasNumFormat, sal_uInt32 nNumFormat,
+ bool bHasValue, double nValue, bool bNoWrap, bool bCovered );
+
+ // Protect an empty 1x1 cell
+ void SetProtected();
+
+ // Set/Get cell content
+ void SetContents(std::shared_ptr<HTMLTableCnts> const& rCnts) { m_xContents = rCnts; }
+ const std::shared_ptr<HTMLTableCnts>& GetContents() const { return m_xContents; }
+
+ // Set/Get cell ROWSPAN/COLSPAN
+ void SetRowSpan( sal_uInt16 nRSpan ) { m_nRowSpan = nRSpan; }
+ sal_uInt16 GetRowSpan() const { return m_nRowSpan; }
+
+ void SetColSpan( sal_uInt16 nCSpan ) { m_nColSpan = nCSpan; }
+ sal_uInt16 GetColSpan() const { return m_nColSpan; }
+
+ inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth );
+
+ const std::shared_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
+ const std::shared_ptr<SvxBoxItem>& GetBoxItem() const { return m_xBoxItem; }
+
+ inline bool GetNumFormat( sal_uInt32& rNumFormat ) const;
+ inline bool GetValue( double& rValue ) const;
+
+ sal_Int16 GetVertOri() const { return m_eVertOrient; }
+
+ // Is the cell filled or protected ?
+ bool IsUsed() const { return m_xContents || m_bProtected; }
+
+ std::unique_ptr<SwHTMLTableLayoutCell> CreateLayoutInfo();
+
+ bool IsCovered() const { return mbCovered; }
+};
+
+}
+
+
+namespace {
+
+// Row of a HTML table
+class HTMLTableRow
+{
+ std::vector<HTMLTableCell> m_aCells; ///< cells of the row
+ std::unique_ptr<SvxBrushItem> m_xBGBrush; // background of cell from STYLE
+
+ SvxAdjust m_eAdjust;
+ sal_uInt16 m_nHeight; // options of <TR>/<TD>
+ sal_uInt16 m_nEmptyRows; // number of empty rows are following
+ sal_Int16 m_eVertOri;
+ bool m_bIsEndOfGroup : 1;
+ bool m_bBottomBorder : 1; // Is there a line after the row?
+
+public:
+
+ explicit HTMLTableRow( sal_uInt16 nCells ); // cells of the row are empty
+
+ void SetBottomBorder(bool bIn) { m_bBottomBorder = bIn; }
+ bool GetBottomBorder() const { return m_bBottomBorder; }
+
+ inline void SetHeight( sal_uInt16 nHeight );
+ sal_uInt16 GetHeight() const { return m_nHeight; }
+
+ const HTMLTableCell& GetCell(sal_uInt16 nCell) const;
+ HTMLTableCell& GetCell(sal_uInt16 nCell)
+ {
+ return const_cast<HTMLTableCell&>(const_cast<const HTMLTableRow&>(*this).GetCell(nCell));
+ }
+
+ void SetAdjust( SvxAdjust eAdj ) { m_eAdjust = eAdj; }
+ SvxAdjust GetAdjust() const { return m_eAdjust; }
+
+ void SetVertOri( sal_Int16 eV) { m_eVertOri = eV; }
+ sal_Int16 GetVertOri() const { return m_eVertOri; }
+
+ void SetBGBrush(std::unique_ptr<SvxBrushItem>& rBrush ) { m_xBGBrush = std::move(rBrush); }
+ const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
+
+ void SetEndOfGroup() { m_bIsEndOfGroup = true; }
+ bool IsEndOfGroup() const { return m_bIsEndOfGroup; }
+
+ void IncEmptyRows() { m_nEmptyRows++; }
+ sal_uInt16 GetEmptyRows() const { return m_nEmptyRows; }
+
+ // Expand row by adding empty cells
+ void Expand( sal_uInt16 nCells, bool bOneCell=false );
+
+ // Shrink row by deleting empty cells
+ void Shrink( sal_uInt16 nCells );
+};
+
+// Column of a HTML table
+class HTMLTableColumn
+{
+ bool m_bIsEndOfGroup;
+
+ sal_uInt16 m_nWidth; // options of <COL>
+ bool m_bRelWidth;
+
+ SvxAdjust m_eAdjust;
+ sal_Int16 m_eVertOri;
+
+ SwFrameFormat *m_aFrameFormats[6];
+
+ static inline sal_uInt16 GetFrameFormatIdx( bool bBorderLine,
+ sal_Int16 eVertOri );
+
+public:
+
+ bool m_bLeftBorder; // is there a line before the column
+
+ HTMLTableColumn();
+
+ inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth);
+
+ void SetAdjust( SvxAdjust eAdj ) { m_eAdjust = eAdj; }
+ SvxAdjust GetAdjust() const { return m_eAdjust; }
+
+ void SetVertOri( sal_Int16 eV) { m_eVertOri = eV; }
+ sal_Int16 GetVertOri() const { return m_eVertOri; }
+
+ void SetEndOfGroup() { m_bIsEndOfGroup = true; }
+ bool IsEndOfGroup() const { return m_bIsEndOfGroup; }
+
+ inline void SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
+ sal_Int16 eVertOri );
+ inline SwFrameFormat *GetFrameFormat( bool bBorderLine,
+ sal_Int16 eVertOri ) const;
+
+ std::unique_ptr<SwHTMLTableLayoutColumn> CreateLayoutInfo();
+};
+
+}
+
+// HTML table
+typedef std::vector<SdrObject *> SdrObjects;
+
+class HTMLTable : public sdr::ObjectUser
+{
+ OUString m_aId;
+ OUString m_aStyle;
+ OUString m_aClass;
+ OUString m_aDir;
+
+ std::optional<SdrObjects> m_xResizeDrawObjects;// SDR objects
+ std::optional<std::vector<sal_uInt16>> m_xDrawObjectPercentWidths; // column of draw object and its rel. width
+
+ std::vector<HTMLTableRow> m_aRows; ///< table rows
+ std::vector<HTMLTableColumn> m_aColumns; ///< table columns
+
+ sal_uInt16 m_nRows; // number of rows
+ sal_uInt16 m_nCols; // number of columns
+ sal_uInt16 m_nFilledColumns; // number of filled columns
+
+ sal_uInt16 m_nCurrentRow; // current Row
+ sal_uInt16 m_nCurrentColumn; // current Column
+
+ sal_uInt16 m_nLeftMargin; // Space to the left margin (from paragraph edge)
+ sal_uInt16 m_nRightMargin; // Space to the right margin (from paragraph edge)
+
+ sal_uInt16 m_nCellPadding; // Space from border to Text
+ sal_uInt16 m_nCellSpacing; // Space between two cells
+ sal_uInt16 m_nHSpace;
+ sal_uInt16 m_nVSpace;
+
+ sal_uInt16 m_nBoxes; // number of boxes in the table
+
+ const SwStartNode *m_pPrevStartNode; // the Table-Node or the Start-Node of the section before
+ const SwTable *m_pSwTable; // SW-Table (only on Top-Level)
+public:
+ std::unique_ptr<SwTableBox> m_xBox1; // TableBox, generated when the Top-Level-Table was build
+private:
+ SwTableBoxFormat *m_pBoxFormat; // frame::Frame-Format from SwTableBox
+ SwTableLineFormat *m_pLineFormat; // frame::Frame-Format from SwTableLine
+ SwTableLineFormat *m_pLineFrameFormatNoHeight;
+ std::unique_ptr<SvxBrushItem> m_xBackgroundBrush; // background of the table
+ std::unique_ptr<SvxBrushItem> m_xInheritedBackgroundBrush; // "inherited" background of the table
+ const SwStartNode *m_pCaptionStartNode; // Start-Node of the table-caption
+ //lines for the border
+ SvxBorderLine m_aTopBorderLine;
+ SvxBorderLine m_aBottomBorderLine;
+ SvxBorderLine m_aLeftBorderLine;
+ SvxBorderLine m_aRightBorderLine;
+ SvxBorderLine m_aBorderLine;
+ SvxBorderLine m_aInheritedLeftBorderLine;
+ SvxBorderLine m_aInheritedRightBorderLine;
+ bool m_bTopBorder; // is there a line on the top of the table
+ bool m_bRightBorder; // is there a line on the top right of the table
+ bool m_bTopAllowed; // is it allowed to set the border?
+ bool m_bRightAllowed;
+ bool m_bFillerTopBorder; // gets the left/right filler-cell a border on the
+ bool m_bFillerBottomBorder; // top or in the bottom
+ bool m_bInheritedLeftBorder;
+ bool m_bInheritedRightBorder;
+ bool m_bBordersSet; // the border is set already
+ bool m_bForceFrame;
+ bool m_bTableAdjustOfTag; // comes nTableAdjust from <TABLE>?
+ sal_uInt32 m_nHeadlineRepeat; // repeating rows
+ bool m_bIsParentHead;
+ bool m_bHasParentSection;
+ bool m_bHasToFly;
+ bool m_bFixedCols;
+ bool m_bColSpec; // where there COL(GROUP)-elements?
+ bool m_bPercentWidth; // width is declared in %
+
+ SwHTMLParser *m_pParser; // the current parser
+ std::unique_ptr<HTMLTableCnts> m_xParentContents;
+
+ std::unique_ptr<HTMLTableContext> m_pContext; // the context of the table
+
+ std::shared_ptr<SwHTMLTableLayout> m_xLayoutInfo;
+
+ // the following parameters are from the <TABLE>-Tag
+ sal_uInt16 m_nWidth; // width of the table
+ sal_uInt16 m_nHeight; // absolute height of the table
+ SvxAdjust m_eTableAdjust; // drawing::Alignment of the table
+ sal_Int16 m_eVertOrientation; // Default vertical direction of the cells
+ sal_uInt16 m_nBorder; // width of the external border
+ HTMLTableFrame m_eFrame; // frame around the table
+ HTMLTableRules m_eRules; // frame in the table
+ bool m_bTopCaption; // Caption of the table
+
+ void InitCtor(const HTMLTableOptions& rOptions);
+
+ // Correction of the Row-Spans for all cells above the chosen cell and the cell itself for the indicated content. The chosen cell gets the Row-Span 1
+ void FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, const HTMLTableCnts *pCnts );
+
+ // Protects the chosen cell and the cells among
+ void ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan );
+
+ // Looking for the SwStartNodes of the box ahead
+ // If nRow==nCell==USHRT_MAX, return the last Start-Node of the table.
+ const SwStartNode* GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCell ) const;
+
+ sal_uInt16 GetTopCellSpace( sal_uInt16 nRow ) const;
+ sal_uInt16 GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const;
+
+ // Conforming of the frame::Frame-Format of the box
+ void FixFrameFormat( SwTableBox *pBox, sal_uInt16 nRow, sal_uInt16 nCol,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ bool bFirstPara=true, bool bLastPara=true ) const;
+
+ // Create a table with the content (lines/boxes)
+ void MakeTable_( SwTableBox *pUpper );
+
+ // Generate a new SwTableBox, which contains a SwStartNode
+ SwTableBox *NewTableBox( const SwStartNode *pStNd,
+ SwTableLine *pUpper ) const;
+
+ // Generate a SwTableLine from the cells of the rectangle
+ // (nTopRow/nLeftCol) inclusive to (nBottomRow/nRightRow) exclusive
+ SwTableLine *MakeTableLine( SwTableBox *pUpper,
+ sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
+ sal_uInt16 nBottomRow, sal_uInt16 nRightCol );
+
+ // Generate a SwTableBox from the content of the cell
+ SwTableBox *MakeTableBox( SwTableLine *pUpper,
+ HTMLTableCnts *pCnts,
+ sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
+ sal_uInt16 nBootomRow, sal_uInt16 nRightCol );
+
+ // Autolayout-Algorithm
+
+ // Setting the border with the help of guidelines of the Parent-Table
+ void InheritBorders( const HTMLTable *pParent,
+ sal_uInt16 nRow, sal_uInt16 nCol,
+ sal_uInt16 nRowSpan,
+ bool bFirstPara, bool bLastPara );
+
+ // Inherit the left and the right border of the surrounding table
+ void InheritVertBorders( const HTMLTable *pParent,
+ sal_uInt16 nCol, sal_uInt16 nColSpan );
+
+ // Set the border with the help of the information from the user
+ void SetBorders();
+
+ // is the border already set?
+ bool BordersSet() const { return m_bBordersSet; }
+
+ const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBackgroundBrush; }
+ const std::unique_ptr<SvxBrushItem>& GetInhBGBrush() const { return m_xInheritedBackgroundBrush; }
+
+ sal_uInt16 GetBorderWidth( const SvxBorderLine& rBLine,
+ bool bWithDistance=false ) const;
+
+ virtual void ObjectInDestruction(const SdrObject& rObject) override;
+
+public:
+
+ bool m_bFirstCell; // is there a cell created already?
+
+ HTMLTable(SwHTMLParser* pPars,
+ bool bParHead, bool bHasParentSec,
+ bool bHasToFly,
+ const HTMLTableOptions& rOptions);
+
+ virtual ~HTMLTable();
+
+ // Identifying of a cell
+ const HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const;
+ HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell)
+ {
+ return const_cast<HTMLTableCell&>(const_cast<const HTMLTable&>(*this).GetCell(nRow, nCell));
+ }
+
+ // set/determine caption
+ inline void SetCaption( const SwStartNode *pStNd, bool bTop );
+ const SwStartNode *GetCaptionStartNode() const { return m_pCaptionStartNode; }
+ bool IsTopCaption() const { return m_bTopCaption; }
+
+ SvxAdjust GetTableAdjust( bool bAny ) const
+ {
+ return (m_bTableAdjustOfTag || bAny) ? m_eTableAdjust : SvxAdjust::End;
+ }
+
+ sal_uInt16 GetHSpace() const { return m_nHSpace; }
+ sal_uInt16 GetVSpace() const { return m_nVSpace; }
+
+ // get inherited drawing::Alignment of rows and column
+ SvxAdjust GetInheritedAdjust() const;
+ sal_Int16 GetInheritedVertOri() const;
+
+ // Insert a cell on the current position
+ void InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ sal_uInt16 nWidth, bool bRelWidth, sal_uInt16 nHeight,
+ sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrushItem,
+ std::shared_ptr<SvxBoxItem> const& rBoxItem,
+ bool bHasNumFormat, sal_uInt32 nNumFormat,
+ bool bHasValue, double nValue, bool bNoWrap );
+
+ // announce the start/end of a new row
+ void OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOri, std::unique_ptr<SvxBrushItem>& rBGBrush);
+ void CloseRow( bool bEmpty );
+
+ // announce the end of a new section
+ inline void CloseSection( bool bHead );
+
+ // announce the end of a column-group
+ inline void CloseColGroup( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
+ SvxAdjust eAdjust, sal_Int16 eVertOri );
+
+ // insert a new column
+ void InsertCol( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
+ SvxAdjust eAdjust, sal_Int16 eVertOri );
+
+ // End a table definition (needs to be called for every table)
+ void CloseTable();
+
+ // Construct a SwTable (including child tables)
+ void MakeTable( SwTableBox *pUpper, sal_uInt16 nAbsAvail,
+ sal_uInt16 nRelAvail=0, sal_uInt16 nAbsLeftSpace=0,
+ sal_uInt16 nAbsRightSpace=0, sal_uInt16 nInhAbsSpace=0 );
+
+ bool IsNewDoc() const { return m_pParser->IsNewDoc(); }
+
+ void SetHasParentSection( bool bSet ) { m_bHasParentSection = bSet; }
+ bool HasParentSection() const { return m_bHasParentSection; }
+
+ void SetParentContents(std::unique_ptr<HTMLTableCnts> pCnts) { m_xParentContents = std::move(pCnts); }
+ std::unique_ptr<HTMLTableCnts>& GetParentContents() { return m_xParentContents; }
+
+ void MakeParentContents();
+
+ bool HasToFly() const { return m_bHasToFly; }
+
+ void SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt,
+ sal_uInt16 nLeft, sal_uInt16 nRight,
+ const SwTable *pSwTab=nullptr, bool bFrcFrame=false );
+
+ HTMLTableContext *GetContext() const { return m_pContext.get(); }
+
+ const std::shared_ptr<SwHTMLTableLayout>& CreateLayoutInfo();
+
+ bool HasColTags() const { return m_bColSpec; }
+
+ sal_uInt16 IncGrfsThatResize() { return m_pSwTable ? const_cast<SwTable *>(m_pSwTable)->IncGrfsThatResize() : 0; }
+
+ void RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth );
+
+ const SwTable *GetSwTable() const { return m_pSwTable; }
+
+ void SetBGBrush(const SvxBrushItem& rBrush) { m_xBackgroundBrush.reset(new SvxBrushItem(rBrush)); }
+
+ const OUString& GetId() const { return m_aId; }
+ const OUString& GetClass() const { return m_aClass; }
+ const OUString& GetStyle() const { return m_aStyle; }
+ const OUString& GetDirection() const { return m_aDir; }
+
+ void IncBoxCount() { m_nBoxes++; }
+ bool IsOverflowing() const { return m_nBoxes > 64000; }
+};
+
+void HTMLTableCnts::InitCtor()
+{
+ m_pNext = nullptr;
+ m_xLayoutInfo.reset();
+ m_bNoBreak = false;
+}
+
+HTMLTableCnts::HTMLTableCnts(const SwStartNode* pStNd)
+ : m_pStartNode(pStNd)
+{
+ InitCtor();
+}
+
+HTMLTableCnts::HTMLTableCnts(std::shared_ptr<HTMLTable> xTab)
+ : m_pStartNode(nullptr)
+ , m_xTable(std::move(xTab))
+{
+ InitCtor();
+}
+
+HTMLTableCnts::~HTMLTableCnts()
+{
+ m_xTable.reset(); // we don't need the tables anymore
+ m_pNext.reset();
+}
+
+void HTMLTableCnts::Add( std::unique_ptr<HTMLTableCnts> pNewCnts )
+{
+ HTMLTableCnts *pCnts = this;
+
+ while( pCnts->m_pNext )
+ pCnts = pCnts->m_pNext.get();
+
+ pCnts->m_pNext = std::move(pNewCnts);
+}
+
+inline void HTMLTableCnts::SetTableBox( SwTableBox *pBox )
+{
+ OSL_ENSURE(m_xLayoutInfo, "There is no layout info");
+ if (m_xLayoutInfo)
+ m_xLayoutInfo->SetTableBox(pBox);
+}
+
+const std::shared_ptr<SwHTMLTableLayoutCnts>& HTMLTableCnts::CreateLayoutInfo()
+{
+ if (!m_xLayoutInfo)
+ {
+ std::shared_ptr<SwHTMLTableLayoutCnts> xNextInfo;
+ if (m_pNext)
+ xNextInfo = m_pNext->CreateLayoutInfo();
+ std::shared_ptr<SwHTMLTableLayout> xTableInfo;
+ if (m_xTable)
+ xTableInfo = m_xTable->CreateLayoutInfo();
+ m_xLayoutInfo = std::make_shared<SwHTMLTableLayoutCnts>(m_pStartNode, xTableInfo, m_bNoBreak, xNextInfo);
+ }
+
+ return m_xLayoutInfo;
+}
+
+HTMLTableCell::HTMLTableCell():
+ m_nValue(0),
+ m_nNumFormat(0),
+ m_nRowSpan(1),
+ m_nColSpan(1),
+ m_nWidth( 0 ),
+ m_eVertOrient( text::VertOrientation::NONE ),
+ m_bProtected(false),
+ m_bRelWidth( false ),
+ m_bHasNumFormat(false),
+ m_bHasValue(false),
+ m_bNoWrap(false),
+ mbCovered(false)
+{}
+
+void HTMLTableCell::Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan,
+ sal_Int16 eVert, std::shared_ptr<SvxBrushItem> const& rBrush,
+ std::shared_ptr<SvxBoxItem> const& rBoxItem,
+ bool bHasNF, sal_uInt32 nNF, bool bHasV, double nVal,
+ bool bNWrap, bool bCovered )
+{
+ m_xContents = rCnts;
+ m_nRowSpan = nRSpan;
+ m_nColSpan = nCSpan;
+ m_bProtected = false;
+ m_eVertOrient = eVert;
+ m_xBGBrush = rBrush;
+ m_xBoxItem = rBoxItem;
+
+ m_bHasNumFormat = bHasNF;
+ m_bHasValue = bHasV;
+ m_nNumFormat = nNF;
+ m_nValue = nVal;
+
+ m_bNoWrap = bNWrap;
+ mbCovered = bCovered;
+}
+
+inline void HTMLTableCell::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
+{
+ m_nWidth = nWdth;
+ m_bRelWidth = bRelWdth;
+}
+
+void HTMLTableCell::SetProtected()
+{
+ // The content of this cell doesn't have to be anchored anywhere else,
+ // since they're not gonna be deleted
+
+ m_xContents.reset();
+
+ // Copy background color
+ if (m_xBGBrush)
+ m_xBGBrush = std::make_shared<SvxBrushItem>(*m_xBGBrush);
+
+ m_nRowSpan = 1;
+ m_nColSpan = 1;
+ m_bProtected = true;
+}
+
+inline bool HTMLTableCell::GetNumFormat( sal_uInt32& rNumFormat ) const
+{
+ rNumFormat = m_nNumFormat;
+ return m_bHasNumFormat;
+}
+
+inline bool HTMLTableCell::GetValue( double& rValue ) const
+{
+ rValue = m_nValue;
+ return m_bHasValue;
+}
+
+std::unique_ptr<SwHTMLTableLayoutCell> HTMLTableCell::CreateLayoutInfo()
+{
+ std::shared_ptr<SwHTMLTableLayoutCnts> xCntInfo;
+ if (m_xContents)
+ xCntInfo = m_xContents->CreateLayoutInfo();
+ return std::unique_ptr<SwHTMLTableLayoutCell>(new SwHTMLTableLayoutCell(xCntInfo, m_nRowSpan, m_nColSpan, m_nWidth,
+ m_bRelWidth, m_bNoWrap));
+}
+
+HTMLTableRow::HTMLTableRow(sal_uInt16 const nCells)
+ : m_aCells(nCells)
+ , m_eAdjust(SvxAdjust::End)
+ , m_nHeight(0)
+ , m_nEmptyRows(0)
+ , m_eVertOri(text::VertOrientation::TOP)
+ , m_bIsEndOfGroup(false)
+ , m_bBottomBorder(false)
+{
+ assert(nCells == m_aCells.size() &&
+ "wrong Cell count in new HTML table row");
+}
+
+inline void HTMLTableRow::SetHeight( sal_uInt16 nHght )
+{
+ if( nHght > m_nHeight )
+ m_nHeight = nHght;
+}
+
+const HTMLTableCell& HTMLTableRow::GetCell(sal_uInt16 nCell) const
+{
+ OSL_ENSURE( nCell < m_aCells.size(),
+ "invalid cell index in HTML table row" );
+ return m_aCells.at(nCell);
+}
+
+void HTMLTableRow::Expand( sal_uInt16 nCells, bool bOneCell )
+{
+ // This row will be filled with a single cell if bOneCell is set.
+ // This will only work for rows that don't allow adding cells!
+
+ sal_uInt16 nColSpan = nCells - m_aCells.size();
+ for (sal_uInt16 i = m_aCells.size(); i < nCells; ++i)
+ {
+ m_aCells.emplace_back();
+ if (bOneCell)
+ m_aCells.back().SetColSpan(nColSpan);
+ --nColSpan;
+ }
+
+ OSL_ENSURE(nCells == m_aCells.size(),
+ "wrong Cell count in expanded HTML table row");
+}
+
+void HTMLTableRow::Shrink( sal_uInt16 nCells )
+{
+ OSL_ENSURE(nCells < m_aCells.size(), "number of cells too large");
+
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt16 const nEnd = m_aCells.size();
+#endif
+ // The colspan of empty cells at the end has to be fixed to the new
+ // number of cells.
+ sal_uInt16 i=nCells;
+ while( i )
+ {
+ HTMLTableCell& rCell = m_aCells[--i];
+ if (!rCell.GetContents())
+ {
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( rCell.GetColSpan() == nEnd - i,
+ "invalid col span for empty cell at row end" );
+#endif
+ rCell.SetColSpan( nCells-i);
+ }
+ else
+ break;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ for( i=nCells; i<nEnd; i++ )
+ {
+ HTMLTableCell& rCell = m_aCells[i];
+ OSL_ENSURE( rCell.GetRowSpan() == 1,
+ "RowSpan of to be deleted cell is wrong" );
+ OSL_ENSURE( rCell.GetColSpan() == nEnd - i,
+ "ColSpan of to be deleted cell is wrong" );
+ OSL_ENSURE( !rCell.GetContents(), "To be deleted cell has content" );
+ }
+#endif
+
+ m_aCells.erase(m_aCells.begin() + nCells, m_aCells.end());
+}
+
+HTMLTableColumn::HTMLTableColumn():
+ m_bIsEndOfGroup(false),
+ m_nWidth(0), m_bRelWidth(false),
+ m_eAdjust(SvxAdjust::End), m_eVertOri(text::VertOrientation::TOP),
+ m_bLeftBorder(false)
+{
+ for(SwFrameFormat* & rp : m_aFrameFormats)
+ rp = nullptr;
+}
+
+inline void HTMLTableColumn::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
+{
+ if( m_bRelWidth==bRelWdth )
+ {
+ if( nWdth > m_nWidth )
+ m_nWidth = nWdth;
+ }
+ else
+ m_nWidth = nWdth;
+ m_bRelWidth = bRelWdth;
+}
+
+inline std::unique_ptr<SwHTMLTableLayoutColumn> HTMLTableColumn::CreateLayoutInfo()
+{
+ return std::unique_ptr<SwHTMLTableLayoutColumn>(new SwHTMLTableLayoutColumn( m_nWidth, m_bRelWidth, m_bLeftBorder ));
+}
+
+inline sal_uInt16 HTMLTableColumn::GetFrameFormatIdx( bool bBorderLine,
+ sal_Int16 eVertOrient )
+{
+ OSL_ENSURE( text::VertOrientation::TOP != eVertOrient, "Top is not allowed" );
+ sal_uInt16 n = bBorderLine ? 3 : 0;
+ switch( eVertOrient )
+ {
+ case text::VertOrientation::CENTER: n+=1; break;
+ case text::VertOrientation::BOTTOM: n+=2; break;
+ default:
+ ;
+ }
+ return n;
+}
+
+inline void HTMLTableColumn::SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
+ sal_Int16 eVertOrient )
+{
+ m_aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)] = pFormat;
+}
+
+inline SwFrameFormat *HTMLTableColumn::GetFrameFormat( bool bBorderLine,
+ sal_Int16 eVertOrient ) const
+{
+ return m_aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)];
+}
+
+void HTMLTable::InitCtor(const HTMLTableOptions& rOptions)
+{
+ m_nRows = 0;
+ m_nCurrentRow = 0; m_nCurrentColumn = 0;
+
+ m_pBoxFormat = nullptr; m_pLineFormat = nullptr;
+ m_pLineFrameFormatNoHeight = nullptr;
+ m_xInheritedBackgroundBrush.reset();
+
+ m_pPrevStartNode = nullptr;
+ m_pSwTable = nullptr;
+
+ m_bTopBorder = false; m_bRightBorder = false;
+ m_bTopAllowed = true; m_bRightAllowed = true;
+ m_bFillerTopBorder = false; m_bFillerBottomBorder = false;
+ m_bInheritedLeftBorder = false; m_bInheritedRightBorder = false;
+ m_bBordersSet = false;
+ m_bForceFrame = false;
+ m_nHeadlineRepeat = 0;
+
+ m_nLeftMargin = 0;
+ m_nRightMargin = 0;
+
+ const Color& rBorderColor = rOptions.aBorderColor;
+
+ tools::Long nBorderOpt = static_cast<tools::Long>(rOptions.nBorder);
+ tools::Long nPWidth = nBorderOpt==USHRT_MAX ? NETSCAPE_DFLT_BORDER
+ : nBorderOpt;
+ tools::Long nPHeight = nBorderOpt==USHRT_MAX ? 0 : nBorderOpt;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+
+ // nBorder tells the width of the border as it's used in the width calculation of NetScape
+ // If pOption->nBorder == USHRT_MAX, there wasn't a BORDER option given
+ // Nonetheless, a 1 pixel wide border will be used for width calculation
+ m_nBorder = o3tl::narrowing<sal_uInt16>(nPWidth);
+ if( nBorderOpt==USHRT_MAX )
+ nPWidth = 0;
+
+ if ( rOptions.nCellSpacing != 0 )
+ {
+ m_aTopBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ }
+ m_aTopBorderLine.SetWidth( nPHeight );
+ m_aTopBorderLine.SetColor( rBorderColor );
+ m_aBottomBorderLine = m_aTopBorderLine;
+
+ if( nPWidth == nPHeight )
+ {
+ m_aLeftBorderLine = m_aTopBorderLine;
+ }
+ else
+ {
+ if ( rOptions.nCellSpacing != 0 )
+ {
+ m_aLeftBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ }
+ m_aLeftBorderLine.SetWidth( nPWidth );
+ m_aLeftBorderLine.SetColor( rBorderColor );
+ }
+ m_aRightBorderLine = m_aLeftBorderLine;
+
+ if( rOptions.nCellSpacing != 0 )
+ m_aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ m_aBorderLine.SetWidth(SvxBorderLineWidth::Hairline);
+ m_aBorderLine.SetColor( rBorderColor );
+
+ if( m_nCellPadding )
+ {
+ if( m_nCellPadding==USHRT_MAX )
+ m_nCellPadding = MIN_BORDER_DIST; // default
+ else
+ {
+ m_nCellPadding = SwHTMLParser::ToTwips( m_nCellPadding );
+ if( m_nCellPadding<MIN_BORDER_DIST )
+ m_nCellPadding = MIN_BORDER_DIST;
+ }
+ }
+ if( m_nCellSpacing )
+ {
+ if( m_nCellSpacing==USHRT_MAX )
+ m_nCellSpacing = NETSCAPE_DFLT_CELLSPACING;
+ m_nCellSpacing = SwHTMLParser::ToTwips( m_nCellSpacing );
+ }
+
+ nPWidth = rOptions.nHSpace;
+ nPHeight = rOptions.nVSpace;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ m_nHSpace = o3tl::narrowing<sal_uInt16>(nPWidth);
+ m_nVSpace = o3tl::narrowing<sal_uInt16>(nPHeight);
+
+ m_bColSpec = false;
+
+ m_xBackgroundBrush.reset(m_pParser->CreateBrushItem(
+ rOptions.bBGColor ? &(rOptions.aBGColor) : nullptr,
+ rOptions.aBGImage, OUString(), OUString(), OUString()));
+
+ m_pContext = nullptr;
+ m_xParentContents.reset();
+
+ m_aId = rOptions.aId;
+ m_aClass = rOptions.aClass;
+ m_aStyle = rOptions.aStyle;
+ m_aDir = rOptions.aDir;
+}
+
+HTMLTable::HTMLTable(SwHTMLParser* pPars,
+ bool bParHead,
+ bool bHasParentSec, bool bHasToFlw,
+ const HTMLTableOptions& rOptions) :
+ m_aColumns(rOptions.nCols),
+ m_nCols(rOptions.nCols),
+ m_nFilledColumns( 0 ),
+ m_nCellPadding(rOptions.nCellPadding),
+ m_nCellSpacing(rOptions.nCellSpacing),
+ m_nBoxes( 1 ),
+ m_pCaptionStartNode( nullptr ),
+ m_bTableAdjustOfTag( rOptions.bTableAdjust ),
+ m_bIsParentHead( bParHead ),
+ m_bHasParentSection( bHasParentSec ),
+ m_bHasToFly( bHasToFlw ),
+ m_bFixedCols( rOptions.nCols>0 ),
+ m_bPercentWidth( rOptions.bPercentWidth ),
+ m_pParser( pPars ),
+ m_nWidth( rOptions.nWidth ),
+ m_nHeight( rOptions.nHeight ),
+ m_eTableAdjust( rOptions.eAdjust ),
+ m_eVertOrientation( rOptions.eVertOri ),
+ m_eFrame( rOptions.eFrame ),
+ m_eRules( rOptions.eRules ),
+ m_bTopCaption( false ),
+ m_bFirstCell(true)
+{
+ InitCtor(rOptions);
+ m_pParser->RegisterHTMLTable(this);
+}
+
+void SwHTMLParser::DeregisterHTMLTable(HTMLTable* pOld)
+{
+ if (pOld->m_xBox1)
+ m_aOrphanedTableBoxes.emplace_back(std::move(pOld->m_xBox1));
+ std::erase(m_aTables, pOld);
+}
+
+SwDoc* SwHTMLParser::GetDoc() const
+{
+ return m_xDoc.get();
+}
+
+bool SwHTMLParser::IsReqIF() const
+{
+ return m_bReqIF;
+}
+
+// if any m_xResizeDrawObjects members are deleted during parse, remove them
+// from m_xResizeDrawObjects and m_xDrawObjectPercentWidths
+void HTMLTable::ObjectInDestruction(const SdrObject& rObject)
+{
+ auto it = std::find(m_xResizeDrawObjects->begin(), m_xResizeDrawObjects->end(), &rObject);
+ assert(it != m_xResizeDrawObjects->end());
+ auto nIndex = std::distance(m_xResizeDrawObjects->begin(), it);
+ m_xResizeDrawObjects->erase(it);
+ auto otherit = m_xDrawObjectPercentWidths->begin() + nIndex * 3;
+ m_xDrawObjectPercentWidths->erase(otherit, otherit + 3);
+}
+
+HTMLTable::~HTMLTable()
+{
+ m_pParser->DeregisterHTMLTable(this);
+
+ if (m_xResizeDrawObjects)
+ {
+ size_t nCount = m_xResizeDrawObjects->size();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrObject *pObj = (*m_xResizeDrawObjects)[i];
+ pObj->RemoveObjectUser(*this);
+ }
+ m_xResizeDrawObjects.reset();
+ }
+
+ m_xDrawObjectPercentWidths.reset();
+
+ m_pContext.reset();
+
+ // pLayoutInfo has either already been deleted or is now owned by SwTable
+}
+
+const std::shared_ptr<SwHTMLTableLayout>& HTMLTable::CreateLayoutInfo()
+{
+ sal_uInt16 nW = m_bPercentWidth ? m_nWidth : SwHTMLParser::ToTwips( m_nWidth );
+
+ sal_uInt16 nBorderWidth = GetBorderWidth( m_aBorderLine, true );
+ sal_uInt16 nLeftBorderWidth =
+ m_aColumns[0].m_bLeftBorder ? GetBorderWidth(m_aLeftBorderLine, true) : 0;
+ sal_uInt16 nRightBorderWidth =
+ m_bRightBorder ? GetBorderWidth( m_aRightBorderLine, true ) : 0;
+
+ m_xLayoutInfo = std::make_shared<SwHTMLTableLayout>(
+ m_pSwTable,
+ m_nRows, m_nCols, m_bFixedCols, m_bColSpec,
+ nW, m_bPercentWidth, m_nBorder, m_nCellPadding,
+ m_nCellSpacing, m_eTableAdjust,
+ m_nLeftMargin, m_nRightMargin,
+ nBorderWidth, nLeftBorderWidth, nRightBorderWidth);
+
+ bool bExportable = true;
+ sal_uInt16 i;
+ for( i=0; i<m_nRows; i++ )
+ {
+ HTMLTableRow& rRow = m_aRows[i];
+ for( sal_uInt16 j=0; j<m_nCols; j++ )
+ {
+ m_xLayoutInfo->SetCell(rRow.GetCell(j).CreateLayoutInfo(), i, j);
+ SwHTMLTableLayoutCell* pLayoutCell = m_xLayoutInfo->GetCell(i, j );
+
+ if( bExportable )
+ {
+ const std::shared_ptr<SwHTMLTableLayoutCnts>& rLayoutCnts =
+ pLayoutCell->GetContents();
+ bExportable = !rLayoutCnts ||
+ (rLayoutCnts->GetStartNode() && !rLayoutCnts->GetNext());
+ }
+ }
+ }
+
+ m_xLayoutInfo->SetExportable( bExportable );
+
+ for( i=0; i<m_nCols; i++ )
+ m_xLayoutInfo->SetColumn(m_aColumns[i].CreateLayoutInfo(), i);
+
+ return m_xLayoutInfo;
+}
+
+inline void HTMLTable::SetCaption( const SwStartNode *pStNd, bool bTop )
+{
+ m_pCaptionStartNode = pStNd;
+ m_bTopCaption = bTop;
+}
+
+void HTMLTable::FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol,
+ const HTMLTableCnts *pCnts )
+{
+ sal_uInt16 nRowSpan=1;
+ while (true)
+ {
+ HTMLTableCell& rCell = GetCell(nRow, nCol);
+ if (rCell.GetContents().get() != pCnts)
+ break;
+ rCell.SetRowSpan(nRowSpan);
+ if (m_xLayoutInfo)
+ m_xLayoutInfo->GetCell(nRow,nCol)->SetRowSpan(nRowSpan);
+
+ if( !nRow ) break;
+ nRowSpan++; nRow--;
+ }
+}
+
+void HTMLTable::ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan )
+{
+ for( sal_uInt16 i=0; i<nRowSpan; i++ )
+ {
+ GetCell(nRow+i,nCol).SetProtected();
+ if (m_xLayoutInfo)
+ m_xLayoutInfo->GetCell(nRow+i,nCol)->SetProtected();
+ }
+}
+
+// Search the SwStartNode of the last used predecessor box
+const SwStartNode* HTMLTable::GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCol ) const
+{
+ const HTMLTableCnts *pPrevCnts = nullptr;
+
+ if( 0==nRow )
+ {
+ // always the predecessor cell
+ if( nCol>0 )
+ pPrevCnts = GetCell(0, nCol - 1).GetContents().get();
+ else
+ return m_pPrevStartNode;
+ }
+ else if( USHRT_MAX==nRow && USHRT_MAX==nCol )
+ // contents of preceding cell
+ pPrevCnts = GetCell(m_nRows - 1, m_nCols - 1).GetContents().get();
+ else
+ {
+ sal_uInt16 i;
+ const HTMLTableRow& rPrevRow = m_aRows[nRow-1];
+
+ // maybe a cell in the current row
+ i = nCol;
+ while( i )
+ {
+ i--;
+ if( 1 == rPrevRow.GetCell(i).GetRowSpan() )
+ {
+ pPrevCnts = GetCell(nRow, i).GetContents().get();
+ break;
+ }
+ }
+
+ // otherwise the last filled cell of the row before
+ if( !pPrevCnts )
+ {
+ i = m_nCols;
+ while( !pPrevCnts && i )
+ {
+ i--;
+ pPrevCnts = rPrevRow.GetCell(i).GetContents().get();
+ }
+ }
+ }
+ OSL_ENSURE( pPrevCnts, "No previous filled cell found" );
+ if( !pPrevCnts )
+ {
+ pPrevCnts = GetCell(0, 0).GetContents().get();
+ if( !pPrevCnts )
+ return m_pPrevStartNode;
+ }
+
+ while( pPrevCnts->Next() )
+ pPrevCnts = pPrevCnts->Next();
+
+ const SwStartNode* pRet = pPrevCnts->GetStartNode();
+ if (pRet)
+ return pRet;
+ HTMLTable* pTable = pPrevCnts->GetTable().get();
+ if (!pTable)
+ return nullptr;
+ return pTable->GetPrevBoxStartNode(USHRT_MAX, USHRT_MAX);
+}
+
+sal_uInt16 HTMLTable::GetTopCellSpace( sal_uInt16 nRow ) const
+{
+ sal_uInt16 nSpace = m_nCellPadding;
+
+ if( nRow == 0 )
+ {
+ nSpace += m_nBorder + m_nCellSpacing;
+ }
+
+ return nSpace;
+}
+
+sal_uInt16 HTMLTable::GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const
+{
+ sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
+
+ if( nRow+nRowSpan == m_nRows )
+ {
+ nSpace = nSpace + m_nBorder;
+ }
+
+ return nSpace;
+}
+
+void HTMLTable::FixFrameFormat( SwTableBox *pBox,
+ sal_uInt16 nRow, sal_uInt16 nCol,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ bool bFirstPara, bool bLastPara ) const
+{
+ SwFrameFormat *pFrameFormat = nullptr; // frame::Frame format
+ sal_Int16 eVOri = text::VertOrientation::NONE;
+ const SvxBrushItem *pBGBrushItem = nullptr; // background
+ std::shared_ptr<SvxBoxItem> pBoxItem;
+ bool bTopLine = false, bBottomLine = false, bLastBottomLine = false;
+ bool bReUsable = false; // Format reusable?
+ sal_uInt16 nEmptyRows = 0;
+ bool bHasNumFormat = false;
+ bool bHasValue = false;
+ sal_uInt32 nNumFormat = 0;
+ double nValue = 0.0;
+
+ const HTMLTableColumn& rColumn = m_aColumns[nCol];
+
+ if( pBox->GetSttNd() )
+ {
+ // Determine background color/graphic
+ const HTMLTableCell& rCell = GetCell(nRow, nCol);
+ pBoxItem = rCell.GetBoxItem();
+ pBGBrushItem = rCell.GetBGBrush().get();
+ if( !pBGBrushItem )
+ {
+ // If a cell spans multiple rows, a background to that row should be copied to the cell.
+ if (nRowSpan > 1)
+ {
+ pBGBrushItem = m_aRows[nRow].GetBGBrush().get();
+ }
+ }
+
+ bTopLine = 0==nRow && m_bTopBorder && bFirstPara;
+ if (m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
+ {
+ nEmptyRows = m_aRows[nRow+nRowSpan-1].GetEmptyRows();
+ if( nRow+nRowSpan == m_nRows )
+ bLastBottomLine = true;
+ else
+ bBottomLine = true;
+ }
+
+ eVOri = rCell.GetVertOri();
+ bHasNumFormat = rCell.GetNumFormat( nNumFormat );
+ if( bHasNumFormat )
+ bHasValue = rCell.GetValue( nValue );
+
+ if( nColSpan==1 && !bTopLine && !bLastBottomLine && !nEmptyRows &&
+ !pBGBrushItem && !bHasNumFormat && !pBoxItem)
+ {
+ pFrameFormat = rColumn.GetFrameFormat( bBottomLine, eVOri );
+ bReUsable = !pFrameFormat;
+ }
+ }
+
+ if( !pFrameFormat )
+ {
+ pFrameFormat = pBox->ClaimFrameFormat();
+
+ // calculate width of the box
+ SwTwips nFrameWidth = static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol)
+ ->GetRelColWidth());
+ for( sal_uInt16 i=1; i<nColSpan; i++ )
+ nFrameWidth += static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol+i)
+ ->GetRelColWidth());
+
+ // Only set the border on edit boxes.
+ // On setting the upper and lower border, keep in mind if
+ // it's the first or the last paragraph of the cell
+ if( pBox->GetSttNd() )
+ {
+ bool bSet = (m_nCellPadding > 0);
+
+ SvxBoxItem aBoxItem( RES_BOX );
+ tools::Long nInnerFrameWidth = nFrameWidth;
+
+ if( bTopLine )
+ {
+ aBoxItem.SetLine( &m_aTopBorderLine, SvxBoxItemLine::TOP );
+ bSet = true;
+ }
+ if( bLastBottomLine )
+ {
+ aBoxItem.SetLine( &m_aBottomBorderLine, SvxBoxItemLine::BOTTOM );
+ bSet = true;
+ }
+ else if( bBottomLine )
+ {
+ if( nEmptyRows && !m_aBorderLine.GetInWidth() )
+ {
+ // For now, empty rows can only be emulated by thick lines, if it's a single line
+ SvxBorderLine aThickBorderLine( m_aBorderLine );
+
+ sal_uInt16 nBorderWidth = m_aBorderLine.GetOutWidth();
+ nBorderWidth *= (nEmptyRows + 1);
+ aThickBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
+ aThickBorderLine.SetWidth( nBorderWidth );
+ aBoxItem.SetLine( &aThickBorderLine, SvxBoxItemLine::BOTTOM );
+ }
+ else
+ {
+ aBoxItem.SetLine( &m_aBorderLine, SvxBoxItemLine::BOTTOM );
+ }
+ bSet = true;
+ }
+ if (m_aColumns[nCol].m_bLeftBorder)
+ {
+ const SvxBorderLine& rBorderLine =
+ 0==nCol ? m_aLeftBorderLine : m_aBorderLine;
+ aBoxItem.SetLine( &rBorderLine, SvxBoxItemLine::LEFT );
+ nInnerFrameWidth -= GetBorderWidth( rBorderLine );
+ bSet = true;
+ }
+ if( m_bRightBorder )
+ {
+ aBoxItem.SetLine( &m_aRightBorderLine, SvxBoxItemLine::RIGHT );
+ nInnerFrameWidth -= GetBorderWidth( m_aRightBorderLine );
+ bSet = true;
+ }
+
+ if (pBoxItem)
+ {
+ pFrameFormat->SetFormatAttr( *pBoxItem );
+ }
+ else if (bSet)
+ {
+ // BorderDist is not part of a cell with fixed width
+ sal_uInt16 nBDist = static_cast< sal_uInt16 >(
+ (2*m_nCellPadding <= nInnerFrameWidth) ? m_nCellPadding
+ : (nInnerFrameWidth / 2) );
+ // We only set the item if there's a border or a border distance
+ // If the latter is missing, there's gonna be a border and we'll have to set the distance
+ aBoxItem.SetAllDistances(nBDist ? nBDist : MIN_BORDER_DIST);
+ pFrameFormat->SetFormatAttr( aBoxItem );
+ }
+ else
+ pFrameFormat->ResetFormatAttr( RES_BOX );
+
+ if( pBGBrushItem )
+ {
+ pFrameFormat->SetFormatAttr( *pBGBrushItem );
+ }
+ else
+ pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
+
+ // Only set format if there's a value or the box is empty
+ if( bHasNumFormat && (bHasValue || pBox->IsEmpty()) )
+ {
+ bool bLock = pFrameFormat->GetDoc()->GetNumberFormatter()
+ ->IsTextFormat( nNumFormat );
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>
+ aItemSet( *pFrameFormat->GetAttrSet().GetPool() );
+ SvxAdjust eAdjust = SvxAdjust::End;
+ SwContentNode *pCNd = nullptr;
+ if( !bLock )
+ {
+ const SwStartNode *pSttNd = pBox->GetSttNd();
+ pCNd = pSttNd->GetNodes()[pSttNd->GetIndex()+1]
+ ->GetContentNode();
+ const SvxAdjustItem *pItem;
+ if( pCNd && pCNd->HasSwAttrSet() &&
+ (pItem = pCNd->GetpSwAttrSet()->GetItemIfSet(
+ RES_PARATR_ADJUST, false )) )
+ {
+ eAdjust = pItem->GetAdjust();
+ }
+ }
+ aItemSet.Put( SwTableBoxNumFormat(nNumFormat) );
+ if( bHasValue )
+ aItemSet.Put( SwTableBoxValue(nValue) );
+
+ if( bLock )
+ pFrameFormat->LockModify();
+ pFrameFormat->SetFormatAttr( aItemSet );
+ if( bLock )
+ pFrameFormat->UnlockModify();
+ else if( pCNd && SvxAdjust::End != eAdjust )
+ {
+ SvxAdjustItem aAdjItem( eAdjust, RES_PARATR_ADJUST );
+ pCNd->SetAttr( aAdjItem );
+ }
+ }
+ else
+ pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
+
+ OSL_ENSURE( eVOri != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
+ if( text::VertOrientation::NONE != eVOri )
+ {
+ pFrameFormat->SetFormatAttr( SwFormatVertOrient( 0, eVOri ) );
+ }
+ else
+ pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
+
+ if( bReUsable )
+ const_cast<HTMLTableColumn&>(rColumn).SetFrameFormat(pFrameFormat, bBottomLine, eVOri);
+ }
+ else
+ {
+ pFrameFormat->ResetFormatAttr( RES_BOX );
+ pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
+ pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
+ pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
+ }
+
+ if (m_pParser->IsReqIF())
+ {
+ // ReqIF case, cells would have no formatting. Apply the default
+ // table autoformat on them, so imported and UI-created tables look
+ // the same.
+ SwTableAutoFormatTable& rTable = m_pParser->GetDoc()->GetTableStyles();
+ SwTableAutoFormat* pTableFormat = rTable.FindAutoFormat(
+ SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_DEFAULT, OUString()));
+ if (pTableFormat)
+ {
+ sal_uInt8 nPos = SwTableAutoFormat::CountPos(nCol, m_nCols, nRow, m_nRows);
+ const SfxItemSet& rAttrSet = pFrameFormat->GetAttrSet();
+ std::unique_ptr<SvxBoxItem> pOldBoxItem;
+ if (const SvxBoxItem* pBoxItem2 = rAttrSet.GetItemIfSet(RES_BOX))
+ pOldBoxItem.reset(pBoxItem2->Clone());
+ pTableFormat->UpdateToSet(nPos, m_nRows==1, m_nCols==1,
+ const_cast<SfxItemSet&>(rAttrSet),
+ SwTableAutoFormatUpdateFlags::Box,
+ pFrameFormat->GetDoc()->GetNumberFormatter());
+ if (pOldBoxItem)
+ {
+ // There was an old item, so it's guaranteed that there's a new item
+ const SvxBoxItem* pBoxItem2(rAttrSet.GetItem(RES_BOX));
+ if (*pBoxItem2 != *pOldBoxItem)
+ {
+ std::unique_ptr<SvxBoxItem> pNewBoxItem(pBoxItem2->Clone());
+ // Restore the box elements that could have been already set
+ for (auto eLine : { SvxBoxItemLine::TOP, SvxBoxItemLine::BOTTOM,
+ SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT })
+ {
+ if (auto pLine = pOldBoxItem->GetLine(eLine))
+ pNewBoxItem->SetLine(pLine, eLine);
+ if (auto nDistance = pOldBoxItem->GetDistance(eLine, true))
+ pNewBoxItem->SetDistance(nDistance, eLine);
+ }
+
+ pFrameFormat->SetFormatAttr(*pNewBoxItem);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( pBox->GetSttNd() ||
+ SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState(
+ RES_VERT_ORIENT, false ),
+ "Box without content has vertical orientation" );
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFrameFormat) );
+ }
+
+}
+
+SwTableBox *HTMLTable::NewTableBox( const SwStartNode *pStNd,
+ SwTableLine *pUpper ) const
+{
+ SwTableBox *pBox;
+
+ if (m_xBox1 && m_xBox1->GetSttNd() == pStNd)
+ {
+ // If the StartNode is the StartNode of the initially created box, we take that box
+ pBox = const_cast<HTMLTable*>(this)->m_xBox1.release();
+ pBox->SetUpper(pUpper);
+ }
+ else
+ pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper );
+
+ return pBox;
+}
+
+static void ResetLineFrameFormatAttrs( SwFrameFormat *pFrameFormat )
+{
+ pFrameFormat->ResetFormatAttr( RES_FRM_SIZE );
+ pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
+ OSL_ENSURE( SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState(
+ RES_VERT_ORIENT, false ),
+ "Cell has vertical orientation" );
+}
+
+// !!! could be simplified
+SwTableLine *HTMLTable::MakeTableLine( SwTableBox *pUpper,
+ sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
+ sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
+{
+ SwTableLine *pLine;
+ if (!pUpper && 0 == nTopRow)
+ pLine = (m_pSwTable->GetTabLines())[0];
+ else
+ pLine = new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight
+ : m_pLineFormat,
+ 0, pUpper );
+
+ const HTMLTableRow& rTopRow = m_aRows[nTopRow];
+ sal_uInt16 nRowHeight = rTopRow.GetHeight();
+ const SvxBrushItem *pBGBrushItem = nullptr;
+ if (nTopRow > 0 || nBottomRow < m_nRows)
+ {
+ // It doesn't make sense to set a color on a line,
+ // if it's the outermost and simultaneously sole line of a table in a table
+ pBGBrushItem = rTopRow.GetBGBrush().get();
+ }
+ if( nTopRow==nBottomRow-1 && (nRowHeight || pBGBrushItem) )
+ {
+ SwTableLineFormat *pFrameFormat = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
+ ResetLineFrameFormatAttrs( pFrameFormat );
+
+ if( nRowHeight )
+ {
+ // set table height. Since it's a minimum height it can be calculated like in Netscape,
+ // so without considering the actual border width
+ nRowHeight += GetTopCellSpace( nTopRow ) +
+ GetBottomCellSpace( nTopRow, 1 );
+
+ pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, nRowHeight ) );
+ }
+
+ if( pBGBrushItem )
+ {
+ pFrameFormat->SetFormatAttr( *pBGBrushItem );
+ }
+
+ }
+ else if( !m_pLineFrameFormatNoHeight )
+ {
+ // else, we'll have to remove the height from the attribute and remember the format
+ m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
+
+ ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
+ }
+
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+
+ sal_uInt16 nStartCol = nLeftCol;
+ while( nStartCol<nRightCol )
+ {
+ sal_uInt16 nCol = nStartCol;
+ sal_uInt16 nSplitCol = nRightCol;
+ bool bSplitted = false;
+ while( !bSplitted )
+ {
+ OSL_ENSURE( nCol < nRightCol, "Gone too far" );
+
+ HTMLTableCell& rCell = GetCell(nTopRow,nCol);
+ const bool bSplit = 1 == rCell.GetColSpan();
+
+ OSL_ENSURE((nCol != nRightCol-1) || bSplit, "Split-Flag wrong");
+ if( bSplit )
+ {
+ SwTableBox* pBox = nullptr;
+ HTMLTableCell& rCell2 = GetCell(nTopRow, nStartCol);
+ if (rCell2.GetColSpan() == (nCol+1-nStartCol))
+ {
+ // The HTML tables represent a box. So we need to split behind that box
+ nSplitCol = nCol + 1;
+
+ sal_Int32 nBoxRowSpan = rCell2.GetRowSpan();
+ if (!rCell2.GetContents() || rCell2.IsCovered())
+ {
+ if (rCell2.IsCovered())
+ nBoxRowSpan = -1 * nBoxRowSpan;
+
+ const SwStartNode* pPrevStartNd =
+ GetPrevBoxStartNode( nTopRow, nStartCol );
+ auto xCnts = std::make_shared<HTMLTableCnts>(
+ m_pParser->InsertTableSection(pPrevStartNd));
+ const std::shared_ptr<SwHTMLTableLayoutCnts> xCntsLayoutInfo =
+ xCnts->CreateLayoutInfo();
+
+ rCell2.SetContents(xCnts);
+ SwHTMLTableLayoutCell *pCurrCell = m_xLayoutInfo->GetCell(nTopRow, nStartCol);
+ pCurrCell->SetContents(xCntsLayoutInfo);
+ if( nBoxRowSpan < 0 )
+ pCurrCell->SetRowSpan( 0 );
+
+ // check COLSPAN if needed
+ for( sal_uInt16 j=nStartCol+1; j<nSplitCol; j++ )
+ {
+ GetCell(nTopRow, j).SetContents(xCnts);
+ m_xLayoutInfo->GetCell(nTopRow, j)
+ ->SetContents(xCntsLayoutInfo);
+ }
+ }
+
+ pBox = MakeTableBox(pLine, rCell2.GetContents().get(),
+ nTopRow, nStartCol,
+ nBottomRow, nSplitCol);
+
+ if (1 != nBoxRowSpan && pBox)
+ pBox->setRowSpan( nBoxRowSpan );
+
+ bSplitted = true;
+ }
+
+ OSL_ENSURE( pBox, "Colspan trouble" );
+
+ if( pBox )
+ rBoxes.push_back( pBox );
+ }
+ nCol++;
+ }
+ nStartCol = nSplitCol;
+ }
+
+ return pLine;
+}
+
+SwTableBox *HTMLTable::MakeTableBox( SwTableLine *pUpper,
+ HTMLTableCnts *pCnts,
+ sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
+ sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
+{
+ SwTableBox *pBox;
+ sal_uInt16 nColSpan = nRightCol - nLeftCol;
+ sal_uInt16 nRowSpan = nBottomRow - nTopRow;
+
+ if( !pCnts->Next() )
+ {
+ // just one content section
+ if( pCnts->GetStartNode() )
+ {
+ // ... that's not a table
+ pBox = NewTableBox( pCnts->GetStartNode(), pUpper );
+ pCnts->SetTableBox( pBox );
+ }
+ else if (HTMLTable* pTable = pCnts->GetTable().get())
+ {
+ pTable->InheritVertBorders( this, nLeftCol,
+ nRightCol-nLeftCol );
+ // ... that's a table. We'll build a new box and put the rows of the table
+ // in the rows of the box
+ pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
+ sal_uInt16 nAbs, nRel;
+ m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel );
+ sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol, nColSpan );
+ sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol, nColSpan );
+ sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan );
+ pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace, nRSpace,
+ nInhSpace );
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+ else
+ {
+ // multiple content sections: we'll build a box with rows
+ pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
+ SwTableLines& rLines = pBox->GetTabLines();
+ bool bFirstPara = true;
+
+ while( pCnts )
+ {
+ if( pCnts->GetStartNode() )
+ {
+ // normal paragraphs are gonna be boxes in a row
+ SwTableLine *pLine =
+ new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight
+ : m_pLineFormat, 0, pBox );
+ if( !m_pLineFrameFormatNoHeight )
+ {
+ // If there's no line format without height yet, we can use that one
+ m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
+
+ ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
+ }
+
+ SwTableBox* pCntBox = NewTableBox( pCnts->GetStartNode(),
+ pLine );
+ pCnts->SetTableBox( pCntBox );
+ FixFrameFormat( pCntBox, nTopRow, nLeftCol, nRowSpan, nColSpan,
+ bFirstPara, nullptr==pCnts->Next() );
+ pLine->GetTabBoxes().push_back( pCntBox );
+
+ rLines.push_back( pLine );
+ }
+ else
+ {
+ pCnts->GetTable()->InheritVertBorders( this, nLeftCol,
+ nRightCol-nLeftCol );
+ // Tables are entered directly
+ sal_uInt16 nAbs, nRel;
+ m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel );
+ sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol,
+ nColSpan );
+ sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol,
+ nColSpan );
+ sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan );
+ pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace,
+ nRSpace, nInhSpace );
+ }
+
+ pCnts = pCnts->Next();
+ bFirstPara = false;
+ }
+ }
+
+ FixFrameFormat( pBox, nTopRow, nLeftCol, nRowSpan, nColSpan );
+
+ return pBox;
+}
+
+void HTMLTable::InheritBorders( const HTMLTable *pParent,
+ sal_uInt16 nRow, sal_uInt16 nCol,
+ sal_uInt16 nRowSpan,
+ bool bFirstPara, bool bLastPara )
+{
+ OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
+ "Was CloseTable not called?" );
+
+ // The child table needs a border, if the surrounding cell has a margin on that side.
+ // The upper/lower border is only set if the table is the first/last paragraph in that cell
+ // It can't be determined if a border for that table is needed or possible for the left or right side,
+ // since that's depending on if filler cells are gonna be added. We'll only collect info for now
+
+ if( 0==nRow && pParent->m_bTopBorder && bFirstPara )
+ {
+ m_bTopBorder = true;
+ m_bFillerTopBorder = true; // fillers get a border too
+ m_aTopBorderLine = pParent->m_aTopBorderLine;
+ }
+ if (pParent->m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
+ {
+ m_aRows[m_nRows-1].SetBottomBorder(true);
+ m_bFillerBottomBorder = true; // fillers get a border too
+ m_aBottomBorderLine =
+ nRow+nRowSpan==pParent->m_nRows ? pParent->m_aBottomBorderLine
+ : pParent->m_aBorderLine;
+ }
+
+ // The child table mustn't get an upper or lower border, if that's already done by the surrounding table
+ // It can get an upper border if the table is not the first paragraph in that cell
+ m_bTopAllowed = ( !bFirstPara || (pParent->m_bTopAllowed &&
+ (0==nRow || !pParent->m_aRows[nRow-1].GetBottomBorder())) );
+
+ // The child table has to inherit the color of the cell it's contained in, if it doesn't have one
+ const SvxBrushItem *pInhBG = pParent->GetCell(nRow, nCol).GetBGBrush().get();
+ if( !pInhBG && pParent != this &&
+ pParent->GetCell(nRow,nCol).GetRowSpan() == pParent->m_nRows )
+ {
+ // the whole surrounding table is a table in a table and consists only of a single line
+ // that's gonna be GC-ed (correctly). That's why the background of that line is copied.
+ pInhBG = pParent->m_aRows[nRow].GetBGBrush().get();
+ if( !pInhBG )
+ pInhBG = pParent->GetBGBrush().get();
+ if( !pInhBG )
+ pInhBG = pParent->GetInhBGBrush().get();
+ }
+ if( pInhBG )
+ m_xInheritedBackgroundBrush.reset(new SvxBrushItem(*pInhBG));
+}
+
+void HTMLTable::InheritVertBorders( const HTMLTable *pParent,
+ sal_uInt16 nCol, sal_uInt16 nColSpan )
+{
+ sal_uInt16 nInhLeftBorderWidth = 0;
+ sal_uInt16 nInhRightBorderWidth = 0;
+
+ if( nCol+nColSpan==pParent->m_nCols && pParent->m_bRightBorder )
+ {
+ m_bInheritedRightBorder = true; // just remember for now
+ m_aInheritedRightBorderLine = pParent->m_aRightBorderLine;
+ nInhRightBorderWidth =
+ GetBorderWidth( m_aInheritedRightBorderLine, true ) + MIN_BORDER_DIST;
+ }
+
+ if (pParent->m_aColumns[nCol].m_bLeftBorder)
+ {
+ m_bInheritedLeftBorder = true; // just remember for now
+ m_aInheritedLeftBorderLine = 0==nCol ? pParent->m_aLeftBorderLine
+ : pParent->m_aBorderLine;
+ nInhLeftBorderWidth =
+ GetBorderWidth( m_aInheritedLeftBorderLine, true ) + MIN_BORDER_DIST;
+ }
+
+ if( !m_bInheritedLeftBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) )
+ nInhLeftBorderWidth = 2 * MIN_BORDER_DIST;
+ if( !m_bInheritedRightBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) )
+ nInhRightBorderWidth = 2 * MIN_BORDER_DIST;
+ m_xLayoutInfo->SetInhBorderWidths( nInhLeftBorderWidth,
+ nInhRightBorderWidth );
+
+ m_bRightAllowed = ( pParent->m_bRightAllowed &&
+ (nCol+nColSpan==pParent->m_nCols ||
+ !pParent->m_aColumns[nCol+nColSpan].m_bLeftBorder) );
+}
+
+void HTMLTable::SetBorders()
+{
+ sal_uInt16 i;
+ for( i=1; i<m_nCols; i++ )
+ if( HTMLTableRules::All==m_eRules || HTMLTableRules::Cols==m_eRules ||
+ ((HTMLTableRules::Rows==m_eRules || HTMLTableRules::Groups==m_eRules) &&
+ m_aColumns[i-1].IsEndOfGroup()))
+ {
+ m_aColumns[i].m_bLeftBorder = true;
+ }
+
+ for( i=0; i<m_nRows-1; i++ )
+ if( HTMLTableRules::All==m_eRules || HTMLTableRules::Rows==m_eRules ||
+ ((HTMLTableRules::Cols==m_eRules || HTMLTableRules::Groups==m_eRules) &&
+ m_aRows[i].IsEndOfGroup()))
+ {
+ m_aRows[i].SetBottomBorder(true);
+ }
+
+ if( m_bTopAllowed && (HTMLTableFrame::Above==m_eFrame || HTMLTableFrame::HSides==m_eFrame ||
+ HTMLTableFrame::Box==m_eFrame) )
+ m_bTopBorder = true;
+ if( HTMLTableFrame::Below==m_eFrame || HTMLTableFrame::HSides==m_eFrame ||
+ HTMLTableFrame::Box==m_eFrame )
+ {
+ m_aRows[m_nRows-1].SetBottomBorder(true);
+ }
+ if( HTMLTableFrame::RHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame ||
+ HTMLTableFrame::Box==m_eFrame )
+ m_bRightBorder = true;
+ if( HTMLTableFrame::LHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame || HTMLTableFrame::Box==m_eFrame )
+ {
+ m_aColumns[0].m_bLeftBorder = true;
+ }
+
+ for( i=0; i<m_nRows; i++ )
+ {
+ HTMLTableRow& rRow = m_aRows[i];
+ for (sal_uInt16 j=0; j<m_nCols; ++j)
+ {
+ HTMLTableCell& rCell = rRow.GetCell(j);
+ if (rCell.GetContents())
+ {
+ HTMLTableCnts *pCnts = rCell.GetContents().get();
+ bool bFirstPara = true;
+ while( pCnts )
+ {
+ HTMLTable *pTable = pCnts->GetTable().get();
+ if( pTable && !pTable->BordersSet() )
+ {
+ pTable->InheritBorders(this, i, j,
+ rCell.GetRowSpan(),
+ bFirstPara,
+ nullptr==pCnts->Next());
+ pTable->SetBorders();
+ }
+ bFirstPara = false;
+ pCnts = pCnts->Next();
+ }
+ }
+ }
+ }
+
+ m_bBordersSet = true;
+}
+
+sal_uInt16 HTMLTable::GetBorderWidth( const SvxBorderLine& rBLine,
+ bool bWithDistance ) const
+{
+ sal_uInt16 nBorderWidth = rBLine.GetWidth();
+ if( bWithDistance )
+ {
+ if( m_nCellPadding )
+ nBorderWidth = nBorderWidth + m_nCellPadding;
+ else if( nBorderWidth )
+ nBorderWidth = nBorderWidth + MIN_BORDER_DIST;
+ }
+
+ return nBorderWidth;
+}
+
+const HTMLTableCell& HTMLTable::GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const
+{
+ OSL_ENSURE(nRow < m_aRows.size(), "invalid row index in HTML table");
+ return m_aRows[nRow].GetCell(nCell);
+}
+
+SvxAdjust HTMLTable::GetInheritedAdjust() const
+{
+ SvxAdjust eAdjust = (m_nCurrentColumn<m_nCols ? m_aColumns[m_nCurrentColumn].GetAdjust()
+ : SvxAdjust::End );
+ if( SvxAdjust::End==eAdjust )
+ eAdjust = m_aRows[m_nCurrentRow].GetAdjust();
+
+ return eAdjust;
+}
+
+sal_Int16 HTMLTable::GetInheritedVertOri() const
+{
+ // text::VertOrientation::TOP is default!
+ sal_Int16 eVOri = m_aRows[m_nCurrentRow].GetVertOri();
+ if( text::VertOrientation::TOP==eVOri && m_nCurrentColumn<m_nCols )
+ eVOri = m_aColumns[m_nCurrentColumn].GetVertOri();
+ if( text::VertOrientation::TOP==eVOri )
+ eVOri = m_eVertOrientation;
+
+ OSL_ENSURE( m_eVertOrientation != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
+ return eVOri;
+}
+
+void HTMLTable::InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ sal_uInt16 nCellWidth, bool bRelWidth, sal_uInt16 nCellHeight,
+ sal_Int16 eVertOrient, std::shared_ptr<SvxBrushItem> const& rBGBrushItem,
+ std::shared_ptr<SvxBoxItem> const& rBoxItem,
+ bool bHasNumFormat, sal_uInt32 nNumFormat,
+ bool bHasValue, double nValue, bool bNoWrap )
+{
+ if( !nRowSpan || static_cast<sal_uInt32>(m_nCurrentRow) + nRowSpan > USHRT_MAX )
+ nRowSpan = 1;
+
+ if( !nColSpan || static_cast<sal_uInt32>(m_nCurrentColumn) + nColSpan > USHRT_MAX )
+ nColSpan = 1;
+
+ sal_uInt16 nColsReq = m_nCurrentColumn + nColSpan;
+ sal_uInt16 nRowsReq = m_nCurrentRow + nRowSpan;
+ sal_uInt16 i, j;
+
+ // if we need more columns than we currently have, we need to add cells for all rows
+ if( m_nCols < nColsReq )
+ {
+ m_aColumns.resize(nColsReq);
+ for( i=0; i<m_nRows; i++ )
+ m_aRows[i].Expand( nColsReq, i<m_nCurrentRow );
+ m_nCols = nColsReq;
+ OSL_ENSURE(m_aColumns.size() == m_nCols,
+ "wrong number of columns after expanding");
+ }
+ if( nColsReq > m_nFilledColumns )
+ m_nFilledColumns = nColsReq;
+
+ // if we need more rows than we currently have, we need to add cells
+ if( m_nRows < nRowsReq )
+ {
+ for( i=m_nRows; i<nRowsReq; i++ )
+ m_aRows.emplace_back(m_nCols);
+ m_nRows = nRowsReq;
+ OSL_ENSURE(m_nRows == m_aRows.size(), "wrong number of rows in Insert");
+ }
+
+ // Check if we have an overlap and could remove that
+ sal_uInt16 nSpanedCols = 0;
+ if( m_nCurrentRow>0 )
+ {
+ HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
+ for( i=m_nCurrentColumn; i<nColsReq; i++ )
+ {
+ HTMLTableCell& rCell = rCurRow.GetCell(i);
+ if (rCell.GetContents())
+ {
+ // A cell from a row further above overlaps this one.
+ // Content and colors are coming from that cell and can be overwritten
+ // or deleted (content) or copied (color) by ProtectRowSpan
+ nSpanedCols = i + rCell.GetColSpan();
+ FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() );
+ if (rCell.GetRowSpan() > nRowSpan)
+ ProtectRowSpan( nRowsReq, i,
+ rCell.GetRowSpan()-nRowSpan );
+ }
+ }
+ for( i=nColsReq; i<nSpanedCols; i++ )
+ {
+ // These contents are anchored in the row above in any case
+ HTMLTableCell& rCell = rCurRow.GetCell(i);
+ FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() );
+ ProtectRowSpan( m_nCurrentRow, i, rCell.GetRowSpan() );
+ }
+ }
+
+ // Fill the cells
+ for( i=nColSpan; i>0; i-- )
+ {
+ for( j=nRowSpan; j>0; j-- )
+ {
+ const bool bCovered = i != nColSpan || j != nRowSpan;
+ GetCell( nRowsReq-j, nColsReq-i )
+ .Set( rCnts, j, i, eVertOrient, rBGBrushItem, rBoxItem,
+ bHasNumFormat, nNumFormat, bHasValue, nValue, bNoWrap, bCovered );
+ }
+ }
+
+ Size aTwipSz( bRelWidth ? 0 : nCellWidth, nCellHeight );
+ if( aTwipSz.Width() || aTwipSz.Height() )
+ {
+ aTwipSz = o3tl::convert(aTwipSz, o3tl::Length::px, o3tl::Length::twip);
+ }
+
+ // Only set width on the first cell!
+ if( nCellWidth )
+ {
+ sal_uInt16 nTmp = bRelWidth ? nCellWidth : o3tl::narrowing<sal_uInt16>(aTwipSz.Width());
+ GetCell( m_nCurrentRow, m_nCurrentColumn ).SetWidth( nTmp, bRelWidth );
+ }
+
+ // Remember height
+ if( nCellHeight && 1==nRowSpan )
+ {
+ m_aRows[m_nCurrentRow].SetHeight(o3tl::narrowing<sal_uInt16>(aTwipSz.Height()));
+ }
+
+ // Set the column counter behind the new cells
+ m_nCurrentColumn = nColsReq;
+ if( nSpanedCols > m_nCurrentColumn )
+ m_nCurrentColumn = nSpanedCols;
+
+ // and search for the next free cell
+ while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
+ m_nCurrentColumn++;
+}
+
+inline void HTMLTable::CloseSection( bool bHead )
+{
+ // Close the preceding sections if there's already a row
+ OSL_ENSURE( m_nCurrentRow<=m_nRows, "invalid current row" );
+ if( m_nCurrentRow>0 && m_nCurrentRow<=m_nRows )
+ m_aRows[m_nCurrentRow-1].SetEndOfGroup();
+ if( bHead )
+ m_nHeadlineRepeat = m_nCurrentRow;
+}
+
+void HTMLTable::OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOrient,
+ std::unique_ptr<SvxBrushItem>& rBGBrushItem)
+{
+ sal_uInt16 nRowsReq = m_nCurrentRow+1;
+
+ // create the next row if it's not there already
+ if( m_nRows<nRowsReq )
+ {
+ for( sal_uInt16 i=m_nRows; i<nRowsReq; i++ )
+ m_aRows.emplace_back(m_nCols);
+ m_nRows = nRowsReq;
+ OSL_ENSURE( m_nRows == m_aRows.size(),
+ "Row number in OpenRow is wrong" );
+ }
+
+ HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
+ rCurRow.SetAdjust(eAdjust);
+ rCurRow.SetVertOri(eVertOrient);
+ if (rBGBrushItem)
+ m_aRows[m_nCurrentRow].SetBGBrush(rBGBrushItem);
+
+ // reset the column counter
+ m_nCurrentColumn=0;
+
+ // and search for the next free cell
+ while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
+ m_nCurrentColumn++;
+}
+
+void HTMLTable::CloseRow( bool bEmpty )
+{
+ OSL_ENSURE( m_nCurrentRow<m_nRows, "current row after table end" );
+
+ // empty cells just get a slightly thicker lower border!
+ if( bEmpty )
+ {
+ if( m_nCurrentRow > 0 )
+ m_aRows[m_nCurrentRow-1].IncEmptyRows();
+ return;
+ }
+
+ HTMLTableRow& rRow = m_aRows[m_nCurrentRow];
+
+ // modify the COLSPAN of all empty cells at the row end in a way, that they're forming a single cell
+ // that can be done here (and not earlier) since there's no more cells in that row
+ sal_uInt16 i=m_nCols;
+ while( i )
+ {
+ HTMLTableCell& rCell = rRow.GetCell(--i);
+ if (!rCell.GetContents())
+ {
+ sal_uInt16 nColSpan = m_nCols-i;
+ if( nColSpan > 1 )
+ rCell.SetColSpan(nColSpan);
+ }
+ else
+ break;
+ }
+
+ m_nCurrentRow++;
+}
+
+inline void HTMLTable::CloseColGroup( sal_uInt16 nSpan, sal_uInt16 _nWidth,
+ bool bRelWidth, SvxAdjust eAdjust,
+ sal_Int16 eVertOrient )
+{
+ if( nSpan )
+ InsertCol( nSpan, _nWidth, bRelWidth, eAdjust, eVertOrient );
+
+ OSL_ENSURE( m_nCurrentColumn<=m_nCols, "invalid column" );
+ if( m_nCurrentColumn>0 && m_nCurrentColumn<=m_nCols )
+ m_aColumns[m_nCurrentColumn-1].SetEndOfGroup();
+}
+
+void HTMLTable::InsertCol( sal_uInt16 nSpan, sal_uInt16 nColWidth, bool bRelWidth,
+ SvxAdjust eAdjust, sal_Int16 eVertOrient )
+{
+ // #i35143# - no columns, if rows already exist.
+ if ( m_nRows > 0 )
+ return;
+
+ sal_uInt16 i;
+
+ if( !nSpan )
+ nSpan = 1;
+
+ sal_uInt16 nColsReq = m_nCurrentColumn + nSpan;
+
+ if( m_nCols < nColsReq )
+ {
+ m_aColumns.resize(nColsReq);
+ m_nCols = nColsReq;
+ }
+
+ sal_uInt16 nTwipWidth(bRelWidth ? 0 : o3tl::convert(nColWidth, o3tl::Length::px, o3tl::Length::twip));
+
+ for( i=m_nCurrentColumn; i<nColsReq; i++ )
+ {
+ HTMLTableColumn& rCol = m_aColumns[i];
+ sal_uInt16 nTmp = bRelWidth ? nColWidth : o3tl::narrowing<sal_uInt16>(nTwipWidth);
+ rCol.SetWidth( nTmp, bRelWidth );
+ rCol.SetAdjust( eAdjust );
+ rCol.SetVertOri( eVertOrient );
+ }
+
+ m_bColSpec = true;
+
+ m_nCurrentColumn = nColsReq;
+}
+
+void HTMLTable::CloseTable()
+{
+ sal_uInt16 i;
+
+ // The number of table rows is only dependent on the <TR> elements (i.e. nCurRow).
+ // Rows that are spanned via ROWSPAN behind nCurRow need to be deleted
+ // and we need to adjust the ROWSPAN in the rows above
+ if( m_nRows>m_nCurrentRow )
+ {
+ HTMLTableRow& rPrevRow = m_aRows[m_nCurrentRow-1];
+ for( i=0; i<m_nCols; i++ )
+ {
+ HTMLTableCell& rCell = rPrevRow.GetCell(i);
+ if (rCell.GetRowSpan() > 1)
+ {
+ FixRowSpan(m_nCurrentRow-1, i, rCell.GetContents().get());
+ ProtectRowSpan(m_nCurrentRow, i, m_aRows[m_nCurrentRow].GetCell(i).GetRowSpan());
+ }
+ }
+ for( i=m_nRows-1; i>=m_nCurrentRow; i-- )
+ m_aRows.erase(m_aRows.begin() + i);
+ m_nRows = m_nCurrentRow;
+ }
+
+ // if the table has no column, we need to add one
+ if( 0==m_nCols )
+ {
+ m_aColumns.resize(1);
+ for( i=0; i<m_nRows; i++ )
+ m_aRows[i].Expand(1);
+ m_nCols = 1;
+ m_nFilledColumns = 1;
+ }
+
+ // if the table has no row, we need to add one
+ if( 0==m_nRows )
+ {
+ m_aRows.emplace_back(m_nCols);
+ m_nRows = 1;
+ m_nCurrentRow = 1;
+ }
+
+ if( m_nFilledColumns < m_nCols )
+ {
+ m_aColumns.erase(m_aColumns.begin() + m_nFilledColumns, m_aColumns.begin() + m_nCols);
+ for( i=0; i<m_nRows; i++ )
+ m_aRows[i].Shrink( m_nFilledColumns );
+ m_nCols = m_nFilledColumns;
+ }
+}
+
+void HTMLTable::MakeTable_( SwTableBox *pBox )
+{
+ SwTableLines& rLines = (pBox ? pBox->GetTabLines()
+ : const_cast<SwTable *>(m_pSwTable)->GetTabLines() );
+
+ for( sal_uInt16 i=0; i<m_nRows; i++ )
+ {
+ SwTableLine *pLine = MakeTableLine( pBox, i, 0, i+1, m_nCols );
+ if( pBox || i > 0 )
+ rLines.push_back( pLine );
+ }
+}
+
+/* How are tables aligned?
+
+first row: without paragraph indents
+second row: with paragraph indents
+
+ALIGN= LEFT RIGHT CENTER -
+-------------------------------------------------------------------------
+xxx for tables with WIDTH=nn% the percentage value is important:
+xxx nn = 100 text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL %
+xxx text::HoriOrientation::NONE text::HoriOrientation::NONE text::HoriOrientation::NONE % text::HoriOrientation::NONE %
+xxx nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
+xxx frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE %
+
+for tables with WIDTH=nn% the percentage value is important:
+nn = 100 text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
+ text::HoriOrientation::LEFT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT_AND %
+nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
+ frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE %
+
+otherwise the calculated width w
+w = avail* text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT
+ HORI_LEDT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT_AND
+w < avail frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::LEFT
+ frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::NONE
+
+xxx *) if for the table no size was specified, always
+xxx text::HoriOrientation::FULL is taken
+
+*/
+
+void HTMLTable::MakeTable( SwTableBox *pBox, sal_uInt16 nAbsAvail,
+ sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
+ sal_uInt16 nAbsRightSpace, sal_uInt16 nInhAbsSpace )
+{
+ OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
+ "Was CloseTable not called?" );
+
+ OSL_ENSURE(m_xLayoutInfo == nullptr, "Table already has layout info");
+
+ // Calculate borders of the table and all contained tables
+ SetBorders();
+
+ // Step 1: needed layout structures are created (including tables in tables)
+ CreateLayoutInfo();
+
+ if (!utl::ConfigManager::IsFuzzing()) // skip slow path for fuzzing
+ {
+ // Step 2: the minimal and maximal column width is calculated
+ // (including tables in tables). Since we don't have boxes yet,
+ // we'll work on the start nodes
+ m_xLayoutInfo->AutoLayoutPass1();
+
+ // Step 3: the actual column widths of this table are calculated (not tables in tables)
+ // We need this now to decide if we need filler cells
+ // (Pass1 was needed because of this as well)
+ m_xLayoutInfo->AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace,
+ nAbsRightSpace, nInhAbsSpace );
+ }
+
+ // Set adjustment for the top table
+ sal_Int16 eHoriOri;
+ if (m_bForceFrame)
+ {
+ // The table should go in a text frame and it's narrower than the
+ // available space and not 100% wide. So it gets a border
+ eHoriOri = m_bPercentWidth ? text::HoriOrientation::FULL : text::HoriOrientation::LEFT;
+ }
+ else switch (m_eTableAdjust)
+ {
+ // The table either fits the page but shouldn't get a text frame,
+ // or it's wider than the page so it doesn't need a text frame
+
+ case SvxAdjust::Right:
+ // Don't be considerate of the right margin in right-adjusted tables
+ eHoriOri = text::HoriOrientation::RIGHT;
+ break;
+ case SvxAdjust::Center:
+ // Centred tables are not considerate of margins
+ eHoriOri = text::HoriOrientation::CENTER;
+ break;
+ case SvxAdjust::Left:
+ default:
+ // left-adjusted tables are only considerate of the left margin
+ eHoriOri = m_nLeftMargin ? text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::LEFT;
+ break;
+ }
+
+ if (!m_pSwTable)
+ {
+ SAL_WARN("sw.html", "no table");
+ return;
+ }
+
+ // get the table format and adapt it
+ SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
+ pFrameFormat->SetFormatAttr( SwFormatHoriOrient(0, eHoriOri) );
+ if (text::HoriOrientation::LEFT_AND_WIDTH == eHoriOri)
+ {
+ OSL_ENSURE( m_nLeftMargin || m_nRightMargin,
+ "There are still leftovers from relative margins" );
+
+ // The right margin will be ignored anyway.
+ SvxLRSpaceItem aLRItem( m_pSwTable->GetFrameFormat()->GetLRSpace() );
+ aLRItem.SetLeft( m_nLeftMargin );
+ aLRItem.SetRight( m_nRightMargin );
+ pFrameFormat->SetFormatAttr( aLRItem );
+ }
+
+ if (m_bPercentWidth && text::HoriOrientation::FULL != eHoriOri)
+ {
+ pFrameFormat->LockModify();
+ SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
+ aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidth) );
+ pFrameFormat->SetFormatAttr( aFrameSize );
+ pFrameFormat->UnlockModify();
+ }
+
+ // get the default line and box format
+ // remember the first box and unlist it from the first row
+ SwTableLine *pLine1 = (m_pSwTable->GetTabLines())[0];
+ m_xBox1.reset((pLine1->GetTabBoxes())[0]);
+ pLine1->GetTabBoxes().erase(pLine1->GetTabBoxes().begin());
+
+ m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat());
+ m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_xBox1->GetFrameFormat());
+
+ MakeTable_( pBox );
+
+ // Finally, we'll do a garbage collection for the top level table
+
+ if( 1==m_nRows && m_nHeight && 1==m_pSwTable->GetTabLines().size() )
+ {
+ // Set height of a one-row table as the minimum width of the row
+ // Was originally a fixed height, but that made problems
+ // and is not Netscape 4.0 compliant
+ m_nHeight = SwHTMLParser::ToTwips( m_nHeight );
+ if( m_nHeight < MINLAY )
+ m_nHeight = MINLAY;
+
+ (m_pSwTable->GetTabLines())[0]->ClaimFrameFormat();
+ (m_pSwTable->GetTabLines())[0]->GetFrameFormat()
+ ->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, m_nHeight ) );
+ }
+
+ if( GetBGBrush() )
+ m_pSwTable->GetFrameFormat()->SetFormatAttr( *GetBGBrush() );
+
+ const_cast<SwTable *>(m_pSwTable)->SetRowsToRepeat( static_cast< sal_uInt16 >(m_nHeadlineRepeat) );
+ const_cast<SwTable *>(m_pSwTable)->GCLines();
+
+ bool bIsInFlyFrame = m_pContext && m_pContext->GetFrameFormat();
+ if( bIsInFlyFrame && !m_nWidth )
+ {
+ SvxAdjust eAdjust = GetTableAdjust(false);
+ if (eAdjust != SvxAdjust::Left &&
+ eAdjust != SvxAdjust::Right)
+ {
+ // If a table with a width attribute isn't flowed around left or right
+ // we'll stack it with a border of 100% width, so its size will
+ // be adapted. That text frame mustn't be modified
+ OSL_ENSURE( HasToFly(), "Why is the table in a frame?" );
+ sal_uInt32 nMin = m_xLayoutInfo->GetMin();
+ if( nMin > USHRT_MAX )
+ nMin = USHRT_MAX;
+ SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, static_cast<SwTwips>(nMin), MINLAY );
+ aFlyFrameSize.SetWidthPercent( 100 );
+ m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize );
+ bIsInFlyFrame = false;
+ }
+ else
+ {
+ // left or right adjusted table without width mustn't be adjusted in width
+ // as they would only shrink but never grow
+ m_xLayoutInfo->SetMustNotRecalc( true );
+ if( m_pContext->GetFrameFormat()->GetAnchor().GetAnchorNode()
+ ->FindTableNode() )
+ {
+ sal_uInt32 nMax = m_xLayoutInfo->GetMax();
+ if( nMax > USHRT_MAX )
+ nMax = USHRT_MAX;
+ SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, static_cast<SwTwips>(nMax), MINLAY );
+ m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize );
+ bIsInFlyFrame = false;
+ }
+ else
+ {
+ m_xLayoutInfo->SetMustNotResize( true );
+ }
+ }
+ }
+ m_xLayoutInfo->SetMayBeInFlyFrame( bIsInFlyFrame );
+
+ // Only tables with relative width or without width should be modified
+ m_xLayoutInfo->SetMustResize( m_bPercentWidth || !m_nWidth );
+
+ if (!pLine1->GetTabBoxes().empty())
+ m_xLayoutInfo->SetWidths();
+ else
+ SAL_WARN("sw.html", "no table box");
+
+ const_cast<SwTable *>(m_pSwTable)->SetHTMLTableLayout(m_xLayoutInfo);
+
+ if( !m_xResizeDrawObjects )
+ return;
+
+ sal_uInt16 nCount = m_xResizeDrawObjects->size();
+ for( sal_uInt16 i=0; i<nCount; i++ )
+ {
+ SdrObject *pObj = (*m_xResizeDrawObjects)[i];
+ sal_uInt16 nRow = (*m_xDrawObjectPercentWidths)[3*i];
+ sal_uInt16 nCol = (*m_xDrawObjectPercentWidths)[3*i+1];
+ sal_uInt8 nPercentWidth = static_cast<sal_uInt8>((*m_xDrawObjectPercentWidths)[3*i+2]);
+
+ SwHTMLTableLayoutCell *pLayoutCell =
+ m_xLayoutInfo->GetCell( nRow, nCol );
+ sal_uInt16 nColSpan = pLayoutCell->GetColSpan();
+
+ sal_uInt16 nWidth2, nDummy;
+ m_xLayoutInfo->GetAvail( nCol, nColSpan, nWidth2, nDummy );
+ nWidth2 = static_cast< sal_uInt16 >((static_cast<tools::Long>(m_nWidth) * nPercentWidth) / 100);
+
+ SwHTMLParser::ResizeDrawObject( pObj, nWidth2 );
+ }
+
+}
+
+void HTMLTable::SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt,
+ sal_uInt16 nLeft, sal_uInt16 nRight,
+ const SwTable *pSwTab, bool bFrcFrame )
+{
+ m_pPrevStartNode = pStNd;
+ m_pSwTable = pSwTab;
+ m_pContext = std::move(pCntxt);
+
+ m_nLeftMargin = nLeft;
+ m_nRightMargin = nRight;
+
+ m_bForceFrame = bFrcFrame;
+}
+
+void HTMLTable::RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth )
+{
+ if( !m_xResizeDrawObjects )
+ m_xResizeDrawObjects.emplace();
+ m_xResizeDrawObjects->push_back( pObj );
+ pObj->AddObjectUser(*this);
+
+ if( !m_xDrawObjectPercentWidths )
+ m_xDrawObjectPercentWidths.emplace();
+ m_xDrawObjectPercentWidths->push_back( m_nCurrentRow );
+ m_xDrawObjectPercentWidths->push_back( m_nCurrentColumn );
+ m_xDrawObjectPercentWidths->push_back( o3tl::narrowing<sal_uInt16>(nPercentWidth) );
+}
+
+void HTMLTable::MakeParentContents()
+{
+ if( !GetContext() && !HasParentSection() )
+ {
+ SetParentContents(
+ m_pParser->InsertTableContents( m_bIsParentHead ) );
+
+ SetHasParentSection( true );
+ }
+}
+
+void HTMLTableContext::SavePREListingXMP( SwHTMLParser& rParser )
+{
+ m_bRestartPRE = rParser.IsReadPRE();
+ m_bRestartXMP = rParser.IsReadXMP();
+ m_bRestartListing = rParser.IsReadListing();
+ rParser.FinishPREListingXMP();
+}
+
+void HTMLTableContext::RestorePREListingXMP( SwHTMLParser& rParser )
+{
+ rParser.FinishPREListingXMP();
+
+ if( m_bRestartPRE )
+ rParser.StartPRE();
+
+ if( m_bRestartXMP )
+ rParser.StartXMP();
+
+ if( m_bRestartListing )
+ rParser.StartListing();
+}
+
+const SwStartNode *SwHTMLParser::InsertTableSection
+ ( const SwStartNode *pPrevStNd )
+{
+ OSL_ENSURE( pPrevStNd, "Start-Node is NULL" );
+
+ m_pCSS1Parser->SetTDTagStyles();
+ SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TABLE );
+
+ const SwStartNode *pStNd;
+ if (m_xTable->m_bFirstCell )
+ {
+ SwNode *const pNd = & m_pPam->GetPoint()->GetNode();
+ pNd->GetTextNode()->ChgFormatColl( pColl );
+ pStNd = pNd->FindTableBoxStartNode();
+ m_xTable->m_bFirstCell = false;
+ }
+ else if (pPrevStNd)
+ {
+ const SwNode* pNd;
+ if( pPrevStNd->IsTableNode() )
+ pNd = pPrevStNd;
+ else
+ pNd = pPrevStNd->EndOfSectionNode();
+ SwNodeIndex nIdx( *pNd, 1 );
+ pStNd = m_xDoc->GetNodes().MakeTextSection( nIdx.GetNode(), SwTableBoxStartNode,
+ pColl );
+ m_xTable->IncBoxCount();
+ }
+ else
+ {
+ eState = SvParserState::Error;
+ return nullptr;
+ }
+
+ //Added defaults to CJK and CTL
+ SwContentNode *pCNd = m_xDoc->GetNodes()[pStNd->GetIndex()+1] ->GetContentNode();
+ SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
+ pCNd->SetAttr( aFontHeight );
+ SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
+ pCNd->SetAttr( aFontHeightCJK );
+ SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
+ pCNd->SetAttr( aFontHeightCTL );
+
+ return pStNd;
+}
+
+const SwStartNode *SwHTMLParser::InsertTableSection( sal_uInt16 nPoolId )
+{
+ switch( nPoolId )
+ {
+ case RES_POOLCOLL_TABLE_HDLN:
+ m_pCSS1Parser->SetTHTagStyles();
+ break;
+ case RES_POOLCOLL_TABLE:
+ m_pCSS1Parser->SetTDTagStyles();
+ break;
+ }
+
+ SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( nPoolId );
+
+ SwNode *const pNd = & m_pPam->GetPoint()->GetNode();
+ const SwStartNode *pStNd;
+ if (m_xTable->m_bFirstCell)
+ {
+ SwTextNode* pTextNd = pNd->GetTextNode();
+ if (!pTextNd)
+ {
+ eState = SvParserState::Error;
+ return nullptr;
+ }
+ pTextNd->ChgFormatColl(pColl);
+ m_xTable->m_bFirstCell = false;
+ pStNd = pNd->FindTableBoxStartNode();
+ }
+ else
+ {
+ SwTableNode *pTableNd = pNd->FindTableNode();
+ if (!pTableNd)
+ {
+ eState = SvParserState::Error;
+ return nullptr;
+ }
+ if( pTableNd->GetTable().GetHTMLTableLayout() )
+ { // if there is already a HTMTableLayout, this table is already finished
+ // and we have to look for the right table in the environment
+ SwTableNode *pOutTable = pTableNd;
+ do {
+ pTableNd = pOutTable;
+ pOutTable = pOutTable->StartOfSectionNode()->FindTableNode();
+ } while( pOutTable && pTableNd->GetTable().GetHTMLTableLayout() );
+ }
+ pStNd = m_xDoc->GetNodes().MakeTextSection( *pTableNd->EndOfSectionNode(), SwTableBoxStartNode,
+ pColl );
+
+ m_pPam->GetPoint()->Assign( pStNd->GetIndex() + 1 );
+ m_xTable->IncBoxCount();
+ }
+
+ if (!pStNd)
+ {
+ eState = SvParserState::Error;
+ }
+
+ return pStNd;
+}
+
+SwStartNode *SwHTMLParser::InsertTempTableCaptionSection()
+{
+ SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TEXT );
+ SwStartNode *pStNd = m_xDoc->GetNodes().MakeTextSection( m_xDoc->GetNodes().GetEndOfExtras(),
+ SwNormalStartNode, pColl );
+
+ m_pPam->GetPoint()->Assign( pStNd->GetIndex() + 1);
+
+ return pStNd;
+}
+
+sal_Int32 SwHTMLParser::StripTrailingLF()
+{
+ sal_Int32 nStripped = 0;
+
+ if (IsReqIF())
+ {
+ // One <br> is exactly one line-break in the ReqIF case.
+ return nStripped;
+ }
+
+ const sal_Int32 nLen = m_pPam->GetPoint()->GetContentIndex();
+ if( nLen )
+ {
+ SwTextNode* pTextNd = m_pPam->GetPoint()->GetNode().GetTextNode();
+ // careful, when comments aren't ignored!!!
+ if( pTextNd )
+ {
+ sal_Int32 nPos = nLen;
+ sal_Int32 nLFCount = 0;
+ while (nPos && ('\x0a' == pTextNd->GetText()[--nPos]))
+ nLFCount++;
+
+ if( nLFCount )
+ {
+ if( nLFCount > 2 )
+ {
+ // On Netscape, a paragraph end matches 2 LFs
+ // (1 is just a newline, 2 creates a blank line)
+ // We already have this space with the lower paragraph gap
+ // If there's a paragraph after the <BR>, we take the maximum
+ // of the gap that results from the <BR> and <P>
+ // That's why we need to delete 2 respectively all if less than 2
+ nLFCount = 2;
+ }
+
+ nPos = nLen - nLFCount;
+ SwContentIndex nIdx( pTextNd, nPos );
+ pTextNd->EraseText( nIdx, nLFCount );
+ nStripped = nLFCount;
+ }
+ }
+ }
+
+ return nStripped;
+}
+
+SvxBrushItem* SwHTMLParser::CreateBrushItem( const Color *pColor,
+ const OUString& rImageURL,
+ const OUString& rStyle,
+ const OUString& rId,
+ const OUString& rClass )
+{
+ SvxBrushItem *pBrushItem = nullptr;
+
+ if( !rStyle.isEmpty() || !rId.isEmpty() || !rClass.isEmpty() )
+ {
+ SfxItemSetFixed<RES_BACKGROUND, RES_BACKGROUND> aItemSet( m_xDoc->GetAttrPool() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( !rClass.isEmpty() )
+ {
+ OUString aClass( rClass );
+ SwCSS1Parser::GetScriptFromClass( aClass );
+ const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass );
+ if( pClass )
+ aItemSet.Put( pClass->GetItemSet() );
+ }
+
+ if( !rId.isEmpty() )
+ {
+ const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId );
+ if( pId )
+ aItemSet.Put( pId->GetItemSet() );
+ }
+
+ m_pCSS1Parser->ParseStyleOption( rStyle, aItemSet, aPropInfo );
+ if( const SvxBrushItem *pItem = aItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ pBrushItem = new SvxBrushItem( *pItem );
+ }
+ }
+
+ if( !pBrushItem && (pColor || !rImageURL.isEmpty()) )
+ {
+ pBrushItem = new SvxBrushItem(RES_BACKGROUND);
+
+ if( pColor )
+ pBrushItem->SetColor(*pColor);
+
+ if( !rImageURL.isEmpty() )
+ {
+ pBrushItem->SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), rImageURL, Link<OUString *, bool>(), false) );
+ pBrushItem->SetGraphicPos( GPOS_TILED );
+ }
+ }
+
+ return pBrushItem;
+}
+
+class SectionSaveStruct : public SwPendingData
+{
+ sal_uInt16 m_nBaseFontStMinSave, m_nFontStMinSave, m_nFontStHeadStartSave;
+ sal_uInt16 m_nDefListDeepSave;
+ size_t m_nContextStMinSave;
+ size_t m_nContextStAttrMinSave;
+
+public:
+
+ std::shared_ptr<HTMLTable> m_xTable;
+
+ explicit SectionSaveStruct( SwHTMLParser& rParser );
+
+#if OSL_DEBUG_LEVEL > 0
+ size_t GetContextStAttrMin() const { return m_nContextStAttrMinSave; }
+#endif
+ void Restore( SwHTMLParser& rParser );
+};
+
+SectionSaveStruct::SectionSaveStruct( SwHTMLParser& rParser ) :
+ m_nBaseFontStMinSave(rParser.m_nBaseFontStMin),
+ m_nFontStMinSave(rParser.m_nFontStMin),
+ m_nFontStHeadStartSave(rParser.m_nFontStHeadStart),
+ m_nDefListDeepSave(rParser.m_nDefListDeep),
+ m_nContextStMinSave(rParser.m_nContextStMin),
+ m_nContextStAttrMinSave(rParser.m_nContextStAttrMin)
+{
+ // Freeze font stacks
+ rParser.m_nBaseFontStMin = rParser.m_aBaseFontStack.size();
+
+ rParser.m_nFontStMin = rParser.m_aFontStack.size();
+
+ // Freeze context stack
+ rParser.m_nContextStMin = rParser.m_aContexts.size();
+ rParser.m_nContextStAttrMin = rParser.m_nContextStMin;
+
+ // And remember a few counters
+ rParser.m_nDefListDeep = 0;
+}
+
+void SectionSaveStruct::Restore( SwHTMLParser& rParser )
+{
+ // Unfreeze font stacks
+ sal_uInt16 nMin = rParser.m_nBaseFontStMin;
+ if( rParser.m_aBaseFontStack.size() > nMin )
+ rParser.m_aBaseFontStack.erase( rParser.m_aBaseFontStack.begin() + nMin,
+ rParser.m_aBaseFontStack.end() );
+ rParser.m_nBaseFontStMin = m_nBaseFontStMinSave;
+
+ nMin = rParser.m_nFontStMin;
+ if( rParser.m_aFontStack.size() > nMin )
+ rParser.m_aFontStack.erase( rParser.m_aFontStack.begin() + nMin,
+ rParser.m_aFontStack.end() );
+ rParser.m_nFontStMin = m_nFontStMinSave;
+ rParser.m_nFontStHeadStart = m_nFontStHeadStartSave;
+
+ OSL_ENSURE( rParser.m_aContexts.size() == rParser.m_nContextStMin &&
+ rParser.m_aContexts.size() == rParser.m_nContextStAttrMin,
+ "The Context Stack was not cleaned up" );
+ rParser.m_nContextStMin = m_nContextStMinSave;
+ rParser.m_nContextStAttrMin = m_nContextStAttrMinSave;
+
+ // Reconstruct a few counters
+ rParser.m_nDefListDeep = m_nDefListDeepSave;
+
+ // Reset a few flags
+ rParser.m_bNoParSpace = false;
+ rParser.m_nOpenParaToken = HtmlTokenId::NONE;
+
+ rParser.m_aParaAttrs.clear();
+}
+
+class CellSaveStruct : public SectionSaveStruct
+{
+ OUString m_aStyle, m_aId, m_aClass;
+ OUString m_aBGImage;
+ Color m_aBGColor;
+ std::shared_ptr<SvxBoxItem> m_xBoxItem;
+
+ std::shared_ptr<HTMLTableCnts> m_xCnts; // List of all contents
+ HTMLTableCnts* m_pCurrCnts; // current content or 0
+ std::optional<SwNodeIndex> m_oNoBreakEndNodeIndex; // Paragraph index of a <NOBR>
+
+ double m_nValue;
+
+ sal_uInt32 m_nNumFormat;
+
+ sal_uInt16 m_nRowSpan, m_nColSpan, m_nWidth, m_nHeight;
+ sal_Int32 m_nNoBreakEndContentPos; // Character index of a <NOBR>
+
+ sal_Int16 m_eVertOri;
+
+ bool m_bHead : 1;
+ bool m_bPercentWidth : 1;
+ bool m_bHasNumFormat : 1;
+ bool m_bHasValue : 1;
+ bool m_bBGColor : 1;
+ bool m_bNoWrap : 1; // NOWRAP option
+ bool m_bNoBreak : 1; // NOBREAK tag
+
+public:
+
+ CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable, bool bHd,
+ bool bReadOpt );
+
+ void AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts );
+ bool HasFirstContents() const { return bool(m_xCnts); }
+
+ void ClearIsInSection() { m_pCurrCnts = nullptr; }
+ bool IsInSection() const { return m_pCurrCnts!=nullptr; }
+
+ void InsertCell( SwHTMLParser& rParser, HTMLTable *pCurTable );
+
+ bool IsHeaderCell() const { return m_bHead; }
+
+ void StartNoBreak( const SwPosition& rPos );
+ void EndNoBreak( const SwPosition& rPos );
+ void CheckNoBreak( const SwPosition& rPos );
+};
+
+CellSaveStruct::CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable,
+ bool bHd, bool bReadOpt ) :
+ SectionSaveStruct( rParser ),
+ m_pCurrCnts( nullptr ),
+ m_nValue( 0.0 ),
+ m_nNumFormat( 0 ),
+ m_nRowSpan( 1 ),
+ m_nColSpan( 1 ),
+ m_nWidth( 0 ),
+ m_nHeight( 0 ),
+ m_nNoBreakEndContentPos( 0 ),
+ m_eVertOri( pCurTable->GetInheritedVertOri() ),
+ m_bHead( bHd ),
+ m_bPercentWidth( false ),
+ m_bHasNumFormat( false ),
+ m_bHasValue( false ),
+ m_bBGColor( false ),
+ m_bNoWrap( false ),
+ m_bNoBreak( false )
+{
+ OUString aNumFormat, aValue, aDir, aLang;
+ SvxAdjust eAdjust( pCurTable->GetInheritedAdjust() );
+
+ if( bReadOpt )
+ {
+ const HTMLOptions& rOptions = rParser.GetOptions();
+ for (size_t i = rOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ m_aId = rOption.GetString();
+ break;
+ case HtmlOptionId::COLSPAN:
+ m_nColSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if (m_nColSpan > 256)
+ {
+ SAL_INFO("sw.html", "ignoring huge COLSPAN " << m_nColSpan);
+ m_nColSpan = 1;
+ }
+ break;
+ case HtmlOptionId::ROWSPAN:
+ m_nRowSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing()))
+ {
+ SAL_INFO("sw.html", "ignoring huge ROWSPAN " << m_nRowSpan);
+ m_nRowSpan = 1;
+ }
+ break;
+ case HtmlOptionId::ALIGN:
+ eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
+ break;
+ case HtmlOptionId::VALIGN:
+ m_eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, m_eVertOri );
+ break;
+ case HtmlOptionId::WIDTH:
+ m_nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber()); // Just for Netscape
+ m_bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ if( m_bPercentWidth && m_nWidth>100 )
+ m_nWidth = 100;
+ break;
+ case HtmlOptionId::HEIGHT:
+ m_nHeight = o3tl::narrowing<sal_uInt16>(rOption.GetNumber()); // Just for Netscape
+ if( rOption.GetString().indexOf('%') != -1)
+ m_nHeight = 0; // don't consider % attributes
+ break;
+ case HtmlOptionId::BGCOLOR:
+ // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape
+ // *really* not on other tags
+ if( !rOption.GetString().isEmpty() )
+ {
+ rOption.GetColor( m_aBGColor );
+ m_bBGColor = true;
+ }
+ break;
+ case HtmlOptionId::BACKGROUND:
+ m_aBGImage = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ m_aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ m_aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ case HtmlOptionId::SDNUM:
+ aNumFormat = rOption.GetString();
+ m_bHasNumFormat = true;
+ break;
+ case HtmlOptionId::SDVAL:
+ m_bHasValue = true;
+ aValue = rOption.GetString();
+ break;
+ case HtmlOptionId::NOWRAP:
+ m_bNoWrap = true;
+ break;
+ default: break;
+ }
+ }
+
+ if( !m_aId.isEmpty() )
+ rParser.InsertBookmark( m_aId );
+ }
+
+ if( m_bHasNumFormat )
+ {
+ LanguageType eLang;
+ m_nValue = SfxHTMLParser::GetTableDataOptionsValNum(
+ m_nNumFormat, eLang, aValue, aNumFormat,
+ *rParser.m_xDoc->GetNumberFormatter() );
+ }
+
+ // Create a new context but don't anchor the drawing::Alignment attribute there,
+ // since there's no section yet
+ HtmlTokenId nToken;
+ sal_uInt16 nColl;
+ if( m_bHead )
+ {
+ nToken = HtmlTokenId::TABLEHEADER_ON;
+ nColl = RES_POOLCOLL_TABLE_HDLN;
+ }
+ else
+ {
+ nToken = HtmlTokenId::TABLEDATA_ON;
+ nColl = RES_POOLCOLL_TABLE;
+ }
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, OUString(), true));
+ if( SvxAdjust::End != eAdjust )
+ rParser.InsertAttr(&rParser.m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST),
+ xCntxt.get());
+
+ if( SwHTMLParser::HasStyleOptions( m_aStyle, m_aId, m_aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( rParser.m_xDoc->GetAttrPool(),
+ rParser.m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( rParser.ParseStyleOptions( m_aStyle, m_aId, m_aClass, aItemSet,
+ aPropInfo, &aLang, &aDir ) )
+ {
+ if (SvxBoxItem const* pItem = aItemSet.GetItemIfSet(RES_BOX, false))
+ { // fdo#41796: steal box item to set it in FixFrameFormat later!
+ m_xBoxItem.reset(pItem->Clone());
+ aItemSet.ClearItem(RES_BOX);
+ }
+ rParser.InsertAttrs(aItemSet, aPropInfo, xCntxt.get());
+ }
+ }
+
+ rParser.SplitPREListingXMP(xCntxt.get());
+
+ rParser.PushContext(xCntxt);
+}
+
+void CellSaveStruct::AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts )
+{
+ m_pCurrCnts = pNewCnts.get();
+
+ if (m_xCnts)
+ m_xCnts->Add( std::move(pNewCnts) );
+ else
+ m_xCnts = std::move(pNewCnts);
+}
+
+void CellSaveStruct::InsertCell( SwHTMLParser& rParser,
+ HTMLTable *pCurTable )
+{
+#if OSL_DEBUG_LEVEL > 0
+ // The attributes need to have been removed when tidying up the context stack,
+ // Otherwise something's wrong. Let's check that...
+
+ // MIB 8.1.98: When attributes were opened outside of a cell,
+ // they're still in the attribute table and will only be deleted at the end
+ // through the CleanContext calls in BuildTable. We don't check that there
+ // so that we get no assert [violations, by translator]
+ // We can see this on nContextStAttrMin: the remembered value of nContextStAttrMinSave
+ // is the value that nContextStAttrMin had at the start of the table. And the
+ // current value of nContextStAttrMin corresponds to the number of contexts
+ // we found at the start of the cell. If the values differ, contexts
+ // were created and we don't check anything.
+
+ if( rParser.m_nContextStAttrMin == GetContextStAttrMin() )
+ {
+ HTMLAttr** pTable = reinterpret_cast<HTMLAttr**>(rParser.m_xAttrTab.get());
+
+ for( auto nCnt = sizeof( HTMLAttrTable ) / sizeof( HTMLAttr* );
+ nCnt--; ++pTable )
+ {
+ OSL_ENSURE( !*pTable, "The attribute table isn't empty" );
+ }
+ }
+#endif
+
+ // we need to add the cell on the current position
+ std::shared_ptr<SvxBrushItem> xBrushItem(
+ rParser.CreateBrushItem(m_bBGColor ? &m_aBGColor : nullptr, m_aBGImage,
+ m_aStyle, m_aId, m_aClass));
+ pCurTable->InsertCell( m_xCnts, m_nRowSpan, m_nColSpan, m_nWidth,
+ m_bPercentWidth, m_nHeight, m_eVertOri, xBrushItem, m_xBoxItem,
+ m_bHasNumFormat, m_nNumFormat, m_bHasValue, m_nValue,
+ m_bNoWrap );
+ Restore( rParser );
+}
+
+void CellSaveStruct::StartNoBreak( const SwPosition& rPos )
+{
+ if( !m_xCnts ||
+ (!rPos.GetContentIndex() && m_pCurrCnts == m_xCnts.get() &&
+ m_xCnts->GetStartNode() &&
+ m_xCnts->GetStartNode()->GetIndex() + 1 ==
+ rPos.GetNodeIndex()) )
+ {
+ m_bNoBreak = true;
+ }
+}
+
+void CellSaveStruct::EndNoBreak( const SwPosition& rPos )
+{
+ if( m_bNoBreak )
+ {
+ m_oNoBreakEndNodeIndex.emplace( rPos.GetNode() );
+ m_nNoBreakEndContentPos = rPos.GetContentIndex();
+ m_bNoBreak = false;
+ }
+}
+
+void CellSaveStruct::CheckNoBreak( const SwPosition& rPos )
+{
+ if (!(m_xCnts && m_pCurrCnts == m_xCnts.get()))
+ return;
+
+ if( m_bNoBreak )
+ {
+ // <NOBR> wasn't closed
+ m_xCnts->SetNoBreak();
+ }
+ else if( m_oNoBreakEndNodeIndex &&
+ m_oNoBreakEndNodeIndex->GetIndex() == rPos.GetNodeIndex() )
+ {
+ if( m_nNoBreakEndContentPos == rPos.GetContentIndex() )
+ {
+ // <NOBR> was closed immediately before the cell end
+ m_xCnts->SetNoBreak();
+ }
+ else if( m_nNoBreakEndContentPos + 1 == rPos.GetContentIndex() )
+ {
+ SwTextNode const*const pTextNd(rPos.GetNode().GetTextNode());
+ if( pTextNd )
+ {
+ sal_Unicode const cLast =
+ pTextNd->GetText()[m_nNoBreakEndContentPos];
+ if( ' '==cLast || '\x0a'==cLast )
+ {
+ // There's just a blank or a newline between the <NOBR> and the cell end
+ m_xCnts->SetNoBreak();
+ }
+ }
+ }
+ }
+}
+
+std::unique_ptr<HTMLTableCnts> SwHTMLParser::InsertTableContents(
+ bool bHead )
+{
+ // create a new section, the PaM is gonna be there
+ const SwStartNode *pStNd =
+ InsertTableSection( static_cast< sal_uInt16 >(bHead ? RES_POOLCOLL_TABLE_HDLN
+ : RES_POOLCOLL_TABLE) );
+
+ if( GetNumInfo().GetNumRule() )
+ {
+ // Set the first paragraph to non-enumerated
+ sal_uInt8 nLvl = GetNumInfo().GetLevel();
+
+ SetNodeNum( nLvl );
+ }
+
+ // Reset attributation start
+ const SwNode& rSttPara = m_pPam->GetPoint()->GetNode();
+ sal_Int32 nSttCnt = m_pPam->GetPoint()->GetContentIndex();
+
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ for (sal_uInt16 nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
+ {
+ HTMLAttr *pAttr = *pHTMLAttributes;
+ while( pAttr )
+ {
+ OSL_ENSURE( !pAttr->GetPrev(), "Attribute has previous list" );
+ pAttr->m_nStartPara = rSttPara;
+ pAttr->m_nEndPara = rSttPara;
+ pAttr->m_nStartContent = nSttCnt;
+ pAttr->m_nEndContent = nSttCnt;
+
+ pAttr = pAttr->GetNext();
+ }
+ }
+
+ return std::make_unique<HTMLTableCnts>( pStNd );
+}
+
+sal_uInt16 SwHTMLParser::IncGrfsThatResizeTable()
+{
+ return m_xTable ? m_xTable->IncGrfsThatResize() : 0;
+}
+
+void SwHTMLParser::RegisterDrawObjectToTable( HTMLTable *pCurTable,
+ SdrObject *pObj, sal_uInt8 nPercentWidth )
+{
+ pCurTable->RegisterDrawObject( pObj, nPercentWidth );
+}
+
+void SwHTMLParser::BuildTableCell( HTMLTable *pCurTable, bool bReadOptions,
+ bool bHead )
+{
+ if( !IsParserWorking() && m_vPendingStack.empty() )
+ return;
+
+ ::comphelper::FlagRestorationGuard g(m_isInTableStructure, false);
+ std::unique_ptr<CellSaveStruct> xSaveStruct;
+
+ HtmlTokenId nToken = HtmlTokenId::NONE;
+ bool bPending = false;
+ if( !m_vPendingStack.empty() )
+ {
+ xSaveStruct.reset(static_cast<CellSaveStruct*>(m_vPendingStack.back().pData.release()));
+
+ m_vPendingStack.pop_back();
+ nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
+ bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
+
+ SaveState( nToken );
+ }
+ else
+ {
+ // <TH> resp. <TD> were already read
+ if (m_xTable->IsOverflowing())
+ {
+ SaveState( HtmlTokenId::NONE );
+ return;
+ }
+
+ if( !pCurTable->GetContext() )
+ {
+ bool bTopTable = m_xTable.get() == pCurTable;
+
+ // the table has no content yet, this means the actual table needs
+ // to be created first
+
+ SfxItemSetFixed<
+ RES_PARATR_SPLIT, RES_PARATR_SPLIT,
+ RES_PAGEDESC, RES_PAGEDESC,
+ RES_BREAK, RES_BREAK,
+ RES_BACKGROUND, RES_BACKGROUND,
+ RES_KEEP, RES_KEEP,
+ RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT,
+ RES_FRAMEDIR, RES_FRAMEDIR
+ > aItemSet( m_xDoc->GetAttrPool() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ bool bStyleParsed = ParseStyleOptions( pCurTable->GetStyle(),
+ pCurTable->GetId(),
+ pCurTable->GetClass(),
+ aItemSet, aPropInfo,
+ nullptr, &pCurTable->GetDirection() );
+ if( bStyleParsed )
+ {
+ if( const SvxBrushItem* pItem = aItemSet.GetItemIfSet(
+ RES_BACKGROUND, false ) )
+ {
+ pCurTable->SetBGBrush( *pItem );
+ aItemSet.ClearItem( RES_BACKGROUND );
+ }
+ if( const SvxFormatSplitItem* pSplitItem = aItemSet.GetItemIfSet(
+ RES_PARATR_SPLIT, false ) )
+ {
+ aItemSet.Put(
+ SwFormatLayoutSplit( pSplitItem->GetValue() ) );
+ aItemSet.ClearItem( RES_PARATR_SPLIT );
+ }
+ }
+
+ sal_uInt16 nLeftSpace = 0;
+ sal_uInt16 nRightSpace = 0;
+ short nIndent;
+ GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent );
+
+ // save the current position we'll get back to some time
+ SwPosition *pSavePos = nullptr;
+ bool bForceFrame = false;
+ bool bAppended = false;
+ bool bParentLFStripped = false;
+ if( bTopTable )
+ {
+ SvxAdjust eTableAdjust = m_xTable->GetTableAdjust(false);
+
+ // If the table is left or right adjusted or should be in a text frame,
+ // it'll get one
+ bForceFrame = eTableAdjust == SvxAdjust::Left ||
+ eTableAdjust == SvxAdjust::Right ||
+ pCurTable->HasToFly();
+
+ // The table either shouldn't get in a text frame and isn't in one
+ // (it gets simulated through cells),
+ // or there's already content at that position
+ OSL_ENSURE( !bForceFrame || pCurTable->HasParentSection(),
+ "table in frame has no parent!" );
+
+ bool bAppend = false;
+ if( bForceFrame )
+ {
+ // If the table gets in a border, we only need to open a new
+ //paragraph if the paragraph has text frames that don't fly
+ bAppend = HasCurrentParaFlys(true);
+ }
+ else
+ {
+ // Otherwise, we need to open a new paragraph if the paragraph
+ // is empty or contains text frames or bookmarks
+ bAppend =
+ m_pPam->GetPoint()->GetContentIndex() ||
+ HasCurrentParaFlys() ||
+ HasCurrentParaBookmarks();
+ }
+ if( bAppend )
+ {
+ if( !m_pPam->GetPoint()->GetContentIndex() )
+ {
+ //Set default to CJK and CTL
+ m_xDoc->SetTextFormatColl( *m_pPam,
+ m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_STANDARD) );
+ SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
+
+ HTMLAttr* pTmp =
+ new HTMLAttr( *m_pPam->GetPoint(), aFontHeight, nullptr, std::shared_ptr<HTMLAttrTable>() );
+ m_aSetAttrTab.push_back( pTmp );
+
+ SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
+ pTmp =
+ new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCJK, nullptr, std::shared_ptr<HTMLAttrTable>() );
+ m_aSetAttrTab.push_back( pTmp );
+
+ SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
+ pTmp =
+ new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCTL, nullptr, std::shared_ptr<HTMLAttrTable>() );
+ m_aSetAttrTab.push_back( pTmp );
+
+ pTmp = new HTMLAttr( *m_pPam->GetPoint(),
+ SvxULSpaceItem( 0, 0, RES_UL_SPACE ), nullptr, std::shared_ptr<HTMLAttrTable>() );
+ m_aSetAttrTab.push_front( pTmp ); // Position 0, since
+ // something can be set by
+ // the table end before
+ }
+ AppendTextNode( AM_NOSPACE );
+ bAppended = true;
+ }
+ else if( !m_aParaAttrs.empty() )
+ {
+ if( !bForceFrame )
+ {
+ // The paragraph will be moved right behind the table.
+ // That's why we remove all hard attributes of that paragraph
+
+ for(HTMLAttr* i : m_aParaAttrs)
+ i->Invalidate();
+ }
+
+ m_aParaAttrs.clear();
+ }
+
+ pSavePos = new SwPosition( *m_pPam->GetPoint() );
+ }
+ else if( pCurTable->HasParentSection() )
+ {
+ bParentLFStripped = StripTrailingLF() > 0;
+
+ // Close paragraph resp. headers
+ m_nOpenParaToken = HtmlTokenId::NONE;
+ m_nFontStHeadStart = m_nFontStMin;
+
+ // The hard attributes on that paragraph are never gonna be invalid anymore
+ m_aParaAttrs.clear();
+ }
+
+ // create a table context
+ std::unique_ptr<HTMLTableContext> pTCntxt(
+ new HTMLTableContext( pSavePos, m_nContextStMin,
+ m_nContextStAttrMin ) );
+
+ // end all open attributes and open them again behind the table
+ std::optional<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts;
+ if( !bForceFrame && (bTopTable || pCurTable->HasParentSection()) )
+ {
+ SplitAttrTab(pTCntxt->m_xAttrTab, bTopTable);
+ // If we reuse an already existing paragraph, we can't add
+ // PostIts since the paragraph gets behind that table.
+ // They're gonna be moved into the first paragraph of the table
+ // If we have tables in tables, we also can't add PostIts to a
+ // still empty paragraph, since it's not gonna be deleted that way
+ if( (bTopTable && !bAppended) ||
+ (!bTopTable && !bParentLFStripped &&
+ !m_pPam->GetPoint()->GetContentIndex()) )
+ pPostIts.emplace();
+ SetAttr( bTopTable, bTopTable, pPostIts ? &*pPostIts : nullptr );
+ }
+ else
+ {
+ SaveAttrTab(pTCntxt->m_xAttrTab);
+ if( bTopTable && !bAppended )
+ {
+ pPostIts.emplace();
+ SetAttr( true, true, &*pPostIts );
+ }
+ }
+ m_bNoParSpace = false;
+
+ // Save current numbering and turn it off
+ pTCntxt->SetNumInfo( GetNumInfo() );
+ GetNumInfo().Clear();
+ pTCntxt->SavePREListingXMP( *this );
+
+ if( bTopTable )
+ {
+ if( bForceFrame )
+ {
+ // the table should be put in a text frame
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1>
+ aFrameSet( m_xDoc->GetAttrPool() );
+ if( !pCurTable->IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( aFrameSet );
+
+ css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
+ sal_Int16 eHori;
+
+ switch( pCurTable->GetTableAdjust(true) )
+ {
+ case SvxAdjust::Right:
+ eHori = text::HoriOrientation::RIGHT;
+ eSurround = css::text::WrapTextMode_LEFT;
+ break;
+ case SvxAdjust::Center:
+ eHori = text::HoriOrientation::CENTER;
+ break;
+ case SvxAdjust::Left:
+ eSurround = css::text::WrapTextMode_RIGHT;
+ [[fallthrough]];
+ default:
+ eHori = text::HoriOrientation::LEFT;
+ break;
+ }
+ SetAnchorAndAdjustment( text::VertOrientation::NONE, eHori, aFrameSet,
+ true );
+ aFrameSet.Put( SwFormatSurround(eSurround) );
+
+ constexpr tools::Long constTwips_100mm = o3tl::convert(tools::Long(100), o3tl::Length::mm, o3tl::Length::twip);
+
+ SwFormatFrameSize aFrameSize( SwFrameSize::Variable, constTwips_100mm, MINLAY );
+ aFrameSize.SetWidthPercent( 100 );
+ aFrameSet.Put( aFrameSize );
+
+ sal_uInt16 nSpace = pCurTable->GetHSpace();
+ if( nSpace )
+ aFrameSet.Put( SvxLRSpaceItem(nSpace, nSpace, 0, RES_LR_SPACE) );
+ nSpace = pCurTable->GetVSpace();
+ if( nSpace )
+ aFrameSet.Put( SvxULSpaceItem(nSpace,nSpace, RES_UL_SPACE) );
+
+ RndStdIds eAnchorId = aFrameSet.
+ Get( RES_ANCHOR ).
+ GetAnchorId();
+ SwFrameFormat *pFrameFormat = m_xDoc->MakeFlySection(
+ eAnchorId, m_pPam->GetPoint(), &aFrameSet );
+
+ pTCntxt->SetFrameFormat( pFrameFormat );
+ const SwFormatContent& rFlyContent = pFrameFormat->GetContent();
+ m_pPam->GetPoint()->Assign( *rFlyContent.GetContentIdx() );
+ m_xDoc->GetNodes().GoNext( m_pPam->GetPoint() );
+ }
+
+ // create a SwTable with a box and set the PaM to the content of
+ // the box section (the adjustment parameter is a dummy for now
+ // and will be corrected later)
+ OSL_ENSURE( !m_pPam->GetPoint()->GetContentIndex(),
+ "The paragraph after the table is not empty!" );
+ const SwTable* pSwTable = m_xDoc->InsertTable(
+ SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ),
+ *m_pPam->GetPoint(), 1, 1, text::HoriOrientation::LEFT );
+ SwFrameFormat *pFrameFormat = pSwTable ? pSwTable->GetFrameFormat() : nullptr;
+
+ if( bForceFrame )
+ {
+ SwNodeIndex aDstIdx( m_pPam->GetPoint()->GetNode() );
+ m_pPam->Move( fnMoveBackward );
+ m_xDoc->GetNodes().Delete( aDstIdx );
+ }
+ else
+ {
+ if (bStyleParsed && pFrameFormat)
+ {
+ m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo );
+ pFrameFormat->SetFormatAttr( aItemSet );
+ }
+ m_pPam->Move( fnMoveBackward );
+ }
+
+ SwNode const*const pNd = & m_pPam->GetPoint()->GetNode();
+ SwTextNode *const pOldTextNd = (!bAppended && !bForceFrame) ?
+ pSavePos->GetNode().GetTextNode() : nullptr;
+
+ if (pFrameFormat && pOldTextNd)
+ {
+ const SwFormatPageDesc* pPageDescItem = pOldTextNd->GetSwAttrSet()
+ .GetItemIfSet( RES_PAGEDESC, false );
+ if( pPageDescItem && pPageDescItem->GetPageDesc() )
+ {
+ pFrameFormat->SetFormatAttr( *pPageDescItem );
+ pOldTextNd->ResetAttr( RES_PAGEDESC );
+ }
+
+ if( const SvxFormatBreakItem* pBreakItem = pOldTextNd->GetSwAttrSet()
+ .GetItemIfSet( RES_BREAK ) )
+ {
+ switch( pBreakItem->GetBreak() )
+ {
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ pFrameFormat->SetFormatAttr( *pBreakItem );
+ pOldTextNd->ResetAttr( RES_BREAK );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if( !bAppended && pPostIts )
+ {
+ // set still-existing PostIts to the first paragraph of the table
+ InsertAttrs( std::move(*pPostIts) );
+ pPostIts.reset();
+ }
+
+ pTCntxt->SetTableNode( const_cast<SwTableNode *>(pNd->FindTableNode()) );
+
+ auto pTableNode = pTCntxt->GetTableNode();
+ pCurTable->SetTable( pTableNode, std::move(pTCntxt),
+ nLeftSpace, nRightSpace,
+ pSwTable, bForceFrame );
+
+ OSL_ENSURE( !pPostIts, "unused PostIts" );
+ }
+ else
+ {
+ // still open sections need to be deleted
+ if( EndSections( bParentLFStripped ) )
+ bParentLFStripped = false;
+
+ if( pCurTable->HasParentSection() )
+ {
+ // after that, we remove a possibly redundant empty paragraph,
+ // but only if it was empty before we stripped the LFs
+ if( !bParentLFStripped )
+ StripTrailingPara();
+
+ if( pPostIts )
+ {
+ // move still existing PostIts to the end of the current paragraph
+ InsertAttrs( std::move(*pPostIts) );
+ pPostIts.reset();
+ }
+ }
+
+ SwNode const*const pNd = & m_pPam->GetPoint()->GetNode();
+ const SwStartNode *pStNd = (m_xTable->m_bFirstCell ? pNd->FindTableNode()
+ : pNd->FindTableBoxStartNode() );
+
+ pCurTable->SetTable( pStNd, std::move(pTCntxt), nLeftSpace, nRightSpace );
+ }
+
+ // Freeze the context stack, since there could be attributes set
+ // outside of cells. Can't happen earlier, since there may be
+ // searches in the stack
+ m_nContextStMin = m_aContexts.size();
+ m_nContextStAttrMin = m_nContextStMin;
+ }
+
+ xSaveStruct.reset(new CellSaveStruct(*this, pCurTable, bHead, bReadOptions));
+
+ // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
+ SaveState( HtmlTokenId::NONE );
+ }
+
+ if( nToken == HtmlTokenId::NONE )
+ nToken = GetNextToken(); // Token after <TABLE>
+
+ bool bDone = false;
+ while( (IsParserWorking() && !bDone) || bPending )
+ {
+ SaveState( nToken );
+
+ nToken = FilterToken( nToken );
+
+ OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || xSaveStruct->IsInSection(),
+ "Where is the section??" );
+ if( m_vPendingStack.empty() && m_bCallNextToken && xSaveStruct->IsInSection() )
+ {
+ // Call NextToken directly (e.g. ignore the content of floating frames or applets)
+ NextToken( nToken );
+ }
+ else switch( nToken )
+ {
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ case HtmlTokenId::TABLEROW_ON:
+ case HtmlTokenId::TABLEROW_OFF:
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::THEAD_OFF:
+ case HtmlTokenId::TFOOT_ON:
+ case HtmlTokenId::TFOOT_OFF:
+ case HtmlTokenId::TBODY_ON:
+ case HtmlTokenId::TBODY_OFF:
+ case HtmlTokenId::TABLE_OFF:
+ SkipToken();
+ [[fallthrough]];
+ case HtmlTokenId::TABLEHEADER_OFF:
+ case HtmlTokenId::TABLEDATA_OFF:
+ bDone = true;
+ break;
+ case HtmlTokenId::TABLE_ON:
+ {
+ bool bHasToFly = false;
+ SvxAdjust eTabAdjust = SvxAdjust::End;
+ if( m_vPendingStack.empty() )
+ {
+ // only if we create a new table, but not if we're still
+ // reading in the table after a Pending
+ xSaveStruct->m_xTable = m_xTable;
+
+ // HACK: create a section for a table that goes in a text frame
+ if( !xSaveStruct->IsInSection() )
+ {
+ // The loop needs to be forward, since the
+ // first option always wins
+ bool bNeedsSection = false;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (const auto & rOption : rHTMLOptions)
+ {
+ if( HtmlOptionId::ALIGN==rOption.GetToken() )
+ {
+ SvxAdjust eAdjust = rOption.GetEnum( aHTMLPAlignTable, SvxAdjust::End );
+ bNeedsSection = SvxAdjust::Left == eAdjust ||
+ SvxAdjust::Right == eAdjust;
+ break;
+ }
+ }
+ if( bNeedsSection )
+ {
+ xSaveStruct->AddContents(
+ InsertTableContents(bHead ) );
+ }
+ }
+ else
+ {
+ // If Flys are anchored in the current paragraph,
+ // the table needs to get in a text frame
+ bHasToFly = HasCurrentParaFlys(false,true);
+ }
+
+ // There could be a section in the cell
+ eTabAdjust = m_xAttrTab->pAdjust
+ ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()).
+ GetAdjust()
+ : SvxAdjust::End;
+ }
+
+ std::shared_ptr<HTMLTable> xSubTable = BuildTable(eTabAdjust,
+ bHead,
+ xSaveStruct->IsInSection(),
+ bHasToFly);
+ if( SvParserState::Pending != GetStatus() )
+ {
+ // Only if the table is really complete
+ if (xSubTable)
+ {
+ OSL_ENSURE( xSubTable->GetTableAdjust(false)!= SvxAdjust::Left &&
+ xSubTable->GetTableAdjust(false)!= SvxAdjust::Right,
+ "left or right aligned tables belong in frames" );
+
+ auto& rParentContents = xSubTable->GetParentContents();
+ if (rParentContents)
+ {
+ OSL_ENSURE( !xSaveStruct->IsInSection(),
+ "Where is the section" );
+
+ // If there's no table coming, we have a section
+ xSaveStruct->AddContents(std::move(rParentContents));
+ }
+
+ const SwStartNode *pCapStNd =
+ xSubTable->GetCaptionStartNode();
+
+ if (xSubTable->GetContext())
+ {
+ OSL_ENSURE( !xSubTable->GetContext()->GetFrameFormat(),
+ "table in frame" );
+
+ if( pCapStNd && xSubTable->IsTopCaption() )
+ {
+ xSaveStruct->AddContents(
+ std::make_unique<HTMLTableCnts>(pCapStNd) );
+ }
+
+ xSaveStruct->AddContents(
+ std::make_unique<HTMLTableCnts>(xSubTable) );
+
+ if( pCapStNd && !xSubTable->IsTopCaption() )
+ {
+ xSaveStruct->AddContents(
+ std::make_unique<HTMLTableCnts>(pCapStNd) );
+ }
+
+ // We don't have a section anymore
+ xSaveStruct->ClearIsInSection();
+ }
+ else if( pCapStNd )
+ {
+ // Since we can't delete this section (it might
+ // belong to the first box), we'll add it
+ xSaveStruct->AddContents(
+ std::make_unique<HTMLTableCnts>(pCapStNd) );
+
+ // We don't have a section anymore
+ xSaveStruct->ClearIsInSection();
+ }
+ }
+
+ m_xTable = xSaveStruct->m_xTable;
+ }
+ }
+ break;
+
+ case HtmlTokenId::NOBR_ON:
+ // HACK for MS: Is the <NOBR> at the start of the cell?
+ xSaveStruct->StartNoBreak( *m_pPam->GetPoint() );
+ break;
+
+ case HtmlTokenId::NOBR_OFF:
+ xSaveStruct->EndNoBreak( *m_pPam->GetPoint() );
+ break;
+
+ case HtmlTokenId::COMMENT:
+ // Spaces are not gonna be deleted with comment fields,
+ // and we don't want a new cell for a comment
+ NextToken( nToken );
+ break;
+
+ case HtmlTokenId::MARQUEE_ON:
+ if( !xSaveStruct->IsInSection() )
+ {
+ // create a new section, the PaM is gonna be there
+ xSaveStruct->AddContents(
+ InsertTableContents( bHead ) );
+ }
+ m_bCallNextToken = true;
+ NewMarquee( pCurTable );
+ break;
+
+ case HtmlTokenId::TEXTTOKEN:
+ // Don't add a section for an empty string
+ if( !xSaveStruct->IsInSection() && 1==aToken.getLength() &&
+ ' '==aToken[0] )
+ break;
+ [[fallthrough]];
+ default:
+ if( !xSaveStruct->IsInSection() )
+ {
+ // add a new section, the PaM's gonna be there
+ xSaveStruct->AddContents(
+ InsertTableContents( bHead ) );
+ }
+
+ if( IsParserWorking() || bPending )
+ NextToken( nToken );
+ break;
+ }
+
+ OSL_ENSURE( !bPending || m_vPendingStack.empty(),
+ "SwHTMLParser::BuildTableCell: There is a PendStack again" );
+ bPending = false;
+ if( IsParserWorking() )
+ SaveState( HtmlTokenId::NONE );
+
+ if( !bDone )
+ nToken = GetNextToken();
+ }
+
+ if( SvParserState::Pending == GetStatus() )
+ {
+ m_vPendingStack.emplace_back( bHead ? HtmlTokenId::TABLEHEADER_ON
+ : HtmlTokenId::TABLEDATA_ON );
+ m_vPendingStack.back().pData = std::move(xSaveStruct);
+
+ return;
+ }
+
+ // If the content of the cell was empty, we need to create an empty content
+ // We also create an empty content if the cell ended with a table and had no
+ // COL tags. Otherwise, it was probably exported by us and we don't
+ // want to have an additional paragraph
+ if( !xSaveStruct->HasFirstContents() ||
+ (!xSaveStruct->IsInSection() && !pCurTable->HasColTags()) )
+ {
+ OSL_ENSURE( xSaveStruct->HasFirstContents() ||
+ !xSaveStruct->IsInSection(),
+ "Section or not, that is the question here" );
+ const SwStartNode *pStNd =
+ InsertTableSection( static_cast< sal_uInt16 >(xSaveStruct->IsHeaderCell()
+ ? RES_POOLCOLL_TABLE_HDLN
+ : RES_POOLCOLL_TABLE ));
+
+ if (!pStNd)
+ eState = SvParserState::Error;
+ else
+ {
+ const SwEndNode *pEndNd = pStNd->EndOfSectionNode();
+ SwContentNode *pCNd = m_xDoc->GetNodes()[pEndNd->GetIndex()-1] ->GetContentNode();
+ if (!pCNd)
+ eState = SvParserState::Error;
+ else
+ {
+ //Added defaults to CJK and CTL
+ SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
+ pCNd->SetAttr( aFontHeight );
+ SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
+ pCNd->SetAttr( aFontHeightCJK );
+ SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
+ pCNd->SetAttr( aFontHeightCTL );
+ }
+ }
+
+ xSaveStruct->AddContents( std::make_unique<HTMLTableCnts>(pStNd) );
+ xSaveStruct->ClearIsInSection();
+ }
+
+ if( xSaveStruct->IsInSection() )
+ {
+ xSaveStruct->CheckNoBreak( *m_pPam->GetPoint() );
+
+ // End all open contexts. We'll take AttrMin because nContextStMin might
+ // have been modified. Since it's gonna be restored by EndContext, it's okay
+ while( m_aContexts.size() > m_nContextStAttrMin+1 )
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ EndContext(xCntxt.get());
+ }
+
+ // Remove LFs at the paragraph end
+ if (StripTrailingLF() == 0 && !m_pPam->GetPoint()->GetContentIndex())
+ {
+ HTMLTableContext* pTableContext = m_xTable ? m_xTable->GetContext() : nullptr;
+ SwPosition* pSavedPos = pTableContext ? pTableContext->GetPos() : nullptr;
+ const bool bDeleteSafe = !pSavedPos || pSavedPos->GetNode() != m_pPam->GetPoint()->GetNode();
+ if (bDeleteSafe)
+ StripTrailingPara();
+ }
+
+ // If there was an adjustment set for the cell, we need to close it
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ if (xCntxt)
+ EndContext(xCntxt.get());
+ }
+ else
+ {
+ // Close all still open contexts
+ while( m_aContexts.size() > m_nContextStAttrMin )
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ if (!xCntxt)
+ break;
+ ClearContext(xCntxt.get());
+ }
+ }
+
+ // end an enumeration
+ GetNumInfo().Clear();
+
+ SetAttr( false );
+
+ xSaveStruct->InsertCell( *this, pCurTable );
+
+ // we're probably before a <TH>, <TD>, <TR> or </TABLE>
+ xSaveStruct.reset();
+}
+
+namespace {
+
+class RowSaveStruct : public SwPendingData
+{
+public:
+ SvxAdjust eAdjust;
+ sal_Int16 eVertOri;
+ bool bHasCells;
+
+ RowSaveStruct() :
+ eAdjust( SvxAdjust::End ), eVertOri( text::VertOrientation::TOP ), bHasCells( false )
+ {}
+};
+
+}
+
+void SwHTMLParser::BuildTableRow( HTMLTable *pCurTable, bool bReadOptions,
+ SvxAdjust eGrpAdjust,
+ sal_Int16 eGrpVertOri )
+{
+ // <TR> was already read
+
+ if( !IsParserWorking() && m_vPendingStack.empty() )
+ return;
+
+ HtmlTokenId nToken = HtmlTokenId::NONE;
+ std::unique_ptr<RowSaveStruct> xSaveStruct;
+
+ bool bPending = false;
+ if( !m_vPendingStack.empty() )
+ {
+ xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
+
+ m_vPendingStack.pop_back();
+ nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
+ bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
+
+ SaveState( nToken );
+ }
+ else
+ {
+ SvxAdjust eAdjust = eGrpAdjust;
+ sal_Int16 eVertOri = eGrpVertOri;
+ Color aBGColor;
+ OUString aBGImage, aStyle, aId, aClass;
+ bool bBGColor = false;
+ xSaveStruct.reset(new RowSaveStruct);
+
+ if( bReadOptions )
+ {
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
+ break;
+ case HtmlOptionId::VALIGN:
+ eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri );
+ break;
+ case HtmlOptionId::BGCOLOR:
+ // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/>TH> like Netscape
+ // *really* not on other tags
+ if( !rOption.GetString().isEmpty() )
+ {
+ rOption.GetColor( aBGColor );
+ bBGColor = true;
+ }
+ break;
+ case HtmlOptionId::BACKGROUND:
+ aBGImage = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass= rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+ }
+
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+
+ std::unique_ptr<SvxBrushItem> xBrushItem(
+ CreateBrushItem( bBGColor ? &aBGColor : nullptr, aBGImage, aStyle,
+ aId, aClass ));
+ pCurTable->OpenRow(eAdjust, eVertOri, xBrushItem);
+ // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
+ SaveState( HtmlTokenId::NONE );
+ }
+
+ if( nToken == HtmlTokenId::NONE )
+ nToken = GetNextToken();
+
+ bool bDone = false;
+ while( (IsParserWorking() && !bDone) || bPending )
+ {
+ SaveState( nToken );
+
+ nToken = FilterToken( nToken );
+
+ OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
+ pCurTable->GetContext() || pCurTable->HasParentSection(),
+ "Where is the section??" );
+ if( m_vPendingStack.empty() && m_bCallNextToken &&
+ (pCurTable->GetContext() || pCurTable->HasParentSection()) )
+ {
+ /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
+ NextToken( nToken );
+ }
+ else switch( nToken )
+ {
+ case HtmlTokenId::TABLE_ON:
+ if( !pCurTable->GetContext() )
+ {
+ SkipToken();
+ bDone = true;
+ }
+
+ break;
+ case HtmlTokenId::TABLEROW_ON:
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::THEAD_OFF:
+ case HtmlTokenId::TBODY_ON:
+ case HtmlTokenId::TBODY_OFF:
+ case HtmlTokenId::TFOOT_ON:
+ case HtmlTokenId::TFOOT_OFF:
+ case HtmlTokenId::TABLE_OFF:
+ SkipToken();
+ [[fallthrough]];
+ case HtmlTokenId::TABLEROW_OFF:
+ bDone = true;
+ break;
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ BuildTableCell( pCurTable, true, HtmlTokenId::TABLEHEADER_ON==nToken );
+ if( SvParserState::Pending != GetStatus() )
+ {
+ xSaveStruct->bHasCells = true;
+ bDone = m_xTable->IsOverflowing();
+ }
+ break;
+ case HtmlTokenId::CAPTION_ON:
+ BuildTableCaption( pCurTable );
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::CAPTION_OFF:
+ case HtmlTokenId::TABLEHEADER_OFF:
+ case HtmlTokenId::TABLEDATA_OFF:
+ case HtmlTokenId::COLGROUP_ON:
+ case HtmlTokenId::COLGROUP_OFF:
+ case HtmlTokenId::COL_ON:
+ case HtmlTokenId::COL_OFF:
+ // Where no cell started, there can't be a cell ending
+ // all the other tokens are bogus anyway and only break the table
+ break;
+ case HtmlTokenId::MULTICOL_ON:
+ // we can't add columned text frames here
+ break;
+ case HtmlTokenId::FORM_ON:
+ NewForm( false ); // don't create a new paragraph
+ break;
+ case HtmlTokenId::FORM_OFF:
+ EndForm( false ); // don't create a new paragraph
+ break;
+ case HtmlTokenId::COMMENT:
+ NextToken( nToken );
+ break;
+ case HtmlTokenId::MAP_ON:
+ // an image map doesn't add anything, so we can parse it without a cell
+ NextToken( nToken );
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ if( (pCurTable->GetContext() ||
+ !pCurTable->HasParentSection()) &&
+ 1==aToken.getLength() && ' '==aToken[0] )
+ break;
+ [[fallthrough]];
+ default:
+ pCurTable->MakeParentContents();
+ NextToken( nToken );
+ break;
+ }
+
+ OSL_ENSURE( !bPending || m_vPendingStack.empty(),
+ "SwHTMLParser::BuildTableRow: There is a PendStack again" );
+ bPending = false;
+ if( IsParserWorking() )
+ SaveState( HtmlTokenId::NONE );
+
+ if( !bDone )
+ nToken = GetNextToken();
+ }
+
+ if( SvParserState::Pending == GetStatus() )
+ {
+ m_vPendingStack.emplace_back( HtmlTokenId::TABLEROW_ON );
+ m_vPendingStack.back().pData = std::move(xSaveStruct);
+ }
+ else
+ {
+ pCurTable->CloseRow(!xSaveStruct->bHasCells);
+ xSaveStruct.reset();
+ }
+
+ // we're probably before <TR> or </TABLE>
+}
+
+void SwHTMLParser::BuildTableSection( HTMLTable *pCurTable,
+ bool bReadOptions,
+ bool bHead )
+{
+ // <THEAD>, <TBODY> resp. <TFOOT> were read already
+ if( !IsParserWorking() && m_vPendingStack.empty() )
+ return;
+
+ HtmlTokenId nToken = HtmlTokenId::NONE;
+ bool bPending = false;
+ std::unique_ptr<RowSaveStruct> xSaveStruct;
+
+ if( !m_vPendingStack.empty() )
+ {
+ xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
+
+ m_vPendingStack.pop_back();
+ nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
+ bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
+
+ SaveState( nToken );
+ }
+ else
+ {
+ xSaveStruct.reset(new RowSaveStruct);
+
+ if( bReadOptions )
+ {
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ InsertBookmark( rOption.GetString() );
+ break;
+ case HtmlOptionId::ALIGN:
+ xSaveStruct->eAdjust =
+ rOption.GetEnum( aHTMLPAlignTable, xSaveStruct->eAdjust );
+ break;
+ case HtmlOptionId::VALIGN:
+ xSaveStruct->eVertOri =
+ rOption.GetEnum( aHTMLTableVAlignTable,
+ xSaveStruct->eVertOri );
+ break;
+ default: break;
+ }
+ }
+ }
+
+ // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
+ SaveState( HtmlTokenId::NONE );
+ }
+
+ if( nToken == HtmlTokenId::NONE )
+ nToken = GetNextToken();
+
+ bool bDone = false;
+ while( (IsParserWorking() && !bDone) || bPending )
+ {
+ SaveState( nToken );
+
+ nToken = FilterToken( nToken );
+
+ OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
+ pCurTable->GetContext() || pCurTable->HasParentSection(),
+ "Where is the section?" );
+ if( m_vPendingStack.empty() && m_bCallNextToken &&
+ (pCurTable->GetContext() || pCurTable->HasParentSection()) )
+ {
+ // Call NextToken directly (e.g. ignore the content of floating frames or applets)
+ NextToken( nToken );
+ }
+ else switch( nToken )
+ {
+ case HtmlTokenId::TABLE_ON:
+ if( !pCurTable->GetContext() )
+ {
+ SkipToken();
+ bDone = true;
+ }
+
+ break;
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::TFOOT_ON:
+ case HtmlTokenId::TBODY_ON:
+ case HtmlTokenId::TABLE_OFF:
+ SkipToken();
+ [[fallthrough]];
+ case HtmlTokenId::THEAD_OFF:
+ case HtmlTokenId::TBODY_OFF:
+ case HtmlTokenId::TFOOT_OFF:
+ bDone = true;
+ break;
+ case HtmlTokenId::CAPTION_ON:
+ BuildTableCaption( pCurTable );
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::CAPTION_OFF:
+ break;
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ SkipToken();
+ BuildTableRow( pCurTable, false, xSaveStruct->eAdjust,
+ xSaveStruct->eVertOri );
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::TABLEROW_ON:
+ BuildTableRow( pCurTable, true, xSaveStruct->eAdjust,
+ xSaveStruct->eVertOri );
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::MULTICOL_ON:
+ // we can't add columned text frames here
+ break;
+ case HtmlTokenId::FORM_ON:
+ NewForm( false ); // don't create a new paragraph
+ break;
+ case HtmlTokenId::FORM_OFF:
+ EndForm( false ); // don't create a new paragraph
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ // blank strings may be a series of CR+LF and no text
+ if( (pCurTable->GetContext() ||
+ !pCurTable->HasParentSection()) &&
+ 1==aToken.getLength() && ' ' == aToken[0] )
+ break;
+ [[fallthrough]];
+ default:
+ pCurTable->MakeParentContents();
+ NextToken( nToken );
+ }
+
+ OSL_ENSURE( !bPending || m_vPendingStack.empty(),
+ "SwHTMLParser::BuildTableSection: There is a PendStack again" );
+ bPending = false;
+ if( IsParserWorking() )
+ SaveState( HtmlTokenId::NONE );
+
+ if( !bDone )
+ nToken = GetNextToken();
+ }
+
+ if( SvParserState::Pending == GetStatus() )
+ {
+ m_vPendingStack.emplace_back( bHead ? HtmlTokenId::THEAD_ON
+ : HtmlTokenId::TBODY_ON );
+ m_vPendingStack.back().pData = std::move(xSaveStruct);
+ }
+ else
+ {
+ pCurTable->CloseSection( bHead );
+ xSaveStruct.reset();
+ }
+
+ // now we stand (perhaps) in front of <TBODY>,... or </TABLE>
+}
+
+namespace {
+
+struct TableColGrpSaveStruct : public SwPendingData
+{
+ sal_uInt16 nColGrpSpan;
+ sal_uInt16 nColGrpWidth;
+ bool bRelColGrpWidth;
+ SvxAdjust eColGrpAdjust;
+ sal_Int16 eColGrpVertOri;
+
+ inline TableColGrpSaveStruct();
+
+ inline void CloseColGroup( HTMLTable *pTable );
+};
+
+}
+
+inline TableColGrpSaveStruct::TableColGrpSaveStruct() :
+ nColGrpSpan( 1 ), nColGrpWidth( 0 ),
+ bRelColGrpWidth( false ), eColGrpAdjust( SvxAdjust::End ),
+ eColGrpVertOri( text::VertOrientation::TOP )
+{}
+
+inline void TableColGrpSaveStruct::CloseColGroup( HTMLTable *pTable )
+{
+ pTable->CloseColGroup( nColGrpSpan, nColGrpWidth,
+ bRelColGrpWidth, eColGrpAdjust, eColGrpVertOri );
+}
+
+void SwHTMLParser::BuildTableColGroup( HTMLTable *pCurTable,
+ bool bReadOptions )
+{
+ // <COLGROUP> was read already if bReadOptions is set
+
+ if( !IsParserWorking() && m_vPendingStack.empty() )
+ return;
+
+ HtmlTokenId nToken = HtmlTokenId::NONE;
+ bool bPending = false;
+ std::unique_ptr<TableColGrpSaveStruct> pSaveStruct;
+
+ if( !m_vPendingStack.empty() )
+ {
+ pSaveStruct.reset(static_cast<TableColGrpSaveStruct*>(m_vPendingStack.back().pData.release()));
+
+
+ m_vPendingStack.pop_back();
+ nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
+ bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
+
+ SaveState( nToken );
+ }
+ else
+ {
+
+ pSaveStruct.reset(new TableColGrpSaveStruct);
+ if( bReadOptions )
+ {
+ const HTMLOptions& rColGrpOptions = GetOptions();
+ for (size_t i = rColGrpOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rColGrpOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ InsertBookmark( rOption.GetString() );
+ break;
+ case HtmlOptionId::SPAN:
+ pSaveStruct->nColGrpSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if (pSaveStruct->nColGrpSpan > 256)
+ {
+ SAL_INFO("sw.html", "ignoring huge SPAN " << pSaveStruct->nColGrpSpan);
+ pSaveStruct->nColGrpSpan = 1;
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ pSaveStruct->nColGrpWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ pSaveStruct->bRelColGrpWidth =
+ (rOption.GetString().indexOf('*') != -1);
+ break;
+ case HtmlOptionId::ALIGN:
+ pSaveStruct->eColGrpAdjust =
+ rOption.GetEnum( aHTMLPAlignTable, pSaveStruct->eColGrpAdjust );
+ break;
+ case HtmlOptionId::VALIGN:
+ pSaveStruct->eColGrpVertOri =
+ rOption.GetEnum( aHTMLTableVAlignTable,
+ pSaveStruct->eColGrpVertOri );
+ break;
+ default: break;
+ }
+ }
+ }
+ // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
+ SaveState( HtmlTokenId::NONE );
+ }
+
+ if( nToken == HtmlTokenId::NONE )
+ nToken = GetNextToken(); // naechstes Token
+
+ bool bDone = false;
+ while( (IsParserWorking() && !bDone) || bPending )
+ {
+ SaveState( nToken );
+
+ nToken = FilterToken( nToken );
+
+ OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
+ pCurTable->GetContext() || pCurTable->HasParentSection(),
+ "Where is the section?" );
+ if( m_vPendingStack.empty() && m_bCallNextToken &&
+ (pCurTable->GetContext() || pCurTable->HasParentSection()) )
+ {
+ // Call NextToken directly (e.g. ignore the content of floating frames or applets)
+ NextToken( nToken );
+ }
+ else switch( nToken )
+ {
+ case HtmlTokenId::TABLE_ON:
+ if( !pCurTable->GetContext() )
+ {
+ SkipToken();
+ bDone = true;
+ }
+
+ break;
+ case HtmlTokenId::COLGROUP_ON:
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::TFOOT_ON:
+ case HtmlTokenId::TBODY_ON:
+ case HtmlTokenId::TABLEROW_ON:
+ case HtmlTokenId::TABLE_OFF:
+ SkipToken();
+ [[fallthrough]];
+ case HtmlTokenId::COLGROUP_OFF:
+ bDone = true;
+ break;
+ case HtmlTokenId::COL_ON:
+ {
+ sal_uInt16 nColSpan = 1;
+ sal_uInt16 nColWidth = pSaveStruct->nColGrpWidth;
+ bool bRelColWidth = pSaveStruct->bRelColGrpWidth;
+ SvxAdjust eColAdjust = pSaveStruct->eColGrpAdjust;
+ sal_Int16 eColVertOri = pSaveStruct->eColGrpVertOri;
+
+ const HTMLOptions& rColOptions = GetOptions();
+ for (size_t i = rColOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rColOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ InsertBookmark( rOption.GetString() );
+ break;
+ case HtmlOptionId::SPAN:
+ nColSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if (nColSpan > 256)
+ {
+ SAL_INFO("sw.html", "ignoring huge SPAN " << nColSpan);
+ nColSpan = 1;
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ nColWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ bRelColWidth =
+ (rOption.GetString().indexOf('*') != -1);
+ break;
+ case HtmlOptionId::ALIGN:
+ eColAdjust = rOption.GetEnum( aHTMLPAlignTable, eColAdjust );
+ break;
+ case HtmlOptionId::VALIGN:
+ eColVertOri =
+ rOption.GetEnum( aHTMLTableVAlignTable, eColVertOri );
+ break;
+ default: break;
+ }
+ }
+ pCurTable->InsertCol( nColSpan, nColWidth, bRelColWidth,
+ eColAdjust, eColVertOri );
+
+ // the attributes in <COLGRP> should be ignored, if there are <COL> elements
+ pSaveStruct->nColGrpSpan = 0;
+ }
+ break;
+ case HtmlTokenId::COL_OFF:
+ break; // Ignore
+ case HtmlTokenId::MULTICOL_ON:
+ // we can't add columned text frames here
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ if( (pCurTable->GetContext() ||
+ !pCurTable->HasParentSection()) &&
+ 1==aToken.getLength() && ' '==aToken[0] )
+ break;
+ [[fallthrough]];
+ default:
+ pCurTable->MakeParentContents();
+ NextToken( nToken );
+ }
+
+ OSL_ENSURE( !bPending || m_vPendingStack.empty(),
+ "SwHTMLParser::BuildTableColGrp: There is a PendStack again" );
+ bPending = false;
+ if( IsParserWorking() )
+ SaveState( HtmlTokenId::NONE );
+
+ if( !bDone )
+ nToken = GetNextToken();
+ }
+
+ if( SvParserState::Pending == GetStatus() )
+ {
+ m_vPendingStack.emplace_back( HtmlTokenId::COL_ON );
+ m_vPendingStack.back().pData = std::move(pSaveStruct);
+ }
+ else
+ {
+ pSaveStruct->CloseColGroup( pCurTable );
+ }
+}
+
+class CaptionSaveStruct : public SectionSaveStruct
+{
+ SwPosition m_aSavePos;
+ SwHTMLNumRuleInfo m_aNumRuleInfo; // valid numbering
+
+public:
+
+ std::shared_ptr<HTMLAttrTable> m_xAttrTab; // attributes
+
+ CaptionSaveStruct( SwHTMLParser& rParser, SwPosition aPos ) :
+ SectionSaveStruct( rParser ), m_aSavePos(std::move( aPos )),
+ m_xAttrTab(std::make_shared<HTMLAttrTable>())
+ {
+ rParser.SaveAttrTab(m_xAttrTab);
+
+ // The current numbering was remembered and just needs to be closed
+ m_aNumRuleInfo.Set( rParser.GetNumInfo() );
+ rParser.GetNumInfo().Clear();
+ }
+
+ const SwPosition& GetPos() const { return m_aSavePos; }
+
+ void RestoreAll( SwHTMLParser& rParser )
+ {
+ // Recover the old stack
+ Restore( rParser );
+
+ // Recover the old attribute tables
+ rParser.RestoreAttrTab(m_xAttrTab);
+
+ // Re-open the old numbering
+ rParser.GetNumInfo().Set( m_aNumRuleInfo );
+ }
+};
+
+void SwHTMLParser::BuildTableCaption( HTMLTable *pCurTable )
+{
+ // <CAPTION> was read already
+
+ if( !IsParserWorking() && m_vPendingStack.empty() )
+ return;
+
+ HtmlTokenId nToken = HtmlTokenId::NONE;
+ std::unique_ptr<CaptionSaveStruct> xSaveStruct;
+
+ if( !m_vPendingStack.empty() )
+ {
+ xSaveStruct.reset(static_cast<CaptionSaveStruct*>(m_vPendingStack.back().pData.release()));
+
+ m_vPendingStack.pop_back();
+ nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
+ OSL_ENSURE( m_vPendingStack.empty(), "Where does a PendStack coming from?" );
+
+ SaveState( nToken );
+ }
+ else
+ {
+ if (m_xTable->IsOverflowing())
+ {
+ SaveState( HtmlTokenId::NONE );
+ return;
+ }
+
+ bool bTop = true;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for ( size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ if( HtmlOptionId::ALIGN == rOption.GetToken() )
+ {
+ if (rOption.GetString().equalsIgnoreAsciiCase(
+ OOO_STRING_SVTOOLS_HTML_VA_bottom))
+ {
+ bTop = false;
+ }
+ }
+ }
+
+ // Remember old PaM position
+ xSaveStruct.reset(new CaptionSaveStruct(*this, *m_pPam->GetPoint()));
+
+ // Add a text section in the icon section as a container for the header
+ // and set the PaM there
+ const SwStartNode *pStNd;
+ if (m_xTable.get() == pCurTable)
+ pStNd = InsertTempTableCaptionSection();
+ else
+ pStNd = InsertTableSection( RES_POOLCOLL_TEXT );
+
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::CAPTION_ON));
+
+ // Table headers are always centered
+ NewAttr(m_xAttrTab, &m_xAttrTab->pAdjust, SvxAdjustItem(SvxAdjust::Center, RES_PARATR_ADJUST));
+
+ HTMLAttrs &rAttrs = xCntxt->GetAttrs();
+ rAttrs.push_back( m_xAttrTab->pAdjust );
+
+ PushContext(xCntxt);
+
+ // Remember the start node of the section at the table
+ pCurTable->SetCaption( pStNd, bTop );
+
+ // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
+ SaveState( HtmlTokenId::NONE );
+ }
+
+ if( nToken == HtmlTokenId::NONE )
+ nToken = GetNextToken();
+
+ // </CAPTION> is needed according to DTD
+ bool bDone = false;
+ while( IsParserWorking() && !bDone )
+ {
+ SaveState( nToken );
+
+ nToken = FilterToken( nToken );
+
+ switch( nToken )
+ {
+ case HtmlTokenId::TABLE_ON:
+ if( m_vPendingStack.empty() )
+ {
+ xSaveStruct->m_xTable = m_xTable;
+ bool bHasToFly = xSaveStruct->m_xTable.get() != pCurTable;
+ BuildTable( pCurTable->GetTableAdjust( true ),
+ false, true, bHasToFly );
+ }
+ else
+ {
+ BuildTable( SvxAdjust::End );
+ }
+ if( SvParserState::Pending != GetStatus() )
+ {
+ m_xTable = xSaveStruct->m_xTable;
+ }
+ break;
+ case HtmlTokenId::TABLE_OFF:
+ case HtmlTokenId::COLGROUP_ON:
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::TFOOT_ON:
+ case HtmlTokenId::TBODY_ON:
+ case HtmlTokenId::TABLEROW_ON:
+ SkipToken();
+ bDone = true;
+ break;
+
+ case HtmlTokenId::CAPTION_OFF:
+ bDone = true;
+ break;
+ default:
+ if( !m_vPendingStack.empty() )
+ {
+ m_vPendingStack.pop_back();
+ OSL_ENSURE( m_vPendingStack.empty(), "Further it can't go!" );
+ }
+
+ if( IsParserWorking() )
+ NextToken( nToken );
+ break;
+ }
+
+ if( IsParserWorking() )
+ SaveState( HtmlTokenId::NONE );
+
+ if( !bDone )
+ nToken = GetNextToken();
+ }
+
+ if( SvParserState::Pending==GetStatus() )
+ {
+ m_vPendingStack.emplace_back( HtmlTokenId::CAPTION_ON );
+ m_vPendingStack.back().pData = std::move(xSaveStruct);
+ return;
+ }
+
+ // end all still open contexts
+ while( m_aContexts.size() > m_nContextStAttrMin+1 )
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ EndContext(xCntxt.get());
+ }
+
+ bool bLFStripped = StripTrailingLF() > 0;
+
+ if (m_xTable.get() == pCurTable)
+ {
+ // On moving the caption later, the last paragraph isn't moved as well.
+ // That means, there has to be an empty paragraph at the end of the section
+ if( m_pPam->GetPoint()->GetContentIndex() || bLFStripped )
+ AppendTextNode( AM_NOSPACE );
+ }
+ else
+ {
+ // Strip LFs at the end of the paragraph
+ if( !m_pPam->GetPoint()->GetContentIndex() && !bLFStripped )
+ StripTrailingPara();
+ }
+
+ // If there's an adjustment for the cell, we need to close it
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ xCntxt.reset();
+ }
+
+ SetAttr( false );
+
+ // Recover stack and attribute table
+ xSaveStruct->RestoreAll(*this);
+
+ // Recover PaM
+ *m_pPam->GetPoint() = xSaveStruct->GetPos();
+}
+
+namespace {
+
+class TableSaveStruct : public SwPendingData
+{
+public:
+ std::shared_ptr<HTMLTable> m_xCurrentTable;
+
+ explicit TableSaveStruct(std::shared_ptr<HTMLTable> xCurTable)
+ : m_xCurrentTable(std::move(xCurTable))
+ {
+ }
+
+ // Initiate creation of the table and put the table in a text frame if
+ // needed. If it returns true, we need to insert a paragraph.
+ void MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc );
+};
+
+}
+
+void TableSaveStruct::MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc )
+{
+ m_xCurrentTable->MakeTable(nullptr, nWidth);
+
+ HTMLTableContext *pTCntxt = m_xCurrentTable->GetContext();
+ OSL_ENSURE( pTCntxt, "Where is the table context" );
+
+ SwTableNode *pTableNd = pTCntxt->GetTableNode();
+ OSL_ENSURE( pTableNd, "Where is the table node" );
+
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pTableNd )
+ {
+ // If there's already a layout, the BoxFrames need to be regenerated at this table
+
+ if( pTCntxt->GetFrameFormat() )
+ {
+ pTCntxt->GetFrameFormat()->DelFrames();
+ pTableNd->DelFrames();
+ pTCntxt->GetFrameFormat()->MakeFrames();
+ }
+ else
+ {
+ pTableNd->DelFrames();
+ SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
+ OSL_ENSURE( aIdx.GetIndex() <= pTCntxt->GetPos()->GetNodeIndex(),
+ "unexpected node for table layout" );
+ pTableNd->MakeOwnFrames();
+ }
+ }
+
+ rPos = *pTCntxt->GetPos();
+}
+
+HTMLTableOptions::HTMLTableOptions( const HTMLOptions& rOptions,
+ SvxAdjust eParentAdjust ) :
+ nCols( 0 ),
+ nWidth( 0 ), nHeight( 0 ),
+ nCellPadding( USHRT_MAX ), nCellSpacing( USHRT_MAX ),
+ nBorder( USHRT_MAX ),
+ nHSpace( 0 ), nVSpace( 0 ),
+ eAdjust( eParentAdjust ), eVertOri( text::VertOrientation::CENTER ),
+ eFrame( HTMLTableFrame::Void ), eRules( HTMLTableRules::NONE ),
+ bPercentWidth( false ),
+ bTableAdjust( false ),
+ bBGColor( false ),
+ aBorderColor( COL_GRAY )
+{
+ bool bBorderColor = false;
+ bool bHasFrame = false, bHasRules = false;
+
+ for (size_t i = rOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::COLS:
+ nCols = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::WIDTH:
+ nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ if( bPercentWidth && nWidth>100 )
+ nWidth = 100;
+ break;
+ case HtmlOptionId::HEIGHT:
+ nHeight = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if( rOption.GetString().indexOf('%') != -1 )
+ nHeight = 0; // don't use % attributes
+ break;
+ case HtmlOptionId::CELLPADDING:
+ nCellPadding = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::CELLSPACING:
+ nCellSpacing = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::ALIGN:
+ {
+ if( rOption.GetEnum( eAdjust, aHTMLPAlignTable ) )
+ {
+ bTableAdjust = true;
+ }
+ }
+ break;
+ case HtmlOptionId::VALIGN:
+ eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri );
+ break;
+ case HtmlOptionId::BORDER:
+ // Handle BORDER and BORDER=BORDER like BORDER=1
+ if (!rOption.GetString().isEmpty() &&
+ !rOption.GetString().equalsIgnoreAsciiCase(
+ OOO_STRING_SVTOOLS_HTML_O_border))
+ {
+ nBorder = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ }
+ else
+ nBorder = 1;
+
+ if( !bHasFrame )
+ eFrame = ( nBorder ? HTMLTableFrame::Box : HTMLTableFrame::Void );
+ if( !bHasRules )
+ eRules = ( nBorder ? HTMLTableRules::All : HTMLTableRules::NONE );
+ break;
+ case HtmlOptionId::FRAME:
+ eFrame = rOption.GetTableFrame();
+ bHasFrame = true;
+ break;
+ case HtmlOptionId::RULES:
+ eRules = rOption.GetTableRules();
+ bHasRules = true;
+ break;
+ case HtmlOptionId::BGCOLOR:
+ // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape
+ // *really* not on other tags
+ if( !rOption.GetString().isEmpty() )
+ {
+ rOption.GetColor( aBGColor );
+ bBGColor = true;
+ }
+ break;
+ case HtmlOptionId::BACKGROUND:
+ aBGImage = rOption.GetString();
+ break;
+ case HtmlOptionId::BORDERCOLOR:
+ rOption.GetColor( aBorderColor );
+ bBorderColor = true;
+ break;
+ case HtmlOptionId::BORDERCOLORDARK:
+ if( !bBorderColor )
+ rOption.GetColor( aBorderColor );
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ case HtmlOptionId::HSPACE:
+ nHSpace = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::VSPACE:
+ nVSpace = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ default: break;
+ }
+ }
+
+ if( nCols && !nWidth )
+ {
+ nWidth = 100;
+ bPercentWidth = true;
+ }
+
+ // If BORDER=0 or no BORDER given, then there shouldn't be a border
+ if( 0==nBorder || USHRT_MAX==nBorder )
+ {
+ eFrame = HTMLTableFrame::Void;
+ eRules = HTMLTableRules::NONE;
+ }
+}
+
+void SwHTMLParser::DeleteSection(SwStartNode* pSttNd)
+{
+ //if section to be deleted contains a pending m_pMarquee, it will be deleted
+ //so clear m_pMarquee pointer if that's the case
+ SwFrameFormat* pObjectFormat = m_pMarquee ? ::FindFrameFormat(m_pMarquee.get()) : nullptr;
+ FrameDeleteWatch aWatch(pObjectFormat);
+
+ m_xDoc->getIDocumentContentOperations().DeleteSection(pSttNd);
+
+ if (pObjectFormat)
+ {
+ if (aWatch.WasDeleted())
+ m_pMarquee = nullptr;
+ else
+ aWatch.EndListeningAll();
+ }
+}
+
+std::shared_ptr<HTMLTable> SwHTMLParser::BuildTable(SvxAdjust eParentAdjust,
+ bool bIsParentHead,
+ bool bHasParentSection,
+ bool bHasToFly)
+{
+ TableDepthGuard aGuard(*this);
+ if (aGuard.TooDeep())
+ eState = SvParserState::Error;
+
+ if (!IsParserWorking() && m_vPendingStack.empty())
+ return std::shared_ptr<HTMLTable>();
+
+ ::comphelper::FlagRestorationGuard g(m_isInTableStructure, true);
+ HtmlTokenId nToken = HtmlTokenId::NONE;
+ bool bPending = false;
+ std::unique_ptr<TableSaveStruct> xSaveStruct;
+
+ if( !m_vPendingStack.empty() )
+ {
+ xSaveStruct.reset(static_cast<TableSaveStruct*>(m_vPendingStack.back().pData.release()));
+
+ m_vPendingStack.pop_back();
+ nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
+ bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
+
+ SaveState( nToken );
+ }
+ else
+ {
+ m_xTable.reset();
+
+ // Parse CSS on the table.
+ OUString aStyle;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i;)
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ if (rOption.GetToken() == HtmlOptionId::STYLE)
+ {
+ aStyle = rOption.GetString();
+ }
+ }
+ if (!aStyle.isEmpty())
+ {
+ // Have inline CSS.
+ SfxItemSet aItemSet(m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap());
+ SvxCSS1PropertyInfo aPropInfo;
+ if (ParseStyleOptions(aStyle, /*aId=*/OUString(), /*aClass=*/OUString(), aItemSet,
+ aPropInfo))
+ {
+ if (aPropInfo.m_eLeftMarginType == SVX_CSS1_LTYPE_AUTO
+ && aPropInfo.m_eRightMarginType == SVX_CSS1_LTYPE_AUTO)
+ {
+ // Both left & right is set to auto: that's our center.
+ eParentAdjust = SvxAdjust::Center;
+ }
+ }
+ }
+
+ HTMLTableOptions aTableOptions(GetOptions(), eParentAdjust);
+
+ if (!aTableOptions.aId.isEmpty())
+ InsertBookmark(aTableOptions.aId);
+
+ std::shared_ptr<HTMLTable> xCurTable(std::make_shared<HTMLTable>(this,
+ bIsParentHead,
+ bHasParentSection,
+ bHasToFly,
+ aTableOptions));
+ m_xTable = xCurTable;
+
+ xSaveStruct.reset(new TableSaveStruct(xCurTable));
+
+ // Is pending on the first GetNextToken, needs to be re-read on each construction
+ SaveState( HtmlTokenId::NONE );
+ }
+
+ std::shared_ptr<HTMLTable> xCurTable = xSaveStruct->m_xCurrentTable;
+
+ // </TABLE> is needed according to DTD
+ if( nToken == HtmlTokenId::NONE )
+ nToken = GetNextToken();
+
+ bool bDone = false;
+ while( (IsParserWorking() && !bDone) || bPending )
+ {
+ SaveState( nToken );
+
+ nToken = FilterToken( nToken );
+
+ OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
+ xCurTable->GetContext() || xCurTable->HasParentSection(),
+ "Where is the section?" );
+ if( m_vPendingStack.empty() && m_bCallNextToken &&
+ (xCurTable->GetContext() || xCurTable->HasParentSection()) )
+ {
+ /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
+ NextToken( nToken );
+ }
+ else switch( nToken )
+ {
+ case HtmlTokenId::TABLE_ON:
+ if( !xCurTable->GetContext() )
+ {
+ // If there's no table added, read the next table'
+ SkipToken();
+ bDone = true;
+ }
+
+ break;
+ case HtmlTokenId::TABLE_OFF:
+ bDone = true;
+ break;
+ case HtmlTokenId::CAPTION_ON:
+ BuildTableCaption(xCurTable.get());
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::COL_ON:
+ SkipToken();
+ BuildTableColGroup(xCurTable.get(), false);
+ break;
+ case HtmlTokenId::COLGROUP_ON:
+ BuildTableColGroup(xCurTable.get(), true);
+ break;
+ case HtmlTokenId::TABLEROW_ON:
+ case HtmlTokenId::TABLEHEADER_ON:
+ case HtmlTokenId::TABLEDATA_ON:
+ SkipToken();
+ BuildTableSection(xCurTable.get(), false, false);
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::THEAD_ON:
+ case HtmlTokenId::TFOOT_ON:
+ case HtmlTokenId::TBODY_ON:
+ BuildTableSection(xCurTable.get(), true, HtmlTokenId::THEAD_ON==nToken);
+ bDone = m_xTable->IsOverflowing();
+ break;
+ case HtmlTokenId::MULTICOL_ON:
+ // we can't add columned text frames here
+ break;
+ case HtmlTokenId::FORM_ON:
+ NewForm( false ); // don't add a new paragraph
+ break;
+ case HtmlTokenId::FORM_OFF:
+ EndForm( false ); // don't add a new paragraph
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ // blank strings may be a series of CR+LF and no text
+ if( (xCurTable->GetContext() ||
+ !xCurTable->HasParentSection()) &&
+ 1==aToken.getLength() && ' '==aToken[0] )
+ break;
+ [[fallthrough]];
+ default:
+ xCurTable->MakeParentContents();
+ NextToken( nToken );
+ break;
+ }
+
+ OSL_ENSURE( !bPending || m_vPendingStack.empty(),
+ "SwHTMLParser::BuildTable: There is a PendStack again" );
+ bPending = false;
+ if( IsParserWorking() )
+ SaveState( HtmlTokenId::NONE );
+
+ if( !bDone )
+ nToken = GetNextToken();
+ }
+
+ if( SvParserState::Pending == GetStatus() )
+ {
+ m_vPendingStack.emplace_back( HtmlTokenId::TABLE_ON );
+ m_vPendingStack.back().pData = std::move(xSaveStruct);
+ return std::shared_ptr<HTMLTable>();
+ }
+
+ HTMLTableContext *pTCntxt = xCurTable->GetContext();
+ if( pTCntxt )
+ {
+
+ // Modify table structure
+ xCurTable->CloseTable();
+
+ // end contexts that began out of cells. Needs to exist before (!) we move the table,
+ // since the current one doesn't exist anymore afterwards
+ while( m_aContexts.size() > m_nContextStAttrMin )
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ if (!xCntxt)
+ break;
+ ClearContext(xCntxt.get());
+ }
+
+ m_nContextStMin = pTCntxt->GetContextStMin();
+ m_nContextStAttrMin = pTCntxt->GetContextStAttrMin();
+
+ if (m_xTable == xCurTable)
+ {
+ // Set table caption
+ const SwStartNode *pCapStNd = m_xTable->GetCaptionStartNode();
+ if( pCapStNd )
+ {
+ // The last paragraph of the section is never part of the copy.
+ // That's why the section needs to contain at least two paragraphs
+
+ if( pCapStNd->EndOfSectionIndex() - pCapStNd->GetIndex() > SwNodeOffset(2) )
+ {
+ // Don't copy start node and the last paragraph
+ SwNodeRange aSrcRg( *pCapStNd, SwNodeOffset(1),
+ *pCapStNd->EndOfSectionNode(), SwNodeOffset(-1) );
+
+ bool bTop = m_xTable->IsTopCaption();
+ SwStartNode *pTableStNd = pTCntxt->GetTableNode();
+
+ OSL_ENSURE( pTableStNd, "Where is the table node" );
+ OSL_ENSURE( pTableStNd == m_pPam->GetPointNode().FindTableNode(),
+ "Are we in the wrong table?" );
+
+ if (pTableStNd)
+ {
+ SwNode* pNd;
+ if( bTop )
+ pNd = pTableStNd;
+ else
+ pNd = pTableStNd->EndOfSectionNode();
+ SwNodeIndex aDstIdx( *pNd, bTop ? 0 : 1 );
+
+ m_xDoc->getIDocumentContentOperations().MoveNodeRange( aSrcRg, aDstIdx.GetNode(),
+ SwMoveFlags::DEFAULT );
+
+ // If the caption was added before the table, a page style on that table
+ // needs to be moved to the first paragraph of the header.
+ // Additionally, all remembered indices that point to the table node
+ // need to be moved
+ if( bTop )
+ {
+ MovePageDescAttrs( pTableStNd, aSrcRg.aStart.GetIndex(),
+ false );
+ }
+ }
+ }
+
+ // The section isn't needed anymore
+ m_pPam->SetMark();
+ m_pPam->DeleteMark();
+ DeleteSection(const_cast<SwStartNode*>(pCapStNd));
+ m_xTable->SetCaption( nullptr, false );
+ }
+
+ // Process SwTable
+ sal_uInt16 nBrowseWidth = o3tl::narrowing<sal_uInt16>(GetCurrentBrowseWidth());
+ xSaveStruct->MakeTable(nBrowseWidth, *m_pPam->GetPoint(), m_xDoc.get());
+ }
+
+ GetNumInfo().Set( pTCntxt->GetNumInfo() );
+ pTCntxt->RestorePREListingXMP( *this );
+ RestoreAttrTab(pTCntxt->m_xAttrTab);
+
+ if (m_xTable == xCurTable)
+ {
+ // Set upper paragraph spacing
+ m_bUpperSpace = true;
+ SetTextCollAttrs();
+
+ SwTableNode* pTableNode = pTCntxt->GetTableNode();
+ size_t nTableBoxSize = pTableNode ? pTableNode->GetTable().GetTabSortBoxes().size() : 0;
+ m_nParaCnt = m_nParaCnt - std::min(m_nParaCnt, nTableBoxSize);
+
+ // Jump to a table if needed
+ if( JumpToMarks::Table == m_eJumpTo && m_xTable->GetSwTable() &&
+ m_xTable->GetSwTable()->GetFrameFormat()->GetName() == m_sJmpMark )
+ {
+ m_bChkJumpMark = true;
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+
+ // If the import was canceled, don't call Show again here since
+ // the SwViewShell was already deleted
+ // That's not enough. Even in the ACCEPTING_STATE, a Show mustn't be called
+ // because otherwise the parser's gonna be destroyed on the reschedule,
+ // if there's still a DataAvailable link coming. So: only in the WORKING state
+ if( !m_nParaCnt && SvParserState::Working == GetStatus() )
+ Show();
+ }
+ }
+ else if (m_xTable == xCurTable)
+ {
+ // There was no table read
+
+ // We maybe need to delete a read caption
+ const SwStartNode *pCapStNd = xCurTable->GetCaptionStartNode();
+ if( pCapStNd )
+ {
+ m_pPam->SetMark();
+ m_pPam->DeleteMark();
+ DeleteSection(const_cast<SwStartNode*>(pCapStNd));
+ xCurTable->SetCaption( nullptr, false );
+ }
+ }
+
+ if (m_xTable == xCurTable)
+ {
+ xSaveStruct->m_xCurrentTable.reset();
+ m_xTable.reset();
+ }
+
+ std::shared_ptr<HTMLTable> xRetTable = xSaveStruct->m_xCurrentTable;
+ xSaveStruct.reset();
+
+ return xRetTable;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/htmltabw.cxx b/sw/source/filter/html/htmltabw.cxx
new file mode 100644
index 0000000000..b1ef282396
--- /dev/null
+++ b/sw/source/filter/html/htmltabw.cxx
@@ -0,0 +1,1156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <vcl/svapp.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/HtmlWriter.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <fmtornt.hxx>
+#include <frmfmt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <frmatr.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swrect.hxx>
+#include <cellatr.hxx>
+#include <poolfmt.hxx>
+#include <swtable.hxx>
+#include <htmltbl.hxx>
+#include "htmlnum.hxx"
+#include "wrthtml.hxx"
+#include <wrtswtbl.hxx>
+#ifdef DBG_UTIL
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#endif
+#include <rtl/strbuf.hxx>
+#include <sal/types.h>
+#include <osl/diagnose.h>
+
+#define MAX_DEPTH (3)
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SwHTMLWrtTable : public SwWriteTable
+{
+ static void Pixelize( sal_uInt16& rValue );
+ void PixelizeBorders();
+
+ /// Writes a single table cell.
+ ///
+ /// bCellRowSpan decides if the cell's row span should be written or not.
+ void OutTableCell( SwHTMLWriter& rWrt, const SwWriteTableCell *pCell,
+ bool bOutVAlign,
+ bool bCellRowSpan ) const;
+
+ /// Writes a single table row.
+ ///
+ /// rSkipRows decides if the next N rows should be skipped or written.
+ void OutTableCells( SwHTMLWriter& rWrt,
+ const SwWriteTableCells& rCells,
+ const SvxBrushItem *pBrushItem,
+ sal_uInt16& rSkipRows ) const;
+
+ virtual bool ShouldExpandSub( const SwTableBox *pBox,
+ bool bExpandedBefore, sal_uInt16 nDepth ) const override;
+
+ static bool HasTabBackground( const SwTableLine& rLine,
+ bool bTop, bool bBottom, bool bLeft, bool bRight );
+ static bool HasTabBackground( const SwTableBox& rBox,
+ bool bTop, bool bBottom, bool bLeft, bool bRight );
+
+public:
+ SwHTMLWrtTable( const SwTableLines& rLines, tools::Long nWidth, sal_uInt32 nBWidth,
+ bool bRel, sal_uInt16 nLeftSub, sal_uInt16 nRightSub,
+ sal_uInt16 nNumOfRowsToRepeat );
+ explicit SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo );
+
+ void Write( SwHTMLWriter& rWrt, sal_Int16 eAlign=text::HoriOrientation::NONE,
+ bool bTHead=false, const SwFrameFormat *pFrameFormat=nullptr,
+ const OUString *pCaption=nullptr, bool bTopCaption=false,
+ sal_uInt16 nHSpace=0, sal_uInt16 nVSpace=0 ) const;
+};
+
+}
+
+SwHTMLWrtTable::SwHTMLWrtTable( const SwTableLines& rLines, tools::Long nWidth,
+ sal_uInt32 nBWidth, bool bRel,
+ sal_uInt16 nLSub, sal_uInt16 nRSub,
+ sal_uInt16 nNumOfRowsToRepeat )
+ : SwWriteTable(nullptr, rLines, nWidth, nBWidth, bRel, MAX_DEPTH, nLSub, nRSub, nNumOfRowsToRepeat)
+{
+ PixelizeBorders();
+}
+
+SwHTMLWrtTable::SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo )
+ : SwWriteTable(nullptr, pLayoutInfo)
+{
+ // Adjust some Twip values to pixel limits
+ if( m_bCollectBorderWidth )
+ PixelizeBorders();
+}
+
+void SwHTMLWrtTable::Pixelize( sal_uInt16& rValue )
+{
+ if( rValue )
+ {
+ rValue = o3tl::convert(SwHTMLWriter::ToPixel(rValue), o3tl::Length::px, o3tl::Length::twip);
+ }
+}
+
+void SwHTMLWrtTable::PixelizeBorders()
+{
+ Pixelize( m_nBorder );
+ Pixelize( m_nCellSpacing );
+ Pixelize( m_nCellPadding );
+}
+
+bool SwHTMLWrtTable::HasTabBackground( const SwTableBox& rBox,
+ bool bTop, bool bBottom, bool bLeft, bool bRight )
+{
+ OSL_ENSURE( bTop || bBottom || bLeft || bRight,
+ "HasTabBackground: cannot be called" );
+
+ bool bRet = false;
+ if( rBox.GetSttNd() )
+ {
+ std::unique_ptr<SvxBrushItem> aBrushItem =
+ rBox.GetFrameFormat()->makeBackgroundBrushItem();
+
+ /// The table box has a background, if its background color is not "no fill"/
+ /// "auto fill" or it has a background graphic.
+ bRet = aBrushItem->GetColor() != COL_TRANSPARENT ||
+ !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic();
+ }
+ else
+ {
+ const SwTableLines& rLines = rBox.GetTabLines();
+ const SwTableLines::size_type nCount = rLines.size();
+ bool bLeftRight = bLeft || bRight;
+ for( SwTableLines::size_type i=0; !bRet && i<nCount; ++i )
+ {
+ bool bT = bTop && 0 == i;
+ bool bB = bBottom && nCount-1 == i;
+ if( bT || bB || bLeftRight )
+ bRet = HasTabBackground( *rLines[i], bT, bB, bLeft, bRight);
+ }
+ }
+
+ return bRet;
+}
+
+bool SwHTMLWrtTable::HasTabBackground( const SwTableLine& rLine,
+ bool bTop, bool bBottom, bool bLeft, bool bRight )
+{
+ OSL_ENSURE( bTop || bBottom || bLeft || bRight,
+ "HasTabBackground: cannot be called" );
+
+ std::unique_ptr<SvxBrushItem> aBrushItem = rLine.GetFrameFormat()->makeBackgroundBrushItem();
+ /// The table line has a background, if its background color is not "no fill"/
+ /// "auto fill" or it has a background graphic.
+ bool bRet = aBrushItem->GetColor() != COL_TRANSPARENT ||
+ !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic();
+
+ if( !bRet )
+ {
+ const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
+ const SwTableBoxes::size_type nCount = rBoxes.size();
+ bool bTopBottom = bTop || bBottom;
+ for( SwTableBoxes::size_type i=0; !bRet && i<nCount; ++i )
+ {
+ bool bL = bLeft && 0 == i;
+ bool bR = bRight && nCount-1 == i;
+ if( bTopBottom || bL || bR )
+ bRet = HasTabBackground( *rBoxes[i], bTop, bBottom, bL, bR );
+ }
+ }
+
+ return bRet;
+}
+
+static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders );
+
+static bool lcl_TableBox_HasTabBorders( const SwTableBox* pBox, bool *pBorders )
+{
+ if( *pBorders )
+ return false;
+
+ if( !pBox->GetSttNd() )
+ {
+ for( const auto& rpLine : pBox->GetTabLines() )
+ {
+ if ( lcl_TableLine_HasTabBorders( rpLine, pBorders ) )
+ break;
+ }
+ }
+ else
+ {
+ const SvxBoxItem& rBoxItem =
+ pBox->GetFrameFormat()->GetFormatAttr( RES_BOX );
+
+ *pBorders = rBoxItem.GetTop() || rBoxItem.GetBottom() ||
+ rBoxItem.GetLeft() || rBoxItem.GetRight();
+ }
+
+ return !*pBorders;
+}
+
+static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders )
+{
+ if( *pBorders )
+ return false;
+
+ for( const auto& rpBox : pLine->GetTabBoxes() )
+ {
+ if ( lcl_TableBox_HasTabBorders( rpBox, pBorders ) )
+ break;
+ }
+ return !*pBorders;
+}
+
+bool SwHTMLWrtTable::ShouldExpandSub( const SwTableBox *pBox,
+ bool bExpandedBefore,
+ sal_uInt16 nDepth ) const
+{
+ bool bExpand = !pBox->GetSttNd() && nDepth>0;
+ if( bExpand && bExpandedBefore )
+ {
+ // MIB 30.6.97: If a box was already expanded, another one is only
+ // expanded when it has a border.
+ bool bBorders = false;
+ lcl_TableBox_HasTabBorders( pBox, &bBorders );
+ if( !bBorders )
+ bBorders = HasTabBackground( *pBox, true, true, true, true );
+ bExpand = bBorders;
+ }
+
+ return bExpand;
+}
+
+// Write a box as single cell
+void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt,
+ const SwWriteTableCell *pCell,
+ bool bOutVAlign,
+ bool bCellRowSpan ) const
+{
+ const SwTableBox *pBox = pCell->GetBox();
+ sal_uInt16 nRow = pCell->GetRow();
+ sal_uInt16 nCol = pCell->GetCol();
+ sal_uInt16 nRowSpan = pCell->GetRowSpan();
+ sal_uInt16 nColSpan = pCell->GetColSpan();
+
+ if ( !nRowSpan )
+ return;
+
+ const SwStartNode* pSttNd = pBox->GetSttNd();
+ bool bHead = false;
+ if( pSttNd )
+ {
+ SwNodeOffset nNdPos = pSttNd->GetIndex()+1;
+
+ // determine the type of cell (TD/TH)
+ SwNode* pNd;
+ while( !( pNd = rWrt.m_pDoc->GetNodes()[nNdPos])->IsEndNode() )
+ {
+ if( pNd->IsTextNode() )
+ {
+ // The only paragraphs relevant for the distinction are those
+ // where the style is one of the two table related styles
+ // or inherits from one of these.
+ const SwFormat *pFormat = &static_cast<SwTextNode*>(pNd)->GetAnyFormatColl();
+ sal_uInt16 nPoolId = pFormat->GetPoolFormatId();
+ while( !pFormat->IsDefault() &&
+ RES_POOLCOLL_TABLE_HDLN!=nPoolId &&
+ RES_POOLCOLL_TABLE!=nPoolId )
+ {
+ pFormat = pFormat->DerivedFrom();
+ nPoolId = pFormat->GetPoolFormatId();
+ }
+
+ if( !pFormat->IsDefault() )
+ {
+ bHead = (RES_POOLCOLL_TABLE_HDLN==nPoolId);
+ break;
+ }
+ }
+ nNdPos++;
+ }
+ }
+
+ rWrt.OutNewLine(); // <TH>/<TD> in new line
+ OStringBuffer sOut("<");
+ OString aTag(bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata);
+ sOut.append(rWrt.GetNamespace() + aTag);
+
+ // output ROW- and COLSPAN
+ if (nRowSpan > 1 && bCellRowSpan)
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_rowspan
+ "=\"" + OString::number(nRowSpan) + "\"");
+ }
+ if( nColSpan > 1 )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_colspan
+ "=\"" + OString::number(nColSpan) + "\"");
+ }
+
+ tools::Long nWidth = 0;
+ bool bOutWidth = true;
+ sal_uInt32 nPercentWidth = SAL_MAX_UINT32;
+
+ if( m_bLayoutExport )
+ {
+ if( pCell->HasPercentWidthOpt() )
+ {
+ nPercentWidth = pCell->GetWidthOpt();
+ }
+ else
+ {
+ nWidth = pCell->GetWidthOpt();
+ if( !nWidth )
+ bOutWidth = false;
+ }
+ }
+ else
+ {
+ if( HasRelWidths() )
+ nPercentWidth = GetPercentWidth(nCol, nColSpan);
+ else
+ nWidth = GetAbsWidth( nCol, nColSpan );
+ }
+
+ if (rWrt.mbReqIF)
+ // ReqIF implies strict XHTML: no width for <td>.
+ bOutWidth = false;
+
+ tools::Long nHeight = pCell->GetHeight() > 0
+ ? GetAbsHeight( pCell->GetHeight(), nRow, nRowSpan )
+ : 0;
+ Size aPixelSz(SwHTMLWriter::ToPixel(nWidth), SwHTMLWriter::ToPixel(nHeight));
+
+ // output WIDTH: from layout or calculated
+ if( bOutWidth )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
+ if( nPercentWidth != SAL_MAX_UINT32 )
+ {
+ sOut.append(OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%");
+ }
+ else
+ {
+ sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
+ }
+ sOut.append("\"");
+ }
+
+ if (rWrt.mbReqIF)
+ {
+ // ReqIF implies strict XHTML: no height for <td>.
+ nHeight = 0;
+ }
+
+ if( nHeight )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height
+ "=\"" + OString::number(aPixelSz.Height()) + "\"");
+ }
+
+ const SfxItemSet& rItemSet = pBox->GetFrameFormat()->GetAttrSet();
+
+ // ALIGN is only outputted at the paragraphs from now on
+
+ // output VALIGN
+ if( bOutVAlign )
+ {
+ sal_Int16 eVertOri = pCell->GetVertOri();
+ if( text::VertOrientation::TOP==eVertOri || text::VertOrientation::BOTTOM==eVertOri )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_valign
+ "=\"").append(text::VertOrientation::TOP==eVertOri ?
+ OOO_STRING_SVTOOLS_HTML_VA_top :
+ OOO_STRING_SVTOOLS_HTML_VA_bottom)
+ .append("\"");
+ }
+ }
+
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ rWrt.m_bTextAttr = false;
+ rWrt.m_bOutOpts = true;
+ const SvxBrushItem *pBrushItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
+ if( !pBrushItem )
+ pBrushItem = pCell->GetBackground();
+
+ if( pBrushItem )
+ {
+ // output background
+ if (!rWrt.mbReqIF)
+ // Avoid non-CSS version in the ReqIF case.
+ rWrt.OutBackground( pBrushItem, false );
+
+ if (!rWrt.m_bCfgOutStyles)
+ pBrushItem = nullptr;
+ }
+
+ // tdf#132739 with rWrt.m_bCfgOutStyles of true bundle the brush item css
+ // properties into the same "style" tag as the borders so there is only one
+ // style tag
+ rWrt.OutCSS1_TableCellBordersAndBG(*pBox->GetFrameFormat(), pBrushItem);
+
+ sal_uInt32 nNumFormat = 0;
+ double nValue = 0.0;
+ bool bNumFormat = false, bValue = false;
+ if( const SwTableBoxNumFormat* pItem = rItemSet.GetItemIfSet( RES_BOXATR_FORMAT, false ) )
+ {
+ nNumFormat = pItem->GetValue();
+ bNumFormat = true;
+ }
+ if( const SwTableBoxValue* pItem = rItemSet.GetItemIfSet( RES_BOXATR_VALUE, false ) )
+ {
+ nValue = pItem->GetValue();
+ bValue = true;
+ if( !bNumFormat )
+ nNumFormat = pBox->GetFrameFormat()->GetTableBoxNumFormat().GetValue();
+ }
+
+ if ((bNumFormat || bValue) && !rWrt.mbXHTML)
+ {
+ sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValue, nValue,
+ nNumFormat, *rWrt.m_pDoc->GetNumberFormatter()));
+ }
+ sOut.append('>');
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ rWrt.SetLFPossible(true);
+
+ rWrt.IncIndentLevel(); // indent the content of <TD>...</TD>
+
+ if( pSttNd )
+ {
+ HTMLSaveData aSaveData( rWrt, pSttNd->GetIndex()+1,
+ pSttNd->EndOfSectionIndex() );
+ rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
+ }
+ else
+ {
+ sal_uInt16 nTWidth;
+ sal_uInt32 nBWidth;
+ sal_uInt16 nLSub, nRSub;
+ if( HasRelWidths() )
+ {
+ nTWidth = 100;
+ nBWidth = GetRawWidth( nCol, nColSpan );
+ nLSub = 0;
+ nRSub = 0;
+ }
+ else
+ {
+ nTWidth = GetAbsWidth( nCol, nColSpan );
+ nBWidth = nTWidth;
+ nLSub = GetLeftSpace( nCol );
+ nRSub = GetRightSpace( nCol, nColSpan );
+ }
+
+ SwHTMLWrtTable aTableWrt( pBox->GetTabLines(), nTWidth,
+ nBWidth, HasRelWidths(), nLSub, nRSub, /*nNumOfRowsToRepeat*/0 );
+ aTableWrt.Write( rWrt );
+ }
+
+ rWrt.DecIndentLevel(); // indent the content of <TD>...</TD>
+
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine();
+ aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata;
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
+ rWrt.SetLFPossible(true);
+}
+
+// output a line as lines
+void SwHTMLWrtTable::OutTableCells( SwHTMLWriter& rWrt,
+ const SwWriteTableCells& rCells,
+ const SvxBrushItem *pBrushItem,
+ sal_uInt16& rSkipRows ) const
+{
+ // If the line contains more the one cell and all cells have the same
+ // alignment, then output the VALIGN at the line instead of the cell.
+ sal_Int16 eRowVertOri = text::VertOrientation::NONE;
+ if( rCells.size() > 1 )
+ {
+ for (SwWriteTableCells::size_type nCell = 0; nCell < rCells.size(); ++nCell)
+ {
+ sal_Int16 eCellVertOri = rCells[nCell]->GetVertOri();
+ if( 0==nCell )
+ {
+ eRowVertOri = eCellVertOri;
+ }
+ else if( eRowVertOri != eCellVertOri )
+ {
+ eRowVertOri = text::VertOrientation::NONE;
+ break;
+ }
+ }
+ }
+
+ rWrt.OutNewLine(); // <TR> in new line
+ rWrt.Strm().WriteChar( '<' ).WriteOString( Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow) );
+ if( pBrushItem )
+ {
+ if (!rWrt.mbXHTML)
+ {
+ rWrt.OutBackground(pBrushItem, false);
+ }
+
+ rWrt.m_bTextAttr = false;
+ rWrt.m_bOutOpts = true;
+ if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
+ OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
+ }
+
+ if( text::VertOrientation::TOP==eRowVertOri || text::VertOrientation::BOTTOM==eRowVertOri )
+ {
+ OStringBuffer sOut;
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_valign
+ "=\"").append(text::VertOrientation::TOP==eRowVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
+ .append("\"");
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ }
+
+ rWrt.Strm().WriteChar( '>' );
+
+ rWrt.IncIndentLevel(); // indent content of <TR>...</TR>
+
+ bool bCellRowSpan = true;
+ if (!rCells.empty() && rCells[0]->GetRowSpan() > 1)
+ {
+ // Skip the rowspan attrs of <td> elements if they are the same for every cell of this row.
+ bCellRowSpan = std::adjacent_find(rCells.begin(), rCells.end(),
+ [](const std::unique_ptr<SwWriteTableCell>& pA,
+ const std::unique_ptr<SwWriteTableCell>& pB)
+ { return pA->GetRowSpan() != pB->GetRowSpan(); })
+ != rCells.end();
+ if (!bCellRowSpan)
+ {
+ // If no rowspan is written, then skip rows which would only contain covered cells, but
+ // not the current row.
+ rSkipRows = rCells[0]->GetRowSpan() - 1;
+ }
+ }
+
+ for (const auto &rpCell : rCells)
+ {
+ OutTableCell(rWrt, rpCell.get(), text::VertOrientation::NONE == eRowVertOri, bCellRowSpan);
+ }
+
+ rWrt.DecIndentLevel(); // indent content of <TR>...</TR>
+
+ rWrt.OutNewLine(); // </TR> in new line
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow), false );
+}
+
+void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign,
+ bool bTHead, const SwFrameFormat *pFrameFormat,
+ const OUString *pCaption, bool bTopCaption,
+ sal_uInt16 nHSpace, sal_uInt16 nVSpace ) const
+{
+ // determine value of RULES
+ bool bRowsHaveBorder = false;
+ bool bRowsHaveBorderOnly = true;
+ assert(m_aRows.begin() != m_aRows.end());
+ for (auto row = m_aRows.begin(), next = std::next(row); next < m_aRows.end(); ++row, ++next)
+ {
+ SwWriteTableRow* pRow = row->get();
+ SwWriteTableRow* pNextRow = next->get();
+ bool bBorder = ( pRow->HasBottomBorder() || pNextRow->HasTopBorder() );
+ bRowsHaveBorder |= bBorder;
+ bRowsHaveBorderOnly &= bBorder;
+
+ pRow->SetBottomBorder(bBorder);
+ pNextRow->SetTopBorder(bBorder);
+ }
+
+ bool bColsHaveBorder = false;
+ bool bColsHaveBorderOnly = true;
+ assert(m_aCols.begin() != m_aCols.end());
+ for (auto col = m_aCols.begin(), next = std::next(col); next < m_aCols.end(); ++col, ++next)
+ {
+ SwWriteTableCol* pCol = col->get();
+ SwWriteTableCol* pNextCol = next->get();
+ bool bBorder = ( pCol->m_bRightBorder || pNextCol->m_bLeftBorder );
+ bColsHaveBorder |= bBorder;
+ bColsHaveBorderOnly &= bBorder;
+ pCol->m_bRightBorder = bBorder;
+ pNextCol->m_bLeftBorder = bBorder;
+ }
+
+ // close previous numbering, etc
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine(); // <TABLE> in new line
+ OStringBuffer sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table);
+
+ const SvxFrameDirection nOldDirection = rWrt.m_nDirection;
+ if( pFrameFormat )
+ rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
+ if( rWrt.m_bOutFlyFrame || nOldDirection != rWrt.m_nDirection )
+ {
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ rWrt.OutDirection( rWrt.m_nDirection );
+ }
+
+ // output ALIGN=
+ if( text::HoriOrientation::RIGHT == eAlign )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
+ "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"");
+ }
+ else if( text::HoriOrientation::CENTER == eAlign )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
+ "=\"" OOO_STRING_SVTOOLS_HTML_AL_center "\"");
+ }
+ else if( text::HoriOrientation::LEFT == eAlign )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
+ "=\"" OOO_STRING_SVTOOLS_HTML_AL_left "\"");
+ }
+
+ // output WIDTH: from layout or calculated
+ if( m_nTabWidth )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
+ if( HasRelWidths() )
+ sOut.append(OString::number(static_cast<sal_Int32>(m_nTabWidth)) + "%");
+ else
+ {
+ sal_Int32 nPixWidth = SwHTMLWriter::ToPixel(m_nTabWidth);
+ sOut.append(nPixWidth);
+ }
+ sOut.append("\"");
+ }
+
+ if( (nHSpace || nVSpace) && !rWrt.mbReqIF)
+ {
+ if (auto nPixHSpace = SwHTMLWriter::ToPixel(nHSpace))
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
+ "=\"" + OString::number(nPixHSpace) + "\"");
+ }
+
+ if (auto nPixVSpace = SwHTMLWriter::ToPixel(nVSpace))
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
+ "=\"" + OString::number(nPixVSpace) + "\"");
+ }
+ }
+
+ // output CELLPADDING: from layout or calculated
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellpadding
+ "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellPadding)) + "\"");
+
+ // output CELLSPACING: from layout or calculated
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
+ "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellSpacing)) + "\"");
+
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ // output background
+ if( pFrameFormat )
+ {
+ if (!rWrt.mbXHTML)
+ {
+ rWrt.OutBackground(pFrameFormat->GetAttrSet(), false);
+ }
+
+ if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
+ {
+ rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat );
+ }
+ }
+
+ sOut.append('>');
+ rWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ rWrt.IncIndentLevel(); // indent content of table
+
+ // output caption
+ if( pCaption && !pCaption->isEmpty() )
+ {
+ rWrt.OutNewLine(); // <CAPTION> in new line
+ OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption);
+ sOutStr.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"")
+ .append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
+ .append("\"");
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOutStr) );
+ HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption );
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption), false );
+ }
+
+ // output <COLGRP>/<COL>: If exporting via layout only when during import
+ // some were there, otherwise always.
+ bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly);
+ if( m_bColTags )
+ {
+ if( bColGroups )
+ {
+ rWrt.OutNewLine(); // <COLGRP> in new line
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
+
+ rWrt.IncIndentLevel(); // indent content of <COLGRP>
+ }
+
+ const SwWriteTableCols::size_type nCols = m_aCols.size();
+ for( SwWriteTableCols::size_type nCol=0; nCol<nCols; ++nCol )
+ {
+ rWrt.OutNewLine(); // </COL> in new line
+
+ const SwWriteTableCol *pColumn = m_aCols[nCol].get();
+
+ HtmlWriter html(rWrt.Strm(), rWrt.maNamespace);
+ html.prettyPrint(false); // We add newlines ourself
+ html.start(OOO_STRING_SVTOOLS_HTML_col ""_ostr);
+
+ sal_uInt32 nWidth;
+ bool bRel;
+ if( m_bLayoutExport )
+ {
+ bRel = pColumn->HasRelWidthOpt();
+ nWidth = pColumn->GetWidthOpt();
+ }
+ else
+ {
+ bRel = HasRelWidths();
+ nWidth = bRel ? GetRelWidth(nCol,1) : GetAbsWidth(nCol,1);
+ }
+
+ if( bRel )
+ html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, Concat2View(OString::number(nWidth) + "*"));
+ else
+ html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(SwHTMLWriter::ToPixel(nWidth)));
+ html.end();
+
+ if( bColGroups && pColumn->m_bRightBorder && nCol<nCols-1 )
+ {
+ rWrt.DecIndentLevel(); // indent content of <COLGRP>
+ rWrt.OutNewLine(); // </COLGRP> in new line
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
+ false );
+ rWrt.OutNewLine(); // <COLGRP> in new line
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
+ rWrt.IncIndentLevel(); // indent content of <COLGRP>
+ }
+ }
+ if( bColGroups )
+ {
+ rWrt.DecIndentLevel(); // indent content of <COLGRP>
+
+ rWrt.OutNewLine(); // </COLGRP> in new line
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
+ false );
+ }
+ }
+
+ // output the lines as table lines
+
+ // Output <TBODY>?
+ bool bTSections = (bRowsHaveBorder && !bRowsHaveBorderOnly);
+ bool bTBody = bTSections;
+
+ // If sections must be outputted, then a THEAD around the first line only
+ // can be outputted if there is a line below the cell.
+ if( bTHead &&
+ (bTSections || bColGroups) &&
+ m_nHeadEndRow<m_aRows.size()-1 && !m_aRows[m_nHeadEndRow]->HasBottomBorder() )
+ bTHead = false;
+
+ // Output <TBODY> only if <THEAD> is outputted.
+ bTSections |= bTHead;
+
+ if( bTSections )
+ {
+ rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
+ OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag));
+
+ rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
+ }
+
+ sal_uInt16 nSkipRows = 0;
+ for( SwWriteTableRows::size_type nRow = 0; nRow < m_aRows.size(); ++nRow )
+ {
+ const SwWriteTableRow *pRow = m_aRows[nRow].get();
+
+ if (nSkipRows == 0)
+ {
+ OutTableCells(rWrt, pRow->GetCells(), pRow->GetBackground(), nSkipRows);
+ }
+ else
+ {
+ --nSkipRows;
+ }
+ if( ( (bTHead && nRow==m_nHeadEndRow) ||
+ (bTBody && pRow->HasBottomBorder()) ) &&
+ nRow < m_aRows.size()-1 )
+ {
+ rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
+ rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
+ OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
+ rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
+
+ if( bTHead && nRow==m_nHeadEndRow )
+ bTHead = false;
+
+ aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag));
+ rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
+ }
+ }
+
+ if( bTSections )
+ {
+ rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
+
+ rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
+ OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
+ }
+
+ rWrt.DecIndentLevel(); // indent content of <TABLE>
+
+ rWrt.OutNewLine(); // </TABLE> in new line
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table), false );
+
+ rWrt.m_nDirection = nOldDirection;
+}
+
+SwHTMLWriter& OutHTML_SwTableNode( SwHTMLWriter& rWrt, SwTableNode & rNode,
+ const SwFrameFormat *pFlyFrameFormat,
+ const OUString *pCaption, bool bTopCaption )
+{
+
+ SwTable& rTable = rNode.GetTable();
+
+ rWrt.m_bOutTable = true;
+
+ // The horizontal alignment of the frame (if exists) has priority.
+ // NONE means that no horizontal alignment was outputted.
+ sal_Int16 eFlyHoriOri = text::HoriOrientation::NONE;
+ css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
+ sal_uInt8 nFlyPercentWidth = 0;
+ tools::Long nFlyWidth = 0;
+ sal_uInt16 nFlyHSpace = 0;
+ sal_uInt16 nFlyVSpace = 0;
+ if( pFlyFrameFormat )
+ {
+ eSurround = pFlyFrameFormat->GetSurround().GetSurround();
+ const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize();
+ nFlyPercentWidth = rFrameSize.GetWidthPercent();
+ nFlyWidth = rFrameSize.GetSize().Width();
+
+ eFlyHoriOri = pFlyFrameFormat->GetHoriOrient().GetHoriOrient();
+ if( text::HoriOrientation::NONE == eFlyHoriOri )
+ eFlyHoriOri = text::HoriOrientation::LEFT;
+
+ const SvxLRSpaceItem& rLRSpace = pFlyFrameFormat->GetLRSpace();
+ nFlyHSpace = static_cast< sal_uInt16 >((rLRSpace.GetLeft() + rLRSpace.GetRight()) / 2);
+
+ const SvxULSpaceItem& rULSpace = pFlyFrameFormat->GetULSpace();
+ nFlyVSpace = (rULSpace.GetUpper() + rULSpace.GetLower()) / 2;
+ }
+
+ // maybe open a FORM
+ bool bPreserveForm = false;
+ if( !rWrt.m_bPreserveForm )
+ {
+ rWrt.OutForm( true, &rNode );
+ bPreserveForm = rWrt.mxFormComps.is();
+ rWrt.m_bPreserveForm = bPreserveForm;
+ }
+
+ SwFrameFormat *pFormat = rTable.GetFrameFormat();
+
+ const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize();
+ tools::Long nWidth = rFrameSize.GetSize().Width();
+ sal_uInt8 nPercentWidth = rFrameSize.GetWidthPercent();
+ sal_uInt16 nBaseWidth = o3tl::narrowing<sal_uInt16>(nWidth);
+
+ sal_Int16 eTabHoriOri = pFormat->GetHoriOrient().GetHoriOrient();
+
+ // text::HoriOrientation::NONE and text::HoriOrientation::FULL tables need relative widths
+ sal_uInt16 nNewDefListLvl = 0;
+ bool bRelWidths = false;
+ bool bCheckDefList = false;
+ switch( eTabHoriOri )
+ {
+ case text::HoriOrientation::FULL:
+ // Tables with automatic alignment become tables with 100% width.
+ bRelWidths = true;
+ nWidth = 100;
+ eTabHoriOri = text::HoriOrientation::LEFT;
+ break;
+ case text::HoriOrientation::NONE:
+ {
+ const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
+ if( aLRItem.GetRight() )
+ {
+ // The table width is defined on the basis of the left and
+ // right margin. Therefore we try to define the actual
+ // width of the table. If that's not possible we transform
+ // it to a table with width 100%.
+ nWidth = pFormat->FindLayoutRect(true).Width();
+ if( !nWidth )
+ {
+ bRelWidths = true;
+ nWidth = 100;
+ }
+
+ }
+ else if( nPercentWidth )
+ {
+ // Without a right border the %-width is maintained.
+ nWidth = nPercentWidth;
+ bRelWidths = true;
+ }
+ else
+ {
+ // Without a right margin also an absolute width is maintained.
+ // We still try to define the actual width via the layout.
+ tools::Long nRealWidth = pFormat->FindLayoutRect(true).Width();
+ if( nRealWidth )
+ nWidth = nRealWidth;
+ }
+ bCheckDefList = true;
+ }
+ break;
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ eTabHoriOri = text::HoriOrientation::LEFT;
+ bCheckDefList = true;
+ [[fallthrough]];
+ default:
+ // In all other case it's possible to use directly an absolute
+ // or relative width.
+ if( nPercentWidth )
+ {
+ bRelWidths = true;
+ nWidth = nPercentWidth;
+ }
+ break;
+ }
+
+ // In ReqIF case, do not emulate indentation with fake description list
+ if( bCheckDefList && !rWrt.mbReqIF )
+ {
+ OSL_ENSURE( !rWrt.GetNumInfo().GetNumRule() ||
+ rWrt.GetNextNumInfo(),
+ "NumInfo for next paragraph is missing!" );
+ const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
+ if( aLRItem.GetLeft() > 0 && rWrt.m_nDefListMargin > 0 &&
+ ( !rWrt.GetNumInfo().GetNumRule() ||
+ ( rWrt.GetNextNumInfo() &&
+ (rWrt.GetNumInfo().GetNumRule() != rWrt.GetNextNumInfo()->GetNumRule() ||
+ rWrt.GetNextNumInfo()->IsRestart(rWrt.GetNumInfo())) ) ) )
+ {
+ // If the paragraph before the table is not numbered or the
+ // paragraph after the table starts with a new numbering or with
+ // a different rule, we can maintain the indentation with a DL.
+ // Otherwise we keep the indentation of the numbering.
+ nNewDefListLvl = static_cast< sal_uInt16 >(
+ (aLRItem.GetLeft() + (rWrt.m_nDefListMargin/2)) /
+ rWrt.m_nDefListMargin );
+ }
+ }
+
+ if( !pFlyFrameFormat && !rWrt.mbReqIF && nNewDefListLvl != rWrt.m_nDefListLvl )
+ rWrt.OutAndSetDefList( nNewDefListLvl );
+
+ // eFlyHoriOri and eTabHoriOri now only contain the values of
+ // LEFT/CENTER and RIGHT!
+ if( eFlyHoriOri!=text::HoriOrientation::NONE )
+ {
+ eTabHoriOri = eFlyHoriOri;
+ // MIB 4.7.97: If the table has a relative width, then the width is
+ // adjusted to the width of the frame, therefore we export its width.
+ // If fixed width, the table width is relevant. Whoever puts tables with
+ // relative width <100% into frames is to blame when the result looks bad.
+ if( bRelWidths )
+ {
+ nWidth = nFlyPercentWidth ? nFlyPercentWidth : nFlyWidth;
+ bRelWidths = nFlyPercentWidth > 0;
+ }
+ }
+
+ sal_Int16 eDivHoriOri = text::HoriOrientation::NONE;
+ switch( eTabHoriOri )
+ {
+ case text::HoriOrientation::LEFT:
+ // If a left-aligned table has no right sided flow, then we don't need
+ // an ALIGN=LEFT in the table.
+ if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_LEFT )
+ eTabHoriOri = text::HoriOrientation::NONE;
+ break;
+ case text::HoriOrientation::RIGHT:
+ // Something like that also applies to right-aligned tables,
+ // here we use a <DIV ALIGN=RIGHT> instead.
+ if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_RIGHT )
+ {
+ eDivHoriOri = text::HoriOrientation::RIGHT;
+ eTabHoriOri = text::HoriOrientation::NONE;
+ }
+ break;
+ case text::HoriOrientation::CENTER:
+ // Almost nobody understands ALIGN=CENTER, therefore we abstain
+ // from it and use a <CENTER>.
+ eDivHoriOri = text::HoriOrientation::CENTER;
+ eTabHoriOri = text::HoriOrientation::NONE;
+ break;
+ default:
+ ;
+ }
+ if( text::HoriOrientation::NONE==eTabHoriOri )
+ nFlyHSpace = nFlyVSpace = 0;
+
+ if( !pFormat->GetName().isEmpty() )
+ rWrt.OutImplicitMark( pFormat->GetName(), "table" );
+
+ if( text::HoriOrientation::NONE!=eDivHoriOri )
+ {
+ if (rWrt.IsLFPossible())
+ rWrt.OutNewLine(); // <CENTER> in new line
+ if( text::HoriOrientation::CENTER==eDivHoriOri )
+ {
+ if (!rWrt.mbXHTML)
+ {
+ // Not XHTML's css center: start <center>.
+ HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center) );
+ }
+ }
+ else
+ {
+ if (rWrt.mbReqIF)
+ {
+ // In ReqIF, div cannot have an 'align' attribute. For now, use 'style' only
+ // for ReqIF; maybe it makes sense to use it in both cases?
+ static constexpr char sOut[] = OOO_STRING_SVTOOLS_HTML_division
+ " style=\"display: flex; flex-direction: column; align-items: flex-end\"";
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut));
+ }
+ else
+ {
+ static constexpr char sOut[] = OOO_STRING_SVTOOLS_HTML_division
+ " " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"";
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut));
+ }
+ }
+ rWrt.IncIndentLevel(); // indent content of <CENTER>
+ rWrt.SetLFPossible(true);
+ }
+
+ // If the table isn't in a frame, then you always can output a LF.
+ if( text::HoriOrientation::NONE==eTabHoriOri )
+ rWrt.SetLFPossible(true);
+
+ const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout();
+
+#ifdef DBG_UTIL
+ {
+ SwViewShell *pSh = rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pSh && pSh->GetViewOptions()->IsTest1() )
+ pLayout = nullptr;
+ }
+#endif
+
+ if( pLayout && pLayout->IsExportable() )
+ {
+ SwHTMLWrtTable aTableWrt( pLayout );
+ aTableWrt.Write( rWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
+ pFormat, pCaption, bTopCaption,
+ nFlyHSpace, nFlyVSpace );
+ }
+ else
+ {
+ SwHTMLWrtTable aTableWrt( rTable.GetTabLines(), nWidth,
+ nBaseWidth, bRelWidths, 0, 0, rTable.GetRowsToRepeat() );
+ aTableWrt.Write( rWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
+ pFormat, pCaption, bTopCaption,
+ nFlyHSpace, nFlyVSpace );
+ }
+
+ // If the table wasn't in a frame, then you always can output a LF.
+ if( text::HoriOrientation::NONE==eTabHoriOri )
+ rWrt.SetLFPossible(true);
+
+ if( text::HoriOrientation::NONE!=eDivHoriOri )
+ {
+ rWrt.DecIndentLevel(); // indent content of <CENTER>
+ rWrt.OutNewLine(); // </CENTER> in new line
+ OString aTag = text::HoriOrientation::CENTER == eDivHoriOri
+ ? OOO_STRING_SVTOOLS_HTML_center
+ : OOO_STRING_SVTOOLS_HTML_division;
+ if (!rWrt.mbXHTML || eDivHoriOri != text::HoriOrientation::CENTER)
+ {
+ // Not XHTML's css center: end <center>.
+ HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
+ }
+ rWrt.SetLFPossible(true);
+ }
+
+ // move Pam behind the table
+ rWrt.m_pCurrentPam->GetPoint()->Assign( *rNode.EndOfSectionNode() );
+
+ if( bPreserveForm )
+ {
+ rWrt.m_bPreserveForm = false;
+ rWrt.OutForm( false );
+ }
+
+ rWrt.m_bOutTable = false;
+
+ if( rWrt.GetNextNumInfo() &&
+ rWrt.GetNextNumInfo()->GetNumRule() == rWrt.GetNumInfo().GetNumRule() &&
+ !rWrt.GetNextNumInfo()->IsRestart(rWrt.GetNumInfo()) )
+ {
+ // If the paragraph after the table is numbered with the same rule as the
+ // one before, then the NumInfo of the next paragraph holds the level of
+ // paragraph before the table. Therefore NumInfo must be fetched again
+ // to maybe close the Num list.
+ rWrt.ClearNextNumInfo();
+ rWrt.FillNextNumInfo();
+ OutHTML_NumberBulletListEnd( rWrt, *rWrt.GetNextNumInfo() );
+ }
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/parcss1.cxx b/sw/source/filter/html/parcss1.cxx
new file mode 100644
index 0000000000..f3145f1fa5
--- /dev/null
+++ b/sw/source/filter/html/parcss1.cxx
@@ -0,0 +1,1388 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/color.hxx>
+#include <tools/solar.h>
+#include <svtools/htmltokn.h>
+#include <comphelper/string.hxx>
+#include "parcss1.hxx"
+
+// Loop-Check: Used to avoid infinite loops, is checked after every
+// loop, if there is progress of the input position
+#define LOOP_CHECK
+
+#ifdef LOOP_CHECK
+
+#define LOOP_CHECK_DECL \
+ sal_Int32 nOldInPos = SAL_MAX_INT32;
+#define LOOP_CHECK_RESTART \
+ nOldInPos = SAL_MAX_INT32;
+#define LOOP_CHECK_CHECK( where ) \
+ OSL_ENSURE( nOldInPos!=m_nInPos || m_cNextCh==sal_Unicode(EOF), where ); \
+ if( nOldInPos==m_nInPos && m_cNextCh!=sal_Unicode(EOF) ) \
+ break; \
+ else \
+ nOldInPos = m_nInPos;
+
+#else
+
+#define LOOP_CHECK_DECL
+#define LOOP_CHECK_RESTART
+#define LOOP_CHECK_CHECK( where )
+
+#endif
+
+const sal_Int32 MAX_LEN = 1024;
+
+void CSS1Parser::InitRead( const OUString& rIn )
+{
+ m_nlLineNr = 0;
+ m_nlLinePos = 0;
+
+ m_bWhiteSpace = true; // if nothing was read it's like there was WS
+ m_bEOF = false;
+ m_eState = CSS1_PAR_WORKING;
+ m_nValue = 0.;
+
+ m_aIn = rIn;
+ m_nInPos = 0;
+ m_cNextCh = GetNextChar();
+ m_nToken = GetNextToken();
+}
+
+sal_Unicode CSS1Parser::GetNextChar()
+{
+ if( m_nInPos >= m_aIn.getLength() )
+ {
+ m_bEOF = true;
+ return sal_Unicode(EOF);
+ }
+
+ sal_Unicode c = m_aIn[m_nInPos];
+ m_nInPos++;
+
+ if( c == '\n' )
+ {
+ ++m_nlLineNr;
+ m_nlLinePos = 1;
+ }
+ else
+ ++m_nlLinePos;
+
+ return c;
+}
+
+// This function implements the scanner described in
+
+// http://www.w3.org/pub/WWW/TR/WD-css1.html
+// resp. http://www.w3.org/pub/WWW/TR/WD-css1-960220.html
+
+// for CSS1. It's a direct implementation of the
+// described Lex grammar.
+
+CSS1Token CSS1Parser::GetNextToken()
+{
+ CSS1Token nRet = CSS1_NULL;
+ m_aToken.clear();
+
+ do {
+ // remember if white space was read
+ bool bPrevWhiteSpace = m_bWhiteSpace;
+ m_bWhiteSpace = false;
+
+ bool bNextCh = true;
+ switch( m_cNextCh )
+ {
+ case '/': // COMMENT | '/'
+ {
+ m_cNextCh = GetNextChar();
+ if( '*' == m_cNextCh )
+ {
+ // COMMENT
+ m_cNextCh = GetNextChar();
+
+ bool bAsterisk = false;
+ while( !(bAsterisk && '/'==m_cNextCh) && !IsEOF() )
+ {
+ bAsterisk = ('*'==m_cNextCh);
+ m_cNextCh = GetNextChar();
+ }
+ }
+ else
+ {
+ // '/'
+ bNextCh = false;
+ nRet = CSS1_SLASH;
+ }
+ }
+ break;
+
+ case '@': // '@import' | '@XXX'
+ {
+ m_cNextCh = GetNextChar();
+ if (rtl::isAsciiAlpha(m_cNextCh))
+ {
+ // scan the next identifier
+ OUStringBuffer sTmpBuffer(32);
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( (rtl::isAsciiAlphanumeric(m_cNextCh) ||
+ '-' == m_cNextCh) && !IsEOF() );
+
+ m_aToken += sTmpBuffer;
+
+ // check if we know it
+ switch( m_aToken[0] )
+ {
+ case 'i':
+ case 'I':
+ if( m_aToken.equalsIgnoreAsciiCase( "import" ) )
+ nRet = CSS1_IMPORT_SYM;
+ break;
+ case 'p':
+ case 'P':
+ if( m_aToken.equalsIgnoreAsciiCase( "page" ) )
+ nRet = CSS1_PAGE_SYM;
+ break;
+ }
+
+ // error handling: ignore '@indent' and the rest until
+ // semicolon at end of the next block
+ if( CSS1_NULL==nRet )
+ {
+ m_aToken.clear();
+ int nBlockLvl = 0;
+ sal_Unicode cQuoteCh = 0;
+ bool bDone = false, bEscape = false;
+ while( !bDone && !IsEOF() )
+ {
+ bool bOldEscape = bEscape;
+ bEscape = false;
+ switch( m_cNextCh )
+ {
+ case '{':
+ if( !cQuoteCh && !bOldEscape )
+ nBlockLvl++;
+ break;
+ case ';':
+ if( !cQuoteCh && !bOldEscape )
+ bDone = nBlockLvl==0;
+ break;
+ case '}':
+ if( !cQuoteCh && !bOldEscape )
+ bDone = --nBlockLvl==0;
+ break;
+ case '\"':
+ case '\'':
+ if( !bOldEscape )
+ {
+ if( cQuoteCh )
+ {
+ if( cQuoteCh == m_cNextCh )
+ cQuoteCh = 0;
+ }
+ else
+ {
+ cQuoteCh = m_cNextCh;
+ }
+ }
+ break;
+ case '\\':
+ if( !bOldEscape )
+ bEscape = true;
+ break;
+ }
+ m_cNextCh = GetNextChar();
+ }
+ }
+
+ bNextCh = false;
+ }
+ }
+ break;
+
+ case '!': // '!' 'legal' | '!' 'important' | syntax error
+ {
+ // ignore white space
+ m_cNextCh = GetNextChar();
+ while( ( ' ' == m_cNextCh ||
+ (m_cNextCh >= 0x09 && m_cNextCh <= 0x0d) ) && !IsEOF() )
+ {
+ m_bWhiteSpace = true;
+ m_cNextCh = GetNextChar();
+ }
+
+ if( 'i'==m_cNextCh || 'I'==m_cNextCh)
+ {
+ // scan next identifier
+ OUStringBuffer sTmpBuffer(32);
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( (rtl::isAsciiAlphanumeric(m_cNextCh) ||
+ '-' == m_cNextCh) && !IsEOF() );
+
+ m_aToken += sTmpBuffer;
+
+ if( ( 'i'==m_aToken[0] || 'I'==m_aToken[0] ) &&
+ m_aToken.equalsIgnoreAsciiCase( "important" ) )
+ {
+ // '!' 'important'
+ nRet = CSS1_IMPORTANT_SYM;
+ }
+ else
+ {
+ // error handling: ignore '!', not IDENT
+ nRet = CSS1_IDENT;
+ }
+
+ m_bWhiteSpace = false;
+ bNextCh = false;
+ }
+ else
+ {
+ // error handling: ignore '!'
+ bNextCh = false;
+ }
+ }
+ break;
+
+ case '\"':
+ case '\'': // STRING
+ {
+ // \... isn't possible yet!!!
+ sal_Unicode cQuoteChar = m_cNextCh;
+ m_cNextCh = GetNextChar();
+
+ OUStringBuffer sTmpBuffer( MAX_LEN );
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( cQuoteChar != m_cNextCh && !IsEOF() );
+
+ m_aToken += sTmpBuffer;
+
+ nRet = CSS1_STRING;
+ }
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': // NUMBER | PERCENTAGE | LENGTH
+ {
+ // save current position
+ std::size_t nInPosSave = m_nInPos;
+ sal_Unicode cNextChSave = m_cNextCh;
+ sal_uInt32 nlLineNrSave = m_nlLineNr;
+ sal_uInt32 nlLinePosSave = m_nlLinePos;
+ bool bEOFSave = m_bEOF;
+
+ // first try to parse a hex digit
+ OUStringBuffer sTmpBuffer( 16 );
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( sTmpBuffer.getLength() < 7 &&
+ ( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
+ ('A'<=m_cNextCh && 'F'>=m_cNextCh) ||
+ ('a'<=m_cNextCh && 'f'>=m_cNextCh) ) &&
+ !IsEOF() );
+
+ if( sTmpBuffer.getLength()==6 )
+ {
+ // we found a color in hex
+ m_aToken += sTmpBuffer;
+ nRet = CSS1_HEXCOLOR;
+ bNextCh = false;
+
+ break;
+ }
+
+ // otherwise we try a number
+ m_nInPos = nInPosSave;
+ m_cNextCh = cNextChSave;
+ m_nlLineNr = nlLineNrSave;
+ m_nlLinePos = nlLinePosSave;
+ m_bEOF = bEOFSave;
+
+ // first parse the number
+ sTmpBuffer.setLength( 0 );
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( (('0'<=m_cNextCh && '9'>=m_cNextCh) || '.'==m_cNextCh) &&
+ !IsEOF() );
+
+ m_aToken += sTmpBuffer;
+ m_nValue = m_aToken.toDouble();
+
+ // ignore white space
+ while( ( ' ' == m_cNextCh ||
+ (m_cNextCh >= 0x09 && m_cNextCh <= 0x0d) ) && !IsEOF() )
+ {
+ m_bWhiteSpace = true;
+ m_cNextCh = GetNextChar();
+ }
+
+ // check now, of there is a unit
+ switch( m_cNextCh )
+ {
+ case '%': // PERCENTAGE
+ m_bWhiteSpace = false;
+ nRet = CSS1_PERCENTAGE;
+ break;
+
+ case 'c':
+ case 'C': // LENGTH cm | LENGTH IDENT
+ case 'e':
+ case 'E': // LENGTH (em | ex) | LENGTH IDENT
+ case 'i':
+ case 'I': // LENGTH inch | LENGTH IDENT
+ case 'p':
+ case 'P': // LENGTH (pt | px | pc) | LENGTH IDENT
+ case 'm':
+ case 'M': // LENGTH mm | LENGTH IDENT
+ {
+ // save current position
+ sal_Int32 nInPosOld = m_nInPos;
+ sal_Unicode cNextChOld = m_cNextCh;
+ sal_uInt32 nlLineNrOld = m_nlLineNr;
+ sal_uInt32 nlLinePosOld = m_nlLinePos;
+ bool bEOFOld = m_bEOF;
+
+ // parse the next identifier
+ OUString aIdent;
+ OUStringBuffer sTmpBuffer2(64);
+ do {
+ sTmpBuffer2.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( (rtl::isAsciiAlphanumeric(m_cNextCh) ||
+ '-' == m_cNextCh) && !IsEOF() );
+
+ aIdent += sTmpBuffer2;
+
+ // Is it a unit?
+ const char *pCmp1 = nullptr, *pCmp2 = nullptr, *pCmp3 = nullptr;
+ double nScale1 = 1., nScale2 = 1.;
+ CSS1Token nToken1 = CSS1_LENGTH,
+ nToken2 = CSS1_LENGTH,
+ nToken3 = CSS1_LENGTH;
+ switch( aIdent[0] )
+ {
+ case 'c':
+ case 'C':
+ pCmp1 = "cm";
+ nScale1 = (72.*20.)/2.54; // twip
+ break;
+ case 'e':
+ case 'E':
+ pCmp1 = "em";
+ nToken1 = CSS1_EMS;
+
+ pCmp2 = "ex";
+ nToken2 = CSS1_EMX;
+ break;
+ case 'i':
+ case 'I':
+ pCmp1 = "in";
+ nScale1 = 72.*20.; // twip
+ break;
+ case 'm':
+ case 'M':
+ pCmp1 = "mm";
+ nScale1 = (72.*20.)/25.4; // twip
+ break;
+ case 'p':
+ case 'P':
+ pCmp1 = "pt";
+ nScale1 = 20.; // twip
+
+ pCmp2 = "pc";
+ nScale2 = 12.*20.; // twip
+
+ pCmp3 = "px";
+ nToken3 = CSS1_PIXLENGTH;
+ break;
+ }
+
+ double nScale = 0.0;
+ OSL_ENSURE( pCmp1, "Where does the first digit come from?" );
+ if( aIdent.equalsIgnoreAsciiCaseAscii( pCmp1 ) )
+ {
+ nScale = nScale1;
+ nRet = nToken1;
+ }
+ else if( pCmp2 &&
+ aIdent.equalsIgnoreAsciiCaseAscii( pCmp2 ) )
+ {
+ nScale = nScale2;
+ nRet = nToken2;
+ }
+ else if( pCmp3 &&
+ aIdent.equalsIgnoreAsciiCaseAscii( pCmp3 ) )
+ {
+ nScale = 1.; // nScale3
+ nRet = nToken3;
+ }
+ else
+ {
+ nRet = CSS1_NUMBER;
+ }
+
+ if( CSS1_LENGTH==nRet && nScale!=1.0 )
+ m_nValue *= nScale;
+
+ if( nRet == CSS1_NUMBER )
+ {
+ m_nInPos = nInPosOld;
+ m_cNextCh = cNextChOld;
+ m_nlLineNr = nlLineNrOld;
+ m_nlLinePos = nlLinePosOld;
+ m_bEOF = bEOFOld;
+ }
+ else
+ {
+ m_bWhiteSpace = false;
+ }
+ bNextCh = false;
+ }
+ break;
+ default: // NUMBER IDENT
+ bNextCh = false;
+ nRet = CSS1_NUMBER;
+ break;
+ }
+ }
+ break;
+
+ case ':': // ':'
+ // catch link/visited/active !!!
+ nRet = CSS1_COLON;
+ break;
+
+ case '.': // DOT_W_WS | DOT_WO_WS
+ nRet = bPrevWhiteSpace ? CSS1_DOT_W_WS : CSS1_DOT_WO_WS;
+ break;
+
+ case '+': // '+'
+ nRet = CSS1_PLUS;
+ break;
+
+ case '-': // '-'
+ nRet = CSS1_MINUS;
+ break;
+
+ case '{': // '{'
+ nRet = CSS1_OBRACE;
+ break;
+
+ case '}': // '}'
+ nRet = CSS1_CBRACE;
+ break;
+
+ case ';': // ';'
+ nRet = CSS1_SEMICOLON;
+ break;
+
+ case ',': // ','
+ nRet = CSS1_COMMA;
+ break;
+
+ case '#': // '#'
+ m_cNextCh = GetNextChar();
+ if( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
+ ('a'<=m_cNextCh && 'f'>=m_cNextCh) ||
+ ('A'<=m_cNextCh && 'F'>=m_cNextCh) )
+ {
+ // save current position
+ sal_Int32 nInPosSave = m_nInPos;
+ sal_Unicode cNextChSave = m_cNextCh;
+ sal_uInt32 nlLineNrSave = m_nlLineNr;
+ sal_uInt32 nlLinePosSave = m_nlLinePos;
+ bool bEOFSave = m_bEOF;
+
+ // first try to parse a hex digit
+ OUStringBuffer sTmpBuffer(8);
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ m_cNextCh = GetNextChar();
+ } while( sTmpBuffer.getLength() < 9 &&
+ ( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
+ ('A'<=m_cNextCh && 'F'>=m_cNextCh) ||
+ ('a'<=m_cNextCh && 'f'>=m_cNextCh) ) &&
+ !IsEOF() );
+
+ if( sTmpBuffer.getLength()==6 || sTmpBuffer.getLength()==3 )
+ {
+ // we found a color in hex (RGB)
+ m_aToken += sTmpBuffer;
+ nRet = CSS1_HEXCOLOR;
+ bNextCh = false;
+
+ break;
+ }
+
+ if( sTmpBuffer.getLength()==8 )
+ {
+ // we found a color in hex (RGBA)
+ // we convert it to RGB assuming white background
+ sal_uInt32 nColor = sTmpBuffer.makeStringAndClear().toUInt32(16);
+ sal_uInt32 nRed = (nColor & 0xff000000) >> 24;
+ sal_uInt32 nGreen = (nColor & 0xff0000) >> 16;
+ sal_uInt32 nBlue = (nColor & 0xff00) >> 8;
+ double nAlpha = (nColor & 0xff) / 255.0;
+ nRed = (1 - nAlpha) * 255 + nAlpha * nRed;
+ nGreen = (1 - nAlpha) * 255 + nAlpha * nGreen;
+ nBlue = (1 - nAlpha) * 255 + nAlpha * nBlue;
+ nColor = (nRed << 16) + (nGreen << 8) + nBlue;
+ m_aToken += OUString::number(nColor, 16);
+ nRet = CSS1_HEXCOLOR;
+ bNextCh = false;
+
+ break;
+ }
+
+ // otherwise we try a number
+ m_nInPos = nInPosSave;
+ m_cNextCh = cNextChSave;
+ m_nlLineNr = nlLineNrSave;
+ m_nlLinePos = nlLinePosSave;
+ m_bEOF = bEOFSave;
+ }
+
+ nRet = CSS1_HASH;
+ bNextCh = false;
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n': // White-Space
+ m_bWhiteSpace = true;
+ break;
+
+ case sal_Unicode(EOF):
+ if( IsEOF() )
+ {
+ m_eState = CSS1_PAR_ACCEPTED;
+ bNextCh = false;
+ break;
+ }
+ [[fallthrough]];
+
+ default: // IDENT | syntax error
+ if (rtl::isAsciiAlpha(m_cNextCh))
+ {
+ // IDENT
+
+ bool bHexColor = true;
+
+ // parse the next identifier
+ OUStringBuffer sTmpBuffer(64);
+ do {
+ sTmpBuffer.append( m_cNextCh );
+ if( bHexColor )
+ {
+ bHexColor = sTmpBuffer.getLength()<7 &&
+ ( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
+ ('A'<=m_cNextCh && 'F'>=m_cNextCh) ||
+ ('a'<=m_cNextCh && 'f'>=m_cNextCh) );
+ }
+ m_cNextCh = GetNextChar();
+ } while( (rtl::isAsciiAlphanumeric(m_cNextCh) ||
+ '-' == m_cNextCh) && !IsEOF() );
+
+ m_aToken += sTmpBuffer;
+
+ if( bHexColor && sTmpBuffer.getLength()==6 )
+ {
+ bNextCh = false;
+ nRet = CSS1_HEXCOLOR;
+
+ break;
+ }
+ if( '('==m_cNextCh &&
+ ( (('u'==m_aToken[0] || 'U'==m_aToken[0]) &&
+ m_aToken.equalsIgnoreAsciiCase( "url" )) ||
+ (('r'==m_aToken[0] || 'R'==m_aToken[0]) &&
+ (m_aToken.equalsIgnoreAsciiCase( "rgb" ) || m_aToken.equalsIgnoreAsciiCase( "rgba" ) )
+ ) ) )
+ {
+ int nNestCnt = 0;
+ OUStringBuffer sTmpBuffer2(64);
+ do {
+ sTmpBuffer2.append( m_cNextCh );
+ switch( m_cNextCh )
+ {
+ case '(': nNestCnt++; break;
+ case ')': nNestCnt--; break;
+ }
+ m_cNextCh = GetNextChar();
+ } while( (nNestCnt>1 || ')'!=m_cNextCh) && !IsEOF() );
+ sTmpBuffer2.append( m_cNextCh );
+ m_aToken += sTmpBuffer2;
+ bNextCh = true;
+ nRet = 'u'==m_aToken[0] || 'U'==m_aToken[0]
+ ? CSS1_URL
+ : CSS1_RGB;
+ }
+ else
+ {
+ bNextCh = false;
+ nRet = CSS1_IDENT;
+ }
+ }
+ // error handling: ignore digit
+ break;
+ }
+ if( bNextCh )
+ m_cNextCh = GetNextChar();
+
+ } while( CSS1_NULL==nRet && IsParserWorking() );
+
+ return nRet;
+}
+
+// These functions implement the parser described in
+
+// http://www.w3.org/pub/WWW/TR/WD-css1.html
+// resp. http://www.w3.org/pub/WWW/TR/WD-css1-960220.html
+
+// for CSS1. It's a direct implementation of the
+// described Lex grammar.
+
+// stylesheet
+// : import* rule*
+
+// import
+// : IMPORT_SYM url
+
+// url
+// : STRING
+
+void CSS1Parser::ParseStyleSheet()
+{
+ LOOP_CHECK_DECL
+
+ // import*
+ bool bDone = false;
+ while( !bDone && IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseStyleSheet()/import *" )
+
+ switch( m_nToken )
+ {
+ case CSS1_IMPORT_SYM:
+ // IMPORT_SYM url
+ // URL are skipped without checks
+ m_nToken = GetNextToken();
+ break;
+ case CSS1_IDENT: // Look-Aheads
+ case CSS1_DOT_W_WS:
+ case CSS1_HASH:
+ case CSS1_PAGE_SYM:
+ // rule
+ bDone = true;
+ break;
+ default:
+ // error handling: ignore
+ break;
+ }
+
+ if( !bDone )
+ m_nToken = GetNextToken();
+ }
+
+ LOOP_CHECK_RESTART
+
+ // rule *
+ while( IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseStyleSheet()/rule *" )
+
+ switch( m_nToken )
+ {
+ case CSS1_IDENT: // Look-Aheads
+ case CSS1_DOT_W_WS:
+ case CSS1_HASH:
+ case CSS1_PAGE_SYM:
+ // rule
+ ParseRule();
+ break;
+ default:
+ // error handling: ignore
+ m_nToken = GetNextToken();
+ break;
+ }
+ }
+}
+
+// rule
+// : selector [ ',' selector ]*
+// '{' declaration [ ';' declaration ]* '}'
+
+void CSS1Parser::ParseRule()
+{
+ // selector
+ std::unique_ptr<CSS1Selector> pSelector = ParseSelector();
+ if( !pSelector )
+ return;
+
+ // process selector
+ SelectorParsed( std::move(pSelector), true );
+
+ LOOP_CHECK_DECL
+
+ // [ ',' selector ]*
+ while( CSS1_COMMA==m_nToken && IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseRule()/selector *" )
+
+ // ignore ','
+ m_nToken = GetNextToken();
+
+ // selector
+ pSelector = ParseSelector();
+ if( !pSelector )
+ return;
+
+ // process selector
+ SelectorParsed( std::move(pSelector), false );
+ }
+
+ // '{'
+ if( CSS1_OBRACE != m_nToken )
+ return;
+ m_nToken = GetNextToken();
+
+ // declaration
+ OUString aProperty;
+ std::unique_ptr<CSS1Expression> pExpr = ParseDeclaration( aProperty );
+ if( !pExpr )
+ return;
+
+ // process expression
+ DeclarationParsed( aProperty, std::move(pExpr) );
+
+ LOOP_CHECK_RESTART
+
+ // [ ';' declaration ]*
+ while( CSS1_SEMICOLON==m_nToken && IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseRule()/declaration *" )
+
+ // ';'
+ m_nToken = GetNextToken();
+
+ // declaration
+ if( CSS1_IDENT == m_nToken )
+ {
+ std::unique_ptr<CSS1Expression> pExp = ParseDeclaration( aProperty );
+ if( pExp )
+ {
+ // process expression
+ DeclarationParsed( aProperty, std::move(pExp));
+ }
+ }
+ }
+
+ // '}'
+ if( CSS1_CBRACE == m_nToken )
+ m_nToken = GetNextToken();
+}
+
+// selector
+// : simple_selector+ [ ':' pseudo_element ]?
+
+// simple_selector
+// : element_name [ DOT_WO_WS class ]?
+// | DOT_W_WS class
+// | id_selector
+
+// element_name
+// : IDENT
+
+// class
+// : IDENT
+
+// id_selector
+// : '#' IDENT
+
+// pseudo_element
+// : IDENT
+
+std::unique_ptr<CSS1Selector> CSS1Parser::ParseSelector()
+{
+ std::unique_ptr<CSS1Selector> pRoot;
+ CSS1Selector *pLast = nullptr;
+
+ bool bDone = false;
+ CSS1Selector *pNew = nullptr;
+
+ LOOP_CHECK_DECL
+
+ // simple_selector+
+ while( !bDone && IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseSelector()" )
+
+ bool bNextToken = true;
+
+ switch( m_nToken )
+ {
+ case CSS1_IDENT:
+ {
+ // element_name [ DOT_WO_WS class ]?
+
+ // element_name
+ OUString aElement = m_aToken;
+ CSS1SelectorType eType = CSS1_SELTYPE_ELEMENT;
+ m_nToken = GetNextToken();
+
+ if( CSS1_DOT_WO_WS == m_nToken )
+ {
+ // DOT_WO_WS
+ m_nToken = GetNextToken();
+
+ // class
+ if( CSS1_IDENT == m_nToken )
+ {
+ aElement += "." + m_aToken;
+ eType = CSS1_SELTYPE_ELEM_CLASS;
+ }
+ else
+ {
+ // missing class
+ return pRoot;
+ }
+ }
+ else
+ {
+ // that was a look-ahead
+ bNextToken = false;
+ }
+ pNew = new CSS1Selector( eType, aElement );
+ }
+ break;
+ case CSS1_DOT_W_WS:
+ // DOT_W_WS class
+
+ // DOT_W_WS
+ m_nToken = GetNextToken();
+
+ if( CSS1_IDENT==m_nToken )
+ {
+ // class
+ pNew = new CSS1Selector( CSS1_SELTYPE_CLASS, m_aToken );
+ }
+ else
+ {
+ // missing class
+ return pRoot;
+ }
+ break;
+ case CSS1_HASH:
+ // '#' id_selector
+
+ // '#'
+ m_nToken = GetNextToken();
+
+ if( CSS1_IDENT==m_nToken )
+ {
+ // id_selector
+ pNew = new CSS1Selector( CSS1_SELTYPE_ID, m_aToken );
+ }
+ else
+ {
+ // missing id_selector
+ return pRoot;
+ }
+ break;
+
+ case CSS1_PAGE_SYM:
+ {
+ // @page
+ pNew = new CSS1Selector( CSS1_SELTYPE_PAGE, m_aToken );
+ }
+ break;
+
+ default:
+ // stop because we don't know what's next
+ bDone = true;
+ break;
+ }
+
+ // if created a new selector then save it
+ if( pNew )
+ {
+ OSL_ENSURE( (pRoot!=nullptr) == (pLast!=nullptr),
+ "Root-Selector, but no Last" );
+ if( pLast )
+ pLast->SetNext( pNew );
+ else
+ pRoot.reset(pNew);
+
+ pLast = pNew;
+ pNew = nullptr;
+ }
+
+ if( bNextToken && !bDone )
+ m_nToken = GetNextToken();
+ }
+
+ if( !pRoot )
+ {
+ // missing simple_selector
+ return pRoot;
+ }
+
+ // [ ':' pseudo_element ]?
+ if( CSS1_COLON==m_nToken && IsParserWorking() )
+ {
+ // ':' pseudo element
+ m_nToken = GetNextToken();
+ if( CSS1_IDENT==m_nToken )
+ {
+ if (pLast)
+ pLast->SetNext( new CSS1Selector(CSS1_SELTYPE_PSEUDO,m_aToken) );
+ m_nToken = GetNextToken();
+ }
+ else
+ {
+ // missing pseudo_element
+ return pRoot;
+ }
+ }
+
+ return pRoot;
+}
+
+// declaration
+// : property ':' expr prio?
+// | /* empty */
+
+// expression
+// : term [ operator term ]*
+
+// term
+// : unary_operator?
+// [ NUMBER | STRING | PERCENTAGE | LENGTH | EMS | EXS | IDENT |
+// HEXCOLOR | URL | RGB ]
+
+// operator
+// : '/' | ',' | /* empty */
+
+// unary_operator
+// : '-' | '+'
+
+// property
+// : ident
+
+// the sign is only used for numeric values (except PERCENTAGE)
+// and it's applied on nValue!
+std::unique_ptr<CSS1Expression> CSS1Parser::ParseDeclaration( OUString& rProperty )
+{
+ std::unique_ptr<CSS1Expression> pRoot;
+ CSS1Expression *pLast = nullptr;
+
+ // property
+ if( CSS1_IDENT != m_nToken )
+ {
+ // missing property
+ return pRoot;
+ }
+ rProperty = m_aToken;
+
+ m_nToken = GetNextToken();
+
+ // ':'
+ if( CSS1_COLON != m_nToken )
+ {
+ // missing ':'
+ return pRoot;
+ }
+ m_nToken = GetNextToken();
+
+ // term [operator term]*
+ // here we're pretty lax regarding the syntax, but this shouldn't
+ // be a problem
+ bool bDone = false;
+ sal_Unicode cSign = 0, cOp = 0;
+ CSS1Expression *pNew = nullptr;
+
+ LOOP_CHECK_DECL
+
+ while( !bDone && IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseDeclaration()" )
+
+ switch( m_nToken )
+ {
+ case CSS1_MINUS:
+ cSign = '-';
+ break;
+
+ case CSS1_PLUS:
+ cSign = '+';
+ break;
+
+ case CSS1_NUMBER:
+ case CSS1_LENGTH:
+ case CSS1_PIXLENGTH:
+ case CSS1_EMS:
+ case CSS1_EMX:
+ if( '-'==cSign )
+ m_nValue = -m_nValue;
+ [[fallthrough]];
+ case CSS1_STRING:
+ case CSS1_PERCENTAGE:
+ case CSS1_IDENT:
+ case CSS1_URL:
+ case CSS1_RGB:
+ case CSS1_HEXCOLOR:
+ pNew = new CSS1Expression( m_nToken, m_aToken, m_nValue, cOp );
+ m_nValue = 0; // otherwise this also is applied to next ident
+ cSign = 0;
+ cOp = 0;
+ break;
+
+ case CSS1_SLASH:
+ cOp = '/';
+ cSign = 0;
+ break;
+
+ case CSS1_COMMA:
+ cOp = ',';
+ cSign = 0;
+ break;
+
+ default:
+ bDone = true;
+ break;
+ }
+
+ // if created a new expression save it
+ if( pNew )
+ {
+ OSL_ENSURE( (pRoot!=nullptr) == (pLast!=nullptr),
+ "Root-Selector, but no Last" );
+ if( pLast )
+ pLast->SetNext( pNew );
+ else
+ pRoot.reset(pNew);
+
+ pLast = pNew;
+ pNew = nullptr;
+ }
+
+ if( !bDone )
+ m_nToken = GetNextToken();
+ }
+
+ if( !pRoot )
+ {
+ // missing term
+ return pRoot;
+ }
+
+ // prio?
+ if( CSS1_IMPORTANT_SYM==m_nToken )
+ {
+ // IMPORTANT_SYM
+ m_nToken = GetNextToken();
+ }
+
+ return pRoot;
+}
+
+CSS1Parser::CSS1Parser()
+ : m_bWhiteSpace(false)
+ , m_bEOF(false)
+ , m_cNextCh(0)
+ , m_nInPos(0)
+ , m_nlLineNr(0)
+ , m_nlLinePos(0)
+ , m_nValue(0)
+ , m_eState(CSS1_PAR_ACCEPTED)
+ , m_nToken(CSS1_NULL)
+{
+}
+
+CSS1Parser::~CSS1Parser()
+{
+}
+
+void CSS1Parser::ParseStyleSheet( const OUString& rIn )
+{
+ OUString aTmp( rIn );
+
+ sal_Unicode c;
+ while( !aTmp.isEmpty() &&
+ ( ' '==(c=aTmp[0]) || '\t'==c || '\r'==c || '\n'==c ) )
+ aTmp = aTmp.copy( 1 );
+
+ while( !aTmp.isEmpty() && ( ' '==(c=aTmp[aTmp.getLength()-1])
+ || '\t'==c || '\r'==c || '\n'==c ) )
+ aTmp = aTmp.copy( 0, aTmp.getLength()-1 );
+
+ // remove SGML comments
+ if( aTmp.getLength() >= 4 &&
+ aTmp.startsWith( "<!--" ) )
+ aTmp = aTmp.copy( 4 );
+
+ if( aTmp.getLength() >=3 &&
+ aTmp.endsWith("-->") )
+ aTmp = aTmp.copy( 0, aTmp.getLength() - 3 );
+
+ if( aTmp.isEmpty() )
+ return;
+
+ InitRead( aTmp );
+
+ ParseStyleSheet();
+}
+
+void CSS1Parser::ParseStyleOption( const OUString& rIn )
+{
+ if( rIn.isEmpty() )
+ return;
+
+ InitRead( rIn );
+
+ // fdo#41796: skip over spurious semicolons
+ while (CSS1_SEMICOLON == m_nToken)
+ {
+ m_nToken = GetNextToken();
+ }
+
+ OUString aProperty;
+ std::unique_ptr<CSS1Expression> pExpr = ParseDeclaration( aProperty );
+ if( !pExpr )
+ return;
+
+ // process expression
+ DeclarationParsed( aProperty, std::move(pExpr) );
+
+ LOOP_CHECK_DECL
+
+ // [ ';' declaration ]*
+ while( CSS1_SEMICOLON==m_nToken && IsParserWorking() )
+ {
+ LOOP_CHECK_CHECK( "Infinite loop in ParseStyleOption()" )
+
+ m_nToken = GetNextToken();
+ if( CSS1_IDENT==m_nToken )
+ {
+ std::unique_ptr<CSS1Expression> pExp = ParseDeclaration( aProperty );
+ if( pExp )
+ {
+ // process expression
+ DeclarationParsed( aProperty, std::move(pExp) );
+ }
+ }
+ }
+}
+
+void CSS1Parser::SelectorParsed( std::unique_ptr<CSS1Selector> /* pSelector */, bool /*bFirst*/ )
+{
+}
+
+void CSS1Parser::DeclarationParsed( const OUString& /*rProperty*/,
+ std::unique_ptr<CSS1Expression> /* pExpr */ )
+{
+}
+
+CSS1Selector::~CSS1Selector()
+{
+ delete m_pNext;
+}
+
+CSS1Expression::~CSS1Expression()
+{
+ delete pNext;
+}
+
+void CSS1Expression::GetURL( OUString& rURL ) const
+{
+ OSL_ENSURE( CSS1_URL==eType, "CSS1-Expression is not URL" );
+
+ OSL_ENSURE( aValue.startsWithIgnoreAsciiCase( "url" ) &&
+ aValue.getLength() > 5 &&
+ '(' == aValue[3] &&
+ ')' == aValue[aValue.getLength()-1],
+ "no valid URL(...)" );
+
+ if( aValue.getLength() <= 5 )
+ return;
+
+ rURL = aValue.copy( 4, aValue.getLength() - 5 );
+
+ // tdf#94088 original stripped only spaces, but there may also be
+ // double quotes in CSS style URLs, so be prepared to spaces followed
+ // by a single quote followed by spaces
+ const sal_Unicode aSpace(' ');
+ const sal_Unicode aSingleQuote('\'');
+
+ rURL = comphelper::string::strip(rURL, aSpace);
+ rURL = comphelper::string::strip(rURL, aSingleQuote);
+ rURL = comphelper::string::strip(rURL, aSpace);
+}
+
+bool CSS1Expression::GetColor( Color &rColor ) const
+{
+ OSL_ENSURE( CSS1_IDENT==eType || CSS1_RGB==eType ||
+ CSS1_HEXCOLOR==eType || CSS1_STRING==eType,
+ "CSS1-Expression cannot be colour" );
+
+ bool bRet = false;
+ sal_uInt32 nColor = SAL_MAX_UINT32;
+
+ switch( eType )
+ {
+ case CSS1_RGB:
+ {
+ // fourth value to 255 means no alpha transparency
+ // so the right by default value
+ sal_uInt8 aColors[4] = { 0, 0, 0, 255 };
+
+ // it can be "rgb" or "rgba"
+ if (!aValue.startsWithIgnoreAsciiCase( "rgb" ) || aValue.getLength() < 6 ||
+ (aValue[3] != '(' && aValue[4] != '(' ) || aValue[aValue.getLength()-1] != ')')
+ {
+ break;
+ }
+
+ sal_Int32 nPos = aValue.startsWithIgnoreAsciiCase( "rgba" )?5:4; // start after "rgba(" or "rgb("
+ char cSep = (aValue.indexOf(',') != -1)?',':' ';
+ // alpha value can be after a "/" or ","
+ bool bIsSepAlphaDiv = (aValue.indexOf('/') != -1)?true:false;
+ for ( int nCol = 0; nCol < 4 && nPos > 0; ++nCol )
+ {
+ const std::u16string_view aNumber = o3tl::getToken(aValue, 0, cSep, nPos);
+
+ sal_Int32 nNumber = o3tl::toInt32(aNumber);
+ if( nNumber<0 )
+ {
+ nNumber = 0;
+ }
+ else if( aNumber.find('%') != std::u16string_view::npos )
+ {
+ if( nNumber > 100 )
+ nNumber = 100;
+ nNumber *= 255;
+ nNumber /= 100;
+ }
+ else if( nNumber > 255 )
+ nNumber = 255;
+ else if( aNumber.find('.') != std::u16string_view::npos )
+ {
+ // in this case aNumber contains something like "0.3" so not an sal_Int32
+ nNumber = static_cast<sal_Int32>(255.0*o3tl::toDouble(aNumber));
+ }
+ aColors[nCol] = static_cast<sal_uInt8>(nNumber);
+ // rgb with alpha and '/' has this form: rgb(255 0 0 / 50%)
+ if (bIsSepAlphaDiv && nCol == 2)
+ {
+ // but there can be some spaces or not before and after the "/", so skip them
+ while (aValue[nPos] == '/' || aValue[nPos] == ' ')
+ ++nPos;
+ }
+ }
+
+ rColor.SetRed( aColors[0] );
+ rColor.SetGreen( aColors[1] );
+ rColor.SetBlue( aColors[2] );
+ rColor.SetAlpha( aColors[3] );
+
+ bRet = true; // something different than a colour isn't possible
+ }
+ break;
+
+ case CSS1_IDENT:
+ case CSS1_STRING:
+ {
+ OUString aTmp( aValue.toAsciiUpperCase() );
+ nColor = GetHTMLColor( aTmp );
+ bRet = nColor != SAL_MAX_UINT32;
+ }
+ if( bRet || CSS1_STRING != eType || aValue.isEmpty() ||
+ aValue[0] != '#' )
+ break;
+ [[fallthrough]];
+ case CSS1_HEXCOLOR:
+ {
+ // MS-IE hack: colour can also be a string
+ sal_Int32 nOffset = CSS1_STRING==eType ? 1 : 0;
+ bool bDouble = aValue.getLength()-nOffset == 3;
+ sal_Int32 i = nOffset, nEnd = (bDouble ? 3 : 6) + nOffset;
+
+ nColor = 0;
+ for( ; i<nEnd; i++ )
+ {
+ sal_Unicode c = (i<aValue.getLength() ? aValue[i]
+ : '0' );
+ if( c >= '0' && c <= '9' )
+ c -= 48;
+ else if( c >= 'A' && c <= 'F' )
+ c -= 55;
+ else if( c >= 'a' && c <= 'f' )
+ c -= 87;
+ else
+ c = 16;
+
+ nColor *= 16;
+ if( c<16 )
+ nColor += c;
+ if( bDouble )
+ {
+ nColor *= 16;
+ if( c<16 )
+ nColor += c;
+ }
+ }
+ bRet = true;
+ }
+ break;
+ default:
+ ;
+ }
+
+ if( bRet && nColor!=SAL_MAX_UINT32 )
+ {
+ rColor.SetRed( static_cast<sal_uInt8>((nColor & 0x00ff0000UL) >> 16) );
+ rColor.SetGreen( static_cast<sal_uInt8>((nColor & 0x0000ff00UL) >> 8) );
+ rColor.SetBlue( static_cast<sal_uInt8>(nColor & 0x000000ffUL) );
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/parcss1.hxx b/sw/source/filter/html/parcss1.hxx
new file mode 100644
index 0000000000..99f46af8e8
--- /dev/null
+++ b/sw/source/filter/html/parcss1.hxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_PARCSS1_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_PARCSS1_HXX
+
+#include <rtl/ustring.hxx>
+#include <tools/color.hxx>
+
+#include <memory>
+#include <utility>
+
+// tokens of the CSS1 parser
+enum CSS1Token
+{
+ CSS1_NULL,
+
+ CSS1_IDENT,
+ CSS1_STRING,
+ CSS1_NUMBER,
+ CSS1_PERCENTAGE,
+ CSS1_LENGTH, // absolute length in 1/100 MM
+ CSS1_PIXLENGTH, // length in pixels
+ CSS1_EMS,
+ CSS1_EMX,
+ CSS1_HEXCOLOR,
+
+ CSS1_DOT_W_WS,
+ CSS1_DOT_WO_WS,
+ CSS1_COLON,
+ CSS1_SLASH,
+ CSS1_PLUS,
+ CSS1_MINUS,
+ CSS1_OBRACE,
+ CSS1_CBRACE,
+ CSS1_SEMICOLON,
+ CSS1_COMMA,
+ CSS1_HASH,
+
+ CSS1_IMPORT_SYM,
+ CSS1_PAGE_SYM, // Feature: PrintExt
+
+ CSS1_IMPORTANT_SYM,
+
+ CSS1_URL,
+ CSS1_RGB
+};
+
+enum CSS1ParserState
+{
+ CSS1_PAR_ACCEPTED = 0,
+ CSS1_PAR_WORKING
+};
+
+enum CSS1SelectorType
+{
+ CSS1_SELTYPE_ELEMENT,
+ CSS1_SELTYPE_ELEM_CLASS,
+ CSS1_SELTYPE_CLASS,
+ CSS1_SELTYPE_ID,
+ CSS1_SELTYPE_PSEUDO,
+ CSS1_SELTYPE_PAGE // Feature: PrintExt
+};
+
+/** A simple selector
+ *
+ * This class represents a simple selector, e.g.
+ * - a HTML element name
+ * - a HTML element name with a class (separated by a dot)
+ * - a class (without a dot)
+ * - an ID (set with ID=xxx)
+ * - a pseudo element
+ *
+ * These simple selectors are chained in a list to complete selectors
+ */
+class CSS1Selector
+{
+ CSS1SelectorType m_eType; // the type
+ OUString m_aSelector; // the selector itself
+ CSS1Selector *m_pNext; // the following component
+
+public:
+ CSS1Selector( CSS1SelectorType eTyp, OUString aSel )
+ : m_eType(eTyp), m_aSelector(std::move( aSel )), m_pNext( nullptr )
+ {}
+
+ ~CSS1Selector();
+
+ CSS1SelectorType GetType() const { return m_eType; }
+ const OUString& GetString() const { return m_aSelector; }
+
+ void SetNext( CSS1Selector *pNxt ) { m_pNext = pNxt; }
+ const CSS1Selector *GetNext() const { return m_pNext; }
+};
+
+/** a subexpression of a CSS1 declaration
+ *
+ * It contains
+ * - the type of this expression (= token)
+ * - the value as string (and/or double, with algebraic sign for NUMBER and LENGTH)
+ * - the operator with that it is connected with the *predecessor* expression
+ */
+struct CSS1Expression
+{
+private:
+ sal_Unicode cOp; // type of the link with its predecessor
+ CSS1Token eType; // type of the expression
+ OUString aValue; // value as string
+ double nValue; // value as number (TWIPs for LENGTH)
+ CSS1Expression *pNext; // the following component
+
+public:
+ CSS1Expression( CSS1Token eTyp, OUString aVal,
+ double nVal, sal_Unicode cO = 0 )
+ : cOp(cO), eType(eTyp), aValue(std::move(aVal)), nValue(nVal), pNext(nullptr)
+ {}
+
+ ~CSS1Expression();
+
+ inline void Set( CSS1Token eTyp, const OUString &rVal, double nVal );
+
+ CSS1Token GetType() const { return eType; }
+ const OUString& GetString() const { return aValue; }
+ double GetNumber() const { return nValue; }
+ inline sal_uInt32 GetULength() const;
+ inline sal_Int32 GetSLength() const;
+ sal_Unicode GetOp() const { return cOp; }
+
+ void GetURL( OUString& rURL ) const;
+ bool GetColor( Color &rRGB ) const;
+
+ void SetNext( CSS1Expression *pNxt ) { pNext = pNxt; }
+ const CSS1Expression *GetNext() const { return pNext; }
+};
+
+inline void CSS1Expression::Set( CSS1Token eTyp, const OUString &rVal,
+ double nVal )
+{
+ cOp = 0; eType = eTyp; aValue = rVal; nValue = nVal; pNext = nullptr;
+}
+
+inline sal_uInt32 CSS1Expression::GetULength() const
+{
+ return nValue < 0. ? 0UL : static_cast<sal_uInt32>(nValue + .5);
+}
+
+inline sal_Int32 CSS1Expression::GetSLength() const
+{
+ return static_cast<sal_Int32>(nValue + (nValue < 0. ? -.5 : .5 ));
+}
+
+/** Parser of a style element/option
+ *
+ * This class parses the content of a style element or a style option and preprocesses it.
+ *
+ * The result of the parser is forwarded to derived parsers by the methods SelectorParsed()
+ * and DeclarationParsed(). Example:
+ * H1, H2 { font-weight: bold; text-align: right }
+ * | | | |
+ * | | | DeclP( 'text-align', 'right' )
+ * | | DeclP( 'font-weight', 'bold' )
+ * | SelP( 'H2', false )
+ * SelP( 'H1', true )
+ */
+class CSS1Parser
+{
+ bool m_bWhiteSpace : 1; // read a whitespace?
+ bool m_bEOF : 1; // is end of "file"?
+
+ sal_Unicode m_cNextCh; // next character
+
+ sal_Int32 m_nInPos; // current position in the input string
+
+ sal_uInt32 m_nlLineNr; // current row number
+ sal_uInt32 m_nlLinePos; // current column number
+
+ double m_nValue; // value of the token as number
+
+ CSS1ParserState m_eState; // current state of the parser
+ CSS1Token m_nToken; // the current token
+
+ OUString m_aIn; // the string to parse
+ OUString m_aToken; // token as string
+
+ /// prepare parsing
+ void InitRead( const OUString& rIn );
+
+ /// @returns the next character to parse
+ sal_Unicode GetNextChar();
+
+ /// @returns the next token to parse
+ CSS1Token GetNextToken();
+
+ /// Is the parser still working?
+ bool IsParserWorking() const { return CSS1_PAR_WORKING == m_eState; }
+
+ bool IsEOF() const { return m_bEOF; }
+
+ // parse parts of the grammar
+ void ParseRule();
+ std::unique_ptr<CSS1Selector> ParseSelector();
+ std::unique_ptr<CSS1Expression> ParseDeclaration( OUString& rProperty );
+
+protected:
+ void ParseStyleSheet();
+
+ /** parse the content of a HTML style element
+ *
+ * For each selector and each declaration the methods SelectorParsed()
+ * or DeclarationParsed() need to be called afterwards
+ *
+ * @param rIn the style element as string
+ */
+ void ParseStyleSheet( const OUString& rIn );
+
+ /** parse the content of a HTML style option
+ *
+ * For each selector and each declaration the methods SelectorParsed()
+ * or DeclarationParsed() need to be called afterwards.
+ *
+ * @param rIn the style option as string
+ * @return true if ???
+ */
+ void ParseStyleOption( const OUString& rIn );
+
+ /** Called after a selector was parsed.
+ *
+ * @param pSelector The selector that was parsed
+ * @param bFirst if true, a new declaration starts with this selector
+ */
+ virtual void SelectorParsed( std::unique_ptr<CSS1Selector> pSelector, bool bFirst );
+
+ /** Called after a declaration or property was parsed
+ *
+ * @param rProperty The declaration/property
+ * @param pExpr ???
+ */
+ virtual void DeclarationParsed( const OUString& rProperty,
+ std::unique_ptr<CSS1Expression> pExpr );
+
+public:
+ CSS1Parser();
+ virtual ~CSS1Parser();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/svxcss1.cxx b/sw/source/filter/html/svxcss1.cxx
new file mode 100644
index 0000000000..d75cc487da
--- /dev/null
+++ b/sw/source/filter/html/svxcss1.cxx
@@ -0,0 +1,3153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <memory>
+#include <stdlib.h>
+
+#include <svx/svxids.hrc>
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svl/urihelper.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/orphitem.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <hintids.hxx>
+
+#include "css1kywd.hxx"
+#include "svxcss1.hxx"
+#include "htmlnum.hxx"
+
+using namespace ::com::sun::star;
+
+/// type of functions to parse CSS1 properties
+typedef void (*FnParseCSS1Prop)( const CSS1Expression *pExpr,
+ SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser );
+
+CSS1PropertyEnum const aFontSizeTable[] =
+{
+ { "xx-small", 0 },
+ { "x-small", 1 },
+ { "small", 2 },
+ { "medium", 3 },
+ { "large", 4 },
+ { "x-large", 5 },
+ { "xx-large", 6 },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aFontWeightTable[] =
+{
+ { "extra-light", WEIGHT_NORMAL }, // WEIGHT_ULTRALIGHT (OBS)
+ { "light", WEIGHT_NORMAL }, // WEIGHT_LIGHT (OBSOLETE)
+ { "demi-light", WEIGHT_NORMAL }, // WEIGHT_SEMILIGHT (OBS)
+ { "medium", WEIGHT_NORMAL }, // WEIGHT_MEDIUM (OBS)
+ { "normal", WEIGHT_NORMAL }, // WEIGHT_MEDIUM
+ { "demi-bold", WEIGHT_NORMAL }, // WEIGHT_SEMIBOLD (OBS)
+ { "bold", WEIGHT_BOLD }, // WEIGHT_BOLD (OBSOLETE)
+ { "extra-bold", WEIGHT_BOLD }, // WEIGHT_ULTRABOLD (OBS)
+ { "bolder", WEIGHT_BOLD },
+ { "lighter", WEIGHT_NORMAL },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aFontStyleTable[] =
+{
+ { "normal", ITALIC_NONE },
+ { "italic", ITALIC_NORMAL },
+ { "oblique", ITALIC_NORMAL },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aFontVariantTable[] =
+{
+ { "normal", sal_uInt16(SvxCaseMap::NotMapped) },
+ { "small-caps", sal_uInt16(SvxCaseMap::SmallCaps) },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aTextTransformTable[] =
+{
+ { "uppercase", sal_uInt16(SvxCaseMap::Uppercase) },
+ { "lowercase", sal_uInt16(SvxCaseMap::Lowercase) },
+ { "capitalize", sal_uInt16(SvxCaseMap::Capitalize) },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aDirectionTable[] =
+{
+ { "ltr", sal_uInt16(SvxFrameDirection::Horizontal_LR_TB) },
+ { "rtl", sal_uInt16(SvxFrameDirection::Horizontal_RL_TB) },
+ { "inherit", sal_uInt16(SvxFrameDirection::Environment) },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aBGRepeatTable[] =
+{
+ { "repeat", GPOS_TILED },
+ { "repeat-x", GPOS_TILED },
+ { "repeat-y", GPOS_TILED },
+ { "no-repeat", GPOS_NONE },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aBGHoriPosTable[] =
+{
+ { "left", GPOS_LT },
+ { "center", GPOS_MT },
+ { "right", GPOS_RT },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aBGVertPosTable[] =
+{
+ { "top", GPOS_LT },
+ { "middle", GPOS_LM },
+ { "bottom", GPOS_LB },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aTextAlignTable[] =
+{
+ { "left", sal_uInt16(SvxAdjust::Left) },
+ { "center", sal_uInt16(SvxAdjust::Center) },
+ { "right", sal_uInt16(SvxAdjust::Right) },
+ { "justify", sal_uInt16(SvxAdjust::Block) },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aBorderWidthTable[] =
+{
+ { "thin", 0 }, // DEF_LINE_WIDTH_0 / DEF_DOUBLE_LINE0
+ { "medium", 1 }, // DEF_LINE_WIDTH_1 / DEF_DOUBLE_LINE1
+ { "thick", 2 }, // DEF_LINE_WIDTH_2 / DEF_DOUBLE_LINE2
+ { nullptr, 0 }
+};
+
+namespace {
+
+enum CSS1BorderStyle { CSS1_BS_NONE, CSS1_BS_SINGLE, CSS1_BS_DOUBLE, CSS1_BS_DOTTED, CSS1_BS_DASHED, CSS1_BS_GROOVE, CSS1_BS_RIDGE, CSS1_BS_INSET, CSS1_BS_OUTSET };
+
+}
+
+CSS1PropertyEnum const aBorderStyleTable[] =
+{
+ { "none", CSS1_BS_NONE },
+ { "dotted", CSS1_BS_DOTTED },
+ { "dashed", CSS1_BS_DASHED },
+ { "solid", CSS1_BS_SINGLE },
+ { "double", CSS1_BS_DOUBLE },
+ { "groove", CSS1_BS_GROOVE },
+ { "ridge", CSS1_BS_RIDGE },
+ { "inset", CSS1_BS_INSET },
+ { "outset", CSS1_BS_OUTSET },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aFloatTable[] =
+{
+ { "left", sal_uInt16(SvxAdjust::Left) },
+ { "right", sal_uInt16(SvxAdjust::Right) },
+ { "none", sal_uInt16(SvxAdjust::End) },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aPositionTable[] =
+{
+ { "absolute", SVX_CSS1_POS_ABSOLUTE },
+ { "relative", SVX_CSS1_POS_RELATIVE },
+ { "static", SVX_CSS1_POS_STATIC },
+ { nullptr, 0 }
+};
+
+// Feature: PrintExt
+CSS1PropertyEnum const aSizeTable[] =
+{
+ { "auto", SVX_CSS1_STYPE_AUTO },
+ { "landscape", SVX_CSS1_STYPE_LANDSCAPE },
+ { "portrait", SVX_CSS1_STYPE_PORTRAIT },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aPageBreakTable[] =
+{
+ { "auto", SVX_CSS1_PBREAK_AUTO },
+ { "always", SVX_CSS1_PBREAK_ALWAYS },
+ { "avoid", SVX_CSS1_PBREAK_AVOID },
+ { "left", SVX_CSS1_PBREAK_LEFT },
+ { "right", SVX_CSS1_PBREAK_RIGHT },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aNumberStyleTable[] =
+{
+ { "decimal", SVX_NUM_ARABIC },
+ { "lower-alpha", SVX_NUM_CHARS_LOWER_LETTER },
+ { "lower-latin", SVX_NUM_CHARS_LOWER_LETTER },
+ { "lower-roman", SVX_NUM_ROMAN_LOWER },
+ { "upper-alpha", SVX_NUM_CHARS_UPPER_LETTER },
+ { "upper-latin", SVX_NUM_CHARS_UPPER_LETTER },
+ { "upper-roman", SVX_NUM_ROMAN_UPPER },
+ { nullptr, 0 }
+};
+
+CSS1PropertyEnum const aBulletStyleTable[] =
+{
+ { "circle", HTML_BULLETCHAR_CIRCLE },
+ { "disc", HTML_BULLETCHAR_DISC },
+ { "square", HTML_BULLETCHAR_SQUARE },
+ { nullptr, 0 }
+};
+
+sal_uInt16 const aBorderWidths[] =
+{
+ SvxBorderLineWidth::Hairline,
+ SvxBorderLineWidth::VeryThin,
+ SvxBorderLineWidth::Thin
+};
+
+#undef SBORDER_ENTRY
+#undef DBORDER_ENTRY
+
+namespace {
+
+struct SvxCSS1ItemIds
+{
+ sal_uInt16 nFont;
+ sal_uInt16 nFontCJK;
+ sal_uInt16 nFontCTL;
+ sal_uInt16 nPosture;
+ sal_uInt16 nPostureCJK;
+ sal_uInt16 nPostureCTL;
+ sal_uInt16 nWeight;
+ sal_uInt16 nWeightCJK;
+ sal_uInt16 nWeightCTL;
+ sal_uInt16 nFontHeight;
+ sal_uInt16 nFontHeightCJK;
+ sal_uInt16 nFontHeightCTL;
+ sal_uInt16 nUnderline;
+ sal_uInt16 nOverline;
+ sal_uInt16 nCrossedOut;
+ sal_uInt16 nColor;
+ sal_uInt16 nKerning;
+ sal_uInt16 nCaseMap;
+ sal_uInt16 nBlink;
+
+ sal_uInt16 nLineSpacing;
+ sal_uInt16 nAdjust;
+ sal_uInt16 nWidows;
+ sal_uInt16 nOrphans;
+ sal_uInt16 nFormatSplit;
+
+ // this looks a bit superfluous? TypedWhichId<SvxLRSpaceItem> nLRSpace{0};
+ TypedWhichId<SvxULSpaceItem> nULSpace{0};
+ sal_uInt16 nBox;
+ sal_uInt16 nBrush;
+
+ sal_uInt16 nLanguage;
+ sal_uInt16 nLanguageCJK;
+ sal_uInt16 nLanguageCTL;
+ sal_uInt16 nDirection;
+};
+
+}
+
+static SvxCSS1ItemIds aItemIds;
+
+struct SvxCSS1BorderInfo
+{
+ Color aColor;
+ sal_uInt16 nAbsWidth;
+ sal_uInt16 nNamedWidth;
+ CSS1BorderStyle eStyle;
+
+ SvxCSS1BorderInfo() :
+ aColor( COL_BLACK ), nAbsWidth( USHRT_MAX ),
+ nNamedWidth( USHRT_MAX ), eStyle( CSS1_BS_NONE )
+ {}
+
+ void SetBorderLine( SvxBoxItemLine nLine, SvxBoxItem &rBoxItem ) const;
+};
+
+void SvxCSS1BorderInfo::SetBorderLine( SvxBoxItemLine nLine, SvxBoxItem &rBoxItem ) const
+{
+ if( CSS1_BS_NONE==eStyle || nAbsWidth==0 ||
+ (nAbsWidth==USHRT_MAX && nNamedWidth==USHRT_MAX) )
+ {
+ rBoxItem.SetLine( nullptr, nLine );
+ return;
+ }
+
+ ::editeng::SvxBorderLine aBorderLine( &aColor );
+
+ // Line style double or single?
+ switch ( eStyle )
+ {
+ case CSS1_BS_SINGLE:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
+ break;
+ case CSS1_BS_DOUBLE:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ break;
+ case CSS1_BS_DOTTED:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOTTED);
+ break;
+ case CSS1_BS_DASHED:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DASHED);
+ break;
+ case CSS1_BS_GROOVE:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::ENGRAVED);
+ break;
+ case CSS1_BS_RIDGE:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::EMBOSSED);
+ break;
+ case CSS1_BS_INSET:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::INSET);
+ break;
+ case CSS1_BS_OUTSET:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::OUTSET);
+ break;
+ default:
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::NONE);
+ break;
+ }
+
+ // convert named width, if no absolute is given
+ if( nAbsWidth==USHRT_MAX )
+ aBorderLine.SetWidth( aBorderWidths[ nNamedWidth ] );
+ else
+ aBorderLine.SetWidth( nAbsWidth );
+
+ rBoxItem.SetLine( &aBorderLine, nLine );
+}
+
+SvxCSS1PropertyInfo::SvxCSS1PropertyInfo()
+{
+ Clear();
+}
+
+SvxCSS1PropertyInfo::SvxCSS1PropertyInfo( const SvxCSS1PropertyInfo& rProp ) :
+ m_aId( rProp.m_aId ),
+ m_bTopMargin( rProp.m_bTopMargin ),
+ m_bBottomMargin( rProp.m_bBottomMargin ),
+ m_bLeftMargin( rProp.m_bLeftMargin ),
+ m_bRightMargin( rProp.m_bRightMargin ),
+ m_bTextIndent( rProp.m_bTextIndent ),
+ m_bNumbering ( rProp.m_bNumbering ),
+ m_bBullet ( rProp.m_bBullet ),
+ m_eFloat( rProp.m_eFloat ),
+ m_ePosition( rProp.m_ePosition ),
+ m_nTopBorderDistance( rProp.m_nTopBorderDistance ),
+ m_nBottomBorderDistance( rProp.m_nBottomBorderDistance ),
+ m_nLeftBorderDistance( rProp.m_nLeftBorderDistance ),
+ m_nRightBorderDistance( rProp.m_nRightBorderDistance ),
+ m_nNumberingType ( rProp.m_nNumberingType ),
+ m_cBulletChar( rProp.m_cBulletChar ),
+ m_nColumnCount( rProp.m_nColumnCount ),
+ m_nLeft( rProp.m_nLeft ),
+ m_nTop( rProp.m_nTop ),
+ m_nWidth( rProp.m_nWidth ),
+ m_nHeight( rProp.m_nHeight ),
+ m_nLeftMargin( rProp.m_nLeftMargin ),
+ m_nRightMargin( rProp.m_nRightMargin ),
+ m_eLeftType( rProp.m_eLeftType ),
+ m_eTopType( rProp.m_eTopType ),
+ m_eWidthType( rProp.m_eWidthType ),
+ m_eHeightType( rProp.m_eHeightType ),
+ m_eLeftMarginType( rProp.m_eLeftMarginType ),
+ m_eRightMarginType( rProp.m_eRightMarginType ),
+ m_eSizeType( rProp.m_eSizeType ),
+ m_ePageBreakBefore( rProp.m_ePageBreakBefore ),
+ m_ePageBreakAfter( rProp.m_ePageBreakAfter )
+{
+ for( size_t i=0; i<m_aBorderInfos.size(); ++i )
+ if (rProp.m_aBorderInfos[i])
+ m_aBorderInfos[i].reset( new SvxCSS1BorderInfo( *rProp.m_aBorderInfos[i] ) );
+}
+
+SvxCSS1PropertyInfo::~SvxCSS1PropertyInfo()
+{
+}
+
+void SvxCSS1PropertyInfo::DestroyBorderInfos()
+{
+ for(auto & rp : m_aBorderInfos)
+ rp.reset();
+}
+
+void SvxCSS1PropertyInfo::Clear()
+{
+ m_aId.clear();
+ m_bTopMargin = m_bBottomMargin = false;
+ m_bLeftMargin = m_bRightMargin = m_bTextIndent = false;
+ m_bNumbering = m_bBullet = false;
+ m_nLeftMargin = m_nRightMargin = 0;
+ m_eFloat = SvxAdjust::End;
+
+ m_ePosition = SVX_CSS1_POS_NONE;
+ m_nTopBorderDistance = m_nBottomBorderDistance =
+ m_nLeftBorderDistance = m_nRightBorderDistance = UNSET_BORDER_DISTANCE;
+
+ m_nNumberingType = SVX_NUM_CHARS_UPPER_LETTER;
+ m_cBulletChar = ' ';
+
+ m_nColumnCount = 0;
+
+ m_nLeft = m_nTop = m_nWidth = m_nHeight = 0;
+ m_eLeftType = m_eTopType = m_eWidthType = m_eHeightType = SVX_CSS1_LTYPE_NONE;
+ m_eLeftMarginType = SVX_CSS1_LTYPE_NONE;
+ m_eRightMarginType = SVX_CSS1_LTYPE_NONE;
+
+// Feature: PrintExt
+ m_eSizeType = SVX_CSS1_STYPE_NONE;
+ m_ePageBreakBefore = SVX_CSS1_PBREAK_NONE;
+ m_ePageBreakAfter = SVX_CSS1_PBREAK_NONE;
+
+ DestroyBorderInfos();
+}
+
+void SvxCSS1PropertyInfo::Merge( const SvxCSS1PropertyInfo& rProp )
+{
+ if( rProp.m_bTopMargin )
+ m_bTopMargin = true;
+ if( rProp.m_bBottomMargin )
+ m_bBottomMargin = true;
+
+ if( rProp.m_bLeftMargin )
+ {
+ m_bLeftMargin = true;
+ m_nLeftMargin = rProp.m_nLeftMargin;
+ }
+ if( rProp.m_bRightMargin )
+ {
+ m_bRightMargin = true;
+ m_nRightMargin = rProp.m_nRightMargin;
+ }
+ if( rProp.m_bTextIndent )
+ m_bTextIndent = true;
+
+ for( size_t i=0; i<m_aBorderInfos.size(); ++i )
+ {
+ if( rProp.m_aBorderInfos[i] )
+ m_aBorderInfos[i].reset( new SvxCSS1BorderInfo( *rProp.m_aBorderInfos[i] ) );
+ }
+
+ if( UNSET_BORDER_DISTANCE != rProp.m_nTopBorderDistance )
+ m_nTopBorderDistance = rProp.m_nTopBorderDistance;
+ if( UNSET_BORDER_DISTANCE != rProp.m_nBottomBorderDistance )
+ m_nBottomBorderDistance = rProp.m_nBottomBorderDistance;
+ if( UNSET_BORDER_DISTANCE != rProp.m_nLeftBorderDistance )
+ m_nLeftBorderDistance = rProp.m_nLeftBorderDistance;
+ if( UNSET_BORDER_DISTANCE != rProp.m_nRightBorderDistance )
+ m_nRightBorderDistance = rProp.m_nRightBorderDistance;
+
+ m_nColumnCount = rProp.m_nColumnCount;
+
+ if( rProp.m_eFloat != SvxAdjust::End )
+ m_eFloat = rProp.m_eFloat;
+
+ if( rProp.m_ePosition != SVX_CSS1_POS_NONE )
+ m_ePosition = rProp.m_ePosition;
+
+// Feature: PrintExt
+ if( rProp.m_eSizeType != SVX_CSS1_STYPE_NONE )
+ {
+ m_eSizeType = rProp.m_eSizeType;
+ m_nWidth = rProp.m_nWidth;
+ m_nHeight = rProp.m_nHeight;
+ }
+
+ if( rProp.m_ePageBreakBefore != SVX_CSS1_PBREAK_NONE )
+ m_ePageBreakBefore = rProp.m_ePageBreakBefore;
+
+ if( rProp.m_ePageBreakAfter != SVX_CSS1_PBREAK_NONE )
+ m_ePageBreakAfter = rProp.m_ePageBreakAfter;
+
+ if( rProp.m_eLeftType != SVX_CSS1_LTYPE_NONE )
+ {
+ m_eLeftType = rProp.m_eLeftType;
+ m_nLeft = rProp.m_nLeft;
+ }
+
+ if( rProp.m_eTopType != SVX_CSS1_LTYPE_NONE )
+ {
+ m_eTopType = rProp.m_eTopType;
+ m_nTop = rProp.m_nTop;
+ }
+
+ if( rProp.m_eWidthType != SVX_CSS1_LTYPE_NONE )
+ {
+ m_eWidthType = rProp.m_eWidthType;
+ m_nWidth = rProp.m_nWidth;
+ }
+
+ if( rProp.m_eHeightType != SVX_CSS1_LTYPE_NONE )
+ {
+ m_eHeightType = rProp.m_eHeightType;
+ m_nHeight = rProp.m_nHeight;
+ }
+}
+
+SvxCSS1BorderInfo *SvxCSS1PropertyInfo::GetBorderInfo( SvxBoxItemLine nLine, bool bCreate )
+{
+ sal_uInt16 nPos = 0;
+ switch( nLine )
+ {
+ case SvxBoxItemLine::TOP: nPos = 0; break;
+ case SvxBoxItemLine::BOTTOM: nPos = 1; break;
+ case SvxBoxItemLine::LEFT: nPos = 2; break;
+ case SvxBoxItemLine::RIGHT: nPos = 3; break;
+ }
+
+ if( !m_aBorderInfos[nPos] && bCreate )
+ m_aBorderInfos[nPos].reset( new SvxCSS1BorderInfo );
+
+ return m_aBorderInfos[nPos].get();
+}
+
+void SvxCSS1PropertyInfo::CopyBorderInfo( SvxBoxItemLine nSrcLine, SvxBoxItemLine nDstLine,
+ sal_uInt16 nWhat )
+{
+ SvxCSS1BorderInfo *pSrcInfo = GetBorderInfo( nSrcLine, false );
+ if( !pSrcInfo )
+ return;
+
+ SvxCSS1BorderInfo *pDstInfo = GetBorderInfo( nDstLine );
+ if( (nWhat & SVX_CSS1_BORDERINFO_WIDTH) != 0 )
+ {
+ pDstInfo->nAbsWidth = pSrcInfo->nAbsWidth;
+ pDstInfo->nNamedWidth = pSrcInfo->nNamedWidth;
+ }
+
+ if( (nWhat & SVX_CSS1_BORDERINFO_COLOR) != 0 )
+ pDstInfo->aColor = pSrcInfo->aColor;
+
+ if( (nWhat & SVX_CSS1_BORDERINFO_STYLE) != 0 )
+ pDstInfo->eStyle = pSrcInfo->eStyle;
+}
+
+void SvxCSS1PropertyInfo::CopyBorderInfo( sal_uInt16 nCount, sal_uInt16 nWhat )
+{
+ if( nCount==0 )
+ {
+ CopyBorderInfo( SvxBoxItemLine::BOTTOM, SvxBoxItemLine::TOP, nWhat );
+ CopyBorderInfo( SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, nWhat );
+ }
+ if( nCount<=1 )
+ {
+ CopyBorderInfo( SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT, nWhat );
+ }
+}
+
+void SvxCSS1PropertyInfo::SetBoxItem( SfxItemSet& rItemSet,
+ sal_uInt16 nMinBorderDist,
+ const SvxBoxItem *pDfltItem )
+{
+ bool bChg = m_nTopBorderDistance != UNSET_BORDER_DISTANCE ||
+ m_nBottomBorderDistance != UNSET_BORDER_DISTANCE ||
+ m_nLeftBorderDistance != UNSET_BORDER_DISTANCE ||
+ m_nRightBorderDistance != UNSET_BORDER_DISTANCE;
+
+ for( size_t i=0; !bChg && i<m_aBorderInfos.size(); ++i )
+ bChg = m_aBorderInfos[i]!=nullptr;
+
+ if( !bChg )
+ return;
+
+ std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(aItemIds.nBox));
+ if( pDfltItem )
+ aBoxItem.reset(pDfltItem->Clone());
+
+ SvxCSS1BorderInfo *pInfo = GetBorderInfo( SvxBoxItemLine::TOP, false );
+ if( pInfo )
+ pInfo->SetBorderLine( SvxBoxItemLine::TOP, *aBoxItem );
+
+ pInfo = GetBorderInfo( SvxBoxItemLine::BOTTOM, false );
+ if( pInfo )
+ pInfo->SetBorderLine( SvxBoxItemLine::BOTTOM, *aBoxItem );
+
+ pInfo = GetBorderInfo( SvxBoxItemLine::LEFT, false );
+ if( pInfo )
+ pInfo->SetBorderLine( SvxBoxItemLine::LEFT, *aBoxItem );
+
+ pInfo = GetBorderInfo( SvxBoxItemLine::RIGHT, false );
+ if( pInfo )
+ pInfo->SetBorderLine( SvxBoxItemLine::RIGHT, *aBoxItem );
+
+ for( size_t i=0; i<m_aBorderInfos.size(); ++i )
+ {
+ SvxBoxItemLine nLine = SvxBoxItemLine::TOP;
+ sal_uInt16 nDist = 0;
+ switch( i )
+ {
+ case 0: nLine = SvxBoxItemLine::TOP;
+ nDist = m_nTopBorderDistance;
+ m_nTopBorderDistance = UNSET_BORDER_DISTANCE;
+ break;
+ case 1: nLine = SvxBoxItemLine::BOTTOM;
+ nDist = m_nBottomBorderDistance;
+ m_nBottomBorderDistance = UNSET_BORDER_DISTANCE;
+ break;
+ case 2: nLine = SvxBoxItemLine::LEFT;
+ nDist = m_nLeftBorderDistance;
+ m_nLeftBorderDistance = UNSET_BORDER_DISTANCE;
+ break;
+ case 3: nLine = SvxBoxItemLine::RIGHT;
+ nDist = m_nRightBorderDistance;
+ m_nRightBorderDistance = UNSET_BORDER_DISTANCE;
+ break;
+ }
+
+ if( aBoxItem->GetLine( nLine ) )
+ {
+ if( UNSET_BORDER_DISTANCE == nDist )
+ nDist = aBoxItem->GetDistance( nLine );
+
+ if( nDist < nMinBorderDist )
+ nDist = nMinBorderDist;
+ }
+ else
+ {
+ nDist = 0U;
+ }
+
+ aBoxItem->SetDistance( nDist, nLine );
+ }
+
+ rItemSet.Put( *aBoxItem );
+
+ DestroyBorderInfos();
+}
+
+SvxCSS1MapEntry::SvxCSS1MapEntry( SfxItemSet aItemSet,
+ const SvxCSS1PropertyInfo& rProp ) :
+ m_aItemSet(std::move( aItemSet )),
+ m_aPropInfo( rProp )
+{}
+
+void SvxCSS1Parser::StyleParsed( const CSS1Selector * /*pSelector*/,
+ SfxItemSet& /*rItemSet*/,
+ SvxCSS1PropertyInfo& /*rPropInfo*/ )
+{
+ // you see nothing is happening here
+}
+
+void SvxCSS1Parser::SelectorParsed( std::unique_ptr<CSS1Selector> pSelector, bool bFirst )
+{
+ if( bFirst )
+ {
+ OSL_ENSURE( m_pSheetItemSet, "Where is the Item-Set for Style-Sheets?" );
+
+ for (const std::unique_ptr<CSS1Selector> & rpSelection : m_Selectors)
+ {
+ StyleParsed(rpSelection.get(), *m_pSheetItemSet, *m_pSheetPropInfo);
+ }
+ m_pSheetItemSet->ClearItem();
+ m_pSheetPropInfo->Clear();
+
+ // prepare the next rule
+ m_Selectors.clear();
+ }
+
+ m_Selectors.push_back(std::move(pSelector));
+}
+
+SvxCSS1Parser::SvxCSS1Parser( SfxItemPool& rPool, OUString aBaseURL,
+ sal_uInt16 const *pWhichIds, sal_uInt16 nWhichIds ) :
+ m_sBaseURL(std::move( aBaseURL )),
+ m_pItemSet(nullptr),
+ m_pPropInfo( nullptr ),
+ m_eDefaultEnc( RTL_TEXTENCODING_DONTKNOW ),
+ m_bIgnoreFontFamily( false )
+{
+ // also initialize item IDs
+ auto initTrueWhich = [&rPool, this](sal_uInt16 rWid)
+ {
+ rWid = rPool.GetTrueWhich(rWid, false);
+ m_aWhichMap = m_aWhichMap.MergeRange(rWid, rWid);
+ return rWid;
+ };
+
+ aItemIds.nFont = initTrueWhich( SID_ATTR_CHAR_FONT );
+ aItemIds.nFontCJK = initTrueWhich( SID_ATTR_CHAR_CJK_FONT );
+ aItemIds.nFontCTL = initTrueWhich( SID_ATTR_CHAR_CTL_FONT );
+ aItemIds.nPosture = initTrueWhich( SID_ATTR_CHAR_POSTURE );
+ aItemIds.nPostureCJK = initTrueWhich( SID_ATTR_CHAR_CJK_POSTURE );
+ aItemIds.nPostureCTL = initTrueWhich( SID_ATTR_CHAR_CTL_POSTURE );
+ aItemIds.nWeight = initTrueWhich( SID_ATTR_CHAR_WEIGHT );
+ aItemIds.nWeightCJK = initTrueWhich( SID_ATTR_CHAR_CJK_WEIGHT );
+ aItemIds.nWeightCTL = initTrueWhich( SID_ATTR_CHAR_CTL_WEIGHT );
+ aItemIds.nFontHeight = initTrueWhich( SID_ATTR_CHAR_FONTHEIGHT );
+ aItemIds.nFontHeightCJK = initTrueWhich( SID_ATTR_CHAR_CJK_FONTHEIGHT );
+ aItemIds.nFontHeightCTL = initTrueWhich( SID_ATTR_CHAR_CTL_FONTHEIGHT );
+ aItemIds.nUnderline = initTrueWhich( SID_ATTR_CHAR_UNDERLINE );
+ aItemIds.nOverline = initTrueWhich( SID_ATTR_CHAR_OVERLINE );
+ aItemIds.nCrossedOut = initTrueWhich( SID_ATTR_CHAR_STRIKEOUT );
+ aItemIds.nColor = initTrueWhich( SID_ATTR_CHAR_COLOR );
+ aItemIds.nKerning = initTrueWhich( SID_ATTR_CHAR_KERNING );
+ aItemIds.nCaseMap = initTrueWhich( SID_ATTR_CHAR_CASEMAP );
+ aItemIds.nBlink = initTrueWhich( SID_ATTR_FLASH );
+
+ aItemIds.nLineSpacing = initTrueWhich( SID_ATTR_PARA_LINESPACE );
+ aItemIds.nAdjust = initTrueWhich( SID_ATTR_PARA_ADJUST );
+ aItemIds.nWidows = initTrueWhich( SID_ATTR_PARA_WIDOWS );
+ aItemIds.nOrphans = initTrueWhich( SID_ATTR_PARA_ORPHANS );
+ aItemIds.nFormatSplit = initTrueWhich( SID_ATTR_PARA_SPLIT );
+
+ // every id that is used must be added
+ m_aWhichMap = m_aWhichMap.MergeRange(RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE);
+ m_aWhichMap = m_aWhichMap.MergeRange(RES_MARGIN_TEXTLEFT, RES_MARGIN_TEXTLEFT);
+ m_aWhichMap = m_aWhichMap.MergeRange(RES_MARGIN_RIGHT, RES_MARGIN_RIGHT);
+ aItemIds.nULSpace = TypedWhichId<SvxULSpaceItem>(initTrueWhich( SID_ATTR_ULSPACE ));
+ aItemIds.nBox = initTrueWhich( SID_ATTR_BORDER_OUTER );
+ aItemIds.nBrush = initTrueWhich( SID_ATTR_BRUSH );
+
+ aItemIds.nLanguage = initTrueWhich( SID_ATTR_CHAR_LANGUAGE );
+ aItemIds.nLanguageCJK = initTrueWhich( SID_ATTR_CHAR_CJK_LANGUAGE );
+ aItemIds.nLanguageCTL = initTrueWhich( SID_ATTR_CHAR_CTL_LANGUAGE );
+ aItemIds.nDirection = initTrueWhich( SID_ATTR_FRAMEDIRECTION );
+
+ if( pWhichIds && nWhichIds )
+ for (sal_uInt16 i = 0; i < nWhichIds; ++i)
+ m_aWhichMap = m_aWhichMap.MergeRange(pWhichIds[i], pWhichIds[i]);
+
+ m_pSheetItemSet.reset( new SfxItemSet( rPool, m_aWhichMap ) );
+ m_pSheetPropInfo.reset( new SvxCSS1PropertyInfo );
+}
+
+SvxCSS1Parser::~SvxCSS1Parser()
+{
+ m_pSheetItemSet.reset();
+ m_pSheetPropInfo.reset();
+}
+
+void SvxCSS1Parser::InsertId( const OUString& rId,
+ const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp )
+{
+ InsertMapEntry( rId, rItemSet, rProp, m_Ids );
+}
+
+const SvxCSS1MapEntry* SvxCSS1Parser::GetId( const OUString& rId ) const
+{
+ CSS1Map::const_iterator itr = m_Ids.find(rId);
+ return itr == m_Ids.end() ? nullptr : itr->second.get();
+}
+
+void SvxCSS1Parser::InsertClass( const OUString& rClass,
+ const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp )
+{
+ InsertMapEntry( rClass, rItemSet, rProp, m_Classes );
+}
+
+const SvxCSS1MapEntry* SvxCSS1Parser::GetClass( const OUString& rClass ) const
+{
+ CSS1Map::const_iterator itr = m_Classes.find(rClass);
+ return itr == m_Classes.end() ? nullptr : itr->second.get();
+}
+
+void SvxCSS1Parser::InsertPage( const OUString& rPage,
+ bool bPseudo,
+ const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp )
+{
+ OUString aKey( rPage );
+ if( bPseudo )
+ aKey = ":" + aKey;
+ InsertMapEntry( aKey, rItemSet, rProp, m_Pages );
+}
+
+SvxCSS1MapEntry* SvxCSS1Parser::GetPage( const OUString& rPage, bool bPseudo )
+{
+ OUString aKey( rPage );
+ if( bPseudo )
+ aKey = ":" + aKey;
+
+ CSS1Map::iterator itr = m_Pages.find(aKey);
+ return itr == m_Pages.end() ? nullptr : itr->second.get();
+}
+
+void SvxCSS1Parser::InsertTag( const OUString& rTag,
+ const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp )
+{
+ InsertMapEntry( rTag, rItemSet, rProp, m_Tags );
+}
+
+SvxCSS1MapEntry* SvxCSS1Parser::GetTag( const OUString& rTag )
+{
+ CSS1Map::iterator itr = m_Tags.find(rTag);
+ return itr == m_Tags.end() ? nullptr : itr->second.get();
+}
+
+bool SvxCSS1Parser::ParseStyleSheet( const OUString& rIn )
+{
+ m_pItemSet = m_pSheetItemSet.get();
+ m_pPropInfo = m_pSheetPropInfo.get();
+
+ CSS1Parser::ParseStyleSheet( rIn );
+
+ for (const std::unique_ptr<CSS1Selector> & rpSelector : m_Selectors)
+ {
+ StyleParsed(rpSelector.get(), *m_pSheetItemSet, *m_pSheetPropInfo);
+ }
+
+ // and clean up a little bit
+ m_Selectors.clear();
+ m_pSheetItemSet->ClearItem();
+ m_pSheetPropInfo->Clear();
+
+ m_pItemSet = nullptr;
+ m_pPropInfo = nullptr;
+
+ return true;
+}
+
+void SvxCSS1Parser::ParseStyleOption( const OUString& rIn,
+ SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo )
+{
+ m_pItemSet = &rItemSet;
+ m_pPropInfo = &rPropInfo;
+
+ CSS1Parser::ParseStyleOption( rIn );
+ rItemSet.ClearItem( aItemIds.nDirection );
+
+ m_pItemSet = nullptr;
+ m_pPropInfo = nullptr;
+}
+
+bool SvxCSS1Parser::GetEnum( const CSS1PropertyEnum *pPropTable,
+ std::u16string_view rValue, sal_uInt16& rEnum )
+{
+ while( pPropTable->pName )
+ {
+ if( !o3tl::equalsIgnoreAsciiCase( rValue, pPropTable->pName ) )
+ pPropTable++;
+ else
+ break;
+ }
+
+ if( pPropTable->pName )
+ rEnum = pPropTable->nEnum;
+
+ return (pPropTable->pName != nullptr);
+}
+
+void SvxCSS1Parser::PixelToTwip( tools::Long &rWidth, tools::Long &rHeight )
+{
+ rWidth = o3tl::convert(rWidth, o3tl::Length::px, o3tl::Length::twip);
+ rHeight = o3tl::convert(rHeight, o3tl::Length::px, o3tl::Length::twip);
+}
+
+sal_uInt32 SvxCSS1Parser::GetFontHeight( sal_uInt16 nSize ) const
+{
+ sal_uInt16 nHeight;
+
+ switch( nSize )
+ {
+ case 0: nHeight = 8*20; break;
+ case 1: nHeight = 10*20; break;
+ case 2: nHeight = 11*20; break;
+ case 3: nHeight = 12*20; break;
+ case 4: nHeight = 17*20; break;
+ case 5: nHeight = 20*20; break;
+ case 6:
+ default: nHeight = 32*20; break;
+ }
+
+ return nHeight;
+}
+
+const FontList *SvxCSS1Parser::GetFontList() const
+{
+ return nullptr;
+}
+
+void SvxCSS1Parser::InsertMapEntry( const OUString& rKey,
+ const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp,
+ CSS1Map& rMap )
+{
+ auto [itr,inserted] = rMap.insert(std::make_pair(rKey, nullptr));
+ if (inserted)
+ itr->second = std::make_unique<SvxCSS1MapEntry>(rItemSet, rProp);
+ else
+ {
+ SvxCSS1MapEntry *const p = itr->second.get();
+ MergeStyles( rItemSet, rProp,
+ p->GetItemSet(), p->GetPropertyInfo(), true );
+ }
+}
+
+void SvxCSS1Parser::MergeStyles( const SfxItemSet& rSrcSet,
+ const SvxCSS1PropertyInfo& rSrcInfo,
+ SfxItemSet& rTargetSet,
+ SvxCSS1PropertyInfo& rTargetInfo,
+ bool bSmart )
+{
+ if( !bSmart )
+ {
+ rTargetSet.Put( rSrcSet );
+ }
+ else
+ {
+ // not sure if this is really necessary?
+ SfxItemSet copy(rSrcSet);
+ if (!rSrcInfo.m_bTextIndent)
+ {
+ copy.ClearItem(RES_MARGIN_FIRSTLINE);
+ }
+ if (!rSrcInfo.m_bLeftMargin)
+ {
+ copy.ClearItem(RES_MARGIN_TEXTLEFT);
+ }
+ if (!rSrcInfo.m_bRightMargin)
+ {
+ copy.ClearItem(RES_MARGIN_RIGHT);
+ }
+
+ SvxULSpaceItem aULSpace( rTargetSet.Get(aItemIds.nULSpace) );
+
+ rTargetSet.Put(copy);
+
+ if( rSrcInfo.m_bTopMargin || rSrcInfo.m_bBottomMargin )
+ {
+ const SvxULSpaceItem& rNewULSpace = rSrcSet.Get( aItemIds.nULSpace );
+
+ if( rSrcInfo.m_bTopMargin )
+ aULSpace.SetUpper( rNewULSpace.GetUpper() );
+ if( rSrcInfo.m_bBottomMargin )
+ aULSpace.SetLower( rNewULSpace.GetLower() );
+
+ rTargetSet.Put( aULSpace );
+ }
+ }
+
+ rTargetInfo.Merge( rSrcInfo );
+}
+
+void SvxCSS1Parser::SetDfltEncoding( rtl_TextEncoding eEnc )
+{
+ m_eDefaultEnc = eEnc;
+}
+
+static void ParseCSS1_font_size( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& rParser )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ sal_uLong nHeight = 0;
+ sal_uInt16 nPropHeight = 100;
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ nHeight = pExpr->GetULength();
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fHeight = pExpr->GetNumber();
+ if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPHeight = static_cast<tools::Long>(fHeight);
+ tools::Long nPWidth = 0;
+ SvxCSS1Parser::PixelToTwip(nPWidth, nPHeight);
+ nHeight = static_cast<sal_uLong>(nPHeight);
+ }
+ else
+ {
+ SAL_WARN("sw.html", "out-of-size pxlength: " << fHeight);
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // only for drop caps!
+ nPropHeight = o3tl::narrowing<sal_uInt16>(pExpr->GetNumber());
+ break;
+ case CSS1_IDENT:
+ {
+ sal_uInt16 nSize;
+
+ if( SvxCSS1Parser::GetEnum( aFontSizeTable, pExpr->GetString(),
+ nSize ) )
+ {
+ nHeight = rParser.GetFontHeight( nSize );
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ if( nHeight || nPropHeight!=100 )
+ {
+ SvxFontHeightItem aFontHeight( nHeight, nPropHeight,
+ aItemIds.nFontHeight );
+ rItemSet.Put( aFontHeight );
+ aFontHeight.SetWhich( aItemIds.nFontHeightCJK );
+ rItemSet.Put( aFontHeight );
+ aFontHeight.SetWhich( aItemIds.nFontHeightCTL );
+ rItemSet.Put( aFontHeight );
+ }
+}
+
+static void ParseCSS1_font_family( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& rParser )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ OUStringBuffer aName;
+ rtl_TextEncoding eEnc = rParser.GetDfltEncoding();
+ const FontList *pFList = rParser.GetFontList();
+ bool bFirst = true;
+ bool bFound = false;
+ while( pExpr && (bFirst || ','==pExpr->GetOp() || !pExpr->GetOp()) )
+ {
+ CSS1Token eType = pExpr->GetType();
+ if( CSS1_IDENT==eType || CSS1_STRING==eType )
+ {
+ OUString aIdent( pExpr->GetString() );
+
+ if( CSS1_IDENT==eType )
+ {
+ // Collect all following IDs and append them with a space
+ const CSS1Expression *pNext = pExpr->GetNext();
+ while( pNext && !pNext->GetOp() &&
+ CSS1_IDENT==pNext->GetType() )
+ {
+ aIdent += " " + pNext->GetString();
+ pExpr = pNext;
+ pNext = pExpr->GetNext();
+ }
+ }
+ if( !aIdent.isEmpty() )
+ {
+ if( !bFound && pFList )
+ {
+ sal_Handle hFont = pFList->GetFirstFontMetric( aIdent );
+ if( nullptr != hFont )
+ {
+ const FontMetric& rFMetric = FontList::GetFontMetric( hFont );
+ if( RTL_TEXTENCODING_DONTKNOW != rFMetric.GetCharSet() )
+ {
+ bFound = true;
+ if( RTL_TEXTENCODING_SYMBOL == rFMetric.GetCharSet() )
+ eEnc = RTL_TEXTENCODING_SYMBOL;
+ }
+ }
+ }
+ if( !bFirst )
+ aName.append(";");
+ aName.append(aIdent);
+ }
+ }
+
+ pExpr = pExpr->GetNext();
+ bFirst = false;
+ }
+
+ if( !aName.isEmpty() && !rParser.IsIgnoreFontFamily() )
+ {
+ SvxFontItem aFont( FAMILY_DONTKNOW, aName.makeStringAndClear(), OUString(), PITCH_DONTKNOW,
+ eEnc, aItemIds.nFont );
+ rItemSet.Put( aFont );
+ aFont.SetWhich( aItemIds.nFontCJK );
+ rItemSet.Put( aFont );
+ aFont.SetWhich( aItemIds.nFontCTL );
+ rItemSet.Put( aFont );
+ }
+}
+
+static void ParseCSS1_font_weight( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ case CSS1_STRING: // MS-IE, what else
+ {
+ sal_uInt16 nWeight;
+ if( SvxCSS1Parser::GetEnum( aFontWeightTable, pExpr->GetString(),
+ nWeight ) )
+ {
+ SvxWeightItem aWeight( static_cast<FontWeight>(nWeight), aItemIds.nWeight );
+ rItemSet.Put( aWeight );
+ aWeight.SetWhich( aItemIds.nWeightCJK );
+ rItemSet.Put( aWeight );
+ aWeight.SetWhich( aItemIds.nWeightCTL );
+ rItemSet.Put( aWeight );
+ }
+ }
+ break;
+ case CSS1_NUMBER:
+ {
+ sal_uInt16 nWeight = o3tl::narrowing<sal_uInt16>(pExpr->GetNumber());
+ SvxWeightItem aWeight( nWeight>400 ? WEIGHT_BOLD : WEIGHT_NORMAL,
+ aItemIds.nWeight );
+ rItemSet.Put( aWeight );
+ aWeight.SetWhich( aItemIds.nWeightCJK );
+ rItemSet.Put( aWeight );
+ aWeight.SetWhich( aItemIds.nWeightCTL );
+ rItemSet.Put( aWeight );
+ }
+ break;
+
+ default:
+ ;
+ }
+}
+
+static void ParseCSS1_font_style( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ bool bPosture = false;
+ bool bCaseMap = false;
+ FontItalic eItalic = ITALIC_NONE;
+ SvxCaseMap eCaseMap = SvxCaseMap::NotMapped;
+
+ // normal | italic || small-caps | oblique || small-caps | small-caps
+ // (only normal, italic and oblique are valid)
+
+ // the value can have two values!
+ for( int i=0; pExpr && i<2; ++i )
+ {
+ // also here MS-IE parser leaves traces
+ if( (CSS1_IDENT==pExpr->GetType() || CSS1_STRING==pExpr->GetType()) &&
+ !pExpr->GetOp() )
+ {
+ const OUString& rValue = pExpr->GetString();
+ // first check if the value is italic or 'normal'
+ sal_uInt16 nItalic;
+ if( SvxCSS1Parser::GetEnum( aFontStyleTable, rValue, nItalic ) )
+ {
+ eItalic = static_cast<FontItalic>(nItalic);
+ if( !bCaseMap && ITALIC_NONE==eItalic )
+ {
+ // for 'normal' we must also exclude case-map
+ eCaseMap = SvxCaseMap::NotMapped;
+ bCaseMap = true;
+ }
+ bPosture = true;
+ }
+ else if( !bCaseMap &&
+ rValue.equalsIgnoreAsciiCase( "small-caps" ) )
+ {
+ eCaseMap = SvxCaseMap::SmallCaps;
+ bCaseMap = true;
+ }
+ }
+
+ // fetch next expression
+ pExpr = pExpr->GetNext();
+ }
+
+ if( bPosture )
+ {
+ SvxPostureItem aPosture( eItalic, aItemIds.nPosture );
+ rItemSet.Put( aPosture );
+ aPosture.SetWhich( aItemIds.nPostureCJK );
+ rItemSet.Put( aPosture );
+ aPosture.SetWhich( aItemIds.nPostureCTL );
+ rItemSet.Put( aPosture );
+ }
+
+ if( bCaseMap )
+ rItemSet.Put( SvxCaseMapItem( eCaseMap, aItemIds.nCaseMap ) );
+}
+
+static void ParseCSS1_font_variant( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ assert(pExpr && "no expression");
+
+ // normal | small-caps
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ {
+ sal_uInt16 nCaseMap;
+ if( SvxCSS1Parser::GetEnum( aFontVariantTable, pExpr->GetString(),
+ nCaseMap ) )
+ {
+ rItemSet.Put( SvxCaseMapItem( static_cast<SvxCaseMap>(nCaseMap),
+ aItemIds.nCaseMap ) );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void ParseCSS1_text_transform( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ // none | capitalize | uppercase | lowercase
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ {
+ sal_uInt16 nCaseMap;
+ if( SvxCSS1Parser::GetEnum( aTextTransformTable, pExpr->GetString(),
+ nCaseMap ) )
+ {
+ rItemSet.Put( SvxCaseMapItem( static_cast<SvxCaseMap>(nCaseMap),
+ aItemIds.nCaseMap ) );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void ParseCSS1_color( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ case CSS1_RGB:
+ case CSS1_HEXCOLOR:
+ case CSS1_STRING: // because MS-IE
+ {
+ Color aColor;
+ if( pExpr->GetColor( aColor ) )
+ rItemSet.Put( SvxColorItem( aColor, aItemIds.nColor ) );
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+static void ParseCSS1_column_count( const CSS1Expression *pExpr,
+ SfxItemSet& /*rItemSet*/,
+ SvxCSS1PropertyInfo &rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ assert(pExpr && "no expression");
+
+ if ( pExpr->GetType() == CSS1_NUMBER )
+ {
+ double columnCount = pExpr->GetNumber();
+ if ( columnCount >= 2 )
+ {
+ rPropInfo.m_nColumnCount = columnCount;
+ }
+ }
+}
+
+static void ParseCSS1_direction( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ assert(pExpr && "no expression");
+
+ sal_uInt16 nDir;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ case CSS1_STRING:
+ if( SvxCSS1Parser::GetEnum( aDirectionTable, pExpr->GetString(),
+ nDir ) )
+ {
+ rItemSet.Put( SvxFrameDirectionItem(
+ static_cast < SvxFrameDirection >( nDir ),
+ aItemIds.nDirection ) );
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+static void MergeHori( SvxGraphicPosition& ePos, SvxGraphicPosition eHori )
+{
+ OSL_ENSURE( GPOS_LT==eHori || GPOS_MT==eHori || GPOS_RT==eHori,
+ "vertical position not at the top" );
+
+ switch( ePos )
+ {
+ case GPOS_LT:
+ case GPOS_MT:
+ case GPOS_RT:
+ ePos = eHori;
+ break;
+
+ case GPOS_LM:
+ case GPOS_MM:
+ case GPOS_RM:
+ ePos = GPOS_LT==eHori ? GPOS_LM : (GPOS_MT==eHori ? GPOS_MM : GPOS_RM);
+ break;
+
+ case GPOS_LB:
+ case GPOS_MB:
+ case GPOS_RB:
+ ePos = GPOS_LT==eHori ? GPOS_LB : (GPOS_MT==eHori ? GPOS_MB : GPOS_RB);
+ break;
+
+ default:
+ ;
+ }
+}
+
+static void MergeVert( SvxGraphicPosition& ePos, SvxGraphicPosition eVert )
+{
+ OSL_ENSURE( GPOS_LT==eVert || GPOS_LM==eVert || GPOS_LB==eVert,
+ "horizontal position not on the left side" );
+
+ switch( ePos )
+ {
+ case GPOS_LT:
+ case GPOS_LM:
+ case GPOS_LB:
+ ePos = eVert;
+ break;
+
+ case GPOS_MT:
+ case GPOS_MM:
+ case GPOS_MB:
+ ePos = GPOS_LT==eVert ? GPOS_MT : (GPOS_LM==eVert ? GPOS_MM : GPOS_MB);
+ break;
+
+ case GPOS_RT:
+ case GPOS_RM:
+ case GPOS_RB:
+ ePos = GPOS_LT==eVert ? GPOS_RT : (GPOS_LM==eVert ? GPOS_RM : GPOS_RB);
+ break;
+
+ default:
+ ;
+ }
+}
+
+static void ParseCSS1_background( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& rParser )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ Color aColor;
+ OUString aURL;
+
+ bool bColor = false, bTransparent = false;
+ SvxGraphicPosition eRepeat = GPOS_TILED;
+ SvxGraphicPosition ePos = GPOS_LT;
+ bool bHori = false, bVert = false;
+
+ while( pExpr && !pExpr->GetOp() )
+ {
+ switch( pExpr->GetType() )
+ {
+ case CSS1_URL:
+ pExpr->GetURL( aURL );
+ break;
+
+ case CSS1_RGB:
+ bColor = pExpr->GetColor( aColor );
+ break;
+
+ case CSS1_LENGTH:
+ case CSS1_PIXLENGTH:
+ {
+ // since we don't know any absolute position, we
+ // only distinguish between 0 and !0. Therefore pixel
+ // can be handled like all other units.
+
+ bool nonZero = std::trunc(pExpr->GetNumber()) != 0.0;
+ if( !bHori )
+ {
+ ePos = nonZero ? GPOS_MM : GPOS_LT;
+ bHori = true;
+ }
+ else if( !bVert )
+ {
+ MergeVert( ePos, (nonZero ? GPOS_LM : GPOS_LT) );
+ bVert = true;
+ }
+ }
+ break;
+
+ case CSS1_PERCENTAGE:
+ {
+ // the percentage is converted to an enum
+
+ sal_uInt16 nPerc = o3tl::narrowing<sal_uInt16>(pExpr->GetNumber());
+ if( !bHori )
+ {
+ ePos = nPerc < 25 ? GPOS_LT
+ : (nPerc < 75 ? GPOS_MM
+ : GPOS_RB);
+ }
+ else if( !bVert )
+ {
+ SvxGraphicPosition eVert =
+ nPerc < 25 ? GPOS_LT: (nPerc < 75 ? GPOS_LM
+ : GPOS_LB);
+ MergeVert( ePos, eVert );
+ }
+ }
+ break;
+
+ case CSS1_IDENT:
+ case CSS1_HEXCOLOR:
+ case CSS1_STRING: // because of MS-IE
+ {
+ sal_uInt16 nEnum;
+ const OUString &rValue = pExpr->GetString();
+ if( rValue.equalsIgnoreAsciiCase( "transparent" ) )
+ {
+ bTransparent = true;
+ }
+ if( SvxCSS1Parser::GetEnum( aBGRepeatTable, rValue, nEnum ) )
+ {
+ eRepeat = static_cast<SvxGraphicPosition>(nEnum);
+ }
+ else if( SvxCSS1Parser::GetEnum( aBGHoriPosTable, rValue, nEnum ) )
+ {
+ // <position>, horizontal
+ MergeHori( ePos, static_cast<SvxGraphicPosition>(nEnum) );
+ }
+ else if( SvxCSS1Parser::GetEnum( aBGVertPosTable, rValue, nEnum ) )
+ {
+ // <position>, vertical
+ MergeVert( ePos, static_cast<SvxGraphicPosition>(nEnum) );
+ }
+ else if( !bColor )
+ {
+ // <color>
+ bColor = pExpr->GetColor( aColor );
+ }
+ // <scroll> we don't know
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ pExpr = pExpr->GetNext();
+ }
+
+ // transparent beats everything
+ if( bTransparent )
+ {
+ bColor = false;
+ aURL.clear();
+ }
+
+ // repeat has priority over a position
+ if( GPOS_NONE == eRepeat )
+ eRepeat = ePos;
+
+ if( !bTransparent && !bColor && aURL.isEmpty() )
+ return;
+
+ SvxBrushItem aBrushItem( aItemIds.nBrush );
+
+ if( bTransparent )
+ aBrushItem.SetColor( COL_TRANSPARENT);
+ else if( bColor )
+ aBrushItem.SetColor( aColor );
+
+ if( !aURL.isEmpty() )
+ {
+ aBrushItem.SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject( rParser.GetBaseURL()), aURL, Link<OUString *, bool>(), false ) );
+ aBrushItem.SetGraphicPos( eRepeat );
+ }
+
+ rItemSet.Put( aBrushItem );
+}
+
+static void ParseCSS1_background_color( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ Color aColor;
+
+ bool bColor = false, bTransparent = false;
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_RGB:
+ bColor = pExpr->GetColor( aColor );
+ break;
+ case CSS1_IDENT:
+ case CSS1_HEXCOLOR:
+ case CSS1_STRING: // because of MS-IE
+ if( pExpr->GetString().equalsIgnoreAsciiCase( "transparent" ) )
+ {
+ bTransparent = true;
+ }
+ else
+ {
+ // <color>
+ bColor = pExpr->GetColor( aColor );
+ }
+ break;
+ default:
+ ;
+ }
+
+ if( bTransparent || bColor )
+ {
+ SvxBrushItem aBrushItem( aItemIds.nBrush );
+
+ if( bTransparent )
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ else if( bColor )
+ aBrushItem.SetColor( aColor);
+
+ rItemSet.Put( aBrushItem );
+ }
+}
+
+static void ParseCSS1_line_height( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ sal_uInt16 nHeight = 0;
+ sal_uInt16 nPropHeight = 0;
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ nHeight = o3tl::narrowing<sal_uInt16>(pExpr->GetULength());
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fHeight = pExpr->GetNumber();
+ if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPHeight = static_cast<tools::Long>(fHeight);
+ tools::Long nPWidth = 0;
+ SvxCSS1Parser::PixelToTwip(nPWidth, nPHeight);
+ nHeight = o3tl::narrowing<sal_uInt16>(nPHeight);
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ {
+ nPropHeight = o3tl::narrowing<sal_uInt16>(pExpr->GetNumber());
+ }
+ break;
+ case CSS1_NUMBER:
+ {
+ nPropHeight = o3tl::narrowing<sal_uInt16>(pExpr->GetNumber() * 100);
+ }
+ break;
+ default:
+ ;
+ }
+
+ if( nHeight )
+ {
+ if( nHeight < SvxCSS1Parser::GetMinFixLineSpace() )
+ nHeight = SvxCSS1Parser::GetMinFixLineSpace();
+ SvxLineSpacingItem aLSItem( nHeight, aItemIds.nLineSpacing );
+ aLSItem.SetLineHeight( nHeight );
+ // interpret <line-height> attribute as minimum line height
+ aLSItem.SetLineSpaceRule( SvxLineSpaceRule::Min );
+ aLSItem.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off );
+ rItemSet.Put( aLSItem );
+ }
+ else if( nPropHeight )
+ {
+ SvxLineSpacingItem aLSItem( nPropHeight, aItemIds.nLineSpacing );
+ aLSItem.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ if( 100 == nPropHeight )
+ aLSItem.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off );
+ else
+ aLSItem.SetPropLineSpace( nPropHeight );
+ rItemSet.Put( aLSItem );
+ }
+
+}
+
+static void ParseCSS1_list_style_type( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ if( pExpr->GetType() != CSS1_IDENT )
+ return;
+
+ const OUString& rValue = pExpr->GetString();
+
+ // values are context-dependent, so fill both
+ sal_uInt16 nEnum;
+ if( SvxCSS1Parser::GetEnum( aNumberStyleTable, rValue, nEnum ) )
+ {
+ rPropInfo.m_bNumbering = true;
+ rPropInfo.m_nNumberingType = static_cast<SvxNumType>(nEnum);
+ }
+ if( SvxCSS1Parser::GetEnum( aBulletStyleTable, rValue, nEnum ) )
+ {
+ rPropInfo.m_bBullet = true;
+ rPropInfo.m_cBulletChar = nEnum;
+ }
+}
+
+static void ParseCSS1_font( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ FontItalic eItalic = ITALIC_NONE;
+ SvxCaseMap eCaseMap = SvxCaseMap::NotMapped;
+ FontWeight eWeight = WEIGHT_NORMAL;
+
+ // [ <font-style> || <font-variant> || <font-weight> ] ?
+ while( pExpr && !pExpr->GetOp() &&
+ (CSS1_IDENT==pExpr->GetType() ||
+ CSS1_STRING==pExpr->GetType() ||
+ CSS1_NUMBER==pExpr->GetType()) )
+ {
+ if( CSS1_IDENT==pExpr->GetType() ||
+ CSS1_STRING==pExpr->GetType() )
+ {
+ const OUString& rValue = pExpr->GetString();
+
+ sal_uInt16 nEnum;
+
+ if( SvxCSS1Parser::GetEnum( aFontStyleTable, rValue, nEnum ) )
+ {
+ eItalic = static_cast<FontItalic>(nEnum);
+ }
+ else if( SvxCSS1Parser::GetEnum( aFontVariantTable, rValue, nEnum ) )
+ {
+ eCaseMap = static_cast<SvxCaseMap>(nEnum);
+ }
+ else if( SvxCSS1Parser::GetEnum( aFontWeightTable, rValue, nEnum ) )
+ {
+ eWeight = static_cast<FontWeight>(nEnum);
+ }
+ }
+ else
+ {
+ eWeight = o3tl::narrowing<sal_uInt16>(pExpr->GetNumber()) > 400 ? WEIGHT_BOLD
+ : WEIGHT_NORMAL;
+ }
+
+ pExpr = pExpr->GetNext();
+ }
+
+ if( !pExpr || pExpr->GetOp() )
+ return;
+
+ // Since "font" resets all values for which nothing is specified,
+ // we do it here.
+ SvxPostureItem aPosture( eItalic, aItemIds.nPosture );
+ rItemSet.Put( aPosture );
+ aPosture.SetWhich( aItemIds.nPostureCJK );
+ rItemSet.Put( aPosture );
+ aPosture.SetWhich( aItemIds.nPostureCTL );
+ rItemSet.Put( aPosture );
+
+ rItemSet.Put( SvxCaseMapItem( eCaseMap, aItemIds.nCaseMap ) );
+
+ SvxWeightItem aWeight( eWeight, aItemIds.nWeight );
+ rItemSet.Put( aWeight );
+ aWeight.SetWhich( aItemIds.nWeightCJK );
+ rItemSet.Put( aWeight );
+ aWeight.SetWhich( aItemIds.nWeightCTL );
+ rItemSet.Put( aWeight );
+
+ // font-size
+ CSS1Expression aExpr( pExpr->GetType(), pExpr->GetString(),
+ pExpr->GetNumber() );
+ ParseCSS1_font_size( &aExpr, rItemSet, rPropInfo, rParser );
+ pExpr = pExpr->GetNext();
+
+ if( !pExpr )
+ return;
+
+ // [ '/' line-height ]?
+ if( '/' == pExpr->GetOp() )
+ {
+ // '/' line-height
+ aExpr.Set( pExpr->GetType(), pExpr->GetString(), pExpr->GetNumber() );
+ ParseCSS1_line_height( &aExpr, rItemSet, rPropInfo, rParser );
+
+ pExpr = pExpr->GetNext();
+ }
+
+ if( !pExpr || pExpr->GetOp() )
+ return;
+
+ // font-family
+ ParseCSS1_font_family( pExpr, rItemSet, rPropInfo, rParser );
+}
+
+static void ParseCSS1_letter_spacing( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ rItemSet.Put( SvxKerningItem( static_cast<short>(pExpr->GetSLength()),
+ aItemIds.nKerning ) );
+ break;
+
+ case CSS1_PIXLENGTH:
+ {
+ double fHeight = pExpr->GetNumber();
+ if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPWidth = static_cast<tools::Long>(fHeight);
+ tools::Long nPHeight = 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ rItemSet.Put( SvxKerningItem( static_cast<short>(nPWidth), aItemIds.nKerning ) );
+ }
+ }
+ break;
+
+ case CSS1_NUMBER:
+ if( pExpr->GetNumber() == 0 )
+ {
+ // normally unnecessary, but we are tolerant
+ rItemSet.Put( SvxKerningItem( short(0), aItemIds.nKerning ) );
+ }
+ break;
+
+ case CSS1_IDENT:
+ case CSS1_STRING: // As a precaution also MS-IE
+ if( pExpr->GetString().equalsIgnoreAsciiCase( "normal" ) )
+ {
+ rItemSet.Put( SvxKerningItem( short(0), aItemIds.nKerning ) );
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+static void ParseCSS1_text_decoration( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ bool bUnderline = false;
+ bool bOverline = false;
+ bool bCrossedOut = false;
+ bool bBlink = false;
+ bool bBlinkOn = false;
+ FontLineStyle eUnderline = LINESTYLE_NONE;
+ FontLineStyle eOverline = LINESTYLE_NONE;
+ FontStrikeout eCrossedOut = STRIKEOUT_NONE;
+
+ // the value can contain two values! And MS-IE also strings
+ while( pExpr && (pExpr->GetType() == CSS1_IDENT ||
+ pExpr->GetType() == CSS1_STRING) && !pExpr->GetOp() )
+ {
+ OUString aValue = pExpr->GetString().toAsciiLowerCase();
+ bool bKnown = false;
+
+ switch( aValue[0] )
+ {
+ case 'n':
+ if( aValue == "none" )
+ {
+ bUnderline = true;
+ eUnderline = LINESTYLE_NONE;
+
+ bOverline = true;
+ eOverline = LINESTYLE_NONE;
+
+ bCrossedOut = true;
+ eCrossedOut = STRIKEOUT_NONE;
+
+ bBlink = true;
+ bBlinkOn = false;
+
+ bKnown = true;
+ }
+ break;
+
+ case 'u':
+ if( aValue == "underline" )
+ {
+ bUnderline = true;
+ eUnderline = LINESTYLE_SINGLE;
+
+ bKnown = true;
+ }
+ break;
+
+ case 'o':
+ if( aValue == "overline" )
+ {
+ bOverline = true;
+ eOverline = LINESTYLE_SINGLE;
+
+ bKnown = true;
+ }
+ break;
+
+ case 'l':
+ if( aValue == "line-through" )
+ {
+ bCrossedOut = true;
+ eCrossedOut = STRIKEOUT_SINGLE;
+
+ bKnown = true;
+ }
+ break;
+
+ case 'b':
+ if( aValue == "blink" )
+ {
+ bBlink = true;
+ bBlinkOn = true;
+
+ bKnown = true;
+ }
+ break;
+ }
+
+ if( !bKnown )
+ {
+ bUnderline = true;
+ eUnderline = LINESTYLE_SINGLE;
+ }
+
+ pExpr = pExpr->GetNext();
+ }
+
+ if( bUnderline )
+ rItemSet.Put( SvxUnderlineItem( eUnderline, aItemIds.nUnderline ) );
+
+ if( bOverline )
+ rItemSet.Put( SvxOverlineItem( eOverline, aItemIds.nOverline ) );
+
+ if( bCrossedOut )
+ rItemSet.Put( SvxCrossedOutItem( eCrossedOut, aItemIds.nCrossedOut ) );
+
+ if( bBlink )
+ rItemSet.Put( SvxBlinkItem( bBlinkOn, aItemIds.nBlink ) );
+}
+
+static void ParseCSS1_text_align( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ if( CSS1_IDENT==pExpr->GetType() ||
+ CSS1_STRING==pExpr->GetType() ) // MS-IE, again
+ {
+ sal_uInt16 nAdjust;
+ if( SvxCSS1Parser::GetEnum( aTextAlignTable, pExpr->GetString(),
+ nAdjust ) )
+ {
+ rItemSet.Put( SvxAdjustItem( static_cast<SvxAdjust>(nAdjust),
+ aItemIds.nAdjust ) );
+ }
+ }
+}
+
+static void ParseCSS1_text_indent( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ short nIndent = 0;
+ bool bSet = false;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ double n = std::round(pExpr->GetNumber());
+ SAL_WARN_IF(
+ n < std::numeric_limits<short>::min() || n > std::numeric_limits<short>::max(),
+ "sw.html", "clamping length " << n << " to short range");
+ nIndent = static_cast<short>(
+ std::clamp(
+ n, double(std::numeric_limits<short>::min()),
+ double(std::numeric_limits<short>::max())));
+ bSet = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fWidth = pExpr->GetNumber();
+ if (fWidth < SAL_MAX_INT32/2.0 && fWidth > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPWidth = static_cast<tools::Long>(fWidth);
+ tools::Long nPHeight = 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ nIndent = static_cast<short>(nPWidth);
+ bSet = true;
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if( !bSet )
+ return;
+
+ SvxFirstLineIndentItem const firstLine(nIndent, RES_MARGIN_FIRSTLINE);
+ rItemSet.Put(firstLine);
+ rPropInfo.m_bTextIndent = true;
+}
+
+static void ParseCSS1_margin_left( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ tools::Long nLeft = 0;
+ bool bSet = false;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ nLeft = pExpr->GetSLength();
+ bSet = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fLeft = pExpr->GetNumber();
+ if (fLeft < SAL_MAX_INT32/2.0 && fLeft > SAL_MIN_INT32/2.0)
+ {
+ nLeft = static_cast<tools::Long>(fLeft);
+ tools::Long nPHeight = 0;
+ SvxCSS1Parser::PixelToTwip( nLeft, nPHeight );
+ bSet = true;
+ }
+ else
+ {
+ SAL_WARN("sw.html", "out-of-size pxlength: " << fLeft);
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if (pExpr->GetString() == "auto")
+ {
+ rPropInfo.m_bLeftMargin = true;
+ rPropInfo.m_eLeftMarginType = SVX_CSS1_LTYPE_AUTO;
+ }
+
+ if( !bSet )
+ return;
+
+ rPropInfo.m_nLeftMargin = nLeft;
+ if( nLeft < 0 )
+ nLeft = 0;
+
+ // TODO: other things may need a SvxLeftMarginItem ? but they currently convert it anyway so they can convert that too.
+ SvxTextLeftMarginItem const leftMargin(o3tl::narrowing<sal_uInt16>(nLeft), RES_MARGIN_TEXTLEFT);
+ rItemSet.Put(leftMargin);
+ rPropInfo.m_bLeftMargin = true;
+}
+
+static void ParseCSS1_margin_right( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ tools::Long nRight = 0;
+ bool bSet = false;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ nRight = pExpr->GetSLength();
+ bSet = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fRight = pExpr->GetNumber();
+ if (fRight < SAL_MAX_INT32/2.0 && fRight > SAL_MIN_INT32/2.0)
+ {
+ nRight = static_cast<tools::Long>(fRight);
+ tools::Long nPHeight = 0;
+ SvxCSS1Parser::PixelToTwip( nRight, nPHeight );
+ bSet = true;
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if (pExpr->GetString() == "auto")
+ {
+ rPropInfo.m_bRightMargin = true;
+ rPropInfo.m_eRightMarginType = SVX_CSS1_LTYPE_AUTO;
+ }
+
+ if( !bSet )
+ return;
+
+ rPropInfo.m_nRightMargin = nRight;
+ if( nRight < 0 )
+ nRight = 0;
+
+ SvxRightMarginItem rightMargin(o3tl::narrowing<sal_uInt16>(nRight), RES_MARGIN_RIGHT);
+ rItemSet.Put(rightMargin);
+ rPropInfo.m_bRightMargin = true;
+}
+
+static void ParseCSS1_margin_top( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ assert(pExpr && "no expression");
+
+ sal_uInt16 nUpper = 0;
+ bool bSet = false;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ tools::Long nTmp = pExpr->GetSLength();
+ if( nTmp < 0 )
+ nTmp = 0;
+ nUpper = o3tl::narrowing<sal_uInt16>(nTmp);
+ bSet = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fHeight = pExpr->GetNumber();
+ if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPWidth = 0;
+ tools::Long nPHeight = static_cast<tools::Long>(fHeight);
+ if( nPHeight < 0 )
+ nPHeight = 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ nUpper = o3tl::narrowing<sal_uInt16>(nPHeight);
+ bSet = true;
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if( !bSet )
+ return;
+
+ if( const SvxULSpaceItem* pItem = rItemSet.GetItemIfSet( aItemIds.nULSpace, false ) )
+ {
+ SvxULSpaceItem aULItem( *pItem );
+ aULItem.SetUpper( nUpper );
+ rItemSet.Put( aULItem );
+ }
+ else
+ {
+ SvxULSpaceItem aULItem( aItemIds.nULSpace );
+ aULItem.SetUpper( nUpper );
+ rItemSet.Put( aULItem );
+ }
+ rPropInfo.m_bTopMargin = true;
+}
+
+static void ParseCSS1_margin_bottom( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ sal_uInt16 nLower = 0;
+ bool bSet = false;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ tools::Long nTmp = pExpr->GetSLength();
+ if( nTmp < 0 )
+ nTmp = 0;
+ nLower = o3tl::narrowing<sal_uInt16>(nTmp);
+ bSet = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fHeight = pExpr->GetNumber();
+ if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPWidth = 0;
+ tools::Long nPHeight = static_cast<tools::Long>(fHeight);
+ if( nPHeight < 0 )
+ nPHeight = 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ nLower = o3tl::narrowing<sal_uInt16>(nPHeight);
+ bSet = true;
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if( !bSet )
+ return;
+
+ if( const SvxULSpaceItem* pItem = rItemSet.GetItemIfSet( aItemIds.nULSpace, false ) )
+ {
+ SvxULSpaceItem aULItem( *pItem );
+ aULItem.SetLower( nLower );
+ rItemSet.Put( aULItem );
+ }
+ else
+ {
+ SvxULSpaceItem aULItem( aItemIds.nULSpace );
+ aULItem.SetLower( nLower );
+ rItemSet.Put( aULItem );
+ }
+ rPropInfo.m_bBottomMargin = true;
+}
+
+static void ParseCSS1_margin( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ tools::Long nMargins[4] = { 0, 0, 0, 0 };
+ bool bSetMargins[4] = { false, false, false, false };
+
+ for( int i=0; pExpr && i<4 && !pExpr->GetOp(); ++i )
+ {
+ bool bSetThis = false;
+ tools::Long nMargin = 0;
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ nMargin = pExpr->GetSLength();
+ bSetThis = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fMargin = pExpr->GetNumber();
+ if (fMargin < SAL_MAX_INT32/2.0 && fMargin > SAL_MIN_INT32/2.0)
+ {
+ nMargin = static_cast<tools::Long>(fMargin);
+ tools::Long nPWidth = 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nMargin );
+ bSetThis = true;
+ }
+ else
+ {
+ SAL_WARN("sw.html", "out-of-size pxlength: " << fMargin);
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if( bSetThis )
+ {
+ // 0 = top
+ // 1 = right
+ // 2 = bottom
+ // 3 = left
+ switch( i )
+ {
+ case 0:
+ nMargins[0] = nMargins[1] =nMargins[2] = nMargins[3] = nMargin;
+ bSetMargins[0] = bSetMargins[1] =
+ bSetMargins[2] = bSetMargins[3] = true;
+ break;
+ case 1:
+ nMargins[1] = nMargins[3] = nMargin; // right + left
+ bSetMargins[1] = bSetMargins[3] = true;
+ break;
+ case 2:
+ nMargins[2] = nMargin; // bottom
+ bSetMargins[2] = true;
+ break;
+ case 3:
+ nMargins[3] = nMargin; // left
+ bSetMargins[3] = true;
+ break;
+ }
+ }
+ pExpr = pExpr->GetNext();
+ }
+
+ if( bSetMargins[3] || bSetMargins[1] )
+ {
+ if( bSetMargins[3] )
+ {
+ rPropInfo.m_bLeftMargin = true;
+ rPropInfo.m_nLeftMargin = nMargins[3];
+ if( nMargins[3] < 0 )
+ nMargins[3] = 0;
+ }
+ if( bSetMargins[1] )
+ {
+ rPropInfo.m_bRightMargin = true;
+ rPropInfo.m_nRightMargin = nMargins[1];
+ if( nMargins[1] < 0 )
+ nMargins[1] = 0;
+ }
+
+ if (bSetMargins[3])
+ {
+ SvxTextLeftMarginItem const leftMargin(o3tl::narrowing<sal_uInt16>(nMargins[3]), RES_MARGIN_TEXTLEFT);
+ rItemSet.Put(leftMargin);
+ }
+ if (bSetMargins[1])
+ {
+ SvxRightMarginItem const rightMargin(o3tl::narrowing<sal_uInt16>(nMargins[1]), RES_MARGIN_RIGHT);
+ rItemSet.Put(rightMargin);
+ }
+ }
+
+ if( !(bSetMargins[0] || bSetMargins[2]) )
+ return;
+
+ if( nMargins[0] < 0 )
+ nMargins[0] = 0;
+ if( nMargins[2] < 0 )
+ nMargins[2] = 0;
+
+ if( const SvxULSpaceItem* pItem = rItemSet.GetItemIfSet( aItemIds.nULSpace, false ) )
+ {
+ SvxULSpaceItem aULItem( *pItem );
+ if( bSetMargins[0] )
+ aULItem.SetUpper( o3tl::narrowing<sal_uInt16>(nMargins[0]) );
+ if( bSetMargins[2] )
+ aULItem.SetLower( o3tl::narrowing<sal_uInt16>(nMargins[2]) );
+ rItemSet.Put( aULItem );
+ }
+ else
+ {
+ SvxULSpaceItem aULItem( aItemIds.nULSpace );
+ if( bSetMargins[0] )
+ aULItem.SetUpper( o3tl::narrowing<sal_uInt16>(nMargins[0]) );
+ if( bSetMargins[2] )
+ aULItem.SetLower( o3tl::narrowing<sal_uInt16>(nMargins[2]) );
+ rItemSet.Put( aULItem );
+ }
+
+ rPropInfo.m_bTopMargin |= bSetMargins[0];
+ rPropInfo.m_bBottomMargin |= bSetMargins[2];
+}
+
+static bool ParseCSS1_padding_xxx( const CSS1Expression *pExpr,
+ SvxCSS1PropertyInfo& rPropInfo,
+ SvxBoxItemLine nWhichLine )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ bool bSet = false;
+ sal_uInt16 nDist = 0;
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_LENGTH:
+ {
+ tools::Long nTmp = pExpr->GetSLength();
+ if( nTmp < 0 )
+ nTmp = 0;
+ else if( nTmp > SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1 )
+ nTmp = SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1;
+ nDist = o3tl::narrowing<sal_uInt16>(nTmp);
+ bSet = true;
+ }
+ break;
+ case CSS1_PIXLENGTH:
+ {
+ double fWidth = pExpr->GetNumber();
+ if (fWidth < SAL_MAX_INT32/2.0 && fWidth > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPWidth = static_cast<tools::Long>(fWidth);
+ tools::Long nPHeight = 0;
+ if( nPWidth < 0 )
+ nPWidth = 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ if( nPWidth > SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1 )
+ nPWidth = SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1;
+ nDist = o3tl::narrowing<sal_uInt16>(nPWidth);
+ bSet = true;
+ }
+ }
+ break;
+ case CSS1_PERCENTAGE:
+ // we aren't able
+ break;
+ default:
+ ;
+ }
+
+ if( bSet )
+ {
+ switch( nWhichLine )
+ {
+ case SvxBoxItemLine::TOP: rPropInfo.m_nTopBorderDistance = nDist; break;
+ case SvxBoxItemLine::BOTTOM: rPropInfo.m_nBottomBorderDistance = nDist;break;
+ case SvxBoxItemLine::LEFT: rPropInfo.m_nLeftBorderDistance = nDist; break;
+ case SvxBoxItemLine::RIGHT: rPropInfo.m_nRightBorderDistance = nDist; break;
+ }
+ }
+
+ return bSet;
+}
+
+static void ParseCSS1_padding_top( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::TOP );
+}
+
+static void ParseCSS1_padding_bottom( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::BOTTOM );
+}
+
+static void ParseCSS1_padding_left( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::LEFT );
+}
+
+static void ParseCSS1_padding_right( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::RIGHT );
+}
+
+static void ParseCSS1_padding( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ int n=0;
+ while( n<4 && pExpr && !pExpr->GetOp() )
+ {
+ SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT;
+ if( ParseCSS1_padding_xxx( pExpr, rPropInfo, nLine ) )
+ {
+ if( n==0 )
+ {
+ rPropInfo.m_nTopBorderDistance = rPropInfo.m_nBottomBorderDistance;
+ rPropInfo.m_nLeftBorderDistance = rPropInfo.m_nTopBorderDistance;
+ }
+ if( n <= 1 )
+ rPropInfo.m_nRightBorderDistance = rPropInfo.m_nLeftBorderDistance;
+ }
+
+ pExpr = pExpr->GetNext();
+ n++;
+ }
+}
+
+static void ParseCSS1_border_xxx( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/,
+ SvxBoxItemLine nWhichLine, bool bAll )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ sal_uInt16 nWidth = USHRT_MAX; // line thickness
+ sal_uInt16 nNWidth = 1; // named line thickness (and default)
+ CSS1BorderStyle eStyle = CSS1_BS_NONE; // line style
+ Color aColor;
+ bool bColor = false;
+
+ while( pExpr && !pExpr->GetOp() )
+ {
+ switch( pExpr->GetType() )
+ {
+ case CSS1_RGB:
+ case CSS1_HEXCOLOR:
+ if( pExpr->GetColor( aColor ) )
+ bColor = true;
+ break;
+
+ case CSS1_IDENT:
+ {
+ const OUString& rValue = pExpr->GetString();
+ sal_uInt16 nValue;
+ if( SvxCSS1Parser::GetEnum( aBorderWidthTable, rValue, nValue ) )
+ {
+ nNWidth = nValue;
+ }
+ else if( SvxCSS1Parser::GetEnum( aBorderStyleTable, rValue, nValue ) )
+ {
+ eStyle = static_cast<CSS1BorderStyle>(nValue);
+ }
+ else if( pExpr->GetColor( aColor ) )
+ {
+ bColor = true;
+ }
+ }
+ break;
+
+ case CSS1_LENGTH:
+ nWidth = o3tl::narrowing<sal_uInt16>(pExpr->GetULength());
+ break;
+
+ case CSS1_PIXLENGTH:
+ {
+ // One Pixel becomes a hairline (is prettier)
+ double fWidth = pExpr->GetNumber();
+ if (fWidth > 1.0 && fWidth < SAL_MAX_INT32/2.0)
+ {
+ bool bHori = nWhichLine == SvxBoxItemLine::TOP ||
+ nWhichLine == SvxBoxItemLine::BOTTOM;
+
+ tools::Long nPWidth = bHori ? 0 : fWidth;
+ tools::Long nPHeight = bHori ? fWidth : 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ nWidth = o3tl::narrowing<sal_uInt16>(bHori ? nPHeight : nPWidth);
+ }
+ else
+ nWidth = 1;
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ pExpr = pExpr->GetNext();
+ }
+
+ for( int i=0; i<4; ++i )
+ {
+ SvxBoxItemLine nLine = SvxBoxItemLine::TOP;
+ switch( i )
+ {
+ case 0: nLine = SvxBoxItemLine::TOP; break;
+ case 1: nLine = SvxBoxItemLine::BOTTOM; break;
+ case 2: nLine = SvxBoxItemLine::LEFT; break;
+ case 3: nLine = SvxBoxItemLine::RIGHT; break;
+ }
+
+ if( bAll || nLine == nWhichLine )
+ {
+ SvxCSS1BorderInfo *pInfo = rPropInfo.GetBorderInfo( nLine );
+ pInfo->eStyle = eStyle;
+ pInfo->nAbsWidth = nWidth;
+ pInfo->nNamedWidth = nNWidth;
+ if( bColor )
+ pInfo->aColor = aColor;
+ }
+ }
+}
+
+static void ParseCSS1_border_xxx_width( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/,
+ SvxBoxItemLine nWhichLine )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ sal_uInt16 nWidth = USHRT_MAX; // line thickness
+ sal_uInt16 nNWidth = 1; // named line thickness (and default)
+
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ {
+ sal_uInt16 nValue;
+ if( SvxCSS1Parser::GetEnum( aBorderWidthTable, pExpr->GetString(), nValue ) )
+ {
+ nNWidth = nValue;
+ }
+ }
+ break;
+
+ case CSS1_LENGTH:
+ nWidth = o3tl::narrowing<sal_uInt16>(pExpr->GetULength());
+ break;
+
+ case CSS1_PIXLENGTH:
+ {
+ double fLength = pExpr->GetNumber();
+ if (fLength < SAL_MAX_INT32/2.0 && fLength > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nWidthL = static_cast<tools::Long>(fLength);
+
+ bool bHori = nWhichLine == SvxBoxItemLine::TOP ||
+ nWhichLine == SvxBoxItemLine::BOTTOM;
+
+ tools::Long nPWidth = bHori ? 0 : nWidthL;
+ tools::Long nPHeight = bHori ? nWidthL : 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ nWidth = o3tl::narrowing<sal_uInt16>(bHori ? nPHeight : nPWidth);
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ SvxCSS1BorderInfo *pInfo = rPropInfo.GetBorderInfo( nWhichLine );
+ pInfo->nAbsWidth = nWidth;
+ pInfo->nNamedWidth = nNWidth;
+}
+
+static void ParseCSS1_border_top_width( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::TOP );
+}
+
+static void ParseCSS1_border_right_width( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::RIGHT );
+}
+
+static void ParseCSS1_border_bottom_width( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::BOTTOM );
+}
+
+static void ParseCSS1_border_left_width( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::LEFT );
+}
+
+static void ParseCSS1_border_width( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ sal_uInt16 n=0;
+ while( n<4 && pExpr && !pExpr->GetOp() )
+ {
+ SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT;
+ ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, nLine );
+ rPropInfo.CopyBorderInfo( n, SVX_CSS1_BORDERINFO_WIDTH );
+
+ pExpr = pExpr->GetNext();
+ n++;
+ }
+}
+
+static void ParseCSS1_border_color( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ sal_uInt16 n=0;
+ while( n<4 && pExpr && !pExpr->GetOp() )
+ {
+ SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT;
+ Color aColor;
+ switch( pExpr->GetType() )
+ {
+ case CSS1_RGB:
+ case CSS1_HEXCOLOR:
+ case CSS1_IDENT:
+ if( pExpr->GetColor( aColor ) )
+ rPropInfo.GetBorderInfo( nLine )->aColor = aColor;
+ break;
+ default:
+ ;
+ }
+ rPropInfo.CopyBorderInfo( n, SVX_CSS1_BORDERINFO_COLOR );
+
+ pExpr = pExpr->GetNext();
+ n++;
+ }
+}
+
+static void ParseCSS1_border_style( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ sal_uInt16 n=0;
+ while( n<4 && pExpr && !pExpr->GetOp() )
+ {
+ SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT;
+ sal_uInt16 nValue = 0;
+ if( CSS1_IDENT==pExpr->GetType() &&
+ SvxCSS1Parser::GetEnum( aBorderStyleTable, pExpr->GetString(),
+ nValue ) )
+ {
+ rPropInfo.GetBorderInfo( nLine )->eStyle = static_cast<CSS1BorderStyle>(nValue);
+ }
+ rPropInfo.CopyBorderInfo( n, SVX_CSS1_BORDERINFO_STYLE );
+
+ pExpr = pExpr->GetNext();
+ n++;
+ }
+}
+
+static void ParseCSS1_border_top( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::TOP, false );
+}
+
+static void ParseCSS1_border_right( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::RIGHT, false );
+}
+
+static void ParseCSS1_border_bottom( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::BOTTOM, false );
+}
+
+static void ParseCSS1_border_left( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::LEFT, false );
+}
+
+static void ParseCSS1_border( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& rParser )
+{
+ ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::TOP, true );
+}
+
+static void ParseCSS1_float( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ if( CSS1_IDENT==pExpr->GetType() )
+ {
+ sal_uInt16 nFloat;
+ if( SvxCSS1Parser::GetEnum( aFloatTable, pExpr->GetString(), nFloat ) )
+ rPropInfo.m_eFloat = static_cast<SvxAdjust>(nFloat);
+ }
+}
+
+static void ParseCSS1_position( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ OSL_ENSURE( pExpr, "no expression" );
+
+ if( CSS1_IDENT==pExpr->GetType() )
+ {
+ sal_uInt16 nPos;
+ if( SvxCSS1Parser::GetEnum( aPositionTable, pExpr->GetString(), nPos ) )
+ rPropInfo.m_ePosition = static_cast<SvxCSS1Position>(nPos);
+ }
+}
+
+static void ParseCSS1_length( const CSS1Expression *pExpr,
+ tools::Long& rLength,
+ SvxCSS1LengthType& rLengthType,
+ bool bHori )
+{
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ if( pExpr->GetString().equalsIgnoreAsciiCase( "auto" ) )
+ {
+ rLength = 0;
+ rLengthType = SVX_CSS1_LTYPE_AUTO;
+ }
+ break;
+
+ case CSS1_LENGTH:
+ rLength = pExpr->GetSLength();
+ rLengthType = SVX_CSS1_LTYPE_TWIP;
+ break;
+
+ case CSS1_PIXLENGTH:
+ case CSS1_NUMBER: // because of Netscape and IE
+ {
+ double fLength = pExpr->GetNumber();
+ if (fLength < SAL_MAX_INT32/2.0 && fLength > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nWidthL = static_cast<tools::Long>(fLength);
+ tools::Long nPWidth = bHori ? 0 : nWidthL;
+ tools::Long nPHeight = bHori ? nWidthL : 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ rLength = (bHori ? nPHeight : nPWidth);
+ rLengthType = SVX_CSS1_LTYPE_TWIP;
+ }
+ }
+ break;
+
+ case CSS1_PERCENTAGE:
+ rLength = static_cast<tools::Long>(std::min(pExpr->GetNumber(), 100.0));
+ rLengthType = SVX_CSS1_LTYPE_PERCENTAGE;
+ break;
+
+ default:
+ ;
+ }
+}
+
+static void ParseCSS1_width( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_length( pExpr, rPropInfo.m_nWidth, rPropInfo.m_eWidthType, true );
+}
+
+static void ParseCSS1_height( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_length( pExpr, rPropInfo.m_nHeight, rPropInfo.m_eHeightType, false );
+}
+
+static void ParseCSS1_left( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_length( pExpr, rPropInfo.m_nLeft, rPropInfo.m_eLeftType, true );
+}
+
+static void ParseCSS1_top( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_length( pExpr, rPropInfo.m_nTop, rPropInfo.m_eTopType, false );
+}
+
+// Feature: PrintExt
+static void ParseCSS1_size( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ int n=0;
+ while( n<2 && pExpr && !pExpr->GetOp() )
+ {
+ switch( pExpr->GetType() )
+ {
+ case CSS1_IDENT:
+ {
+ sal_uInt16 nValue;
+ if( SvxCSS1Parser::GetEnum( aSizeTable, pExpr->GetString(),
+ nValue ) )
+ {
+ rPropInfo.m_eSizeType = static_cast<SvxCSS1SizeType>(nValue);
+ }
+ }
+ break;
+
+ case CSS1_LENGTH:
+ rPropInfo.m_nHeight = pExpr->GetSLength();
+ if( n==0 )
+ rPropInfo.m_nWidth = rPropInfo.m_nHeight;
+ rPropInfo.m_eSizeType = SVX_CSS1_STYPE_TWIP;
+ break;
+
+ case CSS1_PIXLENGTH:
+ {
+ double fHeight = pExpr->GetNumber();
+ if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0)
+ {
+ tools::Long nPHeight = static_cast<tools::Long>(fHeight);
+ tools::Long nPWidth = n==0 ? nPHeight : 0;
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ rPropInfo.m_nHeight = nPHeight;
+ if( n==0 )
+ rPropInfo.m_nWidth = nPWidth;
+ rPropInfo.m_eSizeType = SVX_CSS1_STYPE_TWIP;
+ }
+ break;
+ }
+ default:
+ ;
+ }
+
+ pExpr = pExpr->GetNext();
+ n++;
+ }
+}
+
+static void ParseCSS1_page_break_xxx( const CSS1Expression *pExpr,
+ SvxCSS1PageBreak& rPBreak )
+{
+ if( CSS1_IDENT == pExpr->GetType() )
+ {
+ sal_uInt16 nValue;
+ if( SvxCSS1Parser::GetEnum( aPageBreakTable, pExpr->GetString(),
+ nValue ) )
+ {
+ rPBreak = static_cast<SvxCSS1PageBreak>(nValue);
+ }
+ }
+}
+
+static void ParseCSS1_page_break_before( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_page_break_xxx( pExpr, rPropInfo.m_ePageBreakBefore );
+}
+
+static void ParseCSS1_page_break_after( const CSS1Expression *pExpr,
+ SfxItemSet & /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ ParseCSS1_page_break_xxx( pExpr, rPropInfo.m_ePageBreakAfter );
+}
+
+static void ParseCSS1_page_break_inside( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ SvxCSS1PageBreak eBreak(SVX_CSS1_PBREAK_NONE);
+ ParseCSS1_page_break_xxx( pExpr, eBreak );
+
+ bool bSetSplit = false, bSplit = true;
+ switch( eBreak )
+ {
+ case SVX_CSS1_PBREAK_AUTO:
+ bSetSplit = true;
+ break;
+ case SVX_CSS1_PBREAK_AVOID:
+ bSplit = false;
+ bSetSplit = true;
+ break;
+ default:
+ ;
+ }
+
+ if( bSetSplit )
+ rItemSet.Put( SvxFormatSplitItem( bSplit, aItemIds.nFormatSplit ) );
+}
+
+static void ParseCSS1_widows( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ if( CSS1_NUMBER == pExpr->GetType() )
+ {
+ sal_uInt8 nVal = pExpr->GetNumber() <= 255
+ ? static_cast<sal_uInt8>(pExpr->GetNumber())
+ : 255;
+ SvxWidowsItem aWidowsItem( nVal, aItemIds.nWidows );
+ rItemSet.Put( aWidowsItem );
+ }
+}
+
+static void ParseCSS1_orphans( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ if( CSS1_NUMBER == pExpr->GetType() )
+ {
+ sal_uInt8 nVal = pExpr->GetNumber() <= 255
+ ? static_cast<sal_uInt8>(pExpr->GetNumber())
+ : 255;
+ SvxOrphansItem aOrphansItem( nVal, aItemIds.nOrphans );
+ rItemSet.Put( aOrphansItem );
+ }
+}
+
+static void ParseCSS1_so_language( const CSS1Expression *pExpr,
+ SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo& /*rPropInfo*/,
+ const SvxCSS1Parser& /*rParser*/ )
+{
+ if( CSS1_IDENT != pExpr->GetType() && CSS1_STRING != pExpr->GetType() )
+ return;
+
+ LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( pExpr->GetString() );
+ if( LANGUAGE_DONTKNOW != eLang )
+ {
+ SvxLanguageItem aLang( eLang, aItemIds.nLanguage );
+ rItemSet.Put( aLang );
+ aLang.SetWhich( aItemIds.nLanguageCJK );
+ rItemSet.Put( aLang );
+ aLang.SetWhich( aItemIds.nLanguageCTL );
+ rItemSet.Put( aLang );
+ }
+}
+
+static void ParseCSS1_visibility(const CSS1Expression* pExpr, SfxItemSet& /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo, const SvxCSS1Parser& /*rParser*/)
+{
+ if (pExpr->GetType() != CSS1_IDENT)
+ return;
+
+ rPropInfo.m_bVisible = pExpr->GetString() != "hidden";
+}
+
+static void ParseCSS1_white_space(const CSS1Expression* pExpr, SfxItemSet& /*rItemSet*/,
+ SvxCSS1PropertyInfo& rPropInfo, const SvxCSS1Parser& /*rParser*/)
+{
+ if (pExpr->GetType() == CSS1_IDENT)
+ {
+ if (pExpr->GetString().equalsIgnoreAsciiCase("pre")
+ || pExpr->GetString().equalsIgnoreAsciiCase("pre-wrap"))
+ {
+ rPropInfo.m_bPreserveSpace = true;
+ }
+ }
+}
+
+namespace {
+
+// the assignment of property to parsing function
+struct CSS1PropEntry
+{
+ std::string_view pName;
+ FnParseCSS1Prop pFunc;
+};
+
+}
+
+// the table with assignments
+CSS1PropEntry constexpr aCSS1PropFnTab[] =
+{
+ { sCSS1_P_background, ParseCSS1_background },
+ { sCSS1_P_background_color, ParseCSS1_background_color },
+ { sCSS1_P_border, ParseCSS1_border },
+ { sCSS1_P_border_bottom, ParseCSS1_border_bottom },
+ { sCSS1_P_border_bottom_width, ParseCSS1_border_bottom_width },
+ { sCSS1_P_border_color, ParseCSS1_border_color },
+ { sCSS1_P_border_left, ParseCSS1_border_left },
+ { sCSS1_P_border_left_width, ParseCSS1_border_left_width },
+ { sCSS1_P_border_right, ParseCSS1_border_right },
+ { sCSS1_P_border_right_width, ParseCSS1_border_right_width },
+ { sCSS1_P_border_style, ParseCSS1_border_style },
+ { sCSS1_P_border_top, ParseCSS1_border_top },
+ { sCSS1_P_border_top_width, ParseCSS1_border_top_width },
+ { sCSS1_P_border_width, ParseCSS1_border_width },
+ { sCSS1_P_color, ParseCSS1_color },
+ { sCSS1_P_column_count, ParseCSS1_column_count },
+ { sCSS1_P_direction, ParseCSS1_direction },
+ { sCSS1_P_float, ParseCSS1_float },
+ { sCSS1_P_font, ParseCSS1_font },
+ { sCSS1_P_font_family, ParseCSS1_font_family },
+ { sCSS1_P_font_size, ParseCSS1_font_size },
+ { sCSS1_P_font_style, ParseCSS1_font_style },
+ { sCSS1_P_font_variant, ParseCSS1_font_variant },
+ { sCSS1_P_font_weight, ParseCSS1_font_weight },
+ { sCSS1_P_height, ParseCSS1_height },
+ { sCSS1_P_left, ParseCSS1_left },
+ { sCSS1_P_letter_spacing, ParseCSS1_letter_spacing },
+ { sCSS1_P_line_height, ParseCSS1_line_height },
+ { sCSS1_P_list_style_type, ParseCSS1_list_style_type },
+ { sCSS1_P_margin, ParseCSS1_margin },
+ { sCSS1_P_margin_bottom, ParseCSS1_margin_bottom },
+ { sCSS1_P_margin_left, ParseCSS1_margin_left },
+ { sCSS1_P_margin_right, ParseCSS1_margin_right },
+ { sCSS1_P_margin_top, ParseCSS1_margin_top },
+ { sCSS1_P_orphans, ParseCSS1_orphans },
+ { sCSS1_P_padding, ParseCSS1_padding },
+ { sCSS1_P_padding_bottom, ParseCSS1_padding_bottom },
+ { sCSS1_P_padding_left, ParseCSS1_padding_left },
+ { sCSS1_P_padding_right, ParseCSS1_padding_right },
+ { sCSS1_P_padding_top, ParseCSS1_padding_top },
+ { sCSS1_P_page_break_after, ParseCSS1_page_break_after },
+ { sCSS1_P_page_break_before, ParseCSS1_page_break_before },
+ { sCSS1_P_page_break_inside, ParseCSS1_page_break_inside },
+ { sCSS1_P_position, ParseCSS1_position },
+ { sCSS1_P_size, ParseCSS1_size },
+ { sCSS1_P_so_language, ParseCSS1_so_language },
+ { sCSS1_P_text_align, ParseCSS1_text_align },
+ { sCSS1_P_text_decoration, ParseCSS1_text_decoration },
+ { sCSS1_P_text_indent, ParseCSS1_text_indent },
+ { sCSS1_P_text_transform, ParseCSS1_text_transform },
+ { sCSS1_P_top, ParseCSS1_top },
+ { sCSS1_P_visibility, ParseCSS1_visibility },
+ { sCSS1_white_space, ParseCSS1_white_space },
+ { sCSS1_P_widows, ParseCSS1_widows },
+ { sCSS1_P_width, ParseCSS1_width },
+};
+
+static_assert(std::is_sorted(std::begin(aCSS1PropFnTab), std::end(aCSS1PropFnTab),
+ [](const auto& lhs, const auto& rhs) constexpr
+ { return lhs.pName < rhs.pName; }));
+
+static bool CSS1PropEntryFindCompare(CSS1PropEntry const & lhs, OUString const & s)
+{
+ return s.compareToIgnoreAsciiCaseAscii(lhs.pName) > 0;
+}
+
+void SvxCSS1Parser::DeclarationParsed( const OUString& rProperty,
+ std::unique_ptr<CSS1Expression> pExpr )
+{
+ OSL_ENSURE( m_pItemSet, "DeclarationParsed() without ItemSet" );
+
+ auto it = std::lower_bound( std::begin(aCSS1PropFnTab), std::end(aCSS1PropFnTab), rProperty,
+ CSS1PropEntryFindCompare );
+ if( it != std::end(aCSS1PropFnTab) && !CSS1PropEntryFindCompare(*it,rProperty) )
+ {
+ it->pFunc( pExpr.get(), *m_pItemSet, *m_pPropInfo, *this );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/svxcss1.hxx b/sw/source/filter/html/svxcss1.hxx
new file mode 100644
index 0000000000..669ed92a5b
--- /dev/null
+++ b/sw/source/filter/html/svxcss1.hxx
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_SVXCSS1_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_SVXCSS1_HXX
+
+#include <svl/itemset.hxx>
+#include <editeng/svxenum.hxx>
+#include <rtl/textenc.h>
+#include "parcss1.hxx"
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <array>
+#include <map>
+#include <memory>
+#include <vector>
+
+class SfxItemPool;
+class SvxBoxItem;
+class FontList;
+enum class SvxBoxItemLine;
+
+enum SvxCSS1Position
+{
+ SVX_CSS1_POS_NONE, // nothing specified
+ SVX_CSS1_POS_STATIC, // normal
+ SVX_CSS1_POS_ABSOLUTE, // absolute
+ SVX_CSS1_POS_RELATIVE, // relative
+};
+
+enum SvxCSS1LengthType
+{
+ SVX_CSS1_LTYPE_NONE, // nothing specified
+ SVX_CSS1_LTYPE_AUTO, // automatic
+ SVX_CSS1_LTYPE_TWIP, // twip
+ SVX_CSS1_LTYPE_PERCENTAGE, // percentage value
+};
+
+// Feature: PrintExt
+enum SvxCSS1SizeType
+{
+ SVX_CSS1_STYPE_NONE, // nothing specified
+ SVX_CSS1_STYPE_AUTO, // automatic
+ SVX_CSS1_STYPE_TWIP, // twip
+ SVX_CSS1_STYPE_LANDSCAPE, // landscape
+ SVX_CSS1_STYPE_PORTRAIT, // portrait
+};
+
+enum SvxCSS1PageBreak
+{
+ SVX_CSS1_PBREAK_NONE, // nothing specified
+ SVX_CSS1_PBREAK_AUTO, // automatic
+ SVX_CSS1_PBREAK_ALWAYS, // always
+ SVX_CSS1_PBREAK_AVOID, // never
+ SVX_CSS1_PBREAK_LEFT, // next page is a left one
+ SVX_CSS1_PBREAK_RIGHT, // next page is a right one
+};
+
+
+enum class Css1ScriptFlags {
+ Western = 0x01,
+ CJK = 0x02,
+ CTL = 0x04,
+ AllMask = Western | CJK | CTL,
+};
+namespace o3tl {
+ template<> struct typed_flags<Css1ScriptFlags> : is_typed_flags<Css1ScriptFlags, 0x07> {};
+}
+
+struct CSS1PropertyEnum
+{
+ const char *pName; // property value
+ sal_uInt16 nEnum; // and the corresponding value of enum
+};
+
+namespace editeng { class SvxBorderLine; }
+
+#define SVX_CSS1_BORDERINFO_WIDTH 1
+#define SVX_CSS1_BORDERINFO_COLOR 2
+#define SVX_CSS1_BORDERINFO_STYLE 4
+
+struct SvxCSS1BorderInfo;
+class SvxCSS1PropertyInfo
+{
+ std::array<std::unique_ptr<SvxCSS1BorderInfo>,4> m_aBorderInfos;
+
+ void DestroyBorderInfos();
+
+public:
+ static constexpr sal_uInt16 UNSET_BORDER_DISTANCE = SAL_MAX_UINT16;
+
+ OUString m_aId; // ID for bookmarks, frame, and so
+
+ bool m_bTopMargin : 1;
+ bool m_bBottomMargin : 1;
+
+ bool m_bLeftMargin : 1;
+ bool m_bRightMargin : 1;
+ bool m_bTextIndent : 1;
+ bool m_bNumbering : 1;
+ bool m_bBullet : 1;
+ bool m_bPreserveSpace : 1 = false;
+
+ SvxAdjust m_eFloat;
+
+ SvxCSS1Position m_ePosition;
+
+ sal_uInt16 m_nTopBorderDistance;
+ sal_uInt16 m_nBottomBorderDistance;
+ sal_uInt16 m_nLeftBorderDistance;
+ sal_uInt16 m_nRightBorderDistance;
+
+ SvxNumType m_nNumberingType;
+ sal_Unicode m_cBulletChar;
+
+ sal_uInt16 m_nColumnCount;
+
+ tools::Long m_nLeft, m_nTop;
+ tools::Long m_nWidth, m_nHeight;
+ tools::Long m_nLeftMargin, m_nRightMargin;
+
+ SvxCSS1LengthType m_eLeftType, m_eTopType;
+ SvxCSS1LengthType m_eWidthType, m_eHeightType;
+ SvxCSS1LengthType m_eLeftMarginType;
+ SvxCSS1LengthType m_eRightMarginType;
+
+ SvxCSS1SizeType m_eSizeType;
+
+ SvxCSS1PageBreak m_ePageBreakBefore;
+ SvxCSS1PageBreak m_ePageBreakAfter;
+
+ bool m_bVisible = true;
+
+ SvxCSS1PropertyInfo();
+ SvxCSS1PropertyInfo( const SvxCSS1PropertyInfo& rProp );
+ ~SvxCSS1PropertyInfo();
+
+ void Merge( const SvxCSS1PropertyInfo& rProp );
+
+ void Clear();
+
+ SvxCSS1BorderInfo *GetBorderInfo( SvxBoxItemLine nLine, bool bCreate=true );
+ void CopyBorderInfo( SvxBoxItemLine nSrcLine, SvxBoxItemLine nDstLine, sal_uInt16 nWhat );
+ void CopyBorderInfo( sal_uInt16 nCount, sal_uInt16 nWhat );
+
+ void SetBoxItem( SfxItemSet& rItemSet, sal_uInt16 nMinBorderDist,
+ const SvxBoxItem* pDflt=nullptr );
+
+};
+
+class SvxCSS1MapEntry
+{
+ SfxItemSet m_aItemSet;
+ SvxCSS1PropertyInfo m_aPropInfo;
+
+public:
+ SvxCSS1MapEntry( SfxItemSet aItemSet,
+ const SvxCSS1PropertyInfo& rProp );
+
+ const SfxItemSet& GetItemSet() const { return m_aItemSet; }
+ SfxItemSet& GetItemSet() { return m_aItemSet; }
+
+ const SvxCSS1PropertyInfo& GetPropertyInfo() const { return m_aPropInfo; }
+ SvxCSS1PropertyInfo& GetPropertyInfo() { return m_aPropInfo; }
+};
+
+// Class is processing the CSS1-Parser output by converting the CSS1 properties
+// into SvxItem(Set). Also the selectors together with associated ItemSet are
+// saved.
+// A derived parser can suppress this for certain selectors by overriding
+// the method StyleParsed.
+
+class SvxCSS1Parser : public CSS1Parser
+{
+ typedef std::vector<std::unique_ptr<CSS1Selector>> CSS1Selectors;
+ typedef std::map<OUString, std::unique_ptr<SvxCSS1MapEntry>> CSS1Map;
+ CSS1Selectors m_Selectors; // List of "open" Selectors
+
+ CSS1Map m_Ids;
+ CSS1Map m_Classes;
+ CSS1Map m_Pages;
+ CSS1Map m_Tags;
+
+ OUString m_sBaseURL;
+
+ std::unique_ptr<SfxItemSet> m_pSheetItemSet; // item set of Style-Sheet
+ SfxItemSet *m_pItemSet; // current item set
+
+ std::unique_ptr<SvxCSS1PropertyInfo> m_pSheetPropInfo;
+ SvxCSS1PropertyInfo *m_pPropInfo;
+
+ // minimum spacing for fixed line spacing
+ static constexpr sal_uInt16 gnMinFixLineSpace = o3tl::toTwips(25, o3tl::Length::mm10);
+
+ rtl_TextEncoding m_eDefaultEnc;
+ bool m_bIgnoreFontFamily;
+ WhichRangesContainer m_aWhichMap; // Which-Map of Parser
+
+ using CSS1Parser::ParseStyleOption;
+
+protected:
+
+ using CSS1Parser::ParseStyleSheet;
+
+ // This method is called for every selector with according item set.
+ // For a selector multiple calls are possible.
+ // If true is returned then the item set resp. the selector isn't saved anymore!
+ // The ItemSet may be modified accordingly!
+ // The implementation returns false.
+ virtual void StyleParsed( const CSS1Selector *pSelector,
+ SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo );
+
+ /// Will be called when a Selector is parsed. If bFirst is true,
+ /// the content of the aItemSet will be copied into all recently
+ /// created Styles.
+ /// Derived classes should not override this method!
+ virtual void SelectorParsed( std::unique_ptr<CSS1Selector> pSelector, bool bFirst ) override;
+
+ /// Will be called for every parsed Property. Adds the item to the
+ /// pItemSet.
+ /// Derived classes should not override this method!
+ virtual void DeclarationParsed( const OUString& rProperty,
+ std::unique_ptr<CSS1Expression> pExpr ) override;
+
+public:
+
+ SvxCSS1Parser( SfxItemPool& rPool,
+ OUString aBaseURL,
+ sal_uInt16 const *pWhichIds, sal_uInt16 nWhichIds );
+ virtual ~SvxCSS1Parser() override;
+
+ bool IsIgnoreFontFamily() const { return m_bIgnoreFontFamily; }
+ void SetIgnoreFontFamily( bool bSet ) { m_bIgnoreFontFamily = bSet; }
+
+ // Parse a style sheet. For every found selector a StyleParsed with
+ // according item set is called.
+ virtual bool ParseStyleSheet( const OUString& rIn );
+
+ // Parse style option. Here only the item set is filled.
+ void ParseStyleOption( const OUString& rIn, SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo );
+
+ // convert a string to enum value
+ static bool GetEnum( const CSS1PropertyEnum *pPropTable,
+ std::u16string_view rValue, sal_uInt16 &rEnum );
+
+ static void PixelToTwip( tools::Long &nWidth, tools::Long &nHeight );
+
+ // determine the font height of a certain font size (0-6)
+ virtual sal_uInt32 GetFontHeight( sal_uInt16 nSize ) const;
+
+ virtual const FontList *GetFontList() const;
+
+ const WhichRangesContainer& GetWhichMap() const { return m_aWhichMap; }
+
+ static void InsertMapEntry( const OUString& rKey, const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp, CSS1Map& rMap );
+
+ void InsertId( const OUString& rId, const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp );
+
+ const SvxCSS1MapEntry* GetId( const OUString& rId ) const;
+
+ void InsertClass( const OUString& rClass, const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp );
+
+ const SvxCSS1MapEntry* GetClass( const OUString& rClass ) const;
+
+ void InsertPage( const OUString& rPage, bool bPseudo,
+ const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp );
+
+ SvxCSS1MapEntry* GetPage( const OUString& rPage, bool bPseudo );
+
+ void InsertTag( const OUString& rTag, const SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rProp );
+
+ SvxCSS1MapEntry* GetTag( const OUString& rTag );
+
+ static void MergeStyles( const SfxItemSet& rSrcSet,
+ const SvxCSS1PropertyInfo& rSrcInfo,
+ SfxItemSet& rTargetSet,
+ SvxCSS1PropertyInfo& rTargetInfo,
+ bool bSmart );
+
+ static sal_uInt16 GetMinFixLineSpace() { return gnMinFixLineSpace; }
+
+ virtual void SetDfltEncoding( rtl_TextEncoding eEnc );
+ rtl_TextEncoding GetDfltEncoding() const { return m_eDefaultEnc; }
+
+ const OUString& GetBaseURL() const { return m_sBaseURL;}
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/swcss1.hxx b/sw/source/filter/html/swcss1.hxx
new file mode 100644
index 0000000000..9d930f6bfc
--- /dev/null
+++ b/sw/source/filter/html/swcss1.hxx
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_SWCSS1_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_SWCSS1_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <svtools/htmltokn.h>
+#include <tools/solar.h>
+
+#include <poolfmt.hxx>
+
+#include "svxcss1.hxx"
+
+class SwDoc;
+class SwCharFormat;
+class SwTextFormatColl;
+class SvxBrushItem;
+class SwFormatDrop;
+class SwPageDesc;
+class SwHTMLParser;
+
+// This header looks harmless, but includes still quite
+// inconspicuous one or the other! On the other hand this class
+// is rarely needed. Therefore its own header.
+
+class SwCSS1Parser : public SvxCSS1Parser
+{
+ SwDoc *m_pDoc;
+ SwHTMLParser const& m_rHTMLParser;
+
+ sal_uLong m_aFontHeights[7];
+
+ sal_uInt16 m_nDropCapCnt;
+
+ bool m_bIsNewDoc : 1;
+
+ bool m_bBodyBGColorSet : 1;
+ bool m_bBodyBackgroundSet : 1;
+ bool m_bBodyTextSet : 1;
+ bool m_bBodyLinkSet : 1;
+ bool m_bBodyVLinkSet : 1;
+
+ bool m_bSetFirstPageDesc : 1;
+ bool m_bSetRightPageDesc : 1;
+
+ bool m_bTableHeaderTextCollSet : 1;
+ bool m_bTableTextCollSet : 1;
+
+ bool m_bLinkCharFormatsSet : 1;
+
+ const SwPageDesc* GetPageDesc( sal_uInt16 nPoolId, bool bCreate );
+
+ void SetTableTextColl( bool bHeader );
+ void SetLinkCharFormats();
+
+protected:
+ virtual void StyleParsed( const CSS1Selector *pSelector,
+ SfxItemSet& rItemSet,
+ SvxCSS1PropertyInfo& rPropInfo ) override;
+
+ using CSS1Parser::ParseStyleSheet;
+
+public:
+ SwCSS1Parser( SwDoc *pDoc, SwHTMLParser const& rParser,
+ sal_uInt32 const aFHeight[7], const OUString& rBaseURL, bool bNewDoc);
+ virtual ~SwCSS1Parser() override;
+
+ virtual bool ParseStyleSheet( const OUString& rIn ) override;
+
+ // determine font height for a certain font size (0-6)
+ virtual sal_uInt32 GetFontHeight( sal_uInt16 nSize ) const override;
+
+ // fetch current font list (also zero is allowed)
+ virtual const FontList *GetFontList() const override;
+
+ // determine the character format of a token and a maybe empty class
+ SwCharFormat* GetChrFormat( HtmlTokenId nToken, const OUString& rClass ) const;
+
+ // determine a TextFormatColl of a Pool-Id
+ SwTextFormatColl *GetTextFormatColl( sal_uInt16 nTextColl, const OUString& rClass );
+
+ // This methods do the same as the one of SwDoc, but change the
+ // encoding if required.
+ SwTextFormatColl *GetTextCollFromPool( sal_uInt16 nPoolId ) const;
+ SwCharFormat *GetCharFormatFromPool( sal_uInt16 nPoolId ) const;
+
+ // Fetch the left or right page style. In documents with only
+ // one style there is only a right page.
+ // Otherwise the right page is the HTML pool style and the left
+ // page a user style which is created on-demand if bCreate is set.
+ SwPageDesc* GetMasterPageDesc();
+ inline const SwPageDesc* GetFirstPageDesc( bool bCreate=false );
+ inline const SwPageDesc* GetRightPageDesc( bool bCreate=false );
+ inline const SwPageDesc* GetLeftPageDesc( bool bCreate=false );
+
+ // Set attributes on the HTML page style (set attributes are
+ // deleted from the Item-Set). Is called for the BODY tag.
+ void SetPageDescAttrs( const SvxBrushItem *pBrush,
+ SfxItemSet *pItemSet=nullptr );
+
+ void ChgPageDesc( const SwPageDesc *pPageDesc,
+ const SwPageDesc& rNewPageDesc );
+
+ // Is called for @page
+ void SetPageDescAttrs( const SwPageDesc *pPageDesc, SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rPropInfo );
+
+ // Fill a DropCap attribute
+ void FillDropCap( SwFormatDrop& rDrop, SfxItemSet& rItemSet,
+ const OUString *pName=nullptr );
+
+ bool SetFormatBreak( SfxItemSet& rItemSet,
+ const SvxCSS1PropertyInfo& rPropInfo );
+
+ static void AddClassName( OUString& rFormatName, std::u16string_view rClass );
+
+ static bool MayBePositioned( const SvxCSS1PropertyInfo& rPropInfo,
+ bool bAutoWidth=false );
+
+ static Css1ScriptFlags GetScriptFromClass( OUString& rClass,
+ bool bSubClassOnly = true );
+
+ bool IsBodyBGColorSet() const { return m_bBodyBGColorSet; }
+ bool IsBodyBackgroundSet() const { return m_bBodyBackgroundSet; }
+ bool IsBodyTextSet() const { return m_bBodyTextSet; }
+ bool IsBodyLinkSet() const { return m_bBodyLinkSet; }
+ bool IsBodyVLinkSet() const { return m_bBodyVLinkSet; }
+
+ bool IsSetFirstPageDesc() const { return m_bSetFirstPageDesc; }
+ bool IsSetRightPageDesc() const { return m_bSetRightPageDesc; }
+
+ void SetBodyBGColorSet() { m_bBodyBGColorSet = true; }
+ void SetBodyBackgroundSet() { m_bBodyBackgroundSet = true; }
+ void SetBodyTextSet() { m_bBodyTextSet = true; }
+ void SetBodyLinkSet() { m_bBodyLinkSet = true; }
+ void SetBodyVLinkSet() { m_bBodyVLinkSet = true; }
+
+ std::unique_ptr<SvxBrushItem> makePageDescBackground() const;
+
+ inline void SetTHTagStyles();
+ inline void SetTDTagStyles();
+ inline void SetATagStyles();
+ inline void SetDelayedStyles();
+
+ virtual void SetDfltEncoding( rtl_TextEncoding eEnc ) override;
+};
+
+inline const SwPageDesc* SwCSS1Parser::GetFirstPageDesc( bool bCreate )
+{
+ return GetPageDesc( RES_POOLPAGE_FIRST, bCreate );
+}
+
+inline const SwPageDesc* SwCSS1Parser::GetRightPageDesc( bool bCreate )
+{
+ return GetPageDesc( RES_POOLPAGE_RIGHT, bCreate );
+}
+
+inline const SwPageDesc* SwCSS1Parser::GetLeftPageDesc( bool bCreate )
+{
+ return GetPageDesc( RES_POOLPAGE_LEFT, bCreate );
+}
+
+inline void SwCSS1Parser::SetTHTagStyles()
+{
+ if( !m_bTableHeaderTextCollSet )
+ SetTableTextColl( true );
+}
+
+inline void SwCSS1Parser::SetTDTagStyles()
+{
+ if( !m_bTableTextCollSet )
+ SetTableTextColl( false );
+}
+
+inline void SwCSS1Parser::SetATagStyles()
+{
+ if( !m_bLinkCharFormatsSet )
+ SetLinkCharFormats();
+}
+
+inline void SwCSS1Parser::SetDelayedStyles()
+{
+ SetTHTagStyles();
+ SetTDTagStyles();
+ SetATagStyles();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx
new file mode 100644
index 0000000000..072d6a2b23
--- /dev/null
+++ b/sw/source/filter/html/swhtml.cxx
@@ -0,0 +1,5648 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <memory>
+#include <config_java.h>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <comphelper/string.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <svx/svxids.hrc>
+#include <svx/svdotext.hxx>
+#if OSL_DEBUG_LEVEL > 0
+#include <stdlib.h>
+#endif
+#include <hintids.hxx>
+
+#include <utility>
+#include <vcl/errinf.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/imap.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/ctrltool.hxx>
+#include <unotools/pathoptions.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <sfx2/linkmgr.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <svx/unobrushitemhelper.hxx>
+
+#include <frmatr.hxx>
+#include <charatr.hxx>
+#include <fmtfld.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfsize.hxx>
+#include <fmtclds.hxx>
+#include <fchrfmt.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentState.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <mdiexp.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <IMark.hxx>
+#include <docsh.hxx>
+#include <editsh.hxx>
+#include <docufld.hxx>
+#include "swcss1.hxx"
+#include <fltini.hxx>
+#include <htmltbl.hxx>
+#include "htmlnum.hxx"
+#include "swhtml.hxx"
+#include "wrthtml.hxx"
+#include <linkenum.hxx>
+#include <breakit.hxx>
+#include <SwAppletImpl.hxx>
+#include <swdll.hxx>
+#include <txatbase.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <svx/svdobj.hxx>
+#include <officecfg/Office/Writer.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/sequence.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <swerror.h>
+#include <ndole.hxx>
+#include <unoframe.hxx>
+#include "css1atr.hxx"
+#include <frameformats.hxx>
+
+#define FONTSIZE_MASK 7
+
+#define HTML_ESC_PROP 80
+#define HTML_ESC_SUPER DFLT_ESC_SUPER
+#define HTML_ESC_SUB DFLT_ESC_SUB
+
+#define HTML_SPTYPE_BLOCK 1
+#define HTML_SPTYPE_HORI 2
+#define HTML_SPTYPE_VERT 3
+
+using editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+// <P ALIGN=xxx>, <Hn ALIGN=xxx>, <TD ALIGN=xxx> etc.
+HTMLOptionEnum<SvxAdjust> const aHTMLPAlignTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_AL_left, SvxAdjust::Left },
+ { OOO_STRING_SVTOOLS_HTML_AL_center, SvxAdjust::Center },
+ { OOO_STRING_SVTOOLS_HTML_AL_middle, SvxAdjust::Center }, // Netscape
+ { OOO_STRING_SVTOOLS_HTML_AL_right, SvxAdjust::Right },
+ { OOO_STRING_SVTOOLS_HTML_AL_justify, SvxAdjust::Block },
+ { OOO_STRING_SVTOOLS_HTML_AL_char, SvxAdjust::Left },
+ { nullptr, SvxAdjust(0) }
+};
+
+// <SPACER TYPE=...>
+HTMLOptionEnum<sal_uInt16> const aHTMLSpacerTypeTable[] =
+{
+ { OOO_STRING_SVTOOLS_HTML_SPTYPE_block, HTML_SPTYPE_BLOCK },
+ { OOO_STRING_SVTOOLS_HTML_SPTYPE_horizontal, HTML_SPTYPE_HORI },
+ { OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical, HTML_SPTYPE_VERT },
+ { nullptr, 0 }
+};
+
+HTMLReader::HTMLReader()
+{
+ m_bTemplateBrowseMode = true;
+}
+
+OUString HTMLReader::GetTemplateName(SwDoc& rDoc) const
+{
+ if (!rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))
+ // HTML import into Writer, avoid loading the Writer/Web template.
+ return OUString();
+
+ static constexpr OUString sTemplateWithoutExt(u"internal/html"_ustr);
+ SvtPathOptions aPathOpt;
+
+ // first search for OpenDocument Writer/Web template
+ // OpenDocument Writer/Web template (extension .oth)
+ OUString sTemplate( sTemplateWithoutExt + ".oth" );
+ if (aPathOpt.SearchFile( sTemplate, SvtPathOptions::Paths::Template ))
+ return sTemplate;
+
+ // no OpenDocument Writer/Web template found.
+ // search for OpenOffice.org Writer/Web template
+ sTemplate = sTemplateWithoutExt + ".stw";
+ if (aPathOpt.SearchFile( sTemplate, SvtPathOptions::Paths::Template ))
+ return sTemplate;
+
+ OSL_ENSURE( false, "The default HTML template cannot be found in the defined template directories!");
+
+ return OUString();
+}
+
+bool HTMLReader::SetStrmStgPtr()
+{
+ OSL_ENSURE( m_pMedium, "Where is the medium??" );
+
+ if( m_pMedium->IsRemote() || !m_pMedium->IsStorage() )
+ {
+ m_pStream = m_pMedium->GetInStream();
+ return true;
+ }
+ return false;
+
+}
+
+// Call for the general Reader-Interface
+ErrCodeMsg HTMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPam, const OUString & rName )
+{
+ SetupFilterOptions();
+
+ if( !m_pStream )
+ {
+ OSL_ENSURE( m_pStream, "HTML-Read without stream" );
+ return ERR_SWG_READ_ERROR;
+ }
+
+ if( !m_bInsertMode )
+ {
+ Reader::ResetFrameFormats( rDoc );
+
+ // Set the HTML page style, when it isn't a HTML document,
+ // otherwise it's already set.
+ if( !rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && m_aNamespace != "reqif-xhtml" )
+ {
+ rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, SwFormatPageDesc(
+ rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false )) );
+ }
+ }
+
+ // so nobody steals the document!
+ rtl::Reference<SwDoc> xHoldAlive(&rDoc);
+ ErrCodeMsg nRet = ERRCODE_NONE;
+ tools::SvRef<SwHTMLParser> xParser = new SwHTMLParser( &rDoc, rPam, *m_pStream,
+ rName, rBaseURL, !m_bInsertMode, m_pMedium,
+ IsReadUTF8(),
+ m_bIgnoreHTMLComments, m_aNamespace );
+
+ SvParserState eState = xParser->CallParser();
+
+ if( SvParserState::Pending == eState )
+ m_pStream->ResetError();
+ else if( SvParserState::Accepted != eState )
+ {
+ const OUString sErr(OUString::number(static_cast<sal_Int32>(xParser->GetLineNr()))
+ + "," + OUString::number(static_cast<sal_Int32>(xParser->GetLinePos())));
+
+ // use the stream as transport for error number
+ nRet = ErrCodeMsg( ERR_FORMAT_ROWCOL, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+
+ return nRet;
+}
+
+SwHTMLParser::SwHTMLParser( SwDoc* pD, SwPaM& rCursor, SvStream& rIn,
+ OUString aPath,
+ OUString aBaseURL,
+ bool bReadNewDoc,
+ SfxMedium* pMed, bool bReadUTF8,
+ bool bNoHTMLComments,
+ const OUString& rNamespace )
+ : SfxHTMLParser( rIn, bReadNewDoc, pMed ),
+ m_aPathToFile(std::move( aPath )),
+ m_sBaseURL(std::move( aBaseURL )),
+ m_xAttrTab(std::make_shared<HTMLAttrTable>()),
+ m_pNumRuleInfo( new SwHTMLNumRuleInfo ),
+ m_xDoc( pD ),
+ m_pActionViewShell( nullptr ),
+ m_pSttNdIdx( nullptr ),
+ m_pFormImpl( nullptr ),
+ m_pImageMap( nullptr ),
+ m_nBaseFontStMin( 0 ),
+ m_nFontStMin( 0 ),
+ m_nDefListDeep( 0 ),
+ m_nFontStHeadStart( 0 ),
+ m_nSBModuleCnt( 0 ),
+ m_nMissingImgMaps( 0 ),
+ m_nParaCnt( 5 ),
+ // #i83625#
+ m_nContextStMin( 0 ),
+ m_nContextStAttrMin( 0 ),
+ m_nSelectEntryCnt( 0 ),
+ m_nOpenParaToken( HtmlTokenId::NONE ),
+ m_eJumpTo( JumpToMarks::NONE ),
+#ifdef DBG_UTIL
+ m_nContinue( 0 ),
+#endif
+ m_eParaAdjust( SvxAdjust::End ),
+ m_bDocInitialized( false ),
+ m_bSetModEnabled( false ),
+ m_bInFloatingFrame( false ),
+ m_bInField( false ),
+ m_bKeepUnknown( false ),
+ m_bCallNextToken( false ),
+ m_bIgnoreRawData( false ),
+ m_bLBEntrySelected ( false ),
+ m_bTAIgnoreNewPara ( false ),
+ m_bFixMarqueeWidth ( false ),
+ m_bNoParSpace( false ),
+ m_bInNoEmbed( false ),
+ m_bInTitle( false ),
+ m_bUpdateDocStat( false ),
+ m_bFixSelectWidth( false ),
+ m_bTextArea( false ),
+ m_bSelect( false ),
+ m_bInFootEndNoteAnchor( false ),
+ m_bInFootEndNoteSymbol( false ),
+ m_bIgnoreHTMLComments( bNoHTMLComments ),
+ m_bRemoveHidden( false ),
+ m_bBodySeen( false ),
+ m_bReadingHeaderOrFooter( false ),
+ m_bNotifyMacroEventRead( false ),
+ m_isInTableStructure(false),
+ m_nTableDepth( 0 ),
+ m_nFloatingFrames( 0 ),
+ m_nListItems( 0 ),
+ m_pTempViewFrame(nullptr)
+{
+ // If requested explicitly, then force ignoring of comments (don't create postits for them).
+ if (!bFuzzing)
+ {
+ if (officecfg::Office::Writer::Filter::Import::HTML::IgnoreComments::get())
+ m_bIgnoreHTMLComments = true;
+ m_bKeepUnknown = officecfg::Office::Common::Filter::HTML::Import::UnknownTag::get();
+ }
+
+ m_nEventId = nullptr;
+ m_bUpperSpace = m_bViewCreated = m_bChkJumpMark = false;
+
+ m_eScriptLang = HTMLScriptLanguage::Unknown;
+
+ rCursor.DeleteMark();
+ m_pPam = &rCursor; // re-use existing cursor: avoids spurious ~SwContentIndexReg assert
+ memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable));
+
+ // Read the font sizes 1-7 from the INI file
+ if (!bFuzzing)
+ {
+ m_aFontHeights[0] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_1::get() * 20;
+ m_aFontHeights[1] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_2::get() * 20;
+ m_aFontHeights[2] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_3::get() * 20;
+ m_aFontHeights[3] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_4::get() * 20;
+ m_aFontHeights[4] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_5::get() * 20;
+ m_aFontHeights[5] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_6::get() * 20;
+ m_aFontHeights[6] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_7::get() * 20;
+ }
+ else
+ {
+ m_aFontHeights[0] = m_aFontHeights[1] = m_aFontHeights[2] = m_aFontHeights[3] =
+ m_aFontHeights[4] = m_aFontHeights[5] = m_aFontHeights[6] = 12 * 20;
+ }
+
+ if(bReadNewDoc)
+ {
+ //CJK has different defaults, so a different object should be used for this
+ //RES_CHARTR_CJK_FONTSIZE is a valid value
+ SvxFontHeightItem aFontHeight(m_aFontHeights[2], 100, RES_CHRATR_FONTSIZE);
+ m_xDoc->SetDefault( aFontHeight );
+ SvxFontHeightItem aFontHeightCJK(m_aFontHeights[2], 100, RES_CHRATR_CJK_FONTSIZE);
+ m_xDoc->SetDefault( aFontHeightCJK );
+ SvxFontHeightItem aFontHeightCTL(m_aFontHeights[2], 100, RES_CHRATR_CTL_FONTSIZE);
+ m_xDoc->SetDefault( aFontHeightCTL );
+
+ // #i18732# - adjust default of option 'FollowTextFlow'
+ // TODO: not sure what the appropriate default for HTML should be?
+ m_xDoc->SetDefault( SwFormatFollowTextFlow(true) );
+ }
+
+ // Change to HTML mode during the import, so that the right styles are created
+ m_bOldIsHTMLMode = m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
+ m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true);
+
+ m_pCSS1Parser.reset(new SwCSS1Parser(m_xDoc.get(), *this, m_aFontHeights, m_sBaseURL, IsNewDoc()));
+ if (!bFuzzing)
+ m_pCSS1Parser->SetIgnoreFontFamily( officecfg::Office::Common::Filter::HTML::Import::FontSetting::get() );
+
+ if( bReadUTF8 )
+ {
+ SetSrcEncoding( RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ {
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ SvKeyValueIterator *pHeaderAttrs =
+ pDocSh->GetHeaderAttributes();
+ if( pHeaderAttrs )
+ SetEncodingByHTTPHeader( pHeaderAttrs );
+ }
+ m_pCSS1Parser->SetDfltEncoding( osl_getThreadTextEncoding() );
+
+ SwDocShell* pDocSh = m_xDoc->GetDocShell();
+ if( pDocSh )
+ {
+ m_bViewCreated = true; // not, load synchronous
+
+ // a jump mark is present
+
+ if( pMed )
+ {
+ m_sJmpMark = pMed->GetURLObject().GetMark();
+ if( !m_sJmpMark.isEmpty() )
+ {
+ m_eJumpTo = JumpToMarks::Mark;
+ sal_Int32 nLastPos = m_sJmpMark.lastIndexOf( cMarkSeparator );
+ sal_Int32 nPos = nLastPos != -1 ? nLastPos : 0;
+
+ OUString sCmp;
+ if (nPos)
+ {
+ sCmp = m_sJmpMark.copy(nPos + 1).replaceAll(" ", "");
+ }
+
+ if( !sCmp.isEmpty() )
+ {
+ sCmp = sCmp.toAsciiLowerCase();
+ if( sCmp == "region" )
+ m_eJumpTo = JumpToMarks::Region;
+ else if( sCmp == "table" )
+ m_eJumpTo = JumpToMarks::Table;
+ else if( sCmp == "graphic" )
+ m_eJumpTo = JumpToMarks::Graphic;
+ else if( sCmp == "outline" ||
+ sCmp == "text" ||
+ sCmp == "frame" )
+ m_eJumpTo = JumpToMarks::NONE; // this is nothing valid!
+ else
+ // otherwise this is a normal (book)mark
+ nPos = -1;
+ }
+ else
+ nPos = -1;
+
+ if( nPos != -1 )
+ m_sJmpMark = m_sJmpMark.copy( 0, nPos );
+ if( m_sJmpMark.isEmpty() )
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+ }
+ }
+
+ if (!rNamespace.isEmpty())
+ {
+ SetNamespace(rNamespace);
+ m_bXHTML = true;
+ if (rNamespace == "reqif-xhtml")
+ m_bReqIF = true;
+ }
+
+ // Extract load parameters which are specific to this filter.
+ if (!pMed)
+ {
+ return;
+ }
+
+ comphelper::SequenceAsHashMap aLoadMap(pMed->GetArgs());
+ auto it = aLoadMap.find("AllowedRTFOLEMimeTypes");
+ if (it == aLoadMap.end())
+ {
+ return;
+ }
+
+ uno::Sequence<OUString> aTypes;
+ it->second >>= aTypes;
+ m_aAllowedRTFOLEMimeTypes = comphelper::sequenceToContainer<std::set<OUString>>(aTypes);
+}
+
+SwHTMLParser::~SwHTMLParser()
+{
+#ifdef DBG_UTIL
+ OSL_ENSURE( !m_nContinue, "DTOR in continue!" );
+#endif
+
+ OSL_ENSURE(m_aContexts.empty(), "There are still contexts on the stack");
+ OSL_ENSURE(!m_nContextStMin, "There are protected contexts");
+ m_nContextStMin = 0;
+ while (!m_aContexts.empty())
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ ClearContext(xCntxt.get());
+ }
+
+ bool bAsync = m_xDoc->IsInLoadAsynchron();
+ m_xDoc->SetInLoadAsynchron( false );
+ m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, m_bOldIsHTMLMode);
+
+ if( m_xDoc->GetDocShell() && m_nEventId )
+ Application::RemoveUserEvent( m_nEventId );
+
+ // the DocumentDetected maybe can delete the DocShells, therefore fetch again
+ if( m_xDoc->GetDocShell() )
+ {
+ // update linked sections
+ sal_uInt16 nLinkMode = m_xDoc->getIDocumentSettingAccess().getLinkUpdateMode( true );
+ if( nLinkMode != NEVER && bAsync &&
+ SfxObjectCreateMode::INTERNAL!=m_xDoc->GetDocShell()->GetCreateMode() )
+ m_xDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks( nLinkMode == MANUAL, false, nullptr );
+
+ if ( m_xDoc->GetDocShell()->IsLoading() )
+ {
+ // #i59688#
+ m_xDoc->GetDocShell()->LoadingFinished();
+ }
+ }
+
+ delete m_pSttNdIdx;
+
+ if( !m_aSetAttrTab.empty() )
+ {
+ OSL_ENSURE( m_aSetAttrTab.empty(),"There are still attributes on the stack" );
+ for ( const auto& rpAttr : m_aSetAttrTab )
+ delete rpAttr;
+ m_aSetAttrTab.clear();
+ }
+
+ m_pCSS1Parser.reset();
+ m_pNumRuleInfo.reset();
+ DeleteFormImpl();
+ m_pFootEndNoteImpl.reset();
+
+ OSL_ENSURE(!m_xTable, "It exists still an open table");
+ m_pImageMaps.reset();
+
+ OSL_ENSURE( m_vPendingStack.empty(),
+ "SwHTMLParser::~SwHTMLParser: Here should not be Pending-Stack anymore" );
+ m_vPendingStack.clear();
+
+ m_xDoc.clear();
+
+ if ( m_pTempViewFrame )
+ {
+ m_pTempViewFrame->DoClose();
+
+ // the temporary view frame is hidden, so the hidden flag might need to be removed
+ if ( m_bRemoveHidden && m_xDoc.is() && m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->GetMedium() )
+ m_xDoc->GetDocShell()->GetMedium()->GetItemSet().ClearItem( SID_HIDDEN );
+ }
+}
+
+IMPL_LINK_NOARG( SwHTMLParser, AsyncCallback, void*, void )
+{
+ m_nEventId=nullptr;
+
+ // #i47907# - If the document has already been destructed,
+ // the parser should be aware of this:
+ if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
+ || 1 == m_xDoc->getReferenceCount() )
+ {
+ // was the import aborted by SFX?
+ eState = SvParserState::Error;
+ }
+
+ GetAsynchCallLink().Call(nullptr);
+}
+
+SvParserState SwHTMLParser::CallParser()
+{
+ // create temporary index on position 0, so it won't be moved!
+ m_pSttNdIdx = new SwNodeIndex( m_xDoc->GetNodes() );
+ if( !IsNewDoc() ) // insert into existing document ?
+ {
+ const SwPosition* pPos = m_pPam->GetPoint();
+
+ m_xDoc->getIDocumentContentOperations().SplitNode( *pPos, false );
+
+ *m_pSttNdIdx = pPos->GetNodeIndex()-1;
+ m_xDoc->getIDocumentContentOperations().SplitNode( *pPos, false );
+
+ SwPaM aInsertionRangePam( *pPos );
+
+ m_pPam->Move( fnMoveBackward );
+
+ // split any redline over the insertion point
+ aInsertionRangePam.SetMark();
+ *aInsertionRangePam.GetPoint() = *m_pPam->GetPoint();
+ aInsertionRangePam.Move( fnMoveBackward );
+ m_xDoc->getIDocumentRedlineAccess().SplitRedline( aInsertionRangePam );
+
+ m_xDoc->SetTextFormatColl( *m_pPam,
+ m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+ }
+
+ if( GetMedium() )
+ {
+ if( !m_bViewCreated )
+ {
+ m_nEventId = Application::PostUserEvent( LINK( this, SwHTMLParser, AsyncCallback ) );
+ }
+ else
+ {
+ m_bViewCreated = true;
+ m_nEventId = nullptr;
+ }
+ }
+ else // show progress bar
+ {
+ rInput.Seek(STREAM_SEEK_TO_END);
+ rInput.ResetError();
+
+ m_xProgress.reset(new ImportProgress(m_xDoc->GetDocShell(), 0, rInput.Tell()));
+
+ rInput.Seek(STREAM_SEEK_TO_BEGIN);
+ rInput.ResetError();
+ }
+
+ StartListening(m_xDoc->GetPageDesc( 0 ).GetNotifier());
+
+ SvParserState eRet = HTMLParser::CallParser();
+ return eRet;
+}
+
+bool SwHTMLParser::CanRemoveNode(SwNodeOffset nNodeIdx) const
+{
+ const SwNode *pPrev = m_xDoc->GetNodes()[nNodeIdx - 1];
+ return pPrev->IsContentNode() || (pPrev->IsEndNode() && pPrev->StartOfSectionNode()->IsSectionNode());
+}
+
+void SwHTMLParser::Continue( HtmlTokenId nToken )
+{
+#ifdef DBG_UTIL
+ OSL_ENSURE(!m_nContinue, "Continue in Continue - not supposed to happen");
+ m_nContinue++;
+#endif
+
+ // When the import (of SFX) is aborted, an error will be set but
+ // we still continue, so that we clean up properly.
+ OSL_ENSURE( SvParserState::Error!=eState,
+ "SwHTMLParser::Continue: already set an error" );
+ if( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
+ eState = SvParserState::Error;
+
+ // Fetch SwViewShell from document, save it and set as current.
+ SwViewShell *pInitVSh = CallStartAction();
+
+ if( SvParserState::Error != eState && GetMedium() && !m_bViewCreated )
+ {
+ // At first call first return, show document and wait for callback
+ // time.
+ // At this point in CallParser only one digit was read and
+ // a SaveState(0) was called.
+ eState = SvParserState::Pending;
+ m_bViewCreated = true;
+ m_xDoc->SetInLoadAsynchron( true );
+
+#ifdef DBG_UTIL
+ m_nContinue--;
+#endif
+
+ return;
+ }
+
+ m_bSetModEnabled = false;
+ if( m_xDoc->GetDocShell() )
+ {
+ m_bSetModEnabled = m_xDoc->GetDocShell()->IsEnableSetModified();
+ if( m_bSetModEnabled )
+ {
+ m_xDoc->GetDocShell()->EnableSetModified( false );
+ }
+ }
+
+ // during import don't call OLE-Modified
+ Link<bool,void> aOLELink( m_xDoc->GetOle2Link() );
+ m_xDoc->SetOle2Link( Link<bool,void>() );
+
+ bool bModified = m_xDoc->getIDocumentState().IsModified();
+ bool const bWasUndo = m_xDoc->GetIDocumentUndoRedo().DoesUndo();
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
+
+ // When the import will be aborted, don't call Continue anymore.
+ // If a Pending-Stack exists make sure the stack is ended with a call
+ // of NextToken.
+ if( SvParserState::Error == eState )
+ {
+ OSL_ENSURE( m_vPendingStack.empty() || m_vPendingStack.back().nToken != HtmlTokenId::NONE,
+ "SwHTMLParser::Continue: Pending-Stack without Token" );
+ if( !m_vPendingStack.empty() && m_vPendingStack.back().nToken != HtmlTokenId::NONE )
+ NextToken( m_vPendingStack.back().nToken );
+ OSL_ENSURE( m_vPendingStack.empty(),
+ "SwHTMLParser::Continue: There is again a Pending-Stack" );
+ }
+ else
+ {
+ HTMLParser::Continue( !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : nToken );
+ }
+
+ // disable progress bar again
+ m_xProgress.reset();
+
+ bool bLFStripped = false;
+ if( SvParserState::Pending != GetStatus() )
+ {
+ // set the last attributes yet
+ {
+ if( !m_aScriptSource.isEmpty() )
+ {
+ SwScriptFieldType *pType =
+ static_cast<SwScriptFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Script ));
+
+ SwScriptField aField( pType, m_aScriptType, m_aScriptSource,
+ false );
+ InsertAttr( SwFormatField( aField ), false );
+ }
+
+ if( m_pAppletImpl )
+ {
+ if( m_pAppletImpl->GetApplet().is() )
+ EndApplet();
+ else
+ EndObject();
+ }
+
+ // maybe remove an existing LF after the last paragraph
+ if( IsNewDoc() )
+ bLFStripped = StripTrailingLF() > 0;
+
+ // close still open numbering
+ while( GetNumInfo().GetNumRule() )
+ EndNumberBulletList();
+
+ OSL_ENSURE( !m_nContextStMin, "There are protected contexts" );
+ // try this twice, first normally to let m_nContextStMin decrease
+ // naturally and get contexts popped in desired order, and if that
+ // fails force it
+ for (int i = 0; i < 2; ++i)
+ {
+ while (m_aContexts.size() > m_nContextStMin)
+ {
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
+ if (xCntxt)
+ EndContext(xCntxt.get());
+ }
+ if (!m_nContextStMin)
+ break;
+ OSL_ENSURE(!m_nContextStMin, "There are still protected contexts");
+ m_nContextStMin = 0;
+ }
+
+ m_aParaAttrs.clear();
+
+ SetAttr( false );
+
+ // set the first delayed styles
+ m_pCSS1Parser->SetDelayedStyles();
+ }
+
+ // again correct the start
+ if( !IsNewDoc() && m_pSttNdIdx->GetIndex() )
+ {
+ SwTextNode* pTextNode = m_pSttNdIdx->GetNode().GetTextNode();
+ SwNodeIndex aNxtIdx( *m_pSttNdIdx );
+ if( pTextNode && pTextNode->CanJoinNext( &aNxtIdx ))
+ {
+ const sal_Int32 nStt = pTextNode->GetText().getLength();
+ // when the cursor is still in the node, then set him at the end
+ if( m_pPam->GetPoint()->GetNode() == aNxtIdx.GetNode() )
+ {
+ m_pPam->GetPoint()->Assign( *pTextNode, nStt );
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+// !!! shouldn't be possible, or ??
+ OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != m_pPam->GetBound().GetNodeIndex(),
+ "Pam.Bound1 is still in the node" );
+ OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != m_pPam->GetBound( false ).GetNodeIndex(),
+ "Pam.Bound2 is still in the node" );
+
+ if( m_pSttNdIdx->GetIndex()+1 == m_pPam->GetBound().GetNodeIndex() )
+ {
+ const sal_Int32 nCntPos = m_pPam->GetBound().GetContentIndex();
+ m_pPam->GetBound().SetContent(
+ pTextNode->GetText().getLength() + nCntPos );
+ }
+ if( m_pSttNdIdx->GetIndex()+1 == m_pPam->GetBound( false ).GetNodeIndex() )
+ {
+ const sal_Int32 nCntPos = m_pPam->GetBound( false ).GetContentIndex();
+ m_pPam->GetBound( false ).SetContent(
+ pTextNode->GetText().getLength() + nCntPos );
+ }
+#endif
+ // Keep character attribute!
+ SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode();
+ if (pTextNode->GetText().getLength())
+ pDelNd->FormatToTextAttr( pTextNode );
+ else
+ pTextNode->ChgFormatColl( pDelNd->GetTextColl() );
+ pTextNode->JoinNext();
+ }
+ }
+ }
+
+ if( SvParserState::Accepted == eState )
+ {
+ if( m_nMissingImgMaps )
+ {
+ // Some Image-Map relations are still missing.
+ // Maybe now the Image-Maps are there?
+ ConnectImageMaps();
+ }
+
+ // now remove the last useless paragraph
+ SwPosition* pPos = m_pPam->GetPoint();
+ if( !pPos->GetContentIndex() && !bLFStripped )
+ {
+ SwTextNode* pCurrentNd;
+ SwNodeOffset nNodeIdx = pPos->GetNodeIndex();
+
+ bool bHasFlysOrMarks =
+ HasCurrentParaFlys() || HasCurrentParaBookmarks( true );
+
+ if( IsNewDoc() )
+ {
+ if (!m_pPam->GetPoint()->GetContentIndex() && CanRemoveNode(nNodeIdx))
+ {
+ SwContentNode* pCNd = m_pPam->GetPointContentNode();
+ if( pCNd && pCNd->StartOfSectionIndex()+2 <
+ pCNd->EndOfSectionIndex() && !bHasFlysOrMarks )
+ {
+ SwViewShell *pVSh = CheckActionViewShell();
+ SwCursorShell *pCursorSh = dynamic_cast<SwCursorShell *>( pVSh );
+ if( pCursorSh &&
+ pCursorSh->GetCursor()->GetPoint()
+ ->GetNodeIndex() == nNodeIdx )
+ {
+ pCursorSh->MovePara(GoPrevPara, fnParaEnd );
+ pCursorSh->SetMark();
+ pCursorSh->ClearMark();
+ }
+ SwNode& rDelNode = m_pPam->GetPoint()->GetNode();
+ // move so we don't have a dangling SwContentIndex to the deleted node
+ m_pPam->GetPoint()->Adjust(SwNodeOffset(1));
+ if (m_pPam->HasMark())
+ m_pPam->GetMark()->Adjust(SwNodeOffset(1));
+ m_xDoc->GetNodes().Delete( rDelNode );
+ }
+ }
+ }
+ else if( nullptr != ( pCurrentNd = m_xDoc->GetNodes()[ nNodeIdx ]->GetTextNode()) && !bHasFlysOrMarks )
+ {
+ if( pCurrentNd->CanJoinNext( pPos ))
+ {
+ SwTextNode* pNextNd = pPos->GetNode().GetTextNode();
+ m_pPam->SetMark(); m_pPam->DeleteMark();
+ pNextNd->JoinPrev();
+ }
+ else if (pCurrentNd->GetText().isEmpty())
+ {
+ m_pPam->SetMark(); m_pPam->DeleteMark();
+ SwNode& rDelNode = pPos->GetNode();
+ // move so we don't have a dangling SwContentIndex to the deleted node
+ m_pPam->GetPoint()->Adjust(SwNodeOffset(+1));
+ m_xDoc->GetNodes().Delete( rDelNode );
+ m_pPam->Move( fnMoveBackward );
+ }
+ }
+ }
+
+ // annul the SplitNode from the beginning
+ else if( !IsNewDoc() )
+ {
+ if( pPos->GetContentIndex() ) // then there was no <p> at the end
+ m_pPam->Move( fnMoveForward, GoInNode ); // therefore to the next
+ SwTextNode* pTextNode = pPos->GetNode().GetTextNode();
+ SwNodeIndex aPrvIdx( pPos->GetNode() );
+ if( pTextNode && pTextNode->CanJoinPrev( &aPrvIdx ) &&
+ *m_pSttNdIdx <= aPrvIdx )
+ {
+ // Normally here should take place a JoinNext, but all cursors and
+ // so are registered in pTextNode, so that it MUST remain.
+
+ // Convert paragraph to character attribute, from Prev adopt
+ // the paragraph attribute and the template!
+ SwTextNode* pPrev = aPrvIdx.GetNode().GetTextNode();
+ pTextNode->ChgFormatColl( pPrev->GetTextColl() );
+ pTextNode->FormatToTextAttr( pPrev );
+ pTextNode->ResetAllAttr();
+
+ if( pPrev->HasSwAttrSet() )
+ pTextNode->SetAttr( *pPrev->GetpSwAttrSet() );
+
+ if( &m_pPam->GetBound().GetNode() == pPrev )
+ m_pPam->GetBound().nContent.Assign( pTextNode, 0 );
+ if( &m_pPam->GetBound(false).GetNode() == pPrev )
+ m_pPam->GetBound(false).nContent.Assign( pTextNode, 0 );
+
+ pTextNode->JoinPrev();
+ }
+ }
+
+ // adjust AutoLoad in DocumentProperties
+ if (!bFuzzing && IsNewDoc())
+ {
+ SwDocShell *pDocShell(m_xDoc->GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell) {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
+ if ( xDocProps.is() && (xDocProps->getAutoloadSecs() > 0) &&
+ (xDocProps->getAutoloadURL().isEmpty()) )
+ {
+ xDocProps->setAutoloadURL(m_aPathToFile);
+ }
+ }
+ }
+
+ if( m_bUpdateDocStat )
+ {
+ m_xDoc->getIDocumentStatistics().UpdateDocStat( false, true );
+ }
+ }
+
+ if( SvParserState::Pending != GetStatus() )
+ {
+ delete m_pSttNdIdx;
+ m_pSttNdIdx = nullptr;
+ }
+
+ // should the parser be the last one who hold the document, then nothing
+ // has to be done anymore, document will be destroyed shortly!
+ if( 1 < m_xDoc->getReferenceCount() )
+ {
+ if( bWasUndo )
+ {
+ m_xDoc->GetIDocumentUndoRedo().DelAllUndoObj();
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(true);
+ }
+ else if( !pInitVSh )
+ {
+ // When at the beginning of Continue no Shell was available,
+ // it's possible in the meantime one was created.
+ // In that case the bWasUndo flag is wrong and we must
+ // enable Undo.
+ SwViewShell *pTmpVSh = CheckActionViewShell();
+ if( pTmpVSh )
+ {
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(true);
+ }
+ }
+
+ m_xDoc->SetOle2Link( aOLELink );
+ if( !bModified )
+ m_xDoc->getIDocumentState().ResetModified();
+ if( m_bSetModEnabled && m_xDoc->GetDocShell() )
+ {
+ m_xDoc->GetDocShell()->EnableSetModified();
+ m_bSetModEnabled = false; // this is unnecessary here
+ }
+ }
+
+ // When the Document-SwVievShell still exists and an Action is open
+ // (doesn't have to be by abort), end the Action, disconnect from Shell
+ // and finally reconstruct the old Shell.
+ CallEndAction( true );
+
+#ifdef DBG_UTIL
+ m_nContinue--;
+#endif
+}
+
+void SwHTMLParser::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListeningAll();
+ ReleaseRef();
+ }
+}
+
+void SwHTMLParser::DocumentDetected()
+{
+ OSL_ENSURE( !m_bDocInitialized, "DocumentDetected called multiple times" );
+ m_bDocInitialized = true;
+ if( IsNewDoc() )
+ {
+ if( IsInHeader() )
+ FinishHeader();
+
+ CallEndAction( true );
+
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
+ // For DocumentDetected in general a SwViewShell is created.
+ // But it also can be created later, in case the UI is captured.
+ CallStartAction();
+ }
+}
+
+// is called for every token that is recognised in CallParser
+void SwHTMLParser::NextToken( HtmlTokenId nToken )
+{
+ if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
+ || 1 == m_xDoc->getReferenceCount() )
+ {
+ // Was the import cancelled by SFX? If a pending stack
+ // exists, clean it.
+ eState = SvParserState::Error;
+ OSL_ENSURE( m_vPendingStack.empty() || m_vPendingStack.back().nToken != HtmlTokenId::NONE,
+ "SwHTMLParser::NextToken: Pending-Stack without token" );
+ if( 1 == m_xDoc->getReferenceCount() || m_vPendingStack.empty() )
+ return ;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ if( !m_vPendingStack.empty() )
+ {
+ switch( nToken )
+ {
+ // tables are read by recursive method calls
+ case HtmlTokenId::TABLE_ON:
+ // For CSS declarations we might have to wait
+ // for a file download to finish
+ case HtmlTokenId::LINK:
+ // For controls we might have to set the size.
+ case HtmlTokenId::INPUT:
+ case HtmlTokenId::TEXTAREA_ON:
+ case HtmlTokenId::SELECT_ON:
+ case HtmlTokenId::SELECT_OFF:
+ break;
+ default:
+ OSL_ENSURE( m_vPendingStack.empty(), "Unknown token for Pending-Stack" );
+ break;
+ }
+ }
+#endif
+
+ // The following special cases have to be treated before the
+ // filter detection, because Netscape doesn't reference the content
+ // of the title for filter detection either.
+ if( m_vPendingStack.empty() )
+ {
+ if( m_bInTitle )
+ {
+ switch( nToken )
+ {
+ case HtmlTokenId::TITLE_OFF:
+ {
+ OUString sTitle = m_sTitle.makeStringAndClear();
+ if( IsNewDoc() && !sTitle.isEmpty() )
+ {
+ if( m_xDoc->GetDocShell() ) {
+ uno::Reference<document::XDocumentPropertiesSupplier>
+ xDPS(m_xDoc->GetDocShell()->GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "no DocumentProperties");
+ if (xDocProps.is()) {
+ xDocProps->setTitle(sTitle);
+ }
+
+ m_xDoc->GetDocShell()->SetTitle(sTitle);
+ }
+ }
+ m_bInTitle = false;
+ break;
+ }
+
+ case HtmlTokenId::NONBREAKSPACE:
+ m_sTitle.append(" ");
+ break;
+
+ case HtmlTokenId::SOFTHYPH:
+ m_sTitle.append("-");
+ break;
+
+ case HtmlTokenId::TEXTTOKEN:
+ m_sTitle.append(aToken);
+ break;
+
+ default:
+ m_sTitle.append("<");
+ if( (nToken >= HtmlTokenId::ONOFF_START) && isOffToken(nToken) )
+ m_sTitle.append("/");
+ m_sTitle.append(sSaveToken);
+ if( !aToken.isEmpty() )
+ {
+ m_sTitle.append(" ");
+ m_sTitle.append(aToken);
+ }
+ m_sTitle.append(">");
+ break;
+ }
+
+ return;
+ }
+ }
+
+ // Find out what type of document it is if we don't know already.
+ // For Controls this has to be finished before the control is inserted
+ // because for inserting a View is needed.
+ if( !m_bDocInitialized )
+ DocumentDetected();
+
+ bool bGetIDOption = false, bInsertUnknown = false;
+ bool bUpperSpaceSave = m_bUpperSpace;
+ m_bUpperSpace = false;
+
+ // The following special cases may or have to be treated after the
+ // filter detection
+ if( m_vPendingStack.empty() )
+ {
+ if( m_bInFloatingFrame )
+ {
+ // <SCRIPT> is ignored here (from us), because it is ignored in
+ // Applets as well
+ if( HtmlTokenId::IFRAME_OFF == nToken )
+ {
+ m_bCallNextToken = false;
+ m_bInFloatingFrame = false;
+ }
+
+ return;
+ }
+ else if( m_bInNoEmbed )
+ {
+ switch( nToken )
+ {
+ case HtmlTokenId::NOEMBED_OFF:
+ m_aContents = convertLineEnd(m_aContents, GetSystemLineEnd());
+ InsertComment( m_aContents, OOO_STRING_SVTOOLS_HTML_noembed );
+ m_aContents.clear();
+ m_bCallNextToken = false;
+ m_bInNoEmbed = false;
+ break;
+
+ case HtmlTokenId::RAWDATA:
+ InsertCommentText( OOO_STRING_SVTOOLS_HTML_noembed );
+ break;
+
+ default:
+ OSL_ENSURE( false, "SwHTMLParser::NextToken: invalid tag" );
+ break;
+ }
+
+ return;
+ }
+ else if( m_pAppletImpl )
+ {
+ // in an applet only <PARAM> tags and the </APPLET> tag
+ // are of interest for us (for the moment)
+ // <SCRIPT> is ignored here (from Netscape)!
+
+ switch( nToken )
+ {
+ case HtmlTokenId::APPLET_OFF:
+ m_bCallNextToken = false;
+ EndApplet();
+ break;
+ case HtmlTokenId::OBJECT_OFF:
+ m_bCallNextToken = false;
+ EndObject();
+ break;
+ case HtmlTokenId::PARAM:
+ InsertParam();
+ break;
+ default: break;
+ }
+
+ return;
+ }
+ else if( m_bTextArea )
+ {
+ // in a TextArea everything up to </TEXTAREA> is inserted as text.
+ // <SCRIPT> is ignored here (from Netscape)!
+
+ switch( nToken )
+ {
+ case HtmlTokenId::TEXTAREA_OFF:
+ m_bCallNextToken = false;
+ EndTextArea();
+ break;
+
+ default:
+ InsertTextAreaText( nToken );
+ break;
+ }
+
+ return;
+ }
+ else if( m_bSelect )
+ {
+ // HAS to be treated after bNoScript!
+ switch( nToken )
+ {
+ case HtmlTokenId::SELECT_OFF:
+ m_bCallNextToken = false;
+ EndSelect();
+ return;
+
+ case HtmlTokenId::OPTION:
+ InsertSelectOption();
+ return;
+
+ case HtmlTokenId::TEXTTOKEN:
+ InsertSelectText();
+ return;
+
+ case HtmlTokenId::INPUT:
+ case HtmlTokenId::SCRIPT_ON:
+ case HtmlTokenId::SCRIPT_OFF:
+ case HtmlTokenId::NOSCRIPT_ON:
+ case HtmlTokenId::NOSCRIPT_OFF:
+ case HtmlTokenId::RAWDATA:
+ // treat in normal switch
+ break;
+
+ default:
+ // ignore
+ return;
+ }
+ }
+ else if( m_pMarquee )
+ {
+ // in a TextArea everything up to </TEXTAREA> is inserted as text.
+ // The <SCRIPT> tags are ignored from MS-IE, we ignore the whole
+ // script.
+ switch( nToken )
+ {
+ case HtmlTokenId::MARQUEE_OFF:
+ m_bCallNextToken = false;
+ EndMarquee();
+ break;
+
+ case HtmlTokenId::TEXTTOKEN:
+ InsertMarqueeText();
+ break;
+ default: break;
+ }
+
+ return;
+ }
+ else if( m_bInField )
+ {
+ switch( nToken )
+ {
+ case HtmlTokenId::SDFIELD_OFF:
+ m_bCallNextToken = false;
+ EndField();
+ break;
+
+ case HtmlTokenId::TEXTTOKEN:
+ InsertFieldText();
+ break;
+ default: break;
+ }
+
+ return;
+ }
+ else if( m_bInFootEndNoteAnchor || m_bInFootEndNoteSymbol )
+ {
+ switch( nToken )
+ {
+ case HtmlTokenId::ANCHOR_OFF:
+ EndAnchor();
+ m_bCallNextToken = false;
+ break;
+
+ case HtmlTokenId::TEXTTOKEN:
+ InsertFootEndNoteText();
+ break;
+ default: break;
+ }
+ return;
+ }
+ else if( !m_aUnknownToken.isEmpty() )
+ {
+ // Paste content of unknown tags.
+ // (but surely if we are not in the header section) fdo#36080 fdo#34666
+ if (!aToken.isEmpty() && !IsInHeader() )
+ {
+ if( !m_bDocInitialized )
+ DocumentDetected();
+ m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aToken.toString());
+
+ // if there are temporary paragraph attributes and the
+ // paragraph isn't empty then the paragraph attributes
+ // are final.
+ m_aParaAttrs.clear();
+
+ SetAttr();
+ }
+
+ // Unknown token in the header are only closed by a matching
+ // end-token, </HEAD> or <BODY>. Text inside is ignored.
+ switch( nToken )
+ {
+ case HtmlTokenId::UNKNOWNCONTROL_OFF:
+ if( m_aUnknownToken != sSaveToken )
+ return;
+ [[fallthrough]];
+ case HtmlTokenId::FRAMESET_ON:
+ case HtmlTokenId::HEAD_OFF:
+ case HtmlTokenId::BODY_ON:
+ case HtmlTokenId::IMAGE: // Don't know why Netscape acts this way.
+ m_aUnknownToken.clear();
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ return;
+ default:
+ m_aUnknownToken.clear();
+ break;
+ }
+ }
+ }
+
+ switch( nToken )
+ {
+ case HtmlTokenId::BODY_ON:
+ if (!m_bBodySeen)
+ {
+ m_bBodySeen = true;
+ if( !m_aStyleSource.isEmpty() )
+ {
+ m_pCSS1Parser->ParseStyleSheet( m_aStyleSource );
+ m_aStyleSource.clear();
+ }
+ if( IsNewDoc() )
+ {
+ InsertBodyOptions();
+ // If there is a template for the first or the right page,
+ // it is set here.
+ const SwPageDesc *pPageDesc = nullptr;
+ if( m_pCSS1Parser->IsSetFirstPageDesc() )
+ pPageDesc = m_pCSS1Parser->GetFirstPageDesc();
+ else if( m_pCSS1Parser->IsSetRightPageDesc() )
+ pPageDesc = m_pCSS1Parser->GetRightPageDesc();
+
+ if( pPageDesc )
+ {
+ m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, SwFormatPageDesc( pPageDesc ) );
+ }
+ }
+ }
+ break;
+
+ case HtmlTokenId::LINK:
+ InsertLink();
+ break;
+
+ case HtmlTokenId::BASE:
+ {
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::HREF:
+ m_sBaseURL = rOption.GetString();
+ break;
+ case HtmlOptionId::TARGET:
+ if( IsNewDoc() )
+ {
+ SwDocShell *pDocShell(m_xDoc->GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell) {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties>
+ xDocProps(xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(),"no DocumentProperties");
+ if (xDocProps.is()) {
+ xDocProps->setDefaultTarget(
+ rOption.GetString());
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ break;
+
+ case HtmlTokenId::META:
+ {
+ SvKeyValueIterator *pHTTPHeader = nullptr;
+ if( IsNewDoc() )
+ {
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ if( pDocSh )
+ pHTTPHeader = pDocSh->GetHeaderAttributes();
+ }
+ SwDocShell *pDocShell(m_xDoc->GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell)
+ {
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ if (IsNewDoc())
+ {
+ const uno::Reference<document::XDocumentPropertiesSupplier>
+ xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ xDocProps = xDPS->getDocumentProperties();
+ OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
+ }
+ ParseMetaOptions( xDocProps, pHTTPHeader );
+ }
+ }
+ break;
+
+ case HtmlTokenId::TITLE_ON:
+ m_bInTitle = true;
+ break;
+
+ case HtmlTokenId::SCRIPT_ON:
+ NewScript();
+ break;
+
+ case HtmlTokenId::SCRIPT_OFF:
+ EndScript();
+ break;
+
+ case HtmlTokenId::NOSCRIPT_ON:
+ case HtmlTokenId::NOSCRIPT_OFF:
+ bInsertUnknown = true;
+ break;
+
+ case HtmlTokenId::STYLE_ON:
+ NewStyle();
+ break;
+
+ case HtmlTokenId::STYLE_OFF:
+ EndStyle();
+ break;
+
+ case HtmlTokenId::RAWDATA:
+ if( !m_bIgnoreRawData )
+ {
+ if( IsReadScript() )
+ {
+ AddScriptSource();
+ }
+ else if( IsReadStyle() )
+ {
+ if( !m_aStyleSource.isEmpty() )
+ m_aStyleSource += "\n";
+ m_aStyleSource += aToken;
+ }
+ }
+ break;
+
+ case HtmlTokenId::OBJECT_ON:
+ if (m_bXHTML)
+ {
+ if (!InsertEmbed())
+ InsertImage();
+ break;
+ }
+#if HAVE_FEATURE_JAVA
+ NewObject();
+ m_bCallNextToken = m_pAppletImpl!=nullptr && m_xTable;
+#endif
+ break;
+
+ case HtmlTokenId::OBJECT_OFF:
+ if (!m_aEmbeds.empty())
+ m_aEmbeds.pop();
+ break;
+
+ case HtmlTokenId::APPLET_ON:
+#if HAVE_FEATURE_JAVA
+ InsertApplet();
+ m_bCallNextToken = m_pAppletImpl!=nullptr && m_xTable;
+#endif
+ break;
+
+ case HtmlTokenId::IFRAME_ON:
+ if (bFuzzing && m_nFloatingFrames > 64)
+ SAL_WARN("sw.html", "Not importing any more FloatingFrames for fuzzing performance");
+ else
+ {
+ InsertFloatingFrame();
+ m_bCallNextToken = m_bInFloatingFrame && m_xTable;
+ }
+ break;
+
+ case HtmlTokenId::LINEBREAK:
+ if( !IsReadPRE() )
+ {
+ InsertLineBreak();
+ break;
+ }
+ else
+ bGetIDOption = true;
+ // <BR>s in <PRE> resemble true LFs, hence no break
+ [[fallthrough]];
+
+ case HtmlTokenId::NEWPARA:
+ // CR in PRE/LISTING/XMP
+ {
+ if( HtmlTokenId::NEWPARA==nToken ||
+ m_pPam->GetPoint()->GetContentIndex() )
+ {
+ AppendTextNode(); // there is no LF at this place
+ // therefore it will cause no problems
+ SetTextCollAttrs();
+ }
+ // progress bar
+ if (m_xProgress)
+ m_xProgress->Update(rInput.Tell());
+ }
+ break;
+
+ case HtmlTokenId::NONBREAKSPACE:
+ m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, OUString(CHAR_HARDBLANK) );
+ break;
+
+ case HtmlTokenId::SOFTHYPH:
+ m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, OUString(CHAR_SOFTHYPHEN) );
+ break;
+
+ case HtmlTokenId::LINEFEEDCHAR:
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode();
+ if (!m_xTable && !m_xDoc->IsInHeaderFooter(m_pPam->GetPoint()->GetNode()))
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
+ EndAttr( m_xAttrTab->pBreak, false );
+ }
+ break;
+
+ case HtmlTokenId::TEXTTOKEN:
+ case HtmlTokenId::CDATA:
+ // insert string without spanning attributes at the end.
+ if (!aToken.isEmpty() && ' ' == aToken[0] && !IsReadPRE() && !GetPreserveSpaces())
+ {
+ sal_Int32 nPos = m_pPam->GetPoint()->GetContentIndex();
+ const SwTextNode* pTextNode = nPos ? m_pPam->GetPoint()->GetNode().GetTextNode() : nullptr;
+ if (pTextNode)
+ {
+ const OUString& rText = pTextNode->GetText();
+ sal_Unicode cLast = rText[--nPos];
+ if( ' ' == cLast || '\x0a' == cLast)
+ aToken.remove(0, 1);
+ }
+ else
+ aToken.remove(0, 1);
+
+ if( aToken.isEmpty() )
+ {
+ m_bUpperSpace = bUpperSpaceSave;
+ break;
+ }
+ }
+
+ if( !aToken.isEmpty() )
+ {
+ if( !m_bDocInitialized )
+ DocumentDetected();
+
+ if (!m_aEmbeds.empty())
+ {
+ // The text token is inside an OLE object, which means
+ // alternate text.
+ SwOLENode* pOLENode = m_aEmbeds.top();
+ if (!pOLENode)
+ {
+ // <object> is mapped to an image -> ignore.
+ break;
+ }
+
+ if (SwFlyFrameFormat* pFormat
+ = dynamic_cast<SwFlyFrameFormat*>(pOLENode->GetFlyFormat()))
+ {
+ if (SdrObject* pObject = SwXFrame::GetOrCreateSdrObject(*pFormat))
+ {
+ pObject->SetTitle(pObject->GetTitle() + aToken);
+ break;
+ }
+ }
+ }
+
+ m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aToken.toString());
+
+ // if there are temporary paragraph attributes and the
+ // paragraph isn't empty then the paragraph attributes
+ // are final.
+ m_aParaAttrs.clear();
+
+ SetAttr();
+ }
+ break;
+
+ case HtmlTokenId::HORZRULE:
+ InsertHorzRule();
+ break;
+
+ case HtmlTokenId::IMAGE:
+ InsertImage();
+ // if only the parser references the doc, we can break and set
+ // an error code
+ if( 1 == m_xDoc->getReferenceCount() )
+ {
+ eState = SvParserState::Error;
+ }
+ break;
+
+ case HtmlTokenId::SPACER:
+ InsertSpacer();
+ break;
+
+ case HtmlTokenId::EMBED:
+ InsertEmbed();
+ break;
+
+ case HtmlTokenId::NOEMBED_ON:
+ m_bInNoEmbed = true;
+ m_bCallNextToken = bool(m_xTable);
+ ReadRawData( OOO_STRING_SVTOOLS_HTML_noembed );
+ break;
+
+ case HtmlTokenId::DEFLIST_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ NewDefList();
+ break;
+ case HtmlTokenId::DEFLIST_OFF:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ EndDefListItem( HtmlTokenId::NONE );
+ EndDefList();
+ break;
+
+ case HtmlTokenId::DD_ON:
+ case HtmlTokenId::DT_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ EndDefListItem();// close <DD>/<DT> and set no template
+ NewDefListItem( nToken );
+ break;
+
+ case HtmlTokenId::DD_OFF:
+ case HtmlTokenId::DT_OFF:
+ // c.f. HtmlTokenId::LI_OFF
+ // Actually we should close a DD/DT now.
+ // But neither Netscape nor Microsoft do this and so don't we.
+ EndDefListItem( nToken );
+ break;
+
+ // divisions
+ case HtmlTokenId::DIVISION_ON:
+ case HtmlTokenId::CENTER_ON:
+ if (!m_isInTableStructure)
+ {
+ if (m_nOpenParaToken != HtmlTokenId::NONE)
+ {
+ if (IsReadPRE())
+ m_nOpenParaToken = HtmlTokenId::NONE;
+ else
+ EndPara();
+ }
+ NewDivision( nToken );
+ }
+ break;
+
+ case HtmlTokenId::DIVISION_OFF:
+ case HtmlTokenId::CENTER_OFF:
+ if (!m_isInTableStructure)
+ {
+ if (m_nOpenParaToken != HtmlTokenId::NONE)
+ {
+ if (IsReadPRE())
+ m_nOpenParaToken = HtmlTokenId::NONE;
+ else
+ EndPara();
+ }
+ EndDivision();
+ }
+ break;
+
+ case HtmlTokenId::MULTICOL_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ NewMultiCol();
+ break;
+
+ case HtmlTokenId::MULTICOL_OFF:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ EndTag( HtmlTokenId::MULTICOL_ON );
+ break;
+
+ case HtmlTokenId::MARQUEE_ON:
+ NewMarquee();
+ m_bCallNextToken = m_pMarquee!=nullptr && m_xTable;
+ break;
+
+ case HtmlTokenId::FORM_ON:
+ NewForm();
+ break;
+ case HtmlTokenId::FORM_OFF:
+ EndForm();
+ break;
+
+ // templates
+ case HtmlTokenId::PARABREAK_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara( true );
+ NewPara();
+ break;
+
+ case HtmlTokenId::PARABREAK_OFF:
+ EndPara( true );
+ break;
+
+ case HtmlTokenId::ADDRESS_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ NewTextFormatColl(HtmlTokenId::ADDRESS_ON, RES_POOLCOLL_SEND_ADDRESS);
+ break;
+
+ case HtmlTokenId::ADDRESS_OFF:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ EndTextFormatColl( HtmlTokenId::ADDRESS_OFF );
+ break;
+
+ case HtmlTokenId::BLOCKQUOTE_ON:
+ case HtmlTokenId::BLOCKQUOTE30_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ NewTextFormatColl( HtmlTokenId::BLOCKQUOTE_ON, RES_POOLCOLL_HTML_BLOCKQUOTE );
+ break;
+
+ case HtmlTokenId::BLOCKQUOTE_OFF:
+ case HtmlTokenId::BLOCKQUOTE30_OFF:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ EndTextFormatColl( HtmlTokenId::BLOCKQUOTE_ON );
+ break;
+
+ case HtmlTokenId::PREFORMTXT_ON:
+ case HtmlTokenId::LISTING_ON:
+ case HtmlTokenId::XMP_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ NewTextFormatColl( nToken, RES_POOLCOLL_HTML_PRE );
+ break;
+
+ case HtmlTokenId::PREFORMTXT_OFF:
+ m_bNoParSpace = true; // the last PRE-paragraph gets a spacing
+ EndTextFormatColl( HtmlTokenId::PREFORMTXT_OFF );
+ break;
+
+ case HtmlTokenId::LISTING_OFF:
+ case HtmlTokenId::XMP_OFF:
+ EndTextFormatColl( nToken );
+ break;
+
+ case HtmlTokenId::HEAD1_ON:
+ case HtmlTokenId::HEAD2_ON:
+ case HtmlTokenId::HEAD3_ON:
+ case HtmlTokenId::HEAD4_ON:
+ case HtmlTokenId::HEAD5_ON:
+ case HtmlTokenId::HEAD6_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ {
+ if( IsReadPRE() )
+ m_nOpenParaToken = HtmlTokenId::NONE;
+ else
+ EndPara();
+ }
+ NewHeading( nToken );
+ break;
+
+ case HtmlTokenId::HEAD1_OFF:
+ case HtmlTokenId::HEAD2_OFF:
+ case HtmlTokenId::HEAD3_OFF:
+ case HtmlTokenId::HEAD4_OFF:
+ case HtmlTokenId::HEAD5_OFF:
+ case HtmlTokenId::HEAD6_OFF:
+ EndHeading();
+ break;
+
+ case HtmlTokenId::TABLE_ON:
+ if( !m_vPendingStack.empty() )
+ BuildTable( SvxAdjust::End );
+ else
+ {
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ OSL_ENSURE(!m_xTable, "table in table not allowed here");
+ if( !m_xTable && (IsNewDoc() || !m_pPam->GetPointNode().FindTableNode()) &&
+ (m_pPam->GetPoint()->GetNodeIndex() >
+ m_xDoc->GetNodes().GetEndOfExtras().GetIndex() ||
+ !m_pPam->GetPointNode().FindFootnoteStartNode() ) )
+ {
+ if ( m_nParaCnt < 5 )
+ Show(); // show what we have up to here
+
+ SvxAdjust eAdjust = m_xAttrTab->pAdjust
+ ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()).
+ GetAdjust()
+ : SvxAdjust::End;
+ BuildTable( eAdjust );
+ }
+ else
+ bInsertUnknown = m_bKeepUnknown;
+ }
+ break;
+
+ // lists
+ case HtmlTokenId::DIRLIST_ON:
+ case HtmlTokenId::MENULIST_ON:
+ case HtmlTokenId::ORDERLIST_ON:
+ case HtmlTokenId::UNORDERLIST_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ NewNumberBulletList( nToken );
+ break;
+
+ case HtmlTokenId::DIRLIST_OFF:
+ case HtmlTokenId::MENULIST_OFF:
+ case HtmlTokenId::ORDERLIST_OFF:
+ case HtmlTokenId::UNORDERLIST_OFF:
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ EndNumberBulletListItem( HtmlTokenId::NONE, true );
+ EndNumberBulletList( nToken );
+ break;
+
+ case HtmlTokenId::LI_ON:
+ case HtmlTokenId::LISTHEADER_ON:
+ if( m_nOpenParaToken != HtmlTokenId::NONE &&
+ (m_pPam->GetPoint()->GetContentIndex()
+ || HtmlTokenId::PARABREAK_ON==m_nOpenParaToken) )
+ {
+ // only finish paragraph for <P><LI>, not for <DD><LI>
+ EndPara();
+ }
+
+ if (bFuzzing && m_nListItems > 1024)
+ {
+ SAL_WARN("sw.html", "skipping remaining bullet import for performance during fuzzing");
+ }
+ else
+ {
+ EndNumberBulletListItem( HtmlTokenId::NONE, false );// close <LI>/<LH> and don't set a template
+ NewNumberBulletListItem( nToken );
+ }
+
+ ++m_nListItems;
+
+ break;
+ case HtmlTokenId::LI_OFF:
+ case HtmlTokenId::LISTHEADER_OFF:
+ EndNumberBulletListItem( nToken, false );
+ break;
+
+ // Attribute :
+ case HtmlTokenId::ITALIC_ON:
+ {
+ SvxPostureItem aPosture( ITALIC_NORMAL, RES_CHRATR_POSTURE );
+ SvxPostureItem aPostureCJK( ITALIC_NORMAL, RES_CHRATR_CJK_POSTURE );
+ SvxPostureItem aPostureCTL( ITALIC_NORMAL, RES_CHRATR_CTL_POSTURE );
+ NewStdAttr( HtmlTokenId::ITALIC_ON,
+ &m_xAttrTab->pItalic, aPosture,
+ &m_xAttrTab->pItalicCJK, &aPostureCJK,
+ &m_xAttrTab->pItalicCTL, &aPostureCTL );
+ }
+ break;
+
+ case HtmlTokenId::BOLD_ON:
+ {
+ SvxWeightItem aWeight( WEIGHT_BOLD, RES_CHRATR_WEIGHT );
+ SvxWeightItem aWeightCJK( WEIGHT_BOLD, RES_CHRATR_CJK_WEIGHT );
+ SvxWeightItem aWeightCTL( WEIGHT_BOLD, RES_CHRATR_CTL_WEIGHT );
+ NewStdAttr( HtmlTokenId::BOLD_ON,
+ &m_xAttrTab->pBold, aWeight,
+ &m_xAttrTab->pBoldCJK, &aWeightCJK,
+ &m_xAttrTab->pBoldCTL, &aWeightCTL );
+ }
+ break;
+
+ case HtmlTokenId::STRIKE_ON:
+ case HtmlTokenId::STRIKETHROUGH_ON:
+ {
+ NewStdAttr( HtmlTokenId::STRIKE_ON, &m_xAttrTab->pStrike,
+ SvxCrossedOutItem(STRIKEOUT_SINGLE, RES_CHRATR_CROSSEDOUT) );
+ }
+ break;
+
+ case HtmlTokenId::UNDERLINE_ON:
+ {
+ NewStdAttr( HtmlTokenId::UNDERLINE_ON, &m_xAttrTab->pUnderline,
+ SvxUnderlineItem(LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE) );
+ }
+ break;
+
+ case HtmlTokenId::SUPERSCRIPT_ON:
+ {
+ NewStdAttr( HtmlTokenId::SUPERSCRIPT_ON, &m_xAttrTab->pEscapement,
+ SvxEscapementItem(HTML_ESC_SUPER,HTML_ESC_PROP, RES_CHRATR_ESCAPEMENT) );
+ }
+ break;
+
+ case HtmlTokenId::SUBSCRIPT_ON:
+ {
+ NewStdAttr( HtmlTokenId::SUBSCRIPT_ON, &m_xAttrTab->pEscapement,
+ SvxEscapementItem(HTML_ESC_SUB,HTML_ESC_PROP, RES_CHRATR_ESCAPEMENT) );
+ }
+ break;
+
+ case HtmlTokenId::BLINK_ON:
+ {
+ NewStdAttr( HtmlTokenId::BLINK_ON, &m_xAttrTab->pBlink,
+ SvxBlinkItem( true, RES_CHRATR_BLINK ) );
+ }
+ break;
+
+ case HtmlTokenId::SPAN_ON:
+ NewStdAttr( HtmlTokenId::SPAN_ON );
+ break;
+
+ case HtmlTokenId::ITALIC_OFF:
+ case HtmlTokenId::BOLD_OFF:
+ case HtmlTokenId::STRIKE_OFF:
+ case HtmlTokenId::UNDERLINE_OFF:
+ case HtmlTokenId::SUPERSCRIPT_OFF:
+ case HtmlTokenId::SUBSCRIPT_OFF:
+ case HtmlTokenId::BLINK_OFF:
+ case HtmlTokenId::SPAN_OFF:
+ EndTag( nToken );
+ break;
+
+ case HtmlTokenId::STRIKETHROUGH_OFF:
+ EndTag( HtmlTokenId::STRIKE_OFF );
+ break;
+
+ case HtmlTokenId::BASEFONT_ON:
+ NewBasefontAttr();
+ break;
+ case HtmlTokenId::BASEFONT_OFF:
+ EndBasefontAttr();
+ break;
+ case HtmlTokenId::FONT_ON:
+ case HtmlTokenId::BIGPRINT_ON:
+ case HtmlTokenId::SMALLPRINT_ON:
+ NewFontAttr( nToken );
+ break;
+ case HtmlTokenId::FONT_OFF:
+ case HtmlTokenId::BIGPRINT_OFF:
+ case HtmlTokenId::SMALLPRINT_OFF:
+ EndFontAttr( nToken );
+ break;
+
+ case HtmlTokenId::EMPHASIS_ON:
+ case HtmlTokenId::CITATION_ON:
+ case HtmlTokenId::STRONG_ON:
+ case HtmlTokenId::CODE_ON:
+ case HtmlTokenId::SAMPLE_ON:
+ case HtmlTokenId::KEYBOARD_ON:
+ case HtmlTokenId::VARIABLE_ON:
+ case HtmlTokenId::DEFINSTANCE_ON:
+ case HtmlTokenId::SHORTQUOTE_ON:
+ case HtmlTokenId::LANGUAGE_ON:
+ case HtmlTokenId::AUTHOR_ON:
+ case HtmlTokenId::PERSON_ON:
+ case HtmlTokenId::ACRONYM_ON:
+ case HtmlTokenId::ABBREVIATION_ON:
+ case HtmlTokenId::INSERTEDTEXT_ON:
+ case HtmlTokenId::DELETEDTEXT_ON:
+
+ case HtmlTokenId::TELETYPE_ON:
+ NewCharFormat( nToken );
+ break;
+
+ case HtmlTokenId::SDFIELD_ON:
+ NewField();
+ m_bCallNextToken = m_bInField && m_xTable;
+ break;
+
+ case HtmlTokenId::EMPHASIS_OFF:
+ case HtmlTokenId::CITATION_OFF:
+ case HtmlTokenId::STRONG_OFF:
+ case HtmlTokenId::CODE_OFF:
+ case HtmlTokenId::SAMPLE_OFF:
+ case HtmlTokenId::KEYBOARD_OFF:
+ case HtmlTokenId::VARIABLE_OFF:
+ case HtmlTokenId::DEFINSTANCE_OFF:
+ case HtmlTokenId::SHORTQUOTE_OFF:
+ case HtmlTokenId::LANGUAGE_OFF:
+ case HtmlTokenId::AUTHOR_OFF:
+ case HtmlTokenId::PERSON_OFF:
+ case HtmlTokenId::ACRONYM_OFF:
+ case HtmlTokenId::ABBREVIATION_OFF:
+ case HtmlTokenId::INSERTEDTEXT_OFF:
+ case HtmlTokenId::DELETEDTEXT_OFF:
+
+ case HtmlTokenId::TELETYPE_OFF:
+ EndTag( nToken );
+ break;
+
+ case HtmlTokenId::HEAD_OFF:
+ if( !m_aStyleSource.isEmpty() )
+ {
+ m_pCSS1Parser->ParseStyleSheet( m_aStyleSource );
+ m_aStyleSource.clear();
+ }
+ break;
+
+ case HtmlTokenId::DOCTYPE:
+ case HtmlTokenId::BODY_OFF:
+ case HtmlTokenId::HTML_OFF:
+ case HtmlTokenId::HEAD_ON:
+ case HtmlTokenId::TITLE_OFF:
+ break; // don't evaluate further???
+ case HtmlTokenId::HTML_ON:
+ {
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ if( HtmlOptionId::DIR == rOption.GetToken() )
+ {
+ const OUString& rDir = rOption.GetString();
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(),
+ m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+ OUString aDummy;
+ ParseStyleOptions( aDummy, aDummy, aDummy, aItemSet,
+ aPropInfo, nullptr, &rDir );
+
+ m_pCSS1Parser->SetPageDescAttrs( nullptr, &aItemSet );
+ break;
+ }
+ }
+ }
+ break;
+
+ case HtmlTokenId::INPUT:
+ InsertInput();
+ break;
+
+ case HtmlTokenId::TEXTAREA_ON:
+ NewTextArea();
+ m_bCallNextToken = m_bTextArea && m_xTable;
+ break;
+
+ case HtmlTokenId::SELECT_ON:
+ NewSelect();
+ m_bCallNextToken = m_bSelect && m_xTable;
+ break;
+
+ case HtmlTokenId::ANCHOR_ON:
+ NewAnchor();
+ break;
+
+ case HtmlTokenId::ANCHOR_OFF:
+ EndAnchor();
+ break;
+
+ case HtmlTokenId::COMMENT:
+ if( ( aToken.getLength() > 5 ) && ( ! m_bIgnoreHTMLComments ) )
+ {
+ // insert as Post-It
+ // If there are no space characters right behind
+ // the <!-- and on front of the -->, leave the comment untouched.
+ if( ' ' == aToken[ 3 ] &&
+ ' ' == aToken[ aToken.getLength()-3 ] )
+ {
+ std::u16string_view aComment( aToken.subView( 3, aToken.getLength()-5 ) );
+ InsertComment(OUString(comphelper::string::strip(aComment, ' ')));
+ }
+ else
+ {
+ OUString aComment = "<" + aToken + ">";
+ InsertComment( aComment );
+ }
+ }
+ break;
+
+ case HtmlTokenId::MAP_ON:
+ // Image Maps are read asynchronously: At first only an image map is created
+ // Areas are processed later. Nevertheless the
+ // ImageMap is inserted into the IMap-Array, because it might be used
+ // already.
+ m_pImageMap = new ImageMap;
+ if( ParseMapOptions( m_pImageMap) )
+ {
+ if (!m_pImageMaps)
+ m_pImageMaps.reset( new ImageMaps );
+ m_pImageMaps->push_back(std::unique_ptr<ImageMap>(m_pImageMap));
+ }
+ else
+ {
+ delete m_pImageMap;
+ m_pImageMap = nullptr;
+ }
+ break;
+
+ case HtmlTokenId::MAP_OFF:
+ // there is no ImageMap anymore (don't delete IMap, because it's
+ // already contained in the array!)
+ m_pImageMap = nullptr;
+ break;
+
+ case HtmlTokenId::AREA:
+ if( m_pImageMap )
+ ParseAreaOptions( m_pImageMap, m_sBaseURL, SvMacroItemId::OnMouseOver,
+ SvMacroItemId::OnMouseOut );
+ break;
+
+ case HtmlTokenId::FRAMESET_ON:
+ bInsertUnknown = m_bKeepUnknown;
+ break;
+
+ case HtmlTokenId::NOFRAMES_ON:
+ if( IsInHeader() )
+ FinishHeader();
+ bInsertUnknown = m_bKeepUnknown;
+ break;
+
+ case HtmlTokenId::UNKNOWNCONTROL_ON:
+ // Ignore content of unknown token in the header, if the token
+ // does not start with a '!'.
+ // (but judging from the code, also if does not start with a '%')
+ // (and also if we're not somewhere we consider PRE)
+ if( IsInHeader() && !IsReadPRE() && m_aUnknownToken.isEmpty() &&
+ !sSaveToken.isEmpty() && '!' != sSaveToken[0] &&
+ '%' != sSaveToken[0] )
+ m_aUnknownToken = sSaveToken;
+ [[fallthrough]];
+
+ default:
+ bInsertUnknown = m_bKeepUnknown;
+ break;
+ }
+
+ if( bGetIDOption )
+ InsertIDOption();
+
+ if( bInsertUnknown )
+ {
+ OUStringBuffer aComment("HTML: <");
+ if( (nToken >= HtmlTokenId::ONOFF_START) && isOffToken(nToken) )
+ aComment.append("/");
+ aComment.append(sSaveToken);
+ if( !aToken.isEmpty() )
+ {
+ UnescapeToken();
+ aComment.append(" " + aToken);
+ }
+ aComment.append(">");
+ InsertComment( aComment.makeStringAndClear() );
+ }
+
+ // if there are temporary paragraph attributes and the
+ // paragraph isn't empty then the paragraph attributes are final.
+ if( !m_aParaAttrs.empty() && m_pPam->GetPoint()->GetContentIndex() )
+ m_aParaAttrs.clear();
+}
+
+static void lcl_swhtml_getItemInfo( const HTMLAttr& rAttr,
+ bool& rScriptDependent,
+ sal_uInt16& rScriptType )
+{
+ switch( rAttr.GetItem().Which() )
+ {
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_LANGUAGE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ rScriptType = i18n::ScriptType::LATIN;
+ rScriptDependent = true;
+ break;
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_LANGUAGE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ rScriptType = i18n::ScriptType::ASIAN;
+ rScriptDependent = true;
+ break;
+ case RES_CHRATR_CTL_FONT:
+ case RES_CHRATR_CTL_FONTSIZE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ case RES_CHRATR_CTL_POSTURE:
+ case RES_CHRATR_CTL_WEIGHT:
+ rScriptType = i18n::ScriptType::COMPLEX;
+ rScriptDependent = true;
+ break;
+ default:
+ rScriptDependent = false;
+ break;
+ }
+}
+
+bool SwHTMLParser::AppendTextNode( SwHTMLAppendMode eMode, bool bUpdateNum )
+{
+ // A hard line break at the end always must be removed.
+ // A second one we replace with paragraph spacing.
+ sal_Int32 nLFStripped = StripTrailingLF();
+ if( (AM_NOSPACE==eMode || AM_SOFTNOSPACE==eMode) && nLFStripped > 1 )
+ eMode = AM_SPACE;
+
+ // the hard attributes of this paragraph will never be invalid again
+ m_aParaAttrs.clear();
+
+ SwTextNode *pTextNode = (AM_SPACE==eMode || AM_NOSPACE==eMode) ?
+ m_pPam->GetPoint()->GetNode().GetTextNode() : nullptr;
+
+ if (pTextNode)
+ {
+ const SvxULSpaceItem& rULSpace =
+ pTextNode->SwContentNode::GetAttr( RES_UL_SPACE );
+
+ bool bChange = AM_NOSPACE==eMode ? rULSpace.GetLower() > 0
+ : rULSpace.GetLower() == 0;
+
+ if( bChange )
+ {
+ const SvxULSpaceItem& rCollULSpace =
+ pTextNode->GetAnyFormatColl().GetULSpace();
+
+ bool bMayReset = AM_NOSPACE==eMode ? rCollULSpace.GetLower() == 0
+ : rCollULSpace.GetLower() > 0;
+
+ if( bMayReset &&
+ rCollULSpace.GetUpper() == rULSpace.GetUpper() )
+ {
+ pTextNode->ResetAttr( RES_UL_SPACE );
+ }
+ else
+ {
+ pTextNode->SetAttr(
+ SvxULSpaceItem( rULSpace.GetUpper(),
+ AM_NOSPACE==eMode ? 0 : HTML_PARSPACE, RES_UL_SPACE ) );
+ }
+ }
+ }
+ m_bNoParSpace = AM_NOSPACE==eMode || AM_SOFTNOSPACE==eMode;
+
+ SwPosition aOldPos( *m_pPam->GetPoint() );
+
+ bool bRet = m_xDoc->getIDocumentContentOperations().AppendTextNode( *m_pPam->GetPoint() );
+
+ // split character attributes and maybe set none,
+ // which are set for the whole paragraph
+ const sal_Int32 nEndCnt = aOldPos.GetContentIndex();
+ const SwPosition& rPos = *m_pPam->GetPoint();
+
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
+ {
+ HTMLAttr *pAttr = *pHTMLAttributes;
+ if( pAttr && pAttr->GetItem().Which() < RES_PARATR_BEGIN )
+ {
+ bool bWholePara = false;
+
+ while( pAttr )
+ {
+ HTMLAttr *pNext = pAttr->GetNext();
+ if( pAttr->GetStartParagraphIdx() < aOldPos.GetNodeIndex() ||
+ (!bWholePara &&
+ pAttr->GetStartParagraph() == aOldPos.GetNode() &&
+ pAttr->GetStartContent() != nEndCnt) )
+ {
+ bWholePara =
+ pAttr->GetStartParagraph() == aOldPos.GetNode() &&
+ pAttr->GetStartContent() == 0;
+
+ sal_Int32 nStt = pAttr->m_nStartContent;
+ bool bScript = false;
+ sal_uInt16 nScriptItem;
+ bool bInsert = true;
+ lcl_swhtml_getItemInfo( *pAttr, bScript,
+ nScriptItem );
+ // set previous part
+ if( bScript )
+ {
+ const SwTextNode *pTextNd =
+ pAttr->GetStartParagraph().GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "No text node" );
+ if( pTextNd )
+ {
+ const OUString& rText = pTextNd->GetText();
+ sal_uInt16 nScriptText =
+ g_pBreakIt->GetBreakIter()->getScriptType(
+ rText, pAttr->GetStartContent() );
+ sal_Int32 nScriptEnd = g_pBreakIt->GetBreakIter()
+ ->endOfScript( rText, nStt, nScriptText );
+ while (nScriptEnd < nEndCnt && nScriptEnd != -1)
+ {
+ if( nScriptItem == nScriptText )
+ {
+ HTMLAttr *pSetAttr =
+ pAttr->Clone( aOldPos.GetNode(), nScriptEnd );
+ pSetAttr->m_nStartContent = nStt;
+ pSetAttr->ClearPrev();
+ if( !pNext || bWholePara )
+ {
+ if (pSetAttr->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pSetAttr );
+ else
+ m_aSetAttrTab.push_back( pSetAttr );
+ }
+ else
+ pNext->InsertPrev( pSetAttr );
+ }
+ nStt = nScriptEnd;
+ nScriptText = g_pBreakIt->GetBreakIter()->getScriptType(
+ rText, nStt );
+ nScriptEnd = g_pBreakIt->GetBreakIter()
+ ->endOfScript( rText, nStt, nScriptText );
+ }
+ bInsert = nScriptItem == nScriptText;
+ }
+ }
+ if( bInsert )
+ {
+ HTMLAttr *pSetAttr =
+ pAttr->Clone( aOldPos.GetNode(), nEndCnt );
+ pSetAttr->m_nStartContent = nStt;
+
+ // When the attribute is for the whole paragraph, the outer
+ // attributes aren't effective anymore. Hence it may not be inserted
+ // in the Prev-List of an outer attribute, because that won't be
+ // set. That leads to shifting when fields are used.
+ if( !pNext || bWholePara )
+ {
+ if (pSetAttr->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pSetAttr );
+ else
+ m_aSetAttrTab.push_back( pSetAttr );
+ }
+ else
+ pNext->InsertPrev( pSetAttr );
+ }
+ else
+ {
+ HTMLAttr *pPrev = pAttr->GetPrev();
+ if( pPrev )
+ {
+ // the previous attributes must be set anyway
+ if( !pNext || bWholePara )
+ {
+ if (pPrev->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pPrev );
+ else
+ m_aSetAttrTab.push_back( pPrev );
+ }
+ else
+ pNext->InsertPrev( pPrev );
+ }
+ }
+ pAttr->ClearPrev();
+ }
+
+ pAttr->SetStart( rPos );
+ pAttr = pNext;
+ }
+ }
+ }
+
+ if( bUpdateNum )
+ {
+ if( GetNumInfo().GetDepth() )
+ {
+ sal_uInt8 nLvl = GetNumInfo().GetLevel();
+ SetNodeNum( nLvl );
+ }
+ else
+ m_pPam->GetPointNode().GetTextNode()->ResetAttr( RES_PARATR_NUMRULE );
+ }
+
+ // We must set the attribute of the paragraph before now (because of JavaScript)
+ SetAttr();
+
+ // Now it is time to get rid of all script dependent hints that are
+ // equal to the settings in the style
+ SwTextNode *pTextNd = aOldPos.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "There is the txt node" );
+ size_t nCntAttr = (pTextNd && pTextNd->GetpSwpHints())
+ ? pTextNd->GetSwpHints().Count() : 0;
+ if( nCntAttr )
+ {
+ // These are the end position of all script dependent hints.
+ // If we find a hint that starts before the current end position,
+ // we have to set it. If we find a hint that start behind or at
+ // that position, we have to take the hint value into account.
+ // If it is equal to the style, or in fact the paragraph value
+ // for that hint, the hint is removed. Otherwise its end position
+ // is remembered.
+ sal_Int32 aEndPos[15] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ SwpHints& rHints = pTextNd->GetSwpHints();
+ for( size_t i=0; i < nCntAttr; i++ )
+ {
+ SwTextAttr *pHt = rHints.Get( i );
+ sal_uInt16 nWhich = pHt->Which();
+ sal_Int16 nIdx = 0;
+ bool bFont = false;
+ switch( nWhich )
+ {
+ case RES_CHRATR_FONT:
+ nIdx = 0;
+ bFont = true;
+ break;
+ case RES_CHRATR_FONTSIZE:
+ nIdx = 1;
+ break;
+ case RES_CHRATR_LANGUAGE:
+ nIdx = 2;
+ break;
+ case RES_CHRATR_POSTURE:
+ nIdx = 3;
+ break;
+ case RES_CHRATR_WEIGHT:
+ nIdx = 4;
+ break;
+ case RES_CHRATR_CJK_FONT:
+ nIdx = 5;
+ bFont = true;
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ nIdx = 6;
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ nIdx = 7;
+ break;
+ case RES_CHRATR_CJK_POSTURE:
+ nIdx = 8;
+ break;
+ case RES_CHRATR_CJK_WEIGHT:
+ nIdx = 9;
+ break;
+ case RES_CHRATR_CTL_FONT:
+ nIdx = 10;
+ bFont = true;
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ nIdx = 11;
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ nIdx = 12;
+ break;
+ case RES_CHRATR_CTL_POSTURE:
+ nIdx = 13;
+ break;
+ case RES_CHRATR_CTL_WEIGHT:
+ nIdx = 14;
+ break;
+ default:
+ // Skip to next attribute
+ continue;
+ }
+ const sal_Int32 nStt = pHt->GetStart();
+ if( nStt >= aEndPos[nIdx] )
+ {
+ const SfxPoolItem& rItem =
+ static_cast<const SwContentNode *>(pTextNd)->GetAttr( nWhich );
+ if( bFont ? swhtml_css1atr_equalFontItems(rItem,pHt->GetAttr())
+ : rItem == pHt->GetAttr() )
+ {
+ // The hint is the same as set in the paragraph and
+ // therefore, it can be deleted
+ // CAUTION!!! This WILL delete the hint and it MAY
+ // also delete the SwpHints!!! To avoid any trouble
+ // we leave the loop immediately if this is the last
+ // hint.
+ pTextNd->DeleteAttribute( pHt );
+ if( 1 == nCntAttr )
+ break;
+ i--;
+ nCntAttr--;
+ }
+ else
+ {
+ // The hint is different. Therefore all hints within that
+ // hint have to be ignored.
+ aEndPos[nIdx] = pHt->GetEnd() ? *pHt->GetEnd() : nStt;
+ }
+ }
+ else
+ {
+ // The hint starts before another one ends.
+ // The hint in this case is not deleted
+ OSL_ENSURE( pHt->GetEnd() && *pHt->GetEnd() <= aEndPos[nIdx],
+ "hints aren't nested properly!" );
+ }
+ }
+ }
+
+ if (!m_xTable && !--m_nParaCnt)
+ Show();
+
+ return bRet;
+}
+
+void SwHTMLParser::AddParSpace()
+{
+ //If it already has ParSpace, return
+ if( !m_bNoParSpace )
+ return;
+
+ m_bNoParSpace = false;
+
+ SwNodeOffset nNdIdx = m_pPam->GetPoint()->GetNodeIndex() - 1;
+
+ SwTextNode *pTextNode = m_xDoc->GetNodes()[nNdIdx]->GetTextNode();
+ if( !pTextNode )
+ return;
+
+ SvxULSpaceItem rULSpace =
+ pTextNode->SwContentNode::GetAttr( RES_UL_SPACE );
+ if( rULSpace.GetLower() )
+ return;
+
+ const SvxULSpaceItem& rCollULSpace =
+ pTextNode->GetAnyFormatColl().GetULSpace();
+ if( rCollULSpace.GetLower() &&
+ rCollULSpace.GetUpper() == rULSpace.GetUpper() )
+ {
+ pTextNode->ResetAttr( RES_UL_SPACE );
+ }
+ else
+ {
+ //What I do here, is that I examine the attributes, and if
+ //I find out, that it's CJK/CTL, then I set the paragraph space
+ //to the value set in HTML_CJK_PARSPACE/HTML_CTL_PARSPACE.
+
+ bool bIsCJK = false;
+ bool bIsCTL = false;
+
+ const size_t nCntAttr = pTextNode->GetpSwpHints()
+ ? pTextNode->GetSwpHints().Count() : 0;
+
+ for(size_t i = 0; i < nCntAttr; ++i)
+ {
+ SwTextAttr *const pHt = pTextNode->GetSwpHints().Get(i);
+ sal_uInt16 const nWhich = pHt->Which();
+ if (RES_CHRATR_CJK_FONT == nWhich ||
+ RES_CHRATR_CJK_FONTSIZE == nWhich ||
+ RES_CHRATR_CJK_LANGUAGE == nWhich ||
+ RES_CHRATR_CJK_POSTURE == nWhich ||
+ RES_CHRATR_CJK_WEIGHT == nWhich)
+ {
+ bIsCJK = true;
+ break;
+ }
+ if (RES_CHRATR_CTL_FONT == nWhich ||
+ RES_CHRATR_CTL_FONTSIZE == nWhich ||
+ RES_CHRATR_CTL_LANGUAGE == nWhich ||
+ RES_CHRATR_CTL_POSTURE == nWhich ||
+ RES_CHRATR_CTL_WEIGHT == nWhich)
+ {
+ bIsCTL = true;
+ break;
+ }
+ }
+
+ if( bIsCTL )
+ {
+ pTextNode->SetAttr(
+ SvxULSpaceItem( rULSpace.GetUpper(), HTML_CTL_PARSPACE, RES_UL_SPACE ) );
+ }
+ else if( bIsCJK )
+ {
+ pTextNode->SetAttr(
+ SvxULSpaceItem( rULSpace.GetUpper(), HTML_CJK_PARSPACE, RES_UL_SPACE ) );
+ } else {
+ pTextNode->SetAttr(
+ SvxULSpaceItem( rULSpace.GetUpper(), HTML_PARSPACE, RES_UL_SPACE ) );
+ }
+ }
+}
+
+void SwHTMLParser::Show()
+{
+ // Here
+ // - a EndAction is called, so the document is formatted
+ // - a Reschedule is called,
+ // - the own View-Shell is set again
+ // - and a StartAction is called
+
+ OSL_ENSURE( SvParserState::Working==eState, "Show not in working state - That can go wrong" );
+ SwViewShell *pOldVSh = CallEndAction();
+
+ Application::Reschedule();
+
+ if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
+ || 1 == m_xDoc->getReferenceCount() )
+ {
+ // was the import aborted by SFX?
+ eState = SvParserState::Error;
+ }
+
+ // Fetch the SwViewShell again, as it could be destroyed in Reschedule.
+ SwViewShell *pVSh = CallStartAction( pOldVSh );
+
+ // is the current node not visible anymore, then we use a bigger increment
+ if( pVSh )
+ {
+ m_nParaCnt = (m_pPam->GetPoint()->GetNode().IsInVisibleArea(pVSh))
+ ? 5 : 50;
+ }
+}
+
+void SwHTMLParser::ShowStatline()
+{
+ // Here
+ // - a Reschedule is called, so it can be scrolled
+ // - the own View-Shell is set again
+ // - a StartAction/EndAction is called, when there was scrolling.
+
+ OSL_ENSURE( SvParserState::Working==eState, "ShowStatLine not in working state - That can go wrong" );
+
+ // scroll bar
+ if (m_xProgress)
+ {
+ m_xProgress->Update(rInput.Tell());
+ CheckActionViewShell();
+ }
+ else
+ {
+ Application::Reschedule();
+
+ if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
+ || 1 == m_xDoc->getReferenceCount() )
+ // was the import aborted by SFX?
+ eState = SvParserState::Error;
+
+ SwViewShell *pVSh = CheckActionViewShell();
+ if( pVSh && pVSh->HasInvalidRect() )
+ {
+ CallEndAction( false, false );
+ CallStartAction( pVSh, false );
+ }
+ }
+}
+
+SwViewShell *SwHTMLParser::CallStartAction( SwViewShell *pVSh, bool bChkPtr )
+{
+ OSL_ENSURE( !m_pActionViewShell, "CallStartAction: SwViewShell already set" );
+
+ if( !pVSh || bChkPtr )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SwViewShell *pOldVSh = pVSh;
+#endif
+ pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( !pVSh || !pOldVSh || pOldVSh == pVSh, "CallStartAction: Who swapped the SwViewShell?" );
+ if( pOldVSh && !pVSh )
+ pVSh = nullptr;
+#endif
+ }
+ m_pActionViewShell = pVSh;
+
+ if( m_pActionViewShell )
+ {
+ if( auto pEditShell = dynamic_cast< SwEditShell *>( m_pActionViewShell ) )
+ pEditShell->StartAction();
+ else
+ m_pActionViewShell->StartAction();
+ }
+
+ return m_pActionViewShell;
+}
+
+SwViewShell *SwHTMLParser::CallEndAction( bool bChkAction, bool bChkPtr )
+{
+ if( bChkPtr )
+ {
+ SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ OSL_ENSURE( !pVSh || m_pActionViewShell == pVSh,
+ "CallEndAction: Who swapped the SwViewShell?" );
+#if OSL_DEBUG_LEVEL > 0
+ if( m_pActionViewShell && !pVSh )
+ pVSh = nullptr;
+#endif
+ if( pVSh != m_pActionViewShell )
+ m_pActionViewShell = nullptr;
+ }
+
+ if( !m_pActionViewShell || (bChkAction && !m_pActionViewShell->ActionPend()) )
+ return m_pActionViewShell;
+
+ if (SwEditShell* pEditShell = dynamic_cast<SwEditShell*>(m_pActionViewShell))
+ {
+ // Already scrolled?, then make sure that the view doesn't move!
+ const bool bOldLock = m_pActionViewShell->IsViewLocked();
+ m_pActionViewShell->LockView( true );
+ pEditShell->EndAction();
+ m_pActionViewShell->LockView( bOldLock );
+
+ // bChkJumpMark is only set when the object was also found
+ if( m_bChkJumpMark )
+ {
+ const Point aVisSttPos( DOCUMENTBORDER, DOCUMENTBORDER );
+ if( GetMedium() && aVisSttPos == m_pActionViewShell->VisArea().Pos() )
+ ::JumpToSwMark( m_pActionViewShell,
+ GetMedium()->GetURLObject().GetMark() );
+ m_bChkJumpMark = false;
+ }
+ }
+ else
+ m_pActionViewShell->EndAction();
+
+ // if the parser holds the last reference to the document, then we can
+ // abort here and set an error.
+ if( 1 == m_xDoc->getReferenceCount() )
+ {
+ eState = SvParserState::Error;
+ }
+
+ SwViewShell *pVSh = m_pActionViewShell;
+ m_pActionViewShell = nullptr;
+
+ return pVSh;
+}
+
+SwViewShell *SwHTMLParser::CheckActionViewShell()
+{
+ SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ OSL_ENSURE( !pVSh || m_pActionViewShell == pVSh,
+ "CheckActionViewShell: Who has swapped SwViewShell?" );
+#if OSL_DEBUG_LEVEL > 0
+ if( m_pActionViewShell && !pVSh )
+ pVSh = nullptr;
+#endif
+ if( pVSh != m_pActionViewShell )
+ m_pActionViewShell = nullptr;
+
+ return m_pActionViewShell;
+}
+
+SwHTMLFrameFormatListener::SwHTMLFrameFormatListener(SwFrameFormat* pFrameFormat)
+ : m_pFrameFormat(pFrameFormat)
+{
+ StartListening(m_pFrameFormat->GetNotifier());
+}
+
+void SwHTMLFrameFormatListener::Notify(const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ m_pFrameFormat = nullptr;
+}
+
+void SwHTMLParser::SetAttr_( bool bChkEnd, bool bBeforeTable,
+ std::deque<std::unique_ptr<HTMLAttr>> *pPostIts )
+{
+ SwPaM aAttrPam( *m_pPam->GetPoint() );
+ const SwPosition& rEndPos = *m_pPam->GetPoint();
+ const sal_Int32 nEndCnt = m_pPam->GetPoint()->GetContentIndex();
+ HTMLAttr* pAttr;
+ SwContentNode* pCNd;
+
+ std::vector<std::unique_ptr<HTMLAttr>> aFields;
+
+ for( auto n = m_aSetAttrTab.size(); n; )
+ {
+ pAttr = m_aSetAttrTab[ --n ];
+ sal_uInt16 nWhich = pAttr->m_pItem->Which();
+
+ SwNodeOffset nEndParaIdx = pAttr->GetEndParagraphIdx();
+ bool bSetAttr;
+ if( bChkEnd )
+ {
+ // Set character attribute with end early on, so set them still in
+ // the current paragraph (because of JavaScript and various "chats"(?)).
+ // This shouldn't be done for attributes which are used for
+ // the whole paragraph, because they could be from a paragraph style
+ // which can't be set. Because the attributes are inserted with
+ // SETATTR_DONTREPLACE, they should be able to be set later.
+ bSetAttr = ( nEndParaIdx < rEndPos.GetNodeIndex() &&
+ ((RES_MARGIN_FIRSTLINE != nWhich && RES_MARGIN_TEXTLEFT != nWhich) || !GetNumInfo().GetNumRule()) ) ||
+ ( !pAttr->IsLikePara() &&
+ nEndParaIdx == rEndPos.GetNodeIndex() &&
+ pAttr->GetEndContent() < nEndCnt &&
+ (isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)) ) ||
+ ( bBeforeTable &&
+ nEndParaIdx == rEndPos.GetNodeIndex() &&
+ !pAttr->GetEndContent() );
+ }
+ else
+ {
+ // Attributes in body nodes array section shouldn't be set if we are in a
+ // special nodes array section, but vice versa it's possible.
+ SwNodeOffset nEndOfIcons = m_xDoc->GetNodes().GetEndOfExtras().GetIndex();
+ bSetAttr = nEndParaIdx < rEndPos.GetNodeIndex() ||
+ rEndPos.GetNodeIndex() > nEndOfIcons ||
+ nEndParaIdx <= nEndOfIcons;
+ }
+
+ if( bSetAttr )
+ {
+ // The attribute shouldn't be in the list of temporary paragraph
+ // attributes, because then it would be deleted.
+ while( !m_aParaAttrs.empty() )
+ {
+ OSL_ENSURE( pAttr != m_aParaAttrs.back(),
+ "SetAttr: Attribute must not yet be set" );
+ m_aParaAttrs.pop_back();
+ }
+
+ // then set it
+ m_aSetAttrTab.erase( m_aSetAttrTab.begin() + n );
+
+ while( pAttr )
+ {
+ HTMLAttr *pPrev = pAttr->GetPrev();
+ if( !pAttr->m_bValid )
+ {
+ // invalid attributes can be deleted
+ delete pAttr;
+ pAttr = pPrev;
+ continue;
+ }
+
+ pCNd = pAttr->m_nStartPara.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ // because of the awful deleting of nodes an index can also
+ // point to an end node :-(
+ if ( (pAttr->GetStartParagraph() == pAttr->GetEndParagraph()) &&
+ !isTXTATR_NOEND(nWhich) )
+ {
+ // when the end index also points to the node, we don't
+ // need to set attributes anymore, except if it's a text attribute.
+ delete pAttr;
+ pAttr = pPrev;
+ continue;
+ }
+ pCNd = m_xDoc->GetNodes().GoNext( &(pAttr->m_nStartPara) );
+ if( pCNd )
+ pAttr->m_nStartContent = 0;
+ else
+ {
+ OSL_ENSURE( false, "SetAttr: GoNext() failed!" );
+ delete pAttr;
+ pAttr = pPrev;
+ continue;
+ }
+ }
+
+ // because of the deleting of BRs the start index can also
+ // point behind the end the text
+ if( pAttr->m_nStartContent > pCNd->Len() )
+ pAttr->m_nStartContent = pCNd->Len();
+ aAttrPam.GetPoint()->Assign( *pCNd, pAttr->m_nStartContent );
+
+ aAttrPam.SetMark();
+ if ( (pAttr->GetStartParagraph() != pAttr->GetEndParagraph()) &&
+ !isTXTATR_NOEND(nWhich) )
+ {
+ pCNd = pAttr->m_nEndPara.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ pCNd = SwNodes::GoPrevious( &(pAttr->m_nEndPara) );
+ if( pCNd )
+ pAttr->m_nEndContent = pCNd->Len();
+ else
+ {
+ OSL_ENSURE( false, "SetAttr: GoPrevious() failed!" );
+ aAttrPam.DeleteMark();
+ delete pAttr;
+ pAttr = pPrev;
+ continue;
+ }
+ }
+ }
+ else if( pAttr->IsLikePara() )
+ {
+ pAttr->m_nEndContent = pCNd->Len();
+ }
+
+ // because of the deleting of BRs the start index can also
+ // point behind the end the text
+ if( pAttr->m_nEndContent > pCNd->Len() )
+ pAttr->m_nEndContent = pCNd->Len();
+
+ aAttrPam.GetPoint()->Assign( *pCNd, pAttr->m_nEndContent );
+ if( bBeforeTable &&
+ aAttrPam.GetPoint()->GetNodeIndex() ==
+ rEndPos.GetNodeIndex() )
+ {
+ // If we're before inserting a table and the attribute ends
+ // in the current node, then we must end it in the previous
+ // node or discard it, if it starts in that node.
+ if( nWhich != RES_BREAK && nWhich != RES_PAGEDESC &&
+ !isTXTATR_NOEND(nWhich) )
+ {
+ if( aAttrPam.GetMark()->GetNodeIndex() !=
+ rEndPos.GetNodeIndex() )
+ {
+ OSL_ENSURE( !aAttrPam.GetPoint()->GetContentIndex(),
+ "Content-Position before table not 0???" );
+ aAttrPam.Move( fnMoveBackward );
+ }
+ else
+ {
+ aAttrPam.DeleteMark();
+ delete pAttr;
+ pAttr = pPrev;
+ continue;
+ }
+ }
+ }
+
+ switch( nWhich )
+ {
+ case RES_FLTR_BOOKMARK: // insert bookmark
+ {
+ const OUString sName( static_cast<SfxStringItem*>(pAttr->m_pItem.get())->GetValue() );
+ IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findMark( sName );
+ if( ppBkmk != pMarkAccess->getAllMarksEnd() &&
+ (*ppBkmk)->GetMarkStart() == *aAttrPam.GetPoint() )
+ break; // do not generate duplicates on this position
+ aAttrPam.DeleteMark();
+ const ::sw::mark::IMark* const pNewMark = pMarkAccess->makeMark(
+ aAttrPam,
+ sName,
+ IDocumentMarkAccess::MarkType::BOOKMARK,
+ ::sw::mark::InsertMode::New);
+
+ // jump to bookmark
+ if( JumpToMarks::Mark == m_eJumpTo && pNewMark->GetName() == m_sJmpMark )
+ {
+ m_bChkJumpMark = true;
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+ }
+ break;
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ {
+ SwFieldIds nFieldWhich =
+ pPostIts
+ ? static_cast<const SwFormatField *>(pAttr->m_pItem.get())->GetField()->GetTyp()->Which()
+ : SwFieldIds::Database;
+ if( pPostIts && (SwFieldIds::Postit == nFieldWhich ||
+ SwFieldIds::Script == nFieldWhich) )
+ {
+ pPostIts->emplace_front( pAttr );
+ }
+ else
+ {
+ aFields.emplace_back( pAttr);
+ }
+ }
+ aAttrPam.DeleteMark();
+ pAttr = pPrev;
+ continue;
+
+ // tdf#94088 expand RES_BACKGROUND to the new fill attribute
+ // definitions in the range [XATTR_FILL_FIRST .. XATTR_FILL_LAST].
+ // This is the right place in the future if the adapted fill attributes
+ // may be handled more directly in HTML import to handle them.
+ case RES_BACKGROUND:
+ {
+ const SvxBrushItem& rBrush = static_cast< SvxBrushItem& >(*pAttr->m_pItem);
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aNewSet(m_xDoc->GetAttrPool());
+
+ setSvxBrushItemAsFillAttributesToTargetSet(rBrush, aNewSet);
+ m_xDoc->getIDocumentContentOperations().InsertItemSet(aAttrPam, aNewSet, SetAttrMode::DONTREPLACE);
+ break;
+ }
+
+ case RES_LR_SPACE:
+ assert(false);
+ break;
+
+ case RES_MARGIN_FIRSTLINE:
+ case RES_MARGIN_TEXTLEFT:
+ case RES_MARGIN_RIGHT:
+ if( aAttrPam.GetPoint()->GetNodeIndex() ==
+ aAttrPam.GetMark()->GetNodeIndex())
+ {
+ // because of numbering set this attribute directly at node
+ pCNd->SetAttr( *pAttr->m_pItem );
+ break;
+ }
+ OSL_ENSURE( false,
+ "LRSpace set over multiple paragraphs!" );
+ [[fallthrough]]; // (shouldn't reach this point anyway)
+ default:
+
+ // maybe jump to a bookmark
+ if( RES_TXTATR_INETFMT == nWhich &&
+ JumpToMarks::Mark == m_eJumpTo &&
+ m_sJmpMark == static_cast<SwFormatINetFormat*>(pAttr->m_pItem.get())->GetName() )
+ {
+ m_bChkJumpMark = true;
+ m_eJumpTo = JumpToMarks::NONE;
+ }
+
+ m_xDoc->getIDocumentContentOperations().InsertPoolItem( aAttrPam, *pAttr->m_pItem, SetAttrMode::DONTREPLACE );
+ }
+ aAttrPam.DeleteMark();
+
+ delete pAttr;
+ pAttr = pPrev;
+ }
+ }
+ }
+
+ for( auto n = m_aMoveFlyFrames.size(); n; )
+ {
+ SwFrameFormat *pFrameFormat = m_aMoveFlyFrames[--n]->GetFrameFormat();
+ if (!pFrameFormat)
+ {
+ SAL_WARN("sw.html", "SwFrameFormat deleted during import");
+ m_aMoveFlyFrames.erase( m_aMoveFlyFrames.begin() + n );
+ m_aMoveFlyCnts.erase( m_aMoveFlyCnts.begin() + n );
+ continue;
+ }
+
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ OSL_ENSURE( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId(),
+ "Only At-Para flys need special handling" );
+ SwNodeOffset nFlyParaIdx = rAnchor.GetAnchorNode()->GetIndex();
+ bool bMoveFly;
+ if( bChkEnd )
+ {
+ bMoveFly = nFlyParaIdx < rEndPos.GetNodeIndex() ||
+ ( nFlyParaIdx == rEndPos.GetNodeIndex() &&
+ m_aMoveFlyCnts[n] < nEndCnt );
+ }
+ else
+ {
+ SwNodeOffset nEndOfIcons = m_xDoc->GetNodes().GetEndOfExtras().GetIndex();
+ bMoveFly = nFlyParaIdx < rEndPos.GetNodeIndex() ||
+ rEndPos.GetNodeIndex() > nEndOfIcons ||
+ nFlyParaIdx <= nEndOfIcons;
+ }
+ if( bMoveFly )
+ {
+ pFrameFormat->DelFrames();
+ *aAttrPam.GetPoint() = *rAnchor.GetContentAnchor();
+ aAttrPam.GetPoint()->SetContent( m_aMoveFlyCnts[n] );
+ SwFormatAnchor aAnchor( rAnchor );
+ aAnchor.SetType( RndStdIds::FLY_AT_CHAR );
+ aAnchor.SetAnchor( aAttrPam.GetPoint() );
+ pFrameFormat->SetFormatAttr( aAnchor );
+
+ const SwFormatHoriOrient& rHoriOri = pFrameFormat->GetHoriOrient();
+ if( text::HoriOrientation::LEFT == rHoriOri.GetHoriOrient() )
+ {
+ SwFormatHoriOrient aHoriOri( rHoriOri );
+ aHoriOri.SetRelationOrient( text::RelOrientation::CHAR );
+ pFrameFormat->SetFormatAttr( aHoriOri );
+ }
+ const SwFormatVertOrient& rVertOri = pFrameFormat->GetVertOrient();
+ if( text::VertOrientation::TOP == rVertOri.GetVertOrient() )
+ {
+ SwFormatVertOrient aVertOri( rVertOri );
+ aVertOri.SetRelationOrient( text::RelOrientation::CHAR );
+ pFrameFormat->SetFormatAttr( aVertOri );
+ }
+
+ pFrameFormat->MakeFrames();
+ m_aMoveFlyFrames.erase( m_aMoveFlyFrames.begin() + n );
+ m_aMoveFlyCnts.erase( m_aMoveFlyCnts.begin() + n );
+ }
+ }
+ for (auto & field : aFields)
+ {
+ pCNd = field->m_nStartPara.GetNode().GetContentNode();
+ aAttrPam.GetPoint()->Assign( *pCNd, field->m_nStartContent );
+
+ if( bBeforeTable &&
+ aAttrPam.GetPoint()->GetNodeIndex() == rEndPos.GetNodeIndex() )
+ {
+ OSL_ENSURE( !bBeforeTable, "Aha, the case does occur" );
+ OSL_ENSURE( !aAttrPam.GetPoint()->GetContentIndex(),
+ "Content-Position before table not 0???" );
+ // !!!
+ aAttrPam.Move( fnMoveBackward );
+ }
+
+ m_xDoc->getIDocumentContentOperations().InsertPoolItem( aAttrPam, *field->m_pItem );
+
+ field.reset();
+ }
+ aFields.clear();
+}
+
+void SwHTMLParser::NewAttr(const std::shared_ptr<HTMLAttrTable>& rAttrTable, HTMLAttr **ppAttr, const SfxPoolItem& rItem )
+{
+ // Font height and font colour as well as escape attributes may not be
+ // combined. Therefore they're saved in a list and in it the last opened
+ // attribute is at the beginning and count is always one. For all other
+ // attributes count is just incremented.
+ if( *ppAttr )
+ {
+ HTMLAttr *pAttr = new HTMLAttr(*m_pPam->GetPoint(), rItem, ppAttr, rAttrTable);
+ pAttr->InsertNext( *ppAttr );
+ (*ppAttr) = pAttr;
+ }
+ else
+ (*ppAttr) = new HTMLAttr(*m_pPam->GetPoint(), rItem, ppAttr, rAttrTable);
+}
+
+bool SwHTMLParser::EndAttr( HTMLAttr* pAttr, bool bChkEmpty )
+{
+ bool bRet = true;
+
+ // The list header is saved in the attribute.
+ HTMLAttr **ppHead = pAttr->m_ppHead;
+
+ OSL_ENSURE( ppHead, "No list header attribute found!" );
+
+ // save the current position as end position
+ const SwPosition* pEndPos = m_pPam->GetPoint();
+ sal_Int32 nEndCnt = m_pPam->GetPoint()->GetContentIndex();
+
+ // Is the last started or an earlier started attribute being ended?
+ HTMLAttr *pLast = nullptr;
+ if( ppHead && pAttr != *ppHead )
+ {
+ // The last started attribute isn't being ended
+
+ // Then we look for attribute which was started immediately afterwards,
+ // which has also not yet been ended (otherwise it would no longer be
+ // in the list).
+ pLast = *ppHead;
+ while( pLast && pLast->GetNext() != pAttr )
+ pLast = pLast->GetNext();
+
+ OSL_ENSURE( pLast, "Attribute not found in own list!" );
+ }
+
+ bool bMoveBack = false;
+ sal_uInt16 nWhich = pAttr->m_pItem->Which();
+ if( !nEndCnt && RES_PARATR_BEGIN <= nWhich &&
+ pEndPos->GetNodeIndex() != pAttr->GetStartParagraph().GetIndex() )
+ {
+ // Then move back one position in the content!
+ bMoveBack = m_pPam->Move( fnMoveBackward );
+ nEndCnt = m_pPam->GetPoint()->GetContentIndex();
+ }
+
+ // now end the attribute
+ HTMLAttr *pNext = pAttr->GetNext();
+
+ bool bInsert;
+ sal_uInt16 nScriptItem = 0;
+ bool bScript = false;
+ // does it have a non-empty range?
+ if( !bChkEmpty || (RES_PARATR_BEGIN <= nWhich && bMoveBack) ||
+ RES_PAGEDESC == nWhich || RES_BREAK == nWhich ||
+ pEndPos->GetNodeIndex() != pAttr->GetStartParagraph().GetIndex() ||
+ nEndCnt != pAttr->GetStartContent() )
+ {
+ bInsert = true;
+ // We do some optimization for script dependent attributes here.
+ if( pEndPos->GetNodeIndex() == pAttr->GetStartParagraph().GetIndex() )
+ {
+ lcl_swhtml_getItemInfo( *pAttr, bScript, nScriptItem );
+ }
+ }
+ else
+ {
+ bInsert = false;
+ }
+
+ const SwTextNode *pTextNd = (bInsert && bScript) ?
+ pAttr->GetStartParagraph().GetNode().GetTextNode() :
+ nullptr;
+
+ if (pTextNd)
+ {
+ const OUString& rText = pTextNd->GetText();
+ sal_uInt16 nScriptText = g_pBreakIt->GetBreakIter()->getScriptType(
+ rText, pAttr->GetStartContent() );
+ sal_Int32 nScriptEnd = g_pBreakIt->GetBreakIter()
+ ->endOfScript( rText, pAttr->GetStartContent(), nScriptText );
+ while (nScriptEnd < nEndCnt && nScriptEnd != -1)
+ {
+ if( nScriptItem == nScriptText )
+ {
+ HTMLAttr *pSetAttr = pAttr->Clone( pEndPos->GetNode(), nScriptEnd );
+ pSetAttr->ClearPrev();
+ if( pNext )
+ pNext->InsertPrev( pSetAttr );
+ else
+ {
+ if (pSetAttr->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pSetAttr );
+ else
+ m_aSetAttrTab.push_back( pSetAttr );
+ }
+ }
+ pAttr->m_nStartContent = nScriptEnd;
+ nScriptText = g_pBreakIt->GetBreakIter()->getScriptType(
+ rText, nScriptEnd );
+ nScriptEnd = g_pBreakIt->GetBreakIter()
+ ->endOfScript( rText, nScriptEnd, nScriptText );
+ }
+ bInsert = nScriptItem == nScriptText;
+ }
+ if( bInsert )
+ {
+ pAttr->m_nEndPara = pEndPos->GetNode();
+ pAttr->m_nEndContent = nEndCnt;
+ pAttr->m_bInsAtStart = RES_TXTATR_INETFMT != nWhich &&
+ RES_TXTATR_CHARFMT != nWhich;
+
+ if( !pNext )
+ {
+ // No open attributes of that type exists any longer, so all
+ // can be set. Except they depend on another attribute, then
+ // they're appended there.
+ if (pAttr->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pAttr );
+ else
+ m_aSetAttrTab.push_back( pAttr );
+ }
+ else
+ {
+ // There are other open attributes of that type,
+ // therefore the setting must be postponed.
+ // Hence the current attribute is added at the end
+ // of the Prev-List of the successor.
+ pNext->InsertPrev( pAttr );
+ }
+ }
+ else
+ {
+ // Then don't insert, but delete. Because of the "faking" of styles
+ // by hard attributing there can be also other empty attributes in the
+ // Prev-List, which must be set anyway.
+ HTMLAttr *pPrev = pAttr->GetPrev();
+ bRet = false;
+ delete pAttr;
+
+ if( pPrev )
+ {
+ // The previous attributes must be set anyway.
+ if( pNext )
+ pNext->InsertPrev( pPrev );
+ else
+ {
+ if (pPrev->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pPrev );
+ else
+ m_aSetAttrTab.push_back( pPrev );
+ }
+ }
+
+ }
+
+ // If the first attribute of the list was set, then the list header
+ // must be corrected as well.
+ if( pLast )
+ pLast->m_pNext = pNext;
+ else if( ppHead )
+ *ppHead = pNext;
+
+ if( bMoveBack )
+ m_pPam->Move( fnMoveForward );
+
+ return bRet;
+}
+
+void SwHTMLParser::DeleteAttr( HTMLAttr* pAttr )
+{
+ // preliminary paragraph attributes are not allowed here, they could
+ // be set here and then the pointers become invalid!
+ OSL_ENSURE(m_aParaAttrs.empty(),
+ "Danger: there are non-final paragraph attributes");
+ m_aParaAttrs.clear();
+
+ // The list header is saved in the attribute
+ HTMLAttr **ppHead = pAttr->m_ppHead;
+
+ OSL_ENSURE( ppHead, "no list header attribute found!" );
+
+ // Is the last started or an earlier started attribute being removed?
+ HTMLAttr *pLast = nullptr;
+ if( ppHead && pAttr != *ppHead )
+ {
+ // The last started attribute isn't being ended
+
+ // Then we look for attribute which was started immediately afterwards,
+ // which has also not yet been ended (otherwise it would no longer be
+ // in the list).
+ pLast = *ppHead;
+ while( pLast && pLast->GetNext() != pAttr )
+ pLast = pLast->GetNext();
+
+ OSL_ENSURE( pLast, "Attribute not found in own list!" );
+ }
+
+ // now delete the attribute
+ HTMLAttr *pNext = pAttr->GetNext();
+ HTMLAttr *pPrev = pAttr->GetPrev();
+ //hold ref to xAttrTab until end of scope to ensure *ppHead validity
+ std::shared_ptr<HTMLAttrTable> xKeepAlive(pAttr->m_xAttrTab);
+ delete pAttr;
+
+ if( pPrev )
+ {
+ // The previous attributes must be set anyway.
+ if( pNext )
+ pNext->InsertPrev( pPrev );
+ else
+ {
+ if (pPrev->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pPrev );
+ else
+ m_aSetAttrTab.push_back( pPrev );
+ }
+ }
+
+ // If the first attribute of the list was deleted, then the list header
+ // must be corrected as well.
+ if( pLast )
+ pLast->m_pNext = pNext;
+ else if( ppHead )
+ *ppHead = pNext;
+}
+
+void SwHTMLParser::SaveAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab)
+{
+ // preliminary paragraph attributes are not allowed here, they could
+ // be set here and then the pointers become invalid!
+ OSL_ENSURE(m_aParaAttrs.empty(),
+ "Danger: there are non-final paragraph attributes");
+ m_aParaAttrs.clear();
+
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get());
+
+ for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes, ++pSaveAttributes)
+ {
+ *pSaveAttributes = *pHTMLAttributes;
+
+ HTMLAttr *pAttr = *pSaveAttributes;
+ while (pAttr)
+ {
+ pAttr->SetHead(pSaveAttributes, rNewAttrTab);
+ pAttr = pAttr->GetNext();
+ }
+
+ *pHTMLAttributes = nullptr;
+ }
+}
+
+void SwHTMLParser::SplitAttrTab( std::shared_ptr<HTMLAttrTable> const & rNewAttrTab,
+ bool bMoveEndBack )
+{
+ // preliminary paragraph attributes are not allowed here, they could
+ // be set here and then the pointers become invalid!
+ OSL_ENSURE(m_aParaAttrs.empty(),
+ "Danger: there are non-final paragraph attributes");
+ m_aParaAttrs.clear();
+
+ SwNodeIndex nEndIdx( m_pPam->GetPoint()->GetNode() );
+
+ // close all still open attributes and re-open them after the table
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get());
+ bool bSetAttr = true;
+ const sal_Int32 nSttCnt = m_pPam->GetPoint()->GetContentIndex();
+ sal_Int32 nEndCnt = nSttCnt;
+
+ if( bMoveEndBack )
+ {
+ SwNodeOffset nOldEnd = nEndIdx.GetIndex();
+ SwNodeOffset nTmpIdx;
+ if( ( nTmpIdx = m_xDoc->GetNodes().GetEndOfExtras().GetIndex()) >= nOldEnd ||
+ ( nTmpIdx = m_xDoc->GetNodes().GetEndOfAutotext().GetIndex()) >= nOldEnd )
+ {
+ nTmpIdx = m_xDoc->GetNodes().GetEndOfInserts().GetIndex();
+ }
+ SwContentNode* pCNd = SwNodes::GoPrevious(&nEndIdx);
+
+ // Don't set attributes, when the PaM was moved outside of the content area.
+ bSetAttr = pCNd && nTmpIdx < nEndIdx.GetIndex();
+
+ nEndCnt = (bSetAttr ? pCNd->Len() : 0);
+ }
+ for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; (++pHTMLAttributes, ++pSaveAttributes))
+ {
+ HTMLAttr *pAttr = *pHTMLAttributes;
+ *pSaveAttributes = nullptr;
+ while( pAttr )
+ {
+ HTMLAttr *pNext = pAttr->GetNext();
+ HTMLAttr *pPrev = pAttr->GetPrev();
+
+ if( bSetAttr &&
+ ( pAttr->GetStartParagraphIdx() < nEndIdx.GetIndex() ||
+ (pAttr->GetStartParagraph() == nEndIdx &&
+ pAttr->GetStartContent() != nEndCnt) ) )
+ {
+ // The attribute must be set before the list. We need the
+ // original and therefore we clone it, because pointer to the
+ // attribute exist in the other contexts. The Next-List is lost
+ // in doing so, but the Previous-List is preserved.
+ HTMLAttr *pSetAttr = pAttr->Clone( nEndIdx.GetNode(), nEndCnt );
+
+ if( pNext )
+ pNext->InsertPrev( pSetAttr );
+ else
+ {
+ if (pSetAttr->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pSetAttr );
+ else
+ m_aSetAttrTab.push_back( pSetAttr );
+ }
+ }
+ else if( pPrev )
+ {
+ // If the attribute doesn't need to be set before the table, then
+ // the previous attributes must still be set.
+ if( pNext )
+ pNext->InsertPrev( pPrev );
+ else
+ {
+ if (pPrev->m_bInsAtStart)
+ m_aSetAttrTab.push_front( pPrev );
+ else
+ m_aSetAttrTab.push_back( pPrev );
+ }
+ }
+
+ // set the start of the attribute anew and break link
+ pAttr->Reset(m_pPam->GetPoint()->GetNode(), nSttCnt, pSaveAttributes, rNewAttrTab);
+
+ if (*pSaveAttributes)
+ {
+ HTMLAttr *pSAttr = *pSaveAttributes;
+ while( pSAttr->GetNext() )
+ pSAttr = pSAttr->GetNext();
+ pSAttr->InsertNext( pAttr );
+ }
+ else
+ *pSaveAttributes = pAttr;
+
+ pAttr = pNext;
+ }
+
+ *pHTMLAttributes = nullptr;
+ }
+}
+
+void SwHTMLParser::RestoreAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab)
+{
+ // preliminary paragraph attributes are not allowed here, they could
+ // be set here and then the pointers become invalid!
+ OSL_ENSURE(m_aParaAttrs.empty(),
+ "Danger: there are non-final paragraph attributes");
+ m_aParaAttrs.clear();
+
+ HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
+ HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get());
+
+ for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes, ++pSaveAttributes)
+ {
+ OSL_ENSURE(!*pHTMLAttributes, "The attribute table is not empty!");
+
+ *pHTMLAttributes = *pSaveAttributes;
+
+ HTMLAttr *pAttr = *pHTMLAttributes;
+ while (pAttr)
+ {
+ OSL_ENSURE( !pAttr->GetPrev() || !pAttr->GetPrev()->m_ppHead,
+ "Previous attribute has still a header" );
+ pAttr->SetHead(pHTMLAttributes, m_xAttrTab);
+ pAttr = pAttr->GetNext();
+ }
+
+ *pSaveAttributes = nullptr;
+ }
+}
+
+void SwHTMLParser::InsertAttr( const SfxPoolItem& rItem, bool bInsAtStart )
+{
+ HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), rItem, nullptr, std::shared_ptr<HTMLAttrTable>());
+ if (bInsAtStart)
+ m_aSetAttrTab.push_front( pTmp );
+ else
+ m_aSetAttrTab.push_back( pTmp );
+}
+
+void SwHTMLParser::InsertAttrs( std::deque<std::unique_ptr<HTMLAttr>> rAttrs )
+{
+ while( !rAttrs.empty() )
+ {
+ std::unique_ptr<HTMLAttr> pAttr = std::move(rAttrs.front());
+ InsertAttr( pAttr->GetItem(), false );
+ rAttrs.pop_front();
+ }
+}
+
+void SwHTMLParser::NewStdAttr( HtmlTokenId nToken )
+{
+ OUString aId, aStyle, aLang, aDir;
+ OUString aClass;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ // parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ {
+ if( HtmlTokenId::SPAN_ON != nToken || aClass.isEmpty() ||
+ !CreateContainer( aClass, aItemSet, aPropInfo, xCntxt.get() ) )
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+ }
+ }
+
+ // save the context
+ PushContext(xCntxt);
+}
+
+void SwHTMLParser::NewStdAttr( HtmlTokenId nToken,
+ HTMLAttr **ppAttr, const SfxPoolItem & rItem,
+ HTMLAttr **ppAttr2, const SfxPoolItem *pItem2,
+ HTMLAttr **ppAttr3, const SfxPoolItem *pItem3 )
+{
+ OUString aId, aStyle, aClass, aLang, aDir;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ // parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ aItemSet.Put( rItem );
+ if( pItem2 )
+ aItemSet.Put( *pItem2 );
+ if( pItem3 )
+ aItemSet.Put( *pItem3 );
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+ }
+ else
+ {
+ InsertAttr( ppAttr ,rItem, xCntxt.get() );
+ if( pItem2 )
+ {
+ OSL_ENSURE( ppAttr2, "missing table entry for item2" );
+ InsertAttr( ppAttr2, *pItem2, xCntxt.get() );
+ }
+ if( pItem3 )
+ {
+ OSL_ENSURE( ppAttr3, "missing table entry for item3" );
+ InsertAttr( ppAttr3, *pItem3, xCntxt.get() );
+ }
+ }
+
+ // save the context
+ PushContext(xCntxt);
+}
+
+void SwHTMLParser::EndTag( HtmlTokenId nToken )
+{
+ // fetch context
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(getOnToken(nToken)));
+ if (xCntxt)
+ {
+ // and maybe end the attributes
+ EndContext(xCntxt.get());
+ }
+}
+
+void SwHTMLParser::NewBasefontAttr()
+{
+ OUString aId, aStyle, aClass, aLang, aDir;
+ sal_uInt16 nSize = 3;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::SIZE:
+ nSize = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ if( nSize < 1 )
+ nSize = 1;
+
+ if( nSize > 7 )
+ nSize = 7;
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::BASEFONT_ON));
+
+ // parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ //CJK has different defaults
+ SvxFontHeightItem aFontHeight( m_aFontHeights[nSize-1], 100, RES_CHRATR_FONTSIZE );
+ aItemSet.Put( aFontHeight );
+ SvxFontHeightItem aFontHeightCJK( m_aFontHeights[nSize-1], 100, RES_CHRATR_CJK_FONTSIZE );
+ aItemSet.Put( aFontHeightCJK );
+ //Complex type can contain so many types of letters,
+ //that it's not really worthy to bother, IMO.
+ //Still, I have set a default.
+ SvxFontHeightItem aFontHeightCTL( m_aFontHeights[nSize-1], 100, RES_CHRATR_CTL_FONTSIZE );
+ aItemSet.Put( aFontHeightCTL );
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+ }
+ else
+ {
+ SvxFontHeightItem aFontHeight( m_aFontHeights[nSize-1], 100, RES_CHRATR_FONTSIZE );
+ InsertAttr( &m_xAttrTab->pFontHeight, aFontHeight, xCntxt.get() );
+ SvxFontHeightItem aFontHeightCJK( m_aFontHeights[nSize-1], 100, RES_CHRATR_CJK_FONTSIZE );
+ InsertAttr( &m_xAttrTab->pFontHeightCJK, aFontHeightCJK, xCntxt.get() );
+ SvxFontHeightItem aFontHeightCTL( m_aFontHeights[nSize-1], 100, RES_CHRATR_CTL_FONTSIZE );
+ InsertAttr( &m_xAttrTab->pFontHeightCTL, aFontHeightCTL, xCntxt.get() );
+ }
+
+ // save the context
+ PushContext(xCntxt);
+
+ // save the font size
+ m_aBaseFontStack.push_back( nSize );
+}
+
+void SwHTMLParser::EndBasefontAttr()
+{
+ EndTag( HtmlTokenId::BASEFONT_ON );
+
+ // avoid stack underflow in tables
+ if( m_aBaseFontStack.size() > m_nBaseFontStMin )
+ m_aBaseFontStack.erase( m_aBaseFontStack.begin() + m_aBaseFontStack.size() - 1 );
+}
+
+void SwHTMLParser::NewFontAttr( HtmlTokenId nToken )
+{
+ sal_uInt16 nBaseSize =
+ ( m_aBaseFontStack.size() > m_nBaseFontStMin
+ ? (m_aBaseFontStack[m_aBaseFontStack.size()-1] & FONTSIZE_MASK)
+ : 3 );
+ sal_uInt16 nFontSize =
+ ( m_aFontStack.size() > m_nFontStMin
+ ? (m_aFontStack[m_aFontStack.size()-1] & FONTSIZE_MASK)
+ : nBaseSize );
+
+ OUString aFace, aId, aStyle, aClass, aLang, aDir;
+ Color aColor;
+ sal_uLong nFontHeight = 0; // actual font height to set
+ sal_uInt16 nSize = 0; // font height in Netscape notation (1-7)
+ bool bColor = false;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::SIZE:
+ if( HtmlTokenId::FONT_ON==nToken && !rOption.GetString().isEmpty() )
+ {
+ sal_Int32 nSSize;
+ if( '+' == rOption.GetString()[0] ||
+ '-' == rOption.GetString()[0] )
+ nSSize = o3tl::saturating_add<sal_Int32>(nBaseSize, rOption.GetSNumber());
+ else
+ nSSize = static_cast<sal_Int32>(rOption.GetNumber());
+
+ if( nSSize < 1 )
+ nSSize = 1;
+ else if( nSSize > 7 )
+ nSSize = 7;
+
+ nSize = o3tl::narrowing<sal_uInt16>(nSSize);
+ nFontHeight = m_aFontHeights[nSize-1];
+ }
+ break;
+ case HtmlOptionId::COLOR:
+ if( HtmlTokenId::FONT_ON==nToken )
+ {
+ rOption.GetColor( aColor );
+ bColor = true;
+ }
+ break;
+ case HtmlOptionId::FACE:
+ if( HtmlTokenId::FONT_ON==nToken )
+ aFace = rOption.GetString();
+ break;
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ if( HtmlTokenId::FONT_ON != nToken )
+ {
+ // HTML_BIGPRINT_ON or HTML_SMALLPRINT_ON
+
+ // In headings the current heading sets the font height
+ // and not BASEFONT.
+ const SwFormatColl *pColl = GetCurrFormatColl();
+ sal_uInt16 nPoolId = pColl ? pColl->GetPoolFormatId() : 0;
+ if( nPoolId>=RES_POOLCOLL_HEADLINE1 &&
+ nPoolId<=RES_POOLCOLL_HEADLINE6 )
+ {
+ // If the font height in the heading wasn't changed yet,
+ // then take the one from the style.
+ if( m_nFontStHeadStart==m_aFontStack.size() )
+ nFontSize = static_cast< sal_uInt16 >(6 - (nPoolId - RES_POOLCOLL_HEADLINE1));
+ }
+ else
+ nPoolId = 0;
+
+ if( HtmlTokenId::BIGPRINT_ON == nToken )
+ nSize = ( nFontSize<7 ? nFontSize+1 : 7 );
+ else
+ nSize = ( nFontSize>1 ? nFontSize-1 : 1 );
+
+ // If possible in headlines we fetch the new font height
+ // from the style.
+ if( nPoolId && nSize>=1 && nSize <=6 )
+ nFontHeight =
+ m_pCSS1Parser->GetTextCollFromPool(
+ RES_POOLCOLL_HEADLINE1+6-nSize )->GetSize().GetHeight();
+ else
+ nFontHeight = m_aFontHeights[nSize-1];
+ }
+
+ OSL_ENSURE( !nSize == !nFontHeight, "HTML-Font-Size != Font-Height" );
+
+ OUString aFontName;
+ const OUString aStyleName;
+ FontFamily eFamily = FAMILY_DONTKNOW; // family and pitch,
+ FontPitch ePitch = PITCH_DONTKNOW; // if not found
+ rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
+
+ if( !aFace.isEmpty() && !m_pCSS1Parser->IsIgnoreFontFamily() )
+ {
+ const FontList *pFList = nullptr;
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+ if( pDocSh )
+ {
+ const SvxFontListItem *pFListItem =
+ static_cast<const SvxFontListItem *>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
+ if( pFListItem )
+ pFList = pFListItem->GetFontList();
+ }
+
+ bool bFound = false;
+ sal_Int32 nStrPos = 0;
+ while( nStrPos!= -1 )
+ {
+ OUString aFName = aFace.getToken( 0, ',', nStrPos );
+ aFName = comphelper::string::strip(aFName, ' ');
+ if( !aFName.isEmpty() )
+ {
+ if( !bFound && pFList )
+ {
+ sal_Handle hFont = pFList->GetFirstFontMetric( aFName );
+ if( nullptr != hFont )
+ {
+ const FontMetric& rFMetric = FontList::GetFontMetric( hFont );
+ if( RTL_TEXTENCODING_DONTKNOW != rFMetric.GetCharSet() )
+ {
+ bFound = true;
+ if( RTL_TEXTENCODING_SYMBOL == rFMetric.GetCharSet() )
+ eEnc = RTL_TEXTENCODING_SYMBOL;
+ }
+ }
+ }
+ if( !aFontName.isEmpty() )
+ aFontName += ";";
+ aFontName += aFName;
+ }
+ }
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ // parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( nFontHeight )
+ {
+ SvxFontHeightItem aFontHeight( nFontHeight, 100, RES_CHRATR_FONTSIZE );
+ aItemSet.Put( aFontHeight );
+ SvxFontHeightItem aFontHeightCJK( nFontHeight, 100, RES_CHRATR_CJK_FONTSIZE );
+ aItemSet.Put( aFontHeightCJK );
+ SvxFontHeightItem aFontHeightCTL( nFontHeight, 100, RES_CHRATR_CTL_FONTSIZE );
+ aItemSet.Put( aFontHeightCTL );
+ }
+ if( bColor )
+ aItemSet.Put( SvxColorItem(aColor, RES_CHRATR_COLOR) );
+ if( !aFontName.isEmpty() )
+ {
+ SvxFontItem aFont( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_FONT );
+ aItemSet.Put( aFont );
+ SvxFontItem aFontCJK( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CJK_FONT );
+ aItemSet.Put( aFontCJK );
+ SvxFontItem aFontCTL( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CTL_FONT );
+ aItemSet.Put( aFontCTL );
+ }
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+ }
+ else
+ {
+ if( nFontHeight )
+ {
+ SvxFontHeightItem aFontHeight( nFontHeight, 100, RES_CHRATR_FONTSIZE );
+ InsertAttr( &m_xAttrTab->pFontHeight, aFontHeight, xCntxt.get() );
+ SvxFontHeightItem aFontHeightCJK( nFontHeight, 100, RES_CHRATR_CJK_FONTSIZE );
+ InsertAttr( &m_xAttrTab->pFontHeight, aFontHeightCJK, xCntxt.get() );
+ SvxFontHeightItem aFontHeightCTL( nFontHeight, 100, RES_CHRATR_CTL_FONTSIZE );
+ InsertAttr( &m_xAttrTab->pFontHeight, aFontHeightCTL, xCntxt.get() );
+ }
+ if( bColor )
+ InsertAttr( &m_xAttrTab->pFontColor, SvxColorItem(aColor, RES_CHRATR_COLOR), xCntxt.get() );
+ if( !aFontName.isEmpty() )
+ {
+ SvxFontItem aFont( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_FONT );
+ InsertAttr( &m_xAttrTab->pFont, aFont, xCntxt.get() );
+ SvxFontItem aFontCJK( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CJK_FONT );
+ InsertAttr( &m_xAttrTab->pFont, aFontCJK, xCntxt.get() );
+ SvxFontItem aFontCTL( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CTL_FONT );
+ InsertAttr( &m_xAttrTab->pFont, aFontCTL, xCntxt.get() );
+ }
+ }
+
+ // save the context
+ PushContext(xCntxt);
+
+ m_aFontStack.push_back( nSize );
+}
+
+void SwHTMLParser::EndFontAttr( HtmlTokenId nToken )
+{
+ EndTag( nToken );
+
+ // avoid stack underflow in tables
+ if( m_aFontStack.size() > m_nFontStMin )
+ m_aFontStack.erase( m_aFontStack.begin() + m_aFontStack.size() - 1 );
+}
+
+void SwHTMLParser::NewPara()
+{
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_SPACE );
+ else
+ AddParSpace();
+
+ m_eParaAdjust = SvxAdjust::End;
+ OUString aId, aStyle, aClass, aLang, aDir;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ m_eParaAdjust = rOption.GetEnum( aHTMLPAlignTable, m_eParaAdjust );
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ case HtmlOptionId::XML_SPACE:
+ if (rOption.GetString() == "preserve")
+ SetPreserveSpaces(true);
+ break;
+
+ default: break;
+ }
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(
+ !aClass.isEmpty() ? new HTMLAttrContext( HtmlTokenId::PARABREAK_ON,
+ RES_POOLCOLL_TEXT, aClass )
+ : new HTMLAttrContext( HtmlTokenId::PARABREAK_ON ));
+
+ // parse styles (Don't consider class. This is only possible as long as none of
+ // the CSS1 properties of the class must be formatted hard!!!)
+ if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
+ {
+ OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
+ "Class is not considered" );
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
+
+ if (aPropInfo.m_bPreserveSpace)
+ SetPreserveSpaces(true);
+ }
+ }
+
+ if( SvxAdjust::End != m_eParaAdjust )
+ InsertAttr( &m_xAttrTab->pAdjust, SvxAdjustItem(m_eParaAdjust, RES_PARATR_ADJUST), xCntxt.get() );
+
+ // and push on stack
+ PushContext( xCntxt );
+
+ // set the current style or its attributes
+ SetTextCollAttrs( !aClass.isEmpty() ? m_aContexts.back().get() : nullptr );
+
+ // progress bar
+ ShowStatline();
+
+ OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE, "Now an open paragraph element will be lost." );
+ m_nOpenParaToken = HtmlTokenId::PARABREAK_ON;
+}
+
+void SwHTMLParser::EndPara( bool bReal )
+{
+ if (HtmlTokenId::LI_ON==m_nOpenParaToken && m_xTable)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const SwNumRule *pNumRule = m_pPam->GetPointNode().GetTextNode()->GetNumRule();
+ OSL_ENSURE( pNumRule, "Where is the NumRule" );
+#endif
+ }
+
+ // Netscape skips empty paragraphs, we do the same; unless in XHTML mode, which prefers mapping
+ // the source document to the doc model 1:1 if possible.
+ if( bReal )
+ {
+ if (m_pPam->GetPoint()->GetContentIndex() || m_bXHTML)
+ AppendTextNode( AM_SPACE );
+ else
+ AddParSpace();
+ }
+
+ // If a DD or DT was open, it's an implied definition list,
+ // which must be closed now.
+ if( (m_nOpenParaToken == HtmlTokenId::DT_ON || m_nOpenParaToken == HtmlTokenId::DD_ON) &&
+ m_nDefListDeep)
+ {
+ m_nDefListDeep--;
+ }
+
+ // Pop the context of the stack. It can also be from an
+ // implied opened definition list.
+ std::unique_ptr<HTMLAttrContext> xCntxt(
+ PopContext( m_nOpenParaToken != HtmlTokenId::NONE ? getOnToken(m_nOpenParaToken) : HtmlTokenId::PARABREAK_ON ));
+
+ // close attribute
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
+ xCntxt.reset();
+ }
+
+ // reset the existing style
+ if( bReal )
+ SetTextCollAttrs();
+
+ m_nOpenParaToken = HtmlTokenId::NONE;
+ SetPreserveSpaces(false);
+}
+
+void SwHTMLParser::NewHeading( HtmlTokenId nToken )
+{
+ m_eParaAdjust = SvxAdjust::End;
+
+ OUString aId, aStyle, aClass, aLang, aDir;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::ALIGN:
+ m_eParaAdjust = rOption.GetEnum( aHTMLPAlignTable, m_eParaAdjust );
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // open a new paragraph
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_SPACE );
+ else
+ AddParSpace();
+
+ // search for the matching style
+ sal_uInt16 nTextColl;
+ switch( nToken )
+ {
+ case HtmlTokenId::HEAD1_ON: nTextColl = RES_POOLCOLL_HEADLINE1; break;
+ case HtmlTokenId::HEAD2_ON: nTextColl = RES_POOLCOLL_HEADLINE2; break;
+ case HtmlTokenId::HEAD3_ON: nTextColl = RES_POOLCOLL_HEADLINE3; break;
+ case HtmlTokenId::HEAD4_ON: nTextColl = RES_POOLCOLL_HEADLINE4; break;
+ case HtmlTokenId::HEAD5_ON: nTextColl = RES_POOLCOLL_HEADLINE5; break;
+ case HtmlTokenId::HEAD6_ON: nTextColl = RES_POOLCOLL_HEADLINE6; break;
+ default: nTextColl = RES_POOLCOLL_STANDARD; break;
+ }
+
+ // create the context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nTextColl, aClass));
+
+ // parse styles (regarding class see also NewPara)
+ if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
+ {
+ OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
+ "Class is not considered" );
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
+ }
+ }
+
+ if( SvxAdjust::End != m_eParaAdjust )
+ InsertAttr( &m_xAttrTab->pAdjust, SvxAdjustItem(m_eParaAdjust, RES_PARATR_ADJUST), xCntxt.get() );
+
+ // and push on stack
+ PushContext(xCntxt);
+
+ // set the current style or its attributes
+ SetTextCollAttrs(m_aContexts.back().get());
+
+ m_nFontStHeadStart = m_aFontStack.size();
+
+ // progress bar
+ ShowStatline();
+}
+
+void SwHTMLParser::EndHeading()
+{
+ // open a new paragraph
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_SPACE );
+ else
+ AddParSpace();
+
+ // search context matching the token and fetch it from stack
+ std::unique_ptr<HTMLAttrContext> xCntxt;
+ auto nPos = m_aContexts.size();
+ while( !xCntxt && nPos>m_nContextStMin )
+ {
+ switch( m_aContexts[--nPos]->GetToken() )
+ {
+ case HtmlTokenId::HEAD1_ON:
+ case HtmlTokenId::HEAD2_ON:
+ case HtmlTokenId::HEAD3_ON:
+ case HtmlTokenId::HEAD4_ON:
+ case HtmlTokenId::HEAD5_ON:
+ case HtmlTokenId::HEAD6_ON:
+ xCntxt = std::move(m_aContexts[nPos]);
+ m_aContexts.erase( m_aContexts.begin() + nPos );
+ break;
+ default: break;
+ }
+ }
+
+ // and now end attributes
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
+ xCntxt.reset();
+ }
+
+ // reset existing style
+ SetTextCollAttrs();
+
+ m_nFontStHeadStart = m_nFontStMin;
+}
+
+void SwHTMLParser::NewTextFormatColl( HtmlTokenId nToken, sal_uInt16 nColl )
+{
+ OUString aId, aStyle, aClass, aLang, aDir;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // open a new paragraph
+ SwHTMLAppendMode eMode = AM_NORMAL;
+ switch( nToken )
+ {
+ case HtmlTokenId::LISTING_ON:
+ case HtmlTokenId::XMP_ON:
+ // These both tags will be mapped to the PRE style. For the case that a
+ // a CLASS exists we will delete it so that we don't get the CLASS of
+ // the PRE style.
+ aClass.clear();
+ [[fallthrough]];
+ case HtmlTokenId::BLOCKQUOTE_ON:
+ case HtmlTokenId::BLOCKQUOTE30_ON:
+ case HtmlTokenId::PREFORMTXT_ON:
+ eMode = AM_SPACE;
+ break;
+ case HtmlTokenId::ADDRESS_ON:
+ eMode = AM_NOSPACE; // ADDRESS can follow on a <P> without </P>
+ break;
+ case HtmlTokenId::DT_ON:
+ case HtmlTokenId::DD_ON:
+ eMode = AM_SOFTNOSPACE;
+ break;
+ default:
+ OSL_ENSURE( false, "unknown style" );
+ break;
+ }
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( eMode );
+ else if( AM_SPACE==eMode )
+ AddParSpace();
+
+ // ... and save in a context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, aClass));
+
+ // parse styles (regarding class see also NewPara)
+ if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
+ {
+ OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
+ "Class is not considered" );
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
+ }
+ }
+
+ PushContext(xCntxt);
+
+ // set the new style
+ SetTextCollAttrs(m_aContexts.back().get());
+
+ // update progress bar
+ ShowStatline();
+}
+
+void SwHTMLParser::EndTextFormatColl( HtmlTokenId nToken )
+{
+ SwHTMLAppendMode eMode = AM_NORMAL;
+ switch( getOnToken(nToken) )
+ {
+ case HtmlTokenId::BLOCKQUOTE_ON:
+ case HtmlTokenId::BLOCKQUOTE30_ON:
+ case HtmlTokenId::PREFORMTXT_ON:
+ case HtmlTokenId::LISTING_ON:
+ case HtmlTokenId::XMP_ON:
+ eMode = AM_SPACE;
+ break;
+ case HtmlTokenId::ADDRESS_ON:
+ case HtmlTokenId::DT_ON:
+ case HtmlTokenId::DD_ON:
+ eMode = AM_SOFTNOSPACE;
+ break;
+ default:
+ OSL_ENSURE( false, "unknown style" );
+ break;
+ }
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( eMode );
+ else if( AM_SPACE==eMode )
+ AddParSpace();
+
+ // pop current context of stack
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(getOnToken(nToken)));
+
+ // and now end attributes
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
+ xCntxt.reset();
+ }
+
+ // reset existing style
+ SetTextCollAttrs();
+}
+
+void SwHTMLParser::NewDefList()
+{
+ OUString aId, aStyle, aClass, aLang, aDir;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // open a new paragraph
+ bool bSpace = (GetNumInfo().GetDepth() + m_nDefListDeep) == 0;
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( bSpace ? AM_SPACE : AM_SOFTNOSPACE );
+ else if( bSpace )
+ AddParSpace();
+
+ // one level more
+ m_nDefListDeep++;
+
+ bool bInDD = false, bNotInDD = false;
+ auto nPos = m_aContexts.size();
+ while( !bInDD && !bNotInDD && nPos>m_nContextStMin )
+ {
+ HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
+ switch( nCntxtToken )
+ {
+ case HtmlTokenId::DEFLIST_ON:
+ case HtmlTokenId::DIRLIST_ON:
+ case HtmlTokenId::MENULIST_ON:
+ case HtmlTokenId::ORDERLIST_ON:
+ case HtmlTokenId::UNORDERLIST_ON:
+ bNotInDD = true;
+ break;
+ case HtmlTokenId::DD_ON:
+ bInDD = true;
+ break;
+ default: break;
+ }
+ }
+
+ // ... and save in a context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::DEFLIST_ON));
+
+ // in it save also the margins
+ sal_uInt16 nLeft=0, nRight=0;
+ short nIndent=0;
+ GetMarginsFromContext( nLeft, nRight, nIndent );
+
+ // The indentation, which already results from a DL, correlates with a DT
+ // on the current level and this correlates to a DD from the previous level.
+ // For a level >=2 we must add DD distance.
+ if( !bInDD && m_nDefListDeep > 1 )
+ {
+
+ // and the one of the DT-style of the current level
+ SvxTextLeftMarginItem const& rTextLeftMargin =
+ m_pCSS1Parser->GetTextFormatColl(RES_POOLCOLL_HTML_DD, OUString())
+ ->GetTextLeftMargin();
+ nLeft = nLeft + static_cast<sal_uInt16>(rTextLeftMargin.GetTextLeft());
+ }
+
+ xCntxt->SetMargins( nLeft, nRight, nIndent );
+
+ // parse styles
+ if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
+ {
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
+ }
+ }
+
+ PushContext(xCntxt);
+
+ // set the attributes of the new style
+ if( m_nDefListDeep > 1 )
+ SetTextCollAttrs(m_aContexts.back().get());
+}
+
+void SwHTMLParser::EndDefList()
+{
+ bool bSpace = (GetNumInfo().GetDepth() + m_nDefListDeep) == 1;
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( bSpace ? AM_SPACE : AM_SOFTNOSPACE );
+ else if( bSpace )
+ AddParSpace();
+
+ // one level less
+ if( m_nDefListDeep > 0 )
+ m_nDefListDeep--;
+
+ // pop current context of stack
+ std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::DEFLIST_ON));
+
+ // and now end attributes
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
+ xCntxt.reset();
+ }
+
+ // and set style
+ SetTextCollAttrs();
+}
+
+void SwHTMLParser::NewDefListItem( HtmlTokenId nToken )
+{
+ // determine if the DD/DT exist in a DL
+ bool bInDefList = false, bNotInDefList = false;
+ auto nPos = m_aContexts.size();
+ while( !bInDefList && !bNotInDefList && nPos>m_nContextStMin )
+ {
+ HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
+ switch( nCntxtToken )
+ {
+ case HtmlTokenId::DEFLIST_ON:
+ bInDefList = true;
+ break;
+ case HtmlTokenId::DIRLIST_ON:
+ case HtmlTokenId::MENULIST_ON:
+ case HtmlTokenId::ORDERLIST_ON:
+ case HtmlTokenId::UNORDERLIST_ON:
+ bNotInDefList = true;
+ break;
+ default: break;
+ }
+ }
+
+ // if not, then implicitly open a new DL
+ if( !bInDefList )
+ {
+ m_nDefListDeep++;
+ OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE,
+ "Now an open paragraph element will be lost." );
+ m_nOpenParaToken = nToken;
+ }
+
+ NewTextFormatColl( nToken, static_cast< sal_uInt16 >(nToken==HtmlTokenId::DD_ON ? RES_POOLCOLL_HTML_DD
+ : RES_POOLCOLL_HTML_DT) );
+}
+
+void SwHTMLParser::EndDefListItem( HtmlTokenId nToken )
+{
+ // open a new paragraph
+ if( nToken == HtmlTokenId::NONE && m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_SOFTNOSPACE );
+
+ // search context matching the token and fetch it from stack
+ nToken = getOnToken(nToken);
+ std::unique_ptr<HTMLAttrContext> xCntxt;
+ auto nPos = m_aContexts.size();
+ while( !xCntxt && nPos>m_nContextStMin )
+ {
+ HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
+ switch( nCntxtToken )
+ {
+ case HtmlTokenId::DD_ON:
+ case HtmlTokenId::DT_ON:
+ if( nToken == HtmlTokenId::NONE || nToken == nCntxtToken )
+ {
+ xCntxt = std::move(m_aContexts[nPos]);
+ m_aContexts.erase( m_aContexts.begin() + nPos );
+ }
+ break;
+ case HtmlTokenId::DEFLIST_ON:
+ // don't look at DD/DT outside the current DefList
+ case HtmlTokenId::DIRLIST_ON:
+ case HtmlTokenId::MENULIST_ON:
+ case HtmlTokenId::ORDERLIST_ON:
+ case HtmlTokenId::UNORDERLIST_ON:
+ // and also not outside another list
+ nPos = m_nContextStMin;
+ break;
+ default: break;
+ }
+ }
+
+ // and now end attributes
+ if (xCntxt)
+ {
+ EndContext(xCntxt.get());
+ SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
+ }
+}
+
+/**
+ *
+ * @param bNoSurroundOnly The paragraph contains at least one frame
+ * without wrapping.
+ * @param bSurroundOnly The paragraph contains at least one frame
+ * with wrapping, but none without wrapping.
+ *
+ * Otherwise the paragraph contains any frame.
+ */
+bool SwHTMLParser::HasCurrentParaFlys( bool bNoSurroundOnly,
+ bool bSurroundOnly ) const
+{
+ SwNode& rNode = m_pPam->GetPoint()->GetNode();
+
+
+ bool bFound = false;
+ for(sw::SpzFrameFormat* pFormat: *m_xDoc->GetSpzFrameFormats())
+ {
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ // A frame was found, when
+ // - it is paragraph-bound, and
+ // - is anchored in current paragraph, and
+ // - every paragraph-bound frame counts, or
+ // - (only frames without wrapping count and) the frame doesn't have
+ // a wrapping
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ *pAnchorNode == rNode )
+ {
+ if( !(bNoSurroundOnly || bSurroundOnly) )
+ {
+ bFound = true;
+ break;
+ }
+ else
+ {
+ // When looking for frames with wrapping, also disregard
+ // ones with wrap-through. In this case it's (still) HIDDEN-Controls,
+ // and you don't want to evade those when positioning.
+ css::text::WrapTextMode eSurround = pFormat->GetSurround().GetSurround();
+ if( bNoSurroundOnly )
+ {
+ if( css::text::WrapTextMode_NONE==eSurround )
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if( bSurroundOnly )
+ {
+ if( css::text::WrapTextMode_NONE==eSurround )
+ {
+ bFound = false;
+ break;
+ }
+ else if( css::text::WrapTextMode_THROUGH!=eSurround )
+ {
+ bFound = true;
+ // Continue searching: It's possible that some without
+ // wrapping will follow...
+ }
+ }
+ }
+ }
+ }
+
+ return bFound;
+}
+
+// the special methods for inserting of objects
+
+const SwFormatColl *SwHTMLParser::GetCurrFormatColl() const
+{
+ const SwContentNode* pCNd = m_pPam->GetPointContentNode();
+ return pCNd ? &pCNd->GetAnyFormatColl() : nullptr;
+}
+
+void SwHTMLParser::SetTextCollAttrs( HTMLAttrContext *pContext )
+{
+ SwTextFormatColl *pCollToSet = nullptr; // the style to set
+ SfxItemSet *pItemSet = nullptr; // set of hard attributes
+ sal_uInt16 nTopColl = pContext ? pContext->GetTextFormatColl() : 0;
+ const OUString rTopClass = pContext ? pContext->GetClass() : OUString();
+ sal_uInt16 nDfltColl = RES_POOLCOLL_TEXT;
+
+ bool bInPRE=false; // some context info
+
+ sal_uInt16 nLeftMargin = 0, nRightMargin = 0; // the margins and
+ short nFirstLineIndent = 0; // indentations
+
+ auto nDepth = m_aContexts.size();
+ if (bFuzzing && nDepth > 128)
+ {
+ SAL_WARN("sw.html", "Not applying any more text collection attributes to a deeply nested node for fuzzing performance");
+ nDepth = 0;
+ }
+
+ for (auto i = m_nContextStAttrMin; i < nDepth; ++i)
+ {
+ const HTMLAttrContext *pCntxt = m_aContexts[i].get();
+
+ sal_uInt16 nColl = pCntxt->GetTextFormatColl();
+ if( nColl )
+ {
+ // There is a style to set. Then at first we must decide,
+ // if the style can be set.
+ bool bSetThis = true;
+ switch( nColl )
+ {
+ case RES_POOLCOLL_HTML_PRE:
+ bInPRE = true;
+ break;
+ case RES_POOLCOLL_TEXT:
+ // <TD><P CLASS=xxx> must become TD.xxx
+ if( nDfltColl==RES_POOLCOLL_TABLE ||
+ nDfltColl==RES_POOLCOLL_TABLE_HDLN )
+ nColl = nDfltColl;
+ break;
+ case RES_POOLCOLL_HTML_HR:
+ // also <HR> in <PRE> set as style, otherwise it can't
+ // be exported anymore
+ break;
+ default:
+ if( bInPRE )
+ bSetThis = false;
+ break;
+ }
+
+ SwTextFormatColl *pNewColl =
+ m_pCSS1Parser->GetTextFormatColl( nColl, pCntxt->GetClass() );
+
+ if( bSetThis )
+ {
+ // If now a different style should be set as previously, the
+ // previous style must be replaced by hard attribution.
+
+ if( pCollToSet )
+ {
+ // insert the attributes hard, which previous style sets
+ if( !pItemSet )
+ pItemSet = new SfxItemSet( pCollToSet->GetAttrSet() );
+ else
+ {
+ const SfxItemSet& rCollSet = pCollToSet->GetAttrSet();
+ SfxItemSet aItemSet( *rCollSet.GetPool(),
+ rCollSet.GetRanges() );
+ aItemSet.Set( rCollSet );
+ pItemSet->Put( aItemSet );
+ }
+ // but remove the attributes, which the current style sets,
+ // because otherwise they will be overwritten later
+ pItemSet->Differentiate( pNewColl->GetAttrSet() );
+ }
+
+ pCollToSet = pNewColl;
+ }
+ else
+ {
+ // hard attribution
+ if( !pItemSet )
+ pItemSet = new SfxItemSet( pNewColl->GetAttrSet() );
+ else
+ {
+ const SfxItemSet& rCollSet = pNewColl->GetAttrSet();
+ SfxItemSet aItemSet( *rCollSet.GetPool(),
+ rCollSet.GetRanges() );
+ aItemSet.Set( rCollSet );
+ pItemSet->Put( aItemSet );
+ }
+ }
+ }
+ else
+ {
+ // Maybe a default style exists?
+ nColl = pCntxt->GetDefaultTextFormatColl();
+ if( nColl )
+ nDfltColl = nColl;
+ }
+
+ // if applicable fetch new paragraph indents
+ if( pCntxt->IsLRSpaceChanged() )
+ {
+ sal_uInt16 nLeft=0, nRight=0;
+
+ pCntxt->GetMargins( nLeft, nRight, nFirstLineIndent );
+ nLeftMargin = nLeft;
+ nRightMargin = nRight;
+ }
+ }
+
+ // If in current context a new style should be set,
+ // its paragraph margins must be inserted in the context.
+ if( pContext && nTopColl )
+ {
+ // <TD><P CLASS=xxx> must become TD.xxx
+ if( nTopColl==RES_POOLCOLL_TEXT &&
+ (nDfltColl==RES_POOLCOLL_TABLE ||
+ nDfltColl==RES_POOLCOLL_TABLE_HDLN) )
+ nTopColl = nDfltColl;
+
+ const SwTextFormatColl *pTopColl =
+ m_pCSS1Parser->GetTextFormatColl( nTopColl, rTopClass );
+ const SfxItemSet& rItemSet = pTopColl->GetAttrSet();
+ if (rItemSet.GetItemIfSet(RES_MARGIN_FIRSTLINE)
+ || rItemSet.GetItemIfSet(RES_MARGIN_TEXTLEFT)
+ || rItemSet.GetItemIfSet(RES_MARGIN_RIGHT))
+ {
+ sal_Int32 nLeft = rItemSet.Get(RES_MARGIN_TEXTLEFT).GetTextLeft();
+ sal_Int32 nRight = rItemSet.Get(RES_MARGIN_RIGHT).GetRight();
+ nFirstLineIndent = rItemSet.Get(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset();
+
+ // In Definition lists the margins also contain the margins from the previous levels
+ if( RES_POOLCOLL_HTML_DD == nTopColl )
+ {
+ auto const*const pColl(m_pCSS1Parser->GetTextFormatColl(RES_POOLCOLL_HTML_DT, OUString()));
+ nLeft -= pColl->GetTextLeftMargin().GetTextLeft();
+ nRight -= pColl->GetRightMargin().GetRight();
+ }
+ else if( RES_POOLCOLL_HTML_DT == nTopColl )
+ {
+ nLeft = 0;
+ nRight = 0;
+ }
+
+ // the paragraph margins add up
+ nLeftMargin = nLeftMargin + static_cast< sal_uInt16 >(nLeft);
+ nRightMargin = nRightMargin + static_cast< sal_uInt16 >(nRight);
+
+ pContext->SetMargins( nLeftMargin, nRightMargin,
+ nFirstLineIndent );
+ }
+ if( const SvxULSpaceItem* pULItem = rItemSet.GetItemIfSet(RES_UL_SPACE) )
+ {
+ pContext->SetULSpace( pULItem->GetUpper(), pULItem->GetLower() );
+ }
+ }
+
+ // If no style is set in the context use the text body.
+ if( !pCollToSet )
+ {
+ pCollToSet = m_pCSS1Parser->GetTextCollFromPool( nDfltColl );
+ if( !nLeftMargin )
+ {
+ nLeftMargin = static_cast<sal_uInt16>(pCollToSet->GetTextLeftMargin().GetTextLeft());
+ }
+ if( !nRightMargin )
+ {
+ nRightMargin = static_cast<sal_uInt16>(pCollToSet->GetRightMargin().GetRight());
+ }
+ if( !nFirstLineIndent )
+ {
+ nFirstLineIndent = pCollToSet->GetFirstLineIndent().GetTextFirstLineOffset();
+ }
+ }
+
+ // remove previous hard attribution of paragraph
+ for( auto pParaAttr : m_aParaAttrs )
+ pParaAttr->Invalidate();
+ m_aParaAttrs.clear();
+
+ // set the style
+ m_xDoc->SetTextFormatColl( *m_pPam, pCollToSet );
+
+ // if applicable correct the paragraph indent
+ const SvxFirstLineIndentItem & rFirstLine = pCollToSet->GetFirstLineIndent();
+ const SvxTextLeftMarginItem & rTextLeftMargin = pCollToSet->GetTextLeftMargin();
+ const SvxRightMarginItem & rRightMargin = pCollToSet->GetRightMargin();
+ bool bSetLRSpace = nLeftMargin != rTextLeftMargin.GetTextLeft() ||
+ nFirstLineIndent != rFirstLine.GetTextFirstLineOffset() ||
+ nRightMargin != rRightMargin.GetRight();
+
+ if( bSetLRSpace )
+ {
+ SvxFirstLineIndentItem firstLine(rFirstLine);
+ SvxTextLeftMarginItem leftMargin(rTextLeftMargin);
+ SvxRightMarginItem rightMargin(rRightMargin);
+ firstLine.SetTextFirstLineOffset(nFirstLineIndent);
+ leftMargin.SetTextLeft(nLeftMargin);
+ rightMargin.SetRight(nRightMargin);
+ if( pItemSet )
+ {
+ pItemSet->Put(firstLine);
+ pItemSet->Put(leftMargin);
+ pItemSet->Put(rightMargin);
+ }
+ else
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pFirstLineIndent, firstLine);
+ m_xAttrTab->pFirstLineIndent->SetLikePara();
+ m_aParaAttrs.push_back(m_xAttrTab->pFirstLineIndent);
+ EndAttr(m_xAttrTab->pFirstLineIndent, false);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pTextLeftMargin, leftMargin);
+ m_xAttrTab->pTextLeftMargin->SetLikePara();
+ m_aParaAttrs.push_back(m_xAttrTab->pTextLeftMargin);
+ EndAttr(m_xAttrTab->pTextLeftMargin, false);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pRightMargin, rightMargin);
+ m_xAttrTab->pRightMargin->SetLikePara();
+ m_aParaAttrs.push_back(m_xAttrTab->pRightMargin);
+ EndAttr(m_xAttrTab->pRightMargin, false);
+ }
+ }
+
+ // and now set the attributes
+ if( pItemSet )
+ {
+ InsertParaAttrs( *pItemSet );
+ delete pItemSet;
+ }
+}
+
+void SwHTMLParser::NewCharFormat( HtmlTokenId nToken )
+{
+ OUString aId, aStyle, aLang, aDir;
+ OUString aClass;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ case HtmlOptionId::LANG:
+ aLang = rOption.GetString();
+ break;
+ case HtmlOptionId::DIR:
+ aDir = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // create a new context
+ std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
+
+ // set the style and save it in the context
+ SwCharFormat* pCFormat = m_pCSS1Parser->GetChrFormat( nToken, aClass );
+ OSL_ENSURE( pCFormat, "No character format found for token" );
+
+ // parse styles (regarding class see also NewPara)
+ if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
+ {
+ OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
+ "Class is not considered" );
+ DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
+ InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
+ }
+ }
+
+ // Character formats are stored in their own stack and can never be inserted
+ // by styles. Therefore the attribute doesn't exist in CSS1-Which-Range.
+ if( pCFormat )
+ InsertAttr( &m_xAttrTab->pCharFormats, SwFormatCharFormat( pCFormat ), xCntxt.get() );
+
+ // save the context
+ PushContext(xCntxt);
+}
+
+void SwHTMLParser::InsertSpacer()
+{
+ // and if applicable change it via the options
+ sal_Int16 eVertOri = text::VertOrientation::TOP;
+ sal_Int16 eHoriOri = text::HoriOrientation::NONE;
+ Size aSize( 0, 0);
+ tools::Long nSize = 0;
+ bool bPercentWidth = false;
+ bool bPercentHeight = false;
+ sal_uInt16 nType = HTML_SPTYPE_HORI;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::TYPE:
+ rOption.GetEnum( nType, aHTMLSpacerTypeTable );
+ break;
+ case HtmlOptionId::ALIGN:
+ eVertOri =
+ rOption.GetEnum( aHTMLImgVAlignTable,
+ eVertOri );
+ eHoriOri =
+ rOption.GetEnum( aHTMLImgHAlignTable,
+ eHoriOri );
+ break;
+ case HtmlOptionId::WIDTH:
+ // First only save as pixel value!
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::HEIGHT:
+ // First only save as pixel value!
+ bPercentHeight = (rOption.GetString().indexOf('%') != -1);
+ aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ break;
+ case HtmlOptionId::SIZE:
+ // First only save as pixel value!
+ nSize = rOption.GetNumber();
+ break;
+ default: break;
+ }
+ }
+
+ switch( nType )
+ {
+ case HTML_SPTYPE_BLOCK:
+ {
+ // create an empty text frame
+
+ // fetch the ItemSet
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameSet( m_xDoc->GetAttrPool() );
+ if( !IsNewDoc() )
+ Reader::ResetFrameFormatAttrs( aFrameSet );
+
+ // set the anchor and the adjustment
+ SetAnchorAndAdjustment( eVertOri, eHoriOri, aFrameSet );
+
+ // and the size of the frame
+ Size aDfltSz( MINFLY, MINFLY );
+ Size aSpace( 0, 0 );
+ SfxItemSet aDummyItemSet( m_xDoc->GetAttrPool(),
+ m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aDummyPropInfo;
+
+ SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight,
+ aDummyPropInfo, aFrameSet );
+ SetSpace( aSpace, aDummyItemSet, aDummyPropInfo, aFrameSet );
+
+ // protect the content
+ SvxProtectItem aProtectItem( RES_PROTECT) ;
+ aProtectItem.SetContentProtect( true );
+ aFrameSet.Put( aProtectItem );
+
+ // create the frame
+ RndStdIds eAnchorId =
+ aFrameSet.Get(RES_ANCHOR).GetAnchorId();
+ SwFrameFormat *pFlyFormat = m_xDoc->MakeFlySection( eAnchorId,
+ m_pPam->GetPoint(), &aFrameSet );
+ // Possibly create frames and register auto-bound frames.
+ RegisterFlyFrame( pFlyFormat );
+ }
+ break;
+ case HTML_SPTYPE_VERT:
+ if( nSize > 0 )
+ {
+ nSize = o3tl::convert(nSize, o3tl::Length::px, o3tl::Length::twip);
+
+ // set a paragraph margin
+ SwTextNode *pTextNode = nullptr;
+ if( !m_pPam->GetPoint()->GetContentIndex() )
+ {
+ // if possible change the bottom paragraph margin
+ // of previous node
+
+ SetAttr(); // set still open paragraph attributes
+
+ pTextNode = m_xDoc->GetNodes()[m_pPam->GetPoint()->GetNodeIndex()-1]
+ ->GetTextNode();
+
+ // If the previous paragraph isn't a text node, then now an
+ // empty paragraph is created, which already generates a single
+ // line of spacing.
+ if( !pTextNode )
+ nSize = nSize>HTML_PARSPACE ? nSize-HTML_PARSPACE : 0;
+ }
+
+ if( pTextNode )
+ {
+ SvxULSpaceItem aULSpace( pTextNode->SwContentNode::GetAttr( RES_UL_SPACE ) );
+ aULSpace.SetLower( aULSpace.GetLower() + o3tl::narrowing<sal_uInt16>(nSize) );
+ pTextNode->SetAttr( aULSpace );
+ }
+ else
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, SvxULSpaceItem(0, o3tl::narrowing<sal_uInt16>(nSize), RES_UL_SPACE));
+ EndAttr( m_xAttrTab->pULSpace, false );
+
+ AppendTextNode(); // Don't change spacing!
+ }
+ }
+ break;
+ case HTML_SPTYPE_HORI:
+ if( nSize > 0 )
+ {
+ // If the paragraph is still empty, set first line
+ // indentation, otherwise apply letter spacing over a space.
+
+ nSize = o3tl::convert(nSize, o3tl::Length::px, o3tl::Length::twip);
+
+ if( !m_pPam->GetPoint()->GetContentIndex() )
+ {
+ sal_uInt16 nLeft=0, nRight=0;
+ short nIndent = 0;
+
+ GetMarginsFromContextWithNumberBullet( nLeft, nRight, nIndent );
+ nIndent = nIndent + static_cast<short>(nSize);
+
+ SvxFirstLineIndentItem const firstLine(nIndent, RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem const leftMargin(nLeft, RES_MARGIN_TEXTLEFT);
+ SvxRightMarginItem const rightMargin(nRight, RES_MARGIN_RIGHT);
+
+ NewAttr(m_xAttrTab, &m_xAttrTab->pFirstLineIndent, firstLine);
+ EndAttr(m_xAttrTab->pFirstLineIndent, false);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pTextLeftMargin, leftMargin);
+ EndAttr(m_xAttrTab->pTextLeftMargin, false);
+ NewAttr(m_xAttrTab, &m_xAttrTab->pRightMargin, rightMargin);
+ EndAttr(m_xAttrTab->pRightMargin, false);
+ }
+ else
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pKerning, SvxKerningItem( static_cast<short>(nSize), RES_CHRATR_KERNING ));
+ m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, " " );
+ EndAttr( m_xAttrTab->pKerning );
+ }
+ }
+ }
+}
+
+sal_uInt16 SwHTMLParser::ToTwips( sal_uInt16 nPixel )
+{
+ return std::min(o3tl::convert(nPixel, o3tl::Length::px, o3tl::Length::twip),
+ sal_Int64(SAL_MAX_UINT16));
+}
+
+SwTwips SwHTMLParser::GetCurrentBrowseWidth()
+{
+ const SwTwips nWidth = SwHTMLTableLayout::GetBrowseWidth( *m_xDoc );
+ if( nWidth )
+ return nWidth;
+
+ if( !m_aHTMLPageSize.Width() )
+ {
+ const SwFrameFormat& rPgFormat = m_pCSS1Parser->GetMasterPageDesc()->GetMaster();
+
+ const SwFormatFrameSize& rSz = rPgFormat.GetFrameSize();
+ const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace();
+ const SvxULSpaceItem& rUL = rPgFormat.GetULSpace();
+ const SwFormatCol& rCol = rPgFormat.GetCol();
+
+ m_aHTMLPageSize.setWidth( rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight() );
+ m_aHTMLPageSize.setHeight( rSz.GetHeight() - rUL.GetUpper() - rUL.GetLower() );
+
+ if( 1 < rCol.GetNumCols() )
+ m_aHTMLPageSize.setWidth( m_aHTMLPageSize.Width() / ( rCol.GetNumCols()) );
+ }
+
+ return m_aHTMLPageSize.Width();
+}
+
+void SwHTMLParser::InsertIDOption()
+{
+ OUString aId;
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ if( HtmlOptionId::ID==rOption.GetToken() )
+ {
+ aId = rOption.GetString();
+ break;
+ }
+ }
+
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+}
+
+void SwHTMLParser::InsertLineBreak()
+{
+ OUString aId, aStyle, aClass; // the id of bookmark
+ SwLineBreakClear eClear = SwLineBreakClear::NONE;
+
+ // then we fetch the options
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::CLEAR:
+ {
+ const OUString &rClear = rOption.GetString();
+ if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_all ) )
+ {
+ eClear = SwLineBreakClear::ALL;
+ }
+ else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
+ {
+ eClear = SwLineBreakClear::LEFT;
+ }
+ else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
+ {
+ eClear = SwLineBreakClear::LEFT;
+ }
+ }
+ break;
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::STYLE:
+ aStyle = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ aClass = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // parse styles
+ std::shared_ptr<SvxFormatBreakItem> aBreakItem(std::make_shared<SvxFormatBreakItem>(SvxBreak::NONE, RES_BREAK));
+ bool bBreakItem = false;
+ if( HasStyleOptions( aStyle, aId, aClass ) )
+ {
+ SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
+ SvxCSS1PropertyInfo aPropInfo;
+
+ if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ) )
+ {
+ if( m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo ) )
+ {
+ aBreakItem.reset(aItemSet.Get(RES_BREAK).Clone());
+ bBreakItem = true;
+ }
+ if( !aPropInfo.m_aId.isEmpty() )
+ InsertBookmark( aPropInfo.m_aId );
+ }
+ }
+
+ if( bBreakItem && SvxBreak::PageAfter == aBreakItem->GetBreak() )
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, *aBreakItem);
+ EndAttr( m_xAttrTab->pBreak, false );
+ }
+
+ if (!bBreakItem)
+ {
+ if (eClear == SwLineBreakClear::NONE)
+ {
+ // If no CLEAR could or should be executed, a line break will be inserted
+ m_xDoc->getIDocumentContentOperations().InsertString(*m_pPam, "\x0A");
+ }
+ else
+ {
+ // <BR CLEAR=xxx> is mapped an SwFormatLineBreak.
+ SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
+ if (pTextNode)
+ {
+ SwFormatLineBreak aLineBreak(eClear);
+ sal_Int32 nPos = m_pPam->GetPoint()->GetContentIndex();
+ pTextNode->InsertItem(aLineBreak, nPos, nPos);
+ }
+ }
+ }
+ else if( m_pPam->GetPoint()->GetContentIndex() )
+ {
+ // If a CLEAR is executed in a non-empty paragraph, then after it
+ // a new paragraph has to be opened.
+ // MIB 21.02.97: Here actually we should change the bottom paragraph
+ // margin to zero. This will fail for something like this <BR ..><P>
+ // (>Netscape). That's why we don't do it.
+ AppendTextNode( AM_NOSPACE );
+ }
+ if( bBreakItem && SvxBreak::PageBefore == aBreakItem->GetBreak() )
+ {
+ NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, *aBreakItem);
+ EndAttr( m_xAttrTab->pBreak, false );
+ }
+}
+
+void SwHTMLParser::InsertHorzRule()
+{
+ sal_uInt16 nSize = 0;
+ sal_uInt16 nWidth = 0;
+
+ SvxAdjust eAdjust = SvxAdjust::End;
+
+ bool bPercentWidth = false;
+ bool bNoShade = false;
+ bool bColor = false;
+
+ Color aColor;
+ OUString aId;
+
+ // let's fetch the options
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ID:
+ aId = rOption.GetString();
+ break;
+ case HtmlOptionId::SIZE:
+ nSize = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ break;
+ case HtmlOptionId::WIDTH:
+ bPercentWidth = (rOption.GetString().indexOf('%') != -1);
+ nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
+ if( bPercentWidth && nWidth>=100 )
+ {
+ // the default case are 100% lines (no attributes necessary)
+ nWidth = 0;
+ bPercentWidth = false;
+ }
+ break;
+ case HtmlOptionId::ALIGN:
+ eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
+ break;
+ case HtmlOptionId::NOSHADE:
+ bNoShade = true;
+ break;
+ case HtmlOptionId::COLOR:
+ rOption.GetColor( aColor );
+ bColor = true;
+ break;
+ default: break;
+ }
+ }
+
+ if( m_pPam->GetPoint()->GetContentIndex() )
+ AppendTextNode( AM_NOSPACE );
+ if( m_nOpenParaToken != HtmlTokenId::NONE )
+ EndPara();
+ AppendTextNode();
+ m_pPam->Move( fnMoveBackward );
+
+ // ...and save in a context
+ std::unique_ptr<HTMLAttrContext> xCntxt(
+ new HTMLAttrContext(HtmlTokenId::HORZRULE, RES_POOLCOLL_HTML_HR, OUString()));
+
+ PushContext(xCntxt);
+
+ // set the new style
+ SetTextCollAttrs(m_aContexts.back().get());
+
+ // the hard attributes of the current paragraph will never become invalid
+ m_aParaAttrs.clear();
+
+ if( nSize>0 || bColor || bNoShade )
+ {
+ // set line colour and/or width
+ if( !bColor )
+ aColor = COL_GRAY;
+
+ SvxBorderLine aBorderLine( &aColor );
+ if( nSize )
+ {
+ tools::Long nPWidth = 0;
+ tools::Long nPHeight = static_cast<tools::Long>(nSize);
+ SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
+ if ( !bNoShade )
+ {
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ }
+ aBorderLine.SetWidth( nPHeight );
+ }
+ else if( bNoShade )
+ {
+ aBorderLine.SetWidth( SvxBorderLineWidth::Medium );
+ }
+ else
+ {
+ aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ aBorderLine.SetWidth(SvxBorderLineWidth::Hairline);
+ }
+
+ SvxBoxItem aBoxItem(RES_BOX);
+ aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::BOTTOM );
+ HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), aBoxItem, nullptr, std::shared_ptr<HTMLAttrTable>());
+ m_aSetAttrTab.push_back( pTmp );
+ }
+ if( nWidth )
+ {
+ // If we aren't in a table, then the width value will be "faked" with
+ // paragraph indents. That makes little sense in a table. In order to
+ // avoid that the line is considered during the width calculation, it
+ // still gets an appropriate LRSpace-Item.
+ if (!m_xTable)
+ {
+ // fake length and alignment of line above paragraph indents
+ tools::Long nBrowseWidth = GetCurrentBrowseWidth();
+ nWidth = bPercentWidth ? o3tl::narrowing<sal_uInt16>((nWidth*nBrowseWidth) / 100)
+ : ToTwips( o3tl::narrowing<sal_uInt16>(nBrowseWidth) );
+ if( nWidth < MINLAY )
+ nWidth = MINLAY;
+
+ const SwFormatColl *pColl = (static_cast<tools::Long>(nWidth) < nBrowseWidth) ? GetCurrFormatColl() : nullptr;
+ if (pColl)
+ {
+ tools::Long nDist = nBrowseWidth - nWidth;
+ ::std::optional<SvxTextLeftMarginItem> oLeft;
+ ::std::optional<SvxRightMarginItem> oRight;
+
+ switch( eAdjust )
+ {
+ case SvxAdjust::Right:
+ oLeft.emplace(o3tl::narrowing<sal_uInt16>(nDist), RES_MARGIN_TEXTLEFT);
+ break;
+ case SvxAdjust::Left:
+ oRight.emplace(o3tl::narrowing<sal_uInt16>(nDist), RES_MARGIN_RIGHT);
+ break;
+ case SvxAdjust::Center:
+ default:
+ nDist /= 2;
+ oLeft.emplace(o3tl::narrowing<sal_uInt16>(nDist), RES_MARGIN_TEXTLEFT);
+ oRight.emplace(o3tl::narrowing<sal_uInt16>(nDist), RES_MARGIN_RIGHT);
+ break;
+ }
+
+ if (oLeft)
+ {
+ HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), *oLeft, nullptr, std::shared_ptr<HTMLAttrTable>());
+ m_aSetAttrTab.push_back( pTmp );
+ }
+ if (oRight)
+ {
+ HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), *oRight, nullptr, std::shared_ptr<HTMLAttrTable>());
+ m_aSetAttrTab.push_back( pTmp );
+ }
+ }
+ }
+ }
+
+ // it's not possible to insert bookmarks in links
+ if( !aId.isEmpty() )
+ InsertBookmark( aId );
+
+ // pop current context of stack
+ std::unique_ptr<HTMLAttrContext> xPoppedContext(PopContext(HtmlTokenId::HORZRULE));
+ xPoppedContext.reset();
+
+ m_pPam->Move( fnMoveForward );
+
+ // and set the current style in the next paragraph
+ SetTextCollAttrs();
+}
+
+void SwHTMLParser::ParseMoreMetaOptions()
+{
+ OUString aName, aContent;
+ bool bHTTPEquiv = false;
+
+ const HTMLOptions& rHTMLOptions = GetOptions();
+ for (size_t i = rHTMLOptions.size(); i; )
+ {
+ const HTMLOption& rOption = rHTMLOptions[--i];
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::NAME:
+ aName = rOption.GetString();
+ bHTTPEquiv = false;
+ break;
+ case HtmlOptionId::HTTPEQUIV:
+ aName = rOption.GetString();
+ bHTTPEquiv = true;
+ break;
+ case HtmlOptionId::CONTENT:
+ aContent = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+
+ // Here things get a little tricky: We know for sure, that the Doc-Info
+ // wasn't changed. Therefore it's enough to query for Generator and Refresh
+ // to find a not processed Token. These are the only ones which won't change
+ // the Doc-Info.
+ if( aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_generator ) ||
+ aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_refresh ) ||
+ aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_content_type ) ||
+ aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_content_script_type ) )
+ return;
+
+ aContent = aContent.replaceAll("\r", "").replaceAll("\n", "");
+
+ if( aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_sdendnote ) )
+ {
+ FillEndNoteInfo( aContent );
+ return;
+ }
+
+ if( aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_sdfootnote ) )
+ {
+ FillFootNoteInfo( aContent );
+ return;
+ }
+
+ OUStringBuffer sText(
+ "HTML: <"
+ OOO_STRING_SVTOOLS_HTML_meta
+ " ");
+ if( bHTTPEquiv )
+ sText.append(OOO_STRING_SVTOOLS_HTML_O_httpequiv);
+ else
+ sText.append(OOO_STRING_SVTOOLS_HTML_O_name);
+ sText.append(
+ "=\"" + aName
+ + "\" "
+ OOO_STRING_SVTOOLS_HTML_O_content
+ "=\""
+ + aContent
+ + "\">");
+
+ SwPostItField aPostItField(
+ static_cast<SwPostItFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Postit )),
+ OUString(), sText.makeStringAndClear(), OUString(), OUString(), DateTime(DateTime::SYSTEM));
+ SwFormatField aFormatField( aPostItField );
+ InsertAttr( aFormatField, false );
+}
+
+HTMLAttr::HTMLAttr( const SwPosition& rPos, const SfxPoolItem& rItem,
+ HTMLAttr **ppHd, std::shared_ptr<HTMLAttrTable> xAttrTab ) :
+ m_nStartPara( rPos.GetNode() ),
+ m_nEndPara( rPos.GetNode() ),
+ m_nStartContent( rPos.GetContentIndex() ),
+ m_nEndContent(rPos.GetContentIndex() ),
+ m_bInsAtStart( true ),
+ m_bLikePara( false ),
+ m_bValid( true ),
+ m_pItem( rItem.Clone() ),
+ m_xAttrTab(std::move( xAttrTab )),
+ m_pNext( nullptr ),
+ m_pPrev( nullptr ),
+ m_ppHead( ppHd )
+{
+}
+
+HTMLAttr::HTMLAttr( const HTMLAttr &rAttr, const SwNode &rEndPara,
+ sal_Int32 nEndCnt, HTMLAttr **ppHd, std::shared_ptr<HTMLAttrTable> xAttrTab ) :
+ m_nStartPara( rAttr.m_nStartPara ),
+ m_nEndPara( rEndPara ),
+ m_nStartContent( rAttr.m_nStartContent ),
+ m_nEndContent( nEndCnt ),
+ m_bInsAtStart( rAttr.m_bInsAtStart ),
+ m_bLikePara( rAttr.m_bLikePara ),
+ m_bValid( rAttr.m_bValid ),
+ m_pItem( rAttr.m_pItem->Clone() ),
+ m_xAttrTab(std::move( xAttrTab )),
+ m_pNext( nullptr ),
+ m_pPrev( nullptr ),
+ m_ppHead( ppHd )
+{
+}
+
+HTMLAttr::~HTMLAttr()
+{
+}
+
+HTMLAttr *HTMLAttr::Clone(const SwNode& rEndPara, sal_Int32 nEndCnt) const
+{
+ // create the attribute anew with old start position
+ HTMLAttr *pNew = new HTMLAttr( *this, rEndPara, nEndCnt, m_ppHead, m_xAttrTab );
+
+ // The Previous-List must be taken over, the Next-List not!
+ pNew->m_pPrev = m_pPrev;
+
+ return pNew;
+}
+
+void HTMLAttr::Reset(const SwNode& rSttPara, sal_Int32 nSttCnt,
+ HTMLAttr **ppHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab)
+{
+ // reset the start (and the end)
+ m_nStartPara = rSttPara;
+ m_nStartContent = nSttCnt;
+ m_nEndPara = rSttPara;
+ m_nEndContent = nSttCnt;
+
+ // correct the head and nullify link
+ m_pNext = nullptr;
+ m_pPrev = nullptr;
+ m_ppHead = ppHd;
+ m_xAttrTab = rAttrTab;
+}
+
+void HTMLAttr::InsertPrev( HTMLAttr *pPrv )
+{
+ OSL_ENSURE( !pPrv->m_pNext || pPrv->m_pNext == this,
+ "HTMLAttr::InsertPrev: pNext wrong" );
+ pPrv->m_pNext = nullptr;
+
+ OSL_ENSURE( nullptr == pPrv->m_ppHead || m_ppHead == pPrv->m_ppHead,
+ "HTMLAttr::InsertPrev: ppHead wrong" );
+ pPrv->m_ppHead = nullptr;
+
+ HTMLAttr *pAttr = this;
+ while( pAttr->GetPrev() )
+ pAttr = pAttr->GetPrev();
+
+ pAttr->m_pPrev = pPrv;
+}
+
+bool SwHTMLParser::ParseMetaOptions(
+ const uno::Reference<document::XDocumentProperties> & i_xDocProps,
+ SvKeyValueIterator *i_pHeader )
+{
+ // always call base ParseMetaOptions, it sets the encoding (#i96700#)
+ bool ret( HTMLParser::ParseMetaOptions(i_xDocProps, i_pHeader) );
+ if (!ret && IsNewDoc())
+ {
+ ParseMoreMetaOptions();
+ }
+ return ret;
+}
+
+// override so we can parse DOCINFO field subtypes INFO[1-4]
+void SwHTMLParser::AddMetaUserDefined( OUString const & i_rMetaName )
+{
+ // unless we already have 4 names, append the argument to m_InfoNames
+ OUString* pName // the first empty string in m_InfoNames
+ (m_InfoNames[0].isEmpty() ? &m_InfoNames[0] :
+ (m_InfoNames[1].isEmpty() ? &m_InfoNames[1] :
+ (m_InfoNames[2].isEmpty() ? &m_InfoNames[2] :
+ (m_InfoNames[3].isEmpty() ? &m_InfoNames[3] : nullptr ))));
+ if (pName)
+ {
+ (*pName) = i_rMetaName;
+ }
+}
+
+void HTMLReader::SetupFilterOptions()
+{
+ // Reset state from previous Read() invocation.
+ m_aNamespace.clear();
+
+ if (!m_pMedium)
+ return;
+
+ auto pItem = m_pMedium->GetItemSet().GetItem(SID_FILE_FILTEROPTIONS);
+ if (!pItem)
+ return;
+
+ OUString aFilterOptions = pItem->GetValue();
+ static constexpr OUString aXhtmlNsKey(u"xhtmlns="_ustr);
+ if (aFilterOptions.startsWith(aXhtmlNsKey))
+ {
+ OUString aNamespace = aFilterOptions.copy(aXhtmlNsKey.getLength());
+ m_aNamespace = aNamespace;
+ }
+}
+
+namespace
+{
+ class FontCacheGuard
+ {
+ public:
+ ~FontCacheGuard()
+ {
+ FlushFontCache();
+ }
+ };
+}
+
+bool TestImportHTML(SvStream &rStream)
+{
+ FontCacheGuard aFontCacheGuard;
+ HTMLReader aReader;
+ aReader.m_pStream = &rStream;
+
+ SwGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ xDocSh->DoInitNew();
+ SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc();
+
+ SwPaM aPaM(pD->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
+ pD->SetInReading(true);
+ bool bRet = false;
+ try
+ {
+ bRet = aReader.Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE;
+ }
+ catch (const std::runtime_error&)
+ {
+ }
+ catch (const std::out_of_range&)
+ {
+ }
+ pD->SetInReading(false);
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/swhtml.hxx b/sw/source/filter/html/swhtml.hxx
new file mode 100644
index 0000000000..33f03ecf3f
--- /dev/null
+++ b/sw/source/filter/html/swhtml.hxx
@@ -0,0 +1,1069 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_SWHTML_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_SWHTML_HXX
+
+#include <config_java.h>
+
+#include <sfx2/sfxhtml.hxx>
+#include <svl/listener.hxx>
+#include <svl/macitem.hxx>
+#include <svtools/htmltokn.h>
+#include <editeng/svxenum.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <deletelistener.hxx>
+#include <fmtftn.hxx>
+#include <fltshell.hxx>
+#include <swtypes.hxx>
+#include <txtftn.hxx>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <memory>
+#include <utility>
+#include <vector>
+#include <deque>
+#include <stack>
+#include <set>
+
+class SfxMedium;
+class SfxViewFrame;
+class SdrObject;
+class SvxMacroTableDtor;
+class SwDoc;
+class SwPaM;
+class SwViewShell;
+class SwStartNode;
+class SwFormatColl;
+class SwField;
+class SwHTMLForm_Impl;
+class SwApplet_Impl;
+struct SwHTMLFootEndNote_Impl;
+class HTMLTableCnts;
+struct SwPending;
+class SvxCSS1PropertyInfo;
+struct ImplSVEvent;
+
+constexpr tools::Long HTML_CJK_PARSPACE = o3tl::toTwips(25, o3tl::Length::mm10); // 2.5mm
+constexpr tools::Long HTML_CTL_PARSPACE = o3tl::toTwips(25, o3tl::Length::mm10); // 2.5mm
+
+constexpr tools::Long HTML_DFLT_IMG_WIDTH = o3tl::toTwips(2, o3tl::Length::cm); // 2cm
+constexpr tools::Long HTML_DFLT_IMG_HEIGHT = o3tl::toTwips(1, o3tl::Length::cm); // 1cm
+
+// some things you often need
+extern HTMLOptionEnum<SvxAdjust> const aHTMLPAlignTable[];
+extern HTMLOptionEnum<sal_Int16> const aHTMLImgHAlignTable[];
+extern HTMLOptionEnum<sal_Int16> const aHTMLImgVAlignTable[];
+
+// attribute stack:
+
+class HTMLAttr;
+typedef std::deque<HTMLAttr *> HTMLAttrs;
+
+// Table of attributes: The order here is important: The attributes in the
+// beginning of the table will set first in EndAllAttrs.
+struct HTMLAttrTable
+{
+ HTMLAttr* pKeep; // frame attributes
+ HTMLAttr* pBox;
+ HTMLAttr* pBrush;
+ HTMLAttr* pBreak;
+ HTMLAttr* pPageDesc;
+
+ HTMLAttr* pFirstLineIndent; // paragraph attributes
+ HTMLAttr* pTextLeftMargin;
+ HTMLAttr* pRightMargin;
+ HTMLAttr* pULSpace;
+ HTMLAttr* pLineSpacing;
+ HTMLAttr* pAdjust;
+ HTMLAttr* pDropCap;
+ HTMLAttr* pSplit;
+ HTMLAttr* pWidows;
+ HTMLAttr* pOrphans;
+ HTMLAttr* pDirection;
+
+ HTMLAttr* pCharFormats; // text attributes
+ HTMLAttr* pINetFormat;
+
+ HTMLAttr* pBold; // character attributes
+ HTMLAttr* pBoldCJK;
+ HTMLAttr* pBoldCTL;
+ HTMLAttr* pItalic;
+ HTMLAttr* pItalicCJK;
+ HTMLAttr* pItalicCTL;
+ HTMLAttr* pStrike;
+ HTMLAttr* pUnderline;
+ HTMLAttr* pBlink;
+ HTMLAttr* pFont;
+ HTMLAttr* pFontCJK;
+ HTMLAttr* pFontCTL;
+ HTMLAttr* pFontHeight;
+ HTMLAttr* pFontHeightCJK;
+ HTMLAttr* pFontHeightCTL;
+ HTMLAttr* pFontColor;
+ HTMLAttr* pEscapement;
+ HTMLAttr* pCaseMap;
+ HTMLAttr* pKerning; // (only for SPACER)
+ HTMLAttr* pCharBrush; // character background
+ HTMLAttr* pLanguage;
+ HTMLAttr* pLanguageCJK;
+ HTMLAttr* pLanguageCTL;
+ HTMLAttr* pCharBox;
+};
+
+class HTMLAttr
+{
+ friend class SwHTMLParser;
+ friend class CellSaveStruct;
+
+ SwNodeIndex m_nStartPara;
+ SwNodeIndex m_nEndPara;
+ sal_Int32 m_nStartContent;
+ sal_Int32 m_nEndContent;
+ bool m_bInsAtStart : 1;
+ bool m_bLikePara : 1; // set attribute above the whole paragraph
+ bool m_bValid : 1; // is the attribute valid?
+
+ std::unique_ptr<SfxPoolItem> m_pItem;
+ std::shared_ptr<HTMLAttrTable> m_xAttrTab;
+ HTMLAttr *m_pNext; // still to close attributes with different values
+ HTMLAttr *m_pPrev; // already closed but not set attributes
+ HTMLAttr **m_ppHead; // list head
+
+ HTMLAttr( const SwPosition& rPos, const SfxPoolItem& rItem,
+ HTMLAttr **pHd, std::shared_ptr<HTMLAttrTable> xAttrTab );
+
+ HTMLAttr( const HTMLAttr &rAttr, const SwNode &rEndPara,
+ sal_Int32 nEndCnt, HTMLAttr **pHd, std::shared_ptr<HTMLAttrTable> xAttrTab );
+
+public:
+
+ ~HTMLAttr();
+
+ HTMLAttr *Clone( const SwNode& rEndPara, sal_Int32 nEndCnt ) const;
+ void Reset( const SwNode& rSttPara, sal_Int32 nSttCnt,
+ HTMLAttr **pHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab );
+ inline void SetStart( const SwPosition& rPos );
+
+ SwNodeOffset GetStartParagraphIdx() const { return m_nStartPara.GetIndex(); }
+ SwNodeOffset GetEndParagraphIdx() const { return m_nEndPara.GetIndex(); }
+
+ const SwNodeIndex& GetStartParagraph() const { return m_nStartPara; }
+ const SwNodeIndex& GetEndParagraph() const { return m_nEndPara; }
+
+ sal_Int32 GetStartContent() const { return m_nStartContent; }
+ sal_Int32 GetEndContent() const { return m_nEndContent; }
+
+ bool IsLikePara() const { return m_bLikePara; }
+ void SetLikePara() { m_bLikePara = true; }
+
+ SfxPoolItem& GetItem() { return *m_pItem; }
+ const SfxPoolItem& GetItem() const { return *m_pItem; }
+
+ HTMLAttr *GetNext() const { return m_pNext; }
+ void InsertNext( HTMLAttr *pNxt ) { m_pNext = pNxt; }
+
+ HTMLAttr *GetPrev() const { return m_pPrev; }
+ void InsertPrev( HTMLAttr *pPrv );
+ void ClearPrev() { m_pPrev = nullptr; }
+
+ void SetHead(HTMLAttr **ppHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab)
+ {
+ m_ppHead = ppHd;
+ m_xAttrTab = rAttrTab;
+ }
+
+ // During setting attributes from styles it can happen that these
+ // shouldn't be set anymore. To delete them would be very expensive, because
+ // you don't know all the places where they are linked in. Therefore they're
+ // made invalid and deleted at the next call of SetAttr_().
+ void Invalidate() { m_bValid = false; }
+};
+
+class HTMLAttrContext_SaveDoc;
+
+enum SwHTMLAppendMode {
+ AM_NORMAL, // no paragraph spacing handling
+ AM_NOSPACE, // set spacing hard to 0cm
+ AM_SPACE, // set spacing hard to 0.5cm
+ AM_SOFTNOSPACE, // don't set spacing, but save 0cm
+ AM_NONE // no append
+};
+
+class HTMLAttrContext
+{
+ HTMLAttrs m_aAttrs; // the attributes created in the context
+
+ OUString m_aClass; // context class
+
+ std::unique_ptr<HTMLAttrContext_SaveDoc> m_pSaveDocContext;
+ std::unique_ptr<SfxItemSet> m_pFrameItemSet;
+
+ HtmlTokenId m_nToken; // the token of the context
+
+ sal_uInt16 m_nTextFormatColl; // a style created in the context or zero
+
+ sal_uInt16 m_nLeftMargin; // a changed left border
+ sal_uInt16 m_nRightMargin; // a changed right border
+ sal_uInt16 m_nFirstLineIndent; // a changed first line indent
+
+ sal_uInt16 m_nUpperSpace;
+ sal_uInt16 m_nLowerSpace;
+
+ SwHTMLAppendMode m_eAppend;
+
+ bool m_bLRSpaceChanged : 1; // left/right border, changed indent?
+ bool m_bULSpaceChanged : 1; // top/bottom border changed?
+ bool m_bDefaultTextFormatColl : 1;// nTextFormatColl is only default
+ bool m_bSpansSection : 1; // the context opens a SwSection
+ bool m_bPopStack : 1; // delete above stack elements
+ bool m_bFinishPREListingXMP : 1;
+ bool m_bRestartPRE : 1;
+ bool m_bRestartXMP : 1;
+ bool m_bRestartListing : 1;
+ bool m_bHeaderOrFooter : 1;
+
+ bool m_bVisible = true;
+
+public:
+ void ClearSaveDocContext();
+
+ HTMLAttrContext( HtmlTokenId nTokn, sal_uInt16 nPoolId, OUString aClass,
+ bool bDfltColl=false );
+ explicit HTMLAttrContext( HtmlTokenId nTokn );
+ ~HTMLAttrContext();
+
+ HtmlTokenId GetToken() const { return m_nToken; }
+
+ sal_uInt16 GetTextFormatColl() const { return m_bDefaultTextFormatColl ? 0 : m_nTextFormatColl; }
+ sal_uInt16 GetDefaultTextFormatColl() const { return m_bDefaultTextFormatColl ? m_nTextFormatColl : 0; }
+
+ const OUString& GetClass() const { return m_aClass; }
+
+ inline void SetMargins( sal_uInt16 nLeft, sal_uInt16 nRight, short nIndent );
+
+ bool IsLRSpaceChanged() const { return m_bLRSpaceChanged; }
+ inline void GetMargins( sal_uInt16& nLeft, sal_uInt16& nRight,
+ short &nIndent ) const;
+
+ inline void SetULSpace( sal_uInt16 nUpper, sal_uInt16 nLower );
+ bool IsULSpaceChanged() const { return m_bULSpaceChanged; }
+ inline void GetULSpace( sal_uInt16& rUpper, sal_uInt16& rLower ) const;
+
+ bool HasAttrs() const { return !m_aAttrs.empty(); }
+ const HTMLAttrs& GetAttrs() const { return m_aAttrs; }
+ HTMLAttrs& GetAttrs() { return m_aAttrs; }
+
+ void SetSpansSection( bool bSet ) { m_bSpansSection = bSet; }
+ bool GetSpansSection() const { return m_bSpansSection; }
+
+ void SetPopStack( bool bSet ) { m_bPopStack = bSet; }
+ bool GetPopStack() const { return m_bPopStack; }
+
+ bool HasSaveDocContext() const { return m_pSaveDocContext!=nullptr; }
+ HTMLAttrContext_SaveDoc *GetSaveDocContext( bool bCreate=false );
+
+ const SfxItemSet *GetFrameItemSet() const { return m_pFrameItemSet.get(); }
+ SfxItemSet *GetFrameItemSet( SwDoc *pCreateDoc );
+
+ void SetFinishPREListingXMP( bool bSet ) { m_bFinishPREListingXMP = bSet; }
+ bool IsFinishPREListingXMP() const { return m_bFinishPREListingXMP; }
+
+ void SetRestartPRE( bool bSet ) { m_bRestartPRE = bSet; }
+ bool IsRestartPRE() const { return m_bRestartPRE; }
+
+ void SetRestartXMP( bool bSet ) { m_bRestartXMP = bSet; }
+ bool IsRestartXMP() const { return m_bRestartXMP; }
+
+ void SetRestartListing( bool bSet ) { m_bRestartListing = bSet; }
+ bool IsRestartListing() const { return m_bRestartListing; }
+
+ void SetHeaderOrFooter( bool bSet ) { m_bHeaderOrFooter = bSet; }
+ bool IsHeaderOrFooter() const { return m_bHeaderOrFooter; }
+
+ void SetAppendMode( SwHTMLAppendMode eMode ) { m_eAppend = eMode; }
+ SwHTMLAppendMode GetAppendMode() const { return m_eAppend; }
+
+ void SetVisible(bool bVisible) { m_bVisible = bVisible; }
+ bool IsVisible() const { return m_bVisible; }
+};
+
+typedef std::vector<std::unique_ptr<HTMLAttrContext>> HTMLAttrContexts;
+
+class HTMLTable;
+class SwCSS1Parser;
+class SwHTMLNumRuleInfo;
+
+typedef std::vector<std::unique_ptr<ImageMap>> ImageMaps;
+
+enum class HtmlContextFlags {
+ ProtectStack = 0x0001,
+ StripPara = 0x0002,
+ KeepNumrule = 0x0004,
+ HeaderDist = 0x0008,
+ FooterDist = 0x0010,
+ KeepAttrs = 0x0020,
+ MultiColMask = StripPara | KeepNumrule | KeepAttrs // for headers, footers or footnotes
+};
+namespace o3tl
+{
+ template<> struct typed_flags<HtmlContextFlags> : is_typed_flags<HtmlContextFlags, 0x03f> {};
+}
+
+enum class HtmlFrameFormatFlags {
+ Box = 0x0001,
+ Background = 0x0002,
+ Padding = 0x0004,
+ Direction = 0x0008,
+};
+namespace o3tl
+{
+ template<> struct typed_flags<HtmlFrameFormatFlags> : is_typed_flags<HtmlFrameFormatFlags, 0x0f> {};
+}
+
+class SwHTMLFrameFormatListener : public SvtListener
+{
+ SwFrameFormat* m_pFrameFormat;
+public:
+ SwHTMLFrameFormatListener(SwFrameFormat* pFrameFormat);
+ SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
+ virtual void Notify(const SfxHint&) override;
+};
+
+class SwHTMLParser : public SfxHTMLParser, public SvtListener
+{
+ friend class SectionSaveStruct;
+ friend class CellSaveStruct;
+ friend class CaptionSaveStruct;
+
+ /*
+ Progress bar
+ */
+ std::unique_ptr<ImportProgress> m_xProgress;
+
+ OUString m_aPathToFile;
+ OUString m_sBaseURL;
+ OUString m_aBasicLib;
+ OUString m_aBasicModule;
+ OUString m_aScriptSource; // content of the current script block
+ OUString m_aScriptType; // type of read script (StarBasic/VB/JAVA)
+ OUString m_aScriptURL; // script URL
+ OUString m_aStyleSource; // content of current style sheet
+ OUString m_aContents; // text of current marquee, field and so
+ OUStringBuffer m_sTitle;
+ OUString m_aUnknownToken; // a started unknown token
+ OUString m_aBulletGrfs[MAXLEVEL];
+ OUString m_sJmpMark;
+
+ std::vector<sal_uInt16> m_aBaseFontStack; // stack for <BASEFONT>
+ // Bit 0-2: font size (1-7)
+ std::vector<sal_uInt16> m_aFontStack; // stack for <FONT>, <BIG>, <SMALL>
+ // Bit 0-2: font size (1-7)
+ // Bit 15: font colour was set
+
+ HTMLAttrs m_aSetAttrTab;// "closed", not set attributes
+ HTMLAttrs m_aParaAttrs; // temporary paragraph attributes
+ std::shared_ptr<HTMLAttrTable> m_xAttrTab; // "open" attributes
+ HTMLAttrContexts m_aContexts;// the current context of attribute/token
+ std::vector<std::unique_ptr<SwHTMLFrameFormatListener>> m_aMoveFlyFrames;// Fly-Frames, the anchor is moved
+ std::deque<sal_Int32> m_aMoveFlyCnts;// and the Content-Positions
+ //stray SwTableBoxes which need to be deleted to avoid leaking, but hold
+ //onto them until parsing is done
+ std::vector<std::unique_ptr<SwTableBox>> m_aOrphanedTableBoxes;
+
+ std::unique_ptr<SwApplet_Impl> m_pAppletImpl; // current applet
+
+ std::unique_ptr<SwCSS1Parser> m_pCSS1Parser; // Style-Sheet-Parser
+ std::unique_ptr<SwHTMLNumRuleInfo> m_pNumRuleInfo;
+ std::vector<SwPending> m_vPendingStack;
+
+ rtl::Reference<SwDoc> m_xDoc;
+ SwPaM *m_pPam; // SwPosition should be enough, or ??
+ SwViewShell *m_pActionViewShell; // SwViewShell, where StartAction was called
+ SwNodeIndex *m_pSttNdIdx;
+
+ std::vector<HTMLTable*> m_aTables;
+ std::shared_ptr<HTMLTable> m_xTable; // current "outermost" table
+ SwHTMLForm_Impl* m_pFormImpl; // current form
+ rtl::Reference<SdrTextObj> m_pMarquee; // current marquee
+ std::unique_ptr<SwField> m_xField; // current field
+ ImageMap *m_pImageMap; // current image map
+ std::unique_ptr<ImageMaps> m_pImageMaps; ///< all Image-Maps that have been read
+ std::unique_ptr<SwHTMLFootEndNote_Impl> m_pFootEndNoteImpl;
+
+ Size m_aHTMLPageSize; // page size of HTML template
+
+ sal_uInt32 m_aFontHeights[7]; // font heights 1-7
+ ImplSVEvent * m_nEventId;
+
+ sal_uInt16 m_nBaseFontStMin;
+ sal_uInt16 m_nFontStMin;
+ sal_uInt16 m_nDefListDeep;
+ sal_uInt16 m_nFontStHeadStart; // elements in font stack at <Hn>
+ sal_uInt16 m_nSBModuleCnt; // counter for basic modules
+ sal_uInt16 m_nMissingImgMaps; // How many image maps are still missing?
+ size_t m_nParaCnt;
+ size_t m_nContextStMin; // lower limit of PopContext
+ size_t m_nContextStAttrMin; // lower limit of attributes
+ sal_uInt16 m_nSelectEntryCnt; // Number of entries in the actual listbox
+ HtmlTokenId m_nOpenParaToken; // opened paragraph element
+
+ enum class JumpToMarks { NONE, Mark, Table, Region, Graphic };
+ JumpToMarks m_eJumpTo;
+
+#ifdef DBG_UTIL
+ sal_uInt16 m_nContinue; // depth of Continue calls
+#endif
+
+ SvxAdjust m_eParaAdjust; // adjustment of current paragraph
+ HTMLScriptLanguage m_eScriptLang; // current script language
+
+ bool m_bOldIsHTMLMode : 1; // Was it a HTML document?
+
+ bool m_bDocInitialized : 1; // document resp. shell was initialize
+ // flag to prevent double init via recursion
+ bool m_bViewCreated : 1; // the view was already created (asynchronous)
+ bool m_bSetModEnabled : 1;
+
+ bool m_bInFloatingFrame : 1; // We are in a floating frame
+ bool m_bInField : 1;
+ bool m_bKeepUnknown : 1; // handle unknown/not supported tokens
+ // 8
+ bool m_bCallNextToken : 1; // In tables: call NextToken in any case
+ bool m_bIgnoreRawData : 1; // ignore content of script/style
+ bool m_bLBEntrySelected : 1; // Is the current option selected?
+ bool m_bTAIgnoreNewPara : 1; // ignore next LF in text area?
+ bool m_bFixMarqueeWidth : 1; // Change size of marquee?
+
+ bool m_bUpperSpace : 1; // top paragraph spacing is needed
+ bool m_bNoParSpace : 1;
+ // 16
+
+ bool m_bInNoEmbed : 1; // we are in a NOEMBED area
+
+ bool m_bInTitle : 1; // we are in title
+
+ bool m_bChkJumpMark : 1; // maybe jump to predetermined mark
+ bool m_bUpdateDocStat : 1;
+ bool m_bFixSelectWidth : 1; // Set new width of select?
+ bool m_bTextArea : 1;
+ // 24
+ bool m_bSelect : 1;
+ bool m_bInFootEndNoteAnchor : 1;
+ bool m_bInFootEndNoteSymbol : 1;
+ bool m_bIgnoreHTMLComments : 1;
+ bool m_bRemoveHidden : 1; // the filter implementation might set the hidden flag
+
+ bool m_bBodySeen : 1;
+ bool m_bReadingHeaderOrFooter : 1;
+ bool m_bNotifyMacroEventRead : 1;
+ bool m_isInTableStructure;
+
+ int m_nTableDepth;
+ int m_nFloatingFrames;
+ int m_nListItems;
+
+ /// the names corresponding to the DOCINFO field subtypes INFO[1-4]
+ OUString m_InfoNames[4];
+
+ SfxViewFrame* m_pTempViewFrame;
+
+ bool m_bXHTML = false;
+ bool m_bReqIF = false;
+
+ /**
+ * Non-owning pointers to already inserted OLE nodes, matching opened
+ * <object> XHTML elements.
+ */
+ std::stack<SwOLENode*> m_aEmbeds;
+
+ std::set<OUString> m_aAllowedRTFOLEMimeTypes;
+
+ /// This is the URL of the outer <object> data if it's not OLE2 or an image.
+ OUString m_aEmbedURL;
+
+ void DeleteFormImpl();
+
+ void DocumentDetected();
+ void Show();
+ void ShowStatline();
+ SwViewShell *CallStartAction( SwViewShell *pVSh = nullptr, bool bChkPtr = true );
+ SwViewShell *CallEndAction( bool bChkAction = false, bool bChkPtr = true );
+ SwViewShell *CheckActionViewShell();
+
+ DECL_LINK( AsyncCallback, void*, void );
+
+ // set attribute on document
+ void SetAttr_( bool bChkEnd, bool bBeforeTable, std::deque<std::unique_ptr<HTMLAttr>> *pPostIts );
+ void SetAttr( bool bChkEnd = true, bool bBeforeTable = false,
+ std::deque<std::unique_ptr<HTMLAttr>> *pPostIts = nullptr )
+ {
+ if( !m_aSetAttrTab.empty() || !m_aMoveFlyFrames.empty() )
+ SetAttr_( bChkEnd, bBeforeTable, pPostIts );
+ }
+
+ HTMLAttr **GetAttrTabEntry( sal_uInt16 nWhich );
+
+ // create a new text node on PaM position
+ bool AppendTextNode( SwHTMLAppendMode eMode=AM_NORMAL, bool bUpdateNum=true );
+ void AddParSpace();
+
+ // start/end an attribute
+ // ppDepAttr indicated an attribute table entry, which attribute has to be
+ // set, before the attribute is closed
+ void NewAttr(const std::shared_ptr<HTMLAttrTable>& rAttrTab, HTMLAttr **ppAttr, const SfxPoolItem& rItem);
+ bool EndAttr( HTMLAttr *pAttr, bool bChkEmpty=true );
+ void DeleteAttr( HTMLAttr* pAttr );
+
+ void EndContextAttrs( HTMLAttrContext *pContext );
+ void SaveAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab);
+ void SplitAttrTab( const SwPosition& rNewPos );
+ void SplitAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab, bool bMoveEndBack);
+ void RestoreAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab);
+ void InsertAttr( const SfxPoolItem& rItem, bool bInsAtStart );
+ void InsertAttrs( std::deque<std::unique_ptr<HTMLAttr>> rAttrs );
+
+ bool DoPositioning( SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo,
+ HTMLAttrContext *pContext );
+ bool CreateContainer( std::u16string_view rClass, SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo,
+ HTMLAttrContext *pContext );
+ bool EndSection( bool bLFStripped=false );
+
+ void InsertAttrs( SfxItemSet &rItemSet, SvxCSS1PropertyInfo const &rPropInfo,
+ HTMLAttrContext *pContext, bool bCharLvl=false );
+ void InsertAttr( HTMLAttr **ppAttr, const SfxPoolItem & rItem,
+ HTMLAttrContext *pCntxt );
+ void SplitPREListingXMP( HTMLAttrContext *pCntxt );
+ void FixHeaderFooterDistance( bool bHeader, const SwPosition *pOldPos );
+
+ void EndContext( HTMLAttrContext *pContext );
+ void ClearContext( HTMLAttrContext *pContext );
+
+ const SwFormatColl *GetCurrFormatColl() const;
+
+ SwTwips GetCurrentBrowseWidth();
+
+ SwHTMLNumRuleInfo& GetNumInfo() { return *m_pNumRuleInfo; }
+ // add parameter <bCountedInList>
+ void SetNodeNum( sal_uInt8 nLevel );
+
+ // Manage paragraph styles
+
+ // set the style resp. its attributes on the stack
+ void SetTextCollAttrs( HTMLAttrContext *pContext = nullptr );
+
+ void InsertParaAttrs( const SfxItemSet& rItemSet );
+
+ // Manage attribute context
+
+ // save current context
+ void PushContext(std::unique_ptr<HTMLAttrContext>& rCntxt)
+ {
+ m_aContexts.push_back(std::move(rCntxt));
+ }
+
+ // Fetch top/specified context but not outside the context with token
+ // nLimit. If bRemove set then remove it.
+ std::unique_ptr<HTMLAttrContext> PopContext(HtmlTokenId nToken = HtmlTokenId::NONE);
+
+ void GetMarginsFromContext( sal_uInt16 &nLeft, sal_uInt16 &nRight, short& nIndent,
+ bool bIgnoreCurrent=false ) const;
+ void GetMarginsFromContextWithNumberBullet( sal_uInt16 &nLeft, sal_uInt16 &nRight,
+ short& nIndent ) const;
+ void GetULSpaceFromContext( sal_uInt16 &rUpper, sal_uInt16 &rLower ) const;
+
+ void MovePageDescAttrs( SwNode *pSrcNd, SwNodeOffset nDestIdx, bool bFormatBreak );
+
+ // Handling of tags at paragraph level
+
+ // <P> and <H1> to <H6>
+ void NewPara();
+ void EndPara( bool bReal = false );
+ void NewHeading( HtmlTokenId nToken );
+ void EndHeading();
+
+ // <ADDRESS>, <BLOCKQUOTE> and <PRE>
+ void NewTextFormatColl( HtmlTokenId nToken, sal_uInt16 nPoolId );
+ void EndTextFormatColl( HtmlTokenId nToken );
+
+ // <DIV> and <CENTER>
+ void NewDivision( HtmlTokenId nToken );
+ void EndDivision();
+
+ // insert/close Fly-Frames
+ void InsertFlyFrame( const SfxItemSet& rItemSet, HTMLAttrContext *pCntxt,
+ const OUString& rId );
+
+ void SaveDocContext( HTMLAttrContext *pCntxt, HtmlContextFlags nFlags,
+ const SwPosition *pNewPos );
+ void RestoreDocContext( HTMLAttrContext *pCntxt );
+
+ // end all opened <DIV> areas
+ bool EndSections( bool bLFStripped );
+
+ // <MULTICOL>
+ void NewMultiCol( sal_uInt16 columnsFromCss=0 );
+
+ // <MARQUEE>
+ void NewMarquee( HTMLTable *pCurTable=nullptr );
+ void EndMarquee();
+ void InsertMarqueeText();
+
+ // Handling of lists
+
+ // order list <OL> and unordered list <UL> with <LI>
+ void NewNumberBulletList( HtmlTokenId nToken );
+ void EndNumberBulletList( HtmlTokenId nToken = HtmlTokenId::NONE );
+ void NewNumberBulletListItem( HtmlTokenId nToken );
+ void EndNumberBulletListItem( HtmlTokenId nToken, bool bSetColl);
+
+ // definitions lists <DL> with <DD>, <DT>
+ void NewDefList();
+ void EndDefList();
+ void NewDefListItem( HtmlTokenId nToken );
+ void EndDefListItem( HtmlTokenId nToken = HtmlTokenId::NONE );
+
+ // Handling of tags on character level
+
+ // handle tags like <B>, <I> and so, which enable/disable a certain
+ // attribute or like <SPAN> get attributes from styles
+ void NewStdAttr( HtmlTokenId nToken );
+ void NewStdAttr( HtmlTokenId nToken,
+ HTMLAttr **ppAttr, const SfxPoolItem & rItem,
+ HTMLAttr **ppAttr2=nullptr, const SfxPoolItem *pItem2=nullptr,
+ HTMLAttr **ppAttr3=nullptr, const SfxPoolItem *pItem3=nullptr );
+ void EndTag( HtmlTokenId nToken );
+
+ // handle font attributes
+ void NewBasefontAttr(); // for <BASEFONT>
+ void EndBasefontAttr();
+ void NewFontAttr( HtmlTokenId nToken ); // for <FONT>, <BIG> and <SMALL>
+ void EndFontAttr( HtmlTokenId nToken );
+
+ // tags realized via character styles
+ void NewCharFormat( HtmlTokenId nToken );
+
+ void DeleteSection(SwStartNode* pSttNd);
+
+ // <SDFIELD>
+public:
+ static SvxNumType GetNumType( std::u16string_view rStr, SvxNumType eDfltType );
+private:
+ void NewField();
+ void EndField();
+ void InsertFieldText();
+
+ // <SPACER>
+ void InsertSpacer();
+
+ // Inserting graphics, plug-ins and applets
+
+ // search image maps and link with graphic nodes
+ ImageMap *FindImageMap( std::u16string_view rURL ) const;
+ void ConnectImageMaps();
+
+ // find anchor of Fly-Frames and set corresponding attributes
+ // in Attrset (htmlgrin.cxx)
+ void SetAnchorAndAdjustment( sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ const SvxCSS1PropertyInfo &rPropInfo,
+ SfxItemSet& rFrameSet );
+ void SetAnchorAndAdjustment( sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ SfxItemSet& rFrameSet,
+ bool bDontAppend=false );
+ void SetAnchorAndAdjustment( const SvxCSS1PropertyInfo &rPropInfo,
+ SfxItemSet &rFrameItemSet );
+
+ static void SetFrameFormatAttrs( SfxItemSet &rItemSet,
+ HtmlFrameFormatFlags nFlags, SfxItemSet &rFrameItemSet );
+
+ // create frames and register auto bound frames
+ void RegisterFlyFrame( SwFrameFormat *pFlyFrame );
+
+ // Adjust the size of the Fly-Frames to requirements and conditions
+ // (not for graphics, therefore htmlplug.cxx)
+ static void SetFixSize( const Size& rPixSize, const Size& rTwipDfltSize,
+ bool bPercentWidth, bool bPercentHeight,
+ SvxCSS1PropertyInfo const &rPropInfo,
+ SfxItemSet& rFlyItemSet );
+ static void SetVarSize( SvxCSS1PropertyInfo const &rPropInfo,
+ SfxItemSet& rFlyItemSet, SwTwips nDfltWidth=MINLAY,
+ sal_uInt8 nDefaultPercentWidth=0 );
+ static void SetSpace( const Size& rPixSpace, SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo, SfxItemSet& rFlyItemSet );
+
+ sal_uInt16 IncGrfsThatResizeTable();
+
+ void GetDefaultScriptType( ScriptType& rType,
+ OUString& rTypeStr ) const;
+
+ // the actual insert methods for <IMG>, <EMBED>, <APPLET> and <PARAM>
+ void InsertImage(); // htmlgrin.cxx
+ bool InsertEmbed(); // htmlplug.cxx
+
+#if HAVE_FEATURE_JAVA
+ void NewObject(); // htmlplug.cxx
+#endif
+ void EndObject(); // link CommandLine with applet (htmlplug.cxx)
+#if HAVE_FEATURE_JAVA
+ void InsertApplet(); // htmlplug.cxx
+#endif
+ void EndApplet(); // link CommandLine with applet (htmlplug.cxx)
+ void InsertParam(); // htmlplug.cxx
+
+ void InsertFloatingFrame();
+
+ // parse <BODY>-tag: set background graphic and background colour (htmlgrin.cxx)
+ void InsertBodyOptions();
+
+ // Inserting links and bookmarks (htmlgrin.cxx)
+
+ // parse <A>-tag: insert a link resp. bookmark
+ void NewAnchor();
+ void EndAnchor();
+
+ // insert bookmark
+ void InsertBookmark( const OUString& rName );
+
+ void InsertCommentText( std::string_view pTag );
+ void InsertComment( const OUString& rName, std::string_view pTag = {} );
+
+ // Has the current paragraph bookmarks?
+ bool HasCurrentParaBookmarks( bool bIgnoreStack=false ) const;
+
+ // Inserting script/basic elements
+
+ // parse the last read basic module (htmlbas.cxx)
+ void NewScript();
+ void EndScript();
+
+ void AddScriptSource();
+
+ // insert event in SFX configuration (htmlbas.cxx)
+ void InsertBasicDocEvent( const OUString& aEventName, const OUString& rName,
+ ScriptType eScrType, const OUString& rScrType );
+
+ // Inserting styles
+
+ // <STYLE>
+ void NewStyle();
+ void EndStyle();
+
+ static inline bool HasStyleOptions( std::u16string_view rStyle, std::u16string_view rId,
+ std::u16string_view rClass, const OUString *pLang=nullptr,
+ const OUString *pDir=nullptr );
+ bool ParseStyleOptions( const OUString &rStyle, const OUString &rId,
+ const OUString &rClass, SfxItemSet &rItemSet,
+ SvxCSS1PropertyInfo &rPropInfo,
+ const OUString *pLang=nullptr, const OUString *pDir=nullptr );
+
+ // Inserting Controls and Forms (htmlform.cxx)
+
+ // Insert draw object into document
+ void InsertDrawObject( SdrObject* pNewDrawObj, const Size& rSpace,
+ sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ SfxItemSet& rCSS1ItemSet,
+ SvxCSS1PropertyInfo& rCSS1PropInfo );
+ css::uno::Reference< css::drawing::XShape > InsertControl(
+ const css::uno::Reference< css::form::XFormComponent > & rFormComp,
+ const css::uno::Reference< css::beans::XPropertySet > & rFCompPropSet,
+ const Size& rSize,
+ sal_Int16 eVertOri,
+ sal_Int16 eHoriOri,
+ SfxItemSet& rCSS1ItemSet,
+ SvxCSS1PropertyInfo& rCSS1PropInfo,
+ const SvxMacroTableDtor& rMacroTable,
+ const std::vector<OUString>& rUnoMacroTable,
+ const std::vector<OUString>& rUnoMacroParamTable,
+ bool bSetPropSet = true,
+ bool bHidden = false );
+ void SetControlSize( const css::uno::Reference< css::drawing::XShape > & rShape, const Size& rTextSz,
+ bool bMinWidth, bool bMinHeight );
+
+public:
+ static void ResizeDrawObject( SdrObject* pObj, SwTwips nWidth );
+private:
+ static void RegisterDrawObjectToTable( HTMLTable *pCurTable, SdrObject* pObj,
+ sal_uInt8 nWidth );
+
+ void NewForm( bool bAppend=true );
+ void EndForm( bool bAppend=true );
+
+ // Insert methods for <INPUT>, <TEXTAREA> and <SELECT>
+ void InsertInput();
+
+ void NewTextArea();
+ void InsertTextAreaText( HtmlTokenId nToken );
+ void EndTextArea();
+
+ void NewSelect();
+ void InsertSelectOption();
+ void InsertSelectText();
+ void EndSelect();
+
+ // Inserting tables (htmltab.cxx)
+public:
+
+ // Insert box content after the given node
+ const SwStartNode *InsertTableSection( const SwStartNode *pPrevStNd );
+
+ // Insert box content at the end of the table containing the PaM
+ // and move the PaM into the cell
+ const SwStartNode *InsertTableSection( sal_uInt16 nPoolId );
+
+ // Insert methods for various table tags
+ std::unique_ptr<HTMLTableCnts> InsertTableContents( bool bHead );
+
+private:
+ // Create a section for the temporary storage of the table caption
+ SwStartNode *InsertTempTableCaptionSection();
+
+ void BuildTableCell( HTMLTable *pTable, bool bReadOptions, bool bHead );
+ void BuildTableRow( HTMLTable *pTable, bool bReadOptions,
+ SvxAdjust eGrpAdjust, sal_Int16 eVertOri );
+ void BuildTableSection( HTMLTable *pTable, bool bReadOptions, bool bHead );
+ void BuildTableColGroup( HTMLTable *pTable, bool bReadOptions );
+ void BuildTableCaption( HTMLTable *pTable );
+ std::shared_ptr<HTMLTable> BuildTable(SvxAdjust eCellAdjust,
+ bool bIsParentHead = false,
+ bool bHasParentSection=true,
+ bool bHasToFlow = false);
+
+ // misc ...
+
+ void ParseMoreMetaOptions();
+
+ bool FileDownload( const OUString& rURL, OUString& rStr );
+ void InsertLink();
+
+ void InsertIDOption();
+ void InsertLineBreak();
+ void InsertHorzRule();
+
+ void FillEndNoteInfo( std::u16string_view aContent );
+ void FillFootNoteInfo( std::u16string_view aContent );
+ void InsertFootEndNote( const OUString& rName, bool bEndNote, bool bFixed );
+ void FinishFootEndNote();
+ void InsertFootEndNoteText();
+ SwNodeIndex *GetFootEndNoteSection( const OUString& rName );
+
+ sal_Int32 StripTrailingLF();
+
+ // Remove empty paragraph at the PaM position
+ void StripTrailingPara();
+ // If removing an empty node would corrupt the document
+ bool CanRemoveNode(SwNodeOffset nNodeIdx) const;
+
+ // Are there fly frames in the current paragraph?
+ bool HasCurrentParaFlys( bool bNoSurroundOnly = false,
+ bool bSurroundOnly = false ) const;
+
+ class TableDepthGuard
+ {
+ private:
+ SwHTMLParser& m_rParser;
+ public:
+ TableDepthGuard(SwHTMLParser& rParser)
+ : m_rParser(rParser)
+ {
+ ++m_rParser.m_nTableDepth;
+ }
+ bool TooDeep() const { return m_rParser.m_nTableDepth > 1024; }
+ ~TableDepthGuard()
+ {
+ --m_rParser.m_nTableDepth;
+ }
+ };
+
+public: // used in tables
+
+ // Create brush item (with new) or 0
+ SvxBrushItem* CreateBrushItem( const Color *pColor,
+ const OUString &rImageURL,
+ const OUString &rStyle,
+ const OUString &rId,
+ const OUString &rClass );
+
+protected:
+ // Executed for each token recognized by CallParser
+ virtual void NextToken( HtmlTokenId nToken ) override;
+ virtual ~SwHTMLParser() override;
+
+ // If the document is removed, remove the parser as well
+ virtual void Notify(const SfxHint&) override;
+
+ virtual void AddMetaUserDefined( OUString const & i_rMetaName ) override;
+
+public:
+
+ SwHTMLParser( SwDoc* pD, SwPaM & rCursor, SvStream& rIn,
+ OUString aFileName,
+ OUString aBaseURL,
+ bool bReadNewDoc,
+ SfxMedium* pMed, bool bReadUTF8,
+ bool bIgnoreHTMLComments,
+ const OUString& rNamespace);
+
+ virtual SvParserState CallParser() override;
+
+ static sal_uInt16 ToTwips( sal_uInt16 nPixel );
+
+ // for reading asynchronously from SvStream
+ virtual void Continue( HtmlTokenId nToken ) override;
+
+ virtual bool ParseMetaOptions( const css::uno::Reference<css::document::XDocumentProperties>&,
+ SvKeyValueIterator* ) override;
+
+
+ void RegisterHTMLTable(HTMLTable* pNew)
+ {
+ m_aTables.push_back(pNew);
+ }
+
+ void DeregisterHTMLTable(HTMLTable* pOld);
+
+ SwDoc* GetDoc() const;
+
+ bool IsReqIF() const;
+
+ bool IsReadingHeaderOrFooter() const { return m_bReadingHeaderOrFooter; }
+
+ void NotifyMacroEventRead();
+
+ /// Strips query and fragment from a URL path if base URL is a file:// one.
+ static OUString StripQueryFromPath(std::u16string_view rBase, const OUString& rPath);
+};
+
+struct SwPendingData
+{
+ virtual ~SwPendingData() {}
+};
+
+struct SwPending
+{
+ HtmlTokenId nToken;
+ std::unique_ptr<SwPendingData> pData;
+
+ SwPending( HtmlTokenId nTkn )
+ : nToken( nTkn )
+ {}
+};
+
+inline void HTMLAttr::SetStart( const SwPosition& rPos )
+{
+ m_nStartPara = rPos.GetNode();
+ m_nStartContent = rPos.GetContentIndex();
+ m_nEndPara = m_nStartPara;
+ m_nEndContent = m_nStartContent;
+}
+
+inline void HTMLAttrContext::SetMargins( sal_uInt16 nLeft, sal_uInt16 nRight,
+ short nIndent )
+{
+ m_nLeftMargin = nLeft;
+ m_nRightMargin = nRight;
+ m_nFirstLineIndent = nIndent;
+ m_bLRSpaceChanged = true;
+}
+
+inline void HTMLAttrContext::GetMargins( sal_uInt16& nLeft,
+ sal_uInt16& nRight,
+ short& nIndent ) const
+{
+ if( m_bLRSpaceChanged )
+ {
+ nLeft = m_nLeftMargin;
+ nRight = m_nRightMargin;
+ nIndent = m_nFirstLineIndent;
+ }
+}
+
+inline void HTMLAttrContext::SetULSpace( sal_uInt16 nUpper, sal_uInt16 nLower )
+{
+ m_nUpperSpace = nUpper;
+ m_nLowerSpace = nLower;
+ m_bULSpaceChanged = true;
+}
+
+inline void HTMLAttrContext::GetULSpace( sal_uInt16& rUpper,
+ sal_uInt16& rLower ) const
+{
+ if( m_bULSpaceChanged )
+ {
+ rUpper = m_nUpperSpace;
+ rLower = m_nLowerSpace;
+ }
+}
+
+inline bool SwHTMLParser::HasStyleOptions( std::u16string_view rStyle,
+ std::u16string_view rId,
+ std::u16string_view rClass,
+ const OUString *pLang,
+ const OUString *pDir )
+{
+ return !rStyle.empty() || !rId.empty() || !rClass.empty() ||
+ (pLang && !pLang->isEmpty()) || (pDir && !pDir->isEmpty());
+}
+
+class SwTextFootnote;
+
+class SwHTMLTextFootnote
+{
+private:
+ OUString m_sName;
+ SwTextFootnote* m_pTextFootnote;
+ std::unique_ptr<SvtDeleteListener> m_xDeleteListener;
+public:
+ SwHTMLTextFootnote(OUString rName, SwTextFootnote* pInTextFootnote)
+ : m_sName(std::move(rName))
+ , m_pTextFootnote(pInTextFootnote)
+ , m_xDeleteListener(new SvtDeleteListener(static_cast<SwFormatFootnote&>(pInTextFootnote->GetAttr()).GetNotifier()))
+ {
+ }
+ const OUString& GetName() const
+ {
+ return m_sName;
+ }
+ const SwNodeIndex* GetStartNode() const
+ {
+ if (m_xDeleteListener->WasDeleted())
+ return nullptr;
+ return m_pTextFootnote->GetStartNode();
+ }
+};
+
+struct SwHTMLFootEndNote_Impl
+{
+ std::vector<SwHTMLTextFootnote> aTextFootnotes;
+
+ OUString sName;
+ OUString sContent; // information for the last footnote
+ bool bEndNote;
+ bool bFixed;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx
new file mode 100644
index 0000000000..bd5015bb82
--- /dev/null
+++ b/sw/source/filter/html/wrthtml.cxx
@@ -0,0 +1,1694 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <hintids.hxx>
+#include <comphelper/string.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/languageoptions.hxx>
+#include <rtl/tencinfo.h>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <svtools/htmlcfg.hxx>
+#include <svtools/htmltokn.h>
+#include <svtools/htmlkywd.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sfx2/frmhtmlw.hxx>
+#include <svx/xoutbmp.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <sfx2/htmlmode.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/frmdiritem.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <fmthdft.hxx>
+#include <fmtfld.hxx>
+#include <fmtpdsc.hxx>
+#include <txatbase.hxx>
+#include <frmatr.hxx>
+#include <charfmt.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <mdiexp.hxx>
+#include <fltini.hxx>
+#include <viewopt.hxx>
+#include <IMark.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <section.hxx>
+#include <swtable.hxx>
+#include <fldbas.hxx>
+#include <fmtclds.hxx>
+#include <docsh.hxx>
+#include "wrthtml.hxx"
+#include "htmlnum.hxx"
+#include "htmlfly.hxx"
+#include <swmodule.hxx>
+#include <strings.hrc>
+#include <swerror.h>
+#include <rtl/strbuf.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <xmloff/odffields.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/file.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <unotools/tempfile.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Writer.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+
+#define MAX_INDENT_LEVEL 20
+
+using namespace css;
+
+static char sIndentTabs[MAX_INDENT_LEVEL+2] =
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL, std::u16string_view rFilterOptions )
+ : m_pNumRuleInfo(new SwHTMLNumRuleInfo)
+ , m_nHTMLMode(0)
+ , m_eCSS1Unit(FieldUnit::NONE)
+ , m_pStartNdIdx(nullptr)
+ , m_pCurrPageDesc(nullptr)
+ , m_pFormatFootnote(nullptr)
+ , m_nWarn(0)
+ , m_nLastLFPos(0)
+ , m_nLastParaToken(HtmlTokenId::NONE)
+ , m_nBkmkTabPos(-1)
+ , m_nImgMapCnt(1)
+ , m_nFormCntrlCnt(0)
+ , m_nEndNote(0)
+ , m_nFootNote(0)
+ , m_nLeftMargin(0)
+ , m_nDfltLeftMargin(0)
+ , m_nDfltRightMargin(0)
+ , m_nFirstLineIndent(0)
+ , m_nDfltFirstLineIndent(0)
+ , m_nDfltTopMargin(0)
+ , m_nDfltBottomMargin(0)
+ , m_nIndentLvl(0)
+ , m_nWishLineLen(70)
+ , m_nDefListLvl(0)
+ , m_nDefListMargin(0)
+ , m_nHeaderFooterSpace(0)
+ , m_nTextAttrsToIgnore(0)
+ , m_nExportMode(0)
+ , m_nCSS1OutMode(0)
+ , m_nCSS1Script(CSS1_OUTMODE_WESTERN)
+ , m_nDirection(SvxFrameDirection::Horizontal_LR_TB)
+ , m_eLang(LANGUAGE_DONTKNOW)
+ , m_bCfgOutStyles( false )
+ , m_bCfgPreferStyles( false )
+ , m_bCfgFormFeed( false )
+ , m_bCfgStarBasic( false )
+ , m_bCfgCpyLinkedGrfs( false )
+ , m_bFirstLine(true)
+ , m_bTagOn( false )
+ , m_bTextAttr( false )
+ , m_bOutOpts( false )
+ , m_bOutTable( false )
+ , m_bOutHeader( false )
+ , m_bOutFooter( false )
+ , m_bOutFlyFrame( false )
+ , m_bFirstCSS1Rule( false )
+ , m_bFirstCSS1Property( false )
+ , m_bCSS1IgnoreFirstPageDesc( false )
+ , m_bNoAlign( false )
+ , m_bClearLeft( false )
+ , m_bClearRight( false )
+ , m_bPreserveForm( false )
+ , m_bCfgNetscape4( false )
+ , mbSkipImages(false)
+ , mbSkipHeaderFooter(false)
+ , mbEmbedImages(false)
+ , m_bCfgPrintLayout( false )
+ , m_bParaDotLeaders( false )
+{
+ SetBaseURL(rBaseURL);
+
+ if (rBaseURL.isEmpty())
+ {
+ // Paste: set base URL to a tempfile, so images are not lost.
+ mpTempBaseURL.reset(new utl::TempFileNamed());
+ mpTempBaseURL->EnableKillingFile();
+ SetBaseURL(mpTempBaseURL->GetURL());
+ }
+
+ SetupFilterOptions(rFilterOptions);
+
+ if (mbXHTML)
+ {
+ m_bNoAlign = true;
+ }
+}
+
+SwHTMLWriter::~SwHTMLWriter()
+{
+}
+
+std::unique_ptr<SwHTMLNumRuleInfo> SwHTMLWriter::ReleaseNextNumInfo()
+{
+ return std::move(m_pNextNumRuleInfo);
+}
+
+void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium)
+{
+ uno::Sequence<beans::PropertyValue> aArgs = rMedium.GetArgs();
+ if (const SfxStringItem* pItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ))
+ {
+ const OUString sFilterOptions = pItem->GetValue();
+
+ if (sFilterOptions.startsWith("{"))
+ {
+ std::vector<beans::PropertyValue> aArgsVec
+ = comphelper::JsonToPropertyValues(sFilterOptions.toUtf8());
+ aArgs = comphelper::containerToSequence(aArgsVec);
+ }
+
+ SetupFilterOptions(sFilterOptions);
+ }
+
+ SetupFilterFromPropertyValues(aArgs);
+}
+
+void SwHTMLWriter::SetupFilterOptions(std::u16string_view rFilterOptions)
+{
+ comphelper::SequenceAsHashMap aStoreMap;
+ if (rFilterOptions.find(u"SkipImages") != std::u16string_view::npos)
+ {
+ aStoreMap["SkipImages"] <<= true;
+ }
+ else if (rFilterOptions.find(u"SkipHeaderFooter") != std::u16string_view::npos)
+ {
+ aStoreMap["SkipHeaderFooter"] <<= true;
+ }
+ else if (rFilterOptions.find(u"EmbedImages") != std::u16string_view::npos)
+ {
+ aStoreMap["EmbedImages"] <<= true;
+ }
+
+ // this option can be "on" together with any of above
+ if (rFilterOptions.find(u"NoLineLimit") != std::u16string_view::npos)
+ {
+ aStoreMap["NoLineLimit"] <<= true;
+ }
+
+ // this option can be "on" together with any of above
+ if (rFilterOptions.find(u"NoPrettyPrint") != std::u16string_view::npos)
+ {
+ aStoreMap["NoPrettyPrint"] <<= true;
+ }
+
+ const uno::Sequence<OUString> aOptionSeq
+ = comphelper::string::convertCommaSeparated(rFilterOptions);
+ static constexpr OUString aXhtmlNsKey(u"xhtmlns="_ustr);
+ for (const auto& rOption : aOptionSeq)
+ {
+ if (rOption == "XHTML")
+ {
+ aStoreMap["XHTML"] <<= true;
+ }
+ else if (rOption.startsWith(aXhtmlNsKey))
+ {
+ aStoreMap["XhtmlNs"] <<= rOption.copy(aXhtmlNsKey.getLength());
+ }
+ }
+
+ SetupFilterFromPropertyValues(aStoreMap.getAsConstPropertyValueList());
+}
+
+void SwHTMLWriter::SetupFilterFromPropertyValues(
+ const css::uno::Sequence<css::beans::PropertyValue>& rPropertyValues)
+{
+ comphelper::SequenceAsHashMap aStoreMap(rPropertyValues);
+ auto it = aStoreMap.find("RTFOLEMimeType");
+ if (it != aStoreMap.end())
+ {
+ it->second >>= m_aRTFOLEMimeType;
+ }
+
+ it = aStoreMap.find("ExportImagesAsOLE");
+ if (it != aStoreMap.end())
+ {
+ it->second >>= m_bExportImagesAsOLE;
+ }
+
+ it = aStoreMap.find("ExportFormulasAsPDF");
+ if (it != aStoreMap.end())
+ {
+ it->second >>= m_bExportFormulasAsPDF;
+ }
+
+ it = aStoreMap.find("ShapeDPI");
+ if (it != aStoreMap.end())
+ {
+ sal_Int32 nVal{};
+ it->second >>= nVal;
+ m_nShapeDPI.emplace(nVal);
+ }
+
+ it = aStoreMap.find("SkipImages");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbSkipImages = bVal;
+ }
+
+ it = aStoreMap.find("SkipHeaderFooter");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbSkipHeaderFooter = bVal;
+ }
+
+ // this option can be "on" together with any of above
+ it = aStoreMap.find("NoPrettyPrint");
+ if (it != aStoreMap.end())
+ {
+ m_nWishLineLen = -1;
+ m_bPrettyPrint = false;
+ }
+
+ it = aStoreMap.find("EmbedImages");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbEmbedImages = bVal;
+ }
+
+ it = aStoreMap.find("NoLineLimit");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ if (bVal)
+ {
+ m_nWishLineLen = -1;
+ }
+ }
+
+ it = aStoreMap.find("XHTML");
+ if (it != aStoreMap.end())
+ {
+ bool bVal{};
+ it->second >>= bVal;
+ mbXHTML = bVal;
+ }
+
+ it = aStoreMap.find("XhtmlNs");
+ if (it != aStoreMap.end())
+ {
+ OUString aVal;
+ it->second >>= aVal;
+
+ maNamespace = aVal.toUtf8();
+ if (maNamespace == "reqif-xhtml")
+ {
+ mbReqIF = true;
+ // XHTML is always just a fragment inside ReqIF.
+ mbSkipHeaderFooter = true;
+ }
+ // XHTML namespace implies XHTML.
+ mbXHTML = true;
+ }
+
+ it = aStoreMap.find("LeadingTabWidth");
+ if (it != aStoreMap.end())
+ {
+ sal_Int32 nVal{};
+ it->second >>= nVal;
+ m_nLeadingTabWidth.emplace(nVal);
+ }
+
+ it = aStoreMap.find("PreserveSpaces");
+ if (it != aStoreMap.end())
+ {
+ // Paragraphs with leading/trailing/repeated whitespace will have "white-space: pre-wrap"
+ // style; whitespace in content will not be altered (except for "LeadingTabWidth" effects)
+ bool bVal = false;
+ it->second >>= bVal;
+ m_bPreserveSpacesOnWrite = bVal;
+ }
+}
+
+ErrCode SwHTMLWriter::WriteStream()
+{
+ if (!SW_MOD())
+ return ERRCODE_ABORT;
+ // Intercept paste output if requested.
+ char* pPasteEnv = getenv("SW_DEBUG_HTML_PASTE_TO");
+ std::unique_ptr<SvStream> pPasteStream;
+ SvStream* pOldPasteStream = nullptr;
+ if (pPasteEnv)
+ {
+ OUString aPasteStr;
+ if (osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pPasteEnv), aPasteStr)
+ == osl::FileBase::E_None)
+ {
+ pPasteStream.reset(new SvFileStream(aPasteStr, StreamMode::WRITE));
+ pOldPasteStream = &Strm();
+ SetStream(pPasteStream.get());
+ }
+ }
+ comphelper::ScopeGuard g([this, pOldPasteStream] { this->SetStream(pOldPasteStream); });
+
+ // font heights 1-7
+ m_aFontHeights[0] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_1::get() * 20;
+ m_aFontHeights[1] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_2::get() * 20;
+ m_aFontHeights[2] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_3::get() * 20;
+ m_aFontHeights[3] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_4::get() * 20;
+ m_aFontHeights[4] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_5::get() * 20;
+ m_aFontHeights[5] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_6::get() * 20;
+ m_aFontHeights[6] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_7::get() * 20;
+
+ // output styles anyway
+ // (then also top and bottom paragraph spacing)
+ m_nExportMode = SvxHtmlOptions::GetExportMode();
+ m_nHTMLMode = GetHtmlMode(nullptr);
+
+ if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode )
+ m_nHTMLMode |= HTMLMODE_BLOCK_SPACER;
+
+ if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_MSIE == m_nExportMode )
+ m_nHTMLMode |= (HTMLMODE_FLOAT_FRAME | HTMLMODE_LSPACE_IN_NUMBER_BULLET);
+
+ if( HTML_CFG_MSIE == m_nExportMode )
+ m_nHTMLMode |= HTMLMODE_NBSP_IN_TABLES;
+
+ // For all three of HTML_CFG_WRITER, HTML_CFG_NS40, HTML_CFG_MSIE
+ m_nHTMLMode |= HTMLMODE_ABS_POS_FLY | HTMLMODE_ABS_POS_DRAW;
+
+ if( HTML_CFG_WRITER == m_nExportMode )
+ m_nHTMLMode |= HTMLMODE_FLY_MARGINS;
+
+ if( HTML_CFG_NS40 == m_nExportMode )
+ m_nHTMLMode |= HTMLMODE_BORDER_NONE;
+
+ m_nHTMLMode |= HTMLMODE_FONT_GENERIC;
+
+ if( HTML_CFG_NS40==m_nExportMode )
+ m_nHTMLMode |= HTMLMODE_NO_CONTROL_CENTERING;
+
+ m_bCfgOutStyles = IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES);
+ m_bCfgNetscape4 = (HTML_CFG_NS40 == m_nExportMode);
+
+ if( IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES) )
+ m_nHTMLMode |= HTMLMODE_PRINT_EXT;
+
+ m_eCSS1Unit = SW_MOD()->GetMetric( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) );
+
+ // Only for the MS-IE we favour the export of styles.
+ m_bCfgPreferStyles = HTML_CFG_MSIE == m_nExportMode;
+
+ m_bCfgStarBasic = officecfg::Office::Common::Filter::HTML::Export::Basic::get();
+
+ m_bCfgFormFeed = !IsHTMLMode(HTMLMODE_PRINT_EXT);
+ m_bCfgCpyLinkedGrfs = officecfg::Office::Common::Filter::HTML::Export::LocalGraphic::get();
+
+ m_bCfgPrintLayout = officecfg::Office::Common::Filter::HTML::Export::PrintLayout::get();
+
+ // get HTML template
+ bool bOldHTMLMode = false;
+ SwTextFormatColls::size_type nOldTextFormatCollCnt = 0;
+ SwCharFormats::size_type nOldCharFormatCnt = 0;
+
+ OSL_ENSURE( !m_xTemplate.is(), "Where is the HTML template coming from?" );
+ m_xTemplate = static_cast<HTMLReader*>(ReadHTML)->GetTemplateDoc(*m_pDoc);
+ if( m_xTemplate.is() )
+ {
+ bOldHTMLMode = m_xTemplate->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
+ m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true);
+
+ nOldTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size();
+ nOldCharFormatCnt = m_xTemplate->GetCharFormats()->size();
+ }
+
+ if( m_bShowProgress )
+ ::StartProgress( STR_STATSTR_W4WWRITE, 0, sal_Int32(m_pDoc->GetNodes().Count()),
+ m_pDoc->GetDocShell());
+
+ m_xDfltColor.reset();
+ m_xFootEndNotes.reset();
+ m_pFormatFootnote = nullptr;
+ m_bOutTable = m_bOutHeader = m_bOutFooter = m_bOutFlyFrame = false;
+ mxFormComps.clear();
+ m_nFormCntrlCnt = 0;
+ m_bPreserveForm = false;
+ m_bClearLeft = m_bClearRight = false;
+ m_bLFPossible = false;
+ m_bSpacePreserve = false;
+
+ m_nLeftMargin = m_nDfltLeftMargin = m_nDfltRightMargin = 0;
+ m_nDfltTopMargin = m_nDfltBottomMargin = 0;
+ m_nFirstLineIndent = m_nDfltFirstLineIndent = 0;
+ m_bFirstCSS1Property = m_bFirstCSS1Rule = false;
+ m_bCSS1IgnoreFirstPageDesc = false;
+ m_nIndentLvl = 0;
+ m_nLastLFPos = 0;
+ m_nDefListLvl = 0;
+ m_nDefListMargin = ((m_xTemplate.is() && !m_bCfgOutStyles) ? m_xTemplate.get() : m_pDoc)
+ ->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_HTML_DD, false )
+ ->GetTextLeftMargin().GetTextLeft();
+ m_nHeaderFooterSpace = 0;
+ m_nTextAttrsToIgnore = 0;
+ m_nCSS1OutMode = 0;
+ SvtScriptType nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( GetAppLanguage() );
+ switch( nScript )
+ {
+ case SvtScriptType::ASIAN:
+ m_nCSS1Script = CSS1_OUTMODE_CJK;
+ break;
+ case SvtScriptType::COMPLEX:
+ m_nCSS1Script = CSS1_OUTMODE_CTL;
+ break;
+ default:
+ m_nCSS1Script = CSS1_OUTMODE_WESTERN;
+ break;
+ }
+ const SvxLanguageItem& rLangitem = m_pDoc->GetDefault(GetLangWhichIdFromScript(m_nCSS1Script));
+ m_eLang = rLangitem.GetLanguage();
+
+ m_nFootNote = m_nEndNote = 0;
+
+ m_nWarn = ERRCODE_NONE;
+ GetNumInfo().Clear();
+ m_pNextNumRuleInfo = nullptr;
+
+ OString aStartTags;
+
+ // respect table and section at document beginning
+ {
+ if (m_bWriteAll)
+ {
+ while (const SwStartNode* pTNd = m_pCurrentPam->GetPointNode().FindTableBoxStartNode())
+ {
+ // start with table node !!
+ m_pCurrentPam->GetPoint()->Assign(*pTNd->FindTableNode());
+
+ if (m_bWriteOnlyFirstTable)
+ m_pCurrentPam->GetMark()->Assign(
+ *m_pCurrentPam->GetPointNode().EndOfSectionNode());
+ }
+ }
+
+ // first node (which can contain a page break)
+ m_pStartNdIdx = new SwNodeIndex( m_pCurrentPam->GetPoint()->GetNode() );
+
+ SwSectionNode * pSNd = m_pCurrentPam->GetPointNode().FindSectionNode();
+ while( pSNd )
+ {
+ if( m_bWriteAll )
+ {
+ // start with section node !!
+ m_pCurrentPam->GetPoint()->Assign(*pSNd);
+ }
+ else
+ {
+ OSL_ENSURE( SectionType::FileLink != pSNd->GetSection().GetType(),
+ "Export linked areas at document beginning is not implemented" );
+
+ // save only the tag of section
+ OString aName = HTMLOutFuncs::ConvertStringToHTML(
+ pSNd->GetSection().GetSectionName() );
+
+ aStartTags =
+ "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
+ " " OOO_STRING_SVTOOLS_HTML_O_id
+ "=\"" + aName + "\">" +
+ aStartTags;
+ }
+ // FindSectionNode() on a SectionNode return the same!
+ pSNd = pSNd->StartOfSectionNode()->FindSectionNode();
+ }
+ }
+
+ // create table of the floating frames, but only when the whole
+ // document is saved
+ m_aHTMLPosFlyFrames.clear();
+ CollectFlyFrames();
+ m_nLastParaToken = HtmlTokenId::NONE;
+
+ if (mbReqIF && !m_bWriteAll
+ && *m_pCurrentPam->GetPoint() == *m_pCurrentPam->GetMark()
+ && m_pCurrentPam->GetPoint()->GetNode().IsOLENode() && m_aHTMLPosFlyFrames.size() == 1)
+ {
+ // A single OLE object selection must be output: do it directly (without replacement)
+ OutHTML_FrameFormatOLENodeGrf(*this, m_aHTMLPosFlyFrames[0]->GetFormat(), true, false);
+ }
+ else
+ {
+ GetControls();
+ CollectLinkTargets();
+
+ sal_uInt16 nHeaderAttrs = 0;
+ m_pCurrPageDesc = MakeHeader( nHeaderAttrs );
+
+ SetLFPossible(true);
+
+ // output forms which contain only HiddenControls
+ OutHiddenForms();
+
+ if( !aStartTags.isEmpty() )
+ Strm().WriteOString( aStartTags );
+
+ const SwFormatHeader *pFormatHeader;
+ const SfxItemSet& rPageItemSet = m_pCurrPageDesc->GetMaster().GetAttrSet();
+ if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() &&
+ (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) &&
+ !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) &&
+ (pFormatHeader = rPageItemSet.GetItemIfSet( RES_HEADER )) )
+ {
+ const SwFrameFormat *pHeaderFormat = pFormatHeader->GetHeaderFormat();
+ if( pHeaderFormat )
+ OutHTML_HeaderFooter( *this, *pHeaderFormat, true );
+ }
+
+ m_nTextAttrsToIgnore = nHeaderAttrs;
+ Out_SwDoc( m_pOrigPam );
+ m_nTextAttrsToIgnore = 0;
+
+ if( mxFormComps.is() )
+ OutForm( false, mxFormComps );
+
+ if( m_xFootEndNotes )
+ OutFootEndNotes();
+
+ const SwFormatFooter* pFormatFooter;
+ if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() &&
+ (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) &&
+ (pFormatFooter = rPageItemSet.GetItemIfSet( RES_FOOTER )) )
+ {
+ const SwFrameFormat *pFooterFormat = pFormatFooter->GetFooterFormat();
+ if( pFooterFormat )
+ OutHTML_HeaderFooter( *this, *pFooterFormat, false );
+ }
+
+ if (IsLFPossible())
+ OutNewLine();
+ if (!mbSkipHeaderFooter)
+ {
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_body), false );
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html), false );
+ }
+ else if (mbReqIF)
+ // ReqIF: end xhtml.BlkStruct.class.
+ HTMLOutFuncs::Out_AsciiTag(Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false);
+ }
+ // delete the table with floating frames
+ OSL_ENSURE( m_aHTMLPosFlyFrames.empty(), "Were not all frames output?" );
+ m_aHTMLPosFlyFrames.clear();
+
+ m_aHTMLControls.clear();
+
+ m_CharFormatInfos.clear();
+ m_TextCollInfos.clear();
+ m_aImgMapNames.clear();
+ m_aImplicitMarks.clear();
+ m_aOutlineMarks.clear();
+ m_aOutlineMarkPoss.clear();
+ m_aNumRuleNames.clear();
+ m_aScriptParaStyles.clear();
+ m_aScriptTextStyles.clear();
+
+ m_xDfltColor.reset();
+
+ delete m_pStartNdIdx;
+ m_pStartNdIdx = nullptr;
+
+ mxFormComps.clear();
+
+ OSL_ENSURE( !m_xFootEndNotes,
+ "SwHTMLWriter::Write: Footnotes not deleted by OutFootEndNotes" );
+
+ m_pCurrPageDesc = nullptr;
+
+ ClearNextNumInfo();
+
+ for(OUString & s : m_aBulletGrfs)
+ s.clear();
+
+ if( m_bShowProgress )
+ ::EndProgress( m_pDoc->GetDocShell() );
+
+ if( m_xTemplate.is() )
+ {
+ // delete character and paragraph templates created during export
+ auto nTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size();
+ while( nTextFormatCollCnt > nOldTextFormatCollCnt )
+ m_xTemplate->DelTextFormatColl( --nTextFormatCollCnt );
+ OSL_ENSURE( m_xTemplate->GetTextFormatColls()->size() == nOldTextFormatCollCnt,
+ "wrong number of TextFormatColls deleted" );
+
+ auto nCharFormatCnt = m_xTemplate->GetCharFormats()->size();
+ while( nCharFormatCnt > nOldCharFormatCnt )
+ m_xTemplate->DelCharFormat( --nCharFormatCnt );
+ OSL_ENSURE( m_xTemplate->GetCharFormats()->size() == nOldCharFormatCnt,
+ "wrong number of CharFormats deleted" );
+
+ // restore HTML mode
+ m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, bOldHTMLMode);
+
+ m_xTemplate.clear();
+ }
+
+ return m_nWarn;
+}
+
+static const SwFormatCol *lcl_html_GetFormatCol( const SwSection& rSection,
+ const SwSectionFormat& rFormat )
+{
+ if( SectionType::FileLink == rSection.GetType() )
+ return nullptr;
+
+ const SwFormatCol *pCol = rFormat.GetAttrSet().GetItemIfSet(RES_COL,false);
+ if (pCol->GetNumCols() > 1 )
+ return pCol;
+
+ return nullptr;
+}
+
+static bool lcl_html_IsMultiColStart( const SwHTMLWriter& rHTMLWrt, SwNodeOffset nIndex )
+{
+ bool bRet = false;
+ const SwSectionNode *pSectNd =
+ rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetSectionNode();
+ if( pSectNd )
+ {
+ const SwSection& rSection = pSectNd->GetSection();
+ const SwSectionFormat *pFormat = rSection.GetFormat();
+ if( pFormat && lcl_html_GetFormatCol( rSection, *pFormat ) )
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+static bool lcl_html_IsMultiColEnd( const SwHTMLWriter& rHTMLWrt, SwNodeOffset nIndex )
+{
+ bool bRet = false;
+ const SwEndNode *pEndNd = rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetEndNode();
+ if( pEndNd )
+ bRet = lcl_html_IsMultiColStart( rHTMLWrt,
+ pEndNd->StartOfSectionIndex() );
+
+ return bRet;
+}
+
+static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt,
+ const SwSection& rSection,
+ const SwSectionFormat& rFormat,
+ const SwFormatCol *pCol,
+ bool bContinued=false )
+{
+ OSL_ENSURE( pCol || !bContinued, "Continuation of DIV" );
+
+ if (rHTMLWrt.IsLFPossible())
+ rHTMLWrt.OutNewLine();
+
+ OStringBuffer sOut("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division);
+
+ const OUString& rName = rSection.GetSectionName();
+ if( !rName.isEmpty() && !bContinued )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_id "=\"");
+ rHTMLWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), rName );
+ sOut.append('\"');
+ }
+
+ rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() );
+ if (!rHTMLWrt.mbXHTML)
+ {
+ SvxFrameDirection nDir = rHTMLWrt.GetHTMLDirection(rFormat.GetAttrSet());
+ rHTMLWrt.OutDirection(nDir);
+ }
+
+ if( SectionType::FileLink == rSection.GetType() )
+ {
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_href "=\"");
+ rHTMLWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ const OUString& aFName = rSection.GetLinkFileName();
+ sal_Int32 nIdx{ 0 };
+ OUString aURL( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
+ OUString aFilter( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
+ OUString aSection( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) );
+
+ OUString aEncURL( URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aURL ) );
+ sal_Unicode cDelim = 255U;
+ bool bURLContainsDelim = (-1 != aEncURL.indexOf( cDelim ) );
+
+ HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aEncURL );
+ const char* const pDelim = "&#255;";
+ if( !aFilter.isEmpty() || !aSection.isEmpty() || bURLContainsDelim )
+ rHTMLWrt.Strm().WriteOString( pDelim );
+ if( !aFilter.isEmpty() )
+ HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aFilter );
+ if( !aSection.isEmpty() || bURLContainsDelim )
+ rHTMLWrt.Strm().WriteOString( pDelim );
+ if( !aSection.isEmpty() )
+ {
+ aSection = aSection.replaceAll(u"%", u"%25");
+ aSection = aSection.replaceAll(OUStringChar(cDelim), u"%FF");
+ HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aSection );
+ }
+ sOut.append('\"');
+ }
+ else if( pCol )
+ {
+ // minimum gutter width
+ sal_uInt16 nGutter = pCol->GetGutterWidth( true );
+ if( nGutter!=USHRT_MAX )
+ {
+ nGutter = SwHTMLWriter::ToPixel(nGutter);
+ sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter "=\"" + OString::number(nGutter) + "\"");
+ }
+ }
+
+ rHTMLWrt.Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ if( rHTMLWrt.IsHTMLMode( rHTMLWrt.m_bCfgOutStyles ? HTMLMODE_ON : 0 ) )
+ rHTMLWrt.OutCSS1_SectionFormatOptions( rFormat, pCol );
+
+ rHTMLWrt.Strm().WriteChar( '>' );
+
+ rHTMLWrt.SetLFPossible(true);
+ if( !rName.isEmpty() && !bContinued )
+ rHTMLWrt.OutImplicitMark( rName, "region" );
+
+ rHTMLWrt.IncIndentLevel();
+}
+
+static void lcl_html_OutSectionEndTag( SwHTMLWriter& rHTMLWrt )
+{
+ rHTMLWrt.DecIndentLevel();
+ if (rHTMLWrt.IsLFPossible())
+ rHTMLWrt.OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( rHTMLWrt.Strm(), Concat2View(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
+ rHTMLWrt.SetLFPossible(true);
+}
+
+static SwHTMLWriter& OutHTML_Section( SwHTMLWriter& rWrt, const SwSectionNode& rSectNd )
+{
+ // End <PRE> and any <DL>, because a definition list's level may
+ // change inside the section.
+ rWrt.ChangeParaToken( HtmlTokenId::NONE );
+ rWrt.OutAndSetDefList( 0 );
+
+ const SwSection& rSection = rSectNd.GetSection();
+ const SwSectionFormat *pFormat = rSection.GetFormat();
+ OSL_ENSURE( pFormat, "Section without a format?" );
+
+ bool bStartTag = true;
+ bool bEndTag = true;
+ const SwSectionFormat *pSurrFormat = nullptr;
+ const SwSectionNode *pSurrSectNd = nullptr;
+ const SwSection *pSurrSection = nullptr;
+ const SwFormatCol *pSurrCol = nullptr;
+
+ SwNodeOffset nSectSttIdx = rSectNd.GetIndex();
+ SwNodeOffset nSectEndIdx = rSectNd.EndOfSectionIndex();
+ const SwFormatCol *pCol = lcl_html_GetFormatCol( rSection, *pFormat );
+ if( pCol )
+ {
+ // If the next node is a columned section node, too, don't export
+ // an empty section.
+ if( lcl_html_IsMultiColStart( rWrt, nSectSttIdx+1 ) )
+ bStartTag = false;
+
+ // The same applies if the section end with another columned section.
+ if( lcl_html_IsMultiColEnd( rWrt, nSectEndIdx-1 ) )
+ bEndTag = false;
+
+ // is there a columned section around this one?
+ const SwStartNode *pSttNd = rSectNd.StartOfSectionNode();
+ if( pSttNd )
+ {
+ pSurrSectNd = pSttNd->FindSectionNode();
+ if( pSurrSectNd )
+ {
+ const SwStartNode *pBoxSttNd = pSttNd->FindTableBoxStartNode();
+ if( !pBoxSttNd ||
+ pBoxSttNd->GetIndex() < pSurrSectNd->GetIndex() )
+ {
+ pSurrSection = &pSurrSectNd->GetSection();
+ pSurrFormat = pSurrSection->GetFormat();
+ if( pSurrFormat )
+ pSurrCol = lcl_html_GetFormatCol( *pSurrSection,
+ *pSurrFormat );
+ }
+ }
+ }
+ }
+
+ // The surrounding section must be closed before the current one is
+ // opened, except that it start immediately before the current one or
+ // another end immediately before the current one
+ if( pSurrCol && nSectSttIdx - pSurrSectNd->GetIndex() > SwNodeOffset(1) &&
+ !lcl_html_IsMultiColEnd( rWrt, nSectSttIdx-1 ) )
+ lcl_html_OutSectionEndTag( rWrt );
+
+ if( bStartTag )
+ lcl_html_OutSectionStartTag( rWrt, rSection, *pFormat, pCol );
+
+ {
+ HTMLSaveData aSaveData( rWrt,
+ rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex()+1,
+ rSectNd.EndOfSectionIndex(),
+ false, pFormat );
+ rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
+ }
+
+ rWrt.m_pCurrentPam->GetPoint()->Assign(*rSectNd.EndOfSectionNode());
+
+ if( bEndTag )
+ lcl_html_OutSectionEndTag( rWrt );
+
+ // The surrounding section must be started again, except that it ends
+ // immediately behind the current one.
+ if( pSurrCol &&
+ pSurrSectNd->EndOfSectionIndex() - nSectEndIdx > SwNodeOffset(1) &&
+ !lcl_html_IsMultiColStart( rWrt, nSectEndIdx+1 ) )
+ lcl_html_OutSectionStartTag( rWrt, *pSurrSection, *pSurrFormat,
+ pSurrCol, true );
+
+ return rWrt;
+}
+
+void SwHTMLWriter::Out_SwDoc( SwPaM* pPam )
+{
+ bool bSaveWriteAll = m_bWriteAll; // save
+ bool bIncludeHidden = officecfg::Office::Writer::FilterFlags::HTML::IncludeHiddenText::get();
+
+ // search next text::Bookmark position from text::Bookmark table
+ m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
+
+ // output all areas of PaM's in the HTML file
+ do {
+ m_bWriteAll = bSaveWriteAll;
+ m_bFirstLine = true;
+
+ // search for first on PaM created FlyFrame
+ // still missing:
+
+ while( m_pCurrentPam->GetPoint()->GetNodeIndex() < m_pCurrentPam->GetMark()->GetNodeIndex() ||
+ (m_pCurrentPam->GetPoint()->GetNodeIndex() == m_pCurrentPam->GetMark()->GetNodeIndex() &&
+ m_pCurrentPam->GetPoint()->GetContentIndex() <= m_pCurrentPam->GetMark()->GetContentIndex()) )
+ {
+ SwNode& rNd = m_pCurrentPam->GetPointNode();
+
+ OSL_ENSURE( !(rNd.IsGrfNode() || rNd.IsOLENode()),
+ "Unexpected Grf- or OLE-Node here" );
+
+ if( rNd.IsTextNode() )
+ {
+ SwTextNode* pTextNd = rNd.GetTextNode();
+ if (!pTextNd->IsHidden() || bIncludeHidden)
+ {
+ if (!m_bFirstLine)
+ m_pCurrentPam->GetPoint()->Assign(*pTextNd, 0);
+
+ OutHTML_SwTextNode(*this, *pTextNd);
+ }
+ }
+ else if( rNd.IsTableNode() )
+ {
+ OutHTML_SwTableNode( *this, *rNd.GetTableNode(), nullptr );
+ m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
+ }
+ else if( rNd.IsSectionNode() )
+ {
+ SwSectionNode* pSectionNode = rNd.GetSectionNode();
+ if (!pSectionNode->GetSection().IsHiddenFlag() || bIncludeHidden)
+ {
+ OutHTML_Section( *this, *pSectionNode );
+ m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1;
+ }
+ }
+ else if( &rNd == &m_pDoc->GetNodes().GetEndOfContent() )
+ break;
+
+ m_pCurrentPam->GetPoint()->Adjust(SwNodeOffset(+1)); // move
+ SwNodeOffset nPos = m_pCurrentPam->GetPoint()->GetNodeIndex();
+
+ if( m_bShowProgress )
+ ::SetProgressState( sal_Int32(nPos), m_pDoc->GetDocShell() ); // How far ?
+
+ /* If only the selected area should be saved, so only the complete
+ * nodes should be saved, this means the first and n-th node
+ * partly, the 2nd till n-1 node complete. (complete means with
+ * all formats!)
+ */
+ m_bWriteAll = bSaveWriteAll ||
+ nPos != m_pCurrentPam->GetMark()->GetNodeIndex();
+ m_bFirstLine = false;
+ m_bOutFooter = false; // after one node no footer anymore
+ }
+
+ ChangeParaToken( HtmlTokenId::NONE ); // MIB 8.7.97: We're doing it here and not at the caller
+ OutAndSetDefList( 0 );
+
+ } while( CopyNextPam( &pPam ) ); // until all PaM's processed
+
+ m_bWriteAll = bSaveWriteAll; // reset to old values
+}
+
+// write the StyleTable, general data, header/footer/footnotes
+static void OutBodyColor( const char* pTag, const SwFormat *pFormat,
+ SwHTMLWriter& rHWrt )
+{
+ const SwFormat *pRefFormat = nullptr;
+
+ if( rHWrt.m_xTemplate.is() )
+ pRefFormat = SwHTMLWriter::GetTemplateFormat( pFormat->GetPoolFormatId(),
+ &rHWrt.m_xTemplate->getIDocumentStylePoolAccess() );
+
+ const SvxColorItem *pColorItem = nullptr;
+
+ const SfxItemSet& rItemSet = pFormat->GetAttrSet();
+ const SvxColorItem *pCItem = rItemSet.GetItemIfSet( RES_CHRATR_COLOR );
+ const SvxColorItem *pRefItem = nullptr;
+ if (pRefFormat)
+ pRefItem = pRefFormat->GetAttrSet().GetItemIfSet( RES_CHRATR_COLOR );
+ if( pCItem )
+ {
+ // only when the item is set in the template of the current document
+ // or has a different value as the in HTML template, it will be set
+
+ if( !pRefItem )
+ {
+ pColorItem = pCItem;
+ }
+ else
+ {
+ Color aColor( pCItem->GetValue() );
+ if( COL_AUTO == aColor )
+ aColor = COL_BLACK;
+
+ Color aRefColor( pRefItem->GetValue() );
+ if( COL_AUTO == aRefColor )
+ aRefColor = COL_BLACK;
+
+ if( !aColor.IsRGBEqual( aRefColor ) )
+ pColorItem = pCItem;
+ }
+ }
+ else if( pRefItem )
+ {
+ // The item was still set in the HTML template so we output the default
+ pColorItem = &rItemSet.GetPool()->GetDefaultItem( RES_CHRATR_COLOR );
+ }
+
+ if( pColorItem )
+ {
+ OString sOut = OString::Concat(" ") + pTag + "=";
+ rHWrt.Strm().WriteOString( sOut );
+ Color aColor( pColorItem->GetValue() );
+ if( COL_AUTO == aColor )
+ aColor = COL_BLACK;
+ HTMLOutFuncs::Out_Color( rHWrt.Strm(), aColor );
+ if( RES_POOLCOLL_STANDARD==pFormat->GetPoolFormatId() )
+ rHWrt.m_xDfltColor = aColor;
+ }
+}
+
+sal_uInt16 SwHTMLWriter::OutHeaderAttrs()
+{
+ SwNodeOffset nIdx = m_pCurrentPam->GetPoint()->GetNodeIndex();
+ SwNodeOffset nEndIdx = m_pCurrentPam->GetMark()->GetNodeIndex();
+
+ SwTextNode *pTextNd = nullptr;
+ while( nIdx<=nEndIdx &&
+ nullptr==(pTextNd=m_pDoc->GetNodes()[nIdx]->GetTextNode()) )
+ nIdx++;
+
+ OSL_ENSURE( pTextNd, "No Text-Node found" );
+ if( !pTextNd || !pTextNd->HasHints() )
+ return 0;
+
+ sal_uInt16 nAttrs = 0;
+ const size_t nCntAttr = pTextNd->GetSwpHints().Count();
+ sal_Int32 nOldPos = 0;
+ for( size_t i=0; i<nCntAttr; ++i )
+ {
+ const SwTextAttr *pHt = pTextNd->GetSwpHints().Get(i);
+ if( !pHt->End() )
+ {
+ sal_Int32 nPos = pHt->GetStart();
+ if( nPos-nOldPos > 1
+ || ( pHt->Which() != RES_TXTATR_FIELD
+ && pHt->Which() != RES_TXTATR_ANNOTATION ) )
+ break;
+
+ const SwFieldIds nFieldWhich =
+ static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->GetTyp()->Which();
+ if( SwFieldIds::Postit!=nFieldWhich &&
+ SwFieldIds::Script!=nFieldWhich )
+ break;
+
+ OutNewLine();
+ OutHTML_SwFormatField( *this, pHt->GetAttr() );
+ nOldPos = nPos;
+ OSL_ENSURE( nAttrs<SAL_MAX_UINT16, "Too many attributes" );
+ nAttrs++;
+ }
+ }
+
+ return nAttrs;
+}
+
+const SwPageDesc *SwHTMLWriter::MakeHeader( sal_uInt16 &rHeaderAttrs )
+{
+ OStringBuffer sOut;
+ if (!mbSkipHeaderFooter)
+ {
+ if (mbXHTML)
+ sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_XHTML_doctype11);
+ else
+ sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_HTML_doctype5);
+ HTMLOutFuncs::Out_AsciiTag( Strm(), sOut.makeStringAndClear() ); // No GetNamespace() here.
+
+ // build prelude
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_html) );
+
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head) );
+
+ IncIndentLevel(); // indent content of <HEAD>
+
+ // DocumentInfo
+ OString sIndent = GetIndentString();
+
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ SwDocShell *pDocShell(m_pDoc->GetDocShell());
+ if (pDocShell)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ xDocProps.set(xDPS->getDocumentProperties());
+ }
+
+ // xDocProps may be null here (when copying)
+ SfxFrameHTMLWriter::Out_DocInfo( Strm(), GetBaseURL(), xDocProps,
+ sIndent.getStr() );
+
+ // comments and meta-tags of first paragraph
+ rHeaderAttrs = OutHeaderAttrs();
+
+ OutFootEndNoteInfo();
+ }
+
+ const SwPageDesc *pPageDesc = nullptr;
+
+ // In none HTML documents the first set template will be exported
+ // and if none is set the default template
+ SwNodeOffset nNodeIdx = m_pCurrentPam->GetPoint()->GetNodeIndex();
+
+ while( nNodeIdx < m_pDoc->GetNodes().Count() )
+ {
+ SwNode *pNd = m_pDoc->GetNodes()[ nNodeIdx ];
+ if( pNd->IsContentNode() )
+ {
+ pPageDesc = pNd->GetContentNode()->GetAttr(RES_PAGEDESC).GetPageDesc();
+ break;
+ }
+ else if( pNd->IsTableNode() )
+ {
+ pPageDesc = pNd->GetTableNode()->GetTable().GetFrameFormat()
+ ->GetPageDesc().GetPageDesc();
+ break;
+ }
+
+ nNodeIdx++;
+ }
+
+ if( !pPageDesc )
+ pPageDesc = &m_pDoc->GetPageDesc( 0 );
+
+ if (!mbSkipHeaderFooter)
+ {
+ // and now ... the style sheet!!!
+ if( m_bCfgOutStyles )
+ {
+ OutStyleSheet( *pPageDesc );
+ }
+
+ // and now ... the BASIC and JavaScript!
+ if( m_pDoc->GetDocShell() ) // BASIC is possible only in case we have a DocShell
+ OutBasic(*this);
+
+ DecIndentLevel(); // indent content of <HEAD>
+ OutNewLine();
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_head), false );
+
+ // the body won't be indented, because then everything would be indented!
+ OutNewLine();
+ sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_body);
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+
+ // language
+ OutLanguage( m_eLang );
+
+ // output text colour, when it was set in the default template or was changed
+ OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_text,
+ m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false ),
+ *this );
+
+ // colour of (un)visited links
+ OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_link,
+ m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL ),
+ *this );
+ OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_vlink,
+ m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_VISIT ),
+ *this );
+
+ const SfxItemSet& rItemSet = pPageDesc->GetMaster().GetAttrSet();
+
+ // fdo#86857 page styles now contain the XATTR_*, not RES_BACKGROUND
+ std::unique_ptr<SvxBrushItem> const aBrushItem(getSvxBrushItemFromSourceSet(rItemSet, RES_BACKGROUND));
+ OutBackground(aBrushItem.get(), true);
+
+ m_nDirection = GetHTMLDirection( rItemSet );
+ OutDirection( m_nDirection );
+
+ if( m_bCfgOutStyles )
+ {
+ OutCSS1_BodyTagStyleOpt( *this, rItemSet );
+ }
+ // append events
+ if( m_pDoc->GetDocShell() ) // BASIC is possible only in case we have a DocShell
+ OutBasicBodyEvents();
+
+ Strm().WriteChar( '>' );
+ }
+ else if (mbReqIF)
+ // ReqIF: start xhtml.BlkStruct.class.
+ HTMLOutFuncs::Out_AsciiTag(Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division));
+
+ return pPageDesc;
+}
+
+void SwHTMLWriter::OutAnchor( const OUString& rName )
+{
+ if (mbReqIF)
+ {
+ // <a id=".."> has to be unique inside the whole document, but
+ // we only write a fragment, so we can't ensure the ID is indeed
+ // unique. Just don't write anchors in the ReqIF case.
+ return;
+ }
+
+ OStringBuffer sOut("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor " ");
+ if (!mbXHTML)
+ {
+ sOut.append(OOO_STRING_SVTOOLS_HTML_O_name "=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), rName ).WriteOString( "\">" );
+ }
+ else
+ {
+ // XHTML wants 'id' instead of 'name', also the value can't contain
+ // spaces.
+ sOut.append(OOO_STRING_SVTOOLS_HTML_O_id "=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), rName.replace(' ', '_') ).WriteOString( "\">" );
+ }
+ HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
+}
+
+void SwHTMLWriter::OutBookmarks()
+{
+ // fetch current bookmark
+ const ::sw::mark::IMark* pBookmark = nullptr;
+ IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
+ if(m_nBkmkTabPos != -1)
+ pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos];
+ // Output all bookmarks in this paragraph. The content position
+ // for the moment isn't considered!
+ SwNodeOffset nNode = m_pCurrentPam->GetPoint()->GetNodeIndex();
+ while( m_nBkmkTabPos != -1
+ && pBookmark->GetMarkPos().GetNodeIndex() == nNode )
+ {
+ // The area of bookmarks is first ignored, because it's not read.
+
+ // first the SWG specific data:
+ if ( dynamic_cast< const ::sw::mark::IBookmark* >(pBookmark) && !pBookmark->GetName().isEmpty() )
+ {
+ OutAnchor( pBookmark->GetName() );
+ }
+
+ if( ++m_nBkmkTabPos >= pMarkAccess->getAllMarksCount() )
+ m_nBkmkTabPos = -1;
+ else
+ pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos];
+ }
+
+ decltype(m_aOutlineMarkPoss)::size_type nPos;
+ for( nPos = 0; nPos < m_aOutlineMarkPoss.size() &&
+ m_aOutlineMarkPoss[nPos] < nNode; nPos++ )
+ ;
+
+ while( nPos < m_aOutlineMarkPoss.size() && m_aOutlineMarkPoss[nPos] == nNode )
+ {
+ OUString sMark( m_aOutlineMarks[nPos] );
+ OutAnchor( sMark.replace('?', '_') ); // '?' causes problems in IE/Netscape 5
+ m_aOutlineMarkPoss.erase( m_aOutlineMarkPoss.begin()+nPos );
+ m_aOutlineMarks.erase( m_aOutlineMarks.begin() + nPos );
+ }
+}
+
+void SwHTMLWriter::OutPointFieldmarks( const SwPosition& rPos )
+{
+ // "point" fieldmarks that occupy single character space, as opposed to
+ // range fieldmarks that are associated with start and end points.
+
+ const IDocumentMarkAccess* pMarkAccess = m_pDoc->getIDocumentMarkAccess();
+ if (!pMarkAccess)
+ return;
+
+ const sw::mark::IFieldmark* pMark = pMarkAccess->getFieldmarkAt(rPos);
+ if (!pMark)
+ return;
+
+ if (pMark->GetFieldname() != ODF_FORMCHECKBOX)
+ return;
+
+ const sw::mark::ICheckboxFieldmark* pCheckBox =
+ dynamic_cast<const sw::mark::ICheckboxFieldmark*>(pMark);
+
+ if (!pCheckBox)
+ return;
+
+ OString aOut("<"
+ OOO_STRING_SVTOOLS_HTML_input
+ " "
+ OOO_STRING_SVTOOLS_HTML_O_type
+ "=\""
+ OOO_STRING_SVTOOLS_HTML_IT_checkbox
+ "\""_ostr);
+
+ if (pCheckBox->IsChecked())
+ {
+ aOut += " "
+ OOO_STRING_SVTOOLS_HTML_O_checked
+ "=\""
+ OOO_STRING_SVTOOLS_HTML_O_checked
+ "\"";
+ }
+
+ aOut += "/>";
+ Strm().WriteOString(aOut);
+
+ // TODO : Handle other single-point fieldmark types here (if any).
+}
+
+void SwHTMLWriter::OutImplicitMark( std::u16string_view rMark,
+ const char *pMarkType )
+{
+ if( !rMark.empty() && !m_aImplicitMarks.empty() )
+ {
+ OUString sMark(rMark + OUStringChar(cMarkSeparator) + OUString::createFromAscii(pMarkType));
+ if( 0 != m_aImplicitMarks.erase( sMark ) )
+ {
+ OutAnchor(sMark.replace('?', '_')); // '?' causes problems in IE/Netscape 5
+ }
+ }
+}
+
+OUString SwHTMLWriter::convertHyperlinkHRefValue(const OUString& rURL)
+{
+ OUString sURL(rURL);
+ sal_Int32 nPos = sURL.lastIndexOf(cMarkSeparator);
+ if (nPos != -1)
+ {
+ OUString sCompare = sURL.copy(nPos + 1).replaceAll(" ", "");
+ if (!sCompare.isEmpty())
+ {
+ sCompare = sCompare.toAsciiLowerCase();
+ if( sCompare == "region" || sCompare == "frame" ||
+ sCompare == "graphic" || sCompare == "ole" ||
+ sCompare == "table" || sCompare == "outline" ||
+ sCompare == "text" )
+ {
+ sURL = sURL.replace( '?', '_' ); // '?' causes problems in IE/Netscape 5
+ }
+ }
+ }
+ else if (!sURL.isEmpty() && sURL[0] != '#')
+ {
+ // Link is not started from "#", so looks like external link. Encode this URL.
+ INetURLObject aURL(sURL);
+ if (!aURL.HasError())
+ sURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+ return URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), sURL );
+}
+
+void SwHTMLWriter::OutHyperlinkHRefValue( const OUString& rURL )
+{
+ OUString sURL = convertHyperlinkHRefValue(rURL);
+ HTMLOutFuncs::Out_String( Strm(), sURL );
+}
+
+void SwHTMLWriter::OutBackground( const SvxBrushItem *pBrushItem, bool bGraphic )
+{
+ const Color &rBackColor = pBrushItem->GetColor();
+ /// check, if background color is not "no fill"/"auto fill", instead of
+ /// only checking, if transparency is not set.
+ if( rBackColor != COL_TRANSPARENT )
+ {
+ Strm().WriteOString( " " OOO_STRING_SVTOOLS_HTML_O_bgcolor "=" );
+ HTMLOutFuncs::Out_Color( Strm(), rBackColor);
+ }
+
+ if( !bGraphic )
+ return;
+
+ const Graphic* pGrf = pBrushItem->GetGraphic();
+ OUString GraphicURL = pBrushItem->GetGraphicLink();
+ if( mbEmbedImages || GraphicURL.isEmpty())
+ {
+ if( pGrf )
+ {
+ OUString aGraphicInBase64;
+ if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
+ {
+ m_nWarn = WARN_SWG_POOR_LOAD;
+ }
+ Strm().WriteOString( " " OOO_STRING_SVTOOLS_HTML_O_background "=\"" );
+ Strm().WriteOString( OOO_STRING_SVTOOLS_HTML_O_data ":" );
+ HTMLOutFuncs::Out_String( Strm(), aGraphicInBase64 ).WriteChar( '\"' );
+ }
+ }
+ else
+ {
+ if( m_bCfgCpyLinkedGrfs )
+ {
+ CopyLocalFileToINet( GraphicURL );
+ }
+ OUString s( URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), GraphicURL));
+ Strm().WriteOString(" " OOO_STRING_SVTOOLS_HTML_O_background "=\"" );
+ HTMLOutFuncs::Out_String( Strm(), s );
+ Strm().WriteOString("\"");
+
+ }
+}
+
+void SwHTMLWriter::OutBackground( const SfxItemSet& rItemSet, bool bGraphic )
+{
+ if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ OutBackground( pItem, bGraphic );
+ }
+}
+
+TypedWhichId<SvxLanguageItem> SwHTMLWriter::GetLangWhichIdFromScript( sal_uInt16 nScript )
+{
+ TypedWhichId<SvxLanguageItem> nWhichId(0);
+ switch( nScript )
+ {
+ case CSS1_OUTMODE_CJK:
+ nWhichId = RES_CHRATR_CJK_LANGUAGE;
+ break;
+ case CSS1_OUTMODE_CTL:
+ nWhichId = RES_CHRATR_CJK_LANGUAGE;
+ break;
+ default:
+ nWhichId = RES_CHRATR_LANGUAGE;
+ break;
+ }
+ return nWhichId;
+}
+
+void SwHTMLWriter::OutLanguage( LanguageType nLang )
+{
+ // ReqIF mode: consumers would ignore language anyway.
+ if (!(LANGUAGE_DONTKNOW != nLang && !mbReqIF))
+ return;
+
+ OStringBuffer sOut(" ");
+ if (mbXHTML)
+ sOut.append(OOO_STRING_SVTOOLS_XHTML_O_lang);
+ else
+ sOut.append(OOO_STRING_SVTOOLS_HTML_O_lang);
+ sOut.append("=\"");
+ Strm().WriteOString( sOut );
+ sOut.setLength(0);
+ HTMLOutFuncs::Out_String( Strm(), LanguageTag::convertToBcp47(nLang) ).WriteChar( '"' );
+}
+
+SvxFrameDirection SwHTMLWriter::GetHTMLDirection( const SfxItemSet& rItemSet ) const
+{
+ return GetHTMLDirection( rItemSet.Get( RES_FRAMEDIR ).GetValue() );
+}
+
+SvxFrameDirection SwHTMLWriter::GetHTMLDirection( SvxFrameDirection nDir ) const
+{
+ switch( nDir )
+ {
+ case SvxFrameDirection::Vertical_LR_TB:
+ nDir = SvxFrameDirection::Horizontal_LR_TB;
+ break;
+ case SvxFrameDirection::Vertical_RL_TB:
+ nDir = SvxFrameDirection::Horizontal_RL_TB;
+ break;
+ case SvxFrameDirection::Environment:
+ nDir = m_nDirection;
+ break;
+ default: break;
+ }
+
+ return nDir;
+}
+
+void SwHTMLWriter::OutDirection( SvxFrameDirection nDir )
+{
+ OString sConverted = convertDirection(nDir);
+ if (!sConverted.isEmpty())
+ {
+ OString sOut =
+ " " OOO_STRING_SVTOOLS_HTML_O_dir
+ "=\"" + sConverted + "\"";
+ Strm().WriteOString( sOut );
+ }
+}
+
+OString SwHTMLWriter::convertDirection(SvxFrameDirection nDir)
+{
+ OString sConverted;
+ switch (nDir)
+ {
+ case SvxFrameDirection::Horizontal_LR_TB:
+ case SvxFrameDirection::Vertical_LR_TB:
+ sConverted = "ltr"_ostr;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ case SvxFrameDirection::Vertical_RL_TB:
+ sConverted = "rtl"_ostr;
+ break;
+ default: break;
+ }
+ return sConverted;
+}
+
+OString SwHTMLWriter::GetIndentString(sal_uInt16 nIncLvl)
+{
+ OString sRet;
+
+ // somewhat cumbersome, but we have only one indent string!
+ sal_uInt16 nLevel = m_nIndentLvl + nIncLvl;
+
+ if( nLevel && nLevel <= MAX_INDENT_LEVEL)
+ {
+ sIndentTabs[nLevel] = 0;
+ sRet = sIndentTabs;
+ sIndentTabs[nLevel] = '\t';
+ }
+
+ return sRet;
+}
+
+void SwHTMLWriter::OutNewLine( bool bCheck )
+{
+ if( !bCheck || (Strm().Tell()-m_nLastLFPos) > m_nIndentLvl )
+ {
+ Strm().WriteOString( SAL_NEWLINE_STRING );
+ m_nLastLFPos = Strm().Tell();
+ }
+
+ if( m_nIndentLvl && m_nIndentLvl <= MAX_INDENT_LEVEL)
+ {
+ sIndentTabs[m_nIndentLvl] = 0;
+ Strm().WriteOString( sIndentTabs );
+ sIndentTabs[m_nIndentLvl] = '\t';
+ }
+}
+
+sal_uInt16 SwHTMLWriter::GetHTMLFontSize( sal_uInt32 nHeight ) const
+{
+ sal_uInt16 nSize = 1;
+ for( sal_uInt16 i=6; i>0; i-- )
+ {
+ if( nHeight > (m_aFontHeights[i] + m_aFontHeights[i-1])/2 )
+ {
+ nSize = i+1;
+ break;
+ }
+ }
+
+ return nSize;
+}
+
+// Paragraphs with Table of Contents and other index styles will be typeset with
+// dot leaders at the position of the last tabulator in PrintLayout (CSS2) mode
+sal_Int32 SwHTMLWriter::indexOfDotLeaders( sal_uInt16 nPoolId, std::u16string_view rStr )
+{
+ if (m_bCfgPrintLayout && ((nPoolId >= RES_POOLCOLL_TOX_CNTNT1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT5) ||
+ (nPoolId >= RES_POOLCOLL_TOX_IDX1 && nPoolId <= RES_POOLCOLL_TOX_IDX3) ||
+ (nPoolId >= RES_POOLCOLL_TOX_USER1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT10) ||
+ nPoolId == RES_POOLCOLL_TOX_ILLUS1 || nPoolId == RES_POOLCOLL_TOX_TABLES1 ||
+ nPoolId == RES_POOLCOLL_TOX_OBJECT1 ||
+ (nPoolId >= RES_POOLCOLL_TOX_AUTHORITIES1 && nPoolId <= RES_POOLCOLL_TOX_USER10))) {
+ size_t i = rStr.rfind('\t');
+ // there are only ASCII (Latin-1) characters after the tabulator
+ if (i != std::u16string_view::npos && OUStringToOString(rStr.substr(i + 1), RTL_TEXTENCODING_ASCII_US).indexOf('?') == -1)
+ return i;
+ }
+ return -1;
+}
+
+OString SwHTMLWriter::GetNamespace() const
+{
+ if (maNamespace.isEmpty())
+ return OString();
+
+ return maNamespace + ":";
+}
+
+// Structure caches the current data of the writer to output a
+// other part of the document, like e.g. header/footer
+HTMLSaveData::HTMLSaveData(SwHTMLWriter& rWriter, SwNodeOffset nStt,
+ SwNodeOffset nEnd, bool bSaveNum,
+ const SwFrameFormat *pFrameFormat)
+ : rWrt(rWriter)
+ , pOldPam(rWrt.m_pCurrentPam)
+ , pOldEnd(rWrt.GetEndPaM())
+ , nOldDefListLvl(rWrt.m_nDefListLvl)
+ , nOldDirection(rWrt.m_nDirection)
+ , bOldOutHeader(rWrt.m_bOutHeader)
+ , bOldOutFooter(rWrt.m_bOutFooter)
+ , bOldOutFlyFrame(rWrt.m_bOutFlyFrame)
+{
+ bOldWriteAll = rWrt.m_bWriteAll;
+
+ rWrt.m_pCurrentPam = Writer::NewUnoCursor(*rWrt.m_pDoc, nStt, nEnd);
+
+ // recognize table in special areas
+ if( nStt != rWrt.m_pCurrentPam->GetMark()->GetNodeIndex() )
+ {
+ const SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ];
+ if( pNd->IsTableNode() || pNd->IsSectionNode() )
+ rWrt.m_pCurrentPam->GetMark()->Assign(*pNd);
+ }
+
+ rWrt.SetEndPaM( rWrt.m_pCurrentPam.get() );
+ rWrt.m_pCurrentPam->Exchange( );
+ rWrt.m_bWriteAll = true;
+ rWrt.m_nDefListLvl = 0;
+ rWrt.m_bOutHeader = rWrt.m_bOutFooter = false;
+
+ // Maybe save the current numbering information, so that it can be started again.
+ // Only then also the numbering information of the next paragraph will be valid.
+ if( bSaveNum )
+ {
+ pOldNumRuleInfo.reset( new SwHTMLNumRuleInfo( rWrt.GetNumInfo() ) );
+ pOldNextNumRuleInfo = rWrt.ReleaseNextNumInfo();
+ }
+ else
+ {
+ rWrt.ClearNextNumInfo();
+ }
+
+ // The numbering will be in any case interrupted.
+ rWrt.GetNumInfo().Clear();
+
+ if( pFrameFormat )
+ rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
+}
+
+HTMLSaveData::~HTMLSaveData()
+{
+ rWrt.m_pCurrentPam.reset(); // delete PaM again
+
+ rWrt.m_pCurrentPam = pOldPam;
+ rWrt.SetEndPaM( pOldEnd );
+ rWrt.m_bWriteAll = bOldWriteAll;
+ rWrt.m_nBkmkTabPos = bOldWriteAll ? rWrt.FindPos_Bkmk( *pOldPam->GetPoint() ) : -1;
+ rWrt.m_nLastParaToken = HtmlTokenId::NONE;
+ rWrt.m_nDefListLvl = nOldDefListLvl;
+ rWrt.m_nDirection = nOldDirection;
+ rWrt.m_bOutHeader = bOldOutHeader;
+ rWrt.m_bOutFooter = bOldOutFooter;
+ rWrt.m_bOutFlyFrame = bOldOutFlyFrame;
+
+ // Maybe continue the numbering from before section. The numbering
+ // of the next paragraph will be invalid in any case.
+ if( pOldNumRuleInfo )
+ {
+ rWrt.GetNumInfo().Set( *pOldNumRuleInfo );
+ pOldNumRuleInfo.reset();
+ rWrt.SetNextNumInfo( std::move(pOldNextNumRuleInfo) );
+ }
+ else
+ {
+ rWrt.GetNumInfo().Clear();
+ rWrt.ClearNextNumInfo();
+ }
+}
+
+void GetHTMLWriter( std::u16string_view rFilterOptions, const OUString& rBaseURL, WriterRef& xRet )
+{
+ xRet = new SwHTMLWriter( rBaseURL, rFilterOptions );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx
new file mode 100644
index 0000000000..a62bff941a
--- /dev/null
+++ b/sw/source/filter/html/wrthtml.hxx
@@ -0,0 +1,748 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_WRTHTML_HXX
+#define INCLUDED_SW_SOURCE_FILTER_HTML_WRTHTML_HXX
+
+#include <memory>
+#include <vector>
+#include <set>
+#include <string_view>
+#include <map>
+#include <optional>
+
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <i18nlangtag/lang.h>
+#include <comphelper/stl_types.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <rtl/ref.hxx>
+#include <svtools/htmlout.hxx>
+#include <tools/fldunit.hxx>
+
+#include <shellio.hxx>
+#include <wrt_fn.hxx>
+#include "htmlfly.hxx"
+
+// some forward declarations
+class Color;
+class SwFrameFormat;
+class SwFlyFrameFormat;
+class SwDrawFrameFormat;
+class SwFormatINetFormat;
+class SwFormatVertOrient;
+class SwFormatFootnote;
+class SwStartNode;
+class SwTableNode;
+class SwPageDesc;
+class SwNodeIndex;
+class ImageMap;
+class SwNumRule;
+class SdrObject;
+class SdrUnoObj;
+class SvxBrushItem;
+class SvxFontItem;
+class SwHTMLNumRuleInfo;
+class SwHTMLPosFlyFrames;
+class SwTextFootnote;
+enum class HtmlPosition;
+enum class HtmlTokenId : sal_Int16;
+namespace utl { class TempFileNamed; }
+
+extern SwAttrFnTab aHTMLAttrFnTab;
+
+#define HTML_PARSPACE (o3tl::toTwips(5, o3tl::Length::mm))
+
+// flags for the output of any kind of frames
+// BORDER only possible if OutHTML_Image
+// ANYSIZE indicates, if also VAR_SIZE and MIN_SIZE values should be exported
+// ABSSIZE indicates, if spacing and framing should be ignored
+enum class HtmlFrmOpts {
+ NONE = 0,
+ Align = 1<<0,
+ SAlign = 1<<1,
+
+ Width = 1<<2,
+ Height = 1<<3,
+ Size = Width | Height,
+ SWidth = 1<<4,
+ SHeight = 1<<5,
+ SSize = SWidth | SHeight,
+ AnySize = 1<<6,
+ AbsSize = 1<<7,
+ MarginSize = 1<<8,
+
+ Space = 1<<9,
+ SSpace = 1<<10,
+
+ Border = 1<<11,
+ SBorder = 1<<12,
+ SNoBorder = 1<<13,
+
+ SBackground = 1<<14,
+
+ Name = 1<<15,
+ Alt = 1<<16,
+ BrClear = 1<<17,
+ SPixSize = 1<<18,
+ Id = 1<<19,
+ Dir = 1<<20,
+ /// The graphic frame is a replacement image of an OLE object.
+ Replacement = 1<<21,
+
+ GenImgAllMask = Alt | Size | AbsSize | Name,
+ GenImgMask = GenImgAllMask | Align | Space | BrClear
+};
+namespace o3tl {
+ template<> struct typed_flags<HtmlFrmOpts> : is_typed_flags<HtmlFrmOpts, ((1<<22)-1)> {};
+}
+
+#define HTMLMODE_BLOCK_SPACER 0x00010000
+#define HTMLMODE_FLOAT_FRAME 0x00020000
+#define HTMLMODE_VERT_SPACER 0x00040000
+#define HTMLMODE_NBSP_IN_TABLES 0x00080000
+#define HTMLMODE_LSPACE_IN_NUMBER_BULLET 0x00100000
+//was HTMLMODE_NO_BR_AT_PAREND 0x00200000
+#define HTMLMODE_PRINT_EXT 0x00400000
+#define HTMLMODE_ABS_POS_FLY 0x00800000
+#define HTMLMODE_ABS_POS_DRAW 0x01000000
+#define HTMLMODE_FLY_MARGINS 0x02000000
+#define HTMLMODE_BORDER_NONE 0x04000000
+#define HTMLMODE_FONT_GENERIC 0x08000000
+#define HTMLMODE_FRSTLINE_IN_NUMBER_BULLET 0x10000000
+#define HTMLMODE_NO_CONTROL_CENTERING 0x20000000
+
+#define HTML_DLCOLL_DD 0x4000
+#define HTML_DLCOLL_DT 0x8000
+
+#define CSS1_FMT_ISTAG (USHRT_MAX)
+#define CSS1_FMT_CMPREF (USHRT_MAX-1)
+#define CSS1_FMT_SPECIAL (USHRT_MAX-1)
+
+// the following flags only specify which descriptors, tags, options,
+// and so on should be outputted
+// bit 0,1,2
+#define CSS1_OUTMODE_SPAN_NO_ON 0x0000U
+#define CSS1_OUTMODE_SPAN_TAG_ON 0x0001U
+#define CSS1_OUTMODE_STYLE_OPT_ON 0x0002U
+#define CSS1_OUTMODE_RULE_ON 0x0003U
+#define CSS1_OUTMODE_SPAN_TAG1_ON 0x0004U
+#define CSS1_OUTMODE_ANY_ON 0x0007U
+
+// bit 3,4,5
+#define CSS1_OUTMODE_SPAN_NO_OFF 0x0000U
+#define CSS1_OUTMODE_SPAN_TAG_OFF (sal_uInt16(0x0001U << 3))
+#define CSS1_OUTMODE_STYLE_OPT_OFF (sal_uInt16(0x0002U << 3))
+#define CSS1_OUTMODE_RULE_OFF (sal_uInt16(0x0003U << 3))
+#define CSS1_OUTMODE_ANY_OFF (sal_uInt16(0x0007U << 3))
+
+#define CSS1_OUTMODE_ONOFF(a) (CSS1_OUTMODE_##a##_ON|CSS1_OUTMODE_##a##_OFF)
+#define CSS1_OUTMODE_SPAN_TAG CSS1_OUTMODE_ONOFF(SPAN_TAG)
+#define CSS1_OUTMODE_STYLE_OPT CSS1_OUTMODE_ONOFF(STYLE_OPT)
+#define CSS1_OUTMODE_RULE CSS1_OUTMODE_ONOFF(RULE)
+
+// the following flags specify what should be outputted
+// bit 6,7,8,9
+#define CSS1_OUTMODE_TEMPLATE 0x0000U
+#define CSS1_OUTMODE_BODY (sal_uInt16(0x0001U << 6))
+#define CSS1_OUTMODE_PARA (sal_uInt16(0x0002U << 6))
+#define CSS1_OUTMODE_HINT (sal_uInt16(0x0003U << 6))
+#define CSS1_OUTMODE_FRAME (sal_uInt16(0x0004U << 6))
+#define CSS1_OUTMODE_TABLE (sal_uInt16(0x0005U << 6))
+#define CSS1_OUTMODE_TABLEBOX (sal_uInt16(0x0006U << 6))
+#define CSS1_OUTMODE_DROPCAP (sal_uInt16(0x0007U << 6))
+#define CSS1_OUTMODE_SECTION (sal_uInt16(0x0008U << 6))
+#define CSS1_OUTMODE_SOURCE (sal_uInt16(0x000fU << 6))
+
+// bit 10
+#define CSS1_OUTMODE_ENCODE (sal_uInt16(0x0001U << 10))
+
+// bit 11,12,13
+// don't care about script
+#define CSS1_OUTMODE_ANY_SCRIPT 0x0000U
+// no cjk or ctl items
+#define CSS1_OUTMODE_WESTERN (sal_uInt16(0x0001U << 11))
+// no western or ctl items
+#define CSS1_OUTMODE_CJK (sal_uInt16(0x0002U << 11))
+// no western or cjk items
+#define CSS1_OUTMODE_CTL (sal_uInt16(0x0003U << 11))
+// no western, cjk or ctl items
+#define CSS1_OUTMODE_NO_SCRIPT (sal_uInt16(0x0004U << 11))
+#define CSS1_OUTMODE_SCRIPT (sal_uInt16(0x0007U << 11))
+
+// the HTML writer
+struct HTMLControl
+{
+ // the form to which the control belongs
+ css::uno::Reference<css::container::XIndexContainer> xFormComps;
+ SwNodeOffset nNdIdx; // the node in which it's anchored
+ sal_Int32 nCount; // how many controls are on the node
+
+ HTMLControl( css::uno::Reference<css::container::XIndexContainer> xForm, SwNodeOffset nIdx );
+ ~HTMLControl();
+
+ // operators for the sort array
+ bool operator<( const HTMLControl& rCtrl ) const
+ {
+ return nNdIdx < rCtrl.nNdIdx;
+ }
+};
+
+class HTMLControls : public o3tl::sorted_vector<std::unique_ptr<HTMLControl>, o3tl::less_uniqueptr_to<HTMLControl> > {
+};
+
+struct SwHTMLFormatInfo
+{
+ const SwFormat *pFormat; // the format itself
+
+ OString aToken; // the token to output
+ OUString aClass; // the class to output
+
+ std::optional<SfxItemSet> moItemSet; // the attribute set to output
+
+ sal_Int32 nLeftMargin; // some default values for
+ sal_Int32 nRightMargin; // paragraph styles
+ short nFirstLineIndent;
+
+ sal_uInt16 nTopMargin;
+ sal_uInt16 nBottomMargin;
+
+ bool bScriptDependent;
+
+ // ctor for a dummy to search
+ explicit SwHTMLFormatInfo( const SwFormat *pF ) :
+ pFormat( pF ),
+ nLeftMargin( 0 ),
+ nRightMargin( 0 ),
+ nFirstLineIndent(0),
+ nTopMargin( 0 ),
+ nBottomMargin( 0 ),
+ bScriptDependent(false)
+ {}
+
+ // ctor for creating of the format information
+ SwHTMLFormatInfo( const SwFormat *pFormat, SwDoc *pDoc, SwDoc *pTemplate,
+ bool bOutStyles, LanguageType eDfltLang=LANGUAGE_DONTKNOW,
+ sal_uInt16 nScript=CSS1_OUTMODE_ANY_SCRIPT );
+ ~SwHTMLFormatInfo();
+
+ friend bool operator<( const SwHTMLFormatInfo& rInfo1,
+ const SwHTMLFormatInfo& rInfo2 )
+ {
+ return reinterpret_cast<sal_IntPtr>(rInfo1.pFormat) < reinterpret_cast<sal_IntPtr>(rInfo2.pFormat);
+ }
+
+};
+
+typedef std::set<std::unique_ptr<SwHTMLFormatInfo>,
+ comphelper::UniquePtrValueLess<SwHTMLFormatInfo>> SwHTMLFormatInfos;
+
+class IDocumentStylePoolAccess;
+
+namespace sw
+{
+enum class Css1Background
+{
+ Attr = 1,
+ Page = 2,
+ Table = 3,
+ Fly = 4,
+ Section = 5,
+ TableRow = 6,
+ TableCell = 7,
+};
+}
+
+class SW_DLLPUBLIC SwHTMLWriter : public Writer
+{
+ SwHTMLPosFlyFrames m_aHTMLPosFlyFrames;
+ std::unique_ptr<SwHTMLNumRuleInfo> m_pNumRuleInfo;// current numbering
+ std::unique_ptr<SwHTMLNumRuleInfo> m_pNextNumRuleInfo;
+ sal_uInt32 m_nHTMLMode; // description of export configuration
+
+ FieldUnit m_eCSS1Unit;
+
+ bool m_bPrettyPrint = true; // Allows to add new lines to make it more readable
+ bool m_bLFPossible = false; // a line break can be inserted
+ bool m_bSpacePreserve = false; // Using xml::space="preserve", or "white-space: pre-wrap" style
+ bool m_bPreserveSpacesOnWrite = false; // If export should use m_bSpacePreserve
+
+ sal_uInt16 OutHeaderAttrs();
+ const SwPageDesc *MakeHeader( sal_uInt16& rHeaderAtrs );
+ void GetControls();
+
+ void AddLinkTarget( std::u16string_view aURL );
+ void CollectLinkTargets();
+
+ void SetupFilterOptions(std::u16string_view rFilterOptions);
+
+protected:
+ ErrCode WriteStream() override;
+ void SetupFilterOptions(SfxMedium& rMedium) override;
+ void SetupFilterFromPropertyValues(
+ const css::uno::Sequence<css::beans::PropertyValue>& rPropertyValues);
+
+public:
+ std::vector<OUString> m_aImgMapNames; // written image maps
+ std::set<OUString> m_aImplicitMarks; // implicit jump marks
+ std::set<OUString> m_aNumRuleNames; // names of exported num rules
+ std::set<OUString> m_aScriptParaStyles; // script dependent para styles
+ std::set<OUString> m_aScriptTextStyles; // script dependent text styles
+ std::vector<OUString> m_aOutlineMarks;
+ std::vector<SwNodeOffset> m_aOutlineMarkPoss;
+ HTMLControls m_aHTMLControls; // the forms to be written
+ SwHTMLFormatInfos m_CharFormatInfos;
+ SwHTMLFormatInfos m_TextCollInfos;
+ std::vector<SwFormatINetFormat*> m_aINetFormats; // the "open" INet attributes
+ std::optional<std::vector<SwTextFootnote*>> m_xFootEndNotes;
+
+ OUString m_aCSS1Selector; // style selector
+ OUString m_aBulletGrfs[MAXLEVEL]; // list graphics
+
+ css::uno::Reference<css::container::XIndexContainer> mxFormComps; // current form
+
+ rtl::Reference<SwDoc> m_xTemplate; // HTML template
+ std::optional<Color> m_xDfltColor; // default colour
+ SwNodeIndex *m_pStartNdIdx; // index of first paragraph
+ const SwPageDesc *m_pCurrPageDesc;// current page style
+ const SwFormatFootnote *m_pFormatFootnote;
+
+ sal_uInt32 m_aFontHeights[7]; // font heights 1-7
+
+ ErrCode m_nWarn; // warning code
+ sal_uInt64 m_nLastLFPos; // last position of LF
+
+ HtmlTokenId m_nLastParaToken; // to hold paragraphs together
+ sal_Int32 m_nBkmkTabPos; // current position in bookmark table
+ sal_uInt16 m_nImgMapCnt;
+ sal_uInt16 m_nFormCntrlCnt;
+ sal_uInt16 m_nEndNote;
+ sal_uInt16 m_nFootNote;
+ sal_Int32 m_nLeftMargin; // left indent (e.g. from lists)
+ sal_Int32 m_nDfltLeftMargin; // defaults which doesn't have to be
+ sal_Int32 m_nDfltRightMargin; // written (from template)
+ short m_nFirstLineIndent; // first line indent (from lists)
+ short m_nDfltFirstLineIndent; // not to write default
+ sal_uInt16 m_nDfltTopMargin; // defaults which doesn't have to be
+ sal_uInt16 m_nDfltBottomMargin; // written (from template)
+ sal_uInt16 m_nIndentLvl; // How far is it indented?
+ sal_Int32 m_nWishLineLen; // How long can a line be?
+ sal_uInt16 m_nDefListLvl; // which DL level exists now
+ sal_Int32 m_nDefListMargin; // How far is the indentation in DL
+ sal_uInt16 m_nHeaderFooterSpace;
+ sal_uInt16 m_nTextAttrsToIgnore;
+ sal_uInt16 m_nExportMode;
+ sal_uInt16 m_nCSS1OutMode;
+ sal_uInt16 m_nCSS1Script; // contains default script (that's the one
+ // that is not contained in class names)
+ SvxFrameDirection m_nDirection; // the current direction
+
+ LanguageType m_eLang;
+
+ // description of the export configuration
+ // 0
+ bool m_bCfgOutStyles : 1; // export styles
+ bool m_bCfgPreferStyles : 1; // prefer styles instead of usual tags
+ bool m_bCfgFormFeed : 1; // export form feeds
+ bool m_bCfgStarBasic : 1; // export StarBasic
+ bool m_bCfgCpyLinkedGrfs : 1;
+
+ // description of what will be exported
+
+ bool m_bFirstLine : 1; // is the first line outputted?
+ bool m_bTagOn : 1; // tag on or off i.e. Attr-Start or Attr-End
+
+ // The following two flags specify how attributes are exported:
+ // bTextAttr bOutOpts
+ // 0 0 style sheets
+ // 1 0 Hints: Every attribute will be written as its own tag
+ // and an end tag exists
+ // 0 1 (paragraph)attribute: The Attribute will be exported as option
+ // of an already written tag. There is no end tag.
+ bool m_bTextAttr : 1;
+ // 8
+ bool m_bOutOpts : 1;
+
+ bool m_bOutTable : 1; // Will the table content be written?
+ bool m_bOutHeader : 1;
+ bool m_bOutFooter : 1;
+ bool m_bOutFlyFrame : 1;
+
+ // flags for style export
+
+ bool m_bFirstCSS1Rule : 1; // was a property already written
+ bool m_bFirstCSS1Property : 1; // was a property already written
+
+ // 16
+ bool m_bCSS1IgnoreFirstPageDesc : 1;
+
+ // what must/can/may not be written?
+
+ bool m_bNoAlign : 1; // HTML tag doesn't allow ALIGN=...
+ bool m_bClearLeft : 1; // <BR CLEAR=LEFT> write at end of paragraph
+ bool m_bClearRight : 1; // <BR CLEAR=RIGHT> write at end of paragraph
+
+ // others
+
+ bool m_bPreserveForm : 1; // preserve the current form
+
+ bool m_bCfgNetscape4 : 1; // Netscape4 hacks
+
+ bool mbSkipImages : 1;
+ /// If HTML header and footer should be written as well, or just the content itself.
+ bool mbSkipHeaderFooter : 1;
+ bool mbEmbedImages : 1;
+ /// Temporary base URL for paste of images.
+ std::unique_ptr<utl::TempFileNamed> mpTempBaseURL;
+ /// If XHTML markup should be written instead of HTML.
+ bool mbXHTML = false;
+ /// XML namespace, in case of XHTML.
+ OString maNamespace;
+ /// If the ReqIF subset of XHTML should be written.
+ bool mbReqIF = false;
+
+#define sCSS2_P_CLASS_leaders "leaders"
+ bool m_bCfgPrintLayout : 1; // PrintLayout option for TOC dot leaders
+ bool m_bParaDotLeaders : 1; // for TOC dot leaders
+ // 26
+
+ /// Tracks which text portion attributes are currently open: a which id -> open count map.
+ std::map<sal_uInt16, int> maStartedAttributes;
+
+ OUString m_aRTFOLEMimeType;
+
+ /// ReqIF mode: export images as OLE objects.
+ bool m_bExportImagesAsOLE = false;
+
+ /// ReqIF mode: export formulas as PDFs.
+ bool m_bExportFormulasAsPDF = false;
+
+ /// DPI used when exporting a vector shape as a bitmap.
+ std::optional<sal_Int32> m_nShapeDPI;
+
+ /// If set, replace leading tabs with this many non-breaking spaces.
+ std::optional<sal_Int32> m_nLeadingTabWidth;
+
+ /// Construct an instance of SwHTMLWriter and optionally give it
+ /// the filter options directly, which can also be set via SetupFilterOptions().
+ explicit SwHTMLWriter( const OUString& rBaseURL, std::u16string_view rFilterOptions = std::u16string_view() );
+ virtual ~SwHTMLWriter() override;
+
+ void Out_SwDoc( SwPaM* ); // write the marked range
+
+ // output all bookmarks of current paragraph
+ void OutAnchor( const OUString& rName );
+ void OutBookmarks();
+ void OutPointFieldmarks( const SwPosition& rPos );
+ void OutImplicitMark( std::u16string_view rMark, const char *pMarkType );
+
+ OUString convertHyperlinkHRefValue(const OUString& rURL);
+
+ void OutHyperlinkHRefValue( const OUString& rURL );
+
+ // output the FlyFrame anchored at current position
+ bool OutFlyFrame( SwNodeOffset nNdIdx, sal_Int32 nContentIdx,
+ HtmlPosition nPos );
+ void OutFrameFormat( AllHtmlFlags nType, const SwFrameFormat& rFormat,
+ const SdrObject *pSdrObj );
+
+ void OutForm( bool bTagOn=true, const SwStartNode *pStNd=nullptr );
+ void OutHiddenForms();
+ void OutHiddenForm( const css::uno::Reference<css::form::XForm>& rForm );
+
+ void OutForm( bool bOn, const css::uno::Reference<css::container::XIndexContainer>& rFormComps );
+ void OutHiddenControls( const css::uno::Reference<css::container::XIndexContainer>& rFormComps,
+ const css::uno::Reference<css::beans::XPropertySet>& rPropSet );
+ bool HasControls() const;
+
+ void OutFootEndNoteInfo();
+ void OutFootEndNotes();
+ OUString GetFootEndNoteSym( const SwFormatFootnote& rFormatFootnote );
+ void OutFootEndNoteSym( const SwFormatFootnote& rFormatFootnote, const OUString& rNum,
+ sal_uInt16 nScript );
+
+ void OutBasic(const SwHTMLWriter& rHTMLWrt);
+
+ // Used to indent inner blocks using dl/dd tags
+ void OutAndSetDefList( sal_uInt16 nNewLvl );
+
+ void OutStyleSheet( const SwPageDesc& rPageDesc );
+
+ inline void OutCSS1_PropertyAscii( std::string_view pProp,
+ std::string_view rVal );
+ inline void OutCSS1_Property( std::string_view pProp, const OUString& rVal );
+ void OutCSS1_Property( std::string_view pProp, std::string_view pVal,
+ const OUString *pSVal, std::optional<sw::Css1Background> oBackground = std::nullopt );
+ void OutCSS1_UnitProperty( std::string_view pProp, tools::Long nVal );
+ void OutCSS1_PixelProperty( std::string_view pProp, tools::Long nTwips );
+ void OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, bool bDeep=true, std::string_view rAdd = {} );
+
+ // events of BODY tag from SFX configuration
+ void OutBasicBodyEvents();
+
+ // BACKGROUND/BGCOLOR option
+ void OutBackground( const SvxBrushItem *pBrushItem, bool bGraphic );
+ void OutBackground( const SfxItemSet& rItemSet, bool bGraphic );
+
+ void OutLanguage( LanguageType eLang );
+ SvxFrameDirection GetHTMLDirection( SvxFrameDirection nDir ) const;
+ SvxFrameDirection GetHTMLDirection( const SfxItemSet& rItemSet ) const;
+ void OutDirection( SvxFrameDirection nDir );
+ static OString convertDirection(SvxFrameDirection nDirection);
+
+ // ALT/ALIGN/WIDTH/HEIGHT/HSPACE/VSPACE option of current
+ // frame format output and maybe add a <BR CLEAR=...> at the
+ // beginning of rEndTags
+ OString OutFrameFormatOptions( const SwFrameFormat& rFrameFormat, const OUString& rAltText,
+ HtmlFrmOpts nFrameOpts );
+
+ void writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, const OUString& rAltText, HtmlFrmOpts nFrameOpts);
+
+ /// Writes the formatting for tables.
+ void OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameFormat );
+
+ /// Writes the borders and background for table cells.
+ void OutCSS1_TableCellBordersAndBG(const SwFrameFormat& rFrameFormat, const SvxBrushItem *pBrushItem);
+
+ void OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameFormat, const SwFormatCol *pCol );
+ void OutCSS1_FrameFormatOptions( const SwFrameFormat& rFrameFormat, HtmlFrmOpts nFrameOpts,
+ const SdrObject *pSdrObj=nullptr,
+ const SfxItemSet *pItemSet=nullptr );
+ void OutCSS1_FrameFormatBackground( const SwFrameFormat& rFrameFormat );
+
+ void ChangeParaToken( HtmlTokenId nNew );
+
+ void IncIndentLevel()
+ {
+ m_nIndentLvl++;
+ }
+ void DecIndentLevel()
+ {
+ if ( m_nIndentLvl ) m_nIndentLvl--;
+ }
+ OString GetIndentString(sal_uInt16 nIncLvl = 0);
+
+ sal_Int32 GetLineLen()
+ {
+ return static_cast<sal_Int32>(Strm().Tell()-m_nLastLFPos);
+ }
+ void OutNewLine( bool bCheck=false );
+
+ // for HTMLSaveData
+ SwPaM* GetEndPaM() { return m_pOrigPam; }
+ void SetEndPaM( SwPaM* pPam ) { m_pOrigPam = pPam; }
+
+ static sal_uInt32 ToPixel(sal_uInt32 nTwips);
+ static Size ToPixel(Size aTwips);
+
+ SwHTMLFrameType GuessFrameType( const SwFrameFormat& rFrameFormat,
+ const SdrObject*& rpStrObj );
+ static SwHTMLFrameType GuessOLENodeFrameType( const SwNode& rNd );
+
+ void CollectFlyFrames();
+
+ sal_uInt16 GetHTMLFontSize( sal_uInt32 nFontHeight ) const;
+
+ // Fetch current numbering information.
+ SwHTMLNumRuleInfo& GetNumInfo() { return *m_pNumRuleInfo; }
+
+ // Fetch current numbering information of next paragraph. They
+ // don't have to exist yet!
+ SwHTMLNumRuleInfo *GetNextNumInfo() { return m_pNextNumRuleInfo.get(); }
+ std::unique_ptr<SwHTMLNumRuleInfo> ReleaseNextNumInfo();
+
+ // Set the numbering information of next paragraph.
+ void SetNextNumInfo( std::unique_ptr<SwHTMLNumRuleInfo> pNxt );
+
+ // Fill the numbering information of next paragraph.
+ void FillNextNumInfo();
+
+ // Clear numbering information of next paragraph.
+ void ClearNextNumInfo();
+
+ static const SdrObject* GetHTMLControl( const SwDrawFrameFormat& rFormat );
+ static const SdrObject* GetMarqueeTextObj( const SwDrawFrameFormat& rFormat );
+ static sal_uInt16 GetCSS1Selector( const SwFormat *pFormat, OString& rToken,
+ OUString& rClass, sal_uInt16& rRefPoolId,
+ OUString *pPseudo=nullptr );
+
+ static const SwFormat *GetTemplateFormat( sal_uInt16 nPoolId, IDocumentStylePoolAccess* /*SwDoc*/ pTemplate );
+ static const SwFormat *GetParentFormat( const SwFormat& rFormat, sal_uInt16 nDeep );
+
+ static void SubtractItemSet( SfxItemSet& rItemSet,
+ const SfxItemSet& rRefItemSet,
+ bool bSetDefaults,
+ bool bClearSame = true,
+ const SfxItemSet *pRefScriptItemSet=nullptr );
+ static bool HasScriptDependentItems( const SfxItemSet& rItemSet,
+ bool bCheckDropCap );
+
+ static void GetEEAttrsFromDrwObj( SfxItemSet& rItemSet,
+ const SdrObject *pObj );
+
+ static sal_uInt16 GetDefListLvl( std::u16string_view rNm, sal_uInt16 nPoolId );
+
+ sal_uInt32 GetHTMLMode() const
+ {
+ return m_nHTMLMode;
+ }
+ bool IsHTMLMode( sal_uInt32 nMode ) const
+ {
+ return (m_nHTMLMode & nMode) != 0;
+ }
+
+ inline bool IsCSS1Source( sal_uInt16 n ) const;
+ inline bool IsCSS1Script( sal_uInt16 n ) const;
+
+ static const char *GetNumFormat( sal_uInt16 nFormat );
+ static void PrepareFontList( const SvxFontItem& rFontItem, OUString& rNames,
+ sal_Unicode cQuote, bool bGeneric );
+ static sal_uInt16 GetCSS1ScriptForScriptType( sal_uInt16 nScriptType );
+ static TypedWhichId<SvxLanguageItem> GetLangWhichIdFromScript( sal_uInt16 nScript );
+
+ FieldUnit GetCSS1Unit() const { return m_eCSS1Unit; }
+
+ sal_Int32 indexOfDotLeaders( sal_uInt16 nPoolId, std::u16string_view rText );
+
+ /// Determines the prefix string needed to respect the requested namespace alias.
+ OString GetNamespace() const;
+
+ bool IsPrettyPrint() const { return !m_bSpacePreserve && m_bPrettyPrint; }
+ bool IsLFPossible() const { return !m_bSpacePreserve && m_bLFPossible; }
+ void SetLFPossible(bool val) { m_bLFPossible = val; }
+ bool IsSpacePreserve() const { return m_bSpacePreserve; }
+ void SetSpacePreserve(bool val) { m_bSpacePreserve = val; }
+ bool IsPreserveSpacesOnWritePrefSet() const { return m_bPreserveSpacesOnWrite; }
+};
+
+inline bool SwHTMLWriter::IsCSS1Source( sal_uInt16 n ) const
+{
+ return n == (m_nCSS1OutMode & CSS1_OUTMODE_SOURCE);
+}
+
+inline bool SwHTMLWriter::IsCSS1Script( sal_uInt16 n ) const
+{
+ sal_uInt16 nScript = (m_nCSS1OutMode & CSS1_OUTMODE_SCRIPT);
+ return CSS1_OUTMODE_ANY_SCRIPT == nScript || n == nScript;
+}
+
+inline void SwHTMLWriter::OutCSS1_PropertyAscii( std::string_view pProp,
+ std::string_view rVal )
+{
+ OutCSS1_Property( pProp, rVal, nullptr );
+}
+
+inline void SwHTMLWriter::OutCSS1_Property( std::string_view pProp,
+ const OUString& rVal )
+{
+ OutCSS1_Property( pProp, std::string_view(), &rVal );
+}
+
+
+// Structure caches the current data of the writer to output
+// another part of the document, like e.g. header/footer
+// With the two USHORTs in the ctor a new PaM is created and sets the
+// positions in the document.
+// In dtor all data is restored and the created PaM is deleted again.
+
+class HTMLSaveData
+{
+ SwHTMLWriter& rWrt;
+ std::shared_ptr<SwUnoCursor> pOldPam;
+ SwPaM *pOldEnd;
+ std::unique_ptr<SwHTMLNumRuleInfo> pOldNumRuleInfo; // Owner = this
+ std::unique_ptr<SwHTMLNumRuleInfo> pOldNextNumRuleInfo;
+ sal_uInt16 nOldDefListLvl;
+ SvxFrameDirection nOldDirection;
+ bool bOldWriteAll : 1;
+ bool bOldOutHeader : 1;
+ bool bOldOutFooter : 1;
+ bool bOldOutFlyFrame : 1;
+
+public:
+ HTMLSaveData( SwHTMLWriter&, SwNodeOffset nStt, SwNodeOffset nEnd,
+ bool bSaveNum=true,
+ const SwFrameFormat *pFrameFormat=nullptr );
+ ~HTMLSaveData();
+};
+
+// some function prototypes
+SwHTMLWriter& OutHTML_FrameFormatOLENode( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
+ bool bInCntnr );
+SwHTMLWriter& OutHTML_FrameFormatOLENodeGrf( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
+ bool bInCntnr, bool bWriteReplacementGraphic = true );
+
+SwHTMLWriter& OutHTML_SwTextNode( SwHTMLWriter&, const SwContentNode& );
+SwHTMLWriter& OutHTML_SwTableNode( SwHTMLWriter& , SwTableNode &, const SwFrameFormat *,
+ const OUString* pCaption=nullptr, bool bTopCaption=false );
+
+SwHTMLWriter& OutHTML_DrawFrameFormatAsControl( SwHTMLWriter& rWrt, const SwDrawFrameFormat& rFormat,
+ const SdrUnoObj& rSdrObj, bool bInCntnr );
+SwHTMLWriter& OutHTML_DrawFrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwDrawFrameFormat& rFormat,
+ const SdrObject& rSdrObj );
+
+SwHTMLWriter& OutHTML_HeaderFooter( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
+ bool bHeader );
+
+SwHTMLWriter& OutHTML_ImageStart( HtmlWriter& rHtml, SwHTMLWriter&, const SwFrameFormat& rFormat,
+ const OUString& rGraphicURL,
+ Graphic const & rGraphic, const OUString& rAlternateText,
+ const Size& rRealSize, HtmlFrmOpts nFrameOpts,
+ const char *pMarkType,
+ const ImageMap *pGenImgMap,
+ const OUString& rMimeType = {} );
+SwHTMLWriter& OutHTML_ImageEnd( HtmlWriter& rHtml, SwHTMLWriter& );
+
+SwHTMLWriter& OutHTML_BulletImage( SwHTMLWriter& rWrt, const char *pTag,
+ const SvxBrushItem* pBrush,
+ const OUString& rGraphicURL);
+
+SwHTMLWriter& OutHTML_SwFormatField( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+SwHTMLWriter& OutHTML_SwFormatFootnote( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+SwHTMLWriter& OutHTML_SwFormatLineBreak(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
+SwHTMLWriter& OutHTML_INetFormat( SwHTMLWriter&, const SwFormatINetFormat& rINetFormat, bool bOn );
+
+SwHTMLWriter& OutCSS1_BodyTagStyleOpt( SwHTMLWriter& rWrt, const SfxItemSet& rItemSet );
+SwHTMLWriter& OutCSS1_ParaTagStyleOpt( SwHTMLWriter& rWrt, const SfxItemSet& rItemSet, std::string_view rAdd = {} );
+
+SwHTMLWriter& OutCSS1_HintSpanTag( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+SwHTMLWriter& OutCSS1_HintStyleOpt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+
+/// Writes the background of table rows.
+SwHTMLWriter& OutCSS1_TableBGStyleOpt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+
+SwHTMLWriter& OutCSS1_NumberBulletListStyleOpt( SwHTMLWriter& rWrt, const SwNumRule& rNumRule,
+ sal_uInt8 nLevel );
+
+SwHTMLWriter& OutHTML_NumberBulletListStart( SwHTMLWriter& rWrt,
+ const SwHTMLNumRuleInfo& rInfo );
+SwHTMLWriter& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt,
+ const SwHTMLNumRuleInfo& rNextInfo );
+
+SwHTMLWriter& OutCSS1_SvxBox( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
+
+OString GetCSS1_Color(const Color& rColor);
+
+/// Determines if rProperty with a given rValue has to be suppressed due to ReqIF mode.
+bool IgnorePropertyForReqIF(bool bReqIF, std::string_view rProperty, std::string_view rValue,
+ std::optional<sw::Css1Background> oBackground = std::nullopt);
+
+#endif // INCLUDED_SW_SOURCE_FILTER_HTML_WRTHTML_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/IndexingExport.hxx b/sw/source/filter/inc/IndexingExport.hxx
new file mode 100644
index 0000000000..782ddf7f5f
--- /dev/null
+++ b/sw/source/filter/inc/IndexingExport.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <ModelTraverser.hxx>
+#include <tools/XmlWriter.hxx>
+
+namespace sw
+{
+class SW_DLLPUBLIC IndexingExport
+{
+private:
+ ModelTraverser m_aModelTraverser;
+ tools::XmlWriter m_aXmlWriter;
+
+public:
+ IndexingExport(SvStream& rStream, SwDoc* pDoc);
+
+ bool runExport();
+};
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/IndexingExportFilter.hxx b/sw/source/filter/inc/IndexingExportFilter.hxx
new file mode 100644
index 0000000000..82c56dfa9a
--- /dev/null
+++ b/sw/source/filter/inc/IndexingExportFilter.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace sw
+{
+class IndexingExportFilter final
+ : public cppu::WeakImplHelper<css::document::XFilter, css::document::XExporter,
+ css::lang::XInitialization, css::lang::XServiceInfo>
+{
+private:
+ css::uno::Reference<css::lang::XComponent> m_xSourceDocument;
+
+public:
+ IndexingExportFilter() {}
+
+ // XFilter
+ virtual sal_Bool SAL_CALL
+ filter(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor) override;
+
+ virtual void SAL_CALL cancel() override {}
+
+ // XExporter
+ virtual void SAL_CALL
+ setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDocument) override
+ {
+ m_xSourceDocument = xDocument;
+ }
+
+ // XInitialization
+ virtual void SAL_CALL
+ initialize(const css::uno::Sequence<css::uno::Any>& /*aArguments*/) override
+ {
+ }
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ {
+ return "com.sun.star.comp.Writer.IndexingExportFilter";
+ }
+
+ virtual sal_Bool SAL_CALL supportsService(OUString const& rServiceName) override
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return { "com.sun.star.document.ExportFilter" };
+ }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/fltini.hxx b/sw/source/filter/inc/fltini.hxx
new file mode 100644
index 0000000000..3ee0b3b539
--- /dev/null
+++ b/sw/source/filter/inc/fltini.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_FLTINI_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_FLTINI_HXX
+
+#include <shellio.hxx>
+
+class SwNumRuleTable;
+class SwDoc;
+class SwTextNode;
+class SwNumRule;
+class SwNodeIndex;
+
+// the special readers
+
+class HTMLReader final : public Reader
+{
+ // we don't want to have the streams/storages open
+ virtual bool SetStrmStgPtr() override;
+ virtual OUString GetTemplateName(SwDoc& rDoc) const override;
+
+ /// Parse FilterOptions passed to the importer.
+ void SetupFilterOptions();
+
+ OUString m_aNamespace;
+
+public:
+ HTMLReader();
+ virtual ErrCodeMsg Read(SwDoc&, const OUString& rBaseURL, SwPaM&, const OUString&) override;
+};
+
+class XMLReader final : public Reader
+{
+ virtual ErrCodeMsg Read(SwDoc&, const OUString& rBaseURL, SwPaM&, const OUString&) override;
+
+public:
+ virtual SwReaderType GetReaderType() override;
+
+ XMLReader();
+
+ // read the sections of the document, which is equal to the medium.
+ // returns the count of it
+ virtual size_t GetSectionList(SfxMedium& rMedium,
+ std::vector<OUString>& rStrings) const override;
+};
+
+// the special writers
+
+void GetWW8Writer(std::u16string_view, const OUString&, WriterRef&);
+
+// Get size of fly (if 'automatic' in WW) and check if not too small
+SW_DLLPUBLIC void CalculateFlySize(SfxItemSet& rFlySet, const SwNode& rAnchor, SwTwips nPageWidth);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/fltshell.hxx b/sw/source/filter/inc/fltshell.hxx
new file mode 100644
index 0000000000..12ec446bf0
--- /dev/null
+++ b/sw/source/filter/inc/fltshell.hxx
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_FLTSHELL_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_FLTSHELL_HXX
+
+#include <hintids.hxx>
+#include <svl/listener.hxx>
+#include <svx/ctredlin.hxx>
+#include <tools/datetime.hxx>
+#include <mdiexp.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <strings.hrc>
+
+#include <cstddef>
+#include <memory>
+
+class SwTOXBase;
+class SwField;
+class SwFieldType;
+class Graphic;
+class SwTableBox;
+class SwDoc;
+class SwPaM;
+
+inline bool SwFltGetFlag(sal_uLong nFieldFlags, int no)
+ { return (nFieldFlags & (sal_uLong(1) << no)) != 0; }
+
+//Subvert the Node/Content system to get positions which don't update as
+//content is appended to them
+struct SW_DLLPUBLIC SwFltPosition
+{
+public:
+ SwNodeIndex m_nNode;
+ sal_Int32 m_nContent;
+public:
+ bool operator==(const SwFltPosition &rOther) const
+ {
+ return (m_nContent == rOther.m_nContent &&
+ m_nNode == rOther.m_nNode);
+ }
+ void SetPos(SwNodeIndex const &rNode, sal_uInt16 nIdx)
+ {
+ m_nNode = rNode;
+ m_nContent = nIdx;
+ }
+
+ //operators with SwPosition, where the node is hacked to the previous one,
+ //and the offset to content is de-dynamic-ified
+ SwFltPosition(const SwPosition &rPos)
+ : m_nNode(rPos.GetNode(), -1)
+ , m_nContent(rPos.GetContentIndex())
+ {
+ }
+
+ void FromSwPosition(const SwPosition &rPos)
+ {
+ m_nNode = rPos.GetNodeIndex()-1;
+ m_nContent = rPos.GetContentIndex();
+ }
+};
+
+// Stack entry for the attributes. It is always pointers to new attributes that are passed.
+class SwFltStackEntry
+{
+private:
+ SwFltStackEntry(SwFltStackEntry const&) = delete;
+ SwFltStackEntry& operator=(SwFltStackEntry const&) = delete;
+
+public:
+ SwFltPosition m_aMkPos;
+ SwFltPosition m_aPtPos;
+
+ std::unique_ptr<SfxPoolItem> m_pAttr;// Format Attribute
+
+ bool m_bOld; // to mark Attributes *before* skipping field results
+ bool m_bOpen; //Entry open, awaiting being closed
+ bool m_bConsumedByField;
+ bool m_isAnnotationOnEnd; ///< annotation already moved onto its end pos.
+
+ SW_DLLPUBLIC SwFltStackEntry(const SwPosition & rStartPos, std::unique_ptr<SfxPoolItem> pHt );
+ SW_DLLPUBLIC ~SwFltStackEntry();
+
+ enum class RegionMode { NoCheck = 0, CheckNodes = 1<<0, CheckFieldmark = 1<<1 };
+ SW_DLLPUBLIC void SetEndPos( const SwPosition & rEndPos);
+ SW_DLLPUBLIC bool MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode eCheck) const;
+ SW_DLLPUBLIC static bool MakeRegion(SwDoc& rDoc, SwPaM& rRegion,
+ RegionMode eCheck, const SwFltPosition &rMkPos, const SwFltPosition &rPtPos,
+ sal_uInt16 nWhich=0);
+};
+
+template<> struct o3tl::typed_flags<SwFltStackEntry::RegionMode>: o3tl::is_typed_flags<SwFltStackEntry::RegionMode, 0x03> {};
+
+class SW_DLLPUBLIC SwFltControlStack
+{
+private:
+ SwFltControlStack(SwFltControlStack const&) = delete;
+ SwFltControlStack& operator=(SwFltControlStack const&) = delete;
+
+ typedef std::vector<std::unique_ptr<SwFltStackEntry>> Entries;
+ Entries m_Entries;
+
+ sal_uLong m_nFieldFlags;
+
+protected:
+ SwDoc& m_rDoc;
+ bool m_bIsEndStack;
+
+ virtual void SetAttrInDoc(const SwPosition& rTmpPos, SwFltStackEntry& rEntry);
+
+public:
+ enum class MoveAttrsMode { DEFAULT, POSTIT_INSERTED };
+ void MoveAttrs(const SwPosition& rPos, MoveAttrsMode = MoveAttrsMode::DEFAULT);
+ enum Flags
+ {
+ HYPO,
+ TAGS_DO_ID,
+ TAGS_VISIBLE,
+ BOOK_TO_VAR_REF,
+ BOOK_AND_REF,
+ TAGS_IN_TEXT,
+ ALLOW_FLD_CR
+ };
+
+ SwFltControlStack(SwDoc& rDo, sal_uLong nFieldFl);
+ virtual ~SwFltControlStack();
+
+ bool IsFlagSet(Flags no) const { return ::SwFltGetFlag(m_nFieldFlags, no);}
+
+ void NewAttr(const SwPosition& rPos, const SfxPoolItem & rAttr );
+
+ virtual SwFltStackEntry* SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, bool bTstEnd=true, tools::Long nHand = LONG_MAX, bool consumedByField=false);
+
+ void StealAttr(const SwNode& rNode);
+ void MarkAllAttrsOld();
+ void KillUnlockedAttrs(const SwPosition& pPos);
+ SfxPoolItem* GetFormatStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos);
+ const SfxPoolItem* GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich);
+ void Delete(const SwPaM &rPam);
+
+ bool empty() const { return m_Entries.empty(); }
+ Entries::size_type size() const { return m_Entries.size(); }
+ SwFltStackEntry& operator[](Entries::size_type nIndex)
+ { return *m_Entries[nIndex]; }
+ void DeleteAndDestroy(Entries::size_type nCnt);
+};
+
+class SwFltAnchorListener;
+
+class SW_DLLPUBLIC SwFltAnchor final : public SfxPoolItem
+{
+ SwFrameFormat* m_pFrameFormat;
+ std::unique_ptr<SwFltAnchorListener> m_pListener;
+
+public:
+ SwFltAnchor(SwFrameFormat* pFlyFormat);
+ SwFltAnchor(const SwFltAnchor&);
+ virtual ~SwFltAnchor() override;
+
+ // "purely virtual methods" of SfxPoolItem
+ virtual bool operator==(const SfxPoolItem&) const override;
+ virtual SwFltAnchor* Clone(SfxItemPool* = nullptr) const override;
+ void SetFrameFormat(SwFrameFormat* _pFrameFormat);
+ const SwFrameFormat* GetFrameFormat() const { return m_pFrameFormat; }
+ SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
+};
+
+/// Used by SwFltAnchor, to listen to an SwFrameFormat (to be aware when it is replaced or deleted).
+class SwFltAnchorListener final : public SvtListener
+{
+ SwFltAnchor* m_pFltAnchor;
+ public:
+ SwFltAnchorListener(SwFltAnchor* pFltAnchor);
+ virtual void Notify(const SfxHint&) override;
+};
+
+class SW_DLLPUBLIC SwFltRedline final : public SfxPoolItem
+{
+public:
+ DateTime m_aStamp;
+ RedlineType m_eType;
+ std::size_t m_nAutorNo;
+
+ SwFltRedline(RedlineType eType_,
+ std::size_t nAutorNo_,
+ const DateTime& rStamp_)
+ : SfxPoolItem(RES_FLTR_REDLINE), m_aStamp(rStamp_),
+ m_eType(eType_),
+ m_nAutorNo(nAutorNo_)
+ {
+ }
+
+ // "purely virtual methods" of SfxPoolItem
+ virtual bool operator==(const SfxPoolItem& rItem) const override;
+ virtual SwFltRedline* Clone(SfxItemPool* = nullptr) const override;
+};
+
+class SW_DLLPUBLIC SwFltBookmark final : public SfxPoolItem
+{
+private:
+
+ tools::Long mnHandle;
+ OUString maName;
+ OUString maVal;
+ bool mbIsTOCBookmark;
+
+public:
+ SwFltBookmark( const OUString& rNa,
+ OUString aVa,
+ tools::Long nHand,
+ const bool bIsTOCBookmark = false );
+
+ // "purely virtual methods" of SfxPoolItem
+ virtual bool operator==(const SfxPoolItem&) const override;
+ virtual SwFltBookmark* Clone(SfxItemPool* = nullptr) const override;
+
+ tools::Long GetHandle() const { return mnHandle; }
+ const OUString& GetName() const { return maName; }
+ const OUString& GetValSys() const { return maVal; }
+ bool IsTOCBookmark() const
+ {
+ return mbIsTOCBookmark;
+ }
+};
+
+/// Stores RDF statements on a paragraph (key-value pairs where the subject is the paragraph).
+class SW_DLLPUBLIC SwFltRDFMark final : public SfxPoolItem
+{
+ tools::Long m_nHandle;
+ std::vector< std::pair<OUString, OUString> > m_aAttributes;
+
+public:
+ SwFltRDFMark();
+
+ virtual bool operator==(const SfxPoolItem&) const override;
+ virtual SwFltRDFMark* Clone(SfxItemPool* = nullptr) const override;
+
+ void SetHandle(tools::Long nHandle);
+ tools::Long GetHandle() const;
+ void SetAttributes(std::vector< std::pair<OUString, OUString> >&& rAttributes);
+ const std::vector< std::pair<OUString, OUString> >& GetAttributes() const;
+};
+
+class SW_DLLPUBLIC SwFltTOX final : public SfxPoolItem
+{
+ std::shared_ptr<SwTOXBase> m_xTOXBase;
+ bool m_bHadBreakItem; // there was a break item BEFORE insertion of the TOX
+ bool m_bHadPageDescItem;
+public:
+ SwFltTOX(std::shared_ptr<SwTOXBase> xBase);
+ // "purely virtual methods" of SfxPoolItem
+ virtual bool operator==(const SfxPoolItem&) const override;
+ virtual SwFltTOX* Clone(SfxItemPool* = nullptr) const override;
+ const SwTOXBase& GetBase() const { return *m_xTOXBase; }
+ void SetHadBreakItem( bool bVal ) { m_bHadBreakItem = bVal; }
+ void SetHadPageDescItem( bool bVal ) { m_bHadPageDescItem = bVal; }
+ bool HadBreakItem() const { return m_bHadBreakItem; }
+ bool HadPageDescItem() const { return m_bHadPageDescItem; }
+};
+
+// The WWEndStack behaves like the WWControlStack, except that the attributes
+// on it are hoarded to the end of the document if they need to be accessed
+// (e.g., book/RefMarks, index, etc.).
+class SwFltEndStack : public SwFltControlStack
+{
+public:
+ SwFltEndStack(SwDoc& rDo, sal_uLong nFieldFl)
+ :SwFltControlStack(rDo, nFieldFl)
+ {
+ m_bIsEndStack = true;
+ }
+};
+
+SW_DLLPUBLIC void UpdatePageDescs(SwDoc &rDoc, size_t nInPageDescOffset);
+
+class ImportProgress
+{
+private:
+ SwDocShell *m_pDocShell;
+public:
+ ImportProgress(SwDocShell *pDocShell, tools::Long nStartVal, tools::Long nEndVal)
+ : m_pDocShell(pDocShell)
+ {
+ ::StartProgress(STR_STATSTR_W4WREAD, nStartVal, nEndVal, m_pDocShell);
+ }
+
+ void Update(sal_uInt16 nProgress)
+ {
+ ::SetProgressState(nProgress, m_pDocShell); // Update
+ }
+
+ ~ImportProgress()
+ {
+ ::EndProgress(m_pDocShell);
+ }
+};
+
+// detect if the SwFrameFormat it is watching was deleted
+class SW_DLLPUBLIC FrameDeleteWatch final: public SvtListener
+{
+ SwFrameFormat* m_pFormat;
+public:
+ FrameDeleteWatch(SwFrameFormat* pFormat);
+
+ virtual void Notify(const SfxHint& rHint) override;
+
+ SwFrameFormat* GetFormat()
+ {
+ return m_pFormat;
+ }
+
+ bool WasDeleted() const
+ {
+ return !m_pFormat;
+ }
+
+ virtual ~FrameDeleteWatch() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/msfilter.hxx b/sw/source/filter/inc/msfilter.hxx
new file mode 100644
index 0000000000..70ef8d12aa
--- /dev/null
+++ b/sw/source/filter/inc/msfilter.hxx
@@ -0,0 +1,409 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_MSFILTER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_MSFILTER_HXX
+
+#include <sal/config.h>
+
+#include <map>
+#include <vector>
+#include <memory>
+#include <swtypes.hxx>
+#include "wwstyles.hxx"
+#include <rtl/textenc.h>
+#include "fltshell.hxx"
+#include <shellio.hxx>
+#include <svl/listener.hxx>
+
+class SwDoc;
+class SwPaM;
+class SwTableNode;
+class SwNodeIndex;
+class SwNoTextNode;
+class SwTextNode;
+class WW8TabDesc;
+
+namespace myImplHelpers
+{
+template<class C> class StyleMapperImpl;
+}
+
+class SwTextFormatColl;
+class SwCharFormat;
+
+namespace sw
+{
+ namespace ms
+ {
+ /** MSOffice appears to set the charset of unicode fonts to MS 932
+
+ But we do "default", whatever that means.
+
+ @param eTextEncoding
+ the OOo encoding to convert from
+
+ @return
+ a msoffice equivalent charset identifier
+ */
+ sal_uInt8 rtl_TextEncodingToWinCharset(rtl_TextEncoding eTextEncoding);
+
+ /** MSOffice appears to set the charset of unicode fonts to MS 932
+
+ Arial Unicode MS for example is a unicode font, but word sets
+ exported uses of it to the MS 932 charset
+
+ */
+ sal_uInt8 rtl_TextEncodingToWinCharsetRTF(OUString const& rFontName,
+ OUString const& rAltName, rtl_TextEncoding eTextEncoding);
+
+ /** Convert from DTTM to Writer's DateTime
+
+ */
+ sal_uInt32 DateTime2DTTM( const DateTime& rDT );
+
+ /** Convert from Word Date/Time field str to Writer's Date Time str
+
+ */
+ sal_uLong MSDateTimeFormatToSwFormat(OUString& rParams, SvNumberFormatter *pFormatter, LanguageType &rLang, bool bHijri, LanguageType nDocLang);
+
+ /*Used to identify if the previous token is AM time field*/
+ bool IsPreviousAM(std::u16string_view rParams, sal_Int32 nPos);
+
+ /*Used to identify if the next token is PM time field*/
+ bool IsNextPM(std::u16string_view rParams, sal_Int32 nPos);
+
+ /** Used by MSDateTimeFormatToSwFormat to identify AM time fields
+
+ */
+ bool IsNotAM(std::u16string_view rParams, sal_Int32 nPos);
+
+ /** Another function used by MSDateTimeFormatToSwFormat
+
+ */
+ void SwapQuotesInField(OUString &rFormat);
+
+ }
+
+ namespace util
+ {
+ /** Clips a value to MAX/MIN 16bit value to make it safe for use
+ as a position value to give to writer. i.e. +-57.8cm. Sometimes
+ we see ridiculous values for positioning in rtf and word document,
+ this captures such ones and clips them to values which are
+ still outside the document, but of a value that doesn't cause
+ problems for writer's layout, e.g. see
+ https://bz.apache.org/ooo/show_bug.cgi?id=i9245
+
+ @param nIn
+
+ @return nIn clipped to min/max 16bit value
+ */
+ SwTwips MakeSafePositioningValue(SwTwips nIn);
+
+ /** Knows which writer style a given word style should be imported as
+
+ Mapping a word style to a writer style has to consider mapping
+ the word builtin styles like "Normal" as the default root style
+ to our default root style which is called "Default" in english,
+ and "Normal" in german.
+
+ Additionally it then has to avoid name collisions such as
+
+ a) styles "Normal" and "Default" in a single document, where
+ we can use the original distinct names "Normal" and "Default" and...
+ b) styles "Normal" and "Default" in a single document, where
+ we can not use the original names, and must come up with an
+ alternative name for one of them...
+
+ And it needs to report to the importer if the style being mapped to
+ was already in existence, for the cut and paste/insert file mode we
+ should not modify the returned style if it is already in use as it
+ does not belong to us to change.
+ */
+ class ParaStyleMapper
+ {
+ private:
+ //I hate these things stupid pImpl things, but it's warranted here
+ std::unique_ptr<::myImplHelpers::StyleMapperImpl<SwTextFormatColl> > mpImpl;
+ public:
+ explicit ParaStyleMapper(SwDoc &rDoc);
+ ~ParaStyleMapper();
+
+ /** StyleResult
+ StyleResult is a std::pair of a pointer to a style and a flag
+ which is true if the style existed previously in the document.
+ */
+ typedef std::pair<SwTextFormatColl*, bool> StyleResult;
+
+ /** Get the writer style which the word style should map to
+
+ @param rName
+ The name of the word style
+
+ @param eSti
+ The style id of the word style, we are really only interested
+ in knowing if the style has either a builtin standard id, or is
+ a user defined style.
+
+ @param rCollisions
+ Cache of previous name collisions to speed up resolution
+ of later duplicates.
+
+ @return
+ The equivalent writer style packaged as a StyleResult to use
+ for this word style.
+
+ It will only return a failure in the pathological case of
+ catastrophic failure elsewhere of there exist already styles
+ rName and WW-rName[0..SAL_MAX_INT32], which is both unlikely
+ and impossible.
+ */
+ StyleResult GetStyle(const OUString& rName, ww::sti eSti,
+ std::map<OUString, sal_Int32>& rCollisions);
+ };
+
+ /** Knows which writer style a given word style should be imported as
+
+ Mapping a word style to a writer style has to consider mapping
+ the word builtin styles like "Normal" as the default root style
+ to our default root style which is called "Default" in english,
+ and "Normal" in german.
+
+ Additionally it then has to avoid name collisions such as
+
+ a) styles "Normal" and "Default" in a single document, where
+ we can use the original distinct names "Normal" and "Default" and...
+ b) styles "Normal" and "Default" in a single document, where
+ we can not use the original names, and must come up with an
+ alternative name for one of them...
+
+ And it needs to report to the importer if the style being mapped to
+ was already in existence, for the cut and paste/insert file mode we
+ should not modify the returned style if it is already in use as it
+ does not belong to us to change.
+ */
+ class CharStyleMapper
+ {
+ private:
+ //I hate these things stupid pImpl things, but it's warranted here
+ std::unique_ptr<::myImplHelpers::StyleMapperImpl<SwCharFormat>> mpImpl;
+ public:
+ explicit CharStyleMapper(SwDoc &rDoc);
+ ~CharStyleMapper();
+
+ /** StyleResult
+ StyleResult is a std::pair of a pointer to a style and a flag
+ which is true if the style existed previously in the document.
+ */
+ typedef std::pair<SwCharFormat*, bool> StyleResult;
+
+ /** Get the writer style which the word style should map to
+
+ @param rName
+ The name of the word style
+
+ @param eSti
+ The style id of the word style, we are really only interested
+ in knowing if the style has either a builtin standard id, or is
+ a user defined style.
+
+ @param rCollisions
+ Cache of previous name collisions to speed up resolution
+ of later duplicates.
+
+ @return
+ The equivalent writer style packaged as a StyleResult to use
+ for this word style.
+
+ It will only return a failure in the pathological case of
+ catastrophic failure elsewhere of there exist already styles
+ rName and WW-rName[0..SAL_MAX_INT32], which is both unlikely
+ and impossible.
+ */
+ StyleResult GetStyle(const OUString& rName, ww::sti eSti,
+ std::map<OUString, sal_Int32>& rCollisions);
+ };
+
+ /** Find suitable names for exporting this font
+
+ Given a fontname description find the best primary and secondary
+ fallback font to use from MSWord's persp font
+
+ @see #i10242#/#i19164# for examples
+ */
+ class FontMapExport
+ {
+ public:
+ OUString msPrimary;
+ OUString msSecondary;
+ explicit FontMapExport(std::u16string_view rFontDescription);
+ };
+
+ class InsertedTableListener final : public SvtListener
+ {
+ SwTableNode* m_pTableNode;
+ public:
+ explicit InsertedTableListener(SwTableNode& rNode);
+ SwTableNode* GetTableNode();
+ virtual void Notify(const SfxHint&) override;
+ };
+
+ /** Handle requirements for table formatting in insert->file mode.
+
+ When inserting a table into a document which already has been
+ formatted and laid out (e.g using insert->file) then tables
+ must be handled in a special way, (or so previous comments and
+ code in the filters leads me to believe).
+
+ Before the document is finalized the new tables need to have
+ their layout frms deleted and recalculated. This TableManager
+ detects the necessity to do this, and all tables inserted into
+ a document should be registered with this manager with
+ InsertTable, and before finalization DelAndMakeTableFrames should
+ be called.
+
+ @see #i25782# for examples
+ */
+ class InsertedTablesManager
+ {
+ public:
+ void DelAndMakeTableFrames();
+ void InsertTable(SwTableNode &rTableNode, SwPaM &rPaM);
+ explicit InsertedTablesManager(const SwDoc &rDoc);
+ private:
+ bool mbHasRoot;
+ std::map<std::unique_ptr<InsertedTableListener>, SwPosition*> maTables;
+ };
+
+ void MoveAttrFieldmarkInserted(SwFltPosition& rMkPos, SwFltPosition& rPtPos, const SwPosition& rPos);
+
+ class RedlineStack
+ {
+ private:
+ std::vector<std::unique_ptr<SwFltStackEntry>> maStack;
+ SwDoc &mrDoc;
+
+ RedlineStack(RedlineStack const&) = delete;
+ RedlineStack& operator=(RedlineStack const&) = delete;
+
+ public:
+ explicit RedlineStack(SwDoc &rDoc) : mrDoc(rDoc) {}
+ void MoveAttrsFieldmarkInserted(const SwPosition& rPos);
+ void open(const SwPosition& rPos, const SfxPoolItem& rAttr);
+ bool close(const SwPosition& rPos, RedlineType eType);
+ void close(const SwPosition& rPos, RedlineType eType,
+ WW8TabDesc* pTabDesc );
+ void closeall(const SwPosition& rPos);
+ ~RedlineStack();
+ };
+
+ class SetInDocAndDelete
+ {
+ private:
+ SwDoc &mrDoc;
+ public:
+ explicit SetInDocAndDelete(SwDoc &rDoc) : mrDoc(rDoc) {}
+ SetInDocAndDelete(SetInDocAndDelete const &) = default;
+ void operator()(std::unique_ptr<SwFltStackEntry> & pEntry);
+ private:
+ SetInDocAndDelete& operator=(const SetInDocAndDelete&) = delete;
+ };
+
+ class SetEndIfOpen //Subclass from something ?
+ {
+ private:
+ const SwPosition &mrPos;
+ public:
+ explicit SetEndIfOpen(const SwPosition &rPos) : mrPos(rPos) {}
+ void operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const
+ {
+ if (pEntry->m_bOpen)
+ pEntry->SetEndPos(mrPos);
+ }
+ SetEndIfOpen(SetEndIfOpen const &) = default;
+ private:
+ SetEndIfOpen& operator=(const SetEndIfOpen&) = delete;
+ };
+
+ class CompareRedlines
+ {
+ public:
+ bool operator()(const std::unique_ptr<SwFltStackEntry> & pOneE, const std::unique_ptr<SwFltStackEntry> & pTwoE)
+ const;
+ };
+
+ class WrtRedlineAuthor
+ {
+ protected:
+ std::vector<OUString> maAuthors; // Array of Sw - Bookmarknames
+
+ private:
+ WrtRedlineAuthor(WrtRedlineAuthor const&) = delete;
+ WrtRedlineAuthor& operator=(WrtRedlineAuthor const&) = delete;
+
+ public:
+ WrtRedlineAuthor() = default;
+ virtual ~WrtRedlineAuthor() {}
+
+ sal_uInt16 AddName( const OUString& rNm );
+ virtual void Write(Writer &rWrt) = 0;
+ };
+
+ struct CharRunEntry
+ {
+ sal_Int32 mnEndPos;
+ sal_uInt16 mnScript;
+ rtl_TextEncoding meCharSet;
+ bool mbRTL;
+ CharRunEntry(sal_Int32 nEndPos, sal_uInt16 nScript,
+ rtl_TextEncoding eCharSet, bool bRTL)
+ : mnEndPos(nEndPos), mnScript(nScript), meCharSet(eCharSet),
+ mbRTL(bRTL)
+ {
+ }
+ };
+
+ typedef std::vector<CharRunEntry> CharRuns;
+
+ /** Collect the ranges of Text which share
+
+ Word generally requires characters which share the same direction,
+ the same script, and occasionally (depending on the format) the
+ same charset to be exported in independent chunks.
+
+ So this function finds these ranges and returns a STL container
+ of CharRuns
+
+ @param rTextNd
+ The TextNode we want to ranges from
+
+ @return STL container of CharRuns which describe the shared
+ direction, script and optionally script of the contiguous sequences
+ of characters
+
+ @see #i22537# for example
+ */
+ CharRuns GetPseudoCharRuns(const SwTextNode& rTextNd);
+ }
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/rtf.hxx b/sw/source/filter/inc/rtf.hxx
new file mode 100644
index 0000000000..ed64c08b76
--- /dev/null
+++ b/sw/source/filter/inc/rtf.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_RTF_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_RTF_HXX
+
+#include <sal/types.h>
+
+class RTFSurround
+{
+ union {
+ struct
+ {
+ sal_uInt8 nGoldCut : 1; // should ideally be bool
+ sal_uInt8 nOrder : 4;
+ sal_uInt8 nJunk : 3;
+ } Flags;
+ sal_uInt8 nVal;
+ } m_Value;
+
+public:
+ RTFSurround(bool bGoldCut, sal_uInt8 nOrder)
+ {
+ m_Value.Flags.nGoldCut = sal_uInt8(bGoldCut);
+ m_Value.Flags.nOrder = nOrder;
+ m_Value.Flags.nJunk = 0;
+ }
+
+ sal_uInt16 GetValue() const { return m_Value.nVal; }
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_INC_RTF_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/wrt_fn.hxx b/sw/source/filter/inc/wrt_fn.hxx
new file mode 100644
index 0000000000..b1fdcafa73
--- /dev/null
+++ b/sw/source/filter/inc/wrt_fn.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_WRT_FN_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_WRT_FN_HXX
+#include <hintids.hxx>
+
+// some forward declarations
+class SwNode;
+class SwContentNode;
+class Writer;
+class SfxPoolItem;
+class SfxItemSet;
+class SwHTMLWriter;
+
+/* function pointers to the attribute-write functions */
+typedef SwHTMLWriter& (*FnAttrOut)( SwHTMLWriter&, const SfxPoolItem& );
+typedef FnAttrOut SwAttrFnTab[ POOLATTR_END - POOLATTR_BEGIN ];
+
+SwHTMLWriter& Out( const SwAttrFnTab, const SfxPoolItem&, SwHTMLWriter& );
+SwHTMLWriter& Out_SfxItemSet( const SwAttrFnTab, SwHTMLWriter&, const SfxItemSet&,
+ bool bDeep );
+
+/* function pointers to the node-write functions */
+
+enum RES_NODE
+{
+RES_NODE_BEGIN = 0,
+ RES_TXTNODE = RES_NODE_BEGIN,
+ RES_GRFNODE,
+ RES_OLENODE,
+RES_NODE_END
+};
+
+typedef Writer& (*FnNodeOut)( Writer&, SwContentNode& );
+typedef FnNodeOut SwNodeFnTab[ RES_NODE_END - RES_NODE_BEGIN ];
+
+Writer& Out( const SwNodeFnTab, SwNode&, Writer & rWrt );
+
+#endif // INCLUDED_SW_SOURCE_FILTER_INC_WRT_FN_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/wrtswtbl.hxx b/sw/source/filter/inc/wrtswtbl.hxx
new file mode 100644
index 0000000000..e3391e7ce7
--- /dev/null
+++ b/sw/source/filter/inc/wrtswtbl.hxx
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_WRTSWTBL_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_WRTSWTBL_HXX
+
+#include <tools/color.hxx>
+#include <tools/long.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+#include <swdllapi.h>
+
+#include <memory>
+#include <vector>
+#include <climits>
+
+class SwTableBox;
+class SwTableLine;
+class SwTableLines;
+class SwHTMLTableLayout;
+class SvxBrushItem;
+
+namespace editeng { class SvxBorderLine; }
+
+// Code from the HTML filter for writing of tables
+
+#define COLFUZZY 20
+#define ROWFUZZY 20
+#define COL_DFLT_WIDTH ((2*COLFUZZY)+1)
+#define ROW_DFLT_HEIGHT (2*ROWFUZZY)+1
+
+class SW_DLLPUBLIC SwWriteTableCell
+{
+ const SwTableBox *m_pBox; // SwTableBox of the cell
+ const SvxBrushItem *m_pBackground; // inherited background of a row
+
+ tools::Long m_nHeight; // fix/minimum height of a row
+
+ sal_uInt32 m_nWidthOpt; // width from option;
+
+ sal_uInt16 m_nRow; // start row
+ sal_uInt16 m_nCol; // start column
+
+ sal_uInt16 m_nRowSpan; // spanned rows
+ sal_uInt16 m_nColSpan; // spanned columns
+
+ bool m_bPercentWidthOpt;
+
+public:
+
+ SwWriteTableCell(const SwTableBox *pB, sal_uInt16 nR, sal_uInt16 nC, sal_uInt16 nRSpan,
+ sal_uInt16 nCSpan, tools::Long nHght, const SvxBrushItem *pBGround)
+ : m_pBox( pB ), m_pBackground( pBGround ), m_nHeight( nHght ), m_nWidthOpt( 0 ),
+ m_nRow( nR ), m_nCol( nC ), m_nRowSpan( nRSpan ), m_nColSpan( nCSpan ),
+ m_bPercentWidthOpt( false )
+ {}
+
+ const SwTableBox *GetBox() const { return m_pBox; }
+
+ sal_uInt16 GetRow() const { return m_nRow; }
+ sal_uInt16 GetCol() const { return m_nCol; }
+
+ sal_uInt16 GetRowSpan() const { return m_nRowSpan; }
+ sal_uInt16 GetColSpan() const { return m_nColSpan; }
+
+ tools::Long GetHeight() const { return m_nHeight; }
+ sal_Int16 GetVertOri() const;
+
+ const SvxBrushItem *GetBackground() const { return m_pBackground; }
+
+ void SetWidthOpt( sal_uInt16 nWidth, bool bPercent )
+ {
+ m_nWidthOpt = nWidth; m_bPercentWidthOpt = bPercent;
+ }
+
+ sal_uInt32 GetWidthOpt() const { return m_nWidthOpt; }
+ bool HasPercentWidthOpt() const { return m_bPercentWidthOpt; }
+};
+
+typedef std::vector<std::unique_ptr<SwWriteTableCell>> SwWriteTableCells;
+
+class SwWriteTableRow final
+{
+ SwWriteTableCells m_Cells; ///< all cells of the rows
+ const SvxBrushItem *m_pBackground; // background
+
+ tools::Long m_nPos; // end position (twips) of the row
+ bool mbUseLayoutHeights;
+ bool m_bTopBorder; // which borders are there?
+ bool m_bBottomBorder;
+
+ SwWriteTableRow & operator= (const SwWriteTableRow &) = delete;
+
+ // GCC >= 3.4 needs accessible T (const T&) to pass T as const T& argument.
+ SwWriteTableRow( const SwWriteTableRow & );
+
+public:
+
+ SwWriteTableRow( tools::Long nPos, bool bUseLayoutHeights );
+
+ SwWriteTableCell *AddCell( const SwTableBox *pBox,
+ sal_uInt16 nRow, sal_uInt16 nCol,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ tools::Long nHeight,
+ const SvxBrushItem *pBackground );
+
+ void SetBackground( const SvxBrushItem *pBGround )
+ {
+ m_pBackground = pBGround;
+ }
+ const SvxBrushItem *GetBackground() const { return m_pBackground; }
+
+ bool HasTopBorder() const { return m_bTopBorder; }
+ void SetTopBorder(bool value) { m_bTopBorder = value; }
+ bool HasBottomBorder() const { return m_bBottomBorder; }
+ void SetBottomBorder(bool value) { m_bBottomBorder = value; }
+
+ const SwWriteTableCells& GetCells() const { return m_Cells; }
+
+ inline bool operator==( const SwWriteTableRow& rRow ) const;
+ inline bool operator<( const SwWriteTableRow& rRow2 ) const;
+};
+
+inline bool SwWriteTableRow::operator==( const SwWriteTableRow& rRow ) const
+{
+ // allow for some fuzzyness
+ return (m_nPos >= rRow.m_nPos ? m_nPos - rRow.m_nPos : rRow.m_nPos - m_nPos ) <=
+ (mbUseLayoutHeights ? 0 : ROWFUZZY);
+}
+
+inline bool SwWriteTableRow::operator<( const SwWriteTableRow& rRow ) const
+{
+ // Since we only know the degrees of truth of 0 and 1 here, we also prefer to
+ // not let x==y and x<y at the same time ;-)
+ return m_nPos < rRow.m_nPos - (mbUseLayoutHeights ? 0 : ROWFUZZY);
+}
+
+using SwWriteTableRows
+ = o3tl::sorted_vector< std::unique_ptr<SwWriteTableRow>, o3tl::less_uniqueptr_to<SwWriteTableRow> >;
+
+class SwWriteTableCol
+{
+ sal_uInt32 m_nPos; // end position of the column
+
+ sal_uInt32 m_nWidthOpt;
+
+ bool m_bRelWidthOpt : 1;
+
+public:
+ bool m_bLeftBorder : 1; // which borders are there?
+ bool m_bRightBorder : 1;
+
+ SwWriteTableCol( sal_uInt32 nPosition );
+
+ sal_uInt32 GetPos() const { return m_nPos; }
+
+ bool HasLeftBorder() const { return m_bLeftBorder; }
+
+ bool HasRightBorder() const { return m_bRightBorder; }
+
+ inline bool operator==( const SwWriteTableCol& rCol ) const;
+ inline bool operator<( const SwWriteTableCol& rCol ) const;
+
+ void SetWidthOpt( sal_uInt32 nWidth, bool bRel )
+ {
+ m_nWidthOpt = nWidth; m_bRelWidthOpt = bRel;
+ }
+ sal_uInt32 GetWidthOpt() const { return m_nWidthOpt; }
+ bool HasRelWidthOpt() const { return m_bRelWidthOpt; }
+};
+
+inline bool SwWriteTableCol::operator==( const SwWriteTableCol& rCol ) const
+{
+ // allow for some fuzzyness
+ return (m_nPos >= rCol.m_nPos ? m_nPos - rCol.m_nPos
+ : rCol.m_nPos - m_nPos ) <= COLFUZZY;
+}
+
+inline bool SwWriteTableCol::operator<( const SwWriteTableCol& rCol ) const
+{
+ // Since we only know the degrees of truth of 0 and 1 here, we also prefer to
+ // not let x==y and x<y at the same time ;-)
+ return m_nPos + COLFUZZY < rCol.m_nPos;
+}
+
+struct SwWriteTableColLess {
+ bool operator()(std::unique_ptr<SwWriteTableCol> const & lhs, std::unique_ptr<SwWriteTableCol> const & rhs) {
+ return lhs->GetPos() < rhs->GetPos();
+ }
+};
+
+class SwWriteTableCols : public o3tl::sorted_vector<std::unique_ptr<SwWriteTableCol>, SwWriteTableColLess> {
+};
+
+class SwTable;
+
+class SW_DLLPUBLIC SwWriteTable
+{
+private:
+ const SwTable* m_pTable;
+protected:
+ SwWriteTableCols m_aCols; // all columns
+ SwWriteTableRows m_aRows; // all rows
+
+ Color m_nBorderColor; // border color
+
+ sal_uInt16 m_nCellSpacing; // thickness of the inner border
+ sal_uInt16 m_nCellPadding; // distance of border to content
+
+ sal_uInt16 m_nBorder; // thickness of the outer border
+ sal_uInt16 m_nInnerBorder; // thickness of the inner border
+ sal_uInt32 m_nBaseWidth; // reference value for SwFormatFrameSize width
+
+ sal_uInt16 m_nHeadEndRow; // last row of the table heading
+
+ sal_uInt16 m_nLeftSub;
+ sal_uInt16 m_nRightSub;
+
+ sal_uInt32 m_nTabWidth; // absolute/relative width of the table
+
+ bool m_bRelWidths : 1; // generate relative widths?
+ bool m_bUseLayoutHeights : 1; // use layout to determine the height?
+#ifdef DBG_UTIL
+ bool m_bGetLineHeightCalled : 1;
+#endif
+
+ bool m_bColTags : 1;
+ bool m_bLayoutExport : 1;
+ bool m_bCollectBorderWidth : 1;
+
+ virtual bool ShouldExpandSub( const SwTableBox *pBox,
+ bool bExpandedBefore, sal_uInt16 nDepth ) const;
+
+ void CollectTableRowsCols( tools::Long nStartRPos, sal_uInt32 nStartCPos,
+ tools::Long nParentLineHeight,
+ sal_uInt32 nParentLineWidth,
+ const SwTableLines& rLines,
+ sal_uInt16 nDepth );
+
+ void FillTableRowsCols( tools::Long nStartRPos, sal_uInt16 nStartRow,
+ sal_uInt32 nStartCPos, sal_uInt16 nStartCol,
+ tools::Long nParentLineHeight,
+ sal_uInt32 nParentLineWidth,
+ const SwTableLines& rLines,
+ const SvxBrushItem* pLineBrush,
+ sal_uInt16 nDepth,
+ sal_uInt16 nNumOfHeaderRows );
+
+ void MergeBorders( const editeng::SvxBorderLine* pBorderLine, bool bTable );
+
+ sal_uInt16 MergeBoxBorders(const SwTableBox *pBox, size_t nRow, size_t nCol,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ sal_uInt16 &rTopBorder, sal_uInt16 &rBottomBorder );
+
+ sal_uInt32 GetBaseWidth() const { return m_nBaseWidth; }
+
+ bool HasRelWidths() const { return m_bRelWidths; }
+
+public:
+ static sal_uInt32 GetBoxWidth( const SwTableBox *pBox );
+
+ sal_uInt32 GetRawWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const;
+ sal_uInt16 GetAbsWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const;
+ sal_uInt16 GetRelWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const;
+ sal_uInt16 GetPercentWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const;
+
+ tools::Long GetAbsHeight(tools::Long nRawWidth, size_t nRow, sal_uInt16 nRowSpan) const;
+
+ double GetAbsWidthRatio() const { return m_nTabWidth == m_nBaseWidth ? 1.0 : double(m_nTabWidth) / m_nBaseWidth; }
+protected:
+ tools::Long GetLineHeight( const SwTableLine *pLine );
+ static tools::Long GetLineHeight( const SwTableBox *pBox );
+ static const SvxBrushItem *GetLineBrush( const SwTableBox *pBox,
+ SwWriteTableRow *pRow );
+
+ sal_uInt16 GetLeftSpace( sal_uInt16 nCol ) const;
+ sal_uInt16 GetRightSpace(size_t nCol, sal_uInt16 nColSpan) const;
+
+public:
+ SwWriteTable(const SwTable* pTable, const SwTableLines& rLines, tools::Long nWidth, sal_uInt32 nBWidth,
+ bool bRel, sal_uInt16 nMaxDepth = USHRT_MAX,
+ sal_uInt16 nLeftSub=0, sal_uInt16 nRightSub=0, sal_uInt32 nNumOfRowsToRepeat=0);
+ SwWriteTable(const SwTable* pTable, const SwHTMLTableLayout *pLayoutInfo);
+ virtual ~SwWriteTable();
+
+ const SwWriteTableRows& GetRows() const { return m_aRows; }
+
+ const SwTable* GetTable() const { return m_pTable; }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/inc/wwstyles.hxx b/sw/source/filter/inc/wwstyles.hxx
new file mode 100644
index 0000000000..7a253d87c5
--- /dev/null
+++ b/sw/source/filter/inc/wwstyles.hxx
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_INC_WWSTYLES_HXX
+#define INCLUDED_SW_SOURCE_FILTER_INC_WWSTYLES_HXX
+
+#include <sal/types.h>
+
+namespace ww
+{
+ // When changing, make sure to update GetStiNames in sw/source/filter/ww8/styles.cxx accordingly
+ enum sti : sal_uInt16
+ {
+ stiNormal = 0, // 0x0000
+ stiLev1 = 1, // 0x0001
+ stiLev2 = 2, // 0x0002
+ stiLev3 = 3, // 0x0003
+ stiLev4 = 4, // 0x0004
+ stiLev5 = 5, // 0x0005
+ stiLev6 = 6, // 0x0006
+ stiLev7 = 7, // 0x0007
+ stiLev8 = 8, // 0x0008
+ stiLev9 = 9, // 0x0009
+ stiLevFirst = stiLev1,
+ stiLevLast = stiLev9,
+ stiIndex1 = 10, // 0x000A
+ stiIndex2 = 11, // 0x000B
+ stiIndex3 = 12, // 0x000C
+ stiIndex4 = 13, // 0x000D
+ stiIndex5 = 14, // 0x000E
+ stiIndex6 = 15, // 0x000F
+ stiIndex7 = 16, // 0x0010
+ stiIndex8 = 17, // 0x0011
+ stiIndex9 = 18, // 0x0012
+ stiIndexFirst = stiIndex1,
+ stiIndexLast = stiIndex9,
+ stiToc1 = 19, // 0x0013
+ stiToc2 = 20, // 0x0014
+ stiToc3 = 21, // 0x0015
+ stiToc4 = 22, // 0x0016
+ stiToc5 = 23, // 0x0017
+ stiToc6 = 24, // 0x0018
+ stiToc7 = 25, // 0x0019
+ stiToc8 = 26, // 0x001A
+ stiToc9 = 27, // 0x001B
+ stiTocFirst = stiToc1,
+ stiTocLast = stiToc9,
+ stiNormIndent = 28, // 0x001C
+ stiFootnoteText = 29, // 0x001D
+ stiAtnText = 30, // 0x001E
+ stiHeader = 31, // 0x001F
+ stiFooter = 32, // 0x0020
+ stiIndexHeading = 33, // 0x0021
+ stiCaption = 34, // 0x0022
+ stiToCaption = 35, // 0x0023
+ stiEnvAddr = 36, // 0x0024
+ stiEnvRet = 37, // 0x0025
+ stiFootnoteRef = 38, // 0x0026 char style
+ stiAtnRef = 39, // 0x0027 char style
+ stiLnn = 40, // 0x0028 char style
+ stiPgn = 41, // 0x0029 char style
+ stiEdnRef = 42, // 0x002A char style
+ stiEdnText = 43, // 0x002B
+ stiToa = 44, // 0x002C
+ stiMacro = 45, // 0x002D
+ stiToaHeading = 46, // 0x002E
+ stiList = 47, // 0x002F
+ stiListBullet = 48, // 0x0030
+ stiListNumber = 49, // 0x0031
+ stiList2 = 50, // 0x0032
+ stiList3 = 51, // 0x0033
+ stiList4 = 52, // 0x0034
+ stiList5 = 53, // 0x0035
+ stiListBullet2 = 54, // 0x0036
+ stiListBullet3 = 55, // 0x0037
+ stiListBullet4 = 56, // 0x0038
+ stiListBullet5 = 57, // 0x0039
+ stiListNumber2 = 58, // 0x003A
+ stiListNumber3 = 59, // 0x003B
+ stiListNumber4 = 60, // 0x003C
+ stiListNumber5 = 61, // 0x003D
+ stiTitle = 62, // 0x003E
+ stiClosing = 63, // 0x003F
+ stiSignature = 64, // 0x0040
+ stiNormalChar = 65, // 0x0041 char style
+ stiBodyText = 66, // 0x0042
+ /*
+ stiBodyTextInd1 was orig stiBodyText2 in documentation, but that
+ collides with the other stiBodyText2 and this seems more reasonable.
+ cmc@openoffice.org
+ */
+ stiBodyTextInd1 = 67, // 0x0043
+ stiListCont = 68, // 0x0044
+ stiListCont2 = 69, // 0x0045
+ stiListCont3 = 70, // 0x0046
+ stiListCont4 = 71, // 0x0047
+ stiListCont5 = 72, // 0x0048
+ stiMsgHeader = 73, // 0x0049
+ stiSubtitle = 74, // 0x004A
+ stiSalutation = 75, // 0x004B
+ stiDate = 76, // 0X004C
+ stiBodyText1I = 77, // 0x004D
+ stiBodyText1I2 = 78, // 0x004E
+ stiNoteHeading = 79, // 0x004F
+ stiBodyText2 = 80, // 0x0050
+ stiBodyText3 = 81, // 0x0051
+ stiBodyTextInd2 = 82, // 0x0052
+ stiBodyTextInd3 = 83, // 0x0053
+ stiBlockQuote = 84, // 0x0054
+ stiHyperlink = 85, // 0x0055 char style
+ stiHyperlinkFollowed = 86, // 0x0056 char style
+ stiStrong = 87, // 0x0057 char style
+ stiEmphasis = 88, // 0x0058 char style
+ stiNavPane = 89, // 0x0059 char style
+ stiPlainText = 90, // 0x005A
+ stiMax = 91, // number of defined sti's
+ stiUser = 0x0ffe, // user styles are distinguished by name
+ stiNil = 0x0fff // max for 12 bits
+ };
+
+ /** Find the WinWord sti index of an old <= Word2 stc (style code)
+
+ When importing a Word 2 document we would like to treat styles as
+ similar to how word 8 does as possible, to this end word will treat
+ some styles with special codes as inbuilt styles, and some as user
+ defined styles.
+
+ @param
+ stc the Style code to test to see what winword sti word would give
+ such a code
+
+ @return the sti that word would give it. stiUser if word would treat
+ it as a user defined style.
+ */
+ sti GetCanonicalStiFromStc(sal_uInt8 stc) noexcept;
+
+ /** Find the WinWord english name from a sti index
+
+ Map the word style index to its english name
+
+ @param
+ sti the Style index
+
+ @return the name word would give it if it's an inbuilt name, otherwise
+ NULL
+ */
+ const char* GetEnglishNameFromSti(sti eSti) noexcept;
+
+ /** Determine if the WinWord sti is standard Character Style
+
+ @param
+ sti the Style index
+
+ @return true if a known inbuild character style
+ */
+ bool StandardStiIsCharStyle(sti eSti) noexcept;
+} // namespace ww
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/indexing/IndexingExport.cxx b/sw/source/filter/indexing/IndexingExport.cxx
new file mode 100644
index 0000000000..acd7aba45b
--- /dev/null
+++ b/sw/source/filter/indexing/IndexingExport.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <IndexingExport.hxx>
+
+#include <ndtxt.hxx>
+#include <ndole.hxx>
+#include <ndgrf.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <swtable.hxx>
+#include <deque>
+
+namespace sw
+{
+namespace
+{
+class IndexingNodeHandler : public ModelTraverseHandler
+{
+private:
+ tools::XmlWriter& m_rXmlWriter;
+
+ std::deque<SwNode*> maNodeStack;
+
+public:
+ IndexingNodeHandler(tools::XmlWriter& rXmlWriter)
+ : m_rXmlWriter(rXmlWriter)
+ {
+ }
+
+ void handleNode(SwNode* pNode) override
+ {
+ if (pNode->IsOLENode())
+ {
+ handleOLENode(pNode->GetOLENode());
+ }
+ else if (pNode->IsGrfNode())
+ {
+ handleGraphicNode(pNode->GetGrfNode());
+ }
+ else if (pNode->IsTextNode())
+ {
+ handleTextNode(pNode->GetTextNode());
+ }
+ else if (pNode->IsTableNode())
+ {
+ handleTableNode(pNode->GetTableNode());
+ }
+ else if (pNode->IsSectionNode())
+ {
+ handleSectionNode(pNode->GetSectionNode());
+ }
+ else if (pNode->IsEndNode())
+ {
+ handleEndNode(pNode->GetEndNode());
+ }
+ }
+
+ void handleOLENode(const SwOLENode* pOleNode)
+ {
+ auto pFrameFormat = pOleNode->GetFlyFormat();
+ m_rXmlWriter.startElement("object");
+ m_rXmlWriter.attribute("alt", pOleNode->GetTitle());
+ m_rXmlWriter.attribute("name", pFrameFormat->GetName());
+ m_rXmlWriter.attribute("object_type", "ole"_ostr);
+ m_rXmlWriter.endElement();
+ }
+
+ void handleGraphicNode(const SwGrfNode* pGraphicNode)
+ {
+ auto pFrameFormat = pGraphicNode->GetFlyFormat();
+ m_rXmlWriter.startElement("object");
+ m_rXmlWriter.attribute("alt", pGraphicNode->GetTitle());
+ m_rXmlWriter.attribute("name", pFrameFormat->GetName());
+ m_rXmlWriter.attribute("object_type", "graphic"_ostr);
+ m_rXmlWriter.endElement();
+ }
+
+ void handleTextNode(const SwTextNode* pTextNode)
+ {
+ SwNodeOffset nParentIndex(-1);
+ if (!maNodeStack.empty() && maNodeStack.back())
+ {
+ nParentIndex = maNodeStack.back()->GetIndex();
+ }
+ const OUString& rString
+ = pTextNode->GetText().replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "");
+ if (rString.isEmpty())
+ return;
+ m_rXmlWriter.startElement("paragraph");
+ m_rXmlWriter.attribute("index", sal_Int32(pTextNode->GetIndex()));
+ m_rXmlWriter.attribute("node_type", "writer"_ostr);
+ if (nParentIndex >= SwNodeOffset(0))
+ m_rXmlWriter.attribute("parent_index", sal_Int32(nParentIndex));
+ m_rXmlWriter.content(rString);
+ m_rXmlWriter.endElement();
+ }
+
+ void handleSdrObject(SdrObject* pObject) override
+ {
+ if (pObject->GetName().isEmpty())
+ return;
+
+ m_rXmlWriter.startElement("object");
+ m_rXmlWriter.attribute("name", pObject->GetName());
+ m_rXmlWriter.attribute("alt", pObject->GetTitle());
+ m_rXmlWriter.attribute("object_type", "shape"_ostr);
+ m_rXmlWriter.attribute("description", pObject->GetDescription());
+
+ m_rXmlWriter.endElement();
+
+ SdrTextObj* pTextObject = DynCastSdrTextObj(pObject);
+ if (!pTextObject)
+ return;
+
+ OutlinerParaObject* pOutlinerParagraphObject = pTextObject->GetOutlinerParaObject();
+ if (!pOutlinerParagraphObject)
+ return;
+
+ const EditTextObject& aEdit = pOutlinerParagraphObject->GetTextObject();
+ for (sal_Int32 nParagraph = 0; nParagraph < aEdit.GetParagraphCount(); ++nParagraph)
+ {
+ OUString sText = aEdit.GetText(nParagraph);
+
+ m_rXmlWriter.startElement("paragraph");
+ m_rXmlWriter.attribute("index", nParagraph);
+ m_rXmlWriter.attribute("node_type", "common"_ostr);
+ m_rXmlWriter.attribute("object_name", pObject->GetName());
+ m_rXmlWriter.content(sText);
+ m_rXmlWriter.endElement();
+ }
+ }
+
+ void handleTableNode(SwTableNode* pTableNode)
+ {
+ const SwTableFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
+ OUString sName = pFormat->GetName();
+
+ m_rXmlWriter.startElement("object");
+ m_rXmlWriter.attribute("index", sal_Int32(pTableNode->GetIndex()));
+ m_rXmlWriter.attribute("name", sName);
+ m_rXmlWriter.attribute("object_type", "table"_ostr);
+ m_rXmlWriter.endElement();
+
+ maNodeStack.push_back(pTableNode);
+ }
+
+ void handleSectionNode(SwSectionNode* pSectionNode)
+ {
+ m_rXmlWriter.startElement("object");
+ m_rXmlWriter.attribute("index", sal_Int32(pSectionNode->GetIndex()));
+ m_rXmlWriter.attribute("name", pSectionNode->GetSection().GetSectionName());
+ m_rXmlWriter.attribute("object_type", "section"_ostr);
+ m_rXmlWriter.endElement();
+
+ maNodeStack.push_back(pSectionNode);
+ }
+
+ void handleEndNode(SwEndNode* pEndNode)
+ {
+ if (!maNodeStack.empty() && pEndNode->StartOfSectionNode() == maNodeStack.back())
+ {
+ maNodeStack.pop_back();
+ }
+ }
+};
+
+} // end anonymous namespace
+
+IndexingExport::IndexingExport(SvStream& rStream, SwDoc* pDoc)
+ : m_aModelTraverser(pDoc)
+ , m_aXmlWriter(&rStream)
+{
+}
+
+bool IndexingExport::runExport()
+{
+ bool bResult = m_aXmlWriter.startDocument(2);
+ if (!bResult)
+ return false;
+
+ m_aXmlWriter.startElement("indexing");
+ m_aModelTraverser.addNodeHandler(std::make_shared<IndexingNodeHandler>(m_aXmlWriter));
+ m_aModelTraverser.traverse();
+ m_aXmlWriter.endElement();
+
+ m_aXmlWriter.endDocument();
+
+ return true;
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/indexing/IndexingExportFilter.cxx b/sw/source/filter/indexing/IndexingExportFilter.cxx
new file mode 100644
index 0000000000..6ac3b6e3a8
--- /dev/null
+++ b/sw/source/filter/indexing/IndexingExportFilter.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <IndexingExportFilter.hxx>
+#include <IndexingExport.hxx>
+
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <svl/outstrm.hxx>
+
+using namespace css;
+
+namespace sw
+{
+sal_Bool IndexingExportFilter::filter(const uno::Sequence<beans::PropertyValue>& aDescriptor)
+{
+ bool bReturn = false;
+
+ utl::MediaDescriptor aMediaDesc = aDescriptor;
+
+ // Actually get the SwRootFrame to call dumpAsXml
+ auto pXTextDocument = comphelper::getFromUnoTunnel<SwXTextDocument>(m_xSourceDocument);
+ if (pXTextDocument)
+ {
+ uno::Reference<io::XOutputStream> xOutputStream = aMediaDesc.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_OUTPUTSTREAM, uno::Reference<io::XOutputStream>());
+
+ std::unique_ptr<SvStream> pStream(new SvOutputStream(xOutputStream));
+ SwDocShell* pShell = pXTextDocument->GetDocShell();
+ SwDoc* pDoc = pShell->GetDoc();
+ if (pDoc)
+ {
+ IndexingExport aIndexingExport(*pStream, pDoc);
+ bReturn = aIndexingExport.runExport();
+ }
+ }
+
+ return bReturn;
+}
+
+} // end namespace sw
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_IndexingExportFilter_get_implementation(
+ css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new sw::IndexingExportFilter());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/rtf/swparrtf.cxx b/sw/source/filter/rtf/swparrtf.cxx
new file mode 100644
index 0000000000..56dd365302
--- /dev/null
+++ b/sw/source/filter/rtf/swparrtf.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <poolfmt.hxx>
+#include <shellio.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <swdll.hxx>
+#include <swerror.h>
+
+#include <unotextrange.hxx>
+
+#include <unotools/streamwrap.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Glue class to call RtfImport as an internal filter, needed by copy&paste support.
+class SwRTFReader : public Reader
+{
+ ErrCodeMsg Read(SwDoc& rDoc, const OUString& rBaseURL, SwPaM& rPam,
+ const OUString& rFileName) override;
+};
+}
+
+ErrCodeMsg SwRTFReader::Read(SwDoc& rDoc, const OUString& /*rBaseURL*/, SwPaM& rPam,
+ const OUString& /*rFileName*/)
+{
+ if (!m_pStream)
+ return ERR_SWG_READ_ERROR;
+
+ // We want to work in an empty paragraph.
+ // Step 1: XTextRange will be updated when content is inserted, so we know
+ // the end position.
+ const rtl::Reference<SwXTextRange> xInsertPosition
+ = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
+ auto pSttNdIdx = std::make_shared<SwNodeIndex>(rDoc.GetNodes());
+ const SwPosition* pPos = rPam.GetPoint();
+
+ // Step 2: Split once and remember the node that has been split.
+ rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
+ *pSttNdIdx = pPos->GetNodeIndex() - 1;
+
+ // Step 3: Split again.
+ rDoc.getIDocumentContentOperations().SplitNode(*pPos, false);
+ auto pSttNdIdx2 = std::make_shared<SwNodeIndex>(rDoc.GetNodes());
+ *pSttNdIdx2 = pPos->GetNodeIndex();
+
+ // Step 4: Insert all content into the new node
+ rPam.Move(fnMoveBackward);
+ rDoc.SetTextFormatColl(
+ rPam, rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false));
+
+ SwDocShell* pDocShell(rDoc.GetDocShell());
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+ comphelper::getProcessServiceFactory());
+ uno::Reference<uno::XInterface> xInterface(
+ xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.RtfFilter"),
+ uno::UNO_SET_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Reference<lang::XComponent> xDstDoc(pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ xImporter->setTargetDocument(xDstDoc);
+
+ const rtl::Reference<SwXTextRange> xInsertTextRange
+ = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
+ { { "InputStream",
+ uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(*m_pStream))) },
+ { "InsertMode", uno::Any(true) },
+ { "TextInsertModeRange",
+ uno::Any(uno::Reference<text::XTextRange>(xInsertTextRange)) } }));
+ auto ret = ERRCODE_NONE;
+ try
+ {
+ xFilter->filter(aDescriptor);
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.rtf", "SwRTFReader::Read()");
+ ret = ERR_SWG_READ_ERROR;
+ }
+
+ // Clean up the fake paragraphs.
+ SwUnoInternalPaM aPam(rDoc);
+ ::sw::XTextRangeToSwPaM(aPam, xInsertPosition);
+ if (pSttNdIdx->GetIndex())
+ {
+ // If we are in insert mode, join the split node that is in front
+ // of the new content with the first new node. Or in other words:
+ // Revert the first split node.
+ SwTextNode* pTextNode = pSttNdIdx->GetNode().GetTextNode();
+ SwNodeIndex aNxtIdx(*pSttNdIdx);
+ if (pTextNode && pTextNode->CanJoinNext(&aNxtIdx)
+ && pSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex())
+ {
+ // If the PaM points to the first new node, move the PaM to the
+ // end of the previous node.
+ if (aPam.GetPoint()->GetNode() == aNxtIdx.GetNode())
+ {
+ aPam.GetPoint()->Assign(*pTextNode, pTextNode->GetText().getLength());
+ }
+ // If the first new node isn't empty, convert the node's text
+ // attributes into hints. Otherwise, set the new node's
+ // paragraph style at the previous (empty) node.
+ SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode();
+ if (pTextNode->GetText().getLength())
+ pDelNd->FormatToTextAttr(pTextNode);
+ else
+ {
+ pTextNode->ChgFormatColl(pDelNd->GetTextColl());
+ if (!pDelNd->GetNoCondAttr(RES_PARATR_LIST_ID, /*bInParents=*/false))
+ {
+ // Lists would need manual merging, but copy paragraph direct formatting
+ // otherwise.
+ pDelNd->CopyCollFormat(*pTextNode);
+ }
+ }
+ pTextNode->JoinNext();
+ }
+ }
+
+ if (pSttNdIdx2->GetIndex())
+ {
+ // If we are in insert mode, join the split node that is after
+ // the new content with the last new node. Or in other words:
+ // Revert the second split node.
+ SwTextNode* pTextNode = pSttNdIdx2->GetNode().GetTextNode();
+ SwNodeIndex aPrevIdx(*pSttNdIdx2);
+ if (pTextNode && pTextNode->CanJoinPrev(&aPrevIdx)
+ && pSttNdIdx2->GetIndex() - 1 == aPrevIdx.GetIndex())
+ {
+ // If the last new node isn't empty, convert the node's text
+ // attributes into hints. Otherwise, set the new node's
+ // paragraph style at the next (empty) node.
+ SwTextNode* pDelNd = aPrevIdx.GetNode().GetTextNode();
+ if (pTextNode->GetText().getLength())
+ pDelNd->FormatToTextAttr(pTextNode);
+ else
+ pTextNode->ChgFormatColl(pDelNd->GetTextColl());
+ pTextNode->JoinPrev();
+ }
+ }
+
+ return ret;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportRTF() { return new SwRTFReader; }
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportRTF(SvStream& rStream)
+{
+ SwGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ xDocSh->DoInitNew();
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+ comphelper::getProcessServiceFactory());
+ uno::Reference<uno::XInterface> xInterface(
+ xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.RtfFilter"),
+ uno::UNO_SET_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Reference<lang::XComponent> xDstDoc(xDocSh->GetModel(), uno::UNO_QUERY_THROW);
+ xImporter->setTargetDocument(xDstDoc);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence(
+ { { "InputStream",
+ uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(rStream))) } }));
+ bool bRet = true;
+ try
+ {
+ xFilter->filter(aDescriptor);
+ }
+ catch (...)
+ {
+ bRet = false;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/writer/writer.cxx b/sw/source/filter/writer/writer.cxx
new file mode 100644
index 0000000000..1671f27e0b
--- /dev/null
+++ b/sw/source/filter/writer/writer.cxx
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+
+#include <sot/storage.hxx>
+#include <sfx2/docfile.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <osl/diagnose.h>
+#include <shellio.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <IMark.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <numrule.hxx>
+#include <swerror.h>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+
+using namespace css;
+
+typedef std::multimap<SwNodeOffset, const ::sw::mark::IMark*> SwBookmarkNodeTable;
+
+struct Writer_Impl
+{
+ SvStream * m_pStream;
+
+ std::map<OUString, OUString> maFileNameMap;
+ std::vector<const SvxFontItem*> aFontRemoveLst;
+ SwBookmarkNodeTable aBkmkNodePos;
+
+ Writer_Impl();
+
+ void RemoveFontList( SwDoc& rDoc );
+ void InsertBkmk( const ::sw::mark::IMark& rBkmk );
+};
+
+Writer_Impl::Writer_Impl()
+ : m_pStream(nullptr)
+{
+}
+
+void Writer_Impl::RemoveFontList( SwDoc& rDoc )
+{
+ for( const auto& rpFontItem : aFontRemoveLst )
+ {
+ rDoc.GetAttrPool().DirectRemoveItemFromPool( *rpFontItem );
+ }
+}
+
+void Writer_Impl::InsertBkmk(const ::sw::mark::IMark& rBkmk)
+{
+ SwNodeOffset nNd = rBkmk.GetMarkPos().GetNodeIndex();
+
+ aBkmkNodePos.emplace( nNd, &rBkmk );
+
+ if(rBkmk.IsExpanded() && rBkmk.GetOtherMarkPos().GetNodeIndex() != nNd)
+ {
+ nNd = rBkmk.GetOtherMarkPos().GetNodeIndex();
+ aBkmkNodePos.emplace( nNd, &rBkmk );
+ }
+}
+
+/*
+ * This module is the central collection point for all writer-filters
+ * and is a DLL !
+ *
+ * So that the Writer can work with different writers, the output-functions
+ * of the content carrying objects have to be mapped to the various
+ * output-functions.
+ *
+ * For that, to inquire its output function, every object can be gripped
+ * via the which-value in a table.
+ * These functions are available in the corresponding Writer-DLL's.
+ */
+
+Writer::Writer()
+ : m_pImpl(std::make_unique<Writer_Impl>())
+ , m_pOrigFileName(nullptr), m_pDoc(nullptr), m_pOrigPam(nullptr)
+ , m_bHideDeleteRedlines(false)
+{
+ m_bWriteAll = m_bShowProgress = m_bUCS2_WithStartChar = true;
+ m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR =
+ m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock =
+ m_bOrganizerMode = false;
+ m_bExportParagraphNumbering = true;
+}
+
+Writer::~Writer()
+{
+}
+
+/*
+ * Document Interface Access
+ */
+IDocumentSettingAccess& Writer::getIDocumentSettingAccess() { return m_pDoc->getIDocumentSettingAccess(); }
+const IDocumentSettingAccess& Writer::getIDocumentSettingAccess() const { return m_pDoc->getIDocumentSettingAccess(); }
+IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() { return m_pDoc->getIDocumentStylePoolAccess(); }
+const IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() const { return m_pDoc->getIDocumentStylePoolAccess(); }
+
+void Writer::ResetWriter()
+{
+ m_pImpl->RemoveFontList( *m_pDoc );
+ m_pImpl.reset(new Writer_Impl);
+
+ if( m_pCurrentPam )
+ {
+ while (m_pCurrentPam->GetNext() != m_pCurrentPam.get())
+ delete m_pCurrentPam->GetNext();
+ m_pCurrentPam.reset();
+ }
+ m_pCurrentPam = nullptr;
+ m_pOrigFileName = nullptr;
+ m_pDoc = nullptr;
+
+ m_bShowProgress = m_bUCS2_WithStartChar = true;
+ m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR =
+ m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock =
+ m_bOrganizerMode = false;
+}
+
+bool Writer::CopyNextPam( SwPaM ** ppPam )
+{
+ if( (*ppPam)->GetNext() == m_pOrigPam )
+ {
+ *ppPam = m_pOrigPam; // set back to the beginning pam
+ return false; // end of the ring
+ }
+
+ // otherwise copy the next value from the next Pam
+ *ppPam = (*ppPam)->GetNext();
+
+ *m_pCurrentPam->GetPoint() = *(*ppPam)->Start();
+ *m_pCurrentPam->GetMark() = *(*ppPam)->End();
+
+ return true;
+}
+
+// search the next Bookmark-Position from the Bookmark-Table
+
+sal_Int32 Writer::FindPos_Bkmk(const SwPosition& rPos) const
+{
+ const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
+ const IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findFirstBookmarkStartsAfter(rPos);
+ if(ppBkmk != pMarkAccess->getBookmarksEnd())
+ return ppBkmk - pMarkAccess->getBookmarksBegin();
+ return -1;
+}
+
+std::shared_ptr<SwUnoCursor>
+Writer::NewUnoCursor(SwDoc & rDoc, SwNodeOffset const nStartIdx, SwNodeOffset const nEndIdx)
+{
+ SwNodes *const pNds = &rDoc.GetNodes();
+
+ SwNodeIndex aStt( *pNds, nStartIdx );
+ SwContentNode* pCNode = aStt.GetNode().GetContentNode();
+ if( !pCNode && nullptr == pNds->GoNext( &aStt ) )
+ {
+ OSL_FAIL( "No more ContentNode at StartPos" );
+ }
+
+ auto const pNew = rDoc.CreateUnoCursor(SwPosition(aStt), false);
+ pNew->SetMark();
+ aStt = nEndIdx;
+ pCNode = aStt.GetNode().GetContentNode();
+ if (!pCNode)
+ pCNode = SwNodes::GoPrevious(&aStt);
+ assert(pCNode && "No more ContentNode at StartPos");
+ pNew->GetPoint()->AssignEndIndex( *pCNode );
+ return pNew;
+}
+
+// Stream-specific
+SvStream& Writer::Strm()
+{
+ assert(m_pImpl->m_pStream && "Oh-oh. Writer with no Stream!");
+ return *m_pImpl->m_pStream;
+}
+
+void Writer::SetStream(SvStream *const pStream)
+{
+ m_pImpl->m_pStream = pStream;
+}
+
+ErrCodeMsg Writer::Write( SwPaM& rPaM, SvStream& rStrm, const OUString* pFName )
+{
+ if ( IsStgWriter() )
+ {
+ ErrCodeMsg nResult = ERRCODE_ABORT;
+ try
+ {
+ tools::SvRef<SotStorage> aRef = new SotStorage( rStrm );
+ nResult = Write( rPaM, *aRef, pFName );
+ if ( nResult == ERRCODE_NONE )
+ aRef->Commit();
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("sw", "Writer::Write caught");
+ }
+ return nResult;
+ }
+
+ m_pDoc = &rPaM.GetDoc();
+ m_pOrigFileName = pFName;
+ m_pImpl->m_pStream = &rStrm;
+
+ // Copy PaM, so that it can be modified
+ m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false);
+ m_pCurrentPam->SetMark();
+ *m_pCurrentPam->GetPoint() = *rPaM.Start();
+ // for comparison secure to the current Pam
+ m_pOrigPam = &rPaM;
+
+ ErrCode nRet = WriteStream();
+
+ ResetWriter();
+
+ return nRet;
+}
+
+void Writer::SetupFilterOptions(SfxMedium& /*rMedium*/)
+{}
+
+ErrCodeMsg Writer::Write( SwPaM& rPam, SfxMedium& rMedium, const OUString* pFileName )
+{
+ SetupFilterOptions(rMedium);
+ // This method must be overridden in SwXMLWriter a storage from medium will be used there.
+ // The microsoft format can write to storage but the storage will be based on the stream.
+ return Write( rPam, *rMedium.GetOutStream(), pFileName );
+}
+
+ErrCodeMsg Writer::Write( SwPaM& /*rPam*/, SotStorage&, const OUString* )
+{
+ OSL_ENSURE( false, "Write in Storages on a stream?" );
+ return ERR_SWG_WRITE_ERROR;
+}
+
+ErrCodeMsg Writer::Write( SwPaM&, const uno::Reference < embed::XStorage >&, const OUString*, SfxMedium* )
+{
+ OSL_ENSURE( false, "Write in Storages on a stream?" );
+ return ERR_SWG_WRITE_ERROR;
+}
+
+bool Writer::CopyLocalFileToINet( OUString& rFileNm )
+{
+ if( !m_pOrigFileName ) // can be happen, by example if we
+ return false; // write into the clipboard
+
+ bool bRet = false;
+ INetURLObject aFileUrl( rFileNm ), aTargetUrl( *m_pOrigFileName );
+
+ if (!(INetProtocol::File == aFileUrl.GetProtocol()
+ && (INetProtocol::Http == aTargetUrl.GetProtocol()
+ || INetProtocol::Https == aTargetUrl.GetProtocol()
+ || INetProtocol::VndSunStarWebdav == aTargetUrl.GetProtocol()
+ || INetProtocol::Smb == aTargetUrl.GetProtocol()
+ || INetProtocol::Sftp == aTargetUrl.GetProtocol()
+ || INetProtocol::Cmis == aTargetUrl.GetProtocol())))
+ {
+ return bRet;
+ }
+
+ // has the file been moved?
+ std::map<OUString, OUString>::iterator it = m_pImpl->maFileNameMap.find( rFileNm );
+ if ( it != m_pImpl->maFileNameMap.end() )
+ {
+ rFileNm = it->second;
+ return true;
+ }
+
+ OUString aSrc = rFileNm;
+ OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName();
+
+ SfxMedium aSrcFile( aSrc, StreamMode::READ );
+ SfxMedium aDstFile( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE );
+
+ aDstFile.GetOutStream()->WriteStream( *aSrcFile.GetInStream() );
+
+ aSrcFile.Close();
+ aDstFile.Commit();
+
+ bRet = ERRCODE_NONE == aDstFile.GetErrorIgnoreWarning();
+
+ if( bRet )
+ {
+ m_pImpl->maFileNameMap.insert( std::make_pair( aSrc, aDest ) );
+ rFileNm = aDest;
+ }
+
+ return bRet;
+}
+
+void Writer::PutNumFormatFontsInAttrPool()
+{
+ // then there are a few fonts in the NumRules
+ // These put into the Pool. After this does they have a RefCount > 1
+ // it can be removed - it is already in the Pool
+ SfxItemPool& rPool = m_pDoc->GetAttrPool();
+ const SwNumRuleTable& rListTable = m_pDoc->GetNumRuleTable();
+ const SwNumFormat* pFormat;
+ const vcl::Font* pDefFont = &numfunc::GetDefBulletFont();
+ bool bCheck = false;
+
+ for( size_t nGet = rListTable.size(); nGet; )
+ {
+ SwNumRule const*const pRule = rListTable[ --nGet ];
+ if (m_pDoc->IsUsed(*pRule))
+ {
+ for( sal_uInt8 nLvl = 0; nLvl < MAXLEVEL; ++nLvl )
+ {
+ if( SVX_NUM_CHAR_SPECIAL == (pFormat = &pRule->Get( nLvl ))->GetNumberingType() ||
+ SVX_NUM_BITMAP == pFormat->GetNumberingType() )
+ {
+ std::optional<vcl::Font> pFont = pFormat->GetBulletFont();
+ if( !pFont )
+ pFont = *pDefFont;
+
+ if( bCheck )
+ {
+ if( *pFont == *pDefFont )
+ continue;
+ }
+ else if( *pFont == *pDefFont )
+ bCheck = true;
+
+ AddFontItem( rPool, SvxFontItem( pFont->GetFamilyType(),
+ pFont->GetFamilyName(), pFont->GetStyleName(),
+ pFont->GetPitch(), pFont->GetCharSet(), RES_CHRATR_FONT ));
+ }
+ }
+ }
+ }
+}
+
+void Writer::PutEditEngFontsInAttrPool()
+{
+ SfxItemPool& rPool = m_pDoc->GetAttrPool();
+ if( rPool.GetSecondaryPool() )
+ {
+ AddFontItems_( rPool, EE_CHAR_FONTINFO );
+ AddFontItems_( rPool, EE_CHAR_FONTINFO_CJK );
+ AddFontItems_( rPool, EE_CHAR_FONTINFO_CTL );
+ }
+}
+
+void Writer::AddFontItems_( SfxItemPool& rPool, sal_uInt16 nW )
+{
+ const SvxFontItem* pFont = static_cast<const SvxFontItem*>(&rPool.GetDefaultItem( nW ));
+ AddFontItem( rPool, *pFont );
+
+ pFont = static_cast<const SvxFontItem*>(rPool.GetPoolDefaultItem( nW ));
+ if( nullptr != pFont )
+ AddFontItem( rPool, *pFont );
+
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nW))
+ AddFontItem( rPool, *static_cast<const SvxFontItem*>(pItem) );
+}
+
+void Writer::AddFontItem( SfxItemPool& rPool, const SvxFontItem& rFont )
+{
+ const SvxFontItem* pItem;
+ if( RES_CHRATR_FONT != rFont.Which() )
+ {
+ SvxFontItem aFont( rFont );
+ aFont.SetWhich( RES_CHRATR_FONT );
+ pItem = &rPool.DirectPutItemInPool( aFont );
+ }
+ else
+ pItem = &rPool.DirectPutItemInPool( rFont );
+
+ if( 1 < pItem->GetRefCount() )
+ rPool.DirectRemoveItemFromPool( *pItem );
+ else
+ {
+ m_pImpl->aFontRemoveLst.push_back( pItem );
+ }
+}
+
+// build a bookmark table, which is sort by the node position. The
+// OtherPos of the bookmarks also inserted.
+void Writer::CreateBookmarkTable()
+{
+ const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess();
+ for(IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getBookmarksBegin();
+ ppBkmk != pMarkAccess->getBookmarksEnd();
+ ++ppBkmk)
+ {
+ m_pImpl->InsertBkmk(**ppBkmk);
+ }
+}
+
+// search all Bookmarks in the range and return it in the Array
+bool Writer::GetBookmarks(const SwContentNode& rNd, sal_Int32 nStt,
+ sal_Int32 nEnd, std::vector< const ::sw::mark::IMark* >& rArr)
+{
+ OSL_ENSURE( rArr.empty(), "there are still entries available" );
+
+ SwNodeOffset nNd = rNd.GetIndex();
+ std::pair<SwBookmarkNodeTable::const_iterator, SwBookmarkNodeTable::const_iterator> aIterPair
+ = m_pImpl->aBkmkNodePos.equal_range( nNd );
+ if( aIterPair.first != aIterPair.second )
+ {
+ // there exist some bookmarks, search now all which is in the range
+ if( !nStt && nEnd == rNd.Len() )
+ // all
+ for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it )
+ rArr.push_back( it->second );
+ else
+ {
+ for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it )
+ {
+ const ::sw::mark::IMark& rBkmk = *(it->second);
+ sal_Int32 nContent;
+ if( rBkmk.GetMarkPos().GetNode() == rNd &&
+ (nContent = rBkmk.GetMarkPos().GetContentIndex() ) >= nStt &&
+ nContent < nEnd )
+ {
+ rArr.push_back( &rBkmk );
+ }
+ else if( rBkmk.IsExpanded() &&
+ (rNd == rBkmk.GetOtherMarkPos().GetNode()) &&
+ (nContent = rBkmk.GetOtherMarkPos().GetContentIndex()) >= nStt &&
+ nContent < nEnd )
+ {
+ rArr.push_back( &rBkmk );
+ }
+ }
+ }
+ }
+ return !rArr.empty();
+}
+
+// Storage-specific
+ErrCode StgWriter::WriteStream()
+{
+ OSL_ENSURE( false, "Write in Storages on a stream?" );
+ return ERR_SWG_WRITE_ERROR;
+}
+
+ErrCodeMsg StgWriter::Write( SwPaM& rPaM, SotStorage& rStg, const OUString* pFName )
+{
+ SetStream(nullptr);
+ m_pStg = &rStg;
+ m_pDoc = &rPaM.GetDoc();
+ m_pOrigFileName = pFName;
+
+ // Copy PaM, so that it can be modified
+ m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false);
+ m_pCurrentPam->SetMark();
+ *m_pCurrentPam->GetPoint() = *rPaM.Start();
+ // for comparison secure to the current Pam
+ m_pOrigPam = &rPaM;
+
+ ErrCodeMsg nRet = WriteStorage();
+
+ m_pStg = nullptr;
+ ResetWriter();
+
+ return nRet;
+}
+
+ErrCodeMsg StgWriter::Write( SwPaM& rPaM, const uno::Reference < embed::XStorage >& rStg, const OUString* pFName, SfxMedium* pMedium )
+{
+ SetStream(nullptr);
+ m_pStg = nullptr;
+ m_xStg = rStg;
+ m_pDoc = &rPaM.GetDoc();
+ m_pOrigFileName = pFName;
+
+ // Copy PaM, so that it can be modified
+ m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false);
+ m_pCurrentPam->SetMark();
+ *m_pCurrentPam->GetPoint() = *rPaM.Start();
+ // for comparison secure to the current Pam
+ m_pOrigPam = &rPaM;
+
+ ErrCodeMsg nRet = pMedium ? WriteMedium( *pMedium ) : WriteStorage();
+
+ m_pStg = nullptr;
+ ResetWriter();
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/writer/wrt_fn.cxx b/sw/source/filter/writer/wrt_fn.cxx
new file mode 100644
index 0000000000..e566aab4ec
--- /dev/null
+++ b/sw/source/filter/writer/wrt_fn.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+#include <osl/diagnose.h>
+
+// tdf#94088 SdrAllFillAttributesHelper needed
+#include <svx/unobrushitemhelper.hxx>
+
+#include <shellio.hxx>
+#include <wrt_fn.hxx>
+#include <node.hxx>
+
+SwHTMLWriter& Out( const SwAttrFnTab pTab, const SfxPoolItem& rHt, SwHTMLWriter & rWrt )
+{
+ sal_uInt16 nId = rHt.Which();
+ OSL_ENSURE( nId < POOLATTR_END && nId >= POOLATTR_BEGIN, "SwAttrFnTab::Out()" );
+ FnAttrOut pOut = pTab[ nId - RES_CHRATR_BEGIN];
+ if( nullptr != pOut )
+ (*pOut)( rWrt, rHt );
+ return rWrt;
+
+}
+
+SwHTMLWriter& Out_SfxItemSet( const SwAttrFnTab pTab, SwHTMLWriter& rWrt,
+ const SfxItemSet& rSet, bool bDeep )
+{
+ // at first give the own attributes out
+ const SfxItemPool& rPool = *rSet.GetPool();
+ const SfxItemSet* pSet = &rSet;
+ if( !pSet->Count() ) // Optimizing - empty Sets
+ {
+ if( !bDeep )
+ return rWrt;
+ while( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() )
+ ;
+ if( !pSet )
+ return rWrt;
+ }
+ const SfxPoolItem* pItem(nullptr);
+ FnAttrOut pOut;
+
+ // tdf#94088 check if any FillAttribute is used [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ // while processing the items
+ bool bFillItemUsed = false;
+
+ if( !bDeep || !pSet->GetParent() )
+ {
+ OSL_ENSURE( rSet.Count(), "It has been handled already, right?" );
+ SfxItemIter aIter( *pSet );
+ pItem = aIter.GetCurItem();
+ do {
+ // pTab only covers POOLATTR_BEGIN..POOLATTR_END.
+ if( pItem->Which() <= POOLATTR_END )
+ {
+ pOut = pTab[ pItem->Which() - RES_CHRATR_BEGIN];
+ if( nullptr != pOut )
+ {
+ (*pOut)( rWrt, *pItem );
+ }
+ }
+ else if(XATTR_FILLSTYLE == pItem->Which())
+ {
+ bFillItemUsed = true;
+ }
+ } while ((pItem = aIter.NextItem()));
+ }
+ else
+ {
+ SfxWhichIter aIter( *pSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ if( SfxItemState::SET == aIter.GetItemState( bDeep, &pItem ) &&
+ ( *pItem != rPool.GetDefaultItem( nWhich )
+ || ( pSet->GetParent() &&
+ *pItem != pSet->GetParent()->Get( nWhich ))
+ ))
+ {
+ pOut = pTab[ nWhich - RES_CHRATR_BEGIN];
+ if( nullptr != pOut )
+ {
+ (*pOut)( rWrt, *pItem );
+ }
+ else if(XATTR_FILLSTYLE == pItem->Which())
+ {
+ bFillItemUsed = true;
+ }
+ }
+ nWhich = aIter.NextWhich();
+ }
+ }
+
+ if(bFillItemUsed)
+ {
+ // tdf#94088 if used, construct a SvxBrushItem and export it using the
+ // existing mechanisms.
+ // This is the right place in the future if the adapted fill attributes
+ // may be handled more directly in HTML export to handle them.
+ const std::unique_ptr<SvxBrushItem> aSvxBrushItem(getSvxBrushItemFromSourceSet(*pSet, RES_BACKGROUND, bDeep));
+
+ pOut = pTab[RES_BACKGROUND - RES_CHRATR_BEGIN];
+ if( nullptr != pOut )
+ {
+ (*pOut)( rWrt, *aSvxBrushItem );
+ }
+ }
+
+ return rWrt;
+}
+
+Writer& Out( const SwNodeFnTab pTab, SwNode& rNode, Writer & rWrt )
+{
+ // It must be a ContentNode!
+ SwContentNode * pCNd = rNode.GetContentNode();
+ if( !pCNd )
+ return rWrt;
+
+ sal_uInt16 nId = RES_TXTNODE;
+ switch (pCNd->GetNodeType())
+ {
+ case SwNodeType::Text:
+ nId = RES_TXTNODE;
+ break;
+ case SwNodeType::Grf:
+ nId = RES_GRFNODE;
+ break;
+ case SwNodeType::Ole:
+ nId = RES_OLENODE;
+ break;
+ default:
+ OSL_FAIL("What kind of node is it now?");
+ break;
+ }
+ FnNodeOut pOut = pTab[ nId - RES_NODE_BEGIN ];
+ if( nullptr != pOut )
+ (*pOut)( rWrt, *pCNd );
+ return rWrt;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/writer/wrtswtbl.cxx b/sw/source/filter/writer/wrtswtbl.cxx
new file mode 100644
index 0000000000..223ccf9626
--- /dev/null
+++ b/sw/source/filter/writer/wrtswtbl.cxx
@@ -0,0 +1,866 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <tools/fract.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <wrtswtbl.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <htmltbl.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+sal_Int16 SwWriteTableCell::GetVertOri() const
+{
+ sal_Int16 eCellVertOri = text::VertOrientation::TOP;
+ if( m_pBox->GetSttNd() )
+ {
+ const SfxItemSet& rItemSet = m_pBox->GetFrameFormat()->GetAttrSet();
+ if( const SwFormatVertOrient *pItem = rItemSet.GetItemIfSet( RES_VERT_ORIENT, false ) )
+ {
+ sal_Int16 eBoxVertOri = pItem->GetVertOrient();
+ if( text::VertOrientation::CENTER==eBoxVertOri || text::VertOrientation::BOTTOM==eBoxVertOri)
+ eCellVertOri = eBoxVertOri;
+ }
+ }
+
+ return eCellVertOri;
+}
+
+SwWriteTableRow::SwWriteTableRow( tools::Long nPosition, bool bUseLayoutHeights )
+ : m_pBackground(nullptr), m_nPos(nPosition), mbUseLayoutHeights(bUseLayoutHeights),
+ m_bTopBorder(true), m_bBottomBorder(true)
+{
+}
+
+SwWriteTableCell *SwWriteTableRow::AddCell( const SwTableBox *pBox,
+ sal_uInt16 nRow, sal_uInt16 nCol,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ tools::Long nHeight,
+ const SvxBrushItem *pBackgroundBrush )
+{
+ SwWriteTableCell *pCell =
+ new SwWriteTableCell( pBox, nRow, nCol, nRowSpan, nColSpan,
+ nHeight, pBackgroundBrush );
+ m_Cells.push_back(std::unique_ptr<SwWriteTableCell>(pCell));
+
+ return pCell;
+}
+
+SwWriteTableCol::SwWriteTableCol(sal_uInt32 nPosition)
+ : m_nPos(nPosition), m_nWidthOpt(0), m_bRelWidthOpt(false),
+ m_bLeftBorder(true), m_bRightBorder(true)
+{
+}
+
+sal_uInt32 SwWriteTable::GetBoxWidth( const SwTableBox *pBox )
+{
+ const SwFrameFormat *pFormat = pBox->GetFrameFormat();
+ const SwFormatFrameSize& aFrameSize=
+ pFormat->GetFormatAttr( RES_FRM_SIZE );
+
+ return sal::static_int_cast<sal_uInt32>(aFrameSize.GetSize().Width());
+}
+
+tools::Long SwWriteTable::GetLineHeight( const SwTableLine *pLine )
+{
+#ifdef DBG_UTIL
+ bool bOldGetLineHeightCalled = m_bGetLineHeightCalled;
+ m_bGetLineHeightCalled = true;
+#endif
+
+ tools::Long nHeight = 0;
+ if( m_bUseLayoutHeights )
+ {
+ // At first we try to get the height of the layout.
+ bool bLayoutAvailable = false;
+ nHeight = pLine->GetTableLineHeight(bLayoutAvailable);
+ if( nHeight > 0 )
+ return nHeight;
+
+ // If no layout is found, we assume that the heights are fixed.
+ // #i60390# - in some cases we still want to continue
+ // to use the layout heights even if one of the rows has a height of 0
+ // ('hidden' rows)
+ m_bUseLayoutHeights = bLayoutAvailable;
+
+#ifdef DBG_UTIL
+ SAL_WARN_IF( !bLayoutAvailable && bOldGetLineHeightCalled, "sw", "Layout invalid?" );
+#endif
+ }
+
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ if( pBox->GetSttNd() )
+ {
+ if( nHeight < ROW_DFLT_HEIGHT )
+ nHeight = ROW_DFLT_HEIGHT;
+ }
+ else
+ {
+ tools::Long nTmp = 0;
+ const SwTableLines &rLines = pBox->GetTabLines();
+ for( size_t nLine=0; nLine<rLines.size(); nLine++ )
+ {
+ nTmp += GetLineHeight( rLines[nLine] );
+ }
+ if( nHeight < nTmp )
+ nHeight = nTmp;
+ }
+ }
+
+ return nHeight;
+}
+
+tools::Long SwWriteTable::GetLineHeight( const SwTableBox *pBox )
+{
+ const SwTableLine *pLine = pBox->GetUpper();
+
+ if( !pLine )
+ return 0;
+
+ const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat();
+ const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet();
+
+ tools::Long nHeight = 0;
+ if( const SwFormatFrameSize* pItem = rItemSet.GetItemIfSet( RES_FRM_SIZE ) )
+ nHeight = pItem->GetHeight();
+
+ return nHeight;
+}
+
+const SvxBrushItem *SwWriteTable::GetLineBrush( const SwTableBox *pBox,
+ SwWriteTableRow *pRow )
+{
+ const SwTableLine *pLine = pBox->GetUpper();
+
+ while( pLine )
+ {
+ const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat();
+ const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet();
+
+ if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ if( !pLine->GetUpper() )
+ {
+ if( !pRow->GetBackground() )
+ pRow->SetBackground( pItem );
+ pItem = nullptr;
+ }
+
+ return pItem;
+ }
+
+ pBox = pLine->GetUpper();
+ pLine = pBox ? pBox->GetUpper() : nullptr;
+ }
+
+ return nullptr;
+}
+
+void SwWriteTable::MergeBorders( const SvxBorderLine* pBorderLine,
+ bool bTable )
+{
+ if( Color(ColorTransparency, 0xffffffff) == m_nBorderColor )
+ {
+ if( !pBorderLine->GetColor().IsRGBEqual( COL_GRAY ) )
+ m_nBorderColor = pBorderLine->GetColor();
+ }
+
+ if( !m_bCollectBorderWidth )
+ return;
+
+ const sal_uInt16 nOutWidth = pBorderLine->GetOutWidth();
+ if( bTable )
+ {
+ if( nOutWidth && (!m_nBorder || nOutWidth < m_nBorder) )
+ m_nBorder = nOutWidth;
+ }
+ else
+ {
+ if( nOutWidth && (!m_nInnerBorder || nOutWidth < m_nInnerBorder) )
+ m_nInnerBorder = nOutWidth;
+ }
+
+ const sal_uInt16 nDist = pBorderLine->GetInWidth() ? pBorderLine->GetDistance()
+ : 0;
+ if( nDist && (!m_nCellSpacing || nDist < m_nCellSpacing) )
+ m_nCellSpacing = nDist;
+}
+
+sal_uInt16 SwWriteTable::MergeBoxBorders( const SwTableBox *pBox,
+ size_t const nRow, size_t const nCol,
+ sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
+ sal_uInt16& rTopBorder,
+ sal_uInt16 &rBottomBorder )
+{
+ sal_uInt16 nBorderMask = 0;
+
+ const SwFrameFormat *pFrameFormat = pBox->GetFrameFormat();
+ const SvxBoxItem& rBoxItem = pFrameFormat->GetFormatAttr( RES_BOX );
+
+ if( rBoxItem.GetTop() )
+ {
+ nBorderMask |= 1;
+ MergeBorders( rBoxItem.GetTop(), nRow==0 );
+ rTopBorder = rBoxItem.GetTop()->GetOutWidth();
+ }
+
+ if( rBoxItem.GetLeft() )
+ {
+ nBorderMask |= 4;
+ MergeBorders( rBoxItem.GetLeft(), nCol==0 );
+ }
+
+ if( rBoxItem.GetBottom() )
+ {
+ nBorderMask |= 2;
+ MergeBorders( rBoxItem.GetBottom(), nRow+nRowSpan==m_aRows.size() );
+ rBottomBorder = rBoxItem.GetBottom()->GetOutWidth();
+ }
+
+ if( rBoxItem.GetRight() )
+ {
+ nBorderMask |= 8;
+ MergeBorders( rBoxItem.GetRight(), nCol+nColSpan==m_aCols.size() );
+ }
+
+ // If any distance is set, the smallest one is used. This holds for
+ // the four distance of a box as well as for the distances of different
+ // boxes.
+ if( m_bCollectBorderWidth )
+ {
+ sal_uInt16 nDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
+ if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) )
+ m_nCellPadding = nDist;
+ nDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
+ if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) )
+ m_nCellPadding = nDist;
+ nDist = rBoxItem.GetDistance( SvxBoxItemLine::LEFT );
+ if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) )
+ m_nCellPadding = nDist;
+ nDist = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT );
+ if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) )
+ m_nCellPadding = nDist;
+ }
+
+ return nBorderMask;
+}
+
+sal_uInt32 SwWriteTable::GetRawWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const
+{
+ sal_uInt32 nWidth = m_aCols[nCol+nColSpan-1]->GetPos();
+ if( nCol > 0 )
+ nWidth = nWidth - m_aCols[nCol-1]->GetPos();
+
+ return nWidth;
+}
+
+sal_uInt16 SwWriteTable::GetLeftSpace( sal_uInt16 nCol ) const
+{
+ sal_uInt16 nSpace = m_nCellPadding + m_nCellSpacing;
+
+ // Additional subtract the line thickness in the first column.
+ if( nCol==0 )
+ {
+ nSpace = nSpace + m_nLeftSub;
+
+ const SwWriteTableCol *pCol = m_aCols[nCol].get();
+ if( pCol->HasLeftBorder() )
+ nSpace = nSpace + m_nBorder;
+ }
+
+ return nSpace;
+}
+
+sal_uInt16
+SwWriteTable::GetRightSpace(size_t const nCol, sal_uInt16 nColSpan) const
+{
+ sal_uInt16 nSpace = m_nCellPadding;
+
+ // Additional subtract in the last column CELLSPACING and
+ // line thickness once again.
+ if( nCol+nColSpan==m_aCols.size() )
+ {
+ nSpace += (m_nCellSpacing + m_nRightSub);
+
+ const SwWriteTableCol *pCol = m_aCols[nCol+nColSpan-1].get();
+ if( pCol->HasRightBorder() )
+ nSpace = nSpace + m_nBorder;
+ }
+
+ return nSpace;
+}
+
+sal_uInt16 SwWriteTable::GetAbsWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const
+{
+ sal_uInt32 nWidth = GetRawWidth( nCol, nColSpan );
+ if( m_nBaseWidth != m_nTabWidth )
+ {
+ nWidth *= m_nTabWidth;
+ nWidth /= m_nBaseWidth;
+ }
+
+ nWidth -= GetLeftSpace( nCol ) + GetRightSpace( nCol, nColSpan );
+
+ OSL_ENSURE( nWidth > 0, "Column Width <= 0. OK?" );
+ return nWidth > 0 ? o3tl::narrowing<sal_uInt16>(nWidth) : 0;
+}
+
+sal_uInt16 SwWriteTable::GetRelWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const
+{
+ tools::Long nWidth = GetRawWidth( nCol, nColSpan );
+
+ return o3tl::narrowing<sal_uInt16>(static_cast<tools::Long>(Fraction( nWidth*256 + GetBaseWidth()/2,
+ GetBaseWidth() )));
+}
+
+sal_uInt16 SwWriteTable::GetPercentWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const
+{
+ tools::Long nWidth = GetRawWidth( nCol, nColSpan );
+
+ // Looks funny, but is nothing more than
+ // [(100 * nWidth) + .5] without rounding errors
+ return o3tl::narrowing<sal_uInt16>(static_cast<tools::Long>(Fraction( nWidth*100 + GetBaseWidth()/2,
+ GetBaseWidth() )));
+}
+
+tools::Long SwWriteTable::GetAbsHeight(tools::Long nRawHeight, size_t const nRow,
+ sal_uInt16 nRowSpan ) const
+{
+ nRawHeight -= (2*m_nCellPadding + m_nCellSpacing);
+
+ // Additional subtract in the first column CELLSPACING and
+ // line thickness once again.
+ const SwWriteTableRow *pRow = nullptr;
+ if( nRow==0 )
+ {
+ nRawHeight -= m_nCellSpacing;
+ pRow = m_aRows[nRow].get();
+ if( pRow->HasTopBorder() )
+ nRawHeight -= m_nBorder;
+ }
+
+ // Subtract the line thickness in the last column
+ if( nRow+nRowSpan==m_aRows.size() )
+ {
+ if( !pRow || nRowSpan > 1 )
+ pRow = m_aRows[nRow+nRowSpan-1].get();
+ if( pRow->HasBottomBorder() )
+ nRawHeight -= m_nBorder;
+ }
+
+ OSL_ENSURE( nRawHeight > 0, "Row Height <= 0. OK?" );
+ return std::max<tools::Long>(nRawHeight, 0);
+}
+
+bool SwWriteTable::ShouldExpandSub(const SwTableBox *pBox, bool /*bExpandedBefore*/,
+ sal_uInt16 nDepth) const
+{
+ return !pBox->GetSttNd() && nDepth > 0;
+}
+
+// FIXME: the degree of coupling between this method and
+// FillTableRowsCols which is called immediately afterwards
+// is -extremely- unpleasant and potentially problematic.
+
+void SwWriteTable::CollectTableRowsCols( tools::Long nStartRPos,
+ sal_uInt32 nStartCPos,
+ tools::Long nParentLineHeight,
+ sal_uInt32 nParentLineWidth,
+ const SwTableLines& rLines,
+ sal_uInt16 nDepth )
+{
+ bool bSubExpanded = false;
+ const SwTableLines::size_type nLines = rLines.size();
+
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nEndCPos = 0;
+#endif
+
+ tools::Long nRPos = nStartRPos;
+ for( SwTableLines::size_type nLine = 0; nLine < nLines; ++nLine )
+ {
+ /*const*/ SwTableLine *pLine = rLines[nLine];
+
+ tools::Long nOldRPos = nRPos;
+
+ if( nLine < nLines-1 || nParentLineHeight==0 )
+ {
+ tools::Long nLineHeight = GetLineHeight( pLine );
+ nRPos += nLineHeight;
+ if( nParentLineHeight && nStartRPos + nParentLineHeight <= nRPos )
+ {
+ /* If you have corrupt line height information, e.g. breaking rows in complex table
+ layout, you may run into this robust code.
+ It's not allowed that subrows leaves their parentrow. If this would happen the line
+ height of subrow is reduced to a part of the remaining height */
+ OSL_FAIL( "Corrupt line height I" );
+ nRPos -= nLineHeight;
+ nLineHeight = nStartRPos + nParentLineHeight - nRPos; // remaining parent height
+ nLineHeight /= nLines - nLine; // divided through the number of remaining sub rows
+ nRPos += nLineHeight;
+ }
+ std::unique_ptr<SwWriteTableRow> pRow(new SwWriteTableRow( nRPos, m_bUseLayoutHeights));
+ m_aRows.insert( std::move(pRow) );
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 0
+ tools::Long nCheckPos = nRPos + GetLineHeight( pLine );
+#endif
+ nRPos = nStartRPos + nParentLineHeight;
+#if OSL_DEBUG_LEVEL > 0
+ SwWriteTableRow aSrchRow( nRPos, m_bUseLayoutHeights );
+ OSL_ENSURE( std::find_if(m_aRows.begin(), m_aRows.end(),
+ [&](std::unique_ptr<SwWriteTableRow> const & p)
+ { return *p == aSrchRow; }) != m_aRows.end(), "Parent-Row not found" );
+ SwWriteTableRow aRowCheckPos(nCheckPos,m_bUseLayoutHeights);
+ SwWriteTableRow aRowRPos(nRPos,m_bUseLayoutHeights);
+ OSL_ENSURE( !m_bUseLayoutHeights ||
+ aRowCheckPos == aRowRPos,
+ "Height of the rows does not correspond with the parent" );
+#endif
+ }
+
+ // If necessary insert a column for all boxes of the row
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ const SwTableBoxes::size_type nBoxes = rBoxes.size();
+
+ sal_uInt32 nCPos = nStartCPos;
+ for( SwTableBoxes::size_type nBox=0; nBox<nBoxes; ++nBox )
+ {
+ const SwTableBox *pBox = rBoxes[nBox];
+
+ sal_uInt32 nOldCPos = nCPos;
+
+ if( nBox < nBoxes-1 || (nParentLineWidth==0 && nLine==0) )
+ {
+ nCPos = nCPos + GetBoxWidth( pBox );
+ std::unique_ptr<SwWriteTableCol> pCol(new SwWriteTableCol( nCPos ));
+
+ m_aCols.insert( std::move(pCol) );
+
+ if( nBox==nBoxes-1 )
+ {
+ OSL_ENSURE( nLine==0 && nParentLineWidth==0,
+ "Now the parent width will be flattened!" );
+ nParentLineWidth = nCPos-nStartCPos;
+ }
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nCheckPos = nCPos + GetBoxWidth( pBox );
+ if( !nEndCPos )
+ {
+ nEndCPos = nCheckPos;
+ }
+ else
+ {
+ OSL_ENSURE( SwWriteTableCol(nCheckPos) ==
+ SwWriteTableCol(nEndCPos),
+ "Cell includes rows of different widths" );
+ }
+#endif
+ nCPos = nStartCPos + nParentLineWidth;
+
+#if OSL_DEBUG_LEVEL > 0
+ SwWriteTableCol aSrchCol( nCPos );
+ OSL_ENSURE( m_aCols.find( &aSrchCol ) != m_aCols.end(),
+ "Parent-Cell not found" );
+ OSL_ENSURE( SwWriteTableCol(nCheckPos) ==
+ SwWriteTableCol(nCPos),
+ "Width of the cells does not correspond with the parent" );
+#endif
+ }
+
+ if( ShouldExpandSub( pBox, bSubExpanded, nDepth ) )
+ {
+ CollectTableRowsCols( nOldRPos, nOldCPos,
+ nRPos - nOldRPos,
+ nCPos - nOldCPos,
+ pBox->GetTabLines(),
+ nDepth-1 );
+ bSubExpanded = true;
+ }
+ }
+ }
+}
+
+void SwWriteTable::FillTableRowsCols( tools::Long nStartRPos, sal_uInt16 nStartRow,
+ sal_uInt32 nStartCPos, sal_uInt16 nStartCol,
+ tools::Long nParentLineHeight,
+ sal_uInt32 nParentLineWidth,
+ const SwTableLines& rLines,
+ const SvxBrushItem* pParentBrush,
+ sal_uInt16 nDepth,
+ sal_uInt16 nNumOfHeaderRows )
+{
+ const SwTableLines::size_type nLines = rLines.size();
+ bool bSubExpanded = false;
+
+ // Specifying the border
+ tools::Long nRPos = nStartRPos;
+ sal_uInt16 nRow = nStartRow;
+
+ for( SwTableLines::size_type nLine = 0; nLine < nLines; ++nLine )
+ {
+ const SwTableLine *pLine = rLines[nLine];
+
+ // Determine the position of the last covered row
+ tools::Long nOldRPos = nRPos;
+ if( nLine < nLines-1 || nParentLineHeight==0 )
+ {
+ tools::Long nLineHeight = GetLineHeight( pLine );
+ nRPos += nLineHeight;
+ if( nParentLineHeight && nStartRPos + nParentLineHeight <= nRPos )
+ {
+ /* See comment in CollectTableRowCols */
+ OSL_FAIL( "Corrupt line height II" );
+ nRPos -= nLineHeight;
+ nLineHeight = nStartRPos + nParentLineHeight - nRPos; // remaining parent height
+ nLineHeight /= nLines - nLine; // divided through the number of remaining sub rows
+ nRPos += nLineHeight;
+ }
+ }
+ else
+ nRPos = nStartRPos + nParentLineHeight;
+
+ // And their index
+ sal_uInt16 nOldRow = nRow;
+ SwWriteTableRow aSrchRow( nRPos,m_bUseLayoutHeights );
+ SwWriteTableRows::const_iterator it2 = std::find_if(m_aRows.begin(), m_aRows.end(),
+ [&](std::unique_ptr<SwWriteTableRow> const &p)
+ { return *p == aSrchRow; });
+
+ // coupled methods out of sync ...
+ assert( it2 != m_aRows.end() );
+ nRow = it2 - m_aRows.begin();
+
+ OSL_ENSURE( nOldRow <= nRow, "Don't look back!" );
+ if( nOldRow > nRow )
+ {
+ nOldRow = nRow;
+ if( nOldRow )
+ --nOldRow;
+ }
+
+ SwWriteTableRow *pRow = m_aRows[nOldRow].get();
+ SwWriteTableRow *pEndRow = m_aRows[nRow].get();
+ if( nLine+1==nNumOfHeaderRows && nParentLineHeight==0 )
+ m_nHeadEndRow = nRow;
+
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+
+ const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat();
+ const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet();
+
+ tools::Long nHeight = 0;
+ if( const SwFormatFrameSize* pFrameSizeItem = rItemSet.GetItemIfSet( RES_FRM_SIZE ))
+ nHeight = pFrameSizeItem->GetHeight();
+
+ const SvxBrushItem *pBrushItem, *pLineBrush = pParentBrush;
+ if( const SvxBrushItem* pTmpBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ pLineBrush = pTmpBrush;
+
+ // If the row spans the entire table, we can
+ // print out the background to the row. Otherwise
+ // we have to print out into the cell.
+ bool bOutAtRow = !nParentLineWidth;
+ if( !bOutAtRow && nStartCPos==0 )
+ {
+ SwWriteTableCol aCol( nParentLineWidth );
+ bOutAtRow = m_aCols.find( &aCol ) == (m_aCols.end() - 1);
+ }
+ if( bOutAtRow )
+ {
+ pRow->SetBackground( pLineBrush );
+ pBrushItem = nullptr;
+ }
+ else
+ pBrushItem = pLineBrush;
+ }
+ else
+ {
+ pRow->SetBackground( pLineBrush );
+ pBrushItem = nullptr;
+ }
+
+ const SwTableBoxes::size_type nBoxes = rBoxes.size();
+ sal_uInt32 nCPos = nStartCPos;
+ sal_uInt16 nCol = nStartCol;
+
+ for( SwTableBoxes::size_type nBox=0; nBox<nBoxes; ++nBox )
+ {
+ const SwTableBox *pBox = rBoxes[nBox];
+
+ // Determine the position of the last covered column
+ sal_uInt32 nOldCPos = nCPos;
+ if( nBox < nBoxes-1 || (nParentLineWidth==0 && nLine==0) )
+ {
+ nCPos = nCPos + GetBoxWidth( pBox );
+ if( nBox==nBoxes-1 )
+ nParentLineWidth = nCPos - nStartCPos;
+ }
+ else
+ nCPos = nStartCPos + nParentLineWidth;
+
+ // And their index
+ sal_uInt16 nOldCol = nCol;
+ SwWriteTableCol aSrchCol( nCPos );
+ SwWriteTableCols::const_iterator it = m_aCols.find( &aSrchCol );
+ OSL_ENSURE( it != m_aCols.end(), "missing column" );
+ if(it != m_aCols.end())
+ {
+ // if find fails for some nCPos value then it used to set nCol value with size of aCols.
+ nCol = it - m_aCols.begin();
+ }
+
+ if( !ShouldExpandSub( pBox, bSubExpanded, nDepth ) )
+ {
+ sal_uInt16 nRowSpan = nRow - nOldRow + 1;
+
+ // The new table model may have true row span attributes
+ const sal_Int32 nAttrRowSpan = pBox->getRowSpan();
+ if ( 1 < nAttrRowSpan )
+ nRowSpan = o3tl::narrowing<sal_uInt16>(nAttrRowSpan);
+ else if ( nAttrRowSpan < 1 )
+ nRowSpan = 0;
+
+ SAL_WARN_IF(nCol < nOldCol, "sw.filter", "unexpected " << nCol << " < " << nOldCol);
+ sal_uInt16 nColSpan = nCol >= nOldCol ? nCol - nOldCol + 1 : 1;
+ pRow->AddCell( pBox, nOldRow, nOldCol,
+ nRowSpan, nColSpan, nHeight,
+ pBrushItem );
+ nHeight = 0; // The height requires only to be written once
+
+ if( pBox->GetSttNd() )
+ {
+ sal_uInt16 nTopBorder = USHRT_MAX, nBottomBorder = USHRT_MAX;
+ sal_uInt16 nBorderMask = MergeBoxBorders(pBox, nOldRow, nOldCol,
+ nRowSpan, nColSpan, nTopBorder, nBottomBorder);
+
+ // #i30094# add a sanity check here to ensure that
+ // we don't access an invalid aCols[] as &nCol
+ // above can be changed.
+ if (!(nBorderMask & 4) && nOldCol < m_aCols.size())
+ {
+ SwWriteTableCol *pCol = m_aCols[nOldCol].get();
+ OSL_ENSURE(pCol, "No TableCol found, panic!");
+ if (pCol)
+ pCol->m_bLeftBorder = false;
+ }
+
+ if (!(nBorderMask & 8))
+ {
+ SwWriteTableCol *pCol = m_aCols[nCol].get();
+ OSL_ENSURE(pCol, "No TableCol found, panic!");
+ if (pCol)
+ pCol->m_bRightBorder = false;
+ }
+
+ if (!(nBorderMask & 1))
+ pRow->SetTopBorder(false);
+
+ if (!(nBorderMask & 2))
+ pEndRow->SetBottomBorder(false);
+ }
+ }
+ else
+ {
+ FillTableRowsCols( nOldRPos, nOldRow, nOldCPos, nOldCol,
+ nRPos-nOldRPos, nCPos-nOldCPos,
+ pBox->GetTabLines(),
+ pLineBrush, nDepth-1,
+ nNumOfHeaderRows );
+ bSubExpanded = true;
+ }
+
+ nCol++; // The next cell begins in the next column
+ }
+
+ nRow++;
+ }
+}
+
+SwWriteTable::SwWriteTable(const SwTable* pTable, const SwTableLines& rLines, tools::Long nWidth,
+ sal_uInt32 nBWidth, bool bRel, sal_uInt16 nMaxDepth, sal_uInt16 nLSub, sal_uInt16 nRSub, sal_uInt32 nNumOfRowsToRepeat)
+ : m_pTable(pTable), m_nBorderColor(ColorTransparency, sal_uInt32(-1)), m_nCellSpacing(0), m_nCellPadding(0), m_nBorder(0),
+ m_nInnerBorder(0), m_nBaseWidth(nBWidth), m_nHeadEndRow(USHRT_MAX),
+ m_nLeftSub(nLSub), m_nRightSub(nRSub), m_nTabWidth(nWidth), m_bRelWidths(bRel),
+ m_bUseLayoutHeights(true),
+#ifdef DBG_UTIL
+ m_bGetLineHeightCalled(false),
+#endif
+ m_bColTags(true), m_bLayoutExport(false),
+ m_bCollectBorderWidth(true)
+{
+ sal_uInt32 nParentWidth = m_nBaseWidth + m_nLeftSub + m_nRightSub;
+
+ // First the table structure set. Behind the table is in each
+ // case the end of a column
+ std::unique_ptr<SwWriteTableCol> pCol(new SwWriteTableCol( nParentWidth ));
+ m_aCols.insert( std::move(pCol) );
+ m_bUseLayoutHeights = true;
+ CollectTableRowsCols( 0, 0, 0, nParentWidth, rLines, nMaxDepth - 1 );
+
+ // FIXME: awfully GetLineHeight writes to this in its first call
+ // and proceeds to return a rather odd number fdo#62336, we have to
+ // behave identically since the code in FillTableRowsCols duplicates
+ // and is highly coupled to CollectTableRowsCols - sadly.
+ m_bUseLayoutHeights = true;
+ // And now fill with life
+ FillTableRowsCols( 0, 0, 0, 0, 0, nParentWidth, rLines, nullptr, nMaxDepth - 1, static_cast< sal_uInt16 >(nNumOfRowsToRepeat) );
+
+ // Adjust some Twip values to pixel boundaries
+ if( !m_nBorder )
+ m_nBorder = m_nInnerBorder;
+}
+
+SwWriteTable::SwWriteTable(const SwTable* pTable, const SwHTMLTableLayout *pLayoutInfo)
+ : m_pTable(pTable), m_nBorderColor(ColorTransparency, sal_uInt32(-1)), m_nCellSpacing(0), m_nCellPadding(0), m_nBorder(0),
+ m_nInnerBorder(0), m_nBaseWidth(pLayoutInfo->GetWidthOption()), m_nHeadEndRow(0),
+ m_nLeftSub(0), m_nRightSub(0), m_nTabWidth(pLayoutInfo->GetWidthOption()),
+ m_bRelWidths(pLayoutInfo->HasPercentWidthOption()), m_bUseLayoutHeights(false),
+#ifdef DBG_UTIL
+ m_bGetLineHeightCalled(false),
+#endif
+ m_bColTags(pLayoutInfo->HasColTags()), m_bLayoutExport(true),
+ m_bCollectBorderWidth(pLayoutInfo->HaveBordersChanged())
+{
+ if( !m_bCollectBorderWidth )
+ {
+ m_nBorder = pLayoutInfo->GetBorder();
+ m_nCellPadding = pLayoutInfo->GetCellPadding();
+ m_nCellSpacing = pLayoutInfo->GetCellSpacing();
+ }
+
+ const sal_uInt16 nCols = pLayoutInfo->GetColCount();
+ const sal_uInt16 nRows = pLayoutInfo->GetRowCount();
+
+ // First set the table structure.
+ for( sal_uInt16 nCol=0; nCol<nCols; ++nCol )
+ {
+ std::unique_ptr<SwWriteTableCol> pCol(
+ new SwWriteTableCol( (nCol+1)*COL_DFLT_WIDTH ));
+
+ if( m_bColTags )
+ {
+ const SwHTMLTableLayoutColumn *pLayoutCol =
+ pLayoutInfo->GetColumn( nCol );
+ pCol->SetWidthOpt( pLayoutCol->GetWidthOption(),
+ pLayoutCol->IsRelWidthOption() );
+ }
+
+ m_aCols.insert( std::move(pCol) );
+ }
+
+ for( sal_uInt16 nRow=0; nRow<nRows; ++nRow )
+ {
+ std::unique_ptr<SwWriteTableRow> pRow(
+ new SwWriteTableRow( (nRow+1)*ROW_DFLT_HEIGHT, m_bUseLayoutHeights ));
+ m_aRows.insert( std::move(pRow) );
+ }
+
+ // And now fill with life
+ for( sal_uInt16 nRow=0; nRow<nRows; ++nRow )
+ {
+ SwWriteTableRow *pRow = m_aRows[nRow].get();
+
+ bool bHeightExported = false;
+ for( sal_uInt16 nCol=0; nCol<nCols; nCol++ )
+ {
+ const SwHTMLTableLayoutCell *pLayoutCell =
+ pLayoutInfo->GetCell( nRow, nCol );
+
+ const SwHTMLTableLayoutCnts *pLayoutCnts =
+ pLayoutCell->GetContents().get();
+
+ // The cell begins actually a row above or further forward?
+ if( ( nRow>0 && pLayoutCnts == pLayoutInfo->GetCell(nRow-1,nCol)
+ ->GetContents().get() ) ||
+ ( nCol>0 && pLayoutCnts == pLayoutInfo->GetCell(nRow,nCol-1)
+ ->GetContents().get() ) )
+ {
+ continue;
+ }
+
+ const sal_uInt16 nRowSpan = pLayoutCell->GetRowSpan();
+ const sal_uInt16 nColSpan = pLayoutCell->GetColSpan();
+ const SwTableBox *pBox = pLayoutCnts->GetTableBox();
+ OSL_ENSURE( pBox,
+ "Table in Table can not be exported over layout" );
+
+ tools::Long nHeight = bHeightExported ? 0 : GetLineHeight( pBox );
+ const SvxBrushItem *pBrushItem = GetLineBrush( pBox, pRow );
+
+ SwWriteTableCell *pCell =
+ pRow->AddCell( pBox, nRow, nCol, nRowSpan, nColSpan,
+ nHeight, pBrushItem );
+ pCell->SetWidthOpt( pLayoutCell->GetWidthOption(),
+ pLayoutCell->IsPercentWidthOption() );
+
+ sal_uInt16 nTopBorder = USHRT_MAX, nBottomBorder = USHRT_MAX;
+ sal_uInt16 nBorderMask =
+ MergeBoxBorders( pBox, nRow, nCol, nRowSpan, nColSpan,
+ nTopBorder, nBottomBorder );
+
+ SwWriteTableCol *pCol = m_aCols[nCol].get();
+ if( !(nBorderMask & 4) )
+ pCol->m_bLeftBorder = false;
+
+ pCol = m_aCols[nCol+nColSpan-1].get();
+ if( !(nBorderMask & 8) )
+ pCol->m_bRightBorder = false;
+
+ if( !(nBorderMask & 1) )
+ pRow->SetTopBorder(false);
+
+ SwWriteTableRow *pEndRow = m_aRows[nRow+nRowSpan-1].get();
+ if( !(nBorderMask & 2) )
+ pEndRow->SetBottomBorder(false);
+
+ // The height requires only to be written once
+ if( nHeight )
+ bHeightExported = true;
+ }
+ }
+
+ // Adjust some Twip values to pixel boundaries
+ if( m_bCollectBorderWidth && !m_nBorder )
+ m_nBorder = m_nInnerBorder;
+}
+
+SwWriteTable::~SwWriteTable()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/README-rtf.txt b/sw/source/filter/ww8/README-rtf.txt
new file mode 100644
index 0000000000..d92cf587f2
--- /dev/null
+++ b/sw/source/filter/ww8/README-rtf.txt
@@ -0,0 +1,244 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+---------------------------------------------------------------------
+
+Summary of new features in RtfExport
+
+---------------------------------------------------------------------
+
+Miklos Vajna
+
+<vmiklos@vmiklos.hu>
+---------------------------------------------------------------------
+
+Table of Contents
+
+1. Introduction
+
+ 1.1. Terminology
+ 1.2. General
+
+2. List if fixed bugs
+3. List of new features
+
+ 3.1. Nested tables
+ 3.2. Character properties
+ 3.3. Sections
+ 3.4. Graphics
+ 3.5. Bookmarks
+ 3.6. Fields
+ 3.7. Drawing
+ 3.8. Form fields
+ 3.9. OLE objects
+
+4. Changes in the source code outside RTF
+
+
+---------------------------------------------------------------------
+
+1.�Introduction
+
+---------------------------------------------------------------------
+
+The biggest difference is that the new exporter is a UNO component,
+and it?s based on the MSWord base classes, the vision here is that
+this way much less code can achieve the same set of features,
+reducing the amount of duplicated code.
+
+
+1.1.�Terminology
+
+--------------
+
+ * The "MSO OK, OOo KO" and similar abbreviations describe if the
+ given new feature is supported by the OOo RTF importer or it can
+ be tested using Microsoft Office.
+ * RtfExport refers to the new UNO-based exporter, RtfWriter refers
+ to the old built-in one.
+
+
+1.2.�General
+
+--------------
+
+RtfWriter sometimes created documents where the first { is closed in
+the middle of the document. MSO ignores this problem, but OOo stops
+parsing the rest of the document if this happens, in other words
+everything after such a bug is ignored. This can be reproduced by for
+example parprops.odt, but it?s triggered in several other cases as
+well. RtfExport has no automatic prevention for this, either - but
+during development I primarily test the output with OOo, so hopefully
+the bug will pop up less frequently.
+
+
+---------------------------------------------------------------------
+
+2.�List if fixed bugs
+
+---------------------------------------------------------------------
+
+ * https://bz.apache.org/ooo/show_bug.cgi?id=51469 postit
+ fields
+ * https://bz.apache.org/ooo/show_bug.cgi?id=66619 page
+ margins
+ * https://bz.apache.org/ooo/show_bug.cgi?id=69856 page
+ numbers
+ * https://bz.apache.org/ooo/show_bug.cgi?id=81569 { and } in
+ document title
+ * https://bz.apache.org/ooo/show_bug.cgi?id=84703 redlines
+ * https://bz.apache.org/ooo/show_bug.cgi?id=91166 russian
+ chars
+ * https://bz.apache.org/ooo/show_bug.cgi?id=92673 bookmarks
+ across tables
+ * https://bz.apache.org/ooo/show_bug.cgi?id=100507 ole
+ object export
+ * https://bz.apache.org/ooo/show_bug.cgi?id=103993 same as #
+ 81569 just for doc comments
+ * https://bz.apache.org/ooo/show_bug.cgi?id=106677
+ listoverride index starts at zero
+ * https://bz.apache.org/ooo/show_bug.cgi?id=38344 enhanced
+ character space
+
+
+---------------------------------------------------------------------
+
+3.�List of new features
+
+---------------------------------------------------------------------
+
+
+3.1.�Nested tables
+
+--------------
+
+This was new in Word2000 and it?s now supported by RtfExport (MSO OK,
+OOo KO)
+
+
+3.2.�Character properties
+
+--------------
+
+The following are now supported:
+
+ * blinking (MSO OK, OOo KO)
+ * expanded spacing (MSO OK, OOo OK)
+ * pair kerning (MSO OK, OOo OK)
+
+
+3.3.�Sections
+
+--------------
+
+RtfExport writes:
+
+ * column breaks (MSO OK, OOo OK)
+ * special breaks (when the next page should be an odd or an even
+ page; MSO OK, OOo KO)
+ * the write-protected property of sections is exported properly
+ (MSO OK, OOo KO)
+ * better page numbers (inherited type from page styles, restarts;
+ MSO OK, OOo KO)
+ * line numbering (MSO OK, OOo KO)
+
+
+3.4.�Graphics
+
+--------------
+
+PNG graphics are exported in WMF format as well, so that not only MSO
+and OOo can display graphics from the output document, but Wordpad as
+well.
+
+
+3.5.�Bookmarks
+
+--------------
+
+Implicit bookmarks like reference to a footnote did not work in OOo
+(one got an Error: Reference source not found message when opening
+the result), this now works as expected. (MSO OK - the importer
+previously autocorrected this as well, OO OK)
+
+
+3.6.�Fields
+
+--------------
+
+ * Table of contents is now written as a field, so it?s properly
+ read-only (MSO OK, OOo KO)
+ * Postit comments are now exported. (MSO OK, OOo KO)
+
+
+3.7.�Drawing
+
+--------------
+
+Drawing objects for Word 97 through Word 2007 (shapes) are now
+implemented:
+
+ * basic shapes (rectangle, ellipse, etc.)
+ * lines, including free-form ones
+ * texts, including vertical ones and their (paragraph and
+ character) formatting
+
+(MSO OK, OOo KO)
+
+
+3.8.�Form fields
+
+--------------
+
+All types supported by the RTF format are exported, namely:
+
+ * text boxes
+ * check boxes
+ * list boxes
+
+(MSO OK, OOo KO)
+
+
+3.9.�OLE objects
+
+--------------
+
+Their result is exported as a picture - RtfWriter did not export
+anything. (MSO OK, OOo OK)
+
+For math, the native data is written as well, so you can edit the
+object, too. (MSO OK, OOo KO)
+
+
+---------------------------------------------------------------------
+
+4.�Changes in the source code outside RTF
+
+---------------------------------------------------------------------
+
+These are refactorings I needed for RTF. To my best knowledge they do
+not change the output of other filters from a user?s point of view.
+
+ * The code that splits runs according to bookmarks is moved from
+ DocxExport to MSWordExportBase
+ * WW8_SdrAttrIter has been refactored to MSWord_SdrAttrIter
+ * MSWordExportBase::SubstituteBullet can avoid replacing bullets
+ * wwFontHelper::InitFontTable can really load all fonts
+ * An obvious typo in WW8AttributeOutput::CharTwoLines has been
+ fixed
+
diff --git a/sw/source/filter/ww8/WW8FFData.cxx b/sw/source/filter/ww8/WW8FFData.cxx
new file mode 100644
index 0000000000..ea7288e349
--- /dev/null
+++ b/sw/source/filter/ww8/WW8FFData.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "WW8FFData.hxx"
+#include <tools/stream.hxx>
+#include "writerwordglue.hxx"
+#include "wrtww8.hxx"
+
+namespace sw
+{
+
+using sw::types::msword_cast;
+
+WW8FFData::WW8FFData()
+ :
+ mnType(0),
+ mnResult(0),
+ mbOwnHelp(false),
+ mbOwnStat(false),
+ mbProtected(false),
+ mbSize(false),
+ mnTextType(0),
+ mbRecalc(false),
+ mbListBox(false),
+ mnMaxLen(0),
+ mnCheckboxHeight(0),
+ mnDefault(0)
+{
+}
+
+WW8FFData::~WW8FFData()
+{
+}
+
+void WW8FFData::setHelp(const OUString & rHelp)
+{
+ msHelp = rHelp;
+ mbOwnHelp = true;
+}
+
+void WW8FFData::setStatus(const OUString & rStatus)
+{
+ msStatus = rStatus;
+ mbOwnStat = true;
+}
+
+void WW8FFData::addListboxEntry(const OUString & rEntry)
+{
+ mbListBox = true;
+ msListEntries.push_back(rEntry);
+}
+
+void WW8FFData::WriteOUString(SvStream * pDataStrm, const OUString & rStr,
+ bool bAddZero)
+{
+ sal_uInt16 nStrLen = msword_cast<sal_uInt16>(rStr.getLength());
+ pDataStrm->WriteUInt16( nStrLen );
+ SwWW8Writer::WriteString16(*pDataStrm, rStr, bAddZero);
+}
+
+void WW8FFData::Write(SvStream * pDataStrm)
+{
+ sal_uInt64 nDataStt = pDataStrm->Tell();
+
+ static const sal_uInt8 aHeader[] =
+ {
+ 0,0,0,0, // len of struct
+ 0x44,0, // the start of "next" data
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // PIC
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+
+ pDataStrm->WriteBytes(aHeader, sizeof(aHeader));
+
+ sal_uInt8 aData[10] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+ };
+
+ aData[4] = mnType | (mnResult << 2);
+
+ if (mbOwnHelp)
+ aData[4] |= (1 << 7);
+
+ aData[5] = (mnTextType << 3);
+
+ if (mbOwnStat)
+ aData[5] |= 1;
+
+ if (mbProtected)
+ aData[5] |= (1 << 1);
+
+ if (mbSize)
+ aData[5] |= (1 << 2);
+
+ if (mbRecalc)
+ aData[5] |= (1 << 6);
+
+ if (mbListBox)
+ aData[5] |= (1 << 7);
+
+ aData[6] = ::sal::static_int_cast<sal_uInt8>(mnMaxLen & 0xffff);
+ aData[7] = ::sal::static_int_cast<sal_uInt8>(mnMaxLen >> 8);
+ aData[8] = ::sal::static_int_cast<sal_uInt8>(mnCheckboxHeight & 0xffff);
+ aData[9] = ::sal::static_int_cast<sal_uInt8>(mnCheckboxHeight >> 8);
+
+ pDataStrm->WriteBytes(aData, sizeof(aData));
+
+ WriteOUString(pDataStrm, msName, true);
+
+ if (mnType == 0)
+ WriteOUString(pDataStrm, msDefault, true);
+ else
+ pDataStrm->WriteUInt16( mnDefault );
+
+ WriteOUString(pDataStrm, msFormat, true);
+ WriteOUString(pDataStrm, msHelp, true);
+ WriteOUString(pDataStrm, msStatus, true);
+ WriteOUString(pDataStrm, msMacroEnter, true);
+ WriteOUString(pDataStrm, msMacroExit, true);
+
+ if (mnType == 2)
+ {
+ sal_uInt8 aData1[2] = { 0xff, 0xff };
+ pDataStrm->WriteBytes(aData1, sizeof(aData1));
+
+ sal_uInt32 nListboxEntries = msListEntries.size();
+ pDataStrm->WriteUInt32( nListboxEntries );
+
+ for (const OUString & rEntry : msListEntries)
+ WriteOUString(pDataStrm, rEntry, false);
+ }
+
+ SwWW8Writer::WriteLong( *pDataStrm, nDataStt,
+ pDataStrm->Tell() - nDataStt );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8FFData.hxx b/sw/source/filter/ww8/WW8FFData.hxx
new file mode 100644
index 0000000000..120f80cf46
--- /dev/null
+++ b/sw/source/filter/ww8/WW8FFData.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8FFDATA_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8FFDATA_HXX
+
+#include <vector>
+#include <rtl/ustring.hxx>
+
+class SvStream;
+
+namespace sw
+{
+class WW8FFData final
+{
+private:
+ // offset 0x4
+ sal_uInt8 mnType; // :2 0x3
+ sal_uInt8 mnResult; // :5 0x7c
+ bool mbOwnHelp; // :1 0x80
+
+ // offset 5
+ bool mbOwnStat; // :1 0x01
+ bool mbProtected; // :1 0x02
+ bool mbSize; // :1 0x04
+ sal_uInt8 mnTextType; // :3 0x38
+ bool mbRecalc; // :1 0x4
+ bool mbListBox; // :1 0x80
+
+ // offset 6
+ sal_uInt16 mnMaxLen; // :15 0x7fff maximum length of text field, 0 <=> no limit
+
+ // offset 8
+ sal_uInt16 mnCheckboxHeight;
+
+ // offset 10 and beyond
+ OUString msName;
+ OUString msDefault; // only for type == 0
+ sal_uInt16 mnDefault; // only for type != 0
+ OUString msFormat;
+ OUString msHelp;
+ OUString msStatus;
+ OUString msMacroEnter;
+ OUString msMacroExit;
+
+ std::vector<OUString> msListEntries;
+
+ static void WriteOUString(SvStream* pStream, const OUString& rStr, bool bAddZero);
+
+public:
+ WW8FFData();
+ ~WW8FFData();
+
+ void setType(sal_uInt8 nType) { mnType = nType; }
+
+ void setResult(sal_uInt8 nResult) { mnResult = nResult; }
+
+ void setName(const OUString& rName) { msName = rName; }
+
+ void setHelp(const OUString& rHelp);
+
+ void setStatus(const OUString& rStatus);
+
+ void addListboxEntry(const OUString& rEntry);
+
+ void Write(SvStream* pDataStrm);
+};
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8FFDATA_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8FibData.cxx b/sw/source/filter/ww8/WW8FibData.cxx
new file mode 100644
index 0000000000..9a257e366a
--- /dev/null
+++ b/sw/source/filter/ww8/WW8FibData.cxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "WW8FibData.hxx"
+
+namespace ww8
+{
+WW8FibData::WW8FibData()
+ : m_bReadOnlyRecommended(false)
+ , m_bWriteReservation(false)
+{
+}
+
+WW8FibData::~WW8FibData() {}
+
+void WW8FibData::setReadOnlyRecommended(bool bReadOnlyRecommended)
+{
+ m_bReadOnlyRecommended = bReadOnlyRecommended;
+}
+
+void WW8FibData::setWriteReservation(bool bWriteReservation)
+{
+ m_bWriteReservation = bWriteReservation;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8FibData.hxx b/sw/source/filter/ww8/WW8FibData.hxx
new file mode 100644
index 0000000000..ee5caf75fe
--- /dev/null
+++ b/sw/source/filter/ww8/WW8FibData.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8FIBDATA_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8FIBDATA_HXX
+#include <IDocumentExternalData.hxx>
+
+namespace ww8
+{
+class WW8FibData : public ::sw::ExternalData
+{
+ bool m_bReadOnlyRecommended;
+ bool m_bWriteReservation;
+
+public:
+ WW8FibData();
+ virtual ~WW8FibData() override;
+
+ void setReadOnlyRecommended(bool bReadOnlyRecommended);
+ void setWriteReservation(bool bWriteReservation);
+
+ bool getReadOnlyRecommended() const { return m_bReadOnlyRecommended; }
+ bool getWriteReservation() const { return m_bWriteReservation; }
+};
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8FIBDATA_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8Sttbf.cxx b/sw/source/filter/ww8/WW8Sttbf.cxx
new file mode 100644
index 0000000000..3143faece7
--- /dev/null
+++ b/sw/source/filter/ww8/WW8Sttbf.cxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <iostream>
+#include "WW8Sttbf.hxx"
+#include <osl/endian.h>
+#include <o3tl/make_shared.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/stream.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+namespace ww8
+{
+ WW8Struct::WW8Struct(SvStream& rSt, sal_uInt32 nPos, sal_uInt32 nSize)
+ : mn_offset(0), mn_size(0)
+ {
+ if (checkSeek(rSt, nPos))
+ {
+ std::size_t nRemainingSize = rSt.remainingSize();
+ nSize = std::min<sal_uInt32>(nRemainingSize, nSize);
+ m_pData = o3tl::make_shared_array<sal_uInt8>(nSize);
+ mn_size = rSt.ReadBytes(m_pData.get(), nSize);
+ }
+ OSL_ENSURE(mn_size == nSize, "short read in WW8Struct::WW8Struct");
+ }
+
+ WW8Struct::WW8Struct(WW8Struct const * pStruct, sal_uInt32 nPos, sal_uInt32 nSize)
+ : m_pData(pStruct->m_pData), mn_offset(pStruct->mn_offset + nPos)
+ , mn_size(nSize)
+ {
+ }
+
+ WW8Struct::~WW8Struct()
+ {
+ }
+
+ sal_uInt8 WW8Struct::getU8(sal_uInt32 nOffset)
+ {
+ sal_uInt8 nResult = 0;
+
+ if (nOffset < mn_size)
+ {
+ nResult = m_pData.get()[mn_offset + nOffset];
+ }
+
+ return nResult;
+ }
+
+ OUString WW8Struct::getUString(sal_uInt32 nOffset,
+ sal_Int32 nCount)
+ {
+ OUString aResult;
+
+ if (nCount > 0)
+ {
+ //clip to available
+ sal_uInt32 nStartOff = mn_offset + nOffset;
+ if (nStartOff >= mn_size)
+ return aResult;
+ sal_uInt32 nAvailable = (mn_size - nStartOff)/sizeof(sal_Unicode);
+ if (o3tl::make_unsigned(nCount) > nAvailable)
+ nCount = nAvailable;
+ OUStringBuffer aBuf(nCount);
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ aBuf.append(static_cast<sal_Unicode>(getU16(nStartOff+i*2)));
+ aResult = aBuf.makeStringAndClear();
+ }
+
+ SAL_INFO( "sw.ww8.level2", "<WW8Struct-getUString offset=\"" << nOffset
+ << "\" count=\"" << nCount << "\">" << aResult << "</WW8Struct-getUString>" );
+
+ return aResult;
+
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8Sttbf.hxx b/sw/source/filter/ww8/WW8Sttbf.hxx
new file mode 100644
index 0000000000..c061362bf8
--- /dev/null
+++ b/sw/source/filter/ww8/WW8Sttbf.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8STTBF_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8STTBF_HXX
+
+#include <memory>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <IDocumentExternalData.hxx>
+
+class SvStream;
+
+namespace ww8
+{
+
+ class WW8Struct : public ::sw::ExternalData
+ {
+ std::shared_ptr<sal_uInt8> m_pData;
+ sal_uInt32 mn_offset;
+ sal_uInt32 mn_size;
+
+ public:
+ WW8Struct(SvStream& rSt, sal_uInt32 nPos, sal_uInt32 nSize);
+ WW8Struct(WW8Struct const * pStruct, sal_uInt32 nPos, sal_uInt32 nSize);
+ virtual ~WW8Struct() override;
+
+ sal_uInt8 getU8(sal_uInt32 nOffset);
+
+ sal_uInt16 getU16(sal_uInt32 nOffset)
+ { return getU8(nOffset) + (getU8(nOffset + 1) << 8); }
+
+ OUString getUString(sal_uInt32 nOffset, sal_Int32 nCount);
+ };
+
+ template <class T>
+ class WW8Sttb : public WW8Struct
+ {
+ typedef std::shared_ptr< void > ExtraPointer_t;
+ bool m_bDoubleByteCharacters;
+ std::vector<OUString> m_Strings;
+ std::vector< ExtraPointer_t > m_Extras;
+
+ public:
+ WW8Sttb(SvStream& rSt, sal_Int32 nPos, sal_uInt32 nSize);
+ virtual ~WW8Sttb() override;
+
+ std::vector<OUString> & getStrings()
+ {
+ return m_Strings;
+ }
+ };
+
+ template <class T>
+ WW8Sttb<T>::WW8Sttb(SvStream& rSt, sal_Int32 nPos, sal_uInt32 nSize)
+ : WW8Struct(rSt, nPos, nSize), m_bDoubleByteCharacters(false)
+ {
+ sal_uInt32 nOffset = 0;
+
+ if (getU16(nOffset) == 0xffff)
+ {
+ m_bDoubleByteCharacters = true;
+ nOffset += 2;
+ }
+
+ sal_uInt16 nCount = getU16(nOffset);
+ sal_uInt16 ncbExtra = getU16(nOffset + 2);
+
+ nOffset += 4;
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ if (m_bDoubleByteCharacters)
+ {
+ sal_uInt16 nStrLen = getU16(nOffset);
+
+ m_Strings.push_back(getUString(nOffset +2, nStrLen));
+
+ nOffset += 2 + 2 * nStrLen;
+ }
+ else
+ {
+ sal_uInt8 nStrLen = getU8(nOffset);
+
+ m_Strings.push_back(getUString(nOffset, nStrLen));
+
+ nOffset += 1 + nStrLen;
+ }
+
+ if (ncbExtra > 0)
+ {
+ ExtraPointer_t pExtra = std::make_shared<T>(this, nOffset, ncbExtra);
+ m_Extras.push_back(pExtra);
+
+ nOffset += ncbExtra;
+ }
+ }
+ }
+
+ template <class T>
+ WW8Sttb<T>::~WW8Sttb()
+ {
+ }
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8STTBF_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8TableInfo.cxx b/sw/source/filter/ww8/WW8TableInfo.cxx
new file mode 100644
index 0000000000..5f843439bf
--- /dev/null
+++ b/sw/source/filter/ww8/WW8TableInfo.cxx
@@ -0,0 +1,1460 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <iostream>
+#include <stdio.h>
+#include "WW8TableInfo.hxx"
+#include <fmtfsize.hxx>
+#include "attributeoutputbase.hxx"
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <dbgoutsw.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <rtl/string.hxx>
+
+namespace ww8
+{
+
+WW8TableNodeInfoInner::WW8TableNodeInfoInner(WW8TableNodeInfo * pParent)
+: mpParent(pParent)
+, mnDepth(0)
+, mnCell(0)
+, mnRow(0)
+, mnShadowsBefore(0)
+, mnShadowsAfter(0)
+, mbEndOfLine(false)
+, mbFinalEndOfLine(false)
+, mbEndOfCell(false)
+, mbFirstInTable(false)
+, mbVertMerge(false)
+, mpTableBox(nullptr)
+, mpTable(nullptr)
+{
+}
+
+void WW8TableNodeInfoInner::setDepth(sal_uInt32 nDepth)
+{
+ mnDepth = nDepth;
+}
+
+void WW8TableNodeInfoInner::setCell(sal_uInt32 nCell)
+{
+ mnCell = nCell;
+}
+
+void WW8TableNodeInfoInner::setRow(sal_uInt32 nRow)
+{
+ mnRow = nRow;
+}
+
+void WW8TableNodeInfoInner::setShadowsBefore(sal_uInt32 nShadowsBefore)
+{
+ mnShadowsBefore = nShadowsBefore;
+}
+
+void WW8TableNodeInfoInner::setShadowsAfter(sal_uInt32 nShadowsAfter)
+{
+ mnShadowsAfter = nShadowsAfter;
+}
+
+void WW8TableNodeInfoInner::setEndOfLine(bool bEndOfLine)
+{
+ mbEndOfLine = bEndOfLine;
+}
+
+void WW8TableNodeInfoInner::setFinalEndOfLine(bool bFinalEndOfLine)
+{
+ mbFinalEndOfLine = bFinalEndOfLine;
+}
+
+void WW8TableNodeInfoInner::setEndOfCell(bool bEndOfCell)
+{
+ mbEndOfCell = bEndOfCell;
+}
+
+void WW8TableNodeInfoInner::setFirstInTable(bool bFirstInTable)
+{
+ mbFirstInTable = bFirstInTable;
+}
+
+void WW8TableNodeInfoInner::setVertMerge(bool bVertMerge)
+
+{
+ mbVertMerge = bVertMerge;
+}
+
+void WW8TableNodeInfoInner::setTableBox(const SwTableBox * pTableBox)
+{
+ mpTableBox = pTableBox;
+}
+
+void WW8TableNodeInfoInner::setTable(const SwTable * pTable)
+{
+ mpTable = pTable;
+}
+
+void WW8TableNodeInfoInner::setRect(const SwRect & rRect)
+{
+ maRect = rRect;
+}
+
+const SwNode * WW8TableNodeInfoInner::getNode() const
+{
+ const SwNode * pResult = nullptr;
+
+ if (mpParent != nullptr)
+ pResult = mpParent->getNode();
+
+ return pResult;
+}
+
+TableBoxVectorPtr WW8TableNodeInfoInner::getTableBoxesOfRow() const
+{
+ TableBoxVectorPtr pResult = std::make_shared<TableBoxVector>();
+
+ WW8TableCellGrid::Pointer_t pCellGrid =
+ mpParent->getParent()->getCellGridForTable(getTable(), false);
+
+ if (!pCellGrid)
+ {
+ const SwTableLine * pTabLine = getTableBox()->GetUpper();
+ const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes();
+
+ sal_uInt8 nBoxes = rTableBoxes.size();
+ if (nBoxes > MAXTABLECELLS)
+ nBoxes = MAXTABLECELLS;
+ for ( sal_uInt8 n = 0; n < nBoxes; n++ )
+ {
+ pResult->push_back(rTableBoxes[n]);
+ }
+ }
+ else
+ pResult = pCellGrid->getTableBoxesOfRow(this);
+
+ return pResult;
+}
+
+GridColsPtr WW8TableNodeInfoInner::getGridColsOfRow(AttributeOutputBase & rBase, bool calculateColumnsFromAllRows)
+{
+ GridColsPtr pResult = std::make_shared<GridCols>();
+ WidthsPtr pWidths;
+
+ // Check which columns should be checked - only the current row,
+ // or all the rows together
+ if (calculateColumnsFromAllRows)
+ {
+ // Calculate the width of all the columns based on ALL the rows.
+ // The difference is that this kind of draws vertical lines,
+ // so that if the rows look like this:
+ //
+ // ------------------------
+ // | | |
+ // ------------------------
+ // | | |
+ // ------------------------
+ // | | |
+ // ------------------------
+
+ // then the actual column widths will be broken down like this:
+ //
+ // ------------------------
+ // | | | | |
+ // ------------------------
+
+ // See the example at
+ // http://officeopenxml.com/WPtableGrid.php
+ // Under "Word 2007 Example"
+ pWidths = getColumnWidthsBasedOnAllRows();
+ }
+ else
+ {
+ // Calculate the width of all the columns based on the current row
+ pWidths = getWidthsOfRow();
+ }
+
+ const SwFrameFormat *pFormat = getTable()->GetFrameFormat();
+ OSL_ENSURE(pFormat,"Impossible");
+ if (!pFormat)
+ return pResult;
+
+ const SwFormatFrameSize &rSize = pFormat->GetFrameSize();
+ tools::ULong nTableSz = static_cast<tools::ULong>(rSize.GetWidth());
+
+ tools::Long nPageSize = 0;
+ bool bRelBoxSize = false;
+
+ rBase.GetTablePageSize( this, nPageSize, bRelBoxSize );
+
+ SwTwips nSz = 0;
+ for (const auto& rWidth : *pWidths)
+ {
+ nSz += rWidth;
+ SwTwips nCalc = nSz;
+ if ( bRelBoxSize )
+ nCalc = ( nCalc * nPageSize ) / nTableSz;
+
+ pResult->push_back( nCalc );
+ }
+
+ return pResult;
+}
+
+WidthsPtr WW8TableNodeInfoInner::getColumnWidthsBasedOnAllRows() const
+{
+ WidthsPtr pWidths;
+
+ WW8TableCellGrid::Pointer_t pCellGrid =
+ mpParent->getParent()->getCellGridForTable(getTable(), false);
+
+ if (!pCellGrid)
+ {
+ const SwTable * pTable = getTable();
+ const SwTableLines& rTableLines = pTable->GetTabLines();
+ const size_t nNumOfLines = rTableLines.size();
+
+ // Go over all the rows - and for each row - calculate where
+ // there is a separator between columns
+ WidthsPtr pSeparators = std::make_shared<Widths>();
+ for ( size_t nLineIndex = 0; nLineIndex < nNumOfLines; ++nLineIndex )
+ {
+ const SwTableLine *pCurrentLine = rTableLines[nLineIndex];
+ const SwTableBoxes & rTabBoxes = pCurrentLine->GetTabBoxes();
+ size_t nBoxes = rTabBoxes.size();
+ if (nBoxes > MAXTABLECELLS)
+ nBoxes = MAXTABLECELLS;
+
+ sal_uInt32 nSeparatorPosition = 0;
+ for (size_t nBoxIndex = 0; nBoxIndex < nBoxes; ++nBoxIndex)
+ {
+ const SwFrameFormat* pBoxFormat = rTabBoxes[ nBoxIndex ]->GetFrameFormat();
+ const SwFormatFrameSize& rLSz = pBoxFormat->GetFrameSize();
+ nSeparatorPosition += rLSz.GetWidth();
+ pSeparators->push_back(nSeparatorPosition);
+ }
+ }
+
+ // Sort the separator positions and remove any duplicates
+ std::sort(pSeparators->begin(), pSeparators->end());
+ std::vector<sal_uInt32>::iterator it = std::unique(pSeparators->begin(), pSeparators->end());
+ pSeparators->erase(it, pSeparators->end());
+
+ // Calculate the widths based on the position of the unique & sorted
+ // column separators
+ pWidths = std::make_shared<Widths>();
+ sal_uInt32 nPreviousWidth = 0;
+ for (const sal_uInt32 nCurrentWidth : *pSeparators)
+ {
+ pWidths->push_back(nCurrentWidth - nPreviousWidth);
+ nPreviousWidth = nCurrentWidth;
+ }
+ }
+ else
+ {
+ pWidths = pCellGrid->getWidthsOfRow(this);
+ }
+
+ return pWidths;
+}
+
+WidthsPtr WW8TableNodeInfoInner::getWidthsOfRow() const
+{
+ WidthsPtr pWidths;
+
+ WW8TableCellGrid::Pointer_t pCellGrid =
+ mpParent->getParent()->getCellGridForTable(getTable(), false);
+
+ if (!pCellGrid)
+ {
+ const SwTableBox * pTabBox = getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
+
+ pWidths = std::make_shared<Widths>();
+ // number of cell written
+ sal_uInt32 nBoxes = rTabBoxes.size();
+ if (nBoxes > MAXTABLECELLS)
+ nBoxes = MAXTABLECELLS;
+
+ for (sal_uInt32 n = 0; n < nBoxes; n++)
+ {
+ const SwFrameFormat* pBoxFormat = rTabBoxes[ n ]->GetFrameFormat();
+ const SwFormatFrameSize& rLSz = pBoxFormat->GetFrameSize();
+
+ pWidths->push_back(rLSz.GetWidth());
+ }
+ }
+ else
+ pWidths = pCellGrid->getWidthsOfRow(this);
+
+ return pWidths;
+}
+
+RowSpansPtr WW8TableNodeInfoInner::getRowSpansOfRow() const
+{
+ RowSpansPtr pResult = std::make_shared<RowSpans>();
+
+ WW8TableCellGrid::Pointer_t pCellGrid =
+ mpParent->getParent()->getCellGridForTable(getTable(), false);
+
+ if (!pCellGrid)
+ {
+ const SwTableBox * pTabBox = getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
+
+ sal_uInt32 nBoxes = rTabBoxes.size();
+ if (nBoxes > MAXTABLECELLS)
+ nBoxes = MAXTABLECELLS;
+
+ for (sal_uInt32 n = 0; n < nBoxes; ++n)
+ {
+ pResult->push_back(rTabBoxes[n]->getRowSpan());
+ }
+ }
+ else
+ pResult = pCellGrid->getRowSpansOfRow(this);
+
+ return pResult;
+ }
+
+
+#ifdef DBG_UTIL
+std::string WW8TableNodeInfoInner::toString() const
+{
+ static char buffer[256];
+ snprintf(buffer, sizeof(buffer),
+ "<tableinner depth=\"%" SAL_PRIuUINT32 "\""
+ " cell=\"%" SAL_PRIuUINT32 "\""
+ " row=\"%" SAL_PRIuUINT32 "\""
+ " endOfCell=\"%s\""
+ " endOfLine=\"%s\""
+ " shadowsBefore=\"%" SAL_PRIuUINT32 "\""
+ " shadowsAfter=\"%" SAL_PRIuUINT32 "\""
+ " vertMerge=\"%s\"/>",
+ mnDepth, mnCell, mnRow,
+ mbEndOfCell ? "yes" : "no",
+ mbEndOfLine ? "yes" : "no",
+ mnShadowsBefore,
+ mnShadowsAfter,
+ mbVertMerge ? "yes" : "no");
+
+ return std::string(buffer);
+}
+#endif
+
+WW8TableNodeInfo::WW8TableNodeInfo(WW8TableInfo * pParent,
+ const SwNode * pNode)
+: mpParent(pParent),
+ mnDepth(0),
+ mpNode(pNode),
+ mpNext(nullptr),
+ mpNextNode(nullptr)
+{
+}
+
+WW8TableNodeInfo::~WW8TableNodeInfo()
+{
+}
+
+#ifdef DBG_UTIL
+std::string WW8TableNodeInfo::toString() const
+{
+ static char buffer[1024];
+ snprintf(buffer, sizeof(buffer),
+ "<tableNodeInfo p=\"%p\" depth=\"%" SAL_PRIuUINT32 "\">"
+ ,this, getDepth());
+
+ std::string sResult(buffer);
+
+ for (const auto& rInner : mInners)
+ {
+ WW8TableNodeInfoInner::Pointer_t pInner = rInner.second;
+ sResult += pInner->toString();
+ }
+ sResult += dbg_out(*mpNode);
+ sResult += "</tableNodeInfo>";
+
+ return sResult;
+}
+#endif
+
+void WW8TableNodeInfo::setDepth(sal_uInt32 nDepth)
+{
+ mnDepth = nDepth;
+
+ Inners_t::iterator aIt = mInners.find(mnDepth);
+
+ if (aIt == mInners.end())
+ mInners[mnDepth] = std::make_shared<ww8::WW8TableNodeInfoInner>(this);
+
+ mInners[mnDepth]->setDepth(mnDepth);
+}
+
+void WW8TableNodeInfo::setEndOfLine(bool bEndOfLine)
+{
+ WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth);
+ pInner->setEndOfLine(bEndOfLine);
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<endOfLine depth=\"" << mnDepth << "\">" << toString() << "</endOfLine>" );
+#endif
+}
+
+void WW8TableNodeInfo::setEndOfCell(bool bEndOfCell)
+{
+ WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth);
+ pInner->setEndOfCell(bEndOfCell);
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<endOfCell depth=\"" << mnDepth << "\">" << toString() << "</endOfCell>" );
+#endif
+}
+
+void WW8TableNodeInfo::setFirstInTable(bool bFirstInTable)
+{
+ WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth);
+
+ pInner->setFirstInTable(bFirstInTable);
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<firstInTable depth=\"" << mnDepth << "\">" << toString() << "</firstInTable>" );
+#endif
+}
+
+void WW8TableNodeInfo::setVertMerge(bool bVertMerge)
+{
+ WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth);
+
+ pInner->setVertMerge(bVertMerge);
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<vertMerge depth=\"" << mnDepth << "\">" << toString() << "</vertMerge>" );
+#endif
+}
+
+void WW8TableNodeInfo::setTableBox(const SwTableBox * pTableBox)
+{
+ getInnerForDepth(mnDepth)->setTableBox(pTableBox);
+}
+
+void WW8TableNodeInfo::setTable(const SwTable * pTable)
+{
+ getInnerForDepth(mnDepth)->setTable(pTable);
+}
+
+void WW8TableNodeInfo::setNext(WW8TableNodeInfo * pNext)
+{
+ mpNext = pNext;
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<setnext><from>" << toString() << "</from><to>" << pNext->toString() << "</to></setnext>" );
+#endif
+}
+
+void WW8TableNodeInfo::setNextNode(const SwNode * pNode)
+{
+ mpNextNode = pNode;
+}
+
+void WW8TableNodeInfo::setRect(const SwRect & rRect)
+{
+ getInnerForDepth(mnDepth)->setRect(rRect);
+}
+
+void WW8TableNodeInfo::setCell(sal_uInt32 nCell)
+{
+ getInnerForDepth(mnDepth)->setCell(nCell);
+}
+
+void WW8TableNodeInfo::setRow(sal_uInt32 nRow)
+{
+ getInnerForDepth(mnDepth)->setRow(nRow);
+}
+
+void WW8TableNodeInfo::setShadowsBefore(sal_uInt32 nShadowsBefore)
+{
+ getInnerForDepth(mnDepth)->setShadowsBefore(nShadowsBefore);
+}
+
+void WW8TableNodeInfo::setShadowsAfter(sal_uInt32 nShadowsAfter)
+{
+ getInnerForDepth(mnDepth)->setShadowsAfter(nShadowsAfter);
+}
+
+
+sal_uInt32 WW8TableNodeInfo::getDepth() const
+{
+ if (!mInners.empty())
+ return mInners.begin()->second->getDepth();
+
+ return mnDepth;
+}
+
+
+const SwTableBox * WW8TableNodeInfo::getTableBox() const
+{
+ return getInnerForDepth(mnDepth)->getTableBox();
+}
+
+sal_uInt32 WW8TableNodeInfo::getCell() const
+{
+ return getInnerForDepth(mnDepth)->getCell();
+}
+
+sal_uInt32 WW8TableNodeInfo::getRow() const
+{
+ return getInnerForDepth(mnDepth)->getRow();
+}
+
+WW8TableNodeInfoInner::Pointer_t WW8TableNodeInfo::getFirstInner() const
+{
+ WW8TableNodeInfoInner::Pointer_t pResult;
+
+ if (!mInners.empty())
+ pResult = mInners.begin()->second;
+
+ return pResult;
+}
+
+WW8TableNodeInfoInner::Pointer_t WW8TableNodeInfo::getInnerForDepth(sal_uInt32 nDepth) const
+{
+ WW8TableNodeInfoInner::Pointer_t pResult;
+
+ Inners_t::const_iterator aIt = mInners.find(nDepth);
+ if (aIt != mInners.end())
+ {
+ pResult = aIt->second;
+ }
+
+ return pResult;
+}
+
+WW8TableInfo::WW8TableInfo()
+{
+}
+
+WW8TableInfo::~WW8TableInfo()
+{
+}
+
+WW8TableNodeInfo *
+WW8TableInfo::processSwTableByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds)
+{
+ SwTableCellInfo aTableCellInfo(pTable);
+
+ while (aTableCellInfo.getNext())
+ {
+ SwRect aRect = aTableCellInfo.getRect();
+
+ SAL_INFO( "sw.ww8", "<CellFrame>" );
+ SAL_INFO( "sw.ww8", "<rect top=\"" << aRect.Top() << "\" bottom=\"" << aRect.Bottom()
+ << "\" left=\"" << aRect.Left() << "\" right=\"" << aRect.Right() << "\"/>" );
+ const SwTableBox * pTableBox = aTableCellInfo.getTableBox();
+ const SwStartNode * pSttNd = pTableBox->GetSttNd();
+
+ if (pSttNd != nullptr)
+ {
+ SwPaM aPam(*pSttNd, 0);
+
+ bool bDone = false;
+ do
+ {
+ SwNode & rNode = aPam.GetPoint()->GetNode();
+
+ insertTableNodeInfo(&rNode, pTable, pTableBox, 0, 0, 1, & aRect);
+
+ if (rNode.IsEndNode())
+ {
+ SwEndNode * pEndNode = rNode.GetEndNode();
+ SwStartNode * pTmpSttNd = pEndNode->StartOfSectionNode();
+
+ if (pTmpSttNd == pSttNd)
+ bDone = true;
+ }
+
+ aPam.GetPoint()->Adjust(SwNodeOffset(1));
+ }
+ while (!bDone);
+ }
+
+ SAL_INFO( "sw.ww8", "</CellFrame>" );
+ }
+
+ return reorderByLayout(pTable, rLastRowEnds);
+}
+
+void WW8TableInfo::processSwTable(const SwTable * pTable)
+{
+ SAL_INFO( "sw.ww8", "<processSwTable>" );
+
+ WW8TableNodeInfo * pPrev = nullptr;
+ RowEndInners_t aLastRowEnds;
+
+ if (pTable->IsTableComplex() && pTable->HasLayout())
+ {
+ pPrev = processSwTableByLayout(pTable, aLastRowEnds);
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", getCellGridForTable(pTable)->toString());
+#endif
+ }
+ else
+ {
+ const SwTableLines & rLines = pTable->GetTabLines();
+
+ for (size_t n = 0; n < rLines.size(); ++n)
+ {
+ const SwTableLine * pLine = rLines[n];
+
+ pPrev = processTableLine(pTable, pLine, static_cast<sal_uInt32>(n), 1, pPrev, aLastRowEnds);
+ }
+
+ }
+
+ if (pPrev)
+ {
+ SwTableNode * pTableNode = pTable->GetTableNode();
+ SwEndNode * pEndNode = pTableNode->EndOfSectionNode();
+ pPrev->setNextNode(pEndNode);
+ assert(!aLastRowEnds.empty());
+ for (auto &a : aLastRowEnds)
+ {
+ assert(a.second->isEndOfLine());
+ a.second->setFinalEndOfLine(true);
+ }
+ }
+ SAL_INFO( "sw.ww8", "</processSwTable>" );
+}
+
+WW8TableNodeInfo *
+WW8TableInfo::processTableLine(const SwTable * pTable,
+ const SwTableLine * pTableLine,
+ sal_uInt32 nRow,
+ sal_uInt32 nDepth,
+ WW8TableNodeInfo * pPrev,
+ RowEndInners_t &rLastRowEnds)
+{
+ SAL_INFO( "sw.ww8", "<processTableLine row=\"" << nRow << "\" depth=\"" << nDepth << "\">" );
+
+ const SwTableBoxes & rBoxes = pTableLine->GetTabBoxes();
+
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ const SwTableBox * pBox = rBoxes[n];
+
+ pPrev = processTableBox(pTable, pBox, nRow, static_cast<sal_uInt32>(n), nDepth, n == rBoxes.size() - 1, pPrev, rLastRowEnds);
+ }
+
+ SAL_INFO( "sw.ww8", "</processTableLine>" );
+
+ return pPrev;
+}
+
+WW8TableNodeInfo::Pointer_t
+WW8TableInfo::processTableBoxLines(const SwTableBox * pBox,
+ const SwTable * pTable,
+ const SwTableBox * pBoxToSet,
+ sal_uInt32 nRow,
+ sal_uInt32 nCell,
+ sal_uInt32 nDepth)
+{
+ SAL_INFO( "sw.ww8", "<processTableBoxLines depth=\"" << nDepth << "\" row=\"" << nRow
+ << "\" cell=\"" << nCell << "\">" );
+
+ const SwTableLines & rLines = pBox->GetTabLines();
+ WW8TableNodeInfo::Pointer_t pNodeInfo;
+
+ if (!rLines.empty())
+ {
+ for (size_t n = 0; n < rLines.size(); ++n)
+ {
+ const SwTableLine * pLine = rLines[n];
+ const SwTableBoxes & rBoxes = pLine->GetTabBoxes();
+
+ for (size_t nBox = 0; nBox < rBoxes.size(); ++nBox)
+ pNodeInfo = processTableBoxLines(rBoxes[nBox], pTable, pBoxToSet, nRow, nCell, nDepth);
+ }
+ }
+ else
+ {
+ const SwStartNode * pSttNd = pBox->GetSttNd();
+ const SwEndNode * pEndNd = pSttNd->EndOfSectionNode();
+ SwPaM aPaM(*pSttNd, 0);
+ SwPaM aEndPaM(*pEndNd, 0);
+
+ bool bDone = false;
+ while (!bDone)
+ {
+ SwNode & rNode = aPaM.GetPoint()->GetNode();
+
+ pNodeInfo = insertTableNodeInfo(&rNode, pTable, pBoxToSet, nRow, nCell, nDepth);
+
+ if (aPaM.GetPoint()->GetNode() == aEndPaM.GetPoint()->GetNode())
+ bDone = true;
+ else
+ aPaM.GetPoint()->Adjust(SwNodeOffset(1));
+ }
+ }
+
+ SAL_INFO( "sw.ww8", "</processTableBoxLines>" );
+
+ return pNodeInfo;
+}
+
+static void updateFinalEndOfLine(RowEndInners_t &rLastRowEnds, WW8TableNodeInfo const * pEndOfCellInfo)
+{
+ sal_Int32 nDepth = pEndOfCellInfo->getDepth();
+ WW8TableNodeInfoInner::Pointer_t pInner = pEndOfCellInfo->getInnerForDepth(nDepth);
+
+ auto aIt = rLastRowEnds.find(nDepth);
+ if (aIt == rLastRowEnds.end() || (pInner->getRow() > aIt->second->getRow()))
+ rLastRowEnds[nDepth] = pInner.get();
+}
+
+WW8TableNodeInfo *
+WW8TableInfo::processTableBox(const SwTable * pTable,
+ const SwTableBox * pBox,
+ sal_uInt32 nRow,
+ sal_uInt32 nCell,
+ sal_uInt32 nDepth,
+ bool bEndOfLine,
+ WW8TableNodeInfo * pPrev,
+ RowEndInners_t &rLastRowEnds)
+{
+ SAL_INFO( "sw.ww8", "<processTableBox row=\"" << nRow << "\" cell=\"" << nCell
+ << "\" depth=\"" << nDepth << "\">" );
+
+ WW8TableNodeInfo::Pointer_t pNodeInfo;
+ const SwTableLines & rLines = pBox->GetTabLines();
+ const SwStartNode * pSttNd = pBox->GetSttNd();
+ WW8TableNodeInfo::Pointer_t pEndOfCellInfo;
+
+ if (!rLines.empty())
+ {
+ pNodeInfo = processTableBoxLines(pBox, pTable, pBox, nRow, nCell, nDepth);
+ pNodeInfo->setEndOfCell(true);
+ if (bEndOfLine)
+ {
+ pNodeInfo->setEndOfLine(true);
+ updateFinalEndOfLine(rLastRowEnds, pNodeInfo.get());
+ }
+
+ for (size_t n = 0; n < rLines.size(); n++)
+ {
+ const SwTableLine * pLine = rLines[n];
+
+ pPrev = processTableLine(pTable, pLine, n, 1, pPrev, rLastRowEnds);
+ }
+ }
+ else
+ {
+ SwPaM aPaM(*pSttNd, 0);
+
+ bool bDone = false;
+ sal_uInt32 nDepthInsideCell = 0;
+
+ do
+ {
+ SwNode & rNode = aPaM.GetPoint()->GetNode();
+
+ if (rNode.IsStartNode())
+ {
+ if (nDepthInsideCell > 0)
+ pEndOfCellInfo.reset();
+
+ nDepthInsideCell++;
+ }
+
+ pNodeInfo = insertTableNodeInfo(&rNode, pTable, pBox, nRow, nCell, nDepth);
+
+ if (pPrev)
+ pPrev->setNext(pNodeInfo.get());
+
+ pPrev = pNodeInfo.get();
+
+ if (nDepthInsideCell == 1 && rNode.IsTextNode())
+ pEndOfCellInfo = pNodeInfo;
+
+ if (rNode.IsEndNode())
+ {
+ nDepthInsideCell--;
+
+ if (nDepthInsideCell == 0 && !pEndOfCellInfo)
+ pEndOfCellInfo = pNodeInfo;
+
+ SwEndNode * pEndNode = rNode.GetEndNode( );
+ SwStartNode * pTmpSttNd = pEndNode->StartOfSectionNode();
+ if (pTmpSttNd == pSttNd)
+ bDone = true;
+ }
+
+ aPaM.GetPoint()->Adjust(SwNodeOffset(1));
+ }
+ while (!bDone);
+
+ if (pEndOfCellInfo)
+ {
+ pEndOfCellInfo->setEndOfCell(true);
+
+ if (bEndOfLine)
+ {
+ pEndOfCellInfo->setEndOfLine(true);
+ updateFinalEndOfLine(rLastRowEnds, pEndOfCellInfo.get());
+ }
+ }
+ }
+
+ SAL_INFO( "sw.ww8", "</processTableBox>" );
+
+ return pPrev;
+}
+
+WW8TableNodeInfo::Pointer_t WW8TableInfo::insertTableNodeInfo
+(const SwNode * pNode,
+ const SwTable * pTable,
+ const SwTableBox * pTableBox,
+ sal_uInt32 nRow,
+ sal_uInt32 nCell,
+ sal_uInt32 nDepth,
+ SwRect const * pRect)
+{
+ WW8TableNodeInfo::Pointer_t pNodeInfo = getTableNodeInfo(pNode);
+
+ if (!pNodeInfo)
+ {
+ pNodeInfo =
+ std::make_shared<ww8::WW8TableNodeInfo>(this, pNode);
+ mMap.emplace(pNode, pNodeInfo);
+ }
+
+ pNodeInfo->setDepth(nDepth + pNodeInfo->getDepth());
+
+ pNodeInfo->setTable(pTable);
+ pNodeInfo->setTableBox(pTableBox);
+
+ pNodeInfo->setCell(nCell);
+ pNodeInfo->setRow(nRow);
+
+ if (pNode->IsTextNode())
+ {
+ FirstInTableMap_t::const_iterator aIt = mFirstInTableMap.find(pTable);
+ if (aIt == mFirstInTableMap.end())
+ {
+ mFirstInTableMap[pTable] = pNode;
+ pNodeInfo->setFirstInTable(true);
+ }
+ }
+
+ if (pRect)
+ {
+ WW8TableCellGrid::Pointer_t pCellGrid = getCellGridForTable(pTable);
+
+ pCellGrid->insert(*pRect, pNodeInfo.get());
+ pNodeInfo->setRect(*pRect);
+ }
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", pNodeInfo->toString());
+#endif
+ return pNodeInfo;
+}
+
+WW8TableCellGrid::Pointer_t WW8TableInfo::getCellGridForTable
+(const SwTable * pTable, bool bCreate)
+{
+ WW8TableCellGrid::Pointer_t pResult;
+ CellGridMap_t::iterator aIt = mCellGridMap.find(pTable);
+
+ if (aIt == mCellGridMap.end())
+ {
+ if (bCreate)
+ {
+ pResult = std::make_shared<ww8::WW8TableCellGrid>();
+ mCellGridMap[pTable] = pResult;
+ }
+ }
+ else
+ pResult = mCellGridMap[pTable];
+
+ return pResult;
+}
+
+WW8TableNodeInfo::Pointer_t WW8TableInfo::getTableNodeInfo
+(const SwNode * pNode)
+{
+ WW8TableNodeInfo::Pointer_t pResult;
+ Map_t::iterator aIt = mMap.find(pNode);
+
+ if (aIt != mMap.end())
+ pResult = (*aIt).second;
+
+ return pResult;
+}
+
+const SwNode * WW8TableInfo::getNextNode(const SwNode * pNode)
+{
+ const SwNode * pResult = nullptr;
+
+ WW8TableNodeInfo::Pointer_t pNodeInfo = getTableNodeInfo(pNode);
+
+ if (pNodeInfo)
+ {
+ WW8TableNodeInfo * pNextInfo = pNodeInfo->getNext();
+
+ if (pNextInfo != nullptr)
+ pResult = pNextInfo->getNode();
+ else
+ {
+ const SwNode * pNextNode = pNodeInfo->getNextNode();
+
+ if (pNextNode != nullptr)
+ pResult = pNextNode;
+ }
+ }
+
+ return pResult;
+}
+
+bool WW8TableNodeInfo::operator < (const WW8TableNodeInfo & rInfo) const
+{
+ bool bRet = false;
+
+ if (rInfo.mpNode != nullptr)
+ {
+ if (mpNode == nullptr)
+ {
+ bRet = true;
+ }
+ else
+ {
+ if (mpNode->GetIndex() < rInfo.mpNode->GetIndex())
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+bool CellInfo::operator < (const CellInfo & aCellInfo) const
+{
+ bool aRet = false;
+
+ if (top() < aCellInfo.top())
+ aRet = true;
+ else if (top() == aCellInfo.top())
+ {
+ if (left() < aCellInfo.left())
+ aRet = true;
+ else if (left() == aCellInfo.left())
+ {
+ if (width() < aCellInfo.width())
+ aRet = true;
+ else if (width() == aCellInfo.width())
+ {
+ if (height() < aCellInfo.height())
+ aRet = true;
+ else if (height() == aCellInfo.height())
+ {
+ if (aCellInfo.getTableNodeInfo())
+ {
+ if (m_pNodeInfo == nullptr)
+ aRet = true;
+ else
+ {
+ aRet = *m_pNodeInfo < *aCellInfo.getTableNodeInfo();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return aRet;
+}
+
+#ifdef DBG_UTIL
+std::string CellInfo::toString() const
+{
+ static char sBuffer[256];
+
+ snprintf(sBuffer, sizeof(sBuffer),
+ "<cellinfo left=\"%" SAL_PRIdINT64 "\""
+ " right=\"%" SAL_PRIdINT64 "\""
+ " top=\"%" SAL_PRIdINT64 "\""
+ " bottom=\"%" SAL_PRIdINT64 "\""
+ " node=\"%p\"/>",
+ sal_Int64(left()),
+ sal_Int64(right()),
+ sal_Int64(top()),
+ sal_Int64(bottom()),
+ m_pNodeInfo);
+
+ return sBuffer;
+}
+#endif
+
+WW8TableNodeInfo * WW8TableInfo::reorderByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds)
+{
+ WW8TableCellGrid::Pointer_t pCellGrid = getCellGridForTable(pTable);
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", pCellGrid->toString());
+#endif
+
+ pCellGrid->addShadowCells();
+ return pCellGrid->connectCells(rLastRowEnds);
+}
+
+WW8TableCellGrid::WW8TableCellGrid()
+{
+}
+
+WW8TableCellGrid::~WW8TableCellGrid()
+{
+}
+
+WW8TableCellGridRow::Pointer_t WW8TableCellGrid::getRow(tools::Long nTop, bool bCreate)
+{
+ WW8TableCellGridRow::Pointer_t pResult;
+
+ RowTops_t::iterator aIt = m_aRowTops.find(nTop);
+
+ if (aIt == m_aRowTops.end())
+ {
+ if (bCreate)
+ {
+ pResult = std::make_shared<ww8::WW8TableCellGridRow>();
+ m_aRows[nTop] = pResult;
+ m_aRowTops.insert(nTop);
+ }
+ }
+ else
+ pResult = m_aRows[nTop];
+
+ return pResult;
+}
+
+WW8TableCellGrid::RowTops_t::const_iterator WW8TableCellGrid::getRowTopsBegin() const
+{
+ return m_aRowTops.begin();
+}
+
+WW8TableCellGrid::RowTops_t::const_iterator WW8TableCellGrid::getRowTopsEnd() const
+{
+ return m_aRowTops.end();
+}
+
+CellInfoMultiSet::const_iterator WW8TableCellGrid::getCellsBegin(tools::Long nTop)
+{
+ return getRow(nTop)->begin();
+}
+
+CellInfoMultiSet::const_iterator WW8TableCellGrid::getCellsEnd(tools::Long nTop)
+{
+ return getRow(nTop)->end();
+}
+
+void WW8TableCellGrid::insert(const SwRect & rRect,
+ WW8TableNodeInfo * pNodeInfo,
+ const tools::ULong * pFormatFrameWidth)
+{
+ CellInfo aCellInfo(rRect, pNodeInfo);
+
+ if (pFormatFrameWidth != nullptr)
+ aCellInfo.setFormatFrameWidth(*pFormatFrameWidth);
+
+ WW8TableCellGridRow::Pointer_t pRow = getRow(rRect.Top());
+ pRow->insert(aCellInfo);
+}
+
+void WW8TableCellGrid::addShadowCells()
+{
+ SAL_INFO( "sw.ww8", "<addShadowCells>" );
+
+ RowTops_t::const_iterator aTopsIt = getRowTopsBegin();
+
+ while (aTopsIt != getRowTopsEnd())
+ {
+ CellInfoMultiSet::const_iterator aCellIt = getCellsBegin(*aTopsIt);
+ CellInfoMultiSet::const_iterator aCellEndIt = getCellsEnd(*aTopsIt);
+
+ RowSpansPtr pRowSpans = std::make_shared<RowSpans>();
+
+ bool bBeginningOfCell = true;
+ bool bVertMerge = false;
+ SwRect aRect = aCellIt->getRect();
+ sal_Int32 nRowSpan = 1;
+ while (aCellIt != aCellEndIt)
+ {
+ WW8TableNodeInfo * pNodeInfo = aCellIt->getTableNodeInfo();
+
+ if (bBeginningOfCell)
+ {
+ RowTops_t::const_iterator aRowSpanIt(aTopsIt);
+ ++aRowSpanIt;
+
+ if (aRowSpanIt != getRowTopsEnd() &&
+ *aRowSpanIt < aCellIt->bottom())
+ {
+ aRect.Top(*aRowSpanIt);
+ tools::ULong nFormatFrameWidth = aCellIt->getFormatFrameWidth();
+ insert(aRect, nullptr, &nFormatFrameWidth);
+
+ bVertMerge = true;
+ }
+ else
+ bVertMerge = false;
+
+ nRowSpan = 1;
+ while (aRowSpanIt != getRowTopsEnd() &&
+ *aRowSpanIt < aCellIt->bottom())
+ {
+ ++aRowSpanIt;
+ nRowSpan++;
+ }
+
+ if (pNodeInfo)
+ pRowSpans->push_back(nRowSpan);
+ else
+ pRowSpans->push_back(-nRowSpan);
+ }
+
+ if (pNodeInfo)
+ {
+ pNodeInfo->setVertMerge(bVertMerge);
+ }
+
+ ++aCellIt;
+ if (aCellIt != aCellEndIt)
+ {
+ bBeginningOfCell = (aRect.Left() != aCellIt->left());
+ aRect = aCellIt->getRect();
+ }
+ }
+
+ WW8TableCellGridRow::Pointer_t pRow = getRow(*aTopsIt);
+ if (pRow)
+ pRow->setRowSpans(pRowSpans);
+
+ ++aTopsIt;
+ }
+ SAL_INFO( "sw.ww8", "</addShadowCells>" );
+}
+
+WW8TableNodeInfo * WW8TableCellGrid::connectCells(RowEndInners_t &rLastRowEnds)
+{
+ RowTops_t::const_iterator aTopsIt = getRowTopsBegin();
+ sal_uInt32 nRow = 0;
+ WW8TableNodeInfo * pLastNodeInfo = nullptr;
+
+ while (aTopsIt != getRowTopsEnd())
+ {
+ CellInfoMultiSet::const_iterator aCellIt = getCellsBegin(*aTopsIt);
+ CellInfoMultiSet::const_iterator aCellEndIt = getCellsEnd(*aTopsIt);
+ GridColsPtr pWidths = std::make_shared<Widths>();
+ TableBoxVectorPtr pTableBoxes = std::make_shared<TableBoxVector>();
+
+ sal_uInt32 nShadows = 0;
+ sal_uInt32 nCell = 0;
+ bool bBeginningOfCell = true;
+ WW8TableNodeInfo * pEndOfCellInfo = nullptr;
+ sal_uInt32 nDepthInCell = 0;
+ while (aCellIt != aCellEndIt)
+ {
+ tools::Long nCellX = aCellIt->left();
+ WW8TableNodeInfo * pNodeInfo = aCellIt->getTableNodeInfo();
+ if (pNodeInfo)
+ {
+ const SwNode * pNode = pNodeInfo->getNode();
+
+ if (pNode->IsStartNode())
+ {
+ nDepthInCell++;
+ pEndOfCellInfo = nullptr;
+ }
+
+ if (nDepthInCell == 1 && pNode->IsTextNode())
+ pEndOfCellInfo = pNodeInfo;
+
+ pNodeInfo->setShadowsBefore(nShadows);
+ pNodeInfo->setCell(nCell);
+ pNodeInfo->setRow(nRow);
+ if (pLastNodeInfo)
+ {
+ pLastNodeInfo->setNext(pNodeInfo);
+ pLastNodeInfo->setNextNode(pNode);
+ }
+ pLastNodeInfo = pNodeInfo;
+ nShadows = 0;
+
+ if (pNode->IsEndNode())
+ {
+ nDepthInCell--;
+
+ if (nDepthInCell == 0 && !pEndOfCellInfo)
+ pEndOfCellInfo = pNodeInfo;
+ }
+ }
+ else
+ {
+ nShadows++;
+ }
+
+ if (bBeginningOfCell)
+ {
+ pWidths->push_back(aCellIt->getFormatFrameWidth());
+
+ if (pNodeInfo)
+ pTableBoxes->push_back(pNodeInfo->getTableBox());
+ else
+ pTableBoxes->push_back(nullptr);
+ }
+
+ ++aCellIt;
+ bBeginningOfCell = false;
+
+ if (aCellIt != aCellEndIt && aCellIt->left() != nCellX)
+ {
+ nCell++;
+ bBeginningOfCell = true;
+
+ if (pEndOfCellInfo)
+ {
+ pEndOfCellInfo->setEndOfCell(true);
+ }
+
+ pEndOfCellInfo = nullptr;
+ }
+ }
+
+ pLastNodeInfo->setShadowsAfter(nShadows);
+
+ if (!pEndOfCellInfo)
+ {
+ pEndOfCellInfo = pLastNodeInfo;
+ }
+
+ pEndOfCellInfo->setEndOfCell(true);
+ pLastNodeInfo->setEndOfLine(true);
+ updateFinalEndOfLine(rLastRowEnds, pLastNodeInfo);
+
+ WW8TableCellGridRow::Pointer_t pRow(getRow(*aTopsIt));
+ pRow->setTableBoxVector(pTableBoxes);
+ pRow->setWidths(pWidths);
+
+ ++aTopsIt;
+ nRow++;
+ }
+
+ return pLastNodeInfo;
+}
+
+#ifdef DBG_UTIL
+std::string WW8TableCellGrid::toString()
+{
+ std::string sResult = "<WW8TableCellGrid>";
+
+ RowTops_t::const_iterator aTopsIt = getRowTopsBegin();
+ static char sBuffer[1024];
+ while (aTopsIt != getRowTopsEnd())
+ {
+ sResult += "<row y=\"";
+ sResult += OString::number(*aTopsIt);
+ sResult += "\">";
+
+ CellInfoMultiSet::const_iterator aCellIt = getCellsBegin(*aTopsIt);
+ CellInfoMultiSet::const_iterator aCellsEnd = getCellsEnd(*aTopsIt);
+
+ while (aCellIt != aCellsEnd)
+ {
+ snprintf(sBuffer, sizeof(sBuffer), "<cellInfo top=\"%" SAL_PRIdINT64 "\" bottom=\"%" SAL_PRIdINT64 "\" left=\"%" SAL_PRIdINT64 "\" right=\"%" SAL_PRIdINT64 "\">",
+ sal_Int64(aCellIt->top()), sal_Int64(aCellIt->bottom()), sal_Int64(aCellIt->left()), sal_Int64(aCellIt->right()));
+ sResult += sBuffer;
+
+ WW8TableNodeInfo * pInfo = aCellIt->getTableNodeInfo();
+ if (pInfo)
+ sResult += pInfo->toString();
+ else
+ sResult += "<shadow/>\n";
+
+ sResult += "</cellInfo>\n";
+ ++aCellIt;
+ }
+
+ WW8TableCellGridRow::Pointer_t pRow = getRow(*aTopsIt);
+ WidthsPtr pWidths = pRow->getWidths();
+ if (pWidths != nullptr)
+ {
+ sResult += "<widths>";
+
+ Widths::const_iterator aItEnd = pWidths->end();
+ for (Widths::const_iterator aIt = pWidths->begin();
+ aIt != aItEnd;
+ ++aIt)
+ {
+ if (aIt != pWidths->begin())
+ sResult += ", ";
+
+ snprintf(sBuffer, sizeof(sBuffer), "%" SAL_PRIxUINT32 "", *aIt);
+ sResult += sBuffer;
+ }
+
+ sResult += "</widths>";
+ }
+
+ RowSpansPtr pRowSpans = pRow->getRowSpans();
+ if (pRowSpans)
+ {
+ sResult += "<rowspans>";
+
+ RowSpans::const_iterator aItEnd = pRowSpans->end();
+ for (RowSpans::const_iterator aIt = pRowSpans->begin();
+ aIt != aItEnd;
+ ++aIt)
+ {
+ if (aIt != pRowSpans->begin())
+ sResult += ", ";
+
+ snprintf(sBuffer, sizeof(sBuffer), "%" SAL_PRIxUINT32 "", *aIt);
+ sResult += sBuffer;
+ }
+
+ sResult += "</rowspans>";
+ }
+
+ sResult += "</row>\n";
+ ++aTopsIt;
+ }
+
+ sResult += "</WW8TableCellGrid>\n";
+
+ return sResult;
+}
+#endif
+
+TableBoxVectorPtr WW8TableCellGrid::getTableBoxesOfRow
+(WW8TableNodeInfoInner const * pNodeInfoInner)
+{
+ TableBoxVectorPtr pResult;
+ WW8TableCellGridRow::Pointer_t pRow =
+ getRow(pNodeInfoInner->getRect().Top(), false);
+
+ if (pRow)
+ {
+ pResult = pRow->getTableBoxVector();
+ }
+
+ return pResult;
+}
+
+WidthsPtr WW8TableCellGrid::getWidthsOfRow
+(WW8TableNodeInfoInner const * pNodeInfoInner)
+{
+ GridColsPtr pResult;
+
+ WW8TableCellGridRow::Pointer_t pRow =
+ getRow(pNodeInfoInner->getRect().Top(), false);
+
+ if (pRow)
+ {
+ pResult = pRow->getWidths();
+ }
+
+ return pResult;
+}
+
+RowSpansPtr WW8TableCellGrid::getRowSpansOfRow
+(WW8TableNodeInfoInner const * pNodeInfoInner)
+{
+ RowSpansPtr pResult;
+
+ WW8TableCellGridRow::Pointer_t pRow =
+ getRow(pNodeInfoInner->getRect().Top(), false);
+
+ if (pRow)
+ {
+ pResult = pRow->getRowSpans();
+ }
+
+ return pResult;
+}
+
+WW8TableCellGridRow::WW8TableCellGridRow()
+: m_pCellInfos(std::make_shared<CellInfoMultiSet>())
+{
+}
+
+WW8TableCellGridRow::~WW8TableCellGridRow()
+{
+}
+
+void WW8TableCellGridRow::insert(const CellInfo & rCellInfo)
+{
+ m_pCellInfos->insert(rCellInfo);
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<gridRowInsert>" << rCellInfo.toString() << "</gridRowInsert>" );
+#endif
+}
+
+CellInfoMultiSet::const_iterator WW8TableCellGridRow::begin() const
+{
+ return m_pCellInfos->begin();
+}
+
+CellInfoMultiSet::const_iterator WW8TableCellGridRow::end() const
+{
+ return m_pCellInfos->end();
+}
+
+void WW8TableCellGridRow::setTableBoxVector(TableBoxVectorPtr const & pTableBoxVector)
+{
+ if (pTableBoxVector->size() > MAXTABLECELLS)
+ pTableBoxVector->resize(MAXTABLECELLS);
+ m_pTableBoxVector = pTableBoxVector;
+}
+
+void WW8TableCellGridRow::setWidths(WidthsPtr const & pWidths)
+{
+ m_pWidths = pWidths;
+}
+
+void WW8TableCellGridRow::setRowSpans(RowSpansPtr const & pRowSpans)
+{
+ m_pRowSpans = pRowSpans;
+}
+
+
+CellInfo::CellInfo(const SwRect & aRect, WW8TableNodeInfo * pNodeInfo)
+: m_aRect(aRect), m_pNodeInfo(pNodeInfo), m_nFormatFrameWidth(0)
+{
+ if (pNodeInfo != nullptr)
+ {
+ const SwTableBox * pBox = pNodeInfo->getTableBox();
+ const SwFrameFormat * pFrameFormat = pBox->GetFrameFormat();
+ const SwFormatFrameSize & rSize = pFrameFormat->GetFrameSize();
+
+ m_nFormatFrameWidth = rSize.GetWidth();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/WW8TableInfo.hxx b/sw/source/filter/ww8/WW8TableInfo.hxx
new file mode 100644
index 0000000000..bae0e69f58
--- /dev/null
+++ b/sw/source/filter/ww8/WW8TableInfo.hxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8TABLEINFO_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8TABLEINFO_HXX
+#include <string>
+#include <map>
+#include <memory>
+#include <set>
+#include <functional>
+#include <unordered_map>
+#include <vector>
+#include <sal/types.h>
+#include <swrect.hxx>
+
+class SwTable;
+class SwTableLine;
+class SwTableBox;
+class SwNode;
+class AttributeOutputBase;
+
+namespace ww8
+{
+const unsigned int MAXTABLECELLS = 63;
+
+class WW8TableNodeInfo;
+typedef std::vector<const SwTableBox *> TableBoxVector;
+typedef std::shared_ptr<TableBoxVector> TableBoxVectorPtr;
+typedef std::vector<sal_uInt32> GridCols;
+typedef std::shared_ptr<GridCols> GridColsPtr;
+typedef std::vector<sal_Int32> RowSpans;
+typedef std::shared_ptr<RowSpans> RowSpansPtr;
+typedef std::vector<sal_uInt32> Widths;
+typedef std::shared_ptr<Widths> WidthsPtr;
+
+class WW8TableNodeInfoInner
+{
+ WW8TableNodeInfo * mpParent;
+ sal_uInt32 mnDepth;
+ sal_uInt32 mnCell;
+ sal_uInt32 mnRow;
+ sal_uInt32 mnShadowsBefore;
+ sal_uInt32 mnShadowsAfter;
+ bool mbEndOfLine;
+ bool mbFinalEndOfLine;
+ bool mbEndOfCell;
+ bool mbFirstInTable;
+ bool mbVertMerge;
+ const SwTableBox * mpTableBox;
+ const SwTable * mpTable;
+ SwRect maRect;
+
+public:
+ typedef std::shared_ptr<WW8TableNodeInfoInner> Pointer_t;
+
+ explicit WW8TableNodeInfoInner(WW8TableNodeInfo * pParent);
+
+ void setDepth(sal_uInt32 nDepth);
+ void setCell(sal_uInt32 nCell);
+ void setRow(sal_uInt32 nRow);
+ void setShadowsBefore(sal_uInt32 nShadowsBefore);
+ void setShadowsAfter(sal_uInt32 nShadowsAfter);
+ void setEndOfLine(bool bEndOfLine);
+ void setFinalEndOfLine(bool bEndOfLine);
+ void setEndOfCell(bool bEndOfCell);
+ void setFirstInTable(bool bFirstInTable);
+ void setVertMerge(bool bVertMerge);
+ void setTableBox(const SwTableBox *pTableBox);
+ void setTable(const SwTable * pTable);
+ void setRect(const SwRect & rRect);
+
+ sal_uInt32 getDepth() const { return mnDepth;}
+ sal_uInt32 getCell() const { return mnCell;}
+ sal_uInt32 getRow() const { return mnRow;}
+ sal_uInt32 getShadowsBefore() const { return mnShadowsBefore;}
+ sal_uInt32 getShadowsAfter() const { return mnShadowsAfter;}
+ bool isEndOfCell() const { return mbEndOfCell;}
+ bool isEndOfLine() const { return mbEndOfLine;}
+ bool isFinalEndOfLine() const { return mbFinalEndOfLine;}
+ bool isFirstInTable() const { return mbFirstInTable;}
+ const SwTableBox * getTableBox() const { return mpTableBox;}
+ const SwTable * getTable() const { return mpTable;}
+ const SwRect & getRect() const { return maRect;}
+
+ const SwNode * getNode() const;
+
+ TableBoxVectorPtr getTableBoxesOfRow() const;
+ WidthsPtr getWidthsOfRow() const;
+ WidthsPtr getColumnWidthsBasedOnAllRows() const;
+ GridColsPtr getGridColsOfRow(AttributeOutputBase & rBase, bool calculateColumnsFromAllRows = false);
+ RowSpansPtr getRowSpansOfRow() const;
+
+#ifdef DBG_UTIL
+ std::string toString() const;
+#endif
+};
+
+class CellInfo
+{
+ SwRect m_aRect;
+ WW8TableNodeInfo * m_pNodeInfo;
+ tools::ULong m_nFormatFrameWidth;
+
+public:
+ CellInfo(const SwRect & aRect, WW8TableNodeInfo * pNodeInfo);
+
+ CellInfo(const CellInfo & aRectAndTableInfo)
+ : m_aRect(aRectAndTableInfo.m_aRect),
+ m_pNodeInfo(aRectAndTableInfo.m_pNodeInfo),
+ m_nFormatFrameWidth(aRectAndTableInfo.m_nFormatFrameWidth)
+ {
+ }
+
+ bool operator < (const CellInfo & aCellInfo) const;
+
+ tools::Long top() const { return m_aRect.Top(); }
+ tools::Long bottom() const { return m_aRect.Bottom(); }
+ tools::Long left() const { return m_aRect.Left(); }
+ tools::Long right() const { return m_aRect.Right(); }
+ tools::Long width() const { return m_aRect.Width(); }
+ tools::Long height() const { return m_aRect.Height(); }
+ const SwRect& getRect() const { return m_aRect; }
+ WW8TableNodeInfo * getTableNodeInfo() const
+ { return m_pNodeInfo; }
+ tools::ULong getFormatFrameWidth() const
+ {
+ return m_nFormatFrameWidth;
+ }
+
+ void setFormatFrameWidth(tools::ULong nFormatFrameWidth)
+ {
+ m_nFormatFrameWidth = nFormatFrameWidth;
+ }
+
+#ifdef DBG_UTIL
+ std::string toString() const;
+#endif
+};
+
+typedef std::multiset<CellInfo> CellInfoMultiSet;
+typedef std::map<sal_uInt32, WW8TableNodeInfoInner*,
+ std::greater<sal_uInt32> > RowEndInners_t;
+
+
+class WW8TableInfo;
+class WW8TableNodeInfo final
+{
+public:
+ typedef std::map<sal_uInt32, WW8TableNodeInfoInner::Pointer_t,
+ std::greater<sal_uInt32> > Inners_t;
+
+private:
+ WW8TableInfo * mpParent;
+ sal_uInt32 mnDepth;
+ const SwNode * mpNode;
+ Inners_t mInners;
+ WW8TableNodeInfo * mpNext;
+ const SwNode * mpNextNode;
+
+public:
+ typedef std::shared_ptr<WW8TableNodeInfo> Pointer_t;
+
+ WW8TableNodeInfo(WW8TableInfo * pParent, const SwNode * pTextNode);
+ ~WW8TableNodeInfo();
+
+ void setDepth(sal_uInt32 nDepth);
+ void setEndOfLine(bool bEndOfLine);
+ void setEndOfCell(bool bEndOfCell);
+ void setFirstInTable(bool bFirstInTable);
+ void setVertMerge(bool bVertMerge);
+ void setTableBox(const SwTableBox *pTableBox);
+ void setTable(const SwTable * pTable);
+ void setCell(sal_uInt32 nCell);
+ void setRow(sal_uInt32 nRow);
+ void setShadowsBefore(sal_uInt32 nShadowsBefore);
+ void setShadowsAfter(sal_uInt32 nShadowsAfter);
+ void setNext(WW8TableNodeInfo * pNext);
+ void setNextNode(const SwNode * pNode);
+ void setRect(const SwRect & rRect);
+
+ WW8TableInfo * getParent() const { return mpParent;}
+ sal_uInt32 getDepth() const;
+ const SwNode * getNode() const { return mpNode;}
+ const SwTableBox * getTableBox() const;
+ WW8TableNodeInfo * getNext() const { return mpNext;}
+ const SwNode * getNextNode() const { return mpNextNode;}
+
+ const Inners_t & getInners() const { return mInners;}
+ WW8TableNodeInfoInner::Pointer_t getFirstInner() const;
+ WW8TableNodeInfoInner::Pointer_t getInnerForDepth(sal_uInt32 nDepth) const;
+
+ sal_uInt32 getCell() const;
+ sal_uInt32 getRow() const;
+
+#ifdef DBG_UTIL
+ std::string toString() const;
+#endif
+
+ bool operator < (const WW8TableNodeInfo & rInfo) const;
+};
+
+struct hashNode
+{
+ size_t operator()(const SwNode * pNode) const
+ { return reinterpret_cast<size_t>(pNode); }
+};
+
+struct hashTable
+{
+ size_t operator()(const SwTable * pTable) const
+ { return reinterpret_cast<size_t>(pTable); }
+};
+
+class WW8TableCellGridRow
+{
+ std::shared_ptr<CellInfoMultiSet> m_pCellInfos;
+ TableBoxVectorPtr m_pTableBoxVector;
+ WidthsPtr m_pWidths;
+ RowSpansPtr m_pRowSpans;
+
+public:
+ typedef std::shared_ptr<WW8TableCellGridRow> Pointer_t;
+ WW8TableCellGridRow();
+ ~WW8TableCellGridRow();
+
+ void insert(const CellInfo & rCellInfo);
+ CellInfoMultiSet::const_iterator begin() const;
+ CellInfoMultiSet::const_iterator end() const;
+
+ void setTableBoxVector(TableBoxVectorPtr const & pTableBoxVector);
+ void setWidths(WidthsPtr const & pGridCols);
+ void setRowSpans(RowSpansPtr const & pRowSpans);
+
+ const TableBoxVectorPtr& getTableBoxVector() const { return m_pTableBoxVector;}
+ const WidthsPtr& getWidths() const { return m_pWidths;}
+ const RowSpansPtr& getRowSpans() const { return m_pRowSpans;}
+};
+
+class WW8TableCellGrid
+{
+ typedef std::set<tools::Long> RowTops_t;
+ typedef std::map<tools::Long, WW8TableCellGridRow::Pointer_t> Rows_t;
+
+ RowTops_t m_aRowTops;
+ Rows_t m_aRows;
+
+ WW8TableCellGridRow::Pointer_t getRow(tools::Long nTop, bool bCreate = true);
+ RowTops_t::const_iterator getRowTopsBegin() const;
+ RowTops_t::const_iterator getRowTopsEnd() const;
+ CellInfoMultiSet::const_iterator getCellsBegin(tools::Long nTop);
+ CellInfoMultiSet::const_iterator getCellsEnd(tools::Long nTop);
+
+public:
+ typedef std::shared_ptr<WW8TableCellGrid> Pointer_t;
+
+ WW8TableCellGrid();
+ ~WW8TableCellGrid();
+
+ void insert(const SwRect & rRect, WW8TableNodeInfo * pNodeInfo,
+ tools::ULong const * pFormatFrameWidth = nullptr);
+ void addShadowCells();
+ WW8TableNodeInfo *connectCells(RowEndInners_t &rLastRowEnds);
+
+#ifdef DBG_UTIL
+ std::string toString();
+#endif
+
+ TableBoxVectorPtr getTableBoxesOfRow(WW8TableNodeInfoInner const * pNodeInfo);
+ WidthsPtr getWidthsOfRow(WW8TableNodeInfoInner const * pNodeInfo);
+ RowSpansPtr getRowSpansOfRow(WW8TableNodeInfoInner const * pNodeInfo);
+};
+
+class WW8TableInfo final
+{
+ friend class WW8TableNodeInfoInner;
+ typedef std::unordered_map<const SwNode *, WW8TableNodeInfo::Pointer_t, hashNode > Map_t;
+ Map_t mMap;
+
+ typedef std::unordered_map<const SwTable *, WW8TableCellGrid::Pointer_t, hashTable > CellGridMap_t;
+ CellGridMap_t mCellGridMap;
+
+ typedef std::unordered_map<const SwTable *, const SwNode *, hashTable > FirstInTableMap_t;
+ FirstInTableMap_t mFirstInTableMap;
+
+ WW8TableNodeInfo *
+ processTableLine(const SwTable * pTable,
+ const SwTableLine * pTableLine,
+ sal_uInt32 nRow,
+ sal_uInt32 nDepth, WW8TableNodeInfo * pPrev, RowEndInners_t &rLastRowEnds);
+
+ WW8TableNodeInfo *
+ processTableBox(const SwTable * pTable,
+ const SwTableBox * pTableBox,
+ sal_uInt32 nRow,
+ sal_uInt32 nCell,
+ sal_uInt32 nDepth, bool bEndOfLine,
+ WW8TableNodeInfo * pPrev, RowEndInners_t &rLastRowEnds);
+
+ WW8TableNodeInfo::Pointer_t
+ processTableBoxLines(const SwTableBox * pBox,
+ const SwTable * pTable,
+ const SwTableBox * pBoxToSet,
+ sal_uInt32 nRow,
+ sal_uInt32 nCell,
+ sal_uInt32 nDepth);
+
+ WW8TableNodeInfo::Pointer_t
+ insertTableNodeInfo(const SwNode * pNode,
+ const SwTable * pTable,
+ const SwTableBox * pTableBox,
+ sal_uInt32 nRow,
+ sal_uInt32 nCell,
+ sal_uInt32 nDepth,
+ SwRect const * pRect = nullptr);
+
+ WW8TableCellGrid::Pointer_t getCellGridForTable(const SwTable * pTable,
+ bool bCreate = true);
+
+public:
+ typedef std::shared_ptr<WW8TableInfo> Pointer_t;
+
+ WW8TableInfo();
+ ~WW8TableInfo();
+
+ void processSwTable(const SwTable * pTable);
+ WW8TableNodeInfo * processSwTableByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds);
+ WW8TableNodeInfo::Pointer_t getTableNodeInfo(const SwNode * pNode);
+ const SwNode * getNextNode(const SwNode * pNode);
+
+ WW8TableNodeInfo * reorderByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds);
+};
+
+}
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8TABLEINFO_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx
new file mode 100644
index 0000000000..bad43d3784
--- /dev/null
+++ b/sw/source/filter/ww8/attributeoutputbase.hxx
@@ -0,0 +1,711 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_ATTRIBUTEOUTPUTBASE_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_ATTRIBUTEOUTPUTBASE_HXX
+
+#include "WW8TableInfo.hxx"
+#include "wrtww8.hxx"
+
+#include <rtl/textenc.h>
+#include <editeng/svxenum.hxx>
+#include <tools/solar.h>
+#include <optional>
+
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <swtypes.hxx>
+#include <fldbas.hxx>
+#include <utility>
+
+class Point;
+class SvxCaseMapItem;
+class SvxColorItem;
+class SvxContourItem;
+class SvxCrossedOutItem;
+class SvxEscapementItem;
+class SvxFontItem;
+class SvxFontHeightItem;
+class SvxKerningItem;
+class SvxLanguageItem;
+class SvxPostureItem;
+class SvxShadowedItem;
+class SvxUnderlineItem;
+class SvxWeightItem;
+class SvxAutoKernItem;
+class SvxBlinkItem;
+class SvxBrushItem;
+class XFillStyleItem;
+class XFillGradientItem;
+class SvxFontItem;
+class SvxFontHeightItem;
+class SvxLanguageItem;
+class SvxPostureItem;
+class SvxWeightItem;
+class SvxFontItem;
+class SvxFontHeightItem;
+class SvxLanguageItem;
+class SvxPostureItem;
+class SvxWeightItem;
+class SvxCharRotateItem;
+class SvxEmphasisMarkItem;
+class SvxTwoLinesItem;
+class SvxCharScaleWidthItem;
+class SvxCharReliefItem;
+class SvxCharHiddenItem;
+class SvxBoxItem;
+class SwFormatINetFormat;
+class SwFormatCharFormat;
+class SwFormatField;
+class SwFormatFlyCnt;
+class SwFormatFootnote;
+class SvxLineSpacingItem;
+class SvxAdjustItem;
+class SvxFormatSplitItem;
+class SvxWidowsItem;
+class SvxTabStopItem;
+class SvxHyphenZoneItem;
+class SwNumRuleItem;
+class SfxBoolItem;
+class SfxPoolItem;
+class SfxItemSet;
+class SvxParaVertAlignItem;
+class SvxParaGridItem;
+class SwFormatFrameSize;
+class SvxPaperBinItem;
+class SvxLRSpaceItem;
+class SvxULSpaceItem;
+class SwFormatPageDesc;
+class SvxFormatBreakItem;
+class SwFormatSurround;
+class SwFormatVertOrient;
+class SwFormatHoriOrient;
+class SwFormatAnchor;
+class SvxBrushItem;
+class SvxBoxItem;
+class SwFormatCol;
+class SvxFormatKeepItem;
+class SwTextGridItem;
+class SwFormatLineNumber;
+class SvxFrameDirectionItem;
+class SfxGrabBagItem;
+class SfxUInt16Item;
+class SwFormatRuby;
+class SwTextNode;
+class SwTOXMark;
+class SwRedlineData;
+class SwSection;
+class SwFormatDrop;
+class SwFrameFormat;
+class SwNumFormat;
+class SwFormat;
+struct WW8_SepInfo;
+class SwLineNumberInfo;
+class SwNumRule;
+class wwFont;
+
+namespace editeng { class SvxBorderLine; }
+
+namespace rtl { class OUString; }
+
+class MSWordExportBase;
+
+namespace ww8 { class Frame; }
+
+namespace msword {
+ const sal_uInt8 ColumnBreak = 0xE;
+ const sal_uInt8 PageBreak = 0xC;
+}
+
+/// Type of a style in the style table.
+enum StyleType
+{
+ STYLE_TYPE_PARA,
+ STYLE_TYPE_CHAR,
+ STYLE_TYPE_LIST
+};
+
+class AttributeOutputBase
+{
+private:
+ OUString m_sBaseURL; // To be used in ConvertURL
+
+ OUString ConvertURL( const OUString& rUrl, bool bAbsoluteOut );
+
+public:
+ /// Export the state of RTL/CJK.
+ virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) = 0;
+
+ /// Start of the paragraph.
+ virtual sal_Int32 StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, bool bGenerateParaId ) = 0;
+
+ /// End of the paragraph.
+ virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) = 0;
+
+ /// Called in order to output section breaks.
+ virtual void SectionBreaks(const SwNode& rNode) = 0;
+
+ /// Called before we start outputting the attributes.
+ virtual void StartParagraphProperties() = 0;
+
+ /// Called after we end outputting the attributes.
+ virtual void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) = 0;
+
+ /// Empty paragraph.
+ virtual void EmptyParagraph() = 0;
+
+ /// Start of the text run.
+ virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) = 0;
+
+ /// End of the text run.
+ virtual void EndRun( const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool bLastRun = false ) = 0;
+
+ /// Called before we start outputting the attributes.
+ virtual void StartRunProperties() = 0;
+
+ /// Called after we end outputting the attributes.
+ virtual void EndRunProperties( const SwRedlineData* pRedlineData ) = 0;
+
+ /// docx requires footnoteRef/endnoteRef tag at the beginning of each of them
+ virtual bool FootnoteEndnoteRefTag() { return false; };
+
+ /// for footnote/endnote section properties
+ virtual void SectFootnoteEndnotePr() {};
+
+ /// for docx w:commentReference
+ virtual void WritePostitFieldReference() {};
+
+ /// Output text (inside a run).
+ virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) = 0;
+
+ /// Output text (without markup).
+ virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) = 0;
+
+ /// Output ruby start.
+ virtual void StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) = 0;
+
+ /// Output ruby end.
+ virtual void EndRuby( const SwTextNode& rNode, sal_Int32 nPos ) = 0;
+
+ /// Output URL start.
+ virtual bool StartURL( const OUString& rUrl, const OUString& rTarget ) = 0;
+
+ /// Output URL end.
+ virtual bool EndURL(bool isAtEndOfParagraph) = 0;
+
+ virtual void FieldVanish(const OUString& rText, ww::eField eType, OUString const* pBookmarkName) = 0;
+
+ /// MSO uses bookmarks to reference sequence fields, so we need to generate these additional bookmarks during export
+ void GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter);
+
+ void StartTOX( const SwSection& rSect );
+
+ void EndTOX( const SwSection& rSect,bool bCareEnd=true );
+
+ virtual void OnTOXEnding() {}
+
+ void TOXMark( const SwTextNode& rNode, const SwTOXMark& rAttr );
+
+ /// Output redlining.
+ virtual void Redline( const SwRedlineData* pRedline ) = 0;
+
+ virtual void FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) = 0;
+
+ /// Output FKP (Formatted disK Page) - necessary for binary formats only.
+ /// FIXME having it in AttributeOutputBase is probably a hack, it
+ /// should be in WW8AttributeOutput only...
+ virtual void OutputFKP(bool /*bForce*/) {}
+
+ /// Output style.
+ virtual void ParagraphStyle( sal_uInt16 nStyle ) = 0;
+
+ virtual void TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0;
+
+ virtual void TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) = 0;
+
+ virtual void TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) = 0;
+
+ virtual void TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) = 0;
+
+ virtual void TableRowEnd( sal_uInt32 nDepth ) = 0;
+
+ /// Start of the styles table.
+ virtual void StartStyles() = 0;
+
+ /// End of the styles table.
+ virtual void EndStyles( sal_uInt16 nNumberOfStyles ) = 0;
+
+ /// Write default style.
+ virtual void DefaultStyle() = 0;
+
+ /// Start of a style in the styles table.
+ virtual void StartStyle( const OUString& rName, StyleType eType,
+ sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nSlot,
+ bool bAutoUpdate ) = 0;
+
+ /// End of a style in the styles table.
+ virtual void EndStyle() = 0;
+
+ /// Start of (paragraph or run) properties of a style.
+ virtual void StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) = 0;
+
+ /// End of (paragraph or run) properties of a style.
+ virtual void EndStyleProperties( bool bParProp ) = 0;
+
+ /// Numbering rule and Id.
+ virtual void OutlineNumbering(sal_uInt8 nLvl) = 0;
+
+ /// Page break
+ /// As a paragraph property - the paragraph should be on the next page.
+ virtual void PageBreakBefore( bool bBreak ) = 0;
+
+ /// Write a section break
+ /// msword::ColumnBreak or msword::PageBreak
+ /// bBreakAfter: the break must be scheduled for insertion in the end of current paragraph
+ virtual void SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr, bool bExtraPageBreak = false ) = 0;
+
+ // preserve page vertical alignment
+ virtual void TextVerticalAdjustment( const css::drawing::TextVerticalAdjust) {};
+
+ /// Start of the section properties.
+ virtual void StartSection() = 0;
+
+ /// End of the section properties.
+ virtual void EndSection() = 0;
+
+ /// Protection of forms.
+ virtual void SectionFormProtection( bool bProtected ) = 0;
+
+ /// Numbering of the lines in the document.
+ virtual void SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) = 0;
+
+ /// Has different headers/footers for the title page.
+ virtual void SectionTitlePage() = 0;
+
+ /// Set the state of the Fly at current position
+ virtual void SetStateOfFlyFrame( FlyProcessingState /*nStateOfFlyFrame*/ ){};
+ /// If the node has an anchor linked.
+ virtual void SetAnchorIsLinkedToNode( bool /*bAnchorLinkedToNode*/){};
+
+ /// Is processing of fly postponed ?
+ virtual bool IsFlyProcessingPostponed(){ return false; };
+
+ /// Reset the flag for FlyProcessing
+ virtual void ResetFlyProcessingFlag(){};
+
+ /// Description of the page borders.
+ virtual void SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* pFirstPageFormat ) = 0;
+
+ /// Columns populated from right/numbers on the right side?
+ virtual void SectionBiDi( bool bBiDi ) = 0;
+
+ /// The style of the page numbers.
+ ///
+ virtual void SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) = 0;
+
+ /// The type of breaking.
+ virtual void SectionType( sal_uInt8 nBreakCode ) = 0;
+
+ /// Definition of a numbering instance.
+ virtual void NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) = 0;
+
+ /// Numbering definition that overrides abstract numbering definition
+ virtual void OverrideNumberingDefinition(SwNumRule const&, sal_uInt16 /*nNum*/, sal_uInt16 /*nAbstractNum*/,
+ const std::map< size_t, size_t > & /*rLevelOverrides*/)
+ { assert(false); } // TODO implement for WW8/RTF
+
+ /// Start of the abstract numbering definition instance.
+ virtual void StartAbstractNumbering( sal_uInt16 /*nId*/ ) {}
+
+ /// End of the abstract numbering definition instance.
+ virtual void EndAbstractNumbering() {}
+
+ /// All the numbering level information.
+ virtual void NumberingLevel( sal_uInt8 nLevel,
+ sal_uInt16 nStart,
+ sal_uInt16 nNumberingType,
+ SvxAdjust eAdjust,
+ const sal_uInt8 *pNumLvlPos,
+ sal_uInt8 nFollow,
+ const wwFont *pFont,
+ const SfxItemSet *pOutSet,
+ sal_Int16 nIndentAt,
+ sal_Int16 nFirstLineIndex,
+ sal_Int16 nListTabPos,
+ const OUString &rNumberingString,
+ const SvxBrushItem* pBrush, // #i120928 export graphic of bullet
+ bool isLegal) = 0;
+
+protected:
+
+ static void GetNumberPara( OUString& rStr, const SwField& rField );
+
+ /// Output frames - the implementation.
+ virtual void OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) = 0;
+
+ /// Sfx item Sfx item RES_CHRATR_CASEMAP
+ virtual void CharCaseMap( const SvxCaseMapItem& ) = 0;
+
+ /// Sfx item Sfx item RES_CHRATR_COLOR
+ virtual void CharColor( const SvxColorItem& ) = 0;
+
+ /// Sfx item Sfx item RES_CHRATR_CONTOUR
+ virtual void CharContour( const SvxContourItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CROSSEDOUT
+ virtual void CharCrossedOut( const SvxCrossedOutItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_ESCAPEMENT
+ virtual void CharEscapement( const SvxEscapementItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_FONT
+ virtual void CharFont( const SvxFontItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_FONTSIZE
+ virtual void CharFontSize( const SvxFontHeightItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_KERNING
+ virtual void CharKerning( const SvxKerningItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_LANGUAGE
+ virtual void CharLanguage( const SvxLanguageItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_POSTURE
+ virtual void CharPosture( const SvxPostureItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_SHADOWED
+ virtual void CharShadow( const SvxShadowedItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_UNDERLINE
+ virtual void CharUnderline( const SvxUnderlineItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_WEIGHT
+ virtual void CharWeight( const SvxWeightItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_AUTOKERN
+ virtual void CharAutoKern( const SvxAutoKernItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_BLINK
+ virtual void CharAnimatedText( const SvxBlinkItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_BACKGROUND
+ void CharBackgroundBase( const SvxBrushItem& );
+ virtual void CharBackground( const SvxBrushItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CJK_FONT
+ virtual void CharFontCJK( const SvxFontItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CJK_FONTSIZE
+ virtual void CharFontSizeCJK( const SvxFontHeightItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CJK_LANGUAGE
+ virtual void CharLanguageCJK( const SvxLanguageItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CJK_POSTURE
+ virtual void CharPostureCJK( const SvxPostureItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CJK_WEIGHT
+ virtual void CharWeightCJK( const SvxWeightItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CTL_FONT
+ virtual void CharFontCTL( const SvxFontItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CTL_FONTSIZE
+ virtual void CharFontSizeCTL( const SvxFontHeightItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CTL_LANGUAGE
+ virtual void CharLanguageCTL( const SvxLanguageItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CTL_POSTURE
+ virtual void CharPostureCTL( const SvxPostureItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_CTL_WEIGHT
+ virtual void CharWeightCTL( const SvxWeightItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_BidiRTL
+ virtual void CharBidiRTL( const SfxPoolItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_IdctHint
+ virtual void CharIdctHint( const SfxPoolItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_ROTATE
+ virtual void CharRotate( const SvxCharRotateItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_EMPHASIS_MARK
+ virtual void CharEmphasisMark( const SvxEmphasisMarkItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_TWO_LINES
+ virtual void CharTwoLines( const SvxTwoLinesItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_SCALEW
+ virtual void CharScaleWidth( const SvxCharScaleWidthItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_RELIEF
+ virtual void CharRelief( const SvxCharReliefItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_HIDDEN
+ virtual void CharHidden( const SvxCharHiddenItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_BOX
+ void FormatCharBorder( const SvxBoxItem& rBox );
+ virtual void CharBorder( const ::editeng::SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) = 0;
+
+ /// Sfx item RES_CHRATR_HIGHLIGHT
+ virtual void CharHighlight( const SvxBrushItem& ) = 0;
+
+ /// Sfx item RES_TXTATR_INETFMT
+ virtual void TextINetFormat( const SwFormatINetFormat& ) = 0;
+
+ /// Sfx item RES_TXTATR_CHARFMT
+ virtual void TextCharFormat( const SwFormatCharFormat& ) = 0;
+
+ /// Sfx item RES_TXTATR_FIELD, RES_TXTATR_ANNOTATION and RES_TXTATR_INPUTFIELD
+ void TextField( const SwFormatField& );
+
+ /// Sfx item RES_TXTATR_FLYCNT
+ void TextFlyContent( const SwFormatFlyCnt& );
+
+ /// Sfx item RES_TXTATR_FTN
+ ///
+ /// This one is common for both WW8AttributeOutput as well as
+ /// DocxAttributeOutput.
+ void TextFootnote( const SwFormatFootnote& );
+
+ /// Sfx item RES_TXTATR_FTN
+ virtual void TextFootnote_Impl( const SwFormatFootnote& ) = 0;
+
+ /// RES_TXTATR_LINEBREAK, i.e. clearing breaks.
+ virtual void TextLineBreak(const SwFormatLineBreak&) = 0;
+
+ /// Sfx item RES_PARATR_LINESPACING
+ void ParaLineSpacing( const SvxLineSpacingItem& );
+
+ /// Count the values in ParaLineSpacing, and pass theme here.
+ virtual void ParaLineSpacing_Impl( short nSpace, short nMulti ) = 0;
+
+ /// Sfx item RES_PARATR_ADJUST
+ virtual void ParaAdjust( const SvxAdjustItem& ) = 0;
+
+ /// Sfx item RES_PARATR_SPLIT
+ virtual void ParaSplit( const SvxFormatSplitItem& ) = 0;
+
+ /// Sfx item RES_PARATR_WIDOWS
+ virtual void ParaWidows( const SvxWidowsItem& ) = 0;
+
+ /// Sfx item RES_PARATR_TABSTOP
+ virtual void ParaTabStop( const SvxTabStopItem& ) = 0;
+
+ /// Sfx item RES_PARATR_HYPHENZONE
+ virtual void ParaHyphenZone( const SvxHyphenZoneItem& ) = 0;
+
+ /// Sfx item RES_PARATR_NUMRULE
+ void ParaNumRule( const SwNumRuleItem& );
+
+ /// Numbering - the implementation.
+ virtual void ParaNumRule_Impl( const SwTextNode *pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) = 0;
+
+ /// Sfx item RES_PARATR_SCRIPTSPACE
+ virtual void ParaScriptSpace( const SfxBoolItem& ) = 0;
+
+ /// Sfx item RES_PARATR_HANGINGPUNCTUATION
+ virtual void ParaHangingPunctuation( const SfxBoolItem& ) = 0;
+
+ /// Sfx item RES_PARATR_FORBIDDEN_RULES
+ virtual void ParaForbiddenRules( const SfxBoolItem& ) = 0;
+
+ /// Sfx item RES_PARATR_VERTALIGN
+ virtual void ParaVerticalAlign( const SvxParaVertAlignItem& ) = 0;
+
+ /// Sfx item RES_PARATR_SNAPTOGRID
+ virtual void ParaSnapToGrid( const SvxParaGridItem& ) = 0;
+
+ /// Sfx item RES_FRM_SIZE
+ virtual void FormatFrameSize( const SwFormatFrameSize& ) = 0;
+
+ /// Sfx item RES_PAPER_BIN
+ virtual void FormatPaperBin( const SvxPaperBinItem& ) = 0;
+
+ /// Sfx item RES_MARGIN_FIRSTLINE
+ virtual void FormatFirstLineIndent(const SvxFirstLineIndentItem & rFirstLine) = 0;
+ /// Sfx item RES_MARGIN_TEXTLEFT
+ virtual void FormatTextLeftMargin(const SvxTextLeftMarginItem & rTextLeftMargin) = 0;
+ /// Sfx item RES_MARGIN_RIGHT
+ virtual void FormatRightMargin(const SvxRightMarginItem & rRightMargin) = 0;
+
+ /// Sfx item RES_LR_SPACE
+ virtual void FormatLRSpace( const SvxLRSpaceItem& ) = 0;
+
+ /// Sfx item RES_UL_SPACE
+ virtual void FormatULSpace( const SvxULSpaceItem& ) = 0;
+
+ /// Sfx item RES_PAGEDESC
+ void FormatPageDescription( const SwFormatPageDesc& );
+
+ /// Sfx item RES_BREAK
+ void FormatBreak( const SvxFormatBreakItem& );
+
+ /// Sfx item RES_SURROUND
+ virtual void FormatSurround( const SwFormatSurround& ) = 0;
+
+ /// Sfx item RES_VERT_ORIENT
+ virtual void FormatVertOrientation( const SwFormatVertOrient& ) = 0;
+
+ /// Sfx item RES_HORI_ORIENT
+ virtual void FormatHorizOrientation( const SwFormatHoriOrient& ) = 0;
+
+ /// Sfx item RES_ANCHOR
+ virtual void FormatAnchor( const SwFormatAnchor& ) = 0;
+
+ /// Sfx item RES_BACKGROUND
+ virtual void FormatBackground( const SvxBrushItem& ) = 0;
+
+ /// Sfx item RES_FILL_STYLE
+ virtual void FormatFillStyle( const XFillStyleItem& ) = 0;
+
+ /// Sfx item RES_FILL_GRADIENT
+ virtual void FormatFillGradient( const XFillGradientItem& ) = 0;
+
+ /// Sfx item RES_BOX
+ virtual void FormatBox( const SvxBoxItem& ) = 0;
+
+ /// Sfx item RES_COL
+ void FormatColumns( const SwFormatCol& );
+
+ virtual void FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) = 0;
+
+ /// Sfx item RES_KEEP
+ virtual void FormatKeep( const SvxFormatKeepItem& ) = 0;
+
+ /// Compute the grid character pitch
+ sal_uInt32 GridCharacterPitch( const SwTextGridItem& rGrid ) const;
+
+ /// Sfx item RES_TEXTGRID
+ virtual void FormatTextGrid( const SwTextGridItem& ) = 0;
+
+ /// Sfx item RES_LINENUMBER
+ void FormatLineNumberingBase(const SwFormatLineNumber&);
+ virtual void FormatLineNumbering( const SwFormatLineNumber& ) = 0;
+
+ /// Sfx item RES_FRAMEDIR
+ virtual void FormatFrameDirection( const SvxFrameDirectionItem& ) = 0;
+
+ /// Sfx item RES_PARATR_GRABBAG
+ virtual void ParaGrabBag( const SfxGrabBagItem& ) = 0;
+
+ /// Sfx item RES_CHRATR_GRABBAG
+ virtual void CharGrabBag( const SfxGrabBagItem& ) = 0;
+
+ /// Sfx item RES_PARATR_OUTLINELEVEL
+ void ParaOutlineLevelBase( const SfxUInt16Item& rItem );
+ virtual void ParaOutlineLevel( const SfxUInt16Item& ) = 0;
+
+ /// Write the expanded field
+ virtual void WriteExpand( const SwField* pField ) = 0;
+
+ virtual void RefField( const SwField& rField, const OUString& rRef ) = 0;
+ virtual void HiddenField( const SwField& rField ) = 0;
+ virtual void SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) = 0;
+ virtual void PostitField( const SwField* pField ) = 0;
+ virtual bool DropdownField( const SwField* pField ) = 0;
+ virtual bool PlaceholderField( const SwField* pField ) = 0;
+
+ virtual bool AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark );
+
+ /// Insert a bookmark inside the currently processed paragraph.
+ virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) = 0;
+
+ ww8::GridColsPtr GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+ ww8::WidthsPtr GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+
+ /// RES_RTL_GUTTER
+ virtual void SectionRtlGutter(const SfxBoolItem& rRtlGutter) = 0;
+
+public:
+ AttributeOutputBase(OUString sBaseURL)
+ : m_sBaseURL(std::move(sBaseURL))
+ {
+ }
+ virtual ~AttributeOutputBase() {}
+
+ /// Return the right export class.
+ virtual MSWordExportBase& GetExport() = 0;
+
+ /// @overload
+ const MSWordExportBase& GetExport() const { return const_cast< AttributeOutputBase* >( this )->GetExport(); }
+
+ /// Call the right virtual function according to the type of the item.
+ void OutputItem( const SfxPoolItem& rHt );
+
+ /// Use OutputItem() on an item set - for styles.
+ void OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault );
+
+ /// Output frames.
+ void OutputFlyFrame( const ww8::Frame& rFormat );
+
+ void GetTablePageSize
+ ( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner,
+ tools::Long& rPageSize, bool& rRelBoxSize );
+
+ virtual bool MaybeOutputBrushItem(SfxItemSet const&) { return false; }
+
+ /// Exports the definition (image, size) of a single numbering picture bullet.
+ virtual void BulletDefinition(int /*nId*/, const Graphic& /*rGraphic*/, Size /*aSize*/) {}
+
+ // Returns whether or not the 'SwTextNode' has a paragraph marker inserted \ deleted (using 'track changes')
+ const SwRedlineData* GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType );
+};
+
+class WW8Ruby
+{
+ sal_Int32 m_nJC;
+ char m_cDirective;
+ sal_uInt32 m_nRubyHeight;
+ sal_uInt32 m_nBaseHeight;
+ OUString m_sFontFamily;
+
+public:
+ WW8Ruby(const SwTextNode& rNode, const SwFormatRuby& rRuby, const MSWordExportBase& rExport );
+ sal_Int32 GetJC() const { return m_nJC; }
+ char GetDirective() const { return m_cDirective; }
+ sal_uInt32 GetRubyHeight() const { return m_nRubyHeight; }
+ sal_uInt32 GetBaseHeight() const { return m_nBaseHeight; }
+ OUString const & GetFontFamily() const { return m_sFontFamily; }
+};
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_ATTRIBUTEOUTPUTBASE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
new file mode 100644
index 0000000000..d84549d430
--- /dev/null
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -0,0 +1,10247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "docxattributeoutput.hxx"
+#include "docxhelper.hxx"
+#include "docxsdrexport.hxx"
+#include "docxexportfilter.hxx"
+#include "docxfootnotes.hxx"
+#include "writerwordglue.hxx"
+#include "ww8par.hxx"
+#include <fmtcntnt.hxx>
+#include <fmtftn.hxx>
+#include <fchrfmt.hxx>
+#include <tgrditem.hxx>
+#include <fmtruby.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <fmtanchr.hxx>
+#include <breakit.hxx>
+#include <redline.hxx>
+#include <unoframe.hxx>
+#include <textboxhelper.hxx>
+#include <rdfhelper.hxx>
+#include "wrtww8.hxx"
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/random.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/sequence.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/mathml/imexport.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <oox/ole/olehelper.hxx>
+#include <oox/export/drawingml.hxx>
+
+#include <editeng/autokernitem.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/borderline.hxx>
+#include <sax/tools/converter.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <svl/grabbagitem.hxx>
+#include <tools/date.hxx>
+#include <tools/datetime.hxx>
+#include <tools/datetimeutils.hxx>
+#include <svl/whiter.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <sot/exchange.hxx>
+
+#include <docufld.hxx>
+#include <authfld.hxx>
+#include <flddropdown.hxx>
+#include <fmtclds.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtline.hxx>
+#include <ftninfo.hxx>
+#include <htmltbl.hxx>
+#include <lineinfo.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <ndtxt.hxx>
+#include <pagedesc.hxx>
+#include <paratr.hxx>
+#include <swmodule.hxx>
+#include <swtable.hxx>
+#include <txtftn.hxx>
+#include <fmtautofmt.hxx>
+#include <docsh.hxx>
+#include <docary.hxx>
+#include <fmtclbl.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <grfatr.hxx>
+#include <frmatr.hxx>
+#include <txtatr.hxx>
+#include <frameformats.hxx>
+#include <textcontentcontrol.hxx>
+#include <formatflysplit.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/file.hxx>
+#include <utility>
+#include <vcl/embeddedfontshelper.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/drawing/ShadingPattern.hpp>
+#include <com/sun/star/text/GraphicCrop.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/text/ControlCharacter.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdarg.h>
+#include <string_view>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <unicode/regex.h>
+#include <frozen/bits/defines.h>
+#include <frozen/bits/elsa_std.h>
+#include <frozen/unordered_map.h>
+
+using ::editeng::SvxBorderLine;
+
+using namespace oox;
+using namespace docx;
+using namespace sax_fastparser;
+using namespace nsSwDocInfoSubType;
+using namespace sw::util;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::drawing;
+
+namespace {
+
+class FFDataWriterHelper
+{
+ ::sax_fastparser::FSHelperPtr m_pSerializer;
+ void writeCommonStart( const OUString& rName,
+ const OUString& rEntryMacro,
+ const OUString& rExitMacro,
+ const OUString& rHelp,
+ const OUString& rHint )
+ {
+ m_pSerializer->startElementNS(XML_w, XML_ffData);
+ m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName);
+ m_pSerializer->singleElementNS(XML_w, XML_enabled);
+ m_pSerializer->singleElementNS(XML_w, XML_calcOnExit, FSNS(XML_w, XML_val), "0");
+
+ if ( !rEntryMacro.isEmpty() )
+ m_pSerializer->singleElementNS( XML_w, XML_entryMacro,
+ FSNS(XML_w, XML_val), rEntryMacro );
+
+ if ( !rExitMacro.isEmpty() )
+ m_pSerializer->singleElementNS(XML_w, XML_exitMacro, FSNS(XML_w, XML_val), rExitMacro);
+
+ if ( !rHelp.isEmpty() )
+ m_pSerializer->singleElementNS( XML_w, XML_helpText,
+ FSNS(XML_w, XML_type), "text",
+ FSNS(XML_w, XML_val), rHelp );
+
+ if ( !rHint.isEmpty() )
+ m_pSerializer->singleElementNS( XML_w, XML_statusText,
+ FSNS(XML_w, XML_type), "text",
+ FSNS(XML_w, XML_val), rHint );
+
+ }
+ void writeFinish()
+ {
+ m_pSerializer->endElementNS( XML_w, XML_ffData );
+ }
+public:
+ explicit FFDataWriterHelper( ::sax_fastparser::FSHelperPtr rSerializer ) : m_pSerializer(std::move( rSerializer )){}
+ void WriteFormCheckbox( const OUString& rName,
+ const OUString& rEntryMacro,
+ const OUString& rExitMacro,
+ const OUString& rHelp,
+ const OUString& rHint,
+ bool bChecked )
+ {
+ writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
+ // Checkbox specific bits
+ m_pSerializer->startElementNS(XML_w, XML_checkBox);
+ // currently hardcoding autosize
+ // #TODO check if this defaulted
+ m_pSerializer->startElementNS(XML_w, XML_sizeAuto);
+ m_pSerializer->endElementNS( XML_w, XML_sizeAuto );
+ if ( bChecked )
+ m_pSerializer->singleElementNS(XML_w, XML_checked);
+ m_pSerializer->endElementNS( XML_w, XML_checkBox );
+ writeFinish();
+ }
+
+ void WriteFormText( const OUString& rName,
+ const OUString& rEntryMacro,
+ const OUString& rExitMacro,
+ const OUString& rHelp,
+ const OUString& rHint,
+ const OUString& rType,
+ const OUString& rDefaultText,
+ sal_uInt16 nMaxLength,
+ const OUString& rFormat )
+ {
+ writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
+
+ m_pSerializer->startElementNS(XML_w, XML_textInput);
+ if ( !rType.isEmpty() )
+ m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), rType);
+ if ( !rDefaultText.isEmpty() )
+ m_pSerializer->singleElementNS(XML_w, XML_default, FSNS(XML_w, XML_val), rDefaultText);
+ if ( nMaxLength )
+ m_pSerializer->singleElementNS( XML_w, XML_maxLength,
+ FSNS(XML_w, XML_val), OString::number(nMaxLength) );
+ if ( !rFormat.isEmpty() )
+ m_pSerializer->singleElementNS(XML_w, XML_format, FSNS(XML_w, XML_val), rFormat);
+ m_pSerializer->endElementNS( XML_w, XML_textInput );
+
+ writeFinish();
+ }
+};
+
+class FieldMarkParamsHelper
+{
+ const sw::mark::IFieldmark& mrFieldmark;
+ public:
+ explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {}
+ OUString const & getName() const { return mrFieldmark.GetName(); }
+ template < typename T >
+ bool extractParam( const OUString& rKey, T& rResult )
+ {
+ bool bResult = false;
+ if ( mrFieldmark.GetParameters() )
+ {
+ sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey );
+ if ( it != mrFieldmark.GetParameters()->end() )
+ bResult = ( it->second >>= rResult );
+ }
+ return bResult;
+ }
+};
+
+// [ISO/IEC29500-1:2016] 17.18.50 ST_LongHexNumber (Eight Digit Hexadecimal Value)
+OUString NumberToHexBinary(sal_Int32 n)
+{
+ OUStringBuffer aBuf;
+ sax::Converter::convertNumberToHexBinary(aBuf, n);
+ return aBuf.makeStringAndClear();
+}
+
+// Returns a new reference with the previous content of src; src is empty after this
+auto detachFrom(rtl::Reference<sax_fastparser::FastAttributeList>& src)
+{
+ return rtl::Reference(std::move(src));
+}
+
+void lclAddThemeValuesToCustomAttributes(
+ rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, model::ComplexColor const& rComplexColor,
+ sal_Int32 nThemeAttrId, sal_Int32 nThemeTintAttrId, sal_Int32 nThemeShadeAttrId)
+{
+ static constexpr auto constThemeColorTypeTokenMap = frozen::make_unordered_map<model::ThemeColorType, const char*>({
+ { model::ThemeColorType::Dark1, "dark1" },
+ { model::ThemeColorType::Light1, "light1" },
+ { model::ThemeColorType::Dark2, "dark2" },
+ { model::ThemeColorType::Light2, "light2" },
+ { model::ThemeColorType::Accent1, "accent1" },
+ { model::ThemeColorType::Accent2, "accent2" },
+ { model::ThemeColorType::Accent3, "accent3" },
+ { model::ThemeColorType::Accent4, "accent4" },
+ { model::ThemeColorType::Accent5, "accent5" },
+ { model::ThemeColorType::Accent6, "accent6" },
+ { model::ThemeColorType::Hyperlink, "hyperlink" },
+ { model::ThemeColorType::FollowedHyperlink, "followedHyperlink" }
+ });
+
+ if (rComplexColor.isValidThemeType())
+ {
+ const auto iter = constThemeColorTypeTokenMap.find(rComplexColor.getThemeColorType());
+ assert(iter != constThemeColorTypeTokenMap.end());
+ OString sSchemeType = iter->second;
+ if (rComplexColor.getThemeColorUsage() == model::ThemeColorUsage::Text)
+ {
+ if (rComplexColor.getThemeColorType() == model::ThemeColorType::Dark1)
+ sSchemeType = "text1"_ostr;
+ else if (rComplexColor.getThemeColorType() == model::ThemeColorType::Dark2)
+ sSchemeType = "text2"_ostr;
+ }
+ else if (rComplexColor.getThemeColorUsage() == model::ThemeColorUsage::Background)
+ {
+ if (rComplexColor.getThemeColorType() == model::ThemeColorType::Light1)
+ sSchemeType = "background1"_ostr;
+ else if (rComplexColor.getThemeColorType() == model::ThemeColorType::Light2)
+ sSchemeType = "background2"_ostr;
+ }
+
+ DocxAttributeOutput::AddToAttrList(pAttrList, FSNS(XML_w, nThemeAttrId), sSchemeType);
+
+ sal_Int16 nLumMod = 10'000;
+ sal_Int16 nLumOff = 0;
+ sal_Int16 nTint = 0;
+ sal_Int16 nShade = 0;
+
+ for (auto const& rTransform : rComplexColor.getTransformations())
+ {
+ if (rTransform.meType == model::TransformationType::LumMod)
+ nLumMod = rTransform.mnValue;
+ if (rTransform.meType == model::TransformationType::LumOff)
+ nLumOff = rTransform.mnValue;
+ if (rTransform.meType == model::TransformationType::Tint)
+ nTint = rTransform.mnValue;
+ if (rTransform.meType == model::TransformationType::Shade)
+ nShade = rTransform.mnValue;
+ }
+ if (nLumMod == 10'000 && nLumOff == 0)
+ {
+ if (nTint != 0)
+ {
+ // Convert from 0-100 into 0-255
+ sal_Int16 nTint255 = std::round(255.0 - (double(nTint) / 10000.0) * 255.0);
+ DocxAttributeOutput::AddToAttrList(pAttrList, FSNS(XML_w, nThemeTintAttrId), OString::number(nTint255, 16));
+ }
+ else if (nShade != 0)
+ {
+ // Convert from 0-100 into 0-255
+ sal_Int16 nShade255 = std::round(255.0 - (double(nShade) / 10000.0) * 255.0);
+ DocxAttributeOutput::AddToAttrList(pAttrList, FSNS(XML_w, nThemeShadeAttrId), OString::number(nShade255, 16));
+ }
+ }
+ else
+ {
+ double nPercentage = 0.0;
+
+ if (nLumOff > 0)
+ nPercentage = double(nLumOff) / 100.0;
+ else
+ nPercentage = (-10'000 + double(nLumMod)) / 100.0;
+
+ // Convert from 0-100 into 0-255
+ sal_Int16 nTintShade255 = std::round(255.0 - (std::abs(nPercentage) / 100.0) * 255.0);
+
+ if (nPercentage > 0)
+ DocxAttributeOutput::AddToAttrList(pAttrList, FSNS(XML_w, nThemeTintAttrId), OString::number(nTintShade255, 16));
+ else if (nPercentage < 0)
+ DocxAttributeOutput::AddToAttrList(pAttrList, FSNS(XML_w, nThemeShadeAttrId), OString::number(nTintShade255, 16));
+ }
+ }
+}
+
+void lclAddThemeFillColorAttributes(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, model::ComplexColor const& rComplexColor)
+{
+ lclAddThemeValuesToCustomAttributes(pAttrList, rComplexColor, XML_themeFill, XML_themeFillTint, XML_themeFillShade);
+}
+
+void lclAddThemeColorAttributes(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, model::ComplexColor const& rComplexColor)
+{
+ lclAddThemeValuesToCustomAttributes(pAttrList, rComplexColor, XML_themeColor, XML_themeTint, XML_themeShade);
+}
+
+} // end anonymous namespace
+
+void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
+{
+ if (bIsRTL)
+ m_pSerializer->singleElementNS(XML_w, XML_rtl, FSNS(XML_w, XML_val), "true");
+}
+
+/// Are multiple paragraphs disallowed inside this type of SDT?
+static bool lcl_isOnelinerSdt(std::u16string_view rName)
+{
+ return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
+}
+
+// write a floating table directly to docx without the surrounding frame
+void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame)
+{
+ const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
+ m_aFloatingTablesOfParagraph.insert(&rFrameFormat);
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+
+ //Save data here and restore when out of scope
+ ExportDataSaveRestore aDataGuard(GetExport(), nStt, nEnd, pParentFrame);
+
+ // Stash away info about the current table, so m_tableReference is clean.
+ DocxTableExportContext aTableExportContext(*this);
+
+ // set a floatingTableFrame AND unset parent frame,
+ // otherwise exporter thinks we are still in a frame
+ m_rExport.SetFloatingTableFrame(pParentFrame);
+ m_rExport.m_pParentFrame = nullptr;
+
+ GetExport().WriteText();
+
+ m_rExport.SetFloatingTableFrame(nullptr);
+}
+
+static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput)
+{
+ const auto& rExport = rDocxAttributeOutput.GetExport();
+ // iterate though all SpzFrameFormats and check whether they are anchored to the current text node
+ for( sal_uInt16 nCnt = rExport.m_rDoc.GetSpzFrameFormats()->size(); nCnt; )
+ {
+ const SwFrameFormat* pFrameFormat = (*rExport.m_rDoc.GetSpzFrameFormats())[ --nCnt ];
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
+
+ if (!pAnchorNode || ! rExport.m_pCurPam->GetPointNode().GetTextNode())
+ continue;
+
+ if (*pAnchorNode != *rExport.m_pCurPam->GetPointNode().GetTextNode())
+ continue;
+
+ const SwNodeIndex* pStartNode = pFrameFormat->GetContent().GetContentIdx();
+ if (!pStartNode)
+ continue;
+
+ SwNodeIndex aStartNode = *pStartNode;
+
+ // go to the next node (actual content)
+ ++aStartNode;
+
+ // this has to be a table
+ if (!aStartNode.GetNode().IsTableNode())
+ continue;
+
+ // go to the end of the table
+ SwNodeOffset aEndIndex = aStartNode.GetNode().EndOfSectionIndex();
+ // go one deeper
+ aEndIndex++;
+ // this has to be the end of the content
+ if (aEndIndex != pFrameFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionIndex())
+ continue;
+
+ // check for a grabBag and "TablePosition" attribute -> then we can export the table directly
+ SwTableNode* pTableNode = aStartNode.GetNode().GetTableNode();
+ SwTable& rTable = pTableNode->GetTable();
+ SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
+ const SfxGrabBagItem* pTableGrabBag = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG);
+ const std::map<OUString, css::uno::Any> & rTableGrabBag = pTableGrabBag->GetGrabBag();
+ // no grabbag?
+ if (rTableGrabBag.find("TablePosition") == rTableGrabBag.end())
+ {
+ if (pFrameFormat->GetFlySplit().GetValue())
+ {
+ ww8::Frame aFrame(*pFrameFormat, *rAnchor.GetContentAnchor());
+ rDocxAttributeOutput.WriteFloatingTable(&aFrame);
+ }
+ continue;
+ }
+
+ // write table to docx
+ ww8::Frame aFrame(*pFrameFormat, *rAnchor.GetContentAnchor());
+ rDocxAttributeOutput.WriteFloatingTable(&aFrame);
+ }
+}
+
+sal_Int32 DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+ bool bGenerateParaId)
+{
+ // Paragraphs (in headers/footers/comments/frames etc) can start before another finishes.
+ // So a stack is needed to keep track of each paragraph's status separately.
+ // Complication: Word can't handle nested text boxes, so those need to be collected together.
+ if ( !m_aFramesOfParagraph.size() || !m_nTextFrameLevel )
+ m_aFramesOfParagraph.push(std::vector<ww8::Frame>());
+
+ if ( m_nColBreakStatus == COLBRK_POSTPONE )
+ m_nColBreakStatus = COLBRK_WRITE;
+
+ // Output table/table row/table cell starts if needed
+ if ( pTextNodeInfo )
+ {
+ // New cell/row?
+ if ( m_tableReference.m_nTableDepth > 0 && !m_tableReference.m_bTableCellOpen )
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference.m_nTableDepth ) );
+ if ( pDeepInner->getCell() == 0 )
+ StartTableRow( pDeepInner );
+
+ const sal_uInt32 nCell = pDeepInner->getCell();
+ const sal_uInt32 nRow = pDeepInner->getRow();
+
+ SyncNodelessCells(pDeepInner, nCell, nRow);
+ StartTableCell(pDeepInner, nCell, nRow);
+ }
+
+ sal_uInt32 nRow = pTextNodeInfo->getRow();
+ sal_uInt32 nCell = pTextNodeInfo->getCell();
+ if (nCell == 0)
+ {
+ // Do we have to start the table?
+ // [If we are at the right depth already, it means that we
+ // continue the table cell]
+ sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
+
+ if ( nCurrentDepth > m_tableReference.m_nTableDepth )
+ {
+ // Start all the tables that begin here
+ for ( sal_uInt32 nDepth = m_tableReference.m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth )
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) );
+
+ StartTable( pInner );
+ StartTableRow( pInner );
+
+ StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0);
+ }
+
+ m_tableReference.m_nTableDepth = nCurrentDepth;
+ }
+ }
+ }
+
+ // look ahead for floating tables that were put into a frame during import
+ // floating tables in shapes are not supported: exclude this case
+ if (!m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
+ {
+ // Do this after opening table/row/cell, so floating tables anchored at cell start go inside
+ // the cell, not outside.
+ checkAndWriteFloatingTables(*this);
+ }
+
+ // Look up the "sdt end before this paragraph" property early, when it
+ // would normally arrive, it would be too late (would be after the
+ // paragraph start has been written).
+ bool bEndParaSdt = false;
+ if (m_aParagraphSdt.m_bStartedSdt)
+ {
+ SwTextNode* pTextNode = m_rExport.m_pCurPam->GetPointNode().GetTextNode();
+ if (pTextNode && pTextNode->GetpSwAttrSet())
+ {
+ const SfxItemSet* pSet = pTextNode->GetpSwAttrSet();
+ if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG))
+ {
+ const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem);
+ const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag();
+ bEndParaSdt = m_aParagraphSdt.m_bStartedSdt && rMap.find("ParaSdtEndBefore") != rMap.end();
+ }
+ }
+ }
+ // TODO also avoid multiline paragraphs in those SDT types for shape text
+ bool bOneliner = m_aParagraphSdt.m_bStartedSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias);
+ if (bEndParaSdt || (m_aParagraphSdt.m_bStartedSdt && m_bHadSectPr) || bOneliner)
+ {
+ // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph.
+ m_aParagraphSdt.EndSdtBlock(m_pSerializer);
+ m_aStartedParagraphSdtPrAlias.clear();
+ }
+ m_bHadSectPr = false;
+
+ // this mark is used to be able to enclose the paragraph inside a sdr tag.
+ // We will only know if we have to do that later.
+ m_pSerializer->mark(Tag_StartParagraph_1);
+
+ std::optional<OUString> aParaId;
+ sal_Int32 nParaId = 0;
+ if (bGenerateParaId)
+ {
+ nParaId = m_nNextParaId++;
+ aParaId = NumberToHexBinary(nParaId);
+ }
+ m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
+
+ // postpone the output of the run (we get it before the paragraph
+ // properties, but must write it after them)
+ m_pSerializer->mark(Tag_StartParagraph_2);
+
+ // no section break in this paragraph yet; can be set in SectionBreak()
+ m_pSectionInfo.reset();
+
+ m_bParagraphOpened = true;
+ m_bIsFirstParagraph = false;
+ m_nHyperLinkCount.push_back(0);
+
+ return nParaId;
+}
+
+OString DocxAttributeOutput::convertToOOXMLVertOrient(sal_Int16 nOrient)
+{
+ switch( nOrient )
+ {
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ return "center"_ostr;
+ case text::VertOrientation::BOTTOM:
+ return "bottom"_ostr;
+ case text::VertOrientation::LINE_BOTTOM:
+ return "outside"_ostr;
+ case text::VertOrientation::TOP:
+ return "top"_ostr;
+ case text::VertOrientation::LINE_TOP:
+ return "inside"_ostr;
+ default:
+ return OString();
+ }
+}
+
+OString DocxAttributeOutput::convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
+{
+ switch( nOrient )
+ {
+ case text::HoriOrientation::LEFT:
+ return bIsPosToggle ? "inside" : "left";
+ case text::HoriOrientation::INSIDE:
+ return "inside"_ostr;
+ case text::HoriOrientation::RIGHT:
+ return bIsPosToggle ? "outside" : "right";
+ case text::HoriOrientation::OUTSIDE:
+ return "outside"_ostr;
+ case text::HoriOrientation::CENTER:
+ case text::HoriOrientation::FULL:
+ return "center"_ostr;
+ default:
+ return OString();
+ }
+}
+
+OString DocxAttributeOutput::convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
+{
+ switch (nOrientRel)
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ return "margin"_ostr;
+ case text::RelOrientation::PAGE_FRAME:
+ return "page"_ostr;
+ case text::RelOrientation::FRAME:
+ case text::RelOrientation::TEXT_LINE:
+ default:
+ return "text"_ostr;
+ }
+}
+
+OString DocxAttributeOutput::convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
+{
+ switch (nOrientRel)
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ return "margin"_ostr;
+ case text::RelOrientation::PAGE_FRAME:
+ return "page"_ostr;
+ case text::RelOrientation::CHAR:
+ case text::RelOrientation::PAGE_RIGHT:
+ case text::RelOrientation::FRAME:
+ default:
+ return "text"_ostr;
+ }
+}
+
+void FramePrHelper::SetFrame(ww8::Frame* pFrame, sal_Int32 nTableDepth)
+{
+ assert(!pFrame || !m_pFrame);
+ m_pFrame = pFrame;
+ m_nTableDepth = nTableDepth;
+ if (m_pFrame)
+ {
+ m_bUseFrameBorders = true;
+ m_bUseFrameBackground = true;
+ m_bUseFrameTextDirection = true;
+ }
+}
+
+bool FramePrHelper::UseFrameBorders(sal_Int32 nTableDepth)
+{
+ if (!m_pFrame || m_nTableDepth < nTableDepth)
+ return false;
+
+ return m_bUseFrameBorders;
+}
+
+bool FramePrHelper::UseFrameBackground()
+{
+ if (!m_pFrame)
+ return false;
+
+ return m_bUseFrameBackground;
+}
+
+bool FramePrHelper::UseFrameTextDirection(sal_Int32 nTableDepth)
+{
+ if (!m_pFrame || m_nTableDepth < nTableDepth)
+ return false;
+
+ return m_bUseFrameTextDirection;
+}
+
+void SdtBlockHelper::DeleteAndResetTheLists()
+{
+ if (m_pTokenChildren.is() )
+ m_pTokenChildren.clear();
+ if (m_pDataBindingAttrs.is() )
+ m_pDataBindingAttrs.clear();
+ if (m_pTextAttrs.is())
+ m_pTextAttrs.clear();
+ if (!m_aAlias.isEmpty())
+ m_aAlias.clear();
+ if (!m_aTag.isEmpty())
+ m_aTag.clear();
+ if (!m_aLock.isEmpty())
+ m_aLock.clear();
+ if (!m_aPlaceHolderDocPart.isEmpty())
+ m_aPlaceHolderDocPart.clear();
+ if (!m_aColor.isEmpty())
+ m_aColor.clear();
+ if (!m_aAppearance.isEmpty())
+ m_aAppearance.clear();
+ m_bShowingPlaceHolder = false;
+ m_nId = 0;
+ m_nTabIndex = 0;
+}
+
+void SdtBlockHelper::WriteSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing)
+{
+ if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_nId)
+ return;
+
+ // sdt start mark
+ pSerializer->mark(DocxAttributeOutput::Tag_WriteSdtBlock);
+
+ pSerializer->startElementNS(XML_w, XML_sdt);
+
+ // output sdt properties
+ pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+ if (m_nSdtPrToken > 0 && m_pTokenChildren.is())
+ {
+ if (!m_pTokenAttributes.is())
+ pSerializer->startElement(m_nSdtPrToken);
+ else
+ {
+ pSerializer->startElement(m_nSdtPrToken, detachFrom(m_pTokenAttributes));
+ }
+
+ if (m_nSdtPrToken == FSNS(XML_w, XML_date) || m_nSdtPrToken == FSNS(XML_w, XML_docPartObj) || m_nSdtPrToken == FSNS(XML_w, XML_docPartList) || m_nSdtPrToken == FSNS(XML_w14, XML_checkbox)) {
+ const uno::Sequence<xml::FastAttribute> aChildren = m_pTokenChildren->getFastAttributes();
+ for (const auto& rChild : aChildren)
+ pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), rChild.Value);
+ }
+
+ pSerializer->endElement(m_nSdtPrToken);
+ }
+ else if ((m_nSdtPrToken > 0) && m_nSdtPrToken != FSNS(XML_w, XML_id) && !(bRunTextIsOn && bParagraphHasDrawing))
+ {
+ if (!m_pTokenAttributes.is())
+ pSerializer->singleElement(m_nSdtPrToken);
+ else
+ {
+ pSerializer->singleElement(m_nSdtPrToken, detachFrom(m_pTokenAttributes));
+ }
+ }
+
+ WriteExtraParams(pSerializer);
+
+ pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+ // sdt contents start tag
+ pSerializer->startElementNS(XML_w, XML_sdtContent);
+
+ // prepend the tags since the sdt start mark before the paragraph
+ pSerializer->mergeTopMarks(DocxAttributeOutput::Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
+
+ // write the ending tags after the paragraph
+ m_bStartedSdt = true;
+
+ // clear sdt status
+ m_nSdtPrToken = 0;
+ DeleteAndResetTheLists();
+}
+
+void SdtBlockHelper::WriteExtraParams(const ::sax_fastparser::FSHelperPtr& pSerializer)
+{
+ if (m_nId)
+ {
+ pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), OString::number(m_nId));
+ }
+
+ if (m_pDataBindingAttrs.is())
+ {
+ pSerializer->singleElementNS(XML_w, XML_dataBinding, detachFrom(m_pDataBindingAttrs));
+ }
+
+ if (m_pTextAttrs.is())
+ {
+ pSerializer->singleElementNS(XML_w, XML_text, detachFrom(m_pTextAttrs));
+ }
+
+ if (!m_aPlaceHolderDocPart.isEmpty())
+ {
+ pSerializer->startElementNS(XML_w, XML_placeholder);
+ pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), m_aPlaceHolderDocPart);
+ pSerializer->endElementNS(XML_w, XML_placeholder);
+ }
+
+ if (m_bShowingPlaceHolder)
+ pSerializer->singleElementNS(XML_w, XML_showingPlcHdr);
+
+ if (!m_aColor.isEmpty())
+ {
+ pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), m_aColor);
+ }
+
+ if (!m_aAppearance.isEmpty())
+ {
+ pSerializer->singleElementNS(XML_w15, XML_appearance, FSNS(XML_w15, XML_val), m_aAppearance);
+ }
+
+ if (!m_aAlias.isEmpty())
+ pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), m_aAlias);
+
+ if (!m_aTag.isEmpty())
+ pSerializer->singleElementNS(XML_w, XML_tag, FSNS(XML_w, XML_val), m_aTag);
+
+ if (m_nTabIndex)
+ pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, XML_val),
+ OString::number(m_nTabIndex));
+
+ if (!m_aLock.isEmpty())
+ pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val), m_aLock);
+}
+
+void SdtBlockHelper::EndSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer)
+{
+ pSerializer->endElementNS(XML_w, XML_sdtContent);
+ pSerializer->endElementNS(XML_w, XML_sdt);
+ m_bStartedSdt = false;
+}
+
+void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
+{
+ for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
+ {
+ if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
+ {
+ m_nSdtPrToken = FSNS(XML_w14, XML_checkbox);
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ aPropertyValue.Value >>= aGrabBag;
+ for (const auto& rProp : aGrabBag)
+ {
+ if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
+ FSNS(XML_w14, XML_checked), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
+ FSNS(XML_w14, XML_checkedState), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
+ FSNS(XML_w14, XML_uncheckedState), rProp.Value.get<OUString>());
+ }
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pDataBindingAttrs.is())
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ aPropertyValue.Value >>= aGrabBag;
+ for (const auto& rProp : aGrabBag)
+ {
+ if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
+ DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
+ FSNS( XML_w, XML_prefixMappings ), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
+ DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
+ FSNS( XML_w, XML_xpath ), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
+ DocxAttributeOutput::AddToAttrList( m_pDataBindingAttrs,
+ FSNS( XML_w, XML_storeItemID ), rProp.Value.get<OUString>());
+ }
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ aPropertyValue.Value >>= aGrabBag;
+ if (aGrabBag.hasElements())
+ {
+ for (const auto& rProp : aGrabBag)
+ {
+ if (rProp.Name == "ooxml:CT_SdtText_multiLine")
+ DocxAttributeOutput::AddToAttrList(m_pTextAttrs,
+ FSNS(XML_w, XML_multiLine), rProp.Value.get<OUString>());
+ }
+ }
+ else
+ {
+ // We still have w:text, but no attrs
+ m_nSdtPrToken = FSNS(XML_w, XML_text);
+ }
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPlaceholder_docPart")
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ aPropertyValue.Value >>= aGrabBag;
+ for (const auto& rProp : std::as_const(aGrabBag))
+ {
+ OUString sValue = rProp.Value.get<OUString>();
+ if (rProp.Name == "ooxml:CT_SdtPlaceholder_docPart_val")
+ m_aPlaceHolderDocPart = sValue;
+ }
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_color")
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ aPropertyValue.Value >>= aGrabBag;
+ for (const auto& rProp : std::as_const(aGrabBag))
+ {
+ OUString sValue = rProp.Value.get<OUString>();
+ if (rProp.Name == "ooxml:CT_SdtColor_val")
+ m_aColor = sValue;
+ }
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_appearance")
+ {
+ if (!(aPropertyValue.Value >>= m_aAppearance))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt appearance value");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_showingPlcHdr")
+ {
+ if (!(aPropertyValue.Value >>= m_bShowingPlaceHolder))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt ShowingPlcHdr");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aAlias.isEmpty())
+ {
+ if (!(aPropertyValue.Value >>= m_aAlias))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt alias value");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_tag" && m_aTag.isEmpty())
+ {
+ if (!(aPropertyValue.Value >>= m_aTag))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt tag value");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id")
+ {
+ if (!(aPropertyValue.Value >>= m_nId))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt id value");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_tabIndex" && !m_nTabIndex)
+ {
+ if (!(aPropertyValue.Value >>= m_nTabIndex))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt tabIndex value");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_lock" && m_aLock.isEmpty())
+ {
+ if (!(aPropertyValue.Value >>= m_aLock))
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt lock value");
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
+ m_nSdtPrToken = FSNS(XML_w, XML_citation);
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
+ aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
+ {
+ if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
+ m_nSdtPrToken = FSNS(XML_w, XML_docPartObj);
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
+ m_nSdtPrToken = FSNS(XML_w, XML_docPartList);
+
+ uno::Sequence<beans::PropertyValue> aGrabBag;
+ aPropertyValue.Value >>= aGrabBag;
+ for (const auto& rProp : aGrabBag)
+ {
+ if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
+ FSNS(XML_w, XML_docPartGallery), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory")
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren,
+ FSNS(XML_w, XML_docPartCategory), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique")
+ {
+ OUString sValue = rProp.Value.get<OUString>();
+ if (sValue.isEmpty())
+ sValue = "true";
+ DocxAttributeOutput::AddToAttrList(m_pTokenChildren, FSNS(XML_w, XML_docPartUnique),
+ sValue);
+ }
+ }
+ }
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation")
+ m_nSdtPrToken = FSNS(XML_w, XML_equation);
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture")
+ m_nSdtPrToken = FSNS(XML_w, XML_picture);
+ else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group")
+ m_nSdtPrToken = FSNS(XML_w, XML_group);
+ else
+ SAL_WARN("sw.ww8", "GetSdtParamsFromGrabBag unhandled SdtPr grab bag property " << aPropertyValue.Name);
+ }
+}
+
+void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize)
+{
+ rtl::Reference<sax_fastparser::FastAttributeList> attrList = FastSerializerHelper::createAttrList();
+
+ const SwFormatHoriOrient& rHoriOrient = pFrameFormat->GetHoriOrient();
+ const SwFormatVertOrient& rVertOrient = pFrameFormat->GetVertOrient();
+ awt::Point aPos(rHoriOrient.GetPos(), rVertOrient.GetPos());
+
+ // A few assumptions need to be made here, because framePr is a confused mixture
+ // of (multiple) paragraph's border properties being transferred to/from a frame.
+ // The frame size describes the size BEFORE the PARAGRAPH border spacing is applied.
+ // However, we can't actually look at all the paragraphs' borders because they might be
+ // different, and all MUST specify the same frame width in order to belong to the same frame.
+ // In order for them all to be consistent, the only choice is to use the frame's border spacing.
+ // During import, the frame was assigned border spacing based on the contained paragraphs.
+ // So now at export time we have to assume that none of this has been changed by the user.
+
+ // 620 (31pt) is the maximum paragraph border spacing allowed in MS Formats,
+ // so if the value is greater than that, avoid adjusting the size - the user has interfered.
+ const sal_uInt32 nLeftBorderSpacing = pFrameFormat->GetBox().GetDistance(SvxBoxItemLine::LEFT);
+ const sal_uInt32 nRighttBorderSpacing = pFrameFormat->GetBox().GetDistance(SvxBoxItemLine::RIGHT);
+ sal_uInt32 nAdjustedWidth = rSize.Width();
+ if (nLeftBorderSpacing < 621 && nRighttBorderSpacing < 621
+ && nAdjustedWidth > nLeftBorderSpacing + nRighttBorderSpacing)
+ {
+ nAdjustedWidth -= nLeftBorderSpacing + nRighttBorderSpacing;
+ }
+ attrList->add( FSNS( XML_w, XML_w), OString::number(nAdjustedWidth));
+ attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height()));
+
+ const OString relativeFromH = convertToOOXMLHoriOrientRel(rHoriOrient.GetRelationOrient());
+ const OString relativeFromV = convertToOOXMLVertOrientRel(rVertOrient.GetRelationOrient());
+ OString aXAlign = convertToOOXMLHoriOrient(rHoriOrient.GetHoriOrient(), /*bIsPosToggle=*/false);
+ OString aYAlign = convertToOOXMLVertOrient(rVertOrient.GetVertOrient());
+ if (!aXAlign.isEmpty())
+ attrList->add(FSNS(XML_w, XML_xAlign), aXAlign);
+ else if (aPos.X)
+ attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X));
+ if (!aYAlign.isEmpty() && relativeFromV != "text")
+ attrList->add(FSNS(XML_w, XML_yAlign), aYAlign);
+ else if (aPos.Y)
+ attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y));
+
+ sal_Int16 nLeft = pFrameFormat->GetLRSpace().GetLeft();
+ sal_Int16 nRight = pFrameFormat->GetLRSpace().GetRight();
+ sal_Int16 nUpper = pFrameFormat->GetULSpace().GetUpper();
+ sal_Int16 nLower = pFrameFormat->GetULSpace().GetLower();
+
+ // To emulate, on import left was ignored (set to zero) if aligned to left,
+ // so just double up the right spacing in order to prevent cutting in half each round-trip.
+ if (rHoriOrient.GetHoriOrient() == text::HoriOrientation::LEFT)
+ nLeft = nRight;
+ else if (rHoriOrient.GetHoriOrient() == text::HoriOrientation::RIGHT)
+ nRight = nLeft;
+
+ attrList->add(FSNS(XML_w, XML_hSpace), OString::number((nLeft + nRight) / 2));
+ attrList->add(FSNS(XML_w, XML_vSpace), OString::number((nUpper + nLower) / 2));
+
+ switch (pFrameFormat->GetSurround().GetValue())
+ {
+ case css::text::WrapTextMode_NONE:
+ attrList->add( FSNS( XML_w, XML_wrap), "notBeside");
+ break;
+ case css::text::WrapTextMode_DYNAMIC:
+ attrList->add(FSNS(XML_w, XML_wrap), "auto");
+ break;
+ case css::text::WrapTextMode_PARALLEL:
+ default:
+ attrList->add(FSNS(XML_w, XML_wrap), "around");
+ break;
+ }
+ attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV );
+ attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH );
+ attrList->add( FSNS( XML_w, XML_hRule), "exact");
+
+ m_pSerializer->singleElementNS( XML_w, XML_framePr, attrList );
+}
+
+bool DocxAttributeOutput::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat)
+{
+ SdrObject* pSdrObj = const_cast<SdrObject*>(rFrameFormat.FindRealSdrObject());
+ if (!pSdrObj)
+ return false;
+
+ uno::Reference<beans::XPropertySet> xPropertySet(pSdrObj->getUnoShape(), uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return false;
+
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo(xPropertySet->getPropertySetInfo());
+ if (!xPropSetInfo.is() || !xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
+ return false;
+
+ bool bRet = false;
+ uno::Sequence<beans::PropertyValue> propList;
+ xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
+ auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; });
+ if (pProp != std::cend(propList))
+ pProp->Value >>= bRet;
+
+ return bRet;
+}
+
+void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
+{
+ // write the paragraph properties + the run, already in the correct order
+ m_pSerializer->mergeTopMarks(Tag_StartParagraph_2);
+ std::vector< std::shared_ptr <ww8::Frame> > aFramePrTextbox;
+ // Write the anchored frame if any
+ // Word can't handle nested text boxes, so write them on the same level.
+ ++m_nTextFrameLevel;
+ if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
+ {
+ comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_aParagraphSdt.m_bStartedSdt, false);
+
+ assert(!m_oPostponedCustomShape);
+ m_oPostponedCustomShape.emplace();
+
+ // The for loop can change the size of m_aFramesOfParagraph, so the max size cannot be set in stone before the loop.
+ size_t nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
+ for (size_t nIndex = 0; nIndex < nFrames; ++nIndex)
+ {
+ m_bParagraphFrameOpen = true;
+ ww8::Frame aFrame = m_aFramesOfParagraph.top()[nIndex];
+ const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat();
+
+ if (!m_bWritingHeaderFooter && TextBoxIsFramePr(rFrameFormat))
+ {
+ std::shared_ptr<ww8::Frame> pFramePr = std::make_shared<ww8::Frame>(aFrame);
+ aFramePrTextbox.push_back(pFramePr);
+ }
+ else
+ {
+ if (m_aRunSdt.m_bStartedSdt)
+ {
+ // Run-level SDT still open? Close it before AlternateContent.
+ m_aRunSdt.EndSdtBlock(m_pSerializer);
+ }
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
+ m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "wps");
+ /**
+ This is to avoid AlternateContent within another AlternateContent.
+ So when Choice is Open, only write the DML Drawing instead of both DML
+ and VML Drawing in another AlternateContent.
+ **/
+ SetAlternateContentChoiceOpen( true );
+ /** Save the table info's before writing the shape
+ as there might be a new table that might get
+ spawned from within the VML & DML block and alter
+ the contents.
+ */
+ ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.m_pTableInfo;
+ //Reset the table infos after saving.
+ m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
+
+ /** FDO#71834 :
+ Save the table reference attributes before calling WriteDMLTextFrame,
+ otherwise the StartParagraph function will use the previous existing
+ table reference attributes since the variable is being shared.
+ */
+ {
+ DocxTableExportContext aDMLTableExportContext(*this);
+ m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++);
+ }
+ m_pSerializer->endElementNS(XML_mc, XML_Choice);
+ SetAlternateContentChoiceOpen( false );
+
+ // Reset table infos, otherwise the depth of the cells will be incorrect,
+ // in case the text frame had table(s) and we try to export the
+ // same table second time.
+ m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
+ //reset the tableReference.
+
+ m_pSerializer->startElementNS(XML_mc, XML_Fallback);
+ {
+ DocxTableExportContext aVMLTableExportContext(*this);
+ m_rExport.SdrExporter().writeVMLTextFrame(&aFrame);
+ }
+ m_rExport.m_pTableInfo = pOldTableInfo;
+
+ m_pSerializer->endElementNS(XML_mc, XML_Fallback);
+ m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ m_bParagraphFrameOpen = false;
+ }
+
+ nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
+ }
+ if (!m_oPostponedCustomShape->empty())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ WritePostponedCustomShape();
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ }
+ m_oPostponedCustomShape.reset();
+
+ if ( m_aFramesOfParagraph.size() )
+ m_aFramesOfParagraph.top().clear();
+
+ if (!pTextNodeInfoInner)
+ {
+ // Ending a non-table paragraph, clear floating tables before paragraph.
+ m_aFloatingTablesOfParagraph.clear();
+ }
+ }
+
+ --m_nTextFrameLevel;
+ if ( m_aFramesOfParagraph.size() && !m_nTextFrameLevel )
+ m_aFramesOfParagraph.pop();
+
+ /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed.
+ * This is due to nested hyperlink tags. So close it before end of paragraph.
+ */
+ if(m_nHyperLinkCount.back() > 0)
+ {
+ for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount.back(); ++nHyperLinkToClose)
+ m_pSerializer->endElementNS( XML_w, XML_hyperlink );
+ }
+ m_nHyperLinkCount.pop_back();
+
+ if (m_aRunSdt.m_bStartedSdt)
+ {
+ // Run-level SDT still open? Close it now.
+ m_aRunSdt.EndSdtBlock(m_pSerializer);
+ }
+
+ if (m_bPageBreakAfter)
+ {
+ // tdf#128889 Trailing page break
+ SectionBreak(msword::PageBreak, false);
+ m_bPageBreakAfter = false;
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_p );
+ // on export sdt blocks are never nested ATM
+ if (!m_bAnchorLinkedToNode && !m_aParagraphSdt.m_bStartedSdt)
+ {
+ m_aParagraphSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, m_rExport.SdrExporter().IsParagraphHasDrawing());
+
+ if (m_aParagraphSdt.m_bStartedSdt)
+ {
+ if (m_tableReference.m_bTableCellOpen)
+ m_tableReference.m_bTableCellParaSdtOpen = true;
+ if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
+ m_rExport.SdrExporter().setParagraphSdtOpen(true);
+ }
+ }
+ else
+ {
+ //These should be written out to the actual Node and not to the anchor.
+ //Clear them as they will be repopulated when the node is processed.
+ m_aParagraphSdt.m_nSdtPrToken = 0;
+ m_aParagraphSdt.DeleteAndResetTheLists();
+ }
+
+ m_pSerializer->mark(Tag_StartParagraph_2);
+
+ // Write framePr
+ for ( const auto & pFrame : aFramePrTextbox )
+ {
+ DocxTableExportContext aTableExportContext(*this);
+ m_aFramePr.SetFrame(pFrame.get(), !m_xTableWrt ? -1 : m_tableReference.m_nTableDepth);
+ m_rExport.SdrExporter().writeOnlyTextOfFrame(pFrame.get());
+ m_aFramePr.SetFrame(nullptr);
+ }
+
+ m_pSerializer->mergeTopMarks(Tag_StartParagraph_2, sax_fastparser::MergeMarks::PREPEND);
+
+ //sdtcontent is written so Set m_bParagraphHasDrawing to false
+ m_rExport.SdrExporter().setParagraphHasDrawing(false);
+ m_bRunTextIsOn = false;
+ m_pSerializer->mergeTopMarks(Tag_StartParagraph_1);
+
+ aFramePrTextbox.clear();
+ // Check for end of cell, rows, tables here
+ FinishTableRowCell( pTextNodeInfoInner );
+
+ if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
+ m_bParagraphOpened = false;
+
+ // Clear bookmarks of the current paragraph
+ m_aBookmarksOfParagraphStart.clear();
+ m_aBookmarksOfParagraphEnd.clear();
+}
+
+#define MAX_CELL_IN_WORD 62
+
+void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow)
+{
+ sal_Int32 nOpenCell = m_LastOpenCell.back();
+ if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD)
+ EndTableCell(nOpenCell);
+
+ sal_Int32 nClosedCell = m_LastClosedCell.back();
+ for (sal_Int32 i = nClosedCell+1; i < nCell; ++i)
+ {
+ if (i >= MAX_CELL_IN_WORD)
+ break;
+
+ if (i == 0)
+ StartTableRow(pInner);
+
+ StartTableCell(pInner, i, nRow);
+ m_pSerializer->singleElementNS(XML_w, XML_p);
+ EndTableCell(i);
+ }
+}
+
+void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph )
+{
+ if ( !pInner )
+ return;
+
+ // Where are we in the table
+ sal_uInt32 nRow = pInner->getRow();
+ sal_Int32 nCell = pInner->getCell();
+
+ InitTableHelper( pInner );
+
+ // HACK
+ // msoffice seems to have an internal limitation of 63 columns for tables
+ // and refuses to load .docx with more, even though the spec seems to allow that;
+ // so simply if there are more columns, don't close the last one msoffice will handle
+ // and merge the contents of the remaining ones into it (since we don't close the cell
+ // here, following ones will not be opened)
+ const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine());
+ const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround;
+ const bool bEndRow = pInner->isEndOfLine();
+
+ if (bEndCell)
+ {
+ while (pInner->getDepth() < m_tableReference.m_nTableDepth)
+ {
+ //we expect that the higher depth row was closed, and
+ //we are just missing the table close
+ assert(m_LastOpenCell.back() == -1 && m_LastClosedCell.back() == -1);
+ EndTable();
+ }
+
+ SyncNodelessCells(pInner, nCell, nRow);
+
+ sal_Int32 nClosedCell = m_LastClosedCell.back();
+ if (nCell == nClosedCell)
+ {
+ //Start missing trailing cell(s)
+ ++nCell;
+ StartTableCell(pInner, nCell, nRow);
+
+ //Continue on missing next trailing cell(s)
+ ww8::RowSpansPtr xRowSpans = pInner->getRowSpansOfRow();
+ sal_Int32 nRemainingCells = xRowSpans->size() - nCell;
+ for (sal_Int32 i = 1; i < nRemainingCells; ++i)
+ {
+ if (bForceEmptyParagraph)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_p);
+ }
+
+ EndTableCell(nCell);
+
+ StartTableCell(pInner, nCell, nRow);
+ }
+ }
+
+ if (bForceEmptyParagraph)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_p);
+ }
+
+ EndTableCell(nCell);
+ }
+
+ // This is a line end
+ if (bEndRow)
+ EndTableRow();
+
+ // This is the end of the table
+ if (pInner->isFinalEndOfLine())
+ EndTable();
+}
+
+void DocxAttributeOutput::EmptyParagraph()
+{
+ m_pSerializer->singleElementNS(XML_w, XML_p);
+}
+
+void DocxAttributeOutput::SectionBreaks(const SwNode& rNode)
+{
+ // output page/section breaks
+ // Writer can have them at the beginning of a paragraph, or at the end, but
+ // in docx, we have to output them in the paragraph properties of the last
+ // paragraph in a section. To get it right, we have to switch to the next
+ // paragraph, and detect the section breaks there.
+ SwNodeIndex aNextIndex( rNode, 1 );
+
+ if (rNode.IsTextNode() || rNode.IsSectionNode())
+ {
+ if (aNextIndex.GetNode().IsTextNode())
+ {
+ const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
+ m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference.m_bTableCellOpen);
+ }
+ else if (aNextIndex.GetNode().IsTableNode())
+ {
+ const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
+ const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
+ m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
+ }
+ }
+ else if (rNode.IsEndNode())
+ {
+ if (aNextIndex.GetNode().IsTextNode())
+ {
+ // Handle section break between a table and a text node following it.
+ // Also handle section endings
+ const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
+ if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode())
+ m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference.m_bTableCellOpen);
+ }
+ else if (aNextIndex.GetNode().IsTableNode())
+ {
+ // Handle section break between tables.
+ const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
+ const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
+ m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
+ }
+ }
+}
+
+void DocxAttributeOutput::StartParagraphProperties()
+{
+ m_pSerializer->mark(Tag_StartParagraphProperties);
+
+ m_pSerializer->startElementNS(XML_w, XML_pPr);
+
+ // and output the section break now (if it appeared)
+ if (m_pSectionInfo && m_rExport.m_nTextTyp == TXT_MAINTEXT)
+ {
+ m_rExport.SectionProperties( *m_pSectionInfo );
+ m_pSectionInfo.reset();
+ }
+
+ InitCollectedParagraphProperties();
+}
+
+void DocxAttributeOutput::InitCollectedParagraphProperties()
+{
+ m_pLRSpaceAttrList.clear();
+ m_pParagraphSpacingAttrList.clear();
+
+ // Write the elements in the spec order
+ static const sal_Int32 aOrder[] =
+ {
+ FSNS( XML_w, XML_pStyle ),
+ FSNS( XML_w, XML_keepNext ),
+ FSNS( XML_w, XML_keepLines ),
+ FSNS( XML_w, XML_pageBreakBefore ),
+ FSNS( XML_w, XML_framePr ),
+ FSNS( XML_w, XML_widowControl ),
+ FSNS( XML_w, XML_numPr ),
+ FSNS( XML_w, XML_suppressLineNumbers ),
+ FSNS( XML_w, XML_pBdr ),
+ FSNS( XML_w, XML_shd ),
+ FSNS( XML_w, XML_tabs ),
+ FSNS( XML_w, XML_suppressAutoHyphens ),
+ FSNS( XML_w, XML_kinsoku ),
+ FSNS( XML_w, XML_wordWrap ),
+ FSNS( XML_w, XML_overflowPunct ),
+ FSNS( XML_w, XML_topLinePunct ),
+ FSNS( XML_w, XML_autoSpaceDE ),
+ FSNS( XML_w, XML_autoSpaceDN ),
+ FSNS( XML_w, XML_bidi ),
+ FSNS( XML_w, XML_adjustRightInd ),
+ FSNS( XML_w, XML_snapToGrid ),
+ FSNS( XML_w, XML_spacing ),
+ FSNS( XML_w, XML_ind ),
+ FSNS( XML_w, XML_contextualSpacing ),
+ FSNS( XML_w, XML_mirrorIndents ),
+ FSNS( XML_w, XML_suppressOverlap ),
+ FSNS( XML_w, XML_jc ),
+ FSNS( XML_w, XML_textDirection ),
+ FSNS( XML_w, XML_textAlignment ),
+ FSNS( XML_w, XML_textboxTightWrap ),
+ FSNS( XML_w, XML_outlineLvl ),
+ FSNS( XML_w, XML_divId ),
+ FSNS( XML_w, XML_cnfStyle ),
+ FSNS( XML_w, XML_rPr ),
+ FSNS( XML_w, XML_sectPr ),
+ FSNS( XML_w, XML_pPrChange )
+ };
+
+ // postpone the output so that we can later [in EndParagraphProperties()]
+ // prepend the properties before the run
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ m_pSerializer->mark(Tag_InitCollectedParagraphProperties, comphelper::containerToSequence(aOrder));
+}
+
+void DocxAttributeOutput::WriteCollectedParagraphProperties()
+{
+ if ( m_rExport.SdrExporter().getFlyAttrList().is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_framePr,
+ detachFrom(m_rExport.SdrExporter().getFlyAttrList() ) );
+ }
+
+ if (m_pLRSpaceAttrList.is())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_ind, detachFrom(m_pLRSpaceAttrList));
+ }
+
+ if ( m_pParagraphSpacingAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_spacing, detachFrom( m_pParagraphSpacingAttrList ) );
+ }
+
+ if ( m_pBackgroundAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_shd, detachFrom( m_pBackgroundAttrList ) );
+ m_aFramePr.SetUseFrameBackground(false);
+ }
+}
+
+namespace
+{
+
+/// Outputs an item set, that contains the formatting of the paragraph marker.
+void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties)
+{
+ const SfxItemSet* pOldI = rAttributeOutput.GetExport().GetCurItemSet();
+ rAttributeOutput.GetExport().SetCurItemSet(&rParagraphMarkerProperties);
+
+ SfxWhichIter aIter(rParagraphMarkerProperties);
+ sal_uInt16 nWhichId = aIter.FirstWhich();
+ const SfxPoolItem* pItem = nullptr;
+ // Did we already produce a <w:sz> element?
+ bool bFontSizeWritten = false;
+ bool bBoldWritten = false;
+ while (nWhichId)
+ {
+ if (aIter.GetItemState(true, &pItem) == SfxItemState::SET)
+ {
+ if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT)
+ {
+ // Will this item produce a <w:sz> element?
+ bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE;
+ bool bBoldItem = nWhichId == RES_CHRATR_WEIGHT || nWhichId == RES_CHRATR_CJK_WEIGHT;
+ if (!(bFontSizeWritten && bFontSizeItem) && !(bBoldWritten && bBoldItem))
+ rAttributeOutput.OutputItem(*pItem);
+ if (bFontSizeItem)
+ bFontSizeWritten = true;
+ if (bBoldItem)
+ bBoldWritten = true;
+ }
+ else if (nWhichId == RES_TXTATR_AUTOFMT)
+ {
+ const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem);
+ lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle());
+ }
+ }
+ nWhichId = aIter.NextWhich();
+ }
+ rAttributeOutput.GetExport().SetCurItemSet(pOldI);
+}
+
+const char *RubyAlignValues[] =
+{
+ "center",
+ "distributeLetter",
+ "distributeSpace",
+ "left",
+ "right",
+ "rightVertical"
+};
+
+
+const char *lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC)
+{
+ const sal_Int32 nElements = SAL_N_ELEMENTS(RubyAlignValues);
+ if ( nJC >=0 && nJC < nElements )
+ return RubyAlignValues[nJC];
+ return RubyAlignValues[0];
+}
+
+}
+
+void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted)
+{
+ // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties.
+ // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
+
+ // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline().
+ // As there will be another pPr for redline and LO might mix both.
+ if(pRedlineData)
+ WriteCollectedParagraphProperties();
+ Redline( pRedlineData );
+
+ WriteCollectedParagraphProperties();
+
+ // Merge the marks for the ordered elements
+ m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
+
+ // Write 'Paragraph Mark' properties
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ // mark() before paragraph mark properties child elements.
+ InitCollectedRunProperties();
+
+ // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information
+ // that should be collected by different properties in the core, and are all flushed together
+ // to the DOCX when the function 'WriteCollectedRunProperties' gets called.
+ // So we need to store the current status of these lists, so that we can revert back to them when
+ // we are done exporting the redline attributes.
+ auto pFontsAttrList_Original(detachFrom(m_pFontsAttrList));
+ auto pEastAsianLayoutAttrList_Original(detachFrom(m_pEastAsianLayoutAttrList));
+ auto pCharLangAttrList_Original(detachFrom(m_pCharLangAttrList));
+
+ lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties);
+
+ // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
+ WriteCollectedRunProperties();
+
+ // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
+ m_pFontsAttrList = std::move(pFontsAttrList_Original);
+ m_pEastAsianLayoutAttrList = std::move(pEastAsianLayoutAttrList_Original);
+ m_pCharLangAttrList = std::move(pCharLangAttrList_Original);
+
+ if ( pRedlineParagraphMarkerDeleted )
+ {
+ StartRedline( pRedlineParagraphMarkerDeleted, /*bLastRun=*/true );
+ EndRedline( pRedlineParagraphMarkerDeleted, /*bLastRun=*/true );
+ }
+ if ( pRedlineParagraphMarkerInserted )
+ {
+ StartRedline( pRedlineParagraphMarkerInserted, /*bLastRun=*/true );
+ EndRedline( pRedlineParagraphMarkerInserted, /*bLastRun=*/true );
+ }
+
+ // mergeTopMarks() after paragraph mark properties child elements.
+ m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+
+ if (!m_bWritingHeaderFooter && m_aFramePr.Frame())
+ {
+ const SwFrameFormat& rFrameFormat = m_aFramePr.Frame()->GetFrameFormat();
+ assert(TextBoxIsFramePr(rFrameFormat) && "by definition, because Frame()");
+
+ const Size aSize = m_aFramePr.Frame()->GetSize();
+ PopulateFrameProperties(&rFrameFormat, aSize);
+
+ // if the paragraph itself never called FormatBox, do so now
+ if (m_aFramePr.UseFrameBorders(!m_xTableWrt ? -1 : m_tableReference.m_nTableDepth))
+ FormatBox(rFrameFormat.GetBox());
+
+ if (m_aFramePr.UseFrameBackground())
+ {
+ // The frame is usually imported as 100% transparent. Ignore in that case.
+ // Background only exports as fully opaque. Emulate - ignore transparency more than 50%
+ const SwAttrSet& rSet = rFrameFormat.GetAttrSet();
+ const XFillStyleItem* pFillStyle(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
+ if (pFillStyle && pFillStyle->GetValue() != drawing::FillStyle_NONE)
+ {
+ std::unique_ptr<SvxBrushItem> pBrush(
+ getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
+ if (pBrush->GetColor().GetAlpha() > 127) // more opaque than transparent
+ {
+ FormatBackground(*pBrush);
+ WriteCollectedParagraphProperties();
+ }
+ }
+ }
+
+ if (m_aFramePr.UseFrameTextDirection(!m_xTableWrt ? -1 : m_tableReference.m_nTableDepth))
+ {
+ const SvxFrameDirectionItem& rFrameDir = rFrameFormat.GetFrameDir();
+ if (rFrameDir.GetValue() != SvxFrameDirection::Environment)
+ {
+ assert(!m_rExport.m_bOutPageDescs);
+ // hack: use existing variable to write out the full TextDirection attribute.
+ // This is valid for paragraphs/styles - just not native in LO, so hack for now.
+ m_rExport.m_bOutPageDescs = true;
+ FormatFrameDirection(rFrameDir);
+ m_rExport.m_bOutPageDescs = false;
+ }
+ }
+
+ // reset to true in preparation for the next paragraph in the frame
+ m_aFramePr.SetUseFrameBorders(true);
+ m_aFramePr.SetUseFrameBackground(true);
+ m_aFramePr.SetUseFrameTextDirection(true);
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_pPr );
+
+ // RDF metadata for this text node.
+ SwTextNode* pTextNode = m_rExport.m_pCurPam->GetPointNode().GetTextNode();
+ std::map<OUString, OUString> aStatements;
+ if (pTextNode)
+ aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode);
+ if (!aStatements.empty())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_smartTag,
+ FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ FSNS(XML_w, XML_element), "RDF");
+ m_pSerializer->startElementNS(XML_w, XML_smartTagPr);
+ for (const auto& rStatement : aStatements)
+ m_pSerializer->singleElementNS(XML_w, XML_attr,
+ FSNS(XML_w, XML_name), rStatement.first,
+ FSNS(XML_w, XML_val), rStatement.second);
+ m_pSerializer->endElementNS(XML_w, XML_smartTagPr);
+ m_pSerializer->endElementNS(XML_w, XML_smartTag);
+ }
+
+ if ((m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE)
+ && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column");
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ if ( m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE )
+ m_nColBreakStatus = COLBRK_POSTPONE;
+ else
+ m_nColBreakStatus = COLBRK_NONE;
+ }
+
+ if (m_bPostponedPageBreak && !m_bWritingHeaderFooter
+ && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ m_bPostponedPageBreak = false;
+ }
+
+ // merge the properties _before_ the run (strictly speaking, just
+ // after the start of the paragraph)
+ m_pSerializer->mergeTopMarks(Tag_StartParagraphProperties, sax_fastparser::MergeMarks::PREPEND);
+}
+
+void DocxAttributeOutput::SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame )
+{
+ m_nStateOfFlyFrame = nStateOfFlyFrame;
+}
+
+void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode )
+{
+ m_bAnchorLinkedToNode = bAnchorLinkedToNode ;
+}
+
+void DocxAttributeOutput::ResetFlyProcessingFlag()
+{
+ m_bPostponedProcessingFly = false ;
+}
+
+bool DocxAttributeOutput::IsFlyProcessingPostponed()
+{
+ return m_bPostponedProcessingFly;
+}
+
+void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ )
+{
+ // Don't start redline data here, possibly there is a hyperlink later, and
+ // that has to be started first.
+ m_pRedlineData = pRedlineData;
+
+ // this mark is used to be able to enclose the run inside a sdr tag.
+ m_pSerializer->mark(Tag_StartRun_1);
+
+ // postpone the output of the start of a run (there are elements that need
+ // to be written before the start of the run, but we learn which they are
+ // _inside_ of the run)
+ m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start"
+
+ // postpone the output of the text (we get it before the run properties,
+ // but must write it after them)
+ m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text"
+}
+
+void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool bLastRun)
+{
+ int nFieldsInPrevHyperlink = m_nFieldsInHyperlink;
+ // Reset m_nFieldsInHyperlink if a new hyperlink is about to start
+ if ( m_pHyperlinkAttrList.is() )
+ {
+ m_nFieldsInHyperlink = 0;
+ }
+
+ // Write field starts
+ for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); )
+ {
+ // Add the fields starts for all but hyperlinks and TOCs
+ if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN &&
+ // it is not an input field with extra grabbag params (sdt field)
+ (!(pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements()))
+ )
+ {
+ StartField_Impl( pNode, nPos, *pIt );
+
+ // Remove the field from the stack if only the start has to be written
+ // Unknown fields should be removed too
+ if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) )
+ {
+ pIt = m_Fields.erase( pIt );
+ continue;
+ }
+
+ if (m_nHyperLinkCount.back() > 0 || m_pHyperlinkAttrList.is())
+ {
+ ++m_nFieldsInHyperlink;
+ }
+ }
+ ++pIt;
+ }
+
+ // write the run properties + the text, already in the correct order
+ m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above
+
+ // level down, to be able to prepend the actual run start attribute (just
+ // before "postponed run start")
+ m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start"
+ bool bCloseEarlierSDT = false;
+
+ if (m_bEndCharSdt)
+ {
+ // This is the common case: "close sdt before the current run" was requested by the next run.
+
+ // if another sdt starts in this run, then wait
+ // as closing the sdt now, might cause nesting of sdts
+ if (m_aRunSdt.m_nSdtPrToken > 0)
+ bCloseEarlierSDT = true;
+ else
+ m_aRunSdt.EndSdtBlock(m_pSerializer);
+ m_bEndCharSdt = false;
+ }
+
+ if ( m_closeHyperlinkInPreviousRun )
+ {
+ if (m_nHyperLinkCount.back() > 0)
+ {
+ for ( int i = 0; i < nFieldsInPrevHyperlink; i++ )
+ {
+ // If fields begin before hyperlink then
+ // it should end before hyperlink close
+ EndField_Impl( pNode, nPos, m_Fields.back( ) );
+ m_Fields.pop_back();
+ }
+ m_pSerializer->endElementNS( XML_w, XML_hyperlink );
+ m_endPageRef = false;
+ m_nHyperLinkCount.back()--;
+ m_closeHyperlinkInPreviousRun = false;
+ }
+ else
+ {
+ bool bIsStartedHyperlink = false;
+ for (const sal_Int32 nLinkCount : m_nHyperLinkCount)
+ {
+ if (nLinkCount > 0)
+ {
+ bIsStartedHyperlink = true;
+ break;
+ }
+ }
+ if (!bIsStartedHyperlink)
+ m_closeHyperlinkInPreviousRun = false;
+ }
+ }
+
+ // Write the hyperlink and toc fields starts
+ for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
+ {
+ // Add the fields starts for hyperlinks, TOCs and index marks
+ if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN ||
+ // InputField with extra grabbag params - it is sdt field
+ (pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements())))
+ {
+ StartRedline( m_pRedlineData, bLastRun );
+ StartField_Impl( pNode, nPos, *pIt, true );
+ EndRedline( m_pRedlineData, bLastRun );
+
+ if (m_nHyperLinkCount.back() > 0)
+ ++m_nFieldsInHyperlink;
+
+ // Remove the field if no end needs to be written
+ if (!pIt->bSep)
+ {
+ pIt = m_Fields.erase( pIt );
+ continue;
+ }
+ }
+ if (pIt->bSep && !pIt->pField)
+ {
+ // for TOXMark:
+ // Word ignores bookmarks in field result that is empty;
+ // work around this by writing bookmark into field command.
+ if (!m_sFieldBkm.isEmpty())
+ {
+ DoWriteBookmarkTagStart(m_sFieldBkm);
+ DoWriteBookmarkTagEnd(m_nNextBookmarkId);
+ m_nNextBookmarkId++;
+ m_sFieldBkm.clear();
+ }
+ CmdEndField_Impl(pNode, nPos, true);
+ // Remove the field if no end needs to be written
+ if (!pIt->bClose)
+ {
+ pIt = m_Fields.erase( pIt );
+ continue;
+ }
+ }
+ ++pIt;
+ }
+
+ // Start the hyperlink after the fields separators or we would generate invalid file
+ bool newStartedHyperlink(false);
+ if ( m_pHyperlinkAttrList.is() )
+ {
+ // if we are ending a hyperlink and there's another one starting here,
+ // don't do this, so that the fields are closed further down when
+ // the end hyperlink is handled, which is more likely to put the end in
+ // the right place, as far as i can tell (not very far in this muck)
+ if (!m_closeHyperlinkInThisRun)
+ {
+ // end ToX fields that want to end _before_ starting the hyperlink
+ for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
+ {
+ if (it->bClose && !it->pField)
+ {
+ EndField_Impl( pNode, nPos, *it );
+ it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+ newStartedHyperlink = true;
+
+ m_pSerializer->startElementNS( XML_w, XML_hyperlink, detachFrom( m_pHyperlinkAttrList ) );
+ m_nHyperLinkCount.back()++;
+ }
+
+ // if there is some redlining in the document, output it
+ StartRedline( m_pRedlineData, bLastRun );
+
+ // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks.
+ // The same is applied for permission ranges.
+ // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end.
+ DoWriteBookmarksStart(m_rBookmarksStart, m_pMoveRedlineData);
+ DoWriteBookmarksEnd(m_rBookmarksEnd);
+ DoWritePermissionsStart();
+ DoWriteAnnotationMarks();
+
+ if (m_closeHyperlinkInThisRun && m_nHyperLinkCount.back() > 0 && !m_hyperLinkAnchor.isEmpty()
+ && m_hyperLinkAnchor.startsWith("_Toc"))
+ {
+ OUString sToken;
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS(XML_w, XML_webHidden);
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ m_pSerializer->startElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "begin");
+ m_pSerializer->endElementNS( XML_w, XML_fldChar );
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS(XML_w, XML_webHidden);
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph.
+ DoWriteCmd( sToken );
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ // Write the Field separator
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS(XML_w, XML_webHidden);
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ m_pSerializer->singleElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "separate" );
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ // At start of every "PAGEREF" field m_endPageRef value should be true.
+ m_endPageRef = true;
+ }
+
+ DoWriteBookmarkStartIfExist(nPos);
+
+ if (nLen != -1)
+ {
+ SwTextAttr* pAttr = pNode->GetTextAttrAt(nPos, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Default);
+ if (pAttr && pAttr->GetStart() == nPos)
+ {
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ m_pContentControl = pTextContentControl->GetContentControl().GetContentControl();
+ if (!m_tableReference.m_bTableCellChanged)
+ {
+ WriteContentControlStart();
+ }
+ }
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is())
+ {
+ RunText("\t") ;
+ }
+ m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above
+
+ if ( !m_sRawText.isEmpty() )
+ {
+ RunText( m_sRawText );
+ m_sRawText.clear();
+ }
+
+ // write the run start + the run content
+ m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start"
+ // append the actual run end
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ if (nLen != -1)
+ {
+ sal_Int32 nEnd = nPos + nLen;
+ SwTextAttr* pAttr = pNode->GetTextAttrAt(nPos, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Default);
+ if (pAttr && *pAttr->GetEnd() == nEnd && !m_tableReference.m_bTableCellChanged)
+ {
+ WriteContentControlEnd();
+ }
+ }
+
+ // if there is some redlining in the document, output it
+ // (except in the case of fields with multiple runs)
+ EndRedline( m_pRedlineData, bLastRun );
+
+ // enclose in a sdt block, if necessary: if one is already started, then don't do it for now
+ // (so on export sdt blocks are never nested ATM)
+ if ( !m_bAnchorLinkedToNode && !m_aRunSdt.m_bStartedSdt)
+ {
+ m_aRunSdt.WriteSdtBlock(m_pSerializer, m_bRunTextIsOn, m_rExport.SdrExporter().IsParagraphHasDrawing());
+ }
+ else
+ {
+ //These should be written out to the actual Node and not to the anchor.
+ //Clear them as they will be repopulated when the node is processed.
+ m_aRunSdt.m_nSdtPrToken = 0;
+ m_aRunSdt.DeleteAndResetTheLists();
+ }
+
+ if (bCloseEarlierSDT)
+ {
+ m_pSerializer->mark(Tag_EndRun_2);
+ m_aRunSdt.EndSdtBlock(m_pSerializer);
+ m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND);
+ }
+
+ m_pSerializer->mergeTopMarks(Tag_StartRun_1);
+
+ // XML_r node should be surrounded with permission-begin and permission-end nodes if it has permission.
+ DoWritePermissionsEnd();
+
+ for (const auto& rpMath : m_aPostponedMaths)
+ WritePostponedMath(rpMath.pMathObject, rpMath.nMathObjAlignment);
+ m_aPostponedMaths.clear();
+
+ for (const auto& rpControl : m_aPostponedFormControls)
+ WritePostponedFormControl(rpControl);
+ m_aPostponedFormControls.clear();
+
+ WritePostponedActiveXControl(false);
+
+ WritePendingPlaceholder();
+
+ if ( !m_bWritingField )
+ {
+ m_pRedlineData = nullptr;
+ }
+
+ if ( m_closeHyperlinkInThisRun )
+ {
+ if (m_nHyperLinkCount.back() > 0)
+ {
+ if( m_endPageRef )
+ {
+ // Hyperlink is started and fldchar "end" needs to be written for PAGEREF
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS(XML_w, XML_webHidden);
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ m_pSerializer->singleElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "end" );
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ m_endPageRef = false;
+ m_hyperLinkAnchor.clear();
+ }
+ for ( int i = 0; i < m_nFieldsInHyperlink; i++ )
+ {
+ // If fields begin after hyperlink start then
+ // it should end before hyperlink close
+ EndField_Impl( pNode, nPos, m_Fields.back( ) );
+ m_Fields.pop_back();
+ }
+ m_nFieldsInHyperlink = 0;
+
+ m_pSerializer->endElementNS( XML_w, XML_hyperlink );
+ m_nHyperLinkCount.back()--;
+ m_closeHyperlinkInThisRun = false;
+ }
+ else
+ {
+ bool bIsStartedHyperlink = false;
+ for (const sal_Int32 nLinkCount : m_nHyperLinkCount)
+ {
+ if (nLinkCount > 0)
+ {
+ bIsStartedHyperlink = true;
+ break;
+ }
+ }
+ if (!bIsStartedHyperlink)
+ m_closeHyperlinkInThisRun = false;
+ }
+ }
+
+ if (!newStartedHyperlink)
+ {
+ while ( m_Fields.begin() != m_Fields.end() )
+ {
+ EndField_Impl( pNode, nPos, m_Fields.front( ) );
+ m_Fields.erase( m_Fields.begin( ) );
+ }
+ m_nFieldsInHyperlink = 0;
+ }
+
+ // end ToX fields
+ for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
+ {
+ if (it->bClose && !it->pField)
+ {
+ EndField_Impl( pNode, nPos, *it );
+ it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ if ( m_pRedlineData )
+ {
+ EndRedline( m_pRedlineData, bLastRun );
+ m_pRedlineData = nullptr;
+ }
+
+ DoWriteBookmarksStart(m_rFinalBookmarksStart);
+ DoWriteBookmarksEnd(m_rFinalBookmarksEnd);
+ DoWriteBookmarkEndIfExist(nPos);
+}
+
+void DocxAttributeOutput::DoWriteBookmarkTagStart(const OUString& bookmarkName)
+{
+ m_pSerializer->singleElementNS(XML_w, XML_bookmarkStart,
+ FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId),
+ FSNS(XML_w, XML_name), GetExport().BookmarkToWord(bookmarkName));
+}
+
+void DocxAttributeOutput::DoWriteBookmarkTagEnd(sal_Int32 const nId)
+{
+ m_pSerializer->singleElementNS(XML_w, XML_bookmarkEnd,
+ FSNS(XML_w, XML_id), OString::number(nId));
+}
+
+void DocxAttributeOutput::DoWriteMoveRangeTagStart(std::u16string_view bookmarkName,
+ bool bFrom, const SwRedlineData* pRedlineData)
+{
+ bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ) && !SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
+
+ const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
+ const DateTime aDateTime = pRedlineData->GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo ||
+ ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ pAttributeList->add(FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId));
+ pAttributeList->add(FSNS(XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) )
+ : OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8));
+ if (!bNoDate)
+ pAttributeList->add(FSNS(XML_w, XML_date ), DateTimeToOString( aDateTime ));
+ pAttributeList->add(FSNS(XML_w, XML_name), bookmarkName);
+ m_pSerializer->singleElementNS( XML_w, bFrom ? XML_moveFromRangeStart : XML_moveToRangeStart, pAttributeList );
+
+ // tdf#150166 avoid of unpaired moveRangeEnd at moved ToC
+ m_rSavedBookmarksIds.insert(m_nNextBookmarkId);
+}
+
+void DocxAttributeOutput::DoWriteMoveRangeTagEnd(sal_Int32 const nId, bool bFrom)
+{
+ if ( m_rSavedBookmarksIds.count(nId) )
+ {
+ m_pSerializer->singleElementNS(XML_w, bFrom
+ ? XML_moveFromRangeEnd
+ : XML_moveToRangeEnd,
+ FSNS(XML_w, XML_id), OString::number(nId));
+
+ m_rSavedBookmarksIds.erase(nId);
+ }
+}
+
+void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nRunPos)
+{
+ auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos);
+ for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
+ {
+ DoWriteBookmarkTagStart(aIter->second);
+ m_rOpenedBookmarksIds[aIter->second] = m_nNextBookmarkId;
+ m_sLastOpenedBookmark = GetExport().BookmarkToWord(aIter->second);
+ m_nNextBookmarkId++;
+ }
+}
+
+void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nRunPos)
+{
+ auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos);
+ for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
+ {
+ // Get the id of the bookmark
+ auto pPos = m_rOpenedBookmarksIds.find(aIter->second);
+ if (pPos != m_rOpenedBookmarksIds.end())
+ {
+ // Output the bookmark
+ DoWriteBookmarkTagEnd(pPos->second);
+ m_rOpenedBookmarksIds.erase(aIter->second);
+ }
+ }
+}
+
+/// Write the start bookmarks
+void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts, const SwRedlineData* pRedlineData)
+{
+ for (const OUString & bookmarkName : rStarts)
+ {
+ // Output the bookmark (including MoveBookmark of the tracked moving)
+ bool bMove = false;
+ bool bFrom = false;
+ OUString sBookmarkName = GetExport().BookmarkToWord(bookmarkName, &bMove, &bFrom);
+ if ( bMove )
+ {
+ // TODO: redline data of MoveBookmark is restored from the first redline of the bookmark
+ // range. But a later deletion within a tracked moving is still imported as plain
+ // deletion, so check IsMoved() and skip the export of the tracked moving to avoid
+ // export with bad author or date
+ if ( pRedlineData && pRedlineData->IsMoved() )
+ DoWriteMoveRangeTagStart(sBookmarkName, bFrom, pRedlineData);
+ }
+ else
+ DoWriteBookmarkTagStart(bookmarkName);
+
+ m_rOpenedBookmarksIds[bookmarkName] = m_nNextBookmarkId;
+ m_sLastOpenedBookmark = sBookmarkName;
+ m_nNextBookmarkId++;
+ }
+ rStarts.clear();
+}
+
+/// export the end bookmarks
+void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds)
+{
+ for (const OUString & bookmarkName : rEnds)
+ {
+ // Get the id of the bookmark
+ auto pPos = m_rOpenedBookmarksIds.find(bookmarkName);
+
+ if (pPos != m_rOpenedBookmarksIds.end())
+ {
+ bool bMove = false;
+ bool bFrom = false;
+ GetExport().BookmarkToWord(bookmarkName, &bMove, &bFrom);
+ // Output the bookmark (including MoveBookmark of the tracked moving)
+ if ( bMove )
+ DoWriteMoveRangeTagEnd(pPos->second, bFrom);
+ else
+ DoWriteBookmarkTagEnd(pPos->second);
+
+ m_rOpenedBookmarksIds.erase(bookmarkName);
+ }
+ }
+ rEnds.clear();
+}
+
+// For construction of the special bookmark name template for permissions:
+// see, PermInsertPosition::createBookmarkName()
+//
+// Syntax:
+// - "permission-for-user:<permission-id>:<permission-user-name>"
+// - "permission-for-group:<permission-id>:<permission-group-name>"
+//
+void DocxAttributeOutput::DoWritePermissionTagStart(std::u16string_view permission)
+{
+ std::u16string_view permissionIdAndName;
+
+ if (o3tl::starts_with(permission, u"permission-for-group:", &permissionIdAndName))
+ {
+ const std::size_t separatorIndex = permissionIdAndName.find(u':');
+ assert(separatorIndex != std::u16string_view::npos);
+ const OUString permissionId(permissionIdAndName.substr(0, separatorIndex));
+ const OUString permissionName(permissionIdAndName.substr(separatorIndex + 1));
+
+ m_pSerializer->singleElementNS(XML_w, XML_permStart,
+ FSNS(XML_w, XML_id), GetExport().BookmarkToWord(permissionId),
+ FSNS(XML_w, XML_edGrp), GetExport().BookmarkToWord(permissionName));
+ }
+ else
+ {
+ auto const ok = o3tl::starts_with(
+ permission, u"permission-for-user:", &permissionIdAndName);
+ assert(ok); (void)ok;
+ const std::size_t separatorIndex = permissionIdAndName.find(u':');
+ assert(separatorIndex != std::u16string_view::npos);
+ const OUString permissionId(permissionIdAndName.substr(0, separatorIndex));
+ const OUString permissionName(permissionIdAndName.substr(separatorIndex + 1));
+
+ m_pSerializer->singleElementNS(XML_w, XML_permStart,
+ FSNS(XML_w, XML_id), GetExport().BookmarkToWord(permissionId),
+ FSNS(XML_w, XML_ed), GetExport().BookmarkToWord(permissionName));
+ }
+}
+
+
+// For construction of the special bookmark name template for permissions:
+// see, PermInsertPosition::createBookmarkName()
+//
+// Syntax:
+// - "permission-for-user:<permission-id>:<permission-user-name>"
+// - "permission-for-group:<permission-id>:<permission-group-name>"
+//
+void DocxAttributeOutput::DoWritePermissionTagEnd(std::u16string_view permission)
+{
+ std::u16string_view permissionIdAndName;
+
+ auto const ok = o3tl::starts_with(permission, u"permission-for-group:", &permissionIdAndName) ||
+ o3tl::starts_with(permission, u"permission-for-user:", &permissionIdAndName);
+ assert(ok); (void)ok;
+
+ const std::size_t separatorIndex = permissionIdAndName.find(u':');
+ assert(separatorIndex != std::u16string_view::npos);
+ const OUString permissionId(permissionIdAndName.substr(0, separatorIndex));
+
+ m_pSerializer->singleElementNS(XML_w, XML_permEnd,
+ FSNS(XML_w, XML_id), GetExport().BookmarkToWord(permissionId));
+}
+
+/// Write the start permissions
+void DocxAttributeOutput::DoWritePermissionsStart()
+{
+ for (const OUString & permission : m_rPermissionsStart)
+ {
+ DoWritePermissionTagStart(permission);
+ }
+ m_rPermissionsStart.clear();
+}
+
+/// export the end permissions
+void DocxAttributeOutput::DoWritePermissionsEnd()
+{
+ for (const OUString & permission : m_rPermissionsEnd)
+ {
+ DoWritePermissionTagEnd(permission);
+ }
+ m_rPermissionsEnd.clear();
+}
+
+void DocxAttributeOutput::DoWriteAnnotationMarks()
+{
+ // Write the start annotation marks
+ for ( const auto & rName : m_rAnnotationMarksStart )
+ {
+ // Output the annotation mark
+ /* Ensure that the existing Annotation Marks are not overwritten
+ as it causes discrepancy when DocxAttributeOutput::PostitField
+ refers to this map & while mapping comment id's in document.xml &
+ comment.xml.
+ */
+ if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) )
+ {
+ const sal_Int32 nId = m_nNextAnnotationMarkId++;
+ m_rOpenedAnnotationMarksIds[rName] = nId;
+ m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart,
+ FSNS( XML_w, XML_id ), OString::number(nId) );
+ m_sLastOpenedAnnotationMark = rName;
+ }
+ }
+ m_rAnnotationMarksStart.clear();
+
+ // export the end annotation marks
+ for ( const auto & rName : m_rAnnotationMarksEnd )
+ {
+ // Get the id of the annotation mark
+ auto pPos = m_rOpenedAnnotationMarksIds.find( rName );
+ if ( pPos != m_rOpenedAnnotationMarksIds.end( ) )
+ {
+ const sal_Int32 nId = ( *pPos ).second;
+ m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd,
+ FSNS( XML_w, XML_id ), OString::number(nId) );
+ m_rOpenedAnnotationMarksIds.erase( rName );
+
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ),
+ OString::number(nId) );
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ }
+ }
+ m_rAnnotationMarksEnd.clear();
+}
+
+void DocxAttributeOutput::WriteFFData( const FieldInfos& rInfos )
+{
+ const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark;
+ FieldMarkParamsHelper params( rFieldmark );
+
+ OUString sEntryMacro;
+ params.extractParam("EntryMacro", sEntryMacro);
+ OUString sExitMacro;
+ params.extractParam("ExitMacro", sExitMacro);
+ OUString sHelp;
+ params.extractParam("Help", sHelp);
+ OUString sHint;
+ params.extractParam("Hint", sHint); // .docx StatusText
+ if ( sHint.isEmpty() )
+ params.extractParam("Description", sHint); // .doc StatusText
+
+ if ( rInfos.eType == ww::eFORMDROPDOWN )
+ {
+ uno::Sequence< OUString> vListEntries;
+ OUString sName, sSelected;
+
+ params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries );
+ if (vListEntries.getLength() > ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT)
+ vListEntries = uno::Sequence< OUString>(vListEntries.getArray(), ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT);
+
+ sName = params.getName();
+ sal_Int32 nSelectedIndex = 0;
+
+ if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) )
+ {
+ if (nSelectedIndex < vListEntries.getLength() )
+ sSelected = vListEntries[ nSelectedIndex ];
+ }
+
+ GetExport().DoComboBox( sName, OUString(), OUString(), sSelected, vListEntries );
+ }
+ else if ( rInfos.eType == ww::eFORMCHECKBOX )
+ {
+ const OUString sName = params.getName();
+ bool bChecked = false;
+
+ const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark);
+ if ( pCheckboxFm && pCheckboxFm->IsChecked() )
+ bChecked = true;
+
+ FFDataWriterHelper ffdataOut( m_pSerializer );
+ ffdataOut.WriteFormCheckbox( sName, sEntryMacro, sExitMacro, sHelp, sHint, bChecked );
+ }
+ else if ( rInfos.eType == ww::eFORMTEXT )
+ {
+ OUString sType;
+ params.extractParam("Type", sType);
+ OUString sDefaultText;
+ params.extractParam("Content", sDefaultText);
+ sal_uInt16 nMaxLength = 0;
+ params.extractParam("MaxLength", nMaxLength);
+ OUString sFormat;
+ params.extractParam("Format", sFormat);
+ FFDataWriterHelper ffdataOut( m_pSerializer );
+ ffdataOut.WriteFormText( params.getName(), sEntryMacro, sExitMacro, sHelp, sHint,
+ sType, sDefaultText, nMaxLength, sFormat );
+ }
+}
+
+void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
+{
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+ if(!sFullDate.isEmpty())
+ m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sFullDate);
+ else
+ m_pSerializer->startElementNS(XML_w, XML_date);
+
+ // Replace quotation mark used for marking static strings in date format
+ OUString sDateFormat1 = sDateFormat.replaceAll("\"", "'");
+ m_pSerializer->singleElementNS(XML_w, XML_dateFormat,
+ FSNS(XML_w, XML_val), sDateFormat1);
+ m_pSerializer->singleElementNS(XML_w, XML_lid,
+ FSNS(XML_w, XML_val), sLang);
+ m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
+ FSNS(XML_w, XML_val), "dateTime");
+ m_pSerializer->singleElementNS(XML_w, XML_calendar,
+ FSNS(XML_w, XML_val), "gregorian");
+ m_pSerializer->endElementNS(XML_w, XML_date);
+
+ if (aGrabBagSdt.hasElements())
+ {
+ // There are some extra sdt parameters came from grab bag
+ SdtBlockHelper aSdtBlock;
+ aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt);
+ aSdtBlock.WriteExtraParams(m_pSerializer);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+}
+
+void DocxAttributeOutput::WriteSdtPlainText(const OUString & sValue, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt)
+{
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+ if (aGrabBagSdt.hasElements())
+ {
+ // There are some extra sdt parameters came from grab bag
+ SdtBlockHelper aSdtBlock;
+ aSdtBlock.GetSdtParamsFromGrabBag(aGrabBagSdt);
+ aSdtBlock.WriteExtraParams(m_pSerializer);
+
+ if (aSdtBlock.m_nSdtPrToken && aSdtBlock.m_nSdtPrToken != FSNS(XML_w, XML_id))
+ {
+ // Write <w:text/> or whatsoever from grabbag
+ m_pSerializer->singleElement(aSdtBlock.m_nSdtPrToken);
+ }
+
+ // Store databindings data for later writing to corresponding XMLs
+ OUString sPrefixMapping, sXpath;
+ for (const auto& rProp : std::as_const(aGrabBagSdt))
+ {
+ if (rProp.Name == "ooxml:CT_SdtPr_dataBinding")
+ {
+ uno::Sequence<beans::PropertyValue> aDataBindingProps;
+ rProp.Value >>= aDataBindingProps;
+ for (const auto& rDBProp : std::as_const(aDataBindingProps))
+ {
+ if (rDBProp.Name == "ooxml:CT_DataBinding_prefixMappings")
+ sPrefixMapping = rDBProp.Value.get<OUString>();
+ else if (rDBProp.Name == "ooxml:CT_DataBinding_xpath")
+ sXpath = rDBProp.Value.get<OUString>();
+ }
+ }
+ }
+
+ if (sXpath.getLength())
+ {
+ // Given xpath is sufficient
+ m_rExport.AddSdtData(sPrefixMapping, sXpath, sValue);
+ }
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+}
+
+void DocxAttributeOutput::WriteContentControlStart()
+{
+ if (!m_pContentControl)
+ {
+ return;
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+ if (!m_pContentControl->GetPlaceholderDocPart().isEmpty())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_placeholder);
+ m_pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val),
+ m_pContentControl->GetPlaceholderDocPart());
+ m_pSerializer->endElementNS(XML_w, XML_placeholder);
+ }
+
+ if (!m_pContentControl->GetDataBindingPrefixMappings().isEmpty() || !m_pContentControl->GetDataBindingXpath().isEmpty() || !m_pContentControl->GetDataBindingStoreItemID().isEmpty())
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_dataBinding,
+ FSNS(XML_w, XML_prefixMappings), m_pContentControl->GetDataBindingPrefixMappings(),
+ FSNS(XML_w, XML_xpath), m_pContentControl->GetDataBindingXpath(),
+ FSNS(XML_w, XML_storeItemID), m_pContentControl->GetDataBindingStoreItemID());
+ }
+
+ if (!m_pContentControl->GetColor().isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val),
+ m_pContentControl->GetColor());
+ }
+
+ if (!m_pContentControl->GetAppearance().isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w15, XML_appearance, FSNS(XML_w15, XML_val),
+ m_pContentControl->GetAppearance());
+ }
+
+ if (!m_pContentControl->GetAlias().isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val),
+ m_pContentControl->GetAlias());
+ }
+
+ if (!m_pContentControl->GetTag().isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_tag, FSNS(XML_w, XML_val),
+ m_pContentControl->GetTag());
+ }
+
+ if (m_pContentControl->GetId())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
+ OString::number(m_pContentControl->GetId()));
+ }
+
+ if (m_pContentControl->GetTabIndex())
+ {
+ // write the unsigned value as if it were signed since that is all we can import
+ const sal_Int32 nTabIndex = static_cast<sal_Int32>(m_pContentControl->GetTabIndex());
+ m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, XML_val),
+ OString::number(nTabIndex));
+ }
+
+ if (!m_pContentControl->GetLock().isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val),
+ m_pContentControl->GetLock());
+ }
+
+ if (m_pContentControl->GetShowingPlaceHolder())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr);
+ }
+
+ if (m_pContentControl->GetPicture())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_picture);
+ }
+
+ if (m_pContentControl->GetCheckbox())
+ {
+ m_pSerializer->startElementNS(XML_w14, XML_checkbox);
+ m_pSerializer->singleElementNS(XML_w14, XML_checked, FSNS(XML_w14, XML_val),
+ OString::number(int(m_pContentControl->GetChecked())));
+ OUString aCheckedState = m_pContentControl->GetCheckedState();
+ if (!aCheckedState.isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w14, XML_checkedState, FSNS(XML_w14, XML_val),
+ OString::number(aCheckedState[0], /*radix=*/16));
+ }
+ OUString aUncheckedState = m_pContentControl->GetUncheckedState();
+ if (!aUncheckedState.isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w14, XML_uncheckedState, FSNS(XML_w14, XML_val),
+ OString::number(aUncheckedState[0], /*radix=*/16));
+ }
+ m_pSerializer->endElementNS(XML_w14, XML_checkbox);
+ }
+
+ if (m_pContentControl->GetComboBox() || m_pContentControl->GetDropDown())
+ {
+ if (m_pContentControl->GetComboBox())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_comboBox);
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_w, XML_dropDownList);
+ }
+ for (const auto& rItem : m_pContentControl->GetListItems())
+ {
+ rtl::Reference<FastAttributeList> xAttributes = FastSerializerHelper::createAttrList();
+ if (!rItem.m_aDisplayText.isEmpty())
+ {
+ // If there is no display text, need to omit the attribute, not write an empty one.
+ xAttributes->add(FSNS(XML_w, XML_displayText), rItem.m_aDisplayText);
+ }
+ xAttributes->add(FSNS(XML_w, XML_value), rItem.m_aValue);
+ m_pSerializer->singleElementNS(XML_w, XML_listItem, xAttributes);
+ }
+ if (m_pContentControl->GetComboBox())
+ {
+ m_pSerializer->endElementNS(XML_w, XML_comboBox);
+ }
+ else
+ {
+ m_pSerializer->endElementNS(XML_w, XML_dropDownList);
+ }
+ }
+
+ if (m_pContentControl->GetDate())
+ {
+ OUString aCurrentDate = m_pContentControl->GetCurrentDate();
+ if (aCurrentDate.isEmpty())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_date);
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), aCurrentDate);
+ }
+ OUString aDateFormat = m_pContentControl->GetDateFormat().replaceAll("\"", "'");
+ if (!aDateFormat.isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_dateFormat, FSNS(XML_w, XML_val),
+ aDateFormat);
+ }
+ OUString aDateLanguage = m_pContentControl->GetDateLanguage();
+ if (!aDateLanguage.isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val),
+ aDateLanguage);
+ }
+ m_pSerializer->endElementNS(XML_w, XML_date);
+ }
+
+ if (!m_pContentControl->GetMultiLine().isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_text, FSNS(XML_w, XML_multiLine), m_pContentControl->GetMultiLine());
+ }
+ else if (m_pContentControl->GetPlainText())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_text);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_sdtPr);
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+
+ const OUString& rPrefixMapping = m_pContentControl->GetDataBindingPrefixMappings();
+ const OUString& rXpath = m_pContentControl->GetDataBindingXpath();
+ if (!rXpath.isEmpty())
+ {
+ // This content control has a data binding, update the data source.
+ SwTextContentControl* pTextAttr = m_pContentControl->GetTextAttr();
+ SwTextNode* pTextNode = m_pContentControl->GetTextNode();
+ if (pTextNode && pTextAttr)
+ {
+ SwPosition aPoint(*pTextNode, pTextAttr->GetStart());
+ SwPosition aMark(*pTextNode, *pTextAttr->GetEnd());
+ SwPaM aPam(aMark, aPoint);
+ OUString aSnippet = aPam.GetText();
+ static sal_Unicode const aForbidden[] = {
+ CH_TXTATR_BREAKWORD,
+ 0
+ };
+ aSnippet = comphelper::string::removeAny(aSnippet, aForbidden);
+ m_rExport.AddSdtData(rPrefixMapping, rXpath, aSnippet);
+ }
+ }
+
+ m_pContentControl = nullptr;
+}
+
+void DocxAttributeOutput::WriteContentControlEnd()
+{
+ m_pSerializer->endElementNS(XML_w, XML_sdtContent);
+ m_pSerializer->endElementNS(XML_w, XML_sdt);
+}
+
+void DocxAttributeOutput::WriteSdtDropDownStart(
+ OUString const& rName,
+ OUString const& rSelected,
+ uno::Sequence<OUString> const& rListItems)
+{
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->singleElementNS(XML_w, XML_alias,
+ FSNS(XML_w, XML_val), rName);
+
+ sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
+ if (nId == -1)
+ {
+ nId = 0;
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_dropDownList,
+ FSNS(XML_w, XML_lastValue), OString::number(nId));
+
+ for (auto const& rItem : rListItems)
+ {
+ auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8));
+ m_pSerializer->singleElementNS(XML_w, XML_listItem,
+ FSNS(XML_w, XML_value), item,
+ FSNS(XML_w, XML_displayText), item);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_dropDownList);
+ m_pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+}
+
+void DocxAttributeOutput::WriteSdtDropDownEnd(OUString const& rSelected,
+ uno::Sequence<OUString> const& rListItems)
+{
+ // note: rSelected might be empty?
+ sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
+ if (nId == -1)
+ {
+ nId = 0;
+ }
+
+ // the lastValue only identifies the entry in the list, also export
+ // currently selected item's displayText as run content (if one exists)
+ if (rListItems.size())
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_w, XML_t);
+ m_pSerializer->writeEscaped(rListItems[nId]);
+ m_pSerializer->endElementNS(XML_w, XML_t);
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ }
+
+ WriteContentControlEnd();
+}
+
+void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
+{
+ if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN )
+ {
+ // Expand unsupported fields
+ RunText(rInfos.pField->ExpandField(/*bCached=*/true, nullptr));
+ return;
+ }
+ else if ( rInfos.eType == ww::eFORMDATE )
+ {
+ const sw::mark::IDateFieldmark& rFieldmark = dynamic_cast<const sw::mark::IDateFieldmark&>(*rInfos.pFieldmark);
+ FieldMarkParamsHelper params(rFieldmark);
+
+ OUString sFullDate;
+ OUString sCurrentDate;
+ params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate );
+ if(!sCurrentDate.isEmpty())
+ {
+ sFullDate = sCurrentDate + "T00:00:00Z";
+ }
+ else
+ {
+ std::pair<bool, double> aResult = rFieldmark.GetCurrentDate();
+ if(aResult.first)
+ {
+ sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z";
+ }
+ }
+
+ OUString sDateFormat;
+ params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat );
+ OUString sLang;
+ params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang );
+
+ uno::Sequence<beans::PropertyValue> aSdtParams;
+ params.extractParam(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, aSdtParams);
+
+ WriteFormDateStart( sFullDate, sDateFormat, sLang, aSdtParams);
+ return;
+ }
+ else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
+ {
+ assert(!rInfos.pFieldmark);
+ SwDropDownField const& rField2(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
+ WriteSdtDropDownStart(rField2.GetName(),
+ rField2.GetSelectedItem(),
+ rField2.GetItemSequence());
+ return;
+ }
+ else if (rInfos.eType == ww::eFILLIN)
+ {
+ const SwInputField* pField = static_cast<SwInputField const*>(rInfos.pField.get());
+ if (pField && pField->getGrabBagParams().hasElements())
+ {
+ WriteSdtPlainText(pField->GetPar1(), pField->getGrabBagParams());
+ m_sRawText = pField->GetPar1(); // Write field content also as a fallback
+ return;
+ }
+ }
+
+ if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands
+ {
+ if ( bWriteRun )
+ m_pSerializer->startElementNS(XML_w, XML_r);
+
+ if ( rInfos.eType == ww::eFORMDROPDOWN )
+ {
+ m_pSerializer->startElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "begin" );
+ assert( rInfos.pFieldmark && !rInfos.pField );
+ WriteFFData(rInfos);
+ m_pSerializer->endElementNS( XML_w, XML_fldChar );
+
+ if ( bWriteRun )
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
+ }
+ else
+ {
+ // Write the field start
+ if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD )
+ {
+ m_pSerializer->startElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "begin",
+ FSNS( XML_w, XML_fldLock ), "true" );
+ }
+ else
+ {
+ m_pSerializer->startElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "begin" );
+ }
+
+ if ( rInfos.pFieldmark )
+ WriteFFData( rInfos );
+
+ m_pSerializer->endElementNS( XML_w, XML_fldChar );
+
+ if ( bWriteRun )
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ // The hyperlinks fields can't be expanded: the value is
+ // normally in the text run
+ if ( !rInfos.pField )
+ CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
+ else
+ m_bWritingField = true;
+ }
+ }
+}
+
+void DocxAttributeOutput::DoWriteCmd( std::u16string_view rCmd )
+{
+ std::u16string_view sCmd = o3tl::trim(rCmd);
+ if (o3tl::starts_with(sCmd, u"SEQ"))
+ {
+ OUString sSeqName( o3tl::trim(msfilter::util::findQuotedText(sCmd, u"SEQ ", '\\')) );
+ m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark);
+ }
+ // Write the Field command
+ sal_Int32 nTextToken = XML_instrText;
+ if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete )
+ nTextToken = XML_delInstrText;
+
+ m_pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve");
+ m_pSerializer->writeEscaped( rCmd );
+ m_pSerializer->endElementNS( XML_w, nTextToken );
+
+}
+
+void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
+{
+ // Write the Field instruction
+ if ( bWriteRun )
+ {
+ bool bWriteCombChars(false);
+ m_pSerializer->startElementNS(XML_w, XML_r);
+
+ if (rInfos.eType == ww::eEQ)
+ bWriteCombChars = true;
+
+ DoWriteFieldRunProperties( pNode, nPos, bWriteCombChars );
+ }
+
+ sal_Int32 nIdx { rInfos.sCmd.isEmpty() ? -1 : 0 };
+ while ( nIdx >= 0 )
+ {
+ OUString sToken = rInfos.sCmd.getToken( 0, '\t', nIdx );
+ if ( rInfos.eType == ww::eCREATEDATE
+ || rInfos.eType == ww::eSAVEDATE
+ || rInfos.eType == ww::ePRINTDATE
+ || rInfos.eType == ww::eDATE
+ || rInfos.eType == ww::eTIME )
+ {
+ sToken = sToken.replaceAll("NNNN", "dddd");
+ sToken = sToken.replaceAll("NN", "ddd");
+ }
+ else if ( rInfos.eType == ww::eEquals )
+ {
+ // Use original OOXML formula, if it exists and its conversion hasn't been changed
+ bool bIsChanged = true;
+ if ( pNode->GetTableBox() )
+ {
+ if ( const SfxGrabBagItem* pItem = pNode->GetTableBox()->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG) )
+ {
+ OUString sActualFormula = sToken.trim();
+ const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
+ std::map<OUString, uno::Any>::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted");
+ if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 &&
+ o3tl::trim(sActualFormula.subView(1)) == o3tl::trim(aStoredFormula->second.get<OUString>()) )
+ {
+ aStoredFormula = rGrabBag.find("CellFormula");
+ if ( aStoredFormula != rGrabBag.end() )
+ {
+ sToken = " =" + aStoredFormula->second.get<OUString>();
+ bIsChanged = false;
+ }
+ }
+ }
+ }
+
+ if ( bIsChanged )
+ {
+ UErrorCode nErr(U_ZERO_ERROR);
+ icu::UnicodeString sInput(sToken.getStr());
+ // remove < and > around cell references, e.g. <A1> to A1, <A1:B2> to A1:B2
+ icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr);
+ sInput = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr);
+ // convert MEAN to AVERAGE
+ icu::RegexMatcher aMatcher2("\\bMEAN\\b", sInput, UREGEX_CASE_INSENSITIVE, nErr);
+ sToken = aMatcher2.replaceAll(icu::UnicodeString("AVERAGE"), nErr).getTerminatedBuffer();
+ }
+ }
+
+ // Write the Field command
+ DoWriteCmd( sToken );
+
+ // Replace tabs by </instrText><tab/><instrText>
+ if ( nIdx > 0 ) // Is another token expected?
+ RunText( "\t" );
+ }
+
+ if ( bWriteRun )
+ {
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ }
+}
+
+void DocxAttributeOutput::CmdEndField_Impl(SwTextNode const*const pNode,
+ sal_Int32 const nPos, bool const bWriteRun)
+{
+ // Write the Field separator
+ if ( bWriteRun )
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ DoWriteFieldRunProperties( pNode, nPos );
+ }
+
+ m_pSerializer->singleElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "separate" );
+
+ if ( bWriteRun )
+ {
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ }
+}
+
+/// Writes properties for run that is used to separate field implementation.
+/// There are several runs are used:
+/// <w:r>
+/// <w:rPr>
+/// <!-- properties written with StartRunProperties() / EndRunProperties().
+/// </w:rPr>
+/// <w:fldChar w:fldCharType="begin" />
+/// </w:r>
+/// <w:r>
+/// <w:rPr>
+/// <!-- properties written with DoWriteFieldRunProperties()
+/// </w:rPr>
+/// <w:instrText>TIME \@"HH:mm:ss"</w:instrText>
+/// </w:r>
+/// <w:r>
+/// <w:rPr>
+/// <!-- properties written with DoWriteFieldRunProperties()
+/// </w:rPr>
+/// <w:fldChar w:fldCharType="separate" />
+/// </w:r>
+/// <w:r>
+/// <w:rPr>
+/// <!-- properties written with DoWriteFieldRunProperties()
+/// </w:rPr>
+/// <w:t>14:01:13</w:t>
+/// </w:r>
+/// <w:r>
+/// <w:rPr>
+/// <!-- properties written with DoWriteFieldRunProperties()
+/// </w:rPr>
+/// <w:fldChar w:fldCharType="end" />
+/// </w:r>
+/// See, tdf#38778
+void DocxAttributeOutput::DoWriteFieldRunProperties( const SwTextNode * pNode, sal_Int32 nPos, bool bWriteCombChars)
+{
+ if (! pNode)
+ {
+ // nothing to do
+ return;
+ }
+
+ m_bPreventDoubleFieldsHandling = true;
+
+ {
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+
+ // 1. output webHidden flag
+ if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_webHidden);
+ }
+
+ // 2. find all active character properties
+ SwWW8AttrIter aAttrIt( m_rExport, *pNode );
+ aAttrIt.OutAttr( nPos, bWriteCombChars );
+
+ // 3. write the character properties
+ WriteCollectedRunProperties();
+
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ }
+
+ m_bPreventDoubleFieldsHandling = false;
+}
+
+void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos )
+{
+ if (rInfos.eType == ww::eFORMDATE)
+ {
+ WriteContentControlEnd();
+ return;
+ }
+ else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
+ {
+ // write selected item from End not Start to ensure that any bookmarks
+ // precede it
+ SwDropDownField const& rField(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
+ WriteSdtDropDownEnd(rField.GetSelectedItem(), rField.GetItemSequence());
+ return;
+ }
+ else if (rInfos.eType == ww::eFILLIN && rInfos.pField)
+ {
+ SwInputField const& rField(*static_cast<SwInputField const*>(rInfos.pField.get()));
+ if (rField.getGrabBagParams().hasElements())
+ {
+ WriteContentControlEnd();
+ return;
+ }
+ }
+ // The command has to be written before for the hyperlinks
+ if ( rInfos.pField )
+ {
+ CmdField_Impl( pNode, nPos, rInfos, true );
+ CmdEndField_Impl( pNode, nPos, true );
+ }
+
+ // Write the bookmark start if any
+ if ( !m_sFieldBkm.isEmpty() )
+ {
+ DoWriteBookmarkTagStart(m_sFieldBkm);
+ }
+
+ if (rInfos.pField ) // For hyperlinks and TOX
+ {
+ // Write the Field latest value
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ DoWriteFieldRunProperties( pNode, nPos );
+
+ OUString sExpand;
+ if(rInfos.eType == ww::eCITATION)
+ {
+ sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get())
+ ->ExpandCitation(AUTH_FIELD_TITLE, nullptr);
+ }
+ else if(rInfos.eType != ww::eFORMDROPDOWN)
+ {
+ sExpand = rInfos.pField->ExpandField(true, nullptr);
+ }
+ // newlines embedded in fields are 0x0B in MSO and 0x0A for us
+ RunText(sExpand.replace(0x0A, 0x0B));
+
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ }
+
+ // Write the bookmark end if any
+ if ( !m_sFieldBkm.isEmpty() )
+ {
+ DoWriteBookmarkTagEnd(m_nNextBookmarkId);
+
+ m_nNextBookmarkId++;
+ }
+
+ // Write the Field end
+ if ( rInfos.bClose )
+ {
+ m_bWritingField = false;
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ DoWriteFieldRunProperties( pNode, nPos );
+ m_pSerializer->singleElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "end");
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ }
+ // Write the ref field if a bookmark had to be set and the field
+ // should be visible
+ if ( !rInfos.pField )
+ {
+ m_sFieldBkm.clear();
+ return;
+ }
+
+ sal_uInt16 nSubType = rInfos.pField->GetSubType( );
+ bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == SwFieldIds::SetExp;
+ bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0;
+
+ if (!bShowRef)
+ {
+ m_sFieldBkm.clear();
+ }
+
+ if (m_sFieldBkm.isEmpty())
+ return;
+
+ // Write the field beginning
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS( XML_w, XML_fldChar,
+ FSNS( XML_w, XML_fldCharType ), "begin" );
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ rInfos.sCmd = FieldString( ww::eREF );
+ rInfos.sCmd += "\"";
+ rInfos.sCmd += m_sFieldBkm;
+ rInfos.sCmd += "\" ";
+
+ // Clean the field bookmark data to avoid infinite loop
+ m_sFieldBkm = OUString( );
+
+ // Write the end of the field
+ EndField_Impl( pNode, nPos, rInfos );
+}
+
+void DocxAttributeOutput::StartRunProperties()
+{
+ // postpone the output so that we can later [in EndRunProperties()]
+ // prepend the properties before the text
+ m_pSerializer->mark(Tag_StartRunProperties);
+
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+
+ if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_webHidden);
+ }
+ InitCollectedRunProperties();
+
+ assert( !m_oPostponedGraphic );
+ m_oPostponedGraphic.emplace();
+
+ assert( !m_oPostponedDiagrams );
+ m_oPostponedDiagrams.emplace();
+
+ assert(!m_oPostponedDMLDrawings);
+ m_oPostponedDMLDrawings.emplace();
+
+ assert( !m_oPostponedOLEs );
+ m_oPostponedOLEs.emplace();
+}
+
+void DocxAttributeOutput::InitCollectedRunProperties()
+{
+ m_pFontsAttrList = nullptr;
+ m_pEastAsianLayoutAttrList = nullptr;
+ m_pCharLangAttrList = nullptr;
+
+ // Write the elements in the spec order
+ static const sal_Int32 aOrder[] =
+ {
+ FSNS( XML_w, XML_rStyle ),
+ FSNS( XML_w, XML_rFonts ),
+ FSNS( XML_w, XML_b ),
+ FSNS( XML_w, XML_bCs ),
+ FSNS( XML_w, XML_i ),
+ FSNS( XML_w, XML_iCs ),
+ FSNS( XML_w, XML_caps ),
+ FSNS( XML_w, XML_smallCaps ),
+ FSNS( XML_w, XML_strike ),
+ FSNS( XML_w, XML_dstrike ),
+ FSNS( XML_w, XML_outline ),
+ FSNS( XML_w, XML_shadow ),
+ FSNS( XML_w, XML_emboss ),
+ FSNS( XML_w, XML_imprint ),
+ FSNS( XML_w, XML_noProof ),
+ FSNS( XML_w, XML_snapToGrid ),
+ FSNS( XML_w, XML_vanish ),
+ FSNS( XML_w, XML_webHidden ),
+ FSNS( XML_w, XML_color ),
+ FSNS( XML_w, XML_spacing ),
+ FSNS( XML_w, XML_w ),
+ FSNS( XML_w, XML_kern ),
+ FSNS( XML_w, XML_position ),
+ FSNS( XML_w, XML_sz ),
+ FSNS( XML_w, XML_szCs ),
+ FSNS( XML_w, XML_highlight ),
+ FSNS( XML_w, XML_u ),
+ FSNS( XML_w, XML_effect ),
+ FSNS( XML_w, XML_bdr ),
+ FSNS( XML_w, XML_shd ),
+ FSNS( XML_w, XML_fitText ),
+ FSNS( XML_w, XML_vertAlign ),
+ FSNS( XML_w, XML_rtl ),
+ FSNS( XML_w, XML_cs ),
+ FSNS( XML_w, XML_em ),
+ FSNS( XML_w, XML_lang ),
+ FSNS( XML_w, XML_eastAsianLayout ),
+ FSNS( XML_w, XML_specVanish ),
+ FSNS( XML_w, XML_oMath ),
+ FSNS( XML_w, XML_rPrChange ),
+ FSNS( XML_w, XML_del ),
+ FSNS( XML_w, XML_ins ),
+ FSNS( XML_w, XML_moveFrom ),
+ FSNS( XML_w, XML_moveTo ),
+ FSNS( XML_w14, XML_glow ),
+ FSNS( XML_w14, XML_shadow ),
+ FSNS( XML_w14, XML_reflection ),
+ FSNS( XML_w14, XML_textOutline ),
+ FSNS( XML_w14, XML_textFill ),
+ FSNS( XML_w14, XML_scene3d ),
+ FSNS( XML_w14, XML_props3d ),
+ FSNS( XML_w14, XML_ligatures ),
+ FSNS( XML_w14, XML_numForm ),
+ FSNS( XML_w14, XML_numSpacing ),
+ FSNS( XML_w14, XML_stylisticSets ),
+ FSNS( XML_w14, XML_cntxtAlts ),
+ };
+
+ // postpone the output so that we can later [in EndParagraphProperties()]
+ // prepend the properties before the run
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ m_pSerializer->mark(Tag_InitCollectedRunProperties, comphelper::containerToSequence(aOrder));
+}
+
+namespace
+{
+
+struct NameToId
+{
+ OUString maName;
+ sal_Int32 maId;
+};
+
+const NameToId constNameToIdMapping[] =
+{
+ { OUString("glow"), FSNS( XML_w14, XML_glow ) },
+ { OUString("shadow"), FSNS( XML_w14, XML_shadow ) },
+ { OUString("reflection"), FSNS( XML_w14, XML_reflection ) },
+ { OUString("textOutline"), FSNS( XML_w14, XML_textOutline ) },
+ { OUString("textFill"), FSNS( XML_w14, XML_textFill ) },
+ { OUString("scene3d"), FSNS( XML_w14, XML_scene3d ) },
+ { OUString("props3d"), FSNS( XML_w14, XML_props3d ) },
+ { OUString("ligatures"), FSNS( XML_w14, XML_ligatures ) },
+ { OUString("numForm"), FSNS( XML_w14, XML_numForm ) },
+ { OUString("numSpacing"), FSNS( XML_w14, XML_numSpacing ) },
+ { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) },
+ { OUString("cntxtAlts"), FSNS( XML_w14, XML_cntxtAlts ) },
+
+ { OUString("val"), FSNS( XML_w14, XML_val ) },
+ { OUString("rad"), FSNS( XML_w14, XML_rad ) },
+ { OUString("blurRad"), FSNS( XML_w14, XML_blurRad ) },
+ { OUString("stA"), FSNS( XML_w14, XML_stA ) },
+ { OUString("stPos"), FSNS( XML_w14, XML_stPos ) },
+ { OUString("endA"), FSNS( XML_w14, XML_endA ) },
+ { OUString("endPos"), FSNS( XML_w14, XML_endPos ) },
+ { OUString("dist"), FSNS( XML_w14, XML_dist ) },
+ { OUString("dir"), FSNS( XML_w14, XML_dir ) },
+ { OUString("fadeDir"), FSNS( XML_w14, XML_fadeDir ) },
+ { OUString("sx"), FSNS( XML_w14, XML_sx ) },
+ { OUString("sy"), FSNS( XML_w14, XML_sy ) },
+ { OUString("kx"), FSNS( XML_w14, XML_kx ) },
+ { OUString("ky"), FSNS( XML_w14, XML_ky ) },
+ { OUString("algn"), FSNS( XML_w14, XML_algn ) },
+ { OUString("w"), FSNS( XML_w14, XML_w ) },
+ { OUString("cap"), FSNS( XML_w14, XML_cap ) },
+ { OUString("cmpd"), FSNS( XML_w14, XML_cmpd ) },
+ { OUString("pos"), FSNS( XML_w14, XML_pos ) },
+ { OUString("ang"), FSNS( XML_w14, XML_ang ) },
+ { OUString("scaled"), FSNS( XML_w14, XML_scaled ) },
+ { OUString("path"), FSNS( XML_w14, XML_path ) },
+ { OUString("l"), FSNS( XML_w14, XML_l ) },
+ { OUString("t"), FSNS( XML_w14, XML_t ) },
+ { OUString("r"), FSNS( XML_w14, XML_r ) },
+ { OUString("b"), FSNS( XML_w14, XML_b ) },
+ { OUString("lim"), FSNS( XML_w14, XML_lim ) },
+ { OUString("prst"), FSNS( XML_w14, XML_prst ) },
+ { OUString("rig"), FSNS( XML_w14, XML_rig ) },
+ { OUString("lat"), FSNS( XML_w14, XML_lat ) },
+ { OUString("lon"), FSNS( XML_w14, XML_lon ) },
+ { OUString("rev"), FSNS( XML_w14, XML_rev ) },
+ { OUString("h"), FSNS( XML_w14, XML_h ) },
+ { OUString("extrusionH"), FSNS( XML_w14, XML_extrusionH ) },
+ { OUString("contourW"), FSNS( XML_w14, XML_contourW ) },
+ { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) },
+ { OUString("id"), FSNS( XML_w14, XML_id ) },
+
+ { OUString("schemeClr"), FSNS( XML_w14, XML_schemeClr ) },
+ { OUString("srgbClr"), FSNS( XML_w14, XML_srgbClr ) },
+ { OUString("tint"), FSNS( XML_w14, XML_tint ) },
+ { OUString("shade"), FSNS( XML_w14, XML_shade ) },
+ { OUString("alpha"), FSNS( XML_w14, XML_alpha ) },
+ { OUString("hueMod"), FSNS( XML_w14, XML_hueMod ) },
+ { OUString("sat"), FSNS( XML_w14, XML_sat ) },
+ { OUString("satOff"), FSNS( XML_w14, XML_satOff ) },
+ { OUString("satMod"), FSNS( XML_w14, XML_satMod ) },
+ { OUString("lum"), FSNS( XML_w14, XML_lum ) },
+ { OUString("lumOff"), FSNS( XML_w14, XML_lumOff ) },
+ { OUString("lumMod"), FSNS( XML_w14, XML_lumMod ) },
+ { OUString("noFill"), FSNS( XML_w14, XML_noFill ) },
+ { OUString("solidFill"), FSNS( XML_w14, XML_solidFill ) },
+ { OUString("gradFill"), FSNS( XML_w14, XML_gradFill ) },
+ { OUString("gsLst"), FSNS( XML_w14, XML_gsLst ) },
+ { OUString("gs"), FSNS( XML_w14, XML_gs ) },
+ { OUString("pos"), FSNS( XML_w14, XML_pos ) },
+ { OUString("lin"), FSNS( XML_w14, XML_lin ) },
+ { OUString("path"), FSNS( XML_w14, XML_path ) },
+ { OUString("fillToRect"), FSNS( XML_w14, XML_fillToRect ) },
+ { OUString("prstDash"), FSNS( XML_w14, XML_prstDash ) },
+ { OUString("round"), FSNS( XML_w14, XML_round ) },
+ { OUString("bevel"), FSNS( XML_w14, XML_bevel ) },
+ { OUString("miter"), FSNS( XML_w14, XML_miter ) },
+ { OUString("camera"), FSNS( XML_w14, XML_camera ) },
+ { OUString("lightRig"), FSNS( XML_w14, XML_lightRig ) },
+ { OUString("rot"), FSNS( XML_w14, XML_rot ) },
+ { OUString("bevelT"), FSNS( XML_w14, XML_bevelT ) },
+ { OUString("bevelB"), FSNS( XML_w14, XML_bevelB ) },
+ { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) },
+ { OUString("contourClr"), FSNS( XML_w14, XML_contourClr ) },
+ { OUString("styleSet"), FSNS( XML_w14, XML_styleSet ) },
+};
+
+std::optional<sal_Int32> lclGetElementIdForName(std::u16string_view rName)
+{
+ for (auto const & i : constNameToIdMapping)
+ {
+ if (rName == i.maName)
+ {
+ return i.maId;
+ }
+ }
+ return std::optional<sal_Int32>();
+}
+
+void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer)
+{
+ css::uno::Sequence<css::beans::PropertyValue> aAttributes;
+ rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
+
+ for (const auto& rElement : rElements)
+ {
+ if (rElement.Name == "attributes")
+ {
+ rElement.Value >>= aAttributes;
+ }
+ }
+
+ for (const auto& rAttribute : std::as_const(aAttributes))
+ {
+ uno::Any aAny = rAttribute.Value;
+ OString aValue;
+
+ if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get())
+ {
+ aValue = OString::number(aAny.get<sal_Int32>());
+ }
+ else if(aAny.getValueType() == cppu::UnoType<OUString>::get())
+ {
+ aValue = OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US);
+ }
+
+ std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rAttribute.Name);
+ if(aSubElementId)
+ pAttributes->add(*aSubElementId, aValue);
+ }
+
+ pSerializer->startElement(aElementId, pAttributes);
+
+ for (const auto& rElement : rElements)
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aSumElements;
+
+ std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElement.Name);
+ if(aSubElementId)
+ {
+ rElement.Value >>= aSumElements;
+ lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer);
+ }
+ }
+
+ pSerializer->endElement(aElementId);
+}
+
+}
+
+void DocxAttributeOutput::WriteCollectedRunProperties()
+{
+ // Write all differed properties
+ if ( m_pFontsAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_rFonts, detachFrom( m_pFontsAttrList ) );
+ }
+
+ if ( m_pColorAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_color, m_pColorAttrList );
+ }
+
+ if ( m_pEastAsianLayoutAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout,
+ detachFrom(m_pEastAsianLayoutAttrList ) );
+ }
+
+ if ( m_pCharLangAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_lang, detachFrom( m_pCharLangAttrList ) );
+ }
+
+ if (m_nCharTransparence != 0 && m_pColorAttrList && m_aTextEffectsGrabBag.empty())
+ {
+ std::string_view pVal;
+ m_pColorAttrList->getAsView(FSNS(XML_w, XML_val), pVal);
+ if (!pVal.empty() && pVal != "auto")
+ {
+ m_pSerializer->startElementNS(XML_w14, XML_textFill);
+ m_pSerializer->startElementNS(XML_w14, XML_solidFill);
+ m_pSerializer->startElementNS(XML_w14, XML_srgbClr, FSNS(XML_w14, XML_val), pVal.data());
+ sal_Int32 nTransparence = m_nCharTransparence * oox::drawingml::MAX_PERCENT / 255.0;
+ m_pSerializer->singleElementNS(XML_w14, XML_alpha, FSNS(XML_w14, XML_val), OString::number(nTransparence));
+ m_pSerializer->endElementNS(XML_w14, XML_srgbClr);
+ m_pSerializer->endElementNS(XML_w14, XML_solidFill);
+ m_pSerializer->endElementNS(XML_w14, XML_textFill);
+ m_nCharTransparence = 0;
+ }
+ }
+ m_pColorAttrList.clear();
+ for (const beans::PropertyValue & i : m_aTextEffectsGrabBag)
+ {
+ std::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name);
+ if(aElementId)
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBagSeq;
+ i.Value >>= aGrabBagSeq;
+ lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer);
+ }
+ }
+ m_aTextEffectsGrabBag.clear();
+}
+
+void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
+{
+ // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties.
+ // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
+
+ // If there is RedlineData present, call WriteCollectedRunProperties() for writing rPr before calling Redline().
+ // As there will be another rPr for redline and LO might mix both.
+ if(pRedlineData)
+ WriteCollectedRunProperties();
+ Redline( pRedlineData );
+
+ WriteCollectedRunProperties();
+
+ // Merge the marks for the ordered elements
+ m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
+
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+
+ // write footnotes/endnotes if we have any
+ FootnoteEndnoteReference();
+
+ WriteLineBreak();
+
+ // merge the properties _before_ the run text (strictly speaking, just
+ // after the start of the run)
+ m_pSerializer->mergeTopMarks(Tag_StartRunProperties, sax_fastparser::MergeMarks::PREPEND);
+
+ WritePostponedGraphic();
+
+ WritePostponedDiagram();
+ //We need to write w:drawing tag after the w:rPr.
+ WritePostponedChart();
+
+ //We need to write w:pict tag after the w:rPr.
+ WritePostponedDMLDrawing();
+
+ WritePostponedOLE();
+
+ WritePostponedActiveXControl(true);
+}
+
+void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj)
+{
+ if (!pSdrObj)
+ return;
+
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape());
+ uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
+ if( !xPropSet.is() )
+ return;
+
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ uno::Sequence< beans::PropertyValue > aGrabBag;
+ if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
+ {
+ xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
+ }
+ else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
+ {
+ xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
+ }
+
+ auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
+ [this](const beans::PropertyValue& rProp) {
+ return "SdtEndBefore" == rProp.Name && m_aRunSdt.m_bStartedSdt && !m_bEndCharSdt; });
+ if (pProp != std::cend(aGrabBag))
+ pProp->Value >>= m_bEndCharSdt;
+}
+
+void DocxAttributeOutput::WritePostponedGraphic()
+{
+ for (const auto & rPostponedDiagram : *m_oPostponedGraphic)
+ FlyFrameGraphic(rPostponedDiagram.grfNode, rPostponedDiagram.size,
+ nullptr, nullptr,
+ rPostponedDiagram.pSdrObj);
+ m_oPostponedGraphic.reset();
+}
+
+void DocxAttributeOutput::WritePostponedDiagram()
+{
+ for( const auto & rPostponedDiagram : *m_oPostponedDiagrams )
+ m_rExport.SdrExporter().writeDiagram(rPostponedDiagram.object,
+ *rPostponedDiagram.frame, m_anchorId++);
+ m_oPostponedDiagrams.reset();
+}
+
+bool DocxAttributeOutput::FootnoteEndnoteRefTag()
+{
+ if( m_footnoteEndnoteRefTag == 0 )
+ return false;
+
+ // output the character style for MS Word's benefit
+ const SwEndNoteInfo& rInfo = m_footnoteEndnoteRefTag == XML_footnoteRef ?
+ m_rExport.m_rDoc.GetFootnoteInfo() : m_rExport.m_rDoc.GetEndNoteInfo();
+ const SwCharFormat* pCharFormat = rInfo.GetCharFormat( m_rExport.m_rDoc );
+ if ( pCharFormat )
+ {
+ const OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ }
+
+ if (m_footnoteCustomLabel.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, m_footnoteEndnoteRefTag);
+ else
+ RunText(m_footnoteCustomLabel);
+ m_footnoteEndnoteRefTag = 0;
+ return true;
+}
+
+/** Output sal_Unicode* as a run text (<t>the text</t>).
+
+ When bMove is true, update rBegin to point _after_ the end of the text +
+ 1, meaning that it skips one character after the text. This is to make
+ the switch in DocxAttributeOutput::RunText() nicer ;-)
+ */
+static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken,
+ const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true,
+ const OUString& rSymbolFont = OUString() )
+{
+ const sal_Unicode *pBegin = rBegin;
+
+ // skip one character after the end
+ if ( bMove )
+ rBegin = pEnd + 1;
+
+ if ( pBegin >= pEnd )
+ return false; // we want to write at least one character
+
+ bool bIsSymbol = !rSymbolFont.isEmpty();
+
+ std::u16string_view aView( pBegin, pEnd - pBegin );
+ if (bIsSymbol)
+ {
+ for (char16_t aChar : aView)
+ {
+ pSerializer->singleElementNS(XML_w, XML_sym,
+ FSNS(XML_w, XML_font), rSymbolFont,
+ FSNS(XML_w, XML_char), OString::number(aChar, 16));
+ }
+ }
+ else
+ {
+ // we have to add 'preserve' when starting/ending with space
+ if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' )
+ pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve");
+ else
+ pSerializer->startElementNS(XML_w, nTextToken);
+
+ pSerializer->writeEscaped( aView );
+ pSerializer->endElementNS( XML_w, nTextToken );
+ }
+
+ return true;
+}
+
+void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/, const OUString& rSymbolFont )
+{
+ if( m_closeHyperlinkInThisRun )
+ {
+ m_closeHyperlinkInPreviousRun = true;
+ }
+ m_bRunTextIsOn = true;
+ // one text can be split into more <w:t>blah</w:t>'s by line breaks etc.
+ const sal_Unicode *pBegin = rText.getStr();
+ const sal_Unicode *pEnd = pBegin + rText.getLength();
+
+ // the text run is usually XML_t, with the exception of the deleted (and not moved) text
+ sal_Int32 nTextToken = XML_t;
+
+ bool bMoved = m_pRedlineData && m_pRedlineData->IsMoved() &&
+ // tdf#150166 save tracked moving around TOC as w:ins, w:del
+ SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr;
+
+ if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete && !bMoved )
+ {
+ nTextToken = XML_delText;
+ }
+
+ sal_Unicode prevUnicode = *pBegin;
+
+ for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt )
+ {
+ switch ( *pIt )
+ {
+ case 0x09: // tab
+ impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
+ m_pSerializer->singleElementNS(XML_w, XML_tab);
+ prevUnicode = *pIt;
+ break;
+ case 0x0b: // line break
+ case static_cast<sal_Unicode>(text::ControlCharacter::LINE_BREAK):
+ {
+ if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || prevUnicode < 0x0020)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_br);
+ prevUnicode = *pIt;
+ }
+ }
+ break;
+ case 0x1E: //non-breaking hyphen
+ impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
+ m_pSerializer->singleElementNS(XML_w, XML_noBreakHyphen);
+ prevUnicode = *pIt;
+ break;
+ case 0x1F: //soft (on demand) hyphen
+ impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
+ m_pSerializer->singleElementNS(XML_w, XML_softHyphen);
+ prevUnicode = *pIt;
+ break;
+ default:
+ if ( *pIt < 0x0020 ) // filter out the control codes
+ {
+ impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
+ SAL_INFO("sw.ww8", "Ignored control code in a text run: " << unsigned(*pIt) );
+ }
+ prevUnicode = *pIt;
+ break;
+ }
+ }
+
+ impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false, rSymbolFont );
+}
+
+void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/)
+{
+ m_sRawText = rText;
+}
+
+void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby )
+{
+ WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() );
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" );
+ EndRun( &rNode, nPos, -1 ); // end run before starting ruby to avoid nested runs, and overlap
+ assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby
+ assert(!m_closeHyperlinkInPreviousRun);
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->startElementNS(XML_w, XML_ruby);
+ m_pSerializer->startElementNS(XML_w, XML_rubyPr);
+
+ m_pSerializer->singleElementNS( XML_w, XML_rubyAlign,
+ FSNS( XML_w, XML_val ), lclConvertWW8JCToOOXMLRubyAlign(aWW8Ruby.GetJC()) );
+ sal_uInt32 nHps = (aWW8Ruby.GetRubyHeight() + 5) / 10;
+ sal_uInt32 nHpsBaseText = (aWW8Ruby.GetBaseHeight() + 5) / 10;
+ m_pSerializer->singleElementNS(XML_w, XML_hps, FSNS(XML_w, XML_val), OString::number(nHps));
+
+ m_pSerializer->singleElementNS( XML_w, XML_hpsRaise,
+ FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
+
+ m_pSerializer->singleElementNS( XML_w, XML_hpsBaseText,
+ FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
+
+ lang::Locale aLocale( SwBreakIt::Get()->GetLocale(
+ rNode.GetLang( nPos ) ) );
+ OUString sLang( LanguageTag::convertToBcp47( aLocale) );
+ m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val), sLang);
+
+ m_pSerializer->endElementNS( XML_w, XML_rubyPr );
+
+ m_pSerializer->startElementNS(XML_w, XML_rt);
+ StartRun( nullptr, nPos );
+ StartRunProperties( );
+
+ if (rRuby.GetTextRuby() && rRuby.GetTextRuby()->GetCharFormat())
+ {
+ const SwCharFormat* pFormat = rRuby.GetTextRuby()->GetCharFormat();
+ sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0);
+ TypedWhichId<SvxFontItem> nWhichFont = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONT : RES_CHRATR_CJK_FONT;
+ TypedWhichId<SvxFontHeightItem> nWhichFontSize = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONTSIZE : RES_CHRATR_CJK_FONTSIZE;
+
+ CharFont(pFormat->GetFormatAttr(nWhichFont));
+ CharFontSize(pFormat->GetFormatAttr(nWhichFontSize));
+ CharFontSize(pFormat->GetFormatAttr(RES_CHRATR_CTL_FONTSIZE));
+ }
+
+ EndRunProperties( nullptr );
+ RunText( rRuby.GetText( ) );
+ EndRun( &rNode, nPos, -1 );
+ m_pSerializer->endElementNS( XML_w, XML_rt );
+
+ m_pSerializer->startElementNS(XML_w, XML_rubyBase);
+ StartRun( nullptr, nPos );
+}
+
+void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos)
+{
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" );
+ EndRun( &rNode, nPos, -1 );
+ m_pSerializer->endElementNS( XML_w, XML_rubyBase );
+ m_pSerializer->endElementNS( XML_w, XML_ruby );
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it
+}
+
+bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark )
+{
+ bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );
+ if (bBookMarkOnly)
+ *pMark = GetExport().BookmarkToWord(*pMark);
+
+ if (!pMark->isEmpty() && (bBookMarkOnly || rTarget.isEmpty()))
+ {
+ OUString sURL = *pLinkURL;
+
+ if ( bBookMarkOnly )
+ sURL = FieldString( ww::eHYPERLINK );
+ else
+ sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\"";
+
+ sURL += " \\l \"" + *pMark + "\"";
+
+ if ( !rTarget.isEmpty() )
+ sURL += " \\n " + rTarget;
+
+ *pLinkURL = sURL;
+ }
+
+ return bBookMarkOnly;
+}
+
+void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos )
+{
+ m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName));
+ m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName));
+}
+
+bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget )
+{
+ OUString sMark;
+ OUString sUrl;
+
+ bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark );
+
+ m_hyperLinkAnchor = sMark;
+
+ if (!sMark.isEmpty() && !bBookmarkOnly && rTarget.isEmpty())
+ {
+ m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl );
+ }
+ else
+ {
+ // Output a hyperlink XML element
+ m_pHyperlinkAttrList = FastSerializerHelper::createAttrList();
+
+ if ( !bBookmarkOnly )
+ {
+ OUString sId = GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
+ oox::getRelationship(Relationship::HYPERLINK),
+ sUrl, true );
+
+ m_pHyperlinkAttrList->add(FSNS(XML_r, XML_id), sId);
+ if (!sMark.isEmpty())
+ {
+ sMark = sMark.replace(' ', '_');
+ m_pHyperlinkAttrList->add(FSNS(XML_w, XML_anchor), sMark);
+ }
+ }
+ else
+ {
+ // Is this a link to a sequence? Then try to replace that with a
+ // normal bookmark, as Word won't understand our special
+ // <seqname>!<index>|sequence syntax.
+ if (sMark.endsWith("|sequence"))
+ {
+ sal_Int32 nPos = sMark.indexOf('!');
+ if (nPos != -1)
+ {
+ // Extract <seqname>, the field instruction text has the name quoted.
+ OUString aSequenceName = sMark.copy(0, nPos);
+ // Extract <index>.
+ sal_uInt32 nIndex = o3tl::toUInt32(sMark.subView(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")));
+ auto it = m_aSeqBookmarksNames.find(aSequenceName);
+ if (it != m_aSeqBookmarksNames.end())
+ {
+ std::vector<OUString>& rNames = it->second;
+ if (rNames.size() > nIndex)
+ // We know the bookmark name for this sequence and this index, do the replacement.
+ sMark = rNames[nIndex];
+ }
+ }
+ }
+ else if (sMark.endsWith("|toxmark"))
+ {
+ if (auto const it = GetExport().m_TOXMarkBookmarksByURL.find(sMark);
+ it != GetExport().m_TOXMarkBookmarksByURL.end())
+ {
+ sMark = it->second;
+ }
+ }
+ // Spaces are prohibited in bookmark name.
+ sMark = sMark.replace(' ', '_');
+ m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ), sMark );
+ }
+
+ if ( !rTarget.isEmpty() )
+ {
+ m_pHyperlinkAttrList->add(FSNS(XML_w, XML_tgtFrame), rTarget);
+ }
+ }
+
+ return true;
+}
+
+bool DocxAttributeOutput::EndURL(bool const)
+{
+ m_closeHyperlinkInThisRun = true;
+ if (m_nHyperLinkCount.back() > 0 && !m_hyperLinkAnchor.isEmpty()
+ && m_hyperLinkAnchor.startsWith("_Toc"))
+ {
+ m_endPageRef = true;
+ }
+ return true;
+}
+
+void DocxAttributeOutput::FieldVanish(const OUString& rText,
+ ww::eField const eType, OUString const*const pBookmarkName)
+{
+ WriteField_Impl(nullptr, eType, rText, FieldFlags::All, pBookmarkName);
+}
+
+// The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
+// 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
+// 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
+void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData)
+{
+ if ( !pRedlineData )
+ return;
+
+ bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ) && !SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
+
+ OString aId( OString::number( pRedlineData->GetSeqNo() ) );
+ const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
+ const DateTime aDateTime = pRedlineData->GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo ||
+ ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
+
+ switch( pRedlineData->GetType() )
+ {
+ case RedlineType::Insert:
+ break;
+
+ case RedlineType::Delete:
+ break;
+
+ case RedlineType::Format:
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ pAttributeList->add(FSNS( XML_w, XML_id ), aId);
+ pAttributeList->add(FSNS( XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor.toUtf8());
+ if (!bNoDate)
+ pAttributeList->add(FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ));
+ m_pSerializer->startElementNS( XML_w, XML_rPrChange, pAttributeList );
+
+ // Check if there is any extra data stored in the redline object
+ if (pRedlineData->GetExtraData())
+ {
+ const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
+ const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
+
+ // Check if the extra data is of type 'formatting changes'
+ if (pFormattingChanges)
+ {
+ // Get the item set that holds all the changes properties
+ const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
+ if (pChangesSet)
+ {
+ m_pSerializer->mark(Tag_Redline_1);
+
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+
+ // Output the redline item set
+ if (pChangesSet)
+ m_rExport.OutputItemSet( *pChangesSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
+
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+
+ m_pSerializer->mergeTopMarks(Tag_Redline_1, sax_fastparser::MergeMarks::PREPEND);
+ }
+ }
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_rPrChange );
+ break;
+ }
+ case RedlineType::ParagraphFormat:
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ pAttributeList->add(FSNS( XML_w, XML_id ), aId);
+ pAttributeList->add(FSNS( XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor.toUtf8());
+ if (!bNoDate)
+ pAttributeList->add(FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ));
+ m_pSerializer->startElementNS( XML_w, XML_pPrChange, pAttributeList );
+
+ // Check if there is any extra data stored in the redline object
+ if (pRedlineData->GetExtraData())
+ {
+ const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
+ const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
+
+ // Check if the extra data is of type 'formatting changes'
+ if (pFormattingChanges)
+ {
+ // Get the item set that holds all the changes properties
+ const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
+ const OUString & sParaStyleName = pFormattingChanges->GetFormatName();
+ if (pChangesSet || !sParaStyleName.isEmpty())
+ {
+ m_pSerializer->mark(Tag_Redline_2);
+
+ m_pSerializer->startElementNS(XML_w, XML_pPr);
+
+ if (!sParaStyleName.isEmpty())
+ {
+ OString sStyleName;
+ if (auto format = m_rExport.m_rDoc.FindTextFormatCollByName(sParaStyleName))
+ if (auto slot = m_rExport.m_pStyles->GetSlot(format); slot != 0xfff)
+ sStyleName = m_rExport.m_pStyles->GetStyleId(slot);
+ // The resolved style name can be empty at this point, sParaStyleName can be
+ // an arbitrary string from the original document.
+ // Note that Word does *not* roundtrip unknown style names in redlines!
+ if (sStyleName.isEmpty())
+ sStyleName = MSWordStyles::CreateStyleId(sParaStyleName);
+ if (!sStyleName.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName);
+ }
+
+ // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information
+ // that should be collected by different properties in the core, and are all flushed together
+ // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called.
+ // So we need to store the current status of these lists, so that we can revert back to them when
+ // we are done exporting the redline attributes.
+ auto pFlyAttrList_Original(detachFrom(m_rExport.SdrExporter().getFlyAttrList()));
+ auto pLRSpaceAttrList_Original(detachFrom(m_pLRSpaceAttrList));
+ auto pParagraphSpacingAttrList_Original(detachFrom(m_pParagraphSpacingAttrList));
+
+ // Output the redline item set
+ if (pChangesSet)
+ m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
+
+ // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
+ WriteCollectedParagraphProperties();
+
+ // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
+ m_rExport.SdrExporter().getFlyAttrList() = std::move(pFlyAttrList_Original);
+ m_pLRSpaceAttrList = std::move(pLRSpaceAttrList_Original);
+ m_pParagraphSpacingAttrList = std::move(pParagraphSpacingAttrList_Original);
+
+ m_pSerializer->endElementNS( XML_w, XML_pPr );
+
+ m_pSerializer->mergeTopMarks(Tag_Redline_2, sax_fastparser::MergeMarks::PREPEND);
+ }
+ }
+ }
+ m_pSerializer->endElementNS( XML_w, XML_pPrChange );
+ break;
+ }
+ default:
+ SAL_WARN("sw.ww8", "Unhandled redline type for export " << SwRedlineTypeToOUString(pRedlineData->GetType()));
+ break;
+ }
+}
+
+// The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
+// 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
+// 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
+void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData, bool bLastRun )
+{
+ if ( !pRedlineData )
+ return;
+
+ // write out stack of this redline recursively (first the oldest)
+ if ( !bLastRun )
+ StartRedline( pRedlineData->Next(), false );
+
+ OString aId( OString::number( m_nRedlineId++ ) );
+
+ bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ) && !SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
+
+ const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
+ OString aAuthor( OUStringToOString( bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor, RTL_TEXTENCODING_UTF8 ) );
+
+ const DateTime aDateTime = pRedlineData->GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo ||
+ ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
+ bool bMoved = pRedlineData->IsMoved() &&
+ // tdf#150166 save tracked moving around TOC as w:ins, w:del
+ SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr;
+ switch ( pRedlineData->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Delete:
+ {
+ sal_Int32 eElement = RedlineType::Insert == pRedlineData->GetType()
+ ? ( bMoved ? XML_moveTo : XML_ins )
+ : ( bMoved ? XML_moveFrom : XML_del );
+ if ( bNoDate )
+ m_pSerializer->startElementNS( XML_w, eElement,
+ FSNS( XML_w, XML_id ), aId,
+ FSNS( XML_w, XML_author ), aAuthor );
+ else
+ m_pSerializer->startElementNS( XML_w, eElement,
+ FSNS( XML_w, XML_id ), aId,
+ FSNS( XML_w, XML_author ), aAuthor,
+ FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) );
+ break;
+ }
+ case RedlineType::Format:
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" );
+ break;
+ default:
+ break;
+ }
+}
+
+void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData, bool bLastRun )
+{
+ if ( !pRedlineData || m_bWritingField )
+ return;
+
+ bool bMoved = pRedlineData->IsMoved() &&
+ // tdf#150166 save tracked moving around TOC as w:ins, w:del
+ SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr;
+ switch ( pRedlineData->GetType() )
+ {
+ case RedlineType::Insert:
+ m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveTo : XML_ins );
+ break;
+
+ case RedlineType::Delete:
+ m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveFrom : XML_del );
+ break;
+
+ case RedlineType::Format:
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRedline()" );
+ break;
+ default:
+ break;
+ }
+
+ // write out stack of this redline recursively (first the newest)
+ if ( !bLastRun )
+ EndRedline( pRedlineData->Next(), false );
+}
+
+void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t )
+{
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" );
+}
+
+void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
+{
+ OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle));
+
+ m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), aStyleId);
+}
+
+static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist,
+ bool bWriteShadow, const table::BorderLine2* pStyleProps = nullptr)
+{
+ // Compute val attribute value
+ // Can be one of:
+ // single, double,
+ // basicWideOutline, basicWideInline
+ // OOXml also supports those types of borders, but we'll try to play with the first ones.
+ // thickThinMediumGap, thickThinLargeGap, thickThinSmallGap
+ // thinThickLargeGap, thinThickMediumGap, thinThickSmallGap
+ const char* pVal = "nil";
+ if ( pBorderLine && !pBorderLine->isEmpty( ) )
+ {
+ switch (pBorderLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ pVal = "single";
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ pVal = "dotted";
+ break;
+ case SvxBorderLineStyle::DASHED:
+ pVal = "dashed";
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ pVal = "double";
+ break;
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ pVal = "thinThickSmallGap";
+ break;
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ pVal = "thinThickMediumGap";
+ break;
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ pVal = "thinThickLargeGap";
+ break;
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ pVal = "thickThinSmallGap";
+ break;
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ pVal = "thickThinMediumGap";
+ break;
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ pVal = "thickThinLargeGap";
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ pVal = "threeDEmboss";
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ pVal = "threeDEngrave";
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ pVal = "outset";
+ break;
+ case SvxBorderLineStyle::INSET:
+ pVal = "inset";
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ pVal = "dashSmallGap";
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ pVal = "dotDash";
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ pVal = "dotDotDash";
+ break;
+ case SvxBorderLineStyle::NONE:
+ default:
+ break;
+ }
+ }
+ else if (!pStyleProps || !pStyleProps->LineWidth)
+ // no line, and no line set by the style either:
+ // there is no need to write the property
+ return;
+
+ // compare the properties with the theme properties before writing them:
+ // if they are equal, it means that they were style-defined and there is
+ // no need to write them.
+ if (pStyleProps && pBorderLine && !pBorderLine->isEmpty()
+ && pBorderLine->GetBorderLineStyle()
+ == static_cast<SvxBorderLineStyle>(pStyleProps->LineStyle)
+ && pBorderLine->GetColor() == Color(ColorTransparency, pStyleProps->Color)
+ && pBorderLine->GetWidth() == o3tl::toTwips(pStyleProps->LineWidth, o3tl::Length::mm100))
+ {
+ return;
+ }
+
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+ pAttr->add( FSNS( XML_w, XML_val ), pVal );
+
+ if ( pBorderLine && !pBorderLine->isEmpty() )
+ {
+ // Compute the sz attribute
+
+ double const fConverted( ::editeng::ConvertBorderWidthToWord(
+ pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth()));
+ // The unit is the 8th of point
+ sal_Int32 nWidth = sal_Int32( fConverted / 2.5 );
+ const sal_Int32 nMinWidth = 2;
+ const sal_Int32 nMaxWidth = 96;
+
+ if ( nWidth > nMaxWidth )
+ nWidth = nMaxWidth;
+ else if ( nWidth < nMinWidth )
+ nWidth = nMinWidth;
+
+ pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) );
+
+ // Get the distance (in pt)
+ pAttr->add(FSNS(XML_w, XML_space), OString::number(rtl::math::round(nDist / 20.0)));
+
+ // Get the color code as an RRGGBB hex value
+ OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) );
+ pAttr->add( FSNS(XML_w, XML_color), sColor);
+
+ model::ComplexColor const& rComplexColor = pBorderLine->getComplexColor();
+ lclAddThemeColorAttributes(pAttr, rComplexColor);
+ }
+
+ if (bWriteShadow)
+ {
+ // Set the shadow value
+ pAttr->add( FSNS( XML_w, XML_shadow ), "1" );
+ }
+
+ pSerializer->singleElementNS( XML_w, elementToken, pAttr );
+}
+
+static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma)
+{
+ OutputBorderOptions rOptions;
+
+ rOptions.tag = XML_tcBorders;
+ rOptions.bUseStartEnd = !bEcma;
+ rOptions.bWriteTag = true;
+ rOptions.bWriteDistance = false;
+
+ return rOptions;
+}
+
+static OutputBorderOptions lcl_getBoxBorderOptions()
+{
+ OutputBorderOptions rOptions;
+
+ rOptions.tag = XML_pBdr;
+ rOptions.bUseStartEnd = false;
+ rOptions.bWriteTag = false;
+ rOptions.bWriteDistance = true;
+
+ return rOptions;
+}
+
+static void impl_borders( FSHelperPtr const & pSerializer,
+ const SvxBoxItem& rBox,
+ const OutputBorderOptions& rOptions,
+ std::map<SvxBoxItemLine,
+ css::table::BorderLine2> &rTableStyleConf,
+ ww8::Frame* pFramePr = nullptr)
+{
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+
+ const sal_Int32 aXmlElements[] =
+ {
+ XML_top,
+ rOptions.bUseStartEnd ? XML_start : XML_left,
+ XML_bottom,
+ rOptions.bUseStartEnd ? XML_end : XML_right
+ };
+ bool tagWritten = false;
+ const SvxBoxItemLine* pBrd = aBorders;
+
+ for( int i = 0; i < 4; ++i, ++pBrd )
+ {
+ const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
+ const table::BorderLine2 *aStyleProps = nullptr;
+ auto it = rTableStyleConf.find( *pBrd );
+ if( it != rTableStyleConf.end() )
+ aStyleProps = &(it->second);
+
+ if (!tagWritten && rOptions.bWriteTag)
+ {
+ pSerializer->startElementNS(XML_w, rOptions.tag);
+ tagWritten = true;
+ }
+
+ bool bWriteShadow = false;
+ if (rOptions.aShadowLocation == SvxShadowLocation::NONE)
+ {
+ // The border has no shadow
+ }
+ else if (rOptions.aShadowLocation == SvxShadowLocation::BottomRight)
+ {
+ // Special case of 'Bottom-Right' shadow:
+ // If the shadow location is 'Bottom-Right' - then turn on the shadow
+ // for ALL the sides. This is because in Word - if you select a shadow
+ // for a border - it turn on the shadow for ALL the sides (but shows only
+ // the bottom-right one).
+ // This is so that no information will be lost if passed through LibreOffice
+ bWriteShadow = true;
+ }
+ else
+ {
+ // If there is a shadow, and it's not the regular 'Bottom-Right',
+ // then write only the 'shadowed' sides of the border
+ if (
+ ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::TOP ) ||
+ ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::BottomLeft) && *pBrd == SvxBoxItemLine::LEFT ) ||
+ ((rOptions.aShadowLocation == SvxShadowLocation::BottomLeft ) && *pBrd == SvxBoxItemLine::BOTTOM) ||
+ ((rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::RIGHT )
+ )
+ {
+ bWriteShadow = true;
+ }
+ }
+
+ sal_uInt16 nDist = 0;
+ if (rOptions.bWriteDistance)
+ {
+ if (rOptions.pDistances)
+ {
+ if ( *pBrd == SvxBoxItemLine::TOP)
+ nDist = rOptions.pDistances->nTop;
+ else if ( *pBrd == SvxBoxItemLine::LEFT)
+ nDist = rOptions.pDistances->nLeft;
+ else if ( *pBrd == SvxBoxItemLine::BOTTOM)
+ nDist = rOptions.pDistances->nBottom;
+ else if ( *pBrd == SvxBoxItemLine::RIGHT)
+ nDist = rOptions.pDistances->nRight;
+ }
+ else
+ {
+ nDist = rBox.GetDistance(*pBrd);
+ }
+ }
+
+ if (pFramePr)
+ {
+ assert(rOptions.bWriteDistance && !rOptions.pDistances);
+
+ // In addition to direct properties, and paragraph styles,
+ // for framePr-floated paragraphs the frame borders also affect the exported values.
+
+ // For border spacing, there is a special situation to consider
+ // because a compat setting ignores left/right paragraph spacing on layout.
+ const SwFrameFormat& rFormat = pFramePr->GetFrameFormat();
+ const SvxBoxItem& rFramePrBox = rFormat.GetBox();
+ const IDocumentSettingAccess& rIDSA = rFormat.GetDoc()->getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::INVERT_BORDER_SPACING)
+ && (*pBrd == SvxBoxItemLine::LEFT || *pBrd == SvxBoxItemLine::RIGHT))
+ {
+ // only the frame's border spacing affects layout - so use that value instead.
+ nDist = rFramePrBox.GetDistance(*pBrd);
+ }
+ else
+ {
+ nDist += rFramePrBox.GetDistance(*pBrd);
+ }
+
+ // Unless the user added a paragraph border, the border normally comes from the frame.
+ if (!pLn)
+ pLn = rFramePrBox.GetLine(*pBrd);
+ }
+
+ impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps );
+ }
+ if (tagWritten && rOptions.bWriteTag) {
+ pSerializer->endElementNS( XML_w, rOptions.tag );
+ }
+}
+
+void DocxAttributeOutput::ImplCellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins)
+{
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+
+ const sal_Int32 aXmlElements[] =
+ {
+ XML_top,
+ bUseStartEnd ? XML_start : XML_left,
+ XML_bottom,
+ bUseStartEnd ? XML_end : XML_right
+ };
+ bool tagWritten = false;
+ const SvxBoxItemLine* pBrd = aBorders;
+ for( int i = 0; i < 4; ++i, ++pBrd )
+ {
+ sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) );
+
+ if (pDefaultMargins)
+ {
+ // Skip output if cell margin == table default margin
+ if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist)
+ continue;
+ }
+
+ if (!tagWritten) {
+ pSerializer->startElementNS(XML_w, tag);
+ tagWritten = true;
+ }
+ pSerializer->singleElementNS( XML_w, aXmlElements[i],
+ FSNS( XML_w, XML_w ), OString::number(nDist),
+ FSNS( XML_w, XML_type ), "dxa" );
+ }
+ if (tagWritten) {
+ pSerializer->endElementNS( XML_w, tag );
+ }
+}
+
+void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
+{
+ m_pSerializer->startElementNS(XML_w, XML_tcPr);
+
+ const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
+
+ bool const bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+
+ // Output any table cell redlines if there are any attached to this specific cell
+ TableCellRedline( pTableTextNodeInfoInner );
+
+ // Cell preferred width
+ SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell );
+ if ( nCell )
+ nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 );
+ m_pSerializer->singleElementNS( XML_w, XML_tcW,
+ FSNS( XML_w, XML_w ), OString::number(nWidth),
+ FSNS( XML_w, XML_type ), "dxa" );
+
+ // Horizontal spans
+ const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
+ if (nRow >= rRows.size())
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::TableCellProperties: out of range row: " << nRow);
+ else
+ {
+ SwWriteTableRow *pRow = rRows[ nRow ].get();
+ const SwWriteTableCells& rTableCells = pRow->GetCells();
+ if (nCell < rTableCells.size() )
+ {
+ const SwWriteTableCell& rCell = *rTableCells[nCell];
+ const sal_uInt16 nColSpan = rCell.GetColSpan();
+ if ( nColSpan > 1 )
+ m_pSerializer->singleElementNS( XML_w, XML_gridSpan,
+ FSNS( XML_w, XML_val ), OString::number(nColSpan) );
+ }
+ }
+
+ // Vertical merges
+ ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
+ sal_Int32 vSpan = (*xRowSpans)[nCell];
+ if ( vSpan > 1 )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "restart");
+ }
+ else if ( vSpan < 0 )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "continue");
+ }
+
+ if (const SfxGrabBagItem* pItem = pTableBox->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
+ {
+ const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
+ std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle");
+ if (it != rGrabBag.end())
+ {
+ uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
+ m_pTableStyleExport->CnfStyle(aAttributes);
+ }
+ }
+
+
+ const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( );
+ const SvxBoxItem& rDefaultBox = (*m_TableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( );
+ {
+ // The cell borders
+ impl_borders(m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma),
+ m_aTableStyleConfs.back());
+ }
+
+ TableBackgrounds( pTableTextNodeInfoInner );
+
+ {
+ // Cell margins
+ DocxAttributeOutput::ImplCellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
+ }
+
+ TableVerticalCell( pTableTextNodeInfoInner );
+
+ m_pSerializer->endElementNS( XML_w, XML_tcPr );
+}
+
+void DocxAttributeOutput::InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ if (m_xTableWrt && pTable == m_xTableWrt->GetTable())
+ return;
+
+ tools::Long nPageSize = 0;
+ bool bRelBoxSize = false;
+
+ // Create the SwWriteTable instance to use col spans (and maybe other infos)
+ GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
+
+ const SwFrameFormat *pFormat = pTable->GetFrameFormat( );
+ const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( ));
+
+ const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout();
+ if( pLayout && pLayout->IsExportable() )
+ m_xTableWrt.reset(new SwWriteTable(pTable, pLayout));
+ else
+ m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false));
+}
+
+void DocxAttributeOutput::StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
+{
+ m_aTableStyleConfs.push_back({});
+
+ // In case any paragraph SDT's are open, close them here.
+ EndParaSdtBlock();
+
+ m_pSerializer->startElementNS(XML_w, XML_tbl);
+
+ m_TableFirstCells.push_back(pTableTextNodeInfoInner);
+ m_LastOpenCell.push_back(-1);
+ m_LastClosedCell.push_back(-1);
+
+ InitTableHelper( pTableTextNodeInfoInner );
+ TableDefinition( pTableTextNodeInfoInner );
+}
+
+void DocxAttributeOutput::EndTable()
+{
+ m_pSerializer->endElementNS( XML_w, XML_tbl );
+
+ if ( m_tableReference.m_nTableDepth > 0 )
+ --m_tableReference.m_nTableDepth;
+
+ m_LastClosedCell.pop_back();
+ m_LastOpenCell.pop_back();
+ m_TableFirstCells.pop_back();
+
+ // We closed the table; if it is a nested table, the cell that contains it
+ // still continues
+ // set to true only if we were in a nested table, not otherwise.
+ if( !m_TableFirstCells.empty() )
+ m_tableReference.m_bTableCellOpen = true;
+
+ // Cleans the table helper
+ m_xTableWrt.reset();
+
+ m_aTableStyleConfs.pop_back();
+}
+
+void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
+{
+ m_pSerializer->startElementNS(XML_w, XML_tr);
+
+ // Output the row properties
+ m_pSerializer->startElementNS(XML_w, XML_trPr);
+
+ // Header row: tblHeader
+ const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
+ if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
+ m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSNS(XML_w, XML_val), "true"); // TODO to overwrite table style may need explicit false
+
+ TableRowRedline( pTableTextNodeInfoInner );
+ TableHeight( pTableTextNodeInfoInner );
+ TableCanSplit( pTableTextNodeInfoInner );
+
+ const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTableLine = pTableBox->GetUpper();
+ if (const SfxGrabBagItem* pItem = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
+ {
+ const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
+ std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle");
+ if (it != rGrabBag.end())
+ {
+ uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
+ m_pTableStyleExport->CnfStyle(aAttributes);
+ }
+ }
+
+
+ m_pSerializer->endElementNS( XML_w, XML_trPr );
+}
+
+void DocxAttributeOutput::EndTableRow( )
+{
+ m_pSerializer->endElementNS( XML_w, XML_tr );
+ m_LastOpenCell.back() = -1;
+ m_LastClosedCell.back() = -1;
+}
+
+void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
+{
+ m_LastOpenCell.back() = nCell;
+
+ InitTableHelper( pTableTextNodeInfoInner );
+
+ // check tracked table column deletion or insertion
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ SwRedlineTable::size_type nChange = pTabBox->GetRedline();
+ if (nChange != SwRedlineTable::npos)
+ m_tableReference.m_bTableCellChanged = true;
+
+ m_pSerializer->startElementNS(XML_w, XML_tc);
+
+ // Write the cell properties here
+ TableCellProperties( pTableTextNodeInfoInner, nCell, nRow );
+
+ m_tableReference.m_bTableCellOpen = true;
+}
+
+void DocxAttributeOutput::EndTableCell(sal_uInt32 nCell)
+{
+ m_LastClosedCell.back() = nCell;
+ m_LastOpenCell.back() = -1;
+
+ if (m_tableReference.m_bTableCellParaSdtOpen)
+ EndParaSdtBlock();
+
+ m_pSerializer->endElementNS( XML_w, XML_tc );
+
+ m_tableReference.m_bTableCellOpen = false;
+ m_tableReference.m_bTableCellParaSdtOpen = false;
+ m_tableReference.m_bTableCellChanged = false;
+}
+
+void DocxAttributeOutput::StartStyles()
+{
+ m_pSerializer->startElementNS( XML_w, XML_styles,
+ FSNS( XML_xmlns, XML_w ), GetExport().GetFilter().getNamespaceURL(OOX_NS(doc)),
+ FSNS( XML_xmlns, XML_w14 ), GetExport().GetFilter().getNamespaceURL(OOX_NS(w14)),
+ FSNS( XML_xmlns, XML_mc ), GetExport().GetFilter().getNamespaceURL(OOX_NS(mce)),
+ FSNS( XML_mc, XML_Ignorable ), "w14" );
+
+ DocDefaults();
+ LatentStyles();
+}
+
+sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, std::u16string_view rName)
+{
+ OString sName = OUStringToOString(rName, RTL_TEXTENCODING_UTF8);
+ while (pMap->pToken)
+ {
+ if (sName == pMap->pToken)
+ return pMap->nToken;
+ ++pMap;
+ }
+ return 0;
+}
+
+namespace
+{
+
+DocxStringTokenMap const aDefaultTokens[] = {
+ {"defQFormat", XML_defQFormat},
+ {"defUnhideWhenUsed", XML_defUnhideWhenUsed},
+ {"defSemiHidden", XML_defSemiHidden},
+ {"count", XML_count},
+ {"defUIPriority", XML_defUIPriority},
+ {"defLockedState", XML_defLockedState},
+ {nullptr, 0}
+};
+
+DocxStringTokenMap const aExceptionTokens[] = {
+ {"name", XML_name},
+ {"locked", XML_locked},
+ {"uiPriority", XML_uiPriority},
+ {"semiHidden", XML_semiHidden},
+ {"unhideWhenUsed", XML_unhideWhenUsed},
+ {"qFormat", XML_qFormat},
+ {nullptr, 0}
+};
+
+}
+
+void DocxAttributeOutput::LatentStyles()
+{
+ // Do we have latent styles available?
+ uno::Reference<beans::XPropertySet> xPropertySet(m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aInteropGrabBag;
+ xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
+ uno::Sequence<beans::PropertyValue> aLatentStyles;
+ auto pProp = std::find_if(std::cbegin(aInteropGrabBag), std::cend(aInteropGrabBag),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "latentStyles"; });
+ if (pProp != std::cend(aInteropGrabBag))
+ pProp->Value >>= aLatentStyles;
+ if (!aLatentStyles.hasElements())
+ return;
+
+ // Extract default attributes first.
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList();
+ uno::Sequence<beans::PropertyValue> aLsdExceptions;
+ for (const auto& rLatentStyle : std::as_const(aLatentStyles))
+ {
+ if (sal_Int32 nToken = DocxStringGetToken(aDefaultTokens, rLatentStyle.Name))
+ pAttributeList->add(FSNS(XML_w, nToken), rLatentStyle.Value.get<OUString>());
+ else if (rLatentStyle.Name == "lsdExceptions")
+ rLatentStyle.Value >>= aLsdExceptions;
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_latentStyles, detachFrom(pAttributeList));
+
+ // Then handle the exceptions.
+ for (const auto& rLsdException : std::as_const(aLsdExceptions))
+ {
+ pAttributeList = FastSerializerHelper::createAttrList();
+
+ uno::Sequence<beans::PropertyValue> aAttributes;
+ rLsdException.Value >>= aAttributes;
+ for (const auto& rAttribute : std::as_const(aAttributes))
+ if (sal_Int32 nToken = DocxStringGetToken(aExceptionTokens, rAttribute.Name))
+ pAttributeList->add(FSNS(XML_w, nToken), rAttribute.Value.get<OUString>());
+
+ m_pSerializer->singleElementNS(XML_w, XML_lsdException, detachFrom(pAttributeList));
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_latentStyles);
+}
+
+void DocxAttributeOutput::OutputDefaultItem(const SfxPoolItem& rHt)
+{
+ bool bMustWrite = true;
+ switch (rHt.Which())
+ {
+ case RES_CHRATR_CASEMAP:
+ bMustWrite = static_cast< const SvxCaseMapItem& >(rHt).GetCaseMap() != SvxCaseMap::NotMapped;
+ break;
+ case RES_CHRATR_COLOR:
+ bMustWrite = static_cast< const SvxColorItem& >(rHt).GetValue() != COL_AUTO;
+ break;
+ case RES_CHRATR_CONTOUR:
+ bMustWrite = static_cast< const SvxContourItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_CROSSEDOUT:
+ bMustWrite = static_cast< const SvxCrossedOutItem& >(rHt).GetStrikeout() != STRIKEOUT_NONE;
+ break;
+ case RES_CHRATR_ESCAPEMENT:
+ bMustWrite = static_cast< const SvxEscapementItem& >(rHt).GetEscapement() != SvxEscapement::Off;
+ break;
+ case RES_CHRATR_FONT:
+ bMustWrite = true;
+ break;
+ case RES_CHRATR_FONTSIZE:
+ bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
+ break;
+ case RES_CHRATR_KERNING:
+ bMustWrite = static_cast< const SvxKerningItem& >(rHt).GetValue() != 0;
+ break;
+ case RES_CHRATR_LANGUAGE:
+ bMustWrite = true;
+ break;
+ case RES_CHRATR_POSTURE:
+ bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
+ break;
+ case RES_CHRATR_SHADOWED:
+ bMustWrite = static_cast< const SvxShadowedItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_UNDERLINE:
+ bMustWrite = static_cast< const SvxUnderlineItem& >(rHt).GetLineStyle() != LINESTYLE_NONE;
+ break;
+ case RES_CHRATR_WEIGHT:
+ bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
+ break;
+ case RES_CHRATR_AUTOKERN:
+ bMustWrite = static_cast< const SvxAutoKernItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_BLINK:
+ bMustWrite = static_cast< const SvxBlinkItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_BACKGROUND:
+ {
+ const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
+ bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
+ rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
+ rBrushItem.GetGraphic() != nullptr ||
+ rBrushItem.GetGraphicObject() != nullptr);
+ }
+ break;
+
+ case RES_CHRATR_CJK_FONT:
+ bMustWrite = true;
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ bMustWrite = false; // we have written it already as RES_CHRATR_FONTSIZE
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ bMustWrite = true;
+ break;
+ case RES_CHRATR_CJK_POSTURE:
+ bMustWrite = false; // we have written it already as RES_CHRATR_POSTURE
+ break;
+ case RES_CHRATR_CJK_WEIGHT:
+ bMustWrite = false; // we have written it already as RES_CHRATR_WEIGHT
+ break;
+
+ case RES_CHRATR_CTL_FONT:
+ bMustWrite = true;
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ bMustWrite = true;
+ break;
+ case RES_CHRATR_CTL_POSTURE:
+ bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
+ break;
+ case RES_CHRATR_CTL_WEIGHT:
+ bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
+ break;
+
+ case RES_CHRATR_ROTATE:
+ bMustWrite = static_cast< const SvxCharRotateItem& >(rHt).GetValue() != 0_deg10;
+ break;
+ case RES_CHRATR_EMPHASIS_MARK:
+ bMustWrite = static_cast< const SvxEmphasisMarkItem& >(rHt).GetEmphasisMark() != FontEmphasisMark::NONE;
+ break;
+ case RES_CHRATR_TWO_LINES:
+ bMustWrite = static_cast< const SvxTwoLinesItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_SCALEW:
+ bMustWrite = static_cast< const SvxCharScaleWidthItem& >(rHt).GetValue() != 100;
+ break;
+ case RES_CHRATR_RELIEF:
+ bMustWrite = static_cast< const SvxCharReliefItem& >(rHt).GetValue() != FontRelief::NONE;
+ break;
+ case RES_CHRATR_HIDDEN:
+ bMustWrite = static_cast< const SvxCharHiddenItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_BOX:
+ {
+ const SvxBoxItem& rBoxItem = static_cast< const SvxBoxItem& >(rHt);
+ bMustWrite = rBoxItem.GetTop() || rBoxItem.GetLeft() ||
+ rBoxItem.GetBottom() || rBoxItem.GetRight() ||
+ rBoxItem.GetSmallestDistance();
+ }
+ break;
+ case RES_CHRATR_HIGHLIGHT:
+ {
+ const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
+ bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
+ rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
+ rBrushItem.GetGraphic() != nullptr ||
+ rBrushItem.GetGraphicObject() != nullptr);
+ }
+ break;
+
+ case RES_PARATR_LINESPACING:
+ bMustWrite = static_cast< const SvxLineSpacingItem& >(rHt).GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off;
+ break;
+ case RES_PARATR_ADJUST:
+ bMustWrite = static_cast< const SvxAdjustItem& >(rHt).GetAdjust() != SvxAdjust::Left;
+ break;
+ case RES_PARATR_SPLIT:
+ bMustWrite = !static_cast< const SvxFormatSplitItem& >(rHt).GetValue();
+ break;
+ case RES_PARATR_WIDOWS:
+ bMustWrite = static_cast< const SvxWidowsItem& >(rHt).GetValue();
+ break;
+ case RES_PARATR_TABSTOP:
+ bMustWrite = static_cast< const SvxTabStopItem& >(rHt).Count() != 0;
+ break;
+ case RES_PARATR_HYPHENZONE:
+ bMustWrite = true;
+ break;
+ case RES_PARATR_NUMRULE:
+ bMustWrite = !static_cast< const SwNumRuleItem& >(rHt).GetValue().isEmpty();
+ break;
+ case RES_PARATR_SCRIPTSPACE:
+ bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
+ break;
+ case RES_PARATR_HANGINGPUNCTUATION:
+ bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
+ break;
+ case RES_PARATR_FORBIDDEN_RULES:
+ bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
+ break;
+ case RES_PARATR_VERTALIGN:
+ bMustWrite = static_cast< const SvxParaVertAlignItem& >(rHt).GetValue() != SvxParaVertAlignItem::Align::Automatic;
+ break;
+ case RES_PARATR_SNAPTOGRID:
+ bMustWrite = !static_cast< const SvxParaGridItem& >(rHt).GetValue();
+ break;
+ case RES_CHRATR_GRABBAG:
+ bMustWrite = true;
+ break;
+
+ default:
+ SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
+ break;
+ }
+
+ if (bMustWrite)
+ OutputItem(rHt);
+}
+
+void DocxAttributeOutput::DocDefaults( )
+{
+ // Write the '<w:docDefaults>' section here
+ m_pSerializer->startElementNS(XML_w, XML_docDefaults);
+
+ // Output the default run properties
+ m_pSerializer->startElementNS(XML_w, XML_rPrDefault);
+
+ StartStyleProperties(false, 0);
+
+ for (int i = int(RES_CHRATR_BEGIN); i < int(RES_CHRATR_END); ++i)
+ OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i));
+
+ EndStyleProperties(false);
+
+ m_pSerializer->endElementNS(XML_w, XML_rPrDefault);
+
+ // Output the default paragraph properties
+ m_pSerializer->startElementNS(XML_w, XML_pPrDefault);
+
+ StartStyleProperties(true, 0);
+
+ for (int i = int(RES_PARATR_BEGIN); i < int(RES_PARATR_END); ++i)
+ OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i));
+
+ EndStyleProperties(true);
+
+ m_pSerializer->endElementNS(XML_w, XML_pPrDefault);
+
+ m_pSerializer->endElementNS(XML_w, XML_docDefaults);
+}
+
+void DocxAttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
+{
+ // HACK
+ // Ms Office seems to have an internal limitation of 4091 styles
+ // and refuses to load .docx with more, even though the spec seems to allow that;
+ // so simply if there are more styles, don't export those
+ const sal_Int32 nCountStylesToWrite = MSWORD_MAX_STYLES_LIMIT - nNumberOfStyles;
+ m_pTableStyleExport->TableStyles(nCountStylesToWrite);
+ m_pSerializer->endElementNS( XML_w, XML_styles );
+}
+
+void DocxAttributeOutput::DefaultStyle()
+{
+ // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)?
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::DefaultStyle()");
+}
+
+/* Writes <a:srcRect> tag back to document.xml if a file contains a cropped image.
+* NOTE : Tested on images of type JPEG,EMF/WMF,BMP, PNG and GIF.
+*/
+void DocxAttributeOutput::WriteSrcRect(
+ const css::uno::Reference<css::beans::XPropertySet>& xShapePropSet,
+ const SwFrameFormat* pFrameFormat)
+{
+ uno::Reference<graphic::XGraphic> xGraphic;
+ xShapePropSet->getPropertyValue("Graphic") >>= xGraphic;
+ const Graphic aGraphic(xGraphic);
+
+ Size aOriginalSize(aGraphic.GetPrefSize());
+
+ const MapMode aMap100mm( MapUnit::Map100thMM );
+ const MapMode& rMapMode = aGraphic.GetPrefMapMode();
+ if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
+ {
+ aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, aMap100mm);
+ }
+
+ css::text::GraphicCrop aGraphicCropStruct;
+ xShapePropSet->getPropertyValue("GraphicCrop") >>= aGraphicCropStruct;
+ sal_Int32 nCropL = aGraphicCropStruct.Left;
+ sal_Int32 nCropR = aGraphicCropStruct.Right;
+ sal_Int32 nCropT = aGraphicCropStruct.Top;
+ sal_Int32 nCropB = aGraphicCropStruct.Bottom;
+
+ // simulate border padding as a negative crop.
+ const SvxBoxItem* pBoxItem;
+ if (pFrameFormat && (pBoxItem = pFrameFormat->GetItemIfSet(RES_BOX, false)))
+ {
+ nCropL -= pBoxItem->GetDistance( SvxBoxItemLine::LEFT );
+ nCropR -= pBoxItem->GetDistance( SvxBoxItemLine::RIGHT );
+ nCropT -= pBoxItem->GetDistance( SvxBoxItemLine::TOP );
+ nCropB -= pBoxItem->GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+
+ if (nCropL == 0 && nCropT == 0 && nCropR == 0 && nCropB == 0)
+ return;
+
+ double widthMultiplier = 100000.0/aOriginalSize.Width();
+ double heightMultiplier = 100000.0/aOriginalSize.Height();
+
+ sal_Int32 left = static_cast<sal_Int32>(rtl::math::round(nCropL * widthMultiplier));
+ sal_Int32 right = static_cast<sal_Int32>(rtl::math::round(nCropR * widthMultiplier));
+ sal_Int32 top = static_cast<sal_Int32>(rtl::math::round(nCropT * heightMultiplier));
+ sal_Int32 bottom = static_cast<sal_Int32>(rtl::math::round(nCropB * heightMultiplier));
+
+ m_pSerializer->singleElementNS( XML_a, XML_srcRect,
+ XML_l, OString::number(left),
+ XML_t, OString::number(top),
+ XML_r, OString::number(right),
+ XML_b, OString::number(bottom) );
+}
+
+uno::Reference<css::text::XTextFrame> DocxAttributeOutput::GetUnoTextFrame(
+ css::uno::Reference<css::drawing::XShape> xShape)
+{
+ return SwTextBoxHelper::getUnoTextFrame(xShape);
+}
+
+static rtl::Reference<::sax_fastparser::FastAttributeList> CreateDocPrAttrList(
+ DocxExport & rExport, int const nAnchorId, std::u16string_view const& rName,
+ std::u16string_view const& rTitle, std::u16string_view const& rDescription)
+{
+ rtl::Reference<::sax_fastparser::FastAttributeList> const pAttrs(FastSerializerHelper::createAttrList());
+ pAttrs->add(XML_id, OString::number(nAnchorId));
+ pAttrs->add(XML_name, rName);
+ if (rExport.GetFilter().getVersion() != oox::core::ECMA_376_1ST_EDITION)
+ {
+ pAttrs->add(XML_descr, rDescription);
+ pAttrs->add(XML_title, rTitle);
+ }
+ else
+ { // tdf#148952 no title attribute, merge it into descr
+ OUString const value(rTitle.empty()
+ ? OUString(rDescription)
+ : rDescription.empty()
+ ? OUString(rTitle)
+ : OUString::Concat(rTitle) + "\n" + rDescription);
+ pAttrs->add(XML_descr, value);
+ }
+ return pAttrs;
+}
+
+void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj )
+{
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) - some stuff still missing" );
+
+ GetSdtEndBefore(pSdrObj);
+
+ // detect mis-use of the API
+ assert(pGrfNode || (pOLEFrameFormat && pOLENode));
+ const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat;
+ // create the relation ID
+ OString aRelId;
+ OUString sSvgRelId;
+ sal_Int32 nImageType;
+ if ( pGrfNode && pGrfNode->IsLinkedFile() )
+ {
+ // linked image, just create the relation
+ OUString aFileName;
+ pGrfNode->GetFileFilterNms( &aFileName, nullptr );
+
+ sal_Int32 const nFragment(aFileName.indexOf('#'));
+ sal_Int32 const nForbiddenU(aFileName.indexOf("%5C"));
+ sal_Int32 const nForbiddenL(aFileName.indexOf("%5c"));
+ if ( (nForbiddenU != -1 && (nFragment == -1 || nForbiddenU < nFragment))
+ || (nForbiddenL != -1 && (nFragment == -1 || nForbiddenL < nFragment)))
+ {
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::FlyFrameGraphic: ignoring image with invalid link URL");
+ return;
+ }
+
+ // TODO Convert the file name to relative for better interoperability
+
+ aRelId = m_rExport.AddRelation(
+ oox::getRelationship(Relationship::IMAGE),
+ aFileName );
+
+ nImageType = XML_link;
+ }
+ else
+ {
+ // inline, we also have to write the image itself
+ Graphic aGraphic;
+ if (pGrfNode)
+ aGraphic = pGrfNode->GetGrf();
+ else
+ aGraphic = *pOLENode->GetGraphic();
+
+ m_rDrawingML.SetFS(m_pSerializer); // to be sure that we write to the right stream
+ auto pGraphicExport = m_rDrawingML.createGraphicExport();
+ OUString aImageId = pGraphicExport->writeToStorage(aGraphic, false);
+ aRelId = OUStringToOString(aImageId, RTL_TEXTENCODING_UTF8);
+
+ if (aGraphic.getVectorGraphicData() && aGraphic.getVectorGraphicData()->getType() == VectorGraphicDataType::Svg)
+ {
+ sSvgRelId = pGraphicExport->writeToStorage(aGraphic, false, drawingml::GraphicExport::TypeHint::SVG);
+ }
+ nImageType = XML_embed;
+ }
+
+ // In case there are any grab-bag items on the graphic frame, emit them now.
+ // These are always character grab-bags, as graphics are at-char or as-char in Word.
+ if (const SfxGrabBagItem* pGrabBag = pFrameFormat->GetAttrSet().GetItemIfSet(RES_FRMATR_GRABBAG))
+ {
+ CharGrabBag(*pGrabBag);
+ }
+
+ rtl::Reference<sax_fastparser::FastAttributeList> xFrameAttributes(
+ FastSerializerHelper::createAttrList());
+ if (pGrfNode)
+ {
+ const SwAttrSet& rSet = pGrfNode->GetSwAttrSet();
+ MirrorGraph eMirror = rSet.Get(RES_GRFATR_MIRRORGRF).GetValue();
+ if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
+ // Mirror on the vertical axis is a horizontal flip.
+ xFrameAttributes->add(XML_flipH, "1");
+ // RES_GRFATR_ROTATION is sal_uInt16; use sal_uInt32 for multiplication later
+ if (Degree10 nRot = rSet.Get(RES_GRFATR_ROTATION).GetValue())
+ {
+ // RES_GRFATR_ROTATION is in 10ths of degree; convert to 100ths for macro
+ sal_uInt32 mOOXMLRot = oox::drawingml::ExportRotateClockwisify(to<Degree100>(nRot));
+ xFrameAttributes->add(XML_rot, OString::number(mOOXMLRot));
+ }
+ }
+
+ css::uno::Reference<css::beans::XPropertySet> xShapePropSet;
+ if (pSdrObj)
+ {
+ css::uno::Reference<css::drawing::XShape> xShape(
+ const_cast<SdrObject*>(pSdrObj)->getUnoShape(), css::uno::UNO_QUERY);
+ xShapePropSet.set(xShape, css::uno::UNO_QUERY);
+ assert(xShapePropSet);
+ }
+
+ Size aSize = rSize;
+ // We need the original (cropped, but unrotated) size of object. So prefer the object data,
+ // and only use passed frame size as fallback.
+ if (xShapePropSet)
+ {
+ if (css::awt::Size val; xShapePropSet->getPropertyValue("Size") >>= val)
+ aSize = Size(o3tl::toTwips(val.Width, o3tl::Length::mm100), o3tl::toTwips(val.Height, o3tl::Length::mm100));
+ }
+
+ m_rExport.SdrExporter().startDMLAnchorInline(pFrameFormat, aSize);
+
+ // picture description (used for pic:cNvPr later too)
+ OUString const descr(pGrfNode ? pGrfNode->GetDescription() : pOLEFrameFormat->GetObjDescription());
+ OUString const title(pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle());
+ auto const docPrattrList(CreateDocPrAttrList(
+ GetExport(), m_anchorId++, pFrameFormat->GetName(), title, descr));
+ m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrattrList );
+
+ OUString sURL, sRelId;
+ if (xShapePropSet)
+ {
+ xShapePropSet->getPropertyValue("HyperLinkURL") >>= sURL;
+ if(!sURL.isEmpty())
+ {
+ if (sURL.startsWith("#") && sURL.indexOf(' ') != -1 && !sURL.endsWith("|outline") && !sURL.endsWith("|table") &&
+ !sURL.endsWith("|frame") && !sURL.endsWith("|graphic") && !sURL.endsWith("|ole") && !sURL.endsWith("|region"))
+ {
+ // Spaces are prohibited in bookmark name.
+ sURL = sURL.replace(' ', '_');
+ }
+ sRelId = GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
+ oox::getRelationship(Relationship::HYPERLINK),
+ sURL, !sURL.startsWith("#") );
+ m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
+ FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
+ FSNS( XML_r, XML_id ), sRelId);
+ }
+ AddExtLst(m_pSerializer, GetExport(), xShapePropSet);
+ }
+
+ m_pSerializer->endElementNS( XML_wp, XML_docPr );
+
+ m_pSerializer->startElementNS(XML_wp, XML_cNvGraphicFramePr);
+ // TODO change aspect?
+ m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks,
+ FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)),
+ XML_noChangeAspect, "1" );
+ m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr );
+
+ m_pSerializer->startElementNS( XML_a, XML_graphic,
+ FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) );
+ m_pSerializer->startElementNS( XML_a, XML_graphicData,
+ XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture" );
+
+ m_pSerializer->startElementNS( XML_pic, XML_pic,
+ FSNS( XML_xmlns, XML_pic ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlPicture)) );
+
+ m_pSerializer->startElementNS(XML_pic, XML_nvPicPr);
+ // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes
+ m_pSerializer->startElementNS(XML_pic, XML_cNvPr, docPrattrList);
+
+ if(!sURL.isEmpty())
+ m_pSerializer->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
+
+ m_pSerializer->endElementNS( XML_pic, XML_cNvPr );
+
+ m_pSerializer->startElementNS(XML_pic, XML_cNvPicPr);
+ // TODO change aspect?
+ m_pSerializer->singleElementNS( XML_a, XML_picLocks,
+ XML_noChangeAspect, "1", XML_noChangeArrowheads, "1" );
+ m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr );
+ m_pSerializer->endElementNS( XML_pic, XML_nvPicPr );
+
+ // the actual picture
+ m_pSerializer->startElementNS(XML_pic, XML_blipFill);
+
+/* At this point we are certain that, WriteImage returns empty RelId
+ for unhandled graphic type. Therefore we write the picture description
+ and not the relation( coz there ain't any), so that the user knows
+ there is an image/graphic in the doc but it is broken instead of
+ completely discarding it.
+*/
+ if ( aRelId.isEmpty() )
+ m_pSerializer->startElementNS(XML_a, XML_blip);
+ else
+ m_pSerializer->startElementNS(XML_a, XML_blip, FSNS(XML_r, nImageType), aRelId);
+
+ const SfxEnumItemInterface* pGrafModeItem = nullptr;
+ if ( pGrfNode && (pGrafModeItem = pGrfNode->GetSwAttrSet().GetItemIfSet(RES_GRFATR_DRAWMODE)))
+ {
+ GraphicDrawMode nMode = static_cast<GraphicDrawMode>(pGrafModeItem->GetEnumValue());
+ if (nMode == GraphicDrawMode::Greys)
+ m_pSerializer->singleElementNS (XML_a, XML_grayscl);
+ else if (nMode == GraphicDrawMode::Mono) //black/white has a 0,5 threshold in LibreOffice
+ m_pSerializer->singleElementNS (XML_a, XML_biLevel, XML_thresh, OString::number(50000));
+ else if (nMode == GraphicDrawMode::Watermark) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice
+ m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000) );
+ }
+
+ if (!sSvgRelId.isEmpty())
+ {
+ auto pGraphicExport = m_rDrawingML.createGraphicExport();
+ pGraphicExport->writeSvgExtension(sSvgRelId);
+ }
+
+ m_pSerializer->endElementNS( XML_a, XML_blip );
+
+ if (xShapePropSet)
+ WriteSrcRect(xShapePropSet, pFrameFormat);
+
+ m_pSerializer->startElementNS(XML_a, XML_stretch);
+ m_pSerializer->singleElementNS(XML_a, XML_fillRect);
+ m_pSerializer->endElementNS( XML_a, XML_stretch );
+ m_pSerializer->endElementNS( XML_pic, XML_blipFill );
+
+ // TODO setup the right values below
+ m_pSerializer->startElementNS(XML_pic, XML_spPr, XML_bwMode, "auto");
+
+ m_pSerializer->startElementNS(XML_a, XML_xfrm, xFrameAttributes);
+
+ m_pSerializer->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
+ OString aWidth( OString::number( TwipsToEMU( aSize.Width() ) ) );
+ OString aHeight( OString::number( TwipsToEMU( aSize.Height() ) ) );
+ m_pSerializer->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
+ m_pSerializer->endElementNS( XML_a, XML_xfrm );
+ m_pSerializer->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect");
+ m_pSerializer->singleElementNS(XML_a, XML_avLst);
+ m_pSerializer->endElementNS( XML_a, XML_prstGeom );
+
+ const SvxBoxItem& rBoxItem = pFrameFormat->GetBox();
+ const SvxBorderLine* pLeft = rBoxItem.GetLine(SvxBoxItemLine::LEFT);
+ const SvxBorderLine* pRight = rBoxItem.GetLine(SvxBoxItemLine::RIGHT);
+ const SvxBorderLine* pTop = rBoxItem.GetLine(SvxBoxItemLine::TOP);
+ const SvxBorderLine* pBottom = rBoxItem.GetLine(SvxBoxItemLine::BOTTOM);
+ if (pLeft || pRight || pTop || pBottom)
+ m_rExport.SdrExporter().writeBoxItemLine(rBoxItem);
+
+ m_rExport.SdrExporter().writeDMLEffectLst(*pFrameFormat);
+
+ m_pSerializer->endElementNS( XML_pic, XML_spPr );
+
+ m_pSerializer->endElementNS( XML_pic, XML_pic );
+
+ m_pSerializer->endElementNS( XML_a, XML_graphicData );
+ m_pSerializer->endElementNS( XML_a, XML_graphic );
+ m_rExport.SdrExporter().endDMLAnchorInline(pFrameFormat);
+}
+
+void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment )
+{
+ if( WriteOLEChart( pSdrObj, rSize, pFlyFrameFormat ))
+ return;
+ if( WriteOLEMath( rOLENode , nFormulaAlignment))
+ return;
+ PostponeOLE( rOLENode, rSize, pFlyFrameFormat );
+}
+
+bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
+{
+ uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
+ if (!xShape.is())
+ return false;
+
+ uno::Reference<beans::XPropertySet> const xPropSet(xShape, uno::UNO_QUERY);
+ if (!xPropSet.is())
+ return false;
+
+ OUString clsid; // why is the property of type string, not sequence<byte>?
+ xPropSet->getPropertyValue("CLSID") >>= clsid;
+ assert(!clsid.isEmpty());
+ SvGlobalName aClassID;
+ bool const isValid(aClassID.MakeId(clsid));
+ assert(isValid); (void)isValid;
+
+ if (!SotExchange::IsChart(aClassID))
+ return false;
+
+ m_aPostponedCharts.push_back(PostponedChart(pSdrObj, rSize, pFlyFrameFormat));
+ return true;
+}
+
+/*
+ * Write chart hierarchy in w:drawing after end element of w:rPr tag.
+ */
+void DocxAttributeOutput::WritePostponedChart()
+{
+ if (m_aPostponedCharts.empty())
+ return;
+
+ for (const PostponedChart& rChart : m_aPostponedCharts)
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc;
+ uno::Reference< drawing::XShape > xShape(const_cast<SdrObject*>(rChart.object)->getUnoShape(), uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
+ if( xPropSet.is() )
+ xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY );
+ }
+
+ if( xChartDoc.is() )
+ {
+ SAL_INFO("sw.ww8", "DocxAttributeOutput::WriteOLE2Obj: export chart ");
+
+ m_rExport.SdrExporter().startDMLAnchorInline(rChart.frame, rChart.size);
+
+ OUString sName("Object 1");
+ uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY );
+ if( xNamed.is() )
+ sName = xNamed->getName();
+
+ // tdf#153203 export a11y related properties
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ OUString const title(xShapeProps->getPropertyValue("Title").get<OUString>());
+ OUString const descr(xShapeProps->getPropertyValue("Description").get<OUString>());
+
+ /* If there is a scenario where a chart is followed by a shape
+ which is being exported as an alternate content then, the
+ docPr Id is being repeated, ECMA 20.4.2.5 says that the
+ docPr Id should be unique, ensuring the same here.
+ */
+ auto const docPrattrList(CreateDocPrAttrList(
+ GetExport(), m_anchorId++, sName, title, descr));
+ m_pSerializer->singleElementNS(XML_wp, XML_docPr, docPrattrList);
+
+ m_pSerializer->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
+
+ m_pSerializer->startElementNS( XML_a, XML_graphic,
+ FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) );
+
+ m_pSerializer->startElementNS( XML_a, XML_graphicData,
+ XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
+
+ OString aRelId;
+ m_nChartCount++;
+ aRelId = m_rExport.OutputChart( xChartDoc, m_nChartCount, m_pSerializer );
+
+ m_pSerializer->singleElementNS( XML_c, XML_chart,
+ FSNS( XML_xmlns, XML_c ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlChart)),
+ FSNS( XML_xmlns, XML_r ), GetExport().GetFilter().getNamespaceURL(OOX_NS(officeRel)),
+ FSNS( XML_r, XML_id ), aRelId );
+
+ m_pSerializer->endElementNS( XML_a, XML_graphicData );
+ m_pSerializer->endElementNS( XML_a, XML_graphic );
+
+ m_rExport.SdrExporter().endDMLAnchorInline(rChart.frame);
+ }
+ }
+
+ m_aPostponedCharts.clear();
+}
+
+bool DocxAttributeOutput::WriteOLEMath( const SwOLENode& rOLENode ,const sal_Int8 nAlign)
+{
+ uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef());
+ SvGlobalName aObjName(xObj->getClassID());
+
+ if( !SotExchange::IsMath(aObjName) )
+ return false;
+
+ try
+ {
+ PostponedMathObjects aPostponedMathObject;
+ aPostponedMathObject.pMathObject = const_cast<SwOLENode*>( &rOLENode);
+ aPostponedMathObject.nMathObjAlignment = nAlign;
+ m_aPostponedMaths.push_back(aPostponedMathObject);
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return true;
+}
+
+void DocxAttributeOutput::WritePostponedMath(const SwOLENode* pPostponedMath, sal_Int8 nAlign)
+{
+ uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(pPostponedMath)->GetOLEObj().GetOleRef());
+ if (embed::EmbedStates::LOADED == xObj->getCurrentState())
+ {
+ // must be running so there is a Component
+ try
+ {
+ xObj->changeState(embed::EmbedStates::RUNNING);
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY );
+ if (!xInterface.is())
+ {
+ SAL_WARN("sw.ww8", "Broken math object");
+ return;
+ }
+ if( oox::FormulaImExportBase* formulaexport = dynamic_cast< oox::FormulaImExportBase* >( xInterface.get()))
+ formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion(),
+ oox::drawingml::DOCUMENT_DOCX, nAlign);
+ else
+ OSL_FAIL( "Math OLE object cannot write out OOXML" );
+}
+
+void DocxAttributeOutput::WritePostponedFormControl(const SdrObject* pObject)
+{
+ if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm)
+ return;
+
+ SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
+ if (!pFormObj)
+ return;
+
+ uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
+ uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
+ if (!xInfo.is())
+ return;
+
+ if (xInfo->supportsService("com.sun.star.form.component.DateField"))
+ {
+ // gather component properties
+
+ OUString sDateFormat;
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
+
+ OString sDate;
+ OUString aContentText;
+ bool bHasDate = false;
+ css::util::Date aUNODate;
+ if (xPropertySet->getPropertyValue("Date") >>= aUNODate)
+ {
+ bHasDate = true;
+ Date aDate(aUNODate.Day, aUNODate.Month, aUNODate.Year);
+ sDate = DateToOString(aDate);
+ aContentText = OUString::createFromAscii(DateToDDMMYYYYOString(aDate));
+ sDateFormat = "dd/MM/yyyy";
+ }
+ else
+ {
+ aContentText = xPropertySet->getPropertyValue("HelpText").get<OUString>();
+ if(sDateFormat.isEmpty())
+ sDateFormat = "dd/MM/yyyy"; // Need to set date format even if there is no date set
+ }
+
+ // output component
+
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+ if (bHasDate)
+ m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sDate);
+ else
+ m_pSerializer->startElementNS(XML_w, XML_date);
+
+ m_pSerializer->singleElementNS(XML_w, XML_dateFormat, FSNS(XML_w, XML_val), sDateFormat);
+ m_pSerializer->singleElementNS(XML_w, XML_lid,
+ FSNS(XML_w, XML_val), "en-US");
+ m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
+ FSNS(XML_w, XML_val), "dateTime");
+ m_pSerializer->singleElementNS(XML_w, XML_calendar,
+ FSNS(XML_w, XML_val), "gregorian");
+
+ m_pSerializer->endElementNS(XML_w, XML_date);
+ m_pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+ m_pSerializer->startElementNS(XML_w, XML_r);
+
+ RunText(aContentText);
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ m_pSerializer->endElementNS(XML_w, XML_sdtContent);
+
+ m_pSerializer->endElementNS(XML_w, XML_sdt);
+ }
+ else if (xInfo->supportsService("com.sun.star.form.component.ComboBox"))
+ {
+ // gather component properties
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
+ OUString sText = xPropertySet->getPropertyValue("Text").get<OUString>();
+ const uno::Sequence<OUString> aItems = xPropertySet->getPropertyValue("StringItemList").get< uno::Sequence<OUString> >();
+
+ // output component
+
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->startElementNS(XML_w, XML_dropDownList);
+
+ for (const auto& rItem : aItems)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_listItem,
+ FSNS(XML_w, XML_displayText), rItem,
+ FSNS(XML_w, XML_value), rItem);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_dropDownList);
+ m_pSerializer->endElementNS(XML_w, XML_sdtPr);
+
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ RunText(sText);
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ m_pSerializer->endElementNS(XML_w, XML_sdtContent);
+
+ m_pSerializer->endElementNS(XML_w, XML_sdt);
+ }
+}
+
+void DocxAttributeOutput::WritePostponedActiveXControl(bool bInsideRun)
+{
+ for( const auto & rPostponedDrawing : m_aPostponedActiveXControls )
+ {
+ WriteActiveXControl(rPostponedDrawing.object, *rPostponedDrawing.frame, bInsideRun);
+ }
+ m_aPostponedActiveXControls.clear();
+}
+
+
+void DocxAttributeOutput::WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun)
+{
+ SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
+ if (!pFormObj)
+ return;
+
+ uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
+ if (!xControlModel.is())
+ return;
+
+ const bool bAnchoredInline = rFrameFormat.GetAnchor().GetAnchorId() == static_cast<RndStdIds>(css::text::TextContentAnchorType_AS_CHARACTER);
+
+ if(!bInsideRun)
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ }
+
+ // w:pict for floating embedded control and w:object for inline embedded control
+ if(bAnchoredInline)
+ m_pSerializer->startElementNS(XML_w, XML_object);
+ else
+ m_pSerializer->startElementNS(XML_w, XML_pict);
+
+ // write ActiveX fragment and ActiveX binary
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(), uno::UNO_QUERY);
+ std::pair<OString,OString> sRelIdAndName = m_rExport.WriteActiveXObject(xShape, xControlModel);
+
+ // VML shape definition
+ m_rExport.VMLExporter().SetSkipwzName(true);
+ m_rExport.VMLExporter().SetHashMarkForType(true);
+ m_rExport.VMLExporter().OverrideShapeIDGen(true, "control_shape_"_ostr);
+ OString sShapeId;
+ if(bAnchoredInline)
+ {
+ sShapeId = m_rExport.VMLExporter().AddInlineSdrObject(*pObject, true);
+ }
+ else
+ {
+ SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow());
+ const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
+ const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
+ SwFormatSurround const& rSurround(rFrameFormat.GetSurround());
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround));
+ sShapeId = m_rExport.VMLExporter().AddSdrObject(*pObject,
+ rFlow.GetValue(),
+ rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
+ rHoriOri.GetRelationOrient(),
+ rVertOri.GetRelationOrient(),
+ pAttrList.get(),
+ true);
+ }
+ // Restore default values
+ m_rExport.VMLExporter().SetSkipwzName(false);
+ m_rExport.VMLExporter().SetHashMarkForType(false);
+ m_rExport.VMLExporter().OverrideShapeIDGen(false);
+
+ // control
+ m_pSerializer->singleElementNS(XML_w, XML_control,
+ FSNS(XML_r, XML_id), sRelIdAndName.first,
+ FSNS(XML_w, XML_name), sRelIdAndName.second,
+ FSNS(XML_w, XML_shapeid), sShapeId);
+
+ if(bAnchoredInline)
+ m_pSerializer->endElementNS(XML_w, XML_object);
+ else
+ m_pSerializer->endElementNS(XML_w, XML_pict);
+
+ if(!bInsideRun)
+ {
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ }
+}
+
+bool DocxAttributeOutput::ExportAsActiveXControl(const SdrObject* pObject) const
+{
+ SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
+ if (!pFormObj)
+ return false;
+
+ uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
+ if (!xControlModel.is())
+ return false;
+
+ uno::Reference< css::frame::XModel > xModel( m_rExport.m_rDoc.GetDocShell() ? m_rExport.m_rDoc.GetDocShell()->GetModel() : nullptr );
+ if (!xModel.is())
+ return false;
+
+ uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
+ if (!xInfo.is())
+ return false;
+
+ // See WritePostponedFormControl
+ // By now date field and combobox is handled on a different way, so let's not interfere with the other method.
+ if(xInfo->supportsService("com.sun.star.form.component.DateField") ||
+ xInfo->supportsService("com.sun.star.form.component.ComboBox"))
+ return false;
+
+ oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, xControlModel);
+ return exportHelper.isValid();
+}
+
+void DocxAttributeOutput::PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
+{
+ if( !m_oPostponedOLEs )
+ //cannot be postponed, try to write now
+ WriteOLE( rNode, rSize, pFlyFrameFormat );
+ else
+ m_oPostponedOLEs->push_back( PostponedOLE( &rNode, rSize, pFlyFrameFormat ) );
+}
+
+/*
+ * Write w:object hierarchy for embedded objects after end element of w:rPr tag.
+ */
+void DocxAttributeOutput::WritePostponedOLE()
+{
+ if( !m_oPostponedOLEs )
+ return;
+
+ for( const auto & rPostponedOLE : *m_oPostponedOLEs )
+ {
+ WriteOLE( *rPostponedOLE.object, rPostponedOLE.size, rPostponedOLE.frame );
+ }
+
+ // clear list of postponed objects
+ m_oPostponedOLEs.reset();
+}
+
+void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
+{
+ OSL_ASSERT(pFlyFrameFormat);
+
+ // get interoperability information about embedded objects
+ uno::Reference< beans::XPropertySet > xPropSet( m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
+ uno::Sequence< beans::PropertyValue > aGrabBag, aObjectsInteropList,aObjectInteropAttributes;
+ xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= aGrabBag;
+ auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "EmbeddedObjects"; });
+ if (pProp != std::cend(aGrabBag))
+ pProp->Value >>= aObjectsInteropList;
+
+ SwOLEObj& aObject = rNode.GetOLEObj();
+ uno::Reference < embed::XEmbeddedObject > xObj( aObject.GetOleRef() );
+ comphelper::EmbeddedObjectContainer* aContainer = aObject.GetObject().GetContainer();
+ OUString sObjectName = aContainer->GetEmbeddedObjectName( xObj );
+
+ // set some attributes according to the type of the embedded object
+ OUString sProgID, sDrawAspect;
+ switch (rNode.GetAspect())
+ {
+ case embed::Aspects::MSOLE_CONTENT: sDrawAspect = "Content"; break;
+ case embed::Aspects::MSOLE_DOCPRINT: sDrawAspect = "DocPrint"; break;
+ case embed::Aspects::MSOLE_ICON: sDrawAspect = "Icon"; break;
+ case embed::Aspects::MSOLE_THUMBNAIL: sDrawAspect = "Thumbnail"; break;
+ default:
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::WriteOLE: invalid aspect value");
+ }
+ auto pObjectsInterop = std::find_if(std::cbegin(aObjectsInteropList), std::cend(aObjectsInteropList),
+ [&sObjectName](const beans::PropertyValue& rProp) { return rProp.Name == sObjectName; });
+ if (pObjectsInterop != std::cend(aObjectsInteropList))
+ pObjectsInterop->Value >>= aObjectInteropAttributes;
+
+ for( const auto& rObjectInteropAttribute : std::as_const(aObjectInteropAttributes) )
+ {
+ if ( rObjectInteropAttribute.Name == "ProgID" )
+ {
+ rObjectInteropAttribute.Value >>= sProgID;
+ }
+ }
+
+ // write embedded file
+ OString sId = m_rExport.WriteOLEObject(aObject, sProgID);
+
+ if( sId.isEmpty() )
+ {
+ // the embedded file could not be saved
+ // fallback: save as an image
+ FlyFrameGraphic( nullptr, rSize, pFlyFrameFormat, &rNode );
+ return;
+ }
+
+ // write preview image
+ const Graphic* pGraphic = rNode.GetGraphic();
+ m_rDrawingML.SetFS(m_pSerializer);
+ OUString sImageId = m_rDrawingML.writeGraphicToStorage(*pGraphic);
+
+ if ( sDrawAspect == "Content" )
+ {
+ try
+ {
+ awt::Size aSize = xObj->getVisualAreaSize( rNode.GetAspect() );
+
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( rNode.GetAspect() ) );
+ Size aOriginalSize( OutputDevice::LogicToLogic(Size( aSize.Width, aSize.Height),
+ MapMode(aUnit), MapMode(MapUnit::MapTwip)));
+
+ m_pSerializer->startElementNS( XML_w, XML_object,
+ FSNS(XML_w, XML_dxaOrig), OString::number(aOriginalSize.Width()),
+ FSNS(XML_w, XML_dyaOrig), OString::number(aOriginalSize.Height()) );
+ }
+ catch ( uno::Exception& )
+ {
+ m_pSerializer->startElementNS(XML_w, XML_object);
+ }
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_w, XML_object);
+ }
+
+ OString sShapeId = "ole_" + sId;
+
+ //OLE Shape definition
+ WriteOLEShape(*pFlyFrameFormat, rSize, sShapeId, sImageId);
+
+ //OLE Object definition
+ m_pSerializer->singleElementNS(XML_o, XML_OLEObject,
+ XML_Type, "Embed",
+ XML_ProgID, sProgID,
+ XML_ShapeID, sShapeId,
+ XML_DrawAspect, sDrawAspect,
+ XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())),
+ FSNS( XML_r, XML_id ), sId );
+
+ m_pSerializer->endElementNS(XML_w, XML_object);
+}
+
+void DocxAttributeOutput::WriteOLEShape(const SwFlyFrameFormat& rFrameFormat, const Size& rSize,
+ std::string_view rShapeId, const OUString& rImageId)
+{
+ assert(m_pSerializer);
+
+ //Here is an attribute list where we collect the attributes what we want to export
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+ pAttr->add(XML_id, rShapeId);
+
+ //export the fixed shape type for picture frame
+ m_pSerializer->write(vml::VMLExport::GetVMLShapeTypeDefinition(rShapeId, true));
+ pAttr->add(XML_type, OString::Concat("_x0000_t") + rShapeId);
+
+ //Export the style attribute for position and size
+ pAttr->add(XML_style, GetOLEStyle(rFrameFormat, rSize));
+ //Get the OLE frame
+ const SvxBoxItem& rBox = rFrameFormat.GetAttrSet().GetBox();
+ OString sLineType;
+ OString sDashType;
+ //Word does not handle differently the four sides,
+ //so we have to choose, and the left one is the winner:
+ if (rBox.GetLeft())
+ {
+ //Get the left border color and width
+ const Color aLineColor = rBox.GetLeft()->GetColor();
+ const tools::Long aLineWidth = rBox.GetLeft()->GetWidth();
+
+ //Convert the left OLE border style to OOXML
+ //FIXME improve if it's necessary
+ switch (rBox.GetLeft()->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ sLineType = "Single"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ sLineType = "Single"_ostr;
+ sDashType = "Dash"_ostr;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ sLineType = "Single"_ostr;
+ sDashType = "DashDot"_ostr;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ sLineType = "Single"_ostr;
+ sDashType = "ShortDashDotDot"_ostr;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ sLineType = "Single"_ostr;
+ sDashType = "Dot"_ostr;
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ sLineType = "ThinThin"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ sLineType = "ThinThin"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ sLineType = "Single"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ sLineType = "Single"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ sLineType = "Single"_ostr;
+ sDashType = "Dot"_ostr;
+ break;
+ case SvxBorderLineStyle::INSET:
+ sLineType = "Single"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ sLineType = "Single"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ sLineType = "ThickThin"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ sLineType = "ThinThick"_ostr;
+ sDashType = "Solid"_ostr;
+ break;
+ case SvxBorderLineStyle::NONE:
+ sLineType = ""_ostr;
+ sDashType = ""_ostr;
+ break;
+ default:
+ SAL_WARN("sw.ww8", "Unknown line type on OOXML ELE export!");
+ break;
+ }
+
+ //If there is a line add it for export
+ if (!sLineType.isEmpty() && !sDashType.isEmpty())
+ {
+ pAttr->add(XML_stroked, "t");
+ pAttr->add(XML_strokecolor, "#" + msfilter::util::ConvertColor(aLineColor));
+ pAttr->add(XML_strokeweight, OString::number(aLineWidth / 20) + "pt");
+ }
+ }
+
+ //Let's check the filltype of the OLE
+ switch (rFrameFormat.GetAttrSet().Get(XATTR_FILLSTYLE).GetValue())
+ {
+ case drawing::FillStyle::FillStyle_SOLID:
+ {
+ //If solid, we get the color and add it to the exporter
+ const Color rShapeColor = rFrameFormat.GetAttrSet().Get(XATTR_FILLCOLOR).GetColorValue();
+ pAttr->add(XML_filled, "t");
+ pAttr->add(XML_fillcolor, "#" + msfilter::util::ConvertColor(rShapeColor));
+ break;
+ }
+ case drawing::FillStyle::FillStyle_GRADIENT:
+ case drawing::FillStyle::FillStyle_HATCH:
+ case drawing::FillStyle::FillStyle_BITMAP:
+ //TODO
+ break;
+ case drawing::FillStyle::FillStyle_NONE:
+ {
+ pAttr->add(XML_filled, "f");
+ break;
+ }
+ default:
+ SAL_WARN("sw.ww8", "Unknown fill type on OOXML OLE export!");
+ break;
+ }
+ pAttr->addNS(XML_o, XML_ole, ""); //compulsory, even if it's empty
+ m_pSerializer->startElementNS(XML_v, XML_shape, pAttr);//Write the collected attrs...
+
+ if (!sLineType.isEmpty() && !sDashType.isEmpty()) //If there is a line/dash style it is time to export it
+ {
+ m_pSerializer->singleElementNS(XML_v, XML_stroke, XML_linestyle, sLineType, XML_dashstyle, sDashType);
+ }
+
+ // shape filled with the preview image
+ m_pSerializer->singleElementNS(XML_v, XML_imagedata,
+ FSNS(XML_r, XML_id), rImageId,
+ FSNS(XML_o, XML_title), "");
+
+ //export wrap settings
+ if (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) //As-char objs does not have surround.
+ ExportOLESurround(rFrameFormat.GetSurround());
+
+ m_pSerializer->endElementNS(XML_v, XML_shape);
+}
+
+OString DocxAttributeOutput::GetOLEStyle(const SwFlyFrameFormat& rFormat, const Size& rSize)
+{
+ //tdf#131539: Export OLE positions in docx:
+ //This string will store the position output for the xml
+ OString aPos;
+ //This string will store the relative position for aPos
+ OString aAnch;
+
+ if (rFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ //Get the horizontal alignment of the OLE via the frame format, to aHAlign
+ OString aHAlign = convertToOOXMLHoriOrient(rFormat.GetHoriOrient().GetHoriOrient(),
+ rFormat.GetHoriOrient().IsPosToggle());
+ //Get the vertical alignment of the OLE via the frame format to aVAlign
+ OString aVAlign = convertToOOXMLVertOrient(rFormat.GetVertOrient().GetVertOrient());
+
+ // Check if the OLE anchored to page:
+ const bool bIsPageAnchor = rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE;
+
+ //Get the relative horizontal positions for the anchors
+ OString aHAnch
+ = bIsPageAnchor
+ ? "page"_ostr
+ : convertToOOXMLHoriOrientRel(rFormat.GetHoriOrient().GetRelationOrient());
+ //Get the relative vertical positions for the anchors
+ OString aVAnch = convertToOOXMLVertOrientRel(rFormat.GetVertOrient().GetRelationOrient());
+
+ //Choice that the horizontal position is relative or not
+ if (!aHAlign.isEmpty())
+ aHAlign = ";mso-position-horizontal:" + aHAlign;
+ aHAlign = ";mso-position-horizontal-relative:" + aHAnch;
+
+ //Choice that the vertical position is relative or not
+ if (!aVAlign.isEmpty())
+ aVAlign = ";mso-position-vertical:" + aVAlign;
+ aVAlign = ";mso-position-vertical-relative:" + aVAnch;
+
+ //Set the anchoring information into one string for aPos
+ aAnch = aHAlign + aVAlign;
+
+ //Query the positions to aPos from frameformat
+ aPos =
+ "position:absolute;margin-left:" + OString::number(double(rFormat.GetHoriOrient().GetPos()) / 20) +
+ "pt;margin-top:" + OString::number(double(rFormat.GetVertOrient().GetPos()) / 20) + "pt;";
+ }
+
+ OString sShapeStyle = "width:" + OString::number( double( rSize.Width() ) / 20 ) +
+ "pt;height:" + OString::number( double( rSize.Height() ) / 20 ) +
+ "pt"; //from VMLExport::AddRectangleDimensions(), it does: value/20
+
+ const SvxLRSpaceItem& rLRSpace = rFormat.GetLRSpace();
+ if (rLRSpace.IsExplicitZeroMarginValLeft() || rLRSpace.GetLeft())
+ sShapeStyle += ";mso-wrap-distance-left:" + OString::number(double(rLRSpace.GetLeft()) / 20) + "pt";
+ if (rLRSpace.IsExplicitZeroMarginValRight() || rLRSpace.GetRight())
+ sShapeStyle += ";mso-wrap-distance-right:" + OString::number(double(rLRSpace.GetRight()) / 20) + "pt";
+ const SvxULSpaceItem& rULSpace = rFormat.GetULSpace();
+ if (rULSpace.GetUpper())
+ sShapeStyle += ";mso-wrap-distance-top:" + OString::number(double(rULSpace.GetUpper()) / 20) + "pt";
+ if (rULSpace.GetLower())
+ sShapeStyle += ";mso-wrap-distance-bottom:" + OString::number(double(rULSpace.GetLower()) / 20) + "pt";
+
+ //Export anchor setting, if it exists
+ if (!aPos.isEmpty() && !aAnch.isEmpty())
+ sShapeStyle = aPos + sShapeStyle + aAnch;
+
+ return sShapeStyle;
+}
+
+void DocxAttributeOutput::ExportOLESurround(const SwFormatSurround& rWrap)
+{
+ const bool bIsContour = rWrap.IsContour(); //Has the shape contour or not
+ OString sSurround;
+ OString sSide;
+
+ //Map the ODF wrap settings to OOXML one
+ switch (rWrap.GetSurround())
+ {
+ case text::WrapTextMode::WrapTextMode_NONE:
+ sSurround = "topAndBottom"_ostr;
+ break;
+ case text::WrapTextMode::WrapTextMode_PARALLEL:
+ sSurround = bIsContour ? "tight"_ostr : "square"_ostr;
+ break;
+ case text::WrapTextMode::WrapTextMode_DYNAMIC:
+ sSide = "largest"_ostr;
+ sSurround = bIsContour ? "tight"_ostr : "square"_ostr;
+ break;
+ case text::WrapTextMode::WrapTextMode_LEFT:
+ sSide = "left"_ostr;
+ sSurround = bIsContour ? "tight"_ostr : "square"_ostr;
+ break;
+ case text::WrapTextMode::WrapTextMode_RIGHT:
+ sSide = "right"_ostr;
+ sSurround = bIsContour ? "tight"_ostr : "square"_ostr;
+ break;
+ default:
+ SAL_WARN("sw.ww8", "Unknown surround type on OOXML export!");
+ break;
+ }
+
+ //if there is a setting export it:
+ if (!sSurround.isEmpty())
+ {
+ if (sSide.isEmpty())
+ m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround);
+ else
+ m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround, XML_side, sSide);
+ }
+}
+
+void DocxAttributeOutput::WritePostponedCustomShape()
+{
+ if (!m_oPostponedCustomShape)
+ return;
+
+ for( const auto & rPostponedDrawing : *m_oPostponedCustomShape)
+ {
+ m_rExport.GetFilter().SetMaxDocId(m_anchorId + 1);
+ if ( IsAlternateContentChoiceOpen() )
+ m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++);
+ else
+ m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++);
+ m_anchorId = m_rExport.GetFilter().GetMaxDocId();
+ }
+ m_oPostponedCustomShape.reset();
+}
+
+void DocxAttributeOutput::WritePostponedDMLDrawing()
+{
+ if (!m_oPostponedDMLDrawings)
+ return;
+
+ // Clear the list early, this method may be called recursively.
+ std::optional< std::vector<PostponedDrawing> > pPostponedDMLDrawings(std::move(m_oPostponedDMLDrawings));
+ std::optional< std::vector<PostponedOLE> > pPostponedOLEs(std::move(m_oPostponedOLEs));
+ m_oPostponedDMLDrawings.reset();
+ m_oPostponedOLEs.reset();
+
+ for( const auto & rPostponedDrawing : *pPostponedDMLDrawings )
+ {
+ // Avoid w:drawing within another w:drawing.
+ m_rExport.GetFilter().SetMaxDocId(m_anchorId + 1);
+ if ( IsAlternateContentChoiceOpen() && !( m_rExport.SdrExporter().IsDrawingOpen()) )
+ m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++);
+ else
+ m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++);
+ m_anchorId = m_rExport.GetFilter().GetMaxDocId();
+ }
+
+ m_oPostponedOLEs = std::move(pPostponedOLEs);
+}
+
+void DocxAttributeOutput::WriteFlyFrame(const ww8::Frame& rFrame)
+{
+ m_pSerializer->mark(Tag_OutputFlyFrame);
+
+ switch ( rFrame.GetWriterType() )
+ {
+ case ww8::Frame::eGraphic:
+ {
+ const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
+ const SwNode *pNode = rFrame.GetContent();
+ const SwGrfNode *pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
+ if ( pGrfNode )
+ {
+ if (!m_oPostponedGraphic)
+ {
+ m_bPostponedProcessingFly = false ;
+ FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize(), nullptr, nullptr, pSdrObj);
+ }
+ else // we are writing out attributes, but w:drawing should not be inside w:rPr,
+ { // so write it out later
+ m_bPostponedProcessingFly = true ;
+ m_oPostponedGraphic->push_back(PostponedGraphic(pGrfNode, rFrame.GetLayoutSize(), pSdrObj));
+ }
+ }
+ }
+ break;
+ case ww8::Frame::eDrawing:
+ {
+ const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
+ if ( pSdrObj )
+ {
+ const bool bIsDiagram(nullptr != pSdrObj && pSdrObj->isDiagram());
+
+ if (bIsDiagram)
+ {
+ if ( !m_oPostponedDiagrams )
+ {
+ m_bPostponedProcessingFly = false ;
+ m_rExport.SdrExporter().writeDiagram( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++);
+ }
+ else // we are writing out attributes, but w:drawing should not be inside w:rPr,
+ { // so write it out later
+ m_bPostponedProcessingFly = true ;
+ m_oPostponedDiagrams->push_back( PostponedDiagram( pSdrObj, &(rFrame.GetFrameFormat()) ));
+ }
+ }
+ else
+ {
+ if (!m_oPostponedDMLDrawings)
+ {
+ m_rExport.GetFilter().SetMaxDocId(m_anchorId + 1);
+ if ( IsAlternateContentChoiceOpen() )
+ {
+ // Do not write w:drawing inside w:drawing. Instead Postpone the Inner Drawing.
+ if( m_rExport.SdrExporter().IsDrawingOpen() )
+ m_oPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat())));
+ else
+ m_rExport.SdrExporter().writeDMLDrawing( pSdrObj, &rFrame.GetFrameFormat(), m_anchorId++);
+ }
+ else
+ m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++);
+ m_anchorId = m_rExport.GetFilter().GetMaxDocId();
+
+ m_bPostponedProcessingFly = false ;
+ }
+ // IsAlternateContentChoiceOpen(): check is to ensure that only one object is getting added. Without this check, plus one object gets added
+ // m_bParagraphFrameOpen: check if the frame is open.
+ else if (IsAlternateContentChoiceOpen() && m_bParagraphFrameOpen)
+ m_oPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat())));
+ else
+ {
+ // we are writing out attributes, but w:drawing should not be inside w:rPr, so write it out later
+ m_bPostponedProcessingFly = true ;
+ m_oPostponedDMLDrawings->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat())));
+ }
+ }
+ }
+ }
+ break;
+ case ww8::Frame::eTextBox:
+ {
+ // If this is a TextBox of a shape, then ignore: it's handled in WriteTextBox().
+ if (DocxSdrExport::isTextBox(rFrame.GetFrameFormat()))
+ break;
+
+ // If this is a TextBox containing a table which we already exported directly, ignore it
+ if (m_aFloatingTablesOfParagraph.find(&rFrame.GetFrameFormat()) != m_aFloatingTablesOfParagraph.end())
+ break;
+
+ // The frame output is postponed to the end of the anchor paragraph
+ bool bDuplicate = false;
+ const OUString& rName = rFrame.GetFrameFormat().GetName();
+ unsigned nSize = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
+ for( unsigned nIndex = 0; nIndex < nSize; ++nIndex )
+ {
+ const OUString& rNameExisting = m_aFramesOfParagraph.top()[nIndex].GetFrameFormat().GetName();
+
+ if (!rName.isEmpty() && !rNameExisting.isEmpty())
+ {
+ if (rName == rNameExisting)
+ bDuplicate = true;
+ }
+ }
+
+ if( !bDuplicate )
+ {
+ m_bPostponedProcessingFly = true ;
+ if ( m_aFramesOfParagraph.size() )
+ m_aFramesOfParagraph.top().emplace_back(rFrame);
+ }
+ }
+ break;
+ case ww8::Frame::eOle:
+ {
+ const SwFrameFormat &rFrameFormat = rFrame.GetFrameFormat();
+ const SdrObject *pSdrObj = rFrameFormat.FindRealSdrObject();
+ if ( pSdrObj )
+ {
+ SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
+ SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
+
+ //output variable for the formula alignment (default inline)
+ sal_Int8 nAlign(FormulaImExportBase::eFormulaAlign::INLINE);
+ auto xObj(rOLENd.GetOLEObj().GetOleRef()); //get the xObject of the formula
+
+ //tdf133030: Export formula position
+ //If we have a formula with inline anchor...
+ if(SotExchange::IsMath(xObj->getClassID()) && rFrame.IsInline())
+ {
+ SwNode const* const pAnchorNode = rFrameFormat.GetAnchor().GetAnchorNode();
+ if(pAnchorNode)
+ {
+ //Get the text node what the formula anchored to
+ const SwTextNode* pTextNode = pAnchorNode->GetTextNode();
+ if(pTextNode && pTextNode->Len() == 1)
+ {
+ //Get the paragraph alignment
+ auto aParaAdjust = pTextNode->GetSwAttrSet().GetAdjust().GetAdjust();
+ //And set the formula according to the paragraph alignment
+ if (aParaAdjust == SvxAdjust::Center)
+ nAlign = FormulaImExportBase::eFormulaAlign::CENTER;
+ else if (aParaAdjust == SvxAdjust::Right)
+ nAlign = FormulaImExportBase::eFormulaAlign::RIGHT;
+ else // left in the case of left and justified paragraph alignments
+ nAlign = FormulaImExportBase::eFormulaAlign::LEFT;
+ }
+ }
+ }
+ WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrameFormat*>( &rFrameFormat ), nAlign);
+ m_bPostponedProcessingFly = false ;
+ }
+ }
+ break;
+ case ww8::Frame::eFormControl:
+ {
+ const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject();
+ if(ExportAsActiveXControl(pObject))
+ m_aPostponedActiveXControls.emplace_back(pObject, &(rFrame.GetFrameFormat()));
+ else
+ m_aPostponedFormControls.push_back(pObject);
+ m_bPostponedProcessingFly = true ;
+ }
+ break;
+ default:
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFrame ) - frame type " <<
+ ( rFrame.GetWriterType() == ww8::Frame::eTextBox ? "eTextBox":
+ ( rFrame.GetWriterType() == ww8::Frame::eOle ? "eOle": "???" ) ) );
+ break;
+ }
+
+ m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame);
+}
+
+void DocxAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
+{
+ /// The old OutputFlyFrame_Impl() moved to WriteFlyFrame().
+ /// Now if a frame anchored inside another frame, it will
+ /// not be exported immediately, because OOXML does not
+ /// support that feature, instead it postponed and exported
+ /// later when the original shape closed.
+
+ if (rFrame.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ || rFrame.IsInline())
+ {
+ m_nEmbedFlyLevel++;
+ WriteFlyFrame(rFrame);
+ m_nEmbedFlyLevel--;
+ return;
+ }
+
+ if (m_nEmbedFlyLevel == 0)
+ {
+ if (m_vPostponedFlys.empty())
+ {
+ m_nEmbedFlyLevel++;
+ WriteFlyFrame(rFrame);
+ m_nEmbedFlyLevel--;
+ }
+ else
+ for (auto it = m_vPostponedFlys.begin(); it != m_vPostponedFlys.end();)
+ {
+ m_nEmbedFlyLevel++;
+ WriteFlyFrame(*it);
+ it = m_vPostponedFlys.erase(it);
+ m_nEmbedFlyLevel--;
+ }
+ }
+ else
+ {
+ bool bFound = false;
+ for (const auto& i : m_vPostponedFlys)
+ {
+ if (i.RefersToSameFrameAs(rFrame))
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ {
+ if (auto pParentFly = rFrame.GetContentNode()->GetFlyFormat())
+ {
+ auto aHori(rFrame.GetFrameFormat().GetHoriOrient());
+ aHori.SetPos(aHori.GetPos() + pParentFly->GetHoriOrient().GetPos());
+ auto aVori(rFrame.GetFrameFormat().GetVertOrient());
+ aVori.SetPos(aVori.GetPos() + pParentFly->GetVertOrient().GetPos());
+
+ const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aHori);
+ const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aVori);
+ const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(pParentFly->GetAnchor());
+
+ m_vPostponedFlys.push_back(rFrame);
+ }
+
+ }
+ }
+}
+
+void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj)
+{
+ const EditTextObject& rEditObj = rParaObj.GetTextObject();
+ MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX );
+
+ sal_Int32 nPara = rEditObj.GetParagraphCount();
+
+ m_pSerializer->startElementNS(XML_w, XML_txbxContent);
+ for (sal_Int32 n = 0; n < nPara; ++n)
+ {
+ if( n )
+ aAttrIter.NextPara( n );
+
+ OUString aStr( rEditObj.GetText( n ));
+ sal_Int32 nCurrentPos = 0;
+ sal_Int32 nEnd = aStr.getLength();
+
+ StartParagraph(ww8::WW8TableNodeInfo::Pointer_t(), false);
+
+ // Write paragraph properties.
+ StartParagraphProperties();
+ aAttrIter.OutParaAttr(false);
+ SfxItemSet aParagraphMarkerProperties(m_rExport.m_rDoc.GetAttrPool());
+ EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr);
+
+ do {
+ const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
+
+ m_pSerializer->startElementNS(XML_w, XML_r);
+
+ // Write run properties.
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ aAttrIter.OutAttr(nCurrentPos);
+ WriteCollectedRunProperties();
+ m_pSerializer->endElementNS(XML_w, XML_rPr);
+
+ bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos );
+ if( !bTextAtr )
+ {
+ OUString aOut( aStr.copy( nCurrentPos, nNextAttr - nCurrentPos ) );
+ RunText(aOut);
+ }
+
+ if ( !m_sRawText.isEmpty() )
+ {
+ RunText( m_sRawText );
+ m_sRawText.clear();
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_r );
+
+ nCurrentPos = nNextAttr;
+ aAttrIter.NextPos();
+ }
+ while( nCurrentPos < nEnd );
+ EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t());
+ }
+ m_pSerializer->endElementNS( XML_w, XML_txbxContent );
+}
+
+void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rContext)
+{
+ rContext.m_pTableInfo = m_rExport.m_pTableInfo;
+ m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
+
+ rContext.m_bTableCellOpen = m_tableReference.m_bTableCellOpen;
+ m_tableReference.m_bTableCellOpen = false;
+
+ rContext.m_nTableDepth = m_tableReference.m_nTableDepth;
+ m_tableReference.m_nTableDepth = 0;
+
+ rContext.m_bStartedParaSdt = m_aParagraphSdt.m_bStartedSdt;
+ m_aParagraphSdt.m_bStartedSdt = false;
+ rContext.m_bStartedRunSdt = m_aRunSdt.m_bStartedSdt;
+ m_aRunSdt.m_bStartedSdt = false;
+
+ rContext.m_nHyperLinkCount = m_nHyperLinkCount.back();
+ m_nHyperLinkCount.back() = 0;
+}
+
+void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const & rContext)
+{
+ m_rExport.m_pTableInfo = rContext.m_pTableInfo;
+ m_tableReference.m_bTableCellOpen = rContext.m_bTableCellOpen;
+ m_tableReference.m_nTableDepth = rContext.m_nTableDepth;
+ m_aParagraphSdt.m_bStartedSdt = rContext.m_bStartedParaSdt;
+ m_aRunSdt.m_bStartedSdt = rContext.m_bStartedRunSdt;
+ m_nHyperLinkCount.back() = rContext.m_nHyperLinkCount;
+}
+
+void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape)
+{
+ DocxTableExportContext aTableExportContext(*this);
+
+ SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+ assert(pTextBox);
+ const SwPosition* pAnchor = nullptr;
+ const bool bFlyAtPage = pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE;
+ if (bFlyAtPage) //tdf135711
+ {
+ auto pNdIdx = pTextBox->GetContent().GetContentIdx();
+ if (pNdIdx) //Is that possible it is null?
+ pAnchor = new SwPosition(*pNdIdx);
+ }
+ else
+ {
+ pAnchor = pTextBox->GetAnchor().GetContentAnchor();//This might be null
+ }
+
+ if (pAnchor) //pAnchor can be null, so that's why not assert here.
+ {
+ ww8::Frame aFrame(*pTextBox, *pAnchor);
+ m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++, /*bTextBoxOnly=*/true);
+ if (bFlyAtPage)
+ {
+ delete pAnchor;
+ }
+ }
+}
+
+void DocxAttributeOutput::WriteVMLTextBox(uno::Reference<drawing::XShape> xShape)
+{
+ DocxTableExportContext aTableExportContext(*this);
+
+ SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+ assert(pTextBox);
+ const SwPosition* pAnchor = nullptr;
+ if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) //tdf135711
+ {
+ auto pNdIdx = pTextBox->GetContent().GetContentIdx();
+ if (pNdIdx) //Is that possible it is null?
+ pAnchor = new SwPosition(*pNdIdx);
+ }
+ else
+ {
+ pAnchor = pTextBox->GetAnchor().GetContentAnchor();//This might be null
+ }
+
+ if (pAnchor) //pAnchor can be null, so that's why not assert here.
+ {
+ ww8::Frame aFrame(*pTextBox, *pAnchor);
+ m_rExport.SdrExporter().writeVMLTextFrame(&aFrame, /*bTextBoxOnly=*/true);
+ if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ delete pAnchor;
+ }
+ }
+}
+
+oox::drawingml::DrawingML& DocxAttributeOutput::GetDrawingML()
+{
+ return m_rDrawingML;
+}
+
+bool DocxAttributeOutput::MaybeOutputBrushItem(SfxItemSet const& rSet)
+{
+ const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
+
+ if ((pXFillStyleItem && pXFillStyleItem->GetValue() != drawing::FillStyle_NONE)
+ || !m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ return false;
+ }
+
+ // sw text frames are opaque by default, even with fill none!
+ std::unique_ptr<SfxItemSet> const pClone(rSet.Clone());
+ XFillColorItem const aColor(OUString(), COL_WHITE);
+ pClone->Put(aColor);
+ // call getSvxBrushItemForSolid - this also takes XFillTransparenceItem into account
+ XFillStyleItem const aSolid(drawing::FillStyle_SOLID);
+ pClone->Put(aSolid);
+ std::unique_ptr<SvxBrushItem> const pBrush(getSvxBrushItemFromSourceSet(*pClone, RES_BACKGROUND));
+ FormatBackground(*pBrush);
+ return true;
+}
+
+namespace {
+
+/// Functor to do case-insensitive ordering of OUString instances.
+struct OUStringIgnoreCase
+{
+ bool operator() (std::u16string_view lhs, std::u16string_view rhs) const
+ {
+ return o3tl::compareToIgnoreAsciiCase(lhs, rhs) < 0;
+ }
+};
+
+}
+
+/// Guesses if a style created in Writer (no grab-bag) should be qFormat or not.
+static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId)
+{
+ // If the style has no dedicated STI number, then it's probably a custom style -> qFormat.
+ if (nWwId == ww::stiUser)
+ return true;
+
+ // Allow exported built-in styles UI language neutral
+ if ( nWwId == ww::stiNormal ||
+ ( nWwId>= ww::stiLev1 && nWwId <= ww::stiLev9 ) ||
+ nWwId == ww::stiCaption || nWwId == ww::stiTitle ||
+ nWwId == ww::stiSubtitle || nWwId == ww::stiStrong ||
+ nWwId == ww::stiEmphasis )
+ return true;
+
+ static o3tl::sorted_vector<OUString, OUStringIgnoreCase> const aAllowlist
+ {
+ "No Spacing",
+ "List Paragraph",
+ "Quote",
+ "Intense Quote",
+ "Subtle Emphasis",
+ "Intense Emphasis",
+ "Subtle Reference",
+ "Intense Reference",
+ "Book Title",
+ "TOC Heading",
+ };
+ // Not custom style? Then we have a list of standard styles which should be qFormat.
+ return aAllowlist.find(rName) != aAllowlist.end();
+}
+
+void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
+ sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nSlot, bool bAutoUpdate )
+{
+ bool bQFormat = false, bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false;
+ OUString aRsid, aUiPriority;
+ rtl::Reference<FastAttributeList> pStyleAttributeList = FastSerializerHelper::createAttrList();
+ uno::Any aAny;
+ if (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR)
+ {
+ const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nSlot);
+ pFormat->GetGrabBagItem(aAny);
+ }
+ else
+ {
+ const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nSlot);
+ pRule->GetGrabBagItem(aAny);
+ }
+ const uno::Sequence<beans::PropertyValue>& rGrabBag = aAny.get< uno::Sequence<beans::PropertyValue> >();
+
+ for (const auto& rProp : rGrabBag)
+ {
+ if (rProp.Name == "uiPriority")
+ aUiPriority = rProp.Value.get<OUString>();
+ else if (rProp.Name == "qFormat")
+ bQFormat = true;
+ else if (rProp.Name == "rsid")
+ aRsid = rProp.Value.get<OUString>();
+ else if (rProp.Name == "unhideWhenUsed")
+ bUnhideWhenUsed = true;
+ else if (rProp.Name == "semiHidden")
+ bSemiHidden = true;
+ else if (rProp.Name == "locked")
+ bLocked = true;
+ else if (rProp.Name == "default")
+ bDefault = rProp.Value.get<bool>();
+ else if (rProp.Name == "customStyle")
+ bCustomStyle = rProp.Value.get<bool>();
+ else
+ SAL_WARN("sw.ww8", "Unhandled style property: " << rProp.Name);
+ }
+
+ const char* pType = nullptr;
+ switch (eType)
+ {
+ case STYLE_TYPE_PARA:
+ pType = "paragraph";
+ break;
+ case STYLE_TYPE_CHAR:
+ pType = "character";
+ break;
+ case STYLE_TYPE_LIST: pType = "numbering"; break;
+ }
+ pStyleAttributeList->add(FSNS( XML_w, XML_type ), pType);
+ pStyleAttributeList->add(FSNS(XML_w, XML_styleId), m_rExport.m_pStyles->GetStyleId(nSlot));
+ if (bDefault)
+ pStyleAttributeList->add(FSNS(XML_w, XML_default), "1");
+ if (bCustomStyle)
+ pStyleAttributeList->add(FSNS(XML_w, XML_customStyle), "1");
+ m_pSerializer->startElementNS( XML_w, XML_style, pStyleAttributeList);
+ m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName);
+
+ if ( nBase != 0x0FFF && eType != STYLE_TYPE_LIST)
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_basedOn,
+ FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nBase) );
+ }
+
+ if ( nNext != nSlot && eType != STYLE_TYPE_LIST)
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_next,
+ FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nNext) );
+ }
+
+ if (nLink != 0x0FFF && (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR))
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_link, FSNS(XML_w, XML_val),
+ m_rExport.m_pStyles->GetStyleId(nLink));
+ }
+
+ if ( bAutoUpdate )
+ m_pSerializer->singleElementNS(XML_w, XML_autoRedefine);
+
+ if (!aUiPriority.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_uiPriority, FSNS(XML_w, XML_val), aUiPriority);
+ if (bSemiHidden)
+ m_pSerializer->singleElementNS(XML_w, XML_semiHidden);
+ if (bUnhideWhenUsed)
+ m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed);
+
+ if (bQFormat || lcl_guessQFormat(rName, nWwId))
+ m_pSerializer->singleElementNS(XML_w, XML_qFormat);
+ if (bLocked)
+ m_pSerializer->singleElementNS(XML_w, XML_locked);
+ if (!aRsid.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_rsid, FSNS(XML_w, XML_val), aRsid);
+}
+
+void DocxAttributeOutput::EndStyle()
+{
+ m_pSerializer->endElementNS( XML_w, XML_style );
+}
+
+void DocxAttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 /*nStyle*/ )
+{
+ if ( bParProp )
+ {
+ m_pSerializer->startElementNS(XML_w, XML_pPr);
+ InitCollectedParagraphProperties();
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ InitCollectedRunProperties();
+ }
+}
+
+void DocxAttributeOutput::EndStyleProperties( bool bParProp )
+{
+ if ( bParProp )
+ {
+ WriteCollectedParagraphProperties();
+
+ // Merge the marks for the ordered elements
+ m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
+
+ m_pSerializer->endElementNS( XML_w, XML_pPr );
+ }
+ else
+ {
+ WriteCollectedRunProperties();
+
+ // Merge the marks for the ordered elements
+ m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
+
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ }
+}
+
+void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/)
+{
+ // Handled by ParaOutlineLevel() instead.
+}
+
+void DocxAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
+{
+ sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel));
+ // Outline Level: in LO Body Text = 0, in MS Body Text = 9
+ nOutLvl = nOutLvl ? nOutLvl - 1 : 9;
+ m_pSerializer->singleElementNS(XML_w, XML_outlineLvl, FSNS(XML_w, XML_val), OString::number(nOutLvl));
+}
+
+void DocxAttributeOutput::PageBreakBefore( bool bBreak )
+{
+ if ( bBreak )
+ m_pSerializer->singleElementNS(XML_w, XML_pageBreakBefore);
+ else
+ m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore,
+ FSNS( XML_w, XML_val ), "false" );
+}
+
+void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo, bool bExtraPageBreak)
+{
+ switch ( nC )
+ {
+ case msword::ColumnBreak:
+ // The column break should be output in the next paragraph...
+ if ( m_nColBreakStatus == COLBRK_WRITE )
+ m_nColBreakStatus = COLBRK_WRITEANDPOSTPONE;
+ else
+ m_nColBreakStatus = COLBRK_POSTPONE;
+ break;
+ case msword::PageBreak:
+ if ( pSectionInfo )
+ {
+ // Detect when the current node is the last node in the
+ // document: the last section is written explicitly in
+ // DocxExport::WriteMainText(), don't duplicate that here.
+ SwNodeIndex aCurrentNode(m_rExport.m_pCurPam->GetPointNode());
+ SwNodeIndex aLastNode(m_rExport.m_rDoc.GetNodes().GetEndOfContent(), -1);
+ bool bEmit = aCurrentNode != aLastNode;
+
+ if (!bEmit)
+ {
+ // Need to still emit an empty section at the end of the
+ // document in case balanced columns are wanted, since the last
+ // section in Word is always balanced.
+ sal_uInt16 nColumns = 1;
+ bool bBalance = false;
+ if (const SwSectionFormat* pFormat = pSectionInfo->pSectionFormat)
+ {
+ if (pFormat != reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
+ {
+ nColumns = pFormat->GetCol().GetNumCols();
+ const SwFormatNoBalancedColumns& rNoBalanced = pFormat->GetBalancedColumns();
+ bBalance = !rNoBalanced.GetValue();
+ }
+ }
+ bEmit = (nColumns > 1 && bBalance);
+ }
+
+ // don't add section properties if this will be the first
+ // paragraph in the document
+ if ( !m_bParagraphOpened && !m_bIsFirstParagraph && bEmit )
+ {
+ // Create a dummy paragraph if needed
+ m_pSerializer->startElementNS(XML_w, XML_p);
+ m_pSerializer->startElementNS(XML_w, XML_pPr);
+
+ m_rExport.SectionProperties( *pSectionInfo );
+
+ m_pSerializer->endElementNS( XML_w, XML_pPr );
+ if (bExtraPageBreak)
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ }
+ m_pSerializer->endElementNS( XML_w, XML_p );
+ }
+ else
+ {
+ if (bExtraPageBreak && m_bParagraphOpened)
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ }
+ // postpone the output of this; it has to be done inside the
+ // paragraph properties, so remember it until then
+ m_pSectionInfo.reset( new WW8_SepInfo( *pSectionInfo ));
+ }
+ }
+ else if ( m_bParagraphOpened )
+ {
+ if (bBreakAfter)
+ // tdf#128889
+ m_bPageBreakAfter = true;
+ else
+ {
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ }
+ }
+ else
+ m_bPostponedPageBreak = true;
+
+ break;
+ default:
+ SAL_INFO("sw.ww8", "Unknown section break to write: " << nC );
+ break;
+ }
+}
+
+void DocxAttributeOutput::EndParaSdtBlock()
+{
+ if (m_aParagraphSdt.m_bStartedSdt)
+ {
+ // Paragraph-level SDT still open? Close it now.
+ m_aParagraphSdt.EndSdtBlock(m_pSerializer);
+ }
+}
+
+void DocxAttributeOutput::StartSection()
+{
+ m_pSerializer->startElementNS(XML_w, XML_sectPr);
+ m_bOpenedSectPr = true;
+
+ // Write the elements in the spec order
+ static const sal_Int32 aOrder[] =
+ {
+ FSNS( XML_w, XML_headerReference ),
+ FSNS( XML_w, XML_footerReference ),
+ FSNS( XML_w, XML_footnotePr ),
+ FSNS( XML_w, XML_endnotePr ),
+ FSNS( XML_w, XML_type ),
+ FSNS( XML_w, XML_pgSz ),
+ FSNS( XML_w, XML_pgMar ),
+ FSNS( XML_w, XML_paperSrc ),
+ FSNS( XML_w, XML_pgBorders ),
+ FSNS( XML_w, XML_lnNumType ),
+ FSNS( XML_w, XML_pgNumType ),
+ FSNS( XML_w, XML_cols ),
+ FSNS( XML_w, XML_formProt ),
+ FSNS( XML_w, XML_vAlign ),
+ FSNS( XML_w, XML_noEndnote ),
+ FSNS( XML_w, XML_titlePg ),
+ FSNS( XML_w, XML_textDirection ),
+ FSNS( XML_w, XML_bidi ),
+ FSNS( XML_w, XML_rtlGutter ),
+ FSNS( XML_w, XML_docGrid ),
+ FSNS( XML_w, XML_printerSettings ),
+ FSNS( XML_w, XML_sectPrChange )
+ };
+
+ // postpone the output so that we can later [in EndParagraphProperties()]
+ // prepend the properties before the run
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ m_pSerializer->mark(Tag_StartSection, comphelper::containerToSequence(aOrder));
+ m_bHadSectPr = true;
+}
+
+void DocxAttributeOutput::EndSection()
+{
+ // Write the section properties
+ if ( m_pSectionSpacingAttrList.is() )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_pgMar, detachFrom( m_pSectionSpacingAttrList ) );
+ }
+
+ // Order the elements
+ m_pSerializer->mergeTopMarks(Tag_StartSection);
+
+ m_pSerializer->endElementNS( XML_w, XML_sectPr );
+ m_bOpenedSectPr = false;
+}
+
+void DocxAttributeOutput::SectionFormProtection( bool bProtected )
+{
+ if ( bProtected )
+ m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "true");
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
+{
+ if (!rRtlGutter.GetValue())
+ {
+ return;
+ }
+
+ m_pSerializer->singleElementNS(XML_w, XML_rtlGutter);
+}
+
+void DocxAttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
+{
+ m_oLineBreakClear = rLineBreak.GetValue();
+}
+
+void DocxAttributeOutput::WriteLineBreak()
+{
+ if (!m_oLineBreakClear.has_value())
+ {
+ return;
+ }
+
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+ pAttr->add(FSNS(XML_w, XML_type), "textWrapping");
+ switch (*m_oLineBreakClear)
+ {
+ case SwLineBreakClear::NONE:
+ pAttr->add(FSNS(XML_w, XML_clear), "none");
+ break;
+ case SwLineBreakClear::LEFT:
+ pAttr->add(FSNS(XML_w, XML_clear), "left");
+ break;
+ case SwLineBreakClear::RIGHT:
+ pAttr->add(FSNS(XML_w, XML_clear), "right");
+ break;
+ case SwLineBreakClear::ALL:
+ pAttr->add(FSNS(XML_w, XML_clear), "all");
+ break;
+ }
+ m_oLineBreakClear.reset();
+
+ m_pSerializer->singleElementNS(XML_w, XML_br, pAttr);
+}
+
+void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
+{
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+ pAttr->add( FSNS( XML_w, XML_countBy ), OString::number(rLnNumInfo.GetCountBy()));
+ pAttr->add( FSNS( XML_w, XML_restart ), rLnNumInfo.IsRestartEachPage() ? "newPage" : "continuous" );
+ if( rLnNumInfo.GetPosFromLeft())
+ pAttr->add( FSNS( XML_w, XML_distance ), OString::number(rLnNumInfo.GetPosFromLeft()));
+ if (nRestartNo > 0)
+ // Writer is 1-based, Word is 0-based.
+ pAttr->add(FSNS(XML_w, XML_start), OString::number(nRestartNo - 1));
+ m_pSerializer->singleElementNS( XML_w, XML_lnNumType, pAttr );
+}
+
+void DocxAttributeOutput::SectionTitlePage()
+{
+ m_pSerializer->singleElementNS(XML_w, XML_titlePg);
+}
+
+void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* /*pFirstPageFormat*/ )
+{
+ // Output the margins
+
+ const SvxBoxItem& rBox = pFormat->GetBox( );
+
+ const SvxBorderLine* pLeft = rBox.GetLeft( );
+ const SvxBorderLine* pTop = rBox.GetTop( );
+ const SvxBorderLine* pRight = rBox.GetRight( );
+ const SvxBorderLine* pBottom = rBox.GetBottom( );
+
+ if ( !(pBottom || pTop || pLeft || pRight) )
+ return;
+
+ OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
+
+ // Check if there is a shadow item
+ const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
+ if ( pItem )
+ {
+ const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
+ aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation();
+ }
+
+ // By top margin, impl_borders() means the distance between the top of the page and the header frame.
+ editeng::WordPageMargins aMargins = m_pageMargins;
+ HdFtDistanceGlue aGlue(pFormat->GetAttrSet());
+ if (aGlue.HasHeader())
+ aMargins.nTop = aGlue.m_DyaHdrTop;
+ // Ditto for bottom margin.
+ if (aGlue.HasFooter())
+ aMargins.nBottom = aGlue.m_DyaHdrBottom;
+
+ if (pFormat->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
+ {
+ aMargins.nTop += pFormat->GetLRSpace().GetGutterMargin();
+ }
+ else
+ {
+ aMargins.nLeft += pFormat->GetLRSpace().GetGutterMargin();
+ }
+
+ aOutputBorderOptions.pDistances = std::make_shared<editeng::WordBorderDistances>();
+ editeng::BorderDistancesToWord(rBox, aMargins, *aOutputBorderOptions.pDistances);
+
+ // All distances are relative to the text margins
+ m_pSerializer->startElementNS(XML_w, XML_pgBorders,
+ FSNS(XML_w, XML_display), "allPages",
+ FSNS(XML_w, XML_offsetFrom), aOutputBorderOptions.pDistances->bFromEdge ? "page" : "text");
+
+ std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map
+ impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap );
+
+ m_pSerializer->endElementNS( XML_w, XML_pgBorders );
+
+}
+
+void DocxAttributeOutput::SectionBiDi( bool bBiDi )
+{
+ if ( bBiDi )
+ m_pSerializer->singleElementNS(XML_w, XML_bidi);
+}
+
+// Converting Numbering Format Code to string
+static OString lcl_ConvertNumberingType(sal_Int16 nNumberingType, const SfxItemSet* pOutSet, OString& rFormat, const OString& sDefault = ""_ostr )
+{
+ OString aType = sDefault;
+
+ switch ( nNumberingType )
+ {
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N: aType = "upperLetter"_ostr; break;
+
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N: aType = "lowerLetter"_ostr; break;
+
+ case SVX_NUM_ROMAN_UPPER: aType = "upperRoman"_ostr; break;
+ case SVX_NUM_ROMAN_LOWER: aType = "lowerRoman"_ostr; break;
+ case SVX_NUM_ARABIC: aType = "decimal"_ostr; break;
+
+ case SVX_NUM_BITMAP:
+ case SVX_NUM_CHAR_SPECIAL: aType = "bullet"_ostr; break;
+
+ case style::NumberingType::CHARS_HEBREW: aType = "hebrew2"_ostr; break;
+ case style::NumberingType::NUMBER_HEBREW: aType = "hebrew1"_ostr; break;
+ case style::NumberingType::NUMBER_NONE: aType = "none"_ostr; break;
+ case style::NumberingType::FULLWIDTH_ARABIC: aType="decimalFullWidth"_ostr; break;
+ case style::NumberingType::TIAN_GAN_ZH: aType="ideographTraditional"_ostr; break;
+ case style::NumberingType::DI_ZI_ZH: aType="ideographZodiac"_ostr; break;
+ case style::NumberingType::NUMBER_LOWER_ZH:
+ aType="taiwaneseCountingThousand"_ostr;
+ if (pOutSet) {
+ const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE);
+ const LanguageType eLang = rLang.GetLanguage();
+
+ if (LANGUAGE_CHINESE_SIMPLIFIED == eLang) {
+ aType="chineseCountingThousand"_ostr;
+ }
+ }
+ break;
+ case style::NumberingType::NUMBER_UPPER_ZH_TW: aType="ideographLegalTraditional"_ostr;break;
+ case style::NumberingType::NUMBER_UPPER_ZH: aType="chineseLegalSimplified"_ostr; break;
+ case style::NumberingType::NUMBER_TRADITIONAL_JA: aType="japaneseLegal"_ostr;break;
+ case style::NumberingType::AIU_FULLWIDTH_JA: aType="aiueoFullWidth"_ostr;break;
+ case style::NumberingType::AIU_HALFWIDTH_JA: aType="aiueo"_ostr;break;
+ case style::NumberingType::IROHA_FULLWIDTH_JA: aType="iroha"_ostr;break;
+ case style::NumberingType::IROHA_HALFWIDTH_JA: aType="irohaFullWidth"_ostr;break;
+ case style::NumberingType::HANGUL_SYLLABLE_KO: aType="ganada"_ostr;break;
+ case style::NumberingType::HANGUL_JAMO_KO: aType="chosung"_ostr;break;
+ case style::NumberingType::NUMBER_HANGUL_KO: aType="koreanCounting"_ostr; break;
+ case style::NumberingType::NUMBER_LEGAL_KO: aType = "koreanLegal"_ostr; break;
+ case style::NumberingType::NUMBER_DIGITAL_KO: aType = "koreanDigital"_ostr; break;
+ case style::NumberingType::NUMBER_DIGITAL2_KO: aType = "koreanDigital2"_ostr; break;
+ case style::NumberingType::CIRCLE_NUMBER: aType="decimalEnclosedCircle"_ostr; break;
+ case style::NumberingType::CHARS_ARABIC: aType="arabicAlpha"_ostr; break;
+ case style::NumberingType::CHARS_ARABIC_ABJAD: aType="arabicAbjad"_ostr; break;
+ case style::NumberingType::CHARS_THAI: aType="thaiLetters"_ostr; break;
+ case style::NumberingType::CHARS_PERSIAN:
+ case style::NumberingType::CHARS_NEPALI: aType="hindiVowels"_ostr; break;
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU: aType = "russianUpper"_ostr; break;
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU: aType = "russianLower"_ostr; break;
+ case style::NumberingType::TEXT_NUMBER: aType="ordinal"_ostr; break;
+ case style::NumberingType::TEXT_CARDINAL: aType="cardinalText"_ostr; break;
+ case style::NumberingType::TEXT_ORDINAL: aType="ordinalText"_ostr; break;
+ case style::NumberingType::SYMBOL_CHICAGO: aType="chicago"_ostr; break;
+ case style::NumberingType::ARABIC_ZERO: aType = "decimalZero"_ostr; break;
+ case style::NumberingType::ARABIC_ZERO3:
+ aType = "custom"_ostr;
+ rFormat = "001, 002, 003, ..."_ostr;
+ break;
+ case style::NumberingType::ARABIC_ZERO4:
+ aType = "custom"_ostr;
+ rFormat = "0001, 0002, 0003, ..."_ostr;
+ break;
+ case style::NumberingType::ARABIC_ZERO5:
+ aType = "custom"_ostr;
+ rFormat = "00001, 00002, 00003, ..."_ostr;
+ break;
+/*
+ Fallback the rest to the suggested default.
+ case style::NumberingType::NATIVE_NUMBERING:
+ case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
+ case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
+ case style::NumberingType::CHARS_GREEK_UPPER_LETTER:
+ case style::NumberingType::CHARS_GREEK_LOWER_LETTER:
+ case style::NumberingType::PAGE_DESCRIPTOR:
+ case style::NumberingType::TRANSLITERATION:
+ case style::NumberingType::CHARS_KHMER:
+ case style::NumberingType::CHARS_LAO:
+ case style::NumberingType::CHARS_TIBETAN:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_BG:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_BG:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_BG:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_BG:
+ case style::NumberingType::CHARS_MYANMAR:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_SR:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_SR:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_SR:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_SR:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_UK:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_UK:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_UK:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_UK:
+*/
+ default: break;
+ }
+ return aType;
+}
+
+
+void DocxAttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber )
+{
+ // FIXME Not called properly with page styles like "First Page"
+
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+
+ // std::nullopt means no restart: then don't output that attribute if it is negative
+ if ( oPageRestartNumber )
+ pAttr->add( FSNS( XML_w, XML_start ), OString::number( *oPageRestartNumber ) );
+
+ // nNumType corresponds to w:fmt. See WW8Export::GetNumId() for more precisions
+ OString aCustomFormat;
+ OString aFormat(lcl_ConvertNumberingType(nNumType, nullptr, aCustomFormat));
+ if (!aFormat.isEmpty() && aCustomFormat.isEmpty())
+ pAttr->add(FSNS(XML_w, XML_fmt), aFormat);
+
+ m_pSerializer->singleElementNS( XML_w, XML_pgNumType, pAttr );
+
+ // see 2.6.12 pgNumType (Page Numbering Settings)
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::SectionPageNumbering()" );
+}
+
+void DocxAttributeOutput::SectionType( sal_uInt8 nBreakCode )
+{
+ /* break code: 0 No break, 1 New column
+ 2 New page, 3 Even page, 4 Odd page
+ */
+ const char* pType;
+ switch ( nBreakCode )
+ {
+ case 1: pType = "nextColumn"; break;
+ case 2: pType = "nextPage"; break;
+ case 3: pType = "evenPage"; break;
+ case 4: pType = "oddPage"; break;
+ default: pType = "continuous"; break;
+ }
+
+ m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), pType);
+}
+
+void DocxAttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
+{
+ switch( nVA )
+ {
+ case drawing::TextVerticalAdjust_CENTER:
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
+ break;
+ case drawing::TextVerticalAdjust_BOTTOM:
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
+ break;
+ case drawing::TextVerticalAdjust_BLOCK: //justify
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "both");
+ break;
+ default:
+ break;
+ }
+}
+
+void DocxAttributeOutput::StartFont( const OUString& rFamilyName ) const
+{
+ m_pSerializer->startElementNS(XML_w, XML_font, FSNS(XML_w, XML_name), rFamilyName);
+}
+
+void DocxAttributeOutput::EndFont() const
+{
+ m_pSerializer->endElementNS( XML_w, XML_font );
+}
+
+void DocxAttributeOutput::FontAlternateName( const OUString& rName ) const
+{
+ m_pSerializer->singleElementNS(XML_w, XML_altName, FSNS(XML_w, XML_val), rName);
+}
+
+void DocxAttributeOutput::FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const
+{
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+
+ OString aCharSet( OString::number( nCharSet, 16 ) );
+ if ( aCharSet.getLength() == 1 )
+ aCharSet = "0" + aCharSet;
+ pAttr->add(FSNS(XML_w, XML_val), aCharSet);
+
+ if (GetExport().GetFilter().getVersion() != oox::core::ECMA_376_1ST_EDITION)
+ {
+ if( const char* charset = rtl_getMimeCharsetFromTextEncoding( nEncoding ))
+ pAttr->add( FSNS( XML_w, XML_characterSet ), charset );
+ }
+
+ m_pSerializer->singleElementNS( XML_w, XML_charset, pAttr );
+}
+
+void DocxAttributeOutput::FontFamilyType( FontFamily eFamily ) const
+{
+ const char* pFamily;
+ switch ( eFamily )
+ {
+ case FAMILY_ROMAN: pFamily = "roman"; break;
+ case FAMILY_SWISS: pFamily = "swiss"; break;
+ case FAMILY_MODERN: pFamily = "modern"; break;
+ case FAMILY_SCRIPT: pFamily = "script"; break;
+ case FAMILY_DECORATIVE: pFamily = "decorative"; break;
+ default: pFamily = "auto"; break; // no font family
+ }
+
+ m_pSerializer->singleElementNS(XML_w, XML_family, FSNS(XML_w, XML_val), pFamily);
+}
+
+void DocxAttributeOutput::FontPitchType( FontPitch ePitch ) const
+{
+ const char* pPitch;
+ switch ( ePitch )
+ {
+ case PITCH_VARIABLE: pPitch = "variable"; break;
+ case PITCH_FIXED: pPitch = "fixed"; break;
+ default: pPitch = "default"; break; // no info about the pitch
+ }
+
+ m_pSerializer->singleElementNS(XML_w, XML_pitch, FSNS(XML_w, XML_val), pPitch);
+}
+
+void DocxAttributeOutput::EmbedFont( std::u16string_view name, FontFamily family, FontPitch pitch )
+{
+ if( !m_rExport.m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS ))
+ return; // no font embedding with this document
+ EmbedFontStyle( name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch );
+ EmbedFontStyle( name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch );
+ EmbedFontStyle( name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch );
+ EmbedFontStyle( name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch );
+}
+
+static char toHexChar( int value )
+{
+ return value >= 10 ? value + 'A' - 10 : value + '0';
+}
+
+void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, FontFamily family, FontItalic italic,
+ FontWeight weight, FontPitch pitch )
+{
+ // Embed font if at least viewing is allowed (in which case the opening app must check
+ // the font license rights too and open either read-only or not use the font for editing).
+ OUString fontUrl = EmbeddedFontsHelper::fontFileUrl( name, family, italic, weight, pitch,
+ EmbeddedFontsHelper::FontRights::ViewingAllowed );
+ if( fontUrl.isEmpty())
+ return;
+ // TODO IDocumentSettingAccess::EMBED_SYSTEM_FONTS
+ if( !m_FontFilesMap.count( fontUrl ))
+ {
+ osl::File file( fontUrl );
+ if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None )
+ return;
+ uno::Reference< css::io::XOutputStream > xOutStream = m_rExport.GetFilter().openFragmentStream(
+ "word/fonts/font" + OUString::number(m_nextFontId) + ".odttf",
+ "application/vnd.openxmlformats-officedocument.obfuscatedFont" );
+ // Not much point in trying hard with the obfuscation key, whoever reads the spec can read the font anyway,
+ // so just alter the first and last part of the key.
+ char fontKeyStr[] = "{00014A78-CABC-4EF0-12AC-5CD89AEFDE00}";
+ sal_uInt8 fontKey[ 16 ] = { 0, 0xDE, 0xEF, 0x9A, 0xD8, 0x5C, 0xAC, 0x12, 0xF0, 0x4E,
+ 0xBC, 0xCA, 0x78, 0x4A, 0x01, 0 };
+ fontKey[ 0 ] = fontKey[ 15 ] = m_nextFontId % 256;
+ fontKeyStr[ 1 ] = fontKeyStr[ 35 ] = toHexChar(( m_nextFontId % 256 ) / 16 );
+ fontKeyStr[ 2 ] = fontKeyStr[ 36 ] = toHexChar(( m_nextFontId % 256 ) % 16 );
+ unsigned char buffer[ 4096 ];
+ sal_uInt64 readSize;
+ file.read( buffer, 32, readSize );
+ if( readSize < 32 )
+ {
+ SAL_WARN( "sw.ww8", "Font file size too small (" << fontUrl << ")" );
+ xOutStream->closeOutput();
+ return;
+ }
+ for( int i = 0;
+ i < 16;
+ ++i )
+ {
+ buffer[ i ] ^= fontKey[ i ];
+ buffer[ i + 16 ] ^= fontKey[ i ];
+ }
+ xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), 32 ));
+ for(;;)
+ {
+ sal_Bool eof;
+ if( file.isEndOfFile( &eof ) != osl::File::E_None )
+ {
+ SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl );
+ xOutStream->closeOutput();
+ return;
+ }
+ if( eof )
+ break;
+ if( file.read( buffer, 4096, readSize ) != osl::File::E_None )
+ {
+ SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl );
+ xOutStream->closeOutput();
+ return;
+ }
+ if( readSize == 0 )
+ break;
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize ));
+ }
+ xOutStream->closeOutput();
+ OString relId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
+ oox::getRelationship(Relationship::FONT),
+ Concat2View("fonts/font" + OUString::number( m_nextFontId ) + ".odttf") ), RTL_TEXTENCODING_UTF8 );
+ EmbeddedFontRef ref;
+ ref.relId = relId;
+ ref.fontKey = fontKeyStr;
+ m_FontFilesMap[ fontUrl ] = ref;
+ ++m_nextFontId;
+ }
+ m_pSerializer->singleElementNS( XML_w, tag,
+ FSNS( XML_r, XML_id ), m_FontFilesMap[ fontUrl ].relId,
+ FSNS( XML_w, XML_fontKey ), m_FontFilesMap[ fontUrl ].fontKey );
+}
+
+OString DocxAttributeOutput::TransHighlightColor( sal_uInt8 nIco )
+{
+ switch (nIco)
+ {
+ case 0: return "none"_ostr; break;
+ case 1: return "black"_ostr; break;
+ case 2: return "blue"_ostr; break;
+ case 3: return "cyan"_ostr; break;
+ case 4: return "green"_ostr; break;
+ case 5: return "magenta"_ostr; break;
+ case 6: return "red"_ostr; break;
+ case 7: return "yellow"_ostr; break;
+ case 8: return "white"_ostr; break;
+ case 9: return "darkBlue"_ostr; break;
+ case 10: return "darkCyan"_ostr; break;
+ case 11: return "darkGreen"_ostr; break;
+ case 12: return "darkMagenta"_ostr; break;
+ case 13: return "darkRed"_ostr; break;
+ case 14: return "darkYellow"_ostr; break;
+ case 15: return "darkGray"_ostr; break;
+ case 16: return "lightGray"_ostr; break;
+ default: return OString(); break;
+ }
+}
+
+void DocxAttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule )
+{
+ // nId is the same both for abstract numbering definition as well as the
+ // numbering definition itself
+ // TODO check that this is actually true & fix if not ;-)
+ OString aId( OString::number( nId ) );
+
+ m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), aId);
+
+ m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), aId);
+
+#if OSL_DEBUG_LEVEL > 1
+ // TODO ww8 version writes this, anything to do about it here?
+ if ( rRule.IsContinusNum() )
+ SAL_INFO("sw", "TODO DocxAttributeOutput::NumberingDefinition()" );
+#else
+ (void) rRule; // to quiet the warning...
+#endif
+
+ m_pSerializer->endElementNS( XML_w, XML_num );
+}
+
+// Not all attributes of SwNumFormat are important for export, so can't just use embedded in
+// that classes comparison.
+static bool lcl_ListLevelsAreDifferentForExport(const SwNumFormat & rFormat1, const SwNumFormat & rFormat2)
+{
+ if (rFormat1 == rFormat2)
+ // They are equal, nothing to do
+ return false;
+
+ if (!rFormat1.GetCharFormat() != !rFormat2.GetCharFormat())
+ // One has charformat, other not. they are different
+ return true;
+
+ if (rFormat1.GetCharFormat() && rFormat2.GetCharFormat())
+ {
+ const SwAttrSet & a1 = rFormat1.GetCharFormat()->GetAttrSet();
+ const SwAttrSet & a2 = rFormat2.GetCharFormat()->GetAttrSet();
+
+ if (!(a1 == a2))
+ // Difference in charformat: they are different
+ return true;
+ }
+
+ // Compare numformats with empty charformats
+ SwNumFormat modified1 = rFormat1;
+ SwNumFormat modified2 = rFormat2;
+ modified1.SetCharFormatName(OUString());
+ modified2.SetCharFormatName(OUString());
+ modified1.SetCharFormat(nullptr);
+ modified2.SetCharFormat(nullptr);
+ return modified1 != modified2;
+}
+
+void DocxAttributeOutput::OverrideNumberingDefinition(
+ SwNumRule const& rRule,
+ sal_uInt16 const nNum, sal_uInt16 const nAbstractNum, const std::map< size_t, size_t > & rLevelOverrides )
+{
+ m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), OString::number(nNum));
+
+ m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), OString::number(nAbstractNum));
+
+ SwNumRule const& rAbstractRule = *(*m_rExport.m_pUsedNumTable)[nAbstractNum - 1];
+ sal_uInt8 const nLevels = static_cast<sal_uInt8>(rRule.IsContinusNum()
+ ? WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel);
+ sal_uInt8 nPreviousOverrideLevel = 0;
+ for (sal_uInt8 nLevel = 0; nLevel < nLevels; ++nLevel)
+ {
+ const auto levelOverride = rLevelOverrides.find(nLevel);
+ bool bListsAreDifferent = lcl_ListLevelsAreDifferentForExport(rRule.Get(nLevel), rAbstractRule.Get(nLevel));
+
+ // Export list override only if it is different to abstract one
+ // or we have a level numbering override
+ if (bListsAreDifferent || levelOverride != rLevelOverrides.end())
+ {
+ // If there are "gaps" in w:lvlOverride numbers, MS Word can have issues with numbering.
+ // So we need to emit default override tokens up to current one.
+ while (nPreviousOverrideLevel < nLevel)
+ {
+ const SwNumFormat& rFormat = rRule.Get(nPreviousOverrideLevel);
+ m_pSerializer->startElementNS(XML_w, XML_lvlOverride, FSNS(XML_w, XML_ilvl), OString::number(nPreviousOverrideLevel));
+ // tdf#153104: absent startOverride is treated by Word as "startOverride value 0".
+ m_pSerializer->singleElementNS(XML_w, XML_startOverride, FSNS(XML_w, XML_val), OString::number(rFormat.GetStart()));
+ m_pSerializer->endElementNS(XML_w, XML_lvlOverride);
+ nPreviousOverrideLevel++;
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_lvlOverride, FSNS(XML_w, XML_ilvl), OString::number(nLevel));
+
+ if (bListsAreDifferent)
+ {
+ GetExport().NumberingLevel(rRule, nLevel);
+ }
+ if (levelOverride != rLevelOverrides.end())
+ {
+ // list numbering restart override
+ m_pSerializer->singleElementNS(XML_w, XML_startOverride,
+ FSNS(XML_w, XML_val), OString::number(levelOverride->second));
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_lvlOverride);
+ }
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_num );
+}
+
+void DocxAttributeOutput::StartAbstractNumbering( sal_uInt16 nId )
+{
+ const SwNumRule* pRule = (*m_rExport.m_pUsedNumTable)[nId - 1];
+ m_bExportingOutline = pRule && pRule->IsOutlineRule();
+ m_pSerializer->startElementNS( XML_w, XML_abstractNum,
+ FSNS( XML_w, XML_abstractNumId ), OString::number(nId) );
+}
+
+void DocxAttributeOutput::EndAbstractNumbering()
+{
+ m_pSerializer->endElementNS( XML_w, XML_abstractNum );
+}
+
+void DocxAttributeOutput::NumberingLevel( sal_uInt8 nLevel,
+ sal_uInt16 nStart,
+ sal_uInt16 nNumberingType,
+ SvxAdjust eAdjust,
+ const sal_uInt8 * /*pNumLvlPos*/,
+ sal_uInt8 nFollow,
+ const wwFont *pFont,
+ const SfxItemSet *pOutSet,
+ sal_Int16 nIndentAt,
+ sal_Int16 nFirstLineIndex,
+ sal_Int16 nListTabPos,
+ const OUString &rNumberingString,
+ const SvxBrushItem* pBrush,
+ bool isLegal)
+{
+ m_pSerializer->startElementNS(XML_w, XML_lvl, FSNS(XML_w, XML_ilvl), OString::number(nLevel));
+
+ // start with the nStart value. Do not write w:start if Numbered Lists
+ // starts from zero.As it's an optional parameter.
+ // refer ECMA 376 Second edition Part-1
+ if(0 != nLevel || 0 != nStart)
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_start,
+ FSNS( XML_w, XML_val ), OString::number(nStart) );
+ }
+
+ if (m_bExportingOutline)
+ {
+ sal_uInt16 nId = m_rExport.m_pStyles->GetHeadingParagraphStyleId( nLevel );
+ if ( nId != SAL_MAX_UINT16 )
+ m_pSerializer->singleElementNS( XML_w, XML_pStyle ,
+ FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nId) );
+ }
+
+ if (isLegal)
+ m_pSerializer->singleElementNS(XML_w, XML_isLgl);
+
+ // format
+ OString aCustomFormat;
+ OString aFormat(lcl_ConvertNumberingType(nNumberingType, pOutSet, aCustomFormat, "decimal"_ostr));
+
+ {
+ if (aCustomFormat.isEmpty())
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat);
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
+ m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "w14");
+
+ m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat,
+ FSNS(XML_w, XML_format), aCustomFormat);
+
+ m_pSerializer->endElementNS(XML_mc, XML_Choice);
+ m_pSerializer->startElementNS(XML_mc, XML_Fallback);
+ m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), "decimal");
+ m_pSerializer->endElementNS(XML_mc, XML_Fallback);
+ m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
+ }
+ }
+
+ // suffix
+ const char *pSuffix = nullptr;
+ switch ( nFollow )
+ {
+ case 1: pSuffix = "space"; break;
+ case 2: pSuffix = "nothing"; break;
+ default: /*pSuffix = "tab";*/ break;
+ }
+ if ( pSuffix )
+ m_pSerializer->singleElementNS(XML_w, XML_suff, FSNS(XML_w, XML_val), pSuffix);
+
+ // text
+ OUStringBuffer aBuffer( rNumberingString.getLength() + WW8ListManager::nMaxLevel );
+
+ const sal_Unicode *pPrev = rNumberingString.getStr();
+ const sal_Unicode *pIt = rNumberingString.getStr();
+ while ( pIt < rNumberingString.getStr() + rNumberingString.getLength() )
+ {
+ // convert the level values to %NUMBER form
+ // (we don't use pNumLvlPos at all)
+ // FIXME so far we support the ww8 limit of levels only
+ if ( *pIt < sal_Unicode( WW8ListManager::nMaxLevel ) )
+ {
+ aBuffer.append( OUString::Concat(std::u16string_view(pPrev, pIt - pPrev))
+ + "%"
+ + OUString::number(sal_Int32( *pIt ) + 1 ));
+
+ pPrev = pIt + 1;
+ }
+ ++pIt;
+ }
+ if ( pPrev < pIt )
+ aBuffer.append( pPrev, pIt - pPrev );
+
+ // If bullet char is empty, set lvlText as empty
+ if ( rNumberingString == OUStringChar('\0') && nNumberingType == SVX_NUM_CHAR_SPECIAL )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), "");
+ }
+ else
+ {
+ // Writer's "zero width space" suffix is necessary, so that LabelFollowedBy shows up, but Word doesn't require that.
+ OUString aLevelText = aBuffer.makeStringAndClear();
+ static OUString aZeroWidthSpace(u'\x200B');
+ if (aLevelText == aZeroWidthSpace)
+ aLevelText.clear();
+ m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), aLevelText);
+ }
+
+ // bullet
+ if (nNumberingType == SVX_NUM_BITMAP && pBrush)
+ {
+ int nIndex = m_rExport.GetGrfIndex(*pBrush);
+ if (nIndex != -1)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_lvlPicBulletId,
+ FSNS(XML_w, XML_val), OString::number(nIndex));
+ }
+ }
+
+ // justification
+ const char *pJc;
+ bool const ecmaDialect = m_rExport.GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+ switch ( eAdjust )
+ {
+ case SvxAdjust::Center: pJc = "center"; break;
+ case SvxAdjust::Right: pJc = !ecmaDialect ? "end" : "right"; break;
+ default: pJc = !ecmaDialect ? "start" : "left"; break;
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_lvlJc, FSNS(XML_w, XML_val), pJc);
+
+ // indentation
+ m_pSerializer->startElementNS(XML_w, XML_pPr);
+ if( nListTabPos >= 0 )
+ {
+ m_pSerializer->startElementNS(XML_w, XML_tabs);
+ m_pSerializer->singleElementNS( XML_w, XML_tab,
+ FSNS( XML_w, XML_val ), "num",
+ FSNS( XML_w, XML_pos ), OString::number(nListTabPos) );
+ m_pSerializer->endElementNS( XML_w, XML_tabs );
+ }
+
+ sal_Int32 nToken = ecmaDialect ? XML_left : XML_start;
+ sal_Int32 nIndentToken = nFirstLineIndex > 0 ? XML_firstLine : XML_hanging;
+ m_pSerializer->singleElementNS( XML_w, XML_ind,
+ FSNS( XML_w, nToken ), OString::number(nIndentAt),
+ FSNS( XML_w, nIndentToken ), OString::number(abs(nFirstLineIndex)) );
+ m_pSerializer->endElementNS( XML_w, XML_pPr );
+
+ // font
+ if ( pOutSet )
+ {
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+
+ SfxItemSet aTempSet(*pOutSet);
+ if ( pFont )
+ {
+ GetExport().GetId( *pFont ); // ensure font info is written to fontTable.xml
+ OString aFamilyName( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
+ m_pSerializer->singleElementNS( XML_w, XML_rFonts,
+ FSNS( XML_w, XML_ascii ), aFamilyName,
+ FSNS( XML_w, XML_hAnsi ), aFamilyName,
+ FSNS( XML_w, XML_cs ), aFamilyName,
+ FSNS( XML_w, XML_hint ), "default" );
+ aTempSet.ClearItem(RES_CHRATR_FONT);
+ aTempSet.ClearItem(RES_CHRATR_CTL_FONT);
+ }
+ m_rExport.OutputItemSet(aTempSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF);
+
+ WriteCollectedRunProperties();
+
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ }
+
+ // TODO anything to do about nListTabPos?
+
+ m_pSerializer->endElementNS( XML_w, XML_lvl );
+}
+
+void DocxAttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
+{
+ switch ( rCaseMap.GetValue() )
+ {
+ case SvxCaseMap::SmallCaps:
+ m_pSerializer->singleElementNS(XML_w, XML_smallCaps);
+ break;
+ case SvxCaseMap::Uppercase:
+ m_pSerializer->singleElementNS(XML_w, XML_caps);
+ break;
+ default: // Something that ooxml does not support
+ m_pSerializer->singleElementNS(XML_w, XML_smallCaps, FSNS(XML_w, XML_val), "false");
+ m_pSerializer->singleElementNS(XML_w, XML_caps, FSNS(XML_w, XML_val), "false");
+ break;
+ }
+}
+
+void DocxAttributeOutput::CharColor(const SvxColorItem& rColorItem)
+{
+ const Color aColor = rColorItem.getColor();
+ const model::ComplexColor aComplexColor = rColorItem.getComplexColor();
+
+ OString aColorString = msfilter::util::ConvertColor(aColor);
+
+ std::string_view pExistingValue;
+ if (m_pColorAttrList.is() && m_pColorAttrList->getAsView(FSNS(XML_w, XML_val), pExistingValue))
+ {
+ assert(aColorString.equalsL(pExistingValue.data(), pExistingValue.size()));
+ return;
+ }
+
+ lclAddThemeColorAttributes(m_pColorAttrList, aComplexColor);
+
+ AddToAttrList(m_pColorAttrList, FSNS(XML_w, XML_val), aColorString);
+ m_nCharTransparence = 255 - aColor.GetAlpha();
+}
+
+void DocxAttributeOutput::CharContour( const SvxContourItem& rContour )
+{
+ if ( rContour.GetValue() )
+ m_pSerializer->singleElementNS(XML_w, XML_outline);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_outline, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossedOut )
+{
+ switch ( rCrossedOut.GetStrikeout() )
+ {
+ case STRIKEOUT_DOUBLE:
+ m_pSerializer->singleElementNS(XML_w, XML_dstrike);
+ break;
+ case STRIKEOUT_NONE:
+ m_pSerializer->singleElementNS(XML_w, XML_dstrike, FSNS(XML_w, XML_val), "false");
+ m_pSerializer->singleElementNS(XML_w, XML_strike, FSNS(XML_w, XML_val), "false");
+ break;
+ default:
+ m_pSerializer->singleElementNS(XML_w, XML_strike);
+ break;
+ }
+}
+
+void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
+{
+ OString sIss;
+ short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
+
+ bool bParaStyle = false;
+ if (m_rExport.m_bStyDef && m_rExport.m_pCurrentStyle)
+ {
+ bParaStyle = m_rExport.m_pCurrentStyle->Which() == RES_TXTFMTCOLL;
+ }
+
+ // Simplify styles to avoid impossible complexity. Import and export as defaults only
+ if ( m_rExport.m_bStyDef && nEsc && !(bParaStyle && nEsc < 0))
+ {
+ nProp = DFLT_ESC_PROP;
+ nEsc = (nEsc > 0) ? DFLT_ESC_AUTO_SUPER : DFLT_ESC_AUTO_SUB;
+ }
+
+ if ( !nEsc )
+ {
+ sIss = "baseline"_ostr;
+ nEsc = 0;
+ nProp = 100;
+ }
+ else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
+ {
+ if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
+ sIss = "subscript"_ostr;
+ else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
+ sIss = "superscript"_ostr;
+ }
+ else if ( DFLT_ESC_AUTO_SUPER == nEsc )
+ {
+ // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
+ // The ascent is generally about 80% of the total font height.
+ // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
+ nEsc = .8 * (100 - nProp);
+ }
+ else if ( DFLT_ESC_AUTO_SUB == nEsc )
+ {
+ // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
+ // The descent is generally about 20% of the total font height.
+ // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
+ nEsc = .2 * -(100 - nProp);
+ }
+
+ if ( !sIss.isEmpty() )
+ m_pSerializer->singleElementNS(XML_w, XML_vertAlign, FSNS(XML_w, XML_val), sIss);
+
+ if (!(sIss.isEmpty() || sIss.match("baseline")))
+ return;
+
+ const SvxFontHeightItem& rItem = m_rExport.GetItem(RES_CHRATR_FONTSIZE);
+ float fHeight = rItem.GetHeight();
+ OString sPos = OString::number( round(( fHeight * nEsc ) / 1000) );
+ m_pSerializer->singleElementNS(XML_w, XML_position, FSNS(XML_w, XML_val), sPos);
+
+ if( ( 100 != nProp || sIss.match( "baseline" ) ) && !m_rExport.m_bFontSizeWritten )
+ {
+ OString sSize = OString::number( round(( fHeight * nProp ) / 1000) );
+ m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), sSize);
+ }
+}
+
+void DocxAttributeOutput::CharFont( const SvxFontItem& rFont)
+{
+ GetExport().GetId( rFont ); // ensure font info is written to fontTable.xml
+ const OUString& sFontName(rFont.GetFamilyName());
+ if (sFontName.isEmpty())
+ return;
+
+ if (m_pFontsAttrList &&
+ ( m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_ascii )) ||
+ m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_hAnsi )) )
+ )
+ {
+ // tdf#38778: do to fields output into DOC the font could be added before and after field declaration
+ // that all sub runs of the field will have correct font inside.
+ // For DOCX we should do not add the same font information twice in the same node
+ return;
+ }
+
+ AddToAttrList( m_pFontsAttrList,
+ FSNS( XML_w, XML_ascii ), sFontName,
+ FSNS( XML_w, XML_hAnsi ), sFontName );
+}
+
+void DocxAttributeOutput::CharFontSize( const SvxFontHeightItem& rFontSize)
+{
+ OString fontSize = OString::number( ( rFontSize.GetHeight() + 5 ) / 10 );
+
+ switch ( rFontSize.Which() )
+ {
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_CJK_FONTSIZE:
+ m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), fontSize);
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ m_pSerializer->singleElementNS(XML_w, XML_szCs, FSNS(XML_w, XML_val), fontSize);
+ break;
+ }
+}
+
+void DocxAttributeOutput::CharKerning( const SvxKerningItem& rKerning )
+{
+ OString aKerning = OString::number( rKerning.GetValue() );
+ m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), aKerning);
+}
+
+void DocxAttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
+{
+ OUString aLanguageCode(LanguageTag( rLanguage.GetLanguage()).getBcp47MS());
+
+ switch ( rLanguage.Which() )
+ {
+ case RES_CHRATR_LANGUAGE:
+ AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_val ), aLanguageCode );
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_eastAsia ), aLanguageCode );
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_bidi ), aLanguageCode );
+ break;
+ }
+}
+
+void DocxAttributeOutput::CharPosture( const SvxPostureItem& rPosture )
+{
+ if ( rPosture.GetPosture() != ITALIC_NONE )
+ m_pSerializer->singleElementNS(XML_w, XML_i);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
+{
+ if ( rShadow.GetValue() )
+ m_pSerializer->singleElementNS(XML_w, XML_shadow);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_shadow, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
+{
+ const char *pUnderlineValue;
+
+ switch ( rUnderline.GetLineStyle() )
+ {
+ case LINESTYLE_SINGLE: pUnderlineValue = "single"; break;
+ case LINESTYLE_BOLD: pUnderlineValue = "thick"; break;
+ case LINESTYLE_DOUBLE: pUnderlineValue = "double"; break;
+ case LINESTYLE_DOTTED: pUnderlineValue = "dotted"; break;
+ case LINESTYLE_DASH: pUnderlineValue = "dash"; break;
+ case LINESTYLE_DASHDOT: pUnderlineValue = "dotDash"; break;
+ case LINESTYLE_DASHDOTDOT: pUnderlineValue = "dotDotDash"; break;
+ case LINESTYLE_WAVE: pUnderlineValue = "wave"; break;
+ case LINESTYLE_BOLDDOTTED: pUnderlineValue = "dottedHeavy"; break;
+ case LINESTYLE_BOLDDASH: pUnderlineValue = "dashedHeavy"; break;
+ case LINESTYLE_LONGDASH: pUnderlineValue = "dashLongHeavy"; break;
+ case LINESTYLE_BOLDLONGDASH: pUnderlineValue = "dashLongHeavy"; break;
+ case LINESTYLE_BOLDDASHDOT: pUnderlineValue = "dashDotHeavy"; break;
+ case LINESTYLE_BOLDDASHDOTDOT: pUnderlineValue = "dashDotDotHeavy"; break;
+ case LINESTYLE_BOLDWAVE: pUnderlineValue = "wavyHeavy"; break;
+ case LINESTYLE_DOUBLEWAVE: pUnderlineValue = "wavyDouble"; break;
+ case LINESTYLE_NONE: // fall through
+ default: pUnderlineValue = "none"; break;
+ }
+
+ Color aUnderlineColor = rUnderline.GetColor();
+ bool bUnderlineHasColor = !aUnderlineColor.IsTransparent();
+ if (bUnderlineHasColor)
+ {
+ model::ComplexColor const& rComplexColor = rUnderline.getComplexColor();
+ // Underline has a color
+ rtl::Reference<FastAttributeList> pAttrList = FastSerializerHelper::createAttrList();
+ pAttrList->add(FSNS(XML_w, XML_val), pUnderlineValue);
+ pAttrList->add(FSNS(XML_w, XML_color), msfilter::util::ConvertColor(aUnderlineColor));
+ lclAddThemeColorAttributes(pAttrList, rComplexColor);
+ m_pSerializer->singleElementNS(XML_w, XML_u, pAttrList);
+
+ }
+ else
+ {
+ // Underline has no color
+ m_pSerializer->singleElementNS(XML_w, XML_u, FSNS(XML_w, XML_val), pUnderlineValue);
+ }
+}
+
+void DocxAttributeOutput::CharWeight( const SvxWeightItem& rWeight )
+{
+ if ( rWeight.GetWeight() == WEIGHT_BOLD )
+ m_pSerializer->singleElementNS(XML_w, XML_b);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern )
+{
+ // auto kerning is bound to a minimum font size in Word - but is just a boolean in Writer :-(
+ // kerning is based on half-point sizes, so 2 enables kerning for fontsize 1pt or higher. (1 is treated as size 12, and 0 is treated as disabled.)
+ const OString sFontSize = OString::number( static_cast<sal_uInt32>(rAutoKern.GetValue()) * 2 );
+ m_pSerializer->singleElementNS(XML_w, XML_kern, FSNS(XML_w, XML_val), sFontSize);
+}
+
+void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
+{
+ if ( rBlink.GetValue() )
+ m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "blinkBackground");
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "none");
+}
+
+constexpr OUStringLiteral MSWORD_CH_SHADING_FILL = u"FFFFFF"; // The attribute w:fill of w:shd, for MS-Word's character shading,
+constexpr OUStringLiteral MSWORD_CH_SHADING_COLOR = u"auto"; // The attribute w:color of w:shd, for MS-Word's character shading,
+constexpr OUStringLiteral MSWORD_CH_SHADING_VAL = u"pct15"; // The attribute w:value of w:shd, for MS-Word's character shading,
+
+void DocxAttributeOutput::CharBackground( const SvxBrushItem& rBrush )
+{
+ // Check if the brush shading pattern is 'PCT15'. If so - write it back to the DOCX
+ if (rBrush.GetShadingValue() == ShadingPattern::PCT15)
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_shd,
+ FSNS( XML_w, XML_val ), MSWORD_CH_SHADING_VAL,
+ FSNS( XML_w, XML_color ), MSWORD_CH_SHADING_COLOR,
+ FSNS( XML_w, XML_fill ), MSWORD_CH_SHADING_FILL );
+ }
+ else
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_shd,
+ FSNS( XML_w, XML_fill ), msfilter::util::ConvertColor(rBrush.GetColor()),
+ FSNS( XML_w, XML_val ), "clear" );
+ }
+}
+
+void DocxAttributeOutput::CharFontCJK( const SvxFontItem& rFont )
+{
+ if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_eastAsia)))
+ {
+ // tdf#38778: do to fields output into DOC the font could be added before and after field declaration
+ // that all sub runs of the field will have correct font inside.
+ // For DOCX we should do not add the same font information twice in the same node
+ return;
+ }
+
+ AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsia ), rFont.GetFamilyName() );
+}
+
+void DocxAttributeOutput::CharPostureCJK( const SvxPostureItem& rPosture )
+{
+ if ( rPosture.GetPosture() != ITALIC_NONE )
+ m_pSerializer->singleElementNS(XML_w, XML_i);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharWeightCJK( const SvxWeightItem& rWeight )
+{
+ if ( rWeight.GetWeight() == WEIGHT_BOLD )
+ m_pSerializer->singleElementNS(XML_w, XML_b);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharFontCTL( const SvxFontItem& rFont )
+{
+ if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_cs)))
+ {
+ // tdf#38778: do to fields output into DOC the font could be added before and after field declaration
+ // that all sub runs of the field will have correct font inside.
+ // For DOCX we should do not add the same font information twice in the same node
+ return;
+ }
+
+ AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cs ), rFont.GetFamilyName() );
+}
+
+void DocxAttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture)
+{
+ if ( rPosture.GetPosture() != ITALIC_NONE )
+ m_pSerializer->singleElementNS(XML_w, XML_iCs);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_iCs, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
+{
+ if ( rWeight.GetWeight() == WEIGHT_BOLD )
+ m_pSerializer->singleElementNS(XML_w, XML_bCs);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_bCs, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharBidiRTL( const SfxPoolItem& )
+{
+}
+
+void DocxAttributeOutput::CharIdctHint( const SfxPoolItem& )
+{
+}
+
+void DocxAttributeOutput::CharRotate( const SvxCharRotateItem& rRotate)
+{
+ // Not rotated?
+ if ( !rRotate.GetValue())
+ return;
+
+ AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vert ), "true" );
+
+ if (rRotate.IsFitToLine())
+ AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vertCompress ), "true" );
+}
+
+void DocxAttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
+{
+ const char *pEmphasis;
+ const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
+
+ if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
+ pEmphasis = "dot";
+ else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
+ pEmphasis = "comma";
+ else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
+ pEmphasis = "circle";
+ else if (v == (FontEmphasisMark::Dot|FontEmphasisMark::PosBelow))
+ pEmphasis = "underDot";
+ else
+ pEmphasis = "none";
+
+ m_pSerializer->singleElementNS(XML_w, XML_em, FSNS(XML_w, XML_val), pEmphasis);
+}
+
+void DocxAttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
+{
+ if ( !rTwoLines.GetValue() )
+ return;
+
+ AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combine ), "true" );
+
+ sal_Unicode cStart = rTwoLines.GetStartBracket();
+ sal_Unicode cEnd = rTwoLines.GetEndBracket();
+
+ if (!cStart && !cEnd)
+ return;
+
+ std::string_view sBracket;
+ if ((cStart == '{') || (cEnd == '}'))
+ sBracket = "curly";
+ else if ((cStart == '<') || (cEnd == '>'))
+ sBracket = "angle";
+ else if ((cStart == '[') || (cEnd == ']'))
+ sBracket = "square";
+ else
+ sBracket = "round";
+ AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combineBrackets ), sBracket );
+}
+
+void DocxAttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
+{
+ // Clamp CharScaleWidth to OOXML limits ([1..600])
+ const sal_Int16 nScaleWidth( std::max<sal_Int16>( 1,
+ std::min<sal_Int16>( rScaleWidth.GetValue(), 600 ) ) );
+ m_pSerializer->singleElementNS( XML_w, XML_w,
+ FSNS( XML_w, XML_val ), OString::number(nScaleWidth) );
+}
+
+void DocxAttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
+{
+ switch ( rRelief.GetValue() )
+ {
+ case FontRelief::Embossed:
+ m_pSerializer->singleElementNS(XML_w, XML_emboss);
+ break;
+ case FontRelief::Engraved:
+ m_pSerializer->singleElementNS(XML_w, XML_imprint);
+ break;
+ default:
+ m_pSerializer->singleElementNS(XML_w, XML_emboss, FSNS(XML_w, XML_val), "false");
+ m_pSerializer->singleElementNS(XML_w, XML_imprint, FSNS(XML_w, XML_val), "false");
+ break;
+ }
+}
+
+void DocxAttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
+{
+ if ( rHidden.GetValue() )
+ m_pSerializer->singleElementNS(XML_w, XML_vanish);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_vanish, FSNS(XML_w, XML_val), "false");
+}
+
+void DocxAttributeOutput::CharBorder(
+ const SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow )
+{
+ css::table::BorderLine2 rStyleBorder;
+ const SvxBoxItem* pInherited = nullptr;
+ if ( GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() )
+ pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_CHRATR_BOX);
+ else if ( m_rExport.m_pChpIter ) // incredibly undocumented, but this is the character-style info, right?
+ {
+ if (const SvxBoxItem* pPoolItem = GetExport().m_pChpIter->HasTextItem(RES_CHRATR_BOX))
+ {
+ pInherited = pPoolItem;
+ }
+ }
+
+ if ( pInherited )
+ rStyleBorder = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false);
+
+ impl_borderLine( m_pSerializer, XML_bdr, pAllBorder, nDist, bShadow, &rStyleBorder );
+}
+
+void DocxAttributeOutput::CharHighlight( const SvxBrushItem& rHighlight )
+{
+ const OString sColor = TransHighlightColor( msfilter::util::TransColToIco(rHighlight.GetColor()) );
+ if ( !sColor.isEmpty() )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_highlight, FSNS(XML_w, XML_val), sColor);
+ }
+}
+
+void DocxAttributeOutput::TextINetFormat( const SwFormatINetFormat& rLink )
+{
+ const SwCharFormat* pFormat = m_rExport.m_rDoc.FindCharFormatByName(rLink.GetINetFormat());
+ if (pFormat)
+ {
+ OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pFormat)));
+ if (!aStyleId.equalsIgnoreAsciiCase("DefaultStyle"))
+ m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
+ }
+}
+
+void DocxAttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
+{
+ OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(rCharFormat.GetCharFormat())));
+
+ m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
+}
+
+void DocxAttributeOutput::RefField( const SwField& rField, const OUString& rRef )
+{
+ SwFieldIds nType = rField.GetTyp( )->Which( );
+ if ( nType == SwFieldIds::GetExp )
+ {
+ OUString sCmd = FieldString( ww::eREF ) +
+ "\"" + rRef + "\" ";
+
+ m_rExport.OutputField( &rField, ww::eREF, sCmd );
+ }
+
+ // There is nothing to do here for the set fields
+}
+
+void DocxAttributeOutput::HiddenField(const SwField& /*rField*/)
+{
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::HiddenField()" );
+}
+
+void DocxAttributeOutput::PostitField( const SwField* pField )
+{
+ assert( dynamic_cast< const SwPostItField* >( pField ));
+ const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField);
+ sal_Int32 nId = 0;
+ auto it = m_rOpenedAnnotationMarksIds.find(pPostItField->GetName());
+ if (it != m_rOpenedAnnotationMarksIds.end())
+ // If the postit field has an annotation mark associated, we already have an id.
+ nId = it->second;
+ else
+ // Otherwise get a new one.
+ nId = m_nNextAnnotationMarkId++;
+ m_postitFields.emplace_back(pPostItField, PostItDOCXData{ nId });
+}
+
+void DocxAttributeOutput::WritePostitFieldReference()
+{
+ while( m_postitFieldsMaxId < m_postitFields.size())
+ {
+ OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second.id);
+
+ // In case this file is inside annotation marks, we want to write the
+ // comment reference after the annotation mark is closed, not here.
+ const OUString& idname = m_postitFields[m_postitFieldsMaxId].first->GetName();
+ auto it = m_rOpenedAnnotationMarksIds.find( idname );
+ if ( it == m_rOpenedAnnotationMarksIds.end( ) )
+ m_pSerializer->singleElementNS(XML_w, XML_commentReference, FSNS(XML_w, XML_id), idstr);
+ ++m_postitFieldsMaxId;
+ }
+}
+
+DocxAttributeOutput::hasProperties DocxAttributeOutput::WritePostitFields()
+{
+ bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ) && !SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
+
+ hasProperties eResult = hasProperties::no;
+ for (auto& [f1, data1] : m_postitFields)
+ {
+ if (f1->GetParentId() != 0)
+ {
+ for (size_t i = 0; i < m_postitFields.size(); i++)
+ {
+ auto& [f2, data2] = m_postitFields[i];
+ if (f2->GetParaId() == f1->GetParentId())
+ {
+ data2.parentStatus = ParentStatus::IsParent;
+ data1.parentStatus = ParentStatus::HasParent;
+ data1.parentIndex = i;
+ break;
+ }
+ }
+ }
+ }
+ for (auto& [f, data] : m_postitFields)
+ {
+ const DateTime aDateTime = f->GetDateTime();
+ bool bNoDate = bRemovePersonalInfo ||
+ ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 );
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ pAttributeList->add(FSNS( XML_w, XML_id ), OString::number(data.id));
+ pAttributeList->add(FSNS( XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OString::number( GetExport().GetInfoID(f->GetPar1()) )
+ : f->GetPar1().toUtf8());
+ if (!bNoDate)
+ pAttributeList->add(FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ));
+ pAttributeList->add(FSNS( XML_w, XML_initials ), bRemovePersonalInfo
+ ? OString::number( GetExport().GetInfoID(f->GetInitials()) )
+ : f->GetInitials().toUtf8());
+ m_pSerializer->startElementNS( XML_w, XML_comment, pAttributeList );
+
+ // Make sure to give parent/child fields a paraId
+ const bool bNeedParaId = f->GetResolved() || data.parentStatus != ParentStatus::None;
+ if (bNeedParaId)
+ eResult = hasProperties::yes;
+
+ if (f->GetTextObject() != nullptr)
+ {
+ // richtext
+ data.lastParaId = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, bNeedParaId);
+ }
+ else
+ {
+ // just plain text - eg. when the field was created via the
+ // .uno:InsertAnnotation API
+ std::optional<OUString> aParaId;
+ if (bNeedParaId)
+ {
+ data.lastParaId = m_nNextParaId++;
+ aParaId = NumberToHexBinary(data.lastParaId);
+ }
+ m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ RunText(f->GetText());
+ m_pSerializer->endElementNS(XML_w, XML_r);
+ m_pSerializer->endElementNS(XML_w, XML_p);
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_comment );
+ }
+ return eResult;
+}
+
+void DocxAttributeOutput::WritePostItFieldsResolved()
+{
+ for (auto& [f, data] : m_postitFields)
+ {
+ // Parent fields don't need to be exported here if they don't have a resolved attribute
+ if (!f->GetResolved() && data.parentStatus != ParentStatus::HasParent)
+ continue;
+ OUString idstr = NumberToHexBinary(data.lastParaId);
+ std::optional<OUString> sDone, sParentId;
+ if (f->GetParentId() != 0)
+ {
+ if (data.parentStatus == ParentStatus::HasParent)
+ {
+ // Since parent fields have been resolved first, they should already have an id
+ const PostItDOCXData& aParentFieldData = m_postitFields[data.parentIndex].second;
+ sParentId = NumberToHexBinary(aParentFieldData.lastParaId);
+ }
+ else
+ {
+ SAL_WARN("sw.ww8", "SwPostItField has a parent id, but a matching parent was not found");
+ }
+ }
+ if (f->GetResolved())
+ sDone = "1";
+ m_pSerializer->singleElementNS(XML_w15, XML_commentEx,
+ FSNS(XML_w15, XML_paraId), idstr,
+ FSNS(XML_w15, XML_done), sDone,
+ FSNS(XML_w15, XML_paraIdParent), sParentId);
+ }
+}
+
+bool DocxAttributeOutput::DropdownField( const SwField* pField )
+{
+ ww::eField eType = ww::eFORMDROPDOWN;
+ OUString sCmd = FieldString( eType );
+ GetExport( ).OutputField( pField, eType, sCmd );
+
+ return false;
+}
+
+bool DocxAttributeOutput::PlaceholderField( const SwField* pField )
+{
+ assert( m_PendingPlaceholder == nullptr );
+ m_PendingPlaceholder = pField;
+ return false; // do not expand
+}
+
+void DocxAttributeOutput::WritePendingPlaceholder()
+{
+ if( m_PendingPlaceholder == nullptr )
+ return;
+ const SwField* pField = m_PendingPlaceholder;
+ m_PendingPlaceholder = nullptr;
+ m_pSerializer->startElementNS(XML_w, XML_sdt);
+ m_pSerializer->startElementNS(XML_w, XML_sdtPr);
+ if( !pField->GetPar2().isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), pField->GetPar2());
+ m_pSerializer->singleElementNS(XML_w, XML_temporary);
+ m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr);
+ m_pSerializer->singleElementNS(XML_w, XML_text);
+ m_pSerializer->endElementNS( XML_w, XML_sdtPr );
+ m_pSerializer->startElementNS(XML_w, XML_sdtContent);
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ RunText( pField->GetPar1());
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ m_pSerializer->endElementNS( XML_w, XML_sdtContent );
+ m_pSerializer->endElementNS( XML_w, XML_sdt );
+}
+
+void DocxAttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
+{
+ // field bookmarks are handled in the EndRun method
+ GetExport().OutputField(&rField, eType, rCmd );
+}
+
+void DocxAttributeOutput::WriteExpand( const SwField* pField )
+{
+ // Will be written in the next End Run
+ m_rExport.OutputField( pField, ww::eUNKNOWN, OUString() );
+}
+
+void DocxAttributeOutput::WriteField_Impl(const SwField *const pField,
+ ww::eField const eType, const OUString& rFieldCmd, FieldFlags const nMode,
+ OUString const*const pBookmarkName)
+{
+ if (m_bPreventDoubleFieldsHandling)
+ return;
+
+ struct FieldInfos infos;
+ if (pField)
+ infos.pField = pField->CopyField();
+ infos.sCmd = rFieldCmd;
+ infos.eType = eType;
+ infos.bClose = bool(FieldFlags::Close & nMode);
+ infos.bSep = bool(FieldFlags::CmdEnd & nMode);
+ infos.bOpen = bool(FieldFlags::Start & nMode);
+ m_Fields.push_back( infos );
+
+ if (pBookmarkName)
+ {
+ m_sFieldBkm = *pBookmarkName;
+ }
+
+ if ( !pField )
+ return;
+
+ SwFieldIds nType = pField->GetTyp( )->Which( );
+ sal_uInt16 nSubType = pField->GetSubType();
+
+ // TODO Any other field types here ?
+ if ( ( nType == SwFieldIds::SetExp ) && ( nSubType & nsSwGetSetExpType::GSE_STRING ) )
+ {
+ const SwSetExpField *pSet = static_cast<const SwSetExpField*>( pField );
+ m_sFieldBkm = pSet->GetPar1( );
+ }
+ else if ( nType == SwFieldIds::Dropdown )
+ {
+ const SwDropDownField* pDropDown = static_cast<const SwDropDownField*>( pField );
+ m_sFieldBkm = pDropDown->GetName( );
+ }
+}
+
+void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark )
+{
+ if ( !m_Fields.empty() )
+ m_Fields.begin()->pFieldmark = &rFieldmark;
+}
+
+void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds, const SwRedlineData* pRedlineData )
+{
+ for ( const OUString & name : rStarts )
+ {
+ if (name.startsWith("permission-for-group:") ||
+ name.startsWith("permission-for-user:"))
+ {
+ m_rPermissionsStart.push_back(name);
+ }
+ else
+ {
+ m_rBookmarksStart.push_back(name);
+ m_pMoveRedlineData = const_cast<SwRedlineData*>(pRedlineData);
+ }
+ }
+ rStarts.clear();
+
+ for ( const OUString & name : rEnds )
+ {
+ if (name.startsWith("permission-for-group:") ||
+ name.startsWith("permission-for-user:"))
+ {
+ m_rPermissionsEnd.push_back(name);
+ }
+ else
+ {
+ m_rBookmarksEnd.push_back(name);
+ }
+ }
+ rEnds.clear();
+}
+
+void DocxAttributeOutput::WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds )
+{
+ for ( const OUString & name : rStarts )
+ {
+ if (name.startsWith("permission-for-group:") ||
+ name.startsWith("permission-for-user:"))
+ {
+ m_rPermissionsStart.push_back(name);
+ }
+ else
+ {
+ m_rFinalBookmarksStart.push_back(name);
+ }
+ }
+ rStarts.clear();
+
+ for ( const OUString & name : rEnds )
+ {
+ if (name.startsWith("permission-for-group:") ||
+ name.startsWith("permission-for-user:"))
+ {
+ m_rPermissionsEnd.push_back(name);
+ }
+ else
+ {
+ m_rFinalBookmarksEnd.push_back(name);
+ }
+ }
+ rEnds.clear();
+}
+
+void DocxAttributeOutput::WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts,
+ std::vector< OUString >& rEnds )
+{
+ m_rAnnotationMarksStart.insert(m_rAnnotationMarksStart.end(), rStarts.begin(), rStarts.end());
+ rStarts.clear();
+
+ m_rAnnotationMarksEnd.insert(m_rAnnotationMarksEnd.end(), rEnds.begin(), rEnds.end());
+ rEnds.clear();
+}
+
+void DocxAttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
+{
+ const SwEndNoteInfo& rInfo = rFootnote.IsEndNote()?
+ m_rExport.m_rDoc.GetEndNoteInfo(): m_rExport.m_rDoc.GetFootnoteInfo();
+
+ // footnote/endnote run properties
+ const SwCharFormat* pCharFormat = rInfo.GetAnchorCharFormat( m_rExport.m_rDoc );
+
+ OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
+
+ m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
+
+ // remember the footnote/endnote to
+ // 1) write the footnoteReference/endnoteReference in EndRunProperties()
+ // 2) be able to dump them all to footnotes.xml/endnotes.xml
+ if ( !rFootnote.IsEndNote() && m_rExport.m_rDoc.GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER )
+ m_pFootnotesList->add( rFootnote );
+ else
+ m_pEndnotesList->add( rFootnote );
+}
+
+void DocxAttributeOutput::FootnoteEndnoteReference()
+{
+ sal_Int32 nId;
+ const SwFormatFootnote *pFootnote = m_pFootnotesList->getCurrent( nId );
+ sal_Int32 nToken = XML_footnoteReference;
+
+ // both cannot be set at the same time - if they are, it's a bug
+ if ( !pFootnote )
+ {
+ pFootnote = m_pEndnotesList->getCurrent( nId );
+ nToken = XML_endnoteReference;
+ }
+
+ if ( !pFootnote )
+ return;
+
+ // write it
+ if ( pFootnote->GetNumStr().isEmpty() )
+ {
+ // autonumbered
+ m_pSerializer->singleElementNS(XML_w, nToken, FSNS(XML_w, XML_id), OString::number(nId));
+ }
+ else
+ {
+ // not autonumbered
+ m_pSerializer->singleElementNS( XML_w, nToken,
+ FSNS( XML_w, XML_customMarkFollows ), "1",
+ FSNS( XML_w, XML_id ), OString::number(nId) );
+
+ RunText( pFootnote->GetNumStr() );
+ }
+}
+
+static void WriteFootnoteSeparatorHeight(
+ ::sax_fastparser::FSHelperPtr const& pSerializer, SwTwips const nHeight)
+{
+ // try to get the height by setting font size of the paragraph
+ if (nHeight != 0)
+ {
+ pSerializer->startElementNS(XML_w, XML_pPr);
+ pSerializer->startElementNS(XML_w, XML_rPr);
+ pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val),
+ OString::number((nHeight + 5) / 10));
+ pSerializer->endElementNS(XML_w, XML_rPr);
+ pSerializer->endElementNS(XML_w, XML_pPr);
+ }
+}
+
+void DocxAttributeOutput::FootnotesEndnotes( bool bFootnotes )
+{
+ const FootnotesVector& rVector = bFootnotes? m_pFootnotesList->getVector(): m_pEndnotesList->getVector();
+
+ sal_Int32 nBody = bFootnotes? XML_footnotes: XML_endnotes;
+ sal_Int32 nItem = bFootnotes? XML_footnote: XML_endnote;
+
+ m_pSerializer->startElementNS( XML_w, nBody, m_rExport.MainXmlNamespaces() );
+
+ sal_Int32 nIndex = 0;
+
+ // separator
+ // note: can only be defined for the whole document, not per section
+ m_pSerializer->startElementNS( XML_w, nItem,
+ FSNS( XML_w, XML_id ), OString::number(nIndex++),
+ FSNS( XML_w, XML_type ), "separator" );
+ m_pSerializer->startElementNS(XML_w, XML_p);
+
+ bool bSeparator = true;
+ SwTwips nHeight(0);
+ if (bFootnotes)
+ {
+ const SwPageFootnoteInfo& rFootnoteInfo = m_rExport.m_rDoc.GetPageDesc(0).GetFootnoteInfo();
+ // Request separator only if both width and thickness are non-zero.
+ bSeparator = rFootnoteInfo.GetLineStyle() != SvxBorderLineStyle::NONE
+ && rFootnoteInfo.GetLineWidth() > 0
+ && double(rFootnoteInfo.GetWidth()) > 0;
+ nHeight = sw::FootnoteSeparatorHeight(rFootnoteInfo);
+ }
+
+ WriteFootnoteSeparatorHeight(m_pSerializer, nHeight);
+
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ if (bSeparator)
+ m_pSerializer->singleElementNS(XML_w, XML_separator);
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ m_pSerializer->endElementNS( XML_w, XML_p );
+ m_pSerializer->endElementNS( XML_w, nItem );
+
+ // separator
+ m_pSerializer->startElementNS( XML_w, nItem,
+ FSNS( XML_w, XML_id ), OString::number(nIndex++),
+ FSNS( XML_w, XML_type ), "continuationSeparator" );
+ m_pSerializer->startElementNS(XML_w, XML_p);
+
+ WriteFootnoteSeparatorHeight(m_pSerializer, nHeight);
+
+ m_pSerializer->startElementNS(XML_w, XML_r);
+ if (bSeparator)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_continuationSeparator);
+ }
+ m_pSerializer->endElementNS( XML_w, XML_r );
+ m_pSerializer->endElementNS( XML_w, XML_p );
+ m_pSerializer->endElementNS( XML_w, nItem );
+
+ // if new special ones are added, update also WriteFootnoteEndnotePr()
+
+ // footnotes/endnotes themselves
+ for ( const auto& rpItem : rVector )
+ {
+ m_footnoteEndnoteRefTag = bFootnotes ? XML_footnoteRef : XML_endnoteRef;
+ m_footnoteCustomLabel = rpItem->GetNumStr();
+
+ m_pSerializer->startElementNS(XML_w, nItem, FSNS(XML_w, XML_id), OString::number(nIndex));
+
+ const SwNodeIndex* pIndex = rpItem->GetTextFootnote()->GetStartNode();
+ m_rExport.WriteSpecialText( pIndex->GetIndex() + 1,
+ pIndex->GetNode().EndOfSectionIndex(),
+ bFootnotes? TXT_FTN: TXT_EDN );
+
+ m_pSerializer->endElementNS( XML_w, nItem );
+ ++nIndex;
+ }
+
+ m_pSerializer->endElementNS( XML_w, nBody );
+
+}
+
+void DocxAttributeOutput::WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag,
+ const SwEndNoteInfo& info, int listtag )
+{
+ fs->startElementNS(XML_w, tag);
+ OString aCustomFormat;
+ OString fmt = lcl_ConvertNumberingType(info.m_aFormat.GetNumberingType(), nullptr, aCustomFormat);
+ if (!fmt.isEmpty() && aCustomFormat.isEmpty())
+ fs->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), fmt);
+ if( info.m_nFootnoteOffset != 0 )
+ fs->singleElementNS( XML_w, XML_numStart, FSNS( XML_w, XML_val ),
+ OString::number(info.m_nFootnoteOffset + 1) );
+
+ const SwFootnoteInfo* pFootnoteInfo = dynamic_cast<const SwFootnoteInfo*>(&info);
+ if( pFootnoteInfo )
+ {
+ switch( pFootnoteInfo->m_eNum )
+ {
+ case FTNNUM_PAGE: fmt = "eachPage"_ostr; break;
+ case FTNNUM_CHAPTER: fmt = "eachSect"_ostr; break;
+ default: fmt.clear(); break;
+ }
+ if (!fmt.isEmpty())
+ fs->singleElementNS(XML_w, XML_numRestart, FSNS(XML_w, XML_val), fmt);
+ }
+
+ if( listtag != 0 ) // we are writing to settings.xml, write also special footnote/endnote list
+ { // there are currently only two hardcoded ones ( see FootnotesEndnotes())
+ fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "0");
+ fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "1");
+ }
+ fs->endElementNS( XML_w, tag );
+}
+
+void DocxAttributeOutput::SectFootnoteEndnotePr()
+{
+ if( HasFootnotes())
+ WriteFootnoteEndnotePr( m_pSerializer, XML_footnotePr, m_rExport.m_rDoc.GetFootnoteInfo(), 0 );
+ if( HasEndnotes())
+ WriteFootnoteEndnotePr( m_pSerializer, XML_endnotePr, m_rExport.m_rDoc.GetEndNoteInfo(), 0 );
+}
+
+void DocxAttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
+{
+ if ( nSpace < 0 )
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_lineRule ), "exact",
+ FSNS( XML_w, XML_line ), OString::number( -nSpace ) );
+ }
+ else if( nSpace > 0 && nMulti )
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_lineRule ), "auto",
+ FSNS( XML_w, XML_line ), OString::number( nSpace ) );
+ }
+ else
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_lineRule ), "atLeast",
+ FSNS( XML_w, XML_line ), OString::number( nSpace ) );
+ }
+}
+
+void DocxAttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
+{
+ const char *pAdjustString;
+
+ bool const bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+
+ const SfxItemSet* pItems = GetExport().GetCurItemSet();
+ const SvxFrameDirectionItem* rFrameDir = pItems?
+ pItems->GetItem( RES_FRAMEDIR ) : nullptr;
+
+ SvxFrameDirection nDir = SvxFrameDirection::Environment;
+ if( rFrameDir != nullptr )
+ nDir = rFrameDir->GetValue();
+ if ( nDir == SvxFrameDirection::Environment )
+ nDir = GetExport( ).GetDefaultFrameDirection( );
+ bool bRtl = ( nDir == SvxFrameDirection::Horizontal_RL_TB );
+
+ switch ( rAdjust.GetAdjust() )
+ {
+ case SvxAdjust::Left:
+ if ( bEcma )
+ {
+ if ( bRtl )
+ pAdjustString = "right";
+ else
+ pAdjustString = "left";
+ }
+ else if ( bRtl )
+ pAdjustString = "end";
+ else
+ pAdjustString = "start";
+ break;
+ case SvxAdjust::Right:
+ if ( bEcma )
+ {
+ if ( bRtl )
+ pAdjustString = "left";
+ else
+ pAdjustString = "right";
+ }
+ else if ( bRtl )
+ pAdjustString = "start";
+ else
+ pAdjustString = "end";
+ break;
+ case SvxAdjust::BlockLine:
+ case SvxAdjust::Block:
+ if (rAdjust.GetLastBlock() == SvxAdjust::Block)
+ pAdjustString = "distribute";
+ else
+ pAdjustString = "both";
+ break;
+ case SvxAdjust::Center:
+ pAdjustString = "center";
+ break;
+ default:
+ return; // not supported attribute
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pAdjustString);
+}
+
+void DocxAttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
+{
+ if (rSplit.GetValue())
+ m_pSerializer->singleElementNS(XML_w, XML_keepLines, FSNS(XML_w, XML_val), "false");
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_keepLines);
+}
+
+void DocxAttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
+{
+ if (rWidows.GetValue())
+ m_pSerializer->singleElementNS(XML_w, XML_widowControl);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_widowControl, FSNS(XML_w, XML_val), "false");
+}
+
+static void impl_WriteTabElement( FSHelperPtr const & pSerializer,
+ const SvxTabStop& rTab, tools::Long tabsOffset )
+{
+ rtl::Reference<FastAttributeList> pTabElementAttrList = FastSerializerHelper::createAttrList();
+
+ switch (rTab.GetAdjustment())
+ {
+ case SvxTabAdjust::Right:
+ pTabElementAttrList->add( FSNS( XML_w, XML_val ), "right" );
+ break;
+ case SvxTabAdjust::Decimal:
+ pTabElementAttrList->add( FSNS( XML_w, XML_val ), "decimal" );
+ break;
+ case SvxTabAdjust::Center:
+ pTabElementAttrList->add( FSNS( XML_w, XML_val ), "center" );
+ break;
+ case SvxTabAdjust::Default:
+ case SvxTabAdjust::Left:
+ default:
+ pTabElementAttrList->add( FSNS( XML_w, XML_val ), "left" );
+ break;
+ }
+
+ // Write position according to used offset of the whole paragraph.
+ // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins.
+ // But in ODT, zero position could be page margins or paragraph indent according to used settings.
+ // This is handled outside of this method and provided for us in tabsOffset parameter.
+ pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::number( rTab.GetTabPos() + tabsOffset ) );
+
+ sal_Unicode cFillChar = rTab.GetFill();
+
+ if ('.' == cFillChar )
+ pTabElementAttrList->add( FSNS( XML_w, XML_leader ), "dot" );
+ else if ( '-' == cFillChar )
+ pTabElementAttrList->add( FSNS( XML_w, XML_leader ), "hyphen" );
+ else if ( u'\x00B7' == cFillChar ) // middle dot
+ pTabElementAttrList->add( FSNS( XML_w, XML_leader ), "middleDot" );
+ else if ( '_' == cFillChar )
+ pTabElementAttrList->add( FSNS( XML_w, XML_leader ), "underscore" );
+ else
+ pTabElementAttrList->add( FSNS( XML_w, XML_leader ), "none" );
+
+ pSerializer->singleElementNS(XML_w, XML_tab, pTabElementAttrList);
+}
+
+void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop )
+{
+ const SvxTabStopItem* pInheritedTabs = nullptr;
+ if ( GetExport().m_pStyAttr )
+ pInheritedTabs = GetExport().m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
+ else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() )
+ pInheritedTabs = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
+ const sal_uInt16 nInheritedTabCount = pInheritedTabs ? pInheritedTabs->Count() : 0;
+ const sal_uInt16 nCount = rTabStop.Count();
+
+ // <w:tabs> must contain at least one <w:tab>, so don't write it empty
+ if ( !nCount && !nInheritedTabCount )
+ return;
+ if( nCount == 1 && rTabStop[ 0 ].GetAdjustment() == SvxTabAdjust::Default )
+ {
+ GetExport().setDefaultTabStop( rTabStop[ 0 ].GetTabPos());
+ return;
+ }
+
+ // do not output inherited tabs twice (inside styles and inside inline properties)
+ if ( nCount == nInheritedTabCount && nCount > 0 )
+ {
+ if ( *pInheritedTabs == rTabStop )
+ return;
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_tabs);
+
+ // Get offset for tabs
+ // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins.
+ // But in ODT, zero position could be page margins or paragraph indent according to used settings.
+ tools::Long tabsOffset = m_rExport.GetParaTabStopOffset();
+
+ // clear unused inherited tabs - otherwise the style will add them back in
+ sal_Int32 nCurrTab = 0;
+ for ( sal_uInt16 i = 0; i < nInheritedTabCount; ++i )
+ {
+ while ( nCurrTab < nCount && rTabStop[nCurrTab] < pInheritedTabs->At(i) )
+ ++nCurrTab;
+
+ if ( nCurrTab == nCount || pInheritedTabs->At(i) < rTabStop[nCurrTab] )
+ {
+ m_pSerializer->singleElementNS( XML_w, XML_tab,
+ FSNS( XML_w, XML_val ), "clear",
+ FSNS( XML_w, XML_pos ), OString::number(pInheritedTabs->At(i).GetTabPos()) );
+ }
+ }
+
+ for (sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default )
+ impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset );
+ else
+ GetExport().setDefaultTabStop( rTabStop[i].GetTabPos());
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_tabs );
+}
+
+void DocxAttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
+{
+ m_pSerializer->singleElementNS( XML_w, XML_suppressAutoHyphens,
+ FSNS( XML_w, XML_val ), OString::boolean( !rHyphenZone.IsHyphen() ) );
+}
+
+void DocxAttributeOutput::ParaNumRule_Impl( const SwTextNode* pTextNd, sal_Int32 nLvl, sal_Int32 nNumId )
+{
+ if ( USHRT_MAX == nNumId )
+ return;
+
+ // LibreOffice is not very flexible with "Outline Numbering" (aka "Outline" numbering style).
+ // Only ONE numbering rule ("Outline") can be associated with a style-assigned-listLevel,
+ // and no other style is able to inherit these numId/nLvl settings - only text nodes can.
+ // So listLevel only exists in paragraph properties EXCEPT for up to ten styles that have been
+ // assigned to one of these special Chapter Numbering listlevels (by default Heading 1-10).
+ const sal_Int32 nTableSize = m_rExport.m_pUsedNumTable ? m_rExport.m_pUsedNumTable->size() : 0;
+ const SwNumRule* pRule = nNumId > 0 && nNumId <= nTableSize ? (*m_rExport.m_pUsedNumTable)[nNumId-1] : nullptr;
+ const SwTextFormatColl* pColl = pTextNd ? pTextNd->GetTextColl() : nullptr;
+ // Do not duplicate numbering that is inherited from the (Chapter numbering) style
+ // (since on import we duplicate style numbering/listlevel to the paragraph).
+ if (pColl && pColl->IsAssignedToListLevelOfOutlineStyle()
+ && nLvl == pColl->GetAssignedOutlineStyleLevel() && pRule && pRule->IsOutlineRule())
+ {
+ // By definition of how LO is implemented, assignToListLevel is only possible
+ // when the style is also using OutlineRule for numbering. Adjust logic if that changes.
+ assert(pRule->GetName() == pColl->GetNumRule(true).GetValue());
+ return;
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_numPr);
+ m_pSerializer->singleElementNS(XML_w, XML_ilvl, FSNS(XML_w, XML_val), OString::number(nLvl));
+ m_pSerializer->singleElementNS(XML_w, XML_numId, FSNS(XML_w, XML_val), OString::number(nNumId));
+ m_pSerializer->endElementNS(XML_w, XML_numPr);
+}
+
+void DocxAttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
+{
+ m_pSerializer->singleElementNS( XML_w, XML_autoSpaceDE,
+ FSNS( XML_w, XML_val ), OString::boolean( rScriptSpace.GetValue() ) );
+}
+
+void DocxAttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
+{
+ m_pSerializer->singleElementNS( XML_w, XML_overflowPunct,
+ FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) );
+}
+
+void DocxAttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
+{
+ m_pSerializer->singleElementNS( XML_w, XML_kinsoku,
+ FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) );
+}
+
+void DocxAttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
+{
+ const char *pAlignString;
+
+ switch ( rAlign.GetValue() )
+ {
+ case SvxParaVertAlignItem::Align::Baseline:
+ pAlignString = "baseline";
+ break;
+ case SvxParaVertAlignItem::Align::Top:
+ pAlignString = "top";
+ break;
+ case SvxParaVertAlignItem::Align::Center:
+ pAlignString = "center";
+ break;
+ case SvxParaVertAlignItem::Align::Bottom:
+ pAlignString = "bottom";
+ break;
+ case SvxParaVertAlignItem::Align::Automatic:
+ pAlignString = "auto";
+ break;
+ default:
+ return; // not supported attribute
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_textAlignment, FSNS(XML_w, XML_val), pAlignString);
+}
+
+void DocxAttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
+{
+ m_pSerializer->singleElementNS( XML_w, XML_snapToGrid,
+ FSNS( XML_w, XML_val ), OString::boolean( rGrid.GetValue() ) );
+}
+
+void DocxAttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
+{
+ if (m_rExport.SdrExporter().getTextFrameSyntax() && m_rExport.SdrExporter().getFlyFrameSize())
+ {
+ const Size* pSize = m_rExport.SdrExporter().getFlyFrameSize();
+ m_rExport.SdrExporter().getTextFrameStyle().append(";width:" + OString::number(double(pSize->Width()) / 20));
+ m_rExport.SdrExporter().getTextFrameStyle().append("pt;height:" + OString::number(double(pSize->Height()) / 20) + "pt");
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ }
+ else if ( m_rExport.m_bOutFlyFrameAttrs )
+ {
+ if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed )
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(),
+ FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ) );
+
+ if ( rSize.GetHeight() )
+ {
+ std::string_view sRule( "exact" );
+ if ( rSize.GetHeightSizeType() == SwFrameSize::Minimum )
+ sRule = "atLeast";
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(),
+ FSNS( XML_w, XML_hRule ), sRule,
+ FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ) );
+ }
+ }
+ else if ( m_rExport.m_bOutPageDescs )
+ {
+ rtl::Reference<FastAttributeList> attrList = FastSerializerHelper::createAttrList( );
+ if ( m_rExport.m_pCurrentPageDesc->GetLandscape( ) )
+ attrList->add( FSNS( XML_w, XML_orient ), "landscape" );
+
+ attrList->add( FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ) );
+ attrList->add( FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ) );
+
+ m_pSerializer->singleElementNS( XML_w, XML_pgSz, attrList );
+ }
+}
+
+void DocxAttributeOutput::FormatPaperBin( const SvxPaperBinItem& )
+{
+ SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatPaperBin()" );
+}
+
+void DocxAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine)
+{
+ sal_Int32 const nFirstLineAdjustment(rFirstLine.GetTextFirstLineOffset());
+ if (nFirstLineAdjustment > 0)
+ {
+ AddToAttrList(m_pLRSpaceAttrList, FSNS(XML_w, XML_firstLine),
+ OString::number(nFirstLineAdjustment));
+ }
+ else
+ {
+ AddToAttrList(m_pLRSpaceAttrList, FSNS(XML_w, XML_hanging),
+ OString::number(- nFirstLineAdjustment));
+ }
+}
+
+void DocxAttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin)
+{
+ SvxTextLeftMarginItem const* pTextLeftMargin(&rTextLeftMargin);
+ ::std::optional<SvxTextLeftMarginItem> oCopy;
+ if (dynamic_cast<SwContentNode const*>(GetExport().m_pOutFormatNode) != nullptr)
+ {
+ auto pTextNd(static_cast<SwTextNode const*>(GetExport().m_pOutFormatNode));
+ // WW doesn't have a concept of a paragraph that's in a list but not
+ // counted in the list - see AttributeOutputBase::ParaNumRule()
+ // forcing non-existent numId="0" in this case.
+ // This means WW won't apply the indents from the numbering,
+ // so try to add them as paragraph properties here.
+ if (!pTextNd->IsCountedInList())
+ {
+ SfxItemSetFixed<RES_MARGIN_TEXTLEFT, RES_MARGIN_TEXTLEFT> temp(m_rExport.m_rDoc.GetAttrPool());
+ pTextNd->GetParaAttr(temp, 0, 0, false, true, true, nullptr);
+ if (auto *const pItem = temp.GetItem(RES_MARGIN_TEXTLEFT))
+ {
+ oCopy.emplace(*pItem);
+ pTextLeftMargin = &*oCopy;
+ }
+ }
+ }
+ bool const bEcma1st(m_rExport.GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION);
+ AddToAttrList(m_pLRSpaceAttrList,
+ FSNS(XML_w, (bEcma1st ? XML_left : XML_start)),
+ OString::number(pTextLeftMargin->GetTextLeft()));
+}
+
+void DocxAttributeOutput::FormatRightMargin(SvxRightMarginItem const& rRightMargin)
+{
+ // (paragraph case, this will be an else branch once others are converted)
+#if 0
+ else
+#endif
+ {
+ bool const bEcma1st(m_rExport.GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION);
+ AddToAttrList(m_pLRSpaceAttrList,
+ FSNS(XML_w, (bEcma1st ? XML_right : XML_end)),
+ OString::number(rRightMargin.GetRight()));
+ }
+}
+
+void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace )
+{
+ bool const bEcma = m_rExport.GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-left:" + OString::number(double(rLRSpace.GetLeft()) / 20) + "pt");
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-right:" + OString::number(double(rLRSpace.GetRight()) / 20) + "pt");
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ }
+ else if ( m_rExport.m_bOutFlyFrameAttrs )
+ {
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hSpace ),
+ OString::number(
+ ( rLRSpace.GetLeft() + rLRSpace.GetRight() ) / 2 ) );
+ }
+ else if ( m_rExport.m_bOutPageDescs )
+ {
+ m_pageMargins.nLeft = 0;
+ m_pageMargins.nRight = 0;
+
+ const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX);
+ if (pBoxItem)
+ {
+ m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
+ m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
+ }
+
+ m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
+ m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
+ sal_uInt16 nGutter = rLRSpace.GetGutterMargin();
+
+ AddToAttrList( m_pSectionSpacingAttrList,
+ FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nLeft ),
+ FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nRight ),
+ FSNS( XML_w, XML_gutter ), OString::number( nGutter ) );
+ }
+ else
+ {
+ // note: this is not possible for SwTextNode but is for EditEngine!
+ SvxLRSpaceItem const* pLRSpace(&rLRSpace);
+ ::std::optional<SvxLRSpaceItem> oLRSpace;
+ assert(dynamic_cast<SwContentNode const*>(GetExport().m_pOutFormatNode) == nullptr);
+ rtl::Reference<FastAttributeList> pLRSpaceAttrList = FastSerializerHelper::createAttrList();
+ if ((0 != pLRSpace->GetTextLeft()) || (pLRSpace->IsExplicitZeroMarginValLeft()))
+ {
+ pLRSpaceAttrList->add( FSNS(XML_w, (bEcma ? XML_left : XML_start)), OString::number(pLRSpace->GetTextLeft()) );
+ }
+ if ((0 != pLRSpace->GetRight()) || (pLRSpace->IsExplicitZeroMarginValRight()))
+ {
+ pLRSpaceAttrList->add( FSNS(XML_w, (bEcma ? XML_right : XML_end)), OString::number(pLRSpace->GetRight()) );
+ }
+ sal_Int32 const nFirstLineAdjustment = pLRSpace->GetTextFirstLineOffset();
+ if (nFirstLineAdjustment > 0)
+ pLRSpaceAttrList->add( FSNS( XML_w, XML_firstLine ), OString::number( nFirstLineAdjustment ) );
+ else
+ pLRSpaceAttrList->add( FSNS( XML_w, XML_hanging ), OString::number( - nFirstLineAdjustment ) );
+ m_pSerializer->singleElementNS( XML_w, XML_ind, pLRSpaceAttrList );
+ }
+}
+
+void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
+{
+
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-top:" + OString::number(double(rULSpace.GetUpper()) / 20) + "pt");
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-bottom:" + OString::number(double(rULSpace.GetLower()) / 20) + "pt");
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ }
+ else if ( m_rExport.m_bOutFlyFrameAttrs )
+ {
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vSpace ),
+ OString::number(
+ ( rULSpace.GetLower() + rULSpace.GetUpper() ) / 2 ) );
+ }
+ else if (m_rExport.m_bOutPageDescs )
+ {
+ OSL_ENSURE( m_rExport.GetCurItemSet(), "Impossible" );
+ if ( !m_rExport.GetCurItemSet() )
+ return;
+
+ HdFtDistanceGlue aDistances( *m_rExport.GetCurItemSet() );
+
+ sal_Int32 nHeader = 0;
+ if ( aDistances.HasHeader() )
+ nHeader = sal_Int32( aDistances.m_DyaHdrTop );
+ else if (m_rExport.m_pFirstPageFormat)
+ {
+ HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet());
+ if (aFirstPageDistances.HasHeader())
+ {
+ // The follow page style has no header, but the first page style has. In Word terms,
+ // this means that the header margin of "the" section is coming from the first page
+ // style.
+ nHeader = sal_Int32(aFirstPageDistances.m_DyaHdrTop);
+ }
+ }
+
+ // Page top
+ m_pageMargins.nTop = aDistances.m_DyaTop;
+
+ sal_Int32 nFooter = 0;
+ if ( aDistances.HasFooter() )
+ nFooter = sal_Int32( aDistances.m_DyaHdrBottom );
+ else if (m_rExport.m_pFirstPageFormat)
+ {
+ HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet());
+ if (aFirstPageDistances.HasFooter())
+ {
+ // The follow page style has no footer, but the first page style has. In Word terms,
+ // this means that the footer margin of "the" section is coming from the first page
+ // style.
+ nFooter = sal_Int32(aFirstPageDistances.m_DyaHdrBottom);
+ }
+ }
+
+ // Page Bottom
+ m_pageMargins.nBottom = aDistances.m_DyaBottom;
+
+ AddToAttrList( m_pSectionSpacingAttrList,
+ FSNS( XML_w, XML_header ), OString::number( nHeader ),
+ FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nTop ),
+ FSNS( XML_w, XML_footer ), OString::number( nFooter ),
+ FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nBottom ) );
+ }
+ else
+ {
+ SAL_INFO("sw.ww8", "DocxAttributeOutput::FormatULSpace: setting spacing" << rULSpace.GetUpper() );
+ // check if before auto spacing was set during import and spacing we get from actual object is same
+ // that we set in import. If yes just write beforeAutoSpacing tag.
+ if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper())
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_beforeAutospacing ), "1" );
+ }
+ else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1)
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_beforeAutospacing ), "0",
+ FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ) );
+ }
+ else
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ) );
+ }
+ m_bParaBeforeAutoSpacing = false;
+ // check if after auto spacing was set during import and spacing we get from actual object is same
+ // that we set in import. If yes just write afterAutoSpacing tag.
+ if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower())
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_afterAutospacing ), "1" );
+ }
+ else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1)
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_afterAutospacing ), "0",
+ FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()) );
+ }
+ else
+ {
+ AddToAttrList( m_pParagraphSpacingAttrList,
+ FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()) );
+ }
+ m_bParaAfterAutoSpacing = false;
+
+ if (rULSpace.GetContext())
+ m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing);
+ else
+ {
+ // Write out Contextual Spacing = false if it would have inherited a true.
+ const SvxULSpaceItem* pInherited = nullptr;
+ if (auto pNd = dynamic_cast<const SwContentNode*>(m_rExport.m_pOutFormatNode)) //paragraph
+ pInherited = &static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetULSpace();
+ else if (m_rExport.m_bStyDef && m_rExport.m_pCurrentStyle && m_rExport.m_pCurrentStyle->DerivedFrom()) //style
+ pInherited = &m_rExport.m_pCurrentStyle->DerivedFrom()->GetULSpace();
+
+ if (pInherited && pInherited->GetContext())
+ m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing, FSNS(XML_w, XML_val), "false");
+ }
+ }
+}
+
+namespace docx {
+
+rtl::Reference<FastAttributeList> SurroundToVMLWrap(SwFormatSurround const& rSurround)
+{
+ std::string_view sType;
+ std::string_view sSide;
+ switch (rSurround.GetSurround())
+ {
+ case css::text::WrapTextMode_NONE:
+ sType = "topAndBottom";
+ break;
+ case css::text::WrapTextMode_PARALLEL:
+ sType = "square";
+ break;
+ case css::text::WrapTextMode_DYNAMIC:
+ sType = "square";
+ sSide = "largest";
+ break;
+ case css::text::WrapTextMode_LEFT:
+ sType = "square";
+ sSide = "left";
+ break;
+ case css::text::WrapTextMode_RIGHT:
+ sType = "square";
+ sSide = "right";
+ break;
+ case css::text::WrapTextMode_THROUGH:
+ /* empty type and side means through */
+ default:
+ sType = "none";
+ break;
+ }
+ rtl::Reference<FastAttributeList> pAttrList;
+ if (!sType.empty())
+ DocxAttributeOutput::AddToAttrList(pAttrList, XML_type, sType);
+ if (!sSide.empty())
+ DocxAttributeOutput::AddToAttrList(pAttrList, XML_side, sSide);
+ return pAttrList;
+}
+
+} // namespace docx
+
+void DocxAttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
+{
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ rtl::Reference<FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround));
+ if (pAttrList)
+ {
+ m_rExport.SdrExporter().setFlyWrapAttrList(pAttrList);
+ }
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ }
+ else if ( m_rExport.m_bOutFlyFrameAttrs )
+ {
+ std::string_view sWrap;
+ switch ( rSurround.GetSurround( ) )
+ {
+ case css::text::WrapTextMode_NONE:
+ sWrap = "none";
+ break;
+ case css::text::WrapTextMode_THROUGH:
+ sWrap = "through";
+ break;
+ case css::text::WrapTextMode_DYNAMIC:
+ case css::text::WrapTextMode_PARALLEL:
+ case css::text::WrapTextMode_LEFT:
+ case css::text::WrapTextMode_RIGHT:
+ default:
+ sWrap = "around";
+ }
+
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_wrap ), sWrap );
+ }
+}
+
+void DocxAttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
+{
+ OString sAlign = convertToOOXMLVertOrient( rFlyVert.GetVertOrient() );
+ OString sVAnchor = convertToOOXMLVertOrientRel( rFlyVert.GetRelationOrient() );
+
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ m_rExport.SdrExporter().getTextFrameStyle().append(";margin-top:" + OString::number(double(rFlyVert.GetPos()) / 20) + "pt");
+ if ( !sAlign.isEmpty() )
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical:" + sAlign);
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical-relative:" + sVAnchor);
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ }
+ else if ( m_rExport.m_bOutFlyFrameAttrs )
+ {
+ if ( !sAlign.isEmpty() )
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_yAlign ), sAlign );
+ else
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_y ),
+ OString::number( rFlyVert.GetPos() ) );
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vAnchor ), sVAnchor );
+ }
+}
+
+void DocxAttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
+{
+ OString sAlign = convertToOOXMLHoriOrient( rFlyHori.GetHoriOrient(), rFlyHori.IsPosToggle() );
+ OString sHAnchor = convertToOOXMLHoriOrientRel( rFlyHori.GetRelationOrient() );
+
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ m_rExport.SdrExporter().getTextFrameStyle().append(";margin-left:" + OString::number(double(rFlyHori.GetPos()) / 20) + "pt");
+ if ( !sAlign.isEmpty() )
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal:" + sAlign);
+ m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal-relative:" + sHAnchor);
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ }
+ else if ( m_rExport.m_bOutFlyFrameAttrs )
+ {
+ if ( !sAlign.isEmpty() )
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_xAlign ), sAlign );
+ else
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_x ),
+ OString::number( rFlyHori.GetPos() ) );
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hAnchor ), sHAnchor );
+ }
+}
+
+void DocxAttributeOutput::FormatAnchor( const SwFormatAnchor& )
+{
+ // Fly frames: anchors here aren't matching the anchors in docx
+}
+
+static std::optional<sal_Int32> lcl_getDmlAlpha(const SvxBrushItem& rBrush)
+{
+ std::optional<sal_Int32> oRet;
+ sal_Int32 nTransparency = 255 - rBrush.GetColor().GetAlpha();
+ if (nTransparency)
+ {
+ // Convert transparency to percent
+ sal_Int8 nTransparencyPercent = SvxBrushItem::TransparencyToPercent(nTransparency);
+
+ // Calculate alpha value
+ // Consider oox/source/drawingml/color.cxx : getTransparency() function.
+ sal_Int32 nAlpha = ::oox::drawingml::MAX_PERCENT - ( ::oox::drawingml::PER_PERCENT * nTransparencyPercent );
+ oRet = nAlpha;
+ }
+ return oRet;
+}
+
+void DocxAttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
+{
+ const Color aColor = rBrush.GetColor();
+ model::ComplexColor const& rComplexColor = rBrush.getComplexColor();
+ OString sColor = msfilter::util::ConvertColor( aColor.GetRGBColor() );
+ std::optional<sal_Int32> oAlpha = lcl_getDmlAlpha(rBrush);
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ // Handle 'Opacity'
+ if (oAlpha)
+ {
+ // Calculate opacity value
+ // Consider oox/source/vml/vmlformatting.cxx : decodeColor() function.
+ double fOpacity = static_cast<double>(*oAlpha) * 65535 / ::oox::drawingml::MAX_PERCENT;
+
+ AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_opacity, OString::number(fOpacity) + "f" );
+ }
+
+ AddToAttrList(m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, "#" + sColor );
+ lclAddThemeFillColorAttributes(m_rExport.SdrExporter().getFlyAttrList(), rComplexColor);
+ }
+ else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ bool bImageBackground = false;
+ const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE);
+ if (pItem)
+ {
+ const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem);
+ if(pFillStyle->GetValue() == drawing::FillStyle_BITMAP)
+ {
+ bImageBackground = true;
+ }
+ }
+ if (!bImageBackground)
+ {
+ m_pSerializer->startElementNS(XML_a, XML_solidFill);
+ m_pSerializer->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
+ if (oAlpha)
+ m_pSerializer->singleElementNS(XML_a, XML_alpha,
+ XML_val, OString::number(*oAlpha));
+ m_pSerializer->endElementNS(XML_a, XML_srgbClr);
+ m_pSerializer->endElementNS(XML_a, XML_solidFill);
+ }
+ }
+ else if ( !m_rExport.m_bOutPageDescs )
+ {
+ // compare fill color with the original fill color
+ OString sOriginalFill = OUStringToOString(
+ m_sOriginalBackgroundColor, RTL_TEXTENCODING_UTF8 );
+
+ if ( aColor == COL_AUTO )
+ sColor = "auto"_ostr;
+
+ if( !m_pBackgroundAttrList.is() )
+ {
+ m_pBackgroundAttrList = FastSerializerHelper::createAttrList();
+ m_pBackgroundAttrList->add(FSNS(XML_w, XML_fill), sColor);
+ m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" );
+ }
+ else if ( sOriginalFill != sColor )
+ {
+ // fill was modified during edition, theme fill attribute must be dropped
+ m_pBackgroundAttrList = FastSerializerHelper::createAttrList();
+ m_pBackgroundAttrList->add(FSNS(XML_w, XML_fill), sColor);
+ m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" );
+ }
+ m_sOriginalBackgroundColor.clear();
+ }
+}
+
+void DocxAttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
+{
+ if (!m_bIgnoreNextFill)
+ m_oFillStyle = rFillStyle.GetValue();
+ else
+ m_bIgnoreNextFill = false;
+
+ // Don't round-trip grabbag OriginalBackground if the background has been cleared.
+ if ( m_pBackgroundAttrList.is() && m_sOriginalBackgroundColor != "auto" && rFillStyle.GetValue() == drawing::FillStyle_NONE )
+ m_pBackgroundAttrList.clear();
+}
+
+void DocxAttributeOutput::FormatFillGradient( const XFillGradientItem& rFillGradient )
+{
+ if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && !m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_type, "gradient" );
+
+ const basegfx::BGradient& rGradient = rFillGradient.GetGradientValue();
+ OString sStartColor = msfilter::util::ConvertColor(Color(rGradient.GetColorStops().front().getStopColor()));
+ OString sEndColor = msfilter::util::ConvertColor(Color(rGradient.GetColorStops().back().getStopColor()));
+
+ // Calculate the angle that was originally in the imported DOCX file
+ // (reverse calculate the angle that was converted in the file
+ // /oox/source/vml/vmlformatting.cxx :: FillModel::pushToPropMap
+ // and also in
+ // /oox/source/drawingml/fillproperties.cxx :: FillProperties::pushToPropMap
+ sal_Int32 nReverseAngle = toDegrees(4500_deg10 - rGradient.GetAngle());
+ nReverseAngle = (270 - nReverseAngle) % 360;
+ if (nReverseAngle != 0)
+ AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(),
+ XML_angle, OString::number( nReverseAngle ) );
+
+ OString sColor1 = sStartColor;
+ OString sColor2 = sEndColor;
+
+ switch (rGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_AXIAL:
+ AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_focus, "50%" );
+ // If it is an 'axial' gradient - swap the colors
+ // (because in the import process they were imported swapped)
+ sColor1 = sEndColor;
+ sColor2 = sStartColor;
+ break;
+ case css::awt::GradientStyle_LINEAR: break;
+ case css::awt::GradientStyle_RADIAL: break;
+ case css::awt::GradientStyle_ELLIPTICAL: break;
+ case css::awt::GradientStyle_SQUARE: break;
+ case css::awt::GradientStyle_RECT: break;
+ default:
+ break;
+ }
+
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, "#" + sColor1 );
+ AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_color2, "#" + sColor2 );
+ }
+ else if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ SwFrameFormat & rFormat(
+ const_cast<SwFrameFormat&>(m_rExport.m_pParentFrame->GetFrameFormat()));
+ rtl::Reference<SwXTextFrame> const xPropertySet =
+ SwXTextFrame::CreateXTextFrame(*rFormat.GetDoc(), &rFormat);
+ m_rDrawingML.SetFS(m_pSerializer);
+ m_rDrawingML.WriteGradientFill(uno::Reference<beans::XPropertySet>(static_cast<SwXFrame*>(xPropertySet.get())));
+ }
+ m_oFillStyle.reset();
+}
+
+void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
+{
+ if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ // ugh, exporting fill here is quite some hack... this OutputItemSet abstraction is quite leaky
+ // <a:gradFill> should be before <a:ln>.
+ const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE);
+ if (pItem)
+ {
+ const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem);
+ FormatFillStyle(*pFillStyle);
+ if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_BITMAP)
+ {
+ const SdrObject* pSdrObj = m_rExport.m_pParentFrame->GetFrameFormat().FindRealSdrObject();
+ if (pSdrObj)
+ {
+ uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySet > xPropertySet( xShape, uno::UNO_QUERY );
+ m_rDrawingML.SetFS(m_pSerializer);
+ m_rDrawingML.WriteBlipFill(xPropertySet, "BackGraphic");
+ }
+ }
+ }
+
+ pItem = GetExport().HasItem(XATTR_FILLGRADIENT);
+ if (pItem)
+ {
+ const XFillGradientItem* pFillGradient = static_cast<const XFillGradientItem*>(pItem);
+ FormatFillGradient(*pFillGradient);
+ }
+ m_bIgnoreNextFill = true;
+ }
+ if (m_rExport.SdrExporter().getTextFrameSyntax() || m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ const SvxBorderLine* pLeft = rBox.GetLeft( );
+ const SvxBorderLine* pTop = rBox.GetTop( );
+ const SvxBorderLine* pRight = rBox.GetRight( );
+ const SvxBorderLine* pBottom = rBox.GetBottom( );
+
+ if (pLeft && pRight && pTop && pBottom &&
+ *pLeft == *pRight && *pLeft == *pTop && *pLeft == *pBottom)
+ {
+ // Check border style
+ SvxBorderLineStyle eBorderStyle = pTop->GetBorderLineStyle();
+ if (eBorderStyle == SvxBorderLineStyle::NONE)
+ {
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(),
+ XML_stroked, "f", XML_strokeweight, "0pt" );
+ }
+ }
+ else
+ {
+ OString sColor(msfilter::util::ConvertColor(pTop->GetColor()));
+ double const fConverted(editeng::ConvertBorderWidthToWord(pTop->GetBorderLineStyle(), pTop->GetWidth()));
+
+ if (m_rExport.SdrExporter().getTextFrameSyntax())
+ {
+ sal_Int32 nWidth = sal_Int32(fConverted / 20);
+ AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(),
+ XML_strokecolor, "#" + sColor,
+ XML_strokeweight, OString::number(nWidth) + "pt" );
+ if( SvxBorderLineStyle::DASHED == pTop->GetBorderLineStyle() ) // Line Style is Dash type
+ AddToAttrList( m_rExport.SdrExporter().getDashLineStyle(),
+ XML_dashstyle, "dash" );
+ }
+ else
+ m_rExport.SdrExporter().writeBoxItemLine(rBox);
+ }
+ }
+
+ if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
+ {
+ m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_lIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::LEFT))));
+ m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_tIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::TOP))));
+ m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_rIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::RIGHT))));
+ m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_bIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::BOTTOM))));
+ return;
+ }
+
+ // v:textbox's inset attribute: inner margin values for textbox text - write only non-default values
+ double fDistanceLeftTwips = double(rBox.GetDistance(SvxBoxItemLine::LEFT));
+ double fDistanceTopTwips = double(rBox.GetDistance(SvxBoxItemLine::TOP));
+ double fDistanceRightTwips = double(rBox.GetDistance(SvxBoxItemLine::RIGHT));
+ double fDistanceBottomTwips = double(rBox.GetDistance(SvxBoxItemLine::BOTTOM));
+
+ // Convert 'TWIPS' to 'INCH' (because in Word the default values are in Inches)
+ double fDistanceLeftInch = o3tl::convert(fDistanceLeftTwips, o3tl::Length::twip, o3tl::Length::in);
+ double fDistanceTopInch = o3tl::convert(fDistanceTopTwips, o3tl::Length::twip, o3tl::Length::in);
+ double fDistanceRightInch = o3tl::convert(fDistanceRightTwips, o3tl::Length::twip, o3tl::Length::in);
+ double fDistanceBottomInch = o3tl::convert(fDistanceBottomTwips, o3tl::Length::twip, o3tl::Length::in);
+
+ // This code will write ONLY the non-default values. The values are in 'left','top','right','bottom' order.
+ // so 'bottom' is checked if it is default and if it is non-default - all the values will be written
+ // otherwise - 'right' is checked if it is default and if it is non-default - all the values except for 'bottom' will be written
+ // and so on.
+ OStringBuffer aInset;
+ if(!aInset.isEmpty() || fDistanceBottomInch != 0.05)
+ aInset.insert(0, Concat2View("," + OString::number(fDistanceBottomInch) + "in"));
+
+ if(!aInset.isEmpty() || fDistanceRightInch != 0.1)
+ aInset.insert(0, Concat2View("," + OString::number(fDistanceRightInch) + "in"));
+
+ if(!aInset.isEmpty() || fDistanceTopInch != 0.05)
+ aInset.insert(0, Concat2View("," + OString::number(fDistanceTopInch) + "in"));
+
+ if(!aInset.isEmpty() || fDistanceLeftInch != 0.1)
+ aInset.insert(0, Concat2View(OString::number(fDistanceLeftInch) + "in"));
+
+ if (!aInset.isEmpty())
+ m_rExport.SdrExporter().getTextboxAttrList()->add(XML_inset, aInset);
+
+ return;
+ }
+
+ OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
+ // Check if there is a shadow item
+ const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
+ if ( pItem )
+ {
+ const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
+ aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation();
+ }
+
+ if ( m_bOpenedSectPr && !GetWritingHeaderFooter())
+ return;
+
+ // Not inside a section
+
+ // Open the paragraph's borders tag
+ m_pSerializer->startElementNS(XML_w, XML_pBdr);
+
+ std::map<SvxBoxItemLine, css::table::BorderLine2> aStyleBorders;
+ const SvxBoxItem* pInherited = nullptr;
+ if ( GetExport().m_pStyAttr )
+ pInherited = GetExport().m_pStyAttr->GetItem<SvxBoxItem>(RES_BOX);
+ else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() )
+ pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_BOX);
+
+ if ( pInherited )
+ {
+ aStyleBorders[ SvxBoxItemLine::TOP ] = SvxBoxItem::SvxLineToLine(pInherited->GetTop(), /*bConvert=*/false);
+ aStyleBorders[ SvxBoxItemLine::BOTTOM ] = SvxBoxItem::SvxLineToLine(pInherited->GetBottom(), false);
+ aStyleBorders[ SvxBoxItemLine::LEFT ] = SvxBoxItem::SvxLineToLine(pInherited->GetLeft(), false);
+ aStyleBorders[ SvxBoxItemLine::RIGHT ] = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false);
+ }
+ bool bUseFrame = m_aFramePr.UseFrameBorders(!m_xTableWrt ? -1 : m_tableReference.m_nTableDepth);
+ impl_borders(m_pSerializer, rBox, aOutputBorderOptions, aStyleBorders,
+ bUseFrame ? m_aFramePr.Frame() : nullptr);
+
+ // Close the paragraph's borders tag
+ m_pSerializer->endElementNS( XML_w, XML_pBdr );
+
+ m_aFramePr.SetUseFrameBorders(false);
+}
+
+void DocxAttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, SwTwips nPageSize )
+{
+ // Get the columns attributes
+ rtl::Reference<FastAttributeList> pColsAttrList = FastSerializerHelper::createAttrList();
+
+ pColsAttrList->add( FSNS( XML_w, XML_num ), OString::number( nCols ) );
+
+ std::string_view pEquals = "false";
+ if ( bEven )
+ {
+ sal_uInt16 nWidth = rCol.GetGutterWidth( true );
+ pColsAttrList->add( FSNS( XML_w, XML_space ), OString::number( nWidth ) );
+
+ pEquals = "true";
+ }
+
+ pColsAttrList->add( FSNS( XML_w, XML_equalWidth ), pEquals );
+
+ bool bHasSep = (COLADJ_NONE != rCol.GetLineAdj());
+
+ pColsAttrList->add( FSNS( XML_w, XML_sep ), OString::boolean( bHasSep ) );
+
+ // Write the element
+ m_pSerializer->startElementNS( XML_w, XML_cols, pColsAttrList );
+
+ // Write the columns width if non-equals
+ const SwColumns & rColumns = rCol.GetColumns( );
+ if ( !bEven )
+ {
+ for ( sal_uInt16 n = 0; n < nCols; ++n )
+ {
+ rtl::Reference<FastAttributeList> pColAttrList = FastSerializerHelper::createAttrList();
+ sal_uInt16 nWidth = rCol.CalcPrtColWidth( n, o3tl::narrowing<sal_uInt16>(nPageSize) );
+ pColAttrList->add( FSNS( XML_w, XML_w ), OString::number( nWidth ) );
+
+ if ( n + 1 != nCols )
+ {
+ sal_uInt16 nSpacing = rColumns[n].GetRight( ) + rColumns[n + 1].GetLeft( );
+ pColAttrList->add( FSNS( XML_w, XML_space ), OString::number( nSpacing ) );
+ }
+
+ m_pSerializer->singleElementNS( XML_w, XML_col, pColAttrList );
+ }
+ }
+
+ m_pSerializer->endElementNS( XML_w, XML_cols );
+}
+
+void DocxAttributeOutput::FormatKeep( const SvxFormatKeepItem& rItem )
+{
+ m_pSerializer->singleElementNS( XML_w, XML_keepNext,
+ FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) );
+}
+
+void DocxAttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
+{
+ rtl::Reference<FastAttributeList> pGridAttrList = FastSerializerHelper::createAttrList();
+
+ std::string_view sGridType;
+ switch ( rGrid.GetGridType( ) )
+ {
+ default:
+ case GRID_NONE:
+ sGridType = "default";
+ break;
+ case GRID_LINES_ONLY:
+ sGridType = "lines";
+ break;
+ case GRID_LINES_CHARS:
+ if ( rGrid.IsSnapToChars( ) )
+ sGridType = "snapToChars";
+ else
+ sGridType = "linesAndChars";
+ break;
+ }
+ pGridAttrList->add(FSNS(XML_w, XML_type), sGridType);
+
+ sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
+ pGridAttrList->add( FSNS( XML_w, XML_linePitch ),
+ OString::number( nHeight ) );
+
+ pGridAttrList->add( FSNS( XML_w, XML_charSpace ),
+ OString::number( GridCharacterPitch( rGrid ) ) );
+
+ m_pSerializer->singleElementNS( XML_w, XML_docGrid, pGridAttrList );
+}
+
+void DocxAttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
+{
+ if ( !rNumbering.IsCount( ) )
+ m_pSerializer->singleElementNS(XML_w, XML_suppressLineNumbers);
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_suppressLineNumbers, FSNS(XML_w, XML_val), "0");
+}
+
+void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
+{
+ OString sTextFlow;
+ bool bBiDi = false;
+ SvxFrameDirection nDir = rDirection.GetValue();
+
+ if ( nDir == SvxFrameDirection::Environment )
+ nDir = GetExport( ).GetDefaultFrameDirection( );
+
+ switch ( nDir )
+ {
+ default:
+ case SvxFrameDirection::Horizontal_LR_TB:
+ sTextFlow = "lrTb"_ostr;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ sTextFlow = "lrTb"_ostr;
+ bBiDi = true;
+ break;
+ case SvxFrameDirection::Vertical_LR_TB: // ~ vert="mongolianVert"
+ sTextFlow = "tbLrV"_ostr;
+ break;
+ case SvxFrameDirection::Vertical_RL_TB: // ~ vert="eaVert"
+ sTextFlow = "tbRl"_ostr;
+ break;
+ case SvxFrameDirection::Vertical_LR_BT: // ~ vert="vert270"
+ sTextFlow = "btLr"_ostr;
+ break;
+ case SvxFrameDirection::Vertical_RL_TB90: // ~ vert="vert"
+ sTextFlow = "tbRlV"_ostr;
+ break;
+ }
+
+ if ( m_rExport.m_bOutPageDescs )
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), sTextFlow);
+ if ( bBiDi )
+ m_pSerializer->singleElementNS(XML_w, XML_bidi);
+ }
+ else if ( !m_rExport.m_bOutFlyFrameAttrs )
+ {
+ if ( bBiDi )
+ m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "1");
+ else
+ m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "0");
+ m_aFramePr.SetUseFrameTextDirection(false);
+ }
+}
+
+void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
+{
+ const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
+ for ( const auto & rGrabBagElement : rMap )
+ {
+ if (rGrabBagElement.first == "MirrorIndents")
+ m_pSerializer->singleElementNS(XML_w, XML_mirrorIndents);
+ else if (rGrabBagElement.first == "ParaTopMarginBeforeAutoSpacing")
+ {
+ m_bParaBeforeAutoSpacing = true;
+ // get fixed value which was set during import
+ rGrabBagElement.second >>= m_nParaBeforeSpacing;
+ m_nParaBeforeSpacing = o3tl::toTwips(m_nParaBeforeSpacing, o3tl::Length::mm100);
+ SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaBeforeSpacing);
+ }
+ else if (rGrabBagElement.first == "ParaBottomMarginAfterAutoSpacing")
+ {
+ m_bParaAfterAutoSpacing = true;
+ // get fixed value which was set during import
+ rGrabBagElement.second >>= m_nParaAfterSpacing;
+ m_nParaAfterSpacing = o3tl::toTwips(m_nParaAfterSpacing, o3tl::Length::mm100);
+ SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaAfterSpacing);
+ }
+ else if (rGrabBagElement.first == "CharThemeFill")
+ {
+ uno::Sequence<beans::PropertyValue> aGrabBagSeq;
+ rGrabBagElement.second >>= aGrabBagSeq;
+
+ for (const auto& rProp : std::as_const(aGrabBagSeq))
+ {
+ OUString sVal = rProp.Value.get<OUString>();
+
+ if (sVal.isEmpty())
+ continue;
+
+ if (rProp.Name == "val")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_val), sVal);
+ else if (rProp.Name == "color")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_color), sVal);
+ else if (rProp.Name == "themeColor")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeColor), sVal);
+ else if (rProp.Name == "themeTint")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeTint), sVal);
+ else if (rProp.Name == "themeShade")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeShade), sVal);
+ else if (rProp.Name == "fill")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_fill), sVal);
+ else if (rProp.Name == "themeFill")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFill), sVal);
+ else if (rProp.Name == "themeFillTint")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillTint), sVal);
+ else if (rProp.Name == "themeFillShade")
+ AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillShade), sVal);
+ else if (rProp.Name == "originalColor")
+ rProp.Value >>= m_sOriginalBackgroundColor;
+ }
+ }
+ else if (rGrabBagElement.first == "SdtPr")
+ {
+ const uno::Sequence<beans::PropertyValue> aGrabBagSdt =
+ rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
+ m_aParagraphSdt.GetSdtParamsFromGrabBag(aGrabBagSdt);
+ m_aStartedParagraphSdtPrAlias = m_aParagraphSdt.m_aAlias;
+ }
+ else if (rGrabBagElement.first == "ParaCnfStyle")
+ {
+ uno::Sequence<beans::PropertyValue> aAttributes = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
+ m_pTableStyleExport->CnfStyle(aAttributes);
+ }
+ else if (rGrabBagElement.first == "ParaSdtEndBefore")
+ {
+ // Handled already in StartParagraph().
+ }
+ else
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled grab bag property " << rGrabBagElement.first );
+ }
+}
+
+void DocxAttributeOutput::CharGrabBag( const SfxGrabBagItem& rItem )
+{
+ if (m_bPreventDoubleFieldsHandling)
+ return;
+
+ const std::map< OUString, css::uno::Any >& rMap = rItem.GetGrabBag();
+
+ // get original values of theme-derived properties to check if they have changed during the edition
+ bool bWriteCSTheme = true;
+ bool bWriteAsciiTheme = true;
+ bool bWriteEastAsiaTheme = true;
+ OUString sOriginalValue;
+ for ( const auto & rGrabBagElement : rMap )
+ {
+ if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameCs" )
+ {
+ if ( rGrabBagElement.second >>= sOriginalValue )
+ bWriteCSTheme =
+ ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_cs ) ) == sOriginalValue );
+ }
+ else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameAscii" )
+ {
+ if ( rGrabBagElement.second >>= sOriginalValue )
+ bWriteAsciiTheme =
+ ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_ascii ) ) == sOriginalValue );
+ }
+ else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameEastAsia" )
+ {
+ if ( rGrabBagElement.second >>= sOriginalValue )
+ bWriteEastAsiaTheme =
+ ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_eastAsia ) ) == sOriginalValue );
+ }
+ }
+
+ // save theme attributes back to the run properties
+ OUString str;
+ for ( const auto & rGrabBagElement : rMap )
+ {
+ if ( rGrabBagElement.first == "CharThemeNameAscii" && bWriteAsciiTheme )
+ {
+ rGrabBagElement.second >>= str;
+ AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_asciiTheme ), str );
+ }
+ else if ( rGrabBagElement.first == "CharThemeNameCs" && bWriteCSTheme )
+ {
+ rGrabBagElement.second >>= str;
+ AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cstheme ), str );
+ }
+ else if ( rGrabBagElement.first == "CharThemeNameEastAsia" && bWriteEastAsiaTheme )
+ {
+ rGrabBagElement.second >>= str;
+ AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsiaTheme ), str );
+ }
+ else if ( rGrabBagElement.first == "CharThemeNameHAnsi" && bWriteAsciiTheme )
+ // this is not a mistake: in LibO we don't directly support the hAnsi family
+ // of attributes so we save the same value from ascii attributes instead
+ {
+ rGrabBagElement.second >>= str;
+ AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_hAnsiTheme ), str );
+ }
+ else if( rGrabBagElement.first == "CharThemeFontNameCs" ||
+ rGrabBagElement.first == "CharThemeFontNameAscii" ||
+ rGrabBagElement.first == "CharThemeFontNameEastAsia" ||
+ rGrabBagElement.first == "CharThemeOriginalColor" )
+ {
+ // just skip these, they were processed before
+ }
+ else if(rGrabBagElement.first == "CharGlowTextEffect" ||
+ rGrabBagElement.first == "CharShadowTextEffect" ||
+ rGrabBagElement.first == "CharReflectionTextEffect" ||
+ rGrabBagElement.first == "CharTextOutlineTextEffect" ||
+ rGrabBagElement.first == "CharTextFillTextEffect" ||
+ rGrabBagElement.first == "CharScene3DTextEffect" ||
+ rGrabBagElement.first == "CharProps3DTextEffect" ||
+ rGrabBagElement.first == "CharLigaturesTextEffect" ||
+ rGrabBagElement.first == "CharNumFormTextEffect" ||
+ rGrabBagElement.first == "CharNumSpacingTextEffect" ||
+ rGrabBagElement.first == "CharStylisticSetsTextEffect" ||
+ rGrabBagElement.first == "CharCntxtAltsTextEffect")
+ {
+ beans::PropertyValue aPropertyValue;
+ rGrabBagElement.second >>= aPropertyValue;
+ m_aTextEffectsGrabBag.push_back(aPropertyValue);
+ }
+ else if (rGrabBagElement.first == "SdtEndBefore")
+ {
+ if (m_aRunSdt.m_bStartedSdt)
+ m_bEndCharSdt = true;
+ }
+ else if (rGrabBagElement.first == "SdtPr" && FLY_NOT_PROCESSED != m_nStateOfFlyFrame )
+ {
+ const uno::Sequence<beans::PropertyValue> aGrabBagSdt =
+ rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
+ m_aRunSdt.GetSdtParamsFromGrabBag(aGrabBagSdt);
+ }
+ else
+ SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled grab bag property " << rGrabBagElement.first);
+ }
+}
+
+DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML )
+ : AttributeOutputBase(rExport.GetFilter().getFileUrl()),
+ m_rExport( rExport ),
+ m_pSerializer( pSerializer ),
+ m_rDrawingML( *pDrawingML ),
+ m_bEndCharSdt(false),
+ m_endPageRef( false ),
+ m_pFootnotesList( new ::docx::FootnotesList() ),
+ m_pEndnotesList( new ::docx::FootnotesList() ),
+ m_footnoteEndnoteRefTag( 0 ),
+ m_pRedlineData( nullptr ),
+ m_nRedlineId( 0 ),
+ m_bOpenedSectPr( false ),
+ m_bHadSectPr(false),
+ m_bRunTextIsOn( false ),
+ m_bWritingHeaderFooter( false ),
+ m_bAnchorLinkedToNode(false),
+ m_bWritingField( false ),
+ m_bPreventDoubleFieldsHandling( false ),
+ m_nNextBookmarkId( 0 ),
+ m_nNextAnnotationMarkId( 0 ),
+ m_nEmbedFlyLevel(0),
+ m_pMoveRedlineData(nullptr),
+ m_bParagraphOpened( false ),
+ m_bParagraphFrameOpen( false ),
+ m_bIsFirstParagraph( true ),
+ m_bAlternateContentChoiceOpen( false ),
+ m_bPostponedProcessingFly( false ),
+ m_nColBreakStatus( COLBRK_NONE ),
+ m_bPostponedPageBreak( false ),
+ m_nTextFrameLevel( 0 ),
+ m_closeHyperlinkInThisRun( false ),
+ m_closeHyperlinkInPreviousRun( false ),
+ m_nFieldsInHyperlink( 0 ),
+ m_bExportingOutline(false),
+ m_nChartCount(0),
+ m_PendingPlaceholder( nullptr ),
+ m_postitFieldsMaxId( 0 ),
+ m_anchorId( 1 ),
+ m_nextFontId( 1 ),
+ m_bIgnoreNextFill(false),
+ m_pTableStyleExport(std::make_shared<DocxTableStyleExport>(rExport.m_rDoc, pSerializer)),
+ m_bParaBeforeAutoSpacing(false),
+ m_bParaAfterAutoSpacing(false),
+ m_nParaBeforeSpacing(0),
+ m_nParaAfterSpacing(0)
+ , m_nStateOfFlyFrame( FLY_NOT_PROCESSED )
+{
+ m_nHyperLinkCount.push_back(0);
+}
+
+DocxAttributeOutput::~DocxAttributeOutput()
+{
+}
+
+DocxExport& DocxAttributeOutput::GetExport()
+{
+ return m_rExport;
+}
+
+void DocxAttributeOutput::SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer )
+{
+ m_pSerializer = pSerializer;
+ m_pTableStyleExport->SetSerializer(pSerializer);
+}
+
+bool DocxAttributeOutput::HasFootnotes() const
+{
+ return !m_pFootnotesList->isEmpty();
+}
+
+bool DocxAttributeOutput::HasEndnotes() const
+{
+ return !m_pEndnotesList->isEmpty();
+}
+
+bool DocxAttributeOutput::HasPostitFields() const
+{
+ return !m_postitFields.empty();
+}
+
+void DocxAttributeOutput::BulletDefinition(int nId, const Graphic& rGraphic, Size aSize)
+{
+ m_pSerializer->startElementNS(XML_w, XML_numPicBullet,
+ FSNS(XML_w, XML_numPicBulletId), OString::number(nId));
+
+ // Size is in twips, we need it in points.
+ OString aStyle = "width:" + OString::number(double(aSize.Width()) / 20)+ "pt;"
+ "height:" + OString::number(double(aSize.Height()) / 20) + "pt";
+ m_pSerializer->startElementNS(XML_w, XML_pict);
+ m_pSerializer->startElementNS( XML_v, XML_shape,
+ XML_style, aStyle,
+ FSNS(XML_o, XML_bullet), "t");
+
+ OUString aRelId = m_rDrawingML.writeGraphicToStorage(rGraphic);
+ m_pSerializer->singleElementNS( XML_v, XML_imagedata,
+ FSNS(XML_r, XML_id), aRelId,
+ FSNS(XML_o, XML_title), "");
+
+ m_pSerializer->endElementNS(XML_v, XML_shape);
+ m_pSerializer->endElementNS(XML_w, XML_pict);
+
+ m_pSerializer->endElementNS(XML_w, XML_numPicBullet);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
new file mode 100644
index 0000000000..28eb32caf8
--- /dev/null
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -0,0 +1,1200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXATTRIBUTEOUTPUT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXATTRIBUTEOUTPUT_HXX
+
+#include <memory>
+#include <string_view>
+
+#include "attributeoutputbase.hxx"
+#include "fields.hxx"
+#include <IMark.hxx>
+#include "docxexport.hxx"
+#include <wrtswtbl.hxx>
+#include <redline.hxx>
+
+#include <editeng/boxitem.hxx>
+#include <sax/fshelper.hxx>
+#include <sax/fastattribs.hxx>
+#include <vcl/vclenum.hxx>
+#include <svx/xenum.hxx>
+
+#include <fldbas.hxx>
+
+#include <vector>
+#include <optional>
+#include <o3tl/sorted_vector.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <oox/export/drawingml.hxx>
+#include "docxtablestyleexport.hxx"
+
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+
+class SwGrfNode;
+class SdrObject;
+enum class SvxBoxItemLine;
+enum class SwLineBreakClear;
+class SwContentControl;
+
+namespace docx { class FootnotesList; }
+namespace oox::drawingml { class DrawingML; }
+
+struct FieldInfos
+{
+ std::shared_ptr<const SwField> pField;
+ const ::sw::mark::IFieldmark* pFieldmark;
+ ww::eField eType;
+ bool bOpen;
+ bool bSep;
+ bool bClose;
+ OUString sCmd;
+ FieldInfos()
+ : pFieldmark(nullptr), eType(ww::eUNKNOWN)
+ , bOpen(false), bSep(false), bClose(false)
+ {}
+};
+
+enum DocxColBreakStatus
+{
+ COLBRK_NONE,
+ COLBRK_POSTPONE,
+ COLBRK_WRITEANDPOSTPONE,
+ COLBRK_WRITE
+};
+
+/**
+ * A structure that holds information about the options selected
+ * when outputting a border to DOCX.
+ *
+ * There are 3 functions that initialize this structure:
+ * - lcl_getTableDefaultBorderOptions - retrieves the options for when outputting table default borders
+ * - lcl_getTableCellBorderOptions - retrieves the options for when outputting table cell borders
+ * - lcl_getBoxBorderOptions - retrieves the options for when outputting box borders
+ *
+ */
+struct OutputBorderOptions
+{
+ sal_Int32 tag = 0;
+ bool bUseStartEnd = false;
+ bool bWriteTag = true;
+ bool bWriteDistance = false;
+ SvxShadowLocation aShadowLocation = SvxShadowLocation::NONE;
+ std::shared_ptr<editeng::WordBorderDistances> pDistances;
+};
+
+struct DocxTableExportContext;
+
+/**
+ * A structure that holds flags for the table export.
+ */
+struct TableReference
+{
+ /// Remember if we are in an open cell, or not.
+ bool m_bTableCellOpen;
+
+ /// If paragraph sdt got opened in this table cell.
+ bool m_bTableCellParaSdtOpen;
+
+ /// Remember if we are in a deleted/inserted cell, or not.
+ bool m_bTableCellChanged;
+
+ /// Remember the current table depth.
+ sal_uInt32 m_nTableDepth;
+
+ TableReference()
+ : m_bTableCellOpen(false),
+ m_bTableCellParaSdtOpen(false),
+ m_bTableCellChanged(false),
+ m_nTableDepth(0)
+ {
+ }
+};
+
+/** Using framePr, a paragraph can be enclosed in a frame described by its pPr paragraph settings,
+ * and therefore it needs to apply the frame's properties when exporting the paragraph properties.
+ */
+class FramePrHelper
+{
+ ww8::Frame* m_pFrame;
+ sal_Int32 m_nTableDepth;
+ bool m_bUseFrameBorders;
+ bool m_bUseFrameBackground;
+ bool m_bUseFrameTextDirection;
+
+public:
+ FramePrHelper()
+ : m_pFrame(nullptr)
+ , m_nTableDepth(0)
+ , m_bUseFrameBorders(true)
+ , m_bUseFrameBackground(true)
+ , m_bUseFrameTextDirection(true)
+ {}
+
+ ww8::Frame* Frame() { return m_pFrame; }
+ void SetFrame(ww8::Frame* pSet, sal_Int32 nTableDepth = -1);
+ bool UseFrameBorders(sal_Int32 nTableDepth);
+ void SetUseFrameBorders(bool bSet) { m_bUseFrameBorders = bSet; }
+ bool UseFrameBackground();
+ void SetUseFrameBackground(bool bSet) { m_bUseFrameBackground = bSet; }
+ bool UseFrameTextDirection(sal_Int32 nTableDepth);
+ void SetUseFrameTextDirection(bool bSet) { m_bUseFrameTextDirection = bSet; }
+};
+
+class SdtBlockHelper
+{
+public:
+ SdtBlockHelper()
+ : m_nId(0)
+ , m_bStartedSdt(false)
+ , m_bShowingPlaceHolder(false)
+ , m_nTabIndex(0)
+ , m_nSdtPrToken(0)
+ {}
+
+ sal_Int32 m_nId;
+ bool m_bStartedSdt;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenChildren;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenAttributes;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pTextAttrs;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pDataBindingAttrs;
+ OUString m_aColor;
+ OUString m_aAppearance;
+ OUString m_aPlaceHolderDocPart;
+ bool m_bShowingPlaceHolder;
+ OUString m_aAlias;
+ OUString m_aTag;
+ sal_Int32 m_nTabIndex;
+ OUString m_aLock;
+ sal_Int32 m_nSdtPrToken;
+
+ void DeleteAndResetTheLists();
+
+ void WriteSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing);
+ void WriteExtraParams(const ::sax_fastparser::FSHelperPtr& pSerializer);
+
+ /// Closes a currently open SDT block.
+ void EndSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer);
+
+ void GetSdtParamsFromGrabBag(const uno::Sequence<beans::PropertyValue>& aGrabBagSdt);
+};
+
+/// The class that has handlers for various resource types when exporting as DOCX.
+class DocxAttributeOutput : public AttributeOutputBase, public oox::vml::VMLTextExport, public oox::drawingml::DMLTextExport
+{
+public:
+ /// Export the state of RTL/CJK.
+ virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) override;
+
+ /// Start of the paragraph.
+ virtual sal_Int32 StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+ bool bGenerateParaId) override;
+
+ /// End of the paragraph.
+ virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override;
+
+ /// Empty paragraph.
+ virtual void EmptyParagraph() override;
+
+ /// Called in order to output section breaks.
+ virtual void SectionBreaks(const SwNode& rNode) override;
+
+ /// Called before we start outputting the attributes.
+ virtual void StartParagraphProperties() override;
+
+ /// Called after we end outputting the attributes.
+ virtual void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) override;
+
+ /// Start of the text run.
+ virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) override;
+
+ /// End of the text run.
+ virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool bLastRun = false) override;
+
+ /// Called before we start outputting the attributes.
+ virtual void StartRunProperties() override;
+
+ /// Called after we end outputting the attributes.
+ virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override;
+
+ virtual bool FootnoteEndnoteRefTag() override;
+
+ virtual void SectFootnoteEndnotePr() override;
+
+ virtual void WritePostitFieldReference() override;
+
+ /// Output text (inside a run).
+ virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) override;
+
+ /// Output text (without markup).
+ virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override;
+
+ /// Output ruby start.
+ virtual void StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) override;
+
+ /// Output ruby end.
+ virtual void EndRuby(const SwTextNode& rNode, sal_Int32 nPos) override;
+
+ /// Output URL start.
+ virtual bool StartURL( const OUString& rUrl, const OUString& rTarget ) override;
+
+ /// Output URL end.
+ virtual bool EndURL(bool) override;
+
+ virtual void FieldVanish(const OUString& rText, ww::eField eType, OUString const*) override;
+
+ /// Output redlining.
+ ///
+ /// The common attribute that can be among the run properties.
+ virtual void Redline( const SwRedlineData* pRedline ) override;
+
+ /// Output redlining.
+ ///
+ /// Start of the tag that encloses the run, fills the info according to
+ /// the value of pRedlineData.
+ void StartRedline( const SwRedlineData * pRedlineData, bool bLastRun );
+
+ /// Output redlining.
+ ///
+ /// End of the tag that encloses the run.
+ void EndRedline( const SwRedlineData * pRedlineData, bool bLastRun );
+
+ virtual void SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame ) override;
+ virtual void SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode ) override;
+ virtual bool IsFlyProcessingPostponed() override;
+ virtual void ResetFlyProcessingFlag() override;
+
+ virtual void FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override;
+
+ /// Output style.
+ virtual void ParagraphStyle( sal_uInt16 nStyle ) override;
+
+ virtual void TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ void TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+ virtual void TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) override;
+ virtual void TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableRowEnd( sal_uInt32 nDepth ) override;
+
+ /// Start of the styles table.
+ virtual void StartStyles() override;
+
+ /// End of the styles table.
+ virtual void EndStyles( sal_uInt16 nNumberOfStyles ) override;
+
+ /// Write default style.
+ virtual void DefaultStyle() override;
+
+ /// Write Doc Defaults
+ void DocDefaults( );
+
+ /// Write latent styles.
+ void LatentStyles();
+
+ /** Similar to OutputItem(), but write something only if it is not the default.
+
+ This is to output the docDefaults, and we should write something out
+ only in case it is not what MSO already uses for the document by default.
+ */
+ void OutputDefaultItem(const SfxPoolItem& rHt);
+
+ /// Start of a style in the styles table.
+ virtual void StartStyle( const OUString& rName, StyleType eType,
+ sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nSlot,
+ bool bAutoUpdate ) override;
+
+ /// End of a style in the styles table.
+ virtual void EndStyle() override;
+
+ /// Start of (paragraph or run) properties of a style.
+ virtual void StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) override;
+
+ /// End of (paragraph or run) properties of a style.
+ virtual void EndStyleProperties( bool bParProp ) override;
+
+ /// Numbering rule and Id.
+ virtual void OutlineNumbering(sal_uInt8 nLvl) override;
+
+ /// Page break
+ /// As a paragraph property - the paragraph should be on the next page.
+ virtual void PageBreakBefore( bool bBreak ) override;
+
+ /// Write a section break
+ /// msword::ColumnBreak or msword::PageBreak
+ /// bBreakAfter: the break must be scheduled for insertion in the end of current paragraph
+ virtual void SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr, bool bExtraPageBreak = false ) override;
+
+ // preserve DOCX page vertical alignment
+ virtual void TextVerticalAdjustment( const css::drawing::TextVerticalAdjust ) override;
+
+ /// Start of the section properties.
+ virtual void StartSection() override;
+
+ /// End of the section properties.
+ virtual void EndSection() override;
+
+ /// Protection of forms.
+ virtual void SectionFormProtection( bool bProtected ) override;
+
+ /// Numbering of the lines in the document.
+ virtual void SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) override;
+
+ /// Has different headers/footers for the title page.
+ virtual void SectionTitlePage() override;
+
+ /// Description of the page borders.
+ virtual void SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* pFirstPageFormat ) override;
+
+ /// Columns populated from right/numbers on the right side?
+ virtual void SectionBiDi( bool bBiDi ) override;
+
+ /// The style of the page numbers.
+ ///
+ virtual void SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) override;
+
+ /// The type of breaking.
+ virtual void SectionType( sal_uInt8 nBreakCode ) override;
+
+ /// Start the font.
+ void StartFont( const OUString& rFamilyName ) const;
+
+ /// End the font.
+ void EndFont() const;
+
+ /// Alternate name for the font.
+ void FontAlternateName( const OUString& rName ) const;
+
+ /// Font charset.
+ void FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const;
+
+ /// Font family.
+ void FontFamilyType( FontFamily eFamily ) const;
+
+ /// Font pitch.
+ void FontPitchType( FontPitch ePitch ) const;
+
+ /// Write out the font into the document, if it's an embedded font.
+ void EmbedFont( std::u16string_view name, FontFamily family, FontPitch pitch );
+
+ /// Definition of a numbering instance.
+ virtual void NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) override;
+
+ /// Numbering definition that overrides abstract numbering definition
+ virtual void OverrideNumberingDefinition( SwNumRule const& rRule,
+ sal_uInt16 nNum, sal_uInt16 nAbstractNum,
+ const std::map< size_t, size_t > & rLevelOverrides ) override;
+
+ /// Start of the abstract numbering definition instance.
+ virtual void StartAbstractNumbering( sal_uInt16 nId ) override;
+
+ /// End of the abstract numbering definition instance.
+ virtual void EndAbstractNumbering() override;
+
+ /// All the numbering level information.
+ virtual void NumberingLevel( sal_uInt8 nLevel,
+ sal_uInt16 nStart,
+ sal_uInt16 nNumberingType,
+ SvxAdjust eAdjust,
+ const sal_uInt8 *pNumLvlPos,
+ sal_uInt8 nFollow,
+ const wwFont *pFont,
+ const SfxItemSet *pOutSet,
+ sal_Int16 nIndentAt,
+ sal_Int16 nFirstLineIndex,
+ sal_Int16 nListTabPos,
+ const OUString &rNumberingString,
+ const SvxBrushItem* pBrush,
+ bool isLegal ) override;
+
+ void WriteField_Impl(const SwField* pField, ww::eField eType,
+ const OUString& rFieldCmd, FieldFlags nMode,
+ OUString const* pBookmarkName = nullptr);
+ void WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark );
+
+ void WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds, const SwRedlineData* pRedlineData = nullptr );
+ void WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds );
+ void WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds );
+ /// End possibly opened paragraph sdt block.
+ void EndParaSdtBlock();
+
+ void WriteFloatingTable(ww8::Frame const* pParentFrame);
+
+private:
+ /// Initialize the structures where we are going to collect some of the paragraph properties.
+ ///
+ /// Some of the properties have to be collected from more sources, and are
+ /// actually not written between StartParagraphProperties and
+ /// EndParagraphProperties. They are output in this method, which is
+ /// supposed to be called just before outputting </rPr> whenever it is done.
+ void InitCollectedParagraphProperties();
+
+ /// Output what we collected during the run properties output.
+ ///
+ /// @see WriteCollectedParagrapProperties().
+ void WriteCollectedParagraphProperties();
+
+ /// Initialize the structures where we are going to collect some of the run properties.
+ ///
+ /// This is an equivalent of InitCollectedParagraphProperties(), resp.
+ /// WriteCollectectedParagraphProperties().
+ ///
+ /// @see InitCollectedParagraphProperties().
+ void InitCollectedRunProperties();
+
+ /// Output what we collected during the run properties output.
+ ///
+ /// @see InitCollectedRunproperties(), WriteCollectedParagraphProperties()
+ void WriteCollectedRunProperties();
+
+ /// Output graphic fly frames or replacement graphics for OLE nodes.
+ ///
+ /// For graphic frames, just use the first two parameters, for OLE
+ /// replacement graphics, set the first as 0, and pass the remaining three.
+ ///
+ /// @see WriteOLE2Obj()
+ void FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj = nullptr);
+ void WriteSrcRect(const css::uno::Reference<css::beans::XPropertySet>& xShapePropSet,
+ const SwFrameFormat* pFrameFormat);
+ void WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment);
+ bool WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat);
+ bool WriteOLEMath( const SwOLENode& rNode, const sal_Int8 nAlign );
+ void PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat );
+ void WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* rFlyFrameFormat );
+ void WriteOLEShape(const SwFlyFrameFormat& rFrameFormat, const Size& rSize,
+ std::string_view rShapeId, const OUString& rImageId);
+ static OString GetOLEStyle(const SwFlyFrameFormat& rFormat, const Size& rSize);
+ void ExportOLESurround(const SwFormatSurround& rWrap);
+
+ void WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun);
+ bool ExportAsActiveXControl(const SdrObject* pObject) const;
+
+ void InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+ void StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+ void StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+ void StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow );
+ void TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow );
+ void EndTableCell( sal_uInt32 nCell );
+ void EndTableRow( );
+ void EndTable();
+ void SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow);
+ void PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize);
+ static bool TextBoxIsFramePr(const SwFrameFormat& rFrameFormat);
+ /// End cell, row, and even the entire table if necessary.
+ void FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph = false );
+
+ void WriteFFData( const FieldInfos& rInfos );
+ void WritePendingPlaceholder();
+
+ void EmbedFontStyle( std::u16string_view name, int tag, FontFamily family, FontItalic italic, FontWeight weight,
+ FontPitch pitch );
+
+ /**
+ * Translate an ico value to the corresponding HighlightColorValues enumeration item
+ *
+ * @param[in] nIco ico value [0..16]
+ * @return color name (e.g. "red"), if color is inside [1..16] range
+ * empty string, otherwise
+ **/
+ static OString TransHighlightColor( sal_uInt8 nIco );
+protected:
+
+ /// Output frames - the implementation.
+ virtual void OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) override;
+
+ /// Sfx item Sfx item RES_CHRATR_CASEMAP
+ virtual void CharCaseMap( const SvxCaseMapItem& rCaseMap ) override;
+
+ /// Sfx item Sfx item RES_CHRATR_COLOR
+ virtual void CharColor( const SvxColorItem& rColor) override;
+
+ /// Sfx item Sfx item RES_CHRATR_CONTOUR
+ virtual void CharContour( const SvxContourItem& rContour ) override;
+
+ /// Sfx item RES_CHRATR_CROSSEDOUT
+ virtual void CharCrossedOut( const SvxCrossedOutItem& rCrossedOut ) override;
+
+ /// Sfx item RES_CHRATR_ESCAPEMENT
+ virtual void CharEscapement( const SvxEscapementItem& rEscapement ) override;
+
+ /// Sfx item RES_CHRATR_FONT
+ virtual void CharFont( const SvxFontItem& rFont ) override;
+
+ /// Sfx item RES_CHRATR_FONTSIZE
+ virtual void CharFontSize( const SvxFontHeightItem& rFontSize ) override;
+
+ /// Sfx item RES_CHRATR_KERNING
+ virtual void CharKerning( const SvxKerningItem& rKerning ) override;
+
+ /// Sfx item RES_CHRATR_LANGUAGE
+ virtual void CharLanguage( const SvxLanguageItem& rLanguage ) override;
+
+ /// Sfx item RES_CHRATR_POSTURE
+ virtual void CharPosture( const SvxPostureItem& rPosture ) override;
+
+ /// Sfx item RES_CHRATR_SHADOWED
+ virtual void CharShadow( const SvxShadowedItem& rShadow ) override;
+
+ /// Sfx item RES_CHRATR_UNDERLINE
+ virtual void CharUnderline( const SvxUnderlineItem& rUnderline ) override;
+
+ /// Sfx item RES_CHRATR_WEIGHT
+ virtual void CharWeight( const SvxWeightItem& rWeight ) override;
+
+ /// Sfx item RES_CHRATR_AUTOKERN
+ virtual void CharAutoKern( const SvxAutoKernItem& ) override;
+
+ /// Sfx item RES_CHRATR_BLINK
+ virtual void CharAnimatedText( const SvxBlinkItem& rBlink ) override;
+
+ /// Sfx item RES_CHRATR_BACKGROUND
+ virtual void CharBackground( const SvxBrushItem& rBrush ) override;
+
+ /// Sfx item RES_CHRATR_CJK_FONT
+ virtual void CharFontCJK( const SvxFontItem& rFont ) override;
+
+ /// Sfx item RES_CHRATR_CJK_FONTSIZE
+ virtual void CharFontSizeCJK( const SvxFontHeightItem& rFontSize ) override { CharFontSize( rFontSize ); }
+
+ /// Sfx item RES_CHRATR_CJK_LANGUAGE
+ virtual void CharLanguageCJK( const SvxLanguageItem& rLanguageItem ) override { CharLanguage( rLanguageItem ); }
+
+ /// Sfx item RES_CHRATR_CJK_POSTURE
+ virtual void CharPostureCJK( const SvxPostureItem& rPosture ) override;
+
+ /// Sfx item RES_CHRATR_CJK_WEIGHT
+ virtual void CharWeightCJK( const SvxWeightItem& rWeight ) override;
+
+ /// Sfx item RES_CHRATR_CTL_FONT
+ virtual void CharFontCTL( const SvxFontItem& rFont ) override;
+
+ /// Sfx item RES_CHRATR_CTL_FONTSIZE
+ virtual void CharFontSizeCTL( const SvxFontHeightItem& rFontSize ) override { CharFontSize( rFontSize ); }
+
+ /// Sfx item RES_CHRATR_CTL_LANGUAGE
+ virtual void CharLanguageCTL( const SvxLanguageItem& rLanguageItem ) override { CharLanguage( rLanguageItem); }
+
+ /// Sfx item RES_CHRATR_CTL_POSTURE
+ virtual void CharPostureCTL( const SvxPostureItem& rWeight ) override;
+
+ /// Sfx item RES_CHRATR_CTL_WEIGHT
+ virtual void CharWeightCTL( const SvxWeightItem& rWeight ) override;
+
+ /// Sfx item RES_CHRATR_BidiRTL
+ virtual void CharBidiRTL( const SfxPoolItem& ) override;
+
+ /// Sfx item RES_CHRATR_IdctHint
+ virtual void CharIdctHint( const SfxPoolItem& ) override;
+
+ /// Sfx item RES_CHRATR_ROTATE
+ virtual void CharRotate( const SvxCharRotateItem& rRotate ) override;
+
+ /// Sfx item RES_CHRATR_EMPHASIS_MARK
+ virtual void CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark ) override;
+
+ /// Sfx item RES_CHRATR_TWO_LINES
+ virtual void CharTwoLines( const SvxTwoLinesItem& rTwoLines ) override;
+
+ /// Sfx item RES_CHRATR_SCALEW
+ virtual void CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth ) override;
+
+ /// Sfx item RES_CHRATR_RELIEF
+ virtual void CharRelief( const SvxCharReliefItem& rRelief) override;
+
+ /// Sfx item RES_CHRATR_HIDDEN
+ virtual void CharHidden( const SvxCharHiddenItem& rHidden ) override;
+
+ /// Sfx item RES_CHRATR_BOX
+ virtual void CharBorder( const ::editeng::SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) override;
+
+ /// Sfx item RES_CHRATR_HIGHLIGHT
+ virtual void CharHighlight( const SvxBrushItem& rHighlight ) override;
+
+ /// Sfx item RES_TXTATR_INETFMT
+ virtual void TextINetFormat( const SwFormatINetFormat& ) override;
+
+ /// Sfx item RES_TXTATR_CHARFMT
+ virtual void TextCharFormat( const SwFormatCharFormat& ) override;
+
+ /// Sfx item RES_TXTATR_FTN
+ virtual void TextFootnote_Impl( const SwFormatFootnote& ) override;
+
+ /// Output the footnote/endnote reference (if there's one to output).
+ void FootnoteEndnoteReference();
+
+ /// Sfx item RES_PARATR_LINESPACING
+ virtual void ParaLineSpacing_Impl( short nSpace, short nMulti ) override;
+
+ /// Sfx item RES_PARATR_ADJUST
+ virtual void ParaAdjust( const SvxAdjustItem& rAdjust ) override;
+
+ /// Sfx item RES_PARATR_SPLIT
+ virtual void ParaSplit( const SvxFormatSplitItem& rSplit ) override;
+
+ /// Sfx item RES_PARATR_WIDOWS
+ virtual void ParaWidows( const SvxWidowsItem& rWidows ) override;
+
+ /// Sfx item RES_PARATR_TABSTOP
+ virtual void ParaTabStop( const SvxTabStopItem& rTabStop ) override;
+
+ /// Sfx item RES_PARATR_HYPHENZONE
+ virtual void ParaHyphenZone( const SvxHyphenZoneItem& ) override;
+
+ /// Sfx item RES_PARATR_NUMRULE
+ virtual void ParaNumRule_Impl( const SwTextNode *pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) override;
+
+ /// Sfx item RES_PARATR_SCRIPTSPACE
+ virtual void ParaScriptSpace( const SfxBoolItem& ) override;
+
+ /// Sfx item RES_PARATR_HANGINGPUNCTUATION
+ virtual void ParaHangingPunctuation( const SfxBoolItem& ) override;
+
+ /// Sfx item RES_PARATR_FORBIDDEN_RULES
+ virtual void ParaForbiddenRules( const SfxBoolItem& ) override;
+
+ /// Sfx item RES_PARATR_VERTALIGN
+ virtual void ParaVerticalAlign( const SvxParaVertAlignItem& rAlign ) override;
+
+ /// Sfx item RES_PARATR_SNAPTOGRID
+ virtual void ParaSnapToGrid( const SvxParaGridItem& ) override;
+
+ /// Sfx item RES_FRM_SIZE
+ virtual void FormatFrameSize( const SwFormatFrameSize& ) override;
+
+ /// Sfx item RES_PAPER_BIN
+ virtual void FormatPaperBin( const SvxPaperBinItem& ) override;
+
+ /// Sfx item RES_MARGIN_FIRSTLINE
+ virtual void FormatFirstLineIndent(const SvxFirstLineIndentItem & rFirstLine) override;
+ /// Sfx item RES_MARGIN_TEXTLEFT
+ virtual void FormatTextLeftMargin(const SvxTextLeftMarginItem & rTextLeftMargin) override;
+ /// Sfx item RES_MARGIN_RIGHT
+ virtual void FormatRightMargin(const SvxRightMarginItem & rRightMargin) override;
+
+ /// Sfx item RES_LR_SPACE
+ virtual void FormatLRSpace( const SvxLRSpaceItem& rLRSpace ) override;
+
+ /// Sfx item RES_UL_SPACE
+ virtual void FormatULSpace( const SvxULSpaceItem& rULSpace ) override;
+
+ /// Sfx item RES_SURROUND
+ virtual void FormatSurround( const SwFormatSurround& ) override;
+
+ /// Sfx item RES_VERT_ORIENT
+ virtual void FormatVertOrientation( const SwFormatVertOrient& ) override;
+
+ /// Sfx item RES_HORI_ORIENT
+ virtual void FormatHorizOrientation( const SwFormatHoriOrient& ) override;
+
+ /// Sfx item RES_ANCHOR
+ virtual void FormatAnchor( const SwFormatAnchor& ) override;
+
+ /// Sfx item RES_BACKGROUND
+ virtual void FormatBackground( const SvxBrushItem& ) override;
+
+ /// Sfx item RES_FILL_STYLE
+ virtual void FormatFillStyle( const XFillStyleItem& ) override;
+
+ /// Sfx item RES_FILL_GRADIENT
+ virtual void FormatFillGradient( const XFillGradientItem& ) override;
+
+ /// Sfx item RES_BOX
+ virtual void FormatBox( const SvxBoxItem& ) override;
+
+ /// Sfx item RES_COL
+ virtual void FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) override;
+
+ /// Sfx item RES_KEEP
+ virtual void FormatKeep( const SvxFormatKeepItem& ) override;
+
+ /// Sfx item RES_TEXTGRID
+ virtual void FormatTextGrid( const SwTextGridItem& ) override;
+
+ /// Sfx item RES_LINENUMBER
+ virtual void FormatLineNumbering( const SwFormatLineNumber& ) override;
+
+ /// Sfx item RES_FRAMEDIR
+ virtual void FormatFrameDirection( const SvxFrameDirectionItem& ) override;
+
+ /// Sfx item RES_PARATR_GRABBAG
+ virtual void ParaGrabBag( const SfxGrabBagItem& ) override;
+
+ /// Sfx item RES_CHRATR_GRABBAG
+ virtual void CharGrabBag( const SfxGrabBagItem& ) override;
+
+ // Sfx item RES_PARATR_OUTLINELEVEL
+ virtual void ParaOutlineLevel( const SfxUInt16Item& ) override;
+
+ /// Write the expanded field
+ virtual void WriteExpand( const SwField* pField ) override;
+
+ virtual void RefField( const SwField& rField, const OUString& rRef ) override;
+ virtual void HiddenField( const SwField& rField ) override;
+ virtual void SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) override;
+ virtual void PostitField( const SwField* pField ) override;
+ virtual bool DropdownField( const SwField* pField ) override;
+ virtual bool PlaceholderField( const SwField* pField ) override;
+
+ virtual bool AnalyzeURL( const OUString& rURL, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) override;
+
+ virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) override;
+
+ void SectionRtlGutter( const SfxBoolItem& rRtlGutter) override;
+
+ void TextLineBreak(const SwFormatLineBreak& rLineBreak) override;
+
+ /// Writes a clearing line break at the end of run properties, if there are any.
+ void WriteLineBreak();
+
+private:
+
+ void DoWriteBookmarkTagStart(const OUString& bookmarkName);
+ void DoWriteBookmarkTagEnd(sal_Int32 nId);
+ void DoWriteMoveRangeTagStart(std::u16string_view bookmarkName,
+ bool bFrom, const SwRedlineData* pRedlineData);
+ void DoWriteMoveRangeTagEnd(sal_Int32 nId, bool bFrom);
+ void DoWriteBookmarksStart(std::vector<OUString>& rStarts, const SwRedlineData* pRedlineData = nullptr);
+ void DoWriteBookmarksEnd(std::vector<OUString>& rEnds);
+ void DoWriteBookmarkStartIfExist(sal_Int32 nRunPos);
+ void DoWriteBookmarkEndIfExist(sal_Int32 nRunPos);
+
+ void DoWritePermissionTagStart(std::u16string_view permission);
+ void DoWritePermissionTagEnd(std::u16string_view permission);
+ void DoWritePermissionsStart();
+ void DoWritePermissionsEnd();
+
+ void DoWriteAnnotationMarks( );
+ void WritePostponedGraphic();
+ void WritePostponedMath(const SwOLENode* pObject, sal_Int8 /*nAlign*/);
+ void WritePostponedFormControl(const SdrObject* pObject);
+ void WritePostponedActiveXControl(bool bInsideRun);
+ void WritePostponedDiagram();
+ void WritePostponedChart();
+ void WritePostponedOLE();
+ void WritePostponedDMLDrawing();
+ void WritePostponedCustomShape();
+ void WriteFlyFrame(const ww8::Frame& rFrame);
+
+ void WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt);
+ void WriteSdtPlainText(const OUString& sValue, const uno::Sequence<beans::PropertyValue>& aGrabBagSdt);
+ void WriteSdtDropDownStart(const OUString& rName, OUString const& rSelected, uno::Sequence<OUString> const& rListItems);
+ void WriteSdtDropDownEnd(OUString const& rSelected, uno::Sequence<OUString> const& rListItems);
+ void WriteContentControlStart();
+ void WriteContentControlEnd();
+
+ void StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun = false );
+ void DoWriteCmd( std::u16string_view rCmd );
+ void CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun );
+ void CmdEndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, bool bWriteRun );
+ void EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos );
+ void DoWriteFieldRunProperties( const SwTextNode* pNode, sal_Int32 nPos, bool bWriteCombChars = false );
+
+ /// Reference to the export, where to get the data from
+ DocxExport &m_rExport;
+
+ /// Fast serializer to output the data
+ ::sax_fastparser::FSHelperPtr m_pSerializer;
+
+ /// DrawingML access
+ oox::drawingml::DrawingML &m_rDrawingML;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pFontsAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pEastAsianLayoutAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pCharLangAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pSectionSpacingAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pLRSpaceAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSpacingAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pHyperlinkAttrList;
+ std::shared_ptr<SwContentControl> m_pContentControl;
+ /// If the current SDT around runs should be ended before the current run.
+ bool m_bEndCharSdt;
+ /// Attributes of the run color
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pColorAttrList;
+ sal_uInt8 m_nCharTransparence = 0;
+ /// Attributes of the paragraph background
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pBackgroundAttrList;
+ OUString m_sOriginalBackgroundColor;
+ OUString m_hyperLinkAnchor;
+ bool m_endPageRef;
+ std::unique_ptr<docx::FootnotesList> m_pFootnotesList;
+ std::unique_ptr<docx::FootnotesList> m_pEndnotesList;
+ int m_footnoteEndnoteRefTag;
+ OUString m_footnoteCustomLabel;
+ std::unique_ptr< const WW8_SepInfo > m_pSectionInfo;
+
+ /// Redline data to remember in the text run.
+ const SwRedlineData *m_pRedlineData;
+
+ /// Id of the redline
+ sal_Int32 m_nRedlineId;
+
+ /// Flag indicating that the section properties are being written
+ bool m_bOpenedSectPr;
+ /// Did we have a section break in this paragraph? Set by StartSection(), reset by the next StartParagraph().
+ bool m_bHadSectPr;
+
+ /// Flag indicating that the Run Text is being written
+ bool m_bRunTextIsOn;
+
+ /// Flag indicating that the header \ footer are being written
+ bool m_bWritingHeaderFooter;
+ bool m_bAnchorLinkedToNode;
+
+ /// Flag indicating that multiple runs of a field are being written
+ bool m_bWritingField;
+
+ /// Field data to remember in the text run
+ bool m_bPreventDoubleFieldsHandling;
+ std::vector< FieldInfos > m_Fields;
+ OUString m_sFieldBkm;
+ sal_Int32 m_nNextBookmarkId;
+ sal_Int32 m_nNextAnnotationMarkId;
+
+ /// [MS-DOCX] section 2.6.2.3
+ sal_Int32 m_nNextParaId = 1; // MUST be greater than 0
+
+ OUString m_sRawText;
+
+ /// The first frame (anchored to the main text) is 0.
+ /// The second frame what is anchored to the previous in, is 1
+ /// The third anchored inside the second is the 2 etc.
+ sal_uInt32 m_nEmbedFlyLevel;
+
+ /// Stores the flys what are anchored inside a fly
+ std::vector<ww8::Frame> m_vPostponedFlys;
+
+ /// Bookmarks to output
+ std::vector<OUString> m_rBookmarksStart;
+ std::vector<OUString> m_rBookmarksEnd;
+ SwRedlineData* m_pMoveRedlineData;
+
+ /// Bookmarks to output at the end
+ std::vector<OUString> m_rFinalBookmarksStart;
+ std::vector<OUString> m_rFinalBookmarksEnd;
+
+ /// Bookmarks of the current paragraph
+ std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphStart;
+ std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphEnd;
+
+ /// Permissions to output
+ std::vector<OUString> m_rPermissionsStart;
+ std::vector<OUString> m_rPermissionsEnd;
+
+ /// Annotation marks to output
+ std::vector<OUString> m_rAnnotationMarksStart;
+ std::vector<OUString> m_rAnnotationMarksEnd;
+
+ /// Maps of the bookmarks ids
+ std::map<OUString, sal_Int32> m_rOpenedBookmarksIds;
+
+ /// Name of the last opened bookmark.
+ OUString m_sLastOpenedBookmark;
+
+ /// Set of ids of the saved bookmarks (used only for moveRange, yet)
+ std::unordered_set<sal_Int32> m_rSavedBookmarksIds;
+
+ /// Maps of the annotation marks ids
+ std::map<OUString, sal_Int32> m_rOpenedAnnotationMarksIds;
+
+ /// Name of the last opened annotation mark.
+ OUString m_sLastOpenedAnnotationMark;
+
+ /// If there are bookmarks around sequence fields, this map contains the
+ /// names of these bookmarks for each sequence.
+ std::map<OUString, std::vector<OUString> > m_aSeqBookmarksNames;
+
+ /// GrabBag for text effects like glow, shadow, ...
+ std::vector<css::beans::PropertyValue> m_aTextEffectsGrabBag;
+
+ /// The current table helper
+ std::unique_ptr<SwWriteTable> m_xTableWrt;
+
+ FramePrHelper m_aFramePr;
+
+ bool m_bParagraphOpened;
+ bool m_bParagraphFrameOpen;
+ bool m_bIsFirstParagraph;
+ bool m_bAlternateContentChoiceOpen;
+ bool m_bPostponedProcessingFly;
+
+ // Remember that a column break has to be opened at the
+ // beginning of the next paragraph
+ DocxColBreakStatus m_nColBreakStatus;
+
+ // Remember that a page break has to be opened at the
+ // beginning of the next paragraph
+ bool m_bPostponedPageBreak;
+
+ // This paragraph must end with page break
+ bool m_bPageBreakAfter = false;
+
+ std::stack< std::vector<ww8::Frame> > m_aFramesOfParagraph;
+ o3tl::sorted_vector<const SwFrameFormat*> m_aFloatingTablesOfParagraph;
+ sal_Int32 m_nTextFrameLevel;
+
+ // close of hyperlink needed
+ bool m_closeHyperlinkInThisRun;
+ bool m_closeHyperlinkInPreviousRun;
+ // Count nested HyperLinks
+ std::vector<sal_Int32> m_nHyperLinkCount;
+ sal_Int16 m_nFieldsInHyperlink;
+
+ // If the exported numbering rule defines the outlines
+ bool m_bExportingOutline;
+
+ struct PostponedGraphic
+ {
+ PostponedGraphic( const SwGrfNode* n, Size s, const SdrObject* sObj )
+ : grfNode( n ), size( s ), pSdrObj(sObj) {};
+
+ const SwGrfNode* grfNode;
+ Size size;
+ const SdrObject* pSdrObj;
+ };
+ std::optional< std::vector<PostponedGraphic> > m_oPostponedGraphic;
+ struct PostponedDiagram
+ {
+ PostponedDiagram( const SdrObject* o, const SwFrameFormat* frm ) : object( o ), frame( frm ) {};
+ const SdrObject* object;
+ const SwFrameFormat* frame;
+ };
+ std::optional< std::vector<PostponedDiagram> > m_oPostponedDiagrams;
+
+ struct PostponedDrawing
+ {
+ PostponedDrawing( const SdrObject* sdrObj, const SwFrameFormat* frm) : object( sdrObj ), frame( frm ) {};
+ const SdrObject* object;
+ const SwFrameFormat* frame;
+ };
+ std::optional< std::vector<PostponedDrawing> > m_oPostponedDMLDrawings;
+ std::optional< std::vector<PostponedDrawing> > m_oPostponedCustomShape;
+
+ struct PostponedOLE
+ {
+ PostponedOLE( SwOLENode* rObject, const Size& rSize, const SwFlyFrameFormat* rFrame ) : object( rObject ), size( rSize ), frame( rFrame ) {};
+ SwOLENode* object;
+ const Size size;
+ const SwFlyFrameFormat* frame;
+ };
+ std::optional< std::vector<PostponedOLE> > m_oPostponedOLEs;
+
+ struct PostponedMathObjects
+ {
+ SwOLENode* pMathObject;
+ sal_Int8 nMathObjAlignment;
+ };
+ std::vector<PostponedMathObjects> m_aPostponedMaths;
+ /// count charts consistently for unit tests
+ unsigned int m_nChartCount;
+ struct PostponedChart
+ {
+ PostponedChart( const SdrObject* sdrObject, const Size& rSize, const SwFlyFrameFormat* rFrame ) : object(sdrObject), size(rSize), frame(rFrame) {};
+ const SdrObject* object;
+ const Size size;
+ const SwFlyFrameFormat* frame;
+ };
+ std::vector<PostponedChart> m_aPostponedCharts;
+ std::vector<const SdrObject*> m_aPostponedFormControls;
+ std::vector<PostponedDrawing> m_aPostponedActiveXControls;
+ const SwField* m_PendingPlaceholder;
+
+ /// Used to store the parent status of a PostIt (parent/child/neither)
+ enum class ParentStatus
+ {
+ None,
+ IsParent,
+ HasParent
+ };
+ struct PostItDOCXData{
+ sal_Int32 id;
+ sal_Int32 lastParaId = 0; // [MS-DOCX] 2.5.3.1 CT_CommentEx needs paraId attribute
+ ParentStatus parentStatus = ParentStatus::None;
+ size_t parentIndex = 0;
+ };
+ /// Maps postit fields to ID's, used in commentRangeStart/End, commentReference and comment.xml.
+ std::vector<std::pair<const SwPostItField*, PostItDOCXData>> m_postitFields;
+ /// Number of postit fields which already have a commentReference written.
+ unsigned int m_postitFieldsMaxId;
+ int m_anchorId;
+ int m_nextFontId;
+ struct EmbeddedFontRef
+ {
+ OString relId;
+ OString fontKey;
+ };
+
+ TableReference m_tableReference;
+
+ std::map< OUString, EmbeddedFontRef > m_FontFilesMap; // font file url to data
+
+ // Remember first cell (used for default borders/margins) of each table
+ std::vector<ww8::WW8TableNodeInfoInner::Pointer_t> m_TableFirstCells;
+ // Remember last open and closed cells on each level
+ std::vector<sal_Int32> m_LastOpenCell;
+ std::vector<sal_Int32> m_LastClosedCell;
+
+ std::optional<css::drawing::FillStyle> m_oFillStyle;
+ /// If FormatBox() already handled fill style / gradient.
+ bool m_bIgnoreNextFill;
+
+ editeng::WordPageMargins m_pageMargins;
+
+ std::shared_ptr<DocxTableStyleExport> m_pTableStyleExport;
+ // flag to check if auto spacing was set in original file
+ bool m_bParaBeforeAutoSpacing,m_bParaAfterAutoSpacing;
+ // store hardcoded value which was set during import.
+ sal_Int32 m_nParaBeforeSpacing,m_nParaAfterSpacing;
+
+ SdtBlockHelper m_aParagraphSdt;
+ SdtBlockHelper m_aRunSdt;
+
+ /// State of the Fly at current position
+ FlyProcessingState m_nStateOfFlyFrame;
+
+ /// Same as m_aParagraphSdtPrAlias, but its content is available till the SDT is closed.
+ OUString m_aStartedParagraphSdtPrAlias;
+
+ std::vector<std::map<SvxBoxItemLine, css::table::BorderLine2>> m_aTableStyleConfs;
+
+ std::optional<SwLineBreakClear> m_oLineBreakClear;
+
+public:
+ DocxAttributeOutput( DocxExport &rExport, const ::sax_fastparser::FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML );
+
+ virtual ~DocxAttributeOutput() override;
+
+ /// Return the right export class.
+ virtual DocxExport& GetExport() override;
+ const DocxExport& GetExport() const { return const_cast< DocxAttributeOutput* >( this )->GetExport(); }
+
+ /// For e.g. the output of the styles, we need to switch the serializer to another one.
+ void SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer );
+
+ /// Occasionally need to use this serializer from the outside
+ const ::sax_fastparser::FSHelperPtr& GetSerializer( ) const { return m_pSerializer; }
+
+ /// Do we have any footnotes?
+ bool HasFootnotes() const;
+
+ /// Do we have any endnotes?
+ bool HasEndnotes() const;
+
+ /// Output the content of the footnotes.xml resp. endnotes.xml
+ void FootnotesEndnotes( bool bFootnotes );
+
+ /// writes the footnotePr/endnotePr (depending on tag) section
+ static void WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag, const SwEndNoteInfo& info, int listtag );
+
+ bool HasPostitFields() const;
+ enum class hasProperties { no, yes };
+ hasProperties WritePostitFields();
+ void WritePostItFieldsResolved();
+
+ /// VMLTextExport
+ virtual void WriteOutliner(const OutlinerParaObject& rParaObj) override;
+ virtual void WriteVMLTextBox(css::uno::Reference<css::drawing::XShape> xShape) override;
+ /// DMLTextExport
+ virtual void WriteTextBox(css::uno::Reference<css::drawing::XShape> xShape) override;
+ virtual css::uno::Reference<css::text::XTextFrame> GetUnoTextFrame(
+ css::uno::Reference<css::drawing::XShape> xShape) override;
+ virtual oox::drawingml::DrawingML& GetDrawingML() override;
+ virtual bool MaybeOutputBrushItem(SfxItemSet const&) override;
+
+ void BulletDefinition(int nId, const Graphic& rGraphic, Size aSize) override;
+
+ void SetWritingHeaderFooter( bool bWritingHeaderFooter ) { m_bWritingHeaderFooter = bWritingHeaderFooter; }
+ bool GetWritingHeaderFooter( ) const { return m_bWritingHeaderFooter; }
+ void SetAlternateContentChoiceOpen( bool bAltContentChoiceOpen ) { m_bAlternateContentChoiceOpen = bAltContentChoiceOpen; }
+ bool IsAlternateContentChoiceOpen( ) const { return m_bAlternateContentChoiceOpen; }
+ void GetSdtEndBefore(const SdrObject* pSdrObj);
+ bool IsFirstParagraph() const { return m_bIsFirstParagraph; }
+
+ /// Stores the table export state to the passed context and resets own state.
+ void pushToTableExportContext(DocxTableExportContext& rContext);
+ /// Restores from the remembered state.
+ void popFromTableExportContext(DocxTableExportContext const & rContext);
+
+ static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle);
+ static OString convertToOOXMLVertOrient(sal_Int16 nOrient);
+ static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel);
+ static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel);
+ static void ImplCellMargins( sax_fastparser::FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr);
+ template <class... Args>
+ static void AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, Args&&... args)
+ {
+ if (!pAttrList)
+ pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pAttrList->add(std::forward<Args>(args)...);
+ }
+
+ static const sal_Int32 Tag_StartParagraph_1 = 1;
+ static const sal_Int32 Tag_StartParagraph_2 = 2;
+ static const sal_Int32 Tag_WriteSdtBlock = 3;
+ static const sal_Int32 Tag_StartParagraphProperties = 4;
+ static const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
+ static const sal_Int32 Tag_StartRun_1 = 6;
+ static const sal_Int32 Tag_StartRun_2 = 7;
+ static const sal_Int32 Tag_StartRun_3 = 8;
+ static const sal_Int32 Tag_EndRun_1 = 9;
+ static const sal_Int32 Tag_EndRun_2 = 10;
+ static const sal_Int32 Tag_StartRunProperties = 11;
+ static const sal_Int32 Tag_InitCollectedRunProperties = 12;
+ static const sal_Int32 Tag_Redline_1 = 13;
+ static const sal_Int32 Tag_Redline_2 = 14;
+ static const sal_Int32 Tag_TableDefinition = 15;
+ static const sal_Int32 Tag_OutputFlyFrame = 16;
+ static const sal_Int32 Tag_StartSection = 17;
+};
+
+/**
+* All the information that should be stashed away when we're in the middle of
+* of a table export and still have to do something else, e.g. export a shape.
+*/
+struct DocxTableExportContext
+{
+ DocxAttributeOutput& m_rOutput;
+ ww8::WW8TableInfo::Pointer_t m_pTableInfo;
+ bool m_bTableCellOpen;
+ bool m_bStartedParaSdt;
+ bool m_bStartedRunSdt;
+ sal_uInt32 m_nTableDepth;
+ sal_Int32 m_nHyperLinkCount = 0;
+ DocxTableExportContext(DocxAttributeOutput& rOutput) : m_rOutput(rOutput) { m_rOutput.pushToTableExportContext(*this); }
+ ~DocxTableExportContext() { m_rOutput.popFromTableExportContext(*this); }
+};
+
+namespace docx {
+
+rtl::Reference<sax_fastparser::FastAttributeList> SurroundToVMLWrap(SwFormatSurround const& rSurround);
+
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXATTRIBUTEOUTPUT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
new file mode 100644
index 0000000000..e7c1972ce9
--- /dev/null
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -0,0 +1,2179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include "docxexport.hxx"
+#include "docxexportfilter.hxx"
+#include "docxattributeoutput.hxx"
+#include "docxsdrexport.hxx"
+#include "docxhelper.hxx"
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XStreamListener.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/xml/xslt/XSLTTransformer.hpp>
+
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <oox/export/chartexport.hxx>
+#include <oox/export/shapes.hxx>
+#include <oox/export/ThemeExport.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/ole/olestorage.hxx>
+#include <oox/ole/olehelper.hxx>
+
+#include <svx/svdpage.hxx>
+
+#include <map>
+#include <algorithm>
+#include <condition_variable>
+#include <mutex>
+
+#include <IMark.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <docsh.hxx>
+#include <ndtxt.hxx>
+#include "wrtww8.hxx"
+#include <fmtline.hxx>
+#include <fmtpdsc.hxx>
+#include <frmfmt.hxx>
+#include <section.hxx>
+#include <ftninfo.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <redline.hxx>
+#include <swdbdata.hxx>
+#include <drawdoc.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+
+#include "ww8scan.hxx"
+#include <oox/token/properties.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <o3tl/any.hxx>
+#include <sal/log.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace sax_fastparser;
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::oox;
+
+using oox::vml::VMLExport;
+
+using sw::mark::IMark;
+
+AttributeOutputBase& DocxExport::AttrOutput() const
+{
+ return *m_pAttrOutput;
+}
+
+DocxAttributeOutput& DocxExport::DocxAttrOutput() const
+{
+ return *m_pAttrOutput;
+}
+
+MSWordSections& DocxExport::Sections() const
+{
+ return *m_pSections;
+}
+
+bool DocxExport::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich )
+{
+ // TODO FIXME is this actually true for docx? - this is ~copied from WW8
+ if ( nScript == i18n::ScriptType::ASIAN )
+ {
+ // for asian in ww8, there is only one fontsize
+ // and one fontstyle (posture/weight)
+ switch ( nWhich )
+ {
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ return false;
+ default:
+ break;
+ }
+ }
+ else if ( nScript != i18n::ScriptType::COMPLEX )
+ {
+ // for western in ww8, there is only one fontsize
+ // and one fontstyle (posture/weight)
+ switch ( nWhich )
+ {
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ return false;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+void DocxExport::AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pRedlineData )
+{
+ std::vector< OUString > aStarts;
+ std::vector< OUString > aEnds;
+
+ IMarkVector aMarks;
+ if ( GetBookmarks( rNode, nCurrentPos, nCurrentPos + nLen, aMarks ) )
+ {
+ for ( IMark* pMark : aMarks )
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ if ( nStart == nCurrentPos )
+ aStarts.push_back( pMark->GetName() );
+
+ if ( nEnd == nCurrentPos )
+ aEnds.push_back( pMark->GetName() );
+ }
+ }
+
+ const OUString& aStr( rNode.GetText() );
+ const sal_Int32 nEnd = aStr.getLength();
+
+ if ( nCurrentPos == nEnd )
+ m_pAttrOutput->WriteFinalBookmarks_Impl( aStarts, aEnds );
+ else
+ m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds, pRedlineData );
+}
+
+void DocxExport::AppendBookmark( const OUString& rName )
+{
+ std::vector< OUString > aStarts { rName };
+ std::vector< OUString > aEnds { rName };
+
+ m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds );
+}
+
+void DocxExport::AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen )
+{
+ std::vector< OUString > aStarts;
+ std::vector< OUString > aEnds;
+
+ IMarkVector aMarks;
+ if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
+ {
+ for ( IMark* pMark : aMarks )
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ if ( nStart == nCurrentPos )
+ aStarts.push_back( pMark->GetName() );
+
+ if ( nEnd == nCurrentPos )
+ aEnds.push_back( pMark->GetName() );
+ }
+ }
+
+ m_pAttrOutput->WriteAnnotationMarks_Impl( aStarts, aEnds );
+}
+
+void DocxExport::ExportGrfBullet(const SwTextNode&)
+{
+ // Just collect the bullets for now, numbering.xml is not yet started.
+ CollectGrfsOfBullets();
+}
+
+OString DocxExport::AddRelation( const OUString& rType, std::u16string_view rTarget )
+{
+ OUString sId = m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ rType, rTarget, true );
+
+ return sId.toUtf8();
+}
+
+bool DocxExport::DisallowInheritingOutlineNumbering( const SwFormat& rFormat )
+{
+ bool bRet( false );
+
+ if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
+ {
+ if (const SwFormat *pParent = rFormat.DerivedFrom())
+ {
+ if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle())
+ {
+ ::sax_fastparser::FSHelperPtr pSerializer = m_pAttrOutput->GetSerializer( );
+ // Level 9 disables the outline
+ pSerializer->singleElementNS(XML_w, XML_outlineLvl, FSNS(XML_w, XML_val), "9");
+
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void DocxExport::WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
+ const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat,
+ sal_uInt8 nBreakCode, bool bEvenAndOddHeaders )
+{
+ m_nHeadersFootersInSection = 1;
+
+ // document setting indicating the requirement of EVEN and ODD for both headers and footers
+ if ( nHeadFootFlags & ( nsHdFtFlags::WW8_FOOTER_EVEN | nsHdFtFlags::WW8_HEADER_EVEN ) && bEvenAndOddHeaders )
+ m_aSettings.evenAndOddHeaders = true;
+
+ // Turn ON flag for 'Writing Headers \ Footers'
+ m_pAttrOutput->SetWritingHeaderFooter( true );
+
+ const bool bPrevSectionHadHeader = m_bHasHdr;
+ const bool bPrevSectionHadFooter = m_bHasFtr;
+ m_bHasHdr = m_bHasFtr = false;
+
+ // headers
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_EVEN )
+ WriteHeaderFooter( &rLeftHeaderFormat, true, "even" );
+ else if ( m_aSettings.evenAndOddHeaders )
+ {
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD )
+ WriteHeaderFooter( &rFormat, true, "even" );
+ else if (bPrevSectionHadHeader && nBreakCode == 2)
+ WriteHeaderFooter( nullptr, true, "even" );
+ }
+
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD )
+ WriteHeaderFooter( &rFormat, true, "default" );
+ else if (bPrevSectionHadHeader && nBreakCode == 2) // 2: nextPage
+ WriteHeaderFooter(nullptr, true, "default");
+
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_FIRST )
+ WriteHeaderFooter( &rFirstPageFormat, true, "first" );
+ else if (bPrevSectionHadHeader && nBreakCode == 2)
+ WriteHeaderFooter(nullptr, true, "first");
+
+ // footers
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_EVEN )
+ WriteHeaderFooter( &rLeftFooterFormat, false, "even" );
+ else if ( m_aSettings.evenAndOddHeaders )
+ {
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD )
+ WriteHeaderFooter( &rFormat, false, "even" );
+ else if (bPrevSectionHadFooter && nBreakCode == 2)
+ WriteHeaderFooter( nullptr, false, "even");
+ }
+
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD )
+ WriteHeaderFooter( &rFormat, false, "default" );
+ else if (bPrevSectionHadFooter && nBreakCode == 2)
+ WriteHeaderFooter(nullptr, false, "default");
+
+ if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_FIRST )
+ WriteHeaderFooter( &rFirstPageFormat, false, "first" );
+ else if (bPrevSectionHadFooter && nBreakCode == 2)
+ WriteHeaderFooter(nullptr, false, "first");
+
+ // Turn OFF flag for 'Writing Headers \ Footers'
+ m_pAttrOutput->SetWritingHeaderFooter( false );
+}
+
+void DocxExport::OutputField( const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd, FieldFlags nMode )
+{
+ m_pAttrOutput->WriteField_Impl( pField, eFieldType, rFieldCmd, nMode );
+}
+
+void DocxExport::WriteFormData( const ::sw::mark::IFieldmark& rFieldmark )
+{
+ m_pAttrOutput->WriteFormData_Impl( rFieldmark );
+}
+
+void DocxExport::WriteHyperlinkData( const ::sw::mark::IFieldmark& /*rFieldmark*/ )
+{
+ SAL_INFO("sw.ww8", "TODO DocxExport::WriteHyperlinkData().");
+}
+
+void DocxExport::DoComboBox(const OUString& rName,
+ const OUString& rHelp,
+ const OUString& rToolTip,
+ const OUString& rSelected,
+ const uno::Sequence<OUString>& rListItems)
+{
+ m_pDocumentFS->startElementNS(XML_w, XML_ffData);
+
+ m_pDocumentFS->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName);
+
+ m_pDocumentFS->singleElementNS(XML_w, XML_enabled);
+
+ if ( !rHelp.isEmpty() )
+ m_pDocumentFS->singleElementNS(XML_w, XML_helpText, FSNS(XML_w, XML_val), rHelp);
+
+ if ( !rToolTip.isEmpty() )
+ m_pDocumentFS->singleElementNS(XML_w, XML_statusText, FSNS(XML_w, XML_val), rToolTip);
+
+ m_pDocumentFS->startElementNS(XML_w, XML_ddList);
+
+ // Output the 0-based index of the selected value
+ sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
+ if (nId == -1)
+ nId = 0;
+
+ m_pDocumentFS->singleElementNS(XML_w, XML_result, FSNS(XML_w, XML_val), OString::number(nId));
+
+ // unfortunately Word 2013 refuses to load DOCX with more than 25 listEntry
+ SAL_WARN_IF(25 < rListItems.getLength(), "sw.ww8", "DocxExport::DoComboBox data loss with more than 25 entries");
+ auto const nSize(std::min(sal_Int32(25), rListItems.getLength()));
+ for (auto i = 0; i < nSize; ++i)
+ {
+ m_pDocumentFS->singleElementNS(XML_w, XML_listEntry, FSNS(XML_w, XML_val), rListItems[i]);
+ }
+
+ m_pDocumentFS->endElementNS( XML_w, XML_ddList );
+
+ m_pDocumentFS->endElementNS( XML_w, XML_ffData );
+}
+
+void DocxExport::DoFormText(const SwInputField* pField)
+{
+ assert(pField);
+ const OUString sStr = FieldString(ww::eFILLIN) + "\"" + pField->GetPar2() + "\"";
+ OutputField(pField, ww::eFILLIN, sStr);
+}
+
+OString DocxExport::OutputChart( uno::Reference< frame::XModel > const & xModel, sal_Int32 nCount, ::sax_fastparser::FSHelperPtr const & m_pSerializer )
+{
+ OUString aFileName = "charts/chart" + OUString::number(nCount) + ".xml";
+ OUString sId = m_rFilter.addRelation( m_pSerializer->getOutputStream(),
+ oox::getRelationship(Relationship::CHART),
+ aFileName );
+ aFileName = "word/charts/chart" + OUString::number(nCount) + ".xml";
+ ::sax_fastparser::FSHelperPtr pChartFS =
+ m_rFilter.openFragmentStreamWithSerializer( aFileName,
+ "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" );
+
+#if !ENABLE_WASM_STRIP_CHART
+ // WASM_CHART change
+ // TODO: With Chart extracted this cannot really happen since
+ // no Chart could've been added at all
+ oox::drawingml::ChartExport aChartExport(XML_w, pChartFS, xModel, &m_rFilter, oox::drawingml::DOCUMENT_DOCX);
+ css::uno::Reference<css::util::XModifiable> xModifiable(xModel, css::uno::UNO_QUERY);
+ const bool bOldModified = xModifiable && xModifiable->isModified();
+ aChartExport.ExportContent();
+ if (!bOldModified && xModifiable && xModifiable->isModified())
+ // tdf#134973: the model could get modified: e.g., calling XChartDocument::getSubTitle(),
+ // which creates the object if absent, and sets the modified state.
+ xModifiable->setModified(bOldModified);
+#else
+ (void)xModel;
+#endif
+ pChartFS->endDocument();
+ return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
+}
+
+OString DocxExport::WriteOLEObject(SwOLEObj& rObject, OUString & io_rProgID)
+{
+ uno::Reference <embed::XEmbeddedObject> xObj( rObject.GetOleRef() );
+ uno::Reference<uno::XComponentContext> const xContext(
+ GetFilter().getComponentContext());
+
+ OUString sMediaType;
+ OUString sRelationType;
+ OUString sSuffix;
+ const char * pProgID(nullptr);
+
+ uno::Reference<io::XInputStream> const xInStream =
+ oox::GetOLEObjectStream(xContext, xObj, io_rProgID,
+ sMediaType, sRelationType, sSuffix, pProgID);
+
+ if (!xInStream.is())
+ {
+ return OString();
+ }
+
+ assert(!sMediaType.isEmpty());
+ assert(!sRelationType.isEmpty());
+ assert(!sSuffix.isEmpty());
+ OUString sFileName = "embeddings/oleObject" + OUString::number( ++m_nOLEObjects ) + "." + sSuffix;
+ uno::Reference<io::XOutputStream> const xOutStream =
+ GetFilter().openFragmentStream("word/" + sFileName, sMediaType);
+ assert(xOutStream.is()); // no reason why that could fail
+
+ try
+ {
+ comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream);
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.ww8", "DocxExport::WriteOLEObject");
+ return OString();
+ }
+
+ OUString const sId = m_rFilter.addRelation( GetFS()->getOutputStream(),
+ sRelationType, sFileName );
+ if (pProgID)
+ {
+ io_rProgID = OUString::createFromAscii(pProgID);
+ }
+
+ return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
+}
+
+std::pair<OString, OString> DocxExport::WriteActiveXObject(const uno::Reference<drawing::XShape>& rxShape,
+ const uno::Reference<awt::XControlModel>& rxControlModel)
+{
+ ++m_nActiveXControls;
+
+ // Write out ActiveX binary
+ const OUString sBinaryFileName = "word/activeX/activeX" + OUString::number(m_nActiveXControls) + ".bin";
+
+ OString sGUID;
+ OString sName;
+ uno::Reference<io::XStream> xOutStorage(m_rFilter.openFragmentStream(sBinaryFileName, "application/vnd.ms-office.activeX"), uno::UNO_QUERY);
+ if(xOutStorage.is())
+ {
+ oox::ole::OleStorage aOleStorage(m_rFilter.getComponentContext(), xOutStorage, false);
+ uno::Reference<io::XOutputStream> xOutputStream(aOleStorage.openOutputStream("contents"), uno::UNO_SET_THROW);
+ uno::Reference< css::frame::XModel > xModel( m_rDoc.GetDocShell() ? m_rDoc.GetDocShell()->GetModel() : nullptr );
+ oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, rxControlModel);
+ if ( !exportHelper.isValid() )
+ return std::make_pair<OString, OString>(OString(), OString());
+ sGUID = OUStringToOString(exportHelper.getGUID(), RTL_TEXTENCODING_UTF8);
+ sName = OUStringToOString(exportHelper.getName(), RTL_TEXTENCODING_UTF8);
+ exportHelper.exportControl(xOutputStream, rxShape->getSize(), true);
+ aOleStorage.commit();
+ }
+
+ // Write out ActiveX fragment
+ const OUString sXMLFileName = "word/activeX/activeX" + OUString::number( m_nActiveXControls ) + ".xml";
+ ::sax_fastparser::FSHelperPtr pActiveXFS = m_rFilter.openFragmentStreamWithSerializer(sXMLFileName, "application/vnd.ms-office.activeX+xml" );
+
+ const OUString sBinaryId = m_rFilter.addRelation( pActiveXFS->getOutputStream(),
+ oox::getRelationship(Relationship::ACTIVEXCONTROLBINARY),
+ sBinaryFileName.subView(sBinaryFileName.lastIndexOf("/") + 1) );
+
+ pActiveXFS->singleElementNS(XML_ax, XML_ocx,
+ FSNS(XML_xmlns, XML_ax), m_rFilter.getNamespaceURL(OOX_NS(ax)),
+ FSNS(XML_xmlns, XML_r), m_rFilter.getNamespaceURL(OOX_NS(officeRel)),
+ FSNS(XML_ax, XML_classid), "{" + sGUID + "}",
+ FSNS(XML_ax, XML_persistence), "persistStorage",
+ FSNS(XML_r, XML_id), sBinaryId);
+
+ OString sXMLId = OUStringToOString(m_rFilter.addRelation(m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::CONTROL),
+ sXMLFileName.subView(sBinaryFileName.indexOf("/") + 1)),
+ RTL_TEXTENCODING_UTF8);
+
+ pActiveXFS->endDocument();
+
+ return std::pair<OString, OString>(sXMLId, sName);
+}
+
+void DocxExport::OutputDML(uno::Reference<drawing::XShape> const & xShape)
+{
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
+ sal_Int32 nNamespace = XML_wps;
+ if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
+ nNamespace = XML_wpg;
+ else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
+ nNamespace = XML_pic;
+ oox::drawingml::ShapeExport aExport(nNamespace, m_pAttrOutput->GetSerializer(), nullptr, &m_rFilter, oox::drawingml::DOCUMENT_DOCX, m_pAttrOutput.get());
+ aExport.WriteShape(xShape);
+}
+
+ErrCode DocxExport::ExportDocument_Impl()
+{
+ // Set the 'Reviewing' flags in the settings structure
+ m_aSettings.revisionView = m_bOrigShowChanges;
+ m_aSettings.trackRevisions = bool( RedlineFlags::On & m_nOrigRedlineFlags );
+
+ InitStyles();
+
+ // init sections
+ m_pSections.reset(new MSWordSections( *this ));
+
+ auto& rGraphicExportCache = oox::drawingml::GraphicExportCache::get();
+
+ // Make sure images are counted from one, even when exporting multiple documents.
+ rGraphicExportCache.push();
+
+ WriteMainText();
+
+ WriteFootnotesEndnotes();
+
+ WritePostitFields();
+
+ WriteNumbering();
+
+ WriteFonts();
+
+ WriteSettings();
+
+ WriteTheme();
+
+ WriteGlossary();
+
+ WriteCustomXml();
+
+ WriteEmbeddings();
+
+ if (m_bDocm)
+ WriteVBA();
+
+ m_aLinkedTextboxesHelper.clear(); //final cleanup
+ m_pStyles.reset();
+ m_pSections.reset();
+
+ rGraphicExportCache.pop();
+
+ return ERRCODE_NONE;
+}
+
+void DocxExport::AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum )
+{
+ AttrOutput().SectionBreak( msword::PageBreak, false, m_pSections->CurrentSectionInfo() );
+ m_pSections->AppendSection( pPageDesc, pFormat, nLnNum, m_pAttrOutput->IsFirstParagraph() );
+}
+
+void DocxExport::OutputEndNode( const SwEndNode& rEndNode )
+{
+ MSWordExportBase::OutputEndNode( rEndNode );
+
+ if ( TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsSectionNode() )
+ {
+ // this originally comes from WW8Export::WriteText(), and looks like it
+ // could have some code common with SectionNode()...
+
+ const SwSection& rSect = rEndNode.StartOfSectionNode()->GetSectionNode()->GetSection();
+ if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() )
+ m_bStartTOX = false;
+
+ SwNodeIndex aIdx( rEndNode, 1 );
+ const SwNode& rNd = aIdx.GetNode();
+ if ( rNd.IsEndNode() && rNd.StartOfSectionNode()->IsSectionNode() )
+ return;
+
+ bool isInTable = IsInTable();
+ if ( !rNd.IsSectionNode() && isInTable ) // No sections in table
+ {
+ const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent();
+ if( !pParentFormat )
+ pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1));
+
+ sal_uLong nRstLnNum;
+ if( rNd.IsContentNode() )
+ nRstLnNum = rNd.GetContentNode()->GetSwAttrSet().GetLineNumber().GetStartValue();
+ else
+ nRstLnNum = 0;
+
+ AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum );
+ }
+ else
+ {
+ AttrOutput().SectionBreaks( rEndNode );
+ }
+ }
+ else if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode())
+ // End node of a table: see if a section break should be written after the table.
+ AttrOutput().SectionBreaks(rEndNode);
+}
+
+void DocxExport::OutputGrfNode( const SwGrfNode& )
+{
+ SAL_INFO("sw.ww8", "TODO DocxExport::OutputGrfNode( const SwGrfNode& )" );
+}
+
+void DocxExport::OutputOLENode( const SwOLENode& )
+{
+ SAL_INFO("sw.ww8", "TODO DocxExport::OutputOLENode( const SwOLENode& )" );
+}
+
+void DocxExport::OutputLinkedOLE( const OUString& )
+{
+ // Nothing to implement here: WW8 only
+}
+
+sal_uInt64 DocxExport::ReplaceCr( sal_uInt8 )
+{
+ // Completely unused for Docx export... only here for code sharing
+ // purpose with binary export
+ return 0;
+}
+
+void DocxExport::PrepareNewPageDesc( const SfxItemSet* pSet,
+ const SwNode& rNd, const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc, bool bExtraPageBreak )
+{
+ // tell the attribute output that we are ready to write the section
+ // break [has to be output inside paragraph properties]
+ AttrOutput().SectionBreak( msword::PageBreak, false, m_pSections->CurrentSectionInfo(), bExtraPageBreak );
+
+ const SwSectionFormat* pFormat = GetSectionFormat( rNd );
+ const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd );
+
+ OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." );
+
+ if ( pNewPgDescFormat )
+ {
+ m_pSections->AppendSection( *pNewPgDescFormat, rNd, pFormat, nLnNm );
+ }
+ else if ( pNewPgDesc )
+ {
+ m_pSections->AppendSection( pNewPgDesc, rNd, pFormat, nLnNm );
+ }
+
+}
+
+void DocxExport::InitStyles()
+{
+ m_pStyles.reset(new MSWordStyles( *this, /*bListStyles =*/ true ));
+
+ // setup word/styles.xml and the relations + content type
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::STYLES),
+ u"styles.xml" );
+
+ ::sax_fastparser::FSHelperPtr pStylesFS =
+ m_rFilter.openFragmentStreamWithSerializer( "word/styles.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml" );
+
+ // switch the serializer to redirect the output to word/styles.xml
+ m_pAttrOutput->SetSerializer( pStylesFS );
+
+ // do the work
+ m_pStyles->OutputStylesTable();
+
+ // switch the serializer back
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+
+ pStylesFS->endDocument();
+}
+
+void DocxExport::WriteFootnotesEndnotes()
+{
+ if ( m_pAttrOutput->HasFootnotes() )
+ {
+ // setup word/styles.xml and the relations + content type
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::FOOTNOTES),
+ u"footnotes.xml" );
+
+ ::sax_fastparser::FSHelperPtr pFootnotesFS =
+ m_rFilter.openFragmentStreamWithSerializer( "word/footnotes.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" );
+
+ // switch the serializer to redirect the output to word/footnotes.xml
+ m_pAttrOutput->SetSerializer( pFootnotesFS );
+ // tdf#99227
+ m_pSdrExport->setSerializer( pFootnotesFS );
+ // tdf#107969
+ m_pVMLExport->SetFS(pFootnotesFS);
+
+ // do the work
+ m_pAttrOutput->FootnotesEndnotes( true );
+
+ // switch the serializer back
+ m_pVMLExport->SetFS(m_pDocumentFS);
+ m_pSdrExport->setSerializer( m_pDocumentFS );
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+
+ pFootnotesFS->endDocument();
+ }
+
+ if ( !m_pAttrOutput->HasEndnotes() )
+ return;
+
+ // setup word/styles.xml and the relations + content type
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::ENDNOTES),
+ u"endnotes.xml" );
+
+ ::sax_fastparser::FSHelperPtr pEndnotesFS =
+ m_rFilter.openFragmentStreamWithSerializer( "word/endnotes.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml" );
+
+ // switch the serializer to redirect the output to word/endnotes.xml
+ m_pAttrOutput->SetSerializer( pEndnotesFS );
+ // tdf#99227
+ m_pSdrExport->setSerializer( pEndnotesFS );
+ // tdf#107969
+ m_pVMLExport->SetFS(pEndnotesFS);
+
+ // do the work
+ m_pAttrOutput->FootnotesEndnotes( false );
+
+ // switch the serializer back
+ m_pVMLExport->SetFS(m_pDocumentFS);
+ m_pSdrExport->setSerializer( m_pDocumentFS );
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+
+ pEndnotesFS->endDocument();
+}
+
+void DocxExport::WritePostitFields()
+{
+ if ( !m_pAttrOutput->HasPostitFields() )
+ return;
+
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::COMMENTS),
+ u"comments.xml" );
+
+ ::sax_fastparser::FSHelperPtr pPostitFS =
+ m_rFilter.openFragmentStreamWithSerializer( "word/comments.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" );
+
+ pPostitFS->startElementNS( XML_w, XML_comments, MainXmlNamespaces());
+ m_pAttrOutput->SetSerializer( pPostitFS );
+ const auto eHasProperties = m_pAttrOutput->WritePostitFields();
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+ pPostitFS->endElementNS( XML_w, XML_comments );
+ pPostitFS->endDocument();
+
+ if (eHasProperties != DocxAttributeOutput::hasProperties::yes)
+ return;
+
+ m_rFilter.addRelation(m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::COMMENTSEXTENDED),
+ u"commentsExtended.xml");
+
+ pPostitFS = m_rFilter.openFragmentStreamWithSerializer(
+ "word/commentsExtended.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml");
+
+ pPostitFS->startElementNS(XML_w15, XML_commentsEx, // Add namespaces manually now
+ FSNS(XML_xmlns, XML_mc), m_rFilter.getNamespaceURL(OOX_NS(mce)),
+ FSNS(XML_xmlns, XML_w15), m_rFilter.getNamespaceURL(OOX_NS(w15)),
+ FSNS(XML_mc, XML_Ignorable), "w15");
+ m_pAttrOutput->SetSerializer(pPostitFS);
+ m_pAttrOutput->WritePostItFieldsResolved();
+ m_pAttrOutput->SetSerializer(m_pDocumentFS);
+ pPostitFS->endElementNS(XML_w15, XML_commentsEx);
+ pPostitFS->endDocument();
+}
+
+void DocxExport::WriteNumbering()
+{
+ if ( !m_pUsedNumTable )
+ return; // no numbering is used
+
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::NUMBERING),
+ u"numbering.xml" );
+
+ ::sax_fastparser::FSHelperPtr pNumberingFS = m_rFilter.openFragmentStreamWithSerializer( "word/numbering.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml" );
+
+ // switch the serializer to redirect the output to word/numbering.xml
+ m_pAttrOutput->SetSerializer( pNumberingFS );
+ m_pDrawingML->SetFS( pNumberingFS );
+
+ pNumberingFS->startElementNS( XML_w, XML_numbering,
+ FSNS( XML_xmlns, XML_w ), m_rFilter.getNamespaceURL(OOX_NS(doc)),
+ FSNS( XML_xmlns, XML_o ), m_rFilter.getNamespaceURL(OOX_NS(vmlOffice)),
+ FSNS( XML_xmlns, XML_r ), m_rFilter.getNamespaceURL(OOX_NS(officeRel)),
+ FSNS( XML_xmlns, XML_v ), m_rFilter.getNamespaceURL(OOX_NS(vml)),
+ FSNS( XML_xmlns, XML_mc ), m_rFilter.getNamespaceURL(OOX_NS(mce)),
+ FSNS( XML_xmlns, XML_w14 ), m_rFilter.getNamespaceURL(OOX_NS(w14)),
+ FSNS( XML_mc, XML_Ignorable ), "w14" );
+
+ BulletDefinitions();
+
+ AbstractNumberingDefinitions();
+
+ NumberingDefinitions();
+
+ pNumberingFS->endElementNS( XML_w, XML_numbering );
+
+ // switch the serializer back
+ m_pDrawingML->SetFS( m_pDocumentFS );
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+
+ pNumberingFS->endDocument();
+}
+
+void DocxExport::WriteHeaderFooter( const SwFormat* pFormat, bool bHeader, const char* pType )
+{
+ // setup the xml stream
+ OUString aRelId;
+ ::sax_fastparser::FSHelperPtr pFS;
+ if ( bHeader )
+ {
+ OUString aName( "header" + OUString::number( ++m_nHeaders ) + ".xml" );
+
+ aRelId = m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::HEADER),
+ aName );
+
+ pFS = m_rFilter.openFragmentStreamWithSerializer( "word/" + aName,
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml" );
+
+ pFS->startElementNS( XML_w, XML_hdr, MainXmlNamespaces());
+ }
+ else
+ {
+ OUString aName( "footer" + OUString::number( ++m_nFooters ) + ".xml" );
+
+ aRelId = m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::FOOTER),
+ aName );
+
+ pFS = m_rFilter.openFragmentStreamWithSerializer( "word/" + aName,
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml" );
+
+ pFS->startElementNS( XML_w, XML_ftr, MainXmlNamespaces());
+ }
+
+ // switch the serializer to redirect the output to word/styles.xml
+ m_pAttrOutput->SetSerializer( pFS );
+ m_pVMLExport->SetFS( pFS );
+ m_pSdrExport->setSerializer(pFS);
+ SetFS( pFS );
+ {
+ DocxTableExportContext aTableExportContext(*m_pAttrOutput);
+ // do the work
+ if (pFormat == nullptr)
+ AttrOutput().EmptyParagraph();
+ else
+ WriteHeaderFooterText(*pFormat, bHeader);
+ m_pAttrOutput->EndParaSdtBlock();
+ }
+
+ // switch the serializer back
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+ m_pVMLExport->SetFS( m_pDocumentFS );
+ m_pSdrExport->setSerializer(m_pDocumentFS);
+ SetFS( m_pDocumentFS );
+
+ // close the tag
+ sal_Int32 nReference;
+ if ( bHeader )
+ {
+ pFS->endElementNS( XML_w, XML_hdr );
+ nReference = XML_headerReference;
+ }
+ else
+ {
+ pFS->endElementNS( XML_w, XML_ftr );
+ nReference = XML_footerReference;
+ }
+
+ // and write the reference
+ m_pDocumentFS->singleElementNS( XML_w, nReference,
+ FSNS( XML_w, XML_type ), pType,
+ FSNS( XML_r, XML_id ), aRelId );
+
+ pFS->endDocument();
+}
+
+void DocxExport::WriteFonts()
+{
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::FONTTABLE),
+ u"fontTable.xml" );
+
+ ::sax_fastparser::FSHelperPtr pFS = m_rFilter.openFragmentStreamWithSerializer(
+ "word/fontTable.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" );
+
+ pFS->startElementNS( XML_w, XML_fonts,
+ FSNS( XML_xmlns, XML_w ), m_rFilter.getNamespaceURL(OOX_NS(doc)),
+ FSNS( XML_xmlns, XML_r ), m_rFilter.getNamespaceURL(OOX_NS(officeRel)) );
+
+ // switch the serializer to redirect the output to word/styles.xml
+ m_pAttrOutput->SetSerializer( pFS );
+
+ // do the work
+ m_aFontHelper.WriteFontTable( *m_pAttrOutput );
+
+ // switch the serializer back
+ m_pAttrOutput->SetSerializer( m_pDocumentFS );
+
+ pFS->endElementNS( XML_w, XML_fonts );
+
+ pFS->endDocument();
+}
+
+void DocxExport::WriteProperties( )
+{
+ // Write the core properties
+ SwDocShell* pDocShell( m_rDoc.GetDocShell( ) );
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ bool bSecurityOptOpenReadOnly = false;
+ if ( pDocShell )
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel( ), uno::UNO_QUERY );
+ xDocProps = xDPS->getDocumentProperties();
+ bSecurityOptOpenReadOnly = pDocShell->IsSecurityOptOpenReadOnly();
+ }
+
+ m_rFilter.exportDocumentProperties( xDocProps, bSecurityOptOpenReadOnly );
+}
+
+void DocxExport::WriteDocVars(const sax_fastparser::FSHelperPtr& pFS)
+{
+ SwDocShell* pDocShell = m_rDoc.GetDocShell();
+ if (!pDocShell)
+ {
+ return;
+ }
+
+ uno::Reference<text::XTextFieldsSupplier> xModel(pDocShell->GetModel(), uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTextFieldMasters = xModel->getTextFieldMasters();
+ uno::Sequence<rtl::OUString> aMasterNames = xTextFieldMasters->getElementNames();
+ if (!aMasterNames.hasElements())
+ {
+ return;
+ }
+
+ // Only write docVars if there will be at least a single docVar.
+ bool bStarted = false;
+ constexpr OUString aPrefix(u"com.sun.star.text.fieldmaster.User."_ustr);
+ for (const auto& rMasterName : std::as_const(aMasterNames))
+ {
+ if (!rMasterName.startsWith(aPrefix))
+ {
+ // Not a user field.
+ continue;
+ }
+
+ uno::Reference<beans::XPropertySet> xField;
+ xTextFieldMasters->getByName(rMasterName) >>= xField;
+ if (!xField.is())
+ {
+ continue;
+ }
+
+ OUString aKey = rMasterName.copy(aPrefix.getLength());
+ OUString aValue;
+ xField->getPropertyValue("Content") >>= aValue;
+ if (!bStarted)
+ {
+ bStarted = true;
+ pFS->startElementNS(XML_w, XML_docVars);
+ }
+ pFS->singleElementNS(XML_w, XML_docVar, FSNS(XML_w, XML_name), aKey,
+ FSNS(XML_w, XML_val), aValue);
+ }
+
+ if (bStarted)
+ {
+ pFS->endElementNS(XML_w, XML_docVars);
+ }
+}
+
+static auto
+WriteCompat(SwDoc const& rDoc, ::sax_fastparser::FSHelperPtr const& rpFS,
+ sal_Int32 & rTargetCompatibilityMode) -> void
+{
+ const IDocumentSettingAccess& rIDSA = rDoc.getIDocumentSettingAccess();
+ if (!rIDSA.get(DocumentSettingId::ADD_EXT_LEADING))
+ {
+ rpFS->singleElementNS(XML_w, XML_noLeading);
+ if (rTargetCompatibilityMode > 14)
+ { // Word ignores noLeading in compatibilityMode 15
+ rTargetCompatibilityMode = 14;
+ }
+ }
+ // Do not justify lines with manual break
+ if (rIDSA.get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK))
+ {
+ rpFS->singleElementNS(XML_w, XML_doNotExpandShiftReturn);
+ }
+ // tdf#146515 export "Use printer metrics for document formatting"
+ if (!rIDSA.get(DocumentSettingId::USE_VIRTUAL_DEVICE))
+ rpFS->singleElementNS(XML_w, XML_usePrinterMetrics);
+
+ if (rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES))
+ {
+ // Map the DoNotBreakWrappedTables compat flag to <w:doNotBreakWrappedTables>.
+ rpFS->singleElementNS(XML_w, XML_doNotBreakWrappedTables);
+ }
+}
+
+void DocxExport::WriteSettings()
+{
+ SwViewShell *pViewShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ if( !pViewShell && !m_aSettings.hasData() && !m_pAttrOutput->HasFootnotes() && !m_pAttrOutput->HasEndnotes())
+ return;
+
+ SwDocShell* pDocShell = m_rDoc.GetDocShell();
+
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::SETTINGS),
+ u"settings.xml" );
+
+ ::sax_fastparser::FSHelperPtr pFS = m_rFilter.openFragmentStreamWithSerializer(
+ "word/settings.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" );
+
+ pFS->startElementNS( XML_w, XML_settings,
+ FSNS( XML_xmlns, XML_w ), m_rFilter.getNamespaceURL(OOX_NS(doc)) );
+
+ // Write protection
+ const uno::Sequence<beans::PropertyValue> aInfo = pDocShell->GetModifyPasswordInfo();
+ if (aInfo.hasElements())
+ {
+ OUString sAlgorithm, sSalt, sHash;
+ sal_Int32 nCount = 0;
+ for (const auto& prop : aInfo)
+ {
+ if (prop.Name == "algorithm-name")
+ prop.Value >>= sAlgorithm;
+ else if (prop.Name == "salt")
+ prop.Value >>= sSalt;
+ else if (prop.Name == "iteration-count")
+ prop.Value >>= nCount;
+ else if (prop.Name == "hash")
+ prop.Value >>= sHash;
+ }
+ if (!sAlgorithm.isEmpty() && !sSalt.isEmpty() && !sHash.isEmpty())
+ {
+ sal_Int32 nAlgorithmSid = 0;
+ if (sAlgorithm == "MD2")
+ nAlgorithmSid = 1;
+ else if (sAlgorithm == "MD4")
+ nAlgorithmSid = 2;
+ else if (sAlgorithm == "MD5")
+ nAlgorithmSid = 3;
+ else if (sAlgorithm == "SHA-1")
+ nAlgorithmSid = 4;
+ else if (sAlgorithm == "MAC")
+ nAlgorithmSid = 5;
+ else if (sAlgorithm == "RIPEMD")
+ nAlgorithmSid = 6;
+ else if (sAlgorithm == "RIPEMD-160")
+ nAlgorithmSid = 7;
+ else if (sAlgorithm == "HMAC")
+ nAlgorithmSid = 9;
+ else if (sAlgorithm == "SHA-256")
+ nAlgorithmSid = 12;
+ else if (sAlgorithm == "SHA-384")
+ nAlgorithmSid = 13;
+ else if (sAlgorithm == "SHA-512")
+ nAlgorithmSid = 14;
+
+ if (nAlgorithmSid != 0)
+ pFS->singleElementNS(XML_w, XML_writeProtection,
+ FSNS(XML_w, XML_cryptProviderType), "rsaAES",
+ FSNS(XML_w, XML_cryptAlgorithmClass), "hash",
+ FSNS(XML_w, XML_cryptAlgorithmType), "typeAny",
+ FSNS(XML_w, XML_cryptAlgorithmSid), OString::number(nAlgorithmSid),
+ FSNS(XML_w, XML_cryptSpinCount), OString::number(nCount),
+ FSNS(XML_w, XML_hash), sHash,
+ FSNS(XML_w, XML_salt), sSalt);
+ }
+ }
+
+ // View
+ if (pViewShell && pViewShell->GetViewOptions()->getBrowseMode())
+ {
+ pFS->singleElementNS(XML_w, XML_view, FSNS(XML_w, XML_val), "web");
+ }
+
+ // Zoom
+ if (pViewShell)
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList(
+ sax_fastparser::FastSerializerHelper::createAttrList());
+
+ switch (pViewShell->GetViewOptions()->GetZoomType())
+ {
+ case SvxZoomType::WHOLEPAGE:
+ pAttributeList->add(FSNS(XML_w, XML_val), "fullPage");
+ break;
+ case SvxZoomType::PAGEWIDTH:
+ pAttributeList->add(FSNS(XML_w, XML_val), "bestFit");
+ break;
+ case SvxZoomType::OPTIMAL:
+ pAttributeList->add(FSNS(XML_w, XML_val), "textFit");
+ break;
+ default:
+ break;
+ }
+
+ OString aZoom(OString::number(pViewShell->GetViewOptions()->GetZoom()));
+ pAttributeList->add(FSNS(XML_w, XML_percent), aZoom);
+ pFS->singleElementNS(XML_w, XML_zoom, pAttributeList);
+ }
+
+ // Display Background Shape
+ if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush)
+ {
+ // Turn on the 'displayBackgroundShape'
+ pFS->singleElementNS(XML_w, XML_displayBackgroundShape);
+ }
+
+ // Track Changes
+ if ( !m_aSettings.revisionView )
+ pFS->singleElementNS( XML_w, XML_revisionView,
+ FSNS( XML_w, XML_insDel ), "0",
+ FSNS( XML_w, XML_formatting ), "0" );
+
+ if ( m_aSettings.trackRevisions )
+ pFS->singleElementNS(XML_w, XML_trackRevisions);
+
+ // Mirror Margins
+ if(isMirroredMargin())
+ pFS->singleElementNS(XML_w, XML_mirrorMargins);
+
+ if (m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
+ {
+ pFS->singleElementNS(XML_w, XML_gutterAtTop);
+ }
+
+ // Embed Fonts
+ if( m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS ))
+ pFS->singleElementNS(XML_w, XML_embedTrueTypeFonts);
+
+ // Embed System Fonts
+ if( m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::EMBED_SYSTEM_FONTS ))
+ pFS->singleElementNS(XML_w, XML_embedSystemFonts);
+
+ // Default Tab Stop
+ if( m_aSettings.defaultTabStop != 0 )
+ pFS->singleElementNS( XML_w, XML_defaultTabStop, FSNS( XML_w, XML_val ),
+ OString::number(m_aSettings.defaultTabStop) );
+
+ // export current mail merge database and table names
+ SwDBData aData = m_rDoc.GetDBData();
+ if ( !aData.sDataSource.isEmpty() && aData.nCommandType == css::sdb::CommandType::TABLE && !aData.sCommand.isEmpty() )
+ {
+ OUString sDataSource =
+ "SELECT * FROM " +
+ aData.sDataSource + // current database
+ ".dbo." + // default database owner
+ aData.sCommand + // sheet name
+ "$"; // sheet identifier
+ pFS->startElementNS( XML_w, XML_mailMerge );
+ pFS->singleElementNS(XML_w, XML_mainDocumentType,
+ FSNS( XML_w, XML_val ), "formLetters" );
+ pFS->singleElementNS(XML_w, XML_dataType,
+ FSNS( XML_w, XML_val ), "textFile" );
+ pFS->singleElementNS( XML_w, XML_query,
+ FSNS( XML_w, XML_val ), sDataSource );
+ pFS->endElementNS( XML_w, XML_mailMerge );
+ }
+
+ // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer.
+ // Set it's value to "auto" and disable on paragraph level, if no hyphenation is used there.
+ pFS->singleElementNS(XML_w, XML_autoHyphenation, FSNS(XML_w, XML_val), "true");
+
+ // Hyphenation details set depending on default style
+ SwTextFormatColl* pColl = m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, /*bRegardLanguage=*/false);
+ const SvxHyphenZoneItem* pZoneItem;
+ if (pColl && (pZoneItem = pColl->GetItemIfSet(RES_PARATR_HYPHENZONE, false)))
+ {
+ if (pZoneItem->IsNoCapsHyphenation())
+ pFS->singleElementNS(XML_w, XML_doNotHyphenateCaps);
+
+ if ( sal_Int16 nHyphenZone = pZoneItem->GetTextHyphenZone() )
+ pFS->singleElementNS(XML_w, XML_hyphenationZone, FSNS(XML_w, XML_val),
+ OString::number(nHyphenZone));
+ }
+
+ // Even and Odd Headers
+ if( m_aSettings.evenAndOddHeaders )
+ pFS->singleElementNS(XML_w, XML_evenAndOddHeaders);
+
+ // Has Footnotes
+ if( m_pAttrOutput->HasFootnotes())
+ DocxAttributeOutput::WriteFootnoteEndnotePr( pFS, XML_footnotePr, m_rDoc.GetFootnoteInfo(), XML_footnote );
+
+ // Has Endnotes
+ if( m_pAttrOutput->HasEndnotes())
+ DocxAttributeOutput::WriteFootnoteEndnotePr( pFS, XML_endnotePr, m_rDoc.GetEndNoteInfo(), XML_endnote );
+
+ // Has themeFontLang information
+ uno::Reference< beans::XPropertySet > xPropSet( pDocShell->GetBaseModel(), uno::UNO_QUERY_THROW );
+
+ bool bUseGrabBagProtection = false;
+ bool bWriterWantsToProtect = false;
+ bool bWriterWantsToProtectForm = false;
+ bool bWriterWantsToProtectRedline = false;
+ bool bHasDummyRedlineProtectionKey = false;
+ bool bReadOnlyStatusUnchanged = true;
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ if ( m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM) ||
+ m_pSections->DocumentIsProtected() )
+ {
+ bWriterWantsToProtect = bWriterWantsToProtectForm = true;
+ }
+ if ( xPropSetInfo->hasPropertyByName( "RedlineProtectionKey" ) )
+ {
+ uno::Sequence<sal_Int8> aKey;
+ xPropSet->getPropertyValue( "RedlineProtectionKey" ) >>= aKey;
+ bool bHasRedlineProtectionKey = aKey.hasElements();
+ bHasDummyRedlineProtectionKey = aKey.getLength() == 1 && aKey[0] == 1;
+ if ( bHasRedlineProtectionKey && !bHasDummyRedlineProtectionKey )
+ bWriterWantsToProtect = bWriterWantsToProtectRedline = true;
+ }
+
+ /* Compatibility Mode (tdf#131304)
+ * 11: .doc level [Word 97-2003]
+ * 12: .docx default [Word 2007] [LO < 7.0] [ECMA 376 1st ed.]
+ * 14: [Word 2010]
+ * 15: [Word 2013/2016/2019] [LO >= 7.0]
+ *
+ * The PRIMARY purpose of compatibility mode does not seem to be related to layout etc.
+ * Its focus is on sharing files between multiple users, tracking the lowest supported mode in the group.
+ * It is to BENEFIT older programs by not using certain new features that they don't understand.
+ *
+ * The next time the compat mode needs to be changed, I foresee the following steps:
+ * 1.) Accept the new mode: Start round-tripping the new value, indicating we understand that format.
+ * 2.) Many years later, change the TargetCompatilityMode for new documents, when we no longer care
+ * about working with perfect compatibility with older versions of MS Word.
+ */
+ sal_Int32 nTargetCompatibilityMode =
+ (GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION)
+ ? 12 : 15; //older versions might not open our files well
+ bool bHasCompatibilityMode = false;
+ const OUString aGrabBagName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
+ if ( xPropSetInfo->hasPropertyByName( aGrabBagName ) )
+ {
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( aGrabBagName ) >>= propList;
+
+ for( const auto& rProp : std::as_const(propList) )
+ {
+ if ( rProp.Name == "ThemeFontLangProps" )
+ {
+ uno::Sequence< beans::PropertyValue > themeFontLangProps;
+ rProp.Value >>= themeFontLangProps;
+ OUString aValues[3];
+ for( const auto& rThemeFontLangProp : std::as_const(themeFontLangProps) )
+ {
+ if( rThemeFontLangProp.Name == "val" )
+ rThemeFontLangProp.Value >>= aValues[0];
+ else if( rThemeFontLangProp.Name == "eastAsia" )
+ rThemeFontLangProp.Value >>= aValues[1];
+ else if( rThemeFontLangProp.Name == "bidi" )
+ rThemeFontLangProp.Value >>= aValues[2];
+ }
+ pFS->singleElementNS( XML_w, XML_themeFontLang,
+ FSNS( XML_w, XML_val ), aValues[0],
+ FSNS( XML_w, XML_eastAsia ), aValues[1],
+ FSNS( XML_w, XML_bidi ), aValues[2] );
+ }
+ else if ( rProp.Name == "CompatSettings" )
+ {
+ pFS->startElementNS(XML_w, XML_compat);
+
+ WriteCompat(m_rDoc, pFS, nTargetCompatibilityMode);
+
+ uno::Sequence< beans::PropertyValue > aCompatSettingsSequence;
+ rProp.Value >>= aCompatSettingsSequence;
+
+ for(const auto& rCompatSetting : std::as_const(aCompatSettingsSequence))
+ {
+ uno::Sequence< beans::PropertyValue > aCompatSetting;
+ rCompatSetting.Value >>= aCompatSetting;
+ OUString aName;
+ OUString aUri;
+ OUString aValue;
+
+ for(const auto& rPropVal : std::as_const(aCompatSetting))
+ {
+ if( rPropVal.Name == "name" )
+ rPropVal.Value >>= aName;
+ else if( rPropVal.Name == "uri" )
+ rPropVal.Value >>= aUri;
+ else if( rPropVal.Name == "val" )
+ rPropVal.Value >>= aValue;
+ }
+ if ( aName == "compatibilityMode" )
+ {
+ bHasCompatibilityMode = true;
+ // Among the group of programs sharing this document, the lowest mode is retained.
+ // Reduce this number if we are not comfortable with the new/unknown mode yet.
+ // Step 1 in accepting a new mode would be to comment out the following clause
+ // and roundtrip the new value instead of overwriting with the older number.
+ // There are no newer modes at the time this code was written.
+ if ( aValue.toInt32() > nTargetCompatibilityMode )
+ aValue = OUString::number(nTargetCompatibilityMode);
+ }
+
+ pFS->singleElementNS( XML_w, XML_compatSetting,
+ FSNS( XML_w, XML_name ), aName,
+ FSNS( XML_w, XML_uri ), aUri,
+ FSNS( XML_w, XML_val ), aValue);
+ }
+
+ if ( !bHasCompatibilityMode )
+ {
+ pFS->singleElementNS( XML_w, XML_compatSetting,
+ FSNS( XML_w, XML_name ), "compatibilityMode",
+ FSNS( XML_w, XML_uri ), "http://schemas.microsoft.com/office/word",
+ FSNS( XML_w, XML_val ), OString::number(nTargetCompatibilityMode));
+ bHasCompatibilityMode = true;
+ }
+
+ pFS->endElementNS( XML_w, XML_compat );
+ }
+ else if (rProp.Name == "DocumentProtection")
+ {
+ uno::Sequence< beans::PropertyValue > rAttributeList;
+ rProp.Value >>= rAttributeList;
+
+ if (rAttributeList.hasElements())
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> xAttributeList = sax_fastparser::FastSerializerHelper::createAttrList();
+ bool bIsProtectionTrackChanges = false;
+ // if grabbag protection is not enforced, allow Writer protection to override
+ bool bEnforced = false;
+ for (const auto& rAttribute : std::as_const(rAttributeList))
+ {
+ static DocxStringTokenMap const aTokens[] =
+ {
+ { "edit", XML_edit },
+ { "enforcement", XML_enforcement },
+ { "formatting", XML_formatting },
+ { "cryptProviderType", XML_cryptProviderType },
+ { "cryptAlgorithmClass", XML_cryptAlgorithmClass },
+ { "cryptAlgorithmType", XML_cryptAlgorithmType },
+ { "cryptAlgorithmSid", XML_cryptAlgorithmSid },
+ { "cryptSpinCount", XML_cryptSpinCount },
+ { "hash", XML_hash },
+ { "salt", XML_salt },
+ { nullptr, 0 }
+ };
+
+ if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
+ {
+ OUString sValue = rAttribute.Value.get<OUString>();
+ xAttributeList->add(FSNS(XML_w, nToken), sValue.toUtf8());
+ if ( nToken == XML_edit && sValue == "trackedChanges" )
+ bIsProtectionTrackChanges = true;
+ else if ( nToken == XML_edit && sValue == "readOnly" )
+ {
+ // Ignore the case where read-only was not enforced, but now is. That is handled by _MarkAsFinal
+ bReadOnlyStatusUnchanged = pDocShell->IsSecurityOptOpenReadOnly();
+ }
+ else if ( nToken == XML_enforcement )
+ bEnforced = sValue.toBoolean();
+ }
+ }
+
+ // we have document protection from input DOCX file
+ if ( !bEnforced )
+ {
+ // Leave as an un-enforced suggestion if Writer doesn't want to set any enforcement
+ bUseGrabBagProtection = !bWriterWantsToProtect;
+ }
+ else
+ {
+ // Check if the grabbag protection is still valid
+ // In the case of change tracking protection, we didn't modify it
+ // and in the case of read-only, we didn't modify it.
+ bUseGrabBagProtection = (!bIsProtectionTrackChanges || bHasDummyRedlineProtectionKey)
+ && bReadOnlyStatusUnchanged;
+ }
+
+ if ( bUseGrabBagProtection )
+ {
+ pFS->singleElementNS(XML_w, XML_documentProtection, xAttributeList);
+ }
+
+ }
+ }
+ else if (rProp.Name == "HyphenationZone")
+ {
+ sal_Int16 nHyphenationZone = *o3tl::doAccess<sal_Int16>(rProp.Value);
+ if (nHyphenationZone > 0)
+ pFS->singleElementNS(XML_w, XML_hyphenationZone, FSNS(XML_w, XML_val),
+ OString::number(nHyphenationZone));
+ }
+ }
+ }
+ if ( !bHasCompatibilityMode )
+ {
+ pFS->startElementNS(XML_w, XML_compat);
+
+ WriteCompat(m_rDoc, pFS, nTargetCompatibilityMode);
+
+ pFS->singleElementNS( XML_w, XML_compatSetting,
+ FSNS( XML_w, XML_name ), "compatibilityMode",
+ FSNS( XML_w, XML_uri ), "http://schemas.microsoft.com/office/word",
+ FSNS( XML_w, XML_val ), OString::number(nTargetCompatibilityMode));
+
+ const IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::ALLOW_TEXT_AFTER_FLOATING_TABLE_BREAK))
+ {
+ // AllowTextAfterFloatingTableBreak doesn't have its own XML element, it's a
+ // <w:compatSetting> with a specific name.
+ pFS->singleElementNS(XML_w, XML_compatSetting,
+ FSNS(XML_w, XML_name), "allowTextAfterFloatingTableBreak",
+ FSNS(XML_w, XML_uri), "http://schemas.microsoft.com/office/word",
+ FSNS(XML_w, XML_val), "1");
+ }
+
+ pFS->endElementNS( XML_w, XML_compat );
+ }
+
+ WriteDocVars(pFS);
+
+ if ( !bUseGrabBagProtection )
+ {
+ // Protect form - highest priority
+ // Section-specific write protection
+ if ( bWriterWantsToProtectForm )
+ {
+ // we have form protection from Writer or from input ODT file
+
+ pFS->singleElementNS(XML_w, XML_documentProtection,
+ FSNS(XML_w, XML_edit), "forms",
+ FSNS(XML_w, XML_enforcement), "true");
+ }
+ // Protect Change Tracking - next priority
+ else if ( bWriterWantsToProtectRedline )
+ {
+ // we have change tracking protection from Writer or from input ODT file
+
+ pFS->singleElementNS(XML_w, XML_documentProtection,
+ FSNS(XML_w, XML_edit), "trackedChanges",
+ FSNS(XML_w, XML_enforcement), "1");
+ }
+ }
+
+ // finish settings.xml
+ pFS->endElementNS( XML_w, XML_settings );
+
+ pFS->endDocument();
+}
+
+void DocxExport::WriteTheme()
+{
+ SdrModel* pModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ if (!pModel)
+ return;
+ auto const& pTheme = pModel->getTheme();
+ if (!pTheme)
+ return;
+
+ m_rFilter.addRelation(m_pDocumentFS->getOutputStream(), oox::getRelationship(Relationship::THEME), u"theme/theme1.xml" );
+
+ oox::ThemeExport aThemeExport(&m_rFilter, oox::drawingml::DOCUMENT_DOCX);
+ aThemeExport.write(u"word/theme/theme1.xml"_ustr, *pTheme);
+}
+
+// See OOXMLDocumentImpl::resolveGlossaryStream
+void DocxExport::WriteGlossary()
+{
+ uno::Reference< beans::XPropertySet > xPropSet( m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
+ if ( !xPropSetInfo->hasPropertyByName( aName ) )
+ return;
+
+ uno::Reference<xml::dom::XDocument> glossaryDocDom;
+ uno::Sequence< uno::Sequence<beans::NamedValue> > glossaryDomList;
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( aName ) >>= propList;
+ sal_Int32 collectedProperties = 0;
+ for ( const auto& rProp : std::as_const(propList) )
+ {
+ OUString propName = rProp.Name;
+ if ( propName == "OOXGlossary" )
+ {
+ rProp.Value >>= glossaryDocDom;
+ collectedProperties++;
+ }
+ if (propName == "OOXGlossaryDom")
+ {
+ rProp.Value >>= glossaryDomList;
+ collectedProperties++;
+ }
+ if (collectedProperties == 2)
+ break;
+ }
+
+ // no glossary dom to write
+ if ( !glossaryDocDom.is() )
+ return;
+
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::GLOSSARYDOCUMENT),
+ u"glossary/document.xml" );
+
+ uno::Reference< io::XOutputStream > xOutputStream = GetFilter().openFragmentStream( "word/glossary/document.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml" );
+
+ uno::Reference< xml::sax::XSAXSerializable > serializer( glossaryDocDom, uno::UNO_QUERY );
+ uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() );
+ writer->setOutputStream( xOutputStream );
+ serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ),
+ uno::Sequence< beans::StringPair >() );
+
+ for (const uno::Sequence<beans::NamedValue>& glossaryElement : glossaryDomList)
+ {
+ OUString gTarget, gType, gId, contentType, targetMode;
+ uno::Reference<xml::dom::XDocument> xDom;
+ for (const auto& [name, value] : glossaryElement)
+ {
+ if (name == "Id")
+ value >>= gId;
+ else if (name == "Type")
+ value >>= gType;
+ else if (name == "Target")
+ value >>= gTarget;
+ else if (name == "TargetMode")
+ value >>= targetMode;
+ else if (name == "_contentType")
+ value >>= contentType;
+ else if (name == "_relDom")
+ value >>= xDom;
+ }
+ if (gId.isEmpty() || gType.isEmpty() || gTarget.isEmpty())
+ continue;
+ const bool bExternal = targetMode == "External";
+ if (!bExternal && !xDom)
+ {
+ // Some internal relation, but we didn't create a DOM for it
+ // in OOXMLDocumentImpl::resolveGlossaryStream?
+ SAL_WARN("sw.ww8", "Glossary internal relation without DOM: Id=\"" + gId
+ + "\" Type=\"" + gType + "\" Target=\"" + gTarget + "\"");
+ continue;
+ }
+ gId = gId.copy(3); //"rId" only save the numeric value
+
+ PropertySet aProps(xOutputStream);
+ aProps.setAnyProperty( PROP_RelId, uno::Any( gId.toInt32() ));
+ m_rFilter.addRelation(xOutputStream, gType, gTarget, bExternal);
+ if (!xDom)
+ continue; // External relation, no stream to write
+ uno::Reference< xml::sax::XSAXSerializable > gserializer( xDom, uno::UNO_QUERY );
+ writer->setOutputStream(GetFilter().openFragmentStream( "word/glossary/" + gTarget, contentType ) );
+ gserializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ),
+ uno::Sequence< beans::StringPair >() );
+ }
+}
+
+namespace {
+ class XsltTransformListener : public ::cppu::WeakImplHelper<io::XStreamListener>
+ {
+ public:
+ XsltTransformListener() : m_bDone(false) {}
+
+ void wait() {
+ std::unique_lock<std::mutex> g(m_mutex);
+ m_cond.wait(g, [this]() { return m_bDone; });
+ }
+
+ private:
+ std::mutex m_mutex;
+ std::condition_variable m_cond;
+ bool m_bDone;
+
+ virtual void SAL_CALL disposing(const lang::EventObject&) noexcept override {}
+ virtual void SAL_CALL started() noexcept override {}
+ virtual void SAL_CALL closed() noexcept override { notifyDone(); }
+ virtual void SAL_CALL terminated() noexcept override { notifyDone(); }
+ virtual void SAL_CALL error(const uno::Any& e) override
+ {
+ notifyDone(); // set on error too, otherwise main thread waits forever
+ SAL_WARN("sw.ww8", e);
+ }
+
+ void notifyDone() {
+ std::scoped_lock<std::mutex> g(m_mutex);
+ m_bDone = true;
+ m_cond.notify_all();
+ }
+ };
+}
+
+static void lcl_UpdateXmlValues(const SdtData& sdtData, const uno::Reference<css::io::XInputStream>& xInputStream, const uno::Reference<css::io::XOutputStream>& xOutputStream)
+{
+ uno::Sequence<uno::Any> aArgs{
+ // XSLT transformation stylesheet:
+ // - write all elements as is
+ // - but if element matches sdtData.xpath, replace its text content by sdtData.xpath
+ uno::Any(beans::NamedValue("StylesheetText", uno::Any(OUString("<?xml version=\"1.0\" encoding=\"UTF-8\"?> \
+<xsl:stylesheet\
+ xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\
+ " + sdtData.namespaces + "\
+ version=\"1.0\">\
+ <xsl:template match=\"@* | node()\">\
+ <xsl:copy>\
+ <xsl:apply-templates select=\"@* | node()\"/>\
+ </xsl:copy>\
+ </xsl:template>\
+ <xsl:template match = \"" + sdtData.xpath + "\">\
+ <xsl:copy>\
+ <xsl:text>" + sdtData.data + "</xsl:text>\
+ </xsl:copy>\
+ </xsl:template>\
+</xsl:stylesheet>\
+"))))
+ };
+
+ css::uno::Reference<css::xml::xslt::XXSLTTransformer> xTransformer =
+ css::xml::xslt::XSLTTransformer::create(comphelper::getProcessComponentContext(), aArgs);
+ xTransformer->setInputStream(xInputStream);
+ xTransformer->setOutputStream(xOutputStream);
+
+ rtl::Reference<XsltTransformListener> xListener = new XsltTransformListener();
+ xTransformer->addListener(xListener);
+
+ xTransformer->start();
+ xListener->wait();
+}
+
+void DocxExport::WriteCustomXml()
+{
+ uno::Reference< beans::XPropertySet > xPropSet( m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ if ( !xPropSetInfo->hasPropertyByName( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) )
+ return;
+
+ uno::Sequence<uno::Reference<xml::dom::XDocument> > customXmlDomlist;
+ uno::Sequence<uno::Reference<xml::dom::XDocument> > customXmlDomPropslist;
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList;
+ auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXCustomXml"; });
+ if (pProp != std::cend(propList))
+ pProp->Value >>= customXmlDomlist;
+
+ pProp = std::find_if(std::cbegin(propList), std::cend(propList),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXCustomXmlProps"; });
+ if (pProp != std::cend(propList))
+ pProp->Value >>= customXmlDomPropslist;
+
+ for (sal_Int32 j = 0; j < customXmlDomlist.getLength(); j++)
+ {
+ uno::Reference<xml::dom::XDocument> customXmlDom = customXmlDomlist[j];
+ uno::Reference<xml::dom::XDocument> customXmlDomProps = customXmlDomPropslist[j];
+ if (customXmlDom.is())
+ {
+ m_rFilter.addRelation( m_pDocumentFS->getOutputStream(),
+ oox::getRelationship(Relationship::CUSTOMXML),
+ Concat2View("../customXml/item"+OUString::number(j+1)+".xml" ));
+
+ uno::Reference< xml::sax::XSAXSerializable > serializer( customXmlDom, uno::UNO_QUERY );
+ uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() );
+
+ uno::Reference < css::io::XOutputStream > xOutStream = GetFilter().openFragmentStream("customXml/item" + OUString::number(j + 1) + ".xml",
+ "application/xml");
+ if (m_SdtData.size())
+ {
+ // There are some SDT blocks data with data bindings which can update some custom xml values
+ uno::Reference< io::XStream > xMemStream(
+ comphelper::getProcessComponentContext()->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream",
+ comphelper::getProcessComponentContext()),
+ uno::UNO_QUERY_THROW);
+
+ writer->setOutputStream(xMemStream->getOutputStream());
+
+ serializer->serialize(uno::Reference< xml::sax::XDocumentHandler >(writer, uno::UNO_QUERY_THROW),
+ uno::Sequence< beans::StringPair >());
+
+ uno::Reference< io::XStream > xXSLTInStream = xMemStream;
+ uno::Reference< io::XStream > xXSLTOutStream;
+ // Apply XSLT transformations for each SDT data binding
+ // Seems it is not possible to do this as one transformation: each data binding
+ // can have different namespaces, but with conflicting names (ns0, ns1, etc..)
+ for (size_t i = 0; i < m_SdtData.size(); i++)
+ {
+ if (i == m_SdtData.size() - 1)
+ {
+ // last transformation
+ lcl_UpdateXmlValues(m_SdtData[i], xXSLTInStream->getInputStream(), xOutStream);
+ }
+ else
+ {
+ xXSLTOutStream.set(
+ comphelper::getProcessComponentContext()->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream",
+ comphelper::getProcessComponentContext()),
+ uno::UNO_QUERY_THROW);
+ lcl_UpdateXmlValues(m_SdtData[i], xXSLTInStream->getInputStream(), xXSLTOutStream->getOutputStream());
+ // Use previous output as an input for next run
+ xXSLTInStream.set( xXSLTOutStream );
+ }
+ }
+
+ }
+ else
+ {
+ writer->setOutputStream(xOutStream);
+
+ serializer->serialize(uno::Reference< xml::sax::XDocumentHandler >(writer, uno::UNO_QUERY_THROW),
+ uno::Sequence< beans::StringPair >());
+ }
+ }
+
+ if (customXmlDomProps.is())
+ {
+ uno::Reference< xml::sax::XSAXSerializable > serializer( customXmlDomProps, uno::UNO_QUERY );
+ uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() );
+ writer->setOutputStream( GetFilter().openFragmentStream( "customXml/itemProps"+OUString::number(j+1)+".xml",
+ "application/vnd.openxmlformats-officedocument.customXmlProperties+xml" ) );
+ serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ),
+ uno::Sequence< beans::StringPair >() );
+
+ // Adding itemprops's relationship entry to item.xml.rels file
+ m_rFilter.addRelation( GetFilter().openFragmentStream( "customXml/item"+OUString::number(j+1)+".xml",
+ "application/xml" ) ,
+ oox::getRelationship(Relationship::CUSTOMXMLPROPS),
+ Concat2View("itemProps"+OUString::number(j+1)+".xml" ));
+ }
+ }
+}
+
+void DocxExport::WriteVBA()
+{
+ uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ if (!xStorageBasedDocument.is())
+ return;
+
+ uno::Reference<embed::XStorage> xDocumentStorage = xStorageBasedDocument->getDocumentStorage();
+ OUString aMacrosName("_MS_VBA_Macros");
+ if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aMacrosName))
+ return;
+
+ const sal_Int32 nOpenMode = embed::ElementModes::READ;
+ uno::Reference<io::XStream> xMacrosStream = xDocumentStorage->openStreamElement(aMacrosName, nOpenMode);
+ uno::Reference<io::XOutputStream> xProjectStream;
+ if (xMacrosStream.is())
+ {
+ // First handle the project stream, this sets xProjectStream.
+ std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xMacrosStream));
+
+ xProjectStream = GetFilter().openFragmentStream("word/vbaProject.bin", "application/vnd.ms-office.vbaProject");
+ uno::Reference<io::XStream> xOutputStream(xProjectStream, uno::UNO_QUERY);
+ if (!xOutputStream.is())
+ return;
+ std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream));
+
+ // Write the stream.
+ pOut->WriteStream(*pIn);
+
+ // Write the relationship.
+ m_rFilter.addRelation(m_pDocumentFS->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin");
+ }
+
+ OUString aDataName("_MS_VBA_Macros_XML");
+ if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aDataName))
+ return;
+
+ uno::Reference<io::XStream> xDataStream = xDocumentStorage->openStreamElement(aDataName, nOpenMode);
+ if (!xDataStream.is())
+ return;
+
+ // Then the data stream, which wants to work with an already set
+ // xProjectStream.
+ std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xDataStream));
+
+ uno::Reference<io::XStream> xOutputStream(GetFilter().openFragmentStream("word/vbaData.xml", "application/vnd.ms-word.vbaData+xml"), uno::UNO_QUERY);
+ if (!xOutputStream.is())
+ return;
+ std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream));
+
+ // Write the stream.
+ pOut->WriteStream(*pIn);
+
+ // Write the relationship.
+ if (!xProjectStream.is())
+ return;
+
+ m_rFilter.addRelation(xProjectStream, oox::getRelationship(Relationship::WORDVBADATA), u"vbaData.xml");
+}
+
+void DocxExport::WriteEmbeddings()
+{
+ uno::Reference< beans::XPropertySet > xPropSet( m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
+ if ( !xPropSetInfo->hasPropertyByName( aName ) )
+ return;
+
+ uno::Sequence< beans::PropertyValue > embeddingsList;
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( aName ) >>= propList;
+ auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXEmbeddings"; });
+ if (pProp != std::cend(propList))
+ pProp->Value >>= embeddingsList;
+ for (const auto& rEmbedding : std::as_const(embeddingsList))
+ {
+ OUString embeddingPath = rEmbedding.Name;
+ uno::Reference<io::XInputStream> embeddingsStream;
+ rEmbedding.Value >>= embeddingsStream;
+ if (!embeddingsStream)
+ continue;
+
+ OUString contentType;
+ if (css::uno::Reference<css::beans::XPropertySet> xProps{ embeddingsStream,
+ css::uno::UNO_QUERY })
+ {
+ try
+ {
+ const css::uno::Any val = xProps->getPropertyValue("MediaType");
+ val >>= contentType;
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.ww8", "WriteEmbeddings: Embedding without MediaType");
+ }
+ }
+
+ if (contentType.isEmpty())
+ {
+ // FIXME: this .xlsm hack is silly - if anything the mime-type for an existing embedded object should be read from [Content_Types].xml
+ if (embeddingPath.endsWith(".xlsm"))
+ contentType = "application/vnd.ms-excel.sheet.macroEnabled.12";
+ else if (embeddingPath.endsWith(".bin"))
+ contentType = "application/vnd.openxmlformats-officedocument.oleObject";
+ else
+ contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ }
+
+ uno::Reference< io::XOutputStream > xOutStream = GetFilter().openFragmentStream(embeddingPath,
+ contentType);
+ try
+ {
+ // tdf#131288: the stream must be seekable for direct access
+ uno::Reference<io::XSeekable> xSeekable(embeddingsStream, uno::UNO_QUERY);
+ if (xSeekable)
+ xSeekable->seek(0); // tdf#131288: a previous save could position it elsewhere
+ comphelper::OStorageHelper::CopyInputToOutput(embeddingsStream, xOutStream);
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.ww8", "WriteEmbeddings() ::Failed to copy Inputstream to outputstream exception caught");
+ }
+ xOutStream->closeOutput();
+ }
+}
+
+bool DocxExport::isMirroredMargin()
+{
+ bool bMirroredMargins = false;
+ if ( UseOnPage::Mirror == (UseOnPage::Mirror & m_rDoc.GetPageDesc(0).ReadUseOn()) )
+ {
+ bMirroredMargins = true;
+ }
+ return bMirroredMargins;
+}
+
+void DocxExport::WriteMainText()
+{
+ // setup the namespaces
+ m_pDocumentFS->startElementNS( XML_w, XML_document, MainXmlNamespaces());
+
+ // reset the incrementing linked-textboxes chain ID before re-saving.
+ m_nLinkedTextboxesChainId=0;
+ m_aLinkedTextboxesHelper.clear();
+
+ // Write background page color
+ if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush)
+ {
+ Color backgroundColor = oBrush->GetColor();
+ OString aBackgroundColorStr = msfilter::util::ConvertColor(backgroundColor);
+
+ m_pDocumentFS->singleElementNS(XML_w, XML_background, FSNS(XML_w, XML_color),
+ aBackgroundColorStr);
+ }
+
+ // body
+ m_pDocumentFS->startElementNS(XML_w, XML_body);
+
+ m_pCurPam->GetPoint()->Assign(*m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode());
+
+ // the text
+ WriteText();
+
+ // clear linked textboxes since old ones can't be linked to frames in a different section (correct?)
+ m_aLinkedTextboxesHelper.clear();
+
+ // the last section info
+ m_pAttrOutput->EndParaSdtBlock();
+ const WW8_SepInfo *pSectionInfo = m_pSections? m_pSections->CurrentSectionInfo(): nullptr;
+ if ( pSectionInfo )
+ SectionProperties( *pSectionInfo );
+
+ // finish body and document
+ m_pDocumentFS->endElementNS( XML_w, XML_body );
+ m_pDocumentFS->endElementNS( XML_w, XML_document );
+}
+
+rtl::Reference<FastAttributeList> DocxExport::MainXmlNamespaces()
+{
+ rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
+ pAttr->add( FSNS( XML_xmlns, XML_o ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(vmlOffice)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_r ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_v ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(vml)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_w ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(doc)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_w10 ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(vmlWord)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_wp ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(dmlWordDr)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_wps ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(wps)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_wpg ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(wpg)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_mc ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_wp14 ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(wp14)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_w14 ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(w14)), RTL_TEXTENCODING_UTF8) );
+ pAttr->add( FSNS( XML_xmlns, XML_w15 ), OUStringToOString(m_rFilter.getNamespaceURL(OOX_NS(w15)), RTL_TEXTENCODING_UTF8));
+ pAttr->add( FSNS( XML_mc, XML_Ignorable ), "w14 wp14 w15" );
+ return pAttr;
+}
+
+bool DocxExport::ignoreAttributeForStyleDefaults( sal_uInt16 nWhich ) const
+{
+ if( nWhich == RES_TEXTGRID )
+ return true; // w:docGrid is written only to document.xml, not to styles.xml
+ if (nWhich == RES_PARATR_HYPHENZONE)
+ return true; // w:suppressAutoHyphens is only a formatting exception, not a default
+ return MSWordExportBase::ignoreAttributeForStyleDefaults( nWhich );
+}
+
+sal_Int32 DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp,
+ bool bNeedsLastParaId)
+{
+ const EditTextObject& rEditObj = rParaObj.GetTextObject();
+ MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp );
+
+ sal_Int32 nPara = rEditObj.GetParagraphCount();
+ sal_Int32 nParaId = 0;
+ for( sal_Int32 n = 0; n < nPara; ++n )
+ {
+ if( n )
+ aAttrIter.NextPara( n );
+
+ nParaId = AttrOutput().StartParagraph(ww8::WW8TableNodeInfo::Pointer_t(),
+ bNeedsLastParaId && n == nPara - 1);
+ rtl_TextEncoding eChrSet = aAttrIter.GetNodeCharSet();
+ OUString aStr( rEditObj.GetText( n ));
+ sal_Int32 nCurrentPos = 0;
+ const sal_Int32 nEnd = aStr.getLength();
+
+ // Write paragraph properties.
+ AttrOutput().StartParagraphProperties();
+ aAttrIter.OutParaAttr(/*bCharAttr=*/false);
+ SfxItemSet aParagraphMarkerProperties(m_rDoc.GetAttrPool());
+ AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr);
+
+ do {
+ AttrOutput().StartRun( nullptr, 0 );
+ const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
+ rtl_TextEncoding eNextChrSet = aAttrIter.GetNextCharSet();
+
+ bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos );
+ if( !bTextAtr )
+ {
+ if( nCurrentPos == 0 && nNextAttr - nCurrentPos == aStr.getLength())
+ AttrOutput().RunText( aStr, eChrSet );
+ else
+ {
+ OUString tmp( aStr.copy( nCurrentPos, nNextAttr - nCurrentPos ));
+ AttrOutput().RunText( tmp, eChrSet );
+ }
+ }
+ AttrOutput().StartRunProperties();
+ aAttrIter.OutAttr( nCurrentPos );
+ AttrOutput().EndRunProperties( nullptr );
+
+ nCurrentPos = nNextAttr;
+ eChrSet = eNextChrSet;
+ aAttrIter.NextPos();
+
+ AttrOutput().EndRun( nullptr, 0, -1 );
+
+ } while( nCurrentPos < nEnd );
+
+ AttrOutput().EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t());
+ }
+ return nParaId;
+}
+
+//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
+//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
+//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
+sal_Int32 DocxExport::getWordCompatibilityModeFromGrabBag() const
+{
+ sal_Int32 nWordCompatibilityMode = -1;
+ uno::Reference< beans::XPropertySet > xPropSet(m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
+ if (xPropSetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
+ {
+ uno::Sequence< beans::PropertyValue > propList;
+ xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList;
+
+ for (const auto& rProp : std::as_const(propList))
+ {
+ if (rProp.Name == "CompatSettings")
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings;
+ rProp.Value >>= aCurrentCompatSettings;
+
+ for (const auto& rCurrentCompatSetting : std::as_const(aCurrentCompatSettings))
+ {
+ uno::Sequence< beans::PropertyValue > aCompatSetting;
+ rCurrentCompatSetting.Value >>= aCompatSetting;
+
+ OUString sName;
+ OUString sUri;
+ OUString sVal;
+
+ for (const auto& rPropVal : std::as_const(aCompatSetting))
+ {
+ if ( rPropVal.Name == "name" ) rPropVal.Value >>= sName;
+ if ( rPropVal.Name == "uri" ) rPropVal.Value >>= sUri;
+ if ( rPropVal.Name == "val" ) rPropVal.Value >>= sVal;
+ }
+
+ if (sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word")
+ {
+ const sal_Int32 nValidMode = sVal.toInt32();
+ // if repeated, highest mode wins in MS Word. 11 is the first valid mode.
+ if (nValidMode > 10 && nValidMode > nWordCompatibilityMode)
+ nWordCompatibilityMode = nValidMode;
+
+ }
+ }
+ }
+ }
+ }
+
+ return nWordCompatibilityMode;
+}
+
+void DocxExport::SetFS( ::sax_fastparser::FSHelperPtr const & pFS )
+{
+ mpFS = pFS;
+}
+
+DocxExport::DocxExport(DocxExportFilter& rFilter, SwDoc& rDocument,
+ std::shared_ptr<SwUnoCursor> & pCurrentPam,
+ SwPaM& rOriginalPam, bool bDocm, bool bTemplate)
+ : MSWordExportBase(rDocument, pCurrentPam, &rOriginalPam),
+ m_rFilter( rFilter ),
+ m_nHeaders( 0 ),
+ m_nFooters( 0 ),
+ m_nOLEObjects( 0 ),
+ m_nActiveXControls( 0 ),
+ m_nHeadersFootersInSection(0),
+ m_bDocm(bDocm),
+ m_bTemplate(bTemplate),
+ m_pAuthorIDs(new SvtSecurityMapPersonalInfo)
+{
+ // Write the document properties
+ WriteProperties( );
+
+ // relations for the document
+ m_rFilter.addRelation( oox::getRelationship(Relationship::OFFICEDOCUMENT),
+ u"word/document.xml" );
+
+ // Set media type depending of document type
+ OUString aMediaType;
+ if (m_bDocm)
+ {
+ if (m_bTemplate)
+ {
+ aMediaType = "application/vnd.ms-word.template.macroEnabledTemplate.main+xml";
+ }
+ else
+ {
+ aMediaType = "application/vnd.ms-word.document.macroEnabled.main+xml";
+ }
+ }
+ else
+ {
+ if (m_bTemplate)
+ {
+ aMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml";
+ }
+ else
+ {
+ aMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml";
+ }
+ }
+
+
+ // the actual document
+ m_pDocumentFS = m_rFilter.openFragmentStreamWithSerializer( "word/document.xml", aMediaType );
+
+ SetFS(m_pDocumentFS);
+
+ // the DrawingML access
+ m_pDrawingML.reset(new oox::drawingml::DrawingML(m_pDocumentFS, &m_rFilter, oox::drawingml::DOCUMENT_DOCX));
+
+ // the attribute output for the document
+ m_pAttrOutput.reset(new DocxAttributeOutput( *this, m_pDocumentFS, m_pDrawingML.get() ));
+
+ // the related VMLExport
+ m_pVMLExport.reset(new VMLExport( m_pDocumentFS, m_pAttrOutput.get() ));
+
+ // the related drawing export
+ m_pSdrExport.reset(new DocxSdrExport( *this, m_pDocumentFS, m_pDrawingML.get() ));
+}
+
+DocxExport::~DocxExport()
+{
+ m_pDocumentFS->endDocument();
+}
+
+DocxSettingsData::DocxSettingsData()
+: evenAndOddHeaders( false )
+, defaultTabStop( 0 )
+, revisionView( true )
+, trackRevisions( false )
+{
+}
+
+bool DocxSettingsData::hasData() const
+{
+ if( evenAndOddHeaders )
+ return true;
+ if( defaultTabStop != 0 )
+ return true;
+ if ( !revisionView )
+ return true;
+ if ( trackRevisions )
+ return true;
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx
new file mode 100644
index 0000000000..95da64d244
--- /dev/null
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORT_HXX
+
+#include "wrtww8.hxx"
+
+#include <sal/log.hxx>
+#include <sax/fshelper.hxx>
+#include <rtl/ustring.hxx>
+
+#include <memory>
+#include <ndole.hxx>
+#include <unotools/securityoptions.hxx>
+
+class DocxAttributeOutput;
+class DocxExportFilter;
+class SwNode;
+class SwEndNode;
+class SwTableNode;
+class SwTextNode;
+class SwGrfNode;
+class SwOLENode;
+class DocxSdrExport;
+
+namespace oox {
+ namespace drawingml { class DrawingML; }
+ namespace vml { class VMLExport; }
+}
+
+namespace com::sun::star {
+ namespace frame { class XModel; }
+ namespace drawing { class XShape; }
+ namespace awt { class XControlModel; }
+}
+
+/// Data to be written in the document settings part of the document
+struct DocxSettingsData
+{
+ DocxSettingsData();
+ bool hasData() const; /// returns true if there are any non-default settings (i.e. something to write)
+ bool evenAndOddHeaders;
+ int defaultTabStop;
+ bool revisionView; // don't show tracked changes
+ bool trackRevisions; // Should 'Track Revisions' be set
+};
+
+/// Data to keep and write to XMLs
+struct SdtData
+{
+ OUString namespaces;
+ OUString xpath;
+ OUString data;
+};
+
+/// The class that does all the actual DOCX export-related work.
+class DocxExport : public MSWordExportBase
+{
+ /// Pointer to the filter that owns us.
+ DocxExportFilter& m_rFilter;
+
+ /// Fast serializer for the document output.
+ ::sax_fastparser::FSHelperPtr m_pDocumentFS;
+
+ /// Fast serializer to output the data.
+ ::sax_fastparser::FSHelperPtr mpFS;
+
+ /// Access to the DrawingML writer.
+ std::unique_ptr<oox::drawingml::DrawingML> m_pDrawingML;
+
+ /// Attribute output for document.
+ std::unique_ptr<DocxAttributeOutput> m_pAttrOutput;
+
+ /// Sections/headers/footers
+ std::unique_ptr<MSWordSections> m_pSections;
+
+ /// Header counter.
+ sal_Int32 m_nHeaders;
+
+ /// Footer counter.
+ sal_Int32 m_nFooters;
+
+ /// OLE objects counter.
+ sal_Int32 m_nOLEObjects;
+
+ /// ActiveX controls counter
+ sal_Int32 m_nActiveXControls;
+
+ ///Footer and Header counter in Section properties
+ sal_Int32 m_nHeadersFootersInSection;
+
+ /// Exporter of the VML shapes.
+ std::unique_ptr<oox::vml::VMLExport> m_pVMLExport;
+
+ /// Exporter of drawings.
+ std::unique_ptr<DocxSdrExport> m_pSdrExport;
+
+ /// If the result will be a .docm file or not.
+ bool m_bDocm;
+
+ /// Export is done into template (.dotx)
+ bool const m_bTemplate;
+
+ DocxSettingsData m_aSettings;
+
+ /// Pointer to the Frame of a floating table it is nested in
+ const ww8::Frame *m_pFloatingTableFrame = nullptr;
+
+ /// Map authors to remove personal info
+ std::unique_ptr<SvtSecurityMapPersonalInfo> m_pAuthorIDs;
+
+ /// Storage for sdt data which need to be written to other XMLs
+ std::vector<SdtData> m_SdtData;
+
+public:
+
+ DocxExportFilter& GetFilter() { return m_rFilter; };
+ const DocxExportFilter& GetFilter() const { return m_rFilter; };
+
+ const ww8::Frame* GetFloatingTableFrame() const { return m_pFloatingTableFrame; }
+
+ /// Access to the attribute output class.
+ virtual AttributeOutputBase& AttrOutput() const override;
+
+ /// Access to the derived attribute output class.
+ DocxAttributeOutput& DocxAttrOutput() const;
+
+ /// Access to the sections/headers/footres.
+ virtual MSWordSections& Sections() const override;
+
+ virtual bool FieldsQuoted() const override { return true; }
+
+ virtual bool AddSectionBreaksForTOX() const override { return true; }
+
+ virtual bool ignoreAttributeForStyleDefaults( sal_uInt16 nWhich ) const override;
+
+ virtual bool PreferPageBreakBefore() const override { return false; }
+
+ /// Guess the script (asian/western).
+ virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) override;
+
+ virtual void AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pRedlineData = nullptr ) override;
+
+ virtual void AppendBookmark( const OUString& rName ) override;
+
+ virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) override;
+
+ virtual void ExportGrfBullet(const SwTextNode&) override;
+
+ /// Returns the relationd id
+ OString AddRelation( const OUString& rType, std::u16string_view rTarget );
+
+ virtual void WriteCR( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t()*/ ) override { /* FIXME no-op for docx, most probably should not even be in MSWordExportBase */ }
+ virtual void WriteChar( sal_Unicode ) override { SAL_WARN("sw.ww8", "FIXME: WriteChar() has nothing to do for docx."); }
+
+ /// Return value indicates if an inherited outline numbering is suppressed.
+ virtual bool DisallowInheritingOutlineNumbering( const SwFormat &rFormat ) override;
+
+ /// Output the actual headers and footers.
+ virtual void WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
+ const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat,
+ sal_uInt8 nBreakCode, bool bEvenAndOddHeaders ) override;
+
+ /// Write the field
+ virtual void OutputField( const SwField* pField, ww::eField eFieldType,
+ const OUString& rFieldCmd, FieldFlags nMode = FieldFlags::All ) override;
+
+ /// Write the data of the form field
+ virtual void WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) override;
+ virtual void WriteHyperlinkData( const ::sw::mark::IFieldmark& rFieldmark ) override;
+
+ virtual void DoComboBox(const OUString &rName,
+ const OUString &rHelp,
+ const OUString &ToolTip,
+ const OUString &rSelected,
+ const css::uno::Sequence<OUString> &rListItems) override;
+
+ virtual void DoFormText(const SwInputField * pField) override;
+
+ virtual sal_uInt64 ReplaceCr( sal_uInt8 nChar ) override;
+
+ /// Returns the relationd id
+ OString OutputChart( css::uno::Reference< css::frame::XModel > const & xModel, sal_Int32 nCount, ::sax_fastparser::FSHelperPtr const & m_pSerializer );
+ OString WriteOLEObject(SwOLEObj& rObject, OUString & io_rProgID);
+ std::pair<OString,OString> WriteActiveXObject(const uno::Reference<css::drawing::XShape>& rxShape,
+ const uno::Reference<awt::XControlModel>& rxControlModel);
+
+ /// Writes the shape using drawingML syntax.
+ void OutputDML( css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ sal_Int32 WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp, bool bNeedsLastParaId);
+
+ virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOCX; }
+
+ void AddSdtData(const OUString & namespaces, const OUString & xpath, const OUString & data)
+ {
+ m_SdtData.push_back({ namespaces, xpath, data });
+ }
+
+protected:
+ /// Format-dependent part of the actual export.
+ virtual ErrCode ExportDocument_Impl() override;
+
+ /// Output SwEndNode
+ virtual void OutputEndNode( const SwEndNode& ) override;
+
+ /// Output SwGrfNode
+ virtual void OutputGrfNode( const SwGrfNode& ) override;
+
+ /// Output SwOLENode
+ virtual void OutputOLENode( const SwOLENode& ) override;
+
+ virtual void OutputLinkedOLE( const OUString& ) override;
+
+ virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) override;
+
+ virtual void SectionBreaksAndFrames( const SwTextNode& /*rNode*/ ) override {}
+
+ /// Get ready for a new section.
+ virtual void PrepareNewPageDesc( const SfxItemSet* pSet,
+ const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc,
+ bool bExtraPageBreak = false) override;
+
+private:
+ /// Setup pStyles and write styles.xml
+ void InitStyles();
+
+ /// Write footnotes.xml and endnotes.xml.
+ void WriteFootnotesEndnotes();
+
+ /// Write comments.xml
+ void WritePostitFields();
+
+ /// Write the numbering table.
+ virtual void WriteNumbering() override;
+
+ /// Write reference to a header/footer + the actual xml containing the text.
+ void WriteHeaderFooter( const SwFormat* pFormat, bool bHeader, const char* pType );
+
+ /// Write word/fontTable.xml.
+ void WriteFonts();
+
+ /// Write docProps/core.xml
+ void WriteProperties();
+
+ /// Write word/settings.xml
+ void WriteSettings();
+
+ /// Writes the <w:docVars> part of settings.xml
+ void WriteDocVars(const sax_fastparser::FSHelperPtr& pFS);
+
+ /// Write word/theme/theme1.xml
+ void WriteTheme();
+
+ void WriteGlossary();
+
+ /// Write customXml/item[n].xml and customXml/itemProps[n].xml
+ void WriteCustomXml();
+
+ /// Write word/embeddings/Worksheet[n].xlsx
+ void WriteEmbeddings();
+
+ /// Writes word/vbaProject.bin.
+ void WriteVBA();
+
+ /// return true if Page Layout is set as Mirrored
+ bool isMirroredMargin();
+
+public:
+ /// All xml namespaces to be used at the top of any text .xml file (main doc, headers, footers,...)
+ rtl::Reference<sax_fastparser::FastAttributeList> MainXmlNamespaces();
+
+ /// FIXME this is temporary, remotely reminding the method of the same
+ /// name in WW8Export.
+ void WriteMainText();
+
+ /// Pass the pDocument, pCurrentPam and pOriginalPam to the base class.
+ DocxExport( DocxExportFilter& rFilter, SwDoc& rDocument,
+ std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM& rOriginalPam,
+ bool bDocm, bool bTemplate);
+
+ /// Destructor.
+ virtual ~DocxExport() override;
+
+ /// Reference to the VMLExport instance for the main document.
+ oox::vml::VMLExport& VMLExporter() { return *m_pVMLExport; }
+
+ /// Reference to the DocxSdrExport instance for the main document.
+ DocxSdrExport& SdrExporter() { return *m_pSdrExport; }
+
+ /// Set the document default tab stop.
+ void setDefaultTabStop( int stop ) { m_aSettings.defaultTabStop = stop; }
+
+ const ::sax_fastparser::FSHelperPtr& GetFS() const { return mpFS; }
+
+ void SetFS(::sax_fastparser::FSHelperPtr const & mpFS);
+
+ void SetFloatingTableFrame(const ww8::Frame* pF) { m_pFloatingTableFrame = pF; }
+
+ // Get author id to remove personal info
+ size_t GetInfoID( const OUString sPersonalInfo ) const { return m_pAuthorIDs->GetInfoID(sPersonalInfo); }
+
+ // needed in docxsdrexport.cxx and docxattributeoutput.cxx
+ sal_Int32 getWordCompatibilityModeFromGrabBag() const;
+
+private:
+ DocxExport( const DocxExport& ) = delete;
+
+ DocxExport& operator=( const DocxExport& ) = delete;
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxexportfilter.cxx b/sw/source/filter/ww8/docxexportfilter.cxx
new file mode 100644
index 0000000000..04a5d01505
--- /dev/null
+++ b/sw/source/filter/ww8/docxexportfilter.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "docxexportfilter.hxx"
+#include "docxexport.hxx"
+
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <PostItMgr.hxx>
+#include <unotxdoc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <viewsh.hxx>
+
+#include <unotools/mediadescriptor.hxx>
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+
+DocxExportFilter::DocxExportFilter( const uno::Reference< uno::XComponentContext >& xContext )
+ : oox::core::XmlFilterBase( xContext )
+{
+}
+
+bool DocxExportFilter::exportDocument()
+{
+ // get SwDoc*
+ uno::Reference< uno::XInterface > xIfc( getModel(), uno::UNO_QUERY );
+ SwXTextDocument *pTextDoc = dynamic_cast< SwXTextDocument * >( xIfc.get() );
+ if ( !pTextDoc )
+ return false;
+
+ SwDoc *pDoc = pTextDoc->GetDocShell()->GetDoc();
+ if ( !pDoc )
+ return false;
+
+ // update layout (if present), for SwWriteTable
+ SwViewShell* pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if (pViewShell != nullptr)
+ pViewShell->CalcLayout();
+
+ // if we have an active postit window, update the document model
+ if (pViewShell &&
+ pViewShell->GetPostItMgr() &&
+ pViewShell->GetPostItMgr()->HasActiveSidebarWin())
+ {
+ pViewShell->GetPostItMgr()->UpdateDataOnActiveSidebarWin();
+ }
+
+ OUString aFilterName;
+ auto& rMediaDescriptor = getMediaDescriptor();
+ rMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= aFilterName;
+ bool bDocm = aFilterName.endsWith("VBA");
+
+ if (!bDocm)
+ {
+ // Check whether application is in headless mode
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(
+ pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ if (xStorageBasedDocument.is())
+ {
+ uno::Reference<embed::XStorage> xDocumentStorage =
+ xStorageBasedDocument->getDocumentStorage();
+ if (xDocumentStorage.is() && xDocumentStorage->hasByName(u"_MS_VBA_Macros"_ustr))
+ {
+ // Let user know that macros won't be saved in this format
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
+ nullptr,
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ SwResId(STR_CANT_SAVE_MACROS))
+ );
+ if (xBox->run() == RET_CANCEL)
+ return false;
+ }
+ }
+ }
+ }
+
+ // get SwPaM*
+ // FIXME so far we get SwPaM for the entire document; probably we should
+ // be able to output just the selection as well - though no idea how to
+ // get the correct SwPaM* then...
+ SwPaM aPam( pDoc->GetNodes().GetEndOfContent() );
+ aPam.SetMark();
+ aPam.Move( fnMoveBackward, GoInDoc );
+
+ std::shared_ptr<SwUnoCursor> pCurPam(pDoc->CreateUnoCursor(*aPam.End(), false));
+ pCurPam->SetMark();
+ *pCurPam->GetPoint() = *aPam.Start();
+
+ // export the document
+ // (in a separate block so that it's destructed before the commit)
+ {
+ DocxExport aExport(*this, *pDoc, pCurPam, aPam, bDocm, isExportTemplate());
+ aExport.ExportDocument( true ); // FIXME support exporting selection only
+ }
+
+ commitStorage();
+
+ // delete the pCurPam
+ while ( pCurPam->GetNext() != pCurPam.get() )
+ delete pCurPam->GetNext();
+
+ return true;
+}
+
+// UNO stuff so that the filter is registered
+
+OUString DocxExportFilter::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.DocxExport";
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_comp_Writer_DocxExport_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new DocxExportFilter(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxexportfilter.hxx b/sw/source/filter/ww8/docxexportfilter.hxx
new file mode 100644
index 0000000000..ae98c87c32
--- /dev/null
+++ b/sw/source/filter/ww8/docxexportfilter.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORTFILTER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORTFILTER_HXX
+
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/drawingml/chart/chartconverter.hxx>
+#include <oox/vml/vmldrawing.hxx>
+
+
+/// The physical access to the DOCX document (for writing).
+class DocxExportFilter : public oox::core::XmlFilterBase
+{
+public:
+ explicit DocxExportFilter( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ // FIXME these should not even exist for the export-only filter!
+ // For now, let's just do empty implementations of those.
+ virtual bool importDocument() override { return false; }
+ virtual const ::oox::drawingml::Theme* getCurrentTheme() const override { return nullptr; }
+ virtual ::oox::vml::Drawing* getVmlDrawing() override { return nullptr; }
+ virtual ::oox::drawingml::chart::ChartConverter* getChartConverter() override { return nullptr; }
+ virtual ::oox::drawingml::table::TableStyleListPtr getTableStyles() override { return ::oox::drawingml::table::TableStyleListPtr(); }
+
+ // Actual export of the DOCX document
+ virtual bool exportDocument() override;
+
+private:
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual ::oox::ole::VbaProject* implCreateVbaProject() const override
+ {
+ return nullptr; // FIXME: implement me !
+ }
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORTFILTER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxfootnotes.hxx b/sw/source/filter/ww8/docxfootnotes.hxx
new file mode 100644
index 0000000000..614069f0dd
--- /dev/null
+++ b/sw/source/filter/ww8/docxfootnotes.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXFOOTNOTES_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXFOOTNOTES_HXX
+
+#include <sal/types.h>
+
+#include <vector>
+
+class SwFormatFootnote;
+
+namespace docx {
+
+typedef std::vector< const SwFormatFootnote* > FootnotesVector;
+
+/** Remember footnotes/endnotes so that we can dump them in one go.
+
+ Also remember the last added footnote Id to be able to write it in the
+ DocxAttributeOutput::EndRunProperties() method.
+*/
+class FootnotesList {
+ /// The current footnote, that was not written yet.
+ sal_Int32 m_nCurrent;
+
+ /// List of the footnotes.
+ FootnotesVector m_aFootnotes;
+
+public:
+ FootnotesList() : m_nCurrent( -1 ) {}
+
+ void add( const SwFormatFootnote& rFootnote )
+ {
+ m_aFootnotes.push_back( &rFootnote );
+ m_nCurrent = m_aFootnotes.size() - 1;
+ }
+
+ /// Return the current footnote/endnote and clear the 'current' state.
+ const SwFormatFootnote* getCurrent( sal_Int32& rId )
+ {
+ // anything to write at all?
+ if ( m_nCurrent < 0 )
+ {
+ rId = -1;
+ return nullptr;
+ }
+
+ // skip ids 0 and 1 - they are reserved for separator and
+ // continuationSeparator
+ rId = m_nCurrent + 2;
+
+ const SwFormatFootnote *pFootnote = m_aFootnotes[m_nCurrent];
+ m_nCurrent = -1;
+
+ return pFootnote;
+ }
+
+ /// Return all the footnotes/endnotes.
+ const FootnotesVector& getVector() const
+ {
+ return m_aFootnotes;
+ }
+
+ /// Do we have any footnotes/endnotes at all?
+ bool isEmpty() const
+ {
+ return m_aFootnotes.empty();
+ }
+};
+
+} // namespace docx
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXFOOTNOTES_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxhelper.hxx b/sw/source/filter/ww8/docxhelper.hxx
new file mode 100644
index 0000000000..7807ec7aa5
--- /dev/null
+++ b/sw/source/filter/ww8/docxhelper.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXHELPER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXHELPER_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <sal/types.h>
+
+struct DocxStringTokenMap
+{
+ const char* pToken;
+ sal_Int32 nToken;
+};
+
+sal_Int32 DocxStringGetToken(DocxStringTokenMap const* pMap, std::u16string_view rName);
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXHELPER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx
new file mode 100644
index 0000000000..0c42d28fc6
--- /dev/null
+++ b/sw/source/filter/ww8/docxsdrexport.cxx
@@ -0,0 +1,2198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "docxsdrexport.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/svditer.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <textboxhelper.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <frmatr.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include "docxattributeoutput.hxx"
+#include "docxexportfilter.hxx"
+#include <comphelper/flagguard.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <frmfmt.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+
+#include <svx/svdtrans.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+using namespace com::sun::star;
+using namespace oox;
+using namespace sax_fastparser;
+
+namespace
+{
+uno::Sequence<beans::PropertyValue> lclGetProperty(const uno::Reference<drawing::XShape>& rShape,
+ const OUString& rPropName)
+{
+ uno::Sequence<beans::PropertyValue> aResult;
+ uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
+
+ if (!xPropertySet.is())
+ return aResult;
+
+ xPropSetInfo = xPropertySet->getPropertySetInfo();
+ if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(rPropName))
+ {
+ xPropertySet->getPropertyValue(rPropName) >>= aResult;
+ }
+ return aResult;
+}
+
+OUString lclGetAnchorIdFromGrabBag(const SdrObject* pObj)
+{
+ OUString aResult;
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObj)->getUnoShape(),
+ uno::UNO_QUERY);
+ OUString aGrabBagName;
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY);
+ if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
+ aGrabBagName = "FrameInteropGrabBag";
+ else
+ aGrabBagName = "InteropGrabBag";
+ const uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, aGrabBagName);
+ auto pProp
+ = std::find_if(propList.begin(), propList.end(),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "AnchorId"; });
+ if (pProp != propList.end())
+ pProp->Value >>= aResult;
+ return aResult;
+}
+
+void lclMovePositionWithRotation(awt::Point& aPos, const Size& rSize, Degree100 nRotation100)
+{
+ // code from ImplEESdrWriter::ImplFlipBoundingBox (filter/source/msfilter/eschesdo.cxx)
+ // TODO: refactor
+ // MSO uses left|top of the unrotated object rectangle as position. When you rotate that rectangle
+ // around its center and build a snap rectangle S from it, then left|top of S has to be the
+ // position used in LO. This method converts LOs aPos to the position used by MSO.
+
+ // rSize has to be size of the logicRect of the object. For calculating the diff, we build a
+ // rectangle with left|top A = (-fWidthHalf | -fHeightHalf) and
+ // right|top B = (fWidthHalf | -fHeightHalf). The rotation matrix R is here
+ // fcos fsin
+ // -fsin fcos
+ // Left of rectangle S = X-coord of R * A, Top of rectangle S = Y-coord of R * B
+
+ // Use nRotation in [0;9000], for to have only one and not four cases.
+ if (nRotation100 == 0_deg100)
+ return;
+ if (nRotation100 < 0_deg100)
+ nRotation100 = (36000_deg100 + nRotation100) % 36000_deg100;
+ if (nRotation100 % 18000_deg100 == 0_deg100)
+ nRotation100 = 0_deg100; // prevents endless loop
+ while (nRotation100 > 9000_deg100)
+ nRotation100 = 18000_deg100 - (nRotation100 % 18000_deg100);
+
+ double fVal = toRadians(nRotation100);
+ double fCos = (nRotation100 == 9000_deg100) ? 0.0 : cos(fVal);
+ double fSin = sin(fVal);
+
+ double fWidthHalf = static_cast<double>(rSize.Width()) / 2.0;
+ double fHeightHalf = static_cast<double>(rSize.Height()) / 2.0;
+
+ double fXDiff = fSin * fHeightHalf + fCos * fWidthHalf - fWidthHalf;
+ double fYDiff = fSin * fWidthHalf + fCos * fHeightHalf - fHeightHalf;
+
+ aPos.X += fXDiff + 0.5;
+ aPos.Y += fYDiff + 0.5;
+}
+
+/// Determines if the anchor is inside a paragraph.
+bool IsAnchorTypeInsideParagraph(const ww8::Frame* pFrame)
+{
+ const SwFormatAnchor& rAnchor = pFrame->GetFrameFormat().GetAttrSet().GetAnchor();
+ return rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE;
+}
+
+bool lcl_IsRotateAngleValid(const SdrObject& rObj)
+{
+ // Some shape types report a rotation angle but are not actually rotated, because all rotation
+ // have been incorporated.
+ switch (rObj.GetObjIdentifier())
+ {
+ case SdrObjKind::Group:
+ case SdrObjKind::Line:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ return false;
+ default:
+ return true;
+ }
+}
+
+void lcl_calculateMSOBaseRectangle(const SdrObject& rObj, double& rfMSOLeft, double& rfMSORight,
+ double& rfMSOTop, double& rfMSOBottom,
+ const bool bIsWord2007Image)
+{
+ // Word rotates around shape center, LO around left/top. Thus logic rectangle of LO is not
+ // directly usable as 'base rectangle'.
+ double fCenterX = (rObj.GetSnapRect().Left() + rObj.GetSnapRect().Right()) / 2.0;
+ double fCenterY = (rObj.GetSnapRect().Top() + rObj.GetSnapRect().Bottom()) / 2.0;
+ double fHalfWidth = rObj.GetLogicRect().getOpenWidth() / 2.0;
+ double fHalfHeight = rObj.GetLogicRect().getOpenHeight() / 2.0;
+
+ // MSO swaps width and height depending on rotation angle; exception: Word 2007 (vers 12) never
+ // swaps width and height for images.
+ double fRotation
+ = lcl_IsRotateAngleValid(rObj) ? toDegrees(NormAngle36000(rObj.GetRotateAngle())) : 0.0;
+ if (((fRotation > 45.0 && fRotation <= 135.0) || (fRotation > 225.0 && fRotation <= 315.0))
+ && !bIsWord2007Image)
+ {
+ rfMSOLeft = fCenterX - fHalfHeight;
+ rfMSORight = fCenterX + fHalfHeight;
+ rfMSOTop = fCenterY - fHalfWidth;
+ rfMSOBottom = fCenterY + fHalfWidth;
+ }
+ else
+ {
+ rfMSOLeft = fCenterX - fHalfWidth;
+ rfMSORight = fCenterX + fHalfWidth;
+ rfMSOTop = fCenterY - fHalfHeight;
+ rfMSOBottom = fCenterY + fHalfHeight;
+ }
+}
+
+void lcl_calculateRawEffectExtent(sal_Int32& rLeft, sal_Int32& rTop, sal_Int32& rRight,
+ sal_Int32& rBottom, const SdrObject& rObj,
+ const bool bUseBoundRect, const bool bIsWord2007Image)
+{
+ // This method calculates the extent needed, to let Word use the same outer area for the object
+ // as LO. Word uses as 'base rectangle' the unrotated shape rectangle, maybe having swapped width
+ // and height depending on rotation angle and version of Word.
+ double fMSOLeft;
+ double fMSORight;
+ double fMSOTop;
+ double fMSOBottom;
+ lcl_calculateMSOBaseRectangle(rObj, fMSOLeft, fMSORight, fMSOTop, fMSOBottom, bIsWord2007Image);
+
+ tools::Rectangle aLORect = bUseBoundRect ? rObj.GetCurrentBoundRect() : rObj.GetSnapRect();
+ rLeft = fMSOLeft - aLORect.Left();
+ rRight = aLORect.Right() - fMSORight;
+ rTop = fMSOTop - aLORect.Top();
+ rBottom = aLORect.Bottom() - fMSOBottom;
+ // Result values might be negative, e.g for a custom shape 'Arc'.
+ return;
+}
+
+bool lcl_makeSingleDistAndEffectExtentNonNegative(sal_Int64& rDist, sal_Int32& rExt)
+{
+ // A negative effectExtent is allowed in OOXML, but Word cannot handle it (bug in Word). It
+ // might occur, if the BoundRect in LO is smaller than the base rect in Word.
+ // A negative wrap distance from text is allowed in ODF. LO can currently only handle left and
+ // right negative values, see bug tdf#141880. Dist must be non-negative in OOXML.
+ // We try to compensate Dist vs effectExtent to get similar visual appearance.
+ if (rExt >= 0 && rDist >= 0)
+ return true;
+ if (rExt < 0 && rDist < 0)
+ {
+ rExt = 0;
+ rDist = 0;
+ return false;
+ }
+ if (rDist + static_cast<sal_Int64>(rExt) < 0) // different sign, so no overflow
+ {
+ rExt = 0;
+ rDist = 0;
+ return false;
+ }
+ // rDist + rExt >= 0
+ if (rDist < 0)
+ {
+ rExt += rDist;
+ rDist = 0;
+ }
+ else // rExt < 0
+ {
+ rDist += rExt;
+ rExt = 0;
+ }
+ return true;
+}
+
+bool lcl_makeDistAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB, sal_Int64& rDistL,
+ sal_Int64& rDistR, sal_Int32& rLeftExt, sal_Int32& rTopExt,
+ sal_Int32& rRightExt, sal_Int32& rBottomExt)
+{
+ bool bLeft = lcl_makeSingleDistAndEffectExtentNonNegative(rDistL, rLeftExt);
+ bool bTop = lcl_makeSingleDistAndEffectExtentNonNegative(rDistT, rTopExt);
+ bool bRight = lcl_makeSingleDistAndEffectExtentNonNegative(rDistR, rRightExt);
+ bool bBottom = lcl_makeSingleDistAndEffectExtentNonNegative(rDistB, rBottomExt);
+ return bLeft && bTop && bRight && bBottom;
+}
+
+void lcl_makeSingleDistZeroAndExtentNonNegative(sal_Int64& rDist, sal_Int32& rExt)
+{
+ if (static_cast<double>(rDist) + static_cast<double>(rExt)
+ >= static_cast<double>(SAL_MAX_INT32))
+ rExt = SAL_MAX_INT32;
+ else if (static_cast<double>(rDist) + static_cast<double>(rExt) <= 0)
+ rExt = 0;
+ else // 0 < rDist + rExt < SAL_MAX_INT32
+ {
+ rExt = static_cast<sal_Int32>(rDist + rExt);
+ if (rExt < 0)
+ rExt = 0;
+ }
+ rDist = 0;
+}
+
+void lcl_makeDistZeroAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB, sal_Int64& rDistL,
+ sal_Int64& rDistR, sal_Int32& rLeftExt,
+ sal_Int32& rTopExt, sal_Int32& rRightExt,
+ sal_Int32& rBottomExt)
+{
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistL, rLeftExt);
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistT, rTopExt);
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistR, rRightExt);
+ lcl_makeSingleDistZeroAndExtentNonNegative(rDistB, rBottomExt);
+}
+
+tools::Polygon lcl_CreateContourPolygon(SdrObject* pSdrObj)
+{
+ tools::Polygon aContour;
+ if (!pSdrObj)
+ {
+ // use rectangular default
+ aContour.Insert(0, Point(0, 0));
+ aContour.Insert(1, Point(21600, 0));
+ aContour.Insert(2, Point(21600, 21600));
+ aContour.Insert(3, Point(0, 21600));
+ aContour.Insert(4, Point(0, 0));
+ return aContour;
+ }
+
+ // Simple version for now: Use ready PolygonFromPolyPolygon().
+ // For that we first create a B2DPolyPolygon from the shape, that ideally contains
+ // the outline of the shape.
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ switch (pSdrObj->GetObjIdentifier())
+ {
+ case SdrObjKind::CustomShape:
+ {
+ // EnhancedCustomShapeEngine::GetLineGeometry() is not directly usable, because the wrap
+ // polygon acts on the untransformed shape in Word. We do here similar as in
+ // GetLineGeometry(), but without transformations.
+ EnhancedCustomShape2d aCustomShape2d(*static_cast<SdrObjCustomShape*>(pSdrObj));
+ rtl::Reference<SdrObject> pLineGeometryObj = aCustomShape2d.CreateLineGeometry();
+ if (!pLineGeometryObj)
+ break;
+
+ // We might have got other object kinds than SdrPathObj, even groups.
+ SdrObjListIter aIter(*pLineGeometryObj, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ basegfx::B2DPolyPolygon aPP;
+ const SdrObject* pNext = aIter.Next();
+ if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext))
+ aPP = pPathObj->GetPathPoly();
+ else
+ {
+ rtl::Reference<SdrObject> pNewObj
+ = pLineGeometryObj->ConvertToPolyObj(false, false);
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>(pNewObj.get());
+ if (pPath)
+ aPP = pPath->GetPathPoly();
+ }
+ if (aPP.count())
+ aPolyPolygon.append(aPP);
+ }
+
+ if (!aPolyPolygon.count())
+ break;
+
+ // Make relative to range 0..21600, 0..21600
+ Point aCenter(pSdrObj->GetSnapRect().Center());
+ basegfx::B2DHomMatrix aTranslateToOrigin(
+ basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
+ aPolyPolygon.transform(aTranslateToOrigin);
+ const double fWidth(pSdrObj->GetLogicRect().getOpenWidth());
+ double fScaleX = fWidth == 0.0 ? 1.0 : 21600.0 / fWidth;
+ const double fHeight(pSdrObj->GetLogicRect().getOpenHeight());
+ double fScaleY = fHeight == 0.0 ? 1.0 : 21600.0 / fHeight;
+ basegfx::B2DHomMatrix aScale(basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
+ aPolyPolygon.transform(aScale);
+
+ basegfx::B2DHomMatrix aTranslateToCenter(
+ basegfx::utils::createTranslateB2DHomMatrix(10800.0, 10800.0));
+ aPolyPolygon.transform(aTranslateToCenter);
+ break;
+ } // end case OBJ_CUSTOMSHAPE
+ case SdrObjKind::Line:
+ {
+ aContour.Insert(0, Point(0, 0));
+ aContour.Insert(1, Point(21600, 21600));
+ aContour.Insert(2, Point(0, 0));
+ return aContour;
+ }
+ case SdrObjKind::PathFill:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathPolyLine:
+ // case OBJ_POLY: FixMe: Creating wrap polygon would work, but export to DML is currently
+ // case OBJ_PLIN: disabled for unknown reason; related bug 75254.
+ {
+ // Includes removing any control points
+ rtl::Reference<SdrObject> pNewObj = pSdrObj->ConvertToPolyObj(false, false);
+ SdrPathObj* pConverted = dynamic_cast<SdrPathObj*>(pNewObj.get());
+ if (!pConverted)
+ break;
+ aPolyPolygon = pConverted->GetPathPoly();
+ pNewObj.clear();
+
+ // Word adds a line from last to first point. That will cut of indentations from being
+ // filled. To prevent this, the wrap polygon is lead along the path back to the first
+ // point and so indentation is kept.
+ if (!aPolyPolygon.isClosed())
+ {
+ basegfx::B2DPolyPolygon aReverse(aPolyPolygon);
+ aReverse.flip();
+ aPolyPolygon.append(aReverse);
+ }
+
+ // Make relative to range 0..21600, 0..21600
+ Point aCenter(pSdrObj->GetSnapRect().Center());
+ basegfx::B2DHomMatrix aTranslateToOrigin(
+ basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
+ aPolyPolygon.transform(aTranslateToOrigin);
+
+ const double fWidth(pSdrObj->GetLogicRect().getOpenWidth());
+ double fScaleX = fWidth == 0.0 ? 1.0 : 21600.0 / fWidth;
+ const double fHeight(pSdrObj->GetLogicRect().getOpenHeight());
+ double fScaleY = fHeight == 0.0 ? 1.0 : 21600.0 / fHeight;
+ basegfx::B2DHomMatrix aScale(
+ basegfx::utils::createScaleB2DHomMatrix(fScaleX, fScaleY));
+ aPolyPolygon.transform(aScale);
+
+ basegfx::B2DHomMatrix aTranslateToCenter(
+ basegfx::utils::createTranslateB2DHomMatrix(10800.0, 10800.0));
+ aPolyPolygon.transform(aTranslateToCenter);
+ break;
+ }
+ case SdrObjKind::NONE:
+ default:
+ break;
+ }
+
+ // Simple version for now: Use ready PolygonFromPolyPolygon()
+ const tools::PolyPolygon aToolsPolyPoly(aPolyPolygon);
+ aContour = sw::util::PolygonFromPolyPolygon(aToolsPolyPoly);
+
+ // The wrap polygon needs at least two points in OOXML and three points in Word.
+ switch (aContour.GetSize())
+ {
+ case 0:
+ // use rectangular default
+ aContour.Insert(0, Point(0, 0));
+ aContour.Insert(1, Point(21600, 0));
+ aContour.Insert(2, Point(21600, 21600));
+ aContour.Insert(3, Point(0, 21600));
+ aContour.Insert(4, Point(0, 0));
+ break;
+ case 1:
+ aContour.Insert(1, aContour.GetPoint(0));
+ aContour.Insert(2, aContour.GetPoint(0));
+ break;
+ case 2:
+ aContour.Insert(2, aContour.GetPoint(0));
+ break;
+ default:
+ break;
+ }
+ return aContour;
+}
+} // end anonymous namespace
+
+ExportDataSaveRestore::ExportDataSaveRestore(DocxExport& rExport, SwNodeOffset nStt,
+ SwNodeOffset nEnd, ww8::Frame const* pParentFrame)
+ : m_rExport(rExport)
+{
+ m_rExport.SaveData(nStt, nEnd);
+ m_rExport.m_pParentFrame = pParentFrame;
+}
+
+ExportDataSaveRestore::~ExportDataSaveRestore() { m_rExport.RestoreData(); }
+
+/// Holds data used by DocxSdrExport only.
+struct DocxSdrExport::Impl
+{
+private:
+ DocxExport& m_rExport;
+ sax_fastparser::FSHelperPtr m_pSerializer;
+ oox::drawingml::DrawingML* m_pDrawingML;
+ const Size* m_pFlyFrameSize;
+ bool m_bTextFrameSyntax;
+ bool m_bDMLTextFrameSyntax;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pTextboxAttrList;
+ OStringBuffer m_aTextFrameStyle;
+ bool m_bDrawingOpen;
+ bool m_bParagraphSdtOpen;
+ bool m_bParagraphHasDrawing; ///Flag for checking drawing in a paragraph.
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyFillAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyWrapAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pBodyPrAttrList;
+ rtl::Reference<sax_fastparser::FastAttributeList> m_pDashLineStyleAttr;
+ bool m_bDMLAndVMLDrawingOpen;
+ /// List of TextBoxes in this document: they are exported as part of their shape, never alone.
+ /// Preserved rotation for TextFrames.
+ Degree100 m_nDMLandVMLTextFrameRotation;
+
+public:
+ bool m_bFlyFrameGraphic = false;
+
+ Impl(DocxExport& rExport, sax_fastparser::FSHelperPtr pSerializer,
+ oox::drawingml::DrawingML* pDrawingML)
+ : m_rExport(rExport)
+ , m_pSerializer(std::move(pSerializer))
+ , m_pDrawingML(pDrawingML)
+ , m_pFlyFrameSize(nullptr)
+ , m_bTextFrameSyntax(false)
+ , m_bDMLTextFrameSyntax(false)
+ , m_bDrawingOpen(false)
+ , m_bParagraphSdtOpen(false)
+ , m_bParagraphHasDrawing(false)
+ , m_bDMLAndVMLDrawingOpen(false)
+ {
+ }
+
+ /// Writes wp wrapper code around an SdrObject, which itself is written using drawingML syntax.
+
+ void textFrameShadow(const SwFrameFormat& rFrameFormat);
+ static bool isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape,
+ const SdrObject* pSdrObject);
+
+ void setSerializer(const sax_fastparser::FSHelperPtr& pSerializer)
+ {
+ m_pSerializer = pSerializer;
+ }
+
+ const sax_fastparser::FSHelperPtr& getSerializer() const { return m_pSerializer; }
+
+ void setFlyFrameSize(const Size* pFlyFrameSize) { m_pFlyFrameSize = pFlyFrameSize; }
+
+ const Size* getFlyFrameSize() const { return m_pFlyFrameSize; }
+
+ void setTextFrameSyntax(bool bTextFrameSyntax) { m_bTextFrameSyntax = bTextFrameSyntax; }
+
+ bool getTextFrameSyntax() const { return m_bTextFrameSyntax; }
+
+ void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax)
+ {
+ m_bDMLTextFrameSyntax = bDMLTextFrameSyntax;
+ }
+
+ bool getDMLTextFrameSyntax() const { return m_bDMLTextFrameSyntax; }
+
+ void setFlyAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pFlyAttrList)
+ {
+ m_pFlyAttrList = pFlyAttrList;
+ }
+
+ rtl::Reference<sax_fastparser::FastAttributeList>& getFlyAttrList() { return m_pFlyAttrList; }
+
+ void
+ setTextboxAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pTextboxAttrList)
+ {
+ m_pTextboxAttrList = pTextboxAttrList;
+ }
+
+ rtl::Reference<sax_fastparser::FastAttributeList>& getTextboxAttrList()
+ {
+ return m_pTextboxAttrList;
+ }
+
+ OStringBuffer& getTextFrameStyle() { return m_aTextFrameStyle; }
+
+ void setDrawingOpen(bool bDrawingOpen) { m_bDrawingOpen = bDrawingOpen; }
+
+ bool getDrawingOpen() const { return m_bDrawingOpen; }
+
+ void setParagraphSdtOpen(bool bParagraphSdtOpen) { m_bParagraphSdtOpen = bParagraphSdtOpen; }
+
+ bool getParagraphSdtOpen() const { return m_bParagraphSdtOpen; }
+
+ void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen)
+ {
+ m_bDMLAndVMLDrawingOpen = bDMLAndVMLDrawingOpen;
+ }
+
+ bool getDMLAndVMLDrawingOpen() const { return m_bDMLAndVMLDrawingOpen; }
+
+ void setParagraphHasDrawing(bool bParagraphHasDrawing)
+ {
+ m_bParagraphHasDrawing = bParagraphHasDrawing;
+ }
+
+ bool getParagraphHasDrawing() const { return m_bParagraphHasDrawing; }
+
+ rtl::Reference<sax_fastparser::FastAttributeList>& getFlyFillAttrList()
+ {
+ return m_pFlyFillAttrList;
+ }
+
+ void
+ setFlyWrapAttrList(rtl::Reference<sax_fastparser::FastAttributeList> const& pFlyWrapAttrList)
+ {
+ m_pFlyWrapAttrList = pFlyWrapAttrList;
+ }
+
+ sax_fastparser::FastAttributeList* getFlyWrapAttrList() const
+ {
+ return m_pFlyWrapAttrList.get();
+ }
+
+ void setBodyPrAttrList(sax_fastparser::FastAttributeList* pBodyPrAttrList)
+ {
+ m_pBodyPrAttrList = pBodyPrAttrList;
+ }
+
+ sax_fastparser::FastAttributeList* getBodyPrAttrList() const { return m_pBodyPrAttrList.get(); }
+
+ rtl::Reference<sax_fastparser::FastAttributeList>& getDashLineStyleAttr()
+ {
+ return m_pDashLineStyleAttr;
+ }
+
+ bool getFlyFrameGraphic() const { return m_bFlyFrameGraphic; }
+
+ oox::drawingml::DrawingML* getDrawingML() const { return m_pDrawingML; }
+
+ DocxExport& getExport() const { return m_rExport; }
+
+ void setDMLandVMLTextFrameRotation(Degree100 nDMLandVMLTextFrameRotation)
+ {
+ m_nDMLandVMLTextFrameRotation = nDMLandVMLTextFrameRotation;
+ }
+
+ Degree100& getDMLandVMLTextFrameRotation() { return m_nDMLandVMLTextFrameRotation; }
+};
+
+DocxSdrExport::DocxSdrExport(DocxExport& rExport, const sax_fastparser::FSHelperPtr& pSerializer,
+ oox::drawingml::DrawingML* pDrawingML)
+ : m_pImpl(std::make_unique<Impl>(rExport, pSerializer, pDrawingML))
+{
+}
+
+DocxSdrExport::~DocxSdrExport() = default;
+
+void DocxSdrExport::setSerializer(const sax_fastparser::FSHelperPtr& pSerializer)
+{
+ m_pImpl->setSerializer(pSerializer);
+}
+
+const Size* DocxSdrExport::getFlyFrameSize() const { return m_pImpl->getFlyFrameSize(); }
+
+bool DocxSdrExport::getTextFrameSyntax() const { return m_pImpl->getTextFrameSyntax(); }
+
+bool DocxSdrExport::getDMLTextFrameSyntax() const { return m_pImpl->getDMLTextFrameSyntax(); }
+
+rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyAttrList()
+{
+ return m_pImpl->getFlyAttrList();
+}
+
+rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getTextboxAttrList()
+{
+ return m_pImpl->getTextboxAttrList();
+}
+
+OStringBuffer& DocxSdrExport::getTextFrameStyle() { return m_pImpl->getTextFrameStyle(); }
+
+bool DocxSdrExport::IsDrawingOpen() const { return m_pImpl->getDrawingOpen(); }
+
+void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen)
+{
+ m_pImpl->setParagraphSdtOpen(bParagraphSdtOpen);
+}
+
+bool DocxSdrExport::IsDMLAndVMLDrawingOpen() const { return m_pImpl->getDMLAndVMLDrawingOpen(); }
+
+bool DocxSdrExport::IsParagraphHasDrawing() const { return m_pImpl->getParagraphHasDrawing(); }
+
+void DocxSdrExport::setParagraphHasDrawing(bool bParagraphHasDrawing)
+{
+ m_pImpl->setParagraphHasDrawing(bParagraphHasDrawing);
+}
+
+rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyFillAttrList()
+{
+ return m_pImpl->getFlyFillAttrList();
+}
+
+sax_fastparser::FastAttributeList* DocxSdrExport::getBodyPrAttrList()
+{
+ return m_pImpl->getBodyPrAttrList();
+}
+
+rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getDashLineStyle()
+{
+ return m_pImpl->getDashLineStyleAttr();
+}
+
+void DocxSdrExport::setFlyWrapAttrList(
+ rtl::Reference<sax_fastparser::FastAttributeList> const& pAttrList)
+{
+ m_pImpl->setFlyWrapAttrList(pAttrList);
+}
+
+void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize)
+{
+ // Word uses size excluding right edge. Caller writeDMLDrawing and writeDiagram are changed for
+ // now. ToDo: Look whether the other callers give the size this way.
+ m_pImpl->setDrawingOpen(true);
+ m_pImpl->setParagraphHasDrawing(true);
+ m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing);
+ const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
+
+ // LO determines the place needed for the object from wrap type, wrap margin ('distance to text'),
+ // object type and anchor type. Word uses dist* for user set margins and effectExtent for place
+ // needed for effects like shadow and glow, for fat stroke and for rotation. We map the LO values
+ // to values needed by Word so that the appearance is the same as far as possible.
+ // All values in Twips, change to EMU is done immediately before writing out.
+
+ bool isAnchor; // true XML_anchor, false XML_inline
+ if (m_pImpl->getFlyFrameGraphic())
+ {
+ isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline
+ }
+ else
+ {
+ isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
+ }
+
+ // tdf#135047: It must be allowed to find in parents too, but default value of bInP parameter
+ // for GetLRSpace() and GetULSpace() is true, so no direct setting is required.
+ const SvxLRSpaceItem& aLRSpaceItem = pFrameFormat->GetLRSpace();
+ const SvxULSpaceItem& aULSpaceItem = pFrameFormat->GetULSpace();
+ sal_Int64 nDistT = aULSpaceItem.GetUpper();
+ sal_Int64 nDistB = aULSpaceItem.GetLower();
+ sal_Int64 nDistL = aLRSpaceItem.GetLeft();
+ sal_Int64 nDistR = aLRSpaceItem.GetRight();
+
+ // LibreOffice behaves different for frames and drawing objects, but MS Office treats frames
+ // as drawing objects too. Therefore we transform the values from frame so as if they come
+ // from a drawing object.
+ sal_Int32 nWidthDiff(0);
+ sal_Int32 nHeightDiff(0);
+ sal_Int32 nPosXDiff(0);
+ sal_Int32 nPosYDiff(0);
+ sal_Int32 nLeftExt(0);
+ sal_Int32 nRightExt(0);
+ sal_Int32 nTopExt(0);
+ sal_Int32 nBottomExt(0);
+
+ if ((!pObj) || (pObj && (pObj->GetObjIdentifier() == SdrObjKind::SwFlyDrawObjIdentifier)))
+ {
+ // Frame objects have a restricted shadow and no further effects. They have border instead of
+ // stroke. LO includes shadow and border in the object size, but Word not.
+ SvxShadowItem aShadowItem = pFrameFormat->GetShadow();
+ if (aShadowItem.GetLocation() != SvxShadowLocation::NONE)
+ {
+ sal_Int32 nShadowWidth(aShadowItem.GetWidth());
+ switch (aShadowItem.GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ nTopExt = nLeftExt = nShadowWidth;
+ nPosXDiff = nLeftExt; // actual move is postponed
+ nPosYDiff = nTopExt;
+ nWidthDiff = -nLeftExt; // actual size extent is postponed
+ nHeightDiff = -nTopExt;
+ break;
+ case SvxShadowLocation::TopRight:
+ nTopExt = nRightExt = nShadowWidth;
+ nPosYDiff = nTopExt;
+ nWidthDiff = -nRightExt;
+ nHeightDiff = -nTopExt;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ nBottomExt = nLeftExt = nShadowWidth;
+ nPosXDiff = nLeftExt;
+ nWidthDiff = -nLeftExt;
+ nHeightDiff = -nBottomExt;
+ break;
+ case SvxShadowLocation::BottomRight:
+ nBottomExt = nRightExt = nShadowWidth;
+ nWidthDiff = -nRightExt;
+ nHeightDiff = -nBottomExt;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ break;
+ }
+ }
+ // ToDo: Position refers to outer edge of border in LO, but to center of border in Word.
+ // Adaption is missing here. Frames in LO have no stroke but border. The current conversion
+ // from border to line treats borders like table borders. That might give wrong values
+ // for drawing frames.
+
+ if (pObj && pObj->GetRotateAngle() != 0_deg100)
+ {
+ Degree100 nRotation = pObj->GetRotateAngle();
+ const SwRect aBoundRect(pFrameFormat->FindLayoutRect());
+ tools::Long nMSOWidth = rSize.Width();
+ tools::Long nMSOHeight = rSize.Height();
+ if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100)
+ || (nRotation > 22500_deg100 && nRotation <= 31500_deg100))
+ std::swap(nMSOWidth, nMSOHeight);
+ nBottomExt += (aBoundRect.Height() - 1 - nMSOHeight) / 2;
+ nTopExt += (aBoundRect.Height() - 1 - nMSOHeight) / 2;
+ nLeftExt += (aBoundRect.Width() - nMSOWidth) / 2;
+ nRightExt += (aBoundRect.Width() - nMSOWidth) / 2;
+ }
+ lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
+ nRightExt, nBottomExt);
+
+ // ToDo: Inline rotated image fails because it would need wrapTight, what is not possible.
+ // ToDo: Image plus shadow fails because of wrong shadow direction.
+ }
+ else // other objects than frames. pObj exists.
+ {
+ // Word 2007 makes no width-height-swap for images. Detect this situation.
+ sal_Int32 nMode = m_pImpl->getExport().getWordCompatibilityModeFromGrabBag();
+ bool bIsWord2007Image(nMode > 0 && nMode < 14
+ && pObj->GetObjIdentifier() == SdrObjKind::Graphic);
+
+ // Word cannot handle negative EffectExtent although allowed in OOXML, the 'dist' attributes
+ // may not be negative. Take care of that.
+ if (isAnchor)
+ {
+ lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, true,
+ bIsWord2007Image);
+ // We have calculated the effectExtent from boundRect, therefore half stroke width is
+ // already contained.
+ // ToDo: The other half of the stroke width needs to be subtracted from padding.
+ // Where is that?
+
+ // The import has added a difference to dist* in case of contour wrap for to give a
+ // rendering nearer to Word. In that case, we need to subtract it on export.
+ uno::Any aAny;
+ pObj->GetGrabBagItem(aAny);
+ comphelper::SequenceAsHashMap aGrabBag(aAny);
+ auto it = aGrabBag.find("AnchorDistDiff");
+ if (it != aGrabBag.end())
+ {
+ comphelper::SequenceAsHashMap aAnchorDistDiff(it->second);
+ for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDiff :
+ aAnchorDistDiff)
+ {
+ const OUString& rName = rDiff.first.maString;
+ if (rName == "distTDiff" && rDiff.second.has<sal_Int32>())
+ nDistT -= round(rDiff.second.get<sal_Int32>());
+ else if (rName == "distBDiff" && rDiff.second.has<sal_Int32>())
+ nDistB -= round(rDiff.second.get<sal_Int32>());
+ else if (rName == "distLDiff" && rDiff.second.has<sal_Int32>())
+ nDistL -= rDiff.second.get<sal_Int32>();
+ else if (rName == "distRDiff" && rDiff.second.has<sal_Int32>())
+ nDistR -= rDiff.second.get<sal_Int32>();
+ }
+ }
+ // ToDo: bool bCompansated = ... to be later able to switch from wrapSquare to wrapTight,
+ // if wrapSquare would require negative effectExtent.
+ lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
+ nRightExt, nBottomExt);
+ }
+ else
+ {
+ lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, false,
+ bIsWord2007Image);
+ // nDistT,... contain the needed distances from import or set by user. But Word
+ // ignores Dist attributes of inline shapes. So we move all needed distances to
+ // effectExtent and force effectExtent to non-negative.
+ lcl_makeDistZeroAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
+ nRightExt, nBottomExt);
+ }
+ }
+
+ if (isAnchor)
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> attrList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ bool bOpaque = pFrameFormat->GetOpaque().GetValue();
+ if (pObj)
+ {
+ // SdrObjects know their layer, consider that instead of the frame format.
+ bOpaque = pObj->GetLayer()
+ != pFrameFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId()
+ && pObj->GetLayer()
+ != pFrameFormat->GetDoc()
+ ->getIDocumentDrawModelAccess()
+ .GetInvisibleHellId();
+ }
+ attrList->add(XML_behindDoc, bOpaque ? "0" : "1");
+
+ attrList->add(XML_distT, OString::number(TwipsToEMU(nDistT)));
+ attrList->add(XML_distB, OString::number(TwipsToEMU(nDistB)));
+ attrList->add(XML_distL, OString::number(TwipsToEMU(nDistL)));
+ attrList->add(XML_distR, OString::number(TwipsToEMU(nDistR)));
+
+ attrList->add(XML_simplePos, "0");
+ attrList->add(XML_locked, "0");
+
+ bool bLclInTabCell = true;
+ if (pObj)
+ {
+ uno::Reference<drawing::XShape> xShape((const_cast<SdrObject*>(pObj)->getUnoShape()),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ if (xShapeProps.is())
+ xShapeProps->getPropertyValue("IsFollowingTextFlow") >>= bLclInTabCell;
+ }
+
+ if (pFrameFormat->GetSurround().GetValue() == text::WrapTextMode_THROUGH
+ && pFrameFormat->GetHoriOrient().GetRelationOrient() == text::RelOrientation::FRAME)
+ {
+ // "In front of text" and horizontal positioning relative to Column is ignored on
+ // import, add it back here.
+ bLclInTabCell = true;
+ }
+
+ if (bLclInTabCell)
+ attrList->add(XML_layoutInCell, "1");
+ else
+ attrList->add(XML_layoutInCell, "0");
+
+ bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap();
+ attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0");
+
+ if (pObj)
+ // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that.
+ attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2));
+ else
+ // relativeHeight is mandatory attribute, if value is not present, we must write default value
+ attrList->add(XML_relativeHeight, "0");
+
+ if (pObj)
+ {
+ OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
+ if (!sAnchorId.isEmpty())
+ attrList->addNS(XML_wp14, XML_anchorId, sAnchorId);
+ }
+
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, attrList);
+
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y,
+ "0"); // required, unused
+
+ // Position is either determined by coordinates aPos or alignment keywords like 'center'.
+ // First prepare them.
+ awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
+ pFrameFormat->GetVertOrient().GetPos());
+
+ aPos.X += nPosXDiff; // Make the postponed position move of frames.
+ aPos.Y += nPosYDiff;
+ if (pObj && lcl_IsRotateAngleValid(*pObj)
+ && pObj->GetObjIdentifier() != SdrObjKind::SwFlyDrawObjIdentifier)
+ lclMovePositionWithRotation(aPos, rSize, pObj->GetRotateAngle());
+
+ const char* relativeFromH;
+ const char* relativeFromV;
+ const char* alignH = nullptr;
+ const char* alignV = nullptr;
+ switch (pFrameFormat->GetVertOrient().GetRelationOrient())
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ relativeFromV = "margin";
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA_TOP:
+ relativeFromV = "topMargin";
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
+ relativeFromV = "bottomMargin";
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ relativeFromV = "page";
+ break;
+ case text::RelOrientation::FRAME:
+ relativeFromV = "paragraph";
+ break;
+ case text::RelOrientation::TEXT_LINE:
+ default:
+ relativeFromV = "line";
+ break;
+ }
+ switch (pFrameFormat->GetVertOrient().GetVertOrient())
+ {
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::CHAR_TOP:
+ case text::VertOrientation::LINE_TOP:
+ if (pFrameFormat->GetVertOrient().GetRelationOrient()
+ == text::RelOrientation::TEXT_LINE)
+ alignV = "bottom";
+ else
+ alignV = "top";
+ break;
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ if (pFrameFormat->GetVertOrient().GetRelationOrient()
+ == text::RelOrientation::TEXT_LINE)
+ alignV = "top";
+ else
+ alignV = "bottom";
+ break;
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::CHAR_CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ alignV = "center";
+ break;
+ default:
+ break;
+ }
+ switch (pFrameFormat->GetHoriOrient().GetRelationOrient())
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ relativeFromH = "margin";
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ relativeFromH = "page";
+ break;
+ case text::RelOrientation::CHAR:
+ relativeFromH = "character";
+ break;
+ case text::RelOrientation::PAGE_RIGHT:
+ relativeFromH = "rightMargin";
+ break;
+ case text::RelOrientation::PAGE_LEFT:
+ relativeFromH = "leftMargin";
+ break;
+ case text::RelOrientation::FRAME:
+ default:
+ relativeFromH = "column";
+ break;
+ }
+ switch (pFrameFormat->GetHoriOrient().GetHoriOrient())
+ {
+ case text::HoriOrientation::LEFT:
+ alignH = "left";
+ break;
+ case text::HoriOrientation::RIGHT:
+ alignH = "right";
+ break;
+ case text::HoriOrientation::CENTER:
+ alignH = "center";
+ break;
+ case text::HoriOrientation::INSIDE:
+ alignH = "inside";
+ break;
+ case text::HoriOrientation::OUTSIDE:
+ alignH = "outside";
+ break;
+ default:
+ break;
+ }
+
+ // write out horizontal position
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom,
+ relativeFromH);
+
+ /**
+ * Sizes of integral types
+ * climits header defines constants with the limits of integral types for the specific system and compiler implementation used.
+ * Use of this might cause platform dependent problem like posOffset exceed the limit.
+ **/
+ const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32;
+ const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32;
+
+ if (alignH != nullptr)
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
+ m_pImpl->getSerializer()->write(alignH);
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
+ }
+ else
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
+ sal_Int64 nPosXEMU = TwipsToEMU(aPos.X);
+
+ /* Absolute Position Offset Value is of type Int. Hence it should not be greater than
+ * Maximum value for Int OR Less than the Minimum value for Int.
+ * - Maximum value for Int = 2147483647
+ * - Minimum value for Int = -2147483648
+ *
+ * As per ECMA Specification : ECMA-376, Second Edition,
+ * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)]
+ *
+ * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html
+ */
+
+ if (nPosXEMU > MAX_INTEGER_VALUE)
+ {
+ nPosXEMU = MAX_INTEGER_VALUE;
+ }
+ else if (nPosXEMU < MIN_INTEGER_VALUE)
+ {
+ nPosXEMU = MIN_INTEGER_VALUE;
+ }
+ m_pImpl->getSerializer()->write(nPosXEMU);
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
+ }
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH);
+
+ // write out vertical position
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom,
+ relativeFromV);
+ sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y);
+
+ // tdf#93675, 0 below line/paragraph and/or top line/paragraph with
+ // wrap top+bottom or other wraps is affecting the line directly
+ // above the anchor line, which seems odd, but a tiny adjustment
+ // here to bring the top down convinces msoffice to wrap like us
+ if (nPosYEMU == 0
+ && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0)
+ && (!alignV || strcmp(alignV, "top") == 0))
+ {
+ alignV = nullptr;
+ nPosYEMU = TwipsToEMU(1);
+ }
+
+ if (alignV != nullptr)
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
+ m_pImpl->getSerializer()->write(alignV);
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
+ }
+ else
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
+ if (nPosYEMU > MAX_INTEGER_VALUE)
+ {
+ nPosYEMU = MAX_INTEGER_VALUE;
+ }
+ else if (nPosYEMU < MIN_INTEGER_VALUE)
+ {
+ nPosYEMU = MIN_INTEGER_VALUE;
+ }
+ m_pImpl->getSerializer()->write(nPosYEMU);
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
+ }
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV);
+ }
+ else // inline
+ {
+ // nDist is forced to zero above and ignored by Word anyway, so write 0 directly.
+ rtl::Reference<sax_fastparser::FastAttributeList> aAttrList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ aAttrList->add(XML_distT, OString::number(0));
+ aAttrList->add(XML_distB, OString::number(0));
+ aAttrList->add(XML_distL, OString::number(0));
+ aAttrList->add(XML_distR, OString::number(0));
+ if (pObj)
+ {
+ OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
+ if (!sAnchorId.isEmpty())
+ aAttrList->addNS(XML_wp14, XML_anchorId, sAnchorId);
+ }
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList);
+ }
+
+ // now the common parts 'extent' and 'effectExtent'
+ /**
+ * Extent width is of type long ( i.e cx & cy ) as
+ *
+ * per ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference
+ * [ 20.4.2.7 extent (Drawing Object Size)]
+ *
+ * cy is of type a:ST_PositiveCoordinate.
+ * Minimum inclusive: 0
+ * Maximum inclusive: 27273042316900
+ *
+ * reference : http://www.schemacentral.com/sc/ooxml/e-wp_extent-1.html
+ *
+ * Though ECMA mentions the max value as aforementioned. It appears that MSO does not
+ * handle for the same, in fact it actually can handle a max value of int32 i.e
+ * 2147483647( MAX_INTEGER_VALUE ).
+ * Therefore changing the following accordingly so that LO sync's up with MSO.
+ **/
+ sal_uInt64 cx = TwipsToEMU(
+ std::clamp(rSize.Width() + nWidthDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
+ OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32))));
+ sal_uInt64 cy = TwipsToEMU(
+ std::clamp(rSize.Height() + nHeightDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
+ OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32))));
+
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight);
+
+ // XML_effectExtent, includes effects, fat stroke and rotation
+ // FixMe: tdf141880. Because LibreOffice currently cannot handle negative vertical margins, they
+ // were forced to zero on import. Especially bottom margin of inline anchored rotated objects are
+ // affected. If the object was not changed, it would be better to export the original values
+ // from grab-Bag. Unfortunately there exists no marker for "not changed", so a heuristic is used
+ // here: If current left, top and right margins do not differ more than 1Hmm = 635EMU from the
+ // values in grab-Bag, it is likely, that the object was not changed and we restore the values
+ // from grab-Bag.
+ sal_Int64 nLeftExtEMU = TwipsToEMU(nLeftExt);
+ sal_Int64 nTopExtEMU = TwipsToEMU(nTopExt);
+ sal_Int64 nRightExtEMU = TwipsToEMU(nRightExt);
+ sal_Int64 nBottomExtEMU = TwipsToEMU(nBottomExt);
+ if (pObj)
+ {
+ uno::Any aAny;
+ pObj->GetGrabBagItem(aAny);
+ comphelper::SequenceAsHashMap aGrabBag(aAny);
+ auto it = aGrabBag.find("CT_EffectExtent");
+ if (it != aGrabBag.end())
+ {
+ comphelper::SequenceAsHashMap aEffectExtent(it->second);
+ sal_Int64 nLeftExtGrabBag(0);
+ sal_Int64 nTopExtGrabBag(0);
+ sal_Int64 nRightExtGrabBag(0);
+ sal_Int64 nBottomExtGrabBag(0);
+ for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDirection :
+ aEffectExtent)
+ {
+ const OUString& rName = rDirection.first.maString;
+ if (rName == "l" && rDirection.second.has<sal_Int32>())
+ nLeftExtGrabBag = rDirection.second.get<sal_Int32>();
+ else if (rName == "t" && rDirection.second.has<sal_Int32>())
+ nTopExtGrabBag = rDirection.second.get<sal_Int32>();
+ else if (rName == "r" && rDirection.second.has<sal_Int32>())
+ nRightExtGrabBag = rDirection.second.get<sal_Int32>();
+ else if (rName == "b" && rDirection.second.has<sal_Int32>())
+ nBottomExtGrabBag = rDirection.second.get<sal_Int32>();
+ }
+ if (abs(nLeftExtEMU - nLeftExtGrabBag) <= 635 && abs(nTopExtEMU - nTopExtGrabBag) <= 635
+ && abs(nRightExtEMU - nRightExtGrabBag) <= 635)
+ {
+ nLeftExtEMU = nLeftExtGrabBag;
+ nTopExtEMU = nTopExtGrabBag;
+ nRightExtEMU = nRightExtGrabBag;
+ nBottomExtEMU = nBottomExtGrabBag;
+ }
+ }
+ }
+ m_pImpl->getSerializer()->singleElementNS(
+ XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExtEMU), XML_t,
+ OString::number(nTopExtEMU), XML_r, OString::number(nRightExtEMU), XML_b,
+ OString::number(nBottomExtEMU));
+
+ if (!isAnchor)
+ return; // OOXML 'inline' has not wrap type at all
+
+ // XML_anchor has exact one of types wrapNone, wrapSquare, wrapTight, wrapThrough and
+ // WrapTopAndBottom. Map our own types to them as far as possible.
+
+ if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGH
+ || pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGHT)
+ {
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
+ return;
+ }
+
+ if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_NONE)
+ {
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
+ return;
+ }
+
+ // All remaining cases need attribute XML_wrapText
+ OUString sWrapType;
+ switch (pFrameFormat->GetSurround().GetSurround())
+ {
+ case text::WrapTextMode_DYNAMIC:
+ sWrapType = OUString("largest");
+ break;
+ case text::WrapTextMode_LEFT:
+ sWrapType = OUString("left");
+ break;
+ case text::WrapTextMode_RIGHT:
+ sWrapType = OUString("right");
+ break;
+ case text::WrapTextMode_PARALLEL:
+ default:
+ sWrapType = OUString("bothSides");
+ break;
+ }
+
+ // ToDo: Exclude cases where LibreOffice wrap without contour is different
+ // from Word XML_wrapSquare or where direct use of distances not possible and workaround
+ // will be done using wrapPolygon.
+ // ToDo: handle case Writer frame, where contour can be set in LibreOffice but is not rendered.
+
+ // This case needs no wrapPolygon
+ if (!pFrameFormat->GetSurround().IsContour())
+ {
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, sWrapType);
+ return;
+ }
+
+ // Contour wrap.
+ sal_Int32 nWrapToken
+ = pFrameFormat->GetSurround().IsOutside() ? XML_wrapTight : XML_wrapThrough;
+
+ // ToDo: cases where wrapPolygon is used as workaround.
+
+ // Own wrap polygon exists only for TextGraphicObject and TextEmbeddedObject. It might be edited
+ // by user. If such exists, we use it and we are done.
+ if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
+ {
+ const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
+ if (pPolyPoly && pPolyPoly->Count())
+ {
+ tools::Polygon aPoly
+ = sw::util::CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/true);
+ if (aPoly.GetSize() >= 3)
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
+ sWrapType);
+ // ToDo: Test whether XML_edited true or false gives better results.
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
+ OString::number(aPoly[0].X()), XML_y,
+ OString::number(aPoly[0].Y()));
+ for (sal_uInt16 i = 1; i < aPoly.GetSize(); ++i)
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_lineTo, XML_x,
+ OString::number(aPoly[i].X()), XML_y,
+ OString::number(aPoly[i].Y()));
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
+
+ m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
+ return;
+ }
+ }
+ }
+
+ // If this shape comes from ooxml import, there might be a wrap polygon in InteropGrabBag.
+ // Wrap polygons can be edited by users in Word. They are independent from changing shape size or
+ // rotation. So it is likely, that it is still usable.
+ if (pObj)
+ {
+ uno::Any aAny;
+ pObj->GetGrabBagItem(aAny);
+ comphelper::SequenceAsHashMap aGrabBag(aAny);
+ auto it = aGrabBag.find("CT_WrapPath");
+ if (it != aGrabBag.end())
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
+
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
+ auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
+ const auto& rPoints = aSeqSeq[0];
+ for (auto i = rPoints.begin(); i != rPoints.end(); ++i)
+ {
+ const awt::Point& rPoint = *i;
+ m_pImpl->getSerializer()->singleElementNS(
+ XML_wp, (i == rPoints.begin() ? XML_start : XML_lineTo), XML_x,
+ OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
+ }
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
+
+ m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
+ return;
+ }
+ }
+
+ // In this case we likely had an odt document to be exported to docx. ODF does not know the
+ // concept of a wrap polygon and LibreOffice has no one internally. So as a workaround, we
+ // generate a wrap polygon from the shape geometry.
+ tools::Polygon aContour = lcl_CreateContourPolygon(const_cast<SdrObject*>(pObj));
+
+ // lcl_CreateContourPolygon() ensures at least three points
+ m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
+
+ // ToDo: Test whether XML_edited true or false gives better results.
+ m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
+ m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
+ OString::number(aContour.GetPoint(0).getX()), XML_y,
+ OString::number(aContour.GetPoint(0).getY()));
+ for (sal_uInt32 i = 1; i < aContour.GetSize(); i++)
+ m_pImpl->getSerializer()->singleElementNS(
+ XML_wp, XML_lineTo, XML_x, OString::number(aContour.GetPoint(i).getX()), XML_y,
+ OString::number(aContour.GetPoint(i).getY()));
+ m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
+
+ m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
+}
+
+void DocxSdrExport::endDMLAnchorInline(const SwFrameFormat* pFrameFormat)
+{
+ bool isAnchor;
+ if (m_pImpl->getFlyFrameGraphic())
+ {
+ isAnchor = false; // end Inline Graphic object inside DMLTextFrame
+ }
+ else
+ {
+ isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
+ }
+ m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline);
+
+ m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing);
+ m_pImpl->setDrawingOpen(false);
+}
+
+void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat)
+{
+ m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict);
+ m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
+ // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually.
+
+ SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow());
+ const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
+ const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
+ SwFormatSurround const& rSurround(rFrameFormat.GetSurround());
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround));
+ m_pImpl->getExport().VMLExporter().AddSdrObject(
+ *sdrObj, rFlow.GetValue(), rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
+ rHoriOri.GetRelationOrient(), rVertOri.GetRelationOrient(), pAttrList.get(), true);
+ m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict);
+}
+
+static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape)
+{
+ const uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag");
+ /*
+ * Export as Locked Canvas only if the property
+ * is in the PropertySet
+ */
+ return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) {
+ return rProp.Name == "LockedCanvas";
+ });
+}
+
+void AddExtLst(sax_fastparser::FSHelperPtr const& pFS, DocxExport const& rExport,
+ uno::Reference<beans::XPropertySet> const& xShape)
+{
+ if (xShape->getPropertyValue("Decorative").get<bool>())
+ {
+ pFS->startElementNS(XML_a, XML_extLst,
+ // apparently for DOCX the namespace isn't declared on the root
+ FSNS(XML_xmlns, XML_a),
+ rExport.GetFilter().getNamespaceURL(OOX_NS(dml)));
+ pFS->startElementNS(XML_a, XML_ext,
+ // Word uses this "URI" which is obviously not a URI
+ XML_uri, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}");
+ pFS->singleElementNS(XML_adec, XML_decorative, FSNS(XML_xmlns, XML_adec),
+ "http://schemas.microsoft.com/office/drawing/2017/decorative", XML_val,
+ "1");
+ pFS->endElementNS(XML_a, XML_ext);
+ pFS->endElementNS(XML_a, XML_extLst);
+ }
+}
+
+void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat,
+ int nAnchorId)
+{
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape());
+ if (!Impl::isSupportedDMLShape(xShape, pSdrObject))
+ return;
+
+ m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
+
+ sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
+ Size aSize(pSdrObject->GetLogicRect().getOpenWidth(),
+ pSdrObject->GetLogicRect().getOpenHeight());
+ startDMLAnchorInline(pFrameFormat, aSize);
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ pDocPrAttrList->add(XML_id, OString::number(nAnchorId));
+ pDocPrAttrList->add(XML_name, pSdrObject->GetName());
+ if (!pSdrObject->GetTitle().isEmpty())
+ pDocPrAttrList->add(XML_title, pSdrObject->GetTitle());
+ if (!pSdrObject->GetDescription().isEmpty())
+ pDocPrAttrList->add(XML_descr, pSdrObject->GetDescription());
+ if (!pSdrObject->IsVisible()
+ && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+
+ pDocPrAttrList->add(XML_hidden, OString::number(1));
+
+ pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
+ OUString sHyperlink = pSdrObject->getHyperlink();
+ if (!sHyperlink.isEmpty())
+ {
+ OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
+ pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
+ oox::drawingml::URLTransformer().getTransformedString(sHyperlink),
+ oox::drawingml::URLTransformer().isExternalURL(sHyperlink));
+ pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
+ FSNS(XML_xmlns, XML_a),
+ m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
+ }
+ uno::Reference<beans::XPropertySet> const xShapeProps(xShape, uno::UNO_QUERY_THROW);
+ AddExtLst(pFS, m_pImpl->getExport(), xShapeProps);
+
+ pFS->endElementNS(XML_wp, XML_docPr);
+
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
+ const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
+ if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
+ pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
+ else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
+ pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture";
+ pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
+ m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
+ pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace);
+
+ bool bLockedCanvas = lcl_isLockedCanvas(xShape);
+ if (bLockedCanvas)
+ pFS->startElementNS(
+ XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc),
+ m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)));
+
+ m_pImpl->getExport().OutputDML(xShape);
+
+ if (bLockedCanvas)
+ pFS->endElementNS(XML_lc, XML_lockedCanvas);
+ pFS->endElementNS(XML_a, XML_graphicData);
+ pFS->endElementNS(XML_a, XML_graphic);
+
+ // Relative size of the drawing.
+ if (pSdrObject->GetRelativeWidth())
+ {
+ // At the moment drawinglayer objects are always relative from page.
+ OUString sValue;
+ switch (pSdrObject->GetRelativeWidthRelation())
+ {
+ case text::RelOrientation::FRAME:
+ sValue = "margin";
+ break;
+ case text::RelOrientation::PAGE_LEFT:
+ if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
+ sValue = "outsideMargin";
+ else
+ sValue = "leftMargin";
+ break;
+ case text::RelOrientation::PAGE_RIGHT:
+ if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
+ sValue = "insideMargin";
+ else
+ sValue = "rightMargin";
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ default:
+ sValue = "page";
+ break;
+ }
+ pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, sValue);
+ pFS->startElementNS(XML_wp14, XML_pctWidth);
+ pFS->writeEscaped(
+ OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT));
+ pFS->endElementNS(XML_wp14, XML_pctWidth);
+ pFS->endElementNS(XML_wp14, XML_sizeRelH);
+ }
+ if (pSdrObject->GetRelativeHeight())
+ {
+ OUString sValue;
+ switch (pSdrObject->GetRelativeHeightRelation())
+ {
+ case text::RelOrientation::FRAME:
+ sValue = "margin";
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ sValue = "topMargin";
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
+ sValue = "bottomMargin";
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ default:
+ sValue = "page";
+ break;
+ }
+ pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, sValue);
+ pFS->startElementNS(XML_wp14, XML_pctHeight);
+ pFS->writeEscaped(
+ OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT));
+ pFS->endElementNS(XML_wp14, XML_pctHeight);
+ pFS->endElementNS(XML_wp14, XML_sizeRelV);
+ }
+
+ endDMLAnchorInline(pFrameFormat);
+}
+
+void DocxSdrExport::Impl::textFrameShadow(const SwFrameFormat& rFrameFormat)
+{
+ const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
+ if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
+ return;
+
+ OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt");
+ OString aOffset;
+ switch (aShadowItem.GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ aOffset = "-" + aShadowWidth + ",-" + aShadowWidth;
+ break;
+ case SvxShadowLocation::TopRight:
+ aOffset = aShadowWidth + ",-" + aShadowWidth;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ aOffset = "-" + aShadowWidth + "," + aShadowWidth;
+ break;
+ case SvxShadowLocation::BottomRight:
+ aOffset = aShadowWidth + "," + aShadowWidth;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ break;
+ }
+ if (aOffset.isEmpty())
+ return;
+
+ OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
+ m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor,
+ XML_offset, aOffset);
+}
+
+bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape,
+ const SdrObject* pSdrObject)
+{
+ uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
+ if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape")
+ || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape"))
+ return false;
+
+ uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
+ // For signature line shapes, we don't want DML, just the VML shape.
+ if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
+ {
+ bool bIsSignatureLineShape = false;
+ xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape;
+ if (bIsSignatureLineShape)
+ return false;
+ }
+
+ // A FontWork shape with bitmap fill cannot be expressed as a modern 'abc transform'
+ // in Word. Only the legacy VML WordArt allows bitmap fill.
+ if (pSdrObject->IsTextPath())
+ {
+ css::drawing::FillStyle eFillStyle = css::drawing::FillStyle_SOLID;
+ xShapeProperties->getPropertyValue("FillStyle") >>= eFillStyle;
+ if (eFillStyle == css::drawing::FillStyle_BITMAP)
+ return false;
+ }
+ return true;
+}
+
+void DocxSdrExport::writeDMLAndVMLDrawing(const SdrObject* sdrObj,
+ const SwFrameFormat& rFrameFormat, int nAnchorId)
+{
+ bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
+ m_pImpl->setDMLAndVMLDrawingOpen(true);
+
+ // Depending on the shape type, we actually don't write the shape as DML.
+ OUString sShapeType;
+ ShapeFlag nMirrorFlags = ShapeFlag::NONE;
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape());
+
+ MSO_SPT eShapeType
+ = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType);
+
+ // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do.
+ // A common service created in util to check for VML shapes which are allowed to have textbox in content
+ if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape, sdrObj)
+ && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent);
+
+ auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj);
+ m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires,
+ (pObjGroup ? "wpg" : "wps"));
+ writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId);
+ m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice);
+
+ m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback);
+ writeVMLDrawing(sdrObj, rFrameFormat);
+ m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback);
+
+ m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent);
+ }
+ else
+ writeVMLDrawing(sdrObj, rFrameFormat);
+
+ m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
+}
+
+// Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000)
+static OString lcl_TransparencyToDrawingMlAlpha(const Color& rColor)
+{
+ if (rColor.IsTransparent())
+ {
+ sal_Int32 nAlphaPercent = float(rColor.GetAlpha()) / 2.55;
+ return OString::number(nAlphaPercent * oox::drawingml::PER_PERCENT);
+ }
+
+ return OString();
+}
+
+void DocxSdrExport::writeDMLEffectLst(const SwFrameFormat& rFrameFormat)
+{
+ const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
+
+ // Output effects
+ if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
+ return;
+
+ // Distance is measured diagonally from corner
+ double nShadowDist
+ = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0);
+ OString aShadowDist(OString::number(TwipsToEMU(nShadowDist)));
+ OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
+ OString aShadowAlpha = lcl_TransparencyToDrawingMlAlpha(aShadowItem.GetColor());
+ sal_uInt32 nShadowDir = 0;
+ switch (aShadowItem.GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ nShadowDir = 13500000;
+ break;
+ case SvxShadowLocation::TopRight:
+ nShadowDir = 18900000;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ nShadowDir = 8100000;
+ break;
+ case SvxShadowLocation::BottomRight:
+ nShadowDir = 2700000;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ break;
+ }
+ OString aShadowDir(OString::number(nShadowDir));
+
+ m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst);
+ m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir,
+ aShadowDir);
+ if (aShadowAlpha.isEmpty())
+ m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
+ else
+ {
+ m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
+ m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha);
+ m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr);
+ }
+ m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw);
+ m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
+}
+
+void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
+ int nDiagramId)
+{
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
+ uno::UNO_QUERY);
+
+ // write necessary tags to document.xml
+ Size aSize(sdrObject->GetSnapRect().getOpenWidth(), sdrObject->GetSnapRect().getOpenHeight());
+ startDMLAnchorInline(&rFrameFormat, aSize);
+
+ m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
+ m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
+
+ endDMLAnchorInline(&rFrameFormat);
+}
+
+void DocxSdrExport::writeOnlyTextOfFrame(ww8::Frame const* pParentFrame)
+{
+ const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+
+ //Save data here and restore when out of scope
+ ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
+
+ m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList().get());
+ ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
+ comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
+ m_pImpl->getExport().WriteText();
+}
+
+void DocxSdrExport::writeBoxItemLine(const SvxBoxItem& rBox)
+{
+ const editeng::SvxBorderLine* pBorderLine = nullptr;
+
+ if (rBox.GetTop())
+ {
+ pBorderLine = rBox.GetTop();
+ }
+ else if (rBox.GetLeft())
+ {
+ pBorderLine = rBox.GetLeft();
+ }
+ else if (rBox.GetBottom())
+ {
+ pBorderLine = rBox.GetBottom();
+ }
+ else if (rBox.GetRight())
+ {
+ pBorderLine = rBox.GetRight();
+ }
+
+ if (!pBorderLine)
+ {
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
+ if (pBorderLine->GetWidth() == SvxBorderLineWidth::Hairline)
+ pFS->startElementNS(XML_a, XML_ln);
+ else
+ {
+ double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(),
+ pBorderLine->GetWidth()));
+ OString sWidth(OString::number(TwipsToEMU(fConverted)));
+ pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth);
+ }
+
+ pFS->startElementNS(XML_a, XML_solidFill);
+ OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor()));
+ pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
+ pFS->endElementNS(XML_a, XML_solidFill);
+
+ if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type
+ pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
+
+ pFS->endElementNS(XML_a, XML_ln);
+}
+
+void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId,
+ bool bTextBoxOnly)
+{
+ bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
+ m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
+
+ sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
+ const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+
+ //Save data here and restore when out of scope
+ ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
+
+ // When a frame has some low height, but automatically expanded due
+ // to lots of contents, this size contains the real size.
+ const Size aSize = pParentFrame->GetSize();
+
+ uno::Reference<drawing::XShape> xShape;
+ const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
+ if (pSdrObj)
+ xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
+ if (xPropertySet.is())
+ xPropSetInfo = xPropertySet->getPropertySetInfo();
+
+ m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList().get());
+ {
+ drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
+ if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust"))
+ xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
+ m_pImpl->getBodyPrAttrList()->add(XML_anchor,
+ oox::drawingml::GetTextVerticalAdjust(eAdjust));
+ }
+
+ if (!bTextBoxOnly)
+ {
+ startDMLAnchorInline(&rFrameFormat, aSize);
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pDocPrAttrList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ pDocPrAttrList->add(XML_id, OString::number(nAnchorId));
+ pDocPrAttrList->add(XML_name, rFrameFormat.GetName());
+
+ pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
+
+ OUString sHyperlink;
+ if (xPropertySet.is())
+ xPropertySet->getPropertyValue("HyperLinkURL") >>= sHyperlink;
+ if (!sHyperlink.isEmpty())
+ {
+ OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
+ pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
+ oox::drawingml::URLTransformer().getTransformedString(sHyperlink),
+ oox::drawingml::URLTransformer().isExternalURL(sHyperlink));
+ pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
+ FSNS(XML_xmlns, XML_a),
+ m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
+ }
+
+ pFS->endElementNS(XML_wp, XML_docPr);
+
+ pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
+ m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
+ pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
+ "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
+ pFS->startElementNS(XML_wps, XML_wsp);
+ pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1");
+
+ uno::Any aRotation;
+ m_pImpl->setDMLandVMLTextFrameRotation(0_deg100);
+ if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
+ {
+ uno::Sequence<beans::PropertyValue> propList;
+ xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
+ auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
+ [](const beans::PropertyValue& rProp) {
+ return rProp.Name == "mso-rotation-angle";
+ });
+ if (pProp != std::cend(propList))
+ aRotation = pProp->Value;
+ }
+ sal_Int32 nTmp;
+ if (aRotation >>= nTmp)
+ m_pImpl->getDMLandVMLTextFrameRotation() = Degree100(nTmp);
+ OString sRotation(OString::number(
+ oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation())));
+ // Shape properties
+ pFS->startElementNS(XML_wps, XML_spPr);
+ if (m_pImpl->getDMLandVMLTextFrameRotation())
+ {
+ pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation);
+ }
+ else
+ {
+ pFS->startElementNS(XML_a, XML_xfrm);
+ }
+ pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
+ OString aWidth(OString::number(TwipsToEMU(aSize.Width())));
+ OString aHeight(OString::number(TwipsToEMU(aSize.Height())));
+ pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
+ pFS->endElementNS(XML_a, XML_xfrm);
+ OUString shapeType = "rect";
+ if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
+ {
+ uno::Sequence<beans::PropertyValue> propList;
+ xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
+ auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
+ [](const beans::PropertyValue& rProp) {
+ return rProp.Name == "mso-orig-shape-type";
+ });
+ if (pProp != std::cend(propList))
+ pProp->Value >>= shapeType;
+ }
+ //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect"
+ if (shapeType.isEmpty())
+ shapeType = "rect";
+
+ pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType);
+ m_pImpl->setDMLTextFrameSyntax(true);
+ m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
+ m_pImpl->setDMLTextFrameSyntax(false);
+ writeDMLEffectLst(rFrameFormat);
+ pFS->endElementNS(XML_wps, XML_spPr);
+ }
+
+ //first, loop through ALL of the chained textboxes to identify a unique ID for each chain, and sequence number for each textbox in that chain.
+ if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized)
+ {
+ sal_Int32 nSeq = 0;
+ for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper)
+ {
+ //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
+ if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty())
+ {
+ //assign this chain a unique ID and start a new sequence
+ nSeq = 0;
+ rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId;
+ rEntry.second.nSeq = nSeq;
+
+ OUString sCheckForBrokenChains = rEntry.first;
+
+ //follow the chain and assign the same id, and incremental sequence numbers.
+ auto followChainIter
+ = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain);
+ while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
+ {
+ //verify that the NEXT textbox also points to me as the PREVIOUS.
+ // A broken link indicates a leftover remnant that can be ignored.
+ if (followChainIter->second.sPrevChain != sCheckForBrokenChains)
+ break;
+
+ followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId;
+ followChainIter->second.nSeq = ++nSeq;
+
+ //empty next chain indicates the end of the linked chain.
+ if (followChainIter->second.sNextChain.isEmpty())
+ break;
+
+ sCheckForBrokenChains = followChainIter->first;
+ followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(
+ followChainIter->second.sNextChain);
+ }
+ }
+ }
+ m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true;
+ }
+
+ m_pImpl->getExport().m_pParentFrame = nullptr;
+ bool skipTxBxContent = false;
+ bool isTxbxLinked = false;
+
+ OUString sLinkChainName;
+ if (xPropSetInfo.is())
+ {
+ if (xPropSetInfo->hasPropertyByName("LinkDisplayName"))
+ xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
+ else if (xPropSetInfo->hasPropertyByName("ChainName"))
+ xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
+ }
+
+ // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
+ auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName);
+ if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
+ {
+ if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0))
+ {
+ //not the first in the chain, so write the tag as linkedTxbx
+ pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id,
+ OString::number(linkedTextboxesIter->second.nId), XML_seq,
+ OString::number(linkedTextboxesIter->second.nSeq));
+ /* no text content should be added to this tag,
+ since the textbox is linked, the entire content
+ is written in txbx block
+ */
+ skipTxBxContent = true;
+ }
+ else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0))
+ {
+ /* this is the first textbox in the chaining, we add the text content
+ to this block*/
+ //since the text box is linked, it needs an id.
+ pFS->startElementNS(XML_wps, XML_txbx, XML_id,
+ OString::number(linkedTextboxesIter->second.nId));
+ isTxbxLinked = true;
+ }
+ }
+
+ if (!skipTxBxContent)
+ {
+ if (!isTxbxLinked)
+ pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id.
+
+ pFS->startElementNS(XML_w, XML_txbxContent);
+
+ const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir();
+ if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB)
+ m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert");
+ else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
+ m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270");
+ else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB)
+ m_pImpl->getBodyPrAttrList()->add(XML_vert, "mongolianVert");
+ else if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
+ m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert");
+ {
+ ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
+ comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
+ m_pImpl->getExport().WriteText();
+ if (m_pImpl->getParagraphSdtOpen())
+ {
+ m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
+ m_pImpl->setParagraphSdtOpen(false);
+ }
+ }
+
+ pFS->endElementNS(XML_w, XML_txbxContent);
+ pFS->endElementNS(XML_wps, XML_txbx);
+ }
+
+ // We need to init padding to 0, if it's not set.
+ // In LO the default is 0 and so ins attributes are not set when padding is 0
+ // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly
+ if (m_pImpl->getBodyPrAttrList())
+ {
+ if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns))
+ m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0));
+ if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns))
+ m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0));
+ if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns))
+ m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0));
+ if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns))
+ m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0));
+ }
+
+ rtl::Reference<FastAttributeList> xBodyPrAttrList(m_pImpl->getBodyPrAttrList());
+ m_pImpl->setBodyPrAttrList(nullptr);
+ if (!bTextBoxOnly)
+ {
+ pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList);
+ // AutoSize of the Text Frame.
+ const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
+ pFS->singleElementNS(
+ XML_a,
+ (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit));
+ pFS->endElementNS(XML_wps, XML_bodyPr);
+
+ pFS->endElementNS(XML_wps, XML_wsp);
+ pFS->endElementNS(XML_a, XML_graphicData);
+ pFS->endElementNS(XML_a, XML_graphic);
+
+ // Relative size of the Text Frame.
+ const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
+ if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
+ {
+ pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
+ (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME
+ ? "page"
+ : "margin"));
+ pFS->startElementNS(XML_wp14, XML_pctWidth);
+ pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT));
+ pFS->endElementNS(XML_wp14, XML_pctWidth);
+ pFS->endElementNS(XML_wp14, XML_sizeRelH);
+ }
+ const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
+ if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED)
+ {
+ pFS->startElementNS(
+ XML_wp14, XML_sizeRelV, XML_relativeFrom,
+ (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page"
+ : "margin"));
+ pFS->startElementNS(XML_wp14, XML_pctHeight);
+ pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT));
+ pFS->endElementNS(XML_wp14, XML_pctHeight);
+ pFS->endElementNS(XML_wp14, XML_sizeRelV);
+ }
+
+ endDMLAnchorInline(&rFrameFormat);
+ }
+ m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
+}
+
+void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly)
+{
+ bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
+ m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
+
+ sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
+ const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+
+ //Save data here and restore when out of scope
+ ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
+
+ // When a frame has some low height, but automatically expanded due
+ // to lots of contents, this size contains the real size.
+ const Size aSize = pParentFrame->GetSize();
+ m_pImpl->setFlyFrameSize(&aSize);
+
+ m_pImpl->setTextFrameSyntax(true);
+ m_pImpl->setFlyAttrList(sax_fastparser::FastSerializerHelper::createAttrList());
+ m_pImpl->setTextboxAttrList(sax_fastparser::FastSerializerHelper::createAttrList());
+ m_pImpl->getTextFrameStyle() = "position:absolute";
+ if (!bTextBoxOnly)
+ {
+ OString sRotation(OString::number(-toDegrees(m_pImpl->getDMLandVMLTextFrameRotation())));
+ m_pImpl->getExport().SdrExporter().getTextFrameStyle().append(";rotation:" + sRotation);
+ }
+ m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
+ m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear());
+
+ const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject();
+ if (pObject != nullptr)
+ {
+ OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject);
+ if (!sAnchorId.isEmpty())
+ m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId, sAnchorId);
+
+ uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ OUString sHyperlink;
+ if (xShapeProps.is())
+ xShapeProps->getPropertyValue("HyperLinkURL") >>= sHyperlink;
+ if (!sHyperlink.isEmpty())
+ m_pImpl->getFlyAttrList()->add(XML_href, sHyperlink);
+ }
+ rtl::Reference<FastAttributeList> xFlyAttrList(m_pImpl->getFlyAttrList());
+ m_pImpl->getFlyAttrList().clear();
+ rtl::Reference<FastAttributeList> xTextboxAttrList(m_pImpl->getTextboxAttrList());
+ m_pImpl->getTextboxAttrList().clear();
+ m_pImpl->setTextFrameSyntax(false);
+ m_pImpl->setFlyFrameSize(nullptr);
+ m_pImpl->getExport().m_pParentFrame = nullptr;
+
+ if (!bTextBoxOnly)
+ {
+ pFS->startElementNS(XML_w, XML_pict);
+ pFS->startElementNS(XML_v, XML_rect, xFlyAttrList);
+ m_pImpl->textFrameShadow(rFrameFormat);
+ if (m_pImpl->getFlyFillAttrList().is())
+ {
+ rtl::Reference<FastAttributeList> xFlyFillAttrList(m_pImpl->getFlyFillAttrList());
+ pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList);
+ }
+ if (m_pImpl->getDashLineStyleAttr().is())
+ {
+ rtl::Reference<FastAttributeList> xDashLineStyleAttr(m_pImpl->getDashLineStyleAttr());
+ pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr);
+ }
+ pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
+ }
+ m_pImpl->getFlyFillAttrList().clear();
+ m_pImpl->getDashLineStyleAttr().clear();
+
+ pFS->startElementNS(XML_w, XML_txbxContent);
+ {
+ ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
+ comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
+ m_pImpl->getExport().WriteText();
+ if (m_pImpl->getParagraphSdtOpen())
+ {
+ m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
+ m_pImpl->setParagraphSdtOpen(false);
+ }
+ }
+ pFS->endElementNS(XML_w, XML_txbxContent);
+ if (!bTextBoxOnly)
+ {
+ pFS->endElementNS(XML_v, XML_textbox);
+
+ if (m_pImpl->getFlyWrapAttrList())
+ {
+ rtl::Reference<FastAttributeList> xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList());
+ m_pImpl->setFlyWrapAttrList(nullptr);
+ pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList);
+ }
+
+ pFS->endElementNS(XML_v, XML_rect);
+ pFS->endElementNS(XML_w, XML_pict);
+ }
+
+ m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
+}
+
+bool DocxSdrExport::isTextBox(const SwFrameFormat& rFrameFormat)
+{
+ return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxsdrexport.hxx b/sw/source/filter/ww8/docxsdrexport.hxx
new file mode 100644
index 0000000000..5ad93d51ad
--- /dev/null
+++ b/sw/source/filter/ww8/docxsdrexport.hxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX
+
+#include <memory>
+
+#include <rtl/strbuf.hxx>
+#include <sax/fshelper.hxx>
+#include <nodeoffset.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+namespace rtl
+{
+template <typename> class Reference;
+}
+namespace oox::drawingml { class DrawingML; }
+class Size;
+class SdrObject;
+class SvxBoxItem;
+
+namespace ww8
+{
+class Frame;
+}
+class SwFrameFormat;
+
+class DocxExport;
+
+/// Helper class, so that the DocxExport::RestoreData() call will always happen.
+class ExportDataSaveRestore
+{
+private:
+ DocxExport& m_rExport;
+public:
+ ExportDataSaveRestore(DocxExport& rExport, SwNodeOffset nStt, SwNodeOffset nEnd, ww8::Frame const* pParentFrame);
+ ~ExportDataSaveRestore();
+};
+
+/// Handles DOCX export of drawings.
+class DocxSdrExport
+{
+ struct Impl;
+ std::unique_ptr<Impl> m_pImpl;
+public:
+ DocxSdrExport(DocxExport& rExport, const sax_fastparser::FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML);
+ ~DocxSdrExport();
+
+ void setSerializer(const sax_fastparser::FSHelperPtr& pSerializer);
+ /// When exporting fly frames, this holds the real size of the frame.
+ const Size* getFlyFrameSize() const;
+ bool getTextFrameSyntax() const;
+ bool getDMLTextFrameSyntax() const;
+ rtl::Reference<sax_fastparser::FastAttributeList>& getFlyAttrList();
+ /// Attributes of the next v:textbox element.
+ rtl::Reference<sax_fastparser::FastAttributeList>& getTextboxAttrList();
+ OStringBuffer& getTextFrameStyle();
+
+ /// Set if paragraph sdt open in the current drawing.
+ void setParagraphSdtOpen(bool bParagraphSdtOpen);
+
+ bool IsDrawingOpen() const;
+ bool IsDMLAndVMLDrawingOpen() const;
+ bool IsParagraphHasDrawing() const;
+ void setParagraphHasDrawing(bool bParagraphHasDrawing);
+ rtl::Reference<sax_fastparser::FastAttributeList>& getFlyFillAttrList();
+ void setFlyWrapAttrList(rtl::Reference<sax_fastparser::FastAttributeList> const & pAttrList);
+ /// Attributes of <wps:bodyPr>, used during DML export of text frames.
+ sax_fastparser::FastAttributeList* getBodyPrAttrList();
+ rtl::Reference<sax_fastparser::FastAttributeList>& getDashLineStyle();
+
+ void startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize);
+ void endDMLAnchorInline(const SwFrameFormat* pFrameFormat);
+ /// Writes a drawing as VML data.
+ void writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat);
+ /// Writes a drawing as DML.
+ void writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat, int nAnchorId);
+ /// Writes shape in both DML and VML format.
+ void writeDMLAndVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat, int nAnchorId);
+ /// Write <a:effectLst>, the effect list.
+ void writeDMLEffectLst(const SwFrameFormat& rFrameFormat);
+ /// Writes a diagram (smartart).
+ void writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat, int nDiagramId);
+ /// Writes text frame in DML format.
+ void writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId, bool bTextBoxOnly = false);
+ /// Writes text frame in VML format.
+ void writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly = false);
+ /// Is this a standalone TextFrame, or used as a TextBox of a shape?
+ static bool isTextBox(const SwFrameFormat& rFrameFormat);
+ /// Writes text from Textbox for <w:framePr>
+ void writeOnlyTextOfFrame(ww8::Frame const* pParentFrame);
+ /// Writes the drawingML <a:ln> markup of a box item.
+ void writeBoxItemLine(const SvxBoxItem& rBox);
+};
+
+void AddExtLst(sax_fastparser::FSHelperPtr const& pFS, DocxExport const& rExport,
+ css::uno::Reference<css::beans::XPropertySet> const& xShape);
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxtableexport.cxx b/sw/source/filter/ww8/docxtableexport.cxx
new file mode 100644
index 0000000000..5972903ec5
--- /dev/null
+++ b/sw/source/filter/ww8/docxtableexport.cxx
@@ -0,0 +1,894 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "docxattributeoutput.hxx"
+
+#include <com/sun/star/text/XTextTable.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <svl/grabbagitem.hxx>
+#include <sax/fshelper.hxx>
+#include <editeng/ulspitem.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <tools/datetimeutils.hxx>
+
+#include <fmtfsize.hxx>
+#include <unocoll.hxx>
+#include <formatflysplit.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <frmatr.hxx>
+#include <swmodule.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+
+#include "docxexportfilter.hxx"
+#include "docxhelper.hxx"
+
+using namespace com::sun::star;
+using namespace sax_fastparser;
+using namespace oox;
+
+namespace
+{
+/// Does the same as comphelper::string::padToLength(), but extends the start, not the end.
+OString lcl_padStartToLength(OString const& aString, sal_Int32 nLen, char cFill)
+{
+ if (nLen > aString.getLength())
+ {
+ sal_Int32 nDiff = nLen - aString.getLength();
+ OStringBuffer aBuffer;
+ comphelper::string::padToLength(aBuffer, nDiff, cFill);
+ aBuffer.append(aString);
+ return aBuffer.makeStringAndClear();
+ }
+ else
+ return aString;
+}
+
+//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
+//Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
+//To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
+sal_Int32 lcl_getWordCompatibilityMode(const DocxExport& rDocExport)
+{
+ sal_Int32 nWordCompatibilityMode = rDocExport.getWordCompatibilityModeFromGrabBag();
+
+ // TODO: this is duplicated, better store it in DocxExport member?
+ if (!rDocExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
+ {
+ if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
+ {
+ nWordCompatibilityMode = 14;
+ }
+ }
+
+ return nWordCompatibilityMode;
+}
+
+void CollectFloatingTableAttributes(DocxExport& rExport, const ww8::Frame& rFrame,
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner,
+ rtl::Reference<FastAttributeList>& pAttributes)
+{
+ // we export the values of the surrounding Frame
+ OString sOrientation;
+ sal_Int32 nValue;
+
+ // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
+ OString sTblpXSpec = DocxAttributeOutput::convertToOOXMLHoriOrient(
+ rFrame.GetFrameFormat().GetHoriOrient().GetHoriOrient(),
+ rFrame.GetFrameFormat().GetHoriOrient().IsPosToggle());
+ OString sTblpYSpec = DocxAttributeOutput::convertToOOXMLVertOrient(
+ rFrame.GetFrameFormat().GetVertOrient().GetVertOrient());
+
+ sOrientation = DocxAttributeOutput::convertToOOXMLVertOrientRel(
+ rFrame.GetFrameFormat().GetVertOrient().GetRelationOrient());
+ pAttributes->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
+
+ if (!sTblpYSpec.isEmpty())
+ pAttributes->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
+
+ sOrientation = DocxAttributeOutput::convertToOOXMLHoriOrientRel(
+ rFrame.GetFrameFormat().GetHoriOrient().GetRelationOrient());
+ pAttributes->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
+
+ if (!sTblpXSpec.isEmpty())
+ pAttributes->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
+
+ nValue = rFrame.GetFrameFormat().GetULSpace().GetLower();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_bottomFromText), OString::number(nValue));
+
+ nValue = rFrame.GetFrameFormat().GetLRSpace().GetLeft();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_leftFromText), OString::number(nValue));
+
+ nValue = rFrame.GetFrameFormat().GetLRSpace().GetRight();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_rightFromText), OString::number(nValue));
+
+ nValue = rFrame.GetFrameFormat().GetULSpace().GetUpper();
+ if (nValue != 0)
+ pAttributes->add(FSNS(XML_w, XML_topFromText), OString::number(nValue));
+
+ if (sTblpXSpec.isEmpty()) // do not write tblpX if tblpXSpec is present
+ {
+ nValue = rFrame.GetFrameFormat().GetHoriOrient().GetPos();
+ // we need to revert the additional shift introduced by
+ // lcl_DecrementHoriOrientPosition() in writerfilter
+ // 1st: left distance of the table
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+ const SvxBoxItem& rBox = pFrameFormat->GetBox();
+ sal_Int32 nMode = lcl_getWordCompatibilityMode(rExport);
+ if (nMode < 15)
+ {
+ sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
+ nValue += nLeftDistance;
+ }
+
+ // 2nd: if a left border is given, revert the shift by half the width
+ // from lcl_DecrementHoriOrientPosition() in writerfilter
+ if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
+ {
+ tools::Long nWidth = pLeftBorder->GetWidth();
+ nValue += (nWidth / 2);
+ }
+
+ pAttributes->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
+ }
+
+ if (sTblpYSpec.isEmpty()) // do not write tblpY if tblpYSpec is present
+ {
+ nValue = rFrame.GetFrameFormat().GetVertOrient().GetPos();
+ pAttributes->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
+ }
+}
+}
+
+void DocxAttributeOutput::TableInfoCell(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+}
+
+void DocxAttributeOutput::TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/)
+{
+}
+
+void DocxAttributeOutput::TableDefinition(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ bool const bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+
+ // Write the table properties
+ m_pSerializer->startElementNS(XML_w, XML_tblPr);
+
+ static const sal_Int32 aOrder[] = { FSNS(XML_w, XML_tblStyle),
+ FSNS(XML_w, XML_tblpPr),
+ FSNS(XML_w, XML_tblOverlap),
+ FSNS(XML_w, XML_bidiVisual),
+ FSNS(XML_w, XML_tblStyleRowBandSize),
+ FSNS(XML_w, XML_tblStyleColBandSize),
+ FSNS(XML_w, XML_tblW),
+ FSNS(XML_w, XML_jc),
+ FSNS(XML_w, XML_tblCellSpacing),
+ FSNS(XML_w, XML_tblInd),
+ FSNS(XML_w, XML_tblBorders),
+ FSNS(XML_w, XML_shd),
+ FSNS(XML_w, XML_tblLayout),
+ FSNS(XML_w, XML_tblCellMar),
+ FSNS(XML_w, XML_tblLook),
+ FSNS(XML_w, XML_tblPrChange) };
+
+ // postpone the output so that we can later []
+ // prepend the properties before the run
+ // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
+ m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder));
+
+ tools::Long nPageSize = 0;
+ const char* widthType = "dxa";
+
+ // If actual width of table is relative it should export is as "pct".`
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ SwFrameFormat* pTableFormat = pTable->GetFrameFormat();
+ const SwFormatFrameSize& rSize = pTableFormat->GetFrameSize();
+ int nWidthPercent = rSize.GetWidthPercent();
+ // If we export a floating table: we use the widthPercent of the surrounding frame
+ const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
+ if (pFloatingTableFrame)
+ {
+ const SwFormatFrameSize& rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize();
+ nWidthPercent = rFrameSize.GetWidthPercent();
+ }
+
+ uno::Reference<beans::XPropertySet> xPropertySet(
+ SwXTextTables::GetObject(*pTable->GetFrameFormat()), uno::UNO_QUERY);
+ bool isWidthRelative = false;
+ xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
+ if (!isWidthRelative && !nWidthPercent)
+ {
+ // The best fit for "automatic" table placement is relative 100%
+ short nHoriOrient = -1;
+ xPropertySet->getPropertyValue("HoriOrient") >>= nHoriOrient;
+ isWidthRelative = nHoriOrient == text::HoriOrientation::FULL;
+ if (isWidthRelative)
+ nWidthPercent = 100;
+ }
+
+ if (isWidthRelative)
+ {
+ /**
+ * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)]
+ * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
+ *
+ * Fiftieths of a Percent :
+ * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
+ * pct Width is in Fiftieths of a Percent
+ *
+ * ex. If the Table width is 50% then
+ * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
+ **/
+ nPageSize = nWidthPercent * 50;
+ widthType = "pct";
+ }
+ else
+ {
+ bool bRelBoxSize = false;
+ // Create the SwWriteTable instance to use col spans (and maybe other infos)
+ GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
+ if (nPageSize == 0)
+ widthType = "auto";
+ }
+
+ // Output the table preferred width
+ m_pSerializer->singleElementNS(XML_w, XML_tblW, FSNS(XML_w, XML_w), OString::number(nPageSize),
+ FSNS(XML_w, XML_type), widthType);
+
+ // Disable layout autofit, as it does not exist in LibreOffice yet
+ m_pSerializer->singleElementNS(XML_w, XML_tblLayout, FSNS(XML_w, XML_type), "fixed");
+
+ // Look for the table style property in the table grab bag
+ const std::map<OUString, css::uno::Any>& rGrabBag
+ = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
+
+ // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
+ // table border style of the previous table.
+ std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back();
+ rTableStyleConf.clear();
+
+ bool bFloatingTableWritten = false;
+ if (pFloatingTableFrame && pFloatingTableFrame->GetFrameFormat().GetFlySplit().GetValue())
+ {
+ rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
+ CollectFloatingTableAttributes(m_rExport, *pFloatingTableFrame, pTableTextNodeInfoInner,
+ pAttributes);
+ m_pSerializer->singleElementNS(XML_w, XML_tblpPr, pAttributes);
+ bFloatingTableWritten = true;
+ // The outer table was floating, make sure potential inner tables are not floating.
+ m_rExport.SetFloatingTableFrame(nullptr);
+
+ const SwFrameFormat& rFloatingTableFormat = pFloatingTableFrame->GetFrameFormat();
+ if (!rFloatingTableFormat.GetWrapInfluenceOnObjPos().GetAllowOverlap())
+ {
+ // Allowing overlap is the default, both in OOXML and in Writer.
+ m_pSerializer->singleElementNS(XML_w, XML_tblOverlap, FSNS(XML_w, XML_val), "never");
+ }
+ }
+
+ // Extract properties from grab bag
+ for (const auto& rGrabBagElement : rGrabBag)
+ {
+ if (rGrabBagElement.first == "TableStyleName")
+ {
+ OString sStyleName
+ = OUStringToOString(rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName);
+ }
+ else if (rGrabBagElement.first == "TableStyleTopBorder")
+ rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleBottomBorder")
+ rTableStyleConf[SvxBoxItemLine::BOTTOM]
+ = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleLeftBorder")
+ rTableStyleConf[SvxBoxItemLine::LEFT]
+ = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleRightBorder")
+ rTableStyleConf[SvxBoxItemLine::RIGHT]
+ = rGrabBagElement.second.get<table::BorderLine2>();
+ else if (rGrabBagElement.first == "TableStyleLook")
+ {
+ rtl::Reference<FastAttributeList> pAttributeList
+ = FastSerializerHelper::createAttrList();
+ const uno::Sequence<beans::PropertyValue> aAttributeList
+ = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue>>();
+
+ for (const auto& rAttribute : aAttributeList)
+ {
+ if (rAttribute.Name == "val")
+ pAttributeList->add(
+ FSNS(XML_w, XML_val),
+ lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16),
+ 4, '0'));
+ else
+ {
+ static DocxStringTokenMap const aTokens[]
+ = { { "firstRow", XML_firstRow },
+ { "lastRow", XML_lastRow },
+ { "firstColumn", XML_firstColumn },
+ { "lastColumn", XML_lastColumn },
+ { "noHBand", XML_noHBand },
+ { "noVBand", XML_noVBand },
+ { nullptr, 0 } };
+
+ if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
+ pAttributeList->add(FSNS(XML_w, nToken),
+ (rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
+ }
+ }
+
+ m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
+ }
+ else if (rGrabBagElement.first == "TablePosition" &&
+ // skip empty table position (tables in footnotes converted to
+ // floating tables temporarily, don't export this)
+ rGrabBagElement.second != uno::Any())
+ {
+ rtl::Reference<FastAttributeList> attrListTablePos
+ = FastSerializerHelper::createAttrList();
+ const uno::Sequence<beans::PropertyValue> aTablePosition
+ = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue>>();
+ // look for a surrounding frame and take it's position values
+ const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
+ if (pFrame)
+ {
+ CollectFloatingTableAttributes(m_rExport, *pFrame, pTableTextNodeInfoInner,
+ attrListTablePos);
+ }
+ else // ( pFrame = 0 )
+ {
+ // we export the values from the grabBag
+ for (const auto& rProp : aTablePosition)
+ {
+ if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
+ }
+ else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sOrientation);
+ }
+ else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
+ }
+ else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty())
+ {
+ OString sOrientation
+ = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
+ attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sOrientation);
+ }
+ else if (rProp.Name == "bottomFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_bottomFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "leftFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_leftFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "rightFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_rightFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "topFromText")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_topFromText),
+ OString::number(nValue));
+ }
+ else if (rProp.Name == "tblpX")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_tblpX), OString::number(nValue));
+ }
+ else if (rProp.Name == "tblpY")
+ {
+ sal_Int32 nValue = rProp.Value.get<sal_Int32>();
+ attrListTablePos->add(FSNS(XML_w, XML_tblpY), OString::number(nValue));
+ }
+ }
+ }
+
+ if (!bFloatingTableWritten)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_tblpPr, attrListTablePos);
+ }
+ }
+ else
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: "
+ << rGrabBagElement.first);
+ }
+
+ // Output the table alignment
+ const char* pJcVal;
+ sal_Int32 nIndent = 0;
+ switch (pTableFormat->GetHoriOrient().GetHoriOrient())
+ {
+ case text::HoriOrientation::CENTER:
+ pJcVal = "center";
+ break;
+ case text::HoriOrientation::RIGHT:
+ if (bEcma)
+ pJcVal = "right";
+ else
+ pJcVal = "end";
+ break;
+ default:
+ case text::HoriOrientation::NONE:
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ {
+ if (bEcma)
+ pJcVal = "left";
+ else
+ pJcVal = "start";
+ nIndent = sal_Int32(pTableFormat->GetLRSpace().GetLeft());
+
+ // Table indentation has different meaning in Word, depending if the table is nested or not.
+ // If nested, tblInd is added to parent table's left spacing and defines left edge position
+ // If not nested, text position of left-most cell must be at absolute X = tblInd
+ // so, table_spacing + table_spacing_to_content = tblInd
+
+ // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
+ // if no compatibilityMode is defined (which now should only happen on a new export to .docx),
+ // LO uses a higher compatibility than 2010's 14.
+ sal_Int32 nMode = lcl_getWordCompatibilityMode(m_rExport);
+
+ const SwFrameFormat* pFrameFormat
+ = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
+ if ((0 < nMode && nMode <= 14) && m_tableReference.m_nTableDepth == 0)
+ nIndent += pFrameFormat->GetBox().GetDistance(SvxBoxItemLine::LEFT);
+ else
+ {
+ // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border.
+ nIndent -= pFrameFormat->GetBox().CalcLineWidth(SvxBoxItemLine::LEFT) / 2;
+ }
+
+ break;
+ }
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal);
+
+ // Output the table background color (although cell value still needs to be specified)
+ const SvxBrushItem* pColorProp
+ = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
+ if (aColor != COL_AUTO)
+ {
+ OString sColor = msfilter::util::ConvertColor(aColor);
+ m_pSerializer->singleElementNS(XML_w, XML_shd, FSNS(XML_w, XML_fill), sColor,
+ FSNS(XML_w, XML_val), "clear");
+ }
+
+ // Output the table borders
+ TableDefaultBorders(pTableTextNodeInfoInner);
+
+ // Output the default cell margins
+ TableDefaultCellMargins(pTableTextNodeInfoInner);
+
+ TableBidi(pTableTextNodeInfoInner);
+
+ // Table indent (need to get written even if == 0)
+ m_pSerializer->singleElementNS(XML_w, XML_tblInd, FSNS(XML_w, XML_w), OString::number(nIndent),
+ FSNS(XML_w, XML_type), "dxa");
+
+ // Merge the marks for the ordered elements
+ m_pSerializer->mergeTopMarks(Tag_TableDefinition);
+
+ m_pSerializer->endElementNS(XML_w, XML_tblPr);
+
+ // Write the table grid infos
+ m_pSerializer->startElementNS(XML_w, XML_tblGrid);
+ sal_Int32 nPrv = 0;
+ ww8::WidthsPtr pColumnWidths = GetColumnWidths(pTableTextNodeInfoInner);
+ for (auto aColumnWidth : *pColumnWidths)
+ {
+ sal_Int32 nWidth = sal_Int32(aColumnWidth) - nPrv;
+ m_pSerializer->singleElementNS(XML_w, XML_gridCol, FSNS(XML_w, XML_w),
+ OString::number(nWidth));
+ nPrv = sal_Int32(aColumnWidth);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_tblGrid);
+}
+
+void DocxAttributeOutput::TableDefaultBorders(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style.
+ // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit.
+}
+
+void DocxAttributeOutput::TableDefaultCellMargins(
+ ww8::WW8TableNodeInfoInner::Pointer_t const& pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+ const SvxBoxItem& rBox = pFrameFormat->GetBox();
+ const bool bEcma = GetExport().GetFilter().getVersion() == oox::core::ECMA_376_1ST_EDITION;
+
+ DocxAttributeOutput::ImplCellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
+}
+
+void DocxAttributeOutput::TableBackgrounds(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTableRow = pTableBox->GetUpper();
+ const SwFrameFormat* pFormat = pTableBox->GetFrameFormat();
+
+ const SvxBrushItem* pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
+
+ const SwFrameFormat* pRowFormat = pTableRow->GetFrameFormat();
+ const SvxBrushItem* pRowColorProp
+ = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if (pRowColorProp && aColor == COL_AUTO)
+ aColor = pRowColorProp->GetColor();
+
+ const SwFrameFormat* pTableFormat = pTable->GetFrameFormat();
+ const SvxBrushItem* pTableColorProp
+ = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if (pTableColorProp && aColor == COL_AUTO)
+ aColor = pTableColorProp->GetColor();
+
+ const OString sColor = msfilter::util::ConvertColor(aColor);
+
+ const std::map<OUString, css::uno::Any>& rGrabBag
+ = pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
+
+ OString sOriginalColor;
+ auto aGrabBagIt = rGrabBag.find("originalColor");
+ if (aGrabBagIt != rGrabBag.end())
+ sOriginalColor
+ = OUStringToOString(aGrabBagIt->second.get<OUString>(), RTL_TEXTENCODING_UTF8);
+
+ if (sOriginalColor != sColor)
+ {
+ // color changed by the user, or no grab bag: write sColor
+ if (sColor != "auto")
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_shd, FSNS(XML_w, XML_fill), sColor,
+ FSNS(XML_w, XML_val), "clear");
+ }
+ }
+ else
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
+
+ for (const auto & [ name, val ] : rGrabBag)
+ {
+ if (!val.has<OUString>())
+ continue;
+
+ if (name == "themeFill")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFill), val.get<OUString>());
+ else if (name == "themeFillTint")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFillTint), val.get<OUString>());
+ else if (name == "themeFillShade")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeFillShade), val.get<OUString>());
+ else if (name == "fill")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_fill), val.get<OUString>());
+ else if (name == "themeColor")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeColor), val.get<OUString>());
+ else if (name == "themeTint")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeTint), val.get<OUString>());
+ else if (name == "themeShade")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_themeShade), val.get<OUString>());
+ else if (name == "color")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_color), val.get<OUString>());
+ else if (name == "val")
+ AddToAttrList(pAttrList, FSNS(XML_w, XML_val), val.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_shd, pAttrList);
+ }
+}
+
+void DocxAttributeOutput::TableRowRedline(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+
+ bool bRemovePersonalInfo
+ = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
+ && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
+
+ // check table row property "HasTextChangesOnly"
+ SwRedlineTable::size_type nPos(0);
+ SwRedlineTable::size_type nChange = pTabLine->UpdateTextChangesOnly(nPos);
+ // tdf#150824 if no tracked table row, is the table in a single redline?
+ // if yes, convert the row to a tracked table row instead of losing its tracking
+ if (nChange == SwRedlineTable::npos)
+ nChange = pTabLine->GetTableRedline();
+ if (nChange != SwRedlineTable::npos)
+ {
+ const SwRedlineTable& aRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ const SwRangeRedline* pRedline = aRedlineTable[nChange];
+ SwTableRowRedline* pTableRowRedline = nullptr;
+ bool bIsInExtra = false;
+
+ // use the original DOCX redline data stored in ExtraRedlineTable,
+ // if it exists and its type wasn't changed
+ const SwExtraRedlineTable& aExtraRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize();
+ ++nCurRedlinePos)
+ {
+ SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
+ pTableRowRedline = dynamic_cast<SwTableRowRedline*>(pExtraRedline);
+ if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine)
+ {
+ bIsInExtra = true;
+ break;
+ }
+ }
+
+ const SwRedlineData& aRedlineData
+ = bIsInExtra &&
+ // still the same type (an inserted row could become a tracked deleted one)
+ pTableRowRedline->GetRedlineData().GetType()
+ == pRedline->GetRedlineData().GetType()
+ ? pTableRowRedline->GetRedlineData()
+ : pRedline->GetRedlineData();
+
+ // Note: all redline ranges and table row redline (with the same author and timestamp)
+ // use the same redline id in OOXML exported by MSO, but it seems, the recent solution
+ // (different IDs for different ranges, also row changes) is also portable.
+ OString aId(OString::number(m_nRedlineId++));
+ const OUString& rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
+ OString aAuthor(OUStringToOString(
+ bRemovePersonalInfo ? "Author" + OUString::number(GetExport().GetInfoID(rAuthor))
+ : rAuthor,
+ RTL_TEXTENCODING_UTF8));
+
+ const DateTime aDateTime = aRedlineData.GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo
+ || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1
+ && aDateTime.GetDay() == 1);
+
+ if (bNoDate)
+ m_pSerializer->singleElementNS(
+ XML_w, RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
+ FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor);
+ else
+ m_pSerializer->singleElementNS(
+ XML_w, RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins,
+ FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor, FSNS(XML_w, XML_date),
+ DateTimeToOString(aDateTime));
+ return;
+ }
+}
+
+void DocxAttributeOutput::TableCellRedline(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+
+ bool bRemovePersonalInfo
+ = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
+ && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
+
+ // check table row property "HasTextChangesOnly"
+ SwRedlineTable::size_type nChange = pTabBox->GetRedline();
+ if (nChange != SwRedlineTable::npos)
+ {
+ const SwRedlineTable& aRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ const SwRangeRedline* pRedline = aRedlineTable[nChange];
+ SwTableCellRedline* pTableCellRedline = nullptr;
+ bool bIsInExtra = false;
+
+ // use the original DOCX redline data stored in ExtraRedlineTable,
+ // if it exists and its type wasn't changed
+ const SwExtraRedlineTable& aExtraRedlineTable
+ = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize();
+ ++nCurRedlinePos)
+ {
+ SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
+ pTableCellRedline = dynamic_cast<SwTableCellRedline*>(pExtraRedline);
+ if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
+ {
+ bIsInExtra = true;
+ break;
+ }
+ }
+
+ const SwRedlineData& aRedlineData
+ = bIsInExtra &&
+ // still the same type (an inserted cell could become a tracked deleted one)
+ pRedline->GetRedlineData().GetType() == pRedline->GetRedlineData().GetType()
+ ? pTableCellRedline->GetRedlineData()
+ : pRedline->GetRedlineData();
+
+ // Note: all redline ranges and table row redline (with the same author and timestamp)
+ // use the same redline id in OOXML exported by MSO, but it seems, the recent solution
+ // (different IDs for different ranges, also row changes) is also portable.
+ OString aId(OString::number(m_nRedlineId++));
+ const OUString& rAuthor(SW_MOD()->GetRedlineAuthor(aRedlineData.GetAuthor()));
+ OString aAuthor(OUStringToOString(
+ bRemovePersonalInfo ? "Author" + OUString::number(GetExport().GetInfoID(rAuthor))
+ : rAuthor,
+ RTL_TEXTENCODING_UTF8));
+
+ const DateTime aDateTime = aRedlineData.GetTimeStamp();
+ bool bNoDate = bRemovePersonalInfo
+ || (aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1
+ && aDateTime.GetDay() == 1);
+
+ if (bNoDate)
+ m_pSerializer->singleElementNS(
+ XML_w, RedlineType::Delete == pRedline->GetType() ? XML_cellDel : XML_cellIns,
+ FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor);
+ else
+ m_pSerializer->singleElementNS(
+ XML_w, RedlineType::Delete == pRedline->GetType() ? XML_cellDel : XML_cellIns,
+ FSNS(XML_w, XML_id), aId, FSNS(XML_w, XML_author), aAuthor, FSNS(XML_w, XML_date),
+ DateTimeToOString(aDateTime));
+ return;
+ }
+}
+
+void DocxAttributeOutput::TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
+
+ const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
+ if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
+ return;
+
+ sal_Int32 nHeight = rLSz.GetHeight();
+ const char* pRule = nullptr;
+
+ switch (rLSz.GetHeightSizeType())
+ {
+ case SwFrameSize::Fixed:
+ pRule = "exact";
+ break;
+ case SwFrameSize::Minimum:
+ pRule = "atLeast";
+ break;
+ default:
+ break;
+ }
+
+ if (pRule)
+ m_pSerializer->singleElementNS(XML_w, XML_trHeight, FSNS(XML_w, XML_val),
+ OString::number(nHeight), FSNS(XML_w, XML_hRule), pRule);
+}
+
+void DocxAttributeOutput::TableCanSplit(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
+
+ const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
+ // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
+ // as default row prop is allow row to break across page.
+ if (!rSplittable.GetValue())
+ m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true");
+}
+
+void DocxAttributeOutput::TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
+
+ if (m_rExport.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true");
+ }
+}
+
+void DocxAttributeOutput::TableVerticalCell(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat* pFrameFormat = pTabBox->GetFrameFormat();
+
+ if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pFrameFormat))
+ m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl");
+ else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pFrameFormat))
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr");
+ }
+
+ const SwWriteTableRows& rRows = m_xTableWrt->GetRows();
+ const auto nRow = pTableTextNodeInfoInner->getRow();
+ if (nRow >= rRows.size())
+ {
+ SAL_WARN("sw.ww8", "DocxAttributeOutput::TableCellProperties: out of range row: " << nRow);
+ return;
+ }
+ SwWriteTableRow* pRow = rRows[nRow].get();
+ sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
+ const SwWriteTableCells& rTableCells = pRow->GetCells();
+ if (nCell >= rTableCells.size())
+ return;
+
+ const SwWriteTableCell* const pCell = pRow->GetCells()[nCell].get();
+ switch (pCell->GetVertOri())
+ {
+ case text::VertOrientation::TOP:
+ break;
+ case text::VertOrientation::CENTER:
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
+ break;
+ case text::VertOrientation::BOTTOM:
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
+ break;
+ }
+}
+
+void DocxAttributeOutput::TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner)
+{
+ // This is called when the nested table ends in a cell, and there's no
+ // paragraph behind that; so we must check for the ends of cell, rows,
+ // tables
+ // ['true' to write an empty paragraph, MS Word insists on that]
+ FinishTableRowCell(pNodeInfoInner, true);
+}
+
+void DocxAttributeOutput::TableOrientation(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( "
+ "ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )");
+}
+
+void DocxAttributeOutput::TableSpacing(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( "
+ "ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )");
+}
+
+void DocxAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/)
+{
+ SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxtablestyleexport.cxx b/sw/source/filter/ww8/docxtablestyleexport.cxx
new file mode 100644
index 0000000000..46a052e0c7
--- /dev/null
+++ b/sw/source/filter/ww8/docxtablestyleexport.cxx
@@ -0,0 +1,736 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "docxtablestyleexport.hxx"
+#include "docxhelper.hxx"
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <oox/token/tokens.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <optional>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace com::sun::star;
+using namespace oox;
+
+/// Methods in this class handle values in a table style.
+struct DocxTableStyleExport::Impl
+{
+private:
+ SwDoc& m_rDoc;
+ sax_fastparser::FSHelperPtr m_pSerializer;
+
+public:
+ Impl(SwDoc& rDoc)
+ : m_rDoc(rDoc)
+ {
+ }
+
+ void TableStyle(const uno::Sequence<beans::PropertyValue>& rStyle);
+
+ void setSerializer(sax_fastparser::FSHelperPtr pSerializer)
+ {
+ m_pSerializer = std::move(pSerializer);
+ }
+
+ const sax_fastparser::FSHelperPtr& getSerializer() const { return m_pSerializer; }
+
+ SwDoc& getDoc() const { return m_rDoc; }
+
+ /// Handles a boolean value.
+ void handleBoolean(std::u16string_view aValue, sal_Int32 nToken);
+
+ /// Export of w:pPr.
+ void tableStylePPr(const uno::Sequence<beans::PropertyValue>& rPPr);
+ /// Export of w:tblStylePr.
+ void tableStyleTableStylePr(const uno::Sequence<beans::PropertyValue>& rTableStylePr);
+ /// Export of w:rPr.
+ void tableStyleRPr(const uno::Sequence<beans::PropertyValue>& rRPr);
+ /// Export of w:rFonts.
+ void tableStyleRRFonts(const uno::Sequence<beans::PropertyValue>& rRFonts);
+ /// Export of w:lang.
+ void tableStyleRLang(const uno::Sequence<beans::PropertyValue>& rLang);
+ /// Export of w:ind in a pPr.
+ void tableStylePInd(const uno::Sequence<beans::PropertyValue>& rInd);
+ /// Export of w:spacing.
+ void tableStylePSpacing(const uno::Sequence<beans::PropertyValue>& rSpacing);
+ /// Export of w:tblPr.
+ void tableStyleTablePr(const uno::Sequence<beans::PropertyValue>& rTablePr);
+ /// Export of w:trPr.
+ void tableStyleTrPr(const uno::Sequence<beans::PropertyValue>& rTrPr);
+ /// Export of w:tcPr.
+ void tableStyleTcPr(const uno::Sequence<beans::PropertyValue>& rTcPr);
+ /// Export of w:tcBorders (and w:tblBorders).
+ void tableStyleTcBorders(const uno::Sequence<beans::PropertyValue>& rTcBorders,
+ sal_Int32 nToken = XML_tcBorders);
+ /// Export of w:tblInd.
+ void tableStyleTableInd(const uno::Sequence<beans::PropertyValue>& rTableInd);
+ /// Export of w:tblCellMar (and w:tcMar).
+ void tableStyleTableCellMar(const uno::Sequence<beans::PropertyValue>& rTableCellMar,
+ sal_Int32 nType = XML_tblCellMar);
+ /// Export of a given table cell border type.
+ void tableStyleTcBorder(sal_Int32 nToken, const uno::Sequence<beans::PropertyValue>& rTcBorder);
+ /// Export of w:shd.
+ void tableStyleShd(const uno::Sequence<beans::PropertyValue>& rShd);
+ /// Export of w:color.
+ void tableStyleRColor(const uno::Sequence<beans::PropertyValue>& rColor);
+};
+
+void DocxTableStyleExport::CnfStyle(const uno::Sequence<beans::PropertyValue>& rAttributeList)
+{
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ for (const auto& rAttribute : rAttributeList)
+ {
+ if (rAttribute.Name == "val")
+ pAttributeList->add(FSNS(XML_w, XML_val), rAttribute.Value.get<OUString>());
+ else
+ {
+ static DocxStringTokenMap const aTokens[]
+ = { { "firstRow", XML_firstRow },
+ { "lastRow", XML_lastRow },
+ { "firstColumn", XML_firstColumn },
+ { "lastColumn", XML_lastColumn },
+ { "oddVBand", XML_oddVBand },
+ { "evenVBand", XML_evenVBand },
+ { "oddHBand", XML_oddHBand },
+ { "evenHBand", XML_evenHBand },
+ { "firstRowFirstColumn", XML_firstRowFirstColumn },
+ { "firstRowLastColumn", XML_firstRowLastColumn },
+ { "lastRowFirstColumn", XML_lastRowFirstColumn },
+ { "lastRowLastColumn", XML_lastRowLastColumn },
+ { nullptr, 0 } };
+
+ if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
+ pAttributeList->add(FSNS(XML_w, nToken), rAttribute.Value.get<OUString>());
+ }
+ }
+
+ m_pImpl->getSerializer()->singleElementNS(XML_w, XML_cnfStyle, pAttributeList);
+}
+
+void DocxTableStyleExport::TableStyles(sal_Int32 nCountStylesToWrite)
+{
+ // Do we have table styles from InteropGrabBag available?
+ uno::Reference<beans::XPropertySet> xPropertySet(
+ m_pImpl->getDoc().GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aInteropGrabBag;
+ xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
+ uno::Sequence<beans::PropertyValue> aTableStyles;
+ auto pProp = std::find_if(
+ std::cbegin(aInteropGrabBag), std::cend(aInteropGrabBag),
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "tableStyles"; });
+ if (pProp != std::cend(aInteropGrabBag))
+ pProp->Value >>= aTableStyles;
+ if (!aTableStyles.hasElements())
+ return;
+
+ if (nCountStylesToWrite > aTableStyles.getLength())
+ nCountStylesToWrite = aTableStyles.getLength();
+
+ for (sal_Int32 i = 0; i < nCountStylesToWrite; ++i)
+ {
+ uno::Sequence<beans::PropertyValue> aTableStyle;
+ aTableStyles[i].Value >>= aTableStyle;
+ m_pImpl->TableStyle(aTableStyle);
+ }
+}
+
+void DocxTableStyleExport::Impl::tableStyleTableCellMar(
+ const uno::Sequence<beans::PropertyValue>& rTableCellMar, sal_Int32 nType)
+{
+ static DocxStringTokenMap const aTableCellMarTokens[]
+ = { { "left", XML_left }, { "right", XML_right }, { "start", XML_start },
+ { "end", XML_end }, { "top", XML_top }, { "bottom", XML_bottom },
+ { nullptr, 0 } };
+
+ if (!rTableCellMar.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, nType);
+ for (const auto& rProp : rTableCellMar)
+ {
+ if (sal_Int32 nToken = DocxStringGetToken(aTableCellMarTokens, rProp.Name))
+ {
+ comphelper::SequenceAsHashMap aMap(
+ rProp.Value.get<uno::Sequence<beans::PropertyValue>>());
+ m_pSerializer->singleElementNS(XML_w, nToken, FSNS(XML_w, XML_w),
+ OString::number(aMap["w"].get<sal_Int32>()),
+ FSNS(XML_w, XML_type), aMap["type"].get<OUString>());
+ }
+ }
+ m_pSerializer->endElementNS(XML_w, nType);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTcBorder(
+ sal_Int32 nToken, const uno::Sequence<beans::PropertyValue>& rTcBorder)
+{
+ static DocxStringTokenMap const aTcBorderTokens[] = { { "val", XML_val },
+ { "sz", XML_sz },
+ { "color", XML_color },
+ { "space", XML_space },
+ { "themeColor", XML_themeColor },
+ { "themeTint", XML_themeTint },
+ { nullptr, 0 } };
+
+ if (!rTcBorder.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rTcBorder)
+ if (sal_Int32 nAttrToken = DocxStringGetToken(aTcBorderTokens, rProp.Name))
+ pAttributeList->add(FSNS(XML_w, nAttrToken), rProp.Value.get<OUString>());
+
+ m_pSerializer->singleElementNS(XML_w, nToken, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTcBorders(
+ const uno::Sequence<beans::PropertyValue>& rTcBorders, sal_Int32 nToken)
+{
+ static DocxStringTokenMap const aTcBordersTokens[] = { { "left", XML_left },
+ { "right", XML_right },
+ { "start", XML_start },
+ { "end", XML_end },
+ { "top", XML_top },
+ { "bottom", XML_bottom },
+ { "insideH", XML_insideH },
+ { "insideV", XML_insideV },
+ { "tl2br", XML_tl2br },
+ { "tr2bl", XML_tr2bl },
+ { nullptr, 0 } };
+
+ if (!rTcBorders.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, nToken);
+ for (const auto& rTcBorder : rTcBorders)
+ if (sal_Int32 nSubToken = DocxStringGetToken(aTcBordersTokens, rTcBorder.Name))
+ tableStyleTcBorder(nSubToken,
+ rTcBorder.Value.get<uno::Sequence<beans::PropertyValue>>());
+ m_pSerializer->endElementNS(XML_w, nToken);
+}
+
+void DocxTableStyleExport::Impl::tableStyleShd(const uno::Sequence<beans::PropertyValue>& rShd)
+{
+ if (!rShd.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rShd)
+ {
+ if (rProp.Name == "val")
+ pAttributeList->add(FSNS(XML_w, XML_val), rProp.Value.get<OUString>());
+ else if (rProp.Name == "color")
+ pAttributeList->add(FSNS(XML_w, XML_color), rProp.Value.get<OUString>());
+ else if (rProp.Name == "fill")
+ pAttributeList->add(FSNS(XML_w, XML_fill), rProp.Value.get<OUString>());
+ else if (rProp.Name == "themeFill")
+ pAttributeList->add(FSNS(XML_w, XML_themeFill), rProp.Value.get<OUString>());
+ else if (rProp.Name == "themeFillShade")
+ pAttributeList->add(FSNS(XML_w, XML_themeFillShade), rProp.Value.get<OUString>());
+ else if (rProp.Name == "themeFillTint")
+ pAttributeList->add(FSNS(XML_w, XML_themeFillTint), rProp.Value.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_shd, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStyleRColor(const uno::Sequence<beans::PropertyValue>& rColor)
+{
+ if (!rColor.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rColor)
+ {
+ if (rProp.Name == "val")
+ pAttributeList->add(FSNS(XML_w, XML_val), rProp.Value.get<OUString>());
+ else if (rProp.Name == "themeColor")
+ pAttributeList->add(FSNS(XML_w, XML_themeColor), rProp.Value.get<OUString>());
+ else if (rProp.Name == "themeTint")
+ pAttributeList->add(FSNS(XML_w, XML_themeTint), rProp.Value.get<OUString>());
+ else if (rProp.Name == "themeShade")
+ pAttributeList->add(FSNS(XML_w, XML_themeShade), rProp.Value.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_color, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStyleRLang(const uno::Sequence<beans::PropertyValue>& rLang)
+{
+ if (!rLang.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rLang)
+ {
+ if (rProp.Name == "eastAsia")
+ pAttributeList->add(FSNS(XML_w, XML_eastAsia), rProp.Value.get<OUString>());
+ else if (rProp.Name == "val")
+ pAttributeList->add(FSNS(XML_w, XML_val), rProp.Value.get<OUString>());
+ else if (rProp.Name == "bidi")
+ pAttributeList->add(FSNS(XML_w, XML_bidi), rProp.Value.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_lang, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStyleRRFonts(
+ const uno::Sequence<beans::PropertyValue>& rRFonts)
+{
+ if (!rRFonts.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rRFont : rRFonts)
+ {
+ if (rRFont.Name == "eastAsiaTheme")
+ pAttributeList->add(FSNS(XML_w, XML_eastAsiaTheme), rRFont.Value.get<OUString>());
+ else if (rRFont.Name == "asciiTheme")
+ pAttributeList->add(FSNS(XML_w, XML_asciiTheme), rRFont.Value.get<OUString>());
+ else if (rRFont.Name == "cstheme")
+ pAttributeList->add(FSNS(XML_w, XML_cstheme), rRFont.Value.get<OUString>());
+ else if (rRFont.Name == "hAnsiTheme")
+ pAttributeList->add(FSNS(XML_w, XML_hAnsiTheme), rRFont.Value.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_rFonts, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStylePSpacing(
+ const uno::Sequence<beans::PropertyValue>& rSpacing)
+{
+ if (!rSpacing.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rSpacing)
+ {
+ if (rProp.Name == "after")
+ pAttributeList->add(FSNS(XML_w, XML_after), rProp.Value.get<OUString>());
+ else if (rProp.Name == "before")
+ pAttributeList->add(FSNS(XML_w, XML_before), rProp.Value.get<OUString>());
+ else if (rProp.Name == "line")
+ pAttributeList->add(FSNS(XML_w, XML_line), rProp.Value.get<OUString>());
+ else if (rProp.Name == "lineRule")
+ pAttributeList->add(FSNS(XML_w, XML_lineRule), rProp.Value.get<OUString>());
+ else if (rProp.Name == "beforeLines")
+ pAttributeList->add(FSNS(XML_w, XML_beforeLines), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ParaTopMarginBeforeAutoSpacing")
+ // Auto spacing will be available in grab bag only if it was set to true
+ pAttributeList->add(FSNS(XML_w, XML_beforeAutospacing), "1");
+ else if (rProp.Name == "afterLines")
+ pAttributeList->add(FSNS(XML_w, XML_afterLines), rProp.Value.get<OUString>());
+ else if (rProp.Name == "ParaBottomMarginAfterAutoSpacing")
+ // Auto spacing will be available in grab bag only if it was set to true
+ pAttributeList->add(FSNS(XML_w, XML_afterAutospacing), "1");
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_spacing, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStylePInd(const uno::Sequence<beans::PropertyValue>& rInd)
+{
+ if (!rInd.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rInd)
+ {
+ if (rProp.Name == "rightChars")
+ pAttributeList->add(FSNS(XML_w, XML_rightChars), rProp.Value.get<OUString>());
+ else if (rProp.Name == "right")
+ pAttributeList->add(FSNS(XML_w, XML_right), rProp.Value.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_ind, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTableInd(
+ const uno::Sequence<beans::PropertyValue>& rTableInd)
+{
+ if (!rTableInd.hasElements())
+ return;
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ for (const auto& rProp : rTableInd)
+ {
+ if (rProp.Name == "w")
+ pAttributeList->add(FSNS(XML_w, XML_w), OString::number(rProp.Value.get<sal_Int32>()));
+ else if (rProp.Name == "type")
+ pAttributeList->add(FSNS(XML_w, XML_type), rProp.Value.get<OUString>());
+ }
+ m_pSerializer->singleElementNS(XML_w, XML_tblInd, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::handleBoolean(std::u16string_view aValue, sal_Int32 nToken)
+{
+ if (aValue.empty())
+ return;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ if (aValue != u"1")
+ pAttributeList->add(FSNS(XML_w, XML_val), aValue);
+ m_pSerializer->singleElementNS(XML_w, nToken, pAttributeList);
+}
+
+void DocxTableStyleExport::Impl::tableStyleRPr(const uno::Sequence<beans::PropertyValue>& rRPr)
+{
+ if (!rRPr.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+
+ uno::Sequence<beans::PropertyValue> aRFonts;
+ uno::Sequence<beans::PropertyValue> aLang;
+ uno::Sequence<beans::PropertyValue> aColor;
+ uno::Sequence<beans::PropertyValue> aSpacingSequence;
+ bool bSequenceFlag = false;
+ OUString aB;
+ OUString aBCs;
+ OUString aI;
+ OUString aSz;
+ OUString aSzCs;
+ OUString aCaps;
+ OUString aSmallCaps;
+ OUString aSpacing;
+ for (const auto& rProp : rRPr)
+ {
+ if (rProp.Name == "rFonts")
+ aRFonts = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "lang")
+ aLang = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "b")
+ aB = rProp.Value.get<OUString>();
+ else if (rProp.Name == "bCs")
+ aBCs = rProp.Value.get<OUString>();
+ else if (rProp.Name == "i")
+ aI = rProp.Value.get<OUString>();
+ else if (rProp.Name == "color")
+ aColor = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "sz")
+ aSz = rProp.Value.get<OUString>();
+ else if (rProp.Name == "szCs")
+ aSzCs = rProp.Value.get<OUString>();
+ else if (rProp.Name == "caps")
+ aCaps = rProp.Value.get<OUString>();
+ else if (rProp.Name == "smallCaps")
+ aSmallCaps = rProp.Value.get<OUString>();
+ else if (rProp.Name == "spacing")
+ {
+ if (rProp.Value.has<OUString>())
+ {
+ aSpacing = rProp.Value.get<OUString>();
+ }
+ else
+ {
+ aSpacingSequence = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ bSequenceFlag = true; // set the uno::Sequence flag.
+ }
+ }
+ }
+ tableStyleRRFonts(aRFonts);
+ tableStyleRLang(aLang);
+ handleBoolean(aB, XML_b);
+ handleBoolean(aBCs, XML_bCs);
+ handleBoolean(aI, XML_i);
+ handleBoolean(aCaps, XML_caps);
+ handleBoolean(aSmallCaps, XML_smallCaps);
+ tableStyleRColor(aColor);
+ if (bSequenceFlag)
+ {
+ m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val),
+ aSpacingSequence[0].Value.get<OUString>());
+ }
+ if (!aSpacing.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), aSpacing);
+ if (!aSz.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), aSz);
+ if (!aSzCs.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_szCs, FSNS(XML_w, XML_val), aSzCs);
+
+ m_pSerializer->endElementNS(XML_w, XML_rPr);
+}
+
+void DocxTableStyleExport::Impl::tableStylePPr(const uno::Sequence<beans::PropertyValue>& rPPr)
+{
+ if (!rPPr.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, XML_pPr);
+
+ uno::Sequence<beans::PropertyValue> aSpacing;
+ uno::Sequence<beans::PropertyValue> aInd;
+ bool bWordWrap = false;
+ OUString aJc;
+ OUString aSnapToGrid;
+ for (const auto& rProp : rPPr)
+ {
+ if (rProp.Name == "spacing")
+ aSpacing = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "ind")
+ aInd = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "wordWrap")
+ bWordWrap = true;
+ else if (rProp.Name == "jc")
+ aJc = rProp.Value.get<OUString>();
+ else if (rProp.Name == "snapToGrid")
+ aSnapToGrid = rProp.Value.get<OUString>();
+ }
+ if (bWordWrap)
+ m_pSerializer->singleElementNS(XML_w, XML_wordWrap);
+ tableStylePInd(aInd);
+ handleBoolean(aSnapToGrid, XML_snapToGrid);
+ tableStylePSpacing(aSpacing);
+ if (!aJc.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), aJc);
+
+ m_pSerializer->endElementNS(XML_w, XML_pPr);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTablePr(
+ const uno::Sequence<beans::PropertyValue>& rTablePr)
+{
+ if (!rTablePr.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, XML_tblPr);
+
+ uno::Sequence<beans::PropertyValue> aTableInd;
+ uno::Sequence<beans::PropertyValue> aTableBorders;
+ uno::Sequence<beans::PropertyValue> aTableCellMar;
+ std::optional<sal_Int32> oTableStyleRowBandSize;
+ std::optional<sal_Int32> oTableStyleColBandSize;
+ for (const auto& rProp : rTablePr)
+ {
+ if (rProp.Name == "tblStyleRowBandSize")
+ oTableStyleRowBandSize = rProp.Value.get<sal_Int32>();
+ else if (rProp.Name == "tblStyleColBandSize")
+ oTableStyleColBandSize = rProp.Value.get<sal_Int32>();
+ else if (rProp.Name == "tblInd")
+ aTableInd = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tblBorders")
+ aTableBorders = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tblCellMar")
+ aTableCellMar = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ }
+ if (oTableStyleRowBandSize)
+ m_pSerializer->singleElementNS(XML_w, XML_tblStyleRowBandSize, FSNS(XML_w, XML_val),
+ OString::number(*oTableStyleRowBandSize));
+ if (oTableStyleColBandSize)
+ m_pSerializer->singleElementNS(XML_w, XML_tblStyleColBandSize, FSNS(XML_w, XML_val),
+ OString::number(*oTableStyleColBandSize));
+ tableStyleTableInd(aTableInd);
+ tableStyleTcBorders(aTableBorders, XML_tblBorders);
+ tableStyleTableCellMar(aTableCellMar);
+
+ m_pSerializer->endElementNS(XML_w, XML_tblPr);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTrPr(const uno::Sequence<beans::PropertyValue>& rTrPr)
+{
+ if (!rTrPr.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, XML_trPr);
+
+ for (const auto& rProp : rTrPr)
+ {
+ if (rProp.Name == "tblHeader")
+ m_pSerializer->singleElementNS(XML_w, XML_tblHeader);
+ }
+
+ m_pSerializer->endElementNS(XML_w, XML_trPr);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTcPr(const uno::Sequence<beans::PropertyValue>& rTcPr)
+{
+ if (!rTcPr.hasElements())
+ return;
+
+ m_pSerializer->startElementNS(XML_w, XML_tcPr);
+
+ uno::Sequence<beans::PropertyValue> aShd;
+ uno::Sequence<beans::PropertyValue> aTcBorders;
+ uno::Sequence<beans::PropertyValue> aTcMar;
+ OUString aVAlign;
+ for (const auto& rProp : rTcPr)
+ {
+ if (rProp.Name == "shd")
+ aShd = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tcBorders")
+ aTcBorders = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tcMar")
+ aTcMar = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "vAlign")
+ aVAlign = rProp.Value.get<OUString>();
+ }
+ tableStyleTcBorders(aTcBorders);
+ tableStyleTableCellMar(aTcMar, XML_tcMar);
+ tableStyleShd(aShd);
+ if (!aVAlign.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), aVAlign);
+
+ m_pSerializer->endElementNS(XML_w, XML_tcPr);
+}
+
+void DocxTableStyleExport::Impl::tableStyleTableStylePr(
+ const uno::Sequence<beans::PropertyValue>& rTableStylePr)
+{
+ if (!rTableStylePr.hasElements())
+ return;
+
+ OUString aType;
+ uno::Sequence<beans::PropertyValue> aPPr;
+ uno::Sequence<beans::PropertyValue> aRPr;
+ uno::Sequence<beans::PropertyValue> aTablePr;
+ uno::Sequence<beans::PropertyValue> aTrPr;
+ uno::Sequence<beans::PropertyValue> aTcPr;
+ for (const auto& rProp : rTableStylePr)
+ {
+ if (rProp.Name == "type")
+ aType = rProp.Value.get<OUString>();
+ else if (rProp.Name == "pPr")
+ aPPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "rPr")
+ aRPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tblPr")
+ aTablePr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "trPr")
+ aTrPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tcPr")
+ aTcPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ }
+
+ m_pSerializer->startElementNS(XML_w, XML_tblStylePr, FSNS(XML_w, XML_type), aType);
+
+ tableStylePPr(aPPr);
+ tableStyleRPr(aRPr);
+ if (aTablePr.hasElements())
+ tableStyleTablePr(aTablePr);
+ else
+ {
+ // Even if we have an empty container, write it out, as Word does.
+ m_pSerializer->singleElementNS(XML_w, XML_tblPr);
+ }
+ tableStyleTrPr(aTrPr);
+ tableStyleTcPr(aTcPr);
+
+ m_pSerializer->endElementNS(XML_w, XML_tblStylePr);
+}
+
+void DocxTableStyleExport::Impl::TableStyle(const uno::Sequence<beans::PropertyValue>& rStyle)
+{
+ bool bDefault = false;
+ bool bCustomStyle = false;
+ bool bQFormat = false;
+ bool bSemiHidden = false;
+ bool bUnhideWhenUsed = false;
+ OUString aStyleId;
+ OUString aName;
+ OUString aBasedOn;
+ OUString aRsid;
+ OUString aUiPriority;
+ uno::Sequence<beans::PropertyValue> aPPr;
+ uno::Sequence<beans::PropertyValue> aRPr;
+ uno::Sequence<beans::PropertyValue> aTablePr;
+ uno::Sequence<beans::PropertyValue> aTcPr;
+ std::vector<uno::Sequence<beans::PropertyValue>> aTableStylePrs;
+ for (const auto& rProp : rStyle)
+ {
+ if (rProp.Name == "default")
+ bDefault = rProp.Value.get<bool>();
+ else if (rProp.Name == "customStyle")
+ bCustomStyle = rProp.Value.get<bool>();
+ else if (rProp.Name == "styleId")
+ aStyleId = rProp.Value.get<OUString>();
+ else if (rProp.Name == "name")
+ aName = rProp.Value.get<OUString>();
+ else if (rProp.Name == "basedOn")
+ aBasedOn = rProp.Value.get<OUString>();
+ else if (rProp.Name == "uiPriority")
+ aUiPriority = rProp.Value.get<OUString>();
+ else if (rProp.Name == "qFormat")
+ bQFormat = true;
+ else if (rProp.Name == "semiHidden")
+ bSemiHidden = true;
+ else if (rProp.Name == "unhideWhenUsed")
+ bUnhideWhenUsed = true;
+ else if (rProp.Name == "rsid")
+ aRsid = rProp.Value.get<OUString>();
+ else if (rProp.Name == "pPr")
+ aPPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "rPr")
+ aRPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tblPr")
+ aTablePr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tcPr")
+ aTcPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>();
+ else if (rProp.Name == "tblStylePr")
+ aTableStylePrs.push_back(rProp.Value.get<uno::Sequence<beans::PropertyValue>>());
+ }
+
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList
+ = sax_fastparser::FastSerializerHelper::createAttrList();
+ pAttributeList->add(FSNS(XML_w, XML_type), "table");
+ if (bDefault)
+ pAttributeList->add(FSNS(XML_w, XML_default), "1");
+ if (bCustomStyle)
+ pAttributeList->add(FSNS(XML_w, XML_customStyle), "1");
+ if (!aStyleId.isEmpty())
+ pAttributeList->add(FSNS(XML_w, XML_styleId), aStyleId);
+ m_pSerializer->startElementNS(XML_w, XML_style, pAttributeList);
+
+ m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), aName);
+ if (!aBasedOn.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_basedOn, FSNS(XML_w, XML_val), aBasedOn);
+ if (!aUiPriority.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_uiPriority, FSNS(XML_w, XML_val), aUiPriority);
+ if (bSemiHidden)
+ m_pSerializer->singleElementNS(XML_w, XML_semiHidden);
+ if (bUnhideWhenUsed)
+ m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed);
+ if (bQFormat)
+ m_pSerializer->singleElementNS(XML_w, XML_qFormat);
+ if (!aRsid.isEmpty())
+ m_pSerializer->singleElementNS(XML_w, XML_rsid, FSNS(XML_w, XML_val), aRsid);
+
+ tableStylePPr(aPPr);
+ tableStyleRPr(aRPr);
+ tableStyleTablePr(aTablePr);
+ tableStyleTcPr(aTcPr);
+ for (const uno::Sequence<beans::PropertyValue>& i : std::as_const(aTableStylePrs))
+ tableStyleTableStylePr(i);
+
+ m_pSerializer->endElementNS(XML_w, XML_style);
+}
+
+void DocxTableStyleExport::SetSerializer(const sax_fastparser::FSHelperPtr& pSerializer)
+{
+ m_pImpl->setSerializer(pSerializer);
+}
+
+DocxTableStyleExport::DocxTableStyleExport(SwDoc& rDoc,
+ const sax_fastparser::FSHelperPtr& pSerializer)
+ : m_pImpl(std::make_unique<Impl>(rDoc))
+{
+ m_pImpl->setSerializer(pSerializer);
+}
+
+DocxTableStyleExport::~DocxTableStyleExport() = default;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxtablestyleexport.hxx b/sw/source/filter/ww8/docxtablestyleexport.hxx
new file mode 100644
index 0000000000..a7f327a098
--- /dev/null
+++ b/sw/source/filter/ww8/docxtablestyleexport.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXTABLESTYLEEXPORT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXTABLESTYLEEXPORT_HXX
+
+#include <memory>
+
+#include <sax/fshelper.hxx>
+
+namespace com::sun::star::beans
+{
+struct PropertyValue;
+}
+class SwDoc;
+
+/// Handles DOCX export of table styles, based on InteropGrabBag.
+class DocxTableStyleExport
+{
+ struct Impl;
+ std::unique_ptr<Impl> m_pImpl;
+
+public:
+ void TableStyles(sal_Int32 nCountStylesToWrite);
+
+ /// Writes <w:cnfStyle .../> based on grab-bagged para, cell or row properties.
+ void CnfStyle(const css::uno::Sequence<css::beans::PropertyValue>& rAttributeList);
+
+ void SetSerializer(const sax_fastparser::FSHelperPtr& pSerializer);
+ DocxTableStyleExport(SwDoc& rDoc, const sax_fastparser::FSHelperPtr& pSerializer);
+ ~DocxTableStyleExport();
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXTABLESTYLEEXPORT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/escher.hxx b/sw/source/filter/ww8/escher.hxx
new file mode 100644
index 0000000000..179b3a16f3
--- /dev/null
+++ b/sw/source/filter/ww8/escher.hxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_ESCHER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_ESCHER_HXX
+
+#include <filter/msfilter/escherex.hxx>
+#include <svx/svdtrans.hxx>
+#include "wrtww8.hxx"
+
+const sal_uInt32 nInlineHack = 0x00010001;
+class SwFrameFormat;
+// #i30669#
+class SwFormatHoriOrient;
+class SwFormatVertOrient;
+
+class WinwordAnchoring : public EscherExClientRecord_Base
+{
+public:
+ void WriteData(EscherEx& rEx) const override;
+ void SetAnchoring(const SwFrameFormat& rFormat);
+
+ /** method to perform conversion of positioning attributes with the help
+ of corresponding layout information
+
+ #i30669#
+ Because most of the Writer object positions doesn't correspond to the
+ object positions in WW8, this method converts the positioning
+ attributes. For this conversion the corresponding layout information
+ is needed. If no layout information exists - e.g. no layout exists - no
+ conversion is performed.
+ No conversion is performed for as-character anchored objects. Whose
+ object positions are already treated special in method <WriteData(..)>.
+ Usage of method: Used by method <SetAnchoring(..)>, nothing else
+
+ @param _iorHoriOri
+ input/output parameter - containing the current horizontal position
+ attributes, which are converted by this method.
+
+ @param _iorVertOri
+ input/output parameter - containing the current vertical position
+ attributes, which are converted by this method.
+
+ @param _rFrameFormat
+ input parameter - frame format of the anchored object
+
+ @return boolean, indicating, if a conversion has been performed.
+ */
+ static bool ConvertPosition( SwFormatHoriOrient& _iorHoriOri,
+ SwFormatVertOrient& _iorVertOri,
+ const SwFrameFormat& _rFrameFormat );
+
+private:
+ bool mbInline;
+ sal_uInt32 mnGroupShapeBooleanProperties;
+ sal_uInt32 mnXAlign;
+ sal_uInt32 mnYAlign;
+ sal_uInt32 mnXRelTo;
+ sal_uInt32 mnYRelTo;
+
+};
+
+class SwEscherExGlobal : public EscherExGlobal
+{
+public:
+ explicit SwEscherExGlobal();
+ virtual ~SwEscherExGlobal() override;
+
+private:
+ /** Override to create a new memory stream for picture data. */
+ virtual SvStream* ImplQueryPictureStream() override;
+
+private:
+ std::shared_ptr< SvStream > mxPicStrm;
+};
+
+class SwBasicEscherEx : public EscherEx
+{
+private:
+ void Init();
+protected:
+ WW8Export& mrWrt;
+ SvStream* mpEscherStrm;
+ tools::Long mnEmuMul, mnEmuDiv;
+
+ virtual sal_Int32 WriteFlyFrameAttr(const SwFrameFormat& rFormat, MSO_SPT eShapeType,
+ EscherPropertyContainer& rPropOpt);
+ void WriteBrushAttr(const SvxBrushItem &rBrush,
+ EscherPropertyContainer& rPropOpt);
+ void WriteOLEPicture(EscherPropertyContainer &rPropOpt,
+ ShapeFlag nShapeFlags, const Graphic &rGraphic, const SdrObject &rObj,
+ sal_uInt32 nShapeId, const css::awt::Rectangle* pVisArea );
+ static void WriteGrfAttr(const SwNoTextNode& rNd, const SwFrameFormat& rFormat, EscherPropertyContainer& rPropOpt);
+
+ sal_Int32 DrawModelToEmu(sal_Int32 nVal) const
+ { return BigMulDiv(nVal, mnEmuMul, mnEmuDiv); }
+
+ static sal_Int32 ToFract16(sal_Int32 nVal, sal_uInt32 nMax);
+
+ virtual void SetPicId(const SdrObject &, sal_uInt32, EscherPropertyContainer &);
+ SdrLayerID GetInvisibleHellId() const;
+
+public:
+ SwBasicEscherEx(SvStream* pStrm, WW8Export& rWrt);
+ sal_Int32 WriteGrfFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId);
+ //For i120928,to export graphic of bullet
+ void WriteGrfBullet(const Graphic&);
+ sal_Int32 WriteOLEFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId);
+ void WriteEmptyFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId);
+ virtual void WriteFrameExtraData(const SwFrameFormat&);
+ virtual void WritePictures();
+ virtual ~SwBasicEscherEx() override;
+ //i120927,this function is added to export hyperlink info,such as graphic/frame/OLE
+ bool IsRelUrl() const;
+ OUString GetBasePath() const;
+ OUString BuildFileName(sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl);
+ void WriteHyperlinkWithinFly( SvMemoryStream& rStrm, const SwFormatURL* pINetFormatArg);
+ void PreWriteHyperlinkWithinFly(const SwFrameFormat& rFormat,EscherPropertyContainer& rPropOpt);
+
+private:
+ SwBasicEscherEx(const SwBasicEscherEx&) = delete;
+ SwBasicEscherEx& operator=(const SwBasicEscherEx&) = delete;
+};
+
+class SwEscherEx : public SwBasicEscherEx
+{
+private:
+ std::vector<sal_uLong> m_aFollowShpIds;
+ EscherExHostAppData m_aHostData;
+ WinwordAnchoring m_aWinwordAnchoring;
+ WW8_WrPlcTextBoxes *m_pTextBxs;
+
+ sal_uInt32 GetFlyShapeId(const SwFrameFormat& rFormat,
+ unsigned int nHdFtIndex, DrawObjPointerVector &rPVec);
+ void MakeZOrderArrAndFollowIds(std::vector<DrawObj>& rSrcArr,
+ DrawObjPointerVector& rDstArr);
+
+ sal_Int32 WriteFlyFrame(const DrawObj &rObj, sal_uInt32 &rShapeId,
+ DrawObjPointerVector &rPVec);
+ sal_Int32 WriteTextFlyFrame(const DrawObj &rObj, sal_uInt32 nShapeId,
+ sal_uInt32 nTextBox, DrawObjPointerVector &rPVec);
+ void WriteOCXControl(const SwFrameFormat& rFormat,sal_uInt32 nShapeId);
+ virtual sal_Int32 WriteFlyFrameAttr(const SwFrameFormat& rFormat, MSO_SPT eShapeType,
+ EscherPropertyContainer& rPropOpt) override;
+
+ virtual sal_uInt32 QueryTextID(
+ const css::uno::Reference< css::drawing::XShape > &, sal_uInt32) override;
+ virtual void SetPicId(const SdrObject &rSdrObj, sal_uInt32 nShapeId,
+ EscherPropertyContainer &rPropOpt) override;
+public:
+ SwEscherEx( SvStream* pStrm, WW8Export& rWW8Wrt );
+ virtual ~SwEscherEx() override;
+ void FinishEscher();
+ virtual void WritePictures() override;
+
+ virtual void WriteFrameExtraData(const SwFrameFormat& rFormat) override;
+
+ EscherExHostAppData* StartShape(const css::uno::Reference< css::drawing::XShape > &, const tools::Rectangle*) override {return &m_aHostData;}
+private:
+ SwEscherEx(const SwEscherEx&) = delete;
+ SwEscherEx &operator=(const SwEscherEx&) = delete;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/fields.cxx b/sw/source/filter/ww8/fields.cxx
new file mode 100644
index 0000000000..4c7dd87600
--- /dev/null
+++ b/sw/source/filter/ww8/fields.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fields.hxx"
+#include <osl/diagnose.h>
+#include <stddef.h>
+
+namespace ww
+{
+ const char *GetEnglishFieldName(eField eIndex) noexcept
+ {
+ //0 Signifies the field names I can't find.
+ // #i43956# - field <eFOOTREF> = 5 should be mapped to "REF"
+ // See [MS-DOC] 2.9.90 flt
+ // https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/28a8d2c2-6107-409d-8f6a-e345ab6d4179
+ static const char *aFieldNames[] =
+ {
+ /* 0*/ nullptr,
+ /* 1*/ nullptr,
+ /* 2*/ nullptr,
+ /* 3*/ "REF",
+ /* 4*/ "XE",
+ /* 5*/ "REF",
+ /* 6*/ "SET",
+ /* 7*/ "IF",
+ /* 8*/ "INDEX",
+ /* 9*/ "TC",
+ /*10*/ "STYLEREF",
+ /*11*/ "RD",
+ /*12*/ "SEQ",
+ /*13*/ "TOC",
+ /*14*/ "INFO",
+ /*15*/ "TITLE",
+ /*16*/ "SUBJECT",
+ /*17*/ "AUTHOR",
+ /*18*/ "KEYWORDS",
+ /*19*/ "COMMENTS",
+ /*20*/ "LASTSAVEDBY",
+ /*21*/ "CREATEDATE",
+ /*22*/ "SAVEDATE",
+ /*23*/ "PRINTDATE",
+ /*24*/ "REVNUM",
+ /*25*/ "EDITTIME",
+ /*26*/ "NUMPAGES",
+ /*27*/ "NUMWORDS",
+ /*28*/ "NUMCHARS",
+ /*29*/ "FILENAME",
+ /*30*/ "TEMPLATE",
+ /*31*/ "DATE",
+ /*32*/ "TIME",
+ /*33*/ "PAGE",
+ /*34*/ "=",
+ /*35*/ "QUOTE",
+ /*36*/ nullptr,
+ /*37*/ "PAGEREF",
+ /*38*/ "ASK",
+ /*39*/ "FILLIN",
+ /*40*/ nullptr,
+ /*41*/ "NEXT",
+ /*42*/ "NEXTIF",
+ /*43*/ "SKIPIF",
+ /*44*/ "MERGEREC",
+ /*45*/ nullptr,
+ /*46*/ nullptr,
+ /*47*/ nullptr,
+ /*48*/ "PRINT",
+ /*49*/ "EQ",
+ /*50*/ "GOTOBUTTON",
+ /*51*/ "MACROBUTTON",
+ /*52*/ "AUTONUMOUT",
+ /*53*/ "AUTONUMLGL",
+ /*54*/ "AUTONUM",
+ /*55*/ nullptr,
+ /*56*/ "LINK",
+ /*57*/ "SYMBOL",
+ /*58*/ "EMBED",
+ /*59*/ "MERGEFIELD",
+ /*60*/ "USERNAME",
+ /*61*/ "USERINITIALS",
+ /*62*/ "USERADDRESS",
+ /*63*/ "BARCODE",
+ /*64*/ "DOCVARIABLE",
+ /*65*/ "SECTION",
+ /*66*/ "SECTIONPAGES",
+ /*67*/ "INCLUDEPICTURE",
+ /*68*/ "INCLUDETEXT",
+ /*69*/ "FILESIZE",
+ /*70*/ "FORMTEXT",
+ /*71*/ "FORMCHECKBOX",
+ /*72*/ "NOTEREF",
+ /*73*/ "TOA",
+ /*74*/ "TA",
+ /*75*/ "MERGESEQ",
+ /*76*/ nullptr,
+ /*77*/ "PRIVATE",
+ /*78*/ "DATABASE",
+ /*79*/ "AUTOTEXT",
+ /*80*/ "COMPARE",
+ /*81*/ nullptr,
+ /*82*/ nullptr,
+ /*83*/ "FORMDROPDOWN",
+ /*84*/ "ADVANCE",
+ /*85*/ "DOCPROPERTY",
+ /*86*/ nullptr,
+ /*87*/ "CONTROL",
+ /*88*/ "HYPERLINK",
+ /*89*/ "AUTOTEXTLIST",
+ /*90*/ "LISTNUM",
+ /*91*/ nullptr,
+ /*92*/ "BIDIOUTLINE",
+ /*93*/ "ADDRESSBLOCK",
+ /*94*/ "GREETINGLINE",
+ /*95*/ "SHAPE",
+ /*96*/ "BIBLIOGRAPHY",
+ /*97*/ "CITATION"
+ };
+
+ size_t nIndex = static_cast<size_t>(eIndex);
+ if (nIndex >= SAL_N_ELEMENTS(aFieldNames))
+ eIndex = eNONE;
+ OSL_ENSURE(eIndex != eNONE, "Unknown WinWord Field");
+ return aFieldNames[eIndex];
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/fields.hxx b/sw/source/filter/ww8/fields.hxx
new file mode 100644
index 0000000000..d11b92e2ad
--- /dev/null
+++ b/sw/source/filter/ww8/fields.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/// @HTML
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_FIELDS_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_FIELDS_HXX
+
+#include <filter/msfilter/ww8fields.hxx>
+
+namespace ww
+{
+/** Find the English Field Name from a winword index
+
+ See OpenOffice.org issue 12831
+ (https://bz.apache.org/ooo/show_bug.cgi?id=12831) and MS
+ Knowledge Base article 268717
+ (http://support.microsoft.com/default.aspx?scid=kb;en-us;268717) for
+ details of why to use english field names and not localized ones since
+ Word 2000.
+
+ @param
+ nIndex the index to search for
+
+ @return 0 if not found, otherwise the fieldname as a C style ASCII
+ string
+*/
+const char* GetEnglishFieldName(eField eIndex) noexcept;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/needed_cast.hxx b/sw/source/filter/ww8/needed_cast.hxx
new file mode 100644
index 0000000000..1fe7ca091e
--- /dev/null
+++ b/sw/source/filter/ww8/needed_cast.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_NEEDED_CAST_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_NEEDED_CAST_HXX
+
+#include <osl/diagnose.h>
+
+namespace ww
+{
+ template<typename Ret, typename Param> Ret checking_cast(Param in, Ret)
+ {
+ return static_cast<Ret>(in);
+ }
+
+ template<typename Ret> Ret checking_cast(Ret in, Ret)
+ {
+ OSL_ENSURE( false, "UnnecessaryCast" );
+ return in;
+ }
+
+ /*
+ needed_cast is the same as static_cast except that there will be a compile
+ time assert when NDEBUG is not defined and the in and out types are the
+ same. i.e. needed_cast catches unnecessary casts
+ */
+ template<typename Ret, typename Param> Ret needed_cast(Param in)
+ {
+ /*
+ Massage a single argument and a ret value into two arguments to allow
+ a determination if the dest type is the same as the source type
+ */
+ return checking_cast(in, Ret());
+ }
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx
new file mode 100644
index 0000000000..2d68556e8a
--- /dev/null
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -0,0 +1,4622 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "rtfattributeoutput.hxx"
+#include <memory>
+#include <cstring>
+#include "rtfsdrexport.hxx"
+#include "writerwordglue.hxx"
+#include "ww8par.hxx"
+#include <fmtcntnt.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <sot/exchange.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/svdouno.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflgrit.hxx>
+#include <docufld.hxx>
+#include <fmtclds.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtline.hxx>
+#include <fmtanchr.hxx>
+#include <ftninfo.hxx>
+#include <htmltbl.hxx>
+#include <ndgrf.hxx>
+#include <pagedesc.hxx>
+#include <swmodule.hxx>
+#include <txtftn.hxx>
+#include <txtinet.hxx>
+#include <grfatr.hxx>
+#include <ndole.hxx>
+#include <lineinfo.hxx>
+#include <redline.hxx>
+#include <rtf.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <oox/mathml/imexport.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <svl/grabbagitem.hxx>
+#include <frmatr.hxx>
+#include <swtable.hxx>
+#include <formatflysplit.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include "rtfexport.hxx"
+
+using namespace ::com::sun::star;
+using namespace sw::util;
+
+static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
+ const char* pStr)
+{
+ OStringBuffer aRet;
+ if (pLine && !pLine->isEmpty())
+ {
+ aRet.append(pStr);
+ // single line
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ {
+ if (SvxBorderLineWidth::Hairline == pLine->GetWidth())
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRHAIR);
+ else
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS);
+ }
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDOT);
+ break;
+ case SvxBorderLineStyle::DASHED:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASH);
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDB);
+ break;
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHSG);
+ break;
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHMG);
+ break;
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHLG);
+ break;
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNSG);
+ break;
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNMG);
+ break;
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNLG);
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDREMBOSS);
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRENGRAVE);
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDROUTSET);
+ break;
+ case SvxBorderLineStyle::INSET:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRINSET);
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHSM);
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHD);
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHDD);
+ break;
+ case SvxBorderLineStyle::NONE:
+ default:
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRNONE);
+ break;
+ }
+
+ double const fConverted(
+ ::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth()));
+ if (255 >= pLine->GetWidth()) // That value comes from RTF specs
+ {
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRW
+ + OString::number(static_cast<sal_Int32>(fConverted)));
+ }
+ else
+ {
+ // use \brdrth to double the value range...
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTH OOO_STRING_SVTOOLS_RTF_BRDRW
+ + OString::number(static_cast<sal_Int32>(fConverted) / 2));
+ }
+
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRCF
+ + OString::number(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor()))));
+ }
+ else // tdf#129758 "no border" may be needed to override style
+ {
+ aRet.append(OString::Concat(pStr) + OOO_STRING_SVTOOLS_RTF_BRDRNONE);
+ }
+ return aRet.makeStringAndClear();
+}
+
+static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
+ const char* pStr, sal_uInt16 nDist,
+ SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE)
+{
+ OStringBuffer aRet(OutTBLBorderLine(rExport, pLine, pStr));
+ if (pLine)
+ {
+ aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP + OString::number(static_cast<sal_Int32>(nDist)));
+ }
+ if (eShadowLocation == SvxShadowLocation::BottomRight)
+ aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
+ return aRet.makeStringAndClear();
+}
+
+void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
+{
+ m_bIsRTL = bIsRTL;
+ m_nScript = nScript;
+ m_bControlLtrRtl = true;
+}
+
+sal_Int32 RtfAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+ bool /*bGenerateParaId*/)
+{
+ if (m_bIsBeforeFirstParagraph && m_rExport.m_nTextTyp != TXT_HDFT)
+ m_bIsBeforeFirstParagraph = false;
+
+ // Output table/table row/table cell starts if needed
+ if (pTextNodeInfo)
+ {
+ sal_uInt32 nRow = pTextNodeInfo->getRow();
+ sal_uInt32 nCell = pTextNodeInfo->getCell();
+
+ // New cell/row?
+ if (m_nTableDepth > 0 && !m_bTableCellOpen)
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner(
+ pTextNodeInfo->getInnerForDepth(m_nTableDepth));
+ OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found");
+ // Make sure we always start a row between ending one and starting a cell.
+ // In case of subtables, we may not get the first cell.
+ if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded))
+ {
+ StartTableRow(pDeepInner);
+ }
+
+ StartTableCell();
+ }
+
+ // Again, if depth was incremented, start a new table even if we skipped the first cell.
+ if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth()))
+ {
+ // Do we have to start the table?
+ // [If we are at the right depth already, it means that we
+ // continue the table cell]
+ sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
+
+ if (nCurrentDepth > m_nTableDepth)
+ {
+ // Start all the tables that begin here
+ for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth();
+ ++nDepth)
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t pInner(
+ pTextNodeInfo->getInnerForDepth(nDepth));
+
+ m_bLastTable = (nDepth == pTextNodeInfo->getDepth());
+ StartTable();
+ StartTableRow(pInner);
+ StartTableCell();
+ }
+
+ m_nTableDepth = nCurrentDepth;
+ }
+ }
+ }
+
+ OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
+ return 0;
+}
+
+void RtfAttributeOutput::EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner)
+{
+ bool bLastPara = false;
+ if (m_rExport.m_nTextTyp == TXT_FTN || m_rExport.m_nTextTyp == TXT_EDN
+ || m_rExport.m_rDoc.IsClipBoard())
+ {
+ // We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard.
+ bLastPara
+ = m_rExport.GetCurrentNodeIndex()
+ && m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->GetNodeIndex();
+ }
+
+ FinishTableRowCell(pTextNodeInfoInner);
+
+ RtfStringBuffer aParagraph;
+
+ aParagraph.appendAndClear(m_aRun);
+ aParagraph->append(m_aAfterRuns);
+ m_aAfterRuns.setLength(0);
+ if (m_bTableAfterCell)
+ m_bTableAfterCell = false;
+ else
+ {
+ aParagraph->append(SAL_NEWLINE_STRING);
+ // RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph.
+ if (!bLastPara)
+ {
+ aParagraph->append(OOO_STRING_SVTOOLS_RTF_PAR);
+ aParagraph->append(' ');
+ }
+ }
+ if (m_nColBreakNeeded)
+ {
+ aParagraph->append(OOO_STRING_SVTOOLS_RTF_COLUMN);
+ m_nColBreakNeeded = false;
+ }
+
+ if (!m_bBufferSectionHeaders)
+ aParagraph.makeStringAndClear(this);
+ else
+ m_aSectionHeaders.append(aParagraph.makeStringAndClear());
+}
+
+void RtfAttributeOutput::EmptyParagraph()
+{
+ m_rExport.Strm()
+ .WriteOString(SAL_NEWLINE_STRING)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_PAR)
+ .WriteChar(' ');
+}
+
+void RtfAttributeOutput::SectionBreaks(const SwNode& rNode)
+{
+ SwNodeIndex aNextIndex(rNode, 1);
+ if (rNode.IsTextNode())
+ {
+ OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
+
+ // output page/section breaks
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ m_bBufferSectionBreaks = true;
+
+ // output section headers / footers
+ if (!m_bBufferSectionHeaders)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionHeaders);
+ m_aSectionHeaders.setLength(0);
+ }
+
+ if (aNextIndex.GetNode().IsTextNode())
+ {
+ const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
+ m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
+ // Save the current page description for now, so later we will be able to access the previous one.
+ m_pPrevPageDesc = pTextNode->FindPageDesc();
+ }
+ else if (aNextIndex.GetNode().IsTableNode())
+ {
+ const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
+ const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
+ m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
+ }
+ m_bBufferSectionBreaks = false;
+ }
+ else if (rNode.IsEndNode())
+ {
+ // End of something: make sure that it's the end of a table.
+ assert(rNode.StartOfSectionNode()->IsTableNode());
+ if (aNextIndex.GetNode().IsTextNode())
+ {
+ // Handle section break between a table and a text node following it.
+ const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
+ m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
+ }
+ }
+}
+
+void RtfAttributeOutput::StartParagraphProperties()
+{
+ OStringBuffer aPar;
+ if (!m_rExport.GetRTFFlySyntax())
+ {
+ aPar.append(OOO_STRING_SVTOOLS_RTF_PARD OOO_STRING_SVTOOLS_RTF_PLAIN " ");
+ }
+ if (!m_bBufferSectionHeaders)
+ m_rExport.Strm().WriteOString(aPar);
+ else
+ m_aSectionHeaders.append(aPar);
+}
+
+void RtfAttributeOutput::EndParagraphProperties(
+ const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/,
+ const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
+ const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
+{
+ // Do not call MoveCharacterProperties(),
+ // Otherwise associate properties in the paragraph style are ruined.
+ const OString aProperties = m_aStyles.makeStringAndClear();
+ m_rExport.Strm().WriteOString(aProperties);
+}
+
+void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/,
+ bool bSingleEmptyRun)
+{
+ SAL_INFO("sw.rtf", __func__ << ", bSingleEmptyRun: " << bSingleEmptyRun);
+
+ m_bInRun = true;
+ m_bSingleEmptyRun = bSingleEmptyRun;
+ if (!m_bSingleEmptyRun)
+ m_aRun->append('{');
+
+ // if there is some redlining in the document, output it
+ Redline(pRedlineData);
+
+ OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty");
+}
+
+void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, sal_Int32 /*nLen*/,
+ bool /*bLastRun*/)
+{
+ m_aRun->append(SAL_NEWLINE_STRING);
+ m_aRun.appendAndClear(m_aRunText);
+
+ if (m_bInRuby)
+ {
+ m_aRun->append(")}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {}}}");
+ m_bInRuby = false;
+ }
+
+ if (!m_bSingleEmptyRun && m_bInRun)
+ m_aRun->append('}');
+ m_bInRun = false;
+}
+
+void RtfAttributeOutput::StartRunProperties()
+{
+ OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
+}
+
+void RtfAttributeOutput::EndRunProperties(const SwRedlineData* /*pRedlineData*/)
+{
+ const OString aProperties = MoveCharacterProperties(true);
+ m_aRun->append(aProperties);
+}
+
+OString RtfAttributeOutput::MoveCharacterProperties(bool aAutoWriteRtlLtr)
+{
+ const OString aAssocHich = m_aStylesAssocHich.makeStringAndClear();
+ const OString aAssocDbch = m_aStylesAssocDbch.makeStringAndClear();
+ const OString aAssocRtlch = m_aStylesAssocRtlch.makeStringAndClear();
+ const OString aAssocLtrch = m_aStylesAssocLtrch.makeStringAndClear();
+ const OString aNormal = m_aStyles.makeStringAndClear();
+ OStringBuffer aBuf;
+
+ if (aAutoWriteRtlLtr && !m_bControlLtrRtl)
+ {
+ m_bControlLtrRtl = !aAssocRtlch.isEmpty();
+ m_bIsRTL = false;
+ m_nScript = i18n::ScriptType::LATIN;
+ }
+
+ if (m_bIsRTL)
+ {
+ if (!aAssocRtlch.isEmpty())
+ {
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch
+ + " " OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch);
+ }
+ }
+ else
+ {
+ if (!aAssocRtlch.isEmpty())
+ {
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch
+ + " " OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch);
+ }
+ if (!aAssocHich.isEmpty())
+ {
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_HICH + aAssocHich);
+ }
+ if (!aNormal.isEmpty())
+ {
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH + aNormal);
+ }
+ if (!aAssocDbch.isEmpty())
+ {
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH + aAssocDbch);
+ }
+ }
+
+ if (m_bControlLtrRtl)
+ {
+ m_bControlLtrRtl = false;
+
+ switch (m_nScript)
+ {
+ case i18n::ScriptType::LATIN:
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH);
+ break;
+ case i18n::ScriptType::ASIAN:
+ aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH);
+ break;
+ case i18n::ScriptType::COMPLEX:
+ /* noop */
+ default:
+ /* should not happen? */
+ break;
+ }
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/,
+ const OUString& /*rSymbolFont*/)
+{
+ SAL_INFO("sw.rtf", __func__ << ", rText: " << rText);
+ RawText(rText, m_rExport.GetCurrentEncoding());
+}
+
+OStringBuffer& RtfAttributeOutput::RunText() { return m_aRunText.getLastBuffer(); }
+
+void RtfAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding eCharSet)
+{
+ m_aRunText->append(msfilter::rtfutil::OutString(rText, eCharSet));
+}
+
+void RtfAttributeOutput::StartRuby(const SwTextNode& rNode, sal_Int32 /*nPos*/,
+ const SwFormatRuby& rRuby)
+{
+ WW8Ruby aWW8Ruby(rNode, rRuby, GetExport());
+ OUString aStr = FieldString(ww::eEQ) + "\\* jc" + OUString::number(aWW8Ruby.GetJC())
+ + " \\* \"Font:" + aWW8Ruby.GetFontFamily() + "\" \\* hps"
+ + OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o";
+ if (aWW8Ruby.GetDirective())
+ {
+ aStr += "\\a" + OUStringChar(aWW8Ruby.GetDirective());
+ }
+ aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "(";
+ m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::Start | FieldFlags::CmdStart);
+ aStr = rRuby.GetText() + "),";
+ m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::NONE);
+ m_bInRuby = true;
+}
+
+void RtfAttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/) {}
+
+bool RtfAttributeOutput::StartURL(const OUString& rUrl, const OUString& rTarget)
+{
+ m_aURLs.push(rUrl);
+ // Ignore hyperlink without a URL.
+ if (!rUrl.isEmpty())
+ {
+ m_aRun->append('{');
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FIELD);
+ m_aRun->append('{');
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_IGNORE);
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FLDINST);
+ m_aRun->append(" HYPERLINK ");
+
+ m_aRun->append("\"");
+ m_aRun->append(msfilter::rtfutil::OutString(rUrl, m_rExport.GetCurrentEncoding()));
+ m_aRun->append("\" ");
+
+ // Adding the target is likely a LO embellishment.
+ // Don't export it to clipboard, since editeng and other RTF readers won't understand it.
+ if (!rTarget.isEmpty() && !m_rExport.m_rDoc.IsClipBoard())
+ {
+ m_aRun->append("\\\\t \"");
+ m_aRun->append(msfilter::rtfutil::OutString(rTarget, m_rExport.GetCurrentEncoding()));
+ m_aRun->append("\" ");
+ }
+
+ m_aRun->append("}");
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {");
+ }
+ return true;
+}
+
+bool RtfAttributeOutput::EndURL(bool const isAtEndOfParagraph)
+{
+ if (m_aURLs.empty())
+ {
+ return true;
+ }
+
+ const OUString& rURL = m_aURLs.top();
+ if (!rURL.isEmpty())
+ {
+ // UGLY: usually EndRun is called earlier, but there is an extra
+ // call to OutAttrWithRange() when at the end of the paragraph,
+ // so in that special case the output needs to be appended to the
+ // new run's text instead of the previous run
+ if (isAtEndOfParagraph)
+ {
+ // close the fldrslt group
+ m_aRunText->append("}}");
+ // close the field group
+ m_aRunText->append('}');
+ }
+ else
+ {
+ // close the fldrslt group
+ m_aRun->append("}}");
+ // close the field group
+ m_aRun->append('}');
+ }
+ }
+ m_aURLs.pop();
+ return true;
+}
+
+void RtfAttributeOutput::FieldVanish(const OUString& /*rText*/, ww::eField /*eType*/,
+ OUString const* /*pBookmarkName*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::Redline(const SwRedlineData* pRedline)
+{
+ if (!pRedline)
+ return;
+
+ if (pRedline->GetType() == RedlineType::Insert)
+ {
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVISED);
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTH);
+ m_aRun->append(static_cast<sal_Int32>(
+ m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTM);
+ }
+ else if (pRedline->GetType() == RedlineType::Delete)
+ {
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_DELETED);
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTHDEL);
+ m_aRun->append(static_cast<sal_Int32>(
+ m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTMDEL);
+ }
+ m_aRun->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(pRedline->GetTimeStamp())));
+ m_aRun->append(' ');
+}
+
+void RtfAttributeOutput::FormatDrop(const SwTextNode& /*rNode*/,
+ const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/,
+ ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/,
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTextNodeInfoInner*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::ParagraphStyle(sal_uInt16 nStyle)
+{
+ OString* pStyle = m_rExport.GetStyle(nStyle);
+ OStringBuffer aStyle(OOO_STRING_SVTOOLS_RTF_S
+ + OString::number(static_cast<sal_Int32>(nStyle)));
+ if (pStyle)
+ aStyle.append(*pStyle);
+ if (!m_bBufferSectionHeaders)
+ m_rExport.Strm().WriteOString(aStyle);
+ else
+ m_aSectionHeaders.append(aStyle);
+}
+
+void RtfAttributeOutput::TableInfoCell(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_INTBL);
+ if (m_nTableDepth > 1)
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ITAP);
+ m_aStyles.append(static_cast<sal_Int32>(m_nTableDepth));
+ }
+ m_bWroteCellInfo = true;
+}
+
+void RtfAttributeOutput::TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/)
+{
+ /* noop */
+}
+
+void RtfAttributeOutput::TablePositioning(SwFrameFormat* pFlyFormat)
+{
+ if (!pFlyFormat || !pFlyFormat->GetFlySplit().GetValue())
+ {
+ return;
+ }
+
+ switch (pFlyFormat->GetVertOrient().GetRelationOrient())
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ // relative to margin
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVMRG);
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ // relative to page
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVPG);
+ break;
+ default:
+ // text::RelOrientation::FRAME
+ // relative to text
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVPARA);
+ break;
+ }
+
+ switch (pFlyFormat->GetHoriOrient().GetRelationOrient())
+ {
+ case text::RelOrientation::FRAME:
+ // relative to column
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHCOL);
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ // relative to margin
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHMRG);
+ break;
+ default:
+ // text::RelOrientation::PAGE_FRAME
+ // relative to page
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHPG);
+ break;
+ }
+
+ // Similar to RtfAttributeOutput::FormatHorizOrientation(), but for tables.
+ switch (pFlyFormat->GetHoriOrient().GetHoriOrient())
+ {
+ case text::HoriOrientation::LEFT:
+ // left
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXL);
+ break;
+ case text::HoriOrientation::CENTER:
+ // centered
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXC);
+ break;
+ case text::HoriOrientation::RIGHT:
+ // right
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXR);
+ break;
+ default:
+ SwTwips nTPosX = pFlyFormat->GetHoriOrient().GetPos();
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSX);
+ m_aRowDefs.append(static_cast<sal_Int32>(nTPosX));
+ break;
+ }
+
+ // Similar to RtfAttributeOutput::FormatVertOrientation(), but for tables.
+ switch (pFlyFormat->GetVertOrient().GetVertOrient())
+ {
+ case text::VertOrientation::TOP:
+ // up
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYT);
+ break;
+ case text::VertOrientation::CENTER:
+ // centered
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYC);
+ break;
+ case text::VertOrientation::BOTTOM:
+ // down
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYB);
+ break;
+ default:
+ SwTwips nTPosY = pFlyFormat->GetVertOrient().GetPos();
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSY);
+ m_aRowDefs.append(static_cast<sal_Int32>(nTPosY));
+ break;
+ }
+
+ // Similar to RtfAttributeOutput::FormatULSpace(), but for tables.
+ sal_uInt16 nTdfrmtxtTop = pFlyFormat->GetULSpace().GetUpper();
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTTOP);
+ m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtTop));
+ sal_uInt16 nTdfrmtxtBottom = pFlyFormat->GetULSpace().GetLower();
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTBOTTOM);
+ m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtBottom));
+
+ // Similar to RtfAttributeOutput::FormatLRSpace(), but for tables.
+ sal_uInt16 nTdfrmtxtLeft = pFlyFormat->GetLRSpace().GetLeft();
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTLEFT);
+ m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtLeft));
+ sal_uInt16 nTdfrmtxtRight = pFlyFormat->GetLRSpace().GetRight();
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTRIGHT);
+ m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtRight));
+
+ if (!pFlyFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap())
+ {
+ // Allowing overlap is the default in both Writer and in RTF.
+ m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TABSNOOVRLP);
+ m_aRowDefs.append(static_cast<sal_Int32>(1));
+ }
+}
+
+void RtfAttributeOutput::TableDefinition(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ InitTableHelper(pTableTextNodeInfoInner);
+
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ SwFrameFormat* pFormat = pTable->GetFrameFormat();
+
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TROWD);
+ TableOrientation(pTableTextNodeInfoInner);
+ TableBidi(pTableTextNodeInfoInner);
+ TableHeight(pTableTextNodeInfoInner);
+ TableCanSplit(pTableTextNodeInfoInner);
+
+ // Write table positioning properties in case this is a floating table.
+ TablePositioning(pTable->GetTableNode()->GetFlyFormat());
+
+ // Cell margins
+ const SvxBoxItem& rBox = pFormat->GetBox();
+ static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
+
+ static const char* aRowPadNames[]
+ = { OOO_STRING_SVTOOLS_RTF_TRPADDT, OOO_STRING_SVTOOLS_RTF_TRPADDL,
+ OOO_STRING_SVTOOLS_RTF_TRPADDB, OOO_STRING_SVTOOLS_RTF_TRPADDR };
+
+ static const char* aRowPadUnits[]
+ = { OOO_STRING_SVTOOLS_RTF_TRPADDFT, OOO_STRING_SVTOOLS_RTF_TRPADDFL,
+ OOO_STRING_SVTOOLS_RTF_TRPADDFB, OOO_STRING_SVTOOLS_RTF_TRPADDFR };
+
+ for (int i = 0; i < 4; ++i)
+ {
+ m_aRowDefs.append(aRowPadUnits[i]);
+ m_aRowDefs.append(sal_Int32(3));
+ m_aRowDefs.append(aRowPadNames[i]);
+ m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
+ }
+
+ // The cell-dependent properties
+ const double fWidthRatio = m_pTableWrt->GetAbsWidthRatio();
+ const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
+ sal_uInt32 nRow = pTableTextNodeInfoInner->getRow();
+ if (nRow >= aRows.size())
+ {
+ SAL_WARN("sw.ww8", "RtfAttributeOutput::TableDefinition: out of range row: " << nRow);
+ return;
+ }
+ SwWriteTableRow* pRow = aRows[nRow].get();
+ SwTwips nSz = 0;
+
+ // Not using m_nTableDepth, which is not yet incremented here.
+ sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
+ m_aCells[nCurrentDepth] = pRow->GetCells().size();
+ for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++)
+ {
+ const SwWriteTableCell* const pCell = pRow->GetCells()[i].get();
+ const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
+
+ pTableTextNodeInfoInner->setCell(i);
+ TableCellProperties(pTableTextNodeInfoInner);
+
+ // Right boundary: this can't be in TableCellProperties as the old
+ // value of nSz is needed.
+ nSz += pCellFormat->GetFrameSize().GetWidth();
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CELLX);
+ m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()
+ + rtl::math::round(nSz * fWidthRatio)));
+ }
+}
+
+void RtfAttributeOutput::TableDefaultBorders(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ /*
+ * The function name is a bit misleading: given that we write borders
+ * before each row, we just have borders, not default ones. Additionally,
+ * this function actually writes borders for a specific cell only and is
+ * called for each cell.
+ */
+
+ const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
+ SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
+ const SwWriteTableCell* const pCell
+ = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
+ const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
+ const SvxBoxItem* pItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BOX);
+ if (!pItem)
+ return;
+
+ auto& rBox = *pItem;
+ static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
+ static const char* aBorderNames[]
+ = { OOO_STRING_SVTOOLS_RTF_CLBRDRT, OOO_STRING_SVTOOLS_RTF_CLBRDRL,
+ OOO_STRING_SVTOOLS_RTF_CLBRDRB, OOO_STRING_SVTOOLS_RTF_CLBRDRR };
+ //Yes left and top are swapped with each other for cell padding! Because
+ //that's what the thundering annoying rtf export/import word xp does.
+ static const char* aCellPadNames[]
+ = { OOO_STRING_SVTOOLS_RTF_CLPADL, OOO_STRING_SVTOOLS_RTF_CLPADT,
+ OOO_STRING_SVTOOLS_RTF_CLPADB, OOO_STRING_SVTOOLS_RTF_CLPADR };
+ static const char* aCellPadUnits[]
+ = { OOO_STRING_SVTOOLS_RTF_CLPADFL, OOO_STRING_SVTOOLS_RTF_CLPADFT,
+ OOO_STRING_SVTOOLS_RTF_CLPADFB, OOO_STRING_SVTOOLS_RTF_CLPADFR };
+ for (int i = 0; i < 4; ++i)
+ {
+ if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i]))
+ m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i]));
+ if (rBox.GetDistance(aBorders[i]))
+ {
+ m_aRowDefs.append(aCellPadUnits[i]);
+ m_aRowDefs.append(sal_Int32(3));
+ m_aRowDefs.append(aCellPadNames[i]);
+ m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
+ }
+ }
+}
+
+void RtfAttributeOutput::TableBackgrounds(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTableLine = pTableBox->GetUpper();
+
+ Color aColor = COL_AUTO;
+ auto pTableColorProp
+ = pTable->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if (pTableColorProp)
+ aColor = pTableColorProp->GetColor();
+
+ auto pRowColorProp
+ = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO)
+ aColor = pRowColorProp->GetColor();
+
+ const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
+ SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
+ const SwWriteTableCell* const pCell
+ = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
+ const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
+ if (const SvxBrushItem* pBrushItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BACKGROUND))
+ {
+ if (pBrushItem->GetColor() != COL_AUTO)
+ aColor = pBrushItem->GetColor();
+ }
+
+ if (!aColor.IsTransparent())
+ {
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLCBPAT);
+ m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
+ }
+}
+
+void RtfAttributeOutput::TableRowRedline(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+}
+
+void RtfAttributeOutput::TableCellRedline(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+}
+
+void RtfAttributeOutput::TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
+ const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
+
+ if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
+ return;
+
+ sal_Int32 nHeight = 0;
+
+ switch (rLSz.GetHeightSizeType())
+ {
+ case SwFrameSize::Fixed:
+ nHeight = -rLSz.GetHeight();
+ break;
+ case SwFrameSize::Minimum:
+ nHeight = rLSz.GetHeight();
+ break;
+ default:
+ break;
+ }
+
+ if (nHeight)
+ {
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRRH);
+ m_aRowDefs.append(nHeight);
+ }
+}
+
+void RtfAttributeOutput::TableCanSplit(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine* pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
+ const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
+
+ // The rtf default is to allow a row to break
+ if (!rSplittable.GetValue())
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRKEEP);
+}
+
+void RtfAttributeOutput::TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
+
+ if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB)
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_LTRROW);
+ else
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_RTLROW);
+}
+
+void RtfAttributeOutput::TableVerticalCell(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
+ SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
+ const SwWriteTableCell* const pCell
+ = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
+ const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
+
+ // Text direction.
+ if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat))
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXTBRL);
+ else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat))
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXBTLR);
+
+ // vertical merges
+ if (pCell->GetRowSpan() > 1)
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMGF);
+ else if (pCell->GetRowSpan() == 0)
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMRG);
+
+ // vertical alignment
+ const SwFormatVertOrient* pVertOrientItem
+ = pCellFormat->GetAttrSet().GetItemIfSet(RES_VERT_ORIENT);
+ if (!pVertOrientItem)
+ return;
+
+ switch (pVertOrientItem->GetVertOrient())
+ {
+ case text::VertOrientation::CENTER:
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALC);
+ break;
+ case text::VertOrientation::BOTTOM:
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALB);
+ break;
+ default:
+ m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALT);
+ break;
+ }
+}
+
+void RtfAttributeOutput::TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner)
+{
+ // This is called when the nested table ends in a cell, and there's no
+ // paragraph behind that; so we must check for the ends of cell, rows,
+ // and tables
+ FinishTableRowCell(pNodeInfoInner);
+}
+
+void RtfAttributeOutput::TableOrientation(
+ ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ SwFrameFormat* pFormat = pTable->GetFrameFormat();
+
+ OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL);
+ switch (pFormat->GetHoriOrient().GetHoriOrient())
+ {
+ case text::HoriOrientation::CENTER:
+ aTableAdjust.setLength(0);
+ aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC);
+ break;
+ case text::HoriOrientation::RIGHT:
+ aTableAdjust.setLength(0);
+ aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR);
+ break;
+ case text::HoriOrientation::NONE:
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT);
+ aTableAdjust.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()));
+ break;
+ default:
+ break;
+ }
+
+ m_aRowDefs.append(aTableAdjust);
+}
+
+void RtfAttributeOutput::TableSpacing(
+ ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */}
+
+/*
+ * Our private table methods.
+ */
+
+void RtfAttributeOutput::InitTableHelper(
+ const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
+{
+ const SwTable* pTable = pTableTextNodeInfoInner->getTable();
+ if (m_pTableWrt && pTable == m_pTableWrt->GetTable())
+ return;
+
+ tools::Long nPageSize = 0;
+ bool bRelBoxSize = false;
+
+ // Create the SwWriteTable instance to use col spans
+ GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
+
+ const SwFrameFormat* pFormat = pTable->GetFrameFormat();
+ const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth();
+
+ const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout();
+ if (pLayout && pLayout->IsExportable())
+ m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout);
+ else
+ m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize,
+ nTableSz, false);
+}
+
+void RtfAttributeOutput::StartTable()
+{
+ // To trigger calling InitTableHelper()
+ m_pTableWrt.reset();
+}
+
+void RtfAttributeOutput::StartTableRow(
+ const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
+{
+ sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
+ SAL_INFO("sw.rtf", __func__ << ", (depth is " << nCurrentDepth << ")");
+ m_bTableRowEnded = false;
+
+ TableDefinition(pTableTextNodeInfoInner);
+
+ if (!m_bLastTable)
+ m_aTables.push_back(m_aRowDefs.makeStringAndClear());
+
+ // We'll write the table definition for nested tables later
+ if (nCurrentDepth > 1)
+ return;
+ // Empty the previous row closing buffer before starting the new one,
+ // necessary for subtables.
+ m_rExport.Strm().WriteOString(m_aAfterRuns);
+ m_aAfterRuns.setLength(0);
+ m_rExport.Strm().WriteOString(m_aRowDefs);
+ m_aRowDefs.setLength(0);
+}
+
+void RtfAttributeOutput::StartTableCell() { m_bTableCellOpen = true; }
+
+void RtfAttributeOutput::TableCellProperties(
+ const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
+{
+ TableDefaultBorders(pTableTextNodeInfoInner);
+ TableBackgrounds(pTableTextNodeInfoInner);
+ TableVerticalCell(pTableTextNodeInfoInner);
+}
+
+void RtfAttributeOutput::EndTableCell()
+{
+ SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
+
+ if (!m_bWroteCellInfo)
+ {
+ m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_INTBL);
+ m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ITAP);
+ m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth));
+ }
+ if (m_nTableDepth > 1)
+ m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTCELL);
+ else
+ m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_CELL);
+
+ m_bTableCellOpen = false;
+ m_bTableAfterCell = true;
+ m_bWroteCellInfo = false;
+ if (m_aCells[m_nTableDepth] > 0)
+ m_aCells[m_nTableDepth]--;
+}
+
+void RtfAttributeOutput::EndTableRow()
+{
+ SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
+
+ if (m_nTableDepth > 1)
+ {
+ m_aAfterRuns.append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_NESTTABLEPROPRS);
+ if (!m_aRowDefs.isEmpty())
+ {
+ m_aAfterRuns.append(m_aRowDefs);
+ m_aRowDefs.setLength(0);
+ }
+ else if (!m_aTables.empty())
+ {
+ m_aAfterRuns.append(m_aTables.back());
+ m_aTables.pop_back();
+ }
+ m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTROW
+ "}"
+ "{" OOO_STRING_SVTOOLS_RTF_NONESTTABLES OOO_STRING_SVTOOLS_RTF_PAR "}");
+ }
+ else
+ {
+ if (!m_aTables.empty())
+ {
+ m_aAfterRuns.append(m_aTables.back());
+ m_aTables.pop_back();
+ }
+ // Make sure that the first word of the next paragraph is not merged with the last control
+ // word of this table row, happens with floating tables.
+ m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ROW OOO_STRING_SVTOOLS_RTF_PARD " ");
+ }
+ m_bTableRowEnded = true;
+}
+
+void RtfAttributeOutput::EndTable()
+{
+ if (m_nTableDepth > 0)
+ {
+ m_nTableDepth--;
+ m_pTableWrt.reset();
+ }
+
+ // We closed the table; if it is a nested table, the cell that contains it
+ // still continues
+ m_bTableCellOpen = true;
+
+ // Cleans the table helper
+ m_pTableWrt.reset();
+}
+
+void RtfAttributeOutput::FinishTableRowCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pInner)
+{
+ if (!pInner)
+ return;
+
+ // Where are we in the table
+ sal_uInt32 nRow = pInner->getRow();
+
+ const SwTable* pTable = pInner->getTable();
+ const SwTableLines& rLines = pTable->GetTabLines();
+ sal_uInt16 nLinesCount = rLines.size();
+
+ if (pInner->isEndOfCell())
+ EndTableCell();
+
+ // This is a line end
+ if (pInner->isEndOfLine())
+ EndTableRow();
+
+ // This is the end of the table
+ if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount)
+ EndTable();
+}
+
+void RtfAttributeOutput::StartStyles()
+{
+ m_rExport.Strm()
+ .WriteOString(SAL_NEWLINE_STRING)
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_COLORTBL);
+ m_rExport.OutColorTable();
+ OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty");
+ m_aStylesheet.append(SAL_NEWLINE_STRING);
+ m_aStylesheet.append('{');
+ m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_STYLESHEET);
+}
+
+void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/)
+{
+ m_rExport.Strm().WriteChar('}');
+ m_rExport.Strm().WriteOString(m_aStylesheet);
+ m_aStylesheet.setLength(0);
+ m_rExport.Strm().WriteChar('}');
+}
+
+void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */}
+
+void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase,
+ sal_uInt16 nNext, sal_uInt16 /*nLink*/, sal_uInt16 /*nWwId*/,
+ sal_uInt16 nSlot, bool bAutoUpdate)
+{
+ SAL_INFO("sw.rtf", __func__ << ", rName = '" << rName << "'");
+
+ m_aStylesheet.append('{');
+ if (eType == STYLE_TYPE_PARA)
+ m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_S);
+ else
+ m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_CS);
+ m_aStylesheet.append(static_cast<sal_Int32>(nSlot));
+
+ if (nBase != 0x0FFF)
+ {
+ m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SBASEDON);
+ m_aStylesheet.append(static_cast<sal_Int32>(nBase));
+ }
+
+ m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SNEXT);
+ m_aStylesheet.append(static_cast<sal_Int32>(nNext));
+
+ if (bAutoUpdate)
+ m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SAUTOUPD);
+
+ m_rStyleName = rName;
+ m_nStyleId = nSlot;
+}
+
+void RtfAttributeOutput::EndStyle()
+{
+ OString aStyles = MoveCharacterProperties();
+ m_rExport.InsStyle(m_nStyleId, aStyles);
+ m_aStylesheet.append(aStyles);
+ m_aStylesheet.append(' ');
+ m_aStylesheet.append(
+ msfilter::rtfutil::OutString(m_rStyleName, m_rExport.GetCurrentEncoding()));
+ m_aStylesheet.append(";}");
+ m_aStylesheet.append(SAL_NEWLINE_STRING);
+}
+
+void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/)
+{
+ /* noop */
+}
+
+void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */}
+
+void RtfAttributeOutput::OutlineNumbering(sal_uInt8 nLvl)
+{
+ if (nLvl >= WW8ListManager::nMaxLevel)
+ nLvl = WW8ListManager::nMaxLevel - 1;
+
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL);
+ m_aStyles.append(static_cast<sal_Int32>(nLvl));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTLINELEVEL);
+ m_aStyles.append(static_cast<sal_Int32>(nLvl));
+}
+
+void RtfAttributeOutput::PageBreakBefore(bool bBreak)
+{
+ if (bBreak)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PAGEBB);
+ }
+}
+
+void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/,
+ const WW8_SepInfo* pSectionInfo, bool /*bExtraPageBreak*/)
+{
+ switch (nC)
+ {
+ case msword::ColumnBreak:
+ m_nColBreakNeeded = true;
+ break;
+ case msword::PageBreak:
+ if (pSectionInfo)
+ m_rExport.SectionProperties(*pSectionInfo);
+ break;
+ }
+}
+
+void RtfAttributeOutput::StartSection()
+{
+ if (m_bIsBeforeFirstParagraph)
+ return;
+
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECT OOO_STRING_SVTOOLS_RTF_SECTD);
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+}
+
+void RtfAttributeOutput::EndSection()
+{
+ /*
+ * noop, \sect must go to StartSection or Word won't notice multiple
+ * columns...
+ */
+}
+
+void RtfAttributeOutput::SectionFormProtection(bool bProtected)
+{
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected));
+}
+
+void RtfAttributeOutput::SectionLineNumbering(sal_uLong nRestartNo,
+ const SwLineNumberInfo& rLnNumInfo)
+{
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEMOD);
+ m_rExport.Strm().WriteNumberAsString(rLnNumInfo.GetCountBy());
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEX);
+ m_rExport.Strm().WriteNumberAsString(rLnNumInfo.GetPosFromLeft());
+ if (!rLnNumInfo.IsRestartEachPage())
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINECONT);
+
+ if (nRestartNo > 0)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINESTARTS);
+ m_rExport.Strm().WriteNumberAsString(nRestartNo);
+ }
+}
+
+void RtfAttributeOutput::SectionTitlePage()
+{
+ /*
+ * noop, handled in RtfExport::WriteHeaderFooter()
+ */
+}
+
+void RtfAttributeOutput::SectionPageBorders(const SwFrameFormat* pFormat,
+ const SwFrameFormat* /*pFirstPageFormat*/)
+{
+ const SvxBoxItem& rBox = pFormat->GetBox();
+ editeng::WordBorderDistances aDistances;
+ editeng::BorderDistancesToWord(rBox, m_aPageMargins, aDistances);
+
+ if (aDistances.bFromEdge)
+ {
+ sal_uInt16 nOpt = (1 << 5);
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGBRDROPT);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
+ }
+
+ const editeng::SvxBorderLine* pLine = rBox.GetTop();
+ if (pLine)
+ m_aSectionBreaks.append(
+ OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRT, aDistances.nTop));
+ pLine = rBox.GetBottom();
+ if (pLine)
+ m_aSectionBreaks.append(
+ OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRB, aDistances.nBottom));
+ pLine = rBox.GetLeft();
+ if (pLine)
+ m_aSectionBreaks.append(
+ OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRL, aDistances.nLeft));
+ pLine = rBox.GetRight();
+ if (pLine)
+ m_aSectionBreaks.append(
+ OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRR, aDistances.nRight));
+}
+
+void RtfAttributeOutput::SectionBiDi(bool bBiDi)
+{
+ m_rExport.Strm().WriteOString(bBiDi ? OOO_STRING_SVTOOLS_RTF_RTLSECT
+ : OOO_STRING_SVTOOLS_RTF_LTRSECT);
+}
+
+void RtfAttributeOutput::SectionPageNumbering(sal_uInt16 nNumType,
+ const ::std::optional<sal_uInt16>& oPageRestartNumber)
+{
+ if (oPageRestartNumber)
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNSTARTS);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber));
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNRESTART);
+ }
+
+ const char* pStr = nullptr;
+ switch (nNumType)
+ {
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pStr = OOO_STRING_SVTOOLS_RTF_PGNUCLTR;
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pStr = OOO_STRING_SVTOOLS_RTF_PGNLCLTR;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pStr = OOO_STRING_SVTOOLS_RTF_PGNUCRM;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pStr = OOO_STRING_SVTOOLS_RTF_PGNLCRM;
+ break;
+
+ case SVX_NUM_ARABIC:
+ pStr = OOO_STRING_SVTOOLS_RTF_PGNDEC;
+ break;
+ }
+ if (pStr)
+ m_aSectionBreaks.append(pStr);
+}
+
+void RtfAttributeOutput::SectionType(sal_uInt8 nBreakCode)
+{
+ SAL_INFO("sw.rtf", __func__ << ", nBreakCode = " << int(nBreakCode));
+
+ /*
+ * break code: 0 No break, 1 New column
+ * 2 New page, 3 Even page, 4 Odd page
+ */
+ const char* sType = nullptr;
+ switch (nBreakCode)
+ {
+ case 1:
+ sType = OOO_STRING_SVTOOLS_RTF_SBKCOL;
+ break;
+ case 2:
+ sType = OOO_STRING_SVTOOLS_RTF_SBKPAGE;
+ break;
+ case 3:
+ sType = OOO_STRING_SVTOOLS_RTF_SBKEVEN;
+ break;
+ case 4:
+ sType = OOO_STRING_SVTOOLS_RTF_SBKODD;
+ break;
+ default:
+ sType = OOO_STRING_SVTOOLS_RTF_SBKNONE;
+ break;
+ }
+ m_aSectionBreaks.append(sType);
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+}
+
+void RtfAttributeOutput::SectFootnoteEndnotePr()
+{
+ WriteFootnoteEndnotePr(true, m_rExport.m_rDoc.GetFootnoteInfo());
+ WriteFootnoteEndnotePr(false, m_rExport.m_rDoc.GetEndNoteInfo());
+}
+
+void RtfAttributeOutput::WriteFootnoteEndnotePr(bool bFootnote, const SwEndNoteInfo& rInfo)
+{
+ const char* pOut = nullptr;
+
+ if (bFootnote)
+ {
+ switch (rInfo.m_aFormat.GetNumberingType())
+ {
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAR;
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_SFTNNALC;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAUC;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRLC;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRUC;
+ break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ pOut = OOO_STRING_SVTOOLS_RTF_SFTNNCHI;
+ break;
+ }
+ }
+ else
+ {
+ switch (rInfo.m_aFormat.GetNumberingType())
+ {
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAR;
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNALC;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAUC;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRLC;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRUC;
+ break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNCHI;
+ break;
+ }
+ }
+
+ m_aSectionBreaks.append(pOut);
+
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+}
+
+void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/)
+{
+ m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDE);
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTID);
+ m_rExport.Strm().WriteNumberAsString(nId);
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDECOUNT).WriteChar('0');
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LS);
+ m_rExport.Strm().WriteNumberAsString(nId).WriteChar('}');
+}
+
+void RtfAttributeOutput::StartAbstractNumbering(sal_uInt16 nId)
+{
+ m_rExport.Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_LIST)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_LISTTEMPLATEID);
+ m_rExport.Strm().WriteNumberAsString(nId);
+ m_nListId = nId;
+}
+
+void RtfAttributeOutput::EndAbstractNumbering()
+{
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTID);
+ m_rExport.Strm().WriteNumberAsString(m_nListId).WriteChar('}').WriteOString(SAL_NEWLINE_STRING);
+}
+
+void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart,
+ sal_uInt16 nNumberingType, SvxAdjust eAdjust,
+ const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
+ const wwFont* pFont, const SfxItemSet* pOutSet,
+ sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex,
+ sal_Int16 /*nListTabPos*/, const OUString& rNumberingString,
+ const SvxBrushItem* pBrush, bool /*isLegal*/)
+{
+ m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
+ if (nLevel > 8) // RTF knows only 9 levels
+ m_rExport.Strm()
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SOUTLVL);
+
+ m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTLEVEL);
+
+ sal_uInt16 nVal = 0;
+ switch (nNumberingType)
+ {
+ case SVX_NUM_ROMAN_UPPER:
+ nVal = 1;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ nVal = 2;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ nVal = 3;
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ nVal = 4;
+ break;
+ case SVX_NUM_FULL_WIDTH_ARABIC:
+ nVal = 14;
+ break;
+ case SVX_NUM_CIRCLE_NUMBER:
+ nVal = 18;
+ break;
+ case SVX_NUM_NUMBER_LOWER_ZH:
+ nVal = 35;
+ if (pOutSet)
+ {
+ const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE);
+ if (rLang.GetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED)
+ {
+ nVal = 39;
+ }
+ }
+ break;
+ case SVX_NUM_NUMBER_UPPER_ZH:
+ nVal = 38;
+ break;
+ case SVX_NUM_NUMBER_UPPER_ZH_TW:
+ nVal = 34;
+ break;
+ case SVX_NUM_TIAN_GAN_ZH:
+ nVal = 30;
+ break;
+ case SVX_NUM_DI_ZI_ZH:
+ nVal = 31;
+ break;
+ case SVX_NUM_NUMBER_TRADITIONAL_JA:
+ nVal = 16;
+ break;
+ case SVX_NUM_AIU_FULLWIDTH_JA:
+ nVal = 20;
+ break;
+ case SVX_NUM_AIU_HALFWIDTH_JA:
+ nVal = 12;
+ break;
+ case SVX_NUM_IROHA_FULLWIDTH_JA:
+ nVal = 21;
+ break;
+ case SVX_NUM_IROHA_HALFWIDTH_JA:
+ nVal = 13;
+ break;
+ case style::NumberingType::HANGUL_SYLLABLE_KO:
+ nVal = 24;
+ break; // ganada
+ case style::NumberingType::HANGUL_JAMO_KO:
+ nVal = 25;
+ break; // chosung
+ case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
+ nVal = 24;
+ break;
+ case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
+ nVal = 25;
+ break;
+ case style::NumberingType::NUMBER_HANGUL_KO:
+ nVal = 42;
+ break; // koreanCounting
+ case style::NumberingType::NUMBER_DIGITAL_KO:
+ nVal = 41; // koreanDigital
+ break;
+ case style::NumberingType::NUMBER_DIGITAL2_KO:
+ nVal = 44; // koreanDigital2
+ break;
+ case style::NumberingType::NUMBER_LEGAL_KO:
+ nVal = 43; // koreanLegal
+ break;
+
+ case SVX_NUM_BITMAP:
+ case SVX_NUM_CHAR_SPECIAL:
+ nVal = 23;
+ break;
+ case SVX_NUM_NUMBER_NONE:
+ nVal = 255;
+ break;
+ case SVX_NUM_ARABIC_ZERO:
+ nVal = 22;
+ break;
+ }
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELNFC);
+ m_rExport.Strm().WriteNumberAsString(nVal);
+
+ switch (eAdjust)
+ {
+ case SvxAdjust::Center:
+ nVal = 1;
+ break;
+ case SvxAdjust::Right:
+ nVal = 2;
+ break;
+ default:
+ nVal = 0;
+ break;
+ }
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELJC);
+ m_rExport.Strm().WriteNumberAsString(nVal);
+
+ // bullet
+ if (nNumberingType == SVX_NUM_BITMAP && pBrush)
+ {
+ int nIndex = m_rExport.GetGrfIndex(*pBrush);
+ if (nIndex != -1)
+ {
+ m_rExport.Strm().WriteOString(LO_STRING_SVTOOLS_RTF_LEVELPICTURE);
+ m_rExport.Strm().WriteNumberAsString(nIndex);
+ }
+ }
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELSTARTAT);
+ m_rExport.Strm().WriteNumberAsString(nStart);
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELFOLLOW);
+ m_rExport.Strm().WriteNumberAsString(nFollow);
+
+ // leveltext group
+ m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELTEXT).WriteChar(' ');
+
+ if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType)
+ {
+ m_rExport.Strm().WriteOString("\\'01");
+ sal_Unicode cChar = rNumberingString[0];
+ m_rExport.Strm().WriteOString("\\u");
+ m_rExport.Strm().WriteNumberAsString(cChar);
+ m_rExport.Strm().WriteOString(" ?");
+ }
+ else
+ {
+ m_rExport.Strm().WriteOString("\\'").WriteOString(
+ msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2));
+ m_rExport.Strm().WriteOString(msfilter::rtfutil::OutString(rNumberingString,
+ m_rExport.GetDefaultEncoding(),
+ /*bUnicode =*/false));
+ }
+
+ m_rExport.Strm().WriteOString(";}");
+
+ // write the levelnumbers
+ m_rExport.Strm().WriteOString("{").WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELNUMBERS);
+ for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i)
+ {
+ m_rExport.Strm().WriteOString("\\'").WriteOString(
+ msfilter::rtfutil::OutHex(pNumLvlPos[i], 2));
+ }
+ m_rExport.Strm().WriteOString(";}");
+
+ if (pOutSet)
+ {
+ if (pFont)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_F);
+ m_rExport.Strm().WriteNumberAsString(m_rExport.m_aFontHelper.GetId(*pFont));
+ }
+ m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN,
+ m_rExport.m_bExportModeRTF);
+ const OString aProperties = MoveCharacterProperties(true);
+ m_rExport.Strm().WriteOString(aProperties);
+ }
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FI);
+ m_rExport.Strm().WriteNumberAsString(nFirstLineIndex).WriteOString(OOO_STRING_SVTOOLS_RTF_LI);
+ m_rExport.Strm().WriteNumberAsString(nIndentAt);
+
+ m_rExport.Strm().WriteChar('}');
+ if (nLevel > 8)
+ m_rExport.Strm().WriteChar('}');
+}
+
+void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/,
+ std::u16string_view rFieldCmd, FieldFlags nMode)
+{
+ // If there are no field instructions, don't export it as a field.
+ bool bHasInstructions = !rFieldCmd.empty();
+ if (FieldFlags::All == nMode)
+ {
+ if (bHasInstructions)
+ {
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
+ if (pField && (pField->GetSubType() & FIXEDFLD))
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDLOCK);
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
+ " ");
+ m_aRunText->append(
+ msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding()));
+ m_aRunText->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
+ }
+ if (pField)
+ m_aRunText->append(msfilter::rtfutil::OutString(pField->ExpandField(true, nullptr),
+ m_rExport.GetDefaultEncoding()));
+ if (bHasInstructions)
+ m_aRunText->append("}}");
+ }
+ else
+ {
+ if (nMode & FieldFlags::CmdStart)
+ {
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
+ // paragraph break closes group so open another one "inside" to
+ " {"); // prevent leaving the field instruction
+ }
+ if (bHasInstructions)
+ m_aRunText->append(
+ msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding()));
+ if (nMode & FieldFlags::CmdEnd)
+ {
+ m_aRunText->append("}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT);
+ // The fldrslt contains its own full copy of character formatting,
+ // but if the result is empty (nMode & FieldFlags::End) or field export is condensed
+ // in any way (multiple flags) then avoid spamming an unnecessary plain character reset.
+ if (nMode == FieldFlags::CmdEnd)
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_PLAIN);
+ m_aRunText->append(" {");
+ }
+ if (nMode & FieldFlags::Close)
+ {
+ m_aRunText->append("}}}");
+ }
+ }
+}
+
+void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts,
+ std::vector<OUString>& rEnds)
+{
+ for (const auto& rStart : rStarts)
+ {
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKSTART " ");
+ m_aRunText->append(msfilter::rtfutil::OutString(rStart, m_rExport.GetCurrentEncoding()));
+ m_aRunText->append('}');
+ }
+ rStarts.clear();
+
+ for (const auto& rEnd : rEnds)
+ {
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKEND " ");
+ m_aRunText->append(msfilter::rtfutil::OutString(rEnd, m_rExport.GetCurrentEncoding()));
+ m_aRunText->append('}');
+ }
+ rEnds.clear();
+}
+
+void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts,
+ std::vector<OUString>& rEnds)
+{
+ for (const auto& rStart : rStarts)
+ {
+ OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8);
+
+ // Output the annotation mark
+ const sal_Int32 nId = m_nNextAnnotationMarkId++;
+ m_rOpenedAnnotationMarksIds[rName] = nId;
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFSTART " ");
+ m_aRun->append(nId);
+ m_aRun->append('}');
+ }
+ rStarts.clear();
+
+ for (const auto& rEnd : rEnds)
+ {
+ OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8);
+
+ // Get the id of the annotation mark
+ auto it = m_rOpenedAnnotationMarksIds.find(rName);
+ if (it != m_rOpenedAnnotationMarksIds.end())
+ {
+ const sal_Int32 nId = it->second;
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFEND " ");
+ m_aRun->append(nId);
+ m_aRun->append('}');
+ m_rOpenedAnnotationMarksIds.erase(rName);
+
+ if (m_aPostitFields.find(nId) != m_aPostitFields.end())
+ {
+ m_aRunText->append("{");
+ m_nCurrentAnnotationMarkId = nId;
+ PostitField(m_aPostitFields[nId]);
+ m_nCurrentAnnotationMarkId = -1;
+ m_aRunText->append("}");
+ }
+ }
+ }
+ rEnds.clear();
+}
+
+void RtfAttributeOutput::WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader,
+ const char* pStr, bool bTitlepg)
+{
+ OStringBuffer aSectionBreaks = m_aSectionBreaks;
+ m_aSectionBreaks.setLength(0);
+ RtfStringBuffer aRun = m_aRun;
+ m_aRun.clear();
+
+ m_aSectionHeaders.append(bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERY
+ : OOO_STRING_SVTOOLS_RTF_FOOTERY);
+ m_aSectionHeaders.append(
+ static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper()));
+ if (bTitlepg)
+ m_aSectionHeaders.append(OOO_STRING_SVTOOLS_RTF_TITLEPG);
+ m_aSectionHeaders.append('{');
+ m_aSectionHeaders.append(pStr);
+ m_bBufferSectionHeaders = true;
+ m_rExport.WriteHeaderFooterText(rFormat, bHeader);
+ m_bBufferSectionHeaders = false;
+ m_aSectionHeaders.append('}');
+
+ m_aSectionBreaks = aSectionBreaks;
+ m_aRun = aRun;
+}
+
+namespace
+{
+void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties,
+ const SwFrameFormat& rFrameFormat)
+{
+ const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
+ if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
+ return;
+
+ rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow"_ostr, OString::number(1)));
+
+ const Color& rColor = aShadowItem.GetColor();
+ // We in fact need RGB to BGR, but the transformation is symmetric.
+ rFlyProperties.push_back(std::make_pair<OString, OString>(
+ "shadowColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
+
+ // Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip.
+ OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700);
+ OString aOffsetX;
+ OString aOffsetY;
+ switch (aShadowItem.GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ aOffsetX = "-" + aShadowWidth;
+ aOffsetY = "-" + aShadowWidth;
+ break;
+ case SvxShadowLocation::TopRight:
+ aOffsetX = aShadowWidth;
+ aOffsetY = "-" + aShadowWidth;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ aOffsetX = "-" + aShadowWidth;
+ aOffsetY = aShadowWidth;
+ break;
+ case SvxShadowLocation::BottomRight:
+ aOffsetX = aShadowWidth;
+ aOffsetY = aShadowWidth;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ break;
+ }
+ if (!aOffsetX.isEmpty())
+ rFlyProperties.emplace_back("shadowOffsetX", aOffsetX);
+ if (!aOffsetY.isEmpty())
+ rFlyProperties.emplace_back("shadowOffsetY", aOffsetY);
+}
+
+void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties,
+ const SwFrameFormat& rFrameFormat)
+{
+ const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
+
+ // Relative size of the Text Frame.
+ const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
+ if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
+ {
+ rFlyProperties.push_back(
+ std::make_pair<OString, OString>("pctHoriz"_ostr, OString::number(nWidthPercent * 10)));
+
+ OString aRelation;
+ switch (rSize.GetWidthPercentRelation())
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ aRelation = "1"_ostr; // page
+ break;
+ default:
+ aRelation = "0"_ostr; // margin
+ break;
+ }
+ rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation));
+ }
+ const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
+ if (!(nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED))
+ return;
+
+ rFlyProperties.push_back(
+ std::make_pair<OString, OString>("pctVert"_ostr, OString::number(nHeightPercent * 10)));
+
+ OString aRelation;
+ switch (rSize.GetHeightPercentRelation())
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ aRelation = "1"_ostr; // page
+ break;
+ default:
+ aRelation = "0"_ostr; // margin
+ break;
+ }
+ rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation));
+}
+}
+
+void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox)
+{
+ RtfStringBuffer aRunText;
+ if (bTextBox)
+ {
+ m_rExport.setStream();
+ aRunText = m_aRunText;
+ m_aRunText.clear();
+ }
+
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHPTXT);
+
+ {
+ // Save table state, in case the inner text also contains a table.
+ ww8::WW8TableInfo::Pointer_t pTableInfoOrig = m_rExport.m_pTableInfo;
+ m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
+ std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt));
+ sal_uInt32 nTableDepth = m_nTableDepth;
+
+ m_nTableDepth = 0;
+ /*
+ * Save m_aRun as we should not lose the opening brace.
+ * OTOH, just drop the contents of m_aRunText in case something
+ * would be there, causing a problem later.
+ */
+ OString aSave = m_aRun.makeStringAndClear();
+ // Also back m_bInRun and m_bSingleEmptyRun up.
+ bool bInRunOrig = m_bInRun;
+ m_bInRun = false;
+ bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
+ m_bSingleEmptyRun = false;
+ m_rExport.SetRTFFlySyntax(true);
+
+ const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd
+ = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+ m_rExport.SaveData(nStt, nEnd);
+ m_rExport.m_pParentFrame = &rFrame;
+ m_rExport.WriteText();
+ m_rExport.RestoreData();
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PARD);
+ m_rExport.SetRTFFlySyntax(false);
+ m_aRun->append(aSave);
+ m_aRunText.clear();
+ m_bInRun = bInRunOrig;
+ m_bSingleEmptyRun = bSingleEmptyRunOrig;
+
+ // Restore table state.
+ m_rExport.m_pTableInfo = pTableInfoOrig;
+ m_pTableWrt = std::move(pTableWrt);
+ m_nTableDepth = nTableDepth;
+ }
+
+ m_rExport.m_pParentFrame = nullptr;
+
+ m_rExport.Strm().WriteChar('}'); // shptxt
+
+ if (bTextBox)
+ {
+ m_aRunText = aRunText;
+ m_aRunText->append(m_rExport.getStream());
+ m_rExport.resetStream();
+ }
+}
+
+/** save the current run state around exporting things that contain paragraphs
+ themselves like text frames.
+ TODO: probably more things need to be saved?
+ */
+class SaveRunState
+{
+private:
+ RtfAttributeOutput& m_rRtf;
+ RtfStringBuffer m_Run;
+ RtfStringBuffer m_RunText;
+ bool const m_bSingleEmptyRun;
+ bool const m_bInRun;
+
+public:
+ explicit SaveRunState(RtfAttributeOutput& rRtf)
+ : m_rRtf(rRtf)
+ , m_Run(std::move(rRtf.m_aRun))
+ , m_RunText(std::move(rRtf.m_aRunText))
+ , m_bSingleEmptyRun(rRtf.m_bSingleEmptyRun)
+ , m_bInRun(rRtf.m_bInRun)
+ {
+ m_rRtf.m_rExport.setStream();
+ }
+ ~SaveRunState()
+ {
+ m_rRtf.m_aRun = std::move(m_Run);
+ m_rRtf.m_aRunText = std::move(m_RunText);
+ m_rRtf.m_bSingleEmptyRun = m_bSingleEmptyRun;
+ m_rRtf.m_bInRun = m_bInRun;
+
+ m_rRtf.m_aRunText->append(m_rRtf.m_rExport.getStream());
+ m_rRtf.m_rExport.resetStream();
+ }
+};
+
+void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
+{
+ const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
+ if (rFrameFormat.GetFlySplit().GetValue())
+ {
+ // The frame can split: this was originally from a floating table, write it back as
+ // such.
+ SaveRunState aState(*this);
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd
+ = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+ m_rExport.SaveData(nStt, nEnd);
+ GetExport().WriteText();
+ m_rExport.RestoreData();
+ return;
+ }
+
+ const SwNode* pNode = rFrame.GetContent();
+ const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
+
+ switch (rFrame.GetWriterType())
+ {
+ case ww8::Frame::eTextBox:
+ {
+ // If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape().
+ if (RtfSdrExport::isTextBox(rFrame.GetFrameFormat()))
+ break;
+
+ SaveRunState const saved(*this);
+
+ m_rExport.m_pParentFrame = &rFrame;
+
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHP);
+ m_rExport.Strm().WriteOString(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
+
+ // Shape properties.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "shapeType"_ostr, OString::number(ESCHER_ShpInst_TextBox)));
+
+ // When a frame has some low height, but automatically expanded due
+ // to lots of contents, this size contains the real size.
+ const Size aSize = rFrame.GetSize();
+ m_pFlyFrameSize = &aSize;
+
+ m_rExport.m_bOutFlyFrameAttrs = true;
+ m_rExport.SetRTFFlySyntax(true);
+ m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
+
+ // Write ZOrder.
+ if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject())
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPZ);
+ m_rExport.Strm().WriteNumberAsString(pObject->GetOrdNum());
+ }
+
+ m_rExport.Strm().WriteOString(m_aRunText.makeStringAndClear());
+ m_rExport.Strm().WriteOString(m_aStyles);
+ m_aStyles.setLength(0);
+ m_rExport.m_bOutFlyFrameAttrs = false;
+ m_rExport.SetRTFFlySyntax(false);
+ m_pFlyFrameSize = nullptr;
+
+ lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat);
+ lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat);
+
+ for (const std::pair<OString, OString>& rPair : m_aFlyProperties)
+ {
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
+ m_rExport.Strm().WriteOString(rPair.first);
+ m_rExport.Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
+ m_rExport.Strm().WriteOString(rPair.second);
+ m_rExport.Strm().WriteOString("}}");
+ }
+ m_aFlyProperties.clear();
+
+ writeTextFrame(rFrame);
+
+ m_rExport.Strm().WriteChar('}'); // shpinst
+ m_rExport.Strm().WriteChar('}'); // shp
+
+ m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
+ }
+ break;
+ case ww8::Frame::eGraphic:
+ if (pGrfNode)
+ {
+ m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()),
+ pGrfNode);
+ }
+ else if (!rFrame.IsInline())
+ {
+ m_rExport.m_pParentFrame = &rFrame;
+ m_rExport.SetRTFFlySyntax(true);
+ m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
+ m_rExport.SetRTFFlySyntax(false);
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE);
+ m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
+ m_aRunText->append('}');
+ m_rExport.m_pParentFrame = nullptr;
+ }
+ break;
+ case ww8::Frame::eDrawing:
+ {
+ const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
+ if (pSdrObj)
+ {
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD "{");
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_IGNORE);
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDINST);
+ m_aRunText->append(" SHAPE ");
+ m_aRunText->append("}"
+ "{" OOO_STRING_SVTOOLS_RTF_FLDRSLT);
+
+ m_rExport.SdrExporter().AddSdrObject(*pSdrObj);
+
+ m_aRunText->append('}');
+ m_aRunText->append('}');
+ }
+ }
+ break;
+ case ww8::Frame::eFormControl:
+ {
+ const SdrObject* pObject = rFrameFormat.FindRealSdrObject();
+
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST);
+
+ if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm)
+ {
+ if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject))
+ {
+ const uno::Reference<awt::XControlModel>& xControlModel
+ = pFormObj->GetUnoControlModel();
+ uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
+ if (xInfo.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo
+ = xPropSet->getPropertySetInfo();
+ OUString sName;
+ if (xInfo->supportsService("com.sun.star.form.component.CheckBox"))
+ {
+ m_aRun->append(OUStringToOString(FieldString(ww::eFORMCHECKBOX),
+ m_rExport.GetCurrentEncoding()));
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ "{");
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox
+ // checkbox size in half points, this seems to be always 20
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHPS "20");
+
+ OUString aStr;
+ sName = "Name";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aStr;
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME
+ " ");
+ m_aRun->append(
+ OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ sName = "HelpText";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aStr;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
+ OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
+ m_aRun->append(
+ OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ sName = "HelpF1Text";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aStr;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
+ OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
+ m_aRun->append(
+ OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ sal_Int16 nTemp = 0;
+ xPropSet->getPropertyValue("DefaultState") >>= nTemp;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES);
+ m_aRun->append(static_cast<sal_Int32>(nTemp));
+ xPropSet->getPropertyValue("State") >>= nTemp;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES);
+ m_aRun->append(static_cast<sal_Int32>(nTemp));
+
+ m_aRun->append("}}");
+
+ // field result is empty, ffres already contains the form result
+ m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
+ }
+ else if (xInfo->supportsService("com.sun.star.form.component.TextField"))
+ {
+ OStringBuffer aBuf;
+ OString aStr;
+ OUString aTmp;
+ const char* pStr;
+
+ m_aRun->append(OUStringToOString(FieldString(ww::eFORMTEXT),
+ m_rExport.GetCurrentEncoding()));
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_DATAFIELD
+ " ");
+ for (int i = 0; i < 8; i++)
+ aBuf.append(char(0x00));
+ xPropSet->getPropertyValue("Name") >>= aTmp;
+ aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding());
+ aBuf.append(OStringChar(static_cast<char>(aStr.getLength())) + aStr
+ + OStringChar(char(0x00)));
+ xPropSet->getPropertyValue("DefaultText") >>= aTmp;
+ aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding());
+ aBuf.append(static_cast<char>(aStr.getLength()));
+ aBuf.append(aStr);
+ for (int i = 0; i < 11; i++)
+ aBuf.append(char(0x00));
+ aStr = aBuf.makeStringAndClear();
+ pStr = aStr.getStr();
+ for (int i = 0; i < aStr.getLength(); i++, pStr++)
+ m_aRun->append(msfilter::rtfutil::OutHex(*pStr, 2));
+ m_aRun->append('}');
+ m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
+ xPropSet->getPropertyValue("Text") >>= aTmp;
+ m_aRun->append(OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ "{");
+ sName = "HelpText";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aTmp;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
+ OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
+ m_aRun->append(
+ OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ sName = "HelpF1Text";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aTmp;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
+ OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
+ m_aRun->append(
+ OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+ m_aRun->append("}");
+ }
+ else if (xInfo->supportsService("com.sun.star.form.component.ListBox"))
+ {
+ OUString aStr;
+ uno::Sequence<sal_Int16> aIntSeq;
+ uno::Sequence<OUString> aStrSeq;
+
+ m_aRun->append(OUStringToOString(FieldString(ww::eFORMDROPDOWN),
+ m_rExport.GetCurrentEncoding()));
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ "{");
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "2"); // 2 = list
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHASLISTBOX);
+
+ xPropSet->getPropertyValue("DefaultSelection") >>= aIntSeq;
+ if (aIntSeq.hasElements())
+ {
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES);
+ // a dropdown list can have only one 'selected item by default'
+ m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
+ }
+
+ xPropSet->getPropertyValue("SelectedItems") >>= aIntSeq;
+ if (aIntSeq.hasElements())
+ {
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES);
+ // a dropdown list can have only one 'currently selected item'
+ m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
+ }
+
+ sName = "Name";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aStr;
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME
+ " ");
+ m_aRun->append(
+ OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ sName = "HelpText";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aStr;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
+ OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
+ m_aRun->append(
+ OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ sName = "HelpF1Text";
+ if (xPropSetInfo->hasPropertyByName(sName))
+ {
+ xPropSet->getPropertyValue(sName) >>= aStr;
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
+ OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
+ m_aRun->append(
+ OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
+ m_aRun->append('}');
+ }
+
+ xPropSet->getPropertyValue("StringItemList") >>= aStrSeq;
+ for (const auto& rStr : std::as_const(aStrSeq))
+ m_aRun->append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFL " "
+ + OUStringToOString(rStr, m_rExport.GetCurrentEncoding())
+ + "}");
+
+ m_aRun->append("}}");
+
+ // field result is empty, ffres already contains the form result
+ m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
+ }
+ else
+ SAL_INFO("sw.rtf", __func__ << " unhandled form control: '"
+ << xInfo->getImplementationName() << "'");
+ m_aRun->append('}');
+ }
+ }
+ }
+
+ m_aRun->append('}');
+ }
+ break;
+ case ww8::Frame::eOle:
+ {
+ const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
+ if (pSdrObj)
+ {
+ SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
+ SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
+ FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd,
+ rFrame.GetLayoutSize());
+ }
+ }
+ break;
+ default:
+ SAL_INFO("sw.rtf", __func__ << ": unknown type ("
+ << static_cast<int>(rFrame.GetWriterType()) << ")");
+ break;
+ }
+}
+
+void RtfAttributeOutput::CharCaseMap(const SvxCaseMapItem& rCaseMap)
+{
+ switch (rCaseMap.GetValue())
+ {
+ case SvxCaseMap::SmallCaps:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS);
+ break;
+ case SvxCaseMap::Uppercase:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS);
+ break;
+ default: // Something that rtf does not support
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS);
+ m_aStyles.append(sal_Int32(0));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS);
+ m_aStyles.append(sal_Int32(0));
+ break;
+ }
+}
+
+void RtfAttributeOutput::CharColor(const SvxColorItem& rColor)
+{
+ const Color aColor(rColor.GetValue());
+
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CF);
+ m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
+}
+
+void RtfAttributeOutput::CharContour(const SvxContourItem& rContour)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTL);
+ if (!rContour.GetValue())
+ m_aStyles.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharCrossedOut(const SvxCrossedOutItem& rCrossedOut)
+{
+ switch (rCrossedOut.GetStrikeout())
+ {
+ case STRIKEOUT_NONE:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE);
+ m_aStyles.append(sal_Int32(0));
+ break;
+ case STRIKEOUT_DOUBLE:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKED);
+ m_aStyles.append(sal_Int32(1));
+ break;
+ default:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE);
+ break;
+ }
+}
+
+void RtfAttributeOutput::CharEscapement(const SvxEscapementItem& rEscapement)
+{
+ short nEsc = rEscapement.GetEsc();
+ short nProp = rEscapement.GetProportionalHeight();
+ sal_Int32 nProp100 = nProp * 100;
+ if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100)
+ {
+ if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc)
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUB);
+ else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc)
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUPER);
+ return;
+ }
+ if (DFLT_ESC_AUTO_SUPER == nEsc)
+ {
+ nEsc = .8 * (100 - nProp);
+ ++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx
+ }
+ else if (DFLT_ESC_AUTO_SUB == nEsc)
+ {
+ nEsc = .2 * -(100 - nProp);
+ ++nProp100;
+ }
+
+ const char* pUpDn;
+
+ double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight();
+
+ if (0 < nEsc)
+ pUpDn = OOO_STRING_SVTOOLS_RTF_UP;
+ else if (0 > nEsc)
+ {
+ pUpDn = OOO_STRING_SVTOOLS_RTF_DN;
+ fHeight = -fHeight;
+ }
+ else
+ return;
+
+ m_aStyles.append('{');
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_IGNORE);
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_UPDNPROP);
+ m_aStyles.append(nProp100);
+ m_aStyles.append('}');
+ m_aStyles.append(pUpDn);
+
+ /*
+ * Calculate the act. FontSize and the percentage of the displacement;
+ * RTF file expects half points, while internally it's in twips.
+ * Formally : (FontSize * 1/20 ) pts x * 2
+ * ----------------------- = ------------
+ * 100% Escapement
+ */
+ m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000)));
+}
+
+void RtfAttributeOutput::CharFont(const SvxFontItem& rFont)
+{
+ // Insert \loch in MoveCharacterProperties
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_F);
+ m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
+
+ // Insert \hich in MoveCharacterProperties
+ m_aStylesAssocHich.append(OOO_STRING_SVTOOLS_RTF_AF);
+ m_aStylesAssocHich.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
+
+ // FIXME: this may be a tad expensive... but the charset needs to be
+ // consistent with what wwFont::WriteRtf() does
+ sw::util::FontMapExport aTmp(rFont.GetFamilyName());
+ sal_uInt8 nWindowsCharset = sw::ms::rtl_TextEncodingToWinCharsetRTF(
+ aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet());
+ m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset));
+ if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW)
+ m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding());
+}
+
+void RtfAttributeOutput::CharFontSize(const SvxFontHeightItem& rFontSize)
+{
+ switch (rFontSize.Which())
+ {
+ case RES_CHRATR_FONTSIZE:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FS);
+ m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AFS);
+ m_aStylesAssocDbch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AFS);
+ m_aStylesAssocRtlch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
+ break;
+ }
+}
+
+void RtfAttributeOutput::CharKerning(const SvxKerningItem& rKerning)
+{
+ // in quarter points then in twips
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPND);
+ m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPNDTW);
+ m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue()));
+}
+
+void RtfAttributeOutput::CharLanguage(const SvxLanguageItem& rLanguage)
+{
+ switch (rLanguage.Which())
+ {
+ case RES_CHRATR_LANGUAGE:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LANG);
+ m_aStyles.append(
+ static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
+ m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANG);
+ m_aStylesAssocLtrch.append(
+ static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_LANGFE);
+ m_aStylesAssocDbch.append(
+ static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
+ m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANGFE);
+ m_aStylesAssocLtrch.append(
+ static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_ALANG);
+ m_aStylesAssocRtlch.append(
+ static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
+ break;
+ }
+}
+
+void RtfAttributeOutput::CharPosture(const SvxPostureItem& rPosture)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_I);
+ if (rPosture.GetPosture() == ITALIC_NONE)
+ m_aStyles.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharShadow(const SvxShadowedItem& rShadow)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SHAD);
+ if (!rShadow.GetValue())
+ m_aStyles.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharUnderline(const SvxUnderlineItem& rUnderline)
+{
+ const char* pStr = nullptr;
+ const SfxPoolItem* pItem = m_rExport.HasItem(RES_CHRATR_WORDLINEMODE);
+ bool bWord = false;
+ // No StaticWhichCast(RES_CHRATR_WORDLINEMODE), this may be for a postit, where the which ids
+ // don't match.
+ if (pItem)
+ bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
+ switch (rUnderline.GetLineStyle())
+ {
+ case LINESTYLE_SINGLE:
+ pStr = bWord ? OOO_STRING_SVTOOLS_RTF_ULW : OOO_STRING_SVTOOLS_RTF_UL;
+ break;
+ case LINESTYLE_DOUBLE:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULDB;
+ break;
+ case LINESTYLE_NONE:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULNONE;
+ break;
+ case LINESTYLE_DOTTED:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULD;
+ break;
+ case LINESTYLE_DASH:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULDASH;
+ break;
+ case LINESTYLE_DASHDOT:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULDASHD;
+ break;
+ case LINESTYLE_DASHDOTDOT:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULDASHDD;
+ break;
+ case LINESTYLE_BOLD:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULTH;
+ break;
+ case LINESTYLE_WAVE:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULWAVE;
+ break;
+ case LINESTYLE_BOLDDOTTED:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULTHD;
+ break;
+ case LINESTYLE_BOLDDASH:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASH;
+ break;
+ case LINESTYLE_LONGDASH:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULLDASH;
+ break;
+ case LINESTYLE_BOLDLONGDASH:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULTHLDASH;
+ break;
+ case LINESTYLE_BOLDDASHDOT:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHD;
+ break;
+ case LINESTYLE_BOLDDASHDOTDOT:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHDD;
+ break;
+ case LINESTYLE_BOLDWAVE:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULHWAVE;
+ break;
+ case LINESTYLE_DOUBLEWAVE:
+ pStr = OOO_STRING_SVTOOLS_RTF_ULULDBWAVE;
+ break;
+ default:
+ break;
+ }
+
+ if (pStr)
+ {
+ m_aStyles.append(pStr);
+ // NEEDSWORK looks like here rUnderline.GetColor() is always black,
+ // even if the color in the odt is for example green...
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ULC);
+ m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor())));
+ }
+}
+
+void RtfAttributeOutput::CharWeight(const SvxWeightItem& rWeight)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_B);
+ if (rWeight.GetWeight() != WEIGHT_BOLD)
+ m_aStyles.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharAutoKern(const SvxAutoKernItem& rAutoKern)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KERNING);
+ m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
+}
+
+void RtfAttributeOutput::CharAnimatedText(const SvxBlinkItem& rBlink)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ANIMTEXT);
+ m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0));
+}
+
+void RtfAttributeOutput::CharBackground(const SvxBrushItem& rBrush)
+{
+ if (!rBrush.GetColor().IsTransparent())
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHCBPAT);
+ m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
+ }
+}
+
+void RtfAttributeOutput::CharFontCJK(const SvxFontItem& rFont)
+{
+ // Insert \dbch in MoveCharacterProperties
+ m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AF);
+ m_aStylesAssocDbch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
+}
+
+void RtfAttributeOutput::CharFontSizeCJK(const SvxFontHeightItem& rFontSize)
+{
+ CharFontSize(rFontSize);
+}
+
+void RtfAttributeOutput::CharLanguageCJK(const SvxLanguageItem& rLanguageItem)
+{
+ CharLanguage(rLanguageItem);
+}
+
+void RtfAttributeOutput::CharPostureCJK(const SvxPostureItem& rPosture)
+{
+ m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_I);
+ if (rPosture.GetPosture() == ITALIC_NONE)
+ m_aStylesAssocDbch.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharWeightCJK(const SvxWeightItem& rWeight)
+{
+ m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AB);
+ if (rWeight.GetWeight() != WEIGHT_BOLD)
+ m_aStylesAssocDbch.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharFontCTL(const SvxFontItem& rFont)
+{
+ // Insert \rtlch in MoveCharacterProperties
+ m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AF);
+ m_aStylesAssocRtlch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
+}
+
+void RtfAttributeOutput::CharFontSizeCTL(const SvxFontHeightItem& rFontSize)
+{
+ CharFontSize(rFontSize);
+}
+
+void RtfAttributeOutput::CharLanguageCTL(const SvxLanguageItem& rLanguageItem)
+{
+ CharLanguage(rLanguageItem);
+}
+
+void RtfAttributeOutput::CharPostureCTL(const SvxPostureItem& rPosture)
+{
+ m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AI);
+ if (rPosture.GetPosture() == ITALIC_NONE)
+ m_aStylesAssocRtlch.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharWeightCTL(const SvxWeightItem& rWeight)
+{
+ m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AB);
+ if (rWeight.GetWeight() != WEIGHT_BOLD)
+ m_aStylesAssocRtlch.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharBidiRTL(const SfxPoolItem& /*rItem*/) {}
+
+void RtfAttributeOutput::CharIdctHint(const SfxPoolItem& /*rItem*/) {}
+
+void RtfAttributeOutput::CharRotate(const SvxCharRotateItem& rRotate)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HORZVERT);
+ m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0));
+}
+
+void RtfAttributeOutput::CharEmphasisMark(const SvxEmphasisMarkItem& rEmphasisMark)
+{
+ FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
+ if (v == FontEmphasisMark::NONE)
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCNONE);
+ else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCDOT);
+ else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCOMMA);
+ else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCIRCLE);
+ else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCUNDERDOT);
+}
+
+void RtfAttributeOutput::CharTwoLines(const SvxTwoLinesItem& rTwoLines)
+{
+ if (!rTwoLines.GetValue())
+ return;
+
+ sal_Unicode cStart = rTwoLines.GetStartBracket();
+ sal_Unicode cEnd = rTwoLines.GetEndBracket();
+
+ sal_uInt16 nType;
+ if (!cStart && !cEnd)
+ nType = 0;
+ else if ('{' == cStart || '}' == cEnd)
+ nType = 4;
+ else if ('<' == cStart || '>' == cEnd)
+ nType = 3;
+ else if ('[' == cStart || ']' == cEnd)
+ nType = 2;
+ else // all other kind of brackets
+ nType = 1;
+
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TWOINONE);
+ m_aStyles.append(static_cast<sal_Int32>(nType));
+}
+
+void RtfAttributeOutput::CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHARSCALEX);
+ m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue()));
+}
+
+void RtfAttributeOutput::CharRelief(const SvxCharReliefItem& rRelief)
+{
+ const char* pStr;
+ switch (rRelief.GetValue())
+ {
+ case FontRelief::Embossed:
+ pStr = OOO_STRING_SVTOOLS_RTF_EMBO;
+ break;
+ case FontRelief::Engraved:
+ pStr = OOO_STRING_SVTOOLS_RTF_IMPR;
+ break;
+ default:
+ pStr = nullptr;
+ break;
+ }
+
+ if (pStr)
+ m_aStyles.append(pStr);
+}
+
+void RtfAttributeOutput::CharHidden(const SvxCharHiddenItem& rHidden)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_V);
+ if (!rHidden.GetValue())
+ m_aStyles.append(sal_Int32(0));
+}
+
+void RtfAttributeOutput::CharBorder(const editeng::SvxBorderLine* pAllBorder,
+ const sal_uInt16 nDist, const bool bShadow)
+{
+ m_aStyles.append(
+ OutBorderLine(m_rExport, pAllBorder, OOO_STRING_SVTOOLS_RTF_CHBRDR, nDist,
+ bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE));
+}
+
+void RtfAttributeOutput::CharHighlight(const SvxBrushItem& rBrush)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HIGHLIGHT);
+ m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor())));
+}
+
+void RtfAttributeOutput::TextINetFormat(const SwFormatINetFormat& rURL)
+{
+ if (rURL.GetValue().isEmpty())
+ return;
+
+ const SwCharFormat* pFormat;
+ const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat();
+
+ if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat()))
+ {
+ sal_uInt16 nStyle = m_rExport.GetId(pFormat);
+ OString* pString = m_rExport.GetStyle(nStyle);
+ if (pString)
+ m_aStyles.append(*pString);
+ }
+}
+
+void RtfAttributeOutput::TextCharFormat(const SwFormatCharFormat& rCharFormat)
+{
+ sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat());
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CS);
+ m_aStyles.append(static_cast<sal_Int32>(nStyle));
+ OString* pString = m_rExport.GetStyle(nStyle);
+ if (pString)
+ m_aStyles.append(*pString);
+}
+
+void RtfAttributeOutput::WriteTextFootnoteNumStr(const SwFormatFootnote& rFootnote)
+{
+ if (rFootnote.GetNumStr().isEmpty())
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_CHFTN);
+ else
+ m_aRun->append(
+ msfilter::rtfutil::OutString(rFootnote.GetNumStr(), m_rExport.GetCurrentEncoding()));
+}
+
+void RtfAttributeOutput::TextFootnote_Impl(const SwFormatFootnote& rFootnote)
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_SUPER " ");
+ EndRunProperties(nullptr);
+ m_aRun->append(' ');
+ WriteTextFootnoteNumStr(rFootnote);
+ m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FOOTNOTE);
+ if (rFootnote.IsEndNote() || m_rExport.m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER)
+ m_aRun->append(OOO_STRING_SVTOOLS_RTF_FTNALT);
+ m_aRun->append(' ');
+ WriteTextFootnoteNumStr(rFootnote);
+
+ /*
+ * The footnote contains a whole paragraph, so we have to:
+ * 1) Reset, then later restore the contents of our run buffer and run state.
+ * 2) Buffer the output of the whole paragraph, as we do so for section headers already.
+ */
+ const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode();
+ RtfStringBuffer aRun = m_aRun;
+ m_aRun.clear();
+ bool bInRunOrig = m_bInRun;
+ m_bInRun = false;
+ bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
+ m_bSingleEmptyRun = false;
+ m_bBufferSectionHeaders = true;
+ m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(),
+ !rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN);
+ m_bBufferSectionHeaders = false;
+ m_bInRun = bInRunOrig;
+ m_bSingleEmptyRun = bSingleEmptyRunOrig;
+ m_aRun = aRun;
+ m_aRun->append(m_aSectionHeaders);
+ m_aSectionHeaders.setLength(0);
+
+ m_aRun->append("}");
+ m_aRun->append("}");
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SL);
+ m_aStyles.append(static_cast<sal_Int32>(nSpace));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SLMULT);
+ m_aStyles.append(static_cast<sal_Int32>(nMulti));
+}
+
+void RtfAttributeOutput::ParaAdjust(const SvxAdjustItem& rAdjust)
+{
+ switch (rAdjust.GetAdjust())
+ {
+ case SvxAdjust::Left:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QL);
+ break;
+ case SvxAdjust::Right:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QR);
+ break;
+ case SvxAdjust::BlockLine:
+ case SvxAdjust::Block:
+ if (rAdjust.GetLastBlock() == SvxAdjust::Block)
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QD);
+ else
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QJ);
+ break;
+ case SvxAdjust::Center:
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QC);
+ break;
+ default:
+ break;
+ }
+}
+
+void RtfAttributeOutput::ParaSplit(const SvxFormatSplitItem& rSplit)
+{
+ if (!rSplit.GetValue())
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEP);
+}
+
+void RtfAttributeOutput::ParaWidows(const SvxWidowsItem& rWidows)
+{
+ if (rWidows.GetValue())
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_WIDCTLPAR);
+ else
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOWIDCTLPAR);
+}
+
+void RtfAttributeOutput::ParaTabStop(const SvxTabStopItem& rTabStop)
+{
+ tools::Long nOffset = m_rExport.GetParaTabStopOffset();
+
+ for (sal_uInt16 n = 0; n < rTabStop.Count(); n++)
+ {
+ const SvxTabStop& rTS = rTabStop[n];
+ if (SvxTabAdjust::Default != rTS.GetAdjustment())
+ {
+ const char* pFill = nullptr;
+ switch (rTS.GetFill())
+ {
+ case cDfltFillChar:
+ break;
+
+ case '.':
+ pFill = OOO_STRING_SVTOOLS_RTF_TLDOT;
+ break;
+ case '_':
+ pFill = OOO_STRING_SVTOOLS_RTF_TLUL;
+ break;
+ case '-':
+ pFill = OOO_STRING_SVTOOLS_RTF_TLTH;
+ break;
+ case '=':
+ pFill = OOO_STRING_SVTOOLS_RTF_TLEQ;
+ break;
+ default:
+ break;
+ }
+ if (pFill)
+ m_aStyles.append(pFill);
+
+ const char* pAdjStr = nullptr;
+ switch (rTS.GetAdjustment())
+ {
+ case SvxTabAdjust::Right:
+ pAdjStr = OOO_STRING_SVTOOLS_RTF_TQR;
+ break;
+ case SvxTabAdjust::Decimal:
+ pAdjStr = OOO_STRING_SVTOOLS_RTF_TQDEC;
+ break;
+ case SvxTabAdjust::Center:
+ pAdjStr = OOO_STRING_SVTOOLS_RTF_TQC;
+ break;
+ default:
+ break;
+ }
+ if (pAdjStr)
+ m_aStyles.append(pAdjStr);
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TX);
+ m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset));
+ }
+ else
+ {
+ m_aTabStop.append(OOO_STRING_SVTOOLS_RTF_DEFTAB);
+ m_aTabStop.append(rTabStop[0].GetTabPos());
+ }
+ }
+}
+
+void RtfAttributeOutput::ParaHyphenZone(const SvxHyphenZoneItem& rHyphenZone)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HYPHPAR);
+ m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen()));
+}
+
+void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl,
+ sal_Int32 nNumId)
+{
+ if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd)
+ return;
+
+ const SwNumRule* pRule = pTextNd->GetNumRule();
+
+ if (!pRule || !pTextNd->IsInList())
+ return;
+
+ SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL,
+ "sw.rtf", "text node does not have valid list level");
+
+ const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl);
+ if (!pFormat)
+ pFormat = &pRule->Get(nLvl);
+
+ const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet();
+
+ m_aStyles.append('{');
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LISTTEXT);
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PARD);
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PLAIN);
+ m_aStyles.append(' ');
+
+ SvxFirstLineIndentItem firstLine(rNdSet.Get(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(rNdSet.Get(RES_MARGIN_TEXTLEFT));
+ leftMargin.SetTextLeft(leftMargin.GetTextLeft() + pFormat->GetIndentAt());
+ firstLine.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: overflow
+
+ sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
+ OString* pString = m_rExport.GetStyle(nStyle);
+ if (pString)
+ m_aStyles.append(*pString);
+
+ {
+ OUString sText;
+ if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType()
+ || SVX_NUM_BITMAP == pFormat->GetNumberingType())
+ {
+ sal_UCS4 cBullet = pFormat->GetBulletChar();
+ sText = OUString(&cBullet, 1);
+ }
+ else
+ sText = pTextNd->GetNumString();
+
+ if (!sText.isEmpty())
+ {
+ m_aStyles.append(' ');
+ m_aStyles.append(msfilter::rtfutil::OutString(sText, m_rExport.GetDefaultEncoding()));
+ }
+
+ if (OUTLINE_RULE != pRule->GetRuleType())
+ {
+ if (!sText.isEmpty())
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB);
+ m_aStyles.append('}');
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL);
+ if (nLvl > 8) // RTF knows only 9 levels
+ {
+ m_aStyles.append(sal_Int32(8));
+ m_aStyles.append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SOUTLVL);
+ m_aStyles.append(nLvl);
+ m_aStyles.append('}');
+ }
+ else
+ m_aStyles.append(nLvl);
+ }
+ else
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB "}");
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LS);
+ m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1);
+ m_aStyles.append(' ');
+ }
+ FormatFirstLineIndent(firstLine);
+ FormatTextLeftMargin(leftMargin);
+}
+
+void RtfAttributeOutput::ParaScriptSpace(const SfxBoolItem& rScriptSpace)
+{
+ if (!rScriptSpace.GetValue())
+ return;
+
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ASPALPHA);
+}
+
+void RtfAttributeOutput::ParaHangingPunctuation(const SfxBoolItem& /*rItem*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::ParaForbiddenRules(const SfxBoolItem& /*rItem*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::ParaVerticalAlign(const SvxParaVertAlignItem& rAlign)
+{
+ const char* pStr;
+ switch (rAlign.GetValue())
+ {
+ case SvxParaVertAlignItem::Align::Top:
+ pStr = OOO_STRING_SVTOOLS_RTF_FAHANG;
+ break;
+ case SvxParaVertAlignItem::Align::Bottom:
+ pStr = OOO_STRING_SVTOOLS_RTF_FAVAR;
+ break;
+ case SvxParaVertAlignItem::Align::Center:
+ pStr = OOO_STRING_SVTOOLS_RTF_FACENTER;
+ break;
+ case SvxParaVertAlignItem::Align::Baseline:
+ pStr = OOO_STRING_SVTOOLS_RTF_FAROMAN;
+ break;
+
+ default:
+ pStr = OOO_STRING_SVTOOLS_RTF_FAAUTO;
+ break;
+ }
+ m_aStyles.append(pStr);
+}
+
+void RtfAttributeOutput::ParaSnapToGrid(const SvxParaGridItem& /*rGrid*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::FormatFrameSize(const SwFormatFrameSize& rSize)
+{
+ if (m_rExport.m_bOutPageDescs)
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGWSXN);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth()));
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGHSXN);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight()));
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+ }
+}
+
+void RtfAttributeOutput::FormatPaperBin(const SvxPaperBinItem& /*rItem*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
+ m_aStyles.append(static_cast<sal_Int32>(rFirstLine.GetTextFirstLineOffset()));
+}
+
+void RtfAttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin)
+{
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI);
+ m_aStyles.append(static_cast<sal_Int32>(rTextLeftMargin.GetTextLeft()));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN);
+ m_aStyles.append(static_cast<sal_Int32>(rTextLeftMargin.GetTextLeft()));
+}
+
+void RtfAttributeOutput::FormatRightMargin(SvxRightMarginItem const& rRightMargin)
+{
+// (paragraph case, this will be an else branch once others are converted)
+#if 0
+ else
+#endif
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI);
+ m_aStyles.append(static_cast<sal_Int32>(rRightMargin.GetRight()));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN);
+ m_aStyles.append(static_cast<sal_Int32>(rRightMargin.GetRight()));
+ }
+}
+
+void RtfAttributeOutput::FormatLRSpace(const SvxLRSpaceItem& rLRSpace)
+{
+ if (!m_rExport.m_bOutFlyFrameAttrs)
+ {
+ if (m_rExport.m_bOutPageDescs)
+ {
+ m_aPageMargins.nLeft = 0;
+ m_aPageMargins.nRight = 0;
+
+ if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
+ {
+ m_aPageMargins.nLeft
+ = pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/ true);
+ m_aPageMargins.nRight
+ = pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/ true);
+ }
+
+ m_aPageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
+ m_aPageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
+
+ if (rLRSpace.GetLeft())
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGLSXN);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
+ }
+ if (rLRSpace.GetRight())
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGRSXN);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
+ }
+ if (rLRSpace.GetGutterMargin())
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_GUTTER);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetGutterMargin()));
+ }
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+ }
+ else
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI);
+ m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI);
+ m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN);
+ m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN);
+ m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
+ m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextFirstLineOffset()));
+ }
+ }
+ else if (m_rExport.GetRTFFlySyntax())
+ {
+ // Wrap: top and bottom spacing, convert from twips to EMUs.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dxWrapDistLeft"_ostr,
+ OString::number(
+ o3tl::convert(rLRSpace.GetLeft(), o3tl::Length::twip, o3tl::Length::emu))));
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dxWrapDistRight"_ostr,
+ OString::number(
+ o3tl::convert(rLRSpace.GetRight(), o3tl::Length::twip, o3tl::Length::emu))));
+ }
+}
+
+void RtfAttributeOutput::FormatULSpace(const SvxULSpaceItem& rULSpace)
+{
+ if (!m_rExport.m_bOutFlyFrameAttrs)
+ {
+ if (m_rExport.m_bOutPageDescs)
+ {
+ OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible");
+ if (!m_rExport.GetCurItemSet())
+ return;
+
+ // If we export a follow page format, then our doc model has
+ // separate header/footer distances for the first page and the
+ // follow pages, but Word can have only a single distance. In case
+ // the two values differ, work with the value from the first page
+ // format to be in sync with the import.
+ sw::util::HdFtDistanceGlue aDistances(m_rExport.GetFirstPageItemSet()
+ ? *m_rExport.GetFirstPageItemSet()
+ : *m_rExport.GetCurItemSet());
+
+ if (aDistances.m_DyaTop)
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGTSXN);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
+ m_aPageMargins.nTop = aDistances.m_DyaTop;
+ }
+ if (aDistances.HasHeader())
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_HEADERY);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrTop));
+ }
+
+ if (aDistances.m_DyaBottom)
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGBSXN);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
+ m_aPageMargins.nBottom = aDistances.m_DyaBottom;
+ }
+ if (aDistances.HasFooter())
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_FOOTERY);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrBottom));
+ }
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+ }
+ else
+ {
+ // Spacing before.
+ if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper())
+ m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "1");
+ else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1)
+ {
+ m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "0");
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB);
+ m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
+ }
+ else
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB);
+ m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
+ }
+ m_bParaBeforeAutoSpacing = false;
+
+ // Spacing after.
+ if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower())
+ m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "1");
+ else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1)
+ {
+ m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "0");
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA);
+ m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
+ }
+ else
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA);
+ m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
+ }
+ m_bParaAfterAutoSpacing = false;
+
+ // Contextual spacing.
+ if (rULSpace.GetContext())
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CONTEXTUALSPACE);
+ }
+ }
+ else if (m_rExport.GetRTFFlySyntax())
+ {
+ // Wrap: top and bottom spacing, convert from twips to EMUs.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dyWrapDistTop"_ostr,
+ OString::number(
+ o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip, o3tl::Length::emu))));
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dyWrapDistBottom"_ostr,
+ OString::number(
+ o3tl::convert(rULSpace.GetLower(), o3tl::Length::twip, o3tl::Length::emu))));
+ }
+}
+
+void RtfAttributeOutput::FormatSurround(const SwFormatSurround& rSurround)
+{
+ if (m_rExport.m_bOutFlyFrameAttrs && !m_rExport.GetRTFFlySyntax())
+ {
+ css::text::WrapTextMode eSurround = rSurround.GetSurround();
+ bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround;
+ if (bGold)
+ eSurround = css::text::WrapTextMode_PARALLEL;
+ RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround));
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYMAINCNT);
+ m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue()));
+ }
+ else if (m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax())
+ {
+ // See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings.
+ sal_Int32 nWr = -1;
+ std::optional<sal_Int32> oWrk;
+ switch (rSurround.GetValue())
+ {
+ case css::text::WrapTextMode_NONE:
+ nWr = 1; // top and bottom
+ break;
+ case css::text::WrapTextMode_THROUGH:
+ nWr = 3; // none
+ break;
+ case css::text::WrapTextMode_PARALLEL:
+ nWr = 2; // around
+ oWrk = 0; // both sides
+ break;
+ case css::text::WrapTextMode_DYNAMIC:
+ default:
+ nWr = 2; // around
+ oWrk = 3; // largest
+ break;
+ }
+
+ if (rSurround.IsContour())
+ nWr = 4; // tight
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPWR);
+ m_rExport.Strm().WriteNumberAsString(nWr);
+ if (oWrk)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPWRK);
+ m_rExport.Strm().WriteNumberAsString(*oWrk);
+ }
+ }
+}
+
+void RtfAttributeOutput::FormatVertOrientation(const SwFormatVertOrient& rFlyVert)
+{
+ if (!(m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()))
+ return;
+
+ switch (rFlyVert.GetRelationOrient())
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posrelv"_ostr, OString::number(1)));
+ break;
+ default:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posrelv"_ostr, OString::number(2)));
+ m_rExport.Strm()
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBYPARA)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE);
+ break;
+ }
+
+ switch (rFlyVert.GetVertOrient())
+ {
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::LINE_TOP:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posv"_ostr, OString::number(1)));
+ break;
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posv"_ostr, OString::number(3)));
+ break;
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posv"_ostr, OString::number(2)));
+ break;
+ default:
+ break;
+ }
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPTOP);
+ m_rExport.Strm().WriteNumberAsString(rFlyVert.GetPos());
+ if (m_pFlyFrameSize)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM);
+ m_rExport.Strm().WriteNumberAsString(rFlyVert.GetPos() + m_pFlyFrameSize->Height());
+ }
+}
+
+void RtfAttributeOutput::FormatHorizOrientation(const SwFormatHoriOrient& rFlyHori)
+{
+ if (!(m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()))
+ return;
+
+ switch (rFlyHori.GetRelationOrient())
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posrelh"_ostr, OString::number(1)));
+ break;
+ default:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posrelh"_ostr, OString::number(2)));
+ m_rExport.Strm()
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBXCOLUMN)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE);
+ break;
+ }
+
+ switch (rFlyHori.GetHoriOrient())
+ {
+ case text::HoriOrientation::LEFT:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posh"_ostr, OString::number(1)));
+ break;
+ case text::HoriOrientation::CENTER:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posh"_ostr, OString::number(2)));
+ break;
+ case text::HoriOrientation::RIGHT:
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("posh"_ostr, OString::number(3)));
+ break;
+ default:
+ break;
+ }
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPLEFT);
+ m_rExport.Strm().WriteNumberAsString(rFlyHori.GetPos());
+ if (m_pFlyFrameSize)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPRIGHT);
+ m_rExport.Strm().WriteNumberAsString(rFlyHori.GetPos() + m_pFlyFrameSize->Width());
+ }
+}
+
+void RtfAttributeOutput::FormatAnchor(const SwFormatAnchor& rAnchor)
+{
+ if (m_rExport.GetRTFFlySyntax())
+ return;
+
+ RndStdIds eId = rAnchor.GetAnchorId();
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYANCHOR);
+ m_aRunText->append(static_cast<sal_Int32>(eId));
+ switch (eId)
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYPAGE);
+ m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum()));
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AS_CHAR:
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYCNTNT);
+ break;
+ default:
+ break;
+ }
+}
+
+void RtfAttributeOutput::FormatBackground(const SvxBrushItem& rBrush)
+{
+ if (m_rExport.GetRTFFlySyntax())
+ {
+ const Color& rColor = rBrush.GetColor();
+ // We in fact need RGB to BGR, but the transformation is symmetric.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "fillColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
+ }
+ else if (!rBrush.GetColor().IsTransparent())
+ {
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CBPAT);
+ m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
+ }
+}
+
+void RtfAttributeOutput::FormatFillStyle(const XFillStyleItem& rFillStyle)
+{
+ m_oFillStyle = rFillStyle.GetValue();
+}
+
+void RtfAttributeOutput::FormatFillGradient(const XFillGradientItem& rFillGradient)
+{
+ if (*m_oFillStyle != drawing::FillStyle_GRADIENT)
+ return;
+
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "fillType"_ostr, OString::number(7))); // Shade using the fillAngle
+
+ const basegfx::BGradient& rGradient(rFillGradient.GetGradientValue());
+ const basegfx::BColorStops& rColorStops(rGradient.GetColorStops());
+
+ // MCGR: It would be best to export the full MCGR definition here
+ // with all ColorStops in rColorStops, but rtf does not support this.
+ // Best thing to do and to stay compatible is to export front/back
+ // colors as start/end and - when more than two ColorStops are defined -
+ // guess that GradientStyle_AXIAL is used and thus create a "fillFocus"
+ // entry
+ // NOTE: I also found that loading file from testTextframeGradient
+ // "textframe-gradient.rtf" and save-as *inverts* the gradient, so I
+ // exchanged here fillColor/fillBackColor to get the correct order
+ const Color aStartColor(rColorStops.front().getStopColor());
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "fillColor"_ostr, OString::number(wwUtility::RGBToBGR(aStartColor))));
+
+ if (rColorStops.size() < 3)
+ {
+ // two-color version, use back as 2nd color
+ const Color aEndColor(rColorStops.back().getStopColor());
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "fillBackColor"_ostr, OString::number(wwUtility::RGBToBGR(aEndColor))));
+ }
+ else
+ {
+ // assume what was formally GradientStyle_AXIAL, see above and also refer to
+ // FillModel::pushToPropMap 'fFocus' value and usage.
+ // The 2nd color is the in-between color, use it
+ const Color aEndColor(rColorStops[1].getStopColor());
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "fillBackColor"_ostr, OString::number(wwUtility::RGBToBGR(aEndColor))));
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("fillFocus"_ostr, OString::number(50)));
+ }
+}
+
+void RtfAttributeOutput::FormatBox(const SvxBoxItem& rBox)
+{
+ static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
+ static const char* aBorderNames[]
+ = { OOO_STRING_SVTOOLS_RTF_BRDRT, OOO_STRING_SVTOOLS_RTF_BRDRL,
+ OOO_STRING_SVTOOLS_RTF_BRDRB, OOO_STRING_SVTOOLS_RTF_BRDRR };
+
+ sal_uInt16 const nDist = rBox.GetSmallestDistance();
+
+ if (m_rExport.GetRTFFlySyntax())
+ {
+ // Borders: spacing to contents, convert from twips to EMUs.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dxTextLeft"_ostr,
+ OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT),
+ o3tl::Length::twip, o3tl::Length::emu))));
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dyTextTop"_ostr,
+ OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP), o3tl::Length::twip,
+ o3tl::Length::emu))));
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dxTextRight"_ostr,
+ OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT),
+ o3tl::Length::twip, o3tl::Length::emu))));
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "dyTextBottom"_ostr,
+ OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::BOTTOM),
+ o3tl::Length::twip, o3tl::Length::emu))));
+
+ const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT);
+ const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT);
+ const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP);
+ const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM);
+
+ if (!pLeft && !pRight && !pBottom && !pTop)
+ {
+ // fLine has default 'true', so need to write it out in case of no border.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine"_ostr, "0"_ostr));
+ return;
+ }
+
+ // RTF has the flags fTopLine, fBottomLine, fLeftLine and fRightLine to disable single border
+ // lines. But Word cannot disable single border lines. So we do not use them. In case of
+ // single border lines it is better to draw all four borders than drawing none. So we look
+ // whether a border line exists, which is effectively drawn.
+ const editeng::SvxBorderLine* pBorder = nullptr;
+ if (pTop && pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
+ pBorder = pTop;
+ else if (pBottom && pBottom->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
+ pBorder = pBottom;
+ else if (pLeft && pLeft->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
+ pBorder = pLeft;
+ else if (pRight && pRight->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
+ pBorder = pRight;
+
+ if (!pBorder)
+ {
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine"_ostr, "0"_ostr));
+ return;
+ }
+
+ const Color& rColor = pBorder->GetColor();
+ // We in fact need RGB to BGR, but the transformation is symmetric.
+ m_aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "lineColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
+
+ double const fConverted(
+ editeng::ConvertBorderWidthToWord(pBorder->GetBorderLineStyle(), pBorder->GetWidth()));
+ sal_Int32 nWidth = o3tl::convert(fConverted, o3tl::Length::twip, o3tl::Length::emu);
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("lineWidth"_ostr, OString::number(nWidth)));
+
+ return;
+ }
+
+ if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight()
+ && *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft()
+ && *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP)
+ && nDist == rBox.GetDistance(SvxBoxItemLine::LEFT)
+ && nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM)
+ && nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT))
+ m_aSectionBreaks.append(
+ OutBorderLine(m_rExport, rBox.GetTop(), OOO_STRING_SVTOOLS_RTF_BOX, nDist));
+ else
+ {
+ SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE;
+ if (const SvxShadowItem* pItem = GetExport().HasItem(RES_SHADOW))
+ eShadowLocation = pItem->GetLocation();
+
+ const SvxBoxItemLine* pBrd = aBorders;
+ const char** pBrdNms = aBorderNames;
+ for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
+ {
+ editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
+ m_aSectionBreaks.append(
+ OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
+ }
+ }
+
+ if (!m_bBufferSectionBreaks)
+ {
+ m_aStyles.append(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+}
+
+void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
+ SwTwips nPageSize)
+{
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLS);
+ m_rExport.Strm().WriteNumberAsString(nCols);
+
+ if (rCol.GetLineAdj() != COLADJ_NONE)
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEBETCOL);
+
+ if (bEven)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLSX);
+ m_rExport.Strm().WriteNumberAsString(rCol.GetGutterWidth(true));
+ }
+ else
+ {
+ const SwColumns& rColumns = rCol.GetColumns();
+ for (sal_uInt16 n = 0; n < nCols;)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLNO);
+ m_rExport.Strm().WriteNumberAsString(n + 1);
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLW);
+ m_rExport.Strm().WriteNumberAsString(rCol.CalcPrtColWidth(n, nPageSize));
+
+ if (++n != nCols)
+ {
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLSR);
+ m_rExport.Strm().WriteNumberAsString(rColumns[n - 1].GetRight()
+ + rColumns[n].GetLeft());
+ }
+ }
+ }
+}
+
+void RtfAttributeOutput::FormatKeep(const SvxFormatKeepItem& rItem)
+{
+ if (rItem.GetValue())
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEPN);
+}
+
+void RtfAttributeOutput::FormatTextGrid(const SwTextGridItem& /*rGrid*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::FormatLineNumbering(const SwFormatLineNumber& rNumbering)
+{
+ if (!rNumbering.IsCount())
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOLINE);
+}
+
+void RtfAttributeOutput::FormatFrameDirection(const SvxFrameDirectionItem& rDirection)
+{
+ SvxFrameDirection nDir = rDirection.GetValue();
+ if (nDir == SvxFrameDirection::Environment)
+ nDir = GetExport().GetDefaultFrameDirection();
+
+ if (m_rExport.m_bOutPageDescs)
+ {
+ if (nDir == SvxFrameDirection::Vertical_RL_TB)
+ {
+ m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_STEXTFLOW);
+ m_aSectionBreaks.append(static_cast<sal_Int32>(1));
+ if (!m_bBufferSectionBreaks)
+ {
+ m_rExport.Strm().WriteOString(m_aSectionBreaks);
+ m_aSectionBreaks.setLength(0);
+ }
+ }
+ return;
+ }
+
+ if (m_rExport.GetRTFFlySyntax())
+ {
+ if (nDir == SvxFrameDirection::Vertical_RL_TB)
+ {
+ // Top to bottom non-ASCII font
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("txflTextFlow"_ostr, "3"_ostr));
+ }
+ else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
+ {
+ // Bottom to top non-ASCII font
+ m_aFlyProperties.push_back(
+ std::make_pair<OString, OString>("txflTextFlow"_ostr, "2"_ostr));
+ }
+ return;
+ }
+
+ if (nDir == SvxFrameDirection::Horizontal_RL_TB)
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RTLPAR);
+ else
+ m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LTRPAR);
+}
+
+void RtfAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
+{
+ const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
+ for (const auto& rValue : rMap)
+ {
+ if (rValue.first == "ParaTopMarginBeforeAutoSpacing")
+ {
+ m_bParaBeforeAutoSpacing = true;
+ rValue.second >>= m_nParaBeforeSpacing;
+ m_nParaBeforeSpacing = o3tl::toTwips(m_nParaBeforeSpacing, o3tl::Length::mm100);
+ }
+ else if (rValue.first == "ParaBottomMarginAfterAutoSpacing")
+ {
+ m_bParaAfterAutoSpacing = true;
+ rValue.second >>= m_nParaAfterSpacing;
+ m_nParaAfterSpacing = o3tl::toTwips(m_nParaAfterSpacing, o3tl::Length::mm100);
+ }
+ }
+}
+
+void RtfAttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/) {}
+
+void RtfAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/) {}
+
+void RtfAttributeOutput::WriteExpand(const SwField* pField)
+{
+ OUString sCmd; // for optional Parameters
+ switch (pField->GetTyp()->Which())
+ {
+ //#i119803# Export user field for RTF filter
+ case SwFieldIds::User:
+ sCmd = pField->GetTyp()->GetName();
+ m_rExport.OutputField(pField, ww::eNONE, sCmd);
+ break;
+ default:
+ m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd);
+ break;
+ }
+}
+
+void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::HiddenField(const SwField& /*rField*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/,
+ const OUString& /*rCmd*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfAttributeOutput::PostitField(const SwField* pField)
+{
+ const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField);
+
+ OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8);
+ auto it = m_rOpenedAnnotationMarksIds.find(aName);
+ if (it != m_rOpenedAnnotationMarksIds.end())
+ {
+ // In case this field is inside annotation marks, we want to write the
+ // annotation itself after the annotation mark is closed, not here.
+ m_aPostitFields[it->second] = &rPField;
+ return;
+ }
+
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNID " ");
+ m_aRunText->append(OUStringToOString(rPField.GetInitials(), m_rExport.GetCurrentEncoding()));
+ m_aRunText->append("}");
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNAUTHOR " ");
+ m_aRunText->append(OUStringToOString(rPField.GetPar1(), m_rExport.GetCurrentEncoding()));
+ m_aRunText->append("}");
+ m_aRunText->append(OOO_STRING_SVTOOLS_RTF_CHATN);
+
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ANNOTATION);
+
+ if (m_nCurrentAnnotationMarkId != -1)
+ {
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNREF " ");
+ m_aRunText->append(m_nCurrentAnnotationMarkId);
+ m_aRunText->append('}');
+ }
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNDATE " ");
+ m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime())));
+ m_aRunText->append('}');
+ if (const OutlinerParaObject* pObject = rPField.GetTextObject())
+ m_rExport.SdrExporter().WriteOutliner(*pObject, TXT_ATN);
+ m_aRunText->append('}');
+}
+
+bool RtfAttributeOutput::DropdownField(const SwField* /*pField*/)
+{
+ // this is handled in OutputFlyFrame_Impl()
+ return true;
+}
+
+bool RtfAttributeOutput::PlaceholderField(const SwField* pField)
+{
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
+ " MACROBUTTON None ");
+ RunText(pField->GetPar1());
+ m_aRunText->append("}}");
+ return false; // do not expand
+}
+
+RtfAttributeOutput::RtfAttributeOutput(RtfExport& rExport)
+ : AttributeOutputBase("") // ConvertURL isn't used now in RTF output
+ , m_rExport(rExport)
+ , m_pPrevPageDesc(nullptr)
+ , m_nStyleId(0)
+ , m_nListId(0)
+ , m_bIsRTL(false)
+ , m_nScript(i18n::ScriptType::LATIN)
+ , m_bControlLtrRtl(false)
+ , m_nNextAnnotationMarkId(0)
+ , m_nCurrentAnnotationMarkId(-1)
+ , m_bTableCellOpen(false)
+ , m_nTableDepth(0)
+ , m_bTableAfterCell(false)
+ , m_nColBreakNeeded(false)
+ , m_bBufferSectionBreaks(false)
+ , m_bBufferSectionHeaders(false)
+ , m_bLastTable(true)
+ , m_bWroteCellInfo(false)
+ , m_bTableRowEnded(false)
+ , m_bIsBeforeFirstParagraph(true)
+ , m_bSingleEmptyRun(false)
+ , m_bInRun(false)
+ , m_bInRuby(false)
+ , m_pFlyFrameSize(nullptr)
+ , m_bParaBeforeAutoSpacing(false)
+ , m_nParaBeforeSpacing(0)
+ , m_bParaAfterAutoSpacing(false)
+ , m_nParaAfterSpacing(0)
+{
+}
+
+RtfAttributeOutput::~RtfAttributeOutput() = default;
+
+MSWordExportBase& RtfAttributeOutput::GetExport() { return m_rExport; }
+
+// These are used by wwFont::WriteRtf()
+
+/// Start the font.
+void RtfAttributeOutput::StartFont(std::u16string_view rFamilyName) const
+{
+ // write the font name hex-encoded, but without Unicode - Word at least
+ // cannot read *both* Unicode and fallback as written by OutString
+ m_rExport.Strm().WriteOString(
+ msfilter::rtfutil::OutString(rFamilyName, m_rExport.GetCurrentEncoding(), false));
+}
+
+/// End the font.
+void RtfAttributeOutput::EndFont() const
+{
+ m_rExport.Strm().WriteOString(";}");
+ m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding());
+}
+
+/// Alternate name for the font.
+void RtfAttributeOutput::FontAlternateName(std::u16string_view rName) const
+{
+ m_rExport.Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_FALT)
+ .WriteChar(' ');
+ // write the font name hex-encoded, but without Unicode - Word at least
+ // cannot read *both* Unicode and fallback as written by OutString
+ m_rExport.Strm()
+ .WriteOString(msfilter::rtfutil::OutString(rName, m_rExport.GetCurrentEncoding(), false))
+ .WriteChar('}');
+}
+
+/// Font charset.
+void RtfAttributeOutput::FontCharset(sal_uInt8 nCharSet) const
+{
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FCHARSET);
+ m_rExport.Strm().WriteNumberAsString(nCharSet);
+ m_rExport.Strm().WriteChar(' ');
+ m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet));
+}
+
+/// Font family.
+void RtfAttributeOutput::FontFamilyType(FontFamily eFamily, const wwFont& rFont) const
+{
+ m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_F);
+
+ const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL;
+ switch (eFamily)
+ {
+ case FAMILY_ROMAN:
+ pStr = OOO_STRING_SVTOOLS_RTF_FROMAN;
+ break;
+ case FAMILY_SWISS:
+ pStr = OOO_STRING_SVTOOLS_RTF_FSWISS;
+ break;
+ case FAMILY_MODERN:
+ pStr = OOO_STRING_SVTOOLS_RTF_FMODERN;
+ break;
+ case FAMILY_SCRIPT:
+ pStr = OOO_STRING_SVTOOLS_RTF_FSCRIPT;
+ break;
+ case FAMILY_DECORATIVE:
+ pStr = OOO_STRING_SVTOOLS_RTF_FDECOR;
+ break;
+ default:
+ break;
+ }
+ m_rExport.Strm().WriteNumberAsString(m_rExport.m_aFontHelper.GetId(rFont)).WriteOString(pStr);
+}
+
+/// Font pitch.
+void RtfAttributeOutput::FontPitchType(FontPitch ePitch) const
+{
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FPRQ);
+
+ sal_uInt16 nVal = 0;
+ switch (ePitch)
+ {
+ case PITCH_FIXED:
+ nVal = 1;
+ break;
+ case PITCH_VARIABLE:
+ nVal = 2;
+ break;
+ default:
+ break;
+ }
+ m_rExport.Strm().WriteNumberAsString(nVal);
+}
+
+static void lcl_AppendSP(OStringBuffer& rBuffer, std::string_view cName, std::u16string_view rValue,
+ const RtfExport& rExport)
+{
+ rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{"
+ rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn "
+ rBuffer.append(cName); //"PropName"
+ rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
+ // "}{ \sv "
+ rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding()));
+ rBuffer.append("}}");
+}
+
+static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig,
+ const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr,
+ const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize,
+ const RtfExport& rExport, SvStream* pStream = nullptr,
+ bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr)
+{
+ OStringBuffer aRet;
+ if (pBLIPType && nSize && pGraphicAry)
+ {
+ bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
+
+ aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT);
+
+ if (pFlyFrameFormat && bWritePicProp)
+ {
+ OUString sDescription = pFlyFrameFormat->GetObjDescription();
+ //write picture properties - wzDescription at first
+ //looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}"
+ aRet.append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_PICPROP); //"{\*\picprop
+ lcl_AppendSP(aRet, "wzDescription", sDescription, rExport);
+ OUString sName = pFlyFrameFormat->GetObjTitle();
+ lcl_AppendSP(aRet, "wzName", sName, rExport);
+
+ if (pAttrSet)
+ {
+ MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue();
+ if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
+ // Mirror on the vertical axis is a horizontal flip.
+ lcl_AppendSP(aRet, "fFlipH", u"1", rExport);
+ }
+
+ aRet.append("}"); //"}"
+ }
+
+ tools::Long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight());
+ tools::Long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom());
+ /* Graphic with a zero height or width, typically copied from webpages, caused crashes. */
+ if (!nXCroppedSize)
+ nXCroppedSize = 100;
+ if (!nYCroppedSize)
+ nYCroppedSize = 100;
+
+ //Given the original size and taking cropping into account
+ //first, how much has the original been scaled to get the
+ //final rendered size
+ aRet.append(
+ OOO_STRING_SVTOOLS_RTF_PICSCALEX
+ + OString::number(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize))
+ + OOO_STRING_SVTOOLS_RTF_PICSCALEY
+ + OString::number(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize))
+
+ + OOO_STRING_SVTOOLS_RTF_PICCROPL + OString::number(rCr.GetLeft())
+ + OOO_STRING_SVTOOLS_RTF_PICCROPR + OString::number(rCr.GetRight())
+ + OOO_STRING_SVTOOLS_RTF_PICCROPT + OString::number(rCr.GetTop())
+ + OOO_STRING_SVTOOLS_RTF_PICCROPB + OString::number(rCr.GetBottom())
+
+ + OOO_STRING_SVTOOLS_RTF_PICW + OString::number(static_cast<sal_Int32>(rMapped.Width()))
+ + OOO_STRING_SVTOOLS_RTF_PICH
+ + OString::number(static_cast<sal_Int32>(rMapped.Height()))
+
+ + OOO_STRING_SVTOOLS_RTF_PICWGOAL
+ + OString::number(static_cast<sal_Int32>(rOrig.Width()))
+ + OOO_STRING_SVTOOLS_RTF_PICHGOAL
+ + OString::number(static_cast<sal_Int32>(rOrig.Height()))
+
+ + pBLIPType);
+ if (bIsWMF)
+ {
+ aRet.append(sal_Int32(8));
+ msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
+ }
+ aRet.append(SAL_NEWLINE_STRING);
+ if (pStream)
+ {
+ pStream->WriteOString(aRet);
+ aRet.setLength(0);
+ }
+ if (pStream)
+ msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream);
+ else
+ aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
+ aRet.append('}');
+ if (pStream)
+ {
+ pStream->WriteOString(aRet);
+ aRet.setLength(0);
+ }
+ }
+ return aRet.makeStringAndClear();
+}
+
+void RtfAttributeOutput::FlyFrameOLEReplacement(const SwFlyFrameFormat* pFlyFrameFormat,
+ SwOLENode& rOLENode, const Size& rSize)
+{
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT);
+ Size aSize(rOLENode.GetTwipSize());
+ Size aRendered(aSize);
+ aRendered.setWidth(rSize.Width());
+ aRendered.setHeight(rSize.Height());
+ const Graphic* pGraphic = rOLENode.GetGraphic();
+ Size aMapped(pGraphic->GetPrefSize());
+ auto& rCr = rOLENode.GetAttr(RES_GRFATR_CROPGRF);
+ const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
+ const sal_uInt8* pGraphicAry = nullptr;
+ SvMemoryStream aStream;
+ if (GraphicConverter::Export(aStream, *pGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
+ SAL_WARN("sw.rtf", "failed to export the graphic");
+ sal_uInt64 nSize = aStream.TellEnd();
+ pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
+ m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
+ pGraphicAry, nSize, m_rExport));
+ m_aRunText->append("}"); // shppict
+ m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_NONSHPPICT);
+ pBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
+ SvMemoryStream aWmfStream;
+ if (GraphicConverter::Export(aWmfStream, *pGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
+ SAL_WARN("sw.rtf", "failed to export the graphic");
+ nSize = aWmfStream.TellEnd();
+ pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData());
+ m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
+ pGraphicAry, nSize, m_rExport));
+ m_aRunText->append("}"); // nonshppict
+}
+
+bool RtfAttributeOutput::FlyFrameOLEMath(const SwFlyFrameFormat* pFlyFrameFormat,
+ SwOLENode& rOLENode, const Size& rSize)
+{
+ uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef());
+ sal_Int64 nAspect = rOLENode.GetAspect();
+ svt::EmbeddedObjectRef aObjRef(xObj, nAspect);
+ SvGlobalName aObjName(aObjRef->getClassID());
+
+ if (!SotExchange::IsMath(aObjName))
+ return false;
+
+ m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATH " ");
+ uno::Reference<util::XCloseable> xClosable = xObj->getComponent();
+ if (!xClosable.is())
+ return false;
+ auto pBase = dynamic_cast<oox::FormulaImExportBase*>(xClosable.get());
+ SAL_WARN_IF(!pBase, "sw.rtf", "Math OLE object cannot write out RTF");
+ if (pBase)
+ {
+ OStringBuffer aBuf;
+ pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding());
+ m_aRunText->append(aBuf);
+ }
+
+ // Replacement graphic.
+ m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATHPICT " ");
+ FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
+ m_aRunText->append("}"); // mmathPict
+ m_aRunText->append("}"); // mmath
+
+ return true;
+}
+
+void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
+ const Size& rSize)
+{
+ if (FlyFrameOLEMath(pFlyFrameFormat, rOLENode, rSize))
+ return;
+
+ FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
+}
+
+void RtfAttributeOutput::FlyFrameGraphic(const SwFlyFrameFormat* pFlyFrameFormat,
+ const SwGrfNode* pGrfNode)
+{
+ SvMemoryStream aStream;
+ const sal_uInt8* pGraphicAry = nullptr;
+ sal_uInt32 nSize = 0;
+
+ const Graphic& rGraphic(pGrfNode->GetGrf());
+
+ // If there is no graphic there is not much point in parsing it
+ if (rGraphic.GetType() == GraphicType::NONE)
+ return;
+
+ ConvertDataFormat aConvertDestinationFormat = ConvertDataFormat::WMF;
+ const char* pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
+
+ GfxLink aGraphicLink;
+ const char* pBLIPType = nullptr;
+ if (rGraphic.IsGfxLink())
+ {
+ aGraphicLink = rGraphic.GetGfxLink();
+ nSize = aGraphicLink.GetDataSize();
+ pGraphicAry = aGraphicLink.GetData();
+ switch (aGraphicLink.GetType())
+ {
+ // #i15508# trying to add BMP type for better exports, need to check if this works
+ // checked, does not work. Also need to reset pGraphicAry to NULL to force conversion
+ // to PNG, else the BMP array will be used.
+ // It may work using direct DIB data, but that needs to be checked eventually
+ //
+ // #i15508# before GfxLinkType::NativeBmp was added the graphic data
+ // (to be hold in pGraphicAry) was not available; thus for now to stay
+ // compatible, keep it that way by assigning NULL value to pGraphicAry
+ case GfxLinkType::NativeBmp:
+ // pBLIPType = OOO_STRING_SVTOOLS_RTF_WBITMAP;
+ pGraphicAry = nullptr;
+ break;
+
+ case GfxLinkType::NativeJpg:
+ pBLIPType = OOO_STRING_SVTOOLS_RTF_JPEGBLIP;
+ break;
+ case GfxLinkType::NativePng:
+ pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
+ break;
+ case GfxLinkType::NativeWmf:
+ pBLIPType = aGraphicLink.IsEMF() ? OOO_STRING_SVTOOLS_RTF_EMFBLIP
+ : OOO_STRING_SVTOOLS_RTF_WMETAFILE;
+ break;
+ case GfxLinkType::NativeGif:
+ // GIF is not supported by RTF, but we override default conversion to WMF, PNG seems fits better here.
+ aConvertDestinationFormat = ConvertDataFormat::PNG;
+ pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
+ break;
+ default:
+ break;
+ }
+ }
+
+ GraphicType eGraphicType = rGraphic.GetType();
+ if (!pGraphicAry)
+ {
+ if (ERRCODE_NONE
+ == GraphicConverter::Export(aStream, rGraphic,
+ (eGraphicType == GraphicType::Bitmap)
+ ? ConvertDataFormat::PNG
+ : ConvertDataFormat::WMF))
+ {
+ pBLIPType = (eGraphicType == GraphicType::Bitmap) ? OOO_STRING_SVTOOLS_RTF_PNGBLIP
+ : OOO_STRING_SVTOOLS_RTF_WMETAFILE;
+ nSize = aStream.TellEnd();
+ pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
+ }
+ }
+
+ Size aMapped(eGraphicType == GraphicType::Bitmap ? rGraphic.GetSizePixel()
+ : rGraphic.GetPrefSize());
+
+ auto& rCr = pGrfNode->GetAttr(RES_GRFATR_CROPGRF);
+
+ //Get original size in twips
+ Size aSize(pGrfNode->GetTwipSize());
+ Size aRendered(aSize);
+
+ const SwFormatFrameSize& rS = pFlyFrameFormat->GetFrameSize();
+ aRendered.setWidth(rS.GetWidth());
+ aRendered.setHeight(rS.GetHeight());
+
+ ww8::Frame* pFrame = nullptr;
+ for (auto& rFrame : m_rExport.m_aFrames)
+ {
+ if (pFlyFrameFormat == &rFrame.GetFrameFormat())
+ {
+ pFrame = &rFrame;
+ break;
+ }
+ }
+
+ const SwAttrSet* pAttrSet = pGrfNode->GetpSwAttrSet();
+ if (pFrame && !pFrame->IsInline())
+ {
+ m_rExport.Strm().WriteOString(
+ "{" OOO_STRING_SVTOOLS_RTF_SHP
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
+ m_pFlyFrameSize = &aRendered;
+ m_rExport.m_pParentFrame = pFrame;
+ m_rExport.m_bOutFlyFrameAttrs = true;
+ m_rExport.SetRTFFlySyntax(true);
+ m_rExport.OutputFormat(pFrame->GetFrameFormat(), false, false, true);
+ m_rExport.m_bOutFlyFrameAttrs = false;
+ m_rExport.SetRTFFlySyntax(false);
+ m_rExport.m_pParentFrame = nullptr;
+ m_pFlyFrameSize = nullptr;
+ std::vector<std::pair<OString, OString>> aFlyProperties{
+ { "shapeType", OString::number(ESCHER_ShpInst_PictureFrame) },
+
+ { "wzDescription", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjDescription(),
+ m_rExport.GetCurrentEncoding()) },
+ { "wzName", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjTitle(),
+ m_rExport.GetCurrentEncoding()) }
+ };
+
+ // If we have a wrap polygon, then handle that here.
+ if (pFlyFrameFormat->GetSurround().IsContour())
+ {
+ if (const SwNoTextNode* pNd
+ = sw::util::GetNoTextNodeFromSwFrameFormat(*pFlyFrameFormat))
+ {
+ const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
+ if (pPolyPoly && pPolyPoly->Count())
+ {
+ tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport(
+ *pPolyPoly, pNd, /*bCorrectCrop=*/true);
+ OStringBuffer aVerticies;
+ for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
+ aVerticies.append(";(" + OString::number(aPoly[i].X()) + ","
+ + OString::number(aPoly[i].Y()) + ")");
+ aFlyProperties.push_back(std::make_pair<OString, OString>(
+ "pWrapPolygonVertices"_ostr,
+ "8;" + OString::number(aPoly.GetSize()) + aVerticies));
+ }
+ }
+ }
+
+ // Below text, behind document, opaque: they all refer to the same thing.
+ if (!pFlyFrameFormat->GetOpaque().GetValue())
+ aFlyProperties.push_back(
+ std::make_pair<OString, OString>("fBehindDocument"_ostr, "1"_ostr));
+
+ if (pAttrSet)
+ {
+ if (Degree10 nRot10 = pAttrSet->Get(RES_GRFATR_ROTATION).GetValue())
+ {
+ // See writerfilter::rtftok::RTFSdrImport::applyProperty(),
+ // positive rotation angles are clockwise in RTF, we have them
+ // as counter-clockwise.
+ // Additionally, RTF type is 0..360*2^16, our is 0..360*10.
+ sal_Int32 nRot = nRot10.get() * -1 * RTF_MULTIPLIER / 10;
+ aFlyProperties.emplace_back("rotation", OString::number(nRot));
+ }
+ }
+
+ for (const std::pair<OString, OString>& rPair : aFlyProperties)
+ {
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
+ m_rExport.Strm().WriteOString(rPair.first);
+ m_rExport.Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
+ m_rExport.Strm().WriteOString(rPair.second);
+ m_rExport.Strm().WriteOString("}}");
+ }
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{" OOO_STRING_SVTOOLS_RTF_SN
+ " pib"
+ "}{" OOO_STRING_SVTOOLS_RTF_SV " ");
+ }
+
+ bool bWritePicProp = !pFrame || pFrame->IsInline();
+ if (pBLIPType)
+ ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize,
+ m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet);
+ else
+ {
+ aStream.Seek(0);
+ if (GraphicConverter::Export(aStream, rGraphic, aConvertDestinationFormat) != ERRCODE_NONE)
+ SAL_WARN("sw.rtf", "failed to export the graphic");
+ pBLIPType = pConvertDestinationBLIPType;
+ nSize = aStream.TellEnd();
+ pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
+
+ ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize,
+ m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet);
+ }
+
+ if (pFrame && !pFrame->IsInline())
+ m_rExport.Strm().WriteOString("}}}}"); // Close SV, SP, SHPINST and SHP.
+
+ m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
+}
+
+void RtfAttributeOutput::BulletDefinition(int /*nId*/, const Graphic& rGraphic, Size aSize)
+{
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT);
+ m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT OOO_STRING_SVTOOLS_RTF_PNGBLIP);
+
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
+ m_rExport.Strm().WriteNumberAsString(aSize.Width());
+ m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
+ m_rExport.Strm().WriteNumberAsString(aSize.Height());
+
+ m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
+ const sal_uInt8* pGraphicAry = nullptr;
+ SvMemoryStream aStream;
+ if (GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
+ SAL_WARN("sw.rtf", "failed to export the numbering picture bullet");
+ sal_uInt64 nSize = aStream.TellEnd();
+ pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
+ msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &m_rExport.Strm());
+ m_rExport.Strm().WriteOString("}}"); // pict, shppict
+}
+
+void RtfAttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
+{
+ if (!rRtlGutter.GetValue())
+ {
+ return;
+ }
+
+ m_rExport.Strm().WriteOString(LO_STRING_SVTOOLS_RTF_RTLGUTTER);
+}
+
+void RtfAttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
+{
+ // Text wrapping break of type:
+ m_aStyles.append(LO_STRING_SVTOOLS_RTF_LBR);
+ m_aStyles.append(static_cast<sal_Int32>(rLineBreak.GetEnumValue()));
+
+ // Write the linebreak itself.
+ RunText("\x0b");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx
new file mode 100644
index 0000000000..5e7e648f64
--- /dev/null
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -0,0 +1,696 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_RTFATTRIBUTEOUTPUT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFATTRIBUTEOUTPUT_HXX
+
+#include <memory>
+#include <com/sun/star/drawing/FillStyle.hpp>
+
+#include "attributeoutputbase.hxx"
+#include "rtfstringbuffer.hxx"
+
+#include <wrtswtbl.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <editeng/boxitem.hxx>
+
+#include <optional>
+
+class SwGrfNode;
+class SwOLENode;
+class SwFlyFrameFormat;
+class RtfExport;
+
+/// The class that has handlers for various resource types when exporting as RTF
+class RtfAttributeOutput : public AttributeOutputBase
+{
+ friend class RtfStringBufferValue;
+ friend class SaveRunState;
+
+public:
+ /// Export the state of RTL/CJK.
+ void RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript) override;
+
+ /// Start of the paragraph.
+ sal_Int32 StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+ bool bGenerateParaId) override;
+
+ /// End of the paragraph.
+ void EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner) override;
+
+ /// Empty paragraph.
+ void EmptyParagraph() override;
+
+ /// Called in order to output section breaks.
+ void SectionBreaks(const SwNode& rNode) override;
+
+ /// Called before we start outputting the attributes.
+ void StartParagraphProperties() override;
+
+ /// Called after we end outputting the attributes.
+ void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties,
+ const SwRedlineData* pRedlineData,
+ const SwRedlineData* pRedlineParagraphMarkerDeleted,
+ const SwRedlineData* pRedlineParagraphMarkerInserted) override;
+
+ /// Start of the text run.
+ void StartRun(const SwRedlineData* pRedlineData, sal_Int32 nPos,
+ bool bSingleEmptyRun = false) override;
+
+ /// End of the text run.
+ void EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen,
+ bool bLastRun = false) override;
+
+ /// Called before we start outputting the attributes.
+ void StartRunProperties() override;
+
+ /// Called after we end outputting the attributes.
+ void EndRunProperties(const SwRedlineData* pRedlineData) override;
+
+ /// Output text (inside a run).
+ void RunText(const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8,
+ const OUString& rSymbolFont = OUString()) override;
+
+ // Access to (anyway) private buffers, used by the sdr exporter
+ OStringBuffer& RunText();
+ OString MoveCharacterProperties(bool aAutoWriteRtlLtr = false);
+
+ /// Output text (without markup).
+ void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override;
+
+ /// Output ruby start.
+ void StartRuby(const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby) override;
+
+ /// Output ruby end.
+ void EndRuby(const SwTextNode& rNode, sal_Int32 nPos) override;
+
+ /// Output URL start.
+ bool StartURL(const OUString& rUrl, const OUString& rTarget) override;
+
+ /// Output URL end.
+ bool EndURL(bool isAtEndOfParagraph) override;
+
+ void FieldVanish(const OUString& rText, ww::eField eType,
+ OUString const* pBookmarkName) override;
+
+ /// Output redlining.
+ ///
+ /// The common attribute that can be among the run properties.
+ void Redline(const SwRedlineData* pRedline) override;
+
+ void FormatDrop(const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle,
+ ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+ ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner) override;
+
+ /// Output style.
+ void ParagraphStyle(sal_uInt16 nStyle) override;
+
+ void TableInfoCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableDefinition(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TablePositioning(SwFrameFormat* pFlyFormat);
+ void
+ TableDefaultBorders(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableBackgrounds(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableRowRedline(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableCellRedline(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableCanSplit(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableVerticalCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner) override;
+ void TableOrientation(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override;
+ void TableRowEnd(sal_uInt32 nDepth) override;
+
+ /// Start of the styles table.
+ void StartStyles() override;
+
+ /// End of the styles table.
+ void EndStyles(sal_uInt16 nNumberOfStyles) override;
+
+ /// Write default style.
+ void DefaultStyle() override;
+
+ /// Start of a style in the styles table.
+ void StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase, sal_uInt16 nNext,
+ sal_uInt16 nLink, sal_uInt16 nWwId, sal_uInt16 nSlot,
+ bool bAutoUpdate) override;
+
+ /// End of a style in the styles table.
+ void EndStyle() override;
+
+ /// Start of (paragraph or run) properties of a style.
+ void StartStyleProperties(bool bParProp, sal_uInt16 nStyle) override;
+
+ /// End of (paragraph or run) properties of a style.
+ void EndStyleProperties(bool bParProp) override;
+
+ /// Numbering rule and Id.
+ void OutlineNumbering(sal_uInt8 nLvl) override;
+
+ /// Page break
+ /// As a paragraph property - the paragraph should be on the next page.
+ void PageBreakBefore(bool bBreak) override;
+
+ /// Write a section break
+ /// msword::ColumnBreak or msword::PageBreak
+ void SectionBreak(sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr,
+ bool bExtraPageBreak = false) override;
+
+ /// Start of the section properties.
+ void StartSection() override;
+
+ /// End of the section properties.
+ void EndSection() override;
+
+ /// Protection of forms.
+ void SectionFormProtection(bool bProtected) override;
+
+ /// Numbering of the lines in the document.
+ void SectionLineNumbering(sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo) override;
+
+ /// Has different headers/footers for the title page.
+ void SectionTitlePage() override;
+
+ /// Description of the page borders.
+ void SectionPageBorders(const SwFrameFormat* pFormat,
+ const SwFrameFormat* pFirstPageFormat) override;
+
+ /// Columns populated from right/numbers on the right side?
+ void SectionBiDi(bool bBiDi) override;
+
+ /// The style of the page numbers.
+ ///
+ void SectionPageNumbering(sal_uInt16 nNumType,
+ const ::std::optional<sal_uInt16>& oPageRestartNumber) override;
+
+ /// The type of breaking.
+ void SectionType(sal_uInt8 nBreakCode) override;
+
+ void SectFootnoteEndnotePr() override;
+
+ void WriteFootnoteEndnotePr(bool bFootnote, const SwEndNoteInfo& rInfo);
+
+ /// Definition of a numbering instance.
+ void NumberingDefinition(sal_uInt16 nId, const SwNumRule& rRule) override;
+
+ /// Start of the abstract numbering definition instance.
+ void StartAbstractNumbering(sal_uInt16 nId) override;
+
+ /// End of the abstract numbering definition instance.
+ void EndAbstractNumbering() override;
+
+ /// All the numbering level information.
+ void NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart, sal_uInt16 nNumberingType,
+ SvxAdjust eAdjust, const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
+ const wwFont* pFont, const SfxItemSet* pOutSet, sal_Int16 nIndentAt,
+ sal_Int16 nFirstLineIndex, sal_Int16 nListTabPos,
+ const OUString& rNumberingString,
+ const SvxBrushItem* pBrush, //For i120928,to export graphic of bullet
+ bool isLegal) override;
+
+ void WriteField_Impl(const SwField* pField, ww::eField eType, std::u16string_view rFieldCmd,
+ FieldFlags nMode);
+ void WriteBookmarks_Impl(std::vector<OUString>& rStarts, std::vector<OUString>& rEnds);
+ void WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts, std::vector<OUString>& rEnds);
+ void WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader, const char* pStr,
+ bool bTitlepg);
+ void WriteBookmarkInActParagraph(const OUString& /*rName*/, sal_Int32 /*nFirstRunPos*/,
+ sal_Int32 /*nLastRunPos*/) override{};
+
+protected:
+ /// Output frames - the implementation.
+ void OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& rNdTopLeft) override;
+
+ /// Sfx item Sfx item RES_CHRATR_CASEMAP
+ void CharCaseMap(const SvxCaseMapItem& rCaseMap) override;
+
+ /// Sfx item Sfx item RES_CHRATR_COLOR
+ void CharColor(const SvxColorItem& rColor) override;
+
+ /// Sfx item Sfx item RES_CHRATR_CONTOUR
+ void CharContour(const SvxContourItem& rContour) override;
+
+ /// Sfx item RES_CHRATR_CROSSEDOUT
+ void CharCrossedOut(const SvxCrossedOutItem& rCrossedOut) override;
+
+ /// Sfx item RES_CHRATR_ESCAPEMENT
+ void CharEscapement(const SvxEscapementItem& rEscapement) override;
+
+ /// Sfx item RES_CHRATR_FONT
+ void CharFont(const SvxFontItem& rFont) override;
+
+ /// Sfx item RES_CHRATR_FONTSIZE
+ void CharFontSize(const SvxFontHeightItem& rFontSize) override;
+
+ /// Sfx item RES_CHRATR_KERNING
+ void CharKerning(const SvxKerningItem& rKerning) override;
+
+ /// Sfx item RES_CHRATR_LANGUAGE
+ void CharLanguage(const SvxLanguageItem& rLanguage) override;
+
+ /// Sfx item RES_CHRATR_POSTURE
+ void CharPosture(const SvxPostureItem& rPosture) override;
+
+ /// Sfx item RES_CHRATR_SHADOWED
+ void CharShadow(const SvxShadowedItem& rShadow) override;
+
+ /// Sfx item RES_CHRATR_UNDERLINE
+ void CharUnderline(const SvxUnderlineItem& rUnderline) override;
+
+ /// Sfx item RES_CHRATR_WEIGHT
+ void CharWeight(const SvxWeightItem& rWeight) override;
+
+ /// Sfx item RES_CHRATR_AUTOKERN
+ void CharAutoKern(const SvxAutoKernItem& rAutoKern) override;
+
+ /// Sfx item RES_CHRATR_BLINK
+ void CharAnimatedText(const SvxBlinkItem& rBlink) override;
+
+ /// Sfx item RES_CHRATR_BACKGROUND
+ void CharBackground(const SvxBrushItem& rBrush) override;
+
+ /// Sfx item RES_CHRATR_CJK_FONT
+ void CharFontCJK(const SvxFontItem& rFont) override;
+
+ /// Sfx item RES_CHRATR_CJK_FONTSIZE
+ void CharFontSizeCJK(const SvxFontHeightItem& rFontSize) override;
+
+ /// Sfx item RES_CHRATR_CJK_LANGUAGE
+ void CharLanguageCJK(const SvxLanguageItem& rLanguageItem) override;
+
+ /// Sfx item RES_CHRATR_CJK_POSTURE
+ void CharPostureCJK(const SvxPostureItem& rPosture) override;
+
+ /// Sfx item RES_CHRATR_CJK_WEIGHT
+ void CharWeightCJK(const SvxWeightItem& rWeight) override;
+
+ /// Sfx item RES_CHRATR_CTL_FONT
+ void CharFontCTL(const SvxFontItem& rFont) override;
+
+ /// Sfx item RES_CHRATR_CTL_FONTSIZE
+ void CharFontSizeCTL(const SvxFontHeightItem& rFontSize) override;
+
+ /// Sfx item RES_CHRATR_CTL_LANGUAGE
+ void CharLanguageCTL(const SvxLanguageItem& rLanguageItem) override;
+
+ /// Sfx item RES_CHRATR_CTL_POSTURE
+ void CharPostureCTL(const SvxPostureItem& rPosture) override;
+
+ /// Sfx item RES_CHRATR_CTL_WEIGHT
+ void CharWeightCTL(const SvxWeightItem& rWeight) override;
+
+ /// Sfx item RES_CHRATR_BidiRTL
+ void CharBidiRTL(const SfxPoolItem& rItem) override;
+
+ /// Sfx item RES_CHRATR_IdctHint
+ void CharIdctHint(const SfxPoolItem& rItem) override;
+
+ /// Sfx item RES_CHRATR_ROTATE
+ void CharRotate(const SvxCharRotateItem& rRotate) override;
+
+ /// Sfx item RES_CHRATR_EMPHASIS_MARK
+ void CharEmphasisMark(const SvxEmphasisMarkItem& rEmphasisMark) override;
+
+ /// Sfx item RES_CHRATR_TWO_LINES
+ void CharTwoLines(const SvxTwoLinesItem& rTwoLines) override;
+
+ /// Sfx item RES_CHRATR_SCALEW
+ void CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth) override;
+
+ /// Sfx item RES_CHRATR_RELIEF
+ void CharRelief(const SvxCharReliefItem& rRelief) override;
+
+ /// Sfx item RES_CHRATR_HIDDEN
+ void CharHidden(const SvxCharHiddenItem& rHidden) override;
+
+ /// Sfx item RES_CHRATR_BOX
+ void CharBorder(const ::editeng::SvxBorderLine* pAllBorder, sal_uInt16 nDist,
+ bool bShadow) override;
+
+ /// Sfx item RES_CHRATR_HIGHLIGHT
+ void CharHighlight(const SvxBrushItem& rBrush) override;
+
+ /// Sfx item RES_TXTATR_INETFMT
+ void TextINetFormat(const SwFormatINetFormat& rURL) override;
+
+ /// Sfx item RES_TXTATR_CHARFMT
+ void TextCharFormat(const SwFormatCharFormat& rCharFormat) override;
+
+ /// Sfx item RES_TXTATR_FTN
+ void TextFootnote_Impl(const SwFormatFootnote& rFootnote) override;
+
+ /// Sfx item RES_PARATR_LINESPACING
+ void ParaLineSpacing_Impl(short nSpace, short nMulti) override;
+
+ /// Sfx item RES_PARATR_ADJUST
+ void ParaAdjust(const SvxAdjustItem& rAdjust) override;
+
+ /// Sfx item RES_PARATR_SPLIT
+ void ParaSplit(const SvxFormatSplitItem& rSplit) override;
+
+ /// Sfx item RES_PARATR_WIDOWS
+ void ParaWidows(const SvxWidowsItem& rWidows) override;
+
+ /// Sfx item RES_PARATR_TABSTOP
+ void ParaTabStop(const SvxTabStopItem& rTabStop) override;
+
+ /// Sfx item RES_PARATR_HYPHENZONE
+ void ParaHyphenZone(const SvxHyphenZoneItem& rHyphenZone) override;
+
+ /// Sfx item RES_PARATR_NUMRULE
+ void ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl, sal_Int32 nNumId) override;
+
+ /// Sfx item RES_PARATR_SCRIPTSPACE
+ void ParaScriptSpace(const SfxBoolItem& rScriptSpace) override;
+
+ /// Sfx item RES_PARATR_HANGINGPUNCTUATION
+ void ParaHangingPunctuation(const SfxBoolItem& rItem) override;
+
+ /// Sfx item RES_PARATR_FORBIDDEN_RULES
+ void ParaForbiddenRules(const SfxBoolItem& rItem) override;
+
+ /// Sfx item RES_PARATR_VERTALIGN
+ void ParaVerticalAlign(const SvxParaVertAlignItem& rAlign) override;
+
+ /// Sfx item RES_PARATR_SNAPTOGRID
+ void ParaSnapToGrid(const SvxParaGridItem& rItem) override;
+
+ /// Sfx item RES_FRM_SIZE
+ void FormatFrameSize(const SwFormatFrameSize& rSize) override;
+
+ /// Sfx item RES_PAPER_BIN
+ void FormatPaperBin(const SvxPaperBinItem& rItem) override;
+
+ /// Sfx item RES_MARGIN_FIRSTLINE
+ virtual void FormatFirstLineIndent(const SvxFirstLineIndentItem& rFirstLine) override;
+ /// Sfx item RES_MARGIN_TEXTLEFT
+ virtual void FormatTextLeftMargin(const SvxTextLeftMarginItem& rTextLeftMargin) override;
+ /// Sfx item RES_MARGIN_RIGHT
+ virtual void FormatRightMargin(const SvxRightMarginItem& rRightMargin) override;
+
+ /// Sfx item RES_LR_SPACE
+ void FormatLRSpace(const SvxLRSpaceItem& rLRSpace) override;
+
+ /// Sfx item RES_UL_SPACE
+ void FormatULSpace(const SvxULSpaceItem& rULSpace) override;
+
+ /// Sfx item RES_SURROUND
+ void FormatSurround(const SwFormatSurround& rSurround) override;
+
+ /// Sfx item RES_VERT_ORIENT
+ void FormatVertOrientation(const SwFormatVertOrient& rFlyVert) override;
+
+ /// Sfx item RES_HORI_ORIENT
+ void FormatHorizOrientation(const SwFormatHoriOrient& rFlyHori) override;
+
+ /// Sfx item RES_ANCHOR
+ void FormatAnchor(const SwFormatAnchor& rAnchor) override;
+
+ /// Sfx item RES_BACKGROUND
+ void FormatBackground(const SvxBrushItem& rBrush) override;
+
+ /// Sfx item RES_FILL_STYLE
+ void FormatFillStyle(const XFillStyleItem& rFillStyle) override;
+
+ /// Sfx item RES_FILL_GRADIENT
+ void FormatFillGradient(const XFillGradientItem& rFillGradient) override;
+
+ /// Sfx item RES_BOX
+ void FormatBox(const SvxBoxItem& rBox) override;
+
+ /// Sfx item RES_COL
+ void FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
+ SwTwips nPageSize) override;
+
+ /// Sfx item RES_KEEP
+ void FormatKeep(const SvxFormatKeepItem& rItem) override;
+
+ /// Sfx item RES_TEXTGRID
+ void FormatTextGrid(const SwTextGridItem& rItem) override;
+
+ /// Sfx item RES_LINENUMBER
+ void FormatLineNumbering(const SwFormatLineNumber& rNumbering) override;
+
+ /// Sfx item RES_FRAMEDIR
+ void FormatFrameDirection(const SvxFrameDirectionItem& rDirection) override;
+
+ /// Sfx item RES_PARATR_GRABBAG
+ void ParaGrabBag(const SfxGrabBagItem& rItem) override;
+
+ /// Sfx item RES_CHRATR_GRABBAG
+ void CharGrabBag(const SfxGrabBagItem& rItem) override;
+
+ /// Sfx item RES_PARATR_OUTLINELEVEL
+ void ParaOutlineLevel(const SfxUInt16Item& rItem) override;
+
+ /// Write the expanded field
+ void WriteExpand(const SwField* pField) override;
+
+ void RefField(const SwField& rField, const OUString& rRef) override;
+ void HiddenField(const SwField& rField) override;
+ void SetField(const SwField& rField, ww::eField eType, const OUString& rCmd) override;
+ void PostitField(const SwField* pField) override;
+ bool DropdownField(const SwField* pField) override;
+ bool PlaceholderField(const SwField* pField) override;
+
+ void SectionRtlGutter(const SfxBoolItem& rRtlGutter) override;
+
+ void TextLineBreak(const SwFormatLineBreak& rLineBreak) override;
+
+private:
+ /// Reference to the export, where to get the data from
+ RtfExport& m_rExport;
+
+ OStringBuffer m_aTabStop;
+
+ /// Access to the page style of the previous paragraph.
+ const SwPageDesc* m_pPrevPageDesc;
+
+ /// Output graphic fly frames.
+ void FlyFrameGraphic(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode);
+ void FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
+ const Size& rSize);
+ void FlyFrameOLEReplacement(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
+ const Size& rSize);
+ /// Math export.
+ bool FlyFrameOLEMath(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
+ const Size& rSize);
+
+ /*
+ * Table methods.
+ */
+ void InitTableHelper(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner);
+ void StartTable();
+ void StartTableRow(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner);
+ void StartTableCell();
+ void TableCellProperties(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner);
+ void EndTableCell();
+ void EndTableRow();
+ void EndTable();
+
+ /// End cell, row, and even the entire table if necessary.
+ void FinishTableRowCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pInner);
+
+ void WriteTextFootnoteNumStr(const SwFormatFootnote& rFootnote);
+
+ /*
+ * Current style name and its ID.
+ */
+ OUString m_rStyleName;
+ sal_uInt16 m_nStyleId;
+ /*
+ * Current list ID.
+ */
+ sal_uInt16 m_nListId;
+ /*
+ * This is needed because the call order is: run text, run properties, paragraph properties.
+ * What we need is the opposite.
+ */
+ RtfStringBuffer m_aRun;
+ RtfStringBuffer m_aRunText;
+ /*
+ * This is written after runs.
+ */
+ OStringBuffer m_aAfterRuns;
+ /*
+ * Same for colors and stylesheets: first we just want to output colors,
+ * need to buffer the stylesheet table to output it after the color one.
+ */
+ OStringBuffer m_aStylesheet;
+ /*
+ * This one just holds the style commands in the current style.
+ */
+ OStringBuffer m_aStyles;
+ /*
+ * This is the same as m_aStyles but the contents of it is Assoc.
+ */
+ OStringBuffer m_aStylesAssocHich;
+ OStringBuffer m_aStylesAssocDbch;
+ OStringBuffer m_aStylesAssocRtlch;
+ OStringBuffer m_aStylesAssocLtrch;
+
+ bool m_bIsRTL;
+ sal_uInt16 m_nScript;
+ bool m_bControlLtrRtl;
+
+ sal_Int32 m_nNextAnnotationMarkId;
+ sal_Int32 m_nCurrentAnnotationMarkId;
+ /// Maps annotation mark names to ID's.
+ std::map<OString, sal_Int32> m_rOpenedAnnotationMarksIds;
+
+ /*
+ * The current table helper.
+ */
+ std::unique_ptr<SwWriteTable> m_pTableWrt;
+
+ /*
+ * Remember if we are in an open cell, or not.
+ */
+ bool m_bTableCellOpen;
+
+ /*
+ * Remember the current table depth.
+ */
+ sal_uInt32 m_nTableDepth;
+
+ /*
+ * Remember if we wrote a \cell or not.
+ */
+ bool m_bTableAfterCell;
+
+ /*
+ * For late output of row definitions.
+ */
+ OStringBuffer m_aRowDefs;
+
+ /*
+ * Is a column break needed after the next \par?
+ */
+ bool m_nColBreakNeeded;
+
+ /*
+ * If section breaks should be buffered to m_aSectionBreaks
+ */
+ bool m_bBufferSectionBreaks;
+ OStringBuffer m_aSectionBreaks;
+
+ /*
+ * If section headers (and footers) should be buffered to
+ * m_aSectionHeaders.
+ */
+ bool m_bBufferSectionHeaders;
+ OStringBuffer m_aSectionHeaders;
+
+ /*
+ * Support for starting multiple tables at the same cell.
+ * If the current table is the last started one.
+ */
+ bool m_bLastTable;
+ /*
+ * List of already started but not yet defined tables (need to be defined
+ * after the nested tables).
+ */
+ std::vector<OString> m_aTables;
+ /*
+ * If cell info is already output.
+ */
+ bool m_bWroteCellInfo;
+
+ /// If we ended a table row without starting a new one.
+ bool m_bTableRowEnded;
+
+ /// Number of cells from the table definition, by depth.
+ std::map<sal_uInt32, sal_uInt32> m_aCells;
+
+ bool m_bIsBeforeFirstParagraph;
+
+ /// If we're in a paragraph that has a single empty run only.
+ bool m_bSingleEmptyRun;
+
+ bool m_bInRun;
+
+ bool m_bInRuby;
+
+ /// Maps ID's to postit fields, used in atrfstart/end and atnref.
+ std::map<sal_uInt16, const SwPostItField*> m_aPostitFields;
+
+ /// When exporting fly frames, this holds the real size of the frame.
+ const Size* m_pFlyFrameSize;
+
+ std::vector<std::pair<OString, OString>> m_aFlyProperties;
+
+ std::optional<css::drawing::FillStyle> m_oFillStyle;
+
+ /// If we're in the process of exporting a hyperlink, then its URL.
+ std::stack<OUString> m_aURLs;
+
+ /// If original file had \sbauto.
+ bool m_bParaBeforeAutoSpacing;
+ /// If m_bParaBeforeAutoSpacing is set, value of \sb.
+ sal_Int32 m_nParaBeforeSpacing;
+ /// If original file had \saauto.
+ bool m_bParaAfterAutoSpacing;
+ /// If m_bParaBeforeAutoSpacing is set, value of \sa.
+ sal_Int32 m_nParaAfterSpacing;
+
+ editeng::WordPageMargins m_aPageMargins;
+
+public:
+ explicit RtfAttributeOutput(RtfExport& rExport);
+
+ ~RtfAttributeOutput() override;
+
+ /// Return the right export class.
+ MSWordExportBase& GetExport() override;
+
+ // These are used by wwFont::WriteRtf()
+ /// Start the font.
+ void StartFont(std::u16string_view rFamilyName) const;
+
+ /// End the font.
+ void EndFont() const;
+
+ /// Alternate name for the font.
+ void FontAlternateName(std::u16string_view rName) const;
+
+ /// Font charset.
+ void FontCharset(sal_uInt8 nCharSet) const;
+
+ /// Font family.
+ void FontFamilyType(FontFamily eFamily, const wwFont& rFont) const;
+
+ /// Font pitch.
+ void FontPitchType(FontPitch ePitch) const;
+
+ void BulletDefinition(int nId, const Graphic& rGraphic, Size aSize) override;
+
+ /// Handles just the {\shptxt ...} part of a shape export.
+ void writeTextFrame(const ww8::Frame& rFrame, bool bTextBox = false);
+
+ OStringBuffer& GetTabStop() { return m_aTabStop; }
+
+ const SwPageDesc* GetPrevPageDesc() const { return m_pPrevPageDesc; }
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFATTRIBUTEOUTPUT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx
new file mode 100644
index 0000000000..6063eb3432
--- /dev/null
+++ b/sw/source/filter/ww8/rtfexport.cxx
@@ -0,0 +1,1541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "rtfexport.hxx"
+
+#include <string_view>
+
+#include "rtfexportfilter.hxx"
+#include "rtfsdrexport.hxx"
+#include "rtfattributeoutput.hxx"
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <docsh.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <fmtpdsc.hxx>
+#include <ftninfo.hxx>
+#include <fmthdft.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <lineinfo.hxx>
+#include <redline.hxx>
+#include <swmodule.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <comphelper/string.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+#include <unotools/docinfohelper.hxx>
+#include <xmloff/odffields.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <svx/xflclit.hxx>
+#include <fmtmeta.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <fmtfsize.hxx>
+#include <ndtxt.hxx>
+#include <numrule.hxx>
+#include <frmatr.hxx>
+#include <swtable.hxx>
+#include <IMark.hxx>
+
+using namespace ::com::sun::star;
+
+// the default text encoding for the export, if it doesn't fit unicode will
+// be used
+#define DEF_ENCODING RTL_TEXTENCODING_ASCII_US
+
+AttributeOutputBase& RtfExport::AttrOutput() const { return *m_pAttrOutput; }
+
+MSWordSections& RtfExport::Sections() const { return *m_pSections; }
+
+RtfSdrExport& RtfExport::SdrExporter() const { return *m_pSdrExport; }
+
+bool RtfExport::CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich)
+{
+ // FIXME is this actually true for rtf? - this is copied from DOCX
+ if (nScript == i18n::ScriptType::ASIAN)
+ {
+ // for asian in ww8, there is only one fontsize
+ // and one fontstyle (posture/weight)
+ switch (nWhich)
+ {
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ return false;
+ default:
+ break;
+ }
+ }
+ else if (nScript != i18n::ScriptType::COMPLEX)
+ {
+ // for western in ww8, there is only one fontsize
+ // and one fontstyle (posture/weight)
+ switch (nWhich)
+ {
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ return false;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+void RtfExport::AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen,
+ const SwRedlineData* /*pRedlineData*/)
+{
+ std::vector<OUString> aStarts;
+ std::vector<OUString> aEnds;
+
+ IMarkVector aMarks;
+ if (GetBookmarks(rNode, nCurrentPos, nCurrentPos + nLen, aMarks))
+ {
+ for (const auto& pMark : aMarks)
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ if (nStart == nCurrentPos)
+ aStarts.push_back(pMark->GetName());
+
+ if (nEnd == nCurrentPos)
+ aEnds.push_back(pMark->GetName());
+ }
+ }
+
+ m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds);
+}
+
+void RtfExport::AppendBookmark(const OUString& rName)
+{
+ std::vector<OUString> aStarts{ rName };
+ std::vector<OUString> aEnds{ rName };
+
+ m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds);
+}
+
+void RtfExport::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos,
+ sal_Int32 nLen)
+{
+ std::vector<OUString> aStarts;
+ std::vector<OUString> aEnds;
+
+ IMarkVector aMarks;
+ if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
+ {
+ for (const auto& pMark : aMarks)
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ if (nStart == nCurrentPos)
+ aStarts.push_back(pMark->GetName());
+
+ if (nEnd == nCurrentPos)
+ aEnds.push_back(pMark->GetName());
+ }
+ }
+
+ m_pAttrOutput->WriteAnnotationMarks_Impl(aStarts, aEnds);
+}
+
+//For i120928,to export graphic of bullet for RTF filter
+void RtfExport::ExportGrfBullet(const SwTextNode& /*rNd*/)
+{
+ // Noop, would be too late, see WriteNumbering() instead.
+}
+
+void RtfExport::WriteChar(sal_Unicode /*c*/) { /* WriteChar() has nothing to do for rtf. */}
+
+static bool IsExportNumRule(const SwNumRule& rRule)
+{
+ sal_uInt8 nEnd = MAXLEVEL;
+ while (nEnd-- && !rRule.GetNumFormat(nEnd))
+ ;
+ ++nEnd;
+
+ sal_uInt8 nLvl;
+
+ for (nLvl = 0; nLvl < nEnd; ++nLvl)
+ {
+ const SwNumFormat* pNFormat = &rRule.Get(nLvl);
+ if (SVX_NUM_NUMBER_NONE != pNFormat->GetNumberingType() || !pNFormat->GetPrefix().isEmpty()
+ || (!pNFormat->GetSuffix().isEmpty() && pNFormat->GetSuffix() != "."))
+ break;
+ }
+
+ return nLvl != nEnd;
+}
+
+void RtfExport::BuildNumbering()
+{
+ const SwNumRuleTable& rListTable = m_rDoc.GetNumRuleTable();
+
+ SwNumRule* pOutlineRule = m_rDoc.GetOutlineNumRule();
+ if (IsExportNumRule(*pOutlineRule))
+ GetNumberingId(*pOutlineRule);
+
+ for (auto n = rListTable.size(); n;)
+ {
+ SwNumRule* pRule = rListTable[--n];
+ if (!m_rDoc.IsUsed(*pRule))
+ continue;
+
+ if (IsExportNumRule(*pRule))
+ GetNumberingId(*pRule);
+ }
+}
+
+void RtfExport::WriteNumbering()
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ if (!m_pUsedNumTable)
+ return; // no numbering is used
+
+ Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_LISTTABLE);
+
+ CollectGrfsOfBullets();
+ if (!m_vecBulletPic.empty())
+ Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(LO_STRING_SVTOOLS_RTF_LISTPICTURE);
+ BulletDefinitions();
+ if (!m_vecBulletPic.empty())
+ Strm().WriteChar('}');
+
+ AbstractNumberingDefinitions();
+ Strm().WriteChar('}');
+
+ Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDETABLE);
+ NumberingDefinitions();
+ Strm().WriteChar('}');
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteRevTab()
+{
+ int nRevAuthors = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size();
+
+ if (nRevAuthors < 1)
+ return;
+
+ // RTF always seems to use Unknown as the default first entry
+ GetRedline("Unknown");
+
+ for (SwRangeRedline* pRedl : m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ GetRedline(SW_MOD()->GetRedlineAuthor(pRedl->GetAuthor()));
+ }
+
+ // Now write the table
+ Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_REVTBL)
+ .WriteChar(' ');
+ for (std::size_t i = 0; i < m_aRedlineTable.size(); ++i)
+ {
+ const OUString* pAuthor = GetRedline(i);
+ Strm().WriteChar('{');
+ if (pAuthor)
+ Strm().WriteOString(msfilter::rtfutil::OutString(*pAuthor, m_eDefaultEncoding));
+ Strm().WriteOString(";}");
+ }
+ Strm().WriteChar('}').WriteOString(SAL_NEWLINE_STRING);
+}
+
+void RtfExport::WriteHeadersFooters(sal_uInt8 nHeadFootFlags, const SwFrameFormat& rFormat,
+ const SwFrameFormat& rLeftHeaderFormat,
+ const SwFrameFormat& rLeftFooterFormat,
+ const SwFrameFormat& rFirstPageFormat, sal_uInt8 /*nBreakCode*/,
+ bool /*bEvenAndOddHeaders*/)
+{
+ // headers
+ if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_EVEN)
+ WriteHeaderFooter(rLeftHeaderFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERL);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD)
+ WriteHeaderFooter(rFormat, true, OOO_STRING_SVTOOLS_RTF_HEADER);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_FIRST)
+ WriteHeaderFooter(rFirstPageFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERF, true);
+
+ // footers
+ if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_EVEN)
+ WriteHeaderFooter(rLeftFooterFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERL);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD)
+ WriteHeaderFooter(rFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTER);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_FIRST)
+ WriteHeaderFooter(rFirstPageFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERF, true);
+}
+
+void RtfExport::OutputField(const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd,
+ FieldFlags nMode)
+{
+ m_pAttrOutput->WriteField_Impl(pField, eFieldType, rFieldCmd, nMode);
+}
+
+void RtfExport::WriteFormData(const ::sw::mark::IFieldmark& rFieldmark)
+{
+ sal_Int32 nType;
+ if (rFieldmark.GetFieldname() == ODF_FORMDROPDOWN)
+ {
+ nType = 2;
+ }
+ /* TODO
+ else if (rFieldmark.GetFieldname() == ODF_FORMCHECKBOX)
+ {
+ nType = 1;
+ }
+ else if (rFieldmark.GetFieldname() == ODF_FORMTEXT)
+ {
+ nType = 0;
+ }
+*/
+ else
+ {
+ SAL_INFO("sw.rtf", "unknown field type");
+ return;
+ }
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ "{" OOO_STRING_SVTOOLS_RTF_FFTYPE);
+ m_pAttrOutput->RunText().append(nType);
+ if (rFieldmark.GetFieldname() == ODF_FORMDROPDOWN)
+ {
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFHASLISTBOX "1");
+ uno::Sequence<OUString> entries;
+ if (auto const it = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_LISTENTRY);
+ it != rFieldmark.GetParameters()->end())
+ {
+ it->second >>= entries;
+ }
+ if (auto const it = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT);
+ it != rFieldmark.GetParameters()->end())
+ {
+ sal_Int32 result(-1);
+ it->second >>= result;
+ if (0 <= result && result < entries.getLength())
+ {
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFRES);
+ m_pAttrOutput->RunText().append(result);
+ }
+ }
+ for (OUString const& rEntry : entries)
+ {
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFL " ");
+ m_pAttrOutput->RunText().append(
+ msfilter::rtfutil::OutString(rEntry, m_eDefaultEncoding));
+ m_pAttrOutput->RunText().append("}");
+ }
+ }
+ m_pAttrOutput->RunText().append("}}"); // close FORMFIELD destination
+}
+
+void RtfExport::WriteHyperlinkData(const ::sw::mark::IFieldmark& /*rFieldmark*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfExport::DoComboBox(const OUString& /*rName*/, const OUString& /*rHelp*/,
+ const OUString& /*rToolTip*/, const OUString& /*rSelected*/,
+ const uno::Sequence<OUString>& /*rListItems*/)
+{
+ // this is handled in RtfAttributeOutput::OutputFlyFrame_Impl
+}
+
+void RtfExport::DoFormText(const SwInputField* pField)
+{
+ OUString sResult = pField->ExpandField(true, nullptr);
+ const OUString& rHelp = pField->GetHelp();
+ OUString sName = pField->GetPar2();
+ const OUString& rStatus = pField->GetToolTip();
+ m_pAttrOutput->RunText().append("{" OOO_STRING_SVTOOLS_RTF_FIELD
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
+ "{ FORMTEXT }");
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ " {" OOO_STRING_SVTOOLS_RTF_FFTYPE "0");
+ if (!rHelp.isEmpty())
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
+ if (!rStatus.isEmpty())
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFTYPETXT "0");
+
+ if (!sName.isEmpty())
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME " "
+ + msfilter::rtfutil::OutString(sName, m_eDefaultEncoding) + "}");
+ if (!rHelp.isEmpty())
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " "
+ + msfilter::rtfutil::OutString(rHelp, m_eDefaultEncoding) + "}");
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFDEFTEXT " "
+ + msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding) + "}");
+ if (!rStatus.isEmpty())
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " "
+ + msfilter::rtfutil::OutString(rStatus, m_eDefaultEncoding) + "}");
+ m_pAttrOutput->RunText().append("}}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
+ m_pAttrOutput->RunText().append(msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding)
+ + "}}");
+}
+
+sal_uInt64 RtfExport::ReplaceCr(sal_uInt8 /*nChar*/)
+{
+ // Completely unused for Rtf export... only here for code sharing
+ // purpose with binary export
+
+ return 0;
+}
+
+void RtfExport::WriteFonts()
+{
+ Strm()
+ .WriteOString(SAL_NEWLINE_STRING)
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_FONTTBL);
+ m_aFontHelper.WriteFontTable(*m_pAttrOutput);
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteStyles()
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+ m_pStyles->OutputStylesTable();
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteFootnoteSettings()
+{
+ const SwPageFootnoteInfo& rFootnoteInfo = m_rDoc.GetPageDesc(0).GetFootnoteInfo();
+ // Request a separator only in case the width is larger than zero.
+ bool bSeparator = double(rFootnoteInfo.GetWidth()) > 0;
+
+ Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_FTNSEP);
+ if (bSeparator)
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_CHFTNSEP);
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteMainText()
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush)
+ {
+ Strm().WriteOString(LO_STRING_SVTOOLS_RTF_VIEWBKSP).WriteChar('1');
+ Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BACKGROUND);
+ Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHP);
+ Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
+
+ std::vector<std::pair<OString, OString>> aProperties{
+ { "shapeType", "1" },
+ { "fillColor", OString::number(wwUtility::RGBToBGR(oBrush->GetColor())) }
+ };
+ for (const std::pair<OString, OString>& rPair : aProperties)
+ {
+ Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
+ Strm().WriteOString(rPair.first);
+ Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
+ Strm().WriteOString(rPair.second);
+ Strm().WriteOString("}}");
+ }
+ Strm().WriteChar('}'); // shpinst
+ Strm().WriteChar('}'); // shp
+ Strm().WriteChar('}'); // background
+ }
+
+ SwTableNode* pTableNode = m_pCurPam->GetPointNode().FindTableNode();
+ if (m_pWriter && m_pWriter->m_bWriteOnlyFirstTable && pTableNode != nullptr)
+ {
+ m_pCurPam->GetPoint()->Assign(*pTableNode);
+ m_pCurPam->GetMark()->Assign(*pTableNode->EndOfSectionNode());
+ }
+ else
+ {
+ m_pCurPam->GetPoint()->Assign(*m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode());
+ }
+
+ WriteText();
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteInfo()
+{
+ OString aGenerator
+ = OUStringToOString(utl::DocInfoHelper::GetGeneratorString(), RTL_TEXTENCODING_UTF8);
+ Strm()
+ .WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_GENERATOR " ")
+ .WriteOString(aGenerator)
+ .WriteChar('}');
+ Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_INFO);
+
+ SwDocShell* pDocShell(m_rDoc.GetDocShell());
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ if (pDocShell)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(),
+ uno::UNO_QUERY);
+ xDocProps.set(xDPS->getDocumentProperties());
+ }
+
+ if (xDocProps.is())
+ {
+ // Handle user-defined properties.
+ uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
+ = xDocProps->getUserDefinedProperties();
+ if (xUserDefinedProperties.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo
+ = xPropertySet->getPropertySetInfo();
+ // Do we have explicit markup in RTF for this property name?
+ if (xPropertySetInfo->hasPropertyByName("Company"))
+ {
+ OUString aValue;
+ xPropertySet->getPropertyValue("Company") >>= aValue;
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_COMPANY, aValue);
+ }
+ }
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_TITLE, xDocProps->getTitle(), true);
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_SUBJECT, xDocProps->getSubject());
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_KEYWORDS,
+ ::comphelper::string::convertCommaSeparated(xDocProps->getKeywords()));
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_DOCCOMM, xDocProps->getDescription());
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getAuthor());
+ OutDateTime(OOO_STRING_SVTOOLS_RTF_CREATIM, xDocProps->getCreationDate());
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getModifiedBy());
+ OutDateTime(OOO_STRING_SVTOOLS_RTF_REVTIM, xDocProps->getModificationDate());
+
+ OutDateTime(OOO_STRING_SVTOOLS_RTF_PRINTIM, xDocProps->getPrintDate());
+ }
+
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteUserPropType(int nType)
+{
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PROPTYPE).WriteNumberAsString(nType);
+}
+
+void RtfExport::WriteUserPropValue(std::u16string_view rValue)
+{
+ Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_STATICVAL " ");
+ Strm().WriteOString(msfilter::rtfutil::OutString(rValue, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteUserProps()
+{
+ Strm().WriteChar('{').WriteOString(
+ OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_USERPROPS);
+
+ SwDocShell* pDocShell(m_rDoc.GetDocShell());
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ if (pDocShell)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(),
+ uno::UNO_QUERY);
+ xDocProps.set(xDPS->getDocumentProperties());
+ }
+ else
+ {
+ // Clipboard document, read metadata from the meta field manager.
+ sw::MetaFieldManager& rManager = m_rDoc.GetMetaFieldManager();
+ xDocProps.set(rManager.getDocumentProperties());
+ }
+
+ if (xDocProps.is())
+ {
+ // Handle user-defined properties.
+ uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
+ = xDocProps->getUserDefinedProperties();
+ if (xUserDefinedProperties.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
+ uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties
+ = xPropertySet->getPropertySetInfo()->getProperties();
+
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (rProperty.Name.startsWith("Company"))
+ // We have explicit markup in RTF for this property.
+ continue;
+
+ // Property name.
+ Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_PROPNAME " ");
+ Strm().WriteOString(
+ msfilter::rtfutil::OutString(rProperty.Name, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+
+ // Property value.
+ OUString aValue;
+ double fValue;
+ bool bValue;
+ util::DateTime aDate;
+ uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
+ if (aAny >>= bValue)
+ {
+ WriteUserPropType(11);
+ WriteUserPropValue(OUString::number(static_cast<int>(bValue)));
+ }
+ else if (aAny >>= aValue)
+ {
+ WriteUserPropType(30);
+ WriteUserPropValue(aValue);
+ }
+ else if (aAny >>= fValue)
+ {
+ aValue = OUString::number(fValue);
+ if (aValue.indexOf('.') == -1)
+ {
+ // Integer.
+ WriteUserPropType(3);
+ WriteUserPropValue(aValue);
+ }
+ else
+ {
+ // Real number.
+ WriteUserPropType(5);
+ WriteUserPropValue(aValue);
+ }
+ }
+ else if (aAny >>= aDate)
+ {
+ WriteUserPropType(64);
+ // Format is 'YYYY. MM. DD.'.
+ aValue += OUString::number(aDate.Year) + ". ";
+ if (aDate.Month < 10)
+ aValue += "0";
+ aValue += OUString::number(aDate.Month) + ". ";
+ if (aDate.Day < 10)
+ aValue += "0";
+ aValue += OUString::number(aDate.Day) + ".";
+ WriteUserPropValue(aValue);
+ }
+ }
+ }
+ }
+
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteDocVars()
+{
+ SwDocShell* pDocShell(m_rDoc.GetDocShell());
+ if (!pDocShell)
+ return;
+
+ uno::Reference<text::XTextFieldsSupplier> xModel(pDocShell->GetModel(), uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTextFieldMasters = xModel->getTextFieldMasters();
+ uno::Sequence<rtl::OUString> aMasterNames = xTextFieldMasters->getElementNames();
+ if (!aMasterNames.hasElements())
+ {
+ return;
+ }
+
+ // Only write docVars if there will be at least a single docVar.
+ constexpr OUString aPrefix(u"com.sun.star.text.fieldmaster.User."_ustr);
+ for (const auto& rMasterName : std::as_const(aMasterNames))
+ {
+ if (!rMasterName.startsWith(aPrefix))
+ {
+ // Not a user field.
+ continue;
+ }
+
+ uno::Reference<beans::XPropertySet> xField;
+ xTextFieldMasters->getByName(rMasterName) >>= xField;
+ if (!xField.is())
+ {
+ continue;
+ }
+
+ OUString aKey = rMasterName.copy(aPrefix.getLength());
+ OUString aValue;
+ xField->getPropertyValue("Content") >>= aValue;
+
+ Strm().WriteChar('{').WriteOString(
+ OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_DOCVAR);
+ Strm().WriteChar(' ');
+
+ Strm().WriteChar('{');
+ Strm().WriteOString(msfilter::rtfutil::OutString(aKey, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+
+ Strm().WriteChar('{');
+ Strm().WriteOString(msfilter::rtfutil::OutString(aValue, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+
+ Strm().WriteChar('}');
+ }
+}
+
+ErrCode RtfExport::ExportDocument_Impl()
+{
+ // Make the header
+ Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_RTF)
+ .WriteChar('1')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_ANSI);
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_DEFF);
+ Strm().WriteNumberAsString(
+ m_aFontHelper.GetId(m_rDoc.GetAttrPool().GetDefaultItem(RES_CHRATR_FONT)));
+ // If this not exist, MS don't understand our ansi characters (0x80-0xff).
+ Strm().WriteOString("\\adeflang1025");
+
+ // Font table
+ WriteFonts();
+
+ m_pStyles = std::make_unique<MSWordStyles>(*this);
+ // Color and stylesheet table
+ WriteStyles();
+
+ // List table
+ BuildNumbering();
+ WriteNumbering();
+
+ WriteRevTab();
+
+ WriteInfo();
+ WriteUserProps();
+ WriteDocVars();
+
+ // Default TabSize
+ Strm().WriteOString(m_pAttrOutput->GetTabStop()).WriteOString(SAL_NEWLINE_STRING);
+ m_pAttrOutput->GetTabStop().setLength(0);
+
+ // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer.
+ // Set it's value to "auto" and disable on paragraph level, if no hyphenation is used there.
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_HYPHAUTO);
+ Strm().WriteOString("1");
+
+ // Zoom
+ SwViewShell* pViewShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ if (pViewShell && pViewShell->GetViewOptions()->GetZoomType() == SvxZoomType::PERCENT)
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_VIEWSCALE);
+ Strm().WriteNumberAsString(pViewShell->GetViewOptions()->GetZoom());
+ }
+ // Record changes?
+ if (RedlineFlags::On & m_nOrigRedlineFlags)
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_REVISIONS);
+ // Mirror margins?
+ if ((UseOnPage::Mirror & m_rDoc.GetPageDesc(0).ReadUseOn()) == UseOnPage::Mirror)
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_MARGMIRROR);
+
+ // Gutter at top?
+ IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::GUTTER_AT_TOP))
+ {
+ Strm().WriteOString(LO_STRING_SVTOOLS_RTF_GUTTERPRL);
+ }
+
+ // Init sections
+ m_pSections = std::make_unique<MSWordSections>(*this);
+
+ // Enable form protection by default if needed, as there is no switch to
+ // enable it on a per-section basis. OTOH don't always enable it as it
+ // breaks moving of drawings - so write it only in case there is really a
+ // protected section in the document.
+ for (auto const& pSectionFormat : m_rDoc.GetSections())
+ {
+ if (!pSectionFormat->IsInUndo() && pSectionFormat->GetProtect().IsContentProtected())
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FORMPROT);
+ break;
+ }
+ }
+
+ // enable form field shading
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FORMSHADE);
+
+ // Enable breaking wrapped tables across pages: the "no" in the control word's name is
+ // confusing.
+ if (!rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES))
+ {
+ Strm().WriteOString(LO_STRING_SVTOOLS_RTF_NOBRKWRPTBL);
+ }
+
+ // size and empty margins of the page
+ if (m_rDoc.GetPageDescCnt())
+ {
+ // Seeking the first SwFormatPageDesc. If no set, the default is valid
+ const SwFormatPageDesc* pSttPgDsc = nullptr;
+ {
+ const SwNode& rSttNd
+ = *m_rDoc.GetNodes()[m_rDoc.GetNodes().GetEndOfExtras().GetIndex() + 2];
+ const SfxItemSet* pSet = nullptr;
+
+ if (rSttNd.IsContentNode())
+ pSet = &rSttNd.GetContentNode()->GetSwAttrSet();
+ else if (rSttNd.IsTableNode())
+ pSet = &rSttNd.GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet();
+
+ else if (rSttNd.IsSectionNode())
+ pSet = &rSttNd.GetSectionNode()->GetSection().GetFormat()->GetAttrSet();
+
+ if (pSet)
+ {
+ std::size_t nPosInDoc;
+ pSttPgDsc = &pSet->Get(RES_PAGEDESC);
+ if (!pSttPgDsc->GetPageDesc())
+ pSttPgDsc = nullptr;
+ else if (m_rDoc.FindPageDesc(pSttPgDsc->GetPageDesc()->GetName(), &nPosInDoc))
+ {
+ Strm()
+ .WriteChar('{')
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_PGDSCNO);
+ Strm().WriteNumberAsString(nPosInDoc).WriteChar('}');
+ }
+ }
+ }
+ const SwPageDesc& rPageDesc = pSttPgDsc ? *pSttPgDsc->GetPageDesc() : m_rDoc.GetPageDesc(0);
+ const SwFrameFormat& rFormatPage = rPageDesc.GetMaster();
+
+ {
+ if (rPageDesc.GetLandscape())
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LANDSCAPE);
+
+ const SwFormatFrameSize& rSz = rFormatPage.GetFrameSize();
+ // Clipboard document is always created without a printer, then
+ // the size will be always LONG_MAX! Solution then is to use A4
+ if (LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth())
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERH);
+ Size a4 = SvxPaperInfo::GetPaperSize(PAPER_A4);
+ Strm().WriteNumberAsString(a4.Height()).WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERW);
+ Strm().WriteNumberAsString(a4.Width());
+ }
+ else
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERH);
+ Strm()
+ .WriteNumberAsString(rSz.GetHeight())
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERW);
+ Strm().WriteNumberAsString(rSz.GetWidth());
+ }
+ }
+
+ {
+ const SvxLRSpaceItem& rLR = rFormatPage.GetLRSpace();
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_MARGL);
+ Strm().WriteNumberAsString(rLR.GetLeft()).WriteOString(OOO_STRING_SVTOOLS_RTF_MARGR);
+ Strm().WriteNumberAsString(rLR.GetRight());
+ }
+
+ {
+ const SvxULSpaceItem& rUL = rFormatPage.GetULSpace();
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_MARGT);
+ Strm().WriteNumberAsString(rUL.GetUpper()).WriteOString(OOO_STRING_SVTOOLS_RTF_MARGB);
+ Strm().WriteNumberAsString(rUL.GetLower());
+ }
+
+ Strm()
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SECTD)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_SBKNONE);
+ m_pAttrOutput->SectFootnoteEndnotePr();
+ // All sections are unlocked by default
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
+ Strm().WriteOString("1");
+ OutPageDescription(rPageDesc, true); // Changed bCheckForFirstPage to true so headers
+ // following title page are correctly added - i13107
+ if (pSttPgDsc)
+ {
+ m_pCurrentPageDesc = &rPageDesc;
+ }
+ }
+
+ // line numbering
+ const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
+ if (rLnNumInfo.IsPaintLineNumbers())
+ {
+ sal_uLong nLnNumRestartNo = 0;
+ if (const WW8_SepInfo* pSectionInfo = m_pSections->CurrentSectionInfo())
+ nLnNumRestartNo = pSectionInfo->nLnNumRestartNo;
+
+ AttrOutput().SectionLineNumbering(nLnNumRestartNo, rLnNumInfo);
+ }
+
+ {
+ // write the footnotes and endnotes-out Info
+ const SwFootnoteInfo& rFootnoteInfo = m_rDoc.GetFootnoteInfo();
+
+ const char* pOut = FTNPOS_CHAPTER == rFootnoteInfo.m_ePos ? OOO_STRING_SVTOOLS_RTF_ENDDOC
+ : OOO_STRING_SVTOOLS_RTF_FTNBJ;
+ Strm().WriteOString(pOut).WriteOString(OOO_STRING_SVTOOLS_RTF_FTNSTART);
+ Strm().WriteNumberAsString(rFootnoteInfo.m_nFootnoteOffset + 1);
+
+ switch (rFootnoteInfo.m_eNum)
+ {
+ case FTNNUM_PAGE:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTPG;
+ break;
+ case FTNNUM_DOC:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTCONT;
+ break;
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNRESTART;
+ break;
+ }
+ Strm().WriteOString(pOut);
+
+ switch (rFootnoteInfo.m_aFormat.GetNumberingType())
+ {
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNALC;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNAUC;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNRLC;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNRUC;
+ break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNCHI;
+ break;
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNAR;
+ break;
+ }
+ Strm().WriteOString(pOut);
+
+ const SwEndNoteInfo& rEndNoteInfo = m_rDoc.GetEndNoteInfo();
+
+ Strm()
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_AENDDOC)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_AFTNRSTCONT)
+ .WriteOString(OOO_STRING_SVTOOLS_RTF_AFTNSTART);
+ Strm().WriteNumberAsString(rEndNoteInfo.m_nFootnoteOffset + 1);
+
+ switch (rEndNoteInfo.m_aFormat.GetNumberingType())
+ {
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNALC;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAUC;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRLC;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRUC;
+ break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNCHI;
+ break;
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAR;
+ break;
+ }
+ Strm().WriteOString(pOut);
+ }
+
+ if (!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX))
+ // RTF default is true, so write compat flag if this should be false.
+ Strm().WriteOString(LO_STRING_SVTOOLS_RTF_HTMAUTSP);
+
+ Strm().WriteOString(SAL_NEWLINE_STRING);
+
+ WriteFootnoteSettings();
+
+ WriteMainText();
+
+ Strm().WriteChar('}');
+
+ return ERRCODE_NONE;
+}
+
+void RtfExport::PrepareNewPageDesc(const SfxItemSet* pSet, const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc, bool bExtraPageBreak)
+{
+ const SwSectionFormat* pFormat = GetSectionFormat(rNd);
+ const sal_uLong nLnNm = GetSectionLineNo(pSet, rNd);
+
+ OSL_ENSURE(pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided.");
+
+ if (pNewPgDescFormat)
+ m_pSections->AppendSection(*pNewPgDescFormat, rNd, pFormat, nLnNm);
+ else if (pNewPgDesc)
+ m_pSections->AppendSection(pNewPgDesc, rNd, pFormat, nLnNm);
+
+ // Don't insert a page break, when we're changing page style just because the next page has to be a different one.
+ if (!m_pAttrOutput->GetPrevPageDesc()
+ || m_pAttrOutput->GetPrevPageDesc()->GetFollow() != pNewPgDesc)
+ AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo(),
+ bExtraPageBreak);
+}
+
+bool RtfExport::DisallowInheritingOutlineNumbering(const SwFormat& rFormat)
+{
+ bool bRet(false);
+
+ if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
+ {
+ if (const SwFormat* pParent = rFormat.DerivedFrom())
+ {
+ if (static_cast<const SwTextFormatColl*>(pParent)
+ ->IsAssignedToListLevelOfOutlineStyle())
+ {
+ // Level 9 disables the outline
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVEL).WriteInt32(9);
+
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void RtfExport::OutputEndNode(const SwEndNode& rEndNode)
+{
+ if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode())
+ // End node of a table: see if a section break should be written after the table.
+ AttrOutput().SectionBreaks(rEndNode);
+}
+
+void RtfExport::OutputGrfNode(const SwGrfNode& /*rGrfNode*/)
+{
+ /* noop, see RtfAttributeOutput::FlyFrameGraphic */
+}
+
+void RtfExport::OutputOLENode(const SwOLENode& /*rOLENode*/)
+{
+ /* noop, see RtfAttributeOutput::FlyFrameOLE */
+}
+
+void RtfExport::OutputLinkedOLE(const OUString& /*rLinked*/) {}
+
+void RtfExport::OutputTextNode(SwTextNode& rNode)
+{
+ m_nCurrentNodeIndex = rNode.GetIndex();
+ if (!m_bOutOutlineOnly || rNode.IsOutline())
+ MSWordExportBase::OutputTextNode(rNode);
+ m_nCurrentNodeIndex = SwNodeOffset(0);
+}
+
+void RtfExport::AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat* pFormat,
+ sal_uLong nLnNum)
+{
+ m_pSections->AppendSection(pPageDesc, pFormat, nLnNum);
+ AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo());
+}
+
+RtfExport::RtfExport(RtfExportFilter* pFilter, SwDoc& rDocument,
+ std::shared_ptr<SwUnoCursor>& pCurrentPam, SwPaM& rOriginalPam,
+ Writer* pWriter, bool bOutOutlineOnly)
+ : MSWordExportBase(rDocument, pCurrentPam, &rOriginalPam)
+ , m_pFilter(pFilter)
+ , m_pWriter(pWriter)
+ , m_bOutOutlineOnly(bOutOutlineOnly)
+ , m_eDefaultEncoding(
+ rtl_getTextEncodingFromWindowsCharset(sw::ms::rtl_TextEncodingToWinCharset(DEF_ENCODING)))
+ , m_eCurrentEncoding(m_eDefaultEncoding)
+ , m_bRTFFlySyntax(false)
+ , m_nCurrentNodeIndex(0)
+{
+ m_bExportModeRTF = true;
+ // the attribute output for the document
+ m_pAttrOutput = std::make_unique<RtfAttributeOutput>(*this);
+ // that just causes problems for RTF
+ m_bSubstituteBullets = false;
+ // needed to have a complete font table
+ m_aFontHelper.m_bLoadAllFonts = true;
+ // the related SdrExport
+ m_pSdrExport = std::make_unique<RtfSdrExport>(*this);
+
+ if (!m_pWriter)
+ m_pWriter = &m_pFilter->GetWriter();
+}
+
+RtfExport::~RtfExport() = default;
+
+SvStream& RtfExport::Strm()
+{
+ if (m_pStream)
+ return *m_pStream;
+
+ return m_pWriter->Strm();
+}
+
+void RtfExport::setStream() { m_pStream = std::make_unique<SvMemoryStream>(); }
+
+OString RtfExport::getStream()
+{
+ OString aRet;
+
+ if (m_pStream)
+ aRet = OString(static_cast<const char*>(m_pStream->GetData()), m_pStream->Tell());
+
+ return aRet;
+}
+
+void RtfExport::resetStream() { m_pStream.reset(); }
+
+void RtfExport::OutUnicode(std::string_view pToken, std::u16string_view rContent, bool bUpr)
+{
+ if (rContent.empty())
+ return;
+
+ if (!bUpr)
+ {
+ Strm().WriteChar('{').WriteOString(pToken).WriteChar(' ');
+ Strm().WriteOString(msfilter::rtfutil::OutString(rContent, m_eCurrentEncoding));
+ Strm().WriteChar('}');
+ }
+ else
+ Strm().WriteOString(msfilter::rtfutil::OutStringUpr(pToken, rContent, m_eCurrentEncoding));
+}
+
+void RtfExport::OutDateTime(std::string_view pStr, const util::DateTime& rDT)
+{
+ Strm().WriteChar('{').WriteOString(pStr).WriteOString(OOO_STRING_SVTOOLS_RTF_YR);
+ Strm().WriteNumberAsString(rDT.Year).WriteOString(OOO_STRING_SVTOOLS_RTF_MO);
+ Strm().WriteNumberAsString(rDT.Month).WriteOString(OOO_STRING_SVTOOLS_RTF_DY);
+ Strm().WriteNumberAsString(rDT.Day).WriteOString(OOO_STRING_SVTOOLS_RTF_HR);
+ Strm().WriteNumberAsString(rDT.Hours).WriteOString(OOO_STRING_SVTOOLS_RTF_MIN);
+ Strm().WriteNumberAsString(rDT.Minutes).WriteChar('}');
+}
+
+sal_uInt16 RtfExport::GetColor(const Color& rColor) const
+{
+ for (const auto& rEntry : m_aColTable)
+ if (rEntry.second == rColor)
+ {
+ SAL_INFO("sw.rtf", __func__ << " returning " << rEntry.first << " (" << rColor.GetRed()
+ << "," << rColor.GetGreen() << "," << rColor.GetBlue()
+ << ")");
+ return rEntry.first;
+ }
+ OSL_FAIL("No such Color in m_aColTable!");
+ return 0;
+}
+
+void RtfExport::InsColor(const Color& rCol)
+{
+ sal_uInt16 n;
+ bool bAutoColorInTable = false;
+ for (const auto& rEntry : m_aColTable)
+ {
+ if (rEntry.second == rCol)
+ return; // Already in the table
+ if (rEntry.second == COL_AUTO)
+ bAutoColorInTable = true;
+ }
+ if (rCol == COL_AUTO)
+ // COL_AUTO gets value 0
+ n = 0;
+ else
+ {
+ // other colors get values >0
+ n = m_aColTable.size();
+ if (!bAutoColorInTable)
+ // reserve value "0" for COL_AUTO (if COL_AUTO wasn't inserted until now)
+ n++;
+ }
+ m_aColTable.insert(std::pair<sal_uInt16, Color>(n, rCol));
+}
+
+void RtfExport::InsColorLine(const SvxBoxItem& rBox)
+{
+ const editeng::SvxBorderLine* pLine = nullptr;
+
+ if (rBox.GetTop())
+ {
+ pLine = rBox.GetTop();
+ InsColor(pLine->GetColor());
+ }
+ if (rBox.GetBottom() && pLine != rBox.GetBottom())
+ {
+ pLine = rBox.GetBottom();
+ InsColor(pLine->GetColor());
+ }
+ if (rBox.GetLeft() && pLine != rBox.GetLeft())
+ {
+ pLine = rBox.GetLeft();
+ InsColor(pLine->GetColor());
+ }
+ if (rBox.GetRight() && pLine != rBox.GetRight())
+ InsColor(rBox.GetRight()->GetColor());
+}
+
+void RtfExport::OutColorTable()
+{
+ // Build the table from rPool since the colors provided to
+ // RtfAttributeOutput callbacks are too late.
+ const SfxItemPool& rPool = m_rDoc.GetAttrPool();
+
+ // MSO Word uses a default color table with 16 colors (which is used e.g. for highlighting)
+ InsColor(COL_BLACK);
+ InsColor(COL_LIGHTBLUE);
+ InsColor(COL_LIGHTCYAN);
+ InsColor(COL_LIGHTGREEN);
+ InsColor(COL_LIGHTMAGENTA);
+ InsColor(COL_LIGHTRED);
+ InsColor(COL_YELLOW);
+ InsColor(COL_WHITE);
+ InsColor(COL_BLUE);
+ InsColor(COL_CYAN);
+ InsColor(COL_GREEN);
+ InsColor(COL_MAGENTA);
+ InsColor(COL_RED);
+ InsColor(COL_BROWN);
+ InsColor(COL_GRAY);
+ InsColor(COL_LIGHTGRAY);
+
+ // char color
+ {
+ auto pCol = GetDfltAttr(RES_CHRATR_COLOR);
+ InsColor(pCol->GetValue());
+ pCol = rPool.GetPoolDefaultItem(RES_CHRATR_COLOR);
+ if (pCol)
+ InsColor(pCol->GetValue());
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_COLOR))
+ {
+ pCol = dynamic_cast<const SvxColorItem*>(pItem);
+ if (pCol)
+ InsColor(pCol->GetValue());
+ }
+
+ auto pUnder = GetDfltAttr(RES_CHRATR_UNDERLINE);
+ InsColor(pUnder->GetColor());
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_UNDERLINE))
+ {
+ pUnder = dynamic_cast<const SvxUnderlineItem*>(pItem);
+ if (pUnder)
+ InsColor(pUnder->GetColor());
+ }
+
+ auto pOver = GetDfltAttr(RES_CHRATR_OVERLINE);
+ InsColor(pOver->GetColor());
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_OVERLINE))
+ {
+ pOver = dynamic_cast<const SvxOverlineItem*>(pItem);
+ if (pOver)
+ InsColor(pOver->GetColor());
+ }
+ }
+
+ // background color
+ static const sal_uInt16 aBrushIds[] = { RES_BACKGROUND, RES_CHRATR_BACKGROUND, 0 };
+
+ for (const sal_uInt16* pIds = aBrushIds; *pIds; ++pIds)
+ {
+ auto pBackground = static_cast<const SvxBrushItem*>(GetDfltAttr(*pIds));
+ InsColor(pBackground->GetColor());
+ pBackground = static_cast<const SvxBrushItem*>(rPool.GetPoolDefaultItem(*pIds));
+ if (pBackground)
+ {
+ InsColor(pBackground->GetColor());
+ }
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pIds))
+ {
+ pBackground = static_cast<const SvxBrushItem*>(pItem);
+ if (pBackground)
+ {
+ InsColor(pBackground->GetColor());
+ }
+ }
+ }
+
+ // shadow color
+ {
+ auto pShadow = GetDfltAttr(RES_SHADOW);
+ InsColor(pShadow->GetColor());
+ pShadow = rPool.GetPoolDefaultItem(RES_SHADOW);
+ if (nullptr != pShadow)
+ {
+ InsColor(pShadow->GetColor());
+ }
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_SHADOW))
+ {
+ pShadow = dynamic_cast<const SvxShadowItem*>(pItem);
+ if (pShadow)
+ {
+ InsColor(pShadow->GetColor());
+ }
+ }
+ }
+
+ // frame border color
+ {
+ const SvxBoxItem* pBox = rPool.GetPoolDefaultItem(RES_BOX);
+ if (nullptr != pBox)
+ InsColorLine(*pBox);
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_BOX))
+ {
+ pBox = dynamic_cast<const SvxBoxItem*>(pItem);
+ if (pBox)
+ InsColorLine(*pBox);
+ }
+ }
+
+ {
+ const SvxBoxItem* pCharBox = rPool.GetPoolDefaultItem(RES_CHRATR_BOX);
+ if (pCharBox)
+ InsColorLine(*pCharBox);
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_BOX))
+ {
+ pCharBox = dynamic_cast<const SvxBoxItem*>(pItem);
+ if (pCharBox)
+ InsColorLine(*pCharBox);
+ }
+ }
+
+ // TextFrame or paragraph background solid fill.
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLCOLOR))
+ {
+ if (auto pColorItem = dynamic_cast<const XFillColorItem*>(pItem))
+ InsColor(pColorItem->GetColorValue());
+ }
+
+ for (std::size_t n = 0; n < m_aColTable.size(); ++n)
+ {
+ const Color& rCol = m_aColTable[n];
+ if (n || COL_AUTO != rCol)
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_RED);
+ Strm().WriteNumberAsString(rCol.GetRed()).WriteOString(OOO_STRING_SVTOOLS_RTF_GREEN);
+ Strm().WriteNumberAsString(rCol.GetGreen()).WriteOString(OOO_STRING_SVTOOLS_RTF_BLUE);
+ Strm().WriteNumberAsString(rCol.GetBlue());
+ }
+ Strm().WriteChar(';');
+ }
+}
+
+void RtfExport::InsStyle(sal_uInt16 nId, const OString& rStyle)
+{
+ m_aStyTable.insert(std::pair<sal_uInt16, OString>(nId, rStyle));
+}
+
+OString* RtfExport::GetStyle(sal_uInt16 nId)
+{
+ auto it = m_aStyTable.find(nId);
+ if (it != m_aStyTable.end())
+ return &it->second;
+ return nullptr;
+}
+
+sal_uInt16 RtfExport::GetRedline(const OUString& rAuthor)
+{
+ const sal_uInt16 nId = m_aRedlineTable.size();
+ // insert if we don't already have one
+ auto[it, inserted] = m_aRedlineTable.insert(std::pair<OUString, sal_uInt16>(rAuthor, nId));
+ (void)inserted;
+ return it->second;
+}
+
+const OUString* RtfExport::GetRedline(sal_uInt16 nId)
+{
+ for (const auto& rEntry : m_aRedlineTable)
+ if (rEntry.second == nId)
+ return &rEntry.first;
+ return nullptr;
+}
+
+void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirstPage)
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+ const SwPageDesc* pSave = m_pCurrentPageDesc;
+
+ m_pCurrentPageDesc = &rPgDsc;
+ if (bCheckForFirstPage && m_pCurrentPageDesc->GetFollow()
+ && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
+ m_pCurrentPageDesc = m_pCurrentPageDesc->GetFollow();
+
+ if (m_pCurrentPageDesc->GetLandscape())
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LNDSCPSXN);
+
+ const SwFormat* pFormat = &m_pCurrentPageDesc->GetMaster(); //GetLeft();
+ m_bOutPageDescs = true;
+ if (m_pCurrentPageDesc != &rPgDsc)
+ m_pFirstPageItemSet = &rPgDsc.GetMaster().GetAttrSet();
+ OutputFormat(*pFormat, true, false);
+ m_pFirstPageItemSet = nullptr;
+ m_bOutPageDescs = false;
+
+ // normal header / footer (without a style)
+ const SfxPoolItem* pItem;
+ if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, true);
+ if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, false);
+
+ // title page
+ if (m_pCurrentPageDesc != &rPgDsc)
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_TITLEPG);
+ m_pCurrentPageDesc = &rPgDsc;
+ if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, true);
+ if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, false);
+ }
+
+ // numbering type
+ AttrOutput().SectionPageNumbering(m_pCurrentPageDesc->GetNumType().GetNumberingType(),
+ std::nullopt);
+
+ m_pCurrentPageDesc = pSave;
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader)
+{
+ if (bHeader)
+ {
+ const auto& rHeader = static_cast<const SwFormatHeader&>(rItem);
+ if (!rHeader.IsActive())
+ return;
+ }
+ else
+ {
+ const auto& rFooter = static_cast<const SwFormatFooter&>(rItem);
+ if (!rFooter.IsActive())
+ return;
+ }
+
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ const char* pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER);
+ /* is this a title page? */
+ if ((m_pCurrentPageDesc->GetFollow() && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
+ || !m_pCurrentPageDesc->IsFirstShared())
+ {
+ Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_TITLEPG);
+ pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF);
+ }
+
+ Strm().WriteChar('{').WriteOString(pStr);
+ WriteHeaderFooterText(m_pCurrentPageDesc->IsFirstShared()
+ ? m_pCurrentPageDesc->GetMaster()
+ : m_pCurrentPageDesc->GetFirstMaster(),
+ bHeader);
+ Strm().WriteChar('}');
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr,
+ bool bTitlepg)
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ m_pAttrOutput->WriteHeaderFooter_Impl(rFormat, bHeader, pStr, bTitlepg);
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+namespace
+{
+/// Glue class to call RtfExport as an internal filter, needed by copy&paste support.
+class SwRTFWriter : public Writer
+{
+private:
+ bool m_bOutOutlineOnly;
+
+public:
+ SwRTFWriter(std::u16string_view rFilterName, const OUString& rBaseURL);
+
+ ErrCode WriteStream() override;
+};
+}
+
+SwRTFWriter::SwRTFWriter(std::u16string_view rFilterName, const OUString& rBaseURL)
+{
+ SetBaseURL(rBaseURL);
+ // export outline nodes, only (send outline to clipboard/presentation)
+ m_bOutOutlineOnly = o3tl::starts_with(rFilterName, u"O");
+}
+
+ErrCode SwRTFWriter::WriteStream()
+{
+ std::shared_ptr<SwUnoCursor> pCurPam(m_pDoc->CreateUnoCursor(*m_pCurrentPam->End(), false));
+ pCurPam->SetMark();
+ *pCurPam->GetPoint() = *m_pCurrentPam->Start();
+ RtfExport aExport(nullptr, *m_pDoc, pCurPam, *m_pCurrentPam, this, m_bOutOutlineOnly);
+ aExport.ExportDocument(true);
+ return ERRCODE_NONE;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void ExportRTF(std::u16string_view rFltName,
+ const OUString& rBaseURL, WriterRef& xRet)
+{
+ xRet = new SwRTFWriter(rFltName, rBaseURL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfexport.hxx b/sw/source/filter/ww8/rtfexport.hxx
new file mode 100644
index 0000000000..11f3f0471f
--- /dev/null
+++ b/sw/source/filter/ww8/rtfexport.hxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORT_HXX
+
+#include <memory>
+#include "wrtww8.hxx"
+
+class RtfAttributeOutput;
+class RtfExportFilter;
+class RtfSdrExport;
+using RtfColorTable = std::map<sal_uInt16, Color>;
+class SwNode;
+class SwTextNode;
+class SwGrfNode;
+class SwOLENode;
+
+/// The class that does all the actual RTF export-related work.
+class RtfExport : public MSWordExportBase
+{
+ /// Pointer to the filter that owns us.
+ RtfExportFilter* m_pFilter;
+ Writer* m_pWriter;
+
+ /// Attribute output for document.
+ std::unique_ptr<RtfAttributeOutput> m_pAttrOutput;
+
+ /// Sections/headers/footers
+ std::unique_ptr<MSWordSections> m_pSections;
+
+ std::unique_ptr<RtfSdrExport> m_pSdrExport;
+ bool m_bOutOutlineOnly;
+
+public:
+ /// Access to the attribute output class.
+ AttributeOutputBase& AttrOutput() const override;
+
+ /// Access to the sections/headers/footres.
+ MSWordSections& Sections() const override;
+
+ /// Access to the Rtf Sdr exporter.
+ RtfSdrExport& SdrExporter() const;
+
+ bool FieldsQuoted() const override { return true; }
+
+ bool AddSectionBreaksForTOX() const override { return false; }
+
+ bool PreferPageBreakBefore() const override { return true; }
+
+ /// Guess the script (asian/western).
+ bool CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich) override;
+
+ void AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen,
+ const SwRedlineData* pSwRedlineData = nullptr) override;
+
+ void AppendBookmark(const OUString& rName) override;
+
+ void AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos,
+ sal_Int32 nLen) override;
+
+ //For i120928,add an interface to export graphic of bullet
+ void ExportGrfBullet(const SwTextNode& rNd) override;
+
+ void
+ WriteCR(ww8::WW8TableNodeInfoInner::
+ Pointer_t /*pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t()*/)
+ override
+ {
+ /* no-op for rtf, most probably should not even be in MSWordExportBase */
+ }
+ void WriteChar(sal_Unicode c) override;
+
+ /// Write the numbering table.
+ void WriteNumbering() override;
+
+ /// Write the revision table.
+ void WriteRevTab();
+
+ /// Output the actual headers and footers.
+ void WriteHeadersFooters(sal_uInt8 nHeadFootFlags, const SwFrameFormat& rFormat,
+ const SwFrameFormat& rLeftHeaderFormat,
+ const SwFrameFormat& rLeftFooterFormat,
+ const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode,
+ bool bEvenAndOddHeaders) override;
+
+ /// Write the field
+ void OutputField(const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd,
+ FieldFlags nMode = FieldFlags::All) override;
+
+ /// Write the data of the form field
+ void WriteFormData(const ::sw::mark::IFieldmark& rFieldmark) override;
+ void WriteHyperlinkData(const ::sw::mark::IFieldmark& rFieldmark) override;
+
+ void DoComboBox(const OUString& rName, const OUString& rHelp, const OUString& ToolTip,
+ const OUString& rSelected,
+ const css::uno::Sequence<OUString>& rListItems) override;
+
+ void DoFormText(const SwInputField* pField) override;
+
+ sal_uInt64 ReplaceCr(sal_uInt8 nChar) override;
+
+ ExportFormat GetExportFormat() const override { return ExportFormat::RTF; }
+
+protected:
+ /// Format-dependent part of the actual export.
+ ErrCode ExportDocument_Impl() override;
+
+ void SectionBreaksAndFrames(const SwTextNode& /*rNode*/) override {}
+
+ /// Get ready for a new section.
+ void PrepareNewPageDesc(const SfxItemSet* pSet, const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat, const SwPageDesc* pNewPgDesc,
+ bool bExtraPageBreak = false) override;
+
+ /// Return value indicates if an inherited outline numbering is suppressed.
+ bool DisallowInheritingOutlineNumbering(const SwFormat& rFormat) override;
+
+ /// Output SwTextNode is depending on outline export mode
+ void OutputTextNode(SwTextNode& rNode) override;
+
+ /// Output SwEndNode
+ void OutputEndNode(const SwEndNode& rEndNode) override;
+
+ /// Output SwGrfNode
+ void OutputGrfNode(const SwGrfNode& rGrfNode) override;
+
+ /// Output SwOLENode
+ void OutputOLENode(const SwOLENode& rOLENode) override;
+
+ void OutputLinkedOLE(const OUString& rLink) override;
+
+ void AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat* pFormat,
+ sal_uLong nLnNum) override;
+
+public:
+ /// Pass the pDocument, pCurrentPam and pOriginalPam to the base class.
+ RtfExport(RtfExportFilter* pFilter, SwDoc& rDocument, std::shared_ptr<SwUnoCursor>& pCurrentPam,
+ SwPaM& rOriginalPam, Writer* pWriter, bool bOutOutlineOnly = false);
+
+ RtfExport(const RtfExport&) = delete;
+
+ RtfExport& operator=(const RtfExport&) = delete;
+
+ /// Destructor.
+ ~RtfExport() override;
+
+private:
+ rtl_TextEncoding m_eDefaultEncoding;
+ rtl_TextEncoding m_eCurrentEncoding;
+ /// This is used by OutputFlyFrame_Impl() to control the written syntax
+ bool m_bRTFFlySyntax;
+ /// Index of the current SwTextNode, if any.
+ SwNodeOffset m_nCurrentNodeIndex;
+
+public:
+ rtl_TextEncoding GetDefaultEncoding() const { return m_eDefaultEncoding; }
+ void SetCurrentEncoding(rtl_TextEncoding eCurrentEncoding)
+ {
+ m_eCurrentEncoding = eCurrentEncoding;
+ }
+ rtl_TextEncoding GetCurrentEncoding() const { return m_eCurrentEncoding; }
+ void SetRTFFlySyntax(bool bRTFFlySyntax) { m_bRTFFlySyntax = bRTFFlySyntax; }
+ bool GetRTFFlySyntax() const { return m_bRTFFlySyntax; }
+ SwNodeOffset GetCurrentNodeIndex() const { return m_nCurrentNodeIndex; }
+ SvStream& Strm();
+ /// From now on, let Strm() return a memory stream, not a real one.
+ void setStream();
+ /// Get the contents of the memory stream as a string.
+ OString getStream();
+ /// Return back to the real stream.
+ void resetStream();
+ void OutUnicode(std::string_view pToken, std::u16string_view rContent, bool bUpr = false);
+ void OutDateTime(std::string_view pStr, const css::util::DateTime& rDT);
+ void OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirstPage);
+
+ sal_uInt16 GetColor(const Color& rColor) const;
+ void InsColor(const Color& rCol);
+ void InsColorLine(const SvxBoxItem& rBox);
+ void OutColorTable();
+ sal_uInt16 GetRedline(const OUString& rAuthor);
+ const OUString* GetRedline(sal_uInt16 nId);
+
+ void InsStyle(sal_uInt16 nId, const OString& rStyle);
+ OString* GetStyle(sal_uInt16 nId);
+
+ const SfxItemSet* GetFirstPageItemSet() const { return m_pFirstPageItemSet; }
+
+private:
+ void WriteFonts();
+ void WriteStyles();
+ void WriteFootnoteSettings();
+ void WriteMainText();
+ void WriteInfo();
+ /// Writes a single user property type.
+ void WriteUserPropType(int nType);
+ /// Writes a single user property value.
+ void WriteUserPropValue(std::u16string_view rValue);
+ /// Writes the userprops group: user defined document properties.
+ void WriteUserProps();
+ /// Writes document variables
+ void WriteDocVars();
+ /// This is necessary to have the numbering table ready before the main text is being processed.
+ void BuildNumbering();
+ void WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader);
+ void WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr,
+ bool bTitlepg = false);
+
+ RtfColorTable m_aColTable;
+ std::map<sal_uInt16, OString> m_aStyTable;
+ std::map<OUString, sal_uInt16> m_aRedlineTable;
+ /// If set, then Strm() returns this stream, instead of m_pWriter's stream.
+ std::unique_ptr<SvMemoryStream> m_pStream;
+ /// Item set of the first page during export of a follow page format.
+ const SfxItemSet* m_pFirstPageItemSet = nullptr;
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfexportfilter.cxx b/sw/source/filter/ww8/rtfexportfilter.cxx
new file mode 100644
index 0000000000..8301672890
--- /dev/null
+++ b/sw/source/filter/ww8/rtfexportfilter.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "rtfexportfilter.hxx"
+#include "rtfexport.hxx"
+
+#include <docsh.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <unotxdoc.hxx>
+#include <viewsh.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+
+using namespace ::com::sun::star;
+
+RtfExportFilter::RtfExportFilter(uno::Reference<uno::XComponentContext> xCtx)
+ : m_xCtx(std::move(xCtx))
+{
+}
+
+RtfExportFilter::~RtfExportFilter() = default;
+
+sal_Bool RtfExportFilter::filter(const uno::Sequence<beans::PropertyValue>& aDescriptor)
+{
+ utl::MediaDescriptor aMediaDesc = aDescriptor;
+ uno::Reference<io::XStream> xStream = aMediaDesc.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_STREAMFOROUTPUT, uno::Reference<io::XStream>());
+ std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(xStream, true);
+ m_aWriter.SetStream(pStream.get());
+
+ // get SwDoc*
+ uno::Reference<uno::XInterface> xIfc(m_xSrcDoc, uno::UNO_QUERY);
+ auto pTextDoc = dynamic_cast<SwXTextDocument*>(xIfc.get());
+ if (!pTextDoc)
+ {
+ return false;
+ }
+
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ if (!pDoc)
+ {
+ return false;
+ }
+
+ // fdo#37161 - update layout (if present), for SwWriteTable
+ SwViewShell* pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if (pViewShell != nullptr)
+ pViewShell->CalcLayout();
+
+ // get SwPaM*
+ // we get SwPaM for the entire document; copy&paste is handled internally, not via UNO
+ SwPaM aPam(pDoc->GetNodes().GetEndOfContent());
+ aPam.SetMark();
+ aPam.Move(fnMoveBackward, GoInDoc);
+
+ std::shared_ptr<SwUnoCursor> pCurPam(pDoc->CreateUnoCursor(*aPam.End(), false));
+ pCurPam->SetMark();
+ *pCurPam->GetPoint() = *aPam.Start();
+
+ // export the document
+ // (in a separate block so that it's destructed before the commit)
+ {
+ RtfExport aExport(this, *pDoc, pCurPam, aPam, nullptr);
+ aExport.ExportDocument(true);
+ }
+
+ // delete the pCurPam
+ while (pCurPam->GetNext() != pCurPam.get())
+ delete pCurPam->GetNext();
+
+ return true;
+}
+
+void RtfExportFilter::cancel() {}
+
+void RtfExportFilter::setSourceDocument(const uno::Reference<lang::XComponent>& xDoc)
+{
+ m_xSrcDoc = xDoc;
+}
+
+OUString RtfExportFilter::getImplementationName() { return "com.sun.star.comp.Writer.RtfExport"; }
+
+sal_Bool RtfExportFilter::supportsService(OUString const& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> RtfExportFilter::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.Writer.RtfExport" };
+}
+
+// UNO helpers
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_comp_Writer_RtfExport_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new RtfExportFilter(pCtx));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfexportfilter.hxx b/sw/source/filter/ww8/rtfexportfilter.hxx
new file mode 100644
index 0000000000..d58a6be5a8
--- /dev/null
+++ b/sw/source/filter/ww8/rtfexportfilter.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORTFILTER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORTFILTER_HXX
+
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <shellio.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+/// Dummy Writer implementation to be able to use the string format methods of the base class
+class RtfWriter : public Writer
+{
+protected:
+ ErrCode WriteStream() override { return ERRCODE_NONE; }
+};
+
+/// The physical access to the RTF document (for writing).
+class RtfExportFilter final
+ : public cppu::WeakImplHelper<css::document::XFilter, css::document::XExporter,
+ css::lang::XServiceInfo>
+{
+ css::uno::Reference<css::uno::XComponentContext> m_xCtx;
+ css::uno::Reference<css::lang::XComponent> m_xSrcDoc;
+ RtfWriter m_aWriter;
+
+public:
+ explicit RtfExportFilter(css::uno::Reference<css::uno::XComponentContext> xCtx);
+ ~RtfExportFilter() override;
+
+ // XFilter
+ sal_Bool SAL_CALL
+ filter(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor) override;
+ void SAL_CALL cancel() override;
+
+ // XExporter
+ void SAL_CALL
+ setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDoc) override;
+
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override;
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ Writer& GetWriter() { return m_aWriter; }
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORTFILTER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfsdrexport.cxx b/sw/source/filter/ww8/rtfsdrexport.cxx
new file mode 100644
index 0000000000..d6427dd45f
--- /dev/null
+++ b/sw/source/filter/ww8/rtfsdrexport.cxx
@@ -0,0 +1,737 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "rtfsdrexport.hxx"
+#include <memory>
+#include "rtfattributeoutput.hxx"
+#include <svtools/rtfkeywd.hxx>
+#include <svtools/unitconv.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/unoapi.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <textboxhelper.hxx>
+#include <dcontact.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <algorithm>
+#include "rtfexport.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+using namespace css;
+
+RtfSdrExport::RtfSdrExport(RtfExport& rExport)
+ : EscherEx(std::make_shared<EscherExGlobal>(), nullptr)
+ , m_rExport(rExport)
+ , m_rAttrOutput(static_cast<RtfAttributeOutput&>(m_rExport.AttrOutput()))
+ , m_pSdrObject(nullptr)
+ , m_nShapeType(ESCHER_ShpInst_Nil)
+ , m_nShapeFlags(ShapeFlag::NONE)
+ , m_aShapeStyle(200)
+ , m_pShapeTypeWritten(new bool[ESCHER_ShpInst_COUNT])
+{
+ mnGroupLevel = 1;
+ memset(m_pShapeTypeWritten.get(), 0, ESCHER_ShpInst_COUNT * sizeof(bool));
+}
+
+RtfSdrExport::~RtfSdrExport() {}
+
+void RtfSdrExport::OpenContainer(sal_uInt16 nEscherContainer, int nRecInstance)
+{
+ EscherEx::OpenContainer(nEscherContainer, nRecInstance);
+
+ if (nEscherContainer == ESCHER_SpContainer)
+ {
+ m_nShapeType = ESCHER_ShpInst_Nil;
+ m_aShapeStyle.setLength(0);
+ m_aShapeStyle.ensureCapacity(200);
+ m_aShapeProps.clear();
+ }
+}
+
+void RtfSdrExport::CloseContainer()
+{
+ if (mRecTypes.back() == ESCHER_SpContainer)
+ {
+ // write the shape now when we have all the info
+ sal_Int32 nShapeElement = StartShape();
+ EndShape(nShapeElement);
+
+ // cleanup
+ m_nShapeType = ESCHER_ShpInst_Nil;
+ }
+
+ EscherEx::CloseContainer();
+}
+
+sal_uInt32 RtfSdrExport::EnterGroup(const OUString& /*rShapeName*/,
+ const tools::Rectangle* /*pRect*/)
+{
+ m_bInGroup = true;
+ return GenerateShapeId();
+}
+
+void RtfSdrExport::LeaveGroup() { m_bInGroup = false; }
+
+void RtfSdrExport::AddShape(sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 /*nShapeId*/)
+{
+ m_nShapeType = nShapeType;
+ m_nShapeFlags = nShapeFlags;
+}
+
+static sal_uInt16 impl_GetUInt16(const sal_uInt8*& pVal)
+{
+ sal_uInt16 nRet = *pVal++;
+ nRet += (*pVal++) << 8;
+ return nRet;
+}
+
+static sal_Int32 impl_GetPointComponent(const sal_uInt8*& pVal, std::size_t& rVerticesPos,
+ sal_uInt16 nPointSize)
+{
+ sal_Int32 nRet = 0;
+ if ((nPointSize == 0xfff0) || (nPointSize == 4))
+ {
+ sal_uInt16 nUnsigned = *pVal++;
+ nUnsigned += (*pVal++) << 8;
+ rVerticesPos += 2;
+
+ nRet = sal_Int16(nUnsigned);
+ }
+ else if (nPointSize == 8)
+ {
+ sal_uInt32 nUnsigned = *pVal++;
+ nUnsigned += (*pVal++) << 8;
+ nUnsigned += (*pVal++) << 16;
+ nUnsigned += (*pVal++) << 24;
+ rVerticesPos += 4;
+
+ nRet = nUnsigned;
+ }
+
+ return nRet;
+}
+
+void RtfSdrExport::Commit(EscherPropertyContainer& rProps, const tools::Rectangle& rRect)
+{
+ if (m_nShapeType == ESCHER_ShpInst_Nil)
+ return;
+
+ if (m_nShapeType == ESCHER_ShpInst_Line)
+ AddLineDimensions(rRect);
+ else
+ AddRectangleDimensions(m_aShapeStyle, rRect);
+
+ // properties
+ const EscherProperties& rOpts = rProps.GetOpts();
+ for (const auto& rOpt : rOpts)
+ {
+ sal_uInt16 nId = (rOpt.nPropId & 0x0FFF);
+
+ switch (nId)
+ {
+ case ESCHER_Prop_WrapText:
+ {
+ int nWrapType = 0;
+ switch (rOpt.nPropValue)
+ {
+ case ESCHER_WrapSquare:
+ nWrapType = 2;
+ break;
+ case ESCHER_WrapByPoints:
+ nWrapType = 4;
+ break;
+ case ESCHER_WrapNone:
+ nWrapType = 3;
+ break;
+ case ESCHER_WrapTopBottom:
+ nWrapType = 1;
+ break;
+ case ESCHER_WrapThrough:
+ nWrapType = 5;
+ break;
+ }
+ if (nWrapType)
+ m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPWR + OString::number(nWrapType));
+ }
+ break;
+ case ESCHER_Prop_fillColor:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("fillColor", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_fillBackColor:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("fillBackColor", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_AnchorText:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("anchorText", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_fNoFillHitTest:
+ if (rOpt.nPropValue)
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("fNoFillHitTest", OString::number(1)));
+ break;
+ case ESCHER_Prop_fNoLineDrawDash:
+ // for some reason the value is set to 0x90000 if lines are switched off
+ if (rOpt.nPropValue == 0x90000)
+ m_aShapeProps.insert(std::pair<OString, OString>("fLine", OString::number(0)));
+ break;
+ case ESCHER_Prop_lineColor:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("lineColor", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_lineBackColor:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("lineBackColor", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_lineJoinStyle:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("lineJoinStyle", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_fshadowObscured:
+ if (rOpt.nPropValue)
+ m_aShapeProps.insert(std::pair<OString, OString>("fshadowObscured", "1"));
+ break;
+ case ESCHER_Prop_geoLeft:
+ case ESCHER_Prop_geoTop:
+ {
+ sal_uInt32 nLeft = 0;
+ sal_uInt32 nTop = 0;
+
+ if (nId == ESCHER_Prop_geoLeft)
+ {
+ nLeft = rOpt.nPropValue;
+ rProps.GetOpt(ESCHER_Prop_geoTop, nTop);
+ }
+ else
+ {
+ nTop = rOpt.nPropValue;
+ rProps.GetOpt(ESCHER_Prop_geoLeft, nLeft);
+ }
+
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("geoLeft", OString::number(sal_Int32(nLeft))));
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("geoTop", OString::number(sal_Int32(nTop))));
+ }
+ break;
+
+ case ESCHER_Prop_geoRight:
+ case ESCHER_Prop_geoBottom:
+ {
+ sal_uInt32 nLeft = 0;
+ sal_uInt32 nRight = 0;
+ sal_uInt32 nTop = 0;
+ sal_uInt32 nBottom = 0;
+ rProps.GetOpt(ESCHER_Prop_geoLeft, nLeft);
+ rProps.GetOpt(ESCHER_Prop_geoTop, nTop);
+
+ if (nId == ESCHER_Prop_geoRight)
+ {
+ nRight = rOpt.nPropValue;
+ rProps.GetOpt(ESCHER_Prop_geoBottom, nBottom);
+ }
+ else
+ {
+ nBottom = rOpt.nPropValue;
+ rProps.GetOpt(ESCHER_Prop_geoRight, nRight);
+ }
+
+ m_aShapeProps.insert(std::pair<OString, OString>(
+ "geoRight", OString::number(sal_Int32(nRight) - sal_Int32(nLeft))));
+ m_aShapeProps.insert(std::pair<OString, OString>(
+ "geoBottom", OString::number(sal_Int32(nBottom) - sal_Int32(nTop))));
+ }
+ break;
+ case ESCHER_Prop_pVertices:
+ case ESCHER_Prop_pSegmentInfo:
+ {
+ EscherPropSortStruct aVertices;
+ EscherPropSortStruct aSegments;
+
+ if (rProps.GetOpt(ESCHER_Prop_pVertices, aVertices)
+ && rProps.GetOpt(ESCHER_Prop_pSegmentInfo, aSegments)
+ && aVertices.nProp.size() >= 6 && aSegments.nProp.size() >= 6)
+ {
+ const sal_uInt8* pVerticesIt = aVertices.nProp.data() + 6;
+ std::size_t nVerticesPos = 6;
+ const sal_uInt8* pSegmentIt = aSegments.nProp.data();
+
+ OStringBuffer aSegmentInfo(512);
+ OStringBuffer aVerticies(512);
+
+ sal_uInt16 nPointSize = aVertices.nProp[4] + (aVertices.nProp[5] << 8);
+
+ // number of segments
+ sal_uInt16 nSegments = impl_GetUInt16(pSegmentIt);
+ sal_Int32 nVertices = 0;
+ aSegmentInfo.append("2;" + OString::number(nSegments));
+ pSegmentIt += 4;
+
+ for (; nSegments; --nSegments)
+ {
+ sal_uInt16 nSeg = impl_GetUInt16(pSegmentIt);
+
+ // The segment type is stored in the upper 3 bits
+ // and segment count is stored in the lower 13
+ // bits.
+ unsigned char nSegmentType = (nSeg & 0xE000) >> 13;
+ unsigned short nSegmentCount = nSeg & 0x03FF;
+
+ aSegmentInfo.append(";" + OString::number(static_cast<sal_Int32>(nSeg)));
+ switch (nSegmentType)
+ {
+ case msopathLineTo:
+ for (unsigned short i = 0; i < nSegmentCount; ++i)
+ {
+ sal_Int32 nX = impl_GetPointComponent(pVerticesIt, nVerticesPos,
+ nPointSize);
+ sal_Int32 nY = impl_GetPointComponent(pVerticesIt, nVerticesPos,
+ nPointSize);
+ aVerticies.append(";(" + OString::number(nX) + ","
+ + OString::number(nY) + ")");
+ nVertices++;
+ }
+ break;
+ case msopathMoveTo:
+ {
+ sal_Int32 nX
+ = impl_GetPointComponent(pVerticesIt, nVerticesPos, nPointSize);
+ sal_Int32 nY
+ = impl_GetPointComponent(pVerticesIt, nVerticesPos, nPointSize);
+ aVerticies.append(";(" + OString::number(nX) + ","
+ + OString::number(nY) + ")");
+ nVertices++;
+ break;
+ }
+ case msopathCurveTo:
+ for (unsigned short j = 0; j < nSegmentCount; ++j)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ sal_Int32 nX = impl_GetPointComponent(
+ pVerticesIt, nVerticesPos, nPointSize);
+ sal_Int32 nY = impl_GetPointComponent(
+ pVerticesIt, nVerticesPos, nPointSize);
+ aVerticies.append(";(" + OString::number(nX) + ","
+ + OString::number(nY) + ")");
+ nVertices++;
+ }
+ }
+ break;
+ case msopathEscape:
+ {
+ // If the segment type is msopathEscape, the lower 13 bits are
+ // divided in a 5 bit escape code and 8 bit
+ // vertex count (not segment count!)
+ unsigned char nVertexCount = nSegmentCount & 0x00FF;
+ nVerticesPos += nVertexCount;
+ break;
+ }
+ case msopathClientEscape:
+ case msopathClose:
+ case msopathEnd:
+ break;
+ default:
+ SAL_WARN("sw.rtf", "Totally b0rked");
+ break;
+ }
+ }
+
+ if (!aVerticies.isEmpty())
+ {
+ // We know the number of vertices at the end only, so we have to prepend them here.
+ m_aShapeProps.insert(std::pair<OString, OString>(
+ "pVerticies", "8;" + OString::number(nVertices) + aVerticies));
+ }
+ if (!aSegmentInfo.isEmpty())
+ m_aShapeProps.insert(std::pair<OString, OString>(
+ "pSegmentInfo", aSegmentInfo.makeStringAndClear()));
+ }
+ else
+ SAL_INFO(
+ "sw.rtf",
+ __func__
+ << ": unhandled shape path, missing either pVertices or pSegmentInfo");
+ }
+ break;
+ case ESCHER_Prop_shapePath:
+ // noop, we use pSegmentInfo instead
+ break;
+ case ESCHER_Prop_fFillOK:
+ if (!rOpt.nPropValue)
+ m_aShapeProps.insert(std::pair<OString, OString>("fFillOK", "0"));
+ break;
+ case ESCHER_Prop_dxTextLeft:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("dxTextLeft", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_dyTextTop:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("dyTextTop", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_dxTextRight:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("dxTextRight", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_dyTextBottom:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("dyTextBottom", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_FitTextToShape:
+ // Size text to fit shape size: not supported by RTF
+ break;
+ case ESCHER_Prop_adjustValue:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("adjustValue", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_txflTextFlow:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("txflTextFlow", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_fillType:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("fillType", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_fillOpacity:
+ m_aShapeProps.insert(
+ std::pair<OString, OString>("fillOpacity", OString::number(rOpt.nPropValue)));
+ break;
+ case ESCHER_Prop_fillBlip:
+ {
+ int nHeaderSize
+ = 25; // The first bytes are WW8-specific, we're only interested in the PNG
+ OString aBuf = "{" OOO_STRING_SVTOOLS_RTF_PICT OOO_STRING_SVTOOLS_RTF_PNGBLIP
+ SAL_NEWLINE_STRING
+ + msfilter::rtfutil::WriteHex(rOpt.nProp.data() + nHeaderSize,
+ rOpt.nProp.size() - nHeaderSize)
+ + "}";
+ m_aShapeProps.insert(std::pair<OString, OString>("fillBlip", aBuf));
+ }
+ break;
+ default:
+ SAL_INFO("sw.rtf", __func__ << ": unhandled property: " << nId
+ << " (value: " << rOpt.nPropValue << ")");
+ break;
+ }
+ }
+}
+
+void RtfSdrExport::AddLineDimensions(const tools::Rectangle& rRectangle)
+{
+ // We get the position relative to (the current?) character
+ m_aShapeProps.insert(std::pair<OString, OString>("posrelh", "3"));
+
+ if (m_nShapeFlags & ShapeFlag::FlipV)
+ m_aShapeProps.insert(std::pair<OString, OString>("fFlipV", "1"));
+
+ if (m_nShapeFlags & ShapeFlag::FlipH)
+ m_aShapeProps.insert(std::pair<OString, OString>("fFlipH", "1"));
+
+ // the actual dimensions
+ m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPLEFT + OString::number(rRectangle.Left()));
+ m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPTOP + OString::number(rRectangle.Top()));
+ m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPRIGHT + OString::number(rRectangle.Right()));
+ m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM + OString::number(rRectangle.Bottom()));
+}
+
+void RtfSdrExport::AddRectangleDimensions(OStringBuffer& rBuffer,
+ const tools::Rectangle& rRectangle)
+{
+ // We get the position relative to (the current?) character
+ m_aShapeProps.insert(std::pair<OString, OString>("posrelh", "3"));
+
+ rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPLEFT + OString::number(rRectangle.Left()));
+ rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPTOP + OString::number(rRectangle.Top()));
+ rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPRIGHT + OString::number(rRectangle.Right()));
+ rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM + OString::number(rRectangle.Bottom()));
+}
+
+static void lcl_AppendSP(OStringBuffer& rRunText, const char* cName, std::string_view rValue)
+{
+ rRunText.append(OString::Concat("{" OOO_STRING_SVTOOLS_RTF_SP "{" OOO_STRING_SVTOOLS_RTF_SN " ")
+ + cName
+ + "}"
+ "{" OOO_STRING_SVTOOLS_RTF_SV " "
+ + rValue
+ + "}"
+ "}");
+}
+
+void RtfSdrExport::impl_writeGraphic()
+{
+ // Get the Graphic object from the Sdr one.
+ uno::Reference<drawing::XShape> xShape
+ = GetXShapeForSdrObject(const_cast<SdrObject*>(m_pSdrObject));
+ uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ Graphic aGraphic;
+
+ try
+ {
+ xPropertySet->getPropertyValue("Graphic") >>= xGraphic;
+ }
+ catch (beans::UnknownPropertyException const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.rtf", "failed to fetch graphic");
+ }
+
+ if (xGraphic.is())
+ {
+ aGraphic = Graphic(xGraphic);
+ }
+
+ // Export it to a stream.
+ SvMemoryStream aStream;
+ (void)GraphicConverter::Export(aStream, aGraphic, ConvertDataFormat::PNG);
+ sal_uInt64 nSize = aStream.TellEnd();
+ auto pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
+
+ Size aMapped(aGraphic.GetPrefSize());
+
+ // Add it to the properties.
+ RtfStringBuffer aBuf;
+ aBuf->append("{" OOO_STRING_SVTOOLS_RTF_PICT OOO_STRING_SVTOOLS_RTF_PNGBLIP);
+ aBuf->append(OOO_STRING_SVTOOLS_RTF_PICW + OString::number(aMapped.Width()));
+ aBuf->append(OOO_STRING_SVTOOLS_RTF_PICH + OString::number(aMapped.Height())
+ + SAL_NEWLINE_STRING);
+ aBuf->append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
+ aBuf->append('}');
+ m_aShapeProps.insert(std::pair<OString, OString>("pib", aBuf.makeStringAndClear()));
+}
+
+sal_Int32 RtfSdrExport::StartShape()
+{
+ if (m_nShapeType == ESCHER_ShpInst_Nil)
+ return -1;
+
+ m_aShapeProps.insert(std::pair<OString, OString>("shapeType", OString::number(m_nShapeType)));
+ if (ESCHER_ShpInst_PictureFrame == m_nShapeType)
+ impl_writeGraphic();
+
+ m_rAttrOutput.RunText().append("{" OOO_STRING_SVTOOLS_RTF_SHP);
+ m_rAttrOutput.RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
+
+ m_rAttrOutput.RunText().append(m_aShapeStyle);
+ m_aShapeStyle.setLength(0);
+ // Ignore \shpbxpage, \shpbxmargin, and \shpbxcolumn, in favor of the posrelh property.
+ m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE);
+ // Ignore \shpbypage, \shpbymargin, and \shpbycolumn, in favor of the posrelh property.
+ m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE);
+
+ // Write ZOrder.
+ if (!m_bInGroup)
+ {
+ // Order inside the group shape is not relevant for the flat shape list
+ // we write.
+ m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPZ);
+ m_rAttrOutput.RunText().append(static_cast<sal_Int64>(m_pSdrObject->GetOrdNum()));
+ }
+
+ for (auto it = m_aShapeProps.rbegin(); it != m_aShapeProps.rend(); ++it)
+ lcl_AppendSP(m_rAttrOutput.RunText(), (*it).first.getStr(), (*it).second);
+
+ lcl_AppendSP(m_rAttrOutput.RunText(), "wzDescription",
+ msfilter::rtfutil::OutString(m_pSdrObject->GetDescription(),
+ m_rExport.GetCurrentEncoding()));
+ lcl_AppendSP(
+ m_rAttrOutput.RunText(), "wzName",
+ msfilter::rtfutil::OutString(m_pSdrObject->GetName(), m_rExport.GetCurrentEncoding()));
+
+ // now check if we have some text
+ const SwFrameFormat* pShape = FindFrameFormat(m_pSdrObject);
+ if (pShape)
+ {
+ if (SwFrameFormat* pTextBox
+ = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
+ {
+ ww8::Frame* pFrame = nullptr;
+ for (auto& rFrame : m_rExport.m_aFrames)
+ {
+ if (pTextBox == &rFrame.GetFrameFormat())
+ {
+ pFrame = &rFrame;
+ break;
+ }
+ }
+
+ if (pFrame)
+ m_rAttrOutput.writeTextFrame(*pFrame, /*bTextBox=*/true);
+ return m_nShapeType;
+ }
+ }
+
+ auto pTextObj = DynCastSdrTextObj(m_pSdrObject);
+ if (pTextObj)
+ {
+ const OutlinerParaObject* pParaObj = nullptr;
+ std::optional<OutlinerParaObject> pOwnedParaObj;
+
+ /*
+ #i13885#
+ When the object is actively being edited, that text is not set into
+ the objects normal text object, but lives in a separate object.
+ */
+ if (pTextObj->IsTextEditActive())
+ {
+ pOwnedParaObj = pTextObj->CreateEditOutlinerParaObject();
+ if (pOwnedParaObj)
+ pParaObj = &*pOwnedParaObj;
+ }
+ else
+ {
+ pParaObj = pTextObj->GetOutlinerParaObject();
+ }
+
+ if (pParaObj)
+ {
+ // this is reached only in case some text is attached to the shape
+ // Watermark or TextBox?
+ if (pTextObj->TakeObjNameSingul().match("Text Frame"))
+ WriteOutliner(*pParaObj, TXT_HFTXTBOX);
+ else
+ {
+ const EditTextObject& rEditObj = pParaObj->GetTextObject();
+ const SfxItemSet& rItemSet = rEditObj.GetParaAttribs(0);
+
+ lcl_AppendSP(m_rAttrOutput.RunText(), "gtextUNICODE",
+ msfilter::rtfutil::OutString(rEditObj.GetText(0),
+ m_rExport.GetCurrentEncoding()));
+
+ const SvxFontItem* pFontFamily = rItemSet.GetItem(SID_ATTR_CHAR_FONT);
+ if (pFontFamily)
+ {
+ lcl_AppendSP(m_rAttrOutput.RunText(), "gtextFont",
+ msfilter::rtfutil::OutString(pFontFamily->GetFamilyName(),
+ m_rExport.GetCurrentEncoding()));
+ }
+
+ auto pFontHeight = rItemSet.GetItem(SID_ATTR_CHAR_FONTHEIGHT);
+ if (pFontHeight)
+ {
+ tools::Long nFontHeight = TransformMetric(pFontHeight->GetHeight(),
+ FieldUnit::TWIP, FieldUnit::POINT);
+ lcl_AppendSP(
+ m_rAttrOutput.RunText(), "gtextSize",
+ msfilter::rtfutil::OutString(OUString::number(nFontHeight * RTF_MULTIPLIER),
+ m_rExport.GetCurrentEncoding()));
+ }
+
+ // RTF angle: 0-360 * 2^16 clockwise
+ // LO angle: 0-360 * 100 counter-clockwise
+ sal_Int32 nRotation
+ = -1 * pTextObj->GetGeoStat().m_nRotationAngle.get() * RTF_MULTIPLIER / 100;
+ lcl_AppendSP(m_rAttrOutput.RunText(), "rotation",
+ msfilter::rtfutil::OutString(OUString::number(nRotation),
+ m_rExport.GetCurrentEncoding()));
+ }
+ }
+ }
+
+ return m_nShapeType;
+}
+
+void RtfSdrExport::WriteOutliner(const OutlinerParaObject& rParaObj, TextTypes eType)
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ const EditTextObject& rEditObj = rParaObj.GetTextObject();
+ MSWord_SdrAttrIter aAttrIter(m_rExport, rEditObj, eType);
+
+ sal_Int32 nPara = rEditObj.GetParagraphCount();
+
+ bool bShape = eType == TXT_HFTXTBOX;
+ if (bShape)
+ m_rAttrOutput.RunText().append("{" OOO_STRING_SVTOOLS_RTF_SHPTXT " ");
+ for (sal_Int32 n = 0; n < nPara; ++n)
+ {
+ if (n)
+ aAttrIter.NextPara(n);
+
+ rtl_TextEncoding eChrSet = aAttrIter.GetNodeCharSet();
+
+ OUString aStr(rEditObj.GetText(n));
+ sal_Int32 nCurrentPos = 0;
+ const sal_Int32 nEnd = aStr.getLength();
+
+ aAttrIter.OutParaAttr(false);
+ m_rAttrOutput.RunText().append(m_rAttrOutput.MoveCharacterProperties(true));
+
+ do
+ {
+ const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
+ rtl_TextEncoding eNextChrSet = aAttrIter.GetNextCharSet();
+
+ aAttrIter.OutAttr(nCurrentPos);
+ m_rAttrOutput.RunText().append('{');
+ m_rAttrOutput.RunText().append(m_rAttrOutput.MoveCharacterProperties(true));
+ m_rAttrOutput.RunText().append(SAL_NEWLINE_STRING);
+ bool bTextAtr = aAttrIter.IsTextAttr(nCurrentPos);
+ if (!bTextAtr)
+ {
+ OUString aOut(aStr.copy(nCurrentPos, nNextAttr - nCurrentPos));
+ m_rAttrOutput.RunText().append(msfilter::rtfutil::OutString(aOut, eChrSet));
+ }
+
+ m_rAttrOutput.RunText().append('}');
+
+ nCurrentPos = nNextAttr;
+ eChrSet = eNextChrSet;
+ aAttrIter.NextPos();
+ } while (nCurrentPos < nEnd);
+ if (bShape || n + 1 < nPara)
+ m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_PAR);
+ }
+ if (bShape)
+ m_rAttrOutput.RunText().append('}');
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfSdrExport::EndShape(sal_Int32 nShapeElement)
+{
+ if (nShapeElement >= 0)
+ {
+ // end of the shape
+ m_rAttrOutput.RunText().append("}}");
+ }
+}
+
+void RtfSdrExport::AddSdrObject(const SdrObject& rObj)
+{
+ m_pSdrObject = &rObj;
+ EscherEx::AddSdrObject(rObj);
+}
+
+bool RtfSdrExport::isTextBox(const SwFrameFormat& rFrameFormat)
+{
+ return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfsdrexport.hxx b/sw/source/filter/ww8/rtfsdrexport.hxx
new file mode 100644
index 0000000000..b0c9f151e7
--- /dev/null
+++ b/sw/source/filter/ww8/rtfsdrexport.hxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_RTFSDREXPORT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFSDREXPORT_HXX
+
+#include <filter/msfilter/escherex.hxx>
+#include <editeng/outlobj.hxx>
+#include <rtl/strbuf.hxx>
+
+#include <map>
+#include <memory>
+
+#include "wrtww8.hxx"
+
+class RtfExport;
+class RtfAttributeOutput;
+class SwFrameFormat;
+
+/// Handles export of drawings using RTF markup
+class RtfSdrExport final : public EscherEx
+{
+ RtfExport& m_rExport;
+
+ RtfAttributeOutput& m_rAttrOutput;
+
+ const SdrObject* m_pSdrObject;
+
+ /// Remember the shape type.
+ sal_uInt32 m_nShapeType;
+
+ /// Remember the shape flags.
+ ShapeFlag m_nShapeFlags;
+
+ /// Remember style, the most important shape attribute ;-)
+ OStringBuffer m_aShapeStyle;
+
+ std::map<OString, OString> m_aShapeProps;
+
+ /// Remember which shape types we had already written.
+ std::unique_ptr<bool[]> m_pShapeTypeWritten;
+
+ bool m_bInGroup = false;
+
+public:
+ explicit RtfSdrExport(RtfExport& rExport);
+ ~RtfSdrExport() override;
+
+ /// Export the sdr object as Sdr.
+ ///
+ /// Call this when you need to export the object as Sdr in RTF.
+ void AddSdrObject(const SdrObject& rObj);
+
+ /// Is this a standalone TextFrame, or used as a TextBox of a shape?
+ static bool isTextBox(const SwFrameFormat& rFrameFormat);
+ /// Write editeng text, e.g. shape or comment.
+ void WriteOutliner(const OutlinerParaObject& rParaObj, TextTypes eType);
+
+private:
+ /// Start the shape for which we just collected the information.
+ ///
+ /// Returns the element's tag number, -1 means we wrote nothing.
+ using EscherEx::StartShape;
+ sal_Int32 StartShape();
+
+ /// End the shape.
+ ///
+ /// The parameter is just what we got from StartShape().
+ using EscherEx::EndShape;
+ void EndShape(sal_Int32 nShapeElement);
+
+ void Commit(EscherPropertyContainer& rProps, const tools::Rectangle& rRect) override;
+
+ void OpenContainer(sal_uInt16 nEscherContainer, int nRecInstance = 0) override;
+ void CloseContainer() override;
+
+ sal_uInt32 EnterGroup(const OUString& rShapeName, const tools::Rectangle* pBoundRect) override;
+ void LeaveGroup() override;
+
+ void AddShape(sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 nShapeId = 0) override;
+
+ /// Add starting and ending point of a line to the m_pShapeAttrList.
+ void AddLineDimensions(const tools::Rectangle& rRectangle);
+
+ /// Add position and size to the OStringBuffer.
+ void AddRectangleDimensions(OStringBuffer& rBuffer, const tools::Rectangle& rRectangle);
+
+ /// Exports the pib property of the shape
+ void impl_writeGraphic();
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFSDREXPORT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfstringbuffer.cxx b/sw/source/filter/ww8/rtfstringbuffer.cxx
new file mode 100644
index 0000000000..ae491e45b4
--- /dev/null
+++ b/sw/source/filter/ww8/rtfstringbuffer.cxx
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "rtfstringbuffer.hxx"
+#include "rtfattributeoutput.hxx"
+#include "rtfexport.hxx"
+
+RtfStringBufferValue::RtfStringBufferValue() = default;
+
+RtfStringBufferValue::RtfStringBufferValue(const SwFlyFrameFormat* pFlyFrameFormat,
+ const SwGrfNode* pGrfNode)
+ : m_pFlyFrameFormat(pFlyFrameFormat)
+ , m_pGrfNode(pGrfNode)
+{
+}
+
+void RtfStringBufferValue::makeStringAndClear(RtfAttributeOutput* pAttributeOutput)
+{
+ if (!isGraphic())
+ {
+ pAttributeOutput->m_rExport.Strm().WriteOString(m_aBuffer);
+ m_aBuffer.setLength(0);
+ }
+ else
+ pAttributeOutput->FlyFrameGraphic(m_pFlyFrameFormat, m_pGrfNode);
+}
+
+OString RtfStringBufferValue::makeStringAndClear() { return m_aBuffer.makeStringAndClear(); }
+
+bool RtfStringBufferValue::isGraphic() const
+{
+ return m_pFlyFrameFormat != nullptr && m_pGrfNode != nullptr;
+}
+
+RtfStringBuffer::RtfStringBuffer() = default;
+
+sal_Int32 RtfStringBuffer::getLength() const
+{
+ sal_Int32 nRet = 0;
+ for (const auto& rValue : m_aValues)
+ if (!rValue.isGraphic())
+ nRet += rValue.getBuffer().getLength();
+ return nRet;
+}
+
+void RtfStringBuffer::makeStringAndClear(RtfAttributeOutput* pAttributeOutput)
+{
+ for (auto& rValue : m_aValues)
+ rValue.makeStringAndClear(pAttributeOutput);
+}
+
+OString RtfStringBuffer::makeStringAndClear()
+{
+ OStringBuffer aBuf;
+ for (auto& rValue : m_aValues)
+ if (!rValue.isGraphic())
+ aBuf.append(rValue.makeStringAndClear());
+ return aBuf.makeStringAndClear();
+}
+
+OStringBuffer& RtfStringBuffer::getLastBuffer()
+{
+ if (m_aValues.empty() || m_aValues.back().isGraphic())
+ m_aValues.emplace_back();
+ return m_aValues.back().getBuffer();
+}
+
+OStringBuffer* RtfStringBuffer::operator->() { return &getLastBuffer(); }
+
+void RtfStringBuffer::clear() { m_aValues.clear(); }
+
+void RtfStringBuffer::append(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode)
+{
+ m_aValues.emplace_back(pFlyFrameFormat, pGrfNode);
+}
+
+void RtfStringBuffer::appendAndClear(RtfStringBuffer& rBuf)
+{
+ m_aValues.insert(m_aValues.end(), rBuf.m_aValues.begin(), rBuf.m_aValues.end());
+ rBuf.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/rtfstringbuffer.hxx b/sw/source/filter/ww8/rtfstringbuffer.hxx
new file mode 100644
index 0000000000..67ad666443
--- /dev/null
+++ b/sw/source/filter/ww8/rtfstringbuffer.hxx
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_RTFSTRINGBUFFER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFSTRINGBUFFER_HXX
+
+#include <rtl/strbuf.hxx>
+#include <vector>
+
+class SwGrfNode;
+class SwFlyFrameFormat;
+class RtfAttributeOutput;
+
+/// Contains a buffered string or graphic during RTF export.
+class RtfStringBufferValue
+{
+public:
+ /// Constructor for a string buffering.
+ RtfStringBufferValue();
+ /// Constructor for graphic buffering.
+ RtfStringBufferValue(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode);
+ /// This version handles graphics.
+ void makeStringAndClear(RtfAttributeOutput* pAttributeOutput);
+ /// This one doesn't.
+ OString makeStringAndClear();
+ bool isGraphic() const;
+ OStringBuffer& getBuffer() { return m_aBuffer; }
+ const OStringBuffer& getBuffer() const { return m_aBuffer; }
+
+private:
+ OStringBuffer m_aBuffer;
+ const SwFlyFrameFormat* m_pFlyFrameFormat = nullptr;
+ const SwGrfNode* m_pGrfNode = nullptr;
+};
+
+/// Wrapper around OStringBuffers, so less hexdump of graphics have to be kept in memory during RTF export.
+class RtfStringBuffer
+{
+public:
+ RtfStringBuffer();
+ /// Length of all the contained buffers.
+ sal_Int32 getLength() const;
+ /// Writes the contents of the buffer directly to the supplied stream.
+ void makeStringAndClear(RtfAttributeOutput* pAttributeOutput);
+ /// Returns the buffered strings as a string (ignores graphic elements!)
+ OString makeStringAndClear();
+ /// Access to the last buffer.
+ OStringBuffer& getLastBuffer();
+ OStringBuffer* operator->();
+ /// Similar to ->setLength(0), but for all buffers.
+ void clear();
+ /// Same as ->append(), but for graphics and without expanding contents to save memory.
+ void append(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode);
+ /// Append all contained buffers and clear the argument.
+ void appendAndClear(RtfStringBuffer& rBuf);
+
+private:
+ using Values_t = std::vector<RtfStringBufferValue>;
+ Values_t m_aValues;
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFSTRINGBUFFER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/sortedarray.hxx b/sw/source/filter/ww8/sortedarray.hxx
new file mode 100644
index 0000000000..40affc5a41
--- /dev/null
+++ b/sw/source/filter/ww8/sortedarray.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_SORTEDARRAY_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_SORTEDARRAY_HXX
+
+#include <osl/diagnose.h>
+#include <algorithm>
+
+//simple template that manages a static [] array by sorting at construction
+
+namespace ww
+{
+ /** simple template that manages a static array
+
+ The template sorts the array at construction in place.
+ */
+ template<class C> class SortedArray
+ {
+ private:
+ //The array e.g. of sprms.
+ C *mpWwSprmTab;
+ size_t mnNoElems;
+
+ SortedArray(const SortedArray&) = delete;
+ SortedArray& operator=(const SortedArray&) = delete;
+ public:
+ //Find an entry, return its address if found and 0 if not
+ const C *search(C aSrch) const
+ {
+ std::pair<C *, C *> aPair =
+ std::equal_range(mpWwSprmTab, mpWwSprmTab + mnNoElems, aSrch);
+ if (aPair.first != aPair.second)
+ return aPair.first;
+ else
+ return nullptr;
+ }
+
+ SortedArray(C *pWwSprmTab, size_t nNoElems)
+ : mpWwSprmTab(pWwSprmTab), mnNoElems(nNoElems)
+ {
+ OSL_ENSURE(mnNoElems && pWwSprmTab, "WW8: empty Array: Don't do that");
+ std::sort(mpWwSprmTab, mpWwSprmTab + mnNoElems);
+ }
+ };
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/sprmids.hxx b/sw/source/filter/ww8/sprmids.hxx
new file mode 100644
index 0000000000..1d023c3424
--- /dev/null
+++ b/sw/source/filter/ww8/sprmids.hxx
@@ -0,0 +1,628 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_SPRMIDS_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_SPRMIDS_HXX
+
+#include <sal/types.h>
+
+namespace NS_sprm
+{
+const sal_uInt16 LN_PFSideBySide = 0x2404;
+const sal_uInt16 LN_PBrcl = 0x2408;
+const sal_uInt16 LN_PBrcp = 0x2409;
+const sal_uInt16 LN_PBrcTop10 = 0x461c;
+const sal_uInt16 LN_PBrcLeft10 = 0x461d;
+const sal_uInt16 LN_PBrcBottom10 = 0x461e;
+const sal_uInt16 LN_PBrcRight10 = 0x461f;
+const sal_uInt16 LN_PBrcBetween10 = 0x4620;
+const sal_uInt16 LN_PBrcBar10 = 0x4621;
+const sal_uInt16 LN_PDxaFromText10 = 0x4622;
+const sal_uInt16 LN_PRuler = 0xc632;
+const sal_uInt16 LN_PISnapBaseLine = 0x243b;
+const sal_uInt16 LN_PAnld = 0xc63e;
+const sal_uInt16 LN_PPropRMark = 0xc63f;
+const sal_uInt16 LN_PCrLf = 0x2444;
+const sal_uInt16 LN_PHugePapx = 0x6645;
+const sal_uInt16 LN_CChs = 0xea08;
+const sal_uInt16 LN_CIdCharType = 0x480b;
+const sal_uInt16 LN_CObjLocation = 0x680e;
+const sal_uInt16 LN_CFFtcAsciSymb = 0x2a10;
+const sal_uInt16 LN_CDefault = 0x2a32;
+const sal_uInt16 LN_CFtcDefault = 0x4a3d;
+const sal_uInt16 LN_CSizePos = 0xea3f;
+const sal_uInt16 LN_CLid = 0x4a41;
+const sal_uInt16 LN_CHpsInc = 0x2a44;
+const sal_uInt16 LN_CHpsPosAdj = 0x2a46;
+const sal_uInt16 LN_CHpsNew50 = 0xca49;
+const sal_uInt16 LN_CHpsInc1 = 0xca4a;
+const sal_uInt16 LN_CMajority50 = 0xca4c;
+const sal_uInt16 LN_CHpsMul = 0x4a4d;
+const sal_uInt16 LN_CFDiacColor = 0x085b;
+const sal_uInt16 LN_CCpg = 0x486b;
+const sal_uInt16 LN_PicBrcl = 0x2e00;
+const sal_uInt16 LN_PicScale = 0xce01;
+const sal_uInt16 LN_SOlstAnm = 0xd202;
+const sal_uInt16 LN_SFAutoPgn = 0x300d;
+const sal_uInt16 LN_SDyaPgn = 0xb00f;
+const sal_uInt16 LN_SDxaPgn = 0xb010;
+const sal_uInt16 LN_SGprfIhdt = 0x3014;
+const sal_uInt16 LN_SBCustomize = 0x301e;
+const sal_uInt16 LN_SPropRMark = 0xd227;
+const sal_uInt16 LN_SFFacingCol = 0x3229;
+const sal_uInt16 LN_TDefTable = 0xd608;
+const sal_uInt16 LN_TDefTable10 = 0xd606;
+const sal_uInt16 LN_THTMLProps = 0x740c;
+const sal_uInt16 LN_TSetBrc10 = 0xd626;
+const sal_uInt16 LN_TSetShd80 = 0x7627;
+const sal_uInt16 LN_TSetShdOdd80 = 0x7628;
+const sal_uInt16 LN_TDiagLine = 0xd62a;
+
+namespace v6
+{
+// Microsoft Word for Windows 6.0 Binary File Format
+// Parameter size
+const sal_uInt16 sprmPIstd = 2; // short
+const sal_uInt16 sprmPIstdPermute = 3; // variable
+const sal_uInt16 sprmPIncLv1 = 4; // byte
+const sal_uInt16 sprmPJc = 5; // byte
+const sal_uInt16 sprmPFSideBySide = 6; // byte
+const sal_uInt16 sprmPFKeep = 7; // byte
+const sal_uInt16 sprmPFKeepFollow = 8; // byte
+const sal_uInt16 sprmPPageBreakBefore = 9; // byte
+const sal_uInt16 sprmPBrcl = 10; // byte
+const sal_uInt16 sprmPBrcp = 11; // byte
+const sal_uInt16 sprmPAnld = 12; // variable
+const sal_uInt16 sprmPNLvlAnm = 13; // byte
+const sal_uInt16 sprmPFNoLineNumb = 14; // byte
+const sal_uInt16 sprmPChgTabsPapx = 15; // variable
+const sal_uInt16 sprmPDxaRight = 16; // word
+const sal_uInt16 sprmPDxaLeft = 17; // word
+const sal_uInt16 sprmPNest = 18; // word
+const sal_uInt16 sprmPDxaLeft1 = 19; // word
+const sal_uInt16 sprmPDyaLine = 20; // long
+const sal_uInt16 sprmPDyaBefore = 21; // word
+const sal_uInt16 sprmPDyaAfter = 22; // word
+const sal_uInt16 sprmPChgTabs = 23; // variable
+const sal_uInt16 sprmPFInTable = 24; // byte
+const sal_uInt16 sprmPTtp = 25; // byte
+const sal_uInt16 sprmPDxaAbs = 26; // word
+const sal_uInt16 sprmPDyaAbs = 27; // word
+const sal_uInt16 sprmPDxaWidth = 28; // word
+const sal_uInt16 sprmPPc = 29; // byte
+const sal_uInt16 sprmPBrcTop10 = 30; // word
+const sal_uInt16 sprmPBrcLeft10 = 31; // word
+const sal_uInt16 sprmPBrcBottom10 = 32; // word
+const sal_uInt16 sprmPBrcRight10 = 33; // word
+const sal_uInt16 sprmPBrcBetween10 = 34; // word
+const sal_uInt16 sprmPBrcBar10 = 35; // word
+const sal_uInt16 sprmPFromText10 = 36; // word
+const sal_uInt16 sprmPWr = 37; // byte
+const sal_uInt16 sprmPBrcTop = 38; // word
+const sal_uInt16 sprmPBrcLeft = 39; // word
+const sal_uInt16 sprmPBrcBottom = 40; // word
+const sal_uInt16 sprmPBrcRight = 41; // word
+const sal_uInt16 sprmPBrcBetween = 42; // word
+const sal_uInt16 sprmPBrcBar = 43; // word
+const sal_uInt16 sprmPFNoAutoHyph = 44; // byte
+const sal_uInt16 sprmPWHeightAbs = 45; // word
+const sal_uInt16 sprmPDcs = 46; // short
+const sal_uInt16 sprmPShd = 47; // word
+const sal_uInt16 sprmPDyaFromText = 48; // word
+const sal_uInt16 sprmPDxaFromText = 49; // word
+const sal_uInt16 sprmPFLocked = 50; // byte
+const sal_uInt16 sprmPFWidowControl = 51; // byte
+const sal_uInt16 sprmPRuler = 52;
+const sal_uInt16 sprmCFStrikeRM = 65; // bit
+const sal_uInt16 sprmCFRMark = 66; // bit
+const sal_uInt16 sprmCFFldVanish = 67; // bit
+const sal_uInt16 sprmCPicLocation = 68; // variable
+const sal_uInt16 sprmCIbstRMark = 69; // short
+const sal_uInt16 sprmCDttmRMark = 70; // long
+const sal_uInt16 sprmCFData = 71; // bit
+const sal_uInt16 sprmCRMReason = 72; // short
+const sal_uInt16 sprmCChse = 73; // 3 bytes
+const sal_uInt16 sprmCSymbol = 74; // variable
+const sal_uInt16 sprmCFOle2 = 75; // bit
+const sal_uInt16 sprmCIstd = 80; // short
+const sal_uInt16 sprmCIstdPermute = 81; // variable
+const sal_uInt16 sprmCDefault = 82; // variable
+const sal_uInt16 sprmCPlain = 83; // 0
+const sal_uInt16 sprmCFBold = 85; // byte
+const sal_uInt16 sprmCFItalic = 86; // byte
+const sal_uInt16 sprmCFStrike = 87; // byte
+const sal_uInt16 sprmCFOutline = 88; // byte
+const sal_uInt16 sprmCFShadow = 89; // byte
+const sal_uInt16 sprmCFSmallCaps = 90; // byte
+const sal_uInt16 sprmCFCaps = 91; // byte
+const sal_uInt16 sprmCFVanish = 92; // byte
+const sal_uInt16 sprmCFtc = 93; // word
+const sal_uInt16 sprmCKul = 94; // byte
+const sal_uInt16 sprmCSizePos = 95; // 3 bytes
+const sal_uInt16 sprmCDxaSpace = 96; // word
+const sal_uInt16 sprmCLid = 97; // word
+const sal_uInt16 sprmCIco = 98; // byte
+const sal_uInt16 sprmCHps = 99; // byte
+const sal_uInt16 sprmCHpsInc = 100; // byte
+const sal_uInt16 sprmCHpsPos = 101; // byte
+const sal_uInt16 sprmCHpsPosAdj = 102; // byte
+const sal_uInt16 sprmCMajority = 103; // variable
+const sal_uInt16 sprmCIss = 104; // byte
+const sal_uInt16 sprmCHpsNew50 = 105; // variable
+const sal_uInt16 sprmCHpsInc1 = 106; // variable
+const sal_uInt16 sprmCHpsKern = 107; // short
+const sal_uInt16 sprmCMajority50 = 108; // variable
+const sal_uInt16 sprmCHpsMul = 109; // short
+const sal_uInt16 sprmCCondHyhen = 110; // short
+const sal_uInt16 sprmCFSpec = 117; // bit
+const sal_uInt16 sprmCFObj = 118; // bit
+const sal_uInt16 sprmPicBrcl = 119; // byte
+const sal_uInt16 sprmPicScale = 120; // length
+const sal_uInt16 sprmPicBrcTop = 121; // word
+const sal_uInt16 sprmPicBrcLeft = 122; // word
+const sal_uInt16 sprmPicBrcBottom = 123; // word
+const sal_uInt16 sprmPicBrcRight = 124; // word
+const sal_uInt16 sprmSScnsPgn = 131; // byte
+const sal_uInt16 sprmSiHeadingPgn = 132; // byte
+const sal_uInt16 sprmSOlstAnm = 133; // variable
+const sal_uInt16 sprmSDxaColWidth = 136; // 3 bytes
+const sal_uInt16 sprmSDxaColSpacing = 137; // 3 bytes
+const sal_uInt16 sprmSFEvenlySpaced = 138; // byte
+const sal_uInt16 sprmSFProtected = 139; // byte
+const sal_uInt16 sprmSDmBinFirst = 140; // word
+const sal_uInt16 sprmSDmBinOther = 141; // word
+const sal_uInt16 sprmSBkc = 142; // byte
+const sal_uInt16 sprmSFTitlePage = 143; // byte
+const sal_uInt16 sprmSCcolumns = 144; // word
+const sal_uInt16 sprmSDxaColumns = 145; // word
+const sal_uInt16 sprmSFAutoPgn = 146; // byte
+const sal_uInt16 sprmSNfcPgn = 147; // byte
+const sal_uInt16 sprmSDyaPgn = 148; // short
+const sal_uInt16 sprmSDxaPgn = 149; // short
+const sal_uInt16 sprmSFPgnRestart = 150; // byte
+const sal_uInt16 sprmSFEndnote = 151; // byte
+const sal_uInt16 sprmSLnc = 152; // byte
+const sal_uInt16 sprmSGprfIhdt = 153; // byte
+const sal_uInt16 sprmSNLnnMod = 154; // word
+const sal_uInt16 sprmSDxaLnn = 155; // word
+const sal_uInt16 sprmSDyaHdrTop = 156; // word
+const sal_uInt16 sprmSDyaHdrBottom = 157; // word
+const sal_uInt16 sprmSLBetween = 158; // byte
+const sal_uInt16 sprmSVjc = 159; // byte
+const sal_uInt16 sprmSLnnMin = 160; // word
+const sal_uInt16 sprmSPgnStart = 161; // word
+const sal_uInt16 sprmSBOrientation = 162; // byte
+const sal_uInt16 sprmSBCustomize = 163;
+const sal_uInt16 sprmSXaPage = 164; // word
+const sal_uInt16 sprmSYaPage = 165; // word
+const sal_uInt16 sprmSDxaLeft = 166; // word
+const sal_uInt16 sprmSDxaRight = 167; // word
+const sal_uInt16 sprmSDyaTop = 168; // word
+const sal_uInt16 sprmSDyaBottom = 169; // word
+const sal_uInt16 sprmSDzaGutter = 170; // word
+const sal_uInt16 sprmSDMPaperReq = 171; // word
+const sal_uInt16 sprmTJc = 182; // word (low
+const sal_uInt16 sprmTDxaLeft = 183; // word
+const sal_uInt16 sprmTDxaGapHalf = 184; // word
+const sal_uInt16 sprmTFCantSplit = 185; // byte
+const sal_uInt16 sprmTTableHeader = 186; // byte
+const sal_uInt16 sprmTTableBorders = 187; // 12 bytes
+const sal_uInt16 sprmTDefTable10 = 188; // variable
+const sal_uInt16 sprmTDyaRowHeight = 189; // word
+const sal_uInt16 sprmTDefTable = 190;
+const sal_uInt16 sprmTDefTableShd = 191;
+const sal_uInt16 sprmTTlp = 192; // 4 bytes
+const sal_uInt16 sprmTSetBrc = 193; // 5 bytes
+const sal_uInt16 sprmTInsert = 194; // 4 bytes
+const sal_uInt16 sprmTDelete = 195; // word
+const sal_uInt16 sprmTDxaCol = 196; // 4 bytes
+const sal_uInt16 sprmTMerge = 197; // word
+const sal_uInt16 sprmTSplit = 198; // word
+const sal_uInt16 sprmTSetBrc10 = 199; // 5 bytes
+const sal_uInt16 sprmTSetShd = 200; // 4 bytes
+const sal_uInt16 sprmMax = 208;
+}
+
+// [MS-DOC] - v20170112 Section 2.2.5.1
+enum class SGC
+{
+ paragraph = 1,
+ character = 2,
+ picture = 3,
+ section = 4,
+ table = 5
+};
+enum class SPRA
+{
+ operand_toggle_1b_0 = 0,
+ operand_1b_1 = 1,
+ operand_2b_2 = 2,
+ operand_4b_3 = 3,
+ operand_2b_4 = 4,
+ operand_2b_5 = 5,
+ operand_varlen_6 = 6,
+ operand_3b_7 = 7
+};
+template <SPRA spra> constexpr int spraLen(); // no definition
+template <> constexpr int spraLen<SPRA::operand_toggle_1b_0>() { return 1; }
+template <> constexpr int spraLen<SPRA::operand_1b_1>() { return 1; }
+template <> constexpr int spraLen<SPRA::operand_2b_2>() { return 2; }
+template <> constexpr int spraLen<SPRA::operand_4b_3>() { return 4; }
+template <> constexpr int spraLen<SPRA::operand_2b_4>() { return 2; }
+template <> constexpr int spraLen<SPRA::operand_2b_5>() { return 2; }
+template <> constexpr int spraLen<SPRA::operand_varlen_6>() { return 0; } // variable
+template <> constexpr int spraLen<SPRA::operand_3b_7>() { return 3; }
+
+template <int ispmd, int fSpec, SGC sgc, SPRA spra> struct sprm
+{
+ static_assert((ispmd & 0x01FF) == ispmd);
+ static_assert((fSpec & 0x0001) == fSpec);
+ static_assert((static_cast<sal_uInt16>(sgc) & 0x0007) == static_cast<sal_uInt16>(sgc));
+ static_assert((static_cast<sal_uInt16>(spra) & 0x0007) == static_cast<sal_uInt16>(spra));
+ static constexpr sal_uInt16 val = ispmd + (fSpec << 9) + (static_cast<sal_uInt16>(sgc) << 10)
+ + (static_cast<sal_uInt16>(spra) << 13);
+ static constexpr int len = spraLen<spra>();
+ static constexpr bool varlen = spra == SPRA::operand_varlen_6;
+};
+
+template <int ispmd, int fSpec, SPRA spra> using sprmPar = sprm<ispmd, fSpec, SGC::paragraph, spra>;
+template <int ispmd, int fSpec, SPRA spra> using sprmChr = sprm<ispmd, fSpec, SGC::character, spra>;
+template <int ispmd, int fSpec, SPRA spra> using sprmPic = sprm<ispmd, fSpec, SGC::picture, spra>;
+template <int ispmd, int fSpec, SPRA spra> using sprmSec = sprm<ispmd, fSpec, SGC::section, spra>;
+template <int ispmd, int fSpec, SPRA spra> using sprmTbl = sprm<ispmd, fSpec, SGC::table, spra>;
+
+// Each of the following NS_sprm::Foo corresponds to a sprmFoo in [MS-DOC].
+// E.g., NS_sprm::CFRMarkDel is for sprmCFRMarkDel.
+
+// [MS-DOC] - v20170112 Section 2.6.1
+using CFRMarkDel = sprmChr<0x00, 0, SPRA::operand_toggle_1b_0>; // 0x0800
+using CFRMarkIns = sprmChr<0x01, 0, SPRA::operand_toggle_1b_0>; // 0x0801
+using CFFldVanish = sprmChr<0x02, 0, SPRA::operand_toggle_1b_0>; // 0x0802
+using CPicLocation = sprmChr<0x03, 1, SPRA::operand_4b_3>; // 0x6A03
+using CIbstRMark = sprmChr<0x04, 0, SPRA::operand_2b_2>; // 0x4804
+using CDttmRMark = sprmChr<0x05, 0, SPRA::operand_4b_3>; // 0x6805
+using CFData = sprmChr<0x06, 0, SPRA::operand_toggle_1b_0>; // 0x0806
+using CIdslRMark = sprmChr<0x07, 0, SPRA::operand_2b_2>; // 0x4807
+using CSymbol = sprmChr<0x09, 1, SPRA::operand_4b_3>; // 0x6A09
+using CFOle2 = sprmChr<0x0A, 0, SPRA::operand_toggle_1b_0>; // 0x080A
+using CHighlight = sprmChr<0x0C, 1, SPRA::operand_1b_1>; // 0x2A0C
+using CFWebHidden = sprmChr<0x11, 0, SPRA::operand_toggle_1b_0>; // 0x0811
+using CRsidProp = sprmChr<0x15, 0, SPRA::operand_4b_3>; // 0x6815
+using CRsidText = sprmChr<0x16, 0, SPRA::operand_4b_3>; // 0x6816
+using CRsidRMDel = sprmChr<0x17, 0, SPRA::operand_4b_3>; // 0x6817
+using CFSpecVanish = sprmChr<0x18, 0, SPRA::operand_toggle_1b_0>; // 0x0818
+using CFMathPr = sprmChr<0x1A, 0, SPRA::operand_varlen_6>; // 0xC81A
+using CIstd = sprmChr<0x30, 1, SPRA::operand_2b_2>; // 0x4A30
+using CIstdPermute = sprmChr<0x31, 1, SPRA::operand_varlen_6>; // 0xCA31
+using CPlain = sprmChr<0x33, 1, SPRA::operand_1b_1>; // 0x2A33
+using CKcd = sprmChr<0x34, 1, SPRA::operand_1b_1>; // 0x2A34
+using CFBold = sprmChr<0x35, 0, SPRA::operand_toggle_1b_0>; // 0x0835
+using CFItalic = sprmChr<0x36, 0, SPRA::operand_toggle_1b_0>; // 0x0836
+using CFStrike = sprmChr<0x37, 0, SPRA::operand_toggle_1b_0>; // 0x0837
+using CFOutline = sprmChr<0x38, 0, SPRA::operand_toggle_1b_0>; // 0x0838
+using CFShadow = sprmChr<0x39, 0, SPRA::operand_toggle_1b_0>; // 0x0839
+using CFSmallCaps = sprmChr<0x3A, 0, SPRA::operand_toggle_1b_0>; // 0x083A
+using CFCaps = sprmChr<0x3B, 0, SPRA::operand_toggle_1b_0>; // 0x083B
+using CFVanish = sprmChr<0x3C, 0, SPRA::operand_toggle_1b_0>; // 0x083C
+using CKul = sprmChr<0x3E, 1, SPRA::operand_1b_1>; // 0x2A3E
+using CDxaSpace = sprmChr<0x40, 0, SPRA::operand_2b_4>; // 0x8840
+using CIco = sprmChr<0x42, 1, SPRA::operand_1b_1>; // 0x2A42
+using CHps = sprmChr<0x43, 1, SPRA::operand_2b_2>; // 0x4A43
+using CHpsPos = sprmChr<0x45, 0, SPRA::operand_2b_2>; // 0x4845
+using CMajority = sprmChr<0x47, 1, SPRA::operand_varlen_6>; // 0xCA47
+using CIss = sprmChr<0x48, 1, SPRA::operand_1b_1>; // 0x2A48
+using CHpsKern = sprmChr<0x4B, 0, SPRA::operand_2b_2>; // 0x484B
+using CHresi = sprmChr<0x4E, 0, SPRA::operand_2b_2>; // 0x484E
+using CRgFtc0 = sprmChr<0x4F, 1, SPRA::operand_2b_2>; // 0x4A4F
+using CRgFtc1 = sprmChr<0x50, 1, SPRA::operand_2b_2>; // 0x4A50
+using CRgFtc2 = sprmChr<0x51, 1, SPRA::operand_2b_2>; // 0x4A51
+using CCharScale = sprmChr<0x52, 0, SPRA::operand_2b_2>; // 0x4852
+using CFDStrike = sprmChr<0x53, 1, SPRA::operand_1b_1>; // 0x2A53
+using CFImprint = sprmChr<0x54, 0, SPRA::operand_toggle_1b_0>; // 0x0854
+using CFSpec = sprmChr<0x55, 0, SPRA::operand_toggle_1b_0>; // 0x0855
+using CFObj = sprmChr<0x56, 0, SPRA::operand_toggle_1b_0>; // 0x0856
+using CPropRMark90 = sprmChr<0x57, 1, SPRA::operand_varlen_6>; // 0xCA57
+using CFEmboss = sprmChr<0x58, 0, SPRA::operand_toggle_1b_0>; // 0x0858
+using CSfxText = sprmChr<0x59, 0, SPRA::operand_1b_1>; // 0x2859
+using CFBiDi = sprmChr<0x5A, 0, SPRA::operand_toggle_1b_0>; // 0x085A
+using CFBoldBi = sprmChr<0x5C, 0, SPRA::operand_toggle_1b_0>; // 0x085C
+using CFItalicBi = sprmChr<0x5D, 0, SPRA::operand_toggle_1b_0>; // 0x085D
+using CFtcBi = sprmChr<0x5E, 1, SPRA::operand_2b_2>; // 0x4A5E
+using CLidBi = sprmChr<0x5F, 0, SPRA::operand_2b_2>; // 0x485F
+using CIcoBi = sprmChr<0x60, 1, SPRA::operand_2b_2>; // 0x4A60
+using CHpsBi = sprmChr<0x61, 1, SPRA::operand_2b_2>; // 0x4A61
+using CDispFldRMark = sprmChr<0x62, 1, SPRA::operand_varlen_6>; // 0xCA62
+using CIbstRMarkDel = sprmChr<0x63, 0, SPRA::operand_2b_2>; // 0x4863
+using CDttmRMarkDel = sprmChr<0x64, 0, SPRA::operand_4b_3>; // 0x6864
+using CBrc80 = sprmChr<0x65, 0, SPRA::operand_4b_3>; // 0x6865
+using CShd80 = sprmChr<0x66, 0, SPRA::operand_2b_2>; // 0x4866
+using CIdslRMarkDel = sprmChr<0x67, 0, SPRA::operand_2b_2>; // 0x4867
+using CFUsePgsuSettings = sprmChr<0x68, 0, SPRA::operand_toggle_1b_0>; // 0x0868
+using CRgLid0_80 = sprmChr<0x6D, 0, SPRA::operand_2b_2>; // 0x486D
+using CRgLid1_80 = sprmChr<0x6E, 0, SPRA::operand_2b_2>; // 0x486E
+using CIdctHint = sprmChr<0x6F, 0, SPRA::operand_1b_1>; // 0x286F
+using CCv = sprmChr<0x70, 0, SPRA::operand_4b_3>; // 0x6870
+using CShd = sprmChr<0x71, 1, SPRA::operand_varlen_6>; // 0xCA71
+using CBrc = sprmChr<0x72, 1, SPRA::operand_varlen_6>; // 0xCA72
+using CRgLid0 = sprmChr<0x73, 0, SPRA::operand_2b_2>; // 0x4873
+using CRgLid1 = sprmChr<0x74, 0, SPRA::operand_2b_2>; // 0x4874
+using CFNoProof = sprmChr<0x75, 0, SPRA::operand_toggle_1b_0>; // 0x0875
+using CFitText = sprmChr<0x76, 1, SPRA::operand_varlen_6>; // 0xCA76
+using CCvUl = sprmChr<0x77, 0, SPRA::operand_4b_3>; // 0x6877
+using CFELayout = sprmChr<0x78, 1, SPRA::operand_varlen_6>; // 0xCA78
+using CLbcCRJ = sprmChr<0x79, 0, SPRA::operand_1b_1>; // 0x2879
+using CFComplexScripts = sprmChr<0x82, 0, SPRA::operand_toggle_1b_0>; // 0x0882
+using CWall = sprmChr<0x83, 1, SPRA::operand_1b_1>; // 0x2A83
+using CCnf = sprmChr<0x85, 1, SPRA::operand_varlen_6>; // 0xCA85
+using CNeedFontFixup = sprmChr<0x86, 1, SPRA::operand_1b_1>; // 0x2A86
+using CPbiIBullet = sprmChr<0x87, 0, SPRA::operand_4b_3>; // 0x6887
+using CPbiGrf = sprmChr<0x88, 0, SPRA::operand_2b_2>; // 0x4888
+using CPropRMark = sprmChr<0x89, 1, SPRA::operand_varlen_6>; // 0xCA89
+using CFSdtVanish = sprmChr<0x90, 1, SPRA::operand_1b_1>; // 0x2A90
+
+// [MS-DOC] - v20170112 Section 2.6.2
+using PIstd = sprmPar<0x00, 1, SPRA::operand_2b_2>; // 0x4600
+using PIstdPermute = sprmPar<0x01, 1, SPRA::operand_varlen_6>; // 0xC601
+using PIncLvl = sprmPar<0x02, 1, SPRA::operand_1b_1>; // 0x2602
+using PJc80 = sprmPar<0x03, 0, SPRA::operand_1b_1>; // 0x2403
+using PFKeep = sprmPar<0x05, 0, SPRA::operand_1b_1>; // 0x2405
+using PFKeepFollow = sprmPar<0x06, 0, SPRA::operand_1b_1>; // 0x2406
+using PFPageBreakBefore = sprmPar<0x07, 0, SPRA::operand_1b_1>; // 0x2407
+using PIlvl = sprmPar<0x0A, 1, SPRA::operand_1b_1>; // 0x260A
+using PIlfo = sprmPar<0x0B, 1, SPRA::operand_2b_2>; // 0x460B
+using PFNoLineNumb = sprmPar<0x0C, 0, SPRA::operand_1b_1>; // 0x240C
+using PChgTabsPapx = sprmPar<0x0D, 1, SPRA::operand_varlen_6>; // 0xC60D
+using PDxaRight80 = sprmPar<0x0E, 0, SPRA::operand_2b_4>; // 0x840E
+using PDxaLeft80 = sprmPar<0x0F, 0, SPRA::operand_2b_4>; // 0x840F
+using PNest80 = sprmPar<0x10, 1, SPRA::operand_2b_2>; // 0x4610
+using PDxaLeft180 = sprmPar<0x11, 0, SPRA::operand_2b_4>; // 0x8411
+using PDyaLine = sprmPar<0x12, 0, SPRA::operand_4b_3>; // 0x6412
+using PDyaBefore = sprmPar<0x13, 0, SPRA::operand_2b_5>; // 0xA413
+using PDyaAfter = sprmPar<0x14, 0, SPRA::operand_2b_5>; // 0xA414
+using PChgTabs = sprmPar<0x15, 1, SPRA::operand_varlen_6>; // 0xC615
+using PFInTable = sprmPar<0x16, 0, SPRA::operand_1b_1>; // 0x2416
+using PFTtp = sprmPar<0x17, 0, SPRA::operand_1b_1>; // 0x2417
+using PDxaAbs = sprmPar<0x18, 0, SPRA::operand_2b_4>; // 0x8418
+using PDyaAbs = sprmPar<0x19, 0, SPRA::operand_2b_4>; // 0x8419
+using PDxaWidth = sprmPar<0x1A, 0, SPRA::operand_2b_4>; // 0x841A
+using PPc = sprmPar<0x1B, 1, SPRA::operand_1b_1>; // 0x261B
+using PWr = sprmPar<0x23, 0, SPRA::operand_1b_1>; // 0x2423
+using PBrcTop80 = sprmPar<0x24, 0, SPRA::operand_4b_3>; // 0x6424
+using PBrcLeft80 = sprmPar<0x25, 0, SPRA::operand_4b_3>; // 0x6425
+using PBrcBottom80 = sprmPar<0x26, 0, SPRA::operand_4b_3>; // 0x6426
+using PBrcRight80 = sprmPar<0x27, 0, SPRA::operand_4b_3>; // 0x6427
+using PBrcBetween80 = sprmPar<0x28, 0, SPRA::operand_4b_3>; // 0x6428
+using PBrcBar80 = sprmPar<0x29, 1, SPRA::operand_4b_3>; // 0x6629
+using PFNoAutoHyph = sprmPar<0x2A, 0, SPRA::operand_1b_1>; // 0x242A
+using PWHeightAbs = sprmPar<0x2B, 0, SPRA::operand_2b_2>; // 0x442B
+using PDcs = sprmPar<0x2C, 0, SPRA::operand_2b_2>; // 0x442C
+using PShd80 = sprmPar<0x2D, 0, SPRA::operand_2b_2>; // 0x442D
+using PDyaFromText = sprmPar<0x2E, 0, SPRA::operand_2b_4>; // 0x842E
+using PDxaFromText = sprmPar<0x2F, 0, SPRA::operand_2b_4>; // 0x842F
+using PFLocked = sprmPar<0x30, 0, SPRA::operand_1b_1>; // 0x2430
+using PFWidowControl = sprmPar<0x31, 0, SPRA::operand_1b_1>; // 0x2431
+using PFKinsoku = sprmPar<0x33, 0, SPRA::operand_1b_1>; // 0x2433
+using PFWordWrap = sprmPar<0x34, 0, SPRA::operand_1b_1>; // 0x2434
+using PFOverflowPunct = sprmPar<0x35, 0, SPRA::operand_1b_1>; // 0x2435
+using PFTopLinePunct = sprmPar<0x36, 0, SPRA::operand_1b_1>; // 0x2436
+using PFAutoSpaceDE = sprmPar<0x37, 0, SPRA::operand_1b_1>; // 0x2437
+using PFAutoSpaceDN = sprmPar<0x38, 0, SPRA::operand_1b_1>; // 0x2438
+using PWAlignFont = sprmPar<0x39, 0, SPRA::operand_2b_2>; // 0x4439
+using PFrameTextFlow = sprmPar<0x3A, 0, SPRA::operand_2b_2>; // 0x443A
+using POutLvl = sprmPar<0x40, 1, SPRA::operand_1b_1>; // 0x2640
+using PFBiDi = sprmPar<0x41, 0, SPRA::operand_1b_1>; // 0x2441
+using PFNumRMIns = sprmPar<0x43, 0, SPRA::operand_1b_1>; // 0x2443
+using PNumRM = sprmPar<0x45, 1, SPRA::operand_varlen_6>; // 0xC645
+using PHugePapx = sprmPar<0x46, 1, SPRA::operand_4b_3>; // 0x6646
+using PFUsePgsuSettings = sprmPar<0x47, 0, SPRA::operand_1b_1>; // 0x2447
+using PFAdjustRight = sprmPar<0x48, 0, SPRA::operand_1b_1>; // 0x2448
+using PItap = sprmPar<0x49, 1, SPRA::operand_4b_3>; // 0x6649
+using PDtap = sprmPar<0x4A, 1, SPRA::operand_4b_3>; // 0x664A
+using PFInnerTableCell = sprmPar<0x4B, 0, SPRA::operand_1b_1>; // 0x244B
+using PFInnerTtp = sprmPar<0x4C, 0, SPRA::operand_1b_1>; // 0x244C
+using PShd = sprmPar<0x4D, 1, SPRA::operand_varlen_6>; // 0xC64D
+using PBrcTop = sprmPar<0x4E, 1, SPRA::operand_varlen_6>; // 0xC64E
+using PBrcLeft = sprmPar<0x4F, 1, SPRA::operand_varlen_6>; // 0xC64F
+using PBrcBottom = sprmPar<0x50, 1, SPRA::operand_varlen_6>; // 0xC650
+using PBrcRight = sprmPar<0x51, 1, SPRA::operand_varlen_6>; // 0xC651
+using PBrcBetween = sprmPar<0x52, 1, SPRA::operand_varlen_6>; // 0xC652
+using PBrcBar = sprmPar<0x53, 1, SPRA::operand_varlen_6>; // 0xC653
+using PDxcRight = sprmPar<0x55, 0, SPRA::operand_2b_2>; // 0x4455
+using PDxcLeft = sprmPar<0x56, 0, SPRA::operand_2b_2>; // 0x4456
+using PDxcLeft1 = sprmPar<0x57, 0, SPRA::operand_2b_2>; // 0x4457
+using PDylBefore = sprmPar<0x58, 0, SPRA::operand_2b_2>; // 0x4458
+using PDylAfter = sprmPar<0x59, 0, SPRA::operand_2b_2>; // 0x4459
+using PFOpenTch = sprmPar<0x5A, 0, SPRA::operand_1b_1>; // 0x245A
+using PFDyaBeforeAuto = sprmPar<0x5B, 0, SPRA::operand_1b_1>; // 0x245B
+using PFDyaAfterAuto = sprmPar<0x5C, 0, SPRA::operand_1b_1>; // 0x245C
+using PDxaRight = sprmPar<0x5D, 0, SPRA::operand_2b_4>; // 0x845D
+using PDxaLeft = sprmPar<0x5E, 0, SPRA::operand_2b_4>; // 0x845E
+using PNest = sprmPar<0x5F, 1, SPRA::operand_2b_2>; // 0x465F
+using PDxaLeft1 = sprmPar<0x60, 0, SPRA::operand_2b_4>; // 0x8460
+using PJc = sprmPar<0x61, 0, SPRA::operand_1b_1>; // 0x2461
+using PFNoAllowOverlap = sprmPar<0x62, 0, SPRA::operand_1b_1>; // 0x2462
+using PWall = sprmPar<0x64, 1, SPRA::operand_1b_1>; // 0x2664
+using PIpgp = sprmPar<0x65, 0, SPRA::operand_4b_3>; // 0x6465
+using PCnf = sprmPar<0x66, 1, SPRA::operand_varlen_6>; // 0xC666
+using PRsid = sprmPar<0x67, 0, SPRA::operand_4b_3>; // 0x6467
+using PIstdListPermute = sprmPar<0x69, 1, SPRA::operand_varlen_6>; // 0xC669
+using PTableProps = sprmPar<0x6B, 0, SPRA::operand_4b_3>; // 0x646B
+using PTIstdInfo = sprmPar<0x6C, 1, SPRA::operand_varlen_6>; // 0xC66C
+using PFContextualSpacing = sprmPar<0x6D, 0, SPRA::operand_1b_1>; // 0x246D
+using PPropRMark = sprmPar<0x6F, 1, SPRA::operand_varlen_6>; // 0xC66F
+using PFMirrorIndents = sprmPar<0x70, 0, SPRA::operand_1b_1>; // 0x2470
+using PTtwo = sprmPar<0x71, 0, SPRA::operand_1b_1>; // 0x2471
+
+// [MS-DOC] - v20170112 Section 2.6.3
+using TJc90 = sprmTbl<0x00, 0, SPRA::operand_2b_2>; // 0x5400
+using TDxaLeft = sprmTbl<0x01, 1, SPRA::operand_2b_4>; // 0x9601
+using TDxaGapHalf = sprmTbl<0x02, 1, SPRA::operand_2b_4>; // 0x9602
+using TFCantSplit90 = sprmTbl<0x03, 0, SPRA::operand_1b_1>; // 0x3403
+using TTableHeader = sprmTbl<0x04, 0, SPRA::operand_1b_1>; // 0x3404
+using TTableBorders80 = sprmTbl<0x05, 1, SPRA::operand_varlen_6>; // 0xD605
+using TDyaRowHeight = sprmTbl<0x07, 0, SPRA::operand_2b_4>; // 0x9407
+using TDefTable = sprmTbl<0x08, 1, SPRA::operand_varlen_6>; // 0xD608
+using TDefTableShd80 = sprmTbl<0x09, 1, SPRA::operand_varlen_6>; // 0xD609
+using TTlp = sprmTbl<0x0A, 0, SPRA::operand_4b_3>; // 0x740A
+using TFBiDi = sprmTbl<0x0B, 1, SPRA::operand_2b_2>; // 0x560B
+using TDefTableShd3rd = sprmTbl<0x0C, 1, SPRA::operand_varlen_6>; // 0xD60C
+using TPc = sprmTbl<0x0D, 1, SPRA::operand_1b_1>; // 0x360D
+using TDxaAbs = sprmTbl<0x0E, 0, SPRA::operand_2b_4>; // 0x940E
+using TDyaAbs = sprmTbl<0x0F, 0, SPRA::operand_2b_4>; // 0x940F
+using TDxaFromText = sprmTbl<0x10, 0, SPRA::operand_2b_4>; // 0x9410
+using TDyaFromText = sprmTbl<0x11, 0, SPRA::operand_2b_4>; // 0x9411
+using TDefTableShd = sprmTbl<0x12, 1, SPRA::operand_varlen_6>; // 0xD612
+using TTableBorders = sprmTbl<0x13, 1, SPRA::operand_varlen_6>; // 0xD613
+using TTableWidth = sprmTbl<0x14, 1, SPRA::operand_3b_7>; // 0xF614
+using TFAutofit = sprmTbl<0x15, 1, SPRA::operand_1b_1>; // 0x3615
+using TDefTableShd2nd = sprmTbl<0x16, 1, SPRA::operand_varlen_6>; // 0xD616
+using TWidthBefore = sprmTbl<0x17, 1, SPRA::operand_3b_7>; // 0xF617
+using TWidthAfter = sprmTbl<0x18, 1, SPRA::operand_3b_7>; // 0xF618
+using TFKeepFollow = sprmTbl<0x19, 1, SPRA::operand_1b_1>; // 0x3619
+using TBrcTopCv = sprmTbl<0x1A, 1, SPRA::operand_varlen_6>; // 0xD61A
+using TBrcLeftCv = sprmTbl<0x1B, 1, SPRA::operand_varlen_6>; // 0xD61B
+using TBrcBottomCv = sprmTbl<0x1C, 1, SPRA::operand_varlen_6>; // 0xD61C
+using TBrcRightCv = sprmTbl<0x1D, 1, SPRA::operand_varlen_6>; // 0xD61D
+using TDxaFromTextRight = sprmTbl<0x1E, 0, SPRA::operand_2b_4>; // 0x941E
+using TDyaFromTextBottom = sprmTbl<0x1F, 0, SPRA::operand_2b_4>; // 0x941F
+using TSetBrc80 = sprmTbl<0x20, 1, SPRA::operand_varlen_6>; // 0xD620
+using TInsert = sprmTbl<0x21, 1, SPRA::operand_4b_3>; // 0x7621
+using TDelete = sprmTbl<0x22, 1, SPRA::operand_2b_2>; // 0x5622
+using TDxaCol = sprmTbl<0x23, 1, SPRA::operand_4b_3>; // 0x7623
+using TMerge = sprmTbl<0x24, 1, SPRA::operand_2b_2>; // 0x5624
+using TSplit = sprmTbl<0x25, 1, SPRA::operand_2b_2>; // 0x5625
+using TTextFlow = sprmTbl<0x29, 1, SPRA::operand_4b_3>; // 0x7629
+using TVertMerge = sprmTbl<0x2B, 1, SPRA::operand_varlen_6>; // 0xD62B
+using TVertAlign = sprmTbl<0x2C, 1, SPRA::operand_varlen_6>; // 0xD62C
+using TSetShd = sprmTbl<0x2D, 1, SPRA::operand_varlen_6>; // 0xD62D
+using TSetShdOdd = sprmTbl<0x2E, 1, SPRA::operand_varlen_6>; // 0xD62E
+using TSetBrc = sprmTbl<0x2F, 1, SPRA::operand_varlen_6>; // 0xD62F
+using TCellPadding = sprmTbl<0x32, 1, SPRA::operand_varlen_6>; // 0xD632
+using TCellSpacingDefault = sprmTbl<0x33, 1, SPRA::operand_varlen_6>; // 0xD633
+using TCellPaddingDefault = sprmTbl<0x34, 1, SPRA::operand_varlen_6>; // 0xD634
+using TCellWidth = sprmTbl<0x35, 1, SPRA::operand_varlen_6>; // 0xD635
+using TFitText = sprmTbl<0x36, 1, SPRA::operand_3b_7>; // 0xF636
+using TFCellNoWrap = sprmTbl<0x39, 1, SPRA::operand_varlen_6>; // 0xD639
+using TIstd = sprmTbl<0x3A, 1, SPRA::operand_2b_2>; // 0x563A
+using TCellPaddingStyle = sprmTbl<0x3E, 1, SPRA::operand_varlen_6>; // 0xD63E
+using TCellFHideMark = sprmTbl<0x42, 1, SPRA::operand_varlen_6>; // 0xD642
+using TSetShdTable = sprmTbl<0x60, 1, SPRA::operand_varlen_6>; // 0xD660
+using TWidthIndent = sprmTbl<0x61, 1, SPRA::operand_3b_7>; // 0xF661
+using TCellBrcType = sprmTbl<0x62, 1, SPRA::operand_varlen_6>; // 0xD662
+using TFBiDi90 = sprmTbl<0x64, 1, SPRA::operand_2b_2>; // 0x5664
+using TFNoAllowOverlap = sprmTbl<0x65, 0, SPRA::operand_1b_1>; // 0x3465
+using TFCantSplit = sprmTbl<0x66, 0, SPRA::operand_1b_1>; // 0x3466
+using TPropRMark = sprmTbl<0x67, 1, SPRA::operand_varlen_6>; // 0xD667
+using TWall = sprmTbl<0x68, 1, SPRA::operand_1b_1>; // 0x3668
+using TIpgp = sprmTbl<0x69, 0, SPRA::operand_4b_3>; // 0x7469
+using TCnf = sprmTbl<0x6A, 1, SPRA::operand_varlen_6>; // 0xD66A
+using TDefTableShdRaw = sprmTbl<0x70, 1, SPRA::operand_varlen_6>; // 0xD670
+using TDefTableShdRaw2nd = sprmTbl<0x71, 1, SPRA::operand_varlen_6>; // 0xD671
+using TDefTableShdRaw3rd = sprmTbl<0x72, 1, SPRA::operand_varlen_6>; // 0xD672
+using TRsid = sprmTbl<0x79, 0, SPRA::operand_4b_3>; // 0x7479
+using TCellVertAlignStyle = sprmTbl<0x7C, 0, SPRA::operand_1b_1>; // 0x347C
+using TCellNoWrapStyle = sprmTbl<0x7D, 0, SPRA::operand_1b_1>; // 0x347D
+using TCellBrcTopStyle = sprmTbl<0x7F, 0, SPRA::operand_varlen_6>; // 0xD47F
+using TCellBrcBottomStyle = sprmTbl<0x80, 1, SPRA::operand_varlen_6>; // 0xD680
+using TCellBrcLeftStyle = sprmTbl<0x81, 1, SPRA::operand_varlen_6>; // 0xD681
+using TCellBrcRightStyle = sprmTbl<0x82, 1, SPRA::operand_varlen_6>; // 0xD682
+using TCellBrcInsideHStyle = sprmTbl<0x83, 1, SPRA::operand_varlen_6>; // 0xD683
+using TCellBrcInsideVStyle = sprmTbl<0x84, 1, SPRA::operand_varlen_6>; // 0xD684
+using TCellBrcTL2BRStyle = sprmTbl<0x85, 1, SPRA::operand_varlen_6>; // 0xD685
+using TCellBrcTR2BLStyle = sprmTbl<0x86, 1, SPRA::operand_varlen_6>; // 0xD686
+using TCellShdStyle = sprmTbl<0x87, 1, SPRA::operand_varlen_6>; // 0xD687
+using TCHorzBands = sprmTbl<0x88, 0, SPRA::operand_1b_1>; // 0x3488
+using TCVertBands = sprmTbl<0x89, 0, SPRA::operand_1b_1>; // 0x3489
+using TJc = sprmTbl<0x8A, 0, SPRA::operand_2b_2>; // 0x548A
+
+// [MS-DOC] - v20170112 Section 2.6.4
+using ScnsPgn = sprmSec<0x00, 0, SPRA::operand_1b_1>; // 0x3000
+using SiHeadingPgn = sprmSec<0x01, 0, SPRA::operand_1b_1>; // 0x3001
+using SDxaColWidth = sprmSec<0x03, 1, SPRA::operand_3b_7>; // 0xF203
+using SDxaColSpacing = sprmSec<0x04, 1, SPRA::operand_3b_7>; // 0xF204
+using SFEvenlySpaced = sprmSec<0x05, 0, SPRA::operand_1b_1>; // 0x3005
+using SFProtected = sprmSec<0x06, 0, SPRA::operand_1b_1>; // 0x3006
+using SDmBinFirst = sprmSec<0x07, 0, SPRA::operand_2b_2>; // 0x5007
+using SDmBinOther = sprmSec<0x08, 0, SPRA::operand_2b_2>; // 0x5008
+using SBkc = sprmSec<0x09, 0, SPRA::operand_1b_1>; // 0x3009
+using SFTitlePage = sprmSec<0x0A, 0, SPRA::operand_1b_1>; // 0x300A
+using SCcolumns = sprmSec<0x0B, 0, SPRA::operand_2b_2>; // 0x500B
+using SDxaColumns = sprmSec<0x0C, 0, SPRA::operand_2b_4>; // 0x900C
+using SNfcPgn = sprmSec<0x0E, 0, SPRA::operand_1b_1>; // 0x300E
+using SFPgnRestart = sprmSec<0x11, 0, SPRA::operand_1b_1>; // 0x3011
+using SFEndnote = sprmSec<0x12, 0, SPRA::operand_1b_1>; // 0x3012
+using SLnc = sprmSec<0x13, 0, SPRA::operand_1b_1>; // 0x3013
+using SNLnnMod = sprmSec<0x15, 0, SPRA::operand_2b_2>; // 0x5015
+using SDxaLnn = sprmSec<0x16, 0, SPRA::operand_2b_4>; // 0x9016
+using SDyaHdrTop = sprmSec<0x17, 0, SPRA::operand_2b_5>; // 0xB017
+using SDyaHdrBottom = sprmSec<0x18, 0, SPRA::operand_2b_5>; // 0xB018
+using SLBetween = sprmSec<0x19, 0, SPRA::operand_1b_1>; // 0x3019
+using SVjc = sprmSec<0x1A, 0, SPRA::operand_1b_1>; // 0x301A
+using SLnnMin = sprmSec<0x1B, 0, SPRA::operand_2b_2>; // 0x501B
+using SPgnStart97 = sprmSec<0x1C, 0, SPRA::operand_2b_2>; // 0x501C
+using SBOrientation = sprmSec<0x1D, 0, SPRA::operand_1b_1>; // 0x301D
+using SXaPage = sprmSec<0x1F, 0, SPRA::operand_2b_5>; // 0xB01F
+using SYaPage = sprmSec<0x20, 0, SPRA::operand_2b_5>; // 0xB020
+using SDxaLeft = sprmSec<0x21, 0, SPRA::operand_2b_5>; // 0xB021
+using SDxaRight = sprmSec<0x22, 0, SPRA::operand_2b_5>; // 0xB022
+using SDyaTop = sprmSec<0x23, 0, SPRA::operand_2b_4>; // 0x9023
+using SDyaBottom = sprmSec<0x24, 0, SPRA::operand_2b_4>; // 0x9024
+using SDzaGutter = sprmSec<0x25, 0, SPRA::operand_2b_5>; // 0xB025
+using SDmPaperReq = sprmSec<0x26, 0, SPRA::operand_2b_2>; // 0x5026
+using SFBiDi = sprmSec<0x28, 1, SPRA::operand_1b_1>; // 0x3228
+using SFRTLGutter = sprmSec<0x2A, 1, SPRA::operand_1b_1>; // 0x322A
+using SBrcTop80 = sprmSec<0x2B, 0, SPRA::operand_4b_3>; // 0x702B
+using SBrcLeft80 = sprmSec<0x2C, 0, SPRA::operand_4b_3>; // 0x702C
+using SBrcBottom80 = sprmSec<0x2D, 0, SPRA::operand_4b_3>; // 0x702D
+using SBrcRight80 = sprmSec<0x2E, 0, SPRA::operand_4b_3>; // 0x702E
+using SPgbProp = sprmSec<0x2F, 1, SPRA::operand_2b_2>; // 0x522F
+using SDxtCharSpace = sprmSec<0x30, 0, SPRA::operand_4b_3>; // 0x7030
+using SDyaLinePitch = sprmSec<0x31, 0, SPRA::operand_2b_4>; // 0x9031
+using SClm = sprmSec<0x32, 0, SPRA::operand_2b_2>; // 0x5032
+using STextFlow = sprmSec<0x33, 0, SPRA::operand_2b_2>; // 0x5033
+using SBrcTop = sprmSec<0x34, 1, SPRA::operand_varlen_6>; // 0xD234
+using SBrcLeft = sprmSec<0x35, 1, SPRA::operand_varlen_6>; // 0xD235
+using SBrcBottom = sprmSec<0x36, 1, SPRA::operand_varlen_6>; // 0xD236
+using SBrcRight = sprmSec<0x37, 1, SPRA::operand_varlen_6>; // 0xD237
+using SWall = sprmSec<0x39, 1, SPRA::operand_1b_1>; // 0x3239
+using SRsid = sprmSec<0x3A, 0, SPRA::operand_4b_3>; // 0x703A
+using SFpc = sprmSec<0x3B, 0, SPRA::operand_1b_1>; // 0x303B
+using SRncFtn = sprmSec<0x3C, 0, SPRA::operand_1b_1>; // 0x303C
+using SRncEdn = sprmSec<0x3E, 0, SPRA::operand_1b_1>; // 0x303E
+using SNFtn = sprmSec<0x3F, 0, SPRA::operand_2b_2>; // 0x503F
+using SNfcFtnRef = sprmSec<0x40, 0, SPRA::operand_2b_2>; // 0x5040
+using SNEdn = sprmSec<0x41, 0, SPRA::operand_2b_2>; // 0x5041
+using SNfcEdnRef = sprmSec<0x42, 0, SPRA::operand_2b_2>; // 0x5042
+using SPropRMark = sprmSec<0x43, 1, SPRA::operand_varlen_6>; // 0xD243
+using SPgnStart = sprmSec<0x44, 0, SPRA::operand_4b_3>; // 0x7044
+
+// [MS-DOC] - v20170112 Section 2.6.5
+using PicBrcTop80 = sprmPic<0x02, 0, SPRA::operand_4b_3>; // 0x6C02
+using PicBrcLeft80 = sprmPic<0x03, 0, SPRA::operand_4b_3>; // 0x6C03
+using PicBrcBottom80 = sprmPic<0x04, 0, SPRA::operand_4b_3>; // 0x6C04
+using PicBrcRight80 = sprmPic<0x05, 0, SPRA::operand_4b_3>; // 0x6C05
+using PicBrcTop = sprmPic<0x08, 1, SPRA::operand_varlen_6>; // 0xCE08
+using PicBrcLeft = sprmPic<0x09, 1, SPRA::operand_varlen_6>; // 0xCE09
+using PicBrcBottom = sprmPic<0x0A, 1, SPRA::operand_varlen_6>; // 0xCE0A
+using PicBrcRight = sprmPic<0x0B, 1, SPRA::operand_varlen_6>; // 0xCE0B
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_SPRMIDS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/styles.cxx b/sw/source/filter/ww8/styles.cxx
new file mode 100644
index 0000000000..33d0ad3dec
--- /dev/null
+++ b/sw/source/filter/ww8/styles.cxx
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <wwstyles.hxx>
+
+#include <osl/diagnose.h>
+
+namespace
+{
+ // Keep in sync with StyleSheetTable::ConvertStyleName
+ const char **GetStiNames() noexcept
+ {
+ // Matches enum ww::sti in sw/source/filter/inc/wwstyles.hxx
+ static const char *stiName[] =
+ {
+ "Normal", // stiNormal
+ "Heading 1", // stiLev1
+ "Heading 2", // stiLev2
+ "Heading 3", // stiLev3
+ "Heading 4", // stiLev4
+ "Heading 5", // stiLev5
+ "Heading 6", // stiLev6
+ "Heading 7", // stiLev7
+ "Heading 8", // stiLev8
+ "Heading 9", // stiLev9
+ "Index 1", // stiIndex1
+ "Index 2", // stiIndex2
+ "Index 3", // stiIndex3
+ "Index 4", // stiIndex4
+ "Index 5", // stiIndex5
+ "Index 6", // stiIndex6
+ "Index 7", // stiIndex7
+ "Index 8", // stiIndex8
+ "Index 9", // stiIndex9
+ "TOC 1", // stiToc1
+ "TOC 2", // stiToc2
+ "TOC 3", // stiToc3
+ "TOC 4", // stiToc4
+ "TOC 5", // stiToc5
+ "TOC 6", // stiToc6
+ "TOC 7", // stiToc7
+ "TOC 8", // stiToc8
+ "TOC 9", // stiToc9
+ "Normal Indent", // stiNormIndent
+ "Footnote Text", // stiFootnoteText
+ "Annotation Text", // stiAtnText
+ "Header", // stiHeader
+ "Footer", // stiFooter
+ "Index Heading", // stiIndexHeading
+ "Caption", // stiCaption
+ "Table of Figures", // stiToCaption
+ "Envelope Address", // stiEnvAddr
+ "Envelope Return", // stiEnvRet
+ "Footnote Reference", // stiFootnoteRef
+ "Annotation Reference", // stiAtnRef
+ "Line Number", // stiLnn
+ "Page Number", // stiPgn
+ "Endnote Reference", // stiEdnRef
+ "Endnote Text", // stiEdnText
+ "Table of Authorities", // stiToa
+ "Macro Text", // stiMacro
+ "TOC Heading", // stiToaHeading - tdf143726
+ "List", // stiList
+ "List Bullet", // stiListBullet
+ "List Number", // stiListNumber
+ "List 2", // stiList2
+ "List 3", // stiList3
+ "List 4", // stiList4
+ "List 5", // stiList5
+ "List Bullet 2", // stiListBullet2
+ "List Bullet 3", // stiListBullet3
+ "List Bullet 4", // stiListBullet4
+ "List Bullet 5", // stiListBullet5
+ "List Number 2", // stiListNumber2
+ "List Number 3", // stiListNumber3
+ "List Number 4", // stiListNumber4
+ "List Number 5", // stiListNumber5
+ "Title", // stiTitle
+ "Closing", // stiClosing
+ "Signature", // stiSignature
+ "Default Paragraph Font", // stiNormalChar
+ "Body Text", // stiBodyText
+ "Body Text Indent", // stiBodyTextInd1
+ "List Continue", // stiListCont
+ "List Continue 2", // stiListCont2
+ "List Continue 3", // stiListCont3
+ "List Continue 4", // stiListCont4
+ "List Continue 5", // stiListCont5
+ "Message Header", // stiMsgHeader
+ "Subtitle", // stiSubtitle
+ "Salutation", // stiSalutation
+ "Date", // stiDate
+ "Body Text First Indent", // stiBodyText1I
+ "Body Text First Indent 2", // stiBodyText1I2
+ "Note Heading", // stiNoteHeading
+ "Body Text 2", // stiBodyText2
+ "Body Text 3", // stiBodyText3
+ "Body Text Indent 2", // stiBodyTextInd2
+ "Body Text Indent 3", // stiBodyTextInd3
+ "Block Text", // stiBlockQuote
+ "Hyperlink", // stiHyperlink
+ "FollowedHyperlink", // stiHyperlinkFollowed
+ "Strong", // stiStrong
+ "Emphasis", // stiEmphasis
+ "Document Map", // stiNavPane
+ "Plain Text", // stiPlainText
+ };
+
+ static_assert(SAL_N_ELEMENTS(stiName) == ww::stiMax, "WrongSizeOfArray");
+
+ return stiName;
+ }
+}
+
+namespace ww
+{
+ const char* GetEnglishNameFromSti(sti eSti) noexcept
+ {
+ if (eSti >= stiMax)
+ return nullptr;
+ else
+ return GetStiNames()[eSti];
+ }
+
+ bool StandardStiIsCharStyle(sti eSti) noexcept
+ {
+ switch (eSti)
+ {
+ case stiFootnoteRef:
+ case stiAtnRef:
+ case stiLnn:
+ case stiPgn:
+ case stiEdnRef:
+ case stiNormalChar:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ sti GetCanonicalStiFromStc(sal_uInt8 stc) noexcept
+ {
+ if (stc == 0)
+ return stiNormal;
+ else if (stc < 222)
+ return stiUser;
+ else
+ {
+ static const sti aMapping[] =
+ {
+ stiNil, stiAtnRef, stiAtnText, stiToc8, stiToc7, stiToc6,
+ stiToc5, stiToc4, stiToc3, stiToc2, stiToc1, stiIndex7,
+ stiIndex6, stiIndex5, stiIndex4, stiIndex3, stiIndex2,
+ stiIndex1, stiLnn, stiIndexHeading, stiFooter, stiHeader,
+ stiFootnoteRef, stiFootnoteText, stiLev9, stiLev8, stiLev7, stiLev6,
+ stiLev5, stiLev4, stiLev3, stiLev2, stiLev1, stiNormIndent
+ };
+ return aMapping[stc-222];
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/types.hxx b/sw/source/filter/ww8/types.hxx
new file mode 100644
index 0000000000..f2fd85817c
--- /dev/null
+++ b/sw/source/filter/ww8/types.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_TYPES_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_TYPES_HXX
+
+#include <sal/types.h>
+
+#include <vector>
+
+namespace ww
+{
+ typedef std::vector<sal_uInt8> bytes;
+
+ enum WordVersion {eWW1 = 1, eWW2 = 2, eWW6 = 6, eWW7 = 7, eWW8 = 8};
+ inline bool IsSevenMinus(WordVersion eVer) { return eVer <= eWW7; }
+ inline bool IsEightPlus(WordVersion eVer) { return eVer >= eWW8; }
+
+ /** For custom wrapping
+
+ When you edit the wrap points of a contour in word, word uses a relative
+ scale of 0 to 21600 where 21600 is apparently 100% of the graphic width
+ */
+ const int nWrap100Percent = 21600;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/writerhelper.cxx b/sw/source/filter/ww8/writerhelper.cxx
new file mode 100644
index 0000000000..6746264854
--- /dev/null
+++ b/sw/source/filter/ww8/writerhelper.cxx
@@ -0,0 +1,913 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/util/CloseVetoException.hpp>
+
+#include <doc.hxx>
+#include "writerhelper.hxx"
+#include <msfilter.hxx>
+#include <com/sun/star/container/XChild.hpp>
+
+#include <algorithm>
+#include <svl/itemiter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <osl/diagnose.h>
+#include <ndtxt.hxx>
+#include <ndnotxt.hxx>
+#include <fmtcntnt.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <flypos.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <docary.hxx>
+#include <charfmt.hxx>
+#include <fchrfmt.hxx>
+#include <redline.hxx>
+#include "types.hxx"
+#include <svtools/embedhlp.hxx>
+#include <numrule.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IMark.hxx>
+#include <grfatr.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+ // #i98791# - adjust sorting
+ // Utility to sort SwTextFormatColl's by their assigned outline style list level
+ class outlinecmp
+ {
+ public:
+ bool operator()(const SwTextFormatColl *pA, const SwTextFormatColl *pB) const
+ {
+ // #i98791#
+ bool bResult( false );
+ const bool bIsAAssignedToOutlineStyle( pA->IsAssignedToListLevelOfOutlineStyle() );
+ const bool bIsBAssignedToOutlineStyle( pB->IsAssignedToListLevelOfOutlineStyle() );
+ if ( bIsAAssignedToOutlineStyle != bIsBAssignedToOutlineStyle )
+ {
+ bResult = bIsBAssignedToOutlineStyle;
+ }
+ else if ( !bIsAAssignedToOutlineStyle )
+ {
+ // pA and pB are equal regarding the sorting criteria.
+ // Thus return value does not matter.
+ bResult = false;
+ }
+ else
+ {
+ bResult = pA->GetAssignedOutlineStyleLevel() < pB->GetAssignedOutlineStyleLevel();
+ }
+
+ return bResult;
+ }
+ };
+
+ bool IsValidSlotWhich(sal_uInt16 nSlotId, sal_uInt16 nWhichId)
+ {
+ return (nSlotId != 0 && nWhichId != 0 && nSlotId != nWhichId);
+ }
+
+ /*
+ Utility to convert a SwPosFlyFrames into a simple vector of ww8::Frames
+
+ The crucial thing is that a ww8::Frame always has an anchor which
+ points to some content in the document. This is a requirement of exporting
+ to Word
+ */
+ ww8::Frames SwPosFlyFramesToFrames(const SwPosFlyFrames &rFlys)
+ {
+ ww8::Frames aRet;
+
+ for(const auto& rFly : rFlys)
+ {
+ const SwFrameFormat &rEntry = rFly.GetFormat();
+
+ if (const SwNode* pAnchor = rEntry.GetAnchor().GetAnchorNode())
+ {
+ // the anchor position will be invalidated by SetRedlineFlags
+ // so set a dummy position and fix it in UpdateFramePositions
+ SwPosition const dummy(const_cast<SwNodes&>(pAnchor->GetNodes()));
+ aRet.emplace_back(rEntry, dummy);
+ }
+ else
+ {
+ SwPosition aPos(rFly.GetNode());
+ aRet.emplace_back(rEntry, aPos);
+ }
+ }
+ return aRet;
+ }
+
+ //Utility to test if a frame is anchored at a given node
+ class anchoredto
+ {
+ private:
+ const SwNode& mrNode;
+ public:
+ explicit anchoredto(const SwNode& rNode) : mrNode(rNode) {}
+ bool operator()(const ww8::Frame &rFrame) const
+ {
+ return (mrNode == rFrame.GetPosition().GetNode());
+ }
+ };
+}
+
+namespace ww8
+{
+ //For i120928,size conversion before exporting graphic of bullet
+ Frame::Frame(const Graphic &rGrf, SwPosition aPos)
+ : mpFlyFrame(nullptr)
+ , maPos(std::move(aPos))
+ , meWriterType(eBulletGrf)
+ , mpStartFrameContent(nullptr)
+ , mbIsInline(true)
+ , mbForBullet(true)
+ , maGrf(rGrf)
+ {
+ const MapMode aMap100mm( MapUnit::Map100thMM );
+ Size aSize( rGrf.GetPrefSize() );
+ if ( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() )
+ {
+ aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap100mm );
+ }
+ else
+ {
+ aSize = OutputDevice::LogicToLogic( aSize,rGrf.GetPrefMapMode(), aMap100mm );
+ }
+ maSize = aSize;
+ maLayoutSize = maSize;
+ }
+
+ Frame::Frame(const SwFrameFormat &rFormat, SwPosition aPos)
+ : mpFlyFrame(&rFormat)
+ , maPos(std::move(aPos))
+ , meWriterType(eTextBox)
+ , mpStartFrameContent(nullptr)
+ // #i43447# - move to initialization list
+ , mbIsInline( (rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
+ // #i120928# - handle graphic of bullet within existing implementation
+ , mbForBullet(false)
+ {
+ switch (rFormat.Which())
+ {
+ case RES_FLYFRMFMT:
+ if (const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx())
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ const SwNode &rNd = aIdx.GetNode();
+ // #i43447# - determine layout size
+ {
+ SwRect aLayRect( rFormat.FindLayoutRect() );
+ tools::Rectangle aRect( aLayRect.SVRect() );
+ // The Object is not rendered (e.g. something in unused
+ // header/footer) - thus, get the values from the format.
+ if ( aLayRect.IsEmpty() )
+ {
+ aRect.SetSize( rFormat.GetFrameSize().GetSize() );
+ }
+ maLayoutSize = aRect.GetSize();
+ }
+ switch (rNd.GetNodeType())
+ {
+ case SwNodeType::Grf:
+ meWriterType = eGraphic;
+ maSize = rNd.GetNoTextNode()->GetTwipSize();
+ break;
+ case SwNodeType::Ole:
+ meWriterType = eOle;
+ maSize = rNd.GetNoTextNode()->GetTwipSize();
+ break;
+ default:
+ meWriterType = eTextBox;
+ // #i43447# - Size equals layout size for text boxes
+ maSize = maLayoutSize;
+ break;
+ }
+ mpStartFrameContent = &rNd;
+ }
+ else
+ {
+ OSL_ENSURE(false, "Impossible");
+ meWriterType = eTextBox;
+ }
+ break;
+ default:
+ if (const SdrObject* pObj = rFormat.FindRealSdrObject())
+ {
+ if (pObj->GetObjInventor() == SdrInventor::FmForm)
+ meWriterType = eFormControl;
+ else
+ meWriterType = eDrawing;
+ maSize = pObj->GetSnapRect().GetSize();
+ maLayoutSize = maSize;
+ }
+ else
+ {
+ OSL_ENSURE(false, "Impossible");
+ meWriterType = eDrawing;
+ }
+ break;
+ }
+ }
+
+
+ void Frame::ForceTreatAsInline()
+ {
+ mbIsInline = true;
+ }
+}
+
+namespace sw
+{
+ namespace hack
+ {
+
+ sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool,
+ const SfxItemPool &rSrcPool, sal_uInt16 nWhich)
+ {
+ sal_uInt16 nSlotId = rSrcPool.GetSlotId(nWhich);
+ if (IsValidSlotWhich(nSlotId, nWhich))
+ nWhich = rDestPool.GetWhich(nSlotId);
+ else
+ nWhich = 0;
+ return nWhich;
+ }
+
+ sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet,
+ const SwDoc &rDoc, sal_uInt16 nWhich)
+ {
+ if (RES_WHICHHINT_END < rSet.GetRanges()[0].first)
+ {
+ nWhich = TransformWhichBetweenPools(*rSet.GetPool(),
+ rDoc.GetAttrPool(), nWhich);
+ }
+ return nWhich;
+ }
+
+ DrawingOLEAdaptor::DrawingOLEAdaptor(SdrOle2Obj &rObj,
+ SfxObjectShell &rPers)
+ : mxIPRef(rObj.GetObjRef()), mrPers(rPers),
+ mpGraphic( rObj.GetGraphic() )
+ {
+ rObj.AbandonObject();
+ }
+
+ bool DrawingOLEAdaptor::TransferToDoc( OUString &rName )
+ {
+ OSL_ENSURE(mxIPRef.is(), "Transferring invalid object to doc");
+ if (!mxIPRef.is())
+ return false;
+
+ uno::Reference < container::XChild > xChild( mxIPRef, uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( mrPers.GetModel() );
+
+ bool bSuccess = mrPers.GetEmbeddedObjectContainer().InsertEmbeddedObject( mxIPRef, rName );
+ if (bSuccess)
+ {
+ if ( mpGraphic )
+ ::svt::EmbeddedObjectRef::SetGraphicToContainer( *mpGraphic,
+ mrPers.GetEmbeddedObjectContainer(),
+ rName,
+ OUString() );
+
+ mxIPRef = nullptr;
+ }
+
+ return bSuccess;
+ }
+
+ DrawingOLEAdaptor::~DrawingOLEAdaptor()
+ {
+ if (!mxIPRef.is())
+ return;
+
+ OSL_ENSURE( !mrPers.GetEmbeddedObjectContainer().HasEmbeddedObject( mxIPRef ), "Object in adaptor is inserted?!" );
+ try
+ {
+ mxIPRef->close(true);
+ }
+ catch ( const css::util::CloseVetoException& )
+ {
+ }
+
+ mxIPRef = nullptr;
+ }
+ }
+
+ namespace util
+ {
+ SwTwips MakeSafePositioningValue(SwTwips nIn)
+ {
+ if (nIn > SHRT_MAX)
+ nIn = SHRT_MAX;
+ else if (nIn < SHRT_MIN)
+ nIn = SHRT_MIN;
+ return nIn;
+ }
+
+ void SetLayer::SendObjectToHell(SdrObject &rObject) const
+ {
+ SetObjectLayer(rObject, eHell);
+ }
+
+ void SetLayer::SendObjectToHeaven(SdrObject &rObject) const
+ {
+ SetObjectLayer(rObject, eHeaven);
+ }
+
+ void SetLayer::SetObjectLayer(SdrObject &rObject, Layer eLayer) const
+ {
+ if (SdrInventor::FmForm == rObject.GetObjInventor())
+ rObject.SetLayer(mnFormLayer);
+ else
+ {
+ switch (eLayer)
+ {
+ case eHeaven:
+ rObject.SetLayer(mnHeavenLayer);
+ break;
+ case eHell:
+ rObject.SetLayer(mnHellLayer);
+ break;
+ }
+ }
+ }
+
+ //SetLayer boilerplate begin
+
+ // #i38889# - by default put objects into the invisible layers.
+ SetLayer::SetLayer(const SwDoc &rDoc)
+ : mnHeavenLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId()),
+ mnHellLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()),
+ mnFormLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleControlsId())
+ {
+ }
+ //SetLayer boilerplate end
+
+ void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet )
+ {
+ if( bExportParentItemSet )
+ {
+ sal_uInt16 nTotal = rSet.TotalCount();
+ for( sal_uInt16 nItem =0; nItem < nTotal; ++nItem )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( SfxItemState::SET == rSet.GetItemState( rSet.GetWhichByOffset( nItem ), true, &pItem ) )
+ {
+ rItems[pItem->Which()] = pItem;
+ }
+ }
+ }
+ else if( rSet.Count())
+ {
+ SfxItemIter aIter(rSet);
+ if (const SfxPoolItem *pItem = aIter.GetCurItem())
+ {
+ do
+ rItems[pItem->Which()] = pItem;
+ while ((pItem = aIter.NextItem()));
+ }
+ }
+// DeduplicateItems(rItems);
+ }
+
+ const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems,
+ sal_uInt16 eType)
+ {
+ auto aIter = rItems.find(eType);
+ if (aIter != rItems.end())
+ return aIter->second;
+ return nullptr;
+ }
+
+ void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet)
+ {
+ if (const SwCharFormat* pCharFormat = rFormat.GetCharFormat())
+ {
+ if (pCharFormat->GetAttrSet().Count())
+ {
+ SfxItemIter aIter(pCharFormat->GetAttrSet());
+ const SfxPoolItem *pItem = aIter.GetCurItem();
+ do
+ rSet.ClearItem(pItem->Which());
+ while ((pItem = aIter.NextItem()));
+ }
+ }
+ }
+
+ ww8::ParaStyles GetParaStyles(const SwDoc &rDoc)
+ {
+ ww8::ParaStyles aStyles;
+ typedef ww8::ParaStyles::size_type mysizet;
+
+ const SwTextFormatColls *pColls = rDoc.GetTextFormatColls();
+ mysizet nCount = pColls ? pColls->size() : 0;
+ aStyles.reserve(nCount);
+ for (mysizet nI = 0; nI < nCount; ++nI)
+ aStyles.push_back((*pColls)[ static_cast< sal_uInt16 >(nI) ]);
+ return aStyles;
+ }
+
+ SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName)
+ {
+ // Search first in the Doc-Styles
+ SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName(rName);
+ if (!pColl)
+ {
+ // Collection not found, try in Pool ?
+ sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
+ SwGetPoolIdFromName::TxtColl);
+ if (n != SAL_MAX_UINT16) // found or standard
+ pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(n, false);
+ }
+ return pColl;
+ }
+
+ SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName)
+ {
+ SwCharFormat *pFormat = rDoc.FindCharFormatByName(rName);
+ if (!pFormat)
+ {
+ // Collection not found, try in Pool ?
+ sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName,
+ SwGetPoolIdFromName::ChrFmt);
+ if (n != SAL_MAX_UINT16) // found or standard
+ pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(n);
+ }
+ return pFormat;
+ }
+
+ // #i98791# - adjust sorting algorithm
+ void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles)
+ {
+ std::sort(rStyles.begin(), rStyles.end(), outlinecmp());
+ }
+
+ /*
+ Utility to extract FlyFormats from a document, potentially from a
+ selection.
+ */
+ ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM /*, bool bAll*/)
+ {
+ SwPosFlyFrames aFlys(rDoc.GetAllFlyFormats(pPaM, true));
+ ww8::Frames aRet(SwPosFlyFramesToFrames(aFlys));
+ return aRet;
+ }
+
+ void UpdateFramePositions(ww8::Frames & rFrames)
+ {
+ for (ww8::Frame & rFrame : rFrames)
+ {
+ SwFormatAnchor const& rAnchor = rFrame.GetFrameFormat().GetAnchor();
+ if (SwPosition const*const pAnchor = rAnchor.GetContentAnchor())
+ {
+ rFrame.SetPosition(*pAnchor);
+ }
+ else
+ { // these don't need to be corrected, they're not in redlines
+ assert(RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId());
+ }
+ }
+ }
+
+ ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode)
+ {
+ ww8::Frames aRet;
+ std::copy_if(rFrames.begin(), rFrames.end(),
+ std::back_inserter(aRet), anchoredto(rNode));
+ return aRet;
+ }
+
+ const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule,
+ int nLevel)
+ {
+ if (nLevel < 0 || nLevel >= MAXLEVEL)
+ {
+ OSL_FAIL("Invalid level");
+ return nullptr;
+ }
+ return &(rRule.Get( static_cast< sal_uInt16 >(nLevel) ));
+ }
+
+ const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode)
+ {
+ const SwNumRule *pRule = nullptr;
+ if (
+ rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
+ nullptr != (pRule = rTextNode.GetNumRule())
+ )
+ {
+ return GetNumFormatFromSwNumRuleLevel(*pRule,
+ rTextNode.GetActualListLevel());
+ }
+
+ if (
+ rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
+ nullptr != (pRule = rTextNode.GetDoc().GetOutlineNumRule())
+ )
+ {
+ return GetNumFormatFromSwNumRuleLevel(*pRule,
+ rTextNode.GetActualListLevel());
+ }
+
+ return nullptr;
+ }
+
+ const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNode)
+ {
+ return GetNormalNumRuleFromTextNode(rTextNode);
+ }
+
+ const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNode)
+ {
+ const SwNumRule *pRule = nullptr;
+
+ if (
+ rTextNode.IsNumbered() && rTextNode.IsCountedInList() &&
+ nullptr != (pRule = rTextNode.GetNumRule())
+ )
+ {
+ return pRule;
+ }
+ return nullptr;
+ }
+
+ SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat)
+ {
+ const SwNodeIndex *pIndex = rFormat.GetContent().GetContentIdx();
+ OSL_ENSURE(pIndex, "No NodeIndex in SwFrameFormat ?, suspicious");
+ if (!pIndex)
+ return nullptr;
+ SwNodeIndex aIdx(*pIndex, 1);
+ return aIdx.GetNode().GetNoTextNode();
+ }
+
+ bool HasPageBreak(const SwNode &rNd)
+ {
+ const SvxFormatBreakItem *pBreak = nullptr;
+ if (rNd.IsTableNode() && rNd.GetTableNode())
+ {
+ const SwTable& rTable = rNd.GetTableNode()->GetTable();
+ const SwFrameFormat* pApply = rTable.GetFrameFormat();
+ OSL_ENSURE(pApply, "impossible");
+ if (pApply)
+ pBreak = &pApply->GetFormatAttr(RES_BREAK);
+ }
+ else if (const SwContentNode *pNd = rNd.GetContentNode())
+ pBreak = &pNd->GetAttr(RES_BREAK);
+
+ return pBreak && pBreak->GetBreak() == SvxBreak::PageBefore;
+ }
+
+ tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly)
+ {
+ if(1 == rPolyPoly.Count())
+ {
+ return rPolyPoly[0];
+ }
+ else
+ {
+ // This method will now just concatenate the polygons contained
+ // in the given PolyPolygon. Anything else which might be thought of
+ // for reducing to a single polygon will just need more power and
+ // cannot create more correct results.
+ sal_uInt32 nPointCount(0);
+ sal_uInt16 a;
+
+ for(a = 0; a < rPolyPoly.Count(); a++)
+ {
+ nPointCount += static_cast<sal_uInt32>(rPolyPoly[a].GetSize());
+ }
+
+ if(nPointCount > 0x0000ffff)
+ {
+ OSL_FAIL("PolygonFromPolyPolygon: too many points for a single polygon (!)");
+ nPointCount = 0x0000ffff;
+ }
+
+ tools::Polygon aRetval(o3tl::narrowing<sal_uInt16>(nPointCount));
+ sal_uInt32 nAppendIndex(0);
+
+ for(a = 0; a < rPolyPoly.Count(); a++)
+ {
+ const tools::Polygon& rCandidate = rPolyPoly[a];
+
+ for(sal_uInt16 b(0); nAppendIndex <= nPointCount && b < rCandidate.GetSize(); b++)
+ {
+ aRetval[o3tl::narrowing<sal_uInt16>(nAppendIndex++)] = rCandidate[b];
+ }
+ }
+
+ return aRetval;
+ }
+ }
+
+ tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop)
+ {
+ tools::Polygon aPoly(PolygonFromPolyPolygon(rPolyPoly));
+ const Size &rOrigSize = pNd->GetGraphic().GetPrefSize();
+
+ const SwAttrSet* pAttrSet = pNd->GetpSwAttrSet();
+ if (bCorrectCrop && pAttrSet)
+ {
+ if (pAttrSet->HasItem(RES_GRFATR_CROPGRF))
+ {
+ // Word's wrap polygon deals with a canvas which has the size of the already
+ // cropped graphic, do the opposite of correctCrop() in writerfilter/.
+ const SwCropGrf& rCrop = pAttrSet->GetCropGrf();
+ sal_Int32 nCropLeft = convertTwipToMm100(rCrop.GetLeft());
+ sal_Int32 nCropRight = convertTwipToMm100(rCrop.GetRight());
+ sal_Int32 nCropTop = convertTwipToMm100(rCrop.GetTop());
+ sal_Int32 nCropBottom = convertTwipToMm100(rCrop.GetBottom());
+ aPoly.Move(-nCropLeft, -nCropTop);
+
+ Fraction aScaleX(rOrigSize.getWidth(), rOrigSize.getWidth() - nCropLeft - nCropRight);
+ Fraction aScaleY(rOrigSize.getHeight(), rOrigSize.getHeight() - nCropTop - nCropBottom);
+ aPoly.Scale(double(aScaleX), double(aScaleY));
+ }
+ }
+
+ Fraction aMapPolyX(ww::nWrap100Percent, rOrigSize.Width());
+ Fraction aMapPolyY(ww::nWrap100Percent, rOrigSize.Height());
+ aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
+
+ /*
+ a) stretch right bound by 15twips
+ b) shrink bottom bound to where it would have been in word
+ c) Move it to the left by 15twips
+
+ See the import for details
+ */
+ const Size &rSize = pNd->GetTwipSize();
+ Fraction aMoveHack(ww::nWrap100Percent, rSize.Width());
+ aMoveHack *= Fraction(15, 1);
+ tools::Long nMove(aMoveHack);
+
+ Fraction aHackX(ww::nWrap100Percent + nMove,
+ ww::nWrap100Percent);
+ Fraction aHackY(ww::nWrap100Percent - nMove,
+ ww::nWrap100Percent);
+ aPoly.Scale(double(aHackX), double(aHackY));
+
+ aPoly.Move(-nMove, 0);
+ return aPoly;
+ }
+
+ void RedlineStack::open(const SwPosition& rPos, const SfxPoolItem& rAttr)
+ {
+ OSL_ENSURE(rAttr.Which() == RES_FLTR_REDLINE, "not a redline");
+ maStack.emplace_back(new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone())));
+ }
+
+ namespace {
+
+ class SameOpenRedlineType
+ {
+ private:
+ RedlineType meType;
+ public:
+ explicit SameOpenRedlineType(RedlineType eType) : meType(eType) {}
+ bool operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const
+ {
+ const SwFltRedline *pTest = static_cast<const SwFltRedline *>
+ (pEntry->m_pAttr.get());
+ return (pEntry->m_bOpen && (pTest->m_eType == meType));
+ }
+ };
+
+ }
+
+ bool RedlineStack::close(const SwPosition& rPos, RedlineType eType)
+ {
+ //Search from end for same type
+ auto aResult = std::find_if(maStack.rbegin(), maStack.rend(),
+ SameOpenRedlineType(eType));
+ if (aResult != maStack.rend())
+ {
+ SwTextNode *const pNode(rPos.GetNode().GetTextNode());
+ sal_Int32 const nIndex(rPos.GetContentIndex());
+ // HACK to prevent overlap of field-mark and redline,
+ // which would destroy field-mark invariants when the redline
+ // is hidden: move the redline end one to the left
+ if (pNode && nIndex > 0
+ && pNode->GetText()[nIndex - 1] == CH_TXT_ATR_FIELDEND)
+ {
+ SwPosition const end(*rPos.GetNode().GetTextNode(),
+ nIndex - 1);
+ sw::mark::IFieldmark *const pFieldMark(
+ rPos.GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(end));
+ SAL_WARN_IF(!pFieldMark, "sw.ww8", "expected a field mark");
+ if (pFieldMark && pFieldMark->GetMarkPos().GetNodeIndex() == (*aResult)->m_aMkPos.m_nNode.GetIndex()+1
+ && pFieldMark->GetMarkPos().GetContentIndex() < (*aResult)->m_aMkPos.m_nContent)
+ {
+ (*aResult)->SetEndPos(end);
+ return true;
+ }
+ }
+ (*aResult)->SetEndPos(rPos);
+ return true;
+ }
+ return false;
+ }
+
+ void RedlineStack::closeall(const SwPosition& rPos)
+ {
+ std::for_each(maStack.begin(), maStack.end(), SetEndIfOpen(rPos));
+ }
+
+ void MoveAttrFieldmarkInserted(SwFltPosition& rMkPos, SwFltPosition& rPtPos, const SwPosition& rPos)
+ {
+ sal_Int32 const nInserted = 2; // CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP
+ SwNodeOffset nPosNd = rPos.GetNodeIndex();
+ sal_Int32 nPosCt = rPos.GetContentIndex() - nInserted;
+
+ bool const isPoint(rMkPos == rPtPos);
+ if ((rMkPos.m_nNode.GetIndex()+1 == nPosNd) &&
+ (nPosCt <= rMkPos.m_nContent))
+ {
+ rMkPos.m_nContent += nInserted;
+ SAL_WARN_IF(rMkPos.m_nContent > rPos.GetNodes()[nPosNd]->GetContentNode()->Len(),
+ "sw.ww8", "redline ends after end of line");
+ if (isPoint) // sigh ... important special case...
+ {
+ rPtPos.m_nContent += nInserted;
+ return;
+ }
+ }
+ // for the end position, leave it alone if it's *on* the dummy
+ // char position, that should remain *before*
+ if ((rPtPos.m_nNode.GetIndex()+1 == nPosNd) &&
+ (nPosCt < rPtPos.m_nContent))
+ {
+ rPtPos.m_nContent += nInserted;
+ SAL_WARN_IF(rPtPos.m_nContent > rPos.GetNodes()[nPosNd]->GetContentNode()->Len(),
+ "sw.ww8", "range ends after end of line");
+ }
+ }
+
+ void RedlineStack::MoveAttrsFieldmarkInserted(const SwPosition& rPos)
+ {
+ for (size_t i = 0, nCnt = maStack.size(); i < nCnt; ++i)
+ {
+ SwFltStackEntry& rEntry = *maStack[i];
+ MoveAttrFieldmarkInserted(rEntry.m_aMkPos, rEntry.m_aPtPos, rPos);
+ }
+ }
+
+ void SetInDocAndDelete::operator()(std::unique_ptr<SwFltStackEntry>& pEntry)
+ {
+ SwPaM aRegion(pEntry->m_aMkPos.m_nNode);
+ if (pEntry->MakeRegion(mrDoc, aRegion,
+ SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark) &&
+ (*aRegion.GetPoint() != *aRegion.GetMark())
+ )
+ {
+ mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete);
+ const SwFltRedline *pFltRedline = static_cast<const SwFltRedline*>
+ (pEntry->m_pAttr.get());
+
+ SwRedlineData aData(pFltRedline->m_eType, pFltRedline->m_nAutorNo,
+ pFltRedline->m_aStamp, 0, OUString(), nullptr);
+
+ SwRangeRedline *const pNewRedline(new SwRangeRedline(aData, aRegion));
+ // the point node may be deleted in AppendRedline, so park
+ // the PaM somewhere safe
+ aRegion.DeleteMark();
+ aRegion.GetPoint()->Assign(*mrDoc.GetNodes()[SwNodeOffset(0)]);
+ mrDoc.getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
+ mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::NONE | RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete );
+ }
+ pEntry.reset();
+ }
+
+ bool CompareRedlines::operator()(const std::unique_ptr<SwFltStackEntry> & pOneE,
+ const std::unique_ptr<SwFltStackEntry> & pTwoE) const
+ {
+ const SwFltRedline *pOne= static_cast<const SwFltRedline*>
+ (pOneE->m_pAttr.get());
+ const SwFltRedline *pTwo= static_cast<const SwFltRedline*>
+ (pTwoE->m_pAttr.get());
+
+ //Return the earlier time, if two have the same time, prioritize
+ //inserts over deletes
+ if (pOne->m_aStamp == pTwo->m_aStamp)
+ return (pOne->m_eType == RedlineType::Insert && pTwo->m_eType != RedlineType::Insert);
+ else
+ return (pOne->m_aStamp < pTwo->m_aStamp);
+ }
+
+ RedlineStack::~RedlineStack()
+ {
+ std::stable_sort(maStack.begin(), maStack.end(), CompareRedlines());
+ std::for_each(maStack.begin(), maStack.end(), SetInDocAndDelete(mrDoc));
+ }
+
+ sal_uInt16 WrtRedlineAuthor::AddName( const OUString& rNm )
+ {
+ sal_uInt16 nRet;
+ auto aIter = std::find(maAuthors.begin(), maAuthors.end(), rNm);
+ if (aIter != maAuthors.end())
+ nRet = static_cast< sal_uInt16 >(aIter - maAuthors.begin());
+ else
+ {
+ nRet = static_cast< sal_uInt16 >(maAuthors.size());
+ maAuthors.push_back(rNm);
+ }
+ return nRet;
+ }
+ }
+
+ namespace util
+ {
+ InsertedTableListener::InsertedTableListener(SwTableNode& rNode)
+ : m_pTableNode(&rNode)
+ {
+ StartListening(rNode.GetNotifier());
+ }
+
+ SwTableNode* InsertedTableListener::GetTableNode()
+ { return m_pTableNode; }
+
+ void InsertedTableListener::Notify(const SfxHint& rHint)
+ {
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pTableNode = nullptr;
+ }
+
+ InsertedTablesManager::InsertedTablesManager(const SwDoc &rDoc)
+ : mbHasRoot(rDoc.getIDocumentLayoutAccess().GetCurrentLayout())
+ { }
+
+ void InsertedTablesManager::DelAndMakeTableFrames()
+ {
+ if (!mbHasRoot)
+ return;
+ for (auto& aTable : maTables)
+ {
+ // If already a layout exists, then the BoxFrames must recreated at this table
+ SwTableNode *pTable = aTable.first->GetTableNode();
+ OSL_ENSURE(pTable, "Why no expected table");
+ if (pTable)
+ {
+ SwFrameFormat * pFrameFormat = pTable->GetTable().GetFrameFormat();
+
+ if (pFrameFormat != nullptr)
+ {
+ SwPosition *pIndex = aTable.second;
+ pTable->DelFrames();
+ pTable->MakeOwnFrames(pIndex);
+ }
+ }
+ }
+ }
+
+ void InsertedTablesManager::InsertTable(SwTableNode& rTableNode, SwPaM& rPaM)
+ {
+ if (!mbHasRoot)
+ return;
+ //Associate this tablenode with this after position, replace an old
+ //node association if necessary
+ maTables.emplace(
+ std::unique_ptr<InsertedTableListener>(new InsertedTableListener(rTableNode)),
+ rPaM.GetPoint());
+ }
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/writerhelper.hxx b/sw/source/filter/ww8/writerhelper.hxx
new file mode 100644
index 0000000000..632d1cb3a6
--- /dev/null
+++ b/sw/source/filter/ww8/writerhelper.hxx
@@ -0,0 +1,620 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WRITERHELPER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WRITERHELPER_HXX
+
+#include <vector>
+#include <map>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdtypes.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <tools/poly.hxx>
+#include <doc.hxx>
+#include <vcl/graph.hxx>
+
+class SwTextFormatColl;
+class SwCharFormat;
+class SdrObject;
+class SdrOle2Obj;
+class OutlinerParaObject;
+class SwNumFormat;
+class SwTextNode;
+class SwNoTextNode;
+class SwFormatCharFormat;
+class SwDoc;
+class SwNumRule;
+
+namespace sw
+{
+ namespace util
+ {
+ class ItemSort
+ {
+ public:
+ bool operator()(sal_uInt16 nA, sal_uInt16 nB) const;
+ };
+ }
+}
+
+namespace ww8
+{
+ /// STL container of Paragraph Styles (SwTextFormatColl)
+ typedef std::vector<SwTextFormatColl *> ParaStyles;
+ /// STL container of SfxPoolItems (Attributes)
+ typedef std::map<sal_uInt16, const SfxPoolItem *, sw::util::ItemSort> PoolItems;
+
+ /** Make exporting a Writer Frame easy
+
+ In word all frames are effectively anchored to character or as
+ character. This is nice and simple, writer is massively complex in this
+ area, so this ww8::Frame simplifies matters by providing a single unified
+ view of the multitude of elements in writer and their differing quirks.
+
+ A ww8::Frame wraps a writer frame and is guaranteed to have a suitable
+ anchor position available from it. It hides much of the needless
+ complexity of the multitude of floating/inline elements in writer, it...
+
+ Guarantees an anchor position for a frame.
+ Provides a readable way to see if we are anchored inline. (as character)
+ Provides a simple way to flag what type of entity this frame describes.
+ Provides the size of the element as drawn by writer.
+ */
+ class Frame
+ {
+ public:
+ enum WriterSource {eTextBox, eGraphic, eOle, eDrawing, eFormControl,eBulletGrf};
+ private:
+ const SwFrameFormat* mpFlyFrame;
+ SwPosition maPos;
+ Size maSize;
+ // #i43447# - Size of the frame in the layout.
+ // Especially needed for graphics, whose layout size can differ from its
+ // size, because it is scaled into its environment.
+ Size maLayoutSize;
+
+ WriterSource meWriterType;
+ const SwNode *mpStartFrameContent;
+ bool mbIsInline;
+ bool mbForBullet:1;
+ Graphic maGrf;
+ public:
+ Frame(const SwFrameFormat &rFlyFrame, SwPosition aPos);
+ Frame(const Graphic&, SwPosition );
+
+ /** Get the writer SwFrameFormat that this object describes
+
+ @return
+ The wrapped SwFrameFormat
+ */
+ const SwFrameFormat &GetFrameFormat() const { return *mpFlyFrame; }
+
+ /** Get the position this frame is anchored at
+
+ @return
+ The anchor position of this frame
+ */
+ const SwPosition &GetPosition() const { return maPos; }
+ void SetPosition(SwPosition const& rPos) { maPos = rPos; }
+
+ /** Get the node this frame is anchored into
+
+ @return
+ The SwTextNode this frame is anchored inside
+ */
+ const SwContentNode *GetContentNode() const
+ { return maPos.GetNode().GetContentNode(); }
+
+ /** Get the type of frame that this wraps
+
+ @return
+ a WriterSource which describes the source type of this wrapper
+ */
+ WriterSource GetWriterType() const { return meWriterType; }
+
+ /** Is this frame inline (as character)
+
+ @return
+ whether this is inline or not
+ */
+ bool IsInline() const { return mbIsInline; }
+
+ /** Even if the frame isn't an inline frame, force it to behave as one
+
+ There are a variety of circumstances where word cannot have
+ anything except inline elements, e.g. inside frames. So its easier
+ to force this ww8::Frame into behaving as one, instead of special
+ casing export code all over the place.
+
+ */
+ void ForceTreatAsInline();
+
+ /** Get the first node of content in the frame
+
+ @return
+ the first node of content in the frame, might not be any at all.
+ */
+ const SwNode *GetContent() const { return mpStartFrameContent; }
+ const Graphic &GetGraphic() const { return maGrf; }
+ bool HasGraphic() const { return mbForBullet; }
+
+ /** Does this ww8::Frame refer to the same writer content as another
+
+ @return
+ if the two ww8::Frames are handling the same writer frame
+ */
+ bool RefersToSameFrameAs(const Frame &rOther) const
+ {
+ if (mbForBullet && rOther.mbForBullet)
+ return (maGrf == rOther.maGrf);
+ else if ((!mbForBullet) && (!rOther.mbForBullet))
+ return (mpFlyFrame == rOther.mpFlyFrame);
+
+ return false;
+ }
+
+ /** The Size of the contained element
+
+ @return
+ the best size to use to export to word
+ */
+ const Size& GetSize() const { return maSize; }
+
+ /** The layout size of the contained element
+
+ #i43447# - Needed for graphics, which are scaled into its environment
+
+ @return layout size
+ */
+ const Size& GetLayoutSize() const
+ {
+ return maLayoutSize;
+ }
+ };
+
+ /// STL container of Frames
+ typedef std::vector<Frame> Frames;
+ /// STL iterator for Frames
+ typedef std::vector<Frame>::iterator FrameIter;
+}
+
+namespace sw
+{
+ namespace util
+ {
+ /** Provide a dynamic_cast style cast for SfxPoolItems
+
+ A SfxPoolItem generally need to be cast back to its original type
+ to be useful, which is both tedious and error prone. So item_cast is
+ a helper template to aid the process and test if the cast is
+ correct.
+
+ @param rItem
+ The SfxPoolItem which is to be casted
+
+ @tplparam T
+ A SfxPoolItem derived class to cast rItem to
+
+ @return A rItem upcasted back to a T
+
+ @exception std::bad_cast Thrown if the rItem was not a T
+ */
+ template<class T> const T & item_cast(const SfxPoolItem &rItem)
+ {
+ assert(dynamic_cast<const T *>(&rItem) && "bad type cast");
+ return static_cast<const T &>(rItem);
+ }
+
+ /** Provide a dynamic_cast style cast for SfxPoolItems
+
+ A SfxPoolItem generally need to be cast back to its original type
+ to be useful, which is both tedious and error prone. So item_cast is
+ a helper template to aid the process and test if the cast is
+ correct.
+
+ @param pItem
+ The SfxPoolItem which is to be casted
+
+ @tplparam T
+ A SfxPoolItem derived class to cast pItem to
+
+ @return A pItem upcasted back to a T or 0 if pItem was not a T
+ */
+ template<class T> const T * item_cast(const SfxPoolItem *pItem)
+ {
+ return dynamic_cast<const T *>(pItem);
+ }
+
+ /** Get the Paragraph Styles of a SwDoc
+
+ Writer's styles are in one of those dreaded macro based pre-STL
+ containers. Give me an STL container of the paragraph styles
+ instead.
+
+ @param rDoc
+ The SwDoc document to get the styles from
+
+ @return A ParaStyles containing the SwDoc's Paragraph Styles
+ */
+ ww8::ParaStyles GetParaStyles(const SwDoc &rDoc);
+
+ /** Get a Paragraph Style which fits a given name
+
+ Its surprisingly tricky to get a style when all you have is a name,
+ but that's what this does
+
+ @param rDoc
+ The SwDoc document to search in
+
+ @param rName
+ The name of the style to search for
+
+ @return A Paragraph Style if one exists which matches the name
+ */
+ SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName);
+
+ /** Get a Character Style which fits a given name
+
+ Its surprisingly tricky to get a style when all you have is a name,
+ but that's what this does
+
+ @param rDoc
+ The SwDoc document to search in
+
+ @param rName
+ The name of the style to search for
+
+ @return A Character Style if one exists which matches the name
+ */
+ SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName);
+
+ /** Sort sequence of Paragraph Styles by assigned outline style list level
+
+ Sort ParaStyles in ascending order of assigned outline style list level,
+ e.g. given Normal/Heading1/Heading2/.../Heading10 at their default
+ assigned outline style list levels of body level/level 1/level 2/.../level 10
+
+ #i98791#
+ adjust the sorting algorithm due to introduced outline level attribute
+
+ @param rStyles
+ The ParaStyles to sort
+ */
+ void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles);
+
+ /** Get the SfxPoolItems of a SfxItemSet
+
+ Writer's SfxPoolItems (attributes) are in one of those dreaded
+ macro based pre-STL containers. Give me an STL container of the
+ items instead.
+
+ @param rSet
+ The SfxItemSet to get the items from
+
+ @param rItems
+ The sw::PoolItems to put the items into
+ */
+ void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet );
+
+ const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems,
+ sal_uInt16 eType);
+
+ template<class T> const T* HasItem(const ww8::PoolItems &rItems,
+ sal_uInt16 eType)
+ {
+ return item_cast<T>(SearchPoolItems(rItems, eType));
+ }
+
+ /** Remove properties from an SfxItemSet which a SwFormatCharFormat overrides
+
+ Given an SfxItemSet and a SwFormatCharFormat remove from the rSet all the
+ properties which the SwFormatCharFormat would override. An SfxItemSet
+ contains attributes, and a SwFormatCharFormat is a "Character Style",
+ so if the SfxItemSet contains bold and so does the character style
+ then delete bold from the SfxItemSet
+
+ @param
+ rFormat the SwFormatCharFormat which describes the Character Style
+
+ @param
+ rSet the SfxItemSet from which we want to remove any properties
+ which the rFormat would override
+
+ @see #i24291# for examples
+ */
+ void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet);
+
+ /** Get the Floating elements in a SwDoc
+
+ Writer's FrameFormats may or may not be anchored to some text content,
+ e.g. Page Anchored elements will not be. For the winword export we
+ need them to have something to be anchored to. So this method
+ returns all the floating elements in a document as a STL container
+ of ww8::Frames which are guaranteed to have an appropriate anchor.
+
+ @param rDoc
+ The SwDoc document to get the styles from
+
+ @param pPaM
+ The SwPam to describe the selection in the document to get the
+ elements from. 0 means the entire document.
+
+ @return A Frames containing the selections Floating elements
+ */
+ ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM);
+
+ /** fix up frame positions, must be called after SetRedlineFlags */
+ void UpdateFramePositions(ww8::Frames & rFrames);
+
+ /** Get the Frames anchored to a given node
+
+ Given a container of frames, find the ones anchored to a given node
+
+ @param rFrames
+ The container of frames to search in
+
+ @param rNode
+ The SwNode to check for anchors to
+
+ @return the Frames in rFrames anchored to rNode
+ */
+ ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode);
+
+ /** Get the Numbering Format used on a paragraph
+
+ There are two differing types of numbering formats that may be on a
+ paragraph, normal and outline. The outline is that numbering you
+ see in tools->outline numbering. There's no difference in the
+ numbering itself, just how you get it from the SwTextNode. Needless
+ to say the filter generally couldn't care less what type of
+ numbering is in use.
+
+ @param rTextNode
+ The SwTextNode that is the paragraph
+
+ @return A SwNumFormat pointer that describes the numbering level
+ on this paragraph, or 0 if there is none.
+ */
+ const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode);
+
+ /** Get the Numbering Format for a given level from a numbering rule
+
+ @param rRule
+ The numbering rule
+
+ @param nLevel
+ The numbering level
+
+ @return A SwNumFormat pointer that describes the numbering level
+ or 0 if the nLevel is out of range
+ */
+ const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule,
+ int nLevel);
+
+ const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNd);
+ const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNd);
+
+ /** Get the SwNoTextNode associated with a SwFrameFormat if here is one
+
+ There are two differing types of numbering formats that may be on a
+ paragraph, normal and outline. The outline is that numbering you
+ see in tools->outline numbering. There's no difference in the
+ numbering itself, just how you get it from the SwTextNode. Needless
+ to say the filter generally couldn't care less what type of
+ numbering is in use.
+
+ @param rFormat
+ The SwFrameFormat that may describe a graphic
+
+ @return A SwNoTextNode pointer that describes the graphic of this
+ frame if there is one, or 0 if there is none.
+ */
+ SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat);
+
+ /** Does a node have a "page break before" applied
+
+ Both text nodes and tables in writer can have "page break before"
+ This function gives a unified view to both entities
+
+ @param rNode
+ The SwNode to query the page break of
+
+ @return true if there is a page break, false otherwise
+ */
+ bool HasPageBreak(const SwNode &rNode);
+
+ /** Make a best fit Polygon from a PolyPolygon
+
+ For custom contours in writer we use a PolyPolygon, while word uses
+ a simple polygon, so we need to try and make the best polygon from
+ a PolyPolygon
+
+ @param rPolyPoly
+ The tools::PolyPolygon to try and turn into a Polygon
+
+ @return best fit Polygon from rPolyPoly
+ */
+ tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly);
+
+ /// Undo all scaling / move tricks of the wrap polygon done during import.
+ tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop);
+
+ /** Make setting a drawing object's layer in a Writer document easy
+
+ Word has the simple concept of a drawing object either in the
+ foreground and in the background. We have an additional complexity
+ that form components live in a separate layer, which seems
+ unnecessarily complicated. So in the winword filter we set the
+ object's layer through this class with either SendObjectToHell for
+ the bottom layer and SendObjectToHeaven for the top and we don't
+ worry about the odd form layer design wrinkle.
+ */
+ class SetLayer
+ {
+ private:
+ SdrLayerID mnHeavenLayer, mnHellLayer, mnFormLayer;
+ enum Layer {eHeaven, eHell};
+ void SetObjectLayer(SdrObject &rObject, Layer eLayer) const;
+ public:
+
+ /** Make Object live in the bottom drawing layer
+
+ @param rObject
+ The object to be set to the bottom layer
+ */
+ void SendObjectToHell(SdrObject &rObject) const;
+
+ /** Make Object lives in the top layer
+
+ @param rObject
+ The object to be set to the top layer
+ */
+ void SendObjectToHeaven(SdrObject &rObject) const;
+
+ /** Normal constructor
+
+ @param rDoc
+ The Writer document whose drawing layers we will be inserting
+ objects into
+ */
+ explicit SetLayer(const SwDoc &rDoc);
+ };
+
+ const SwCharFormat* GetSwCharFormat(const SwFormatINetFormat& rINet, SwDoc& rDoc);
+ }
+
+ namespace hack
+ {
+ /** Map an ID valid in one SfxItemPool to its equivalent in another
+
+ Given a WhichId (the id that identifies a property e.g. bold) which
+ is correct in a given SfxItemPool, get the equivalent whichId in
+ another SfxItemPool
+
+ This arises because the drawing layer uses the same properties as
+ writer e.g. SvxWeight, but for some reason uses different ids
+ for the same properties as writer.
+
+ @param rDestPool
+ The SfxItemPool in whose terms the Id is returned
+
+ @param rSrcPool
+ The SfxItemPool in whose terms the Id is passed in
+
+ @param nWhich
+ The Id to transform from source to dest
+
+ @return 0 on failure, the correct property Id on success
+ */
+ sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool,
+ const SfxItemPool &rSrcPool, sal_uInt16 nWhich);
+
+ /** Map a SwDoc WhichId to the equivalent Id for a given SfxItemSet
+
+ Given a WhichId (the id that identifies a property e.g. bold) which
+ is correct for a Writer document, get the equivalent whichId which
+ for a given SfxItemSet.
+
+ This arises because the drawing layer uses the same properties as
+ writer e.g. SvxWeight, but for some reason uses different ids
+ for the same properties as writer.
+
+ This is effectively the same as TransformWhichBetweenPools except
+ at a slightly different layer.
+
+ @param rSet
+ The SfxItemSet in whose terms the Id is returned
+
+ @param rDoc
+ The SwDoc in whose terms the Id is passed in
+
+ @param nWhich
+ The Id to transform from writer to the SfxItemSet's domain
+
+ @return 0 on failure, the correct SfxItemSet Id on success
+ */
+ sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet,
+ const SwDoc &rDoc, sal_uInt16 nWhich);
+
+ /** Make inserting an OLE object into a Writer document easy
+
+ The rest of Office uses SdrOle2Obj for their OLE objects, Writer
+ doesn't, which makes things a bit difficult as this is the type of
+ object that the escher import code shared by the MSOffice filters
+ produces when it imports an OLE object.
+
+ This utility class takes ownership of the OLE object away from a
+ SdrOle2Obj and can massage it into the condition best suited to
+ insertion into Writer.
+
+ If the object was not transferred into Writer then it is deleted
+ during destruction.
+ */
+ class DrawingOLEAdaptor
+ {
+ private:
+ css::uno::Reference < css::embed::XEmbeddedObject > mxIPRef;
+ SfxObjectShell& mrPers;
+ const Graphic* mpGraphic;
+ public:
+ /** Take ownership of a SdrOle2Objs OLE object
+
+ @param rObj
+ The SdrOle2Obj whose OLE object we want to take control of
+
+ @param rPers
+ The SvPersist of a SwDoc (SwDoc::GetPersist()) into which we
+ may want to move the object, or remove it from if unwanted.
+ */
+ DrawingOLEAdaptor(SdrOle2Obj &rObj, SfxObjectShell &rPers);
+
+ /// Destructor will destroy the owned OLE object if not transferred
+ ~DrawingOLEAdaptor();
+
+ /** Transfer ownership of the OLE object to a document's SvPersist
+
+ TransferToDoc moves the object into the persist under the name
+ passed in. This name is then suitable to be used as an argument
+ to SwDoc::InsertOLE.
+
+ The object is no longer owned by the adaptor after this call,
+ subsequent calls are an error and return false.
+
+ @param rName
+ The name to store the object under in the document.
+
+ @return On success true is returned, otherwise false. On
+ success rName is then suitable for user with SwDoc::InsertOLE
+ */
+ bool TransferToDoc(OUString &rName);
+ private:
+ DrawingOLEAdaptor& operator=(const DrawingOLEAdaptor&) = delete;
+ DrawingOLEAdaptor(const DrawingOLEAdaptor &rDoc) = delete;
+ };
+ }
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/writerwordglue.cxx b/sw/source/filter/ww8/writerwordglue.cxx
new file mode 100644
index 0000000000..5291002897
--- /dev/null
+++ b/sw/source/filter/ww8/writerwordglue.cxx
@@ -0,0 +1,1087 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <msfilter.hxx>
+#include "writerwordglue.hxx"
+#include <doc.hxx>
+#include "writerhelper.hxx"
+#include <IDocumentStylePoolAccess.hxx>
+
+#include <algorithm>
+
+#include <o3tl/string_view.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <svl/numformat.hxx>
+
+#include <unicode/ubidi.h>
+#include <tools/tenccvt.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <frmfmt.hxx>
+#include <fmtclds.hxx>
+#include <hfspacingitem.hxx>
+#include <fmtfsize.hxx>
+#include <poolfmt.hxx>
+#include <swrect.hxx>
+#include <fmthdft.hxx>
+#include <frmatr.hxx>
+#include <ndtxt.hxx>
+#include <breakit.hxx>
+
+using namespace css;
+
+namespace myImplHelpers
+{
+ static SwTwips CalcHdFtDist(const SwFrameFormat& rFormat, sal_uInt16 nSpacing)
+ {
+ /*
+ The normal case for reexporting word docs is to have dynamic spacing,
+ as this is word's only setting, and the reason for the existence of the
+ dynamic spacing features. If we have dynamic spacing active then we can
+ add its spacing to the value height of the h/f and get the wanted total
+ size for word.
+
+ Otherwise we have to get the real layout rendered
+ height, which is totally nonoptimum, but the best we can do.
+ */
+ tools::Long nDist=0;
+ const SwFormatFrameSize& rSz = rFormat.GetFrameSize();
+
+ const SwHeaderAndFooterEatSpacingItem &rSpacingCtrl =
+ rFormat.GetFormatAttr(RES_HEADER_FOOTER_EAT_SPACING);
+ if (rSpacingCtrl.GetValue())
+ nDist += rSz.GetHeight();
+ else
+ {
+ SwRect aRect(rFormat.FindLayoutRect());
+ if (aRect.Height())
+ nDist += aRect.Height();
+ else
+ {
+ const SwFormatFrameSize& rSize = rFormat.GetFrameSize();
+ if (SwFrameSize::Variable != rSize.GetHeightSizeType())
+ nDist += rSize.GetHeight();
+ else
+ {
+ nDist += 274; // default for 12pt text
+ nDist += nSpacing;
+ }
+ }
+ }
+ return nDist;
+ }
+
+ static SwTwips CalcHdDist(const SwFrameFormat& rFormat)
+ {
+ return CalcHdFtDist(rFormat, rFormat.GetULSpace().GetUpper());
+ }
+
+ static SwTwips CalcFtDist(const SwFrameFormat& rFormat)
+ {
+ return CalcHdFtDist(rFormat, rFormat.GetULSpace().GetLower());
+ }
+
+ /*
+ SwTextFormatColl and SwCharFormat are quite distinct types and how they are
+ gotten is also distinct, but the algorithm to match word's equivalents into
+ them is the same, so we put the different stuff into two separate helper
+ implementations and a core template that uses the helpers that uses the
+ same algorithm to do the work. We'll make the helpers specializations of a
+ non existing template so I can let the compiler figure out the right one
+ to use from a simple argument to the algorithm class
+ */
+ template <class C> class MapperImpl;
+ template<> class MapperImpl<SwTextFormatColl>
+ {
+ private:
+ SwDoc &mrDoc;
+ public:
+ MapperImpl(SwDoc &rDoc) : mrDoc(rDoc) {}
+ SwTextFormatColl* GetBuiltInStyle(ww::sti eSti);
+ SwTextFormatColl* GetStyle(const OUString &rName);
+ SwTextFormatColl* MakeStyle(const OUString &rName);
+ };
+
+ SwTextFormatColl* MapperImpl<SwTextFormatColl>::GetBuiltInStyle(ww::sti eSti)
+ {
+ const RES_POOL_COLLFMT_TYPE RES_NONE = RES_POOLCOLL_DOC_END;
+ static const RES_POOL_COLLFMT_TYPE aArr[]=
+ {
+ RES_POOLCOLL_STANDARD, RES_POOLCOLL_HEADLINE1,
+ RES_POOLCOLL_HEADLINE2, RES_POOLCOLL_HEADLINE3,
+ RES_POOLCOLL_HEADLINE4, RES_POOLCOLL_HEADLINE5,
+ RES_POOLCOLL_HEADLINE6, RES_POOLCOLL_HEADLINE7,
+ RES_POOLCOLL_HEADLINE8, RES_POOLCOLL_HEADLINE9,
+ RES_POOLCOLL_TOX_IDX1, RES_POOLCOLL_TOX_IDX2,
+ RES_POOLCOLL_TOX_IDX3, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
+ RES_NONE, RES_NONE, RES_POOLCOLL_TOX_CNTNT1,
+ RES_POOLCOLL_TOX_CNTNT2, RES_POOLCOLL_TOX_CNTNT3,
+ RES_POOLCOLL_TOX_CNTNT4, RES_POOLCOLL_TOX_CNTNT5,
+ RES_POOLCOLL_TOX_CNTNT6, RES_POOLCOLL_TOX_CNTNT7,
+ RES_POOLCOLL_TOX_CNTNT8, RES_POOLCOLL_TOX_CNTNT9, RES_NONE,
+ RES_POOLCOLL_FOOTNOTE, RES_NONE, RES_POOLCOLL_HEADER,
+ RES_POOLCOLL_FOOTER, RES_POOLCOLL_TOX_IDXH, RES_NONE, RES_NONE,
+ RES_POOLCOLL_ENVELOPE_ADDRESS, RES_POOLCOLL_SEND_ADDRESS, RES_NONE,
+ RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_POOLCOLL_ENDNOTE,
+ RES_NONE, RES_NONE, RES_NONE, RES_POOLCOLL_LISTS_BEGIN,
+ RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
+ RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
+ RES_NONE, RES_NONE, RES_POOLCOLL_HEADLINE_BASE, RES_NONE,
+ RES_POOLCOLL_SIGNATURE, RES_NONE, RES_POOLCOLL_TEXT,
+ RES_POOLCOLL_TEXT_MOVE, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
+ RES_NONE, RES_NONE, RES_POOLCOLL_DOC_SUBTITLE
+ };
+
+ OSL_ENSURE(SAL_N_ELEMENTS(aArr) == 75, "Style Array has false size");
+
+ SwTextFormatColl* pRet = nullptr;
+ //If this is a built-in word style that has a built-in writer
+ //equivalent, then map it to one of our built in styles regardless
+ //of its name
+ if (sal::static_int_cast< size_t >(eSti) < SAL_N_ELEMENTS(aArr) && aArr[eSti] != RES_NONE)
+ pRet = mrDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( static_cast< sal_uInt16 >(aArr[eSti]), false);
+ return pRet;
+ }
+
+ SwTextFormatColl* MapperImpl<SwTextFormatColl>::GetStyle(const OUString &rName)
+ {
+ return sw::util::GetParaStyle(mrDoc, rName);
+ }
+
+ SwTextFormatColl* MapperImpl<SwTextFormatColl>::MakeStyle(const OUString &rName)
+ {
+ return mrDoc.MakeTextFormatColl(rName,
+ mrDoc.GetDfltTextFormatColl());
+ }
+
+ template<> class MapperImpl<SwCharFormat>
+ {
+ private:
+ SwDoc &mrDoc;
+ public:
+ MapperImpl(SwDoc &rDoc) : mrDoc(rDoc) {}
+ SwCharFormat* GetBuiltInStyle(ww::sti eSti);
+ SwCharFormat* GetStyle(const OUString &rName);
+ SwCharFormat* MakeStyle(const OUString &rName);
+ };
+
+ SwCharFormat* MapperImpl<SwCharFormat>::GetBuiltInStyle(ww::sti eSti)
+ {
+ RES_POOL_CHRFMT_TYPE eLookup = RES_POOLCHR_NORMAL_END;
+ switch (eSti)
+ {
+ case ww::stiFootnoteRef:
+ eLookup = RES_POOLCHR_FOOTNOTE;
+ break;
+ case ww::stiLnn:
+ eLookup = RES_POOLCHR_LINENUM;
+ break;
+ case ww::stiPgn:
+ eLookup = RES_POOLCHR_PAGENO;
+ break;
+ case ww::stiEdnRef:
+ eLookup = RES_POOLCHR_ENDNOTE;
+ break;
+ case ww::stiHyperlink:
+ eLookup = RES_POOLCHR_INET_NORMAL;
+ break;
+ case ww::stiHyperlinkFollowed:
+ eLookup = RES_POOLCHR_INET_VISIT;
+ break;
+ case ww::stiStrong:
+ eLookup = RES_POOLCHR_HTML_STRONG;
+ break;
+ case ww::stiEmphasis:
+ eLookup = RES_POOLCHR_HTML_EMPHASIS;
+ break;
+ default:
+ eLookup = RES_POOLCHR_NORMAL_END;
+ break;
+ }
+ SwCharFormat *pRet = nullptr;
+ if (eLookup != RES_POOLCHR_NORMAL_END)
+ pRet = mrDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( static_cast< sal_uInt16 >(eLookup) );
+ return pRet;
+ }
+
+ SwCharFormat* MapperImpl<SwCharFormat>::GetStyle(const OUString &rName)
+ {
+ return sw::util::GetCharStyle(mrDoc, rName);
+ }
+
+ SwCharFormat* MapperImpl<SwCharFormat>::MakeStyle(const OUString &rName)
+ {
+ return mrDoc.MakeCharFormat(rName, mrDoc.GetDfltCharFormat());
+ }
+
+ template<class C> class StyleMapperImpl
+ {
+ private:
+ MapperImpl<C> maHelper;
+ o3tl::sorted_vector<const C*> maUsedStyles;
+ C* MakeNonCollidingStyle(const OUString& rName,
+ std::map<OUString, sal_Int32>& rCollisions);
+ public:
+ typedef std::pair<C*, bool> StyleResult;
+ explicit StyleMapperImpl(SwDoc &rDoc) : maHelper(rDoc) {}
+ StyleResult GetStyle(const OUString& rName, ww::sti eSti,
+ std::map<OUString, sal_Int32>& rCollisions);
+ };
+
+ template<class C>
+ typename StyleMapperImpl<C>::StyleResult
+ StyleMapperImpl<C>::GetStyle(const OUString& rName, ww::sti eSti,
+ std::map<OUString, sal_Int32>& rCollisions)
+ {
+ C *pRet = maHelper.GetBuiltInStyle(eSti);
+
+ //If we've used it once, don't reuse it
+ if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet)))
+ pRet = nullptr;
+
+ if (!pRet)
+ {
+ pRet = maHelper.GetStyle(rName);
+ //If we've used it once, don't reuse it
+ if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet)))
+ pRet = nullptr;
+ }
+
+ bool bStyExist = pRet != nullptr;
+
+ if (!pRet)
+ {
+ OUString aName(rName);
+ sal_Int32 nIdx = rName.indexOf(',');
+ // No commas allow in SW style names
+ if (-1 != nIdx)
+ aName = rName.copy( 0, nIdx );
+ pRet = MakeNonCollidingStyle(aName, rCollisions);
+ }
+
+ if (pRet)
+ maUsedStyles.insert(pRet);
+
+ return StyleResult(pRet, bStyExist);
+ }
+
+ template<class C>
+ C* StyleMapperImpl<C>::MakeNonCollidingStyle(const OUString& rName,
+ std::map<OUString, sal_Int32>& rCollisions)
+ {
+ OUString aName(rName);
+ C* pColl = nullptr;
+
+ if (nullptr != (pColl = maHelper.GetStyle(aName)))
+ {
+ //If the style collides first stick WW- in front of it, unless
+ //it already has it and then successively add a larger and
+ //larger number after it, it's got to work at some stage!
+ if (!aName.startsWith("WW-"))
+ aName = "WW-" + aName;
+
+ OUString aBaseName = aName;
+ sal_Int32 nI = 1;
+
+ // if we've seen this basename before then start at
+ // where we finished the last time
+ auto aFind = rCollisions.find(aBaseName);
+ if (aFind != rCollisions.end())
+ nI = aFind->second;
+
+ while (
+ nullptr != (pColl = maHelper.GetStyle(aName)) &&
+ (nI < SAL_MAX_INT32)
+ )
+ {
+ aName = aBaseName + OUString::number(nI++);
+ }
+
+ rCollisions.insert_or_assign(aBaseName, nI);
+ }
+
+ return pColl ? nullptr : maHelper.MakeStyle(aName);
+ }
+
+ static OUString FindBestMSSubstituteFont(std::u16string_view rFont)
+ {
+ if (IsOpenSymbol(rFont))
+ return "Arial Unicode MS";
+ return GetSubsFontName(rFont, SubsFontFlags::ONLYONE | SubsFontFlags::MS);
+ }
+
+ namespace {
+
+ //Utility to remove entries before a given starting position
+ class IfBeforeStart
+ {
+ private:
+ sal_Int32 mnStart;
+ public:
+ explicit IfBeforeStart(sal_Int32 nStart) : mnStart(nStart) {}
+ bool operator()(const sw::util::CharRunEntry &rEntry) const
+ {
+ return rEntry.mnEndPos < mnStart;
+ }
+ };
+
+ }
+}
+
+/// Count what Word calls left/right margin from a format's LRSpace + Box.
+static SvxLRSpaceItem lcl_getWordLRSpace(const SwFrameFormat& rFormat)
+{
+ SvxLRSpaceItem aLR(rFormat.GetLRSpace());
+ const SvxBoxItem& rBox = rFormat.GetBox();
+
+ aLR.SetLeft(aLR.GetLeft() + rBox.GetDistance(SvxBoxItemLine::LEFT));
+ if (const editeng::SvxBorderLine* pLeft = rBox.GetLeft())
+ aLR.SetLeft(aLR.GetLeft() + pLeft->GetWidth());
+
+ aLR.SetRight(aLR.GetRight() + rBox.GetDistance(SvxBoxItemLine::RIGHT));
+ if (const editeng::SvxBorderLine* pRight = rBox.GetRight())
+ aLR.SetRight(aLR.GetRight() + pRight->GetWidth());
+
+ return aLR;
+}
+
+namespace sw
+{
+ namespace util
+ {
+
+ bool IsPlausableSingleWordSection(const SwFrameFormat &rTitleFormat, const SwFrameFormat &rFollowFormat)
+ {
+ bool bPlausableSingleWordSection = true;
+
+ const SwFormatCol& rFirstCols = rTitleFormat.GetCol();
+ const SwFormatCol& rFollowCols = rFollowFormat.GetCol();
+ const SwColumns& rFirstColumns = rFirstCols.GetColumns();
+ const SwColumns& rFollowColumns = rFollowCols.GetColumns();
+ SvxLRSpaceItem aOneLR = lcl_getWordLRSpace(rTitleFormat);
+ SvxLRSpaceItem aTwoLR = lcl_getWordLRSpace(rFollowFormat);
+ const SwFormatFrameSize& rFirstFrameSize = rTitleFormat.GetFrameSize();
+ const SwFormatFrameSize& rFollowFrameSize = rFollowFormat.GetFrameSize();
+
+ if (rFirstColumns.size() != rFollowColumns.size())
+ {
+ //e.g. #i4320#
+ bPlausableSingleWordSection = false;
+ }
+ else if (aOneLR != aTwoLR)
+ bPlausableSingleWordSection = false;
+ else if (rFirstFrameSize != rFollowFrameSize)
+ bPlausableSingleWordSection = false;
+ else
+ {
+ HdFtDistanceGlue aOne(rTitleFormat.GetAttrSet());
+ HdFtDistanceGlue aTwo(rFollowFormat.GetAttrSet());
+ //e.g. #i14509#
+ if (!aOne.StrictEqualTopBottom(aTwo))
+ bPlausableSingleWordSection = false;
+ }
+ return bPlausableSingleWordSection;
+ }
+
+ HdFtDistanceGlue::HdFtDistanceGlue(const SfxItemSet &rPage)
+ {
+ if (const SvxBoxItem *pBox = rPage.GetItem<SvxBoxItem>(RES_BOX))
+ {
+ m_DyaHdrTop = pBox->CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine*/true );
+ m_DyaHdrBottom = pBox->CalcLineSpace( SvxBoxItemLine::BOTTOM, /*bEvenIfNoLine*/true );
+ }
+ else
+ {
+ m_DyaHdrTop = m_DyaHdrBottom = 0;
+ }
+ const SvxULSpaceItem &rUL = rPage.Get(RES_UL_SPACE);
+ m_DyaHdrTop += rUL.GetUpper();
+ m_DyaHdrBottom += rUL.GetLower();
+
+ m_DyaTop = m_DyaHdrTop;
+ m_DyaBottom = m_DyaHdrBottom;
+
+ const SwFormatHeader *pHd = rPage.GetItem<SwFormatHeader>(RES_HEADER);
+ if (pHd && pHd->IsActive() && pHd->GetHeaderFormat())
+ {
+ mbHasHeader = true;
+ m_DyaTop = m_DyaTop + static_cast< sal_uInt16 >( (myImplHelpers::CalcHdDist(*(pHd->GetHeaderFormat()))) );
+ }
+ else
+ mbHasHeader = false;
+
+ const SwFormatFooter *pFt = rPage.GetItem<SwFormatFooter>(RES_FOOTER);
+ if (pFt && pFt->IsActive() && pFt->GetFooterFormat())
+ {
+ mbHasFooter = true;
+ m_DyaBottom = m_DyaBottom + static_cast< sal_uInt16 >( (myImplHelpers::CalcFtDist(*(pFt->GetFooterFormat()))) );
+ }
+ else
+ mbHasFooter = false;
+ }
+
+ bool HdFtDistanceGlue::StrictEqualTopBottom(const HdFtDistanceGlue &rOther)
+ const
+ {
+ // Check top only if both object have a header or if
+ // both object don't have a header
+ if (HasHeader() == rOther.HasHeader())
+ {
+ if (m_DyaTop != rOther.m_DyaTop)
+ return false;
+ }
+
+ // Check bottom only if both object have a footer or if
+ // both object don't have a footer
+ if (HasFooter() == rOther.HasFooter())
+ {
+ if (m_DyaBottom != rOther.m_DyaBottom)
+ return false;
+ }
+
+ return true;
+ }
+
+ ParaStyleMapper::ParaStyleMapper(SwDoc &rDoc)
+ : mpImpl(new myImplHelpers::StyleMapperImpl<SwTextFormatColl>(rDoc))
+ {
+ }
+
+ ParaStyleMapper::~ParaStyleMapper()
+ {
+ }
+
+ ParaStyleMapper::StyleResult ParaStyleMapper::GetStyle(
+ const OUString& rName, ww::sti eSti,
+ std::map<OUString, sal_Int32>& rCollisions)
+ {
+ return mpImpl->GetStyle(rName, eSti, rCollisions);
+ }
+
+ CharStyleMapper::CharStyleMapper(SwDoc &rDoc)
+ : mpImpl(new myImplHelpers::StyleMapperImpl<SwCharFormat>(rDoc))
+ {
+ }
+
+ CharStyleMapper::~CharStyleMapper()
+ {
+ }
+
+ CharStyleMapper::StyleResult CharStyleMapper::GetStyle(
+ const OUString& rName, ww::sti eSti,
+ std::map<OUString, sal_Int32>& rCollisions)
+ {
+ return mpImpl->GetStyle(rName, eSti, rCollisions);
+ }
+
+ FontMapExport::FontMapExport(std::u16string_view rFamilyName)
+ {
+ sal_Int32 nIndex = 0;
+ msPrimary = GetNextFontToken(rFamilyName, nIndex);
+ msSecondary = myImplHelpers::FindBestMSSubstituteFont(msPrimary);
+ if (msSecondary.isEmpty() && nIndex != -1)
+ msSecondary = GetNextFontToken(rFamilyName, nIndex);
+ }
+
+ bool ItemSort::operator()(sal_uInt16 nA, sal_uInt16 nB) const
+ {
+ /*
+ #i24291#
+ All we want to do is ensure for now is that if a charfmt exist
+ in the character properties that it rises to the top and is
+ exported first. In the future we might find more ordering
+ dependencies for export, in which case this is the place to do
+ it
+ */
+ if (nA == nB)
+ return false;
+ if (nA == RES_TXTATR_CHARFMT)
+ return true;
+ if (nB == RES_TXTATR_CHARFMT)
+ return false;
+ if (nA == RES_TXTATR_INETFMT)
+ return true;
+ if (nB == RES_TXTATR_INETFMT)
+ return false;
+ return nA < nB;
+ }
+
+ CharRuns GetPseudoCharRuns(const SwTextNode& rTextNd)
+ {
+ const OUString &rText = rTextNd.GetText();
+
+ bool bParaIsRTL = false;
+ if (SvxFrameDirection::Horizontal_RL_TB ==
+ rTextNd.GetDoc().GetTextDirection(SwPosition(rTextNd)))
+ {
+ bParaIsRTL = true;
+ }
+
+ using namespace ::com::sun::star::i18n;
+
+ sal_uInt16 nScript = i18n::ScriptType::LATIN;
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ if (!rText.isEmpty())
+ nScript = g_pBreakIt->GetBreakIter()->getScriptType(rText, 0);
+
+ TypedWhichId<SvxFontItem> nFontWhichId = GetWhichOfScript(RES_CHRATR_FONT, nScript);
+ rtl_TextEncoding eChrSet = rTextNd.GetAttr(nFontWhichId).GetCharSet();
+ eChrSet = GetExtendedTextEncoding(eChrSet);
+
+ CharRuns aRunChanges;
+
+ if (rText.isEmpty())
+ {
+ aRunChanges.emplace_back(0, nScript, eChrSet,
+ bParaIsRTL);
+ return aRunChanges;
+ }
+
+ typedef std::pair<int32_t, bool> DirEntry;
+ typedef std::pair<sal_Int32, sal_uInt16> ScriptEntry;
+ std::vector<DirEntry> aDirChanges;
+ std::vector<ScriptEntry> aScripts;
+
+ UBiDiDirection eDefaultDir = bParaIsRTL ? UBIDI_RTL : UBIDI_LTR;
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized(rText.getLength(), 0, &nError);
+ ubidi_setPara(pBidi, reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength(),
+ static_cast< UBiDiLevel >(eDefaultDir), nullptr, &nError);
+
+ sal_Int32 nCount = ubidi_countRuns(pBidi, &nError);
+ aDirChanges.reserve(nCount);
+
+ int32_t nStart = 0;
+ int32_t nEnd;
+ UBiDiLevel nCurrDir;
+
+ for (sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx)
+ {
+ ubidi_getLogicalRun(pBidi, nStart, &nEnd, &nCurrDir);
+ /*
+ UBiDiLevel is the type of the level values in this BiDi
+ implementation.
+
+ It holds an embedding level and indicates the visual direction
+ by its bit 0 (even/odd value).
+
+ The value for UBIDI_DEFAULT_LTR is even and the one for
+ UBIDI_DEFAULT_RTL is odd
+ */
+ aDirChanges.emplace_back(nEnd, nCurrDir & 0x1);
+ nStart = nEnd;
+ }
+ ubidi_close(pBidi);
+
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ sal_Int32 nLen = rText.getLength();
+ sal_Int32 nPos = 0;
+ while (nPos < nLen)
+ {
+ sal_Int32 nEnd2 = g_pBreakIt->GetBreakIter()->endOfScript(rText, nPos,
+ nScript);
+ if (nEnd2 < 0)
+ break;
+ nPos = nEnd2;
+ aScripts.emplace_back(nPos, nScript);
+ nScript = g_pBreakIt->GetBreakIter()->getScriptType(rText, nPos);
+ }
+
+ auto aBiDiEnd = aDirChanges.cend();
+ auto aScriptEnd = aScripts.cend();
+
+ auto aBiDiIter = aDirChanges.cbegin();
+ auto aScriptIter = aScripts.cbegin();
+
+ bool bCharIsRTL = bParaIsRTL;
+
+ while (
+ aBiDiIter != aBiDiEnd ||
+ aScriptIter != aScriptEnd
+ )
+ {
+ sal_Int32 nMinPos = rText.getLength();
+
+ if (aBiDiIter != aBiDiEnd)
+ {
+ if (aBiDiIter->first < nMinPos)
+ nMinPos = aBiDiIter->first;
+ bCharIsRTL = aBiDiIter->second;
+ }
+
+ if (aScriptIter != aScriptEnd)
+ {
+ if (aScriptIter->first < nMinPos)
+ nMinPos = aScriptIter->first;
+ nScript = aScriptIter->second;
+ }
+
+ aRunChanges.emplace_back(nMinPos, nScript, eChrSet, bCharIsRTL);
+
+ if (aBiDiIter != aBiDiEnd)
+ {
+ if (aBiDiIter->first == nMinPos)
+ ++aBiDiIter;
+ }
+
+ if (aScriptIter != aScriptEnd)
+ {
+ if (aScriptIter->first == nMinPos)
+ ++aScriptIter;
+ }
+ }
+
+ std::erase_if(aRunChanges, myImplHelpers::IfBeforeStart(0/*nTextStart*/));
+
+ return aRunChanges;
+ }
+ }
+
+ namespace ms
+ {
+ sal_uInt8 rtl_TextEncodingToWinCharset(rtl_TextEncoding eTextEncoding)
+ {
+ sal_uInt8 nRet =
+ rtl_getBestWindowsCharsetFromTextEncoding(eTextEncoding);
+ switch (eTextEncoding)
+ {
+ case RTL_TEXTENCODING_DONTKNOW:
+ case RTL_TEXTENCODING_UCS2:
+ case RTL_TEXTENCODING_UTF7:
+ case RTL_TEXTENCODING_UTF8:
+ case RTL_TEXTENCODING_JAVA_UTF8:
+ nRet = 0x01;
+ break;
+ default:
+ break;
+ }
+ return nRet;
+ }
+
+ static bool
+ CanEncode(OUString const& rString, rtl_TextEncoding const eEncoding)
+ {
+ OString tmp;
+ return rString.convertToString(&tmp, eEncoding,
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR);
+ }
+
+ sal_uInt8 rtl_TextEncodingToWinCharsetRTF(
+ OUString const& rFontName, OUString const& rAltName,
+ rtl_TextEncoding eTextEncoding)
+ {
+ sal_uInt8 nRet =
+ rtl_getBestWindowsCharsetFromTextEncoding(eTextEncoding);
+ rtl_TextEncoding enc2 = rtl_getTextEncodingFromWindowsCharset(nRet);
+ if (!rtl_isOctetTextEncoding(enc2) /* check to avoid asserts */ ||
+ !(CanEncode(rFontName, enc2) && CanEncode(rAltName, enc2)))
+ {
+ static struct { rtl_TextEncoding enc; sal_uInt8 charset; }
+ const s_fallbacks [] = {
+ { RTL_TEXTENCODING_MS_932, 0x80 }, // Shift-JIS
+ { RTL_TEXTENCODING_MS_936, 0x86 }, // GB-2312
+ { RTL_TEXTENCODING_MS_950, 0x88 }, // Big5
+ { RTL_TEXTENCODING_MS_949, 0x81 }, // EUC-KR
+ };
+ for (const auto & i : s_fallbacks)
+ {
+ // fall back to a charset that can at least encode the
+ // font's name
+ if (CanEncode(rFontName, i.enc)
+ && CanEncode(rAltName, i.enc))
+ {
+ return i.charset;
+ }
+ }
+ SAL_INFO("sw.rtf", "no fallback charset found for font: "
+ << rFontName << " " << rAltName);
+ nRet = 0x01; // all hope lost: "default", whatever that is
+ }
+ return nRet;
+ }
+
+ sal_uInt32 DateTime2DTTM( const DateTime& rDT )
+ {
+ /*
+ mint short :6 0000003F minutes (0-59)
+ hr short :5 000007C0 hours (0-23)
+ dom short :5 0000F800 days of month (1-31)
+ mon short :4 000F0000 months (1-12)
+ yr short :9 1FF00000 years (1900-2411)-1900
+ wdy short :3 E0000000 weekday(Sunday=0
+ Monday=1
+ ( wdy can be ignored ) Tuesday=2
+ Wednesday=3
+ Thursday=4
+ Friday=5
+ Saturday=6)
+ */
+
+ if ( rDT.GetDate() == 0 )
+ return 0;
+ sal_uInt32 nDT = ( rDT.GetDayOfWeek() + 1 ) % 7;
+ nDT <<= 9;
+ nDT += ( rDT.GetYear() - 1900 ) & 0x1ff;
+ nDT <<= 4;
+ nDT += rDT.GetMonth() & 0xf;
+ nDT <<= 5;
+ nDT += rDT.GetDay() & 0x1f;
+ nDT <<= 5;
+ nDT += rDT.GetHour() & 0x1f;
+ nDT <<= 6;
+ nDT += rDT.GetMin() & 0x3f;
+ return nDT;
+ }
+
+
+ /** Find cFind in rParams if not embedded in " double quotes.
+ Will NOT find '\\' or '"'.
+ */
+ static sal_Int32 findUnquoted( std::u16string_view aParams, sal_Unicode cFind, sal_Int32 nFromPos )
+ {
+ const sal_Int32 nLen = aParams.size();
+ if (nFromPos < 0 || nLen <= nFromPos)
+ return -1;
+ for (sal_Int32 nI = nFromPos; nI < nLen; ++nI)
+ {
+ const sal_Unicode c = aParams[nI];
+ if (c == '\\')
+ ++nI;
+ else if (c == '\"')
+ {
+ ++nI;
+ // While not at the end and not at an unescaped end quote
+ while (nI < nLen)
+ {
+ if (aParams[nI] == '\"' && aParams[nI-1] != '\\')
+ break;
+ ++nI;
+ }
+ }
+ else //normal unquoted section
+ {
+ if (c == cFind)
+ return nI;
+ }
+ }
+ return -1;
+ }
+
+ /** Find all rFind in rParams if not embedded in " double quotes and
+ replace with rReplace. Will NOT find '\\' or '"'.
+ */
+ static bool replaceUnquoted( OUString& rParams, std::u16string_view aFind, std::u16string_view aReplace )
+ {
+ bool bReplaced = false;
+ if (aFind.empty())
+ return bReplaced;
+ const sal_Unicode cFirst = aFind[0];
+
+ sal_Int32 nLen = rParams.getLength();
+ for (sal_Int32 nI = 0; nI < nLen; ++nI)
+ {
+ const sal_Unicode c = rParams[nI];
+ if (rParams[nI] == '\\')
+ ++nI;
+ else if (rParams[nI] == '\"')
+ {
+ ++nI;
+ // While not at the end and not at an unescaped end quote
+ while (nI < nLen)
+ {
+ if (rParams[nI] == '\"' && rParams[nI-1] != '\\')
+ break;
+ ++nI;
+ }
+ }
+ else //normal unquoted section
+ {
+ if (c == cFirst && rParams.match( aFind, nI))
+ {
+ const sal_Int32 nFindLen = aFind.size();
+ const sal_Int32 nDiff = aReplace.size() - nFindLen;
+ rParams = rParams.replaceAt( nI, nFindLen, aReplace);
+ nI += nFindLen + nDiff - 1;
+ nLen += nDiff;
+ bReplaced = true;
+ }
+ }
+ }
+ return bReplaced;
+ }
+
+ sal_uLong MSDateTimeFormatToSwFormat(OUString& rParams,
+ SvNumberFormatter *pFormatter, LanguageType &rLang, bool bHijri,
+ LanguageType nDocLang)
+ {
+ // tell the Formatter about the new entry
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ sal_uInt32 nKey = 0;
+
+ SwapQuotesInField(rParams);
+
+ // Force to Japanese when finding one of 'geE'.
+ // XXX This actually may not be correct, all era keywords could be
+ // used in other locales as well. I just don't know about Word. But
+ // this is how it was for 10 years...
+ bool bForceJapanese = (-1 != findUnquoted( rParams, 'g', 0));
+ // XXX Why replace? The number formatter does handle them and this
+ // effectively changes from Gengou to Gregorian calendar. Legacy
+ // because it wasn't supported a decade ago and now moot? Or is
+ // that a Word specialty?
+ bForceJapanese |= replaceUnquoted( rParams, u"ee", u"yyyy");
+ bForceJapanese |= replaceUnquoted( rParams, u"EE", u"YYYY");
+ if (LANGUAGE_FRENCH != nDocLang)
+ {
+ // Handle the 'a' case here
+ sal_Int32 nLastPos = 0;
+ do
+ {
+ sal_Int32 nPos = findUnquoted( rParams, 'a', nLastPos + 1 );
+ bForceJapanese |= ( nPos != -1 && IsNotAM( rParams, nPos ) );
+ nLastPos = nPos;
+ } while ( -1 != nLastPos );
+ }
+
+ // Force to NatNum when finding one of 'oOA'
+ bool bForceNatNum = replaceUnquoted( rParams, u"o", u"m")
+ || replaceUnquoted( rParams, u"O", u"M");
+ if (LANGUAGE_FRENCH != nDocLang)
+ {
+ // Handle the 'A' case here
+ sal_Int32 nLastPos = 0;
+ do
+ {
+ sal_Int32 nPos = findUnquoted( rParams, 'A', nLastPos + 1 );
+ bool bIsCharA = ( nPos != -1 && IsNotAM( rParams, nPos ) );
+ bForceNatNum |= bIsCharA;
+ if ( bIsCharA )
+ rParams = rParams.replaceAt( nPos, 1, u"D" );
+ nLastPos = nPos;
+ } while ( -1 != nLastPos );
+ }
+
+ sal_Int32 nLen = rParams.getLength();
+ for (sal_Int32 nI = 0; nI < nLen; ++nI)
+ {
+ if (rParams[nI] == '\\')
+ ++nI;
+ else if (rParams[nI] == '\"')
+ {
+ ++nI;
+ // While not at the end and not at an unescaped end quote
+ while (nI < nLen)
+ {
+ if (rParams[nI] == '\"' && rParams[nI-1] != '\\')
+ break;
+ ++nI;
+ }
+ }
+ else //normal unquoted section
+ {
+ sal_Unicode nChar = rParams[nI];
+
+ // Change the localized word string to english
+ if ( nDocLang == LANGUAGE_FRENCH )
+ {
+ if ( ( nChar == 'a' || nChar == 'A' ) && IsNotAM(rParams, nI) )
+ rParams = rParams.replaceAt(nI, 1, u"Y");
+ }
+ if (nChar == '/')
+ {
+ // MM: We have to escape '/' in case it's used as a char.
+ // But not if it's a '/' inside AM/PM
+ if (!(IsPreviousAM(rParams, nI) && IsNextPM(rParams, nI)))
+ {
+ rParams = rParams.replaceAt(nI, 1, u"\\/");
+ nLen++;
+ }
+ nI++;
+ }
+
+ // Deal with language differences in date format expression.
+ // Should be made with i18n framework.
+ // The list of the mappings and of those "special" locales is to be found at:
+ // http://l10n.openoffice.org/i18n_framework/LocaleData.html
+ if ( !bForceJapanese && !bForceNatNum )
+ {
+ // Convert to the localized equivalent for OOo
+ if ( rLang == LANGUAGE_FINNISH )
+ {
+ if (nChar == 'y' || nChar == 'Y')
+ rParams = rParams.replaceAt(nI, 1, u"V");
+ else if (nChar == 'm' || nChar == 'M')
+ rParams = rParams.replaceAt(nI, 1, u"K");
+ else if (nChar == 'd' || nChar == 'D')
+ rParams = rParams.replaceAt(nI, 1, u"P");
+ else if (nChar == 'h' || nChar == 'H')
+ rParams = rParams.replaceAt(nI, 1, u"T");
+ }
+ else if ( rLang.anyOf(
+ LANGUAGE_DANISH,
+ LANGUAGE_NORWEGIAN,
+ LANGUAGE_NORWEGIAN_BOKMAL,
+ LANGUAGE_NORWEGIAN_NYNORSK,
+ LANGUAGE_SWEDISH,
+ LANGUAGE_SWEDISH_FINLAND))
+ {
+ if (nChar == 'h' || nChar == 'H')
+ rParams = rParams.replaceAt(nI, 1, u"T");
+ }
+ else if ( rLang.anyOf(
+ LANGUAGE_PORTUGUESE,
+ LANGUAGE_PORTUGUESE_BRAZILIAN,
+ LANGUAGE_SPANISH_MODERN,
+ LANGUAGE_SPANISH_DATED,
+ LANGUAGE_SPANISH_MEXICAN,
+ LANGUAGE_SPANISH_GUATEMALA,
+ LANGUAGE_SPANISH_COSTARICA,
+ LANGUAGE_SPANISH_PANAMA,
+ LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
+ LANGUAGE_SPANISH_VENEZUELA,
+ LANGUAGE_SPANISH_COLOMBIA,
+ LANGUAGE_SPANISH_PERU,
+ LANGUAGE_SPANISH_ARGENTINA,
+ LANGUAGE_SPANISH_ECUADOR,
+ LANGUAGE_SPANISH_CHILE,
+ LANGUAGE_SPANISH_URUGUAY,
+ LANGUAGE_SPANISH_PARAGUAY,
+ LANGUAGE_SPANISH_BOLIVIA,
+ LANGUAGE_SPANISH_EL_SALVADOR,
+ LANGUAGE_SPANISH_HONDURAS,
+ LANGUAGE_SPANISH_NICARAGUA,
+ LANGUAGE_SPANISH_PUERTO_RICO))
+ {
+ if (nChar == 'a' || nChar == 'A')
+ rParams = rParams.replaceAt(nI, 1, u"O");
+ else if (nChar == 'y' || nChar == 'Y')
+ rParams = rParams.replaceAt(nI, 1, u"A");
+ }
+ else if ( rLang.anyOf(
+ LANGUAGE_DUTCH,
+ LANGUAGE_DUTCH_BELGIAN))
+ {
+ if (nChar == 'y' || nChar == 'Y')
+ rParams = rParams.replaceAt(nI, 1, u"J");
+ else if (nChar == 'u' || nChar == 'U')
+ rParams = rParams.replaceAt(nI, 1, u"H");
+ }
+ else if ( rLang.anyOf(
+ LANGUAGE_ITALIAN,
+ LANGUAGE_ITALIAN_SWISS))
+ {
+ if (nChar == 'a' || nChar == 'A')
+ rParams = rParams.replaceAt(nI, 1, u"O");
+ else if (nChar == 'g' || nChar == 'G')
+ rParams = rParams.replaceAt(nI, 1, u"X");
+ else if (nChar == 'y' || nChar == 'Y')
+ rParams = rParams.replaceAt(nI, 1, u"A");
+ else if (nChar == 'd' || nChar == 'D')
+ rParams = rParams.replaceAt(nI, 1, u"G");
+ }
+ else if ( rLang.anyOf(
+ LANGUAGE_GERMAN,
+ LANGUAGE_GERMAN_SWISS,
+ LANGUAGE_GERMAN_AUSTRIAN,
+ LANGUAGE_GERMAN_LUXEMBOURG,
+ LANGUAGE_GERMAN_LIECHTENSTEIN))
+ {
+ if (nChar == 'y' || nChar == 'Y')
+ rParams = rParams.replaceAt(nI, 1, u"J");
+ else if (nChar == 'd' || nChar == 'D')
+ rParams = rParams.replaceAt(nI, 1, u"T");
+ }
+ else if ( rLang.anyOf(
+ LANGUAGE_FRENCH,
+ LANGUAGE_FRENCH_BELGIAN,
+ LANGUAGE_FRENCH_CANADIAN,
+ LANGUAGE_FRENCH_SWISS,
+ LANGUAGE_FRENCH_LUXEMBOURG,
+ LANGUAGE_FRENCH_MONACO))
+ {
+ if (nChar == 'y' || nChar == 'Y' || nChar == 'a')
+ rParams = rParams.replaceAt(nI, 1, u"A");
+ else if (nChar == 'd' || nChar == 'D' || nChar == 'j')
+ rParams = rParams.replaceAt(nI, 1, u"J");
+ }
+ }
+ }
+ }
+
+ if (bForceNatNum)
+ bForceJapanese = true;
+
+ if (bForceJapanese)
+ rLang = LANGUAGE_JAPANESE;
+
+ if (bForceNatNum)
+ rParams = "[NatNum1][$-411]" + rParams;
+
+ if (bHijri)
+ rParams = "[~hijri]" + rParams;
+
+ pFormatter->PutEntry(rParams, nCheckPos, nType, nKey, rLang);
+
+ return nKey;
+ }
+
+ bool IsPreviousAM(std::u16string_view rParams, sal_Int32 nPos)
+ {
+ return nPos>=2 && o3tl::matchIgnoreAsciiCase(rParams, u"am", nPos-2);
+ }
+ bool IsNextPM(std::u16string_view rParams, sal_Int32 nPos)
+ {
+ return o3tl::make_unsigned(nPos+2)<rParams.size() && o3tl::matchIgnoreAsciiCase(rParams, u"pm", nPos+1);
+ }
+ bool IsNotAM(std::u16string_view rParams, sal_Int32 nPos)
+ {
+ ++nPos;
+ return o3tl::make_unsigned(nPos)>=rParams.size() || (rParams[nPos]!='M' && rParams[nPos]!='m');
+ }
+
+ void SwapQuotesInField(OUString &rFormat)
+ {
+ //Swap unescaped " and ' with ' and "
+ const sal_Int32 nLen = rFormat.getLength();
+ for (sal_Int32 nI = 0; nI < nLen; ++nI)
+ {
+ if (!nI || rFormat[nI-1]!='\\')
+ {
+ if (rFormat[nI]=='\"')
+ rFormat = rFormat.replaceAt(nI, 1, u"\'");
+ else if (rFormat[nI]=='\'')
+ rFormat = rFormat.replaceAt(nI, 1, u"\"");
+ }
+ }
+ }
+
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/writerwordglue.hxx b/sw/source/filter/ww8/writerwordglue.hxx
new file mode 100644
index 0000000000..a6714e9727
--- /dev/null
+++ b/sw/source/filter/ww8/writerwordglue.hxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WRITERWORDGLUE_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WRITERWORDGLUE_HXX
+
+#include "needed_cast.hxx"
+
+class SwFrameFormat;
+class SfxItemSet;
+
+namespace sw
+{
+ namespace types
+ {
+ /** A static_cast style cast for conversion of word types to writer's
+
+ There are a number of places where the winword types are larger
+ than the writer equivalents requiring a cast to silence warnings.
+ To avoid throwing away this useful information writer_cast is used
+ to identify where writer's types are smaller than word's.
+
+ Based on needed_cast it will compile time assert if the cast
+ becomes unnecessary at any time in the future.
+
+ @tplparam
+ Ret the desired return type
+
+ @tplparam
+ Param the type of the in param
+
+ @param
+ in the value to cast from Param to Ret
+
+ @return in casted to type Ret
+ */
+ template<typename Ret, typename Param> Ret writer_cast(Param in)
+ {
+ return ww::needed_cast<Ret, Param>(in);
+ }
+
+ /** A static_cast style cast for conversion of writer types to word's
+
+ There are a number of places where the writer types are larger than
+ the winword equivalents requiring a cast to silence warnings. To
+ avoid throwing away this useful information writer_cast is used to
+ identify where word's types are smaller than writers's.
+
+ Based on needed_cast it will compile time assert if the cast
+ becomes unnecessary at any time in the future.
+
+ @tplparam
+ Ret the desired return type
+
+ @tplparam
+ Param the type of the in param
+
+ @param
+ in the value to cast from Param to Ret
+
+ @return in casted to type Ret
+ */
+ template<typename Ret, typename Param> Ret msword_cast(Param in)
+ {
+ return ww::needed_cast<Ret, Param>(in);
+ }
+ }
+
+ namespace util
+ {
+ /** See if two page formats can be expressed as a single word section
+
+ Word doesn't have the idea of page descriptors and follow styles
+ like writer does, the only thing it has is a section with a
+ different title page. The only difference of the title page from
+ the rest of the section is different headers/footers, everything
+ else is the same.
+
+ So this function compares two writer page fmts and sees if the
+ follow frame and the title frame are the same from word perspective
+ except for the content of their headers.
+
+ @return true if the rTitleFormat followed by rFollowFormat could be
+ expressed in word as a single word Section with different title
+ page enabled.
+
+ @see #i4320#/#i14509#/#i11717# for examples
+ */
+ bool IsPlausableSingleWordSection(const SwFrameFormat &rTitleFormat,
+ const SwFrameFormat &rFollowFormat);
+
+ /** Make export a word section top/bottom values easy
+
+ The top and bottom margins in word and writer are expressed in very
+ different ways. This class provides the equivalent word values for
+ header/footer distances from a given writer attrset of a page
+ */
+ class HdFtDistanceGlue
+ {
+ private:
+ bool mbHasHeader;
+ bool mbHasFooter;
+ public:
+ sal_uInt16 m_DyaHdrTop;
+ sal_uInt16 m_DyaHdrBottom;
+ sal_uInt16 m_DyaTop;
+ sal_uInt16 m_DyaBottom;
+ explicit HdFtDistanceGlue(const SfxItemSet &rPage);
+ bool HasHeader() const { return mbHasHeader; }
+ bool HasFooter() const { return mbHasFooter; }
+
+ /** Is the top of the page the same in both objects
+ when there are headers\footers present or non-present in both objects
+
+ This test is important, because we would like to ignore cases
+ when there is a header in one object and no header in the second
+ object - because it is wrong to compare between them.
+
+ @param
+ rOther the other HdFtDistanceGlue to compare against
+
+ @return true if the main text areas top and bottom is at the
+ same location, false otherwise (assuming both objects have\don't have
+ a header\footer)
+ */
+ bool StrictEqualTopBottom(const HdFtDistanceGlue &rOther) const;
+
+ };
+ }
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtw8esh.cxx b/sw/source/filter/ww8/wrtw8esh.cxx
new file mode 100644
index 0000000000..19ac50ccf5
--- /dev/null
+++ b/sw/source/filter/ww8/wrtw8esh.cxx
@@ -0,0 +1,3063 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/embed/Aspects.hpp>
+
+#include <hintids.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <o3tl/any.hxx>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <vcl/svapp.hxx>
+#include <sot/storage.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdview.hxx>
+#include <fmtcnct.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <ndindex.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pam.hxx>
+#include <swrect.hxx>
+#include <ndgrf.hxx>
+#include <grfatr.hxx>
+#include <ndole.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include "ww8par.hxx"
+#include <breakit.hxx>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include "attributeoutputbase.hxx"
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "wrtww8.hxx"
+#include "escher.hxx"
+#include <ndtxt.hxx>
+#include "WW8FFData.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <IDocumentStylePoolAccess.hxx>
+#include <oox/ole/olehelper.hxx>
+#include <fmturl.hxx>
+#include <frameformats.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <o3tl/enumrange.hxx>
+#include <o3tl/enumarray.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <algorithm>
+
+using ::editeng::SvxBorderLine;
+using namespace com::sun::star;
+using namespace sw::util;
+using namespace sw::types;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::drawing::XShape;
+
+bool SwBasicEscherEx::IsRelUrl() const
+{
+ bool bRelUrl = false;
+ SfxMedium * pMedium = mrWrt.GetWriter().GetMedia();
+ if ( pMedium )
+ bRelUrl = pMedium->IsRemote()
+ ? officecfg::Office::Common::Save::URL::Internet::get()
+ : officecfg::Office::Common::Save::URL::FileSystem::get();
+ return bRelUrl;
+}
+
+OUString SwBasicEscherEx::GetBasePath() const
+{
+ OUString sDocUrl;
+ SfxMedium * pMedium = mrWrt.GetWriter().GetMedia();
+ if (pMedium)
+ {
+ const SfxStringItem* pPItem = pMedium->GetItemSet().GetItem( SID_FILE_NAME );
+ if ( pPItem )
+ sDocUrl = pPItem->GetValue();
+ }
+
+ return sDocUrl.copy(0, sDocUrl.lastIndexOf('/') + 1);
+}
+
+OUString SwBasicEscherEx::BuildFileName(sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl)
+{
+ OUString aDosName( INetURLObject( rUrl ).getFSysPath( FSysStyle::Dos ) );
+ rnLevel = 0;
+ rbRel = IsRelUrl();
+
+ if (rbRel)
+ {
+ // try to convert to relative file name
+ OUString aTmpName( aDosName );
+ aDosName = INetURLObject::GetRelURL( GetBasePath(), rUrl,
+ INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::WithCharset );
+
+ if (aDosName.startsWith(INET_FILE_SCHEME))
+ {
+ // not converted to rel -> back to old, return absolute flag
+ aDosName = aTmpName;
+ rbRel = false;
+ }
+ else if (aDosName.startsWith("./"))
+ {
+ aDosName = aDosName.copy(2);
+ }
+ else
+ {
+ while (aDosName.startsWith("../"))
+ {
+ ++rnLevel;
+ aDosName = aDosName.copy(3);
+ }
+ }
+ }
+ return aDosName;
+}
+
+void SwBasicEscherEx::WriteHyperlinkWithinFly( SvMemoryStream& rStrm, const SwFormatURL* pINetFormatArg)
+{
+ if ( !pINetFormatArg ) return;
+
+ const sal_uInt8 aGuidStdLink[ 16 ] ={
+ 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
+ const sal_uInt8 aGuidUrlMoniker[ 16 ] = {
+ 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
+
+ const sal_uInt8 aGuidFileMoniker[ 16 ] = {
+ 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
+ const sal_uInt8 aGuidFileTail[] = {
+ 0xFF, 0xFF, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ //const sal_uInt18 WW8_ID_HLINK = 0x01B8;
+ constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL.
+ constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path.
+ //const sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description.
+ constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark.
+ constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame.
+ //const sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path.
+ SvMemoryStream tmpStrm;
+ OUString tmpTextMark;
+
+ OUString rUrl = pINetFormatArg->GetURL();
+ OUString rTarFrame = pINetFormatArg->GetTargetFrameName();
+ sal_uInt32 nFlags = 0;
+
+ INetURLObject aUrlObj( rUrl );
+ const INetProtocol eProtocol = aUrlObj.GetProtocol();
+
+ //Target Frame
+ if (!rTarFrame.isEmpty())
+ {
+ SwWW8Writer::WriteLong(tmpStrm, rTarFrame.getLength()+1);
+ SwWW8Writer::WriteString16(tmpStrm, rTarFrame, false);
+
+ tmpStrm.WriteUInt16( 0 );
+
+ nFlags |= WW8_HLINK_FRAME;
+ }
+
+ // file link or URL
+ if (eProtocol == INetProtocol::File || (eProtocol == INetProtocol::NotValid && rUrl[0] != '#'))
+ {
+ sal_uInt16 nLevel;
+ bool bRel;
+ OUString aFileName( BuildFileName( nLevel, bRel, rUrl ));
+
+ if( !bRel )
+ nFlags |= WW8_HLINK_ABS;
+
+ nFlags |= WW8_HLINK_BODY;
+
+ tmpStrm.WriteBytes(aGuidFileMoniker, sizeof(aGuidFileMoniker));
+ tmpStrm.WriteUInt16( nLevel );
+ SwWW8Writer::WriteLong(tmpStrm, aFileName.getLength()+1);
+ SwWW8Writer::WriteString8( tmpStrm, aFileName, true, RTL_TEXTENCODING_MS_1252 );
+ tmpStrm.WriteBytes(aGuidFileTail, sizeof(aGuidFileTail));
+
+ //For UNICODE
+ SwWW8Writer::WriteLong(tmpStrm, 2*aFileName.getLength()+6);
+ SwWW8Writer::WriteLong(tmpStrm, 2*aFileName.getLength());
+ tmpStrm.WriteUInt16( 0x0003 );
+ SwWW8Writer::WriteString16(tmpStrm, aFileName, false);
+ }
+ else if( eProtocol != INetProtocol::NotValid )
+ {
+ tmpStrm.WriteBytes(aGuidUrlMoniker, sizeof(aGuidUrlMoniker));
+ SwWW8Writer::WriteLong(tmpStrm, 2*(rUrl.getLength()+1));
+
+ SwWW8Writer::WriteString16(tmpStrm, rUrl, true);
+ nFlags |= WW8_HLINK_BODY | WW8_HLINK_ABS;
+ }
+ else if (rUrl[0] == '#' )
+ {
+ OUString aTextMark(rUrl.copy( 1 ));
+ aTextMark = aTextMark.replaceFirst(".", "!");
+ tmpTextMark = aTextMark;
+ }
+
+ if (tmpTextMark.isEmpty() && aUrlObj.HasMark())
+ {
+ tmpTextMark = aUrlObj.GetMark();
+ }
+
+ if (!tmpTextMark.isEmpty())
+ {
+ SwWW8Writer::WriteLong(tmpStrm, tmpTextMark.getLength()+1);
+ SwWW8Writer::WriteString16(tmpStrm, tmpTextMark, true);
+
+ nFlags |= WW8_HLINK_MARK;
+ }
+
+ rStrm.WriteBytes(aGuidStdLink, 16);
+ rStrm .WriteUInt32( 2 )
+ .WriteUInt32( nFlags );
+ tmpStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ sal_uInt32 const nLen = tmpStrm.remainingSize();
+ if(nLen >0)
+ {
+ std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[ nLen ] );
+ tmpStrm.ReadBytes(pBuffer.get(), nLen);
+ rStrm.WriteBytes(pBuffer.get(), nLen);
+ }
+}
+void SwBasicEscherEx::PreWriteHyperlinkWithinFly(const SwFrameFormat& rFormat,EscherPropertyContainer& rPropOpt)
+{
+ const SwAttrSet& rAttrSet = rFormat.GetAttrSet();
+ const SwFormatURL* pINetFormat = rAttrSet.GetItemIfSet(RES_URL);
+ if (!pINetFormat || pINetFormat->GetURL().isEmpty())
+ return;
+
+ SvMemoryStream aStrm;
+ WriteHyperlinkWithinFly( aStrm, pINetFormat );
+ rPropOpt.AddOpt(ESCHER_Prop_pihlShape, true, 0, aStrm);
+ sal_uInt32 nValue;
+ OUString aNamestr = pINetFormat->GetName();
+ if (!aNamestr.isEmpty())
+ {
+ rPropOpt.AddOpt(ESCHER_Prop_wzName, aNamestr );
+ }
+ if(rPropOpt.GetOpt( ESCHER_Prop_fPrint, nValue))
+ {
+ nValue|=0x03080008;
+ rPropOpt.AddOpt(ESCHER_Prop_fPrint, nValue );
+ }
+ else
+ rPropOpt.AddOpt(ESCHER_Prop_fPrint, 0x03080008 );
+}
+
+namespace
+{
+ /// Get the Z ordering number for a DrawObj in a WW8Export.
+ /// @param rWrt The containing WW8Export.
+ /// @param pObj pointer to the drawing object.
+ /// @returns The ordering number.
+ sal_uLong lcl_getSdrOrderNumber(const WW8Export& rWrt, DrawObj const *pObj)
+ {
+ return rWrt.GetSdrOrdNum(pObj->maContent.GetFrameFormat());
+ };
+
+ /// A function object to act as a predicate comparing the ordering numbers
+ /// of two drawing objects in a WW8Export.
+ class CompareDrawObjs
+ {
+ private:
+ const WW8Export& m_rWrt;
+
+ public:
+ explicit CompareDrawObjs(const WW8Export& rWrt) : m_rWrt(rWrt) {};
+ bool operator()(DrawObj const *a, DrawObj const *b) const
+ {
+ sal_uLong aSort = lcl_getSdrOrderNumber(m_rWrt, a);
+ sal_uLong bSort = lcl_getSdrOrderNumber(m_rWrt, b);
+ return aSort < bSort;
+ }
+ };
+
+ /// Make a z-order sorted copy of a collection of DrawObj objects.
+ /// @param rWrt The containing WW8Export.
+ /// @param rSrcArr The source array.
+ /// @param rDstArr The destination array.
+ void lcl_makeZOrderArray(const WW8Export& rWrt,
+ std::vector<DrawObj> &rSrcArr,
+ std::vector<DrawObj*> &rDstArr)
+ {
+ rDstArr.clear();
+ rDstArr.reserve(rSrcArr.size());
+ for(DrawObj & i : rSrcArr)
+ {
+ rDstArr.push_back( &i );
+ }
+ std::sort(rDstArr.begin(), rDstArr.end(), CompareDrawObjs(rWrt));
+ }
+
+}
+
+// get a part fix for this type of element
+bool WW8Export::MiserableFormFieldExportHack(const SwFrameFormat& rFrameFormat)
+{
+ const SdrObject *pObject = rFrameFormat.FindRealSdrObject();
+ if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm)
+ return false;
+
+ const SdrUnoObj *pFormObj = dynamic_cast< const SdrUnoObj* >(pObject);
+ if (!pFormObj)
+ return false;
+
+ uno::Reference< awt::XControlModel > xControlModel =
+ pFormObj->GetUnoControlModel();
+ uno::Reference< lang::XServiceInfo > xInfo(xControlModel,
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
+ if (!xInfo.is())
+ return false;
+
+ if (xInfo->supportsService("com.sun.star.form.component.ComboBox"))
+ {
+ DoComboBox(xPropSet);
+ return true;
+ }
+
+ return false;
+}
+
+void WW8Export::DoComboBox(uno::Reference<beans::XPropertySet> const & xPropSet)
+{
+ OUString sSelected;
+ uno::Sequence<OUString> aListItems;
+ xPropSet->getPropertyValue("StringItemList") >>= aListItems;
+ if (aListItems.hasElements())
+ {
+ uno::Any aTmp = xPropSet->getPropertyValue("DefaultText");
+ auto pStr = o3tl::tryAccess<OUString>(aTmp);
+ if (pStr)
+ sSelected = *pStr;
+ }
+
+ OUString sName;
+ {
+ uno::Any aTmp = xPropSet->getPropertyValue("Name");
+ auto pStr = o3tl::tryAccess<OUString>(aTmp);
+ if (pStr)
+ sName = *pStr;
+ }
+
+ OUString sHelp;
+ {
+ // property "Help" does not exist and due to the no-existence an exception is thrown.
+ try
+ {
+ uno::Any aTmp = xPropSet->getPropertyValue("HelpText");
+ auto pStr = o3tl::tryAccess<OUString>(aTmp);
+ if (pStr)
+ sHelp = *pStr;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+
+ OUString sToolTip;
+ {
+ uno::Any aTmp = xPropSet->getPropertyValue("Name");
+ auto pStr = o3tl::tryAccess<OUString>(aTmp);
+ if (pStr)
+ sToolTip = *pStr;
+ }
+
+ DoComboBox(sName, sHelp, sToolTip, sSelected, aListItems);
+}
+
+void WW8Export::DoComboBox(const OUString &rName,
+ const OUString &rHelp,
+ const OUString &rToolTip,
+ const OUString &rSelected,
+ const uno::Sequence<OUString> &rListItems)
+{
+ OutputField(nullptr, ww::eFORMDROPDOWN, FieldString(ww::eFORMDROPDOWN),
+ FieldFlags::Start | FieldFlags::CmdStart);
+ // write the reference to the "picture" structure
+ sal_uInt64 nDataStt = m_pDataStrm->Tell();
+ m_pChpPlc->AppendFkpEntry( Strm().Tell() );
+
+ WriteChar( 0x01 );
+
+ static sal_uInt8 aArr1[] =
+ {
+ 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation
+ 0x06, 0x08, 0x01, // sprmCFData
+ 0x55, 0x08, 0x01, // sprmCFSpec
+ 0x02, 0x08, 0x01 // sprmCFFieldVanish
+ };
+ sal_uInt8* pDataAdr = aArr1 + 2;
+ Set_UInt32( pDataAdr, nDataStt );
+
+ m_pChpPlc->AppendFkpEntry(Strm().Tell(), sizeof(aArr1), aArr1);
+
+ OutputField(nullptr, ww::eFORMDROPDOWN, FieldString(ww::eFORMDROPDOWN),
+ FieldFlags::Close);
+
+ ::sw::WW8FFData aFFData;
+
+ aFFData.setType(2);
+ aFFData.setName(rName);
+ aFFData.setHelp(rHelp);
+ aFFData.setStatus(rToolTip);
+
+ sal_uInt32 nListItems = rListItems.getLength();
+
+ for (sal_uInt32 i = 0; i < nListItems; i++)
+ {
+ if (i < 0x20 && rSelected == rListItems[i])
+ aFFData.setResult(::sal::static_int_cast<sal_uInt8>(i));
+ aFFData.addListboxEntry(rListItems[i]);
+ }
+
+ aFFData.Write(m_pDataStrm);
+}
+
+void WW8Export::DoFormText(const SwInputField * pField)
+{
+ OutputField(nullptr, ww::eFORMTEXT, FieldString(ww::eFORMTEXT),
+ FieldFlags::Start | FieldFlags::CmdStart);
+ // write the reference to the "picture" structure
+ sal_uInt64 nDataStt = m_pDataStrm->Tell();
+ m_pChpPlc->AppendFkpEntry( Strm().Tell() );
+
+ WriteChar( 0x01 );
+ static sal_uInt8 aArr1[] = {
+ 0x02, 0x08, 0x81, // sprmCFFieldVanish
+ 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation
+
+ 0x06, 0x08, 0x01, // sprmCFData
+ 0x55, 0x08, 0x01 // sprmCFSpec
+ };
+ sal_uInt8* pDataAdr = aArr1 + 5;
+ Set_UInt32( pDataAdr, nDataStt );
+
+ m_pChpPlc->AppendFkpEntry(Strm().Tell(),
+ sizeof( aArr1 ), aArr1 );
+
+ ::sw::WW8FFData aFFData;
+
+ aFFData.setType(0);
+ aFFData.setName(pField->GetPar2());
+ aFFData.setHelp(pField->GetHelp());
+ aFFData.setStatus(pField->GetToolTip());
+ aFFData.Write(m_pDataStrm);
+
+ OutputField(nullptr, ww::eFORMTEXT, OUString(), FieldFlags::CmdEnd);
+
+ const OUString fieldStr( pField->ExpandField(true, nullptr) );
+ SwWW8Writer::WriteString16(Strm(), fieldStr, false);
+
+ static sal_uInt8 aArr2[] = {
+ 0x55, 0x08, 0x01, // sprmCFSpec
+ 0x75, 0x08, 0x01 // ???
+ };
+
+ pDataAdr = aArr2 + 2;
+ Set_UInt32( pDataAdr, nDataStt );
+ m_pChpPlc->AppendFkpEntry(Strm().Tell(),
+ sizeof( aArr2 ), aArr2 );
+
+ OutputField(nullptr, ww::eFORMTEXT, OUString(), FieldFlags::Close);
+}
+
+PlcDrawObj::~PlcDrawObj()
+{
+}
+
+//It's irritating to have to change the RTL frames position into LTR ones
+//so that word will have to place them in the right place. Doubly so that
+//the SO drawings and writer frames have different ideas themselves as to
+//how to be positioned when in RTL mode!
+bool RTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth,
+sal_Int16 eHoriOri, sal_Int16 eHoriRel, SwTwips nPageLeft,
+ SwTwips nPageRight, SwTwips nPageSize)
+{
+ bool bRet = false;
+ if (eHoriOri == text::HoriOrientation::NONE)
+ {
+ if (eHoriRel == text::RelOrientation::PAGE_FRAME)
+ {
+ rLeft = nPageSize - rLeft;
+ bRet = true;
+ }
+ else if (
+ (eHoriRel == text::RelOrientation::PAGE_PRINT_AREA) ||
+ (eHoriRel == text::RelOrientation::FRAME) ||
+ (eHoriRel == text::RelOrientation::PRINT_AREA)
+ )
+ {
+ rLeft = nPageSize - nPageLeft - nPageRight - rLeft;
+ bRet = true;
+ }
+ }
+ if (bRet)
+ rLeft -= nWidth;
+ return bRet;
+}
+
+static bool RTLDrawingsHack(SwTwips &rLeft,
+ sal_Int16 eHoriOri, sal_Int16 eHoriRel, SwTwips nPageLeft,
+ SwTwips nPageRight, SwTwips nPageSize)
+{
+ bool bRet = false;
+ if (eHoriOri == text::HoriOrientation::NONE)
+ {
+ if (eHoriRel == text::RelOrientation::PAGE_FRAME)
+ {
+ rLeft = nPageSize + rLeft;
+ bRet = true;
+ }
+ else if (
+ (eHoriRel == text::RelOrientation::PAGE_PRINT_AREA) ||
+ (eHoriRel == text::RelOrientation::FRAME) ||
+ (eHoriRel == text::RelOrientation::PRINT_AREA)
+ )
+ {
+ rLeft = nPageSize - nPageLeft - nPageRight + rLeft;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void WW8Export::MiserableRTLFrameFormatHack(SwTwips &rLeft, SwTwips &rRight,
+ const ww8::Frame &rFrameFormat)
+{
+ //Require nasty bidi swap
+ if (SvxFrameDirection::Horizontal_RL_TB != m_rDoc.GetTextDirection(rFrameFormat.GetPosition()))
+ return;
+
+ SwTwips nWidth = rRight - rLeft;
+ SwTwips nPageLeft, nPageRight;
+ SwTwips nPageSize = CurrentPageWidth(nPageLeft, nPageRight);
+
+ const SwFormatHoriOrient& rHOr = rFrameFormat.GetFrameFormat().GetHoriOrient();
+
+ bool bRet = false;
+ ww8::Frame::WriterSource eSource = rFrameFormat.GetWriterType();
+ if (eSource == ww8::Frame::eDrawing || eSource == ww8::Frame::eFormControl)
+ {
+ if (RTLDrawingsHack(rLeft, rHOr.GetHoriOrient(),
+ rHOr.GetRelationOrient(), nPageLeft, nPageRight, nPageSize))
+ {
+ bRet = true;
+ }
+ }
+ else
+ {
+ if (RTLGraphicsHack(rLeft, nWidth, rHOr.GetHoriOrient(),
+ rHOr.GetRelationOrient(), nPageLeft, nPageRight, nPageSize))
+ {
+ bRet = true;
+ }
+ }
+ if (bRet)
+ rRight = rLeft + nWidth;
+}
+
+void PlcDrawObj::WritePlc( WW8Export& rWrt ) const
+{
+ if (8 > rWrt.m_pFib->m_nVersion) // Cannot export drawobject in vers 7-
+ return;
+
+ sal_uInt32 nFcStart = rWrt.m_pTableStrm->Tell();
+
+ if (maDrawObjs.empty())
+ return;
+
+ // write CPs
+ WW8Fib& rFib = *rWrt.m_pFib;
+ WW8_CP nCpOffs = GetCpOffset(rFib);
+
+ for (const auto& rDrawObj : maDrawObjs)
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, rDrawObj.mnCp - nCpOffs);
+
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, rFib.m_ccpText + rFib.m_ccpFootnote +
+ rFib.m_ccpHdr + rFib.m_ccpEdn + rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1);
+
+ for (const auto& rDrawObj : maDrawObjs)
+ {
+ // write the fspa-struct
+ const ww8::Frame &rFrameFormat = rDrawObj.maContent;
+ const SwFrameFormat &rFormat = rFrameFormat.GetFrameFormat();
+ const SdrObject* pObj = rFormat.FindRealSdrObject();
+
+ tools::Rectangle aRect;
+ SwFormatVertOrient rVOr = rFormat.GetVertOrient();
+ SwFormatHoriOrient rHOr = rFormat.GetHoriOrient();
+ // #i30669# - convert the positioning attributes.
+ // Most positions are converted, if layout information exists.
+ const bool bPosConverted =
+ WinwordAnchoring::ConvertPosition( rHOr, rVOr, rFormat );
+
+ Point aObjPos;
+ bool bHasHeightWidthSwapped(false);
+ if (RES_FLYFRMFMT == rFormat.Which())
+ {
+ SwRect aLayRect(rFormat.FindLayoutRect(false, &aObjPos));
+ // the Object is not visible - so get the values from
+ // the format. The Position may not be correct.
+ if( aLayRect.IsEmpty() )
+ aRect.SetSize( rFormat.GetFrameSize().GetSize() );
+ else
+ {
+ // #i56090# Do not only consider the first client
+ // Note that we actually would have to find the maximum size of the
+ // frame format clients. However, this already should work in most cases.
+ const SwRect aSizeRect(rFormat.FindLayoutRect());
+ if ( aSizeRect.Width() > aLayRect.Width() )
+ aLayRect.Width( aSizeRect.Width() );
+
+ aRect = aLayRect.SVRect();
+ }
+ }
+ else
+ {
+ OSL_ENSURE(pObj, "Where is the SDR-Object?");
+ if (pObj)
+ {
+ aRect = pObj->GetLogicRect();
+
+ // rotating to vertical means swapping height and width as seen in SvxMSDffManager::ImportShape
+ const Degree100 nAngle = NormAngle36000( pObj->GetRotateAngle() );
+ const bool bAllowSwap = pObj->GetObjIdentifier() != SdrObjKind::Line && pObj->GetObjIdentifier() != SdrObjKind::Group;
+ if ( bAllowSwap && (( nAngle > 4500_deg100 && nAngle <= 13500_deg100 ) || ( nAngle > 22500_deg100 && nAngle <= 31500_deg100 )) )
+ {
+ const tools::Long nWidth = aRect.getOpenWidth();
+ const tools::Long nHeight = aRect.getOpenHeight();
+ aRect.setWidth( nHeight );
+ aRect.setHeight( nWidth );
+ bHasHeightWidthSwapped = true;
+ }
+ }
+ }
+
+ // #i30669# - use converted position, if conversion is performed.
+ // Unify position determination of Writer fly frames
+ // and drawing objects.
+ if ( bPosConverted )
+ {
+ aRect.SetPos( Point( rHOr.GetPos(), rVOr.GetPos() ) );
+ }
+ else
+ {
+ aRect -= rDrawObj.maParentPos;
+ aObjPos = aRect.TopLeft();
+ if (text::VertOrientation::NONE == rVOr.GetVertOrient())
+ {
+ // #i22673#
+ sal_Int16 eOri = rVOr.GetRelationOrient();
+ if (eOri == text::RelOrientation::CHAR || eOri == text::RelOrientation::TEXT_LINE)
+ aObjPos.setY( -rVOr.GetPos() );
+ else
+ aObjPos.setY( rVOr.GetPos() );
+ }
+ if (text::HoriOrientation::NONE == rHOr.GetHoriOrient())
+ aObjPos.setX( rHOr.GetPos() );
+ aRect.SetPos( aObjPos );
+ }
+
+ sal_Int32 nThick = rDrawObj.mnThick;
+
+ //If we are being exported as an inline hack, set
+ //corner to 0 and forget about border thickness for positioning
+ if (rFrameFormat.IsInline())
+ {
+ aRect.SetPos(Point(0,0));
+ nThick = 0;
+ }
+
+ // spid
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, rDrawObj.mnShapeId);
+
+ SwTwips nLeft = aRect.Left() + nThick;
+ SwTwips nRight = aRect.Right() - nThick;
+ SwTwips nTop = aRect.Top() + nThick;
+ SwTwips nBottom = aRect.Bottom() - nThick;
+
+ // tdf#93675, 0 below line/paragraph and/or top line/paragraph with
+ // wrap top+bottom or other wraps is affecting the line directly
+ // above the anchor line, which seems odd, but a tiny adjustment
+ // here to bring the top down convinces msoffice to wrap like us
+ if (nTop == 0 && !rFrameFormat.IsInline() &&
+ rVOr.GetVertOrient() == text::VertOrientation::NONE &&
+ rVOr.GetRelationOrient() == text::RelOrientation::FRAME)
+ {
+ nTop = 8;
+ }
+
+ //Nasty swap for bidi if necessary
+ rWrt.MiserableRTLFrameFormatHack(nLeft, nRight, rFrameFormat);
+
+ // tdf#70838. Word relates the position to the unrotated rectangle,
+ // Writer to the rotated one. Because the rotation is around center,
+ // the difference counts half.
+ if(pObj && pObj->GetRotateAngle())
+ {
+ SwTwips nXOff;
+ SwTwips nYOff;
+ SwTwips nSnapWidth = pObj->GetSnapRect().getOpenWidth();
+ SwTwips nSnapHeight = pObj->GetSnapRect().getOpenHeight();
+ SwTwips nLogicWidth = pObj->GetLogicRect().getOpenWidth();
+ SwTwips nLogicHeight = pObj->GetLogicRect().getOpenHeight();
+ // +1 for to compensate integer arithmetic rounding errors
+ if(bHasHeightWidthSwapped)
+ {
+ nXOff = (nSnapWidth - nLogicHeight + 1) / 2;
+ nYOff = (nSnapHeight - nLogicWidth + 1) / 2;
+ }
+ else
+ {
+ nXOff = (nSnapWidth - nLogicWidth + 1) / 2;
+ nYOff = (nSnapHeight - nLogicHeight + 1) / 2;
+ }
+ nLeft += nXOff;
+ nRight += nXOff;
+ nTop += nYOff;
+ nBottom += nYOff;
+ }
+
+ //xaLeft/yaTop/xaRight/yaBottom - rel. to anchor
+ //(most of) the border is outside the graphic is word, so
+ //change dimensions to fit
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, nLeft);
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, nTop);
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, nRight);
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, nBottom);
+
+ //fHdr/bx/by/wr/wrk/fRcaSimple/fBelowText/fAnchorLock
+ sal_uInt16 nFlags=0;
+ //If nFlags isn't 0x14 its overridden by the escher properties
+ if (RndStdIds::FLY_AT_PAGE == rFormat.GetAnchor().GetAnchorId())
+ nFlags = 0x0000;
+ else
+ nFlags = 0x0014; // x-rel to text, y-rel to text
+
+ const SwFormatSurround& rSurr = rFormat.GetSurround();
+ sal_uInt16 nContour = rSurr.IsContour() ? 0x0080 : 0x0040;
+ css::text::WrapTextMode eSurround = rSurr.GetSurround();
+
+ /*
+ #i3958#
+ The inline elements being export as anchored to character inside
+ the shape field hack are required to be wrap through so as to flow
+ over the following dummy 0x01 graphic
+ */
+ if (rFrameFormat.IsInline())
+ eSurround = css::text::WrapTextMode_THROUGH;
+
+ switch (eSurround)
+ {
+ case css::text::WrapTextMode_NONE:
+ nFlags |= 0x0020;
+ break;
+ case css::text::WrapTextMode_THROUGH:
+ nFlags |= 0x0060;
+ break;
+ case css::text::WrapTextMode_PARALLEL:
+ nFlags |= 0x0000 | nContour;
+ break;
+ case css::text::WrapTextMode_DYNAMIC:
+ nFlags |= 0x0600 | nContour;
+ break;
+ case css::text::WrapTextMode_LEFT:
+ nFlags |= 0x0200 | nContour;
+ break;
+ case css::text::WrapTextMode_RIGHT:
+ nFlags |= 0x0400 | nContour;
+ break;
+ default:
+ OSL_ENSURE(false, "Unsupported surround type for export");
+ break;
+ }
+ if (pObj && (pObj->GetLayer() == rWrt.m_rDoc.getIDocumentDrawModelAccess().GetHellId() ||
+ pObj->GetLayer() == rWrt.m_rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()))
+ {
+ nFlags |= 0x4000;
+ }
+
+ /*
+ #i3958# Required to make this inline stuff work in WordXP, not
+ needed for 2003 interestingly
+ */
+ if (rFrameFormat.IsInline())
+ nFlags |= 0x8000;
+
+ SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, nFlags);
+
+ // cTxbx
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, 0);
+ }
+
+ RegisterWithFib(rFib, nFcStart, rWrt.m_pTableStrm->Tell() - nFcStart);
+}
+
+void MainTextPlcDrawObj::RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart,
+ sal_uInt32 nLen) const
+{
+ rFib.m_fcPlcfspaMom = nStart;
+ rFib.m_lcbPlcfspaMom = nLen;
+}
+
+WW8_CP MainTextPlcDrawObj::GetCpOffset(const WW8Fib &) const
+{
+ return 0;
+}
+
+void HdFtPlcDrawObj::RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart,
+ sal_uInt32 nLen) const
+{
+ rFib.m_fcPlcfspaHdr = nStart;
+ rFib.m_lcbPlcfspaHdr = nLen;
+}
+
+WW8_CP HdFtPlcDrawObj::GetCpOffset(const WW8Fib &rFib) const
+{
+ return rFib.m_ccpText + rFib.m_ccpFootnote;
+}
+
+bool PlcDrawObj::Append( WW8Export const & rWrt, WW8_CP nCp, const ww8::Frame& rFormat,
+ const Point& rNdTopLeft )
+{
+ bool bRet = false;
+ const SwFrameFormat &rFrameFormat = rFormat.GetFrameFormat();
+ if (TXT_HDFT == rWrt.m_nTextTyp || TXT_MAINTEXT == rWrt.m_nTextTyp)
+ {
+ if (RES_FLYFRMFMT == rFrameFormat.Which())
+ {
+ // check for textflyframe and if it is the first in a Chain
+ if (rFrameFormat.GetContent().GetContentIdx())
+ bRet = true;
+ }
+ else
+ bRet = true;
+ }
+
+ if (bRet)
+ {
+ DrawObj aObj(rFormat, nCp, rNdTopLeft, rWrt.TrueFrameDirection(rFrameFormat),
+ rWrt.GetHdFtIndex());
+ maDrawObjs.push_back(aObj);
+ }
+ return bRet;
+}
+
+void DrawObj::SetShapeDetails(sal_uInt32 nId, sal_Int32 nThick)
+{
+ mnShapeId = nId;
+ mnThick = nThick;
+}
+
+bool WW8_WrPlcTextBoxes::WriteText( WW8Export& rWrt )
+{
+ rWrt.m_bInWriteEscher = true;
+ WW8_CP& rccp=TXT_TXTBOX == m_nTyp ? rWrt.m_pFib->m_ccpTxbx : rWrt.m_pFib->m_ccpHdrTxbx;
+
+ bool bRet = WriteGenericText( rWrt, m_nTyp, rccp );
+
+ WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ WW8Fib& rFib = *rWrt.m_pFib;
+ WW8_CP nMyOffset = rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpAtn
+ + rFib.m_ccpEdn;
+ if( TXT_TXTBOX == m_nTyp )
+ rWrt.m_pFieldTextBxs->Finish( nCP, nMyOffset );
+ else
+ rWrt.m_pFieldHFTextBxs->Finish( nCP, nMyOffset + rFib.m_ccpTxbx );
+ rWrt.m_bInWriteEscher = false;
+ return bRet;
+}
+
+void WW8_WrPlcTextBoxes::Append( const SdrObject& rObj, sal_uInt32 nShapeId )
+{
+ m_aContent.push_back( &rObj );
+ m_aShapeIds.push_back( nShapeId );
+ //save NULL, if we have an actual SdrObject
+ m_aSpareFormats.push_back(nullptr);
+}
+
+void WW8_WrPlcTextBoxes::Append( const SwFrameFormat* pFormat, sal_uInt32 nShapeId )
+{
+ //no sdr object, we insert a NULL in the aContent and save the real fmt in aSpareFormats.
+ m_aContent.push_back( nullptr );
+ m_aShapeIds.push_back( nShapeId );
+ m_aSpareFormats.push_back(pFormat);
+}
+
+const std::vector<sal_uInt32>* WW8_WrPlcTextBoxes::GetShapeIdArr() const
+{
+ return &m_aShapeIds;
+}
+
+sal_uInt32 WW8Export::GetSdrOrdNum( const SwFrameFormat& rFormat ) const
+{
+ sal_uInt32 nOrdNum;
+ const SdrObject* pObj = rFormat.FindRealSdrObject();
+ if( pObj )
+ nOrdNum = pObj->GetOrdNum();
+ else
+ {
+ // no Layout for this format, then recalc the ordnum
+ SwFrameFormat* pFormat = const_cast<SwFrameFormat*>(&rFormat);
+ nOrdNum = std::distance(m_rDoc.GetSpzFrameFormats()->begin(),
+ m_rDoc.GetSpzFrameFormats()->find(static_cast<sw::SpzFrameFormat*>(pFormat)));
+
+ const SwDrawModel* pModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ if( pModel )
+ nOrdNum += pModel->GetPage( 0 )->GetObjCount();
+ }
+ return nOrdNum;
+}
+
+void WW8Export::AppendFlyInFlys(const ww8::Frame& rFrameFormat,
+ const Point& rNdTopLeft)
+{
+ OSL_ENSURE(!m_pEscher, "the EscherStream was already written!");
+ if (m_pEscher)
+ return ;
+ PlcDrawObj *pDrwO;
+ if (TXT_HDFT == m_nTextTyp)
+ pDrwO = m_pHFSdrObjs.get();
+ else
+ pDrwO = m_pSdrObjs.get();
+
+ if (rFrameFormat.IsInline())
+ {
+ OutputField(nullptr, ww::eSHAPE, FieldString(ww::eSHAPE),
+ FieldFlags::Start | FieldFlags::CmdStart | FieldFlags::CmdEnd);
+ }
+
+ WW8_CP nCP = Fc2Cp(Strm().Tell());
+ bool bSuccess = pDrwO->Append(*this, nCP, rFrameFormat, rNdTopLeft);
+ OSL_ENSURE(bSuccess, "Couldn't export a graphical element!");
+
+ if (bSuccess)
+ {
+ static const sal_uInt8 aSpec8[] =
+ {
+ 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation
+ 0x55, 0x08, 1 // sprmCFSpec
+ };
+ // fSpec-Attribute true
+
+ // A special character is required in the text for DrawObjects,
+ // therefore a fSpec-Attribute
+ m_pChpPlc->AppendFkpEntry( Strm().Tell() );
+ WriteChar( 0x8 );
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aSpec8 ), aSpec8 );
+
+ //Need dummy picture frame
+ if (rFrameFormat.IsInline())
+ OutGrf(rFrameFormat);
+ }
+
+ if (rFrameFormat.IsInline())
+ OutputField(nullptr, ww::eSHAPE, OUString(), FieldFlags::Close);
+}
+
+MSWord_SdrAttrIter::MSWord_SdrAttrIter( MSWordExportBase& rWr,
+ const EditTextObject& rEditObj, sal_uInt8 nTyp )
+ : MSWordAttrIter( rWr ), m_pEditObj(&rEditObj), m_pEditPool(nullptr), mnTyp(nTyp)
+{
+ NextPara( 0 );
+}
+
+void MSWord_SdrAttrIter::NextPara( sal_Int32 nPar )
+{
+ m_nPara = nPar;
+ // Ignore change of attribute at position 0, because we expect that
+ // the attributes are outputted at start of a paragraph anyway.
+ m_aChrTextAtrArr.clear();
+ m_aChrSetArr.clear();
+ m_nCurrentSwPos = m_nTmpSwPos = 0;
+
+ SfxItemSet aSet( m_pEditObj->GetParaAttribs( m_nPara ));
+ m_pEditPool = aSet.GetPool();
+ m_eNdChrSet = aSet.Get(EE_CHAR_FONTINFO).GetCharSet();
+
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ m_nScript = g_pBreakIt->GetBreakIter()->getScriptType( m_pEditObj->GetText(m_nPara), 0);
+
+ m_pEditObj->GetCharAttribs( m_nPara, m_aTextAtrArr );
+ m_nCurrentSwPos = SearchNext( 1 );
+}
+
+rtl_TextEncoding MSWord_SdrAttrIter::GetNextCharSet() const
+{
+ if( !m_aChrSetArr.empty() )
+ return m_aChrSetArr.back();
+ return m_eNdChrSet;
+}
+
+// the first parameter in SearchNext() returns if it's a TextAtr
+sal_Int32 MSWord_SdrAttrIter::SearchNext( sal_Int32 nStartPos )
+{
+ sal_Int32 nMinPos = SAL_MAX_INT32;
+ for(const auto& rTextAtr : m_aTextAtrArr)
+ {
+ sal_Int32 nPos = rTextAtr.nStart; // first character attribute
+ if( nPos >= nStartPos && nPos <= nMinPos )
+ {
+ nMinPos = nPos;
+ SetCharSet(rTextAtr, true);
+ }
+
+ nPos = rTextAtr.nEnd; // last character attribute + 1
+ if( nPos >= nStartPos && nPos < nMinPos )
+ {
+ nMinPos = nPos;
+ SetCharSet(rTextAtr, false);
+ }
+ }
+ return nMinPos;
+}
+
+void MSWord_SdrAttrIter::SetCharSet(const EECharAttrib& rAttr, bool bStart)
+{
+ const SfxPoolItem& rItem = *rAttr.pAttr;
+ if( rItem.Which() != EE_CHAR_FONTINFO )
+ {
+ return;
+ }
+
+ if( bStart )
+ {
+ rtl_TextEncoding eChrSet = static_cast<const SvxFontItem&>(rItem).GetCharSet();
+ m_aChrSetArr.push_back( eChrSet );
+ m_aChrTextAtrArr.push_back( &rAttr );
+ }
+ else
+ {
+ std::vector<const EECharAttrib*>::iterator it =
+ std::find( m_aChrTextAtrArr.begin(), m_aChrTextAtrArr.end(), &rAttr );
+ if ( it != m_aChrTextAtrArr.end() )
+ {
+ m_aChrSetArr.erase( m_aChrSetArr.begin() + (it - m_aChrTextAtrArr.begin()) );
+ m_aChrTextAtrArr.erase( it );
+ }
+ }
+}
+
+void MSWord_SdrAttrIter::OutEEField(const SfxPoolItem& rHt)
+{
+ const SvxFieldItem &rField = static_cast<const SvxFieldItem &>(rHt);
+ const SvxFieldData *pField = rField.GetField();
+ if (auto pURL = dynamic_cast< const SvxURLField *>( pField ))
+ {
+ sal_uInt8 nOldTextTyp = m_rExport.m_nTextTyp;
+ m_rExport.m_nTextTyp = mnTyp;
+ m_rExport.AttrOutput().StartURL( pURL->GetURL(), pURL->GetTargetFrame() );
+
+ const OUString &rStr = pURL->GetRepresentation();
+ m_rExport.AttrOutput().RawText(rStr, GetNodeCharSet());
+
+ m_rExport.AttrOutput().EndURL(false);
+ m_rExport.m_nTextTyp = nOldTextTyp;
+ }
+}
+
+void MSWord_SdrAttrIter::OutAttr( sal_Int32 nSwPos )
+{
+ //Collect the which ids belong to the run that we will export after
+ //outputting the underlying paragraph attributes. We will exclude
+ //writing these from the underlying paragraph attributes to avoid
+ //duplicate attributes in docx export. Doesn't matter in doc
+ //export as later props just override earlier ones.
+ std::set<sal_uInt16> aUsedRunWhichs;
+ for(const auto& rTextAtr : m_aTextAtrArr)
+ {
+ if (nSwPos >= rTextAtr.nStart && nSwPos < rTextAtr.nEnd)
+ {
+ sal_uInt16 nWhich = rTextAtr.pAttr->Which();
+ aUsedRunWhichs.insert(nWhich);
+ }
+
+ if( nSwPos < rTextAtr.nStart )
+ break;
+ }
+
+ OutParaAttr(true, &aUsedRunWhichs);
+
+ if (m_aTextAtrArr.empty())
+ return;
+
+ const sw::BroadcastingModify* pOldMod = m_rExport.m_pOutFormatNode;
+ m_rExport.m_pOutFormatNode = nullptr;
+
+ const SfxItemPool* pSrcPool = m_pEditPool;
+ const SfxItemPool& rDstPool = m_rExport.m_rDoc.GetAttrPool();
+
+ m_nTmpSwPos = nSwPos;
+ // Did we already produce a <w:sz> element?
+ m_rExport.m_bFontSizeWritten = false;
+ for(const auto& rTextAtr : m_aTextAtrArr)
+ {
+ if (nSwPos >= rTextAtr.nStart && nSwPos < rTextAtr.nEnd)
+ {
+ sal_uInt16 nWhich = rTextAtr.pAttr->Which();
+ if (nWhich == EE_FEATURE_FIELD)
+ {
+ OutEEField(*(rTextAtr.pAttr));
+ continue;
+ }
+ if (nWhich == EE_FEATURE_TAB)
+ {
+ m_rExport.WriteChar(0x9);
+ continue;
+ }
+ if (nWhich == EE_CHAR_BKGCOLOR)
+ {
+ Color aColor(static_cast<const SvxColorItem*>(rTextAtr.pAttr)->GetValue());
+ m_rExport.AttrOutput().OutputItem(SvxBrushItem(aColor, RES_CHRATR_BACKGROUND));
+ continue;
+ }
+
+ const sal_uInt16 nSlotId = pSrcPool->GetSlotId(nWhich);
+ if (nSlotId && nWhich != nSlotId)
+ {
+ nWhich = rDstPool.GetWhich(nSlotId);
+ if (nWhich && nWhich != nSlotId &&
+ nWhich < RES_UNKNOWNATR_BEGIN &&
+ m_rExport.CollapseScriptsforWordOk(m_nScript,nWhich))
+ {
+ // use always the SW-Which Id !
+ std::unique_ptr<SfxPoolItem> pI(rTextAtr.pAttr->Clone());
+ pI->SetWhich( nWhich );
+ // Will this item produce a <w:sz> element?
+ bool bFontSizeItem = nWhich == RES_CHRATR_FONTSIZE || nWhich == RES_CHRATR_CJK_FONTSIZE;
+ if (!m_rExport.m_bFontSizeWritten || !bFontSizeItem)
+ m_rExport.AttrOutput().OutputItem( *pI );
+ if (bFontSizeItem)
+ m_rExport.m_bFontSizeWritten = true;
+ }
+ }
+ }
+
+ if( nSwPos < rTextAtr.nStart )
+ break;
+ }
+ m_rExport.m_bFontSizeWritten = false;
+
+ m_nTmpSwPos = 0; // HasTextItem only allowed in the above area
+ m_rExport.m_pOutFormatNode = pOldMod;
+}
+
+bool MSWord_SdrAttrIter::IsTextAttr(sal_Int32 nSwPos)
+{
+ return std::any_of(m_aTextAtrArr.begin(), m_aTextAtrArr.end(),
+ [nSwPos](const EECharAttrib& rTextAtr) {
+ return (nSwPos >= rTextAtr.nStart && nSwPos < rTextAtr.nEnd) &&
+ (rTextAtr.pAttr->Which() == EE_FEATURE_FIELD ||
+ rTextAtr.pAttr->Which() == EE_FEATURE_TAB);
+ }
+ );
+}
+
+// HasItem is used for the consolidation of the double attribute Underline and
+// WordLineMode as a TextItem. OutAttr() calls the output function, which can
+// query for other items at the start position of attribute via HasItem().
+// Only attributes with an end can be queried.
+// The search is done with bDeep.
+const SfxPoolItem* MSWord_SdrAttrIter::HasTextItem(sal_uInt16 nWhich) const
+{
+ nWhich = sw::hack::TransformWhichBetweenPools(*m_pEditPool,
+ m_rExport.m_rDoc.GetAttrPool(), nWhich);
+ if (nWhich)
+ {
+ for (const auto& rTextAtr : m_aTextAtrArr)
+ {
+ if (nWhich == rTextAtr.pAttr->Which() && m_nTmpSwPos >= rTextAtr.nStart && m_nTmpSwPos < rTextAtr.nEnd)
+ return rTextAtr.pAttr; // Found
+ if (m_nTmpSwPos < rTextAtr.nStart)
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+const SfxPoolItem& MSWord_SdrAttrIter::GetItem( sal_uInt16 nWhich ) const
+{
+ using sw::hack::GetSetWhichFromSwDocWhich;
+ const SfxPoolItem* pRet = HasTextItem(nWhich);
+ if (!pRet)
+ {
+ SfxItemSet aSet(m_pEditObj->GetParaAttribs(m_nPara));
+ nWhich = GetSetWhichFromSwDocWhich(aSet, m_rExport.m_rDoc, nWhich);
+ OSL_ENSURE(nWhich, "Impossible, catastrophic failure imminent");
+ pRet = &aSet.Get(nWhich);
+ }
+ return *pRet;
+}
+
+//Drawing shapes properties inherit from a different pool that the document
+//styles. On export to .doc[x] they will default to style "Normal". Here explicitly
+//set any items which are not already set, but differ from "Normal".
+void MSWord_SdrAttrIter::SetItemsThatDifferFromStandard(bool bCharAttr, SfxItemSet& rSet)
+{
+ SwTextFormatColl* pC = m_rExport.m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool
+ (RES_POOLCOLL_STANDARD, false);
+
+ SfxWhichIter aWhichIter(rSet);
+ for (sal_uInt16 nEEWhich = aWhichIter.FirstWhich(); nEEWhich; nEEWhich = aWhichIter.NextWhich())
+ {
+ if (SfxItemState::SET != aWhichIter.GetItemState(false))
+ {
+ sal_uInt16 nSwWhich = sw::hack::TransformWhichBetweenPools(m_rExport.m_rDoc.GetAttrPool(),
+ *m_pEditPool, nEEWhich);
+ if (!nSwWhich)
+ continue;
+ bool bWanted = ( bCharAttr ? ( nSwWhich >= RES_CHRATR_BEGIN && nSwWhich < RES_TXTATR_END )
+ : ( nSwWhich >= RES_PARATR_BEGIN && nSwWhich < RES_FRMATR_END ) );
+ if (!bWanted)
+ continue;
+
+ const SfxPoolItem& rDrawItem = rSet.Get(nEEWhich);
+ const SfxPoolItem& rStandardItem = pC->GetFormatAttr(nSwWhich);
+ if (rDrawItem != rStandardItem)
+ rSet.Put(rDrawItem);
+ }
+ }
+}
+
+void MSWord_SdrAttrIter::OutParaAttr(bool bCharAttr, const std::set<sal_uInt16>* pWhichsToIgnore)
+{
+ SfxItemSet aSet( m_pEditObj->GetParaAttribs( m_nPara ));
+
+ SetItemsThatDifferFromStandard(bCharAttr, aSet);
+
+ if (!aSet.Count())
+ return;
+
+ const SfxItemSet* pOldSet = m_rExport.GetCurItemSet();
+ m_rExport.SetCurItemSet( &aSet );
+
+ SfxItemIter aIter( aSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+
+ const SfxItemPool* pSrcPool = m_pEditPool,
+ * pDstPool = &m_rExport.m_rDoc.GetAttrPool();
+
+ do
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if (pWhichsToIgnore && pWhichsToIgnore->find(nWhich) != pWhichsToIgnore->end())
+ continue;
+
+ sal_uInt16 nSlotId = pSrcPool->GetSlotId(nWhich);
+
+ if ( nSlotId && nWhich != nSlotId &&
+ 0 != ( nWhich = pDstPool->GetWhich( nSlotId ) ) &&
+ nWhich != nSlotId &&
+ ( bCharAttr ? ( nWhich >= RES_CHRATR_BEGIN && nWhich < RES_TXTATR_END )
+ : ( nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END ) ) )
+ {
+ // use always the SW-Which Id !
+ std::unique_ptr<SfxPoolItem> pI(pItem->Clone());
+ pI->SetWhich( nWhich );
+ if (m_rExport.CollapseScriptsforWordOk(m_nScript,nWhich))
+ m_rExport.AttrOutput().OutputItem(*pI);
+ }
+ } while ((pItem = aIter.NextItem()));
+ m_rExport.SetCurItemSet( pOldSet );
+}
+
+void WW8Export::WriteSdrTextObj(const SdrTextObj& rTextObj, sal_uInt8 nTyp)
+{
+ std::optional<OutlinerParaObject> pParaObj;
+
+ /*
+ #i13885#
+ When the object is actively being edited, that text is not set into
+ the objects normal text object, but lives in a separate object.
+ */
+ if (rTextObj.IsTextEditActive())
+ {
+ pParaObj = rTextObj.CreateEditOutlinerParaObject();
+ }
+ else if (rTextObj.GetOutlinerParaObject())
+ {
+ pParaObj = *rTextObj.GetOutlinerParaObject();
+ }
+
+ if( pParaObj )
+ {
+ WriteOutliner(*pParaObj, nTyp);
+ }
+}
+
+void WW8Export::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp)
+{
+ bool bAnyWrite = false;
+ const EditTextObject& rEditObj = rParaObj.GetTextObject();
+ MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp );
+
+ sal_Int32 nPara = rEditObj.GetParagraphCount();
+ sal_uInt8 bNul = 0;
+ for( sal_Int32 n = 0; n < nPara; ++n )
+ {
+ if( n )
+ aAttrIter.NextPara( n );
+
+ OSL_ENSURE( m_pO->empty(), " pO is not empty at start of line" );
+
+ OUString aStr( rEditObj.GetText( n ));
+ sal_Int32 nCurrentPos = 0;
+ const sal_Int32 nEnd = aStr.getLength();
+
+ const SfxItemSet& aSet(rEditObj.GetParaAttribs(n));
+ bool bIsRTLPara = false;
+ if(const SvxFrameDirectionItem* pItem = aSet.GetItemIfSet(EE_PARA_WRITINGDIR))
+ {
+ SvxFrameDirection nDir = pItem->GetValue();
+ bIsRTLPara = SvxFrameDirection::Horizontal_RL_TB == nDir;
+ }
+
+ do {
+ const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
+
+ bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos );
+ if( !bTextAtr )
+ OutSwString(aStr, nCurrentPos, nNextAttr - nCurrentPos);
+
+ // At the end of the line the attributes are extended over the CR.
+ // exception: foot note at line end
+ if( nNextAttr == nEnd && !bTextAtr )
+ WriteCR(); // CR after it
+
+ // output of character attributes
+ aAttrIter.OutAttr( nCurrentPos ); // nCurrentPos - 1 ??
+
+ if (bIsRTLPara)
+ {
+ // This is necessary to make word order correct in MS Word.
+ // In theory we should do this for complex-script runs only,
+ // but Outliner does not split runs like Writer core did.
+ // Fortunately, both MS Word and Writer seems to tolerate
+ // that we turn it on for non complex-script runs.
+ AttrOutput().OutputItem(SfxInt16Item(RES_CHRATR_BIDIRTL, 1));
+ }
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(),
+ m_pO->size(), m_pO->data() );
+ m_pO->clear();
+
+ // exception: foot note at line end
+ if( nNextAttr == nEnd && bTextAtr )
+ WriteCR(); // CR after it
+ nCurrentPos = nNextAttr;
+ aAttrIter.NextPos();
+ }
+ while( nCurrentPos < nEnd );
+
+ OSL_ENSURE( m_pO->empty(), " pO is not empty at start of line" );
+
+ m_pO->push_back( bNul ); // Style # as short
+ m_pO->push_back( bNul );
+
+ aAttrIter.OutParaAttr(false);
+
+ sal_uInt64 nPos = Strm().Tell();
+ m_pPapPlc->AppendFkpEntry( Strm().Tell(),
+ m_pO->size(), m_pO->data() );
+ m_pO->clear();
+ m_pChpPlc->AppendFkpEntry( nPos );
+ }
+
+ bAnyWrite = 0 != nPara;
+ if( !bAnyWrite )
+ WriteStringAsPara( OUString() );
+}
+
+void WinwordAnchoring::WriteData( EscherEx& rEx ) const
+{
+ //Toplevel groups get their winword extra data attached, and sub elements
+ //use the defaults
+ if (rEx.GetGroupLevel() > 1)
+ return;
+
+ SvStream& rSt = rEx.GetStream();
+ //The last argument denotes the number of sub properties in this atom
+ int nSubProps = mnGroupShapeBooleanProperties ? 1 : 0;
+ if (mbInline)
+ {
+ nSubProps += 3;
+ rEx.AddAtom(6 * nSubProps, DFF_msofbtUDefProp, 3, nSubProps); // Prop id is 0xF122
+ rSt.WriteUInt16( 0x0390 ).WriteUInt32( 3 );
+ rSt.WriteUInt16( 0x0392 ).WriteUInt32( 3 );
+ //This sub property is required to be in the dummy inline frame as
+ //well
+ rSt.WriteUInt16( 0x053F ).WriteUInt32( nInlineHack );
+ }
+ else
+ {
+ nSubProps += 4;
+ rEx.AddAtom(6 * nSubProps, DFF_msofbtUDefProp, 3, nSubProps); // Prop id is 0xF122
+ rSt.WriteUInt16( 0x038F ).WriteUInt32( mnXAlign );
+ rSt.WriteUInt16( 0x0390 ).WriteUInt32( mnXRelTo );
+ rSt.WriteUInt16( 0x0391 ).WriteUInt32( mnYAlign );
+ rSt.WriteUInt16( 0x0392 ).WriteUInt32( mnYRelTo );
+ }
+ if (mnGroupShapeBooleanProperties)
+ rSt.WriteUInt16(0x03BF).WriteUInt32(mnGroupShapeBooleanProperties);
+}
+
+void WW8Export::CreateEscher()
+{
+ SfxItemState eBackSet = m_rDoc.GetPageDesc(0).GetMaster().
+ GetItemState(RES_BACKGROUND);
+ if (m_pHFSdrObjs->size() || m_pSdrObjs->size() || SfxItemState::SET == eBackSet)
+ {
+ OSL_ENSURE( !m_pEscher, "Who did not deleted the pointer?" );
+ SvMemoryStream* pEscherStrm = new SvMemoryStream;
+ pEscherStrm->SetEndian(SvStreamEndian::LITTLE);
+ m_pEscher = new SwEscherEx(pEscherStrm, *this);
+ }
+}
+
+void WW8Export::WriteEscher()
+{
+ if (m_pEscher)
+ {
+ sal_uInt64 nStart = m_pTableStrm->Tell();
+
+ m_pEscher->WritePictures();
+ m_pEscher->FinishEscher();
+
+ m_pFib->m_fcDggInfo = nStart;
+ m_pFib->m_lcbDggInfo = m_pTableStrm->Tell() - nStart;
+ delete m_pEscher;
+ m_pEscher = nullptr;
+ }
+}
+
+void SwEscherEx::WritePictures()
+{
+ if( SvStream* pPicStrm = static_cast< SwEscherExGlobal& >( *mxGlobal ).GetPictureStream() )
+ {
+ // set the blip - entries to the correct stream pos
+ sal_uInt64 nEndPos = mrWrt.Strm().Tell();
+ mxGlobal->SetNewBlipStreamOffset( nEndPos );
+
+ pPicStrm->Seek( 0 );
+ mrWrt.Strm().WriteStream( *pPicStrm );
+ }
+ Flush();
+}
+
+// Output- Routines for Escher Export
+
+SwEscherExGlobal::SwEscherExGlobal()
+{
+}
+
+SwEscherExGlobal::~SwEscherExGlobal()
+{
+}
+
+SvStream* SwEscherExGlobal::ImplQueryPictureStream()
+{
+ // this function will be called exactly once
+ mxPicStrm = std::make_shared<SvMemoryStream>();
+ mxPicStrm->SetEndian(SvStreamEndian::LITTLE);
+ return mxPicStrm.get();
+}
+
+SwBasicEscherEx::SwBasicEscherEx(SvStream* pStrm, WW8Export& rWW8Wrt)
+ : EscherEx( std::make_shared<SwEscherExGlobal>(), pStrm), mrWrt(rWW8Wrt), mpEscherStrm(pStrm)
+{
+ Init();
+}
+
+SwBasicEscherEx::~SwBasicEscherEx()
+{
+}
+
+void SwBasicEscherEx::WriteFrameExtraData(const SwFrameFormat&)
+{
+ AddAtom(4, ESCHER_ClientAnchor);
+ GetStream().WriteUInt32( 0x80000000 );
+}
+
+void SwBasicEscherEx::WriteEmptyFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId)
+{
+ OpenContainer(ESCHER_SpContainer);
+ AddShape(ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, nShapeId);
+ // store anchor attribute
+ WriteFrameExtraData(rFormat);
+
+ AddAtom(6, DFF_msofbtUDefProp, 3, 1); //Prop id is 0xF122
+ GetStream().WriteUInt16( 0x053F ).WriteUInt32( nInlineHack );
+
+ CloseContainer(); // ESCHER_SpContainer
+}
+
+static ShapeFlag AddMirrorFlags(ShapeFlag nFlags, const SwMirrorGrf &rMirror)
+{
+ switch (rMirror.GetValue())
+ {
+ default:
+ case MirrorGraph::Dont:
+ break;
+ case MirrorGraph::Vertical:
+ nFlags |= ShapeFlag::FlipH;
+ break;
+ case MirrorGraph::Horizontal:
+ nFlags |= ShapeFlag::FlipV;
+ break;
+ case MirrorGraph::Both:
+ nFlags |= ShapeFlag::FlipH | ShapeFlag::FlipV;
+ break;
+
+ }
+ return nFlags;
+}
+//For i120928,this function is added to export graphic of bullet
+void SwBasicEscherEx::WriteGrfBullet(const Graphic& rGrf)
+{
+ OpenContainer( ESCHER_SpContainer );
+ AddShape(ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, 0x401);
+ EscherPropertyContainer aPropOpt;
+ GraphicObject aGraphicObject( rGrf );
+ OString aUniqueId = aGraphicObject.GetUniqueID();
+ if ( !aUniqueId.isEmpty() )
+ {
+ sal_uInt32 nBlibId = mxGlobal->GetBlibID( *(mxGlobal->QueryPictureStream()), aGraphicObject );
+ if (nBlibId)
+ aPropOpt.AddOpt(ESCHER_Prop_pib, nBlibId, true);
+ }
+ aPropOpt.AddOpt( ESCHER_Prop_pibFlags, ESCHER_BlipFlagDefault );
+ aPropOpt.AddOpt( ESCHER_Prop_dyTextTop, DrawModelToEmu(0));
+ aPropOpt.AddOpt( ESCHER_Prop_dyTextBottom, DrawModelToEmu(0));
+ aPropOpt.AddOpt( ESCHER_Prop_dxTextLeft, DrawModelToEmu(0));
+ aPropOpt.AddOpt( ESCHER_Prop_dxTextRight, DrawModelToEmu(0));
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 );
+ aPropOpt.AddOpt( ESCHER_Prop_dyTextTop, 0 );
+ aPropOpt.AddOpt( ESCHER_Prop_dyTextBottom, 0 );
+ aPropOpt.AddOpt( ESCHER_Prop_dxTextLeft, 0 );
+ aPropOpt.AddOpt( ESCHER_Prop_dxTextRight, 0 );
+ const Color aTmpColor( COL_WHITE );
+ std::shared_ptr<SvxBrushItem> aBrush(std::make_shared<SvxBrushItem>(aTmpColor, RES_BACKGROUND));
+ const SvxBrushItem* pRet = mrWrt.GetCurrentPageBgBrush();
+ if (pRet && (pRet->GetGraphic() ||( pRet->GetColor() != COL_TRANSPARENT)))
+ aBrush.reset(pRet->Clone());
+ WriteBrushAttr(*aBrush, aPropOpt);
+
+ aPropOpt.AddOpt( ESCHER_Prop_pictureActive, 0 );
+ aPropOpt.Commit( GetStream() );
+ AddAtom(4, ESCHER_ClientAnchor);
+ GetStream().WriteUInt32( 0x80000000 );
+ CloseContainer();
+}
+
+sal_Int32 SwBasicEscherEx::WriteGrfFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId)
+{
+ sal_Int32 nBorderThick=0;
+ SwNoTextNode *pNd = GetNoTextNodeFromSwFrameFormat(rFormat);
+ SwGrfNode *pGrfNd = pNd ? pNd->GetGrfNode() : nullptr;
+ OSL_ENSURE(pGrfNd, "No SwGrfNode ?, suspicious");
+ if (!pGrfNd)
+ return nBorderThick;
+
+ OpenContainer( ESCHER_SpContainer );
+
+ const SwMirrorGrf &rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
+ AddShape(ESCHER_ShpInst_PictureFrame,
+ AddMirrorFlags(ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, rMirror),
+ nShapeId);
+
+ EscherPropertyContainer aPropOpt;
+
+ sal_uInt32 nFlags = ESCHER_BlipFlagDefault;
+
+ if (pGrfNd->IsLinkedFile())
+ {
+ OUString sURL;
+ pGrfNd->GetFileFilterNms( &sURL, nullptr );
+
+ ww::bytes aBuf;
+ SwWW8Writer::InsAsString16( aBuf, sURL );
+ SwWW8Writer::InsUInt16( aBuf, 0 );
+
+ aPropOpt.AddOpt(ESCHER_Prop_pibName, true, aBuf.size(), aBuf);
+ nFlags = ESCHER_BlipFlagLinkToFile | ESCHER_BlipFlagURL |
+ ESCHER_BlipFlagDoNotSave;
+ }
+ else
+ {
+ const Graphic& aGraphic(pGrfNd->GetGrf());
+ GraphicObject aGraphicObject( aGraphic );
+ OString aUniqueId = aGraphicObject.GetUniqueID();
+
+ if (!aUniqueId.isEmpty())
+ {
+ sal_uInt32 nBlibId = mxGlobal->GetBlibID( *QueryPictureStream(), aGraphicObject);
+ if (nBlibId)
+ aPropOpt.AddOpt(ESCHER_Prop_pib, nBlibId, true);
+ }
+ }
+
+ aPropOpt.AddOpt( ESCHER_Prop_pibFlags, nFlags );
+ nBorderThick = WriteFlyFrameAttr(rFormat,mso_sptPictureFrame,aPropOpt);
+ WriteGrfAttr(*pGrfNd, rFormat, aPropOpt);
+
+ aPropOpt.Commit( GetStream() );
+
+ // store anchor attribute
+ WriteFrameExtraData( rFormat );
+
+ CloseContainer(); // ESCHER_SpContainer
+ return nBorderThick;
+}
+
+void SwBasicEscherEx::WriteGrfAttr(const SwNoTextNode& rNd, const SwFrameFormat& rFormat,
+ EscherPropertyContainer& rPropOpt)
+{
+ GraphicDrawMode nMode = GraphicDrawMode::Standard;
+ sal_Int32 nContrast = 0;
+ sal_Int16 nBrightness = 0;
+
+ if (const SfxInt16Item* pItem = rNd.GetSwAttrSet().GetItemIfSet(RES_GRFATR_CONTRAST))
+ {
+ nContrast = pItem->GetValue();
+ }
+
+ if (const SfxInt16Item* pItem = rNd.GetSwAttrSet().GetItemIfSet(RES_GRFATR_LUMINANCE))
+ {
+ nBrightness = pItem->GetValue();
+ }
+
+ if (const SfxEnumItemInterface* pItem = rNd.GetSwAttrSet().GetItemIfSet(RES_GRFATR_DRAWMODE))
+ {
+ nMode = static_cast<GraphicDrawMode>(pItem->GetEnumValue());
+ if (nMode == GraphicDrawMode::Watermark)
+ {
+ /*
+ There is no real watermark mode in word, we must use standard
+ mode and modify our ones by 70% extra brightness and 70% less
+ contrast. This means that unmodified default OOo watermark
+ will turn back into watermark, and modified OOo watermark will
+ change into a close visual representation in standardmode
+ */
+ nBrightness += 70;
+ if (nBrightness > 100)
+ nBrightness = 100;
+ nContrast -= 70;
+ if (nContrast < -100)
+ nContrast = -100;
+ nMode = GraphicDrawMode::Standard;
+ }
+ }
+
+ sal_uInt32 nPictureMode;
+ if (nMode == GraphicDrawMode::Greys)
+ nPictureMode = 0x40004;
+ else if (nMode == GraphicDrawMode::Mono)
+ nPictureMode = 0x60006;
+ else
+ nPictureMode = 0;
+ rPropOpt.AddOpt( ESCHER_Prop_pictureActive, nPictureMode );
+
+ if (nContrast != 0)
+ {
+ nContrast+=100;
+ if (nContrast == 100)
+ nContrast = 0x10000;
+ else if (nContrast < 100)
+ {
+ nContrast *= 0x10000;
+ nContrast /= 100;
+ }
+ else if (nContrast < 200)
+ nContrast = (100 * 0x10000) / (200-nContrast);
+ else
+ nContrast = 0x7fffffff;
+ rPropOpt.AddOpt( ESCHER_Prop_pictureContrast, nContrast);
+ }
+
+ if (nBrightness != 0)
+ rPropOpt.AddOpt( ESCHER_Prop_pictureBrightness, nBrightness * 327 );
+
+ sal_Int32 nCropL = 0;
+ sal_Int32 nCropR = 0;
+ sal_Int32 nCropT = 0;
+ sal_Int32 nCropB = 0;
+ if (const SwCropGrf* pCropItem = rNd.GetSwAttrSet().GetItemIfSet(RES_GRFATR_CROPGRF))
+ {
+ nCropL += pCropItem->GetLeft();
+ nCropR += pCropItem->GetRight();
+ nCropT += pCropItem->GetTop();
+ nCropB += pCropItem->GetBottom();
+ }
+
+ // simulate border padding as a negative crop.
+ if (const SvxBoxItem* pBoxItem = rFormat.GetItemIfSet(RES_BOX, false))
+ {
+ nCropL -= pBoxItem->GetDistance( SvxBoxItemLine::LEFT );
+ nCropR -= pBoxItem->GetDistance( SvxBoxItemLine::RIGHT );
+ nCropT -= pBoxItem->GetDistance( SvxBoxItemLine::TOP );
+ nCropB -= pBoxItem->GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+
+ const Size aSz( rNd.GetTwipSize() );
+ if( 0 != nCropL )
+ rPropOpt.AddOpt( ESCHER_Prop_cropFromLeft, ToFract16( nCropL, aSz.Width()) );
+ if( 0 != nCropR )
+ rPropOpt.AddOpt( ESCHER_Prop_cropFromRight, ToFract16( nCropR, aSz.Width()));
+ if( 0 != nCropT )
+ rPropOpt.AddOpt( ESCHER_Prop_cropFromTop, ToFract16( nCropT, aSz.Height()));
+ if( 0 != nCropB )
+ rPropOpt.AddOpt( ESCHER_Prop_cropFromBottom, ToFract16( nCropB, aSz.Height()));
+}
+
+void SwBasicEscherEx::SetPicId(const SdrObject &, sal_uInt32,
+ EscherPropertyContainer &)
+{
+}
+
+void SwEscherEx::SetPicId(const SdrObject &rSdrObj, sal_uInt32 nShapeId,
+ EscherPropertyContainer &rPropOpt)
+{
+ m_pTextBxs->Append(rSdrObj, nShapeId);
+ sal_uInt32 nPicId = m_pTextBxs->Count();
+ nPicId *= 0x10000;
+ rPropOpt.AddOpt( ESCHER_Prop_pictureId, nPicId );
+}
+
+sal_Int32 SwBasicEscherEx::WriteOLEFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId)
+{
+ sal_Int32 nBorderThick = 0;
+ if (const SdrObject* pSdrObj = rFormat.FindRealSdrObject())
+ {
+ SwNodeIndex aIdx(*rFormat.GetContent().GetContentIdx(), 1);
+ SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
+ sal_Int64 nAspect = rOLENd.GetAspect();
+
+ uno::Reference < embed::XEmbeddedObject > xObj(rOLENd.GetOLEObj().GetOleRef());
+
+ // the rectangle is used to transport the size of the object
+ // the left, top corner is set to ( 0, 0 ) by default constructor,
+ // if the width and height are set correctly bRectIsSet should be set to true
+ awt::Rectangle aRect;
+ bool bRectIsSet = false;
+
+ // TODO/LATER: should the icon size be stored in case of iconified object?
+ if ( xObj.is() && nAspect != embed::Aspects::MSOLE_ICON )
+ {
+ try
+ {
+ awt::Size aSize = xObj->getVisualAreaSize( nAspect );
+ aRect.Width = aSize.Width;
+ aRect.Height = aSize.Height;
+ bRectIsSet = true;
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+
+ /*
+ #i5970#
+ Export floating ole2 .doc ver 8+ wmf ole2 previews as emf previews
+ instead ==> allows unicode text to be preserved
+ */
+#ifdef OLE_PREVIEW_AS_EMF
+ const Graphic* pGraphic = rOLENd.GetGraphic();
+#endif
+ OpenContainer(ESCHER_SpContainer);
+
+ EscherPropertyContainer aPropOpt;
+ const SwMirrorGrf &rMirror = rOLENd.GetSwAttrSet().GetMirrorGrf();
+ WriteOLEPicture(aPropOpt,
+ AddMirrorFlags(ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::OLEShape, rMirror),
+ pGraphic ? *pGraphic : Graphic(), *pSdrObj, nShapeId, bRectIsSet ? &aRect : nullptr );
+
+ nBorderThick = WriteFlyFrameAttr(rFormat, mso_sptPictureFrame, aPropOpt);
+ WriteGrfAttr(rOLENd, rFormat, aPropOpt);
+ aPropOpt.Commit(GetStream());
+
+ // store anchor attribute
+ WriteFrameExtraData( rFormat );
+
+ CloseContainer(); // ESCHER_SpContainer
+ }
+ return nBorderThick;
+}
+
+void SwBasicEscherEx::WriteBrushAttr(const SvxBrushItem &rBrush,
+ EscherPropertyContainer& rPropOpt)
+{
+ bool bSetOpacity = false;
+ sal_uInt32 nOpaque = 0;
+ if (const GraphicObject *pGraphicObject = rBrush.GetGraphicObject())
+ {
+ OString aUniqueId = pGraphicObject->GetUniqueID();
+ if (!aUniqueId.isEmpty())
+ {
+ sal_uInt32 nBlibId = mxGlobal->GetBlibID(*QueryPictureStream(), *pGraphicObject);
+ if (nBlibId)
+ rPropOpt.AddOpt(ESCHER_Prop_fillBlip,nBlibId,true);
+ }
+
+ nOpaque = 255 - pGraphicObject->GetAttr().GetAlpha();
+ if (0 != nOpaque)
+ bSetOpacity = true;
+
+ rPropOpt.AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
+ rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
+ rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0 );
+ }
+ else
+ {
+ sal_uInt32 nFillColor = GetColor(rBrush.GetColor());
+ rPropOpt.AddOpt( ESCHER_Prop_fillColor, nFillColor );
+ rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, nFillColor ^ 0xffffff );
+ rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100010 );
+
+ nOpaque = 255 - rBrush.GetColor().GetAlpha();
+ if (0 != nOpaque)
+ bSetOpacity = true;
+ }
+
+ if (bSetOpacity)
+ {
+ nOpaque = (nOpaque * 100) / 0xFE;
+ nOpaque = ((100 - nOpaque) << 16) / 100;
+ rPropOpt.AddOpt(ESCHER_Prop_fillOpacity, nOpaque);
+ }
+}
+
+sal_Int32 SwBasicEscherEx::WriteFlyFrameAttr(const SwFrameFormat& rFormat,
+ MSO_SPT eShapeType, EscherPropertyContainer& rPropOpt)
+{
+ sal_Int32 nLineWidth=0;
+ bool bFirstLine = true;
+ if (const SvxBoxItem* pItem = rFormat.GetItemIfSet(RES_BOX))
+ {
+ static const o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aExhperProp =
+ {
+ sal_uInt16(ESCHER_Prop_dyTextTop), sal_uInt16(ESCHER_Prop_dyTextBottom),
+ sal_uInt16(ESCHER_Prop_dxTextLeft), sal_uInt16(ESCHER_Prop_dxTextRight)
+ };
+ const SvxBorderLine* pLine;
+
+ for( SvxBoxItemLine n : o3tl::enumrange<SvxBoxItemLine>() )
+ {
+ pLine = pItem->GetLine( n );
+ if( nullptr != pLine )
+ {
+ if( bFirstLine )
+ {
+ sal_uInt32 nLineColor = GetColor(pLine->GetColor());
+ rPropOpt.AddOpt( ESCHER_Prop_lineColor, nLineColor );
+ rPropOpt.AddOpt( ESCHER_Prop_lineBackColor,
+ nLineColor ^ 0xffffff );
+
+ MSO_LineStyle eStyle;
+ if( pLine->isDouble() )
+ {
+ // double line
+ nLineWidth = pLine->GetWidth();
+ if( pLine->GetInWidth() == pLine->GetOutWidth() )
+ eStyle = mso_lineDouble;
+ else if( pLine->GetInWidth() < pLine->GetOutWidth() )
+ eStyle = mso_lineThickThin;
+ else
+ eStyle = mso_lineThinThick;
+ }
+ else
+ {
+ // simple line
+ eStyle = mso_lineSimple;
+ nLineWidth = pLine->GetWidth();
+ }
+
+ rPropOpt.AddOpt( ESCHER_Prop_lineStyle, eStyle );
+ rPropOpt.AddOpt( ESCHER_Prop_lineWidth,
+ DrawModelToEmu( nLineWidth ));
+
+ MSO_LineDashing eDashing = mso_lineSolid;
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::DASHED:
+ eDashing = mso_lineDashGEL;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ eDashing = mso_lineDotGEL;
+ break;
+ case SvxBorderLineStyle::SOLID:
+ default:
+ break;
+ }
+ rPropOpt.AddOpt( ESCHER_Prop_lineDashing, eDashing );
+ rPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x8000E );
+
+ //Use import logic to determine how much of border will go
+ //outside graphic
+ nLineWidth = SwMSDffManager::GetEscherLineMatch(
+ eStyle,eShapeType,nLineWidth);
+ bFirstLine = false;
+ }
+ rPropOpt.AddOpt( aExhperProp[ n ], DrawModelToEmu(
+ pItem->GetDistance( n ) ));
+ }
+ else
+ rPropOpt.AddOpt( aExhperProp[ n ], DrawModelToEmu(pItem->GetDistance( n )) );
+ }
+ }
+ else
+ {
+ rPropOpt.AddOpt( ESCHER_Prop_dyTextTop, 0 );
+ rPropOpt.AddOpt( ESCHER_Prop_dyTextBottom, 0 );
+ rPropOpt.AddOpt( ESCHER_Prop_dxTextLeft, 0 );
+ rPropOpt.AddOpt( ESCHER_Prop_dxTextRight, 0 );
+ }
+
+ if( bFirstLine ) // no valid line found
+ {
+ rPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 );
+ }
+ const SwAttrSet& rAttrSet = rFormat.GetAttrSet();
+ if (rAttrSet.GetItemIfSet(RES_BOX, false))
+ {
+ if (const SvxShadowItem* pSI =rAttrSet.GetItemIfSet(RES_SHADOW))
+ {
+ constexpr sal_uInt32 nShadowType = 131074; // shadow type of ms word. need to set the default value.
+
+ Color nColor = pSI->GetColor();
+ sal_Int32 nOffX
+ = o3tl::convert(pSI->GetWidth(), o3tl::Length::twip, o3tl::Length::emu);
+ sal_Int32 nOffY
+ = o3tl::convert(pSI->GetWidth(), o3tl::Length::twip, o3tl::Length::emu);
+
+ SvxShadowLocation eLocation = pSI->GetLocation();
+ if( (eLocation!=SvxShadowLocation::NONE) && (pSI->GetWidth()!=0) )
+ {
+ switch( eLocation )
+ {
+ case SvxShadowLocation::TopLeft:
+ {
+ nOffX = -nOffX;
+ nOffY = -nOffY;
+ }
+ break;
+ case SvxShadowLocation::TopRight:
+ {
+ nOffY = -nOffY;
+ }
+ break;
+ case SvxShadowLocation::BottomLeft:
+ {
+ nOffX = -nOffX;
+ }
+ break;
+ case SvxShadowLocation::BottomRight:
+ break;
+ default:
+ break;
+ }
+
+ rPropOpt.AddOpt( DFF_Prop_shadowColor, wwUtility::RGBToBGR(nColor));
+ rPropOpt.AddOpt( DFF_Prop_shadowOffsetX, nOffX );
+ rPropOpt.AddOpt( DFF_Prop_shadowOffsetY, nOffY );
+ rPropOpt.AddOpt( DFF_Prop_fshadowObscured, nShadowType );
+ }
+ }
+ }
+
+ // SwWW8ImplReader::Read_GrafLayer() imports these as opaque
+ // unconditionally, so if both are true, don't export the property.
+ const bool bIsInHeader = sw::IsFlyFrameFormatInHeader(rFormat);
+ const bool bIsThrough = rFormat.GetSurround().GetValue() == css::text::WrapTextMode_THROUGH;
+
+ // Anything (like a transparent image) that allows text to wrap through should not force a non-transparent background,
+ // and neither should the commonly seen backgrounds anchored in headers.
+ if (bIsInHeader || bIsThrough)
+ {
+ std::unique_ptr<SvxBrushItem> aBrush(rFormat.makeBackgroundBrushItem());
+ WriteBrushAttr(*aBrush, rPropOpt);
+ }
+ else
+ {
+ // for unknown reasons, force exporting a non-transparent background on fly frames.
+ std::shared_ptr<SvxBrushItem> aBrush(mrWrt.TrueFrameBgBrush(rFormat));
+
+ if(aBrush)
+ {
+ WriteBrushAttr(*aBrush, rPropOpt);
+ }
+ }
+
+ const SdrObject* pObj = rFormat.FindRealSdrObject();
+
+ if( pObj && (pObj->GetLayer() == GetHellLayerId() ||
+ pObj->GetLayer() == GetInvisibleHellId() ) && !(bIsInHeader && bIsThrough))
+ {
+ rPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x200020 );
+ }
+
+ PreWriteHyperlinkWithinFly(rFormat,rPropOpt);
+
+ return nLineWidth;
+}
+
+sal_Int32 SwEscherEx::WriteFlyFrameAttr(const SwFrameFormat& rFormat, MSO_SPT eShapeType,
+ EscherPropertyContainer& rPropOpt)
+{
+ sal_Int32 nLineWidth = SwBasicEscherEx::WriteFlyFrameAttr(rFormat, eShapeType,
+ rPropOpt);
+
+ /*
+ These are not in SwBasicEscherEx::WriteFlyFrameAttr because inline objs
+ can't do it in word and it hacks it in by stretching the graphic that
+ way, perhaps we should actually draw in this space into the graphic we
+ are exporting!
+ */
+ if (const SvxLRSpaceItem* pItem = rFormat.GetItemIfSet(RES_LR_SPACE))
+ {
+ rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft,
+ DrawModelToEmu( pItem->GetLeft() ) );
+ rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight,
+ DrawModelToEmu( pItem->GetRight() ) );
+ }
+ else
+ {
+ rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft, 0 );
+ rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight, 0 );
+ }
+
+ if (const SvxULSpaceItem* pItem = rFormat.GetItemIfSet(RES_UL_SPACE))
+ {
+ rPropOpt.AddOpt( ESCHER_Prop_dyWrapDistTop,
+ DrawModelToEmu( pItem->GetUpper() ) );
+ rPropOpt.AddOpt( ESCHER_Prop_dyWrapDistBottom,
+ DrawModelToEmu( pItem->GetLower() ) );
+ }
+
+ if (rFormat.GetSurround().IsContour())
+ {
+ if (const SwNoTextNode *pNd = GetNoTextNodeFromSwFrameFormat(rFormat))
+ {
+ const tools::PolyPolygon *pPolyPoly = pNd->HasContour();
+ if (pPolyPoly && pPolyPoly->Count())
+ {
+ tools::Polygon aPoly = CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/false);
+ SvMemoryStream aPolyDump;
+ aPolyDump.SetEndian(SvStreamEndian::LITTLE);
+
+ sal_uInt16 nLen = aPoly.GetSize();
+ aPolyDump.WriteUInt16( nLen );
+ aPolyDump.WriteUInt16( nLen );
+ aPolyDump.WriteUInt16( 8 );
+ for (sal_uInt16 nI = 0; nI < nLen; ++nI)
+ {
+ aPolyDump.WriteUInt32( aPoly[nI].X() );
+ aPolyDump.WriteUInt32( aPoly[nI].Y() );
+ }
+
+ rPropOpt.AddOpt(DFF_Prop_pWrapPolygonVertices, false, 0, aPolyDump);
+ }
+ }
+ }
+
+ PreWriteHyperlinkWithinFly(rFormat,rPropOpt);
+
+ return nLineWidth;
+}
+
+void SwBasicEscherEx::Init()
+{
+ MapUnit eMap = MapUnit::MapTwip;
+ if (SwDrawModel *pModel = mrWrt.m_rDoc.getIDocumentDrawModelAccess().GetDrawModel())
+ {
+ // PPT works only with units of 576DPI
+ // WW however is using twips, i.e 1440DPI.
+ eMap = pModel->GetScaleUnit();
+ }
+
+ // MS-DFF-Properties mostly are in EMU (English Metric Units)
+ Fraction aFact = conversionFract(o3tl::Length::mm100, o3tl::Length::emu);
+ aFact /= GetMapFactor(MapUnit::Map100thMM, eMap).X();
+ mnEmuMul = aFact.GetNumerator();
+ mnEmuDiv = aFact.GetDenominator();
+
+ SetHellLayerId(mrWrt.m_rDoc.getIDocumentDrawModelAccess().GetHellId());
+}
+
+sal_Int32 SwBasicEscherEx::ToFract16(sal_Int32 nVal, sal_uInt32 nMax)
+{
+ if (nMax)
+ {
+ if (nVal >= 0)
+ {
+ sal_Int32 nMSVal = (nVal / 65536) * nMax;
+ nMSVal += (nVal * 65536) / nMax;
+ return nMSVal;
+ } else {
+ // negative fraction does not have "-0", fractional part is always
+ // positive: -0.4 represented as -1 + 0.6
+ sal_Int32 const nDiv = (nVal / sal_Int32(nMax)) - 1;
+ sal_uInt32 nMSVal = (sal_uInt32(nDiv) << 16) & 0xffff0000;
+ nMSVal += (nVal * 65536) / sal_Int32(nMax) + (-nDiv * 65536);
+ return nMSVal;
+ }
+ }
+ return 0;
+}
+
+SdrLayerID SwBasicEscherEx::GetInvisibleHellId() const
+{
+ return mrWrt.m_rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId();
+}
+
+void SwBasicEscherEx::WritePictures()
+{
+ if( SvStream* pPicStrm = static_cast< SwEscherExGlobal& >( *mxGlobal ).GetPictureStream() )
+ {
+ // set the blip - entries to the correct stream pos
+ sal_uInt64 nEndPos = pPicStrm->Tell();
+ mxGlobal->WriteBlibStoreEntry(*mpEscherStrm, 1, nEndPos);
+
+ pPicStrm->Seek(0);
+ mpEscherStrm->WriteStream( *pPicStrm );
+ }
+}
+
+SwEscherEx::SwEscherEx(SvStream* pStrm, WW8Export& rWW8Wrt)
+ : SwBasicEscherEx(pStrm, rWW8Wrt),
+ m_pTextBxs(nullptr)
+{
+ m_aHostData.SetClientData(&m_aWinwordAnchoring);
+ OpenContainer( ESCHER_DggContainer );
+
+ sal_uInt16 nColorCount = 4;
+ pStrm ->WriteUInt16( nColorCount << 4 ) // instance
+ .WriteUInt16( ESCHER_SplitMenuColors ) // record type
+ .WriteUInt32( nColorCount * 4 ) // size
+ .WriteUInt32( 0x08000004 )
+ .WriteUInt32( 0x08000001 )
+ .WriteUInt32( 0x08000002 )
+ .WriteUInt32( 0x100000f7 );
+
+ CloseContainer(); // ESCHER_DggContainer
+
+ sal_uInt8 i = 2; // for header/footer and the other
+ PlcDrawObj *pSdrObjs = mrWrt.m_pHFSdrObjs.get();
+ m_pTextBxs = mrWrt.m_pHFTextBxs.get();
+
+ // if no header/footer -> skip over
+ if (!pSdrObjs->size())
+ {
+ --i;
+ pSdrObjs = mrWrt.m_pSdrObjs.get();
+ m_pTextBxs = mrWrt.m_pTextBxs.get();
+ }
+
+ for( ; i--; pSdrObjs = mrWrt.m_pSdrObjs.get(), m_pTextBxs = mrWrt.m_pTextBxs.get() )
+ {
+ // "dummy char" (or any Count ?) - why? Only Microsoft knows it.
+ GetStream().WriteChar( i );
+
+ OpenContainer( ESCHER_DgContainer );
+
+ EnterGroup();
+
+ sal_uLong nSecondShapeId = pSdrObjs == mrWrt.m_pSdrObjs.get() ? GenerateShapeId() : 0;
+
+ // write now all Writer-/DrawObjects
+ DrawObjPointerVector aSorted;
+ MakeZOrderArrAndFollowIds(pSdrObjs->GetObjArr(), aSorted);
+
+ sal_uInt32 nShapeId=0;
+ for (auto& pObj : aSorted)
+ {
+ sal_Int32 nBorderThick=0;
+ OSL_ENSURE(pObj, "impossible");
+ if (!pObj)
+ continue;
+ const ww8::Frame &rFrame = pObj->maContent;
+ const SwFrameFormat& rFormat = rFrame.GetFrameFormat();
+
+ switch (rFrame.GetWriterType())
+ {
+ case ww8::Frame::eTextBox:
+ case ww8::Frame::eOle:
+ case ww8::Frame::eGraphic:
+ nBorderThick = WriteFlyFrame(*pObj, nShapeId, aSorted);
+ break;
+ case ww8::Frame::eFormControl:
+ nShapeId = GenerateShapeId();
+ WriteOCXControl(rFormat, nShapeId);
+ break;
+ case ww8::Frame::eDrawing:
+ {
+ m_aWinwordAnchoring.SetAnchoring(rFormat);
+ const SdrObject* pSdrObj = rFormat.FindRealSdrObject();
+ if (pSdrObj)
+ {
+ nShapeId = AddSdrObject(*pSdrObj);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_ENSURE( false, "Where is the SDR-Object?" );
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( !nShapeId )
+ {
+ nShapeId = AddDummyShape();
+ }
+
+ pObj->SetShapeDetails(nShapeId, nBorderThick);
+ }
+
+ EndSdrObjectPage(); // ???? Bugfix for 74724
+
+ if( nSecondShapeId )
+ {
+ OpenContainer( ESCHER_SpContainer );
+
+ AddShape( ESCHER_ShpInst_Rectangle,
+ ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Background,
+ nSecondShapeId );
+
+ EscherPropertyContainer aPropOpt;
+ const SwFrameFormat &rFormat = mrWrt.m_rDoc.GetPageDesc(0).GetMaster();
+ if (const SvxBrushItem* pBrush = rFormat.GetItemIfSet(RES_BACKGROUND))
+ {
+ WriteBrushAttr(*pBrush, aPropOpt);
+
+ SvxGraphicPosition ePos = pBrush->GetGraphicPos();
+ if( ePos != GPOS_NONE && ePos != GPOS_AREA )
+ {
+ /* #i56806# 0x033F parameter specifies a 32-bit field of shape boolean properties.
+ 0x10001 means fBackground and fUsefBackground flag are true thus background
+ picture will be shown as "tiled" fill.*/
+ aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 );
+ }
+ }
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x8000001 );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 );
+ aPropOpt.AddOpt( ESCHER_Prop_lineWidth, 0 );
+
+ aPropOpt.Commit( *pStrm );
+
+ AddAtom( 4, ESCHER_ClientData );
+ GetStream().WriteInt32( 1 );
+
+ CloseContainer(); // ESCHER_SpContainer
+ }
+ CloseContainer(); // ESCHER_DgContainer
+ }
+}
+
+SwEscherEx::~SwEscherEx()
+{
+}
+
+void SwEscherEx::FinishEscher()
+{
+ mpEscherStrm->Seek(0);
+ mrWrt.m_pTableStrm->WriteStream( *mpEscherStrm );
+ delete mpEscherStrm;
+ mpEscherStrm = nullptr;
+}
+
+
+namespace
+{
+ template<typename OrientType>
+ void lcl_SetRelationOrient(OrientType& rOrient, const sw::WW8AnchorConv eConv, const std::function<void()>& fDefault)
+ {
+ switch(eConv)
+ {
+ case sw::WW8AnchorConv::RELTOTABLECELL:
+ // #i33818#
+ rOrient.SetRelationOrient(text::RelOrientation::PAGE_PRINT_AREA);
+ break;
+ case sw::WW8AnchorConv::CONV2PG:
+ rOrient.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ break;
+ case sw::WW8AnchorConv::CONV2COL_OR_PARA:
+ rOrient.SetRelationOrient(text::RelOrientation::FRAME);
+ break;
+ case sw::WW8AnchorConv::CONV2CHAR:
+ rOrient.SetRelationOrient(text::RelOrientation::CHAR);
+ break;
+ case sw::WW8AnchorConv::CONV2LINE:
+ rOrient.SetRelationOrient(text::RelOrientation::TEXT_LINE);
+ break;
+ default:
+ fDefault();
+ }
+ }
+}
+/** method to perform conversion of positioning attributes with the help
+ of corresponding layout information
+
+ #i30669#
+ Because most of the Writer object positions doesn't correspond to the
+ object positions in WW8, this method converts the positioning
+ attributes. For this conversion the corresponding layout information
+ is needed. If no layout information exists - e.g. no layout exists - no
+ conversion is performed.
+ No conversion is performed for as-character anchored objects. Whose
+ object positions are already treated special in method <WriteData(..)>.
+
+ @param _iorHoriOri
+ input/output parameter - containing the current horizontal position
+ attributes, which are converted by this method.
+
+ @param _iorVertOri
+ input/output parameter - containing the current vertical position
+ attributes, which are converted by this method.
+
+ @param _rFrameFormat
+ input parameter - frame format of the anchored object
+
+ @return boolean, indicating, if a conversion has been performed.
+*/
+bool WinwordAnchoring::ConvertPosition( SwFormatHoriOrient& _iorHoriOri,
+ SwFormatVertOrient& _iorVertOri,
+ const SwFrameFormat& _rFrameFormat )
+{
+ const RndStdIds eAnchor = _rFrameFormat.GetAnchor().GetAnchorId();
+
+ if ( (RndStdIds::FLY_AS_CHAR == eAnchor) || (RndStdIds::FLY_AT_FLY == eAnchor) )
+ {
+ // no conversion for as-character or at frame anchored objects
+ return false;
+ }
+
+ // determine value of attribute 'Follow text flow', because positions aligned
+ // at page areas have to be converted, if it's set.
+ const bool bFollowTextFlow = _rFrameFormat.GetFollowTextFlow().GetValue();
+
+ // check, if horizontal and vertical position have to be converted due to
+ // the fact, that the object is anchored at a paragraph, which has a "column
+ // break before" attribute
+ bool bConvDueToAnchoredAtColBreakPara( false );
+ if ( ( (eAnchor == RndStdIds::FLY_AT_PARA) || (eAnchor == RndStdIds::FLY_AT_CHAR) ) &&
+ _rFrameFormat.GetAnchor().GetAnchorNode() &&
+ _rFrameFormat.GetAnchor().GetAnchorNode()->IsTextNode() )
+ {
+ SwTextNode& rAnchorTextNode =
+ dynamic_cast<SwTextNode&>(*_rFrameFormat.GetAnchor().GetAnchorNode());
+ const SvxFormatBreakItem& rBreak = rAnchorTextNode.GetAttr(RES_BREAK);
+ if (rBreak.GetBreak() == SvxBreak::ColumnBefore)
+ {
+ bConvDueToAnchoredAtColBreakPara = true;
+ }
+ }
+
+ sw::WW8AnchorConv eHoriConv(sw::WW8AnchorConv::NO_CONV);
+ sw::WW8AnchorConv eVertConv(sw::WW8AnchorConv::NO_CONV);
+ // convert horizontal position, if needed
+ {
+
+ // determine, if conversion has to be performed due to the position orientation
+ bool bConvDueToOrientation( false );
+ {
+ const sal_Int16 eHOri = _iorHoriOri.GetHoriOrient();
+ bConvDueToOrientation = eHOri == text::HoriOrientation::LEFT || eHOri == text::HoriOrientation::RIGHT ||
+ eHOri == text::HoriOrientation::INSIDE || eHOri == text::HoriOrientation::OUTSIDE ||
+ ( eHOri != text::HoriOrientation::CENTER && _iorHoriOri.IsPosToggle() );
+ }
+
+ // determine conversion type due to the position relation
+ if ( bConvDueToAnchoredAtColBreakPara )
+ {
+ eHoriConv = sw::WW8AnchorConv::CONV2PG;
+ }
+ else if ( _iorHoriOri.IsPosToggle()
+ && _iorHoriOri.GetHoriOrient() == text::HoriOrientation::RIGHT )
+ {
+ eHoriConv = sw::WW8AnchorConv::NO_CONV;
+ _iorHoriOri.SetHoriOrient( text::HoriOrientation::OUTSIDE );
+ }
+ else
+ {
+ switch ( _iorHoriOri.GetRelationOrient() )
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ if ( bConvDueToOrientation || bFollowTextFlow )
+ eHoriConv = sw::WW8AnchorConv::CONV2PG;
+ }
+ break;
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::PAGE_RIGHT:
+ {
+ // relation not supported by WW8. Thus, conversion always needed.
+ eHoriConv = sw::WW8AnchorConv::CONV2PG;
+ }
+ break;
+ case text::RelOrientation::FRAME:
+ {
+ if ( bConvDueToOrientation )
+ eHoriConv = sw::WW8AnchorConv::CONV2COL_OR_PARA;
+ }
+ break;
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::FRAME_LEFT:
+ case text::RelOrientation::FRAME_RIGHT:
+ {
+ // relation not supported by WW8. Thus, conversion always needed.
+ eHoriConv = sw::WW8AnchorConv::CONV2COL_OR_PARA;
+ }
+ break;
+ case text::RelOrientation::CHAR:
+ {
+ if ( bConvDueToOrientation )
+ eHoriConv = sw::WW8AnchorConv::CONV2CHAR;
+ }
+ break;
+ default:
+ OSL_FAIL( "<WinwordAnchoring::ConvertPosition(..)> - unknown horizontal relation" );
+ }
+ }
+ }
+
+ // convert vertical position, if needed
+ {
+
+ // determine, if conversion has to be performed due to the position orientation
+ bool bConvDueToOrientation( false );
+ {
+ const sal_Int16 eVOri = _iorVertOri.GetVertOrient();
+ bConvDueToOrientation = ( eVOri == text::VertOrientation::TOP ||
+ eVOri == text::VertOrientation::BOTTOM ||
+ eVOri == text::VertOrientation::CHAR_TOP ||
+ eVOri == text::VertOrientation::CHAR_BOTTOM ||
+ eVOri == text::VertOrientation::CHAR_CENTER ||
+ eVOri == text::VertOrientation::LINE_TOP ||
+ eVOri == text::VertOrientation::LINE_BOTTOM ||
+ eVOri == text::VertOrientation::LINE_CENTER );
+ }
+
+ // determine conversion type due to the position relation
+ if ( bConvDueToAnchoredAtColBreakPara )
+ {
+ eVertConv = sw::WW8AnchorConv::CONV2PG;
+ }
+ else
+ {
+ switch ( _iorVertOri.GetRelationOrient() )
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ if ( bConvDueToOrientation || bFollowTextFlow )
+ eVertConv = sw::WW8AnchorConv::CONV2PG;
+ }
+ break;
+ case text::RelOrientation::FRAME:
+ {
+ if ( bConvDueToOrientation ||
+ _iorVertOri.GetVertOrient() == text::VertOrientation::CENTER )
+ {
+ eVertConv = sw::WW8AnchorConv::CONV2COL_OR_PARA;
+ }
+ }
+ break;
+ case text::RelOrientation::PRINT_AREA:
+ {
+ // relation not supported by WW8. Thus, conversion always needed.
+ eVertConv = sw::WW8AnchorConv::CONV2COL_OR_PARA;
+ }
+ break;
+ case text::RelOrientation::CHAR:
+ {
+ // relation not supported by WW8. Thus, conversion always needed.
+ eVertConv = sw::WW8AnchorConv::CONV2COL_OR_PARA;
+ }
+ break;
+ case text::RelOrientation::TEXT_LINE:
+ {
+ if ( bConvDueToOrientation ||
+ _iorVertOri.GetVertOrient() == text::VertOrientation::NONE )
+ {
+ eVertConv = sw::WW8AnchorConv::CONV2LINE;
+ }
+ }
+ break;
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::PAGE_RIGHT:
+ case text::RelOrientation::FRAME_LEFT:
+ case text::RelOrientation::FRAME_RIGHT:
+ default:
+ OSL_FAIL( "<WinwordAnchoring::ConvertPosition(..)> - unknown vertical relation" );
+ }
+ }
+
+ }
+ if (eVertConv != sw::WW8AnchorConv::NO_CONV || eHoriConv != sw::WW8AnchorConv::NO_CONV)
+ {
+ sw::WW8AnchorConvResult aResult(eHoriConv, eVertConv);
+ _rFrameFormat.CallSwClientNotify(sw::WW8AnchorConvHint(aResult));
+ if(!aResult.m_bConverted)
+ return false;
+ if (eHoriConv != sw::WW8AnchorConv::NO_CONV)
+ {
+ lcl_SetRelationOrient(_iorHoriOri, eHoriConv, [&_iorHoriOri]() {_iorHoriOri.SetHoriOrient(text::HoriOrientation::NONE);} );
+ _iorHoriOri.SetPos(aResult.m_aPos.X());
+ }
+ if (eVertConv != sw::WW8AnchorConv::NO_CONV)
+ {
+ lcl_SetRelationOrient(_iorVertOri, eVertConv, [&_iorVertOri]() {_iorVertOri.SetVertOrient(text::VertOrientation::NONE);} );
+ _iorVertOri.SetPos(aResult.m_aPos.Y());
+ }
+ return true;
+ }
+ return false;
+}
+
+void WinwordAnchoring::SetAnchoring(const SwFrameFormat& rFormat)
+{
+ const RndStdIds eAnchor = rFormat.GetAnchor().GetAnchorId();
+ mbInline = (eAnchor == RndStdIds::FLY_AS_CHAR);
+
+ mnGroupShapeBooleanProperties = 0;
+ if (!rFormat.GetFollowTextFlow().GetValue())
+ {
+ // bit32: fUseLayoutInCell, bit16: fLayoutInCell
+ mnGroupShapeBooleanProperties |= 0x80000000;
+ }
+
+ SwFormatHoriOrient rHoriOri = rFormat.GetHoriOrient();
+ SwFormatVertOrient rVertOri = rFormat.GetVertOrient();
+
+ // #i30669# - convert the positioning attributes.
+ // Most positions are converted, if layout information exists.
+ const bool bPosConverted = ConvertPosition( rHoriOri, rVertOri, rFormat );
+
+ const sal_Int16 eHOri = rHoriOri.GetHoriOrient();
+ const sal_Int16 eVOri = rVertOri.GetVertOrient(); // #i22673#
+
+ const sal_Int16 eHRel = rHoriOri.GetRelationOrient();
+ const sal_Int16 eVRel = rVertOri.GetRelationOrient();
+
+ // horizontal Adjustment
+ switch (eHOri)
+ {
+ default:
+ case text::HoriOrientation::NONE:
+ mnXAlign = 0;
+ break;
+ case text::HoriOrientation::LEFT:
+ mnXAlign = 1;
+ break;
+ case text::HoriOrientation::CENTER:
+ mnXAlign = 2;
+ break;
+ case text::HoriOrientation::RIGHT:
+ mnXAlign = 3;
+ break;
+ case text::HoriOrientation::INSIDE:
+ mnXAlign = 4;
+ break;
+ case text::HoriOrientation::OUTSIDE:
+ mnXAlign = 5;
+ break;
+ }
+
+ // vertical Adjustment
+ // #i22673#
+ // When adjustment is vertically relative to line or to char
+ // bottom becomes top and vice versa
+ const bool bVertSwap = !bPosConverted &&
+ ( (eVRel == text::RelOrientation::CHAR) ||
+ (eVRel == text::RelOrientation::TEXT_LINE) );
+ switch (eVOri)
+ {
+ default:
+ case text::VertOrientation::NONE:
+ mnYAlign = 0;
+ break;
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::LINE_TOP:
+ case text::VertOrientation::CHAR_TOP:
+ mnYAlign = bVertSwap ? 3 : 1;
+ break;
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ mnYAlign = 2;
+ break;
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM:
+ mnYAlign = bVertSwap ? 1 : 3;
+ break;
+ }
+
+ // Adjustment is horizontally relative to...
+ switch (eHRel)
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ mnXRelTo = 0;
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_LEFT: //:-(
+ case text::RelOrientation::PAGE_RIGHT: //:-(
+ mnXRelTo = 1;
+ break;
+ case text::RelOrientation::FRAME:
+ case text::RelOrientation::FRAME_LEFT: //:-(
+ case text::RelOrientation::FRAME_RIGHT: //:-(
+ if (eAnchor == RndStdIds::FLY_AT_PAGE)
+ mnXRelTo = 1;
+ else
+ mnXRelTo = 2;
+ break;
+ case text::RelOrientation::PRINT_AREA:
+ if (eAnchor == RndStdIds::FLY_AT_PAGE)
+ mnXRelTo = 0;
+ else
+ mnXRelTo = 2;
+ break;
+ case text::RelOrientation::CHAR:
+ mnXRelTo = 3;
+ break;
+ case text::RelOrientation::TEXT_LINE:
+ break;
+ }
+
+ // Adjustment is vertically relative to...
+ switch (eVRel)
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ mnYRelTo = 0;
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ mnYRelTo = 1;
+ break;
+ case text::RelOrientation::PRINT_AREA:
+ if (eAnchor == RndStdIds::FLY_AT_PAGE)
+ mnYRelTo = 0;
+ else
+ mnYRelTo = 2;
+ break;
+ case text::RelOrientation::FRAME:
+ if (eAnchor == RndStdIds::FLY_AT_PAGE)
+ mnYRelTo = 1;
+ else
+ mnYRelTo = 2;
+ break;
+ case text::RelOrientation::CHAR:
+ case text::RelOrientation::TEXT_LINE: // #i22673# - vertical alignment at top of line
+ case text::RelOrientation::PAGE_LEFT: //nonsense
+ case text::RelOrientation::PAGE_RIGHT: //nonsense
+ case text::RelOrientation::FRAME_LEFT: //nonsense
+ case text::RelOrientation::FRAME_RIGHT: //nonsense
+ mnYRelTo = 3;
+ break;
+ }
+}
+
+void SwEscherEx::WriteFrameExtraData( const SwFrameFormat& rFormat )
+{
+ m_aWinwordAnchoring.SetAnchoring(rFormat);
+ m_aWinwordAnchoring.WriteData(*this);
+
+ AddAtom(4, ESCHER_ClientAnchor);
+ GetStream().WriteInt32( 0 );
+
+ AddAtom(4, ESCHER_ClientData);
+ GetStream().WriteInt32( 1 );
+}
+
+sal_Int32 SwEscherEx::WriteFlyFrame(const DrawObj &rObj, sal_uInt32 &rShapeId,
+ DrawObjPointerVector &rPVec)
+{
+ const SwFrameFormat &rFormat = rObj.maContent.GetFrameFormat();
+
+ // check for textflyframe and if it is the first in a Chain
+ sal_Int32 nBorderThick = 0;
+ const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx();
+ if( pNdIdx )
+ {
+ SwNodeIndex aIdx( *pNdIdx, 1 );
+ switch( aIdx.GetNode().GetNodeType() )
+ {
+ case SwNodeType::Grf:
+ rShapeId = GenerateShapeId();
+ nBorderThick = WriteGrfFlyFrame( rFormat, rShapeId );
+ break;
+ case SwNodeType::Ole:
+ rShapeId = GenerateShapeId();
+ nBorderThick = WriteOLEFlyFrame( rFormat, rShapeId );
+ break;
+ default:
+ if (const SdrObject* pObj = rFormat.FindRealSdrObject())
+ {
+ // check for the first in a Chain
+ sal_uInt32 nTextId;
+ sal_uInt16 nOff = 0;
+ const SwFrameFormat* pFormat = &rFormat, *pPrev;
+ while( nullptr != ( pPrev = pFormat->GetChain().GetPrev() ))
+ {
+ ++nOff;
+ pFormat = pPrev;
+ }
+
+ rShapeId = GetFlyShapeId(rFormat, rObj.mnHdFtIndex, rPVec);
+ if( !nOff )
+ {
+ nTextId = m_pTextBxs->GetPos( pObj );
+ if( USHRT_MAX == nTextId )
+ {
+ m_pTextBxs->Append( *pObj, rShapeId );
+ nTextId = m_pTextBxs->Count();
+ }
+ else
+ ++nTextId;
+ }
+ else
+ {
+ const SdrObject* pPrevObj = pFormat->FindRealSdrObject();
+ nTextId = m_pTextBxs->GetPos( pPrevObj );
+ if( USHRT_MAX == nTextId )
+ {
+ sal_uInt32 nPrevShapeId =
+ GetFlyShapeId(*pFormat, rObj.mnHdFtIndex, rPVec);
+ m_pTextBxs->Append( *pPrevObj, nPrevShapeId );
+ nTextId = m_pTextBxs->Count();
+ }
+ else
+ ++nTextId;
+ }
+ nTextId *= 0x10000;
+ nTextId += nOff;
+
+ nBorderThick = WriteTextFlyFrame(rObj, rShapeId, nTextId, rPVec);
+ }
+
+ //In browse mode the sdr object doesn't always exist. For example, the
+ //object is in the hidden header/footer. We save the fmt directly
+ //in such cases; we copy most of the logic from the block above
+ const bool bBrowseMode = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE);
+ if( bBrowseMode && rFormat.GetDoc())
+ {
+ if( !rFormat.GetChain().GetPrev() )//obj in header/footer?
+ {
+ rShapeId = GetFlyShapeId(rFormat, rObj.mnHdFtIndex, rPVec);
+ m_pTextBxs->Append( &rFormat, rShapeId );
+ sal_uInt32 nTextId = m_pTextBxs->Count();
+
+ nTextId *= 0x10000;
+ nBorderThick = WriteTextFlyFrame(rObj, rShapeId, nTextId, rPVec);
+ }
+ }
+
+ }
+ }
+ return nBorderThick;
+}
+
+static sal_uInt16 FindPos(const SwFrameFormat &rFormat, unsigned int nHdFtIndex,
+ DrawObjPointerVector &rPVec)
+{
+ auto aIter = std::find_if(rPVec.begin(), rPVec.end(),
+ [&rFormat, nHdFtIndex](const DrawObj* pObj) {
+ OSL_ENSURE(pObj, "Impossible");
+ return pObj &&
+ nHdFtIndex == pObj->mnHdFtIndex &&
+ &rFormat == (&pObj->maContent.GetFrameFormat());
+ });
+ if (aIter != rPVec.end())
+ return static_cast< sal_uInt16 >(aIter - rPVec.begin());
+ return USHRT_MAX;
+}
+
+sal_Int32 SwEscherEx::WriteTextFlyFrame(const DrawObj &rObj, sal_uInt32 nShapeId,
+ sal_uInt32 nTextBox, DrawObjPointerVector &rPVec)
+{
+ const SwFrameFormat &rFormat = rObj.maContent.GetFrameFormat();
+ SvxFrameDirection nDirection = rObj.mnDirection;
+
+ sal_Int32 nBorderThick=0;
+ OpenContainer( ESCHER_SpContainer );
+
+ AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, nShapeId );
+ EscherPropertyContainer aPropOpt;
+ aPropOpt.AddOpt(ESCHER_Prop_lTxid, nTextBox);
+ if (const SwFrameFormat *pNext = rFormat.GetChain().GetNext())
+ {
+ sal_uInt16 nPos = FindPos(*pNext, rObj.mnHdFtIndex, rPVec);
+ if (USHRT_MAX != nPos && m_aFollowShpIds[nPos])
+ aPropOpt.AddOpt(ESCHER_Prop_hspNext, m_aFollowShpIds[nPos]);
+ }
+ nBorderThick = WriteFlyFrameAttr( rFormat, mso_sptTextBox, aPropOpt );
+
+ MSO_TextFlow nFlow;
+
+ switch (nDirection)
+ {
+ default:
+ OSL_ENSURE(false, "unknown direction type");
+ [[fallthrough]];
+ case SvxFrameDirection::Horizontal_LR_TB:
+ nFlow=mso_txflHorzN;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ nFlow=mso_txflHorzN;
+ break;
+ case SvxFrameDirection::Vertical_LR_TB: //not really possible in word
+ case SvxFrameDirection::Vertical_RL_TB:
+ nFlow=mso_txflTtoBA;
+ break;
+ case SvxFrameDirection::Vertical_LR_BT:
+ nFlow = mso_txflBtoT;
+ break;
+ }
+ aPropOpt.AddOpt( ESCHER_Prop_txflTextFlow, nFlow );
+
+ aPropOpt.Commit( GetStream() );
+
+ // store anchor attribute
+ WriteFrameExtraData( rFormat );
+
+ AddAtom( 4, ESCHER_ClientTextbox ); GetStream().WriteUInt32( nTextBox );
+
+ CloseContainer(); // ESCHER_SpContainer
+ return nBorderThick;
+}
+
+void SwBasicEscherEx::WriteOLEPicture(EscherPropertyContainer &rPropOpt,
+ ShapeFlag nShapeFlags, const Graphic &rGraphic, const SdrObject &rObj,
+ sal_uInt32 nShapeId, const awt::Rectangle* pVisArea )
+{
+ //nShapeFlags == 0xA00 + flips and ole active
+ AddShape(ESCHER_ShpInst_PictureFrame, nShapeFlags, nShapeId);
+
+ GraphicObject aGraphicObject(rGraphic);
+ OString aId = aGraphicObject.GetUniqueID();
+ if (!aId.isEmpty())
+ {
+ // SJ: the third parameter (pVisArea) should be set...
+ sal_uInt32 nBlibId = mxGlobal->GetBlibID( *QueryPictureStream(), aGraphicObject, pVisArea);
+ if (nBlibId)
+ rPropOpt.AddOpt(ESCHER_Prop_pib, nBlibId, true);
+ }
+
+ SetPicId(rObj, nShapeId, rPropOpt);
+ rPropOpt.AddOpt( ESCHER_Prop_pictureActive, 0x10000 );
+}
+
+void SwEscherEx::WriteOCXControl( const SwFrameFormat& rFormat, sal_uInt32 nShapeId )
+{
+ const SdrObject* pSdrObj = rFormat.FindRealSdrObject();
+ if (!pSdrObj)
+ return;
+
+ OpenContainer( ESCHER_SpContainer );
+
+ SwDrawModel *pModel = mrWrt.m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ OutputDevice *pDevice = Application::GetDefaultDevice();
+ OSL_ENSURE(pModel && pDevice, "no model or device");
+
+ // #i71538# use complete SdrViews
+ // SdrExchangeView aExchange(pModel, pDevice);
+ SdrView aExchange(*pModel, pDevice);
+ const Graphic aGraphic(SdrExchangeView::GetObjGraphic(*pSdrObj));
+ EscherPropertyContainer aPropOpt;
+ WriteOLEPicture(aPropOpt,
+ ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::OLEShape, aGraphic,
+ *pSdrObj, nShapeId, nullptr );
+
+ WriteFlyFrameAttr( rFormat, mso_sptPictureFrame , aPropOpt );
+ aPropOpt.Commit( GetStream() );
+
+ // store anchor attribute
+ WriteFrameExtraData( rFormat );
+
+ CloseContainer(); // ESCHER_SpContainer
+
+}
+
+void SwEscherEx::MakeZOrderArrAndFollowIds(
+ std::vector<DrawObj>& rSrcArr, DrawObjPointerVector&rDstArr)
+{
+ ::lcl_makeZOrderArray(mrWrt, rSrcArr, rDstArr);
+
+ //Now set up the follow IDs
+ m_aFollowShpIds.clear();
+
+ for (DrawObj* p : rDstArr)
+ {
+ const SwFrameFormat &rFormat = p->maContent.GetFrameFormat();
+ bool bNeedsShapeId = false;
+
+ if (RES_FLYFRMFMT == rFormat.Which())
+ {
+ const SwFormatChain &rChain = rFormat.GetChain();
+ if (rChain.GetPrev() || rChain.GetNext())
+ bNeedsShapeId = true;
+ }
+
+ sal_uLong nShapeId = bNeedsShapeId ? GenerateShapeId() : 0;
+
+ m_aFollowShpIds.push_back(nShapeId);
+ }
+}
+
+sal_uInt32 SwEscherEx::GetFlyShapeId(const SwFrameFormat& rFormat,
+ unsigned int nHdFtIndex, DrawObjPointerVector &rpVec)
+{
+ sal_uInt16 nPos = FindPos(rFormat, nHdFtIndex, rpVec);
+ sal_uInt32 nShapeId;
+ if (USHRT_MAX != nPos)
+ {
+ nShapeId = m_aFollowShpIds[nPos];
+ if (0 == nShapeId)
+ {
+ nShapeId = GenerateShapeId();
+ m_aFollowShpIds[ nPos ] = nShapeId;
+ }
+ }
+ else
+ nShapeId = GenerateShapeId();
+ return nShapeId;
+}
+
+sal_uInt32 SwEscherEx::QueryTextID(
+ const uno::Reference< drawing::XShape>& xXShapeRef, sal_uInt32 nShapeId )
+{
+ sal_uInt32 nId = 0;
+ if (SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xXShapeRef))
+ {
+ m_pTextBxs->Append( *pObj, nShapeId );
+ nId = m_pTextBxs->Count();
+ nId *= 0x10000;
+ }
+ return nId;
+}
+
+SwMSConvertControls::SwMSConvertControls( SfxObjectShell const *pDSh, SwPaM *pP ) : oox
+::ole::MSConvertOCXControls( pDSh ? pDSh->GetModel() : nullptr ), m_pPaM( pP ), mnObjectId(0)
+{
+}
+
+
+// in transitioning away old filter for ole/ocx controls, ReadOCXStream has been made pure virtual in
+// filter/source/msocximex.cxx, so... we need an implementation here
+bool SwMSConvertControls::ReadOCXStream( tools::SvRef<SotStorage> const & rSrc1,
+ css::uno::Reference< css::drawing::XShape > *pShapeRef,
+ bool bFloatingCtrl )
+{
+ uno::Reference< form::XFormComponent > xFComp;
+ bool bRes = oox::ole::MSConvertOCXControls::ReadOCXStorage( rSrc1, xFComp );
+ if ( bRes && xFComp.is() )
+ {
+ css::awt::Size aSz; // not used in import
+ bRes = InsertControl( xFComp, aSz,pShapeRef,bFloatingCtrl);
+ }
+ return bRes;
+}
+
+void SwMSConvertControls::ExportControl(WW8Export &rWW8Wrt, const SdrUnoObj& rFormObj)
+{
+ const uno::Reference< awt::XControlModel >& xControlModel =
+ rFormObj.GetUnoControlModel();
+
+ //Why oh lord do we use so many different units ?
+ //I think I painted myself into a little bit of a
+ //corner by trying to use the uno interface for
+ //controls export
+ tools::Rectangle aRect = rFormObj.GetLogicRect();
+ aRect.SetPos(Point(0,0));
+ awt::Size aSize;
+ aSize.Width = convertTwipToMm100(aRect.Right());
+ aSize.Height = convertTwipToMm100(aRect.Bottom());
+
+ //Open the ObjectPool
+ tools::SvRef<SotStorage> xObjPool = rWW8Wrt.GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
+
+ //Create a destination storage for the microsoft control
+ sal_uInt32 nObjId = ++mnObjectId;
+ OUString sStorageName = "_" + OUString::number( static_cast<sal_Int64>( nObjId ));
+ tools::SvRef<SotStorage> xOleStg = xObjPool->OpenSotStorage(sStorageName);
+
+ if (!xOleStg.is())
+ return;
+
+ OUString sUName;
+ if (!WriteOCXStream( mxModel, xOleStg,xControlModel,aSize,sUName))
+ return;
+
+ sal_uInt8 aSpecOLE[] =
+ {
+ 0x03, 0x6a, 0xFF, 0xFF, 0xFF, 0xFF, // sprmCPicLocation
+ 0x0a, 0x08, 1, // sprmCFOLE2
+ 0x55, 0x08, 1, // sprmCFSpec
+ 0x56, 0x08, 1 // sprmCFObj
+ };
+ //Set the obj id into the sprmCPicLocation
+ sal_uInt8 *pData = aSpecOLE+2;
+ Set_UInt32(pData,nObjId );
+
+ OUString sField = FieldString(ww::eCONTROL) + "Forms." + sUName + ".1 \\s ";
+
+ rWW8Wrt.OutputField(nullptr, ww::eCONTROL, sField,
+ FieldFlags::Start|FieldFlags::CmdStart|FieldFlags::CmdEnd);
+
+ rWW8Wrt.m_pChpPlc->AppendFkpEntry(rWW8Wrt.Strm().Tell(),sizeof(aSpecOLE),
+ aSpecOLE);
+ rWW8Wrt.WriteChar( 0x1 );
+ rWW8Wrt.OutputField(nullptr, ww::eCONTROL, OUString(), FieldFlags::End | FieldFlags::Close);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx
new file mode 100644
index 0000000000..39e2f88523
--- /dev/null
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -0,0 +1,3804 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vector>
+#include <utility>
+#include <algorithm>
+#include <iostream>
+
+#include "docxexport.hxx"
+
+#include <officecfg/Office/Common.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <hintids.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <svl/grabbagitem.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/whiter.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <frmatr.hxx>
+#include <paratr.hxx>
+#include <txatbase.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtrfmrk.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <charfmt.hxx>
+#include <tox.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <docary.hxx>
+#include <swtable.hxx>
+#include <swtblfmt.hxx>
+#include <section.hxx>
+#include <pagedesc.hxx>
+#include <swrect.hxx>
+#include <reffld.hxx>
+#include <redline.hxx>
+#include <txttxmrk.hxx>
+#include <fmtline.hxx>
+#include <fmtruby.hxx>
+#include <breakit.hxx>
+#include <txtatr.hxx>
+#include <cellatr.hxx>
+#include <fmtrowsplt.hxx>
+#include <com/sun/star/awt/FontRelief.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/text/RubyPosition.hpp>
+#include <com/sun/star/style/CaseMap.hpp>
+#include <oox/export/vmlexport.hxx>
+#include <sal/log.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/string.hxx>
+
+#include "sprmids.hxx"
+
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include <numrule.hxx>
+#include "wrtww8.hxx"
+#include "ww8par.hxx"
+#include <IMark.hxx>
+#include "ww8attributeoutput.hxx"
+
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <formatflysplit.hxx>
+
+#include <cstdio>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace sw::util;
+using namespace sw::types;
+using namespace sw::mark;
+using namespace ::oox::vml;
+
+static OUString lcl_getFieldCode( const IFieldmark* pFieldmark )
+{
+ assert(pFieldmark);
+
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMTEXT )
+ return " FORMTEXT ";
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMDROPDOWN )
+ return " FORMDROPDOWN ";
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMCHECKBOX )
+ return " FORMCHECKBOX ";
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMDATE )
+ return " ODFFORMDATE ";
+ if ( pFieldmark->GetFieldname( ) == ODF_TOC )
+ return " TOC ";
+ if ( pFieldmark->GetFieldname( ) == ODF_HYPERLINK )
+ return " HYPERLINK ";
+ if ( pFieldmark->GetFieldname( ) == ODF_PAGEREF )
+ return " PAGEREF ";
+ return pFieldmark->GetFieldname();
+}
+
+static ww::eField lcl_getFieldId(const IFieldmark*const pFieldmark)
+{
+ assert(pFieldmark);
+
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMTEXT )
+ return ww::eFORMTEXT;
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMDROPDOWN )
+ return ww::eFORMDROPDOWN;
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMCHECKBOX )
+ return ww::eFORMCHECKBOX;
+ if ( pFieldmark->GetFieldname( ) == ODF_FORMDATE )
+ return ww::eFORMDATE;
+ if ( pFieldmark->GetFieldname( ) == ODF_TOC )
+ return ww::eTOC;
+ if ( pFieldmark->GetFieldname( ) == ODF_HYPERLINK )
+ return ww::eHYPERLINK;
+ if ( pFieldmark->GetFieldname( ) == ODF_PAGEREF )
+ return ww::ePAGEREF;
+ return ww::eUNKNOWN;
+}
+
+static OUString
+lcl_getLinkChainName(const uno::Reference<beans::XPropertySet>& rPropertySet,
+ const uno::Reference<beans::XPropertySetInfo>& rPropertySetInfo)
+{
+ OUString sLinkChainName;
+ if (rPropertySetInfo->hasPropertyByName("LinkDisplayName"))
+ {
+ rPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
+ if (!sLinkChainName.isEmpty())
+ return sLinkChainName;
+ }
+ if (rPropertySetInfo->hasPropertyByName("ChainName"))
+ rPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
+ return sLinkChainName;
+}
+
+MSWordAttrIter::MSWordAttrIter( MSWordExportBase& rExport )
+ : m_pOld( rExport.m_pChpIter ), m_rExport( rExport )
+{
+ m_rExport.m_pChpIter = this;
+}
+
+MSWordAttrIter::~MSWordAttrIter()
+{
+ m_rExport.m_pChpIter = m_pOld;
+}
+
+namespace {
+
+class sortswflys
+{
+public:
+ bool operator()(const ww8::Frame &rOne, const ww8::Frame &rTwo) const
+ {
+ return rOne.GetPosition() < rTwo.GetPosition();
+ }
+};
+
+}
+
+void SwWW8AttrIter::IterToCurrent()
+{
+ OSL_ENSURE(maCharRuns.begin() != maCharRuns.end(), "Impossible");
+ mnScript = maCharRunIter->mnScript;
+ meChrSet = maCharRunIter->meCharSet;
+ mbCharIsRTL = maCharRunIter->mbRTL;
+}
+
+SwWW8AttrIter::SwWW8AttrIter(MSWordExportBase& rWr, const SwTextNode& rTextNd) :
+ MSWordAttrIter(rWr),
+ m_rNode(rTextNd),
+ maCharRuns(GetPseudoCharRuns(rTextNd)),
+ m_pCurRedline(nullptr),
+ m_nCurrentSwPos(0),
+ m_nCurRedlinePos(SwRedlineTable::npos),
+ mrSwFormatDrop(rTextNd.GetSwAttrSet().GetDrop())
+{
+
+ SwPosition aPos(rTextNd);
+ mbParaIsRTL = SvxFrameDirection::Horizontal_RL_TB == rWr.m_rDoc.GetTextDirection(aPos);
+
+ maCharRunIter = maCharRuns.begin();
+ IterToCurrent();
+
+ /*
+ #i2916#
+ Get list of any graphics which may be anchored from this paragraph.
+ */
+ maFlyFrames = GetFramesInNode(rWr.m_aFrames, m_rNode);
+ std::stable_sort(maFlyFrames.begin(), maFlyFrames.end(), sortswflys());
+
+ /*
+ #i18480#
+ If we are inside a frame then anything anchored inside this frame can
+ only be supported by word anchored inline ("as character"), so force
+ this in the supportable case.
+ */
+ if (rWr.m_bInWriteEscher)
+ {
+ for ( auto& aFlyFrame : maFlyFrames )
+ aFlyFrame.ForceTreatAsInline();
+ }
+
+ maFlyIter = maFlyFrames.begin();
+
+ if ( !m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPosition aPosition( m_rNode );
+ m_pCurRedline = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedline( aPosition, &m_nCurRedlinePos );
+ }
+
+ m_nCurrentSwPos = SearchNext(1);
+}
+
+static sal_Int32 lcl_getMinPos( sal_Int32 pos1, sal_Int32 pos2 )
+{
+ if ( pos1 >= 0 && pos2 >= 0 )
+ {
+ // both valid: return minimum one
+ return std::min(pos1, pos2);
+ }
+
+ // return the valid one, if any, or -1
+ return std::max(pos1, pos2);
+}
+
+sal_Int32 SwWW8AttrIter::SearchNext( sal_Int32 nStartPos )
+{
+ const OUString aText = m_rNode.GetText();
+ sal_Int32 fieldEndPos = aText.indexOf(CH_TXT_ATR_FIELDEND, nStartPos - 1);
+ // HACK: for (so far) mysterious reasons the sdtContent element closes
+ // too late in testDateFormField() unless an empty run is exported at
+ // the end of the fieldmark; hence find *also* the position after the
+ // CH_TXT_ATR_FIELDEND here
+ if (0 <= fieldEndPos && fieldEndPos < nStartPos)
+ {
+ ++fieldEndPos;
+ }
+ sal_Int32 fieldSepPos = aText.indexOf(CH_TXT_ATR_FIELDSEP, nStartPos);
+ sal_Int32 fieldStartPos = aText.indexOf(CH_TXT_ATR_FIELDSTART, nStartPos);
+ sal_Int32 formElementPos = aText.indexOf(CH_TXT_ATR_FORMELEMENT, nStartPos - 1);
+ if (0 <= formElementPos && formElementPos < nStartPos)
+ {
+ ++formElementPos; // tdf#133604 put this in its own run
+ }
+
+ const sal_Int32 pos = lcl_getMinPos(
+ lcl_getMinPos(lcl_getMinPos(fieldEndPos, fieldSepPos), fieldStartPos),
+ formElementPos );
+
+ sal_Int32 nMinPos = (pos>=0) ? pos : SAL_MAX_INT32;
+
+ // first the redline, then the attributes
+ if( m_pCurRedline )
+ {
+ const SwPosition* pEnd = m_pCurRedline->End();
+ if (pEnd->GetNode() == m_rNode)
+ {
+ const sal_Int32 i = pEnd->GetContentIndex();
+ if ( i >= nStartPos && i < nMinPos )
+ {
+ nMinPos = i;
+ }
+ }
+ }
+
+ if ( m_nCurRedlinePos < m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
+ {
+ // nCurRedlinePos point to the next redline
+ SwRedlineTable::size_type nRedLinePos = m_nCurRedlinePos;
+ if( m_pCurRedline )
+ ++nRedLinePos;
+
+ for ( ; nRedLinePos < m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++nRedLinePos )
+ {
+ const SwRangeRedline* pRedl = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedLinePos ];
+
+ auto [pStt, pEnd] = pRedl->StartEnd(); // SwPosition*
+
+ if( pStt->GetNode() == m_rNode )
+ {
+ const sal_Int32 i = pStt->GetContentIndex();
+ if( i >= nStartPos && i < nMinPos )
+ nMinPos = i;
+ }
+ else
+ break;
+
+ if( pEnd->GetNode() == m_rNode )
+ {
+ const sal_Int32 i = pEnd->GetContentIndex();
+ if( i >= nStartPos && i < nMinPos )
+ {
+ nMinPos = i;
+ }
+ }
+ }
+ }
+
+ if (mrSwFormatDrop.GetWholeWord() && nStartPos <= m_rNode.GetDropLen(0))
+ nMinPos = m_rNode.GetDropLen(0);
+ else if(nStartPos <= mrSwFormatDrop.GetChars())
+ nMinPos = mrSwFormatDrop.GetChars();
+
+ if(const SwpHints* pTextAttrs = m_rNode.GetpSwpHints())
+ {
+
+// can be optimized if we consider that the TextAttrs are sorted by start position.
+// but then we'd have to save 2 indices
+ for( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ sal_Int32 nPos = pHt->GetStart(); // first Attr characters
+ if( nPos >= nStartPos && nPos <= nMinPos )
+ nMinPos = nPos;
+
+ if( pHt->End() ) // Attr with end
+ {
+ nPos = *pHt->End(); // last Attr character + 1
+ if( nPos >= nStartPos && nPos <= nMinPos )
+ nMinPos = nPos;
+ }
+ if (pHt->HasDummyChar())
+ {
+ // pos + 1 because of CH_TXTATR in Text
+ nPos = pHt->GetStart() + 1;
+ if( nPos >= nStartPos && nPos <= nMinPos )
+ nMinPos = nPos;
+ }
+ }
+ }
+
+ if (maCharRunIter != maCharRuns.end())
+ {
+ if (maCharRunIter->mnEndPos < nMinPos)
+ nMinPos = maCharRunIter->mnEndPos;
+ IterToCurrent();
+ }
+
+ // #i2916# Check to see if there are any graphics anchored to characters in this paragraph's text.
+ sal_Int32 nNextFlyPos = 0;
+ ww8::FrameIter aTmpFlyIter = maFlyIter;
+ while (aTmpFlyIter != maFlyFrames.end() && nNextFlyPos < nStartPos)
+ {
+ const SwPosition &rAnchor = aTmpFlyIter->GetPosition();
+ nNextFlyPos = rAnchor.GetContentIndex();
+
+ ++aTmpFlyIter;
+ }
+ if (nNextFlyPos >= nStartPos && nNextFlyPos < nMinPos)
+ nMinPos = nNextFlyPos;
+
+ //nMinPos found and not going to change at this point
+
+ if (maCharRunIter != maCharRuns.end())
+ {
+ if (maCharRunIter->mnEndPos == nMinPos)
+ ++maCharRunIter;
+ }
+
+ return nMinPos;
+}
+
+void SwWW8AttrIter::OutAttr(sal_Int32 nSwPos, bool bWriteCombChars)
+{
+ m_rExport.AttrOutput().RTLAndCJKState( mbCharIsRTL, GetScript() );
+
+ /*
+ Depending on whether text is in CTL/CJK or Western, get the id of that
+ script, the idea is that the font that is actually in use to render this
+ range of text ends up in pFont
+ */
+ TypedWhichId<SvxFontItem> nFontId = GetWhichOfScript( RES_CHRATR_FONT, GetScript() );
+
+ const SvxFontItem &rParentFont =
+ static_cast<const SwTextFormatColl&>(m_rNode.GetAnyFormatColl()).GetFormatAttr(nFontId);
+ const SvxFontItem *pFont = &rParentFont;
+ const SfxPoolItem *pGrabBag = nullptr;
+
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_TXTATR_END - 1> aExportSet(*m_rNode.GetSwAttrSet().GetPool());
+
+ //The hard formatting properties that affect the entire paragraph
+ if (m_rNode.HasSwAttrSet())
+ {
+ // only copy hard attributes - bDeep = false
+ aExportSet.Set(m_rNode.GetSwAttrSet(), false/*bDeep*/);
+ // get the current font item. Use rNd.GetSwAttrSet instead of aExportSet:
+ const SvxFontItem &rNdFont = m_rNode.GetSwAttrSet().Get(nFontId);
+ pFont = &rNdFont;
+ aExportSet.ClearItem(nFontId);
+ }
+
+ //The additional hard formatting properties that affect this range in the
+ //paragraph
+ ww8::PoolItems aRangeItems;
+ if (const SwpHints* pTextAttrs = m_rNode.GetpSwpHints())
+ {
+ for( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ const sal_Int32* pEnd = pHt->End();
+
+ if (pEnd ? ( nSwPos >= pHt->GetStart() && nSwPos < *pEnd)
+ : nSwPos == pHt->GetStart() )
+ {
+ sal_uInt16 nWhich = pHt->GetAttr().Which();
+ if (nWhich == RES_TXTATR_AUTOFMT)
+ {
+ const SwFormatAutoFormat& rAutoFormat = static_cast<const SwFormatAutoFormat&>(pHt->GetAttr());
+ const std::shared_ptr<SfxItemSet>& pSet = rAutoFormat.GetStyleHandle();
+ SfxWhichIter aIter( *pSet );
+ const SfxPoolItem* pItem;
+ sal_uInt16 nWhichId = aIter.FirstWhich();
+ while( nWhichId )
+ {
+ if( SfxItemState::SET == aIter.GetItemState( false, &pItem ))
+ {
+ if (nWhichId == nFontId)
+ pFont = &(item_cast<SvxFontItem>(*pItem));
+ else if (nWhichId == RES_CHRATR_GRABBAG)
+ pGrabBag = pItem;
+ else
+ aRangeItems[nWhichId] = pItem;
+ }
+ nWhichId = aIter.NextWhich();
+ }
+ }
+ else
+ aRangeItems[nWhich] = (&(pHt->GetAttr()));
+ }
+ else if (nSwPos < pHt->GetStart())
+ break;
+ }
+ }
+// DeduplicateItems(aRangeItems);
+
+ /*
+ For #i24291# we need to explicitly remove any properties from the
+ aExportSet which a SwCharFormat would override, we can't rely on word doing
+ this for us like writer does
+ */
+ const SwFormatCharFormat *pCharFormatItem =
+ HasItem< SwFormatCharFormat >( aRangeItems, RES_TXTATR_CHARFMT );
+ if ( pCharFormatItem )
+ ClearOverridesFromSet( *pCharFormatItem, aExportSet );
+
+ // check toggle properties in DOCX output
+ if (pCharFormatItem)
+ {
+ handleToggleProperty(aExportSet, *pCharFormatItem);
+ }
+
+ // tdf#113790: AutoFormat style overwrites char style, so remove all
+ // elements from CHARFMT grab bag which are set in AUTOFMT grab bag
+ if (const SfxGrabBagItem *pAutoFmtGrabBag = dynamic_cast<const SfxGrabBagItem*>(pGrabBag))
+ {
+ if (const SfxGrabBagItem *pCharFmtGrabBag = aExportSet.GetItem<SfxGrabBagItem>(RES_CHRATR_GRABBAG, false))
+ {
+ std::unique_ptr<SfxGrabBagItem> pNewCharFmtGrabBag(pCharFmtGrabBag->Clone());
+ assert(pNewCharFmtGrabBag);
+ auto & rNewFmtMap = pNewCharFmtGrabBag->GetGrabBag();
+ for (auto const & item : pAutoFmtGrabBag->GetGrabBag())
+ {
+ if (item.second.hasValue())
+ rNewFmtMap.erase(item.first);
+ }
+ aExportSet.Put(std::move(pNewCharFmtGrabBag));
+ }
+ }
+
+ ww8::PoolItems aExportItems;
+ GetPoolItems( aExportSet, aExportItems, false );
+
+ if( m_rNode.GetpSwpHints() == nullptr )
+ m_rExport.SetCurItemSet(&aExportSet);
+
+ for ( const auto& aRangeItem : aRangeItems )
+ {
+ aExportItems[aRangeItem.first] = aRangeItem.second;
+ }
+
+ if ( !aExportItems.empty() )
+ {
+ const sw::BroadcastingModify* pOldMod = m_rExport.m_pOutFormatNode;
+ m_rExport.m_pOutFormatNode = &m_rNode;
+ m_rExport.m_aCurrentCharPropStarts.push( nSwPos );
+
+ // tdf#38778 Fix output of the font in DOC run for fields
+ const SvxFontItem * pFontToOutput = ( rParentFont != *pFont )? pFont : nullptr;
+
+ m_rExport.ExportPoolItemsToCHP( aExportItems, GetScript(), pFontToOutput, bWriteCombChars );
+
+ // HasTextItem only allowed in the above range
+ m_rExport.m_aCurrentCharPropStarts.pop();
+ m_rExport.m_pOutFormatNode = pOldMod;
+ }
+
+ if( m_rNode.GetpSwpHints() == nullptr )
+ m_rExport.SetCurItemSet(nullptr);
+
+ OSL_ENSURE( pFont, "must be *some* font associated with this txtnode" );
+ if ( pFont )
+ {
+ SvxFontItem aFont( *pFont );
+
+ if ( rParentFont != aFont )
+ m_rExport.AttrOutput().OutputItem( aFont );
+ }
+
+ // Output grab bag attributes
+ if (pGrabBag)
+ m_rExport.AttrOutput().OutputItem( *pGrabBag );
+}
+
+// Toggle Properties
+//
+// If the value of the toggle property appears at multiple levels of the style hierarchy (17.7.2), their
+// effective values shall be combined as follows:
+//
+// value_{effective} = val_{table} XOR val_{paragraph} XOR val_{character}
+//
+// If the value specified by the document defaults is true, the effective value is true.
+// Otherwise, the values are combined by a Boolean XOR as follows:
+// i.e., the effective value to be applied to the content shall be true if its effective value is true for
+// an odd number of levels of the style hierarchy.
+//
+// To prevent such logic inside output, it is required to write inline attribute tokens on content level.
+void SwWW8AttrIter::handleToggleProperty(SfxItemSet& rExportSet, const SwFormatCharFormat& rCharFormatItem)
+{
+ if (rExportSet.HasItem(RES_CHRATR_WEIGHT) || rExportSet.HasItem(RES_CHRATR_POSTURE) ||
+ rExportSet.HasItem(RES_CHRATR_CTL_WEIGHT) || rExportSet.HasItem(RES_CHRATR_CTL_POSTURE) ||
+ rExportSet.HasItem(RES_CHRATR_CONTOUR) || rExportSet.HasItem(RES_CHRATR_CASEMAP) ||
+ rExportSet.HasItem(RES_CHRATR_RELIEF) || rExportSet.HasItem(RES_CHRATR_SHADOWED) ||
+ rExportSet.HasItem(RES_CHRATR_CROSSEDOUT) || rExportSet.HasItem(RES_CHRATR_HIDDEN))
+ return;
+
+ SvxWeightItem aBoldProperty(WEIGHT_BOLD, RES_CHRATR_WEIGHT);
+ SvxPostureItem aPostureProperty(ITALIC_NORMAL, RES_CHRATR_POSTURE);
+ SvxContourItem aContouredProperty(true, RES_CHRATR_CONTOUR);
+ SvxCaseMapItem aCaseMapCapsProperty(SvxCaseMap::Uppercase, RES_CHRATR_CASEMAP);
+ SvxCaseMapItem aCaseMapSmallProperty(SvxCaseMap::SmallCaps, RES_CHRATR_CASEMAP);
+ SvxCharReliefItem aEmbossedProperty(FontRelief::Embossed, RES_CHRATR_RELIEF);
+ SvxCharReliefItem aImprintProperty(FontRelief::Engraved, RES_CHRATR_RELIEF);
+ SvxShadowedItem aShadowedProperty(true, RES_CHRATR_SHADOWED);
+ SvxCrossedOutItem aStrikeoutProperty(STRIKEOUT_SINGLE, RES_CHRATR_CROSSEDOUT);
+ SvxCharHiddenItem aHiddenProperty(true, RES_CHRATR_HIDDEN);
+
+ bool hasWeightPropertyInCharStyle = false;
+ bool hasWeightComplexPropertyInCharStyle = false;
+ bool hasPosturePropertyInCharStyle = false;
+ bool hasPostureComplexPropertyInCharStyle = false;
+ bool bHasCapsPropertyInCharStyle = false;
+ bool bHasSmallCapsPropertyInCharStyle = false;
+ bool bHasEmbossedPropertyInCharStyle = false;
+ bool bHasImprintPropertyInCharStyle = false;
+ bool hasContouredPropertyInCharStyle = false;
+ bool hasShadowedPropertyInCharStyle = false;
+ bool hasStrikeoutPropertyInCharStyle = false;
+ bool hasHiddenPropertyInCharStyle = false;
+
+
+ // get attribute flags from specified character style
+ if (const SwCharFormat* pCharFormat = rCharFormatItem.GetCharFormat())
+ {
+ if (const SfxPoolItem* pWeightItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_WEIGHT))
+ hasWeightPropertyInCharStyle = (*pWeightItem == aBoldProperty);
+
+ if (const SfxPoolItem* pWeightComplexItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_CTL_WEIGHT))
+ hasWeightComplexPropertyInCharStyle = (*pWeightComplexItem == aBoldProperty);
+
+ if (const SfxPoolItem* pPostureItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_POSTURE))
+ hasPosturePropertyInCharStyle = (*pPostureItem == aPostureProperty);
+
+ if (const SfxPoolItem* pPostureComplexItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_CTL_POSTURE))
+ hasPostureComplexPropertyInCharStyle = (*pPostureComplexItem == aPostureProperty);
+
+ if (const SfxPoolItem* pContouredItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_CONTOUR))
+ hasContouredPropertyInCharStyle = (*pContouredItem == aContouredProperty);
+
+ if (const SfxPoolItem* pShadowedItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_SHADOWED))
+ hasShadowedPropertyInCharStyle = (*pShadowedItem == aShadowedProperty);
+
+ if (const SfxPoolItem* pStrikeoutItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_CROSSEDOUT))
+ hasStrikeoutPropertyInCharStyle = (*pStrikeoutItem == aStrikeoutProperty);
+
+ if (const SfxPoolItem* pHiddenItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_HIDDEN))
+ hasHiddenPropertyInCharStyle = (*pHiddenItem == aHiddenProperty);
+
+ if (const SfxPoolItem* pCaseMapItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_CASEMAP))
+ {
+ bHasCapsPropertyInCharStyle = (*pCaseMapItem == aCaseMapCapsProperty);
+ bHasSmallCapsPropertyInCharStyle = (*pCaseMapItem == aCaseMapSmallProperty);
+ }
+
+ if (const SfxPoolItem* pReliefItem = pCharFormat->GetAttrSet().GetItem(RES_CHRATR_RELIEF))
+ {
+ bHasEmbossedPropertyInCharStyle = (*pReliefItem == aEmbossedProperty);
+ bHasImprintPropertyInCharStyle = (*pReliefItem == aImprintProperty);
+ }
+ }
+
+ // get attribute flags from specified paragraph style and apply properties if they are set in character and paragraph style
+ {
+ SwTextFormatColl& rTextColl = static_cast<SwTextFormatColl&>( m_rNode.GetAnyFormatColl() );
+ sal_uInt16 nStyle = m_rExport.m_pStyles->GetSlot( &rTextColl );
+ nStyle = ( nStyle != 0xfff ) ? nStyle : 0;
+ const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nStyle);
+ if (pFormat)
+ {
+ const SfxPoolItem* pItem;
+ if (hasWeightPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_WEIGHT)) &&
+ (*pItem == aBoldProperty))
+ rExportSet.Put(aBoldProperty);
+
+ if (hasWeightComplexPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_CTL_WEIGHT)) &&
+ *pItem == aBoldProperty)
+ {
+ rExportSet.Put(aBoldProperty, RES_CHRATR_CTL_WEIGHT);
+ }
+
+ if (hasPosturePropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_POSTURE)) &&
+ *pItem == aPostureProperty)
+ rExportSet.Put(aPostureProperty);
+
+ if (hasPostureComplexPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_CTL_POSTURE)) &&
+ *pItem == aPostureProperty)
+ {
+ rExportSet.Put(aPostureProperty, RES_CHRATR_CTL_POSTURE);
+ }
+
+ if (hasContouredPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_CONTOUR)) && *pItem == aContouredProperty)
+ rExportSet.Put(aContouredProperty);
+
+ if (hasShadowedPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_SHADOWED)) &&
+ *pItem == aShadowedProperty)
+ rExportSet.Put(aShadowedProperty);
+
+ if (hasStrikeoutPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_CROSSEDOUT)) &&
+ *pItem == aStrikeoutProperty)
+ rExportSet.Put(aStrikeoutProperty);
+
+ if (hasHiddenPropertyInCharStyle && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_HIDDEN)) &&
+ (*pItem == aHiddenProperty))
+ rExportSet.Put(aHiddenProperty);
+
+ if ((bHasCapsPropertyInCharStyle||bHasSmallCapsPropertyInCharStyle) && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_CASEMAP)))
+ {
+ if (bHasCapsPropertyInCharStyle && *pItem == aCaseMapCapsProperty)
+ rExportSet.Put(aCaseMapCapsProperty);
+ else if (bHasSmallCapsPropertyInCharStyle && *pItem == aCaseMapSmallProperty)
+ rExportSet.Put(aCaseMapSmallProperty);
+ }
+
+ if ((bHasEmbossedPropertyInCharStyle||bHasImprintPropertyInCharStyle) && (pItem = pFormat->GetAttrSet().GetItem(RES_CHRATR_RELIEF)))
+ {
+ if (bHasEmbossedPropertyInCharStyle && *pItem == aEmbossedProperty)
+ rExportSet.Put(aEmbossedProperty);
+ else if (bHasImprintPropertyInCharStyle && *pItem == aImprintProperty)
+ rExportSet.Put(aImprintProperty);
+ }
+ }
+
+ }
+}
+
+bool SwWW8AttrIter::IsWatermarkFrame()
+{
+ if (maFlyFrames.size() != 1)
+ return false;
+
+ while ( maFlyIter != maFlyFrames.end() )
+ {
+ const SdrObject* pSdrObj = maFlyIter->GetFrameFormat().FindRealSdrObject();
+
+ if (pSdrObj)
+ {
+ if (VMLExport::IsWaterMarkShape(pSdrObj->GetName()))
+ return true;
+ }
+ ++maFlyIter;
+ }
+
+ return false;
+}
+
+bool SwWW8AttrIter::IsAnchorLinkedToThisNode( SwNodeOffset nNodePos )
+{
+ if ( maFlyIter == maFlyFrames.end() )
+ return false;
+
+ /* if current node position and the anchor position are the same
+ then the frame anchor is linked to this node
+ */
+ return nNodePos == maFlyIter->GetPosition().GetNodeIndex();
+}
+
+bool SwWW8AttrIter::HasFlysAt(sal_Int32 nSwPos) const
+{
+ for (const auto& rFly : maFlyFrames)
+ {
+ const SwPosition& rAnchor = rFly.GetPosition();
+ const sal_Int32 nPos = rAnchor.GetContentIndex();
+ if (nPos == nSwPos)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+FlyProcessingState SwWW8AttrIter::OutFlys(sal_Int32 nSwPos)
+{
+ // collection point to first gather info about all of the potentially linked textboxes: to be analyzed later.
+ ww8::FrameIter linkedTextboxesIter = maFlyIter;
+ while ( linkedTextboxesIter != maFlyFrames.end() )
+ {
+ uno::Reference< drawing::XShape > xShape;
+ ww8::Frame aFrame = *linkedTextboxesIter;
+ const SdrObject* pSdrObj = aFrame.GetFrameFormat().FindRealSdrObject();
+ if( pSdrObj )
+ xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySetInfo > xPropertySetInfo;
+ if( xPropertySet.is() )
+ xPropertySetInfo = xPropertySet->getPropertySetInfo();
+ if( xPropertySetInfo.is() )
+ {
+ MSWordExportBase::LinkedTextboxInfo aLinkedTextboxInfo;
+
+ const OUString sLinkChainName = lcl_getLinkChainName(xPropertySet, xPropertySetInfo);
+
+ if( xPropertySetInfo->hasPropertyByName("ChainNextName") )
+ xPropertySet->getPropertyValue("ChainNextName") >>= aLinkedTextboxInfo.sNextChain;
+ if( xPropertySetInfo->hasPropertyByName("ChainPrevName") )
+ xPropertySet->getPropertyValue("ChainPrevName") >>= aLinkedTextboxInfo.sPrevChain;
+
+ //collect a list of linked textboxes: those with a NEXT or PREVIOUS link
+ if( !aLinkedTextboxInfo.sNextChain.isEmpty() || !aLinkedTextboxInfo.sPrevChain.isEmpty() )
+ {
+ assert( !sLinkChainName.isEmpty() );
+
+ //there are many discarded duplicates in documents - no duplicates allowed in the list, so try to find the real one.
+ //if this LinkDisplayName/ChainName already exists on a different shape...
+ // the earlier processed duplicates are thrown out unless this one can be proved as bad. (last processed duplicate usually is stored)
+ auto linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(sLinkChainName);
+ if( linkFinder != m_rExport.m_aLinkedTextboxesHelper.end() )
+ {
+ //If my NEXT/PREV targets have already been discovered, but don't match me, then assume I'm an abandoned remnant
+ // (this logic fails if both me and one of my links are duplicated, and the remnants were added first.)
+ linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(aLinkedTextboxInfo.sNextChain);
+ if( (linkFinder != m_rExport.m_aLinkedTextboxesHelper.end()) && (linkFinder->second.sPrevChain != sLinkChainName) )
+ {
+ ++linkedTextboxesIter;
+ break;
+ }
+
+ linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(aLinkedTextboxInfo.sPrevChain);
+ if( (linkFinder != m_rExport.m_aLinkedTextboxesHelper.end()) && (linkFinder->second.sNextChain != sLinkChainName) )
+ {
+ ++linkedTextboxesIter;
+ break;
+ }
+ }
+ m_rExport.m_bLinkedTextboxesHelperInitialized = false;
+ m_rExport.m_aLinkedTextboxesHelper[sLinkChainName] = aLinkedTextboxInfo;
+ }
+ }
+ ++linkedTextboxesIter;
+ }
+
+ if (maFlyIter == maFlyFrames.end())
+ {
+ // tdf#143039 postponed prevents fly duplication at end of paragraph
+ return m_rExport.AttrOutput().IsFlyProcessingPostponed() ? FLY_POSTPONED : FLY_NONE;
+ }
+
+ /*
+ #i2916#
+ May have an anchored graphic to be placed, loop through sorted array
+ and output all at this position
+ */
+ while ( maFlyIter != maFlyFrames.end() )
+ {
+ const SwPosition &rAnchor = maFlyIter->GetPosition();
+ const sal_Int32 nPos = rAnchor.GetContentIndex();
+
+ assert(nPos >= nSwPos && "a fly must get flagged as a nextAttr/CurrentPos");
+ if ( nPos != nSwPos )
+ return FLY_NOT_PROCESSED ; // We haven't processed the fly
+
+ const SdrObject* pSdrObj = maFlyIter->GetFrameFormat().FindRealSdrObject();
+
+ if (pSdrObj)
+ {
+ if (VMLExport::IsWaterMarkShape(pSdrObj->GetName()))
+ {
+ // This is a watermark object. Should be written ONLY in the header
+ if(m_rExport.m_nTextTyp == TXT_HDFT)
+ {
+ // Should write a watermark in the header
+ m_rExport.AttrOutput().OutputFlyFrame( *maFlyIter );
+ }
+ else
+ {
+ // Should not write watermark object in the main body text
+ }
+ }
+ else
+ {
+ // This is not a watermark object - write normally
+ m_rExport.AttrOutput().OutputFlyFrame( *maFlyIter );
+ }
+ }
+ else
+ {
+ // This is not a watermark object - write normally
+ m_rExport.AttrOutput().OutputFlyFrame( *maFlyIter );
+ }
+ ++maFlyIter;
+ }
+ return ( m_rExport.AttrOutput().IsFlyProcessingPostponed() ? FLY_POSTPONED : FLY_PROCESSED ) ;
+}
+
+bool SwWW8AttrIter::IsTextAttr( sal_Int32 nSwPos ) const
+{
+ // search for attrs with dummy character or content
+ if (const SwpHints* pTextAttrs = m_rNode.GetpSwpHints())
+ {
+ for (size_t i = 0; i < pTextAttrs->Count(); ++i)
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ if (nSwPos == pHt->GetStart())
+ {
+ if (pHt->HasDummyChar() || pHt->HasContent() )
+ {
+ return true;
+ }
+ }
+ else if (nSwPos < pHt->GetStart())
+ {
+ break; // sorted by start
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SwWW8AttrIter::IsExportableAttr(sal_Int32 nSwPos) const
+{
+ if (const SwpHints* pTextAttrs = m_rNode.GetpSwpHints())
+ {
+ for (size_t i = 0; i < pTextAttrs->Count(); ++i)
+ {
+ const SwTextAttr* pHt = pTextAttrs->GetSortedByEnd(i);
+ const sal_Int32 nStart = pHt->GetStart();
+ const sal_Int32 nEnd = pHt->End() ? *pHt->End() : INT_MAX;
+ if (nSwPos >= nStart && nSwPos < nEnd)
+ {
+ switch (pHt->GetAttr().Which())
+ {
+ // Metadata fields should be dynamically generated, not dumped as text.
+ case RES_TXTATR_METAFIELD:
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SwWW8AttrIter::IsDropCap( int nSwPos )
+{
+ // see if the current position falls on a DropCap
+ int nDropChars = mrSwFormatDrop.GetChars();
+ bool bWholeWord = mrSwFormatDrop.GetWholeWord();
+ if (bWholeWord)
+ {
+ const sal_Int32 nWordLen = m_rNode.GetDropLen(0);
+ if(nSwPos == nWordLen && nSwPos != 0)
+ return true;
+ }
+ else
+ {
+ if (nSwPos == nDropChars && nSwPos != 0)
+ return true;
+ }
+ return false;
+}
+
+bool SwWW8AttrIter::RequiresImplicitBookmark()
+{
+ return std::any_of(m_rExport.m_aImplicitBookmarks.begin(), m_rExport.m_aImplicitBookmarks.end(),
+ [this](const aBookmarkPair& rBookmarkPair) { return rBookmarkPair.second == m_rNode.GetIndex(); });
+}
+
+//HasItem is for the summary of the double attributes: Underline and WordlineMode as TextItems.
+// OutAttr () calls the output function, which can call HasItem() for other items at the attribute's start position.
+// Only attributes with end can be queried.
+// It searches with bDeep
+const SfxPoolItem* SwWW8AttrIter::HasTextItem( sal_uInt16 nWhich ) const
+{
+ const SfxPoolItem* pRet = nullptr;
+ const SwpHints* pTextAttrs = m_rNode.GetpSwpHints();
+ if (pTextAttrs && !m_rExport.m_aCurrentCharPropStarts.empty())
+ {
+ const sal_Int32 nTmpSwPos = m_rExport.m_aCurrentCharPropStarts.top();
+ for (size_t i = 0; i < pTextAttrs->Count(); ++i)
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ const SfxPoolItem* pItem = &pHt->GetAttr();
+ const sal_Int32 * pAtrEnd = nullptr;
+ if( nullptr != ( pAtrEnd = pHt->End() ) && // only Attr with an end
+ nTmpSwPos >= pHt->GetStart() && nTmpSwPos < *pAtrEnd )
+ {
+ if ( nWhich == pItem->Which() )
+ {
+ pRet = pItem; // found it
+ break;
+ }
+ else if( RES_TXTATR_INETFMT == pHt->Which() ||
+ RES_TXTATR_CHARFMT == pHt->Which() ||
+ RES_TXTATR_AUTOFMT == pHt->Which() )
+ {
+ const SfxItemSet* pSet = CharFormat::GetItemSet( pHt->GetAttr() );
+ const SfxPoolItem* pCharItem;
+ if ( pSet &&
+ SfxItemState::SET == pSet->GetItemState( nWhich, pHt->Which() != RES_TXTATR_AUTOFMT, &pCharItem ) )
+ {
+ pRet = pCharItem; // found it
+ break;
+ }
+ }
+ }
+ else if (nTmpSwPos < pHt->GetStart())
+ break; // nothing more to come
+ }
+ }
+ return pRet;
+}
+
+void WW8Export::GetCurrentItems(ww::bytes &rItems) const
+{
+ rItems.insert(rItems.end(), m_pO->begin(), m_pO->end());
+}
+
+const SfxPoolItem& SwWW8AttrIter::GetItem(sal_uInt16 nWhich) const
+{
+ const SfxPoolItem* pRet = HasTextItem(nWhich);
+ return pRet ? *pRet : m_rNode.SwContentNode::GetAttr(nWhich);
+}
+
+void WW8AttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 /*nPos*/, const SwFormatRuby& rRuby )
+{
+ WW8Ruby aWW8Ruby(rNode, rRuby, GetExport());
+ OUString aStr =
+ FieldString( ww::eEQ )
+ + "\\* jc"
+ + OUString::number(aWW8Ruby.GetJC())
+ + " \\* \"Font:"
+ + aWW8Ruby.GetFontFamily()
+ + "\" \\* hps"
+ + OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10)
+ + " \\o";
+ if (aWW8Ruby.GetDirective())
+ {
+ aStr += OUString::Concat(u"\\a") + OUStringChar(aWW8Ruby.GetDirective());
+ }
+ aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "(";
+ aStr += rRuby.GetText() + ")";
+
+ // The parameter separator depends on the FIB.lid
+ if ( m_rWW8Export.m_pFib->getNumDecimalSep() == '.' )
+ aStr += ",";
+ else
+ aStr += ";";
+
+ m_rWW8Export.OutputField( nullptr, ww::eEQ, aStr,
+ FieldFlags::Start | FieldFlags::CmdStart );
+}
+
+void WW8AttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/)
+{
+ m_rWW8Export.WriteChar( ')' );
+ m_rWW8Export.OutputField( nullptr, ww::eEQ, OUString(), FieldFlags::End | FieldFlags::Close );
+}
+
+OUString AttributeOutputBase::ConvertURL( const OUString& rUrl, bool bAbsoluteOut )
+{
+ OUString sURL = rUrl;
+
+ INetURLObject anAbsoluteParent(m_sBaseURL);
+ OUString sConvertedParent = INetURLObject::GetScheme( anAbsoluteParent.GetProtocol() ) + anAbsoluteParent.GetURLPath();
+ OUString sParentPath = sConvertedParent.isEmpty() ? m_sBaseURL : sConvertedParent;
+
+ if ( bAbsoluteOut )
+ {
+ INetURLObject anAbsoluteNew;
+
+ if ( anAbsoluteParent.GetNewAbsURL( rUrl, &anAbsoluteNew ) )
+ sURL = anAbsoluteNew.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ else
+ {
+ OUString sToConvert = rUrl.replaceAll( "\\", "/" );
+ INetURLObject aURL( sToConvert );
+ sToConvert = INetURLObject::GetScheme( aURL.GetProtocol() ) + aURL.GetURLPath();
+ OUString sRelative = INetURLObject::GetRelURL( sParentPath, sToConvert, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::NONE );
+ if ( !sRelative.isEmpty() )
+ sURL = sRelative;
+ }
+
+ return sURL;
+}
+
+bool AttributeOutputBase::AnalyzeURL( const OUString& rUrl, const OUString& /*rTarget*/, OUString* pLinkURL, OUString* pMark )
+{
+ bool bBookMarkOnly = false;
+
+ OUString sMark;
+ OUString sURL;
+
+ if ( rUrl.getLength() > 1 && rUrl[0] == '#' )
+ {
+ sMark = BookmarkToWriter( rUrl.subView(1) );
+
+ const sal_Int32 nPos = sMark.lastIndexOf( cMarkSeparator );
+
+ const OUString sRefType(nPos>=0 && nPos+1<sMark.getLength() ?
+ sMark.copy(nPos+1).replaceAll(" ", "") :
+ OUString());
+
+ // #i21465# Only interested in outline references
+ if ( !sRefType.isEmpty() &&
+ (sRefType == "outline" || sRefType == "graphic" || sRefType == "frame" || sRefType == "ole" || sRefType == "region" || sRefType == "table") )
+ {
+ for ( const auto& rBookmarkPair : GetExport().m_aImplicitBookmarks )
+ {
+ if ( rBookmarkPair.first == sMark )
+ {
+ sMark = "_toc" + OUString::number( sal_Int32(rBookmarkPair.second) );
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ INetURLObject aURL( rUrl, INetProtocol::NotValid );
+ sURL = aURL.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
+ sMark = aURL.GetMark( INetURLObject::DecodeMechanism::NONE );
+ INetProtocol aProtocol = aURL.GetProtocol();
+
+ if ( aProtocol == INetProtocol::File || aProtocol == INetProtocol::NotValid )
+ {
+ // INetProtocol::NotValid - may be a relative link
+ bool bExportRelative = officecfg::Office::Common::Save::URL::FileSystem::get();
+ sURL = ConvertURL( rUrl, !bExportRelative );
+ }
+ }
+
+ if ( !sMark.isEmpty() && sURL.isEmpty() )
+ bBookMarkOnly = true;
+
+ *pMark = sMark;
+ *pLinkURL = sURL;
+ return bBookMarkOnly;
+}
+
+bool WW8AttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark )
+{
+ bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );
+
+ OUString sURL = *pLinkURL;
+
+ if ( !sURL.isEmpty() )
+ sURL = URIHelper::simpleNormalizedMakeRelative( m_rWW8Export.GetWriter().GetBaseURL(), sURL );
+
+ if (bBookMarkOnly)
+ {
+ sURL = FieldString(ww::eHYPERLINK);
+ *pMark = GetExport().BookmarkToWord(*pMark);
+ }
+ else
+ sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\"";
+
+ if ( !pMark->isEmpty() )
+ sURL += " \\l \"" + *pMark + "\"";
+
+ if ( !rTarget.isEmpty() )
+ sURL += " \\n " + rTarget;
+
+ *pLinkURL = sURL;
+
+ return bBookMarkOnly;
+}
+
+void WW8AttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos )
+{
+ m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName));
+ m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName));
+}
+
+bool WW8AttributeOutput::StartURL( const OUString &rUrl, const OUString &rTarget )
+{
+ INetURLObject aURL( rUrl );
+ OUString sURL;
+ OUString sMark;
+
+ bool bBookMarkOnly = AnalyzeURL( rUrl, rTarget, &sURL, &sMark );
+
+ m_rWW8Export.OutputField( nullptr, ww::eHYPERLINK, sURL, FieldFlags::Start | FieldFlags::CmdStart );
+
+ // write the reference to the "picture" structure
+ sal_uInt64 nDataStt = m_rWW8Export.m_pDataStrm->Tell();
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() );
+
+ // WinWord 2000 doesn't write this - so it's a temp solution by W97 ?
+ m_rWW8Export.WriteChar( 0x01 );
+
+ static sal_uInt8 aArr1[] = {
+ 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation
+
+ 0x06, 0x08, 0x01, // sprmCFData
+ 0x55, 0x08, 0x01, // sprmCFSpec
+ 0x02, 0x08, 0x01 // sprmCFFieldVanish
+ };
+ sal_uInt8* pDataAdr = aArr1 + 2;
+ Set_UInt32( pDataAdr, nDataStt );
+
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), sizeof( aArr1 ), aArr1 );
+
+ m_rWW8Export.OutputField( nullptr, ww::eHYPERLINK, sURL, FieldFlags::CmdEnd );
+
+ // now write the picture structure
+ sURL = aURL.GetURLNoMark();
+
+ // Compare the URL written by AnalyzeURL with the original one to see if
+ // the output URL is absolute or relative.
+ OUString sRelativeURL;
+ if ( !rUrl.isEmpty() )
+ sRelativeURL = URIHelper::simpleNormalizedMakeRelative( m_rWW8Export.GetWriter().GetBaseURL(), rUrl );
+ bool bAbsolute = sRelativeURL == rUrl;
+
+ static sal_uInt8 aURLData1[] = {
+ 0,0,0,0, // len of struct
+ 0x44,0, // the start of "next" data
+ 0,0,0,0,0,0,0,0,0,0, // PIC-Structure!
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // |
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // |
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // |
+ 0,0,0,0, // /
+ };
+ static sal_uInt8 MAGIC_A[] = {
+ // start of "next" data
+ 0xD0,0xC9,0xEA,0x79,0xF9,0xBA,0xCE,0x11,
+ 0x8C,0x82,0x00,0xAA,0x00,0x4B,0xA9,0x0B
+ };
+
+ m_rWW8Export.m_pDataStrm->WriteBytes(aURLData1, sizeof(aURLData1));
+ /* Write HFD Structure */
+ sal_uInt8 nAnchor = 0x00;
+ if ( !sMark.isEmpty() )
+ nAnchor = 0x08;
+ m_rWW8Export.m_pDataStrm->WriteUChar(nAnchor); // HFDBits
+ m_rWW8Export.m_pDataStrm->WriteBytes(MAGIC_A, sizeof(MAGIC_A)); //clsid
+
+ /* Write Hyperlink Object see [MS-OSHARED] spec*/
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, 0x00000002);
+ sal_uInt32 nFlag = bBookMarkOnly ? 0 : 0x01;
+ if ( bAbsolute )
+ nFlag |= 0x02;
+ if ( !sMark.isEmpty() )
+ nFlag |= 0x08;
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, nFlag );
+
+ INetProtocol eProto = aURL.GetProtocol();
+ if ( eProto == INetProtocol::File || eProto == INetProtocol::Smb )
+ {
+ // version 1 (for a document)
+
+ static sal_uInt8 MAGIC_C[] = {
+ 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x00
+ };
+
+ static sal_uInt8 MAGIC_D[] = {
+ 0xFF, 0xFF, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ // save the links to files as relative
+ sURL = URIHelper::simpleNormalizedMakeRelative( m_rWW8Export.GetWriter().GetBaseURL(), sURL );
+ if ( eProto == INetProtocol::File && sURL.startsWith( "/" ) )
+ sURL = aURL.PathToFileName();
+
+ // special case for the absolute windows names
+ // (convert '/c:/foo/bar.doc' into 'c:\foo\bar.doc')
+ if (sURL.getLength()>=3)
+ {
+ const sal_Unicode aDrive = sURL[1];
+ if ( sURL[0]=='/' && sURL[2]==':' &&
+ ( (aDrive>='A' && aDrive<='Z' ) || (aDrive>='a' && aDrive<='z') ) )
+ {
+ sURL = sURL.copy(1).replaceAll("/", "\\");
+ }
+ }
+
+ // n#261623 convert smb notation to '\\'
+ const char pSmb[] = "smb://";
+ if ( eProto == INetProtocol::Smb && sURL.startsWith( pSmb ) )
+ {
+ sURL = sURL.copy( sizeof(pSmb)-3 ).replaceAll( "/", "\\" );
+ }
+
+ m_rWW8Export.m_pDataStrm->WriteBytes(MAGIC_C, sizeof(MAGIC_C));
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, sURL.getLength()+1 );
+ SwWW8Writer::WriteString8( *m_rWW8Export.m_pDataStrm, sURL, true,
+ RTL_TEXTENCODING_MS_1252 );
+ m_rWW8Export.m_pDataStrm->WriteBytes(MAGIC_D, sizeof(MAGIC_D));
+
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, 2*sURL.getLength() + 6 );
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, 2*sURL.getLength() );
+ SwWW8Writer::WriteShort( *m_rWW8Export.m_pDataStrm, 3 );
+ SwWW8Writer::WriteString16( *m_rWW8Export.m_pDataStrm, sURL, false );
+ }
+ else if ( eProto != INetProtocol::NotValid )
+ {
+ // version 2 (simple url)
+ // and write some data to the data stream, but don't ask
+ // what the data mean, except for the URL.
+ // The First piece is the WW8_PIC structure.
+ static sal_uInt8 MAGIC_B[] = {
+ 0xE0,0xC9,0xEA,0x79,0xF9,0xBA,0xCE,0x11,
+ 0x8C,0x82,0x00,0xAA,0x00,0x4B,0xA9,0x0B
+ };
+
+ m_rWW8Export.m_pDataStrm->WriteBytes(MAGIC_B, sizeof(MAGIC_B));
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, 2 * ( sURL.getLength() + 1 ) );
+ SwWW8Writer::WriteString16( *m_rWW8Export.m_pDataStrm, sURL, true );
+ }
+
+ if ( !sMark.isEmpty() )
+ {
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, sMark.getLength()+1 );
+ SwWW8Writer::WriteString16( *m_rWW8Export.m_pDataStrm, sMark, true );
+ }
+ SwWW8Writer::WriteLong( *m_rWW8Export.m_pDataStrm, nDataStt,
+ m_rWW8Export.m_pDataStrm->Tell() - nDataStt );
+
+ return true;
+}
+
+bool WW8AttributeOutput::EndURL(bool const)
+{
+ m_rWW8Export.OutputField( nullptr, ww::eHYPERLINK, OUString(), FieldFlags::Close );
+
+ return true;
+}
+
+OUString BookmarkToWriter(std::u16string_view rBookmark)
+{
+ return INetURLObject::decode(rBookmark,
+ INetURLObject::DecodeMechanism::Unambiguous, RTL_TEXTENCODING_ASCII_US);
+}
+
+void SwWW8AttrIter::OutSwFormatRefMark(const SwFormatRefMark& rAttr)
+{
+ if(m_rExport.HasRefToAttr(rAttr.GetRefName()))
+ m_rExport.AppendBookmark( m_rExport.GetBookmarkName( REF_SETREFATTR,
+ &rAttr.GetRefName(), 0 ));
+}
+
+void SwWW8AttrIter::SplitRun( sal_Int32 nSplitEndPos )
+{
+ auto aIter = std::find_if(maCharRuns.begin(), maCharRuns.end(),
+ [nSplitEndPos](const CharRunEntry& rCharRun) { return rCharRun.mnEndPos >= nSplitEndPos; });
+ if (aIter == maCharRuns.end() || aIter->mnEndPos == nSplitEndPos)
+ return;
+
+ CharRunEntry aNewEntry = *aIter;
+ aIter->mnEndPos = nSplitEndPos;
+ maCharRuns.insert( ++aIter, aNewEntry);
+ maCharRunIter = maCharRuns.begin();
+ IterToCurrent();
+ m_nCurrentSwPos = SearchNext(1);
+}
+
+void WW8AttributeOutput::FieldVanish(const OUString& rText, ww::eField /*eType*/, OUString const*const /*pBookmarkName*/)
+{
+ ww::bytes aItems;
+ m_rWW8Export.GetCurrentItems( aItems );
+
+ // sprmCFFieldVanish
+ SwWW8Writer::InsUInt16( aItems, NS_sprm::CFFldVanish::val );
+ aItems.push_back( 1 );
+
+ sal_uInt16 nStt_sprmCFSpec = aItems.size();
+
+ // sprmCFSpec -- fSpec-Attribute true
+ SwWW8Writer::InsUInt16( aItems, 0x855 );
+ aItems.push_back( 1 );
+
+ m_rWW8Export.WriteChar( '\x13' );
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), aItems.size(),
+ aItems.data() );
+ m_rWW8Export.OutSwString(rText, 0, rText.getLength());
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), nStt_sprmCFSpec,
+ aItems.data() );
+ m_rWW8Export.WriteChar( '\x15' );
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), aItems.size(),
+ aItems.data() );
+}
+
+void AttributeOutputBase::TOXMark( const SwTextNode& rNode, const SwTOXMark& rAttr )
+{
+ // it's a field; so get the Text from the Node and build the field
+ OUString sText;
+ ww::eField eType = ww::eNONE;
+
+ const SwTextTOXMark& rTextTOXMark = *rAttr.GetTextTOXMark();
+ const sal_Int32* pTextEnd = rTextTOXMark.End();
+ if ( pTextEnd ) // has range?
+ {
+ sText = rNode.GetExpandText(nullptr, rTextTOXMark.GetStart(),
+ *pTextEnd - rTextTOXMark.GetStart() );
+ }
+ else
+ sText = rAttr.GetAlternativeText();
+
+ OUString sUserTypeName;
+ auto aType = rAttr.GetTOXType()->GetType();
+ // user index mark, it needs XE with \f
+ if ( TOX_USER == aType )
+ {
+ sUserTypeName = rAttr.GetTOXType()->GetTypeName();
+ if ( !sUserTypeName.isEmpty() )
+ aType = TOX_INDEX;
+ }
+
+ switch ( aType )
+ {
+ case TOX_INDEX:
+ eType = ww::eXE;
+ if ( !rAttr.GetPrimaryKey().isEmpty() )
+ {
+ if ( !rAttr.GetSecondaryKey().isEmpty() )
+ {
+ sText = rAttr.GetSecondaryKey() + ":" + sText;
+ }
+
+ sText = rAttr.GetPrimaryKey() + ":" + sText;
+ }
+ sText = " XE \"" + sText + "\" ";
+
+ if (!sUserTypeName.isEmpty())
+ {
+ sText += "\\f \"" + sUserTypeName + "\" ";
+ }
+ break;
+
+ case TOX_USER:
+ sText += "\" \\f \"" + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *rAttr.GetTOXType() ) ));
+ [[fallthrough]];
+ case TOX_CONTENT:
+ {
+ eType = ww::eTC;
+ sText = " TC \"" + sText;
+ sal_uInt16 nLvl = rAttr.GetLevel();
+ if (nLvl > WW8ListManager::nMaxLevel)
+ nLvl = WW8ListManager::nMaxLevel;
+
+ sText += "\" \\l " + OUString::number(nLvl) + " ";
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "Unhandled option for toc export" );
+ break;
+ }
+
+ if (!sText.isEmpty())
+ {
+ OUString const* pBookmarkName(nullptr);
+ if (auto const it = GetExport().m_TOXMarkBookmarksByTOXMark.find(&rAttr);
+ it != GetExport().m_TOXMarkBookmarksByTOXMark.end())
+ {
+ pBookmarkName = &it->second;
+ }
+ FieldVanish(sText, eType, pBookmarkName);
+ }
+}
+
+int SwWW8AttrIter::OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos)
+{
+ int nRet = 0;
+ if ( const SwpHints* pTextAttrs = m_rNode.GetpSwpHints() )
+ {
+ m_rExport.m_aCurrentCharPropStarts.push( nPos );
+ const sal_Int32* pEnd;
+ // first process ends of attributes with extent
+ for (size_t i = 0; i < pTextAttrs->Count(); ++i)
+ {
+ const SwTextAttr* pHt = pTextAttrs->GetSortedByEnd(i);
+ const SfxPoolItem* pItem = &pHt->GetAttr();
+ switch ( pItem->Which() )
+ {
+ case RES_TXTATR_INETFMT:
+ pEnd = pHt->End();
+ if (nPos == *pEnd && nPos != pHt->GetStart())
+ {
+ if (m_rExport.AttrOutput().EndURL(nPos == m_rNode.Len()))
+ --nRet;
+ }
+ break;
+ case RES_TXTATR_REFMARK:
+ pEnd = pHt->End();
+ if (nullptr != pEnd && nPos == *pEnd && nPos != pHt->GetStart())
+ {
+ OutSwFormatRefMark(*static_cast<const SwFormatRefMark*>(pItem));
+ --nRet;
+ }
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ pEnd = pHt->End();
+ if (nPos == *pEnd && nPos != pHt->GetStart())
+ {
+ m_rExport.AttrOutput().EndRuby(rNode, nPos);
+ --nRet;
+ }
+ break;
+ }
+ if (nPos < pHt->GetAnyEnd())
+ break; // sorted by end
+ }
+ for ( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ const SfxPoolItem* pItem = &pHt->GetAttr();
+ switch ( pItem->Which() )
+ {
+ case RES_TXTATR_INETFMT:
+ if ( nPos == pHt->GetStart() )
+ {
+ const SwFormatINetFormat *rINet = static_cast< const SwFormatINetFormat* >( pItem );
+ if ( m_rExport.AttrOutput().StartURL( rINet->GetValue(), rINet->GetTargetFrame() ) )
+ ++nRet;
+ }
+ pEnd = pHt->End();
+ if (nPos == *pEnd && nPos == pHt->GetStart())
+ { // special case: empty must be handled here
+ if (m_rExport.AttrOutput().EndURL(nPos == m_rNode.Len()))
+ --nRet;
+ }
+ break;
+ case RES_TXTATR_REFMARK:
+ if ( nPos == pHt->GetStart() )
+ {
+ OutSwFormatRefMark( *static_cast< const SwFormatRefMark* >( pItem ) );
+ ++nRet;
+ }
+ pEnd = pHt->End();
+ if (nullptr != pEnd && nPos == *pEnd && nPos == pHt->GetStart())
+ { // special case: empty TODO: is this possible or would empty one have pEnd null?
+ OutSwFormatRefMark( *static_cast< const SwFormatRefMark* >( pItem ) );
+ --nRet;
+ }
+ break;
+ case RES_TXTATR_TOXMARK:
+ if ( nPos == pHt->GetStart() )
+ m_rExport.AttrOutput().TOXMark( m_rNode, *static_cast< const SwTOXMark* >( pItem ) );
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ if ( nPos == pHt->GetStart() )
+ {
+ m_rExport.AttrOutput().StartRuby( m_rNode, nPos, *static_cast< const SwFormatRuby* >( pItem ) );
+ ++nRet;
+ }
+ pEnd = pHt->End();
+ if (nPos == *pEnd && nPos == pHt->GetStart())
+ { // special case: empty must be handled here
+ m_rExport.AttrOutput().EndRuby( m_rNode, nPos );
+ --nRet;
+ }
+ break;
+ }
+ if (nPos < pHt->GetStart())
+ break; // sorted by start
+ }
+ m_rExport.m_aCurrentCharPropStarts.pop(); // HasTextItem only allowed in the above range
+ }
+ return nRet;
+}
+
+bool SwWW8AttrIter::IncludeEndOfParaCRInRedlineProperties( sal_Int32 nEnd ) const
+{
+ // search next Redline
+ for( SwRedlineTable::size_type nPos = m_nCurRedlinePos;
+ nPos < m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++nPos )
+ {
+ const SwRangeRedline *pRange = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[nPos];
+ const SwPosition* pEnd = pRange->End();
+ const SwPosition* pStart = pRange->Start();
+ bool bBreak = true;
+ // In word the paragraph end marker is a real character, in writer it is not.
+ // Here we find out if the para end marker we will emit is affected by
+ // redlining, in which case it must be included by the range of character
+ // attributes that contains the redlining information.
+ if (pEnd->GetNode() == m_rNode)
+ {
+ if (pEnd->GetContentIndex() == nEnd)
+ {
+ // This condition detects if the pseudo-char we will export
+ // should be explicitly included by the redlining char
+ // properties on this node because the redlining ends right
+ // after it
+ return true;
+ }
+ bBreak = false;
+ }
+ if (pStart->GetNode() == m_rNode)
+ {
+ if (pStart->GetContentIndex() == nEnd)
+ {
+ // This condition detects if the pseudo-char we will export
+ // should be explicitly included by the redlining char
+ // properties on this node because the redlining starts right
+ // before it
+ return true;
+ }
+ bBreak = false;
+ }
+ if (pStart->GetNodeIndex()-1 == m_rNode.GetIndex())
+ {
+ if (pStart->GetContentIndex() == 0)
+ {
+ // This condition detects if the pseudo-char we will export
+ // should be implicitly excluded by the redlining char
+ // properties starting on the next node.
+ return true;
+ }
+ bBreak = false;
+ }
+
+ if (bBreak)
+ break;
+ }
+ return false;
+}
+
+const SwRedlineData* SwWW8AttrIter::GetParagraphLevelRedline( )
+{
+ m_pCurRedline = nullptr;
+
+ // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos'
+ for(SwRangeRedline* pRedl : m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ const SwPosition* pCheckedStt = pRedl->Start();
+
+ if( pCheckedStt->GetNode() == m_rNode )
+ {
+ // Maybe add here a check that also the start & end of the redline is the entire paragraph
+
+ // Only return if this is a paragraph formatting redline
+ if (pRedl->GetType() == RedlineType::ParagraphFormat)
+ {
+ // write data of this redline
+ m_pCurRedline = pRedl;
+ return &( m_pCurRedline->GetRedlineData() );
+ }
+ }
+ }
+ return nullptr;
+}
+
+const SwRedlineData* SwWW8AttrIter::GetRunLevelRedline( sal_Int32 nPos )
+{
+ if( m_pCurRedline )
+ {
+ const SwPosition* pEnd = m_pCurRedline->End();
+ if (pEnd->GetNode() != m_rNode || pEnd->GetContentIndex() > nPos)
+ {
+ switch( m_pCurRedline->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Delete:
+ case RedlineType::Format:
+ // write data of this redline
+ return &( m_pCurRedline->GetRedlineData() );
+ default:
+ break;
+ }
+ }
+ m_pCurRedline = nullptr;
+ ++m_nCurRedlinePos;
+ }
+
+ assert(!m_pCurRedline);
+ // search next Redline
+ for( ; m_nCurRedlinePos < m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size();
+ ++m_nCurRedlinePos )
+ {
+ const SwRangeRedline* pRedl = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nCurRedlinePos ];
+
+ auto [pStt, pEnd] = pRedl->StartEnd(); // SwPosition*
+
+ if( pStt->GetNode() == m_rNode )
+ {
+ if( pStt->GetContentIndex() >= nPos )
+ {
+ if( pStt->GetContentIndex() == nPos )
+ {
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Delete:
+ case RedlineType::Format:
+ // write data of this redline
+ m_pCurRedline = pRedl;
+ return &( m_pCurRedline->GetRedlineData() );
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ if( pEnd->GetNode() == m_rNode &&
+ pEnd->GetContentIndex() < nPos )
+ {
+ m_pCurRedline = pRedl;
+ break;
+ }
+ }
+ return nullptr;
+}
+
+SvxFrameDirection MSWordExportBase::GetCurrentPageDirection() const
+{
+ const SwFrameFormat &rFormat = m_pCurrentPageDesc
+ ? m_pCurrentPageDesc->GetMaster()
+ : m_rDoc.GetPageDesc( 0 ).GetMaster();
+ return rFormat.GetFrameDir().GetValue();
+}
+
+SvxFrameDirection MSWordExportBase::GetDefaultFrameDirection( ) const
+{
+ SvxFrameDirection nDir = SvxFrameDirection::Environment;
+
+ if ( m_bOutPageDescs )
+ nDir = GetCurrentPageDirection( );
+ else if ( m_pOutFormatNode )
+ {
+ if ( m_bOutFlyFrameAttrs ) //frame
+ {
+ nDir = TrueFrameDirection( *static_cast< const SwFrameFormat * >(m_pOutFormatNode) );
+ }
+ else if ( auto pNd = dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) ) //paragraph
+ {
+ SwPosition aPos( *pNd );
+ nDir = m_rDoc.GetTextDirection( aPos );
+ }
+ else if ( dynamic_cast< const SwTextFormatColl *>( m_pOutFormatNode ) != nullptr )
+ {
+ if ( MsLangId::isRightToLeft( GetAppLanguage()) )
+ nDir = SvxFrameDirection::Horizontal_RL_TB;
+ else
+ nDir = SvxFrameDirection::Horizontal_LR_TB; //what else can we do :-(
+ }
+ }
+
+ if ( nDir == SvxFrameDirection::Environment )
+ {
+ // fdo#44029 put direction right when the locale are RTL.
+ if( MsLangId::isRightToLeft( GetAppLanguage()) )
+ nDir = SvxFrameDirection::Horizontal_RL_TB;
+ else
+ nDir = SvxFrameDirection::Horizontal_LR_TB; //Set something
+ }
+
+ return nDir;
+}
+
+SvxFrameDirection MSWordExportBase::TrueFrameDirection( const SwFrameFormat &rFlyFormat ) const
+{
+ const SwFrameFormat *pFlyFormat = &rFlyFormat;
+ const SvxFrameDirectionItem* pItem = nullptr;
+ while ( pFlyFormat )
+ {
+ pItem = &pFlyFormat->GetFrameDir();
+ if ( SvxFrameDirection::Environment == pItem->GetValue() )
+ {
+ pItem = nullptr;
+ const SwFormatAnchor* pAnchor = &pFlyFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
+ pAnchor->GetAnchorNode() )
+ {
+ pFlyFormat = pAnchor->GetAnchorNode()->GetFlyFormat();
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+
+ SvxFrameDirection nRet;
+ if ( pItem )
+ nRet = pItem->GetValue();
+ else
+ nRet = GetCurrentPageDirection();
+
+ OSL_ENSURE( nRet != SvxFrameDirection::Environment, "leaving with environment direction" );
+ return nRet;
+}
+
+const SvxBrushItem* WW8Export::GetCurrentPageBgBrush() const
+{
+ const SwFrameFormat &rFormat = m_pCurrentPageDesc
+ ? m_pCurrentPageDesc->GetMaster()
+ : m_rDoc.GetPageDesc(0).GetMaster();
+
+ //If not set, or "no fill", get real bg
+ const SvxBrushItem* pRet = rFormat.GetItemIfSet(RES_BACKGROUND);
+
+ if (!pRet ||
+ (!pRet->GetGraphic() && pRet->GetColor() == COL_TRANSPARENT))
+ {
+ pRet = &m_rDoc.GetAttrPool().GetDefaultItem(RES_BACKGROUND);
+ }
+ return pRet;
+}
+
+std::shared_ptr<SvxBrushItem> WW8Export::TrueFrameBgBrush(const SwFrameFormat &rFlyFormat) const
+{
+ const SwFrameFormat *pFlyFormat = &rFlyFormat;
+ const SvxBrushItem* pRet = nullptr;
+
+ while (pFlyFormat)
+ {
+ //If not set, or "no fill", get real bg
+ pRet = pFlyFormat->GetItemIfSet(RES_BACKGROUND);
+ if (!pRet || (!pRet->GetGraphic() &&
+ pRet->GetColor() == COL_TRANSPARENT))
+ {
+ pRet = nullptr;
+ const SwFormatAnchor* pAnchor = &pFlyFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
+ pAnchor->GetAnchorNode())
+ {
+ pFlyFormat =
+ pAnchor->GetAnchorNode()->GetFlyFormat();
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+
+ if (!pRet)
+ pRet = GetCurrentPageBgBrush();
+
+ const Color aTmpColor( COL_WHITE );
+ std::shared_ptr<SvxBrushItem> aRet(std::make_shared<SvxBrushItem>(aTmpColor, RES_BACKGROUND));
+
+ if (pRet && (pRet->GetGraphic() ||( pRet->GetColor() != COL_TRANSPARENT)))
+ {
+ aRet.reset(pRet->Clone());
+ }
+
+ return aRet;
+}
+
+/*
+Convert characters that need to be converted, the basic replacements and the
+ridiculously complicated title case attribute mapping to hardcoded upper case
+because word doesn't have the feature
+*/
+OUString SwWW8AttrIter::GetSnippet(const OUString &rStr, sal_Int32 nCurrentPos,
+ sal_Int32 nLen) const
+{
+ if (!nLen)
+ return OUString();
+
+ OUString aSnippet(rStr.copy(nCurrentPos, nLen));
+ // 0x0a ( Hard Line Break ) -> 0x0b
+ // 0xad ( soft hyphen ) -> 0x1f
+ // 0x2011 ( hard hyphen ) -> 0x1e
+ aSnippet = aSnippet.replace(0x0A, 0x0B);
+ aSnippet = aSnippet.replace(CHAR_HARDHYPHEN, 0x1e);
+ aSnippet = aSnippet.replace(CHAR_SOFTHYPHEN, 0x1f);
+ // Ignore the dummy character at the end of content controls.
+ static sal_Unicode const aForbidden[] = {
+ CH_TXTATR_BREAKWORD,
+ 0
+ };
+ aSnippet = comphelper::string::removeAny(aSnippet, aForbidden);
+
+ m_rExport.m_aCurrentCharPropStarts.push( nCurrentPos );
+ const SfxPoolItem &rItem = GetItem(RES_CHRATR_CASEMAP);
+
+ if (SvxCaseMap::Capitalize == static_cast<const SvxCaseMapItem&>(rItem).GetValue())
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_uInt16 nScriptType = g_pBreakIt->GetBreakIter()->getScriptType(aSnippet, 0);
+
+ LanguageType nLanguage;
+ switch (nScriptType)
+ {
+ case i18n::ScriptType::ASIAN:
+ nLanguage = static_cast<const SvxLanguageItem&>(GetItem(RES_CHRATR_CJK_LANGUAGE)).GetLanguage();
+ break;
+ case i18n::ScriptType::COMPLEX:
+ nLanguage = static_cast<const SvxLanguageItem&>(GetItem(RES_CHRATR_CTL_LANGUAGE)).GetLanguage();
+ break;
+ case i18n::ScriptType::LATIN:
+ default:
+ nLanguage = static_cast<const SvxLanguageItem&>(GetItem(RES_CHRATR_LANGUAGE)).GetLanguage();
+ break;
+ }
+
+ SvxFont aFontHelper;
+ aFontHelper.SetCaseMap(SvxCaseMap::Capitalize);
+ aFontHelper.SetLanguage(nLanguage);
+ aSnippet = aFontHelper.CalcCaseMap(aSnippet);
+
+ //If we weren't at the begin of a word undo the case change.
+ //not done before doing the casemap because the sequence might start
+ //with whitespace
+ if (!g_pBreakIt->GetBreakIter()->isBeginWord(
+ rStr, nCurrentPos, g_pBreakIt->GetLocale(nLanguage),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES ) )
+ {
+ aSnippet = OUStringChar(rStr[nCurrentPos]) + aSnippet.subView(1);
+ }
+ }
+ m_rExport.m_aCurrentCharPropStarts.pop();
+
+ return aSnippet;
+}
+
+/** Delivers the right paragraph style
+
+ Because of the different style handling for delete operations,
+ the track changes have to be analysed. A deletion, starting in paragraph A
+ with style A, ending in paragraph B with style B, needs a hack.
+*/
+static SwTextFormatColl& lcl_getFormatCollection( MSWordExportBase& rExport, const SwTextNode* pTextNode )
+{
+ SwRedlineTable::size_type nPos = 0;
+ SwRedlineTable::size_type nMax = rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size();
+ while( nPos < nMax )
+ {
+ const SwRangeRedline* pRedl = rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nPos++ ];
+ auto [pStt, pEnd] = pRedl->StartEnd(); // SwPosition*
+ // Looking for deletions, which ends in current pTextNode
+ if( RedlineType::Delete == pRedl->GetRedlineData().GetType() &&
+ pEnd->GetNode() == *pTextNode && pStt->GetNode() != *pTextNode &&
+ pStt->GetNode().IsTextNode() )
+ {
+ pTextNode = pStt->GetNode().GetTextNode();
+ nMax = nPos;
+ nPos = 0;
+ }
+ }
+ return static_cast<SwTextFormatColl&>( pTextNode->GetAnyFormatColl() );
+}
+
+void WW8AttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop &rSwFormatDrop, sal_uInt16 nStyle,
+ ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
+{
+ short nDropLines = rSwFormatDrop.GetLines();
+ short nDistance = rSwFormatDrop.GetDistance();
+ int rFontHeight, rDropHeight, rDropDescent;
+
+ SVBT16 nSty;
+ ShortToSVBT16( nStyle, nSty );
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // Style #
+
+ m_rWW8Export.InsUInt16( NS_sprm::PPc::val ); // Alignment (sprmPPc)
+ m_rWW8Export.m_pO->push_back( 0x20 );
+
+ m_rWW8Export.InsUInt16( NS_sprm::PWr::val ); // Wrapping (sprmPWr)
+ m_rWW8Export.m_pO->push_back( 0x02 );
+
+ m_rWW8Export.InsUInt16( NS_sprm::PDcs::val ); // Dropcap (sprmPDcs)
+ int nDCS = ( nDropLines << 3 ) | 0x01;
+ m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( nDCS ) );
+
+ m_rWW8Export.InsUInt16( NS_sprm::PDxaFromText::val ); // Distance from text (sprmPDxaFromText)
+ m_rWW8Export.InsUInt16( nDistance );
+
+ if ( rNode.GetDropSize( rFontHeight, rDropHeight, rDropDescent ) )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::PDyaLine::val ); // Line spacing
+ m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( -rDropHeight ) );
+ m_rWW8Export.InsUInt16( 0 );
+ }
+
+ m_rWW8Export.WriteCR( pTextNodeInfoInner );
+
+ if ( pTextNodeInfo )
+ {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", pTextNodeInfo->toString());
+#endif
+ TableInfoCell( pTextNodeInfoInner );
+ }
+
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+ m_rWW8Export.m_pO->clear();
+
+ if ( rNode.GetDropSize( rFontHeight, rDropHeight, rDropDescent ) )
+ {
+ const SwCharFormat *pSwCharFormat = rSwFormatDrop.GetCharFormat();
+ if ( pSwCharFormat )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
+ m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pSwCharFormat ) );
+ }
+
+ m_rWW8Export.InsUInt16( NS_sprm::CHpsPos::val ); // Lower the chars
+ m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( -((nDropLines - 1)*rDropDescent) / 10 ) );
+
+ m_rWW8Export.InsUInt16( NS_sprm::CHps::val ); // Font Size
+ m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( rFontHeight / 10 ) );
+ }
+
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+ m_rWW8Export.m_pO->clear();
+}
+
+sal_Int32 MSWordExportBase::GetNextPos( SwWW8AttrIter const * aAttrIter, const SwTextNode& rNode, sal_Int32 nCurrentPos )
+{
+ // Get the bookmarks for the normal run
+ const sal_Int32 nNextPos = aAttrIter->WhereNext();
+ sal_Int32 nNextBookmark = nNextPos;
+ sal_Int32 nNextAnnotationMark = nNextPos;
+
+ if( nNextBookmark > nCurrentPos ) //no need to search for bookmarks otherwise (checked in UpdatePosition())
+ {
+ GetSortedBookmarks( rNode, nCurrentPos, nNextBookmark - nCurrentPos );
+ NearestBookmark( nNextBookmark, nCurrentPos, false );
+ GetSortedAnnotationMarks(*aAttrIter, nCurrentPos, nNextAnnotationMark - nCurrentPos);
+ NearestAnnotationMark( nNextAnnotationMark, nCurrentPos, false );
+ }
+ return std::min( nNextPos, std::min( nNextBookmark, nNextAnnotationMark ) );
+}
+
+void MSWordExportBase::UpdatePosition( SwWW8AttrIter* aAttrIter, sal_Int32 nCurrentPos )
+{
+ sal_Int32 nNextPos;
+
+ // go to next attribute if no bookmark is found or if the bookmark is after the next attribute position
+ // It may happened that the WhereNext() wasn't used in the previous increment because there was a
+ // bookmark before it. Use that position before trying to find another one.
+ bool bNextBookmark = NearestBookmark( nNextPos, nCurrentPos, true );
+ if( nCurrentPos == aAttrIter->WhereNext() && ( !bNextBookmark || nNextPos > aAttrIter->WhereNext() ) )
+ aAttrIter->NextPos();
+}
+
+bool MSWordExportBase::GetBookmarks( const SwTextNode& rNd, sal_Int32 nStt,
+ sal_Int32 nEnd, IMarkVector& rArr )
+{
+ IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
+
+ const sal_Int32 nMarks = pMarkAccess->getAllMarksCount();
+ for ( sal_Int32 i = 0; i < nMarks; i++ )
+ {
+ IMark* pMark = pMarkAccess->getAllMarksBegin()[i];
+
+ switch (IDocumentMarkAccess::GetType( *pMark ))
+ {
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ continue; // ignore irrelevant marks
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ break;
+ }
+
+ // Only keep the bookmarks starting or ending in this node
+ if ( pMark->GetMarkStart().GetNode() == rNd ||
+ pMark->GetMarkEnd().GetNode() == rNd )
+ {
+ const sal_Int32 nBStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nBEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ // Keep only the bookmarks starting or ending in the snippet
+ bool bIsStartOk = ( pMark->GetMarkStart().GetNode() == rNd ) && ( nBStart >= nStt ) && ( nBStart <= nEnd );
+ bool bIsEndOk = ( pMark->GetMarkEnd().GetNode() == rNd ) && ( nBEnd >= nStt ) && ( nBEnd <= nEnd );
+
+ if ( bIsStartOk || bIsEndOk )
+ {
+ rArr.push_back( pMark );
+ }
+ }
+ }
+ return ( !rArr.empty() );
+}
+
+bool MSWordExportBase::GetAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nStt,
+ sal_Int32 nEnd, IMarkVector& rArr )
+{
+ IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
+ const SwNode& rNd = rAttrs.GetNode();
+
+ const sal_Int32 nMarks = pMarkAccess->getAnnotationMarksCount();
+ for ( sal_Int32 i = 0; i < nMarks; i++ )
+ {
+ IMark* pMark = pMarkAccess->getAnnotationMarksBegin()[i];
+
+ // Only keep the bookmarks starting or ending in this node
+ if ( pMark->GetMarkStart().GetNode() == rNd ||
+ pMark->GetMarkEnd().GetNode() == rNd )
+ {
+ const sal_Int32 nBStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nBEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ // Keep only the bookmarks starting or ending in the snippet
+ bool bIsStartOk = ( pMark->GetMarkStart().GetNode() == rNd ) && ( nBStart >= nStt ) && ( nBStart <= nEnd );
+ bool bIsEndOk = ( pMark->GetMarkEnd().GetNode() == rNd ) && ( nBEnd >= nStt ) && ( nBEnd <= nEnd );
+
+ // Annotation marks always have at least one character: the anchor
+ // point of the comment field. In this case Word wants only the
+ // comment field, so ignore the annotation mark itself.
+ bool bSingleChar = pMark->GetMarkStart().GetNode() == pMark->GetMarkEnd().GetNode() && nBStart + 1 == nBEnd;
+
+ if (bSingleChar)
+ {
+ if (rAttrs.HasFlysAt(nBStart))
+ {
+ // There is content (an at-char anchored frame) between the annotation mark
+ // start/end, so still emit range start/end.
+ bSingleChar = false;
+ }
+ }
+
+ if ( ( bIsStartOk || bIsEndOk ) && !bSingleChar )
+ {
+ rArr.push_back( pMark );
+ }
+ }
+ }
+ return ( !rArr.empty() );
+}
+
+namespace {
+
+class CompareMarksEnd
+{
+public:
+ bool operator() ( const IMark * pOneB, const IMark * pTwoB ) const
+ {
+ const sal_Int32 nOEnd = pOneB->GetMarkEnd().GetContentIndex();
+ const sal_Int32 nTEnd = pTwoB->GetMarkEnd().GetContentIndex();
+
+ return nOEnd < nTEnd;
+ }
+};
+
+}
+
+bool MSWordExportBase::NearestBookmark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly )
+{
+ bool bHasBookmark = false;
+
+ if ( !m_rSortedBookmarksStart.empty() )
+ {
+ IMark* pMarkStart = m_rSortedBookmarksStart.front();
+ const sal_Int32 nNext = pMarkStart->GetMarkStart().GetContentIndex();
+ if( !bNextPositionOnly || (nNext > nCurrentPos ))
+ {
+ rNearest = nNext;
+ bHasBookmark = true;
+ }
+ }
+
+ if ( !m_rSortedBookmarksEnd.empty() )
+ {
+ IMark* pMarkEnd = m_rSortedBookmarksEnd[0];
+ const sal_Int32 nNext = pMarkEnd->GetMarkEnd().GetContentIndex();
+ if( !bNextPositionOnly || nNext > nCurrentPos )
+ {
+ if ( !bHasBookmark )
+ rNearest = nNext;
+ else
+ rNearest = std::min( rNearest, nNext );
+ bHasBookmark = true;
+ }
+ }
+
+ return bHasBookmark;
+}
+
+void MSWordExportBase::NearestAnnotationMark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly )
+{
+ bool bHasAnnotationMark = false;
+
+ if ( !m_rSortedAnnotationMarksStart.empty() )
+ {
+ IMark* pMarkStart = m_rSortedAnnotationMarksStart.front();
+ const sal_Int32 nNext = pMarkStart->GetMarkStart().GetContentIndex();
+ if( !bNextPositionOnly || (nNext > nCurrentPos ))
+ {
+ rNearest = nNext;
+ bHasAnnotationMark = true;
+ }
+ }
+
+ if ( !m_rSortedAnnotationMarksEnd.empty() )
+ {
+ IMark* pMarkEnd = m_rSortedAnnotationMarksEnd[0];
+ const sal_Int32 nNext = pMarkEnd->GetMarkEnd().GetContentIndex();
+ if( !bNextPositionOnly || nNext > nCurrentPos )
+ {
+ if ( !bHasAnnotationMark )
+ rNearest = nNext;
+ else
+ rNearest = std::min( rNearest, nNext );
+ }
+ }
+}
+
+void MSWordExportBase::GetSortedAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen )
+{
+ IMarkVector aMarksStart;
+ if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarksStart))
+ {
+ IMarkVector aSortedEnd;
+ IMarkVector aSortedStart;
+ for ( IMark* pMark : aMarksStart )
+ {
+ // Remove the positions equal to the current pos
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ const SwTextNode& rNode = rAttrs.GetNode();
+ if ( nStart > nCurrentPos && ( pMark->GetMarkStart().GetNode() == rNode) )
+ aSortedStart.push_back( pMark );
+
+ if ( nEnd > nCurrentPos && nEnd <= ( nCurrentPos + nLen ) && (pMark->GetMarkEnd().GetNode() == rNode) )
+ aSortedEnd.push_back( pMark );
+ }
+
+ // Sort the bookmarks by end position
+ std::sort( aSortedEnd.begin(), aSortedEnd.end(), CompareMarksEnd() );
+
+ m_rSortedAnnotationMarksStart.swap( aSortedStart );
+ m_rSortedAnnotationMarksEnd.swap( aSortedEnd );
+ }
+ else
+ {
+ m_rSortedAnnotationMarksStart.clear( );
+ m_rSortedAnnotationMarksEnd.clear( );
+ }
+}
+
+void MSWordExportBase::GetSortedBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen )
+{
+ IMarkVector aMarksStart;
+ if ( GetBookmarks( rNode, nCurrentPos, nCurrentPos + nLen, aMarksStart ) )
+ {
+ IMarkVector aSortedEnd;
+ IMarkVector aSortedStart;
+ for ( IMark* pMark : aMarksStart )
+ {
+ // Remove the positions equal to the current pos
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
+
+ if ( nStart > nCurrentPos && (pMark->GetMarkStart().GetNode() == rNode) )
+ aSortedStart.push_back( pMark );
+
+ if ( nEnd > nCurrentPos && nEnd <= ( nCurrentPos + nLen ) && (pMark->GetMarkEnd().GetNode() == rNode) )
+ aSortedEnd.push_back( pMark );
+ }
+
+ // Sort the bookmarks by end position
+ std::sort( aSortedEnd.begin(), aSortedEnd.end(), CompareMarksEnd() );
+
+ m_rSortedBookmarksStart.swap( aSortedStart );
+ m_rSortedBookmarksEnd.swap( aSortedEnd );
+ }
+ else
+ {
+ m_rSortedBookmarksStart.clear( );
+ m_rSortedBookmarksEnd.clear( );
+ }
+}
+
+bool MSWordExportBase::NeedSectionBreak( const SwNode& rNd ) const
+{
+ if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs || m_pCurrentPageDesc == nullptr )
+ return false;
+
+ const SwPageDesc * pPageDesc = rNd.FindPageDesc()->GetFollow();
+
+ if (m_pCurrentPageDesc != pPageDesc)
+ {
+ if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool MSWordExportBase::NeedTextNodeSplit( const SwTextNode& rNd, SwSoftPageBreakList& pList ) const
+{
+ SwSoftPageBreakList tmp;
+ rNd.fillSoftPageBreakList(tmp);
+ // hack: move the break behind any field marks; currently we can't hide the
+ // field mark instruction so the layout position is quite meaningless
+ IDocumentMarkAccess const& rIDMA(*rNd.GetDoc().getIDocumentMarkAccess());
+ sal_Int32 pos(-1);
+ for (auto const& it : tmp)
+ {
+ if (pos < it) // previous one might have skipped over it
+ {
+ pos = it;
+ while (auto const*const pMark = rIDMA.getInnerFieldmarkFor(SwPosition(rNd, pos)))
+ {
+ if (pMark->GetMarkEnd().GetNode() != rNd)
+ {
+ pos = rNd.Len(); // skip everything
+ break;
+ }
+ pos = pMark->GetMarkEnd().GetContentIndex(); // no +1, it's behind the char
+ }
+ pList.insert(pos);
+ }
+ }
+ pList.insert(0);
+ pList.insert( rNd.GetText().getLength() );
+ return pList.size() > 2 && NeedSectionBreak( rNd );
+}
+
+namespace {
+OUString lcl_GetSymbolFont(SwAttrPool& rPool, const SwTextNode* pTextNode, int nStart, int nEnd)
+{
+ SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONT> aSet( rPool );
+ if ( pTextNode && pTextNode->GetParaAttr(aSet, nStart, nEnd) )
+ {
+ SfxPoolItem const* pPoolItem = aSet.GetItem(RES_CHRATR_FONT);
+ if (pPoolItem)
+ {
+ const SvxFontItem* pFontItem = static_cast<const SvxFontItem*>(pPoolItem);
+ if (pFontItem->GetCharSet() == RTL_TEXTENCODING_SYMBOL)
+ return pFontItem->GetFamilyName();
+ }
+ }
+
+ return OUString();
+}
+}
+
+void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
+{
+ SAL_INFO( "sw.ww8", "<OutWW8_SwTextNode>" );
+
+ ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo( m_pTableInfo->getTableNodeInfo( &rNode ) );
+
+ //For i120928,identify the last node
+ bool bLastCR = false;
+ bool bExported = false;
+ {
+ SwNodeIndex aNextIdx(rNode,1);
+ SwNodeIndex aLastIdx(rNode.GetNodes().GetEndOfContent());
+ if (aNextIdx == aLastIdx)
+ bLastCR = true;
+ }
+
+ // In order to make sure watermark is stored in 'header.xml', check nTextTyp.
+ // if it is document.xml, don't write the tags (watermark should be only in the 'header')
+ SwWW8AttrIter aWatermarkAttrIter( *this, rNode );
+ if (( TXT_HDFT != m_nTextTyp) && aWatermarkAttrIter.IsWatermarkFrame())
+ {
+ return;
+ }
+
+ bool bFlyInTable = m_pParentFrame && IsInTable();
+
+ SwTextFormatColl& rTextColl = lcl_getFormatCollection( *this, &rNode );
+ if ( !bFlyInTable )
+ m_nStyleBeforeFly = GetId( rTextColl );
+
+ // nStyleBeforeFly may change when we recurse into another node, so we
+ // have to remember it in nStyle
+ sal_uInt16 nStyle = m_nStyleBeforeFly;
+
+ SwWW8AttrIter aAttrIter( *this, rNode );
+ rtl_TextEncoding eChrSet = aAttrIter.GetCharSet();
+
+ if ( m_bStartTOX )
+ {
+ // ignore TOX header section
+ const SwSectionNode* pSectNd = rNode.FindSectionNode();
+ if ( pSectNd && SectionType::ToxContent == pSectNd->GetSection().GetType() )
+ {
+ AttrOutput().StartTOX( pSectNd->GetSection() );
+ m_aCurrentCharPropStarts.push( 0 );
+ }
+ }
+
+ // Emulate: If 1-row table is marked as don't split, then set the row as don't split.
+ if ( IsInTable() )
+ {
+ const SwTableNode* pTableNode = rNode.FindTableNode();
+ if ( pTableNode )
+ {
+ const SwTable& rTable = pTableNode->GetTable();
+ const SwTableBox* pBox = rNode.GetTableBox();
+
+ // export formula cell as formula field instead of only its cell content in DOCX
+ if ( pBox->IsFormulaOrValueBox() == RES_BOXATR_FORMULA &&
+ GetExportFormat() == MSWordExportBase::ExportFormat::DOCX )
+ {
+ std::unique_ptr<SwTableBoxFormula> pFormula(pBox->GetFrameFormat()->GetTableBoxFormula().Clone());
+ pFormula->PtrToBoxNm( &pTableNode->GetTable() );
+ OutputField( nullptr, ww::eEquals, " =" + pFormula->GetFormula(),
+ FieldFlags::Start | FieldFlags::CmdStart | FieldFlags::CmdEnd | FieldFlags::Close );
+ }
+
+ const bool bKeep = rTable.GetFrameFormat()->GetKeep().GetValue();
+ const bool bDontSplit = !rTable.GetFrameFormat()->GetLayoutSplit().GetValue();
+ // bKeep handles this a different way later on, so ignore now
+ if ( !bKeep && bDontSplit && rTable.GetTabLines().size() == 1 )
+ {
+ // bDontSplit : set don't split once for the row
+ // but only for non-complex tables
+ const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
+ if ( pLine && !pLine->GetUpper() )
+ {
+ // check if box is first in that line:
+ if ( 0 == pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
+ {
+ // check if paragraph is first in that line:
+ if ( SwNodeOffset(1) == ( rNode.GetIndex() - pBox->GetSttNd()->GetIndex() ) )
+ pLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bDontSplit));
+ }
+ }
+ }
+ }
+ }
+
+ SwSoftPageBreakList softBreakList;
+ // Let's decide if we need to split the paragraph because of a section break
+ bool bNeedParaSplit = NeedTextNodeSplit( rNode, softBreakList )
+ && !IsInTable();
+ const SwPageDesc* pNextSplitParaPageDesc = m_pCurrentPageDesc;
+
+ auto aBreakIt = softBreakList.begin();
+ // iterate through portions on different pages
+ do
+ {
+ sal_Int32 nCurrentPos = *aBreakIt;
+
+ if( softBreakList.size() > 1 ) // not for empty paragraph
+ {
+ // no need to split again if the page style won't change anymore
+ if ( pNextSplitParaPageDesc == pNextSplitParaPageDesc->GetFollow() )
+ aBreakIt = --softBreakList.end();
+ else
+ ++aBreakIt;
+ }
+
+ AttrOutput().StartParagraph(pTextNodeInfo, false);
+
+ const SwSection* pTOXSect = nullptr;
+ if( m_bInWriteTOX )
+ {
+ // check for end of TOX
+ SwNodeIndex aIdx( rNode, 1 );
+ if( !aIdx.GetNode().IsTextNode() )
+ {
+ const SwSectionNode* pTOXSectNd = rNode.FindSectionNode();
+ if ( pTOXSectNd )
+ {
+ pTOXSect = &pTOXSectNd->GetSection();
+
+ const SwNode* pNxt = rNode.GetNodes().GoNext( &aIdx );
+ if( pNxt && pNxt->FindSectionNode() == pTOXSectNd )
+ pTOXSect = nullptr;
+ }
+ }
+ }
+
+ if ( aAttrIter.RequiresImplicitBookmark() )
+ {
+ OUString sBkmkName = "_toc" + OUString::number( sal_Int32(rNode.GetIndex()) );
+ // Add a bookmark converted to a Word name.
+ AppendBookmark( BookmarkToWord( sBkmkName ) );
+ }
+
+ // Call this before write out fields and runs
+ AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter);
+
+ const OUString& aStr( rNode.GetText() );
+
+ sal_Int32 const nEnd = bNeedParaSplit ? *aBreakIt : aStr.getLength();
+ bool bIsEndOfCell = false;
+ bool bIncludeEndOfParaCRInRedlineProperties = false;
+ sal_Int32 nOpenAttrWithRange = 0;
+
+ ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner;
+ if ( pTextNodeInfo )
+ {
+ pTextNodeInfoInner = pTextNodeInfo->getFirstInner();
+ if (pTextNodeInfoInner && pTextNodeInfoInner->isEndOfCell())
+ bIsEndOfCell = true;
+ }
+
+ do {
+
+ const SwRedlineData* pRedlineData = aAttrIter.GetRunLevelRedline( nCurrentPos );
+ bool bPostponeWritingText = false ;
+ bool bStartedPostponedRunProperties = false;
+ OUString aSavedSnippet ;
+
+ sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nCurrentPos );
+
+ // Skip un-exportable attributes.
+ if (!aAttrIter.IsExportableAttr(nCurrentPos))
+ {
+ nCurrentPos = nNextAttr;
+ UpdatePosition(&aAttrIter, nCurrentPos);
+ eChrSet = aAttrIter.GetCharSet();
+ continue;
+ }
+
+ // Is this the only run in this paragraph and it's empty?
+ bool bSingleEmptyRun = nCurrentPos == 0 && nNextAttr == 0;
+ AttrOutput().StartRun( pRedlineData, nCurrentPos, bSingleEmptyRun );
+
+ if( nNextAttr > nEnd )
+ nNextAttr = nEnd;
+
+ if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN )
+ {
+ if( AttrOutput().FootnoteEndnoteRefTag() )
+ {
+ AttrOutput().EndRun( &rNode, nCurrentPos, -1, nNextAttr == nEnd );
+ AttrOutput().StartRun( pRedlineData, nCurrentPos, bSingleEmptyRun );
+ }
+ }
+
+ /*
+ 1) If there is a text node and an overlapping anchor, then write them in two different
+ runs and not as part of the same run.
+ 2) Ensure that it is a text node and not in a fly.
+ 3) If the anchor is associated with a text node with empty text then we ignore.
+ */
+ if( rNode.IsTextNode()
+ && GetExportFormat() == MSWordExportBase::ExportFormat::DOCX
+ && aStr != OUStringChar(CH_TXTATR_BREAKWORD) && !aStr.isEmpty()
+ && !rNode.GetFlyFormat()
+ && aAttrIter.IsAnchorLinkedToThisNode(rNode.GetIndex()) )
+ {
+ bPostponeWritingText = true ;
+ }
+
+ FlyProcessingState nStateOfFlyFrame = aAttrIter.OutFlys( nCurrentPos );
+ AttrOutput().SetStateOfFlyFrame( nStateOfFlyFrame );
+ AttrOutput().SetAnchorIsLinkedToNode( bPostponeWritingText && (FLY_POSTPONED != nStateOfFlyFrame) );
+ // Append bookmarks in this range after flys, exclusive of final
+ // position of this range
+ AppendBookmarks( rNode, nCurrentPos, nNextAttr - nCurrentPos, pRedlineData );
+ // Sadly only possible for main or glossary document parts: ECMA-376 Part 1 sect. 11.3.2
+ if ( m_nTextTyp == TXT_MAINTEXT )
+ AppendAnnotationMarks(aAttrIter, nCurrentPos, nNextAttr - nCurrentPos);
+
+ // At the moment smarttags are only written for paragraphs, at the
+ // beginning of the paragraph.
+ if (nCurrentPos == 0)
+ AppendSmartTags(rNode);
+
+ bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos );
+ nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nCurrentPos );
+
+ OUString aSymbolFont;
+ sal_Int32 nLen = nNextAttr - nCurrentPos;
+ if ( !bTextAtr && nLen )
+ {
+ sal_Unicode ch = aStr[nCurrentPos];
+
+ const sal_Int32 ofs = (ch == CH_TXT_ATR_FIELDSTART
+ || ch == CH_TXT_ATR_FIELDSEP
+ || ch == CH_TXT_ATR_FIELDEND
+ || ch == CH_TXT_ATR_FORMELEMENT)
+ ? 1 : 0;
+ if (ofs == 1
+ && GetExportFormat() == MSWordExportBase::ExportFormat::DOCX
+ // FLY_PROCESSED: there's at least 1 fly already written
+ && nStateOfFlyFrame == FLY_PROCESSED)
+ {
+ // write flys in a separate run before field character
+ AttrOutput().EndRun(&rNode, nCurrentPos, -1, nNextAttr == nEnd);
+ AttrOutput().StartRun(pRedlineData, nCurrentPos, bSingleEmptyRun);
+ }
+
+ IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
+ if ( ch == CH_TXT_ATR_FIELDSTART )
+ {
+ SwPosition aPosition( rNode, nCurrentPos );
+ ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkAt(aPosition);
+ assert(pFieldmark);
+
+ // Date field is exported as content control, not as a simple field
+ if (pFieldmark->GetFieldname() == ODF_FORMDATE)
+ {
+ if(GetExportFormat() == MSWordExportBase::ExportFormat::DOCX) // supported by DOCX only
+ {
+ OutputField( nullptr, lcl_getFieldId( pFieldmark ),
+ lcl_getFieldCode( pFieldmark ),
+ FieldFlags::Start | FieldFlags::CmdStart );
+ WriteFormData( *pFieldmark );
+ }
+ }
+ else
+ {
+
+ if (pFieldmark->GetFieldname() == ODF_FORMTEXT
+ && GetExportFormat() != MSWordExportBase::ExportFormat::DOCX )
+ {
+ AppendBookmark( pFieldmark->GetName() );
+ }
+ ww::eField eFieldId = lcl_getFieldId( pFieldmark );
+ OUString sCode = lcl_getFieldCode( pFieldmark );
+ if (pFieldmark->GetFieldname() == ODF_UNHANDLED )
+ {
+ IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM );
+ if ( it != pFieldmark->GetParameters()->end() )
+ {
+ OUString sFieldId;
+ it->second >>= sFieldId;
+ eFieldId = static_cast<ww::eField>(sFieldId.toInt32());
+ }
+
+ it = pFieldmark->GetParameters()->find( ODF_CODE_PARAM );
+ if ( it != pFieldmark->GetParameters()->end() )
+ {
+ it->second >>= sCode;
+ }
+ }
+
+ OutputField( nullptr, eFieldId, sCode, FieldFlags::Start | FieldFlags::CmdStart );
+
+ if (pFieldmark->GetFieldname() == ODF_FORMTEXT)
+ WriteFormData( *pFieldmark );
+ else if (pFieldmark->GetFieldname() == ODF_HYPERLINK)
+ WriteHyperlinkData( *pFieldmark );
+ }
+ }
+ else if (ch == CH_TXT_ATR_FIELDSEP)
+ {
+ SwPosition aPosition(rNode, nCurrentPos);
+ // the innermost field is the correct one
+ sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getInnerFieldmarkFor(aPosition);
+ assert(pFieldmark);
+ // DateFieldmark / ODF_FORMDATE is not a field...
+ if (pFieldmark->GetFieldname() != ODF_FORMDATE)
+ {
+ FieldFlags nFlags = FieldFlags::CmdEnd;
+ // send hint that fldrslt is empty, to avoid spamming RTF CharProp reset.
+ // ::End does nothing when sending rFieldCmd=OUString(), so safe to do.
+ if (pFieldmark->GetContent().isEmpty())
+ nFlags |= FieldFlags::End;
+ OutputField(nullptr, lcl_getFieldId(pFieldmark), OUString(), nFlags);
+
+ if (pFieldmark->GetFieldname() == ODF_UNHANDLED)
+ {
+ // Check for the presence of a linked OLE object
+ IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_OLE_PARAM );
+ if ( it != pFieldmark->GetParameters()->end() )
+ {
+ OUString sOleId;
+ uno::Any aValue = it->second;
+ aValue >>= sOleId;
+ if ( !sOleId.isEmpty() )
+ OutputLinkedOLE( sOleId );
+ }
+ }
+ }
+ }
+ else if ( ch == CH_TXT_ATR_FIELDEND )
+ {
+ SwPosition aPosition( rNode, nCurrentPos );
+ ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkAt(aPosition);
+
+ assert(pFieldmark);
+
+ if (pFieldmark->GetFieldname() == ODF_FORMDATE)
+ {
+ if(GetExportFormat() == MSWordExportBase::ExportFormat::DOCX) // supported by DOCX only
+ {
+ OutputField( nullptr, ww::eFORMDATE, OUString(), FieldFlags::Close );
+ }
+ }
+ else
+ {
+ ww::eField eFieldId = lcl_getFieldId( pFieldmark );
+ if (pFieldmark->GetFieldname() == ODF_UNHANDLED)
+ {
+ IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM );
+ if ( it != pFieldmark->GetParameters()->end() )
+ {
+ OUString sFieldId;
+ it->second >>= sFieldId;
+ eFieldId = static_cast<ww::eField>(sFieldId.toInt32());
+ }
+ }
+
+ OutputField( nullptr, eFieldId, OUString(), FieldFlags::Close );
+
+ if (pFieldmark->GetFieldname() == ODF_FORMTEXT
+ && GetExportFormat() != MSWordExportBase::ExportFormat::DOCX )
+ {
+ AppendBookmark( pFieldmark->GetName() );
+ }
+ }
+ }
+ else if ( ch == CH_TXT_ATR_FORMELEMENT )
+ {
+ SwPosition aPosition( rNode, nCurrentPos );
+ ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkAt(aPosition);
+ assert(pFieldmark);
+
+ bool const isDropdownOrCheckbox(pFieldmark->GetFieldname() == ODF_FORMDROPDOWN ||
+ pFieldmark->GetFieldname() == ODF_FORMCHECKBOX);
+ if ( isDropdownOrCheckbox )
+ AppendBookmark( pFieldmark->GetName() );
+ OutputField( nullptr, lcl_getFieldId( pFieldmark ),
+ lcl_getFieldCode( pFieldmark ),
+ FieldFlags::Start | FieldFlags::CmdStart );
+ if ( isDropdownOrCheckbox )
+ WriteFormData( *pFieldmark );
+ // tdf#129514 need CmdEnd for docx
+ OutputField(nullptr, lcl_getFieldId(pFieldmark), OUString(),
+ FieldFlags::CmdEnd | FieldFlags::Close);
+ if ( isDropdownOrCheckbox )
+ AppendBookmark( pFieldmark->GetName() );
+ }
+ nLen -= ofs;
+
+ // if paragraph needs to be split, write only until split position
+ assert(!bNeedParaSplit || nCurrentPos <= *aBreakIt);
+ if( bNeedParaSplit && nCurrentPos + ofs + nLen > *aBreakIt)
+ nLen = *aBreakIt - nCurrentPos - ofs;
+ assert(0 <= nLen);
+
+ OUString aSnippet( aAttrIter.GetSnippet( aStr, nCurrentPos + ofs, nLen ) );
+ const SwTextNode* pTextNode( rNode.GetTextNode() );
+ if ( ( m_nTextTyp == TXT_EDN || m_nTextTyp == TXT_FTN ) && nCurrentPos == 0 && nLen > 0 )
+ {
+ // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
+ sal_Int32 nFirstLineIndent=0;
+ SfxItemSetFixed<RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE> aSet( m_rDoc.GetAttrPool() );
+
+ if ( pTextNode && pTextNode->GetAttr(aSet) )
+ {
+ const SvxFirstLineIndentItem *const pFirstLine(aSet.GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
+ if (pFirstLine)
+ nFirstLineIndent = pFirstLine->GetTextFirstLineOffset();
+ }
+
+ // Insert tab for aesthetic purposes #i24762#
+ if ( m_bAddFootnoteTab && nFirstLineIndent < 0 && aSnippet[0] != 0x09 )
+ aSnippet = "\x09" + aSnippet;
+ m_bAddFootnoteTab = false;
+ }
+
+ aSymbolFont = lcl_GetSymbolFont(m_rDoc.GetAttrPool(), pTextNode, nCurrentPos + ofs, nCurrentPos + ofs + nLen);
+
+ if ( bPostponeWritingText && ( FLY_POSTPONED != nStateOfFlyFrame ) )
+ {
+ aSavedSnippet = aSnippet ;
+ }
+ else
+ {
+ bPostponeWritingText = false ;
+ AttrOutput().RunText( aSnippet, eChrSet, aSymbolFont );
+ }
+
+ if (ofs == 1 && nNextAttr == nEnd)
+ {
+ // tdf#152200: There could be flys anchored after the last position; make sure
+ // to provide a separate run after field character to write them
+ AttrOutput().EndRun(&rNode, nCurrentPos, -1, nNextAttr == nEnd);
+ AttrOutput().StartRun(pRedlineData, nCurrentPos, bSingleEmptyRun);
+ }
+ }
+
+ if ( aAttrIter.IsDropCap( nNextAttr ) )
+ AttrOutput().FormatDrop( rNode, aAttrIter.GetSwFormatDrop(), nStyle, pTextNodeInfo, pTextNodeInfoInner );
+
+ // Only output character attributes if this is not a postponed text run.
+ if (0 != nEnd && !(bPostponeWritingText
+ && (FLY_PROCESSED == nStateOfFlyFrame || FLY_NONE == nStateOfFlyFrame)))
+ {
+ // Output the character attributes
+ // #i51277# do this before writing flys at end of paragraph
+ bStartedPostponedRunProperties = true;
+ AttrOutput().StartRunProperties();
+ aAttrIter.OutAttr(nCurrentPos, false);
+ AttrOutput().EndRunProperties( pRedlineData );
+ }
+
+ // At the end of line, output the attributes until the CR.
+ // Exception: footnotes at the end of line
+ if ( nNextAttr == nEnd )
+ {
+ OSL_ENSURE( nOpenAttrWithRange >= 0, "odd to see this happening, expected >= 0" );
+ if ( !bTextAtr && nOpenAttrWithRange <= 0 )
+ {
+ if ( aAttrIter.IncludeEndOfParaCRInRedlineProperties( nEnd ) )
+ bIncludeEndOfParaCRInRedlineProperties = true;
+ else
+ {
+ // insert final graphic anchors if any before CR
+ nStateOfFlyFrame = aAttrIter.OutFlys( nEnd );
+ // insert final bookmarks if any before CR and after flys
+ AppendBookmarks( rNode, nEnd, 1 );
+ AppendAnnotationMarks(aAttrIter, nEnd, 1);
+ if ( pTOXSect )
+ {
+ m_aCurrentCharPropStarts.pop();
+ AttrOutput().EndTOX( *pTOXSect ,false);
+ }
+ //For i120928,the position of the bullet's graphic is at end of doc
+ if (bLastCR && (!bExported))
+ {
+ ExportGrfBullet(rNode);
+ bExported = true;
+ }
+
+ WriteCR( pTextNodeInfoInner );
+
+ if (0 != nEnd && bIsEndOfCell)
+ AttrOutput().OutputFKP(/*bforce=*/true);
+ }
+ }
+ }
+
+ if (0 == nEnd)
+ {
+ // Output the character attributes
+ // do it after WriteCR for an empty paragraph (otherwise
+ // WW8_WrFkp::Append throws SPRMs away...)
+ AttrOutput().StartRunProperties();
+ aAttrIter.OutAttr( nCurrentPos, false );
+ AttrOutput().EndRunProperties( pRedlineData );
+ }
+
+ // Exception: footnotes at the end of line
+ if ( nNextAttr == nEnd )
+ {
+ OSL_ENSURE(nOpenAttrWithRange >= 0,
+ "odd to see this happening, expected >= 0");
+ bool bAttrWithRange = (nOpenAttrWithRange > 0);
+ if ( nCurrentPos != nEnd )
+ {
+ nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nEnd );
+ OSL_ENSURE(nOpenAttrWithRange == 0,
+ "odd to see this happening, expected 0");
+ }
+
+ AttrOutput().OutputFKP(/*bForce=*/false);
+
+ if (bTextAtr || bAttrWithRange || bIncludeEndOfParaCRInRedlineProperties)
+ {
+ AttrOutput().WritePostitFieldReference();
+
+ // insert final graphic anchors if any before CR
+ nStateOfFlyFrame = aAttrIter.OutFlys( nEnd );
+ // insert final bookmarks if any before CR and after flys
+ AppendBookmarks( rNode, nEnd, 1 );
+ AppendAnnotationMarks(aAttrIter, nEnd, 1);
+ WriteCR( pTextNodeInfoInner );
+ // #i120928 - position of the bullet's graphic is at end of doc
+ if (bLastCR && (!bExported))
+ {
+ ExportGrfBullet(rNode);
+ bExported = true;
+ }
+
+ if ( pTOXSect )
+ {
+ m_aCurrentCharPropStarts.pop();
+ AttrOutput().EndTOX( *pTOXSect );
+ }
+
+ if (bIncludeEndOfParaCRInRedlineProperties)
+ {
+ AttrOutput().Redline( aAttrIter.GetRunLevelRedline( nEnd ) );
+ //If there was no redline property emitted, force adding
+ //another entry for the CR so that in the case that this
+ //has no redline, but the next para does, then this one is
+ //not merged with the next
+ AttrOutput().OutputFKP(true);
+ }
+ }
+ }
+
+ AttrOutput().WritePostitFieldReference();
+
+ aSymbolFont = lcl_GetSymbolFont(m_rDoc.GetAttrPool(), &rNode, nCurrentPos, nCurrentPos + nLen);
+
+ if (bPostponeWritingText)
+ {
+ if (FLY_PROCESSED == nStateOfFlyFrame || FLY_NONE == nStateOfFlyFrame)
+ {
+ AttrOutput().EndRun(&rNode, nCurrentPos, -1, /*bLastRun=*/false);
+ if (!aSavedSnippet.isEmpty())
+ bStartedPostponedRunProperties = false;
+
+ AttrOutput().StartRun( pRedlineData, nCurrentPos, bSingleEmptyRun );
+ AttrOutput().SetAnchorIsLinkedToNode( false );
+ AttrOutput().ResetFlyProcessingFlag();
+ }
+ if (0 != nEnd && !bStartedPostponedRunProperties)
+ {
+ AttrOutput().StartRunProperties();
+ aAttrIter.OutAttr( nCurrentPos, false );
+ AttrOutput().EndRunProperties( pRedlineData );
+
+ // OutAttr may have introduced new comments, so write them out now
+ AttrOutput().WritePostitFieldReference();
+ }
+ AttrOutput().RunText( aSavedSnippet, eChrSet, aSymbolFont );
+ AttrOutput().EndRun(&rNode, nCurrentPos, nLen, nNextAttr == nEnd);
+ }
+ else
+ AttrOutput().EndRun(&rNode, nCurrentPos, nLen, nNextAttr == nEnd);
+
+ nCurrentPos = nNextAttr;
+ UpdatePosition( &aAttrIter, nCurrentPos );
+ eChrSet = aAttrIter.GetCharSet();
+ }
+ while ( nCurrentPos < nEnd );
+
+ // if paragraph is split, put the section break between the parts
+ if( bNeedParaSplit && *aBreakIt != rNode.GetText().getLength() )
+ {
+ pNextSplitParaPageDesc = pNextSplitParaPageDesc->GetFollow();
+ assert(pNextSplitParaPageDesc);
+ PrepareNewPageDesc( rNode.GetpSwAttrSet(), rNode, nullptr , pNextSplitParaPageDesc);
+ }
+ else
+ {
+ // else check if section break needed after the paragraph
+ bool bCheckSectionBreak = true;
+ // only try to sectionBreak after a split para if the next node specifies a break
+ if ( bNeedParaSplit )
+ {
+ m_pCurrentPageDesc = pNextSplitParaPageDesc;
+ SwNodeIndex aNextIndex( rNode, 1 );
+ const SwTextNode* pNextNode = aNextIndex.GetNode().GetTextNode();
+ bCheckSectionBreak = pNextNode && !NoPageBreakSection( pNextNode->GetpSwAttrSet() );
+
+ if ( !bCheckSectionBreak )
+ {
+ const SvxFormatBreakItem& rBreak = rNode.GetSwAttrSet().Get(RES_BREAK);
+ if ( rBreak.GetBreak() == SvxBreak::PageAfter )
+ {
+ if ( pNextNode && pNextNode->FindPageDesc() != pNextSplitParaPageDesc )
+ bCheckSectionBreak = true;
+ else
+ AttrOutput().SectionBreak(msword::PageBreak, /*bBreakAfter=*/true);
+ }
+ }
+ }
+
+ if ( bCheckSectionBreak )
+ AttrOutput().SectionBreaks(rNode);
+ }
+
+ AttrOutput().StartParagraphProperties();
+
+ AttrOutput().ParagraphStyle( nStyle );
+
+ if ( m_pParentFrame && IsInTable() ) // Fly-Attrs
+ OutputFormat( m_pParentFrame->GetFrameFormat(), false, false, true );
+
+ if ( pTextNodeInfo )
+ {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", pTextNodeInfo->toString());
+#endif
+
+ AttrOutput().TableInfoCell( pTextNodeInfoInner );
+ if (pTextNodeInfoInner && pTextNodeInfoInner->isFirstInTable())
+ {
+ const SwTable * pTable = pTextNodeInfoInner->getTable();
+
+ const SwTableFormat* pTabFormat = pTable->GetFrameFormat();
+ if (pTabFormat != nullptr)
+ {
+ if (pTabFormat->GetBreak().GetBreak() == SvxBreak::PageBefore)
+ AttrOutput().PageBreakBefore(true);
+ }
+ }
+ }
+
+ if ( !bFlyInTable )
+ {
+ std::optional<SfxItemSet> oTmpSet;
+ const sal_uInt8 nPrvNxtNd = rNode.HasPrevNextLayNode();
+
+ if( (ND_HAS_PREV_LAYNODE|ND_HAS_NEXT_LAYNODE ) != nPrvNxtNd )
+ {
+ const SvxULSpaceItem* pSpaceItem = rNode.GetSwAttrSet().GetItemIfSet(
+ RES_UL_SPACE );
+ if( pSpaceItem &&
+ ( ( !( ND_HAS_PREV_LAYNODE & nPrvNxtNd ) && pSpaceItem->GetUpper()) ||
+ ( !( ND_HAS_NEXT_LAYNODE & nPrvNxtNd ) && pSpaceItem->GetLower()) ))
+ {
+ oTmpSet.emplace( rNode.GetSwAttrSet() );
+ SvxULSpaceItem aUL( *pSpaceItem );
+ // #i25901#- consider compatibility option
+ if (!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES))
+ {
+ if( !(ND_HAS_PREV_LAYNODE & nPrvNxtNd ))
+ aUL.SetUpper( 0 );
+ }
+ // #i25901# - consider compatibility option
+ if (!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
+ {
+ if( !(ND_HAS_NEXT_LAYNODE & nPrvNxtNd ))
+ aUL.SetLower( 0 );
+ }
+ oTmpSet->Put( aUL );
+ }
+ }
+
+ const bool bParaRTL = aAttrIter.IsParaRTL();
+
+ int nNumberLevel = -1;
+ if (rNode.IsNumbered())
+ nNumberLevel = rNode.GetActualListLevel();
+ if (nNumberLevel >= 0 && nNumberLevel < MAXLEVEL)
+ {
+ const SwNumRule* pRule = rNode.GetNumRule();
+ sal_uInt8 nLvl = static_cast< sal_uInt8 >(nNumberLevel);
+ const SwNumFormat* pFormat = pRule->GetNumFormat( nLvl );
+ if( !pFormat )
+ pFormat = &pRule->Get( nLvl );
+
+ if( !oTmpSet )
+ oTmpSet.emplace( rNode.GetSwAttrSet() );
+
+ SvxFirstLineIndentItem firstLine(oTmpSet->Get(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(oTmpSet->Get(RES_MARGIN_TEXTLEFT));
+ // #i86652#
+ if ( pFormat->GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ leftMargin.SetTextLeft(leftMargin.GetTextLeft() + pFormat->GetAbsLSpace());
+ }
+
+ if( rNode.IsNumbered() && rNode.IsCountedInList() )
+ {
+ // #i86652#
+ if ( pFormat->GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ if (bParaRTL)
+ {
+ firstLine.SetTextFirstLineOffsetValue(firstLine.GetTextFirstLineOffset() + pFormat->GetAbsLSpace() - pFormat->GetFirstLineOffset()); //TODO: overflow
+ }
+ else
+ {
+ firstLine.SetTextFirstLineOffset(firstLine.GetTextFirstLineOffset() + GetWordFirstLineOffset(*pFormat));
+ }
+ }
+
+ // correct fix for issue i94187
+ if (SfxItemState::SET !=
+ oTmpSet->GetItemState(RES_PARATR_NUMRULE, false) )
+ {
+ // List style set via paragraph style - then put it into the itemset.
+ // This is needed to get list level and list id exported for
+ // the paragraph.
+ oTmpSet->Put( SwNumRuleItem( pRule->GetName() ));
+
+ // Put indent values into the itemset in case that the list
+ // style is applied via paragraph style and the list level
+ // indent values are not applicable.
+ if ( pFormat->GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_ALIGNMENT)
+ {
+ ::sw::ListLevelIndents const indents(rNode.AreListLevelIndentsApplicable());
+ if (indents & ::sw::ListLevelIndents::FirstLine)
+ {
+ oTmpSet->Put(firstLine);
+ }
+ if (indents & ::sw::ListLevelIndents::LeftMargin)
+ {
+ oTmpSet->Put(leftMargin);
+ }
+ }
+ }
+ }
+ else
+ oTmpSet->ClearItem(RES_PARATR_NUMRULE);
+
+ // #i86652#
+ if ( pFormat->GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ oTmpSet->Put(firstLine);
+ oTmpSet->Put(leftMargin);
+
+ //#i21847#
+ SvxTabStopItem aItem(oTmpSet->Get(RES_PARATR_TABSTOP));
+ SvxTabStop aTabStop(pFormat->GetAbsLSpace());
+ aItem.Insert(aTabStop);
+ oTmpSet->Put(aItem);
+
+ MSWordExportBase::CorrectTabStopInSet(*oTmpSet, pFormat->GetAbsLSpace());
+ }
+ }
+
+ /*
+ If a given para is using the SvxFrameDirection::Environment direction we
+ cannot export that, if it's ltr then that's ok as that is word's
+ default. Otherwise we must add a RTL attribute to our export list
+ Only necessary if the ParaStyle doesn't define the direction.
+ */
+ const SvxFrameDirectionItem* pItem =
+ rNode.GetSwAttrSet().GetItem(RES_FRAMEDIR);
+ if (
+ (!pItem || pItem->GetValue() == SvxFrameDirection::Environment) &&
+ rTextColl.GetFrameDir().GetValue() == SvxFrameDirection::Environment
+ )
+ {
+ if ( !oTmpSet )
+ oTmpSet.emplace(rNode.GetSwAttrSet());
+
+ if ( bParaRTL )
+ oTmpSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_RL_TB, RES_FRAMEDIR));
+ else
+ oTmpSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
+
+ const SvxAdjustItem* pAdjust = rNode.GetSwAttrSet().GetItem(RES_PARATR_ADJUST);
+ if ( pAdjust && (pAdjust->GetAdjust() == SvxAdjust::Left || pAdjust->GetAdjust() == SvxAdjust::Right ) )
+ oTmpSet->Put( *pAdjust, RES_PARATR_ADJUST );
+ }
+ // move code for handling of numbered,
+ // but not counted paragraphs to this place. Otherwise, the paragraph
+ // isn't exported as numbered, but not counted, if no other attribute
+ // is found in <pTmpSet>
+ // #i44815# adjust numbering/indents for numbered paragraphs
+ // without number
+ // #i47013# need to check rNode.GetNumRule()!=NULL as well.
+ if ( ! rNode.IsCountedInList() && rNode.GetNumRule()!=nullptr )
+ {
+ // WW8 does not know numbered paragraphs without number
+ // In WW8AttributeOutput::ParaNumRule(), we will export
+ // the RES_PARATR_NUMRULE as list-id 0, which in WW8 means
+ // no numbering. Here, we will adjust the indents to match
+ // visually.
+
+ if ( !oTmpSet )
+ oTmpSet.emplace(rNode.GetSwAttrSet());
+
+ // create new LRSpace item, based on the current (if present)
+ const SvxFirstLineIndentItem *const pFirstLineIndent(oTmpSet->GetItemIfSet(RES_MARGIN_FIRSTLINE));
+ const SvxTextLeftMarginItem *const pTextLeftMargin(oTmpSet->GetItemIfSet(RES_MARGIN_TEXTLEFT));
+ SvxFirstLineIndentItem firstLine(pFirstLineIndent
+ ? *pFirstLineIndent
+ : SvxFirstLineIndentItem(0, RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(pTextLeftMargin
+ ? *pTextLeftMargin
+ : SvxTextLeftMarginItem(0, RES_MARGIN_TEXTLEFT));
+
+ // new left margin = old left + label space
+ const SwNumRule* pRule = rNode.GetNumRule();
+ int nLevel = rNode.GetActualListLevel();
+
+ if (nLevel < 0)
+ nLevel = 0;
+
+ if (nLevel >= MAXLEVEL)
+ nLevel = MAXLEVEL - 1;
+
+ const SwNumFormat& rNumFormat = pRule->Get( static_cast< sal_uInt16 >(nLevel) );
+
+ // #i86652#
+ if ( rNumFormat.GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ leftMargin.SetTextLeft(leftMargin.GetLeft(firstLine) + rNumFormat.GetAbsLSpace());
+ }
+ else
+ {
+ leftMargin.SetTextLeft(leftMargin.GetLeft(firstLine) + rNumFormat.GetIndentAt());
+ }
+
+ // new first line indent = 0
+ // (first line indent is ignored)
+ if (!bParaRTL)
+ {
+ firstLine.SetTextFirstLineOffset(0);
+ }
+
+ // put back the new item
+ oTmpSet->Put(firstLine);
+ oTmpSet->Put(leftMargin);
+
+ // assure that numbering rule is in <oTmpSet>
+ if (SfxItemState::SET != oTmpSet->GetItemState(RES_PARATR_NUMRULE, false) )
+ {
+ oTmpSet->Put( SwNumRuleItem( pRule->GetName() ));
+ }
+ }
+
+ // #i75457#
+ // Export page break after attribute from paragraph style.
+ // If page break attribute at the text node exist, an existing page
+ // break after at the paragraph style hasn't got to be considered.
+ if ( !rNode.GetpSwAttrSet() ||
+ SfxItemState::SET != rNode.GetpSwAttrSet()->GetItemState(RES_BREAK, false) )
+ {
+ const SvxFormatBreakItem& rBreakAtParaStyle
+ = rNode.GetSwAttrSet().Get(RES_BREAK);
+ if (rBreakAtParaStyle.GetBreak() == SvxBreak::PageAfter)
+ {
+ if ( !oTmpSet )
+ oTmpSet.emplace(rNode.GetSwAttrSet());
+ oTmpSet->Put(rBreakAtParaStyle);
+ }
+ else if( oTmpSet )
+ { // Even a pagedesc item is set, the break item can be set 'NONE',
+ // this has to be overruled.
+ const SwFormatPageDesc& rPageDescAtParaStyle =
+ rNode.GetAttr( RES_PAGEDESC );
+ if( rPageDescAtParaStyle.KnowsPageDesc() )
+ oTmpSet->ClearItem( RES_BREAK );
+ }
+ }
+
+ // #i76520# Emulate non-splitting tables
+ if ( IsInTable() )
+ {
+ const SwTableNode* pTableNode = rNode.FindTableNode();
+
+ if ( pTableNode )
+ {
+ const SwTable& rTable = pTableNode->GetTable();
+ const SvxFormatKeepItem& rKeep = rTable.GetFrameFormat()->GetKeep();
+ const bool bKeep = rKeep.GetValue();
+ const bool bDontSplit = !(bKeep ||
+ rTable.GetFrameFormat()->GetLayoutSplit().GetValue());
+
+ if ( bKeep || bDontSplit )
+ {
+ // bKeep: set keep at first paragraphs in all lines
+ // bDontSplit : set keep at first paragraphs in all lines except from last line
+ // but only for non-complex tables
+ const SwTableBox* pBox = rNode.GetTableBox();
+ const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
+
+ if ( pLine && !pLine->GetUpper() )
+ {
+ // check if box is first in that line:
+ if ( 0 == pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
+ {
+ // check if paragraph is first in that line:
+ if ( SwNodeOffset(1) == ( rNode.GetIndex() - pBox->GetSttNd()->GetIndex() ) )
+ {
+ bool bSetAtPara = false;
+ if ( bKeep )
+ bSetAtPara = true;
+ else if ( bDontSplit )
+ {
+ // check if pLine isn't last line in table
+ if ( rTable.GetTabLines().size() - rTable.GetTabLines().GetPos( pLine ) != 1 )
+ bSetAtPara = true;
+ }
+
+ if ( bSetAtPara )
+ {
+ if ( !oTmpSet )
+ oTmpSet.emplace(rNode.GetSwAttrSet());
+
+ const SvxFormatKeepItem aKeepItem( true, RES_KEEP );
+ oTmpSet->Put( aKeepItem );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ const SfxItemSet* pNewSet = oTmpSet ? &*oTmpSet : rNode.GetpSwAttrSet();
+ if( pNewSet )
+ { // Para-Attrs
+ m_pStyAttr = &rNode.GetAnyFormatColl().GetAttrSet();
+
+ const sw::BroadcastingModify* pOldMod = m_pOutFormatNode;
+ m_pOutFormatNode = &rNode;
+
+ // Pap-Attrs, so script is not necessary
+ OutputItemSet( *pNewSet, true, false, i18n::ScriptType::LATIN, false);
+
+ m_pStyAttr = nullptr;
+ m_pOutFormatNode = pOldMod;
+ }
+ }
+
+ // The formatting of the paragraph marker has two sources:
+ // 0) If there is a RES_PARATR_LIST_AUTOFMT, then use that.
+ // 1) If there are hints at the end of the paragraph, then use that.
+ // 2) Else use the RES_CHRATR_BEGIN..RES_TXTATR_END range of the paragraph
+ // properties.
+ //
+ // Exception: if there is a character style hint at the end of the
+ // paragraph only, then still go with 2), as RES_TXTATR_CHARFMT is always
+ // set as a hint.
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_TXTATR_END> aParagraphMarkerProperties(m_rDoc.GetAttrPool());
+ bool bCharFormatOnly = true;
+
+ SwFormatAutoFormat const& rListAutoFormat(rNode.GetAttr(RES_PARATR_LIST_AUTOFMT));
+ if (std::shared_ptr<SfxItemSet> const& pSet = rListAutoFormat.GetStyleHandle())
+ {
+ aParagraphMarkerProperties.Put(*pSet);
+ bCharFormatOnly = false;
+ }
+ else if (const SwpHints* pTextAttrs = rNode.GetpSwpHints())
+ {
+ for( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ const sal_Int32 startPos = pHt->GetStart(); // first Attr characters
+ const sal_Int32* endPos = pHt->End(); // end Attr characters
+ // Check if these attributes are for the last character in the paragraph
+ // - which means the paragraph marker. If a paragraph has 7 characters,
+ // then properties on character 8 are for the paragraph marker
+ if( endPos && (startPos == *endPos ) && (*endPos == rNode.GetText().getLength()) )
+ {
+ SAL_INFO( "sw.ww8", startPos << "startPos == endPos" << *endPos);
+ sal_uInt16 nWhich = pHt->GetAttr().Which();
+ SAL_INFO( "sw.ww8", "nWhich" << nWhich);
+ if ((nWhich == RES_TXTATR_AUTOFMT && bCharFormatOnly)
+ || nWhich == RES_TXTATR_CHARFMT)
+ {
+ aParagraphMarkerProperties.Put(pHt->GetAttr());
+ }
+ if (nWhich != RES_TXTATR_CHARFMT)
+ bCharFormatOnly = false;
+ }
+ }
+ }
+ if (rNode.GetpSwAttrSet() && bCharFormatOnly)
+ {
+ aParagraphMarkerProperties.Put(*rNode.GetpSwAttrSet());
+ }
+ const SwRedlineData* pRedlineParagraphMarkerDelete = AttrOutput().GetParagraphMarkerRedline( rNode, RedlineType::Delete );
+ const SwRedlineData* pRedlineParagraphMarkerInsert = AttrOutput().GetParagraphMarkerRedline( rNode, RedlineType::Insert );
+ const SwRedlineData* pParagraphRedlineData = aAttrIter.GetParagraphLevelRedline( );
+ AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, pParagraphRedlineData, pRedlineParagraphMarkerDelete, pRedlineParagraphMarkerInsert);
+
+ AttrOutput().EndParagraph( pTextNodeInfoInner );
+ }while(*aBreakIt != rNode.GetText().getLength() && bNeedParaSplit );
+
+ SAL_INFO( "sw.ww8", "</OutWW8_SwTextNode>" );
+}
+
+// Tables
+
+void WW8AttributeOutput::EmptyParagraph()
+{
+ m_rWW8Export.WriteStringAsPara( OUString() );
+}
+
+bool MSWordExportBase::NoPageBreakSection( const SfxItemSet* pSet )
+{
+ bool bRet = false;
+ if( pSet)
+ {
+ bool bNoPageBreak = false;
+ const SwFormatPageDesc* pDescItem = pSet->GetItemIfSet(RES_PAGEDESC);
+ if ( !pDescItem || nullptr == pDescItem->GetPageDesc() )
+ {
+ bNoPageBreak = true;
+ }
+
+ if (bNoPageBreak)
+ {
+ if (const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet(RES_BREAK))
+ {
+ SvxBreak eBreak = pBreakItem->GetBreak();
+ switch (eBreak)
+ {
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageAfter:
+ bNoPageBreak = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ bRet = bNoPageBreak;
+ }
+ return bRet;
+}
+
+void MSWordExportBase::OutputSectionNode( const SwSectionNode& rSectionNode )
+{
+ const SwSection& rSection = rSectionNode.GetSection();
+
+ SwNodeIndex aIdx( rSectionNode, 1 );
+ const SwNode& rNd = aIdx.GetNode();
+ if ( !rNd.IsSectionNode() && !IsInTable() ) //No sections in table
+ {
+ // if the first Node inside the section has an own
+ // PageDesc or PageBreak attribute, then don't write
+ // here the section break
+ sal_uLong nRstLnNum = 0;
+ const SfxItemSet* pSet;
+ if ( rNd.IsContentNode() )
+ {
+ pSet = &rNd.GetContentNode()->GetSwAttrSet();
+ nRstLnNum = pSet->Get( RES_LINENUMBER ).GetStartValue();
+ }
+ else
+ pSet = nullptr;
+
+ if ( pSet && NoPageBreakSection( pSet ) )
+ pSet = nullptr;
+ else
+ AttrOutput().SectionBreaks( rSectionNode );
+
+ const bool bInTOX = rSection.GetType() == SectionType::ToxContent || rSection.GetType() == SectionType::ToxHeader;
+ if ( !pSet && !bInTOX )
+ {
+ // new Section with no own PageDesc/-Break
+ // -> write follow section break;
+ const SwSectionFormat* pFormat = rSection.GetFormat();
+ ReplaceCr( msword::PageBreak ); // Indicator for Page/Section-Break
+
+ // Get the page in use at the top of this section
+ const SwPageDesc *pCurrent = SwPageDesc::GetPageDescOfNode(rNd);
+ if (!pCurrent)
+ pCurrent = m_pCurrentPageDesc;
+
+ AppendSection( pCurrent, pFormat, nRstLnNum );
+ }
+ }
+ if ( SectionType::ToxContent == rSection.GetType() )
+ {
+ m_bStartTOX = true;
+ UpdateTocSectionNodeProperties(rSectionNode);
+ }
+}
+
+// tdf#121561: During export of the ODT file with TOC inside into DOCX format,
+// the TOC title is being exported as regular paragraph. We should surround it
+// with <w:sdt><w:sdtPr><w:sdtContent> to make it (TOC title) recognizable
+// by MS Word as part of the TOC.
+void MSWordExportBase::UpdateTocSectionNodeProperties(const SwSectionNode& rSectionNode)
+{
+ // check section type
+ {
+ const SwSection& rSection = rSectionNode.GetSection();
+ if (SectionType::ToxContent != rSection.GetType())
+ return;
+
+ const SwTOXBase* pTOX = rSection.GetTOXBase();
+ if (pTOX)
+ {
+ TOXTypes type = pTOX->GetType();
+ if (type != TOXTypes::TOX_CONTENT)
+ return;
+ }
+ }
+
+ // get section node, skip toc-header node
+ const SwSectionNode* pSectNd = &rSectionNode;
+ {
+ SwNodeIndex aIdxNext( *pSectNd, 1 );
+ const SwNode& rNdNext = aIdxNext.GetNode();
+
+ if (rNdNext.IsSectionNode())
+ {
+ const SwSectionNode* pSectNdNext = static_cast<const SwSectionNode*>(&rNdNext);
+ if (SectionType::ToxHeader == pSectNdNext->GetSection().GetType() &&
+ pSectNdNext->StartOfSectionNode()->IsSectionNode())
+ {
+ pSectNd = pSectNdNext;
+ }
+ }
+ }
+
+ // get node of the first paragraph inside TOC
+ SwNodeIndex aIdxNext( *pSectNd, 1 );
+ const SwNode& rNdTocPara = aIdxNext.GetNode();
+ const SwContentNode* pNode = rNdTocPara.GetContentNode();
+ if (!pNode)
+ return;
+
+ // put required flags into grab bag of the first node in TOC
+ {
+ uno::Sequence<beans::PropertyValue> aDocPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"ooxml:CT_SdtDocPart_docPartGallery", uno::Any(OUString("Table of Contents"))},
+ {"ooxml:CT_SdtDocPart_docPartUnique", uno::Any(OUString("true"))},
+ }));
+
+ uno::Sequence<beans::PropertyValue> aSdtPrPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"ooxml:CT_SdtPr_docPartObj", uno::Any(aDocPropertyValues)},
+ }));
+
+ SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG);
+ aGrabBag.GetGrabBag()["SdtPr"] <<= aSdtPrPropertyValues;
+
+ // create temp attr set
+ SwAttrSet aSet(pNode->GetSwAttrSet());
+ aSet.Put(aGrabBag);
+
+ // set new attr to node
+ const_cast<SwContentNode*>(pNode)->SetAttr(aSet);
+ }
+
+ // set flag for the next node after TOC
+ // in order to indicate that std area has been finished
+ // see, DomainMapper::lcl_startParagraphGroup() for the same functionality during load
+ {
+ SwNodeIndex aEndTocNext( *rSectionNode.EndOfSectionNode(), 1 );
+ const SwNode& rEndTocNextNode = aEndTocNext.GetNode();
+ const SwContentNode* pNodeAfterToc = rEndTocNextNode.GetContentNode();
+ if (pNodeAfterToc)
+ {
+ SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG);
+ aGrabBag.GetGrabBag()["ParaSdtEndBefore"] <<= true;
+
+ // create temp attr set
+ SwAttrSet aSet(pNodeAfterToc->GetSwAttrSet());
+ aSet.Put(aGrabBag);
+
+ // set new attr to node
+ const_cast<SwContentNode*>(pNodeAfterToc)->SetAttr(aSet);
+ }
+ }
+}
+
+void WW8Export::AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum )
+{
+ m_pSepx->AppendSep(Fc2Cp(Strm().Tell()), pPageDesc, pFormat, nLnNum);
+}
+
+// Flys
+
+void WW8AttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft )
+{
+ const SwFrameFormat &rFrameFormat = rFormat.GetFrameFormat();
+ const SwFormatAnchor& rAnch = rFrameFormat.GetAnchor();
+
+ bool bUseEscher = true;
+
+ if (rFormat.IsInline())
+ {
+ ww8::Frame::WriterSource eType = rFormat.GetWriterType();
+ bUseEscher = eType != ww8::Frame::eGraphic && eType != ww8::Frame::eOle;
+
+ /*
+ A special case for converting some inline form controls to form fields
+ when in winword 8+ mode
+ */
+ if (bUseEscher && (eType == ww8::Frame::eFormControl))
+ {
+ if ( m_rWW8Export.MiserableFormFieldExportHack( rFrameFormat ) )
+ return ;
+ }
+ }
+
+ if (bUseEscher)
+ {
+ // write as escher
+ if (rFrameFormat.GetFlySplit().GetValue())
+ {
+ // The frame can split: this was originally from a floating table, write it back as
+ // such.
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
+ SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+ m_rWW8Export.SaveData(nStt, nEnd);
+ GetExport().WriteText();
+ m_rWW8Export.RestoreData();
+ }
+ else
+ {
+ m_rWW8Export.AppendFlyInFlys(rFormat, rNdTopLeft);
+ }
+ }
+ else
+ {
+ bool bDone = false;
+
+ // Fetch from node and last node the position in the section
+ const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
+
+ SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex()+1 : SwNodeOffset(0);
+ SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
+
+ if( nStt >= nEnd ) // no range, hence no valid node
+ return;
+
+ if ( !m_rWW8Export.IsInTable() && rFormat.IsInline() )
+ {
+ //Test to see if this textbox contains only a single graphic/ole
+ SwTextNode* pParTextNode = rAnch.GetAnchorNode()->GetTextNode();
+ if ( pParTextNode && !m_rWW8Export.m_rDoc.GetNodes()[ nStt ]->IsNoTextNode() )
+ bDone = true;
+ }
+ if( !bDone )
+ {
+
+ m_rWW8Export.SaveData( nStt, nEnd );
+
+ Point aOffset;
+ if ( m_rWW8Export.m_pParentFrame )
+ {
+ /* Munge flys in fly into absolutely positioned elements for word 6 */
+ const SwTextNode* pParTextNode = rAnch.GetAnchorNode()->GetTextNode();
+ const SwRect aPageRect = pParTextNode->FindPageFrameRect();
+
+ aOffset = rFrameFormat.FindLayoutRect().Pos();
+ aOffset -= aPageRect.Pos();
+
+ m_rWW8Export.m_pFlyOffset = &aOffset;
+ m_rWW8Export.m_eNewAnchorType = RndStdIds::FLY_AT_PAGE;
+ }
+
+ m_rWW8Export.m_pParentFrame = &rFormat;
+ if (
+ m_rWW8Export.IsInTable() &&
+ (RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId()) &&
+ !m_rWW8Export.m_rDoc.GetNodes()[ nStt ]->IsNoTextNode()
+ )
+ {
+ // note: set Flag bOutTable again,
+ // because we deliver the normal content of the table cell, and no border
+ // ( Flag was deleted above in aSaveData() )
+ m_rWW8Export.m_bOutTable = true;
+ const OUString& aName = rFrameFormat.GetName();
+ m_rWW8Export.StartCommentOutput(aName);
+ m_rWW8Export.WriteText();
+ m_rWW8Export.EndCommentOutput(aName);
+ }
+ else
+ m_rWW8Export.WriteText();
+
+ m_rWW8Export.RestoreData();
+ }
+ }
+}
+
+void AttributeOutputBase::OutputFlyFrame( const ww8::Frame& rFormat )
+{
+ if ( !rFormat.GetContentNode() )
+ return;
+
+ const SwContentNode &rNode = *rFormat.GetContentNode();
+ Point aLayPos;
+
+ // get the Layout Node-Position
+ if (RndStdIds::FLY_AT_PAGE == rFormat.GetFrameFormat().GetAnchor().GetAnchorId())
+ aLayPos = rNode.FindPageFrameRect().Pos();
+ else
+ aLayPos = rNode.FindLayoutRect().Pos();
+
+ OutputFlyFrame_Impl( rFormat, aLayPos );
+}
+
+// write data of any redline
+void WW8AttributeOutput::Redline( const SwRedlineData* pRedline )
+{
+ if ( !pRedline )
+ return;
+
+ if ( pRedline->Next() )
+ Redline( pRedline->Next() );
+
+ static const sal_uInt16 insSprmIds[ 3 ] =
+ {
+ // Ids for insert // for WW8
+ NS_sprm::CFRMarkIns::val, NS_sprm::CIbstRMark::val, NS_sprm::CDttmRMark::val,
+ };
+ static const sal_uInt16 delSprmIds[ 3 ] =
+ {
+ // Ids for delete // for WW8
+ NS_sprm::CFRMarkDel::val, NS_sprm::CIbstRMarkDel::val, NS_sprm::CDttmRMarkDel::val,
+ };
+
+ const sal_uInt16* pSprmIds = nullptr;
+ switch( pRedline->GetType() )
+ {
+ case RedlineType::Insert:
+ pSprmIds = insSprmIds;
+ break;
+
+ case RedlineType::Delete:
+ pSprmIds = delSprmIds;
+ break;
+
+ case RedlineType::Format:
+ m_rWW8Export.InsUInt16( NS_sprm::CPropRMark90::val );
+ m_rWW8Export.m_pO->push_back( 7 ); // len
+ m_rWW8Export.m_pO->push_back( 1 );
+ m_rWW8Export.InsUInt16( m_rWW8Export.AddRedlineAuthor( pRedline->GetAuthor() ) );
+ m_rWW8Export.InsUInt32( sw::ms::DateTime2DTTM( pRedline->GetTimeStamp() ));
+ break;
+ default:
+ OSL_ENSURE(false, "Unhandled redline type for export");
+ break;
+ }
+
+ if ( pSprmIds )
+ {
+ m_rWW8Export.InsUInt16( pSprmIds[0] );
+ m_rWW8Export.m_pO->push_back( 1 );
+
+ m_rWW8Export.InsUInt16( pSprmIds[1] );
+ m_rWW8Export.InsUInt16( m_rWW8Export.AddRedlineAuthor( pRedline->GetAuthor() ) );
+
+ m_rWW8Export.InsUInt16( pSprmIds[2] );
+ m_rWW8Export.InsUInt32( sw::ms::DateTime2DTTM( pRedline->GetTimeStamp() ));
+ }
+}
+
+void MSWordExportBase::OutputContentNode( SwContentNode& rNode )
+{
+ switch ( rNode.GetNodeType() )
+ {
+ case SwNodeType::Text:
+ OutputTextNode( *rNode.GetTextNode() );
+ break;
+ case SwNodeType::Grf:
+ OutputGrfNode( *rNode.GetGrfNode() );
+ break;
+ case SwNodeType::Ole:
+ OutputOLENode( *rNode.GetOLENode() );
+ break;
+ default:
+ SAL_WARN("sw.ww8", "Unhandled node, type == " << static_cast<int>(rNode.GetNodeType()) );
+ break;
+ }
+}
+
+
+WW8Ruby::WW8Ruby(const SwTextNode& rNode, const SwFormatRuby& rRuby, const MSWordExportBase& rExport ):
+ m_nJC(0),
+ m_cDirective(0),
+ m_nRubyHeight(0),
+ m_nBaseHeight(0)
+{
+ switch ( rRuby.GetAdjustment() )
+ {
+ case css::text::RubyAdjust_LEFT:
+ m_nJC = 3;
+ m_cDirective = 'l';
+ break;
+ case css::text::RubyAdjust_CENTER:
+ //defaults to 0
+ break;
+ case css::text::RubyAdjust_RIGHT:
+ m_nJC = 4;
+ m_cDirective = 'r';
+ break;
+ case css::text::RubyAdjust_BLOCK:
+ m_nJC = 1;
+ m_cDirective = 'd';
+ break;
+ case css::text::RubyAdjust_INDENT_BLOCK:
+ m_nJC = 2;
+ m_cDirective = 'd';
+ break;
+ default:
+ OSL_ENSURE( false,"Unhandled Ruby justification code" );
+ break;
+ }
+
+ if ( rRuby.GetPosition() == css::text::RubyPosition::INTER_CHARACTER )
+ {
+ m_nJC = 5;
+ m_cDirective = 0;
+ }
+
+ /*
+ MS needs to know the name and size of the font used in the ruby item,
+ but we could have written it in a mixture of asian and western
+ scripts, and each of these can be a different font and size than the
+ other, so we make a guess based upon the first character of the text,
+ defaulting to asian.
+ */
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_uInt16 nRubyScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0);
+
+ const SwTextRuby* pRubyText = rRuby.GetTextRuby();
+ const SwCharFormat* pFormat = pRubyText ? pRubyText->GetCharFormat() : nullptr;
+
+ if (pFormat)
+ {
+ const auto& rFont
+ = pFormat->GetFormatAttr( GetWhichOfScript(RES_CHRATR_FONT, nRubyScript) );
+ m_sFontFamily = rFont.GetFamilyName();
+
+ const auto& rHeight =
+ pFormat->GetFormatAttr( GetWhichOfScript(RES_CHRATR_FONTSIZE, nRubyScript) );
+ m_nRubyHeight = rHeight.GetHeight();
+ }
+ else
+ {
+ /*Get defaults if no formatting on ruby text*/
+
+ const SfxItemPool* pPool = rNode.GetSwAttrSet().GetPool();
+ pPool = pPool ? pPool : &rExport.m_rDoc.GetAttrPool();
+
+
+ const SvxFontItem& rFont
+ = pPool->GetDefaultItem( GetWhichOfScript(RES_CHRATR_FONT, nRubyScript) );
+ m_sFontFamily = rFont.GetFamilyName();
+
+ const SvxFontHeightItem& rHeight =
+ pPool->GetDefaultItem( GetWhichOfScript(RES_CHRATR_FONTSIZE, nRubyScript));
+ m_nRubyHeight = rHeight.GetHeight();
+ }
+
+ const OUString &rText = rNode.GetText();
+ sal_uInt16 nScript = i18n::ScriptType::LATIN;
+
+ if (!rText.isEmpty())
+ nScript = g_pBreakIt->GetBreakIter()->getScriptType(rText, 0);
+
+ TypedWhichId<SvxFontHeightItem> nWhich = GetWhichOfScript(RES_CHRATR_FONTSIZE, nScript);
+ const SvxFontHeightItem& rHeightItem = rExport.GetItem(nWhich);
+ m_nBaseHeight = rHeightItem.GetHeight();
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtw8num.cxx b/sw/source/filter/ww8/wrtw8num.cxx
new file mode 100644
index 0000000000..8d59434db6
--- /dev/null
+++ b/sw/source/filter/ww8/wrtw8num.cxx
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <vcl/font.hxx>
+#include <editeng/langitem.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <numrule.hxx>
+#include <charfmt.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include "sprmids.hxx"
+
+#include "ww8attributeoutput.hxx"
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "wrtww8.hxx"
+#include "ww8par.hxx"
+
+using namespace ::com::sun::star;
+using namespace sw::types;
+using namespace sw::util;
+
+SwNumRule* MSWordExportBase::DuplicateNumRuleImpl(const SwNumRule *pRule)
+{
+ const OUString sPrefix("WW8TempExport" + OUString::number( m_nUniqueList++ ));
+ SwNumRule* pMyNumRule =
+ new SwNumRule( m_rDoc.GetUniqueNumRuleName( &sPrefix ),
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION );
+ m_pUsedNumTable->push_back( pMyNumRule );
+
+ for ( sal_uInt16 i = 0; i < MAXLEVEL; i++ )
+ {
+ const SwNumFormat& rSubRule = pRule->Get(i);
+ pMyNumRule->Set( i, rSubRule );
+ }
+ return pMyNumRule;
+}
+
+sal_uInt16 MSWordExportBase::DuplicateNumRule(const SwNumRule* pRule, sal_uInt8 nLevel, sal_uInt16 nVal)
+{
+ SwNumRule* const pMyNumRule = DuplicateNumRuleImpl(pRule);
+
+ SwNumFormat aNumFormat(pMyNumRule->Get(nLevel));
+ aNumFormat.SetStart(nVal);
+ pMyNumRule->Set(nLevel, aNumFormat);
+
+ return GetNumberingId(*pMyNumRule);
+}
+
+// multiple SwList can be based on the same SwNumRule; ensure one w:abstractNum
+// per SwList
+sal_uInt16 MSWordExportBase::DuplicateAbsNum(OUString const& rListId,
+ SwNumRule const& rAbstractRule)
+{
+ auto const it(m_Lists.find(rListId));
+ if (it != m_Lists.end())
+ {
+ return it->second;
+ }
+ else
+ {
+ auto const pNewAbstractRule = DuplicateNumRuleImpl(&rAbstractRule);
+ assert(GetNumberingId(*pNewAbstractRule) == m_pUsedNumTable->size() - 1);
+ (void) pNewAbstractRule;
+ m_Lists.insert(std::make_pair(rListId, m_pUsedNumTable->size() - 1));
+ return m_pUsedNumTable->size() - 1;
+ }
+}
+
+// Ideally we want to map SwList to w:abstractNum and SwNumRule to w:num
+// The current approach is to keep exporting every SwNumRule to
+// 1 w:abstractNum and 1 w:num, and then add extra w:num via this function
+// that reference an existing w:abstractNum and may override its formatting;
+// of course this will end up exporting some w:num that aren't actually used.
+sal_uInt16 MSWordExportBase::OverrideNumRule(
+ SwNumRule const& rExistingRule,
+ OUString const& rListId,
+ SwNumRule const& rAbstractRule)
+{
+ const sal_uInt16 numdef = GetNumberingId(rExistingRule);
+
+ const sal_uInt16 absnumdef = rListId == rAbstractRule.GetDefaultListId()
+ ? GetNumberingId(rAbstractRule)
+ : DuplicateAbsNum(rListId, rAbstractRule);
+ assert(numdef != USHRT_MAX);
+ assert(absnumdef != USHRT_MAX);
+ auto const mapping = std::make_pair(numdef, absnumdef);
+
+ auto it = m_OverridingNums.insert(std::make_pair(m_pUsedNumTable->size(), mapping));
+
+ m_pUsedNumTable->push_back(nullptr); // dummy, it's unique_ptr...
+ ++m_nUniqueList; // counter for DuplicateNumRule...
+
+ return it.first->first;
+}
+
+void MSWordExportBase::AddListLevelOverride(sal_uInt16 nListId,
+ sal_uInt16 nLevelNum,
+ sal_uInt16 nStartAt)
+{
+ m_ListLevelOverrides[nListId][nLevelNum] = nStartAt;
+}
+
+sal_uInt16 MSWordExportBase::GetNumberingId( const SwNumRule& rNumRule )
+{
+ if ( !m_pUsedNumTable )
+ {
+ m_pUsedNumTable.reset(new SwNumRuleTable);
+ m_pUsedNumTable->insert( m_pUsedNumTable->begin(), m_rDoc.GetNumRuleTable().begin(), m_rDoc.GetNumRuleTable().end() );
+ // Check, if the outline rule is already inserted into <pUsedNumTable>.
+ // If yes, do not insert it again.
+ bool bOutlineRuleAdded( false );
+ for ( sal_uInt16 n = m_pUsedNumTable->size(); n; )
+ {
+ const SwNumRule& rRule = *(*m_pUsedNumTable)[ --n ];
+ if (!m_rDoc.IsUsed(rRule))
+ {
+ m_pUsedNumTable->erase( m_pUsedNumTable->begin() + n );
+ }
+ else if ( &rRule == m_rDoc.GetOutlineNumRule() )
+ {
+ bOutlineRuleAdded = true;
+ }
+ }
+
+ if ( !bOutlineRuleAdded )
+ {
+ // still need to paste the OutlineRule
+ SwNumRule* pR = m_rDoc.GetOutlineNumRule();
+ m_pUsedNumTable->push_back( pR );
+ }
+ }
+ SwNumRule* p = const_cast<SwNumRule*>(&rNumRule);
+ sal_uInt16 nRet = o3tl::narrowing<sal_uInt16>(m_pUsedNumTable->GetPos(p));
+
+ return nRet;
+}
+
+// GetFirstLineOffset should problem never appear unadorned apart from
+// here in the ww export filter
+sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat)
+{
+ OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
+ "<GetWordFirstLineOffset> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
+
+ short nFirstLineOffset;
+ if (rFormat.GetNumAdjust() == SvxAdjust::Right)
+ nFirstLineOffset = -rFormat.GetCharTextDistance();
+ else
+ nFirstLineOffset = rFormat.GetFirstLineOffset(); //TODO: overflow
+ return nFirstLineOffset;
+}
+
+void WW8Export::WriteNumbering()
+{
+ if ( !m_pUsedNumTable )
+ return; // no numbering is used
+
+ // list formats - LSTF
+ m_pFib->m_fcPlcfLst = m_pTableStrm->Tell();
+ m_pTableStrm->WriteUInt16( m_pUsedNumTable->size() );
+ NumberingDefinitions();
+ // set len to FIB
+ m_pFib->m_lcbPlcfLst = m_pTableStrm->Tell() - m_pFib->m_fcPlcfLst;
+
+ // list formats - LVLF
+ AbstractNumberingDefinitions();
+
+ // list formats - LFO
+ OutOverrideListTab();
+
+ // list formats - ListNames
+ OutListNamesTab();
+}
+
+void WW8AttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule )
+{
+ m_rWW8Export.m_pTableStrm->WriteUInt32( nId );
+ m_rWW8Export.m_pTableStrm->WriteUInt32( nId );
+
+ // not associated with a Style
+ for ( int i = 0; i < WW8ListManager::nMaxLevel; ++i )
+ m_rWW8Export.m_pTableStrm->WriteUInt16( 0xFFF );
+
+ sal_uInt8 nFlags = 0;
+ if ( rRule.IsContinusNum() )
+ nFlags |= 0x1;
+
+ m_rWW8Export.m_pTableStrm->WriteUChar( nFlags ).WriteUChar( 0/*nDummy*/ );
+}
+
+void MSWordExportBase::NumberingDefinitions()
+{
+ if ( !m_pUsedNumTable )
+ return; // no numbering is used
+
+ sal_uInt16 nCount = m_pUsedNumTable->size();
+
+ // Write static data of SwNumRule - LSTF
+ for ( sal_uInt16 n = 0; n < nCount; ++n )
+ {
+ const SwNumRule * pRule = (*m_pUsedNumTable)[ n ];
+ if (pRule)
+ {
+ AttrOutput().NumberingDefinition(n + 1, *pRule);
+ }
+ else
+ {
+ auto it = m_OverridingNums.find(n);
+ assert(it != m_OverridingNums.end());
+ pRule = (*m_pUsedNumTable)[it->second.first];
+ assert(pRule);
+ AttrOutput().OverrideNumberingDefinition(*pRule, n + 1, it->second.second + 1, m_ListLevelOverrides[n]);
+ }
+ }
+}
+
+/**
+ * Converts the SVX numbering type to MSONFC.
+ *
+ * This is used for special paragraph numbering considerations.
+ */
+static sal_uInt8 GetLevelNFC(sal_uInt16 eNumType, const SfxItemSet* pOutSet, sal_uInt8 nDefault)
+{
+ sal_uInt8 nRet = nDefault;
+ switch( eNumType )
+ {
+ case SVX_NUM_NUMBER_LOWER_ZH:
+ nRet = 35;
+ if ( pOutSet ) {
+ const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE);
+ const LanguageType eLang = rLang.GetLanguage();
+ if (LANGUAGE_CHINESE_SIMPLIFIED ==eLang) {
+ nRet = 39;
+ }
+ }
+ break;
+
+ // LVLF can't contain 0x08, msonfcHex.
+ case style::NumberingType::SYMBOL_CHICAGO:
+ // No SVX_NUM_SYMBOL_CHICAGO here: LVLF can't contain 0x09, msonfcChiManSty.
+ nRet = 0;
+ break;
+ // LVLF can't contain 0x0F / 15, msonfcSbChar / decimalHalfWidth.
+ // LVLF can't contain 0x13 / 19, msonfcDArabic / decimalFullWidth2
+ }
+ return nRet;
+}
+
+
+void WW8AttributeOutput::NumberingLevel( sal_uInt8 /*nLevel*/,
+ sal_uInt16 nStart,
+ sal_uInt16 nNumberingType,
+ SvxAdjust eAdjust,
+ const sal_uInt8 *pNumLvlPos,
+ sal_uInt8 nFollow,
+ const wwFont *pFont,
+ const SfxItemSet *pOutSet,
+ sal_Int16 nIndentAt,
+ sal_Int16 nFirstLineIndex,
+ sal_Int16 nListTabPos,
+ const OUString &rNumberingString,
+ const SvxBrushItem* pBrush, //For i120928,to transfer graphic of bullet
+ bool /*isLegal*/
+ )
+{
+ // Start value
+ m_rWW8Export.m_pTableStrm->WriteUInt32( nStart );
+
+ // Type
+ sal_uInt8 nNumId = GetLevelNFC(nNumberingType, pOutSet, WW8Export::GetNumId(nNumberingType));
+ m_rWW8Export.m_pTableStrm->WriteUChar(nNumId);
+
+ // Justification
+ sal_uInt8 nAlign;
+ switch ( eAdjust )
+ {
+ case SvxAdjust::Center:
+ nAlign = 1;
+ break;
+ case SvxAdjust::Right:
+ nAlign = 2;
+ break;
+ default:
+ nAlign = 0;
+ break;
+ }
+ m_rWW8Export.m_pTableStrm->WriteUChar( nAlign );
+
+ // Write the rgbxchNums[9], positions of placeholders for paragraph
+ // numbers in the text
+ m_rWW8Export.m_pTableStrm->WriteBytes(pNumLvlPos, WW8ListManager::nMaxLevel);
+
+ // Type of the character between the bullet and the text
+ m_rWW8Export.m_pTableStrm->WriteUChar( nFollow );
+
+ // dxaSoace/dxaIndent (Word 6 compatibility)
+ m_rWW8Export.m_pTableStrm->WriteUInt32( 0 );
+ m_rWW8Export.m_pTableStrm->WriteUInt32( 0 );
+
+ // cbGrpprlChpx
+ std::unique_ptr<ww::bytes> pCharAtrs;
+ if ( pOutSet )
+ {
+ std::unique_ptr<ww::bytes> pOldpO = std::move(m_rWW8Export.m_pO);
+ m_rWW8Export.m_pO.reset(new ww::bytes);
+ if ( pFont )
+ {
+ sal_uInt16 nFontID = m_rWW8Export.m_aFontHelper.GetId( *pFont );
+
+ m_rWW8Export.InsUInt16( NS_sprm::CRgFtc0::val );
+ m_rWW8Export.InsUInt16( nFontID );
+ m_rWW8Export.InsUInt16( NS_sprm::CRgFtc2::val );
+ m_rWW8Export.InsUInt16( nFontID );
+ }
+
+ m_rWW8Export.OutputItemSet( *pOutSet, false, true, i18n::ScriptType::LATIN, m_rWW8Export.m_bExportModeRTF );
+ //For i120928,achieve graphic's index of bullet from the bullet bookmark
+ if (SVX_NUM_BITMAP == nNumberingType && pBrush)
+ {
+ int nIndex = m_rWW8Export.GetGrfIndex(*pBrush);
+ if ( nIndex != -1 )
+ {
+ m_rWW8Export.InsUInt16(NS_sprm::CPbiIBullet::val);
+ m_rWW8Export.InsUInt32(nIndex);
+ m_rWW8Export.InsUInt16(NS_sprm::CPbiGrf::val);
+ m_rWW8Export.InsUInt16(1);
+ }
+ }
+
+ pCharAtrs = std::move(m_rWW8Export.m_pO);
+ m_rWW8Export.m_pO = std::move(pOldpO);
+ }
+ m_rWW8Export.m_pTableStrm->WriteUChar(sal_uInt8(pCharAtrs ? pCharAtrs->size() : 0));
+
+ // cbGrpprlPapx
+ sal_uInt8 aPapSprms [] = {
+ 0x5e, 0x84, 0, 0, // sprmPDxaLeft
+ 0x60, 0x84, 0, 0, // sprmPDxaLeft1
+ 0x15, 0xc6, 0x05, 0x00, 0x01, 0, 0, 0x06
+ };
+ m_rWW8Export.m_pTableStrm->WriteUChar( sal_uInt8( sizeof( aPapSprms ) ) );
+
+ // reserved
+ m_rWW8Export.m_pTableStrm->WriteUInt16( 0 );
+
+ // pap sprms
+ sal_uInt8* pData = aPapSprms + 2;
+ Set_UInt16( pData, nIndentAt );
+ pData += 2;
+ Set_UInt16( pData, nFirstLineIndex );
+ pData += 5;
+ Set_UInt16( pData, nListTabPos );
+
+ m_rWW8Export.m_pTableStrm->WriteBytes(aPapSprms, sizeof(aPapSprms));
+
+ // write Chpx
+ if (pCharAtrs && !pCharAtrs->empty())
+ m_rWW8Export.m_pTableStrm->WriteBytes(pCharAtrs->data(), pCharAtrs->size());
+
+ // write the num string
+ m_rWW8Export.m_pTableStrm->WriteUInt16( rNumberingString.getLength() );
+ SwWW8Writer::WriteString16( *m_rWW8Export.m_pTableStrm, rNumberingString, false );
+}
+
+void MSWordExportBase::AbstractNumberingDefinitions()
+{
+ sal_uInt16 nCount = m_pUsedNumTable->size();
+ sal_uInt16 n;
+
+ for( n = 0; n < nCount; ++n )
+ {
+ if (nullptr == (*m_pUsedNumTable)[ n ])
+ {
+ continue;
+ }
+
+ AttrOutput().StartAbstractNumbering( n + 1 );
+
+ const SwNumRule& rRule = *(*m_pUsedNumTable)[ n ];
+ sal_uInt8 nLvl;
+ sal_uInt8 nLevels = static_cast< sal_uInt8 >(rRule.IsContinusNum() ?
+ WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel);
+ for( nLvl = 0; nLvl < nLevels; ++nLvl )
+ {
+ NumberingLevel(rRule, nLvl);
+ }
+
+ AttrOutput().EndAbstractNumbering();
+ }
+}
+
+void MSWordExportBase::NumberingLevel(
+ SwNumRule const& rRule, sal_uInt8 const nLvl)
+{
+ // write the static data of the SwNumFormat of this level
+ sal_uInt8 aNumLvlPos[WW8ListManager::nMaxLevel] = { 0,0,0,0,0,0,0,0,0 };
+
+ const SwNumFormat& rFormat = rRule.Get( nLvl );
+
+ sal_uInt8 nFollow = 0;
+ // #i86652#
+ if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION)
+ {
+ // <nFollow = 2>, if minimum label width equals 0 and
+ // minimum distance between label and text equals 0
+ nFollow = (rFormat.GetFirstLineOffset() == 0 &&
+ rFormat.GetCharTextDistance() == 0)
+ ? 2 : 0; // ixchFollow: 0 - tab, 1 - blank, 2 - nothing
+ }
+ else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT)
+ {
+ switch (rFormat.GetLabelFollowedBy())
+ {
+ case SvxNumberFormat::LISTTAB:
+ {
+ // 0 (tab) unless there would be no content before the tab, in which case 2 (nothing)
+ nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 0 : 2;
+ }
+ break;
+ case SvxNumberFormat::SPACE:
+ {
+ // 1 (space) unless there would be no content before the space in which case 2 (nothing)
+ nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 1 : 2;
+ }
+ break;
+ case SvxNumberFormat::NOTHING:
+ {
+ nFollow = 2;
+ }
+ break;
+ default:
+ {
+ nFollow = 0;
+ OSL_FAIL( "unknown GetLabelFollowedBy() return value" );
+ }
+ }
+ }
+
+ // Build the NumString for this Level
+ OUString sNumStr;
+ OUString sFontName;
+ bool bWriteBullet = false;
+ std::optional<vcl::Font> pBulletFont;
+ rtl_TextEncoding eChrSet=0;
+ FontFamily eFamily=FAMILY_DECORATIVE;
+ if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() ||
+ SVX_NUM_BITMAP == rFormat.GetNumberingType())
+ {
+ // Use bullet
+ sal_UCS4 cBullet = rFormat.GetBulletChar();
+ sNumStr = OUString(&cBullet, 1);
+ }
+ else
+ {
+ // Create level string
+ if (rFormat.HasListFormat())
+ {
+ sal_uInt8* pLvlPos = aNumLvlPos;
+ sNumStr = rFormat.GetListFormat();
+
+ // now search the nums in the string
+ for (sal_uInt8 i = 0; i <= nLvl; ++i)
+ {
+ OUString sSrch("%" + OUString::number(i+1) + "%");
+ sal_Int32 nFnd = sNumStr.indexOf(sSrch);
+ if (-1 != nFnd)
+ {
+ *pLvlPos = static_cast<sal_uInt8>(nFnd + 1);
+ ++pLvlPos;
+ sNumStr = sNumStr.replaceAt(nFnd, sSrch.getLength(), rtl::OUStringChar(static_cast<char>(i)));
+ }
+ }
+ }
+ else if (rFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE)
+ assert(false && "deprecated format still exists and is unhandled. Inform Vasily or Justin");
+ }
+
+ if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() ||
+ SVX_NUM_BITMAP == rFormat.GetNumberingType())
+ {
+ bWriteBullet = true;
+
+ pBulletFont = rFormat.GetBulletFont();
+ if (!pBulletFont)
+ {
+ pBulletFont = numfunc::GetDefBulletFont();
+ }
+
+ eChrSet = pBulletFont->GetCharSet();
+ sFontName = pBulletFont->GetFamilyName();
+ eFamily = pBulletFont->GetFamilyType();
+
+ if (IsOpenSymbol(sFontName))
+ SubstituteBullet(sNumStr, eChrSet, sFontName);
+ }
+
+ // Attributes of the numbering
+ std::unique_ptr<wwFont> pPseudoFont;
+ const SfxItemSet* pOutSet = nullptr;
+
+ // cbGrpprlChpx
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aSet( m_rDoc.GetAttrPool() );
+ if (rFormat.GetCharFormat() || bWriteBullet)
+ {
+ if (bWriteBullet)
+ {
+ pOutSet = &aSet;
+
+ if (rFormat.GetCharFormat())
+ aSet.Put( rFormat.GetCharFormat()->GetAttrSet() );
+ aSet.ClearItem( RES_CHRATR_CJK_FONT );
+ aSet.ClearItem( RES_CHRATR_FONT );
+
+ if (sFontName.isEmpty())
+ sFontName = pBulletFont->GetFamilyName();
+
+ pPseudoFont.reset(new wwFont( sFontName, pBulletFont->GetPitch(),
+ eFamily, eChrSet));
+ }
+ else
+ pOutSet = &rFormat.GetCharFormat()->GetAttrSet();
+ }
+
+ sal_Int16 nIndentAt = 0;
+ sal_Int16 nFirstLineIndex = 0;
+ sal_Int16 nListTabPos = -1;
+
+ // #i86652#
+ if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION)
+ {
+ nIndentAt = nListTabPos = rFormat.GetAbsLSpace(); //TODO: overflow
+ nFirstLineIndex = GetWordFirstLineOffset(rFormat);
+ }
+ else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT)
+ {
+ nIndentAt = static_cast<sal_Int16>(rFormat.GetIndentAt());
+ nFirstLineIndex = static_cast<sal_Int16>(rFormat.GetFirstLineIndent());
+ nListTabPos = rFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB?
+ static_cast<sal_Int16>( rFormat.GetListtabPos() ) : 0;
+ }
+
+ AttrOutput().NumberingLevel( nLvl,
+ rFormat.GetStart(),
+ rFormat.GetNumberingType(),
+ rFormat.GetNumAdjust(),
+ aNumLvlPos,
+ nFollow,
+ pPseudoFont.get(), pOutSet,
+ nIndentAt, nFirstLineIndex, nListTabPos,
+ sNumStr,
+ rFormat.GetNumberingType()==SVX_NUM_BITMAP ? rFormat.GetBrush() : nullptr, rFormat.GetIsLegal());
+}
+
+void WW8Export::OutOverrideListTab()
+{
+ if( !m_pUsedNumTable )
+ return ; // no numbering is used
+
+ // write the "list format override" - LFO
+ sal_uInt16 nCount = m_pUsedNumTable->size();
+ sal_uInt16 n;
+
+ m_pFib->m_fcPlfLfo = m_pTableStrm->Tell();
+ m_pTableStrm->WriteUInt32( nCount );
+
+ // LFO ([MS-DOC] 2.9.131)
+ for( n = 0; n < nCount; ++n )
+ {
+ m_pTableStrm->WriteUInt32( n + 1 );
+ SwWW8Writer::FillCount( *m_pTableStrm, 12 );
+ }
+ // LFOData ([MS-DOC] 2.9.132)
+ for( n = 0; n < nCount; ++n )
+ m_pTableStrm->WriteInt32( -1 ); // no overwrite
+
+ // set len to FIB
+ m_pFib->m_lcbPlfLfo = m_pTableStrm->Tell() - m_pFib->m_fcPlfLfo;
+}
+
+void WW8Export::OutListNamesTab()
+{
+ if( !m_pUsedNumTable )
+ return ; // no numbering is used
+
+ // write the "list format override" - LFO
+ sal_uInt16 nNms = 0, nCount = m_pUsedNumTable->size();
+
+ m_pFib->m_fcSttbListNames = m_pTableStrm->Tell();
+ m_pTableStrm->WriteInt16( -1 );
+ m_pTableStrm->WriteUInt32( nCount );
+
+ for( ; nNms < nCount; ++nNms )
+ {
+ const SwNumRule& rRule = *(*m_pUsedNumTable)[ nNms ];
+ OUString sNm;
+ if( !rRule.IsAutoRule() )
+ sNm = rRule.GetName();
+
+ m_pTableStrm->WriteUInt16( sNm.getLength() );
+ if (!sNm.isEmpty())
+ SwWW8Writer::WriteString16(*m_pTableStrm, sNm, false);
+ }
+
+ SwWW8Writer::WriteLong( *m_pTableStrm, m_pFib->m_fcSttbListNames + 2, nNms );
+ // set len to FIB
+ m_pFib->m_lcbSttbListNames = m_pTableStrm->Tell() - m_pFib->m_fcSttbListNames;
+}
+
+void MSWordExportBase::SubstituteBullet( OUString& rNumStr,
+ rtl_TextEncoding& rChrSet, OUString& rFontName ) const
+{
+ if (!m_bSubstituteBullets)
+ return;
+ OUString sFontName = rFontName;
+
+ // If Bullet char is "", don't change
+ if (rNumStr[0] != u'\0')
+ {
+ rNumStr = rNumStr.replaceAt(0, 1, rtl::OUStringChar(
+ msfilter::util::bestFitOpenSymbolToMSFont(rNumStr[0], rChrSet, sFontName)));
+ }
+
+ rFontName = sFontName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtw8sty.cxx b/sw/source/filter/ww8/wrtw8sty.cxx
new file mode 100644
index 0000000000..9ee7b7532d
--- /dev/null
+++ b/sw/source/filter/ww8/wrtw8sty.cxx
@@ -0,0 +1,2725 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+
+#include <memory>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <hintids.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdouno.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <rtl/character.hxx>
+
+#include <doc.hxx>
+#include "wrtww8.hxx"
+#include <docary.hxx>
+#include <poolfmt.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <ndtxt.hxx>
+#include <ftninfo.hxx>
+#include <fmthdft.hxx>
+#include <section.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtftn.hxx>
+#include <ndindex.hxx>
+#include <txtftn.hxx>
+#include <charfmt.hxx>
+#include <docufld.hxx>
+#include <dcontact.hxx>
+#include <fmtcnct.hxx>
+#include <ftnidx.hxx>
+#include <fmtclds.hxx>
+#include <lineinfo.hxx>
+#include <fmtline.hxx>
+#include <swtable.hxx>
+#include <redline.hxx>
+#include <msfilter.hxx>
+#include <swmodule.hxx>
+#include <charatr.hxx>
+
+#include "sprmids.hxx"
+
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include <wwstyles.hxx>
+#include "ww8par.hxx"
+#include "ww8attributeoutput.hxx"
+#include "docxattributeoutput.hxx"
+#include "rtfattributeoutput.hxx"
+
+#include <unordered_set>
+
+using namespace css;
+using namespace sw::util;
+using namespace nsHdFtFlags;
+
+/// For the output of sections.
+struct WW8_PdAttrDesc
+{
+ std::unique_ptr<sal_uInt8[]> m_pData;
+ sal_uInt16 m_nLen;
+ WW8_FC m_nSepxFcPos;
+ WW8_PdAttrDesc() : m_nLen(0), m_nSepxFcPos(0xffffffff) /*default: none*/
+ { }
+};
+
+namespace {
+
+struct WW8_SED
+{
+ SVBT16 aBits1; // orientation change + internal, Default: 6
+ SVBT32 fcSepx; // FC file offset to beginning of SEPX for section.
+ // 0xFFFFFFFF for no Sprms
+ SVBT16 fnMpr; // used internally by Windows Word, Default: 0
+ SVBT32 fcMpr; // FC, points to offset in FC space for MacWord
+ // Default: 0xffffffff ( nothing )
+ // cbSED is 12 (decimal)), C (hex).
+};
+
+}
+
+// class WW8_WrPlc0 is only used for header and footer positioning
+// ie there is no content support structure
+class WW8_WrPlc0
+{
+private:
+ std::vector<sal_uLong> m_aPos; // PTRARR of CPs / FCs
+ sal_uLong m_nOfs;
+
+ WW8_WrPlc0(WW8_WrPlc0 const&) = delete;
+ WW8_WrPlc0& operator=(WW8_WrPlc0 const&) = delete;
+
+public:
+ explicit WW8_WrPlc0( sal_uLong nOffset );
+ sal_uInt16 Count() const { return m_aPos.size(); }
+ void Append( sal_uLong nStartCpOrFc );
+ void Write( SvStream& rStrm );
+};
+
+// Styles
+
+// According to [MS-DOC] v20221115 2.9.271 STSH,
+// "The beginning of the rglpstd array is reserved for specific "fixed-index" application-defined
+// styles. A particular fixed-index, application-defined style has the same istd value in every
+// stylesheet. The rglpstd MUST contain an LPStd for each of these fixed-index styles and the order
+// MUST match the order in the following table.
+//
+// istd sti of application-defined style (see sti in StdfBase)
+// 0 0
+// 1 1
+// 2 2
+// 3 3
+// 4 4
+// 5 5
+// 6 6
+// 7 7
+// 8 8
+// 9 9
+// 10 65
+// 11 105
+// 12 107
+// 13 Reserved for future use
+// 14 Reserved for future use"
+//
+// And [MS-OE376] v20220816 2.1.236 Part 4 Section 2.7.3.9, name (Primary Style Name)
+// specifies the following mapping:
+//
+// sti Style name Style type
+// 0 Normal paragraph
+// 1 heading 1 paragraph
+// 2 heading 2 paragraph
+// 3 heading 3 paragraph
+// 4 heading 4 paragraph
+// 5 heading 5 paragraph
+// 6 heading 6 paragraph
+// 7 heading 7 paragraph
+// 8 heading 8 paragraph
+// 9 heading 9 paragraph
+// 65 Default Paragraph Font character
+// 105 Normal Table table
+// 107 No List numbering
+
+#define WW8_RESERVED_SLOTS 15
+
+// GetId( SwCharFormat ) for use in text -> zero is not allowed,
+// use "Default Char Style" instead
+sal_uInt16 MSWordExportBase::GetId( const SwCharFormat* pFormat ) const
+{
+ sal_uInt16 nRet = m_pStyles->GetSlot( pFormat );
+ return ( nRet != 0x0fff ) ? nRet : 10; // Default Char Style
+}
+
+// GetId( SwTextFormatColl ) for use in TextNodes -> zero is not allowed,
+// "Standard" instead
+sal_uInt16 MSWordExportBase::GetId( const SwTextFormatColl& rColl ) const
+{
+ sal_uInt16 nRet = m_pStyles->GetSlot( &rColl );
+ return ( nRet != 0xfff ) ? nRet : 0; // Default TextFormatColl
+}
+
+//typedef pFormatT
+MSWordStyles::MSWordStyles( MSWordExportBase& rExport, bool bListStyles )
+ : m_rExport( rExport ),
+ m_bListStyles(bListStyles)
+{
+ // if exist any Foot-/End-Notes then get from the EndNoteInfo struct
+ // the CharFormats. They will create it!
+ if ( !m_rExport.m_rDoc.GetFootnoteIdxs().empty() )
+ {
+ m_rExport.m_rDoc.GetEndNoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
+ m_rExport.m_rDoc.GetEndNoteInfo().GetCharFormat( m_rExport.m_rDoc );
+ m_rExport.m_rDoc.GetFootnoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
+ m_rExport.m_rDoc.GetFootnoteInfo().GetCharFormat( m_rExport.m_rDoc );
+ }
+
+ memset( m_aHeadingParagraphStyles, -1 , MAXLEVEL * sizeof( sal_uInt16));
+
+ BuildStylesTable();
+ BuildWwNames();
+ BuildStyleIds();
+}
+
+MSWordStyles::~MSWordStyles()
+{
+}
+
+// Sty_SetWWSlot() dependencies for the styles -> zero is allowed
+sal_uInt16 MSWordStyles::GetSlot( const SwFormat* pFormat ) const
+{
+ for (size_t slot = 0; slot < m_aStyles.size(); ++slot)
+ if (m_aStyles[slot].format == pFormat)
+ return slot;
+ return 0xfff; // 0xfff: WW: zero
+}
+
+/// Get reserved slot number during building the style table.
+static sal_uInt16 BuildGetSlot(const SwFormat& rFormat)
+{
+ switch (sal_uInt16 nRet = rFormat.GetPoolFormatId())
+ {
+ case RES_POOLCOLL_STANDARD:
+ return 0;
+
+ case RES_POOLCOLL_HEADLINE1:
+ case RES_POOLCOLL_HEADLINE2:
+ case RES_POOLCOLL_HEADLINE3:
+ case RES_POOLCOLL_HEADLINE4:
+ case RES_POOLCOLL_HEADLINE5:
+ case RES_POOLCOLL_HEADLINE6:
+ case RES_POOLCOLL_HEADLINE7:
+ case RES_POOLCOLL_HEADLINE8:
+ case RES_POOLCOLL_HEADLINE9:
+ nRet -= RES_POOLCOLL_HEADLINE1-1;
+ assert(nRet < WW8_RESERVED_SLOTS);
+ return nRet;
+ }
+ return 0xfff;
+}
+
+
+// Keep in sync with StyleSheetTable::ConvertStyleName
+sal_uInt16 MSWordStyles::GetWWId( const SwFormat& rFormat )
+{
+ sal_uInt16 nRet = ww::stiUser; // user style as default
+ sal_uInt16 nPoolId = rFormat.GetPoolFormatId();
+ if( nPoolId == RES_POOLCOLL_STANDARD )
+ nRet = ww::stiNormal;
+ else if( nPoolId >= RES_POOLCOLL_HEADLINE1 &&
+ nPoolId <= RES_POOLCOLL_HEADLINE9 )
+ nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiLevFirst - RES_POOLCOLL_HEADLINE1);
+ else if( nPoolId >= RES_POOLCOLL_TOX_IDX1 &&
+ nPoolId <= RES_POOLCOLL_TOX_IDX3 )
+ nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiIndexFirst - RES_POOLCOLL_TOX_IDX1);
+ else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT1 &&
+ nPoolId <= RES_POOLCOLL_TOX_CNTNT5 )
+ nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiToc1 - RES_POOLCOLL_TOX_CNTNT1);
+ else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT6 &&
+ nPoolId <= RES_POOLCOLL_TOX_CNTNT9 )
+ nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiToc6 - RES_POOLCOLL_TOX_CNTNT6);
+ else
+ switch( nPoolId )
+ {
+ case RES_POOLCOLL_FOOTNOTE: nRet = ww::stiFootnoteText; break;
+ case RES_POOLCOLL_MARGINAL: nRet = ww::stiAtnText; break;
+ case RES_POOLCOLL_HEADER: nRet = ww::stiHeader; break;
+ case RES_POOLCOLL_FOOTER: nRet = ww::stiFooter; break;
+ case RES_POOLCOLL_TOX_IDXH: nRet = ww::stiIndexHeading; break;
+ case RES_POOLCOLL_LABEL: nRet = ww::stiCaption; break;
+ case RES_POOLCOLL_TOX_ILLUS1: nRet = ww::stiToCaption; break;
+ case RES_POOLCOLL_ENVELOPE_ADDRESS: nRet = ww::stiEnvAddr; break;
+ case RES_POOLCOLL_SEND_ADDRESS: nRet = ww::stiEnvRet; break;
+ case RES_POOLCHR_FOOTNOTE_ANCHOR: nRet = ww::stiFootnoteRef; break;
+ case RES_POOLCHR_LINENUM: nRet = ww::stiLnn; break;
+ case RES_POOLCHR_PAGENO: nRet = ww::stiPgn; break;
+ case RES_POOLCHR_ENDNOTE_ANCHOR: nRet = ww::stiEdnRef; break;
+ case RES_POOLCOLL_ENDNOTE: nRet = ww::stiEdnText; break;
+ case RES_POOLCOLL_TOX_AUTHORITIESH: nRet = ww::stiToa; break;
+ case RES_POOLCOLL_TOX_CNTNTH: nRet = ww::stiToaHeading; break;
+ case RES_POOLCOLL_LISTS_BEGIN: nRet = ww::stiList; break;
+ case RES_POOLCOLL_BULLET_LEVEL1: nRet = ww::stiListBullet; break;
+ case RES_POOLCOLL_NUM_LEVEL1: nRet = ww::stiListNumber; break;
+ case RES_POOLCOLL_BULLET_LEVEL2: nRet = ww::stiListBullet2; break;
+ case RES_POOLCOLL_BULLET_LEVEL3: nRet = ww::stiListBullet3; break;
+ case RES_POOLCOLL_BULLET_LEVEL4: nRet = ww::stiListBullet4; break;
+ case RES_POOLCOLL_BULLET_LEVEL5: nRet = ww::stiListBullet5; break;
+ case RES_POOLCOLL_NUM_LEVEL2: nRet = ww::stiListNumber2; break;
+ case RES_POOLCOLL_NUM_LEVEL3: nRet = ww::stiListNumber3; break;
+ case RES_POOLCOLL_NUM_LEVEL4: nRet = ww::stiListNumber4; break;
+ case RES_POOLCOLL_NUM_LEVEL5: nRet = ww::stiListNumber5; break;
+ case RES_POOLCOLL_DOC_TITLE: nRet = ww::stiTitle; break;
+ case RES_POOLCOLL_DOC_APPENDIX: nRet = ww::stiClosing; break;
+ case RES_POOLCOLL_SIGNATURE: nRet = ww::stiSignature; break;
+ case RES_POOLCOLL_TEXT: nRet = ww::stiBodyText; break;
+ case RES_POOLCOLL_TEXT_MOVE: nRet = ww::stiBodyTextInd1; break;
+ case RES_POOLCOLL_BULLET_NONUM1: nRet = ww::stiListCont; break;
+ case RES_POOLCOLL_BULLET_NONUM2: nRet = ww::stiListCont2; break;
+ case RES_POOLCOLL_BULLET_NONUM3: nRet = ww::stiListCont3; break;
+ case RES_POOLCOLL_BULLET_NONUM4: nRet = ww::stiListCont4; break;
+ case RES_POOLCOLL_BULLET_NONUM5: nRet = ww::stiListCont5; break;
+ case RES_POOLCOLL_DOC_SUBTITLE: nRet = ww::stiSubtitle; break;
+ case RES_POOLCOLL_GREETING: nRet = ww::stiSalutation; break;
+ case RES_POOLCOLL_TEXT_IDENT: nRet = ww::stiBodyText1I; break;
+ case RES_POOLCHR_INET_NORMAL: nRet = ww::stiHyperlink; break;
+ case RES_POOLCHR_INET_VISIT: nRet = ww::stiHyperlinkFollowed; break;
+ case RES_POOLCHR_HTML_STRONG: nRet = ww::stiStrong; break;
+ case RES_POOLCHR_HTML_EMPHASIS: nRet = ww::stiEmphasis; break;
+ }
+ return nRet;
+}
+
+void MSWordStyles::BuildStylesTable()
+{
+ assert(m_aStyles.empty());
+ // Put reserved slots first, then character styles, then paragraph styles
+ m_aStyles.resize(WW8_RESERVED_SLOTS);
+
+ const SwCharFormats& rArr = *m_rExport.m_rDoc.GetCharFormats(); // first CharFormat
+ // the default character style ( 0 ) will not be outputted !
+ for (size_t n = 1; n < rArr.size() && m_aStyles.size() < MSWORD_MAX_STYLES_LIMIT; ++n)
+ m_aStyles.emplace_back(rArr[n]);
+
+ const SwTextFormatColls& rArr2 = *m_rExport.m_rDoc.GetTextFormatColls(); // then TextFormatColls
+ // the default paragraph style ( 0 ) will not be outputted !
+ for (size_t n = 1; n < rArr2.size(); ++n)
+ {
+ SwTextFormatColl* pFormat = rArr2[n];
+
+ sal_uInt16 nSlot = BuildGetSlot(*pFormat);
+ if (nSlot != 0xfff)
+ {
+ m_aStyles[nSlot] = { pFormat };
+ }
+ else
+ {
+ if (m_aStyles.size() >= MSWORD_MAX_STYLES_LIMIT)
+ continue;
+ m_aStyles.emplace_back(pFormat);
+ nSlot = m_aStyles.size() - 1;
+ }
+ if ( pFormat->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ int nLvl = pFormat->GetAssignedOutlineStyleLevel() ;
+ if (nLvl >= 0 && nLvl < MAXLEVEL)
+ m_aHeadingParagraphStyles[nLvl] = nSlot;
+ }
+ }
+
+ if (!m_bListStyles)
+ return;
+
+ const SwNumRuleTable& rNumRuleTable = m_rExport.m_rDoc.GetNumRuleTable();
+ for (size_t i = 0; i < rNumRuleTable.size() && m_aStyles.size() < MSWORD_MAX_STYLES_LIMIT; ++i)
+ {
+ const SwNumRule* pNumRule = rNumRuleTable[i];
+ if (pNumRule->IsAutoRule() || pNumRule->GetName().startsWith("WWNum"))
+ continue;
+ m_aStyles.emplace_back(pNumRule);
+ }
+}
+
+// StyleSheetTable::ConvertStyleName appends the suffix do disambiguate conflicting style names
+static OUString StripWWSuffix(const OUString& s)
+{
+ OUString ret = s;
+ (void)ret.endsWith(" (WW)", &ret);
+ return ret;
+}
+
+void MSWordStyles::BuildWwNames()
+{
+ std::unordered_set<OUString> aUsed;
+
+ auto makeUniqueName = [&aUsed](OUString& name) {
+ // toAsciiLowerCase rules out e.g. user's "normal"; no problem if there are non-ASCII chars
+ OUString lower(name.toAsciiLowerCase());
+ if (!aUsed.insert(lower).second)
+ {
+ int nFree = 1;
+ while (!aUsed.insert(lower + OUString::number(nFree)).second)
+ ++nFree;
+
+ name += OUString::number(nFree);
+ }
+ };
+
+ // We want to map LO's default style to Word's "Normal" style.
+ // Word looks for this specific style name when reading docx files.
+ // (It must be the English word regardless of languages and locales)
+ assert(!m_aStyles.empty());
+ assert(!m_aStyles[0].format || m_aStyles[0].ww_id == ww::stiNormal);
+ m_aStyles[0].ww_name = "Normal";
+ aUsed.insert("normal");
+
+ // 1. Handle styles having special wwIds, and thus pre-defined names
+ for (auto& entry : m_aStyles)
+ {
+ if (!entry.ww_name.isEmpty())
+ continue; // "Normal" is already added
+ if (entry.ww_id >= ww::stiMax)
+ continue; // Not a format with special name
+ assert(entry.format);
+
+ entry.ww_name = OUString::createFromAscii(ww::GetEnglishNameFromSti(static_cast<ww::sti>(entry.ww_id)));
+ makeUniqueName(entry.ww_name);
+ }
+
+ // 2. Now handle other styles
+ for (auto& entry : m_aStyles)
+ {
+ if (!entry.ww_name.isEmpty())
+ continue;
+ if (entry.format)
+ entry.ww_name = StripWWSuffix(entry.format->GetName());
+ else if (entry.num_rule)
+ entry.ww_name = StripWWSuffix(entry.num_rule->GetName());
+ else
+ continue;
+ makeUniqueName(entry.ww_name);
+ }
+}
+
+OString MSWordStyles::CreateStyleId(std::u16string_view aName)
+{
+ return OUStringToOString(msfilter::util::CreateDOCXStyleId(aName), RTL_TEXTENCODING_UTF8);
+}
+
+void MSWordStyles::BuildStyleIds()
+{
+ std::unordered_set<OString> aUsed;
+
+ for (auto& entry : m_aStyles)
+ {
+ OString aStyleId = CreateStyleId(entry.ww_name);
+
+ if (aStyleId.isEmpty())
+ aStyleId = "Style"_ostr;
+
+ OString aLower(aStyleId.toAsciiLowerCase());
+
+ // check for uniqueness & construct something unique if we have to
+ if (!aUsed.insert(aLower).second)
+ {
+ int nFree = 1;
+ while (!aUsed.insert(aLower + OString::number(nFree)).second)
+ ++nFree;
+
+ aStyleId += OString::number(nFree);
+ }
+ entry.style_id = aStyleId;
+ }
+}
+
+OString const & MSWordStyles::GetStyleId(sal_uInt16 nSlot) const
+{
+ assert(!m_aStyles[nSlot].style_id.isEmpty());
+ return m_aStyles[nSlot].style_id;
+}
+
+OUString MSWordStyles::GetStyleWWName(SwFormat const*const pFormat) const
+{
+ if (auto slot = m_rExport.m_pStyles->GetSlot(pFormat); slot != 0xfff)
+ {
+ assert(!m_aStyles[slot].ww_name.isEmpty());
+ return m_aStyles[slot].ww_name;
+ }
+ return OUString();
+}
+
+/// For WW8 only - extend pO so that the size of pTableStrm is even.
+static void impl_SkipOdd(std::unique_ptr<ww::bytes> const& pO, std::size_t nTableStrmTell)
+{
+ if ( ( nTableStrmTell + pO->size() ) & 1 ) // start on even
+ pO->push_back( sal_uInt8(0) ); // Address
+}
+
+void WW8AttributeOutput::EndStyle()
+{
+ impl_SkipOdd( m_rWW8Export.m_pO, m_rWW8Export.m_pTableStrm->Tell() );
+
+ short nLen = m_rWW8Export.m_pO->size() - 2; // length of the style
+ sal_uInt8* p = m_rWW8Export.m_pO->data() + m_nPOPosStdLen1;
+ ShortToSVBT16( nLen, p ); // add
+ p = m_rWW8Export.m_pO->data() + m_nPOPosStdLen2;
+ ShortToSVBT16( nLen, p ); // also
+
+ m_rWW8Export.m_pTableStrm->WriteBytes(m_rWW8Export.m_pO->data(), m_rWW8Export.m_pO->size());
+ m_rWW8Export.m_pO->clear();
+}
+
+void WW8AttributeOutput::StartStyle( const OUString& rName, StyleType eType, sal_uInt16 nWwBase,
+ sal_uInt16 nWwNext, sal_uInt16 /*nWwLink*/, sal_uInt16 nWwId, sal_uInt16 /*nSlot*/, bool bAutoUpdate )
+{
+ sal_uInt8 aWW8_STD[ sizeof( WW8_STD ) ] = {};
+ sal_uInt8* pData = aWW8_STD;
+
+ sal_uInt16 nBit16 = 0x1000; // fInvalHeight
+ nBit16 |= (ww::stiNil & nWwId);
+ Set_UInt16( pData, nBit16 );
+
+ nBit16 = nWwBase << 4; // istdBase
+ nBit16 |= (eType == STYLE_TYPE_PARA ? 1 : 2); // sgc
+ Set_UInt16( pData, nBit16 );
+
+ nBit16 = nWwNext << 4; // istdNext
+ nBit16 |= (eType == STYLE_TYPE_PARA ? 2 : 1); // cupx
+ Set_UInt16( pData, nBit16 );
+
+ pData += sizeof( sal_uInt16 ); // bchUpe
+
+ nBit16 = bAutoUpdate ? 1 : 0; // fAutoRedef : 1
+ Set_UInt16( pData, nBit16 );
+ // now new:
+ // from Ver8 there are two fields more:
+ // sal_uInt16 fHidden : 1; /* hidden from UI?
+ // sal_uInt16 : 14; /* unused bits
+
+ sal_uInt16 nLen = static_cast< sal_uInt16 >( ( pData - aWW8_STD ) + 1 +
+ (2 * (rName.getLength() + 1)) ); // temporary
+
+ m_nPOPosStdLen1 = m_rWW8Export.m_pO->size(); // Adr1 for adding the length
+
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nLen );
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aWW8_STD, pData );
+
+ m_nPOPosStdLen2 = m_nPOPosStdLen1 + 8; // Adr2 for adding of "end of upx"
+
+ // write names
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rName.getLength() ); // length
+ SwWW8Writer::InsAsString16( *m_rWW8Export.m_pO, rName );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0) ); // Despite P-String 0 at the end!
+}
+
+void MSWordStyles::SetStyleDefaults( const SwFormat& rFormat, bool bPap )
+{
+ const sw::BroadcastingModify* pOldMod = m_rExport.m_pOutFormatNode;
+ m_rExport.m_pOutFormatNode = &rFormat;
+ bool aFlags[ RES_FRMATR_END - RES_CHRATR_BEGIN ];
+ sal_uInt16 nStt, nEnd, n;
+ if( bPap )
+ {
+ nStt = RES_PARATR_BEGIN;
+ nEnd = RES_FRMATR_END;
+ }
+ else
+ {
+ nStt = RES_CHRATR_BEGIN;
+ nEnd = RES_TXTATR_END;
+ }
+
+ // dynamic defaults
+ const SfxItemPool& rPool = *rFormat.GetAttrSet().GetPool();
+ for( n = nStt; n < nEnd; ++n )
+ aFlags[ n - RES_CHRATR_BEGIN ] = nullptr != rPool.GetPoolDefaultItem( n )
+ || SfxItemState::SET == m_rExport.m_rDoc.GetDfltTextFormatColl()->GetItemState( n, false );
+
+ // static defaults, that differs between WinWord and SO
+ if( bPap )
+ {
+ aFlags[ static_cast< sal_uInt16 >(RES_PARATR_WIDOWS) - RES_CHRATR_BEGIN ] = true;
+ aFlags[ static_cast< sal_uInt16 >(RES_PARATR_HYPHENZONE) - RES_CHRATR_BEGIN ] = true;
+ aFlags[ static_cast< sal_uInt16 >(RES_FRAMEDIR) - RES_CHRATR_BEGIN ] = true;
+ }
+ else
+ {
+ aFlags[ RES_CHRATR_FONTSIZE - RES_CHRATR_BEGIN ] = true;
+ aFlags[ RES_CHRATR_LANGUAGE - RES_CHRATR_BEGIN ] = true;
+ }
+
+ const SfxItemSet* pOldI = m_rExport.GetCurItemSet();
+ m_rExport.SetCurItemSet( &rFormat.GetAttrSet() );
+
+ const bool* pFlags = aFlags + ( nStt - RES_CHRATR_BEGIN );
+ for ( n = nStt; n < nEnd; ++n, ++pFlags )
+ {
+ if ( *pFlags && !m_rExport.ignoreAttributeForStyleDefaults( n )
+ && SfxItemState::SET != rFormat.GetItemState(n, false))
+ {
+ //If we are a character property then see if it is one of the
+ //western/asian ones that must be collapsed together for export to
+ //word. If so default to the western variant.
+ if ( bPap || m_rExport.CollapseScriptsforWordOk(
+ i18n::ScriptType::LATIN, n) )
+ {
+ m_rExport.AttrOutput().OutputItem( rFormat.GetFormatAttr( n ) );
+ }
+ }
+ }
+
+ m_rExport.SetCurItemSet( pOldI );
+ m_rExport.m_pOutFormatNode = pOldMod;
+}
+
+void WW8AttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 nStyle )
+{
+ impl_SkipOdd( m_rWW8Export.m_pO, m_rWW8Export.m_pTableStrm->Tell() );
+
+ sal_uInt16 nLen = bParProp ? 2 : 0; // default length
+ m_nStyleLenPos = m_rWW8Export.m_pO->size(); // adding length
+ // Don't save pointer, because it
+ // changes by _grow!
+
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nLen ); // Style-Len
+
+ m_nStyleStartSize = m_rWW8Export.m_pO->size();
+
+ if ( bParProp )
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nStyle ); // Style-Number
+}
+
+void MSWordStyles::WriteProperties( const SwFormat* pFormat, bool bParProp, sal_uInt16 nPos,
+ bool bInsDefCharSiz )
+{
+ m_rExport.AttrOutput().StartStyleProperties( bParProp, nPos );
+
+ OSL_ENSURE( m_rExport.m_pCurrentStyle == nullptr, "Current style not NULL" ); // set current style before calling out
+ m_rExport.m_pCurrentStyle = pFormat;
+
+ m_rExport.OutputFormat( *pFormat, bParProp, !bParProp );
+
+ OSL_ENSURE( m_rExport.m_pCurrentStyle == pFormat, "current style was changed" );
+ // reset current style...
+ m_rExport.m_pCurrentStyle = nullptr;
+
+ if ( bInsDefCharSiz ) // not derived from other Style
+ SetStyleDefaults( *pFormat, bParProp );
+
+ m_rExport.AttrOutput().EndStyleProperties( bParProp );
+}
+
+void WW8AttributeOutput::EndStyleProperties( bool /*bParProp*/ )
+{
+ sal_uInt16 nLen = m_rWW8Export.m_pO->size() - m_nStyleStartSize;
+ sal_uInt8* pUpxLen = m_rWW8Export.m_pO->data() + m_nStyleLenPos; // adding length
+ ShortToSVBT16( nLen, pUpxLen ); // add default length
+}
+
+void MSWordStyles::GetStyleData( const SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext, sal_uInt16& nLink )
+{
+ bFormatColl = pFormat->Which() == RES_TXTFMTCOLL || pFormat->Which() == RES_CONDTXTFMTCOLL;
+
+ // Default: none
+ nBase = 0xfff;
+
+ // Derived from?
+ if ( !pFormat->IsDefault() )
+ nBase = GetSlot( pFormat->DerivedFrom() );
+
+ const SwFormat* pNext;
+ const SwFormat* pLink = nullptr;
+ if ( bFormatColl )
+ {
+ auto pFormatColl = static_cast<const SwTextFormatColl*>(pFormat);
+ pNext = &pFormatColl->GetNextTextFormatColl();
+ pLink = pFormatColl->GetLinkedCharFormat();
+ }
+ else
+ {
+ pNext = pFormat; // CharFormat: next CharFormat == self
+ auto pCharFormat = static_cast<const SwCharFormat*>(pFormat);
+ pLink = pCharFormat->GetLinkedParaFormat();
+ }
+
+ nNext = GetSlot( pNext );
+
+ if (pLink)
+ {
+ nLink = GetSlot(pLink);
+ }
+}
+
+void WW8AttributeOutput::DefaultStyle()
+{
+ m_rWW8Export.m_pTableStrm->WriteUInt16(0); // empty Style
+}
+
+void MSWordStyles::OutputStyle(sal_uInt16 nSlot)
+{
+ const auto& entry = m_aStyles[nSlot];
+
+ if (entry.num_rule)
+ {
+ m_rExport.AttrOutput().StartStyle( entry.ww_name, STYLE_TYPE_LIST,
+ /*nBase =*/ 0, /*nWwNext =*/ 0, /*nWwLink =*/ 0, /*nWWId =*/ 0, nSlot,
+ /*bAutoUpdateFormat =*/ false );
+
+ m_rExport.AttrOutput().EndStyle();
+ }
+ else if (!entry.format)
+ {
+ m_rExport.AttrOutput().DefaultStyle();
+ }
+ else
+ {
+ bool bFormatColl;
+ sal_uInt16 nBase, nWwNext;
+ sal_uInt16 nWwLink = 0x0FFF;
+
+ GetStyleData(entry.format, bFormatColl, nBase, nWwNext, nWwLink);
+
+ if (!bFormatColl && m_rExport.GetExportFormat() == MSWordExportBase::DOCX &&
+ entry.style_id.startsWith("ListLabel"))
+ {
+ // tdf#92335 don't export redundant DOCX import style "ListLabel"
+ return;
+ }
+
+ m_rExport.AttrOutput().StartStyle(entry.ww_name, (bFormatColl ? STYLE_TYPE_PARA : STYLE_TYPE_CHAR),
+ nBase, nWwNext, nWwLink, m_aStyles[nSlot].ww_id, nSlot,
+ entry.format->IsAutoUpdateOnDirectFormat() );
+
+ if ( bFormatColl )
+ WriteProperties( entry.format, true, nSlot, nBase==0xfff ); // UPX.papx
+
+ WriteProperties( entry.format, false, nSlot, bFormatColl && nBase==0xfff ); // UPX.chpx
+
+ m_rExport.AttrOutput().EndStyle();
+ }
+}
+
+void WW8AttributeOutput::StartStyles()
+{
+ WW8Fib& rFib = *m_rWW8Export.m_pFib;
+
+ sal_uInt64 nCurPos = m_rWW8Export.m_pTableStrm->Tell();
+ if ( nCurPos & 1 ) // start on even
+ {
+ m_rWW8Export.m_pTableStrm->WriteChar( char(0) ); // Address
+ ++nCurPos;
+ }
+ rFib.m_fcStshfOrig = rFib.m_fcStshf = nCurPos;
+ m_nStyleCountPos = nCurPos + 2; // count is added later
+
+ static sal_uInt8 aStShi[] = {
+ 0x12, 0x00,
+ 0x0F, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x5B, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00 };
+
+ m_rWW8Export.m_pTableStrm->WriteBytes(&aStShi, sizeof(aStShi));
+}
+
+void WW8AttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
+{
+ WW8Fib& rFib = *m_rWW8Export.m_pFib;
+
+ rFib.m_lcbStshfOrig = rFib.m_lcbStshf = m_rWW8Export.m_pTableStrm->Tell() - rFib.m_fcStshf;
+ SwWW8Writer::WriteShort( *m_rWW8Export.m_pTableStrm, m_nStyleCountPos, nNumberOfStyles );
+}
+
+void MSWordStyles::OutputStylesTable()
+{
+ m_rExport.m_bStyDef = true;
+
+ m_rExport.AttrOutput().StartStyles();
+
+ // HACK
+ // Ms Office seems to have an internal limitation of 4091 styles
+ // and refuses to load .docx with more, even though the spec seems to allow that;
+ // so simply if there are more styles, don't export those
+ // Implementing check for all exports DOCX, DOC, RTF
+ assert(m_aStyles.size() <= MSWORD_MAX_STYLES_LIMIT);
+ for (size_t slot = 0; slot < m_aStyles.size(); ++slot)
+ OutputStyle(slot);
+
+ m_rExport.AttrOutput().EndStyles(m_aStyles.size());
+
+ m_rExport.m_bStyDef = false;
+}
+
+// Fonts
+
+wwFont::wwFont(std::u16string_view rFamilyName, FontPitch ePitch, FontFamily eFamily,
+ rtl_TextEncoding eChrSet)
+ : mbAlt(false), mePitch(ePitch), meFamily(eFamily), meChrSet(eChrSet)
+{
+ FontMapExport aResult(rFamilyName);
+ msFamilyNm = aResult.msPrimary;
+ msAltNm = aResult.msSecondary;
+ if (!msAltNm.isEmpty() && msAltNm != msFamilyNm &&
+ (msFamilyNm.getLength() + msAltNm.getLength() + 2 <= 65) )
+ {
+ //max size of szFfn in 65 chars
+ mbAlt = true;
+ }
+
+ maWW8_FFN[0] = static_cast<sal_uInt8>( 6 - 1 + 0x22 + ( 2 * ( 1 + msFamilyNm.getLength() ) ));
+ if (mbAlt)
+ maWW8_FFN[0] = static_cast< sal_uInt8 >(maWW8_FFN[0] + 2 * ( 1 + msAltNm.getLength()));
+
+ sal_uInt8 aB = 0;
+ switch(ePitch)
+ {
+ case PITCH_VARIABLE:
+ aB |= 2; // aF.prg = 2
+ break;
+ case PITCH_FIXED:
+ aB |= 1;
+ break;
+ default: // aF.prg = 0 : DEFAULT_PITCH (windows.h)
+ break;
+ }
+ aB |= 1 << 2; // aF.fTrueType = 1; don't know any better;
+
+ switch(eFamily)
+ {
+ case FAMILY_ROMAN:
+ aB |= 1 << 4; // aF.ff = 1;
+ break;
+ case FAMILY_SWISS:
+ aB |= 2 << 4; // aF.ff = 2;
+ break;
+ case FAMILY_MODERN:
+ aB |= 3 << 4; // aF.ff = 3;
+ break;
+ case FAMILY_SCRIPT:
+ aB |= 4 << 4; // aF.ff = 4;
+ break;
+ case FAMILY_DECORATIVE:
+ aB |= 5 << 4; // aF.ff = 5;
+ break;
+ default: // aF.ff = 0; FF_DONTCARE (windows.h)
+ break;
+ }
+ maWW8_FFN[1] = aB;
+
+ ShortToSVBT16( 400, &maWW8_FFN[2] ); // don't know any better
+ // 400 == FW_NORMAL (windows.h)
+
+ //#i61927# For unicode fonts like Arial Unicode, Word 97+ sets the chs
+ //to SHIFTJIS presumably to capture that it's a multi-byte encoding font
+ //but Word95 doesn't do this, and sets it to 0 (ANSI), so we should do the
+ //same
+ maWW8_FFN[4] = sw::ms::rtl_TextEncodingToWinCharset(eChrSet);
+
+ if (mbAlt)
+ maWW8_FFN[5] = static_cast< sal_uInt8 >(msFamilyNm.getLength() + 1);
+}
+
+void wwFont::Write(SvStream *pTableStrm) const
+{
+ pTableStrm->WriteBytes(maWW8_FFN, sizeof(maWW8_FFN)); // fixed part
+ // from Ver8 following two fields intersected,
+ // we ignore them.
+ //char panose[ 10 ]; // 0x6 PANOSE
+ //char fs[ 24 ]; // 0x10 FONTSIGNATURE
+ SwWW8Writer::FillCount(*pTableStrm, 0x22);
+ SwWW8Writer::WriteString16(*pTableStrm, msFamilyNm, true);
+ if (mbAlt)
+ SwWW8Writer::WriteString16(*pTableStrm, msAltNm, true);
+}
+
+void wwFont::WriteDocx( DocxAttributeOutput* rAttrOutput ) const
+{
+ // no font embedding, panose id, subsetting, ... implemented
+
+ if (msFamilyNm.isEmpty())
+ return;
+
+ rAttrOutput->StartFont( msFamilyNm );
+
+ if ( mbAlt )
+ rAttrOutput->FontAlternateName( msAltNm );
+ rAttrOutput->FontCharset( sw::ms::rtl_TextEncodingToWinCharset( meChrSet ), meChrSet );
+ rAttrOutput->FontFamilyType( meFamily );
+ rAttrOutput->FontPitchType( mePitch );
+ rAttrOutput->EmbedFont( msFamilyNm, meFamily, mePitch );
+
+ rAttrOutput->EndFont();
+}
+
+void wwFont::WriteRtf( const RtfAttributeOutput* rAttrOutput ) const
+{
+ rAttrOutput->FontFamilyType( meFamily, *this );
+ rAttrOutput->FontPitchType( mePitch );
+ rAttrOutput->FontCharset(
+ sw::ms::rtl_TextEncodingToWinCharsetRTF(msFamilyNm, msAltNm, meChrSet));
+ rAttrOutput->StartFont( msFamilyNm );
+ if ( mbAlt )
+ rAttrOutput->FontAlternateName( msAltNm );
+ rAttrOutput->EndFont();
+}
+
+bool operator<(const wwFont &r1, const wwFont &r2)
+{
+ int nRet = memcmp(r1.maWW8_FFN, r2.maWW8_FFN, sizeof(r1.maWW8_FFN));
+ if (nRet == 0)
+ {
+ nRet = r1.msFamilyNm.compareTo(r2.msFamilyNm);
+ if (nRet == 0)
+ nRet = r1.msAltNm.compareTo(r2.msAltNm);
+ }
+ return nRet < 0;
+}
+
+sal_uInt16 wwFontHelper::GetId(const wwFont &rFont)
+{
+ sal_uInt16 nRet;
+ std::map<wwFont, sal_uInt16>::const_iterator aIter = maFonts.find(rFont);
+ if (aIter != maFonts.end())
+ nRet = aIter->second;
+ else
+ {
+ nRet = static_cast< sal_uInt16 >(maFonts.size());
+ maFonts[rFont] = nRet;
+ }
+ return nRet;
+}
+
+void wwFontHelper::InitFontTable(const SwDoc& rDoc)
+{
+ GetId(wwFont(u"Times New Roman", PITCH_VARIABLE,
+ FAMILY_ROMAN, RTL_TEXTENCODING_MS_1252));
+
+ GetId(wwFont(u"Symbol", PITCH_VARIABLE, FAMILY_ROMAN,
+ RTL_TEXTENCODING_SYMBOL));
+
+ GetId(wwFont(u"Arial", PITCH_VARIABLE, FAMILY_SWISS,
+ RTL_TEXTENCODING_MS_1252));
+
+ const SvxFontItem* pFont = GetDfltAttr(RES_CHRATR_FONT);
+
+ GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
+ pFont->GetFamily(), pFont->GetCharSet()));
+
+ const SfxItemPool& rPool = rDoc.GetAttrPool();
+ pFont = rPool.GetPoolDefaultItem(RES_CHRATR_FONT);
+ if (nullptr != pFont)
+ {
+ GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
+ pFont->GetFamily(), pFont->GetCharSet()));
+ }
+
+ if (!m_bLoadAllFonts)
+ return;
+
+ const sal_uInt16 aTypes[] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT, 0 };
+ for (const sal_uInt16* pId = aTypes; *pId; ++pId)
+ {
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pId))
+ {
+ pFont = static_cast<const SvxFontItem*>(pItem);
+ GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
+ pFont->GetFamily(), pFont->GetCharSet()));
+ }
+ }
+}
+
+sal_uInt16 wwFontHelper::GetId(const SvxFontItem& rFont)
+{
+ wwFont aFont(rFont.GetFamilyName(), rFont.GetPitch(), rFont.GetFamily(),
+ rFont.GetCharSet());
+ return GetId(aFont);
+}
+
+std::vector< const wwFont* > wwFontHelper::AsVector() const
+{
+ std::vector<const wwFont *> aFontList( maFonts.size() );
+
+ for ( const auto& aFont : maFonts )
+ aFontList[aFont.second] = &aFont.first;
+
+ return aFontList;
+}
+
+void wwFontHelper::WriteFontTable(SvStream *pTableStream, WW8Fib& rFib)
+{
+ rFib.m_fcSttbfffn = pTableStream->Tell();
+ /*
+ * Reserve some space to fill in the len after we know how big it is
+ */
+ SwWW8Writer::WriteLong(*pTableStream, 0);
+
+ /*
+ * Convert from fast insertion map to linear vector in the order that we
+ * want to write.
+ */
+ std::vector<const wwFont *> aFontList( AsVector() );
+
+ /*
+ * Write them all to pTableStream
+ */
+ for ( auto aFont : aFontList )
+ aFont->Write(pTableStream);
+
+ /*
+ * Write the position and len in the FIB
+ */
+ rFib.m_lcbSttbfffn = pTableStream->Tell() - rFib.m_fcSttbfffn;
+ SwWW8Writer::WriteLong( *pTableStream, rFib.m_fcSttbfffn, maFonts.size());
+}
+
+void wwFontHelper::WriteFontTable( DocxAttributeOutput& rAttrOutput )
+{
+ std::vector<const wwFont *> aFontList( AsVector() );
+
+ for ( auto aFont : aFontList )
+ aFont->WriteDocx(&rAttrOutput);
+}
+
+void wwFontHelper::WriteFontTable( const RtfAttributeOutput& rAttrOutput )
+{
+ std::vector<const wwFont *> aFontList( AsVector() );
+
+ for ( auto aFont : aFontList )
+ aFont->WriteRtf(&rAttrOutput);
+}
+
+WW8_WrPlc0::WW8_WrPlc0( sal_uLong nOffset )
+ : m_nOfs( nOffset )
+{
+}
+
+void WW8_WrPlc0::Append( sal_uLong nStartCpOrFc )
+{
+ m_aPos.push_back( nStartCpOrFc - m_nOfs );
+}
+
+void WW8_WrPlc0::Write( SvStream& rStrm )
+{
+ for( const auto& rPos : m_aPos )
+ {
+ rStrm.WriteUInt32(rPos);
+ }
+}
+
+// class MSWordSections : translate PageDescs into Sections
+// also deals with header and footer
+
+MSWordSections::MSWordSections( MSWordExportBase& rExport )
+ : mbDocumentIsProtected( false )
+{
+ const SwSectionFormat *pFormat = nullptr;
+ rExport.m_pCurrentPageDesc = &rExport.m_rDoc.GetPageDesc( 0 );
+
+ const SwNode* pNd = rExport.m_pCurPam->GetPointContentNode();
+ const SfxItemSet* pSet = pNd ? &static_cast<const SwContentNode*>(pNd)->GetSwAttrSet() : nullptr;
+
+ sal_uLong nRstLnNum = pSet ? pSet->Get( RES_LINENUMBER ).GetStartValue() : 0;
+
+ const SwTableNode* pTableNd = rExport.m_pCurPam->GetPointNode().FindTableNode();
+ const SwSectionNode* pSectNd = nullptr;
+ if ( pTableNd )
+ {
+ pSet = &pTableNd->GetTable().GetFrameFormat()->GetAttrSet();
+ pNd = pTableNd;
+ }
+ else if (pNd && nullptr != ( pSectNd = pNd->FindSectionNode() ))
+ {
+ if ( SectionType::ToxHeader == pSectNd->GetSection().GetType() &&
+ pSectNd->StartOfSectionNode()->IsSectionNode() )
+ {
+ pSectNd = pSectNd->StartOfSectionNode()->GetSectionNode();
+ }
+
+ if ( SectionType::ToxContent == pSectNd->GetSection().GetType() )
+ {
+ pNd = pSectNd;
+ rExport.m_pCurPam->GetPoint()->Assign(*pNd);
+ }
+
+ if ( SectionType::Content == pSectNd->GetSection().GetType() )
+ pFormat = pSectNd->GetSection().GetFormat();
+ }
+
+ // tdf#118393: FILESAVE: DOCX Export loses header/footer
+ rExport.m_bFirstTOCNodeWithSection = pSectNd &&
+ ( SectionType::ToxHeader == pSectNd->GetSection().GetType() ||
+ SectionType::ToxContent == pSectNd->GetSection().GetType() );
+
+ // Try to get page descriptor of the first node
+ const SwFormatPageDesc* pDescItem;
+ if ( pSet &&
+ (pDescItem = pSet->GetItemIfSet( RES_PAGEDESC )) &&
+ pDescItem->GetPageDesc() )
+ {
+ AppendSection( *pDescItem, *pNd, pFormat, nRstLnNum );
+ }
+ else
+ AppendSection( rExport.m_pCurrentPageDesc, pFormat, nRstLnNum, /*bIsFirstParagraph=*/true );
+}
+
+WW8_WrPlcSepx::WW8_WrPlcSepx( MSWordExportBase& rExport )
+ : MSWordSections( rExport )
+ , m_bHeaderFooterWritten( false )
+{
+ // to be in sync with the AppendSection() call in the MSWordSections
+ // constructor
+ m_aCps.push_back( 0 );
+}
+
+MSWordSections::~MSWordSections()
+{
+}
+
+WW8_WrPlcSepx::~WW8_WrPlcSepx()
+{
+}
+
+bool MSWordSections::HeaderFooterWritten()
+{
+ return false; // only relevant for WW8
+}
+
+bool WW8_WrPlcSepx::HeaderFooterWritten()
+{
+ return m_bHeaderFooterWritten;
+}
+
+sal_uInt16 MSWordSections::CurrentNumberOfColumns( const SwDoc &rDoc ) const
+{
+ OSL_ENSURE( !m_aSects.empty(), "no segment inserted yet" );
+ if ( m_aSects.empty() )
+ return 1;
+
+ return GetFormatCol(rDoc, m_aSects.back()).GetNumCols();
+}
+
+const SwFormatCol& MSWordSections::GetFormatCol(const SwDoc &rDoc, const WW8_SepInfo& rInfo)
+{
+ const SwPageDesc* pPd = rInfo.pPageDesc;
+ if ( !pPd )
+ pPd = &rDoc.GetPageDesc( 0 );
+
+ const SfxItemSet &rSet = pPd->GetMaster().GetAttrSet();
+ SfxItemSetFixed<RES_COL, RES_COL> aSet( *rSet.GetPool() );
+ aSet.SetParent( &rSet );
+
+ //0xffffffff, what the hell is going on with that!, fixme most terribly
+ if ( rInfo.pSectionFormat && reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rInfo.pSectionFormat )
+ aSet.Put( rInfo.pSectionFormat->GetFormatAttr( RES_COL ) );
+
+ return aSet.Get(RES_COL);
+}
+
+const WW8_SepInfo* MSWordSections::CurrentSectionInfo()
+{
+ if ( !m_aSects.empty() )
+ return &m_aSects.back();
+
+ return nullptr;
+}
+
+void MSWordSections::AppendSection( const SwPageDesc* pPd,
+ const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo, bool bIsFirstParagraph )
+{
+ if (HeaderFooterWritten()) {
+ return; // #i117955# prevent new sections in endnotes
+ }
+ m_aSects.emplace_back( pPd, pSectionFormat, nLnNumRestartNo, std::nullopt, nullptr, bIsFirstParagraph );
+ NeedsDocumentProtected( m_aSects.back() );
+}
+
+void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwPageDesc* pPd,
+ const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
+{
+ if (HeaderFooterWritten()) {
+ return; // #i117955# prevent new sections in endnotes
+ }
+ m_aCps.push_back( nStartCp );
+ AppendSection( pPd, pSectionFormat, nLnNumRestartNo );
+}
+
+void MSWordSections::AppendSection( const SwFormatPageDesc& rPD,
+ const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
+{
+ if (HeaderFooterWritten()) {
+ return; // #i117955# prevent new sections in endnotes
+ }
+
+ WW8_SepInfo aI( rPD.GetPageDesc(), pSectionFormat, nLnNumRestartNo, rPD.GetNumOffset(), &rNd );
+
+ m_aSects.push_back( aI );
+ NeedsDocumentProtected( aI );
+}
+
+void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPD,
+ const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
+{
+ if (HeaderFooterWritten()) {
+ return; // #i117955# prevent new sections in endnotes
+ }
+ m_aCps.push_back( nStartCp );
+ AppendSection( rPD, rNd, pSectionFormat, nLnNumRestartNo );
+}
+
+void WW8_WrPlcSepx::WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt )
+{
+ sal_uInt8 nInfoFlags = 0;
+ const SwFootnoteInfo& rInfo = rWrt.m_rDoc.GetFootnoteInfo();
+ if( !rInfo.m_aErgoSum.isEmpty() ) nInfoFlags |= 0x02;
+ if( !rInfo.m_aQuoVadis.isEmpty() ) nInfoFlags |= 0x04;
+
+ sal_uInt8 nEmptyStt = 0;
+ if( nInfoFlags )
+ {
+ m_pTextPos->Append( nCpStt ); // empty footnote separator
+
+ if( 0x02 & nInfoFlags ) // Footnote continuation separator
+ {
+ m_pTextPos->Append( nCpStt );
+ rWrt.WriteStringAsPara( rInfo.m_aErgoSum );
+ rWrt.WriteStringAsPara( OUString() );
+ nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ }
+ else
+ m_pTextPos->Append( nCpStt );
+
+ if( 0x04 & nInfoFlags ) // Footnote continuation notice
+ {
+ m_pTextPos->Append( nCpStt );
+ rWrt.WriteStringAsPara( rInfo.m_aQuoVadis );
+ rWrt.WriteStringAsPara( OUString() );
+ nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ }
+ else
+ m_pTextPos->Append( nCpStt );
+
+ nEmptyStt = 3;
+ }
+
+ while( 6 > nEmptyStt++ )
+ m_pTextPos->Append( nCpStt );
+
+ // set the flags at the Dop right away
+ WW8Dop& rDop = *rWrt.m_pDop;
+ // Footnote Info
+ switch( rInfo.m_eNum )
+ {
+ case FTNNUM_PAGE: rDop.rncFootnote = 2; break;
+ case FTNNUM_CHAPTER: rDop.rncFootnote = 1; break;
+ default: rDop.rncFootnote = 0; break;
+ } // rncFootnote
+ rDop.nfcFootnoteRef = WW8Export::GetNumId( rInfo.m_aFormat.GetNumberingType() );
+ rDop.nFootnote = rInfo.m_nFootnoteOffset + 1;
+ rDop.fpc = rWrt.m_bFootnoteAtTextEnd ? 2 : 1;
+
+ // Endnote Info
+ rDop.rncEdn = 0; // rncEdn: Don't Restart
+ const SwEndNoteInfo& rEndInfo = rWrt.m_rDoc.GetEndNoteInfo();
+ rDop.nfcEdnRef = WW8Export::GetNumId( rEndInfo.m_aFormat.GetNumberingType() );
+ rDop.nEdn = rEndInfo.m_nFootnoteOffset + 1;
+ rDop.epc = rWrt.m_bEndAtTextEnd ? 3 : 0;
+}
+
+void MSWordSections::SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
+ sal_uInt8 nFlag )
+{
+ const SwFormatHeader* pItem = rFormat.GetItemIfSet(RES_HEADER);
+ if( pItem && pItem->IsActive() && pItem->GetHeaderFormat() )
+ rHeadFootFlags |= nFlag;
+}
+
+void MSWordSections::SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
+ sal_uInt8 nFlag )
+{
+ const SwFormatFooter* pItem = rFormat.GetItemIfSet(RES_FOOTER);
+ if( pItem && pItem->IsActive() && pItem->GetFooterFormat() )
+ rHeadFootFlags |= nFlag;
+}
+
+void WW8_WrPlcSepx::OutHeaderFooter( WW8Export& rWrt, bool bHeader,
+ const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags,
+ sal_uInt8 nFlag, sal_uInt8 nBreakCode)
+{
+ if ( nFlag & nHFFlags )
+ {
+ m_pTextPos->Append( rCpPos );
+ rWrt.WriteHeaderFooterText( rFormat, bHeader);
+ rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
+ rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ }
+ else
+ {
+ m_pTextPos->Append( rCpPos );
+ if ((bHeader? rWrt.m_bHasHdr : rWrt.m_bHasFtr) && nBreakCode!=0)
+ {
+ rWrt.WriteStringAsPara( OUString() ); // Empty paragraph for empty header/footer
+ rWrt.WriteStringAsPara( OUString() ); // a CR that WW8 needs for end of the stream
+ rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ }
+ }
+}
+
+void MSWordSections::NeedsDocumentProtected(const WW8_SepInfo &rInfo)
+{
+ if (rInfo.IsProtected())
+ mbDocumentIsProtected = true;
+}
+
+bool WW8_SepInfo::IsProtected() const
+{
+ bool bRet = false;
+ if (
+ pSectionFormat &&
+ (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != pSectionFormat)
+ )
+ {
+ const SwSection *pSection = pSectionFormat->GetSection();
+ if (pSection && pSection->IsProtect())
+ {
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void MSWordSections::CheckForFacinPg( const WW8Export& rWrt ) const
+{
+ // 2 values getting set
+ // Dop.fFacingPages == Header and Footer different
+ // Dop.fSwapBordersFacingPgs == mirrored borders
+ sal_uInt16 nEnd = 0;
+ for( const WW8_SepInfo& rSepInfo : m_aSects )
+ {
+ if( !rSepInfo.pSectionFormat )
+ {
+ const SwPageDesc* pPd = rSepInfo.pPageDesc;
+ if( pPd->GetFollow() && pPd != pPd->GetFollow() &&
+ pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
+ rSepInfo.pPDNd &&
+ pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) )
+ // so this is first page and subsequent, so only respect follow
+ pPd = pPd->GetFollow();
+
+ // left-/right chain of pagedescs ?
+ else if( !( 1 & nEnd ) &&
+ pPd->GetFollow() && pPd != pPd->GetFollow() &&
+ pPd->GetFollow()->GetFollow() == pPd &&
+ (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
+ UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
+ ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
+ UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
+ {
+ rWrt.m_pDop->fFacingPages = rWrt.m_pDop->fMirrorMargins = true;
+ nEnd |= 1;
+ }
+
+ if( !( 1 & nEnd ) &&
+ ( !pPd->IsHeaderShared() || !pPd->IsFooterShared() ))
+ {
+ rWrt.m_pDop->fFacingPages = true;
+ nEnd |= 1;
+ }
+ if( !( 2 & nEnd ) &&
+ UseOnPage::Mirror == ( UseOnPage::Mirror & pPd->ReadUseOn() ))
+ {
+ rWrt.m_pDop->fSwapBordersFacingPgs =
+ rWrt.m_pDop->fMirrorMargins = true;
+ nEnd |= 2;
+ }
+
+ if( 3 == nEnd )
+ break; // We do not need to go any further
+ }
+ }
+}
+
+bool MSWordSections::HasBorderItem( const SwFormat& rFormat )
+{
+ const SvxBoxItem* pItem = rFormat.GetItemIfSet(RES_BOX);
+ return pItem &&
+ ( pItem->GetTop() ||
+ pItem->GetBottom() ||
+ pItem->GetLeft() ||
+ pItem->GetRight() );
+}
+
+void WW8AttributeOutput::StartSection()
+{
+ m_rWW8Export.m_pO->clear();
+}
+
+void WW8AttributeOutput::SectFootnoteEndnotePr()
+{
+ const SwFootnoteInfo& rInfo = m_rWW8Export.m_rDoc.GetFootnoteInfo();
+ const SwEndNoteInfo& rEndNoteInfo = m_rWW8Export.m_rDoc.GetEndNoteInfo();
+ m_rWW8Export.InsUInt16( NS_sprm::SRncFtn::val );
+ switch( rInfo.m_eNum )
+ {
+ case FTNNUM_PAGE: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncRstPage*/ (2) ); break;
+ case FTNNUM_CHAPTER: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncRstSect*/ (1) ); break;
+ default: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncCont*/ (0) ); break;
+ }
+
+ m_rWW8Export.InsUInt16(NS_sprm::SNfcFtnRef::val);
+ sal_uInt8 nId = WW8Export::GetNumId(rInfo.m_aFormat.GetNumberingType());
+ SwWW8Writer::InsUInt16(*m_rWW8Export.m_pO, nId);
+ m_rWW8Export.InsUInt16(NS_sprm::SNfcEdnRef::val);
+ nId = WW8Export::GetNumId(rEndNoteInfo.m_aFormat.GetNumberingType());
+ SwWW8Writer::InsUInt16(*m_rWW8Export.m_pO, nId);
+}
+
+void WW8AttributeOutput::SectionFormProtection( bool bProtected )
+{
+ //If the document is to be exported as protected, then if a segment
+ //is not protected, set the unlocked flag
+ if ( m_rWW8Export.m_pSepx->DocumentIsProtected() && !bProtected )
+ {
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFProtected::val );
+ m_rWW8Export.m_pO->push_back( 1 );
+ }
+}
+
+void WW8AttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
+{
+ // sprmSNLnnMod - activate Line Numbering and define Modulo
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SNLnnMod::val );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rLnNumInfo.GetCountBy() );
+
+ // sprmSDxaLnn - xPosition of Line Number
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SDxaLnn::val );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rLnNumInfo.GetPosFromLeft() );
+
+ // sprmSLnc - restart number: 0 per page, 1 per section, 2 never restart
+ if ( nRestartNo || !rLnNumInfo.IsRestartEachPage() )
+ {
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SLnc::val );
+ m_rWW8Export.m_pO->push_back( nRestartNo ? 1 : 2 );
+ }
+
+ // sprmSLnnMin - Restart the Line Number with given value
+ if ( nRestartNo )
+ {
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SLnnMin::val );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, o3tl::narrowing<sal_uInt16>(nRestartNo) - 1 );
+ }
+}
+
+void WW8AttributeOutput::SectionTitlePage()
+{
+ // sprmSFTitlePage
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFTitlePage::val );
+ m_rWW8Export.m_pO->push_back( 1 );
+}
+
+void WW8AttributeOutput::SectionPageBorders( const SwFrameFormat* pPdFormat, const SwFrameFormat* pPdFirstPgFormat )
+{
+ // write border of page
+ sal_uInt16 nPgBorder = MSWordSections::HasBorderItem( *pPdFormat ) ? 0 : USHRT_MAX;
+ if ( pPdFormat != pPdFirstPgFormat )
+ {
+ if ( MSWordSections::HasBorderItem( *pPdFirstPgFormat ) )
+ {
+ if ( USHRT_MAX == nPgBorder )
+ {
+ nPgBorder = 1;
+ // only the first page outlined -> Get the BoxItem from the correct format
+ m_rWW8Export.m_pISet = &pPdFirstPgFormat->GetAttrSet();
+ OutputItem( pPdFirstPgFormat->GetFormatAttr( RES_BOX ) );
+ }
+ }
+ else if ( !nPgBorder )
+ nPgBorder = 2;
+ }
+
+ // [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom
+ if (m_bFromEdge)
+ nPgBorder |= (1<<5);
+
+ if ( USHRT_MAX != nPgBorder )
+ {
+ // write the Flag and Border Attribute
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SPgbProp::val );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nPgBorder );
+ }
+}
+
+void WW8AttributeOutput::SectionBiDi( bool bBiDi )
+{
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFBiDi::val );
+ m_rWW8Export.m_pO->push_back( bBiDi? 1: 0 );
+}
+
+void WW8AttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber )
+{
+ // sprmSNfcPgn
+ sal_uInt8 nb = WW8Export::GetNumId( nNumType );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SNfcPgn::val );
+ m_rWW8Export.m_pO->push_back( nb );
+
+ if ( oPageRestartNumber )
+ {
+ // sprmSFPgnRestart
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFPgnRestart::val );
+ m_rWW8Export.m_pO->push_back( 1 );
+
+ // sprmSPgnStart
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SPgnStart97::val );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, *oPageRestartNumber );
+ }
+}
+
+void WW8AttributeOutput::SectionType( sal_uInt8 nBreakCode )
+{
+ if ( 2 != nBreakCode ) // new page is the default
+ {
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SBkc::val );
+ m_rWW8Export.m_pO->push_back( nBreakCode );
+ }
+}
+
+void WW8Export::SetupSectionPositions( WW8_PdAttrDesc* pA )
+{
+ if ( !pA )
+ return;
+
+ if ( !m_pO->empty() ) // are there attributes ?
+ {
+ pA->m_nLen = m_pO->size();
+ pA->m_pData.reset(new sal_uInt8 [m_pO->size()]);
+ // store for later
+ memcpy( pA->m_pData.get(), m_pO->data(), m_pO->size() );
+ m_pO->clear(); // clear HdFt-Text
+ }
+ else // no attributes there
+ {
+ pA->m_pData.reset();
+ pA->m_nLen = 0;
+ }
+}
+
+void WW8AttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
+{
+ if ( drawing::TextVerticalAdjust_TOP == nVA ) // top alignment is the default
+ return;
+
+ sal_uInt8 nMSVA = 0;
+ switch( nVA )
+ {
+ case drawing::TextVerticalAdjust_CENTER:
+ nMSVA = 1;
+ break;
+ case drawing::TextVerticalAdjust_BOTTOM: //Writer = 2, Word = 3
+ nMSVA = 3;
+ break;
+ case drawing::TextVerticalAdjust_BLOCK: //Writer = 3, Word = 2
+ nMSVA = 2;
+ break;
+ default:
+ break;
+ }
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SVjc::val );
+ m_rWW8Export.m_pO->push_back( nMSVA );
+}
+
+void WW8Export::WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
+ const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode, bool /*bEvenAndOddHeaders*/ )
+{
+ sal_uLong nCpPos = Fc2Cp( Strm().Tell() );
+
+ IncrementHdFtIndex();
+ if ( !(nHeadFootFlags & WW8_HEADER_EVEN) && m_pDop->fFacingPages )
+ m_pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
+ else
+ m_pSepx->OutHeaderFooter( *this, true, rLeftHeaderFormat, nCpPos, nHeadFootFlags, WW8_HEADER_EVEN, nBreakCode );
+ IncrementHdFtIndex();
+ m_pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
+
+ IncrementHdFtIndex();
+ if ( !(nHeadFootFlags & WW8_FOOTER_EVEN) && m_pDop->fFacingPages )
+ m_pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
+ else
+ m_pSepx->OutHeaderFooter( *this, false, rLeftFooterFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_EVEN, nBreakCode );
+ IncrementHdFtIndex();
+ m_pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
+
+ //#i24344# Drawing objects cannot be directly shared between main hd/ft
+ //and title hd/ft so we need to differentiate them
+ IncrementHdFtIndex();
+ m_pSepx->OutHeaderFooter( *this, true, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_HEADER_FIRST, nBreakCode );
+ m_pSepx->OutHeaderFooter( *this, false, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_FIRST, nBreakCode );
+}
+
+namespace
+{
+/**
+ * Determines if the continuous section break we start should use page style properties (header,
+ * footer, margins) from the next page style of the previous section.
+ */
+bool UsePrevSectionNextStyle(sal_uInt8 nBreakCode, const SwPageDesc* pPd,
+ const WW8_SepInfo& rSepInfo)
+{
+ if (nBreakCode != 0)
+ {
+ // Not a continuous section break.
+ return false;
+ }
+
+ if (!pPd->GetFollow())
+ {
+ // Page style has no follow style.
+ return false;
+ }
+
+ // We start a continuous section break without headers/footers. Possibly the importer had
+ // headers/footers for this section break and put them to the closest page break's page style's
+ // next page style. See "find a node in the section that has a page break" in writerfilter/.
+ // Try the last-in-practice paragraph of the previous section.
+ const SwSectionFormat* pSection = rSepInfo.pSectionFormat;
+ if (pSection == reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
+ {
+ return false;
+ }
+
+ const SwNodeIndex* pSectionStart = pSection->GetContent().GetContentIdx();
+ if (!pSectionStart)
+ {
+ return false;
+ }
+
+ SwPaM aPaM(*pSectionStart);
+ aPaM.Move(fnMoveBackward);
+ if (!aPaM.GetPointNode().IsTextNode())
+ {
+ return false;
+ }
+
+ SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();
+ const SwAttrSet* pParaProps = &pTextNode->GetSwAttrSet();
+ sal_uInt32 nCharHeight = pParaProps->GetSize().GetHeight();
+ if (nCharHeight > 20)
+ {
+ return false;
+ }
+
+ aPaM.Move(fnMoveBackward);
+ if (!aPaM.GetPointNode().IsTextNode())
+ {
+ return false;
+ }
+
+ pTextNode = aPaM.GetPointNode().GetTextNode();
+ pParaProps = &pTextNode->GetSwAttrSet();
+ return pParaProps->HasItem(RES_PAGEDESC);
+}
+}
+
+void MSWordExportBase::SectionProperties( const WW8_SepInfo& rSepInfo, WW8_PdAttrDesc* pA )
+{
+ const SwPageDesc* pPd = rSepInfo.pPageDesc;
+
+ if ( rSepInfo.pSectionFormat && !pPd )
+ pPd = &m_rDoc.GetPageDesc( 0 );
+
+ m_pCurrentPageDesc = pPd;
+
+ if ( !pPd )
+ return;
+
+ bool bOldPg = m_bOutPageDescs;
+ m_bOutPageDescs = true;
+ const SwPageDesc* pSavedPageDesc = pPd;
+
+ AttrOutput().StartSection();
+
+ AttrOutput().SectFootnoteEndnotePr();
+
+ // forms
+ AttrOutput().SectionFormProtection( rSepInfo.IsProtected() );
+
+ // line numbers
+ const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
+ if ( rLnNumInfo.IsPaintLineNumbers() )
+ AttrOutput().SectionLineNumbering( rSepInfo.nLnNumRestartNo, rLnNumInfo );
+
+ /* sprmSBkc, break code: 0 No break, 1 New column
+ 2 New page, 3 Even page, 4 Odd page
+ */
+ sal_uInt8 nBreakCode = 2; // default start new page
+ bool bOutPgDscSet = true, bLeftRightPgChain = false, bOutputStyleItemSet = false;
+ const SwFrameFormat* pPdFormat = &pPd->GetMaster();
+ bool bUsePrevSectionNextStyle = false;
+ if ( rSepInfo.pSectionFormat )
+ {
+ // if pSectionFormat is set, then there is a SectionNode
+ // valid pointer -> start Section ,
+ // 0xfff -> Section terminated
+ nBreakCode = 0; // consecutive section
+
+ if (rSepInfo.pPDNd && (rSepInfo.pPDNd->IsContentNode() || rSepInfo.pPDNd->IsTableNode()))
+ {
+ const SfxItemSet* pSet
+ = rSepInfo.pPDNd->IsContentNode()
+ ? &rSepInfo.pPDNd->GetContentNode()->GetSwAttrSet()
+ : &rSepInfo.pPDNd->GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet();
+
+ if (!NoPageBreakSection(pSet))
+ nBreakCode = 2;
+ }
+
+ if (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rSepInfo.pSectionFormat)
+ {
+ if ( nBreakCode == 0 )
+ bOutPgDscSet = false;
+
+ // produce Itemset, which inherits PgDesk-Attr-Set:
+ // as child also the parent is searched if 'deep'-OutputItemSet
+ const SfxItemSet* pPdSet = &pPdFormat->GetAttrSet();
+
+ bUsePrevSectionNextStyle = GetExportFormat() == ExportFormat::DOCX
+ && UsePrevSectionNextStyle(nBreakCode, pPd, rSepInfo);
+ if (bUsePrevSectionNextStyle)
+ {
+ // Take page margins from the previous section's next style.
+ pPdSet = &pPd->GetFollow()->GetMaster().GetAttrSet();
+ }
+
+ SfxItemSet aSet( *pPdSet->GetPool(), pPdSet->GetRanges() );
+ aSet.SetParent( pPdSet );
+
+ // at the child ONLY change column structure according to Sect-Attr.
+
+ const SvxLRSpaceItem &rSectionLR =
+ rSepInfo.pSectionFormat->GetFormatAttr( RES_LR_SPACE );
+ const SvxLRSpaceItem &rPageLR =
+ pPdFormat->GetFormatAttr( RES_LR_SPACE );
+
+ SvxLRSpaceItem aResultLR( rPageLR.GetLeft() +
+ rSectionLR.GetLeft(), rPageLR.GetRight() +
+ rSectionLR.GetRight(), 0, RES_LR_SPACE );
+ //i120133: The Section width should consider section indent value.
+ if (rSectionLR.GetLeft()+rSectionLR.GetRight()!=0)
+ {
+ const SwFormatCol& rCol = rSepInfo.pSectionFormat->GetFormatAttr(RES_COL);
+ SwFormatCol aCol(rCol);
+ aCol.SetAdjustValue(rSectionLR.GetLeft()+rSectionLR.GetRight());
+ aSet.Put(aCol);
+ }
+ else
+ aSet.Put(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL));
+
+ aSet.Put( aResultLR );
+
+ // and write into the WW-File
+ const SfxItemSet* pOldI = m_pISet;
+ m_pISet = &aSet;
+
+ // Switch off test on default item values, if page description
+ // set (value of <bOutPgDscSet>) isn't written.
+ AttrOutput().OutputStyleItemSet( aSet, bOutPgDscSet );
+ bOutputStyleItemSet = true;
+
+ //Cannot export as normal page framedir, as continuous sections
+ //cannot contain any grid settings like proper sections
+ AttrOutput().SectionBiDi( SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ) );
+
+ m_pISet = pOldI;
+ }
+ }
+
+ // Libreoffice 4.0 introduces support for page styles (SwPageDesc) with
+ // a different header/footer for the first page. The same effect can be
+ // achieved by chaining two page styles together (SwPageDesc::GetFollow)
+ // which are identical except for header/footer.
+ // The latter method was previously used by the doc/docx import filter.
+ // In both of these cases, we emit a single Word section with different
+ // first page header/footer.
+ const SwFrameFormat* pPdFirstPgFormat = &pPd->GetFirstMaster();
+ bool titlePage = !pPd->IsFirstShared();
+ if ( bOutPgDscSet )
+ {
+ // if a Follow is set and it does not point to itself,
+ // then there is a page chain.
+ // If this emulates a "first page", we can detect it here and write
+ // it as title page.
+ // With Left/Right changes it's different - we have to detect where
+ // the change of pages is, but here it's too late for that!
+ if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
+ pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
+ pPd->IsHeaderShared() && pPd->IsFooterShared() &&
+ ( !rSepInfo.pPDNd || pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) ) )
+ {
+ const SwPageDesc *pFollow = pPd->GetFollow();
+ const SwFrameFormat& rFollowFormat = pFollow->GetMaster();
+ if (sw::util::IsPlausableSingleWordSection(*pPdFirstPgFormat, rFollowFormat))
+ {
+ if (titlePage)
+ {
+ // Do nothing. First format is already set.
+ }
+ else if (rSepInfo.pPDNd)
+ pPdFirstPgFormat = pPd->GetPageFormatOfNode( *rSepInfo.pPDNd );
+ else
+ pPdFirstPgFormat = &pPd->GetMaster();
+
+ m_pCurrentPageDesc = pPd = pFollow;
+ pPdFormat = &rFollowFormat;
+
+ // has different headers/footers for the title page
+ titlePage = true;
+ }
+ }
+ else if (nBreakCode == 2 && pPd == m_pPreviousSectionPageDesc && pPd->GetFollow() == pPd)
+ {
+ // The first title page has already been displayed in the previous section. Drop it.
+ titlePage = false;
+ }
+
+ const SfxItemSet* pOldI = m_pISet;
+
+ const SfxPoolItem* pItem;
+ if ( titlePage && SfxItemState::SET ==
+ pPdFirstPgFormat->GetItemState( RES_PAPER_BIN, true, &pItem ) )
+ {
+ m_pISet = &pPdFirstPgFormat->GetAttrSet();
+ m_bOutFirstPage = true;
+ AttrOutput().OutputItem( *pItem );
+ m_bOutFirstPage = false;
+ }
+
+ // left-/right chain of pagedescs ?
+ if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
+ pPd->GetFollow()->GetFollow() == pPd &&
+ (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
+ UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
+ ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
+ UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
+ {
+ bLeftRightPgChain = true;
+
+ // which is the reference point? (left or right?)
+ // assume it is on the right side!
+ if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
+ {
+ nBreakCode = 3;
+ pPdFormat = &pPd->GetMaster(); //use the current page for settings (margins/width etc)
+ pPd = pPd->GetFollow(); //switch to the right page for the right/odd header/footer
+ }
+ else
+ nBreakCode = 4;
+ }
+
+ m_pISet = &pPdFormat->GetAttrSet();
+ if (!bOutputStyleItemSet)
+ {
+ if (titlePage)
+ {
+ m_pFirstPageFormat = pPdFirstPgFormat;
+ }
+
+ AttrOutput().OutputStyleItemSet( pPdFormat->GetAttrSet(), false );
+
+ if (titlePage)
+ {
+ m_pFirstPageFormat = nullptr;
+ }
+ }
+ AttrOutput().SectionPageBorders( pPdFormat, pPdFirstPgFormat );
+ m_pISet = pOldI;
+
+ // then the rest of the settings from PageDesc
+ AttrOutput().SectionPageNumbering( pPd->GetNumType().GetNumberingType(), rSepInfo.oPgRestartNo );
+
+ // will it be only left or only right pages?
+ if ( 2 == nBreakCode )
+ {
+ if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
+ nBreakCode = 3;
+ else if ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) )
+ nBreakCode = 4;
+ }
+ }
+
+ if (titlePage)
+ AttrOutput().SectionTitlePage();
+
+ AttrOutput().SectionType( nBreakCode );
+
+ if( rSepInfo.pPageDesc ) {
+ AttrOutput().TextVerticalAdjustment( rSepInfo.pPageDesc->GetVerticalAdjustment() );
+ }
+
+ // Header or Footer
+ sal_uInt8 nHeadFootFlags = 0;
+ // Should we output a w:evenAndOddHeaders tag or not?
+ // N.B.: despite its name this tag affects _both_ headers and footers!
+ bool bEvenAndOddHeaders = true;
+ bool bEvenAndOddFooters = true;
+
+ const SwFrameFormat* pPdLeftHeaderFormat = nullptr;
+ const SwFrameFormat* pPdLeftFooterFormat = nullptr;
+ if (bLeftRightPgChain)
+ {
+ const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, true);
+ const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, true);
+ if (pHeaderFormat)
+ {
+ pPdLeftHeaderFormat = pHeaderFormat;
+ bEvenAndOddHeaders = false;
+ }
+ else
+ {
+ pPdLeftHeaderFormat = &pPd->GetFollow()->GetFirstLeft();
+ }
+ if (pFooterFormat)
+ {
+ pPdLeftFooterFormat = pFooterFormat;
+ bEvenAndOddFooters = false;
+ }
+ else
+ {
+ pPdLeftFooterFormat = &pPd->GetFollow()->GetFirstLeft();
+ }
+ }
+ else
+ {
+ const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, false);
+ const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, false);
+ if (pHeaderFormat)
+ {
+ pPdLeftHeaderFormat = pHeaderFormat;
+ bEvenAndOddHeaders = false;
+ }
+ else
+ {
+ pPdLeftHeaderFormat = &pPd->GetLeft();
+ }
+ if (pFooterFormat)
+ {
+ pPdLeftFooterFormat = pFooterFormat;
+ bEvenAndOddFooters = false;
+ }
+ else
+ {
+ pPdLeftFooterFormat = &pPd->GetLeft();
+ }
+ }
+
+ // Ensure that headers are written if section is first paragraph
+ if (nBreakCode != 0 || (rSepInfo.pSectionFormat && rSepInfo.bIsFirstParagraph))
+ {
+ if ( titlePage )
+ {
+ // there is a First Page:
+ MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_HEADER_FIRST );
+ MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_FOOTER_FIRST );
+ }
+ else
+ {
+ if ( pPd->GetStashedFrameFormat(true, true, true) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
+ {
+ MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_FIRST );
+ }
+ if ( pPd->GetStashedFrameFormat(false, true, true) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
+ {
+ MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_FIRST );
+ }
+ }
+
+ MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD );
+ MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD );
+
+ if ( !pPd->IsHeaderShared() || bLeftRightPgChain )
+ {
+ MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
+ }
+ else if ( pPd->IsHeaderShared() && pPd->GetStashedFrameFormat(true, true, false) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
+ {
+ MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
+ bEvenAndOddHeaders = false;
+ }
+
+ if ( !pPd->IsFooterShared() || bLeftRightPgChain )
+ {
+ MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
+ }
+ else if ( pPd->IsFooterShared() && pPd->GetStashedFrameFormat(false, true, false) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
+ {
+ MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
+ bEvenAndOddFooters = false;
+ }
+ }
+
+ // binary filters only
+ SetupSectionPositions( pA );
+
+ /*
+ !!!!!!!!!!!
+ // borders at header and footer texts would be done like this:
+ // This should use something like pOut,
+ // which is repeated with every special text line.
+ const SwFrameFormat* pFFormat = rFt.GetFooterFormat();
+ const SvxBoxItem& rBox = pFFormat->GetBox(false);
+ OutWW8_SwFormatBox1( m_rWW8Export.pOut, rBox, false);
+ !!!!!!!!!!!
+ You can turn this into paragraph attributes, which are then observed in each paragraph.
+ Applies to background / border.
+ !!!!!!!!!!!
+ */
+
+ const SwTextNode *pOldPageRoot = GetHdFtPageRoot();
+ SetHdFtPageRoot( rSepInfo.pPDNd ? rSepInfo.pPDNd->GetTextNode() : nullptr );
+
+ if (bUsePrevSectionNextStyle && nHeadFootFlags == 0)
+ {
+ // Take headers/footers from the previous section's next style.
+ pPdFormat = &pPd->GetFollow()->GetMaster();
+ MSWordSections::SetHeaderFlag(nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD);
+ MSWordSections::SetFooterFlag(nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD);
+ }
+
+ WriteHeadersFooters( nHeadFootFlags, *pPdFormat, *pPdLeftHeaderFormat, *pPdLeftFooterFormat, *pPdFirstPgFormat, nBreakCode, bEvenAndOddHeaders && bEvenAndOddFooters );
+
+ SetHdFtPageRoot( pOldPageRoot );
+
+ AttrOutput().EndSection();
+
+ // outside of the section properties again
+ m_bOutPageDescs = bOldPg;
+ m_pPreviousSectionPageDesc = pSavedPageDesc;
+}
+
+bool WW8_WrPlcSepx::WriteKFText( WW8Export& rWrt )
+{
+ sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+
+ OSL_ENSURE( !m_pTextPos, "who set the pointer?" );
+ m_pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
+
+ WriteFootnoteEndText( rWrt, nCpStart );
+ CheckForFacinPg( rWrt );
+
+ unsigned int nOldIndex = rWrt.GetHdFtIndex();
+ rWrt.SetHdFtIndex( 0 );
+
+ for (const WW8_SepInfo & rSepInfo : m_aSects)
+ {
+ auto pAttrDesc = std::make_shared<WW8_PdAttrDesc>();
+ m_SectionAttributes.push_back(pAttrDesc);
+
+ rWrt.SectionProperties( rSepInfo, pAttrDesc.get() );
+
+ // FIXME: this writes the section properties, but not of all sections;
+ // it's possible that later in the document (e.g. in endnotes) sections
+ // are added, but they won't have their properties written here!
+ m_bHeaderFooterWritten = true;
+ }
+ rWrt.SetHdFtIndex( nOldIndex ); //0
+
+ if ( m_pTextPos->Count() )
+ {
+ // HdFt available?
+ sal_uLong nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ m_pTextPos->Append( nCpEnd ); // End of last Header/Footer for PlcfHdd
+
+ if ( nCpEnd > nCpStart )
+ {
+ ++nCpEnd;
+ m_pTextPos->Append( nCpEnd + 1 ); // End of last Header/Footer for PlcfHdd
+
+ rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
+ }
+ rWrt.m_pFieldHdFt->Finish( nCpEnd, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote );
+ rWrt.m_pFib->m_ccpHdr = nCpEnd - nCpStart;
+ }
+ else
+ {
+ m_pTextPos.reset();
+ }
+
+ return rWrt.m_pFib->m_ccpHdr != 0;
+}
+
+void WW8_WrPlcSepx::WriteSepx( SvStream& rStrm ) const
+{
+ OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(m_aSects.size())
+ , "WriteSepx(): arrays out of sync!");
+ for (const auto & rSectionAttribute : m_SectionAttributes) // all sections
+ {
+ WW8_PdAttrDesc *const pA = rSectionAttribute.get();
+ if (pA->m_nLen && pA->m_pData != nullptr)
+ {
+ pA->m_nSepxFcPos = rStrm.Tell();
+ rStrm.WriteUInt16(pA->m_nLen);
+ rStrm.WriteBytes(pA->m_pData.get(), pA->m_nLen);
+ }
+ }
+}
+
+void WW8_WrPlcSepx::WritePlcSed( WW8Export& rWrt ) const
+{
+ OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(m_aSects.size())
+ , "WritePlcSed(): arrays out of sync!");
+ OSL_ENSURE( m_aCps.size() == m_aSects.size() + 1, "WrPlcSepx: DeSync" );
+ sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
+
+ for( decltype(m_aSects)::size_type i = 0; i <= m_aSects.size(); i++ )
+ {
+ sal_uInt32 nP = m_aCps[i];
+ rWrt.m_pTableStrm->WriteUInt32(nP);
+ }
+
+ static WW8_SED aSed = {{4, 0},{0, 0, 0, 0},{0, 0},{0xff, 0xff, 0xff, 0xff}};
+
+ for (const auto & rSectionAttribute : m_SectionAttributes)
+ {
+ // Sepx-Pos
+ UInt32ToSVBT32( rSectionAttribute->m_nSepxFcPos, aSed.fcSepx );
+ rWrt.m_pTableStrm->WriteBytes(&aSed, sizeof(aSed));
+ }
+ rWrt.m_pFib->m_fcPlcfsed = nFcStart;
+ rWrt.m_pFib->m_lcbPlcfsed = rWrt.m_pTableStrm->Tell() - nFcStart;
+}
+
+void WW8_WrPlcSepx::WritePlcHdd( WW8Export& rWrt ) const
+{
+ // Don't write out the PlcfHdd if ccpHdd is 0: it's a validation failure case.
+ if( rWrt.m_pFib->m_ccpHdr != 0 && m_pTextPos && m_pTextPos->Count() )
+ {
+ rWrt.m_pFib->m_fcPlcfhdd = rWrt.m_pTableStrm->Tell();
+ m_pTextPos->Write( *rWrt.m_pTableStrm ); // Plc0
+ rWrt.m_pFib->m_lcbPlcfhdd = rWrt.m_pTableStrm->Tell() -
+ rWrt.m_pFib->m_fcPlcfhdd;
+ }
+}
+
+void MSWordExportBase::WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader )
+{
+ const SwFormatContent *pContent;
+ if ( bHeader )
+ {
+ m_bHasHdr = true;
+ const SwFormatHeader& rHd = rFormat.GetHeader();
+ OSL_ENSURE( rHd.GetHeaderFormat(), "Header text is not here" );
+
+ if ( !rHd.GetHeaderFormat() )
+ return;
+
+ pContent = &rHd.GetHeaderFormat()->GetContent();
+ }
+ else
+ {
+ m_bHasFtr = true;
+ const SwFormatFooter& rFt = rFormat.GetFooter();
+ OSL_ENSURE( rFt.GetFooterFormat(), "Footer text is not here" );
+
+ if ( !rFt.GetFooterFormat() )
+ return;
+
+ pContent = &rFt.GetFooterFormat()->GetContent();
+ }
+
+ const SwNodeIndex* pSttIdx = pContent->GetContentIdx();
+
+ if ( pSttIdx )
+ {
+ SwNodeIndex aIdx( *pSttIdx, 1 ),
+ aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
+ SwNodeOffset nStart = aIdx.GetIndex();
+ SwNodeOffset nEnd = aEnd.GetIndex();
+
+ // range, i.e. valid node
+ if ( nStart < nEnd )
+ {
+ bool bOldKF = m_bOutKF;
+ m_bOutKF = true;
+ WriteSpecialText( nStart, nEnd, TXT_HDFT );
+ m_bOutKF = bOldKF;
+ }
+ else
+ pSttIdx = nullptr;
+ }
+
+ if ( !pSttIdx )
+ {
+ // there is no Header/Footer, but a CR is still necessary
+ OSL_ENSURE( pSttIdx, "Header/Footer text is not really present" );
+ AttrOutput().EmptyParagraph();
+ }
+}
+
+// class WW8_WrPlcFootnoteEdn : Collect the Footnotes and Endnotes and output their text
+// and Plcs at the end of the document.
+// WW8_WrPlcFootnoteEdn is the class for Footnotes and Endnotes
+
+WW8_WrPlcSubDoc::WW8_WrPlcSubDoc()
+{
+}
+
+WW8_WrPlcSubDoc::~WW8_WrPlcSubDoc()
+{
+}
+
+void WW8_WrPlcFootnoteEdn::Append( WW8_CP nCp, const SwFormatFootnote& rFootnote )
+{
+ m_aCps.push_back( nCp );
+ m_aContent.push_back( &rFootnote );
+}
+
+WW8_Annotation::WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd)
+ :
+ maDateTime( DateTime::EMPTY ),
+ m_nRangeStart(nRangeStart),
+ m_nRangeEnd(nRangeEnd)
+{
+ mpRichText = pPostIt->GetTextObject();
+ if (!mpRichText)
+ msSimpleText = pPostIt->GetText();
+ msOwner = pPostIt->GetPar1();
+ m_sInitials = pPostIt->GetInitials();
+ maDateTime = DateTime(pPostIt->GetDate(), pPostIt->GetTime());
+}
+
+WW8_Annotation::WW8_Annotation(const SwRedlineData* pRedline)
+ :
+ mpRichText(nullptr),
+ msSimpleText(pRedline->GetComment()),
+ msOwner(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())),
+ maDateTime(pRedline->GetTimeStamp()),
+ m_nRangeStart(0),
+ m_nRangeEnd(0)
+{
+}
+
+bool WW8_Annotation::HasRange() const
+{
+ if (m_nRangeStart != m_nRangeEnd)
+ {
+ return true;
+ }
+
+ return !m_bIgnoreEmpty;
+}
+
+void WW8_WrPlcAnnotations::AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp,
+ bool bIgnoreEmpty)
+{
+ m_aRangeStartPositions[rName] = std::make_pair(nStartCp, bIgnoreEmpty);
+}
+
+void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwPostItField *pPostIt )
+{
+ m_aCps.push_back( nCp );
+ WW8_Annotation* p;
+ if( m_aRangeStartPositions.find(pPostIt->GetName()) != m_aRangeStartPositions.end() )
+ {
+ auto [nStartCp, bIgnoreEmpty] = m_aRangeStartPositions[pPostIt->GetName()];
+ p = new WW8_Annotation(pPostIt, nStartCp, nCp);
+ p->m_bIgnoreEmpty = bIgnoreEmpty;
+ m_aRangeStartPositions.erase(pPostIt->GetName());
+ }
+ else
+ {
+ p = new WW8_Annotation(pPostIt, nCp, nCp);
+ }
+ m_aContent.push_back( p );
+}
+
+void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwRedlineData *pRedline )
+{
+ maProcessedRedlines.insert(pRedline);
+ m_aCps.push_back( nCp );
+ WW8_Annotation* p = new WW8_Annotation(pRedline);
+ m_aContent.push_back( p );
+}
+
+bool WW8_WrPlcAnnotations::IsNewRedlineComment( const SwRedlineData *pRedline )
+{
+ return maProcessedRedlines.find(pRedline) == maProcessedRedlines.end();
+}
+
+WW8_WrPlcAnnotations::~WW8_WrPlcAnnotations()
+{
+ for(const void * p : m_aContent)
+ delete static_cast<WW8_Annotation const *>(p);
+}
+
+bool WW8_WrPlcSubDoc::WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp,
+ WW8_CP& rCount )
+{
+ sal_uInt16 nLen = m_aContent.size();
+ if ( !nLen )
+ return false;
+
+ sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ m_pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
+ sal_uInt16 i;
+
+ switch ( nTTyp )
+ {
+ case TXT_ATN:
+ for ( i = 0; i < nLen; i++ )
+ {
+ // beginning for PlcfAtnText
+ m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
+
+ rWrt.WritePostItBegin();
+ const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
+ if (rAtn.mpRichText)
+ rWrt.WriteOutliner(*rAtn.mpRichText, nTTyp);
+ else
+ {
+ OUString sText(rAtn.msSimpleText);
+ rWrt.WriteStringAsPara(sText.replace(0x0A, 0x0B));
+ }
+ }
+ break;
+
+ case TXT_TXTBOX:
+ case TXT_HFTXTBOX:
+ for ( i = 0; i < nLen; i++ )
+ {
+ // textbox content
+ WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ m_aCps.insert( m_aCps.begin()+i, nCP );
+ m_pTextPos->Append( nCP );
+
+ if( m_aContent[ i ] != nullptr )
+ {
+ // is it a writer or sdr - textbox?
+ const SdrObject& rObj = *static_cast<SdrObject const *>(m_aContent[ i ]);
+ if (rObj.GetObjInventor() == SdrInventor::FmForm)
+ {
+ sal_uInt8 nOldTyp = rWrt.m_nTextTyp;
+ rWrt.m_nTextTyp = nTTyp;
+ rWrt.GetOCXExp().ExportControl(rWrt, dynamic_cast<const SdrUnoObj&>(rObj));
+ rWrt.m_nTextTyp = nOldTyp;
+ }
+ else if( auto pText = DynCastSdrTextObj(&rObj) )
+ rWrt.WriteSdrTextObj(*pText, nTTyp);
+ else
+ {
+ const SwFrameFormat* pFormat = ::FindFrameFormat( &rObj );
+ OSL_ENSURE( pFormat, "where is the format?" );
+
+ const SwNodeIndex* pNdIdx = pFormat->GetContent().GetContentIdx();
+ OSL_ENSURE( pNdIdx, "where is the StartNode of the Textbox?" );
+ rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
+ pNdIdx->GetNode().EndOfSectionIndex(),
+ nTTyp );
+ {
+ SwNodeIndex aContentIdx = *pNdIdx;
+ ++aContentIdx;
+ if ( aContentIdx.GetNode().IsTableNode() )
+ {
+ bool bContainsOnlyTables = true;
+ do {
+ aContentIdx = *(aContentIdx.GetNode().EndOfSectionNode());
+ ++aContentIdx;
+ if ( !aContentIdx.GetNode().IsTableNode() &&
+ aContentIdx.GetIndex() != pNdIdx->GetNode().EndOfSectionIndex() )
+ {
+ bContainsOnlyTables = false;
+ }
+ } while ( aContentIdx.GetNode().IsTableNode() );
+ if ( bContainsOnlyTables )
+ {
+ // Additional paragraph containing a space to
+ // assure that by WW created RTF from written WW8
+ // does not crash WW.
+ rWrt.WriteStringAsPara( " " );
+ }
+ }
+ }
+ }
+ }
+ else if (i < m_aSpareFormats.size() && m_aSpareFormats[i])
+ {
+ const SwFrameFormat& rFormat = *m_aSpareFormats[i];
+ const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx();
+ rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
+ pNdIdx->GetNode().EndOfSectionIndex(), nTTyp );
+ }
+
+ // CR at end of one textbox text ( otherwise WW gpft :-( )
+ rWrt.WriteStringAsPara( OUString() );
+ }
+ break;
+
+ case TXT_EDN:
+ case TXT_FTN:
+ for ( i = 0; i < nLen; i++ )
+ {
+ // beginning for PlcfFootnoteText/PlcfEdnText
+ m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
+
+ // Note content
+ const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(m_aContent[ i ]);
+ rWrt.WriteFootnoteBegin( *pFootnote );
+ const SwNodeIndex* pIdx = pFootnote->GetTextFootnote()->GetStartNode();
+ OSL_ENSURE( pIdx, "Where is the start node of Foot-/Endnote?" );
+ rWrt.WriteSpecialText( pIdx->GetIndex() + 1,
+ pIdx->GetNode().EndOfSectionIndex(),
+ nTTyp );
+ }
+ break;
+
+ default:
+ OSL_ENSURE( false, "What kind of SubDocType is that?" );
+ }
+
+ m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
+ // CR to the end ( otherwise WW complains )
+ rWrt.WriteStringAsPara( OUString() );
+
+ WW8_CP nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
+ m_pTextPos->Append( nCpEnd );
+ rCount = nCpEnd - nCpStart;
+
+ return ( rCount != 0 );
+}
+
+static bool lcl_AuthorComp( const std::pair<OUString,OUString>& aFirst, const std::pair<OUString,OUString>& aSecond)
+{
+ return aFirst.first < aSecond.first;
+}
+
+static bool lcl_PosComp( const std::pair<WW8_CP, int>& aFirst, const std::pair<WW8_CP, int>& aSecond)
+{
+ return aFirst.first < aSecond.first;
+}
+
+void WW8_WrPlcSubDoc::WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp,
+ WW8_FC& rTextStart, sal_Int32& rTextCount, WW8_FC& rRefStart, sal_Int32& rRefCount ) const
+{
+
+ sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
+ sal_uInt16 nLen = m_aCps.size();
+ if ( !nLen )
+ return;
+
+ OSL_ENSURE( m_aCps.size() + 2 == m_pTextPos->Count(), "WritePlc: DeSync" );
+
+ std::vector<std::pair<OUString,OUString> > aStrArr;
+ WW8Fib& rFib = *rWrt.m_pFib; // n+1-th CP-Pos according to the manual
+ bool bWriteCP = true;
+
+ switch ( nTTyp )
+ {
+ case TXT_ATN:
+ {
+ std::vector< std::pair<WW8_CP, int> > aRangeStartPos; // The second of the pair is the original index before sorting.
+ std::vector< std::pair<WW8_CP, int> > aRangeEndPos; // Same, so we can map between the indexes before/after sorting.
+ std::map<int, int> aAtnStartMap; // Maps from annotation index to start index.
+ std::map<int, int> aStartAtnMap; // Maps from start index to annotation index.
+ std::map<int, int> aStartEndMap; // Maps from start index to end index.
+ // then write first the GrpXstAtnOwners
+ int nIdx = 0;
+ for ( sal_uInt16 i = 0; i < nLen; ++i )
+ {
+ const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
+ aStrArr.emplace_back(rAtn.msOwner,rAtn.m_sInitials);
+ // record start and end positions for ranges
+ if (rAtn.HasRange())
+ {
+ aRangeStartPos.emplace_back(rAtn.m_nRangeStart, nIdx);
+ aRangeEndPos.emplace_back(rAtn.m_nRangeEnd, nIdx);
+ ++nIdx;
+ }
+ }
+
+ //sort and remove duplicates
+ std::sort(aStrArr.begin(), aStrArr.end(),&lcl_AuthorComp);
+ auto aIter = std::unique(aStrArr.begin(), aStrArr.end());
+ aStrArr.erase(aIter, aStrArr.end());
+
+ // Also sort the start and end positions. We need to reference
+ // the start index in the annotation table and also need to
+ // reference the end index in the start table, so build a map
+ // that knows what index to reference, after sorting.
+ std::sort(aRangeStartPos.begin(), aRangeStartPos.end(), &lcl_PosComp);
+ for (decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i)
+ {
+ aAtnStartMap[aRangeStartPos[i].second] = i;
+ aStartAtnMap[i] = aRangeStartPos[i].second;
+ }
+ std::sort(aRangeEndPos.begin(), aRangeEndPos.end(), &lcl_PosComp);
+ for (decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i)
+ aStartEndMap[aAtnStartMap[ aRangeEndPos[i].second ]] = i;
+
+ for ( decltype(aStrArr)::size_type i = 0; i < aStrArr.size(); ++i )
+ {
+ const OUString& sAuthor = aStrArr[i].first;
+ SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, sAuthor.getLength());
+ SwWW8Writer::WriteString16(*rWrt.m_pTableStrm, sAuthor,
+ false);
+ }
+
+ rFib.m_fcGrpStAtnOwners = nFcStart;
+ nFcStart = rWrt.m_pTableStrm->Tell();
+ rFib.m_lcbGrpStAtnOwners = nFcStart - rFib.m_fcGrpStAtnOwners;
+
+ // Commented text ranges
+ if( !aRangeStartPos.empty() )
+ {
+ // Commented text ranges starting positions (Plcfbkf.aCP)
+ rFib.m_fcPlcfAtnbkf = nFcStart;
+ for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
+ {
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aRangeStartPos[i].first );
+ }
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, rFib.m_ccpText + 1);
+
+ // Commented text ranges additional information (Plcfbkf.aFBKF)
+ for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
+ {
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, aStartEndMap[i] ); // FBKF.ibkl
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 ); // FBKF.bkc
+ }
+
+ nFcStart = rWrt.m_pTableStrm->Tell();
+ rFib.m_lcbPlcfAtnbkf = nFcStart - rFib.m_fcPlcfAtnbkf;
+
+ // Commented text ranges ending positions (PlcfBkl.aCP)
+ rFib.m_fcPlcfAtnbkl = nFcStart;
+ for ( decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i )
+ {
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aRangeEndPos[i].first );
+ }
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, rFib.m_ccpText + 1);
+
+ nFcStart = rWrt.m_pTableStrm->Tell();
+ rFib.m_lcbPlcfAtnbkl = nFcStart - rFib.m_fcPlcfAtnbkl;
+
+ // Commented text ranges as bookmarks (SttbfAtnBkmk)
+ rFib.m_fcSttbfAtnbkmk = nFcStart;
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, sal_Int16(sal_uInt16(0xFFFF)) ); // SttbfAtnBkmk.fExtend
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, aRangeStartPos.size() ); // SttbfAtnBkmk.cData
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0xA ); // SttbfAtnBkmk.cbExtra
+
+ for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
+ {
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 ); // SttbfAtnBkmk.cchData
+ // One ATNBE structure for all text ranges
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0x0100 ); // ATNBE.bmc
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aStartAtnMap[i] ); // ATNBE.lTag
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 ); // ATNBE.lTagOld
+ }
+
+ nFcStart = rWrt.m_pTableStrm->Tell();
+ rFib.m_lcbSttbfAtnbkmk = nFcStart - rFib.m_fcSttbfAtnbkmk;
+ }
+
+ // Write the extended >= Word XP ATRD records
+ for( sal_uInt16 i = 0; i < nLen; ++i )
+ {
+ const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
+
+ sal_uInt32 nDTTM = sw::ms::DateTime2DTTM(rAtn.maDateTime);
+
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nDTTM );
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
+ }
+
+ rFib.m_fcAtrdExtra = nFcStart;
+ nFcStart = rWrt.m_pTableStrm->Tell();
+ rFib.m_lcbAtrdExtra = nFcStart - rFib.m_fcAtrdExtra;
+ rFib.m_fcHplxsdr = 0x01010002; //WTF, but apparently necessary
+ rFib.m_lcbHplxsdr = 0;
+ }
+ break;
+ case TXT_TXTBOX:
+ case TXT_HFTXTBOX:
+ {
+ m_pTextPos->Write( *rWrt.m_pTableStrm );
+ const std::vector<sal_uInt32>* pShapeIds = GetShapeIdArr();
+ OSL_ENSURE( pShapeIds, "Where are the ShapeIds?" );
+
+ for ( sal_uInt16 i = 0; i < nLen; ++i )
+ {
+ // write textbox story - FTXBXS
+ // is it a writer or sdr - textbox?
+ const SdrObject* pObj = static_cast<SdrObject const *>(m_aContent[ i ]);
+ sal_Int32 nCnt = 1;
+ if (DynCastSdrTextObj( pObj ))
+ {
+ // find the "highest" SdrObject of this
+ const SwFrameFormat& rFormat = *::FindFrameFormat( pObj );
+
+ const SwFormatChain* pChn = &rFormat.GetChain();
+ while ( pChn->GetNext() )
+ {
+ // has a chain?
+ // then calc the cur pos in the chain
+ ++nCnt;
+ pChn = &pChn->GetNext()->GetChain();
+ }
+ }
+ if( nullptr == pObj )
+ {
+ if (i < m_aSpareFormats.size() && m_aSpareFormats[i])
+ {
+ const SwFrameFormat& rFormat = *m_aSpareFormats[i];
+
+ const SwFormatChain* pChn = &rFormat.GetChain();
+ while( pChn->GetNext() )
+ {
+ // has a chain?
+ // then calc the cur pos in the chain
+ ++nCnt;
+ pChn = &pChn->GetNext()->GetChain();
+ }
+ }
+ }
+ // long cTxbx / iNextReuse
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nCnt );
+ // long cReusable
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
+ // short fReusable
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
+ // long reserved
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 );
+ // long lid
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm,
+ (*pShapeIds)[i]);
+ // long txidUndo
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
+ }
+ SwWW8Writer::FillCount( *rWrt.m_pTableStrm, 22 );
+ bWriteCP = false;
+ }
+ break;
+ }
+
+ if ( bWriteCP )
+ {
+ // write CP Positions
+ for ( sal_uInt16 i = 0; i < nLen; i++ )
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, m_aCps[ i ] );
+
+ // n+1-th CP-Pos according to the manual
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm,
+ rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpEdn +
+ rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1 );
+
+ if ( TXT_ATN == nTTyp )
+ {
+ sal_uInt16 nlTag = 0;
+ for ( sal_uInt16 i = 0; i < nLen; ++i )
+ {
+ const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
+
+ //aStrArr is sorted
+ auto aIter = std::lower_bound(aStrArr.begin(),
+ aStrArr.end(), std::pair<OUString,OUString>(rAtn.msOwner,OUString()),
+ &lcl_AuthorComp);
+ OSL_ENSURE(aIter != aStrArr.end() && aIter->first == rAtn.msOwner,
+ "Impossible");
+ sal_uInt16 nFndPos = static_cast< sal_uInt16 >(aIter - aStrArr.begin());
+ OUString sInitials( aIter->second );
+ sal_uInt8 nInitialsLen = static_cast<sal_uInt8>(sInitials.getLength());
+ if ( nInitialsLen > 9 )
+ {
+ sInitials = sInitials.copy( 0, 9 );
+ nInitialsLen = 9;
+ }
+
+ // xstUsrInitl[ 10 ] pascal-style String holding initials
+ // of annotation author
+ SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, nInitialsLen);
+ SwWW8Writer::WriteString16(*rWrt.m_pTableStrm, sInitials,
+ false);
+ SwWW8Writer::FillCount( *rWrt.m_pTableStrm,
+ (9 - nInitialsLen) * 2 );
+
+ // documents layout of WriteShort's below:
+
+ // SVBT16 ibst; // index into GrpXstAtnOwners
+ // SVBT16 ak; // not used
+ // SVBT16 grfbmc; // not used
+ // SVBT32 ITagBkmk; // when not -1, this tag identifies the ATNBE
+
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, nFndPos );
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
+ if (rAtn.HasRange())
+ {
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nlTag );
+ ++nlTag;
+ }
+ else
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 );
+ }
+ }
+ else
+ {
+ sal_uInt16 nNo = 0;
+ for ( sal_uInt16 i = 0; i < nLen; ++i ) // write Flags
+ {
+ const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(m_aContent[ i ]);
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm,
+ !pFootnote->GetNumStr().isEmpty() ? 0 : ++nNo );
+ }
+ }
+ }
+ rRefStart = nFcStart;
+ nFcStart = rWrt.m_pTableStrm->Tell();
+ rRefCount = nFcStart - rRefStart;
+
+ m_pTextPos->Write( *rWrt.m_pTableStrm );
+
+ switch ( nTTyp )
+ {
+ case TXT_TXTBOX:
+ case TXT_HFTXTBOX:
+ for ( sal_uInt16 i = 0; i < nLen; ++i )
+ {
+ // write break descriptor (BKD)
+ // short itxbxs
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, i );
+ // short dcpDepend
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
+ // short flags : icol/fTableBreak/fColumnBreak/fMarked/
+ // fUnk/fTextOverflow
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0x800 );
+ }
+ SwWW8Writer::FillCount( *rWrt.m_pTableStrm, 6 );
+ break;
+ }
+
+ rTextStart = nFcStart;
+ rTextCount = rWrt.m_pTableStrm->Tell() - nFcStart;
+}
+
+const std::vector<sal_uInt32>* WW8_WrPlcSubDoc::GetShapeIdArr() const
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx
new file mode 100644
index 0000000000..38fbfb2166
--- /dev/null
+++ b/sw/source/filter/ww8/wrtww8.cxx
@@ -0,0 +1,4626 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <iostream>
+
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/packages/XPackageEncryption.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <algorithm>
+#include <map>
+#include <hintids.hxx>
+#include <string.h>
+#include <o3tl/safeint.hxx>
+#include <osl/endian.h>
+#include <sal/log.hxx>
+#include <docsh.hxx>
+#include <drawdoc.hxx>
+
+#include <unotools/fltrcfg.hxx>
+#include <sot/storage.hxx>
+#include <sfx2/docinf.hxx>
+#include <editeng/tstpitem.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <filter/msfilter/classids.hxx>
+#include <filter/msfilter/msoleexp.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <swtypes.hxx>
+#include <swrect.hxx>
+#include <swtblfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtrowsplt.hxx>
+#include <frmatr.hxx>
+#include <../../core/inc/rootfrm.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentExternalData.hxx>
+#include <viewopt.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <docstat.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <IMark.hxx>
+#include <swtable.hxx>
+#include "wrtww8.hxx"
+#include "ww8par.hxx"
+#include <swmodule.hxx>
+#include <section.hxx>
+#include <fmtinfmt.hxx>
+#include <txtinet.hxx>
+#include <fmturl.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <mdiexp.hxx>
+#include <strings.hrc>
+#include <fmtline.hxx>
+#include <fmtfsize.hxx>
+#include <formatflysplit.hxx>
+#include "sprmids.hxx"
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/processfactory.hxx>
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "ww8attributeoutput.hxx"
+#include <xmloff/odffields.hxx>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <dbgoutsw.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/tempfile.hxx>
+#include <filter/msfilter/mscodec.hxx>
+#include <filter/msfilter/svxmsbas.hxx>
+#include <rtl/random.h>
+#include <vcl/svapp.hxx>
+#include <sfx2/docfilt.hxx>
+#include "WW8Sttbf.hxx"
+#include <editeng/charrotateitem.hxx>
+#include <svx/swframetypes.hxx>
+#include "WW8FibData.hxx"
+#include <numrule.hxx>
+#include <fmtclds.hxx>
+#include <rdfhelper.hxx>
+#include <fmtclbl.hxx>
+#include <iodetect.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+
+using namespace css;
+using namespace sw::util;
+using namespace sw::types;
+
+/** FKP - Formatted disK Page
+*/
+class WW8_WrFkp
+{
+ sal_uInt8* m_pFkp; // Fkp total ( first and only FCs and Sprms )
+ sal_uInt8* m_pOfs; // pointer to the offset area, later copied to pFkp
+ ePLCFT m_ePlc;
+ short m_nStartGrp; // from here on grpprls
+ short m_nOldStartGrp;
+ sal_uInt8 m_nItemSize;
+ sal_uInt8 m_nIMax; // number of entry pairs
+ sal_uInt8 m_nOldVarLen;
+ bool m_bCombined; // true : paste not allowed
+
+ sal_uInt8 SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms );
+
+ WW8_WrFkp(const WW8_WrFkp&) = delete;
+ WW8_WrFkp& operator=(const WW8_WrFkp&) = delete;
+
+public:
+ WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc);
+ ~WW8_WrFkp();
+ bool Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms );
+ void Combine();
+ void Write( SvStream& rStrm, SwWW8WrGrf& rGrf );
+
+ bool IsEqualPos(WW8_FC nEndFc) const
+ { return !m_bCombined && m_nIMax && nEndFc == reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax]; }
+ void MergeToNew( short& rVarLen, sal_uInt8 *& pNewSprms );
+ bool IsEmptySprm() const
+ { return !m_bCombined && m_nIMax && !m_nOldVarLen; }
+ void SetNewEnd( WW8_FC nEnd )
+ { reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax] = nEnd; }
+
+ WW8_FC GetStartFc() const;
+ WW8_FC GetEndFc() const;
+
+ sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen);
+};
+
+// class WW8_WrPc collects all piece entries for one piece
+class WW8_WrPc
+{
+ WW8_CP m_nStartCp; // Starting character position of the text
+ WW8_FC m_nStartFc; // Starting file position of the text
+ sal_uInt16 m_nStatus; // End of paragraph inside the piece?
+
+public:
+ WW8_WrPc(WW8_FC nSFc, WW8_CP nSCp )
+ : m_nStartCp( nSCp ), m_nStartFc( nSFc ), m_nStatus( 0x0040 )
+ {}
+
+ void SetStatus() { m_nStatus = 0x0050; }
+ sal_uInt16 GetStatus() const { return m_nStatus; }
+ WW8_CP GetStartCp() const { return m_nStartCp; }
+ WW8_FC GetStartFc() const { return m_nStartFc; }
+};
+
+typedef std::map<OUString,tools::Long> BKMKNames;
+typedef std::pair<bool,OUString> BKMK;
+typedef std::pair<tools::Long,BKMK> BKMKCP;
+typedef std::multimap<tools::Long,BKMKCP*> BKMKCPs;
+typedef BKMKCPs::iterator CPItr;
+
+class WW8_WrtBookmarks
+{
+private:
+ /// Structure of one item inside this map: (startPos, (endPos, (a bool value?, bookmarkName)))
+ BKMKCPs maSttCps;
+ BKMKNames maSwBkmkNms;
+
+ WW8_WrtBookmarks(WW8_WrtBookmarks const&) = delete;
+ WW8_WrtBookmarks& operator=(WW8_WrtBookmarks const&) = delete;
+
+public:
+ WW8_WrtBookmarks();
+ ~WW8_WrtBookmarks();
+ //! Add a new bookmark to the list OR add an end position to an existing bookmark.
+ void Append( WW8_CP nStartCp, const OUString& rNm );
+ //! Write out bookmarks to file.
+ void Write( WW8Export& rWrt );
+ //! Move existing field marks from one position to another.
+ void MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo);
+};
+
+WW8_WrtBookmarks::WW8_WrtBookmarks()
+{}
+
+WW8_WrtBookmarks::~WW8_WrtBookmarks()
+{
+ for (auto& rEntry : maSttCps)
+ {
+ if (rEntry.second)
+ {
+ delete rEntry.second;
+ rEntry.second = nullptr;
+ }
+ }
+}
+
+void WW8_WrtBookmarks::Append( WW8_CP nStartCp, const OUString& rNm)
+{
+ std::pair<BKMKNames::iterator, bool> aResult = maSwBkmkNms.insert(std::pair<OUString,tools::Long>(rNm,0L));
+ if (aResult.second)
+ {
+ BKMK aBK(false,rNm);
+ BKMKCP* pBKCP = new BKMKCP(static_cast<tools::Long>(nStartCp),aBK);
+ maSttCps.insert(std::pair<tools::Long,BKMKCP*>(nStartCp,pBKCP));
+ aResult.first->second = static_cast<tools::Long>(nStartCp);
+ }
+ else
+ {
+ std::pair<CPItr,CPItr> aRange = maSttCps.equal_range(aResult.first->second);
+ for (CPItr aItr = aRange.first;aItr != aRange.second;++aItr)
+ {
+ if (aItr->second && aItr->second->second.second == rNm)
+ {
+ if (aItr->second->second.first)
+ nStartCp--;
+ aItr->second->first = static_cast<tools::Long>(nStartCp);
+ break;
+ }
+ }
+ }
+}
+
+void WW8_WrtBookmarks::Write( WW8Export& rWrt)
+{
+ if (maSttCps.empty())
+ return;
+ tools::Long n;
+ std::vector<OUString> aNames;
+ SvMemoryStream aTempStrm1(65535,65535);
+ SvMemoryStream aTempStrm2(65535,65535);
+
+ BKMKCPs aEndCps;
+ for (const auto& rEntry : maSttCps)
+ {
+ if (rEntry.second)
+ {
+ aEndCps.insert(std::pair<tools::Long,BKMKCP*>(rEntry.second->first, rEntry.second));
+ aNames.push_back(rEntry.second->second.second);
+ SwWW8Writer::WriteLong(aTempStrm1, rEntry.first);
+ }
+ }
+
+ aTempStrm1.Seek(0);
+ n = 0;
+ for (const auto& rEntry : aEndCps)
+ {
+ if (rEntry.second)
+ {
+ rEntry.second->first = n;
+ SwWW8Writer::WriteLong( aTempStrm2, rEntry.first);
+ }
+ ++n;
+ }
+
+ aTempStrm2.Seek(0);
+ rWrt.WriteAsStringTable(aNames, rWrt.m_pFib->m_fcSttbfbkmk,rWrt.m_pFib->m_lcbSttbfbkmk);
+ SvStream& rStrm = *rWrt.m_pTableStrm;
+ rWrt.m_pFib->m_fcPlcfbkf = rStrm.Tell();
+ rStrm.WriteStream( aTempStrm1 );
+ SwWW8Writer::WriteLong(rStrm, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpTxbx);
+ for (const auto& rEntry : maSttCps)
+ {
+ if (rEntry.second)
+ {
+ SwWW8Writer::WriteLong(rStrm, rEntry.second->first);
+ }
+ }
+ rWrt.m_pFib->m_lcbPlcfbkf = rStrm.Tell() - rWrt.m_pFib->m_fcPlcfbkf;
+ rWrt.m_pFib->m_fcPlcfbkl = rStrm.Tell();
+ rStrm.WriteStream( aTempStrm2 );
+ SwWW8Writer::WriteLong(rStrm, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpTxbx);
+ rWrt.m_pFib->m_lcbPlcfbkl = rStrm.Tell() - rWrt.m_pFib->m_fcPlcfbkl;
+}
+
+void WW8_WrtBookmarks::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
+{
+ std::pair<CPItr,CPItr> aRange = maSttCps.equal_range(nFrom);
+ CPItr aItr = aRange.first;
+ while (aItr != aRange.second)
+ {
+ if (aItr->second)
+ {
+ if (aItr->second->first == static_cast<tools::Long>(nFrom))
+ {
+ aItr->second->second.first = true;
+ aItr->second->first = nTo;
+ }
+ maSttCps.insert(std::pair<tools::Long,BKMKCP*>(nTo,aItr->second));
+ aItr->second = nullptr;
+ aRange = maSttCps.equal_range(nFrom);
+ aItr = aRange.first;
+ continue;
+ }
+ ++aItr;
+ }
+}
+
+/// Handles export of smart tags.
+class WW8_WrtFactoids
+{
+ std::vector<WW8_CP> m_aStartCPs;
+ std::vector<WW8_CP> m_aEndCPs;
+ std::vector< std::map<OUString, OUString> > m_aStatements;
+
+ WW8_WrtFactoids(WW8_WrtFactoids const&) = delete;
+ WW8_WrtFactoids& operator=(WW8_WrtFactoids const&) = delete;
+
+public:
+ WW8_WrtFactoids();
+ void Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements);
+ void Write(WW8Export& rWrt);
+};
+
+WW8_WrtFactoids::WW8_WrtFactoids()
+{
+}
+
+void WW8_WrtFactoids::Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements)
+{
+ m_aStartCPs.push_back(nStartCp);
+ m_aEndCPs.push_back(nEndCp);
+ m_aStatements.push_back(rStatements);
+}
+
+void WW8_WrtFactoids::Write(WW8Export& rExport)
+{
+ if (m_aStartCPs.empty())
+ return;
+
+ // Smart tags are otherwise removed by Word on saving.
+ rExport.m_pDop->fEmbedFactoids = true;
+
+ SvStream& rStream = *rExport.m_pTableStrm;
+
+ rExport.m_pFib->m_fcSttbfBkmkFactoid = rStream.Tell();
+ // Write SttbfBkmkFactoid.
+ rStream.WriteUInt16(0xffff); // fExtend
+ rStream.WriteUInt16(m_aStartCPs.size()); // cData
+ rStream.WriteUInt16(0); // cbExtra
+
+ for (size_t i = 0; i < m_aStartCPs.size(); ++i)
+ {
+ rStream.WriteUInt16(6); // cchData
+ // Write FACTOIDINFO.
+ rStream.WriteUInt32(i); // dwId
+ rStream.WriteUInt16(0); // fSubEntry
+ rStream.WriteUInt16(0); // fto
+ rStream.WriteUInt32(0); // pfpb
+ }
+ rExport.m_pFib->m_lcbSttbfBkmkFactoid = rStream.Tell() - rExport.m_pFib->m_fcSttbfBkmkFactoid;
+
+ rExport.m_pFib->m_fcPlcfBkfFactoid = rStream.Tell();
+ for (const WW8_CP& rCP : m_aStartCPs)
+ rStream.WriteInt32(rCP);
+ rStream.WriteInt32(rExport.m_pFib->m_ccpText + rExport.m_pFib->m_ccpTxbx);
+
+ // Write FBKFD.
+ for (size_t i = 0; i < m_aStartCPs.size(); ++i)
+ {
+ rStream.WriteInt16(i); // ibkl
+ rStream.WriteInt16(0); // bkc
+ rStream.WriteInt16(1); // cDepth, 1 as start and end is the same.
+ }
+
+ rExport.m_pFib->m_lcbPlcfBkfFactoid = rStream.Tell() - rExport.m_pFib->m_fcPlcfBkfFactoid;
+
+ rExport.m_pFib->m_fcPlcfBklFactoid = rStream.Tell();
+ for (const WW8_CP& rCP : m_aEndCPs)
+ rStream.WriteInt32(rCP);
+ rStream.WriteInt32(rExport.m_pFib->m_ccpText + rExport.m_pFib->m_ccpTxbx);
+
+ // Write FBKLD.
+ for (size_t i = 0; i < m_aEndCPs.size(); ++i)
+ {
+ rStream.WriteInt16(i); // ibkf
+ rStream.WriteInt16(0); // cDepth, 0 as does not overlap with any other smart tag.
+ }
+ rExport.m_pFib->m_lcbPlcfBklFactoid = rStream.Tell() - rExport.m_pFib->m_fcPlcfBklFactoid;
+
+ rExport.m_pFib->m_fcFactoidData = rStream.Tell();
+ // Write SmartTagData.
+ MSOFactoidType aFactoidType;
+ aFactoidType.m_nId = 1;
+ aFactoidType.m_aUri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+ aFactoidType.m_aTag = "RDF";
+ WW8SmartTagData aSmartTagData;
+ aSmartTagData.m_aPropBagStore.m_aFactoidTypes.push_back(aFactoidType);
+
+ std::set<OUString> aSet;
+ for (const std::map<OUString, OUString>& rStatements : m_aStatements)
+ {
+ // Statements for a single text node.
+ for (const auto& rPair : rStatements)
+ {
+ aSet.insert(rPair.first);
+ aSet.insert(rPair.second);
+ }
+ }
+ aSmartTagData.m_aPropBagStore.m_aStringTable.assign(aSet.begin(), aSet.end());
+ for (const std::map<OUString, OUString>& rStatements : m_aStatements)
+ {
+ MSOPropertyBag aPropertyBag;
+ aPropertyBag.m_nId = 1;
+ for (const auto& rPair : rStatements)
+ {
+ MSOProperty aProperty;
+ aProperty.m_nKey = std::distance(aSet.begin(), aSet.find(rPair.first));
+ aProperty.m_nValue = std::distance(aSet.begin(), aSet.find(rPair.second));
+ aPropertyBag.m_aProperties.push_back(aProperty);
+ }
+ aSmartTagData.m_aPropBags.push_back(aPropertyBag);
+ }
+
+ aSmartTagData.Write(rExport);
+ rExport.m_pFib->m_lcbFactoidData = rStream.Tell() - rExport.m_pFib->m_fcFactoidData;
+}
+
+#define DEFAULT_STYLES_COUNT 16
+
+// Names of the storage streams
+constexpr OUStringLiteral sMainStream = u"WordDocument";
+constexpr OUStringLiteral sCompObj = u"\1CompObj";
+
+static void WriteDop( WW8Export& rWrt )
+{
+ WW8Dop& rDop = *rWrt.m_pDop;
+
+ // i#78951#, store the value of unknown compatibility options
+ rDop.SetCompatibilityOptions( rWrt.m_rDoc.getIDocumentSettingAccess().Getn32DummyCompatibilityOptions1());
+ rDop.SetCompatibilityOptions2( rWrt.m_rDoc.getIDocumentSettingAccess().Getn32DummyCompatibilityOptions2());
+
+ rDop.fNoLeading = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING);
+ rDop.fUsePrinterMetrics = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE);
+
+ // write default TabStop
+ const SvxTabStopItem& rTabStop =
+ rWrt.m_rDoc.GetAttrPool().GetDefaultItem(RES_PARATR_TABSTOP);
+ rDop.dxaTab = o3tl::narrowing<sal_uInt16>(rTabStop[0].GetTabPos());
+
+ // Zoom factor and type
+ SwViewShell *pViewShell(rWrt.m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ if (pViewShell)
+ {
+ switch ( pViewShell->GetViewOptions()->GetZoomType() )
+ {
+ case SvxZoomType::WHOLEPAGE: rDop.zkSaved = 1; break;
+ case SvxZoomType::PAGEWIDTH: rDop.zkSaved = 2; break;
+ case SvxZoomType::OPTIMAL: rDop.zkSaved = 3; break;
+ default: rDop.zkSaved = 0;
+ rDop.wScaleSaved = pViewShell->GetViewOptions()->GetZoom();
+ break;
+ }
+ }
+
+ // Values from the DocumentStatistics (are definitely needed
+ // for the DocStat fields)
+ rDop.fWCFootnoteEdn = true; // because they are included in StarWriter
+
+ const SwDocStat& rDStat = rWrt.m_rDoc.getIDocumentStatistics().GetDocStat();
+ rDop.cWords = rDStat.nWord;
+ rDop.cCh = rDStat.nChar;
+ rDop.cPg = static_cast< sal_Int16 >(rDStat.nPage);
+ rDop.cParas = rDStat.nPara;
+ rDop.cLines = rDStat.nPara;
+
+ SwDocShell *pDocShell(rWrt.m_rDoc.GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ uno::Reference<beans::XPropertySet> xProps;
+ if ( pDocShell )
+ {
+ uno::Reference<lang::XComponent> xModelComp = pDocShell->GetModel();
+ xProps.set(xModelComp, uno::UNO_QUERY);
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(xModelComp, uno::UNO_QUERY_THROW);
+ xDocProps = xDPS->getDocumentProperties();
+ OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
+
+ rDop.lKeyProtDoc = pDocShell->GetModifyPasswordHash();
+ }
+
+ if ((rWrt.m_pSepx && rWrt.m_pSepx->DocumentIsProtected()) ||
+ rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM ) ||
+ rDop.lKeyProtDoc != 0)
+ {
+ rDop.fProtEnabled = true;
+ // The password was ignored at import if forms protection was enabled,
+ // so round-trip it since protection is still enabled.
+ if ( rDop.lKeyProtDoc == 0 && xProps.is() )
+ {
+ comphelper::SequenceAsHashMap aPropMap( xProps->getPropertyValue("InteropGrabBag"));
+ aPropMap.getValue("FormPasswordHash") >>= rDop.lKeyProtDoc;
+ }
+ }
+ else
+ {
+ rDop.fProtEnabled = false;
+ }
+
+ if (rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
+ {
+ rDop.iGutterPos = true;
+ }
+
+ if (!xDocProps.is())
+ {
+ rDop.dttmCreated = rDop.dttmRevised = rDop.dttmLastPrint = 0x45FBAC69;
+ }
+ else
+ {
+ ::util::DateTime uDT = xDocProps->getCreationDate();
+ rDop.dttmCreated = sw::ms::DateTime2DTTM(DateTime(uDT));
+ uDT = xDocProps->getModificationDate();
+ rDop.dttmRevised = sw::ms::DateTime2DTTM(DateTime(uDT));
+ uDT = xDocProps->getPrintDate();
+ rDop.dttmLastPrint = sw::ms::DateTime2DTTM(DateTime(uDT));
+ }
+
+ // Also, the DocStat fields in headers, footers are not calculated correctly.
+ // ( we do not have this fields! )
+
+ // and also for the Headers and Footers
+ rDop.cWordsFootnoteEnd = rDStat.nWord;
+ rDop.cChFootnoteEdn = rDStat.nChar;
+ rDop.cPgFootnoteEdn = static_cast<sal_Int16>(rDStat.nPage);
+ rDop.cParasFootnoteEdn = rDStat.nPara;
+ rDop.cLinesFootnoteEdn = rDStat.nPara;
+
+ rDop.fDontUseHTMLAutoSpacing = rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX);
+
+ rDop.fExpShRtn = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); // #i56856#
+
+ IDocumentSettingAccess& rIDSA = rWrt.m_rDoc.getIDocumentSettingAccess();
+ rDop.fDontBreakWrappedTables = rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES);
+
+ rDop.Write( *rWrt.m_pTableStrm, *rWrt.m_pFib );
+}
+
+static int lcl_CmpBeginEndChars( const OUString& rSWStr,
+ const sal_Unicode* pMSStr, int nMSStrByteLen )
+{
+ nMSStrByteLen /= sizeof( sal_Unicode );
+ if( nMSStrByteLen > rSWStr.getLength() )
+ nMSStrByteLen = rSWStr.getLength()+1;
+ nMSStrByteLen *= sizeof( sal_Unicode );
+
+ return memcmp( rSWStr.getStr(), pMSStr, nMSStrByteLen );
+}
+
+/*
+Converts the OOo Asian Typography into a best fit match for Microsoft
+Asian typography. This structure is actually dumped to disk within the
+Dop Writer. Assumption is that rTypo is cleared to 0 on entry
+*/
+void WW8Export::ExportDopTypography(WW8DopTypography &rTypo)
+{
+ static const sal_Unicode aLangNotBegin[4][WW8DopTypography::nMaxFollowing]=
+ {
+ //Japanese Level 1
+ {
+ 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
+ 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2030, 0x2032,
+ 0x2033, 0x2103, 0x3001, 0x3002, 0x3005, 0x3009, 0x300b, 0x300d,
+ 0x300f, 0x3011, 0x3015, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
+ 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x309b, 0x309c, 0x309d,
+ 0x309e, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3,
+ 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fc, 0x30fd,
+ 0x30fe, 0xff01, 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b,
+ 0xff1f, 0xff3d, 0xff5d, 0xff61, 0xff63, 0xff64, 0xff65, 0xff67,
+ 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
+ 0xff70, 0xff9e, 0xff9f, 0xffe0
+ },
+ //Simplified Chinese
+ {
+ 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
+ 0x007d, 0x00a8, 0x00b7, 0x02c7, 0x02c9, 0x2015, 0x2016, 0x2019,
+ 0x201d, 0x2026, 0x2236, 0x3001, 0x3002, 0x3003, 0x3005, 0x3009,
+ 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0xff01, 0xff02,
+ 0xff07, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
+ 0xff40, 0xff5c, 0xff5d, 0xff5e, 0xffe0
+ },
+ //Korean
+ {
+ 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
+ 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2032, 0x2033,
+ 0x2103, 0x3009, 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0xff01,
+ 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
+ 0xff5d, 0xffe0
+ },
+ //Traditional Chinese
+ {
+ 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
+ 0x007d, 0x00a2, 0x00b7, 0x2013, 0x2014, 0x2019, 0x201d, 0x2022,
+ 0x2025, 0x2026, 0x2027, 0x2032, 0x2574, 0x3001, 0x3002, 0x3009,
+ 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x301e, 0xfe30, 0xfe31,
+ 0xfe33, 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40,
+ 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, 0xfe52, 0xfe54, 0xfe55,
+ 0xfe56, 0xfe57, 0xfe5a, 0xfe5c, 0xfe5e, 0xff01, 0xff09, 0xff0c,
+ 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff5c, 0xff5d, 0xff64
+ },
+ };
+
+ static const sal_Unicode aLangNotEnd[4][WW8DopTypography::nMaxLeading] =
+ {
+ //Japanese Level 1
+ {
+ 0x0024, 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018,
+ 0x201c, 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04,
+ 0xff08, 0xff3b, 0xff5b, 0xff62, 0xffe1, 0xffe5
+ },
+ //Simplified Chinese
+ {
+ 0x0028, 0x005b, 0x007b, 0x00b7, 0x2018, 0x201c, 0x3008, 0x300a,
+ 0x300c, 0x300e, 0x3010, 0x3014, 0x3016, 0xff08, 0xff0e, 0xff3b,
+ 0xff5b, 0xffe1, 0xffe5
+ },
+ //Korean
+ {
+ 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c,
+ 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, 0xff08,
+ 0xff3b, 0xff5b, 0xffe6
+ },
+ //Traditional Chinese
+ {
+ 0x0028, 0x005b, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c, 0x2035,
+ 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0x301d, 0xfe35,
+ 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59,
+ 0xfe5b, 0xfe5d, 0xff08, 0xff5b
+ },
+ };
+
+ const i18n::ForbiddenCharacters *pForbidden = nullptr;
+ const i18n::ForbiddenCharacters *pUseMe = nullptr;
+ sal_uInt8 nUseReserved=0;
+ int nNoNeeded=0;
+ /*
+ Now we have some minor difficult issues, to wit...
+ a. MicroSoft Office can only store one set of begin and end characters in
+ a given document, not one per language.
+ b. StarOffice has only a concept of one set of begin and end characters for
+ a given language, i.e. not the two levels of kinsoku in japanese
+
+ What is unknown as yet is if our default begin and end chars for
+ japanese, chinese tradition, chinese simplified and korean are different
+ in Word and Writer. I already suspect that they are different between
+ different version of word itself.
+
+ So what have come up with is to simply see if any of the four languages
+ in OOo have been changed away from OUR defaults, and if one has then
+ export that. If more than one has in the future we may hack in something
+ which examines our document properties to see which language is used the
+ most and choose that, for now we choose the first and throw an ASSERT
+ */
+
+ /*Our default Japanese Level is 2, this is a special MS hack to set this*/
+ rTypo.m_reserved2 = 1;
+
+ for (rTypo.m_reserved1=8;rTypo.m_reserved1>0;rTypo.m_reserved1-=2)
+ {
+ pForbidden = m_rDoc.getIDocumentSettingAccess().getForbiddenCharacters(rTypo.GetConvertedLang(),
+ false);
+ if (nullptr != pForbidden)
+ {
+ int nIdx = (rTypo.m_reserved1-2)/2;
+ if( lcl_CmpBeginEndChars( pForbidden->endLine,
+ aLangNotEnd[ nIdx ], sizeof(aLangNotEnd[ nIdx ]) ) ||
+ lcl_CmpBeginEndChars( pForbidden->beginLine,
+ aLangNotBegin[ nIdx ], sizeof(aLangNotBegin[ nIdx ]) ) )
+ {
+ //One exception for Japanese, if it matches a level 1 we
+ //can use one extra flag for that, rather than use a custom
+ if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
+ {
+ if (
+ !lcl_CmpBeginEndChars
+ (
+ pForbidden->endLine,
+ WW8DopTypography::JapanNotEndLevel1.getStr(),
+ WW8DopTypography::nMaxLeading * sizeof(sal_Unicode)
+ )
+ &&
+ !lcl_CmpBeginEndChars
+ (
+ pForbidden->beginLine,
+ WW8DopTypography::JapanNotBeginLevel1.getStr(),
+ WW8DopTypography::nMaxFollowing * sizeof(sal_Unicode)
+ )
+ )
+ {
+ rTypo.m_reserved2 = 0;
+ continue;
+ }
+ }
+
+ if (!pUseMe)
+ {
+ pUseMe = pForbidden;
+ nUseReserved = rTypo.m_reserved1;
+ rTypo.m_iLevelOfKinsoku = 2;
+ }
+ nNoNeeded++;
+ }
+ }
+ }
+
+ OSL_ENSURE( nNoNeeded<=1, "Example of unexportable forbidden chars" );
+ rTypo.m_reserved1=nUseReserved;
+ if (rTypo.m_iLevelOfKinsoku && pUseMe)
+ {
+ rTypo.m_cchFollowingPunct = msword_cast<sal_Int16>
+ (pUseMe->beginLine.getLength());
+ if (rTypo.m_cchFollowingPunct > WW8DopTypography::nMaxFollowing - 1)
+ rTypo.m_cchFollowingPunct = WW8DopTypography::nMaxFollowing - 1;
+
+ rTypo.m_cchLeadingPunct = msword_cast<sal_Int16>
+ (pUseMe->endLine.getLength());
+ if (rTypo.m_cchLeadingPunct > WW8DopTypography::nMaxLeading - 1)
+ rTypo.m_cchLeadingPunct = WW8DopTypography::nMaxLeading -1;
+
+ memcpy(rTypo.m_rgxchFPunct,pUseMe->beginLine.getStr(),
+ (rTypo.m_cchFollowingPunct+1)*2);
+
+ memcpy(rTypo.m_rgxchLPunct,pUseMe->endLine.getStr(),
+ (rTypo.m_cchLeadingPunct+1)*2);
+ }
+
+ const IDocumentSettingAccess& rIDocumentSettingAccess = GetWriter().getIDocumentSettingAccess();
+
+ rTypo.m_fKerningPunct = sal_uInt16(rIDocumentSettingAccess.get(DocumentSettingId::KERN_ASIAN_PUNCTUATION));
+ rTypo.m_iJustification = sal_uInt16(m_rDoc.getIDocumentSettingAccess().getCharacterCompressionType());
+}
+
+// It can only be found something with this method, if it is used within
+// WW8_SwAttrIter::OutAttr() and WW8Export::OutputItemSet()
+const SfxPoolItem* MSWordExportBase::HasItem( sal_uInt16 nWhich ) const
+{
+ const SfxPoolItem* pItem=nullptr;
+ if (m_pISet)
+ {
+ // if write an EditEngine text, then the WhichIds are greater than
+ // our own Ids. So the Id have to translate from our into the
+ // EditEngine Range
+ nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
+ if (nWhich && SfxItemState::SET != m_pISet->GetItemState(nWhich, true, &pItem))
+ pItem = nullptr;
+ }
+ else if( m_pChpIter )
+ pItem = m_pChpIter->HasTextItem( nWhich );
+ else
+ {
+ OSL_ENSURE( false, "Where is my ItemSet / pChpIter ?" );
+ pItem = nullptr;
+ }
+ return pItem;
+}
+
+const SfxPoolItem& MSWordExportBase::GetItem(sal_uInt16 nWhich) const
+{
+ assert((m_pISet || m_pChpIter) && "Where is my ItemSet / pChpIter ?");
+ if (m_pISet)
+ {
+ // if write an EditEngine text, then the WhichIds are greater than
+ // our own Ids. So the Id have to translate from our into the
+ // EditEngine Range
+ nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
+ OSL_ENSURE(nWhich != 0, "All broken, Impossible");
+ return m_pISet->Get(nWhich);
+ }
+ return m_pChpIter->GetItem( nWhich );
+}
+
+WW8_WrPlc1::WW8_WrPlc1( sal_uInt16 nStructSz )
+ : m_pData( new sal_uInt8[ 16 * nStructSz ] ),
+ m_nDataLen(16 * nStructSz),
+ m_nStructSiz( nStructSz )
+{
+}
+
+WW8_WrPlc1::~WW8_WrPlc1()
+{
+}
+
+WW8_CP WW8_WrPlc1::Prev() const
+{
+ bool b = !m_aPos.empty();
+ OSL_ENSURE(b,"Prev called on empty list");
+ return b ? m_aPos.back() : 0;
+}
+
+void WW8_WrPlc1::Append( WW8_CP nCp, const void* pNewData )
+{
+ sal_uLong nInsPos = m_aPos.size() * m_nStructSiz;
+ m_aPos.push_back( nCp );
+ if( m_nDataLen < nInsPos + m_nStructSiz )
+ {
+ sal_uInt8* pNew = new sal_uInt8[ 2 * m_nDataLen ];
+ memcpy( pNew, m_pData.get(), m_nDataLen );
+ m_pData.reset(pNew);
+ m_nDataLen *= 2;
+ }
+ memcpy( m_pData.get() + nInsPos, pNewData, m_nStructSiz );
+}
+
+void WW8_WrPlc1::Finish( sal_uLong nLastCp, sal_uLong nSttCp )
+{
+ if( !m_aPos.empty() )
+ {
+ m_aPos.push_back( nLastCp );
+ if( nSttCp )
+ for(WW8_CP & rCp : m_aPos)
+ rCp -= nSttCp;
+ }
+}
+
+void WW8_WrPlc1::Write( SvStream& rStrm )
+{
+ decltype(m_aPos)::size_type i;
+ for( i = 0; i < m_aPos.size(); ++i )
+ SwWW8Writer::WriteLong( rStrm, m_aPos[i] );
+ if( i )
+ rStrm.WriteBytes(m_pData.get(), (i-1) * m_nStructSiz);
+}
+
+// Class WW8_WrPlcField for fields
+
+void WW8_WrPlcField::Write( WW8Export& rWrt )
+{
+ if( WW8_WrPlc1::Count() <= 1 )
+ return;
+
+ WW8_FC *pfc;
+ sal_Int32 *plc;
+ switch (m_nTextTyp)
+ {
+ case TXT_MAINTEXT:
+ pfc = &rWrt.m_pFib->m_fcPlcffldMom;
+ plc = &rWrt.m_pFib->m_lcbPlcffldMom;
+ break;
+ case TXT_HDFT:
+ pfc = &rWrt.m_pFib->m_fcPlcffldHdr;
+ plc = &rWrt.m_pFib->m_lcbPlcffldHdr;
+ break;
+
+ case TXT_FTN:
+ pfc = &rWrt.m_pFib->m_fcPlcffldFootnote;
+ plc = &rWrt.m_pFib->m_lcbPlcffldFootnote;
+ break;
+
+ case TXT_EDN:
+ pfc = &rWrt.m_pFib->m_fcPlcffldEdn;
+ plc = &rWrt.m_pFib->m_lcbPlcffldEdn;
+ break;
+
+ case TXT_ATN:
+ pfc = &rWrt.m_pFib->m_fcPlcffldAtn;
+ plc = &rWrt.m_pFib->m_lcbPlcffldAtn;
+ break;
+
+ case TXT_TXTBOX:
+ pfc = &rWrt.m_pFib->m_fcPlcffldTxbx;
+ plc = &rWrt.m_pFib->m_lcbPlcffldTxbx;
+ break;
+
+ case TXT_HFTXTBOX:
+ pfc = &rWrt.m_pFib->m_fcPlcffldHdrTxbx;
+ plc = &rWrt.m_pFib->m_lcbPlcffldHdrTxbx;
+ break;
+
+ default:
+ pfc = plc = nullptr;
+ break;
+ }
+
+ if( pfc && plc )
+ {
+ sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
+ WW8_WrPlc1::Write( *rWrt.m_pTableStrm );
+ *pfc = nFcStart;
+ *plc = rWrt.m_pTableStrm->Tell() - nFcStart;
+ }
+}
+
+void WW8_WrMagicTable::Write( WW8Export& rWrt )
+{
+ if( WW8_WrPlc1::Count() <= 1 )
+ return;
+ sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
+ WW8_WrPlc1::Write( *rWrt.m_pTableStrm );
+ rWrt.m_pFib->m_fcPlcfTch = nFcStart;
+ rWrt.m_pFib->m_lcbPlcfTch = rWrt.m_pTableStrm->Tell() - nFcStart;
+}
+
+void WW8_WrMagicTable::Append( WW8_CP nCp, sal_uLong nData)
+{
+ /*
+ Tell the undocumented table hack that everything between here and the last
+ table position is non-table text, don't do it if the previous position is
+ the same as this one, as that would be a region of 0 length
+ */
+ if ((!Count()) || (Prev() != nCp))
+ {
+ SVBT32 nLittle;
+ UInt32ToSVBT32(nData,nLittle);
+ WW8_WrPlc1::Append(nCp, nLittle);
+ }
+}
+
+void SwWW8Writer::FillCount( SvStream& rStrm, sal_uLong nCount )
+{
+ static const sal_uInt32 aNulls[16] =
+ {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 64 Byte
+ };
+
+ while (nCount > 64)
+ {
+ rStrm.WriteBytes(aNulls, 64); // in steps of 64-Byte
+ nCount -= 64;
+ }
+ rStrm.WriteBytes(aNulls, nCount); // write the rest (0 .. 64 Bytes)
+}
+
+sal_uLong SwWW8Writer::FillUntil( SvStream& rStrm, sal_uLong nEndPos )
+{
+ sal_uInt64 nCurPos = rStrm.Tell();
+ if( !nEndPos ) // nEndPos == 0 -> next Page
+ nEndPos = (nCurPos + 0x1ff) & ~0x1ffUL;
+
+ if( nEndPos > nCurPos )
+ SwWW8Writer::FillCount( rStrm, nEndPos - nCurPos );
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_ENSURE( nEndPos == nCurPos, "Wrong FillUntil()" );
+#endif
+ return rStrm.Tell();
+}
+
+WW8_WrPlcPn::WW8_WrPlcPn(WW8Export& rWr, ePLCFT ePl, WW8_FC nStartFc)
+ : m_rWrt(rWr)
+ , m_nFkpStartPage(0)
+ , m_ePlc(ePl)
+{
+ m_Fkps.push_back(std::make_unique<WW8_WrFkp>(m_ePlc, nStartFc));
+}
+
+WW8_WrPlcPn::~WW8_WrPlcPn()
+{
+}
+
+sal_uInt8 *WW8_WrPlcPn::CopyLastSprms(sal_uInt8 &rLen)
+{
+ WW8_WrFkp& rF = *m_Fkps.back();
+ return rF.CopyLastSprms(rLen);
+}
+
+void WW8_WrPlcPn::AppendFkpEntry(WW8_FC nEndFc,short nVarLen,const sal_uInt8* pSprms)
+{
+ WW8_WrFkp* pF = m_Fkps.back().get();
+
+ // big sprm? build the sprmPHugePapx
+ sal_uInt8* pNewSprms = const_cast<sal_uInt8*>(pSprms);
+ sal_uInt8 aHugePapx[ 8 ];
+ if (PAP == m_ePlc && 488 <= nVarLen)
+ {
+ sal_uInt8* p = aHugePapx;
+ *p++ = *pSprms++; // set style Id
+ *p++ = *pSprms++;
+ nVarLen -= 2;
+
+ sal_uInt64 nDataPos = m_rWrt.m_pDataStrm->Tell();
+ SwWW8Writer::WriteShort( *m_rWrt.m_pDataStrm, nVarLen );
+ m_rWrt.m_pDataStrm->WriteBytes(pSprms, nVarLen);
+
+ Set_UInt16( p, 0x6646 ); // set SprmCode
+ Set_UInt32( p, nDataPos ); // set startpos (FC) in the datastream
+ nVarLen = static_cast< short >(p - aHugePapx);
+ pSprms = pNewSprms = aHugePapx;
+ }
+ // if append at the same FC-EndPos and there are sprms, then get the old
+ // sprms and erase it; they will append now with the new sprms
+ else if( nVarLen && pF->IsEqualPos( nEndFc ))
+ pF->MergeToNew( nVarLen, pNewSprms );
+ // has the prev EndFC an empty sprm and the current is empty too, then
+ // expand only the old EndFc to the new EndFc
+ else if( !nVarLen && pF->IsEmptySprm() )
+ {
+ pF->SetNewEnd( nEndFc );
+ return ;
+ }
+
+ bool bOk = pF->Append(nEndFc, nVarLen, pNewSprms);
+ if( !bOk )
+ {
+ pF->Combine();
+ pF = new WW8_WrFkp(m_ePlc, pF->GetEndFc()); // Start new Fkp == end of old Fkp
+
+ m_Fkps.push_back(std::unique_ptr<WW8_WrFkp>(pF));
+ if( !pF->Append( nEndFc, nVarLen, pNewSprms ) )
+ {
+ OSL_ENSURE( false, "Unable to insert Sprm" );
+ }
+ }
+ if( pNewSprms != pSprms ) //Merge to new has created a new block
+ delete[] pNewSprms;
+}
+
+void WW8_WrPlcPn::WriteFkps()
+{
+ m_nFkpStartPage = o3tl::narrowing<sal_uInt16>( SwWW8Writer::FillUntil( m_rWrt.Strm() ) >> 9 );
+
+ for(const std::unique_ptr<WW8_WrFkp> & rp : m_Fkps)
+ {
+ rp->Write( m_rWrt.Strm(), *m_rWrt.m_pGrf );
+ }
+
+ if( CHP == m_ePlc )
+ {
+ m_rWrt.m_pFib->m_pnChpFirst = m_nFkpStartPage;
+ m_rWrt.m_pFib->m_cpnBteChp = m_Fkps.size();
+ }
+ else
+ {
+ m_rWrt.m_pFib->m_pnPapFirst = m_nFkpStartPage;
+ m_rWrt.m_pFib->m_cpnBtePap = m_Fkps.size();
+ }
+}
+
+void WW8_WrPlcPn::WritePlc()
+{
+ sal_uInt64 nFcStart = m_rWrt.m_pTableStrm->Tell();
+ decltype(m_Fkps)::size_type i;
+
+ for (i = 0; i < m_Fkps.size(); ++i)
+ {
+ SwWW8Writer::WriteLong( *m_rWrt.m_pTableStrm,
+ m_Fkps[ i ]->GetStartFc() );
+ }
+
+ SwWW8Writer::WriteLong( *m_rWrt.m_pTableStrm,
+ m_Fkps[ i - 1 ]->GetEndFc() );
+
+ // for every FKP output the page
+ for (i = 0; i < m_Fkps.size(); ++i)
+ {
+ SwWW8Writer::WriteLong( *m_rWrt.m_pTableStrm, i + m_nFkpStartPage );
+ }
+
+ if( CHP == m_ePlc )
+ {
+ m_rWrt.m_pFib->m_fcPlcfbteChpx = nFcStart;
+ m_rWrt.m_pFib->m_lcbPlcfbteChpx = m_rWrt.m_pTableStrm->Tell() - nFcStart;
+ }
+ else
+ {
+ m_rWrt.m_pFib->m_fcPlcfbtePapx = nFcStart;
+ m_rWrt.m_pFib->m_lcbPlcfbtePapx = m_rWrt.m_pTableStrm->Tell() - nFcStart;
+ }
+}
+
+WW8_WrFkp::WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc)
+ : m_ePlc(ePl), m_nStartGrp(511), m_nOldStartGrp(511),
+ m_nItemSize( ( CHP == ePl ) ? 1 : 13 ),
+ m_nIMax(0), m_nOldVarLen(0), m_bCombined(false)
+{
+ m_pFkp = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte
+ m_pOfs = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte
+ memset( m_pFkp, 0, 4 * 128 );
+ memset( m_pOfs, 0, 4 * 128 );
+ reinterpret_cast<sal_Int32*>(m_pFkp)[0] = nStartFc; // 0th entry FC at nStartFc
+}
+
+WW8_WrFkp::~WW8_WrFkp()
+{
+ delete[] reinterpret_cast<sal_Int32 *>(m_pFkp);
+ delete[] reinterpret_cast<sal_Int32 *>(m_pOfs);
+}
+
+sal_uInt8 WW8_WrFkp::SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms )
+{
+ if( 3 < nVarLen )
+ {
+ // if the sprms contained picture-references then never equal!
+ for( sal_uInt8 n = static_cast< sal_uInt8 >(nVarLen - 1); 3 < n; --n )
+ if( pSprms[ n ] == GRF_MAGIC_3 &&
+ pSprms[ n-1 ] == GRF_MAGIC_2 &&
+ pSprms[ n-2 ] == GRF_MAGIC_1 )
+ return 0;
+ }
+
+ short i;
+ for( i = 0; i < m_nIMax; i++ )
+ {
+ sal_uInt8 nStart = m_pOfs[i * m_nItemSize];
+ if( nStart )
+ { // has Sprms
+ const sal_uInt8* p = m_pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
+ if( ( CHP == m_ePlc
+ ? (*p++ == nVarLen)
+ : ((o3tl::narrowing<sal_uInt16>(*p++) << 1 ) == (( nVarLen+1) & 0xfffe)) )
+ && !memcmp( p, pSprms, nVarLen ) )
+ return nStart; // found it
+ }
+ }
+ return 0; // didn't found it
+}
+
+sal_uInt8 *WW8_WrFkp::CopyLastSprms(sal_uInt8 &rLen)
+{
+ rLen=0;
+ sal_uInt8 *pStart=nullptr,*pRet=nullptr;
+
+ if (!m_bCombined)
+ pStart = m_pOfs;
+ else
+ pStart = m_pFkp + ( m_nIMax + 1 ) * 4;
+
+ sal_uInt8 nStart = *(pStart + (m_nIMax-1) * m_nItemSize);
+
+ const sal_uInt8* p = m_pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
+
+ if (!*p)
+ p++;
+
+ if (*p)
+ {
+ rLen = *p++;
+ if (PAP == m_ePlc)
+ rLen *= 2;
+ pRet = new sal_uInt8[rLen];
+ memcpy(pRet,p,rLen);
+ }
+ return pRet;
+}
+
+bool WW8_WrFkp::Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms )
+{
+ assert((!nVarLen || pSprms) && "Item pointer missing");
+
+ OSL_ENSURE( nVarLen < ( ( m_ePlc == PAP ) ? 497U : 502U ), "Sprms too long !" );
+
+ if( m_bCombined )
+ {
+ OSL_ENSURE( false, "Fkp::Append: Fkp is already combined" );
+ return false;
+ }
+ sal_Int32 n = reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax]; // last entry
+ if( nEndFc <= n )
+ {
+ OSL_ENSURE( nEndFc >= n, "+Fkp: FC backwards" );
+ OSL_ENSURE( !nVarLen || !pSprms || nEndFc != n,
+ "+Fkp: used same FC multiple times" );
+ // same FC without Sprm is ignored without grumbling
+
+ return true; // ignore (do not create a new Fkp)
+ }
+
+ sal_uInt8 nOldP = nVarLen ? SearchSameSprm( nVarLen, pSprms ) : 0;
+ // Combine equal entries
+ short nOffset=0, nPos = m_nStartGrp;
+ if (nVarLen && !nOldP)
+ {
+ nPos = PAP == m_ePlc
+ ? ( 13 == m_nItemSize // HACK: PAP and bWrtWW8 !!
+ ? (m_nStartGrp & 0xFFFE ) - nVarLen - 1
+ : (m_nStartGrp - (((nVarLen + 1) & 0xFFFE)+1)) & 0xFFFE )
+ : ((m_nStartGrp - nVarLen - 1) & 0xFFFE);
+ if( nPos < 0 )
+ return false; // doesn't fit at all
+ nOffset = nPos; // save offset (can also be uneven!)
+ nPos &= 0xFFFE; // Pos for Sprms ( gerade Pos )
+ }
+
+ if( o3tl::make_unsigned(nPos) <= ( m_nIMax + 2U ) * 4U + ( m_nIMax + 1U ) * m_nItemSize )
+ // does it fits after the CPs and offsets?
+ return false; // no
+
+ reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax + 1] = nEndFc; // insert FC
+
+ m_nOldVarLen = static_cast<sal_uInt8>(nVarLen);
+ if( nVarLen && !nOldP )
+ { // insert it for real
+ m_nOldStartGrp = m_nStartGrp;
+
+ m_nStartGrp = nPos;
+ m_pOfs[m_nIMax * m_nItemSize] = static_cast<sal_uInt8>( m_nStartGrp >> 1 );
+ // insert (start-of-data >> 1)
+ sal_uInt8 nCnt = static_cast< sal_uInt8 >(CHP == m_ePlc
+ ? ( nVarLen < 256 ) ? static_cast<sal_uInt8>(nVarLen) : 255
+ : ( ( nVarLen + 1 ) >> 1 ));
+
+ m_pFkp[ nOffset ] = nCnt; // Enter data length
+ memcpy( m_pFkp + nOffset + 1, pSprms, nVarLen ); // store Sprms
+ }
+ else
+ {
+ // do not enter for real ( no Sprms or recurrence )
+ // start-of-data 0 ( no data ) or recurrence
+ m_pOfs[m_nIMax * m_nItemSize] = nOldP;
+ }
+ m_nIMax++;
+ return true;
+}
+
+void WW8_WrFkp::Combine()
+{
+ if( m_bCombined )
+ return;
+ if( m_nIMax )
+ memcpy( m_pFkp + ( m_nIMax + 1 ) * 4, m_pOfs, m_nIMax * m_nItemSize );
+ delete[] m_pOfs;
+ m_pOfs = nullptr;
+ m_pFkp[511] = m_nIMax;
+ m_bCombined = true;
+
+#if defined OSL_BIGENDIAN // only the FCs will be rotated here
+ sal_uInt16 i; // the Sprms must be rotated elsewhere
+
+ sal_uInt32* p;
+ for( i = 0, p = (sal_uInt32*)m_pFkp; i <= m_nIMax; i++, p++ )
+ *p = OSL_SWAPDWORD( *p );
+#endif // ifdef OSL_BIGENDIAN
+}
+
+void WW8_WrFkp::Write( SvStream& rStrm, SwWW8WrGrf& rGrf )
+{
+ Combine(); // If not already combined
+
+ sal_uInt8* p; // search magic for nPicLocFc
+ sal_uInt8* pEnd = m_pFkp + m_nStartGrp;
+ for( p = m_pFkp + 511 - 4; p >= pEnd; p-- )
+ {
+ if( *p != GRF_MAGIC_1 ) // search for signature 0x12 0x34 0x56 0xXX
+ continue;
+ if( *(p+1) != GRF_MAGIC_2 )
+ continue;
+ if( *(p+2) != GRF_MAGIC_3 )
+ continue;
+
+ SVBT32 nPos; // signature found
+ UInt32ToSVBT32( rGrf.GetFPos(), nPos ); // FilePos the graphics
+ memcpy( p, nPos, 4 ); // patch FilePos over the signature
+ }
+ rStrm.WriteBytes(m_pFkp, 512);
+}
+
+void WW8_WrFkp::MergeToNew( short& rVarLen, sal_uInt8 *& rpNewSprms )
+{
+ sal_uInt8 nStart = m_pOfs[ (m_nIMax-1) * m_nItemSize ];
+ if( !nStart )
+ return;
+
+// has Sprms
+ sal_uInt8* p = m_pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
+
+ // old and new equal? Then copy only one into the new sprms
+ if( m_nOldVarLen == rVarLen && !memcmp( p+1, rpNewSprms, m_nOldVarLen ))
+ {
+ sal_uInt8* pNew = new sal_uInt8[ m_nOldVarLen ];
+ memcpy( pNew, p+1, m_nOldVarLen );
+ rpNewSprms = pNew;
+ }
+ else
+ {
+ sal_uInt8* pNew = new sal_uInt8[ m_nOldVarLen + rVarLen ];
+ memcpy( pNew, p+1, m_nOldVarLen );
+ memcpy( pNew + m_nOldVarLen, rpNewSprms, rVarLen );
+
+ rpNewSprms = pNew;
+ rVarLen = rVarLen + m_nOldVarLen;
+ }
+ --m_nIMax;
+ // if this Sprms don't used from others, remove it
+ bool bFnd = false;
+ for (sal_uInt16 n = 0; n < m_nIMax; ++n)
+ {
+ if (nStart == m_pOfs[n * m_nItemSize])
+ {
+ bFnd = true;
+ break;
+ }
+ }
+ if (!bFnd)
+ {
+ m_nStartGrp = m_nOldStartGrp;
+ memset( p, 0, m_nOldVarLen+1 );
+ }
+}
+
+WW8_FC WW8_WrFkp::GetStartFc() const
+{
+ // when bCombined, then the array beginning with pFkp is already byte-swapped
+ // to LittleEndian, so to extract the start and end positions they must
+ // be swapped back.
+ if( m_bCombined )
+ return SVBT32ToUInt32( m_pFkp ); // 0. Element
+ return reinterpret_cast<sal_Int32*>(m_pFkp)[0];
+}
+
+WW8_FC WW8_WrFkp::GetEndFc() const
+{
+ if( m_bCombined )
+ return SVBT32ToUInt32( &(m_pFkp[m_nIMax*4]) ); // nIMax-th SVBT32-Element
+ return reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax];
+}
+
+// Method for managing the piece table
+WW8_WrPct::WW8_WrPct(WW8_FC nfcMin)
+ : m_nOldFc(nfcMin)
+{
+ AppendPc(m_nOldFc);
+}
+
+WW8_WrPct::~WW8_WrPct()
+{
+}
+
+// Fill the piece and create a new one
+void WW8_WrPct::AppendPc(WW8_FC nStartFc)
+{
+ WW8_CP nStartCp = nStartFc - m_nOldFc; // subtract the beginning of the text
+ if ( !nStartCp && !m_Pcts.empty())
+ {
+ OSL_ENSURE(1 == m_Pcts.size(), "empty Piece!");
+ m_Pcts.pop_back();
+ }
+
+ m_nOldFc = nStartFc; // remember StartFc as old
+
+ nStartCp >>= 1; // for Unicode: number of characters / 2
+
+ if (!m_Pcts.empty())
+ {
+ nStartCp += m_Pcts.back()->GetStartCp();
+ }
+
+ m_Pcts.push_back(std::make_unique<WW8_WrPc>(nStartFc, nStartCp));
+}
+
+void WW8_WrPct::WritePc( WW8Export& rWrt )
+{
+ sal_uInt64 nPctStart;
+ sal_uLong nOldPos, nEndPos;
+
+ nPctStart = rWrt.m_pTableStrm->Tell(); // Start piece table
+ rWrt.m_pTableStrm->WriteChar( char(0x02) ); // Status byte PCT
+ nOldPos = nPctStart + 1; // remember Position
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 ); // then the length
+
+ for (auto const& it : m_Pcts) // ranges
+ {
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, it->GetStartCp() );
+ }
+
+ // calculate the last Pos
+ sal_uLong nStartCp = rWrt.m_pFib->m_fcMac - m_nOldFc;
+ nStartCp >>= 1; // For Unicode: number of characters / 2
+ nStartCp += m_Pcts.back()->GetStartCp();
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nStartCp );
+
+ // piece references
+ for (auto const& it : m_Pcts)
+ {
+ SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, it->GetStatus());
+ SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, it->GetStartFc());
+ SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0); // PRM=0
+ }
+
+ // entries in the FIB
+ rWrt.m_pFib->m_fcClx = nPctStart;
+ nEndPos = rWrt.m_pTableStrm->Tell();
+ rWrt.m_pFib->m_lcbClx = nEndPos - nPctStart;
+
+ // and register the length as well
+ SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nOldPos,
+ nEndPos - nPctStart-5 );
+
+}
+
+void WW8_WrPct::SetParaBreak()
+{
+ OSL_ENSURE( !m_Pcts.empty(), "SetParaBreak : m_Pcts.empty()" );
+ m_Pcts.back()->SetStatus();
+}
+
+WW8_CP WW8_WrPct::Fc2Cp( sal_uLong nFc ) const
+{
+ OSL_ENSURE( nFc >= o3tl::make_unsigned(m_nOldFc), "FilePos lies in front of last piece" );
+ OSL_ENSURE( ! m_Pcts.empty(), "Fc2Cp no piece available" );
+
+ nFc -= m_nOldFc;
+ nFc /= 2; // Unicode
+ return nFc + m_Pcts.back()->GetStartCp();
+}
+
+void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* /*pRedlineData*/ )
+{
+ std::vector< const ::sw::mark::IMark* > aArr;
+ sal_Int32 nContent;
+ const sal_Int32 nCurrentEnd = nCurrentPos + nLen;
+ if( !GetWriter().GetBookmarks( rNd, nCurrentPos, nCurrentEnd, aArr ))
+ return;
+
+ SwNodeOffset nNd = rNd.GetIndex();
+ sal_uLong nSttCP = Fc2Cp( Strm().Tell() );
+ for(const ::sw::mark::IMark* p : aArr)
+ {
+ const ::sw::mark::IMark& rBkmk = *p;
+ if(dynamic_cast< const ::sw::mark::IFieldmark *>(&rBkmk))
+ continue;
+
+ const SwPosition* pPos = &rBkmk.GetMarkPos();
+ const SwPosition* pOPos = nullptr;
+ if(rBkmk.IsExpanded())
+ pOPos = &rBkmk.GetOtherMarkPos();
+ if( pOPos && pOPos->GetNode() == pPos->GetNode() &&
+ pOPos->GetContentIndex() < pPos->GetContentIndex() )
+ {
+ pPos = pOPos;
+ pOPos = &rBkmk.GetMarkPos();
+ }
+
+ if( !pOPos || ( nNd == pPos->GetNodeIndex() &&
+ ( nContent = pPos->GetContentIndex() ) >= nCurrentPos &&
+ nContent < nCurrentEnd ) )
+ {
+ sal_uLong nCp = nSttCP + pPos->GetContentIndex() - nCurrentPos;
+ m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
+ }
+ if( pOPos && nNd == pOPos->GetNodeIndex() &&
+ ( nContent = pOPos->GetContentIndex() ) >= nCurrentPos &&
+ nContent < nCurrentEnd )
+ {
+ sal_uLong nCp = nSttCP + pOPos->GetContentIndex() - nCurrentPos;
+ m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
+ }
+ }
+}
+
+void WW8Export::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen)
+{
+ IMarkVector aMarks;
+ if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
+ {
+ for (const sw::mark::IMark* pMark : aMarks)
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
+ if (nStart == nCurrentPos)
+ {
+ m_pAtn->AddRangeStartPosition(pMark->GetName(), Fc2Cp(Strm().Tell()),
+ !rAttrs.HasFlysAt(nCurrentPos));
+ }
+ }
+ }
+}
+
+void WW8Export::AppendSmartTags(SwTextNode& rTextNode)
+{
+ std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", rTextNode);
+ if (!aStatements.empty())
+ {
+ WW8_CP nCP = Fc2Cp(Strm().Tell());
+ m_pFactoids->Append(nCP, nCP, aStatements);
+ }
+}
+
+void WW8Export::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
+{
+ m_pBkmks->MoveFieldMarks(nFrom, nTo);
+}
+
+void WW8Export::AppendBookmark( const OUString& rName )
+{
+ sal_uInt64 nSttCP = Fc2Cp( Strm().Tell() );
+ m_pBkmks->Append( nSttCP, rName );
+}
+
+void WW8Export::AppendBookmarkEndWithCorrection( const OUString& rName )
+{
+ sal_uInt64 nEndCP = Fc2Cp( Strm().Tell() );
+ m_pBkmks->Append( nEndCP - 1, rName );
+}
+
+std::unique_ptr<SvxBrushItem> MSWordExportBase::getBackground()
+{
+ const SwFrameFormat &rFormat = m_rDoc.GetPageDesc(0).GetMaster();
+ std::unique_ptr<SvxBrushItem> aBrush = std::make_unique<SvxBrushItem>(RES_BACKGROUND);
+ SfxItemState eState = rFormat.GetBackgroundState(aBrush);
+
+ if (SfxItemState::SET == eState)
+ {
+ // The 'color' is set for the first page style - take it and use it as the background color of the entire DOCX
+ if (aBrush->GetColor() != COL_AUTO)
+ return aBrush;
+ }
+ return nullptr;
+}
+
+// #i120928 collect all the graphics of bullets applied to paragraphs
+int MSWordExportBase::CollectGrfsOfBullets()
+{
+ m_vecBulletPic.clear();
+
+ size_t nCountRule = m_rDoc.GetNumRuleTable().size();
+ for (size_t n = 0; n < nCountRule; ++n)
+ {
+ const SwNumRule &rRule = *( m_rDoc.GetNumRuleTable().at(n) );
+ sal_uInt16 nLevels = rRule.IsContinusNum() ? 1 : 9;
+ for (sal_uInt16 nLvl = 0; nLvl < nLevels; ++nLvl)
+ {
+ const SwNumFormat &rFormat = rRule.Get(nLvl);
+ if (SVX_NUM_BITMAP != rFormat.GetNumberingType())
+ {
+ continue;
+ }
+ const Graphic *pGraf = rFormat.GetBrush()? rFormat.GetBrush()->GetGraphic():nullptr;
+ if ( pGraf )
+ {
+ bool bHas = false;
+ for (const Graphic* p : m_vecBulletPic)
+ {
+ if (p->GetChecksum() == pGraf->GetChecksum())
+ {
+ bHas = true;
+ break;
+ }
+ }
+ if (!bHas)
+ {
+ Size aSize(pGraf->GetPrefSize());
+ if (0 != aSize.Height() && 0 != aSize.Width())
+ m_vecBulletPic.push_back(pGraf);
+ }
+ }
+ }
+ }
+
+ return m_vecBulletPic.size();
+}
+
+void MSWordExportBase::BulletDefinitions()
+{
+ for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
+ {
+ const MapMode aMapMode(MapUnit::MapTwip);
+ const Graphic& rGraphic = *m_vecBulletPic[i];
+ Size aSize(rGraphic.GetPrefSize());
+ if (MapUnit::MapPixel == rGraphic.GetPrefMapMode().GetMapUnit())
+ aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMapMode);
+ else
+ aSize = OutputDevice::LogicToLogic(aSize,rGraphic.GetPrefMapMode(), aMapMode);
+
+ if (0 != aSize.Height() && 0 != aSize.Width())
+ AttrOutput().BulletDefinition(i, rGraphic, aSize);
+ }
+}
+
+//Export Graphic of Bullets
+void WW8Export::ExportGrfBullet(const SwTextNode& rNd)
+{
+ int nCount = CollectGrfsOfBullets();
+ if (nCount > 0)
+ {
+ SwPosition aPos(rNd);
+ OUString aPicBullets("_PictureBullets");
+ AppendBookmark(aPicBullets);
+ for (int i = 0; i < nCount; i++)
+ {
+ ww8::Frame aFrame(*(m_vecBulletPic[i]), aPos);
+ OutGrfBullets(aFrame);
+ }
+ AppendBookmark(aPicBullets);
+ }
+}
+
+static sal_uInt8 nAttrMagicIdx = 0;
+void WW8Export::OutGrfBullets(const ww8::Frame & rFrame)
+{
+ if ( !m_pGrf || !m_pChpPlc || !m_pO )
+ return;
+
+ m_pGrf->Insert(rFrame);
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), m_pO->size(), m_pO->data() );
+ m_pO->clear();
+ // if links...
+ WriteChar( char(1) );
+
+ sal_uInt8 aArr[ 22 ];
+ sal_uInt8* pArr = aArr;
+
+ // sprmCFSpec
+ Set_UInt16( pArr, 0x855 );
+ Set_UInt8( pArr, 1 );
+
+ Set_UInt16( pArr, 0x083c );
+ Set_UInt8( pArr, 0x81 );
+
+ // sprmCPicLocation
+ Set_UInt16( pArr, 0x6a03 );
+ Set_UInt32( pArr, GRF_MAGIC_321 );
+
+ //extern nAttrMagicIdx;
+ --pArr;
+ Set_UInt8( pArr, nAttrMagicIdx++ );
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
+}
+
+int MSWordExportBase::GetGrfIndex(const SvxBrushItem& rBrush)
+{
+ int nIndex = -1;
+
+ const Graphic* pGraphic = rBrush.GetGraphic();
+ if (pGraphic)
+ {
+ for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
+ {
+ if (m_vecBulletPic[i]->GetChecksum() == pGraphic->GetChecksum())
+ {
+ nIndex = i;
+ break;
+ }
+ }
+ }
+
+ return nIndex;
+}
+
+void WW8_WrtRedlineAuthor::Write( Writer& rWrt )
+{
+ WW8Export & rWW8Wrt = *(static_cast<SwWW8Writer&>(rWrt).m_pExport);
+ rWW8Wrt.WriteAsStringTable(maAuthors, rWW8Wrt.m_pFib->m_fcSttbfRMark,
+ rWW8Wrt.m_pFib->m_lcbSttbfRMark);
+}
+
+sal_uInt16 WW8Export::AddRedlineAuthor( std::size_t nId )
+{
+ if( !m_pRedlAuthors )
+ {
+ m_pRedlAuthors.reset(new WW8_WrtRedlineAuthor);
+ m_pRedlAuthors->AddName("Unknown");
+ }
+ return m_pRedlAuthors->AddName( SW_MOD()->GetRedlineAuthor( nId ) );
+}
+
+void WW8Export::WriteAsStringTable(const std::vector<OUString>& rStrings,
+ sal_Int32& rfcSttbf, sal_Int32& rlcbSttbf)
+{
+ sal_uInt16 n, nCount = static_cast< sal_uInt16 >(rStrings.size());
+ if( !nCount )
+ return;
+
+ // we have some Redlines found in the document -> the
+ // Author Name Stringtable
+ SvStream& rStrm = *m_pTableStrm;
+ rfcSttbf = rStrm.Tell();
+ SwWW8Writer::WriteShort( rStrm, -1 );
+ SwWW8Writer::WriteLong( rStrm, nCount );
+ for( n = 0; n < nCount; ++n )
+ {
+ const OUString& rNm = rStrings[n];
+ SwWW8Writer::WriteShort( rStrm, rNm.getLength() );
+ SwWW8Writer::WriteString16(rStrm, rNm, false);
+ }
+ rlcbSttbf = rStrm.Tell() - rfcSttbf;
+}
+
+// WriteShort() sets at FilePos nPos the value nVal and seeks to the old
+// FilePos. Used to insert lengths after the fact.
+void SwWW8Writer::WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal )
+{
+ sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos
+ rStrm.Seek( nPos );
+ SwWW8Writer::WriteShort( rStrm, nVal );
+ rStrm.Seek( nOldPos );
+}
+
+void SwWW8Writer::WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal )
+{
+ sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos
+ rStrm.Seek( nPos );
+ SwWW8Writer::WriteLong( rStrm, nVal );
+ rStrm.Seek( nOldPos );
+}
+
+void SwWW8Writer::InsUInt16(ww::bytes &rO, sal_uInt16 n)
+{
+ SVBT16 nL;
+ ShortToSVBT16( n, nL );
+ rO.push_back(nL[0]);
+ rO.push_back(nL[1]);
+}
+
+void SwWW8Writer::InsUInt32(ww::bytes &rO, sal_uInt32 n)
+{
+ SVBT32 nL;
+ UInt32ToSVBT32( n, nL );
+ rO.push_back(nL[0]);
+ rO.push_back(nL[1]);
+ rO.push_back(nL[2]);
+ rO.push_back(nL[3]);
+}
+
+void SwWW8Writer::InsAsString16(ww::bytes &rO, const OUString& rStr)
+{
+ const sal_Unicode* pStr = rStr.getStr();
+ for (sal_Int32 n = 0, nLen = rStr.getLength(); n < nLen; ++n, ++pStr)
+ SwWW8Writer::InsUInt16( rO, *pStr );
+}
+
+void SwWW8Writer::InsAsString8(ww::bytes &rO, std::u16string_view rStr,
+ rtl_TextEncoding eCodeSet)
+{
+ OString sTmp(OUStringToOString(rStr, eCodeSet));
+ const char *pStart = sTmp.getStr();
+ const char *pEnd = pStart + sTmp.getLength();
+
+ rO.insert( rO.end(), pStart, pEnd );
+}
+
+void SwWW8Writer::WriteString16(SvStream& rStrm, const OUString& rStr,
+ bool bAddZero)
+{
+ ww::bytes aBytes;
+ SwWW8Writer::InsAsString16(aBytes, rStr);
+ if (bAddZero)
+ SwWW8Writer::InsUInt16(aBytes, 0);
+ //vectors are guaranteed to have contiguous memory, so we can do
+ //this while migrating away from WW8Bytes. Meyers Effective STL, item 16
+ if (!aBytes.empty())
+ rStrm.WriteBytes(aBytes.data(), aBytes.size());
+}
+
+void SwWW8Writer::WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero)
+{
+ ww::bytes aBytes;
+ SwWW8Writer::InsUInt16(aBytes, rStr.getLength());
+ SwWW8Writer::InsAsString16(aBytes, rStr);
+ if (bAddZero)
+ SwWW8Writer::InsUInt16(aBytes, 0);
+ rStrm.WriteBytes(aBytes.data(), aBytes.size());
+}
+
+void SwWW8Writer::WriteString8(SvStream& rStrm, std::u16string_view rStr,
+ bool bAddZero, rtl_TextEncoding eCodeSet)
+{
+ ww::bytes aBytes;
+ SwWW8Writer::InsAsString8(aBytes, rStr, eCodeSet);
+ if (bAddZero)
+ aBytes.push_back(0);
+ //vectors are guaranteed to have contiguous memory, so we can do
+ ////this while migrating away from WW8Bytes. Meyers Effective STL, item 16
+ if (!aBytes.empty())
+ rStrm.WriteBytes(aBytes.data(), aBytes.size());
+}
+
+void WW8Export::WriteStringAsPara( const OUString& rText )
+{
+ if( !rText.isEmpty() )
+ OutSwString(rText, 0, rText.getLength());
+ WriteCR(); // CR thereafter
+
+ ww::bytes aArr;
+ SwWW8Writer::InsUInt16( aArr, 0/*nStyleId*/ );
+ if( m_bOutTable )
+ { // Tab-Attr
+ // sprmPFInTable
+ SwWW8Writer::InsUInt16( aArr, NS_sprm::PFInTable::val );
+ aArr.push_back( 1 );
+ }
+
+ sal_uInt64 nPos = Strm().Tell();
+ m_pPapPlc->AppendFkpEntry( nPos, aArr.size(), aArr.data() );
+ m_pChpPlc->AppendFkpEntry( nPos );
+}
+
+void MSWordExportBase::WriteSpecialText( SwNodeOffset nStart, SwNodeOffset nEnd, sal_uInt8 nTTyp )
+{
+ sal_uInt8 nOldTyp = m_nTextTyp;
+ m_nTextTyp = nTTyp;
+ auto const pOldPam = m_pCurPam; //!! Simply shifting the PaM without restoring should do the job too
+ SwNodeOffset nOldStart = m_nCurStart;
+ SwNodeOffset nOldEnd = m_nCurEnd;
+ SwPaM* pOldEnd = m_pOrigPam;
+ bool bOldPageDescs = m_bOutPageDescs;
+ m_bOutPageDescs = false;
+ if ( nTTyp == TXT_FTN || nTTyp == TXT_EDN )
+ m_bAddFootnoteTab = true; // enable one aesthetic tab for this footnote
+
+ SetCurPam(nStart, nEnd);
+
+ // clear linked textboxes since old ones can't be linked to frames in this section
+ m_aLinkedTextboxesHelper.clear();
+
+ // tdf#106261 Reset table infos, otherwise the depth of the cells will be
+ // incorrect, in case the header/footer had table(s) and we try to export
+ // the same table second time.
+ ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_pTableInfo;
+ m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
+
+ WriteText();
+
+ m_pTableInfo = pOldTableInfo;
+
+ m_bOutPageDescs = bOldPageDescs;
+ m_pCurPam = pOldPam; // delete Pam
+ m_nCurStart = nOldStart;
+ m_nCurEnd = nOldEnd;
+ m_pOrigPam = pOldEnd;
+ m_nTextTyp = nOldTyp;
+}
+
+void WW8Export::OutSwString(const OUString& rStr, sal_Int32 nStt,
+ sal_Int32 const nLen)
+
+{
+ SAL_INFO( "sw.ww8.level2", "<OutSwString>" );
+
+ if( nLen )
+ {
+ if( nStt || nLen != rStr.getLength() )
+ {
+ OUString sOut( rStr.copy( nStt, nLen ) );
+
+ SAL_INFO( "sw.ww8.level2", sOut );
+
+ SwWW8Writer::WriteString16(Strm(), sOut, false);
+ }
+ else
+ {
+ SAL_INFO( "sw.ww8.level2", rStr );
+
+ SwWW8Writer::WriteString16(Strm(), rStr, false);
+ }
+ }
+
+ SAL_INFO( "sw.ww8.level2", "</OutSwString>" );
+}
+
+void WW8Export::WriteCR(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ if (pTableTextNodeInfoInner && pTableTextNodeInfoInner->getDepth() == 1 && pTableTextNodeInfoInner->isEndOfCell())
+ WriteChar('\007');
+ else
+ WriteChar( '\015' );
+
+ m_pPiece->SetParaBreak();
+}
+
+void WW8Export::WriteChar( sal_Unicode c )
+{
+ Strm().WriteUInt16( c );
+}
+
+void MSWordExportBase::SetCurPam(SwNodeOffset nStt, SwNodeOffset nEnd)
+{
+ m_nCurStart = nStt;
+ m_nCurEnd = nEnd;
+ m_pCurPam = Writer::NewUnoCursor( m_rDoc, nStt, nEnd );
+
+ // Recognize tables in special cases
+ if ( nStt != m_pCurPam->GetMark()->GetNodeIndex() &&
+ m_rDoc.GetNodes()[ nStt ]->IsTableNode() )
+ {
+ m_pCurPam->GetMark()->Assign(nStt);
+ }
+
+ m_pOrigPam = m_pCurPam.get(); // ???
+ m_pCurPam->Exchange();
+}
+
+void MSWordExportBase::SaveData( SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ MSWordSaveData aData;
+
+ // WW8Export only stuff - zeroed here not to issue warnings
+ aData.pOOld = nullptr;
+
+ // Common stuff
+ aData.pOldPam = m_pCurPam;
+ aData.pOldEnd = m_pOrigPam;
+ aData.pOldFlyFormat = m_pParentFrame;
+ aData.pOldPageDesc = m_pCurrentPageDesc;
+
+ aData.pOldFlyOffset = m_pFlyOffset;
+ aData.eOldAnchorType = m_eNewAnchorType;
+
+ aData.bOldWriteAll = false;
+ aData.bOldOutTable = m_bOutTable;
+ aData.bOldFlyFrameAttrs = m_bOutFlyFrameAttrs;
+ aData.bOldStartTOX = m_bStartTOX;
+ aData.bOldInWriteTOX = m_bInWriteTOX;
+
+ SetCurPam(nStt, nEnd);
+
+ m_bOutTable = false;
+ // Caution: bIsInTable should not be set here
+ m_bOutFlyFrameAttrs = false;
+ m_bStartTOX = false;
+ m_bInWriteTOX = false;
+
+ m_aSaveData.push( std::move(aData) );
+}
+
+void MSWordExportBase::RestoreData()
+{
+ MSWordSaveData &rData = m_aSaveData.top();
+
+ m_pCurPam = rData.pOldPam;
+ m_nCurStart = rData.nOldStart;
+ m_nCurEnd = rData.nOldEnd;
+ m_pOrigPam = rData.pOldEnd;
+
+ m_bOutTable = rData.bOldOutTable;
+ m_bOutFlyFrameAttrs = rData.bOldFlyFrameAttrs;
+ m_bStartTOX = rData.bOldStartTOX;
+ m_bInWriteTOX = rData.bOldInWriteTOX;
+
+ m_pParentFrame = rData.pOldFlyFormat;
+ m_pCurrentPageDesc = rData.pOldPageDesc;
+
+ m_eNewAnchorType = rData.eOldAnchorType;
+ m_pFlyOffset = rData.pOldFlyOffset;
+
+ m_aSaveData.pop();
+}
+
+void WW8Export::SaveData( SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ MSWordExportBase::SaveData( nStt, nEnd );
+
+ MSWordSaveData &rData = m_aSaveData.top();
+
+ if ( !m_pO->empty() )
+ {
+ rData.pOOld = std::move(m_pO);
+ m_pO.reset(new ww::bytes);
+ }
+ else
+ rData.pOOld = nullptr; // reuse pO
+
+ rData.bOldWriteAll = GetWriter().m_bWriteAll;
+ GetWriter().m_bWriteAll = true;
+}
+
+void WW8Export::RestoreData()
+{
+ MSWordSaveData &rData = m_aSaveData.top();
+
+ GetWriter().m_bWriteAll = rData.bOldWriteAll;
+
+ OSL_ENSURE( m_pO->empty(), "pO is not empty in WW8Export::RestoreData()" );
+ if ( rData.pOOld )
+ {
+ m_pO = std::move(rData.pOOld);
+ }
+
+ MSWordExportBase::RestoreData();
+}
+
+void WW8AttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
+
+ if ( nDepth <= 0 )
+ return;
+
+ /* Cell */
+ m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
+ m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
+ m_rWW8Export.InsUInt32( nDepth );
+
+ if ( nDepth > 1 && pTableTextNodeInfoInner->isEndOfCell() )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
+ }
+}
+
+void WW8AttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
+
+ if ( nDepth <= 0 )
+ return;
+
+ /* Row */
+ if ( !pTableTextNodeInfoInner->isEndOfLine() )
+ return;
+
+ m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
+
+ if ( nDepth == 1 )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::PFTtp::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
+ }
+
+ m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
+ m_rWW8Export.InsUInt32( nDepth );
+
+ if ( nDepth > 1 )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
+ m_rWW8Export.InsUInt16( NS_sprm::PFInnerTtp::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
+ }
+
+ // Most of these are per-row definitions, not per-table.
+ // WW8 has no explicit table start/end markup,
+ // simply rows with the same table properties that are grouped together as a table.
+ TableBidi( pTableTextNodeInfoInner );
+ TableOrientation( pTableTextNodeInfoInner );
+ TableSpacing( pTableTextNodeInfoInner );
+ TableDefinition( pTableTextNodeInfoInner ); //per row definitions
+ TableHeight( pTableTextNodeInfoInner ); //per row definitions
+ TableBackgrounds( pTableTextNodeInfoInner ); //per row definitions
+ // Since this isEndOfLine, cell margin defaults for each row come from last column.
+ TableDefaultBorders( pTableTextNodeInfoInner ); //per row definitions
+ TableCanSplit( pTableTextNodeInfoInner ); //per row definitions
+ TableVerticalCell( pTableTextNodeInfoInner ); //per row definitions
+ TableCellBorders( pTableTextNodeInfoInner ); //per row definitions
+}
+
+static sal_uInt16 lcl_TCFlags(SwDoc &rDoc, const SwTableBox * pBox, sal_Int32 nRowSpan)
+{
+ sal_uInt16 nFlags = 0;
+
+ if (nRowSpan > 1)
+ nFlags |= (3 << 5);
+ else if (nRowSpan < 0)
+ nFlags |= (1 << 5);
+
+ if (pBox != nullptr)
+ {
+ const SwFrameFormat * pFormat = pBox->GetFrameFormat();
+ switch (pFormat->GetVertOrient().GetVertOrient())
+ {
+ case text::VertOrientation::CENTER:
+ nFlags |= (1 << 7);
+ break;
+ case text::VertOrientation::BOTTOM:
+ nFlags |= (2 << 7);
+ break;
+ default:
+ break;
+ }
+ const SwStartNode * pSttNd = pBox->GetSttNd();
+ if(pSttNd)
+ {
+ SwNodeIndex aIdx( *pSttNd );
+ const SwContentNode * pCNd = pSttNd->GetNodes().GoNext( &aIdx );
+ if( pCNd && pCNd->IsTextNode())
+ {
+ SfxItemSetFixed<RES_CHRATR_ROTATE, RES_CHRATR_ROTATE> aCoreSet(rDoc.GetAttrPool());
+ static_cast<const SwTextNode*>(pCNd)->GetParaAttr(aCoreSet,
+ 0, static_cast<const SwTextNode*>(pCNd)->GetText().getLength());
+ if ( const SvxCharRotateItem * pRotate = aCoreSet.GetItemIfSet(RES_CHRATR_ROTATE))
+ {
+ if(pRotate && pRotate->GetValue() == 900_deg10)
+ {
+ nFlags = nFlags | 0x0004 | 0x0008;
+ }
+ else if(pRotate && pRotate->GetValue() == 2700_deg10 )
+ {
+ nFlags = nFlags | 0x0004 | 0x0010;
+ }
+ }
+ }
+ }
+ }
+
+ return nFlags;
+}
+
+void WW8AttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes();
+
+ sal_uInt8 nBoxes = rTableBoxes.size();
+ for ( sal_uInt8 n = 0; n < nBoxes; n++ )
+ {
+ const SwTableBox * pTabBox1 = rTableBoxes[n];
+ const SwFrameFormat * pFrameFormat = pTabBox1->GetFrameFormat();
+
+ // Map from our SvxFrameDirection to WW8 TextFlow.
+ sal_uInt16 nTextFlow = 0;
+ switch (m_rWW8Export.TrueFrameDirection(*pFrameFormat))
+ {
+ case SvxFrameDirection::Vertical_RL_TB:
+ nTextFlow = 5;
+ break;
+ case SvxFrameDirection::Vertical_LR_BT:
+ nTextFlow = 3;
+ break;
+ default:
+ break;
+ }
+
+ if (nTextFlow != 0)
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TTextFlow::val );
+ m_rWW8Export.m_pO->push_back( n ); //start range
+ m_rWW8Export.m_pO->push_back( sal_uInt8(n + 1) ); //end range
+ m_rWW8Export.InsUInt16(nTextFlow);
+ }
+ }
+}
+
+void WW8AttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
+
+ /*
+ By default the row can be split in word, and now in writer we have a
+ feature equivalent to this, Word stores 1 for fCantSplit if the row
+ cannot be split, we set true if we can split it. An example is #i4569#
+ */
+
+ const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
+ sal_uInt8 nCantSplit = (!rSplittable.GetValue()) ? 1 : 0;
+ m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit::val );
+ m_rWW8Export.m_pO->push_back( nCantSplit );
+ m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit90::val ); // also write fCantSplit90
+ m_rWW8Export.m_pO->push_back( nCantSplit );
+}
+
+void WW8AttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTable * pTable = pTableTextNodeInfoInner->getTable();
+ const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
+
+ if ( m_rWW8Export.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TFBiDi::val );
+ m_rWW8Export.InsUInt16( 1 );
+ }
+}
+
+void WW8AttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
+{
+}
+
+void WW8AttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
+{
+}
+
+void WW8AttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
+
+ // output line height sprmTDyaRowHeight
+ tools::Long nHeight = 0;
+ const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
+ if ( SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight() )
+ {
+ if ( SwFrameSize::Minimum == rLSz.GetHeightSizeType() )
+ nHeight = rLSz.GetHeight();
+ else
+ nHeight = -rLSz.GetHeight();
+ }
+
+ if ( nHeight )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TDyaRowHeight::val );
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nHeight) );
+ }
+
+}
+
+void WW8AttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTable * pTable = pTableTextNodeInfoInner->getTable();
+
+ const SwFrameFormat *pFormat = pTable->GetFrameFormat();
+ if ( !pFormat )
+ {
+ SAL_WARN( "sw.ww8", "FrameFormat is nil" );
+ return;
+ }
+
+ const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
+ const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
+
+ if (
+ !((text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
+ text::RelOrientation::FRAME == rHori.GetRelationOrient())
+ &&
+ (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
+ text::RelOrientation::FRAME == rVert.GetRelationOrient()))
+ )
+ return;
+
+ const bool bIsRTL = m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB;
+ sal_Int16 eHOri = rHori.GetHoriOrient();
+ switch (eHOri)
+ {
+ case text::HoriOrientation::CENTER:
+ m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //logical orientation required for MSO
+ m_rWW8Export.InsUInt16( 1 );
+ m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //physical orientation required for LO
+ m_rWW8Export.InsUInt16( 1 );
+ break;
+ case text::HoriOrientation::RIGHT:
+ m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
+ m_rWW8Export.InsUInt16( 2 );
+ if ( !bIsRTL )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
+ m_rWW8Export.InsUInt16( 2 );
+ }
+ break;
+ case text::HoriOrientation::LEFT:
+ if ( bIsRTL )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
+ m_rWW8Export.InsUInt16( 2 );
+ }
+ break;
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ // Width can only be specified for the LOGICAL left, so in RTL, that is always PHYSICAL right
+ if ( bIsRTL )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
+ m_rWW8Export.InsUInt16( 2 );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void WW8AttributeOutput::TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
+{
+ const SwTable * pTable = pTableTextNodeInfoInner->getTable();
+ const SwTableFormat* pTableFormat = pTable->GetFrameFormat();
+
+
+ // Writing these SPRM's will make the table a floating one, so only write
+ // them in case the table is already inside a frame.
+ if (!(pTableFormat != nullptr && pTable->GetTableNode()->GetFlyFormat()))
+ return;
+
+ const SvxULSpaceItem & rUL = pTableFormat->GetULSpace();
+
+ if (rUL.GetUpper() > 0)
+ {
+ sal_uInt8 const nPadding = 2;
+ sal_uInt8 const nPcVert = 0;
+ sal_uInt8 const nPcHorz = 0;
+
+ sal_uInt8 const nTPc = (nPadding << 4) | (nPcVert << 2) | nPcHorz;
+
+ m_rWW8Export.InsUInt16(NS_sprm::TPc::val);
+ m_rWW8Export.m_pO->push_back( nTPc );
+
+ m_rWW8Export.InsUInt16(NS_sprm::TDyaAbs::val);
+ m_rWW8Export.InsUInt16(rUL.GetUpper());
+
+ m_rWW8Export.InsUInt16(NS_sprm::TDyaFromText::val);
+ m_rWW8Export.InsUInt16(rUL.GetUpper());
+ }
+
+ if (rUL.GetLower() > 0)
+ {
+ m_rWW8Export.InsUInt16(NS_sprm::TDyaFromTextBottom::val);
+ m_rWW8Export.InsUInt16(rUL.GetLower());
+ }
+}
+
+void WW8AttributeOutput::TablePositioning(SwFrameFormat* pFlyFormat)
+{
+ if (!pFlyFormat || !pFlyFormat->GetFlySplit().GetValue())
+ {
+ return;
+ }
+
+ sal_uInt8 nPcVert = 0;
+ switch (pFlyFormat->GetVertOrient().GetRelationOrient())
+ {
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ // relative to margin
+ nPcVert = 0;
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ // relative to page
+ nPcVert = 1;
+ break;
+ default:
+ // text::RelOrientation::FRAME
+ // relative to text
+ nPcVert = 2;
+ break;
+ }
+ sal_uInt8 nPcHorz = 0;
+ switch (pFlyFormat->GetHoriOrient().GetRelationOrient())
+ {
+ case text::RelOrientation::FRAME:
+ // relative to column
+ nPcHorz = 0;
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ // relative to margin
+ nPcHorz = 1;
+ break;
+ default:
+ // text::RelOrientation::PAGE_FRAME
+ // relative to page
+ nPcHorz = 2;
+ break;
+ }
+ sal_uInt8 nTPc = (nPcVert << 4) | (nPcHorz << 6);
+ m_rWW8Export.InsUInt16(NS_sprm::TPc::val);
+ m_rWW8Export.m_pO->push_back(nTPc);
+
+ // Similar to WW8AttributeOutput::FormatHorizOrientation(), but for tables.
+ sal_Int16 nTDxaAbs = 0;
+ switch (pFlyFormat->GetHoriOrient().GetHoriOrient())
+ {
+ case text::HoriOrientation::LEFT:
+ // left
+ nTDxaAbs = 0;
+ break;
+ case text::HoriOrientation::CENTER:
+ // centered
+ nTDxaAbs = -4;
+ break;
+ case text::HoriOrientation::RIGHT:
+ // right
+ nTDxaAbs = -8;
+ break;
+ default:
+ nTDxaAbs = pFlyFormat->GetHoriOrient().GetPos();
+ break;
+ }
+ m_rWW8Export.InsUInt16(NS_sprm::TDxaAbs::val);
+ m_rWW8Export.InsInt16(nTDxaAbs);
+
+ // Similar to WW8AttributeOutput::FormatVertOrientation(), but for tables.
+ sal_Int16 nTDyaAbs = 0;
+ switch (pFlyFormat->GetVertOrient().GetVertOrient())
+ {
+ case text::VertOrientation::TOP:
+ // up
+ nTDyaAbs = -4;
+ break;
+ case text::VertOrientation::CENTER:
+ // centered
+ nTDyaAbs = -8;
+ break;
+ case text::VertOrientation::BOTTOM:
+ // down
+ nTDyaAbs = -12;
+ break;
+ default:
+ nTDyaAbs = pFlyFormat->GetVertOrient().GetPos();
+ break;
+ }
+ m_rWW8Export.InsUInt16(NS_sprm::TDyaAbs::val);
+ m_rWW8Export.InsInt16(nTDyaAbs);
+
+ // Similar to WW8AttributeOutput::FormatULSpace(), but for tables.
+ sal_uInt16 nDyaFromText = pFlyFormat->GetULSpace().GetUpper();
+ m_rWW8Export.InsUInt16(NS_sprm::TDyaFromText::val);
+ m_rWW8Export.InsUInt16(nDyaFromText);
+ sal_uInt16 nDyaFromTextBottom = pFlyFormat->GetULSpace().GetLower();
+ m_rWW8Export.InsUInt16(NS_sprm::TDyaFromTextBottom::val);
+ m_rWW8Export.InsUInt16(nDyaFromTextBottom);
+
+ // Similar to WW8AttributeOutput::FormatLRSpace(), but for tables.
+ sal_uInt16 nDxaFromText = pFlyFormat->GetLRSpace().GetLeft();
+ m_rWW8Export.InsUInt16(NS_sprm::TDxaFromText::val);
+ m_rWW8Export.InsUInt16(nDxaFromText);
+ sal_uInt16 nDxaFromTextRight = pFlyFormat->GetLRSpace().GetRight();
+ m_rWW8Export.InsUInt16(NS_sprm::TDxaFromTextRight::val);
+ m_rWW8Export.InsUInt16(nDxaFromTextRight);
+
+ if (!pFlyFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap())
+ {
+ // Allowing overlap is the default in both Writer and in WW8.
+ m_rWW8Export.InsUInt16(NS_sprm::TFNoAllowOverlap::val);
+ m_rWW8Export.m_pO->push_back(1);
+ }
+}
+
+void WW8AttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTable * pTable = pTableTextNodeInfoInner->getTable();
+
+ if ( pTable->GetRowsToRepeat() > pTableTextNodeInfoInner->getRow() )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TTableHeader::val );
+ m_rWW8Export.m_pO->push_back( 1 );
+ }
+
+ ww8::TableBoxVectorPtr pTableBoxes =
+ pTableTextNodeInfoInner->getTableBoxesOfRow();
+ // number of cell written
+ sal_uInt32 nBoxes = pTableBoxes->size();
+ assert(nBoxes <= ww8::MAXTABLECELLS);
+
+ // sprm header
+ m_rWW8Export.InsUInt16( NS_sprm::TDefTable::val );
+ sal_uInt16 nSprmSize = 2 + (nBoxes + 1) * 2 + nBoxes * 20;
+ m_rWW8Export.InsUInt16( nSprmSize ); // length
+
+ // number of boxes
+ m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(nBoxes) );
+
+ /* cells */
+ /*
+ ALWAYS relative when text::HoriOrientation::NONE (nPageSize + ( nPageSize / 10 )) < nTableSz,
+ in that case the cell width's and table width's are not real. The table
+ width is maxed and cells relative, so we need the frame (generally page)
+ width that the table is in to work out the true widths.
+ */
+ //const bool bNewTableModel = pTable->IsNewModel();
+ const SwFrameFormat *pFormat = pTable->GetFrameFormat();
+ if ( !pFormat )
+ {
+ SAL_WARN( "sw.ww8", "FrameFormat is nil" );
+ return;
+ }
+
+ const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
+ const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
+
+ SwTwips nTableOffset = 0;
+
+ if (
+ (text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
+ text::RelOrientation::FRAME == rHori.GetRelationOrient())
+ &&
+ (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
+ text::RelOrientation::FRAME == rVert.GetRelationOrient())
+ )
+ {
+ sal_Int16 eHOri = rHori.GetHoriOrient();
+ switch ( eHOri )
+ {
+ case text::HoriOrientation::CENTER:
+ case text::HoriOrientation::RIGHT:
+ break;
+
+ default:
+ nTableOffset = rHori.GetPos();
+ const SvxLRSpaceItem& rLRSp = pFormat->GetLRSpace();
+ nTableOffset += rLRSp.GetLeft();
+
+ // convert offset to be measured from right margin in right-to-left tables
+ if ( nTableOffset && m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB )
+ {
+ SwTwips nLeftPageMargin, nRightPageMargin;
+ const SwTwips nPageSize = m_rWW8Export.CurrentPageWidth(nLeftPageMargin, nRightPageMargin);
+ const SwTwips nTableWidth = pFormat->GetFrameSize().GetWidth();
+ nTableOffset = nPageSize - nLeftPageMargin - nRightPageMargin - nTableWidth - nTableOffset;
+ }
+ break;
+ }
+ }
+
+ m_rWW8Export.InsInt16( nTableOffset );
+
+ ww8::GridColsPtr pGridCols = GetGridCols( pTableTextNodeInfoInner );
+ for ( const auto nCol : *pGridCols )
+ {
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nCol) + nTableOffset );
+ }
+
+ /* TCs */
+ ww8::RowSpansPtr pRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
+ ww8::RowSpans::const_iterator aItRowSpans = pRowSpans->begin();
+
+ for (const SwTableBox * pTabBox1 : *pTableBoxes)
+ {
+ sal_uInt16 npOCount = m_rWW8Export.m_pO->size();
+
+ const SwFrameFormat * pBoxFormat = nullptr;
+ if (pTabBox1 != nullptr)
+ pBoxFormat = pTabBox1->GetFrameFormat();
+
+ sal_uInt16 nFlags =
+ lcl_TCFlags(m_rWW8Export.m_rDoc, pTabBox1, *aItRowSpans);
+ m_rWW8Export.InsUInt16( nFlags );
+
+ static sal_uInt8 aNullBytes[] = { 0x0, 0x0 };
+
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aNullBytes, aNullBytes+2 ); // dummy
+ if (pBoxFormat != nullptr)
+ {
+ const SvxBoxItem & rBoxItem = pBoxFormat->GetBox();
+
+ WW8Export::Out_SwFormatTableBox( *m_rWW8Export.m_pO, &rBoxItem ); // 8/16 Byte
+ }
+ else
+ WW8Export::Out_SwFormatTableBox( *m_rWW8Export.m_pO, nullptr); // 8/16 Byte
+
+ SAL_INFO( "sw.ww8.level2", "<tclength>" << ( m_rWW8Export.m_pO->size() - npOCount ) << "</tclength>" );
+ ++aItRowSpans;
+ }
+
+ int nWidthPercent = pFormat->GetFrameSize().GetWidthPercent();
+
+ // The best fit for "automatic" table placement is relative 100%
+ if (!nWidthPercent && rHori.GetHoriOrient() == text::HoriOrientation::FULL)
+ nWidthPercent = 100;
+
+ // Width is in fiftieths of a percent. For sprmTTableWidth, must be non-negative and 600% max
+ if ( nWidthPercent > 0 && nWidthPercent <= 600 )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::TTableWidth::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8/*ftsPercent*/ (2) );
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nWidthPercent) * 50 );
+ }
+
+ // Write table positioning properties in case this is a floating table.
+ TablePositioning(pTable->GetTableNode()->GetFlyFormat());
+}
+
+ww8::GridColsPtr AttributeOutputBase::GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
+{
+ return pTableTextNodeInfoInner->getGridColsOfRow(*this);
+}
+
+ww8::WidthsPtr AttributeOutputBase::GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
+{
+ // Get the column widths based on ALL the rows, not just the current row
+ return pTableTextNodeInfoInner->getGridColsOfRow(*this, true);
+}
+
+void AttributeOutputBase::GetTablePageSize( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner, tools::Long& rPageSize, bool& rRelBoxSize )
+{
+ tools::Long nPageSize = 0;
+
+ const SwNode *pTextNd = pTableTextNodeInfoInner->getNode( );
+ const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
+
+ const SwFrameFormat *pFormat = pTable->GetFrameFormat();
+ if ( !pFormat )
+ {
+ SAL_WARN( "sw.ww8", "FrameFormat is nil" );
+ return;
+ }
+
+ const SwFormatFrameSize &rSize = pFormat->GetFrameSize();
+ int nWidthPercent = rSize.GetWidthPercent();
+ bool bManualAligned = pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::NONE;
+ if ( (pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::FULL) || bManualAligned )
+ nWidthPercent = 100;
+ bool bRelBoxSize = nWidthPercent != 0;
+ tools::ULong nTableSz = static_cast<tools::ULong>(rSize.GetWidth());
+ if (nTableSz > USHRT_MAX/2 && !bRelBoxSize)
+ {
+ OSL_ENSURE(bRelBoxSize, "huge table width but not relative, suspicious");
+ bRelBoxSize = true;
+ }
+
+ if ( bRelBoxSize )
+ {
+ Point aPt;
+ SwRect aRect( pFormat->FindLayoutRect( false, &aPt ) );
+ if ( aRect.IsEmpty() )
+ {
+ // Then fetch the page width without margins!
+ const SwFrameFormat* pParentFormat =
+ GetExport().m_pParentFrame ?
+ &(GetExport().m_pParentFrame->GetFrameFormat()) :
+ GetExport().m_rDoc.GetPageDesc(0).GetPageFormatOfNode(*pTextNd, false);
+ aRect = pParentFormat->FindLayoutRect(true);
+ nPageSize = aRect.Width();
+ if ( 0 == nPageSize )
+ {
+ const SvxLRSpaceItem& rLR = pParentFormat->GetLRSpace();
+ nPageSize = pParentFormat->GetFrameSize().GetWidth() - rLR.GetLeft()
+ - rLR.GetRight();
+ }
+ }
+ else
+ {
+ nPageSize = aRect.Width();
+ if ( bManualAligned )
+ {
+ // #i37571# For manually aligned tables
+ const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
+ nPageSize -= (rLR.GetLeft() + rLR.GetRight());
+ }
+
+ }
+
+ if ( nWidthPercent )
+ {
+ nPageSize *= nWidthPercent;
+ nPageSize /= 100;
+ }
+ else
+ SAL_WARN( "sw.ww8", "nWidthPercent is zero" );
+ }
+ else
+ {
+ // As the table width is not relative, the TablePageSize equals its width
+ nPageSize = nTableSz;
+ }
+
+ rPageSize = nPageSize;
+ rRelBoxSize = bRelBoxSize;
+}
+
+void WW8AttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ // This function name is misleading because it is not a table default, but a row default,
+ // and it also only sets default cell margins (aka border padding).
+ // The specs suggest there is no way to define default border lines/colors.
+ const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
+
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+
+ // Set row default cell margins using this last cell in the row
+ for ( int i = 0; i < 4; ++i )
+ {
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::TCellPaddingDefault::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(6) );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0) );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(1) );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(1 << i) );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(3) );
+
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO,
+ pFrameFormat->GetBox().GetDistance( aBorders[i] ) );
+ }
+}
+
+void WW8AttributeOutput::TableCellBorders(
+ ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
+{
+ const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
+ sal_uInt8 nBoxes = std::min<size_t>(rTabBoxes.size(), 255);
+ const SvxBoxItem * pLastBox = nullptr;
+ sal_uInt8 nSeqStart = 0; // start of sequence of cells with same borders
+
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+
+ sal_uInt16 nDefaultMargin[4] = {31681, 31681, 31681, 31681}; // outside of documented valid range
+ // last column in each row defines the row default in TableRowDefaultBorders()
+ if ( nBoxes && rTabBoxes.size() == nBoxes )
+ {
+ const SvxBoxItem& rBox = rTabBoxes[ nBoxes-1 ]->GetFrameFormat()->GetBox();
+ for ( int i = 0; i < 4; ++i )
+ nDefaultMargin[i] = rBox.GetDistance( aBorders[i] );
+ }
+
+ // Detect sequences of cells which have the same borders, and output
+ // a border description for each such cell range.
+ for ( unsigned n = 0; n <= nBoxes; ++n )
+ {
+ const SvxBoxItem * pBox = (n == nBoxes) ? nullptr :
+ &rTabBoxes[n]->GetFrameFormat()->GetBox();
+ if( !pLastBox )
+ pLastBox = pBox;
+ else if( !pBox || *pLastBox != *pBox )
+ {
+ // This cell has different borders than the previous cell,
+ // so output the borders for the preceding cell range.
+ m_rWW8Export.Out_CellRangeBorders(pLastBox, nSeqStart, n);
+
+ // The last column is used as the row default for margins, so we can ignore these matching ones
+ if ( n == nBoxes )
+ break;
+
+ // Output cell margins.
+ // One CSSA can define up to all four margins if they are the same size value.
+ sal_uInt16 nMargin[4];
+ sal_uInt8 nSideBits[4] = {0, 0, 0, 0}; // 0001:top, 0010:left, 0100:bottom, 1000:right
+ for ( int i = 0; i < 4; ++i ) // sides: top, left, bottom, right
+ {
+ nMargin[i] = std::min(sal_Int16(31680), pLastBox->GetDistance( aBorders[i] ));
+ if ( nMargin[i] == nDefaultMargin[i] )
+ continue;
+
+ // join a previous side's definition if it shares the same value
+ for ( int p = 0; p < 4; ++p )
+ {
+ if ( nMargin[i] == nMargin[p] )
+ {
+ nSideBits[p] |= 1 << i;
+ break;
+ }
+ }
+ }
+
+ // write out the cell margins definitions that were used
+ for ( int i = 0; i < 4; ++i )
+ {
+ if ( nSideBits[i] )
+ {
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::TCellPadding::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(6) ); // 6 bytes
+ m_rWW8Export.m_pO->push_back( sal_uInt8(nSeqStart) ); // first cell: apply margin
+ m_rWW8Export.m_pO->push_back( sal_uInt8(n) ); // end cell: do not apply margin
+ m_rWW8Export.m_pO->push_back( sal_uInt8(nSideBits[i]) );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(3) ); // FtsDxa: size in twips
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nMargin[i] );
+ }
+ }
+
+ nSeqStart = n;
+ pLastBox = pBox;
+ }
+ }
+}
+
+void WW8AttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
+{
+ const SwTable * pTab = pTableTextNodeInfoInner->getTable();
+ const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ const SwTableLine * pTabLine = pTabBox->GetUpper();
+ const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
+
+ sal_uInt8 nBoxes = rTabBoxes.size();
+ m_rWW8Export.InsUInt16( NS_sprm::TDefTableShd80::val );
+ m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(nBoxes * 2) ); // Len
+
+ Color aRowColor = COL_AUTO;
+ const SvxBrushItem *pTableColorProp = pTab->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if ( pTableColorProp )
+ aRowColor = pTableColorProp->GetColor();
+
+ const SvxBrushItem *pRowColorProp = pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if ( pRowColorProp && pRowColorProp->GetColor() != COL_AUTO )
+ aRowColor = pRowColorProp->GetColor();
+
+ for ( sal_uInt8 n = 0; n < nBoxes; n++ )
+ {
+ const SwTableBox * pBox1 = rTabBoxes[n];
+ const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
+ Color aColor = aRowColor;
+
+ const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
+ aColor = pCellColorProp->GetColor();
+
+ WW8_SHD aShd;
+ WW8Export::TransBrush( aColor, aShd );
+ m_rWW8Export.InsUInt16( aShd.GetValue() );
+ }
+
+/*sprmTDefTableShdRaw:
+ * A DefTableShdOperand value that specifies the ... shading for cells 1 up to 22 in the row,
+ * ... Cells 23 to 44 are shaded by sprmTDefTableShdRaw2nd,
+ * and cells 45 to 63 are shaded by sprmTDefTableShdRaw3rd.
+ */
+ sal_uInt32 const aSprmIds[] { NS_sprm::TDefTableShd::val,
+ NS_sprm::TDefTableShdRaw::val,
+ NS_sprm::TDefTableShdRaw::val,
+ NS_sprm::TDefTableShd2nd::val,
+ NS_sprm::TDefTableShdRaw2nd::val,
+ NS_sprm::TDefTableShd3rd::val,
+ NS_sprm::TDefTableShdRaw3rd::val };
+ for (sal_uInt32 m : aSprmIds)
+ {
+ sal_uInt8 nStart = 0;
+ sal_uInt8 nStop = rTabBoxes.size();
+ switch ( m )
+ {
+ case NS_sprm::TDefTableShd::val:
+ case NS_sprm::TDefTableShdRaw::val:
+ if ( nStop > 21 )
+ nStop = 22;
+ break;
+ case NS_sprm::TDefTableShd2nd::val:
+ case NS_sprm::TDefTableShdRaw2nd::val:
+ nStart = 22;
+ if ( nStop > 43 )
+ nStop = 44;
+ break;
+ case NS_sprm::TDefTableShd3rd::val:
+ case NS_sprm::TDefTableShdRaw3rd::val:
+ nStart = 44;
+ if ( nStop > 62 )
+ nStop = 63;
+ break;
+ }
+ if ( nStart >= nStop )
+ break;
+
+ m_rWW8Export.InsUInt16( m );
+ m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>((nStop-nStart) * 10) );
+
+ for ( sal_uInt8 n = nStart; n < nStop; n++ )
+ {
+ const SwTableBox * pBox1 = rTabBoxes[n];
+ const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
+ Color aColor = aRowColor;
+
+ const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
+ if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
+ aColor = pCellColorProp->GetColor();
+
+ WW8SHDLong aSHD;
+ aSHD.setCvFore( 0xFF000000 );
+
+ if ( aColor == COL_AUTO )
+ aSHD.setCvBack( 0xFF000000 );
+ else
+ aSHD.setCvBack( wwUtility::RGBToBGR( aColor ) );
+
+ aSHD.Write( m_rWW8Export );
+ }
+ }
+}
+
+void WW8Export::SectionBreaksAndFrames( const SwTextNode& rNode )
+{
+ // output page/section breaks
+ OutputSectionBreaks( rNode.GetpSwAttrSet(), rNode );
+}
+
+namespace {
+
+class TrackContentToExport
+{
+private:
+ SwPaM *m_pCurPam;
+ SwNodeOffset m_nStart, m_nEnd;
+public:
+ TrackContentToExport(SwPaM *pCurPam, SwNodeOffset nCurStart, SwNodeOffset nCurEnd)
+ : m_pCurPam(pCurPam)
+ , m_nStart(nCurStart)
+ , m_nEnd(nCurEnd)
+ {
+ }
+
+ bool contentRemainsToExport(ww8::WW8TableInfo *pTableInfo)
+ {
+ bool bSimpleContentRemains = m_pCurPam->GetPoint()->GetNode() < m_pCurPam->GetMark()->GetNode() ||
+ (m_pCurPam->GetPoint()->GetNode() == m_pCurPam->GetMark()->GetNode() &&
+ m_pCurPam->GetPoint()->GetContentIndex() <= m_pCurPam->GetMark()->GetContentIndex());
+ if (bSimpleContentRemains)
+ return true;
+
+ if (!pTableInfo)
+ return false;
+
+ //An old-school table where one cell may points back to a previous node as the next cell
+ //so if this node is the last node in the range, we may need to jump back to a previously
+ //skipped cell to output it in a sane sequence. See ooo47778-3.sxw for one of these
+ //horrors. So if we are at the end of the selection, but this end point is a table
+ //cell whose next cell is in the selection allow jumping back to it
+ const SwNode* pCurrentNode = &m_pCurPam->GetPoint()->GetNode();
+ const SwNode* pNextNode = pTableInfo->getNextNode(pCurrentNode);
+
+ if (pNextNode && pCurrentNode != pNextNode)
+ {
+ return pNextNode->GetIndex() >= m_nStart &&
+ pNextNode->GetIndex() < m_nEnd;
+ }
+
+ return false;
+ }
+};
+
+}
+
+void MSWordExportBase::WriteText()
+{
+ TrackContentToExport aContentTracking(m_pCurPam.get(), m_nCurStart, m_nCurEnd);
+ while (aContentTracking.contentRemainsToExport(m_pTableInfo.get()))
+ {
+ SwNode& rNd = m_pCurPam->GetPointNode();
+
+ // no section breaks exported for Endnotes
+ if ( rNd.IsTextNode() && m_nTextTyp != TXT_EDN && m_nTextTyp != TXT_FTN )
+ {
+ SwSoftPageBreakList breakList;
+ // if paragraph need to be split than handle section break somewhere
+ // else.
+ if( !NeedTextNodeSplit( *rNd.GetTextNode(), breakList) )
+ SectionBreaksAndFrames( *rNd.GetTextNode() );
+ }
+
+
+ // output the various types of nodes
+ if ( rNd.IsContentNode() )
+ {
+ SwContentNode* pCNd = static_cast<SwContentNode*>(&rNd);
+
+ const SwPageDesc* pTemp = rNd.FindPageDesc();
+ if ( pTemp )
+ m_pCurrentPageDesc = pTemp;
+
+ m_pCurPam->GetPoint()->SetContent( 0 );
+ OutputContentNode( *pCNd );
+ }
+ else if ( rNd.IsTableNode() )
+ {
+ m_pTableInfo->processSwTable( &rNd.GetTableNode()->GetTable() );
+ }
+ else if ( rNd.IsSectionNode() && TXT_MAINTEXT == m_nTextTyp )
+ OutputSectionNode( *rNd.GetSectionNode() );
+ else if ( TXT_MAINTEXT == m_nTextTyp && rNd.IsEndNode() &&
+ rNd.StartOfSectionNode()->IsSectionNode() )
+ {
+ const SwSection& rSect = rNd.StartOfSectionNode()->GetSectionNode()
+ ->GetSection();
+ if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() )
+ m_bStartTOX = false;
+
+ SwNodeIndex aIdx( rNd, 1 );
+ if ( aIdx.GetNode().IsEndNode() && aIdx.GetNode().StartOfSectionNode()->IsSectionNode() )
+ ;
+ else if ( aIdx.GetNode().IsSectionNode() )
+ ;
+ else if ( !IsInTable() ) //No sections in table
+ {
+ //#120140# Do not need to insert a page/section break after a section end. Check this case first
+ bool bNeedExportBreakHere = true;
+ if ( rSect.GetType() == SectionType::ToxContent || rSect.GetType() == SectionType::ToxHeader )
+ bNeedExportBreakHere = false;
+ else if ( aIdx.GetNode().IsTextNode() )
+ {
+ SwTextNode *pTempNext = aIdx.GetNode().GetTextNode();
+ if ( pTempNext )
+ {
+ const SwFormatPageDesc * pTempItem = nullptr;
+ if (pTempNext->GetpSwAttrSet()
+ && (pTempItem = pTempNext->GetpSwAttrSet()->GetItemIfSet(RES_PAGEDESC, false))
+ && pTempItem->GetRegisteredIn())
+ {
+ //Next node has a new page style which means this node is a section end. Do not insert another page/section break here
+ bNeedExportBreakHere = false;
+ }
+ }
+ }
+ else
+ {
+ /* Do not export Section Break in case DOCX containing MultiColumn and
+ * aIdx.GetNode().IsTextNode() is False i.e. Text node is NULL.
+ */
+ const SwFrameFormat* pPgFormat = rSect.GetFormat();
+ const SwFormatCol& rCol = pPgFormat->GetCol();
+ sal_uInt16 nColumnCount = rCol.GetNumCols();
+ const SwFormatNoBalancedColumns& rNoBalanced = pPgFormat->GetBalancedColumns();
+ // Prevent the additional section break only for non-balanced columns.
+ if (nColumnCount > 1 && rNoBalanced.GetValue())
+ {
+ bNeedExportBreakHere = false;
+ }
+ // No need to create a "fake" section if this is the end of the document,
+ // except to emulate balanced columns.
+ else if ( nColumnCount < 2 && aIdx == m_rDoc.GetNodes().GetEndOfContent() )
+ bNeedExportBreakHere = false;
+ }
+
+ if (bNeedExportBreakHere) //#120140# End of check
+ {
+ ReplaceCr( char(0xc) ); // indicator for Page/Section-Break
+
+ const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent();
+ if ( !pParentFormat )
+ pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1));
+
+ sal_uLong nRstLnNum;
+ if ( aIdx.GetNode().IsContentNode() )
+ nRstLnNum = static_cast<SwContentNode&>(aIdx.GetNode()).GetSwAttrSet().
+ GetLineNumber().GetStartValue();
+ else
+ nRstLnNum = 0;
+
+ AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum );
+ }
+ else
+ {
+ OutputEndNode( *rNd.GetEndNode() );
+ }
+ }
+ }
+ else if ( rNd.IsStartNode() )
+ {
+ OutputStartNode( *rNd.GetStartNode() );
+ }
+ else if ( rNd.IsEndNode() )
+ {
+ OutputEndNode( *rNd.GetEndNode() );
+ }
+
+ if ( &rNd == &rNd.GetNodes().GetEndOfContent() )
+ break;
+
+ const SwNode * pCurrentNode = &m_pCurPam->GetPoint()->GetNode();
+ const SwNode * pNextNode = m_pTableInfo->getNextNode(pCurrentNode);
+
+ if (pCurrentNode == pNextNode)
+ {
+ SAL_WARN("sw.ww8", "loop in TableInfo");
+ pNextNode = nullptr;
+ }
+
+ if (pNextNode != nullptr)
+ m_pCurPam->GetPoint()->Assign(*pNextNode);
+ else
+ m_pCurPam->GetPoint()->Adjust(SwNodeOffset(1));
+
+ SwNodeOffset nPos = m_pCurPam->GetPoint()->GetNodeIndex();
+ ::SetProgressState( sal_Int32(nPos), m_pCurPam->GetDoc().GetDocShell() );
+ }
+
+ SAL_INFO( "sw.ww8.level2", "</WriteText>" );
+}
+
+void WW8Export::WriteMainText()
+{
+ SAL_INFO( "sw.ww8.level2", "<WriteMainText>" );
+
+ m_pFib->m_fcMin = Strm().Tell();
+
+ m_pCurPam->GetPoint()->Assign(*m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode());
+
+ WriteText();
+
+ if( 0 == Strm().Tell() - m_pFib->m_fcMin ) // no text ?
+ WriteCR(); // then CR at the end ( otherwise WW will complain )
+
+ m_pFib->m_ccpText = Fc2Cp( Strm().Tell() );
+ m_pFieldMain->Finish( m_pFib->m_ccpText, 0 );
+
+ // ccpText includes Footnote and KF-text
+ // therefore pFib->ccpText may get updated as well
+ // save the StyleId of the last paragraph. Because WW97 take the style
+ // from the last CR, that will be written after footer/Header/footnotes/
+ // annotation etc.
+ const SwTextNode* pLastNd = m_pCurPam->GetMark()->GetNode().GetTextNode();
+ if( pLastNd )
+ m_nLastFormatId = GetId( static_cast<SwTextFormatColl&>(pLastNd->GetAnyFormatColl()) );
+
+ SAL_INFO( "sw.ww8.level2", "</WriteMainText>" );
+}
+
+bool MSWordExportBase::IsInTable() const
+{
+ bool bResult = false;
+
+ if (m_pCurPam != nullptr)
+ {
+ SwNode& rNode = m_pCurPam->GetPointNode();
+
+ if (m_pTableInfo)
+ {
+ ww8::WW8TableNodeInfo::Pointer_t pTableNodeInfo = m_pTableInfo->getTableNodeInfo(&rNode);
+
+ if (pTableNodeInfo && pTableNodeInfo->getDepth() > 0)
+ {
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+typedef ww8::WW8Sttb< ww8::WW8Struct > WW8SttbAssoc;
+
+void WW8Export::WriteFkpPlcUsw()
+{
+ // Graphics in the data stream
+ m_pGrf->Write(); // Graphics
+
+ // output into WordDocument stream
+ m_pChpPlc->WriteFkps(); // Fkp.Chpx
+ m_pPapPlc->WriteFkps(); // Fkp.Papx
+ m_pSepx->WriteSepx( Strm() ); // Sepx
+
+ // output into Table stream
+ m_pStyles->OutputStylesTable(); // for WW8 StyleTab
+ m_pFootnote->WritePlc( *this ); // Footnote-Ref & Text Plc
+ m_pEdn->WritePlc( *this ); // Endnote-Ref & Text Plc
+ m_pTextBxs->WritePlc( *this ); // Textbox Text Plc
+ m_pHFTextBxs->WritePlc( *this ); // Head/Foot-Textbox Text Plc
+ m_pAtn->WritePlc( *this ); // Annotation-Ref & Text Plc
+
+ m_pSepx->WritePlcSed( *this ); // Slcx.PlcSed
+ m_pSepx->WritePlcHdd( *this ); // Slcx.PlcHdd
+
+ m_pChpPlc->WritePlc(); // Plcx.Chpx
+ m_pPapPlc->WritePlc(); // Plcx.Papx
+
+ if( m_pRedlAuthors )
+ m_pRedlAuthors->Write( GetWriter() ); // sttbfRMark (RedlineAuthors)
+ m_pFieldMain->Write( *this ); // Fields ( Main Text )
+ m_pFieldHdFt->Write( *this ); // Fields ( Header/Footer )
+ m_pFieldFootnote->Write( *this ); // Fields ( FootNotes )
+ m_pFieldEdn->Write( *this ); // Fields ( EndNotes )
+ m_pFieldAtn->Write( *this ); // Fields ( Annotations )
+ m_pFieldTextBxs->Write( *this ); // Fields ( Textboxes )
+ m_pFieldHFTextBxs->Write( *this ); // Fields ( Head/Foot-Textboxes )
+
+ if (m_pEscher || m_rDoc.ContainsMSVBasic())
+ {
+ /*
+ Every time MS 2000 creates an escher stream there is always
+ an ObjectPool dir (even if empty). It turns out that if a copy of
+ MS 2000 is used to open a document that contains escher graphics
+ exported from StarOffice without this empty dir then *if* that
+ copy of MS Office has never been used to open a MSOffice document
+ that has escher graphics (and an ObjectPool dir of course) and
+ that copy of office has not been used to draw escher graphics then
+ our exported graphics do not appear. Once you do open a ms
+ document with escher graphics or draw an escher graphic with that
+ copy of word, then all documents from staroffice that contain
+ escher work from then on. Tricky to track down, some sort of late
+ binding trickery in MS where solely for first time initialization
+ the existence of an ObjectPool dir is necessary for triggering
+ some magic.
+ */
+ // avoid memory leak #i120098#, the unnamed obj will be released in destructor.
+ m_xEscherStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
+ }
+
+ // dggInfo - escher stream
+ WriteEscher();
+
+ m_pSdrObjs->WritePlc( *this );
+ m_pHFSdrObjs->WritePlc( *this );
+ // spamom - office drawing table
+ // spahdr - header office drawing table
+
+ m_pBkmks->Write( *this ); // Bookmarks - sttbfBkmk/
+ // plcfBkmkf/plcfBkmkl
+ m_pFactoids->Write(*this);
+
+ WriteNumbering();
+
+ RestoreMacroCmds();
+
+ m_pMagicTable->Write( *this );
+
+ m_pPiece->WritePc( *this ); // Piece-Table
+ m_aFontHelper.WriteFontTable(m_pTableStrm, *m_pFib); // FFNs
+
+ //Convert OOo asian typography into MS typography structure
+ ExportDopTypography(m_pDop->doptypography);
+
+ WriteDop( *this ); // Document-Properties
+
+ // Write SttbfAssoc
+ WW8SttbAssoc * pSttbfAssoc = dynamic_cast<WW8SttbAssoc *>
+ (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::STTBF_ASSOC).get());
+
+ if ( pSttbfAssoc ) // #i106057#
+ {
+ std::vector<OUString> aStrings(pSttbfAssoc->getStrings());
+ WriteAsStringTable(aStrings, m_pFib->m_fcSttbfAssoc,
+ m_pFib->m_lcbSttbfAssoc);
+ }
+
+ Strm().Seek( 0 );
+
+ // Reclaim stored FIB data from document.
+ ::ww8::WW8FibData * pFibData = dynamic_cast<ww8::WW8FibData *>
+ (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::FIB).get());
+
+ if ( pFibData )
+ {
+ m_pFib->m_fReadOnlyRecommended =
+ pFibData->getReadOnlyRecommended();
+ m_pFib->m_fWriteReservation =
+ pFibData->getWriteReservation();
+ }
+
+ m_pFib->Write( Strm() ); // FIB
+}
+
+void WW8Export::StoreDoc1()
+{
+ bool bNeedsFinalPara = false;
+ // Start of Text ( overwrite )
+ SwWW8Writer::FillUntil( Strm(), m_pFib->m_fcMin );
+
+ WriteMainText(); // main text
+ sal_uInt8 nSprmsLen;
+ sal_uInt8 *pLastSprms = m_pPapPlc->CopyLastSprms(nSprmsLen);
+
+ bNeedsFinalPara |= m_pFootnote->WriteText( *this ); // Footnote-Text
+ bNeedsFinalPara |= m_pSepx->WriteKFText( *this ); // K/F-Text
+ bNeedsFinalPara |= m_pAtn->WriteText( *this ); // Annotation-Text
+ bNeedsFinalPara |= m_pEdn->WriteText( *this ); // EndNote-Text
+
+ // create the escher streams
+ CreateEscher();
+
+ bNeedsFinalPara |= m_pTextBxs->WriteText( *this ); //Textbox Text Plc
+ bNeedsFinalPara |= m_pHFTextBxs->WriteText( *this );//Head/Foot-Textbox Text Plc
+
+ if (bNeedsFinalPara)
+ {
+ WriteCR();
+ m_pPapPlc->AppendFkpEntry(Strm().Tell(), nSprmsLen, pLastSprms);
+ }
+ delete[] pLastSprms;
+
+ m_pSepx->Finish( Fc2Cp( Strm().Tell() ));// Text + Footnote + HdFt as section end
+ m_pMagicTable->Finish( Fc2Cp( Strm().Tell() ),0);
+
+ m_pFib->m_fcMac = Strm().Tell(); // End of all texts
+
+ WriteFkpPlcUsw(); // FKP, PLC, ...
+}
+
+void MSWordExportBase::AddLinkTarget(std::u16string_view rURL)
+{
+ if( rURL.empty() || rURL[0] != '#' )
+ return;
+
+ OUString aURL( BookmarkToWriter( rURL.substr( 1 ) ) );
+ sal_Int32 nPos = aURL.lastIndexOf( cMarkSeparator );
+
+ if( nPos < 2 )
+ return;
+
+ OUString sCmp = aURL.copy(nPos+1).replaceAll(" ", "");
+ if( sCmp.isEmpty() )
+ return;
+
+ sCmp = sCmp.toAsciiLowerCase();
+ if( sCmp == "outline" )
+ {
+ SwPosition aPos(*m_pCurPam->GetPoint());
+ OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
+ // If we can find the outline this bookmark refers to
+ // save the name of the bookmark and the
+ // node index number of where it points to
+ if( m_rDoc.GotoOutline( aPos, aName ) )
+ m_aImplicitBookmarks.emplace_back(aURL, aPos.GetNodeIndex());
+ }
+ else if( sCmp == "graphic" )
+ {
+ OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
+ if (const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Grf))
+ if (const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx())
+ m_aImplicitBookmarks.emplace_back(aURL, pIdx->GetNext()->GetIndex());
+ }
+ else if( sCmp == "frame" )
+ {
+ OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
+ if (const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Text))
+ if (const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx())
+ m_aImplicitBookmarks.emplace_back(aURL, pIdx->GetIndex() + 1);
+ }
+ else if( sCmp == "ole" )
+ {
+ OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
+ if (const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Ole))
+ if (const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx())
+ m_aImplicitBookmarks.emplace_back(aURL, pIdx->GetNext()->GetIndex());
+ }
+ else if( sCmp == "region" )
+ {
+ OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
+ for (const SwSectionFormat* pFormat : m_rDoc.GetSections())
+ {
+ if (aName == pFormat->GetSection()->GetSectionName())
+ {
+ if (const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx())
+ {
+ m_aImplicitBookmarks.emplace_back(aURL, pIdx->GetIndex() + 1);
+ break;
+ }
+ }
+ }
+ }
+ else if( sCmp == "table" )
+ {
+ OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
+ if (const SwTable* pTable = SwTable::FindTable(m_rDoc.FindTableFormatByName(aName)))
+ if (const SwTableNode* pTableNode = pTable->GetTabSortBoxes()[1]->GetSttNd()->FindTableNode())
+ m_aImplicitBookmarks.emplace_back(aURL, pTableNode->GetIndex() + 2);
+ }
+ else if (sCmp == "toxmark")
+ {
+ OUString const name(aURL.copy(0, nPos));
+ OUString const nameDecoded(INetURLObject::decode(name,
+ INetURLObject::DecodeMechanism::WithCharset));
+ if (const auto tmp = sw::PrepareJumpToTOXMark(m_rDoc, nameDecoded))
+ {
+ SwTOXMark const* pMark(&tmp->first);
+ for (sal_Int32 i = 0; i < tmp->second; ++i)
+ {
+ pMark = &m_rDoc.GotoTOXMark(*pMark, TOX_SAME_NXT, true);
+ }
+ if (!SfxPoolItem::areSame(pMark, &tmp->first))
+ {
+ m_TOXMarkBookmarksByURL.emplace(aURL, name);
+ m_TOXMarkBookmarksByTOXMark.emplace(pMark, nameDecoded);
+ }
+ }
+ }
+}
+
+void MSWordExportBase::CollectOutlineBookmarks(const SwDoc &rDoc)
+{
+ for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if (!pINetFormat)
+ continue;
+
+ const SwTextINetFormat* pTextAttr = pINetFormat->GetTextINetFormat();
+ if (!pTextAttr)
+ continue;
+
+ const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
+ if (!pTextNd)
+ continue;
+
+ if (!pTextNd->GetNodes().IsDocNodes())
+ continue;
+
+ AddLinkTarget( pINetFormat->GetValue() );
+ }
+
+ for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_URL))
+ {
+ auto pURL = dynamic_cast<const SwFormatURL*>(pItem);
+ if (!pURL)
+ continue;
+
+ AddLinkTarget(pURL->GetURL());
+ const ImageMap *pIMap = pURL->GetMap();
+ if (!pIMap)
+ continue;
+
+ for (size_t i=0; i < pIMap->GetIMapObjectCount(); ++i)
+ {
+ const IMapObject* pObj = pIMap->GetIMapObject(i);
+ if (!pObj)
+ continue;
+ AddLinkTarget( pObj->GetURL() );
+ }
+ }
+}
+
+namespace
+{
+ const sal_uInt64 WW_BLOCKSIZE = 0x200;
+
+ ErrCode EncryptRC4(msfilter::MSCodec_Std97& rCtx, SvStream &rIn, SvStream &rOut)
+ {
+ sal_uInt64 nLen = rIn.TellEnd();
+ rIn.Seek(0);
+
+ sal_uInt8 in[WW_BLOCKSIZE];
+ for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
+ {
+ std::size_t nBS = std::min(nLen - nI, WW_BLOCKSIZE);
+ nBS = rIn.ReadBytes(in, nBS);
+ if (!rCtx.InitCipher(nBlock)) {
+ return ERRCODE_IO_NOTSUPPORTED;
+ }
+ rCtx.Encode(in, nBS, in, nBS);
+ rOut.WriteBytes(in, nBS);
+ }
+ return ERRCODE_NONE;
+ }
+}
+
+ErrCode MSWordExportBase::ExportDocument( bool bWriteAll )
+{
+ m_nCharFormatStart = DEFAULT_STYLES_COUNT;
+ m_nFormatCollStart = m_nCharFormatStart + m_rDoc.GetCharFormats()->size() - 1;
+
+ m_bStyDef = m_bBreakBefore = m_bOutKF =
+ m_bOutFlyFrameAttrs = m_bOutPageDescs = m_bOutTable = m_bOutFirstPage =
+ m_bOutGrf = m_bInWriteEscher = m_bStartTOX =
+ m_bInWriteTOX = false;
+
+ m_bFootnoteAtTextEnd = m_bEndAtTextEnd = true;
+
+ m_pParentFrame = nullptr;
+ m_pFlyOffset = nullptr;
+ m_eNewAnchorType = RndStdIds::FLY_AT_PAGE;
+ m_nTextTyp = TXT_MAINTEXT;
+ m_nStyleBeforeFly = m_nLastFormatId = 0;
+ m_pStyAttr = nullptr;
+ m_pCurrentStyle = nullptr;
+ m_pOutFormatNode = nullptr;
+ m_pEscher = nullptr;
+ m_pRedlAuthors = nullptr;
+ m_aTOXArr.clear();
+
+ if ( !m_oOLEExp )
+ {
+ sal_uInt32 nSvxMSDffOLEConvFlags = 0;
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ if ( rOpt.IsMath2MathType() )
+ nSvxMSDffOLEConvFlags |= OLE_STARMATH_2_MATHTYPE;
+ if ( rOpt.IsWriter2WinWord() )
+ nSvxMSDffOLEConvFlags |= OLE_STARWRITER_2_WINWORD;
+ if ( rOpt.IsCalc2Excel() )
+ nSvxMSDffOLEConvFlags |= OLE_STARCALC_2_EXCEL;
+ if ( rOpt.IsImpress2PowerPoint() )
+ nSvxMSDffOLEConvFlags |= OLE_STARIMPRESS_2_POWERPOINT;
+
+ m_oOLEExp.emplace( nSvxMSDffOLEConvFlags );
+ }
+
+ if ( !m_pOCXExp && m_rDoc.GetDocShell() )
+ m_pOCXExp.reset(new SwMSConvertControls(m_rDoc.GetDocShell(), m_pCurPam.get()));
+
+ // #i81405# - Collect anchored objects before changing the redline mode.
+ m_aFrames = GetFrames( m_rDoc, bWriteAll? nullptr : m_pOrigPam );
+
+ m_nOrigRedlineFlags = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+
+ SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
+ m_bOrigShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines();
+
+ if ( !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ //restored to original state by SwWriter::Write
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags |
+ RedlineFlags::ShowDelete |
+ RedlineFlags::ShowInsert);
+ }
+
+ // fix the SwPositions in m_aFrames after SetRedlineFlags
+ UpdateFramePositions(m_aFrames);
+
+ m_aFontHelper.InitFontTable(m_rDoc);
+ GatherChapterFields();
+
+ CollectOutlineBookmarks(m_rDoc);
+
+ // make unique OrdNums (Z-Order) for all drawing-/fly Objects
+ if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )->RecalcObjOrdNums();
+
+ ErrCode err = ExportDocument_Impl();
+
+ m_aFrames.clear();
+
+ // park m_pCurPam in a "safe place" now that document is fully exported
+ // before toggling redline mode to avoid ~SwContentIndexReg assert e.g. export
+ // ooo103014-1.odt to .doc
+ // park m_pOrigPam as well, as needed for exporting abi9915-1.odt to doc
+ m_pOrigPam->DeleteMark();
+ m_pOrigPam->GetPoint()->Assign(m_rDoc.GetNodes().GetEndOfContent());
+ static_cast<SwPaM&>(*m_pCurPam) = *m_pOrigPam;
+
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags);
+
+ return err;
+}
+
+bool SwWW8Writer::InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec )
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+
+ if ( mpMedium )
+ {
+ const SfxUnoAnyItem* pEncryptionDataItem = mpMedium->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem && ( pEncryptionDataItem->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
+ {
+ OSL_ENSURE( false, "Unexpected EncryptionData!" );
+ aEncryptionData.realloc( 0 );
+ }
+
+ if ( !aEncryptionData.hasElements() )
+ {
+ // try to generate the encryption data based on password
+ const SfxStringItem* pPasswordItem = mpMedium->GetItemSet().GetItem(SID_PASSWORD, false);
+ if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() && pPasswordItem->GetValue().getLength() <= 15 )
+ {
+ // Generate random number with a seed of time as salt.
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ sal_uInt8 pDocId[ 16 ];
+ rtl_random_getBytes( aRandomPool, pDocId, 16 );
+
+ rtl_random_destroyPool( aRandomPool );
+
+ sal_uInt16 aPassword[16] = {};
+
+ const OUString& sPassword(pPasswordItem->GetValue());
+ for ( sal_Int32 nChar = 0; nChar < sPassword.getLength(); ++nChar )
+ aPassword[nChar] = sPassword[nChar];
+
+ rCodec.InitKey( aPassword, pDocId );
+ aEncryptionData = rCodec.GetEncryptionData();
+
+ mpMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+ }
+ }
+
+ if ( aEncryptionData.hasElements() )
+ mpMedium->GetItemSet().ClearItem( SID_PASSWORD );
+ }
+
+ // nonempty encryption data means here that the codec was successfully initialized
+ return aEncryptionData.hasElements();
+}
+
+ErrCode WW8Export::ExportDocument_Impl()
+{
+ PrepareStorage();
+
+ m_pFib.reset(new WW8Fib(8, m_bDot));
+
+ tools::SvRef<SotStorageStream> xWwStrm( GetWriter().GetStorage().OpenSotStream( m_aMainStg ) );
+ tools::SvRef<SotStorageStream> xTableStrm( xWwStrm ), xDataStrm( xWwStrm );
+ xWwStrm->SetBufferSize( 32768 );
+
+ m_pFib->m_fWhichTableStm = true;
+ xTableStrm = GetWriter().GetStorage().OpenSotStream(SL::a1Table, StreamMode::STD_WRITE);
+ xDataStrm = GetWriter().GetStorage().OpenSotStream(SL::aData, StreamMode::STD_WRITE);
+
+ xDataStrm->SetBufferSize( 32768 ); // for graphics
+ xTableStrm->SetBufferSize( 16384 ); // for the Font-/Style-Table, etc.
+
+ xTableStrm->SetEndian( SvStreamEndian::LITTLE );
+ xDataStrm->SetEndian( SvStreamEndian::LITTLE );
+
+ GetWriter().SetStream( xWwStrm.get() );
+ m_pTableStrm = xTableStrm.get();
+ m_pDataStrm = xDataStrm.get();
+
+ Strm().SetEndian( SvStreamEndian::LITTLE );
+
+ utl::TempFileFast aTempMain;
+ utl::TempFileFast aTempTable;
+ utl::TempFileFast aTempData;
+
+ msfilter::MSCodec_Std97 aCtx;
+ bool bEncrypt = GetWriter().InitStd97CodecUpdateMedium(aCtx);
+ if ( bEncrypt )
+ {
+ GetWriter().SetStream(
+ aTempMain.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ) );
+
+ m_pTableStrm = aTempTable.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
+
+ m_pDataStrm = aTempData.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
+
+ sal_uInt8 const aRC4EncryptionHeader[ 52 ] = {0};
+ m_pTableStrm->WriteBytes(aRC4EncryptionHeader, 52);
+ }
+
+ // Default: "Standard"
+ m_pSepx.reset(new WW8_WrPlcSepx( *this )); // Sections/headers/footers
+
+ m_pFootnote.reset(new WW8_WrPlcFootnoteEdn( TXT_FTN )); // Footnotes
+ m_pEdn.reset(new WW8_WrPlcFootnoteEdn( TXT_EDN )); // Endnotes
+ m_pAtn.reset(new WW8_WrPlcAnnotations); // PostIts
+ m_pFactoids.reset(new WW8_WrtFactoids); // Smart tags.
+ m_pTextBxs.reset(new WW8_WrPlcTextBoxes( TXT_TXTBOX ));
+ m_pHFTextBxs.reset(new WW8_WrPlcTextBoxes( TXT_HFTXTBOX ));
+
+ m_pSdrObjs.reset(new MainTextPlcDrawObj); // Draw-/Fly-Objects for main text
+ m_pHFSdrObjs.reset(new HdFtPlcDrawObj); // Draw-/Fly-Objects for header/footer
+
+ m_pBkmks.reset(new WW8_WrtBookmarks); // Bookmarks
+ GetWriter().CreateBookmarkTable();
+
+ m_pPapPlc.reset(new WW8_WrPlcPn( *this, PAP, m_pFib->m_fcMin ));
+ m_pChpPlc.reset(new WW8_WrPlcPn( *this, CHP, m_pFib->m_fcMin ));
+ m_pO.reset(new ww::bytes);
+ m_pStyles.reset(new MSWordStyles( *this ));
+ m_pFieldMain.reset(new WW8_WrPlcField( 2, TXT_MAINTEXT ));
+ m_pFieldHdFt.reset(new WW8_WrPlcField( 2, TXT_HDFT ));
+ m_pFieldFootnote.reset(new WW8_WrPlcField( 2, TXT_FTN ));
+ m_pFieldEdn.reset(new WW8_WrPlcField( 2, TXT_EDN ));
+ m_pFieldAtn.reset(new WW8_WrPlcField( 2, TXT_ATN ));
+ m_pFieldTextBxs.reset(new WW8_WrPlcField( 2, TXT_TXTBOX ));
+ m_pFieldHFTextBxs.reset(new WW8_WrPlcField( 2, TXT_HFTXTBOX ));
+
+ m_pMagicTable.reset(new WW8_WrMagicTable);
+
+ m_pGrf.reset(new SwWW8WrGrf( *this ));
+ m_pPiece.reset(new WW8_WrPct( m_pFib->m_fcMin ));
+ m_pDop.reset(new WW8Dop);
+
+ m_pDop->fRevMarking = bool( RedlineFlags::On & m_nOrigRedlineFlags );
+ SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
+ m_pDop->fRMView = pLayout == nullptr || !pLayout->IsHideRedlines();
+ m_pDop->fRMPrint = m_pDop->fRMView;
+
+ // set AutoHyphenation flag if found in default para style
+ const SvxHyphenZoneItem* pItem;
+ SwTextFormatColl* pStdTextFormatColl =
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
+ if (pStdTextFormatColl && (pItem = pStdTextFormatColl->GetItemIfSet(
+ RES_PARATR_HYPHENZONE, false)))
+ {
+ m_pDop->fAutoHyphen = pItem->IsHyphen();
+ }
+
+ StoreDoc1();
+
+ ErrCode err = ERRCODE_NONE;
+ if ( bEncrypt )
+ {
+ SvStream *pStrmTemp, *pTableStrmTemp, *pDataStrmTemp;
+ pStrmTemp = xWwStrm.get();
+ pTableStrmTemp = xTableStrm.get();
+ pDataStrmTemp = xDataStrm.get();
+
+ if ( pDataStrmTemp && pDataStrmTemp != pStrmTemp) {
+ err = EncryptRC4(aCtx, *m_pDataStrm, *pDataStrmTemp);
+ if (err != ERRCODE_NONE) {
+ goto done;
+ }
+ }
+
+ err = EncryptRC4(aCtx, *m_pTableStrm, *pTableStrmTemp);
+ if (err != ERRCODE_NONE) {
+ goto done;
+ }
+
+ // Write Unencrypted Header 52 bytes to the start of the table stream
+ // EncryptionVersionInfo (4 bytes): A Version structure where Version.vMajor MUST be 0x0001, and Version.vMinor MUST be 0x0001.
+ pTableStrmTemp->Seek( 0 );
+ pTableStrmTemp->WriteUInt32( 0x10001 ); // nEncType
+
+ sal_uInt8 pDocId[16];
+ aCtx.GetDocId( pDocId );
+
+ sal_uInt8 pSaltData[16];
+ sal_uInt8 pSaltDigest[16];
+ aCtx.GetEncryptKey( pDocId, pSaltData, pSaltDigest );
+
+ pTableStrmTemp->WriteBytes(pDocId, 16);
+ pTableStrmTemp->WriteBytes(pSaltData, 16);
+ pTableStrmTemp->WriteBytes(pSaltDigest, 16);
+
+ err = EncryptRC4(aCtx, GetWriter().Strm(), *pStrmTemp);
+ if (err != ERRCODE_NONE) {
+ goto done;
+ }
+
+ // Write Unencrypted Fib 68 bytes to the start of the workdocument stream
+ m_pFib->m_fEncrypted = true; // fEncrypted indicates the document is encrypted.
+ m_pFib->m_fObfuscated = false; // Must be 0 for RC4.
+ m_pFib->m_nHash = 0x34; // encrypt header bytes count of table stream.
+ m_pFib->m_nKey = 0; // lkey2 must be 0 for RC4.
+
+ pStrmTemp->Seek( 0 );
+ m_pFib->WriteHeader( *pStrmTemp );
+ done:;
+ }
+
+ m_pGrf.reset();
+ m_pMagicTable.reset();
+ m_pFieldFootnote.reset();
+ m_pFieldTextBxs.reset();
+ m_pFieldHFTextBxs.reset();
+ m_pFieldAtn.reset();
+ m_pFieldEdn.reset();
+ m_pFieldHdFt.reset();
+ m_pFieldMain.reset();
+ m_pStyles.reset();
+ m_pO.reset();
+ m_pChpPlc.reset();
+ m_pPapPlc.reset();
+ m_pSepx.reset();
+
+ m_pRedlAuthors.reset();
+ m_pSdrObjs.reset();
+ m_pHFSdrObjs.reset();
+ m_pTextBxs.reset();
+ m_pHFTextBxs.reset();
+ m_pAtn.reset();
+ m_pEdn.reset();
+ m_pFootnote.reset();
+ m_pBkmks.reset();
+ m_pPiece.reset();
+ m_pDop.reset();
+ m_pFib.reset();
+ GetWriter().SetStream( nullptr );
+
+ xWwStrm->SetBufferSize( 0 );
+ xTableStrm->SetBufferSize( 0 );
+ xDataStrm->SetBufferSize( 0 );
+ if( 0 == m_pDataStrm->Seek( STREAM_SEEK_TO_END ))
+ {
+ xDataStrm.clear();
+ m_pDataStrm = nullptr;
+ GetWriter().GetStorage().Remove(SL::aData);
+ }
+
+ return err;
+}
+
+void WW8Export::PrepareStorage()
+{
+ static const sal_uInt8 pData[] =
+ {
+ 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x09, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46,
+
+ 0x18, 0x00, 0x00, 0x00,
+ 'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
+ 't', ' ', 'W', 'o', 'r', 'd', '-', 'D',
+ 'o', 'k', 'u', 'm', 'e', 'n', 't', 0x0,
+
+ 0x0A, 0x00, 0x00, 0x00,
+ 'M', 'S', 'W', 'o', 'r', 'd', 'D', 'o',
+ 'c', 0x0,
+
+ 0x10, 0x00, 0x00, 0x00,
+ 'W', 'o', 'r', 'd', '.', 'D', 'o', 'c',
+ 'u', 'm', 'e', 'n', 't', '.', '8', 0x0,
+
+ 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ SvGlobalName aGName(MSO_WW8_CLASSID);
+ GetWriter().GetStorage().SetClass(
+ aGName, SotClipboardFormatId::NONE, "Microsoft Word-Document");
+ tools::SvRef<SotStorageStream> xStor( GetWriter().GetStorage().OpenSotStream(sCompObj) );
+ xStor->WriteBytes(pData, sizeof(pData));
+
+ SwDocShell* pDocShell = m_rDoc.GetDocShell ();
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+
+ if (!pDocShell) return;
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
+
+ if (!xDocProps.is())
+ return;
+
+ if ( SvtFilterOptions::Get().IsEnableWordPreview() )
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ pDocShell->GetPreviewMetaFile();
+ uno::Sequence<sal_Int8> metaFile(
+ sfx2::convertMetaFile(xMetaFile.get()));
+ sfx2::SaveOlePropertySet(xDocProps, &GetWriter().GetStorage(), &metaFile);
+ }
+ else
+ sfx2::SaveOlePropertySet( xDocProps, &GetWriter().GetStorage() );
+}
+
+ErrCodeMsg SwWW8Writer::WriteStorage()
+{
+ tools::SvRef<SotStorage> pOrigStg;
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption;
+ std::shared_ptr<SvStream> pSotStorageStream;
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if (mpMedium)
+ {
+ // Check for specific encryption requests
+ const SfxUnoAnyItem* pEncryptionDataItem = mpMedium->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
+ {
+ ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
+ OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString());
+
+ if (sCryptoType.getLength())
+ {
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Sequence<uno::Any> aArguments{
+ uno::Any(beans::NamedValue("Binary", uno::Any(true))) };
+ xPackageEncryption.set(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (xPackageEncryption)
+ {
+ // We have an encryptor
+ // Create new temporary storage for content
+ pOrigStg = m_pStg;
+ pSotStorageStream = std::make_shared<SvMemoryStream>();
+ m_pStg = new SotStorage(*pSotStorageStream);
+ }
+ }
+ }
+ }
+
+ ErrCode nErrorCode = WriteStorageImpl();
+
+ if (xPackageEncryption)
+ {
+ assert(pSotStorageStream && m_pStg && "because always set if xPackageEncryption was set");
+
+ m_pStg->Commit();
+ pSotStorageStream->Seek(0);
+
+ // Encrypt data written into temporary storage
+ xPackageEncryption->setupEncryption(aEncryptionData);
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pSotStorageStream.get(), false));
+ uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
+
+ m_pStg = pOrigStg;
+ for (const beans::NamedValue & aStreamData : std::as_const(aStreams))
+ {
+ // To avoid long paths split and open substorages recursively
+ // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
+ tools::SvRef<SotStorage> pStorage = m_pStg.get();
+ OUString sFileName;
+ sal_Int32 idx = 0;
+ while (pStorage && idx >= 0)
+ {
+ OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
+ if (!sPathElem.isEmpty())
+ {
+ if (idx < 0)
+ {
+ sFileName = sPathElem;
+ }
+ else
+ {
+ pStorage = pStorage->OpenSotStorage(sPathElem);
+ if (!pStorage)
+ break;
+ }
+ }
+ };
+
+ if (!pStorage)
+ {
+ nErrorCode = ERRCODE_IO_GENERAL;
+ break;
+ }
+
+ tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
+ if (!pStream)
+ {
+ nErrorCode = ERRCODE_IO_GENERAL;
+ break;
+ }
+ uno::Sequence<sal_Int8> aStreamContent;
+ aStreamData.Value >>= aStreamContent;
+ size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getArray(), aStreamContent.getLength());
+ if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
+ {
+ nErrorCode = ERRCODE_IO_CANTWRITE;
+ break;
+ }
+ }
+ }
+
+ return nErrorCode;
+}
+ErrCode SwWW8Writer::WriteStorageImpl()
+{
+ // #i34818# - update layout (if present), for SwWriteTable
+ SwViewShell* pViewShell = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pViewShell != nullptr )
+ pViewShell->CalcLayout();
+
+ SwNodeOffset nMaxNode = m_pDoc->GetNodes().Count();
+ ::StartProgress( STR_STATSTR_W4WWRITE, 0, sal_Int32(nMaxNode), m_pDoc->GetDocShell() );
+
+ // Respect table at the beginning of the document
+ {
+ SwTableNode* pTNd = m_pCurrentPam->GetPointNode().FindTableNode();
+ if( pTNd && m_bWriteAll )
+ // start with the table node !!
+ m_pCurrentPam->GetPoint()->Assign(*pTNd);
+ }
+
+ // Do the actual export
+ ErrCode err = ERRCODE_NONE;
+ {
+ bool bDot = mpMedium->GetFilter()->GetName().endsWith("Vorlage");
+ WW8Export aExport(this, *m_pDoc, m_pCurrentPam, m_pOrigPam, bDot);
+ m_pExport = &aExport;
+ err = aExport.ExportDocument( m_bWriteAll );
+ m_pExport = nullptr;
+ }
+
+ ::EndProgress( m_pDoc->GetDocShell() );
+ return err;
+}
+
+ErrCodeMsg SwWW8Writer::WriteMedium( SfxMedium& )
+{
+ return WriteStorage();
+}
+
+ErrCodeMsg SwWW8Writer::Write( SwPaM& rPaM, SfxMedium& rMed,
+ const OUString* pFileName )
+{
+ mpMedium = &rMed;
+ ErrCodeMsg nRet = StgWriter::Write( rPaM, rMed, pFileName );
+ mpMedium = nullptr;
+ return nRet;
+}
+
+MSWordExportBase::MSWordExportBase( SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam )
+ : m_aMainStg(sMainStream)
+ , m_pISet(nullptr)
+ , m_pTopNodeOfHdFtPage(nullptr)
+ , m_pTableInfo(std::make_shared<ww8::WW8TableInfo>())
+ , m_nCharFormatStart(0)
+ , m_nFormatCollStart(0)
+ , m_nStyleBeforeFly(0)
+ , m_nLastFormatId(0)
+ , m_nUniqueList(0)
+ , m_nHdFtIndex(0)
+ , m_nOrigRedlineFlags(RedlineFlags::NONE)
+ , m_bOrigShowChanges(true)
+ , m_pCurrentPageDesc(nullptr)
+ , m_pPreviousSectionPageDesc(nullptr)
+ , m_bFirstTOCNodeWithSection(false)
+ , m_pChpIter(nullptr)
+ , m_pParentFrame(nullptr)
+ , m_pFlyOffset(nullptr)
+ , m_eNewAnchorType(RndStdIds::FLY_AS_CHAR)
+ , m_pStyAttr(nullptr)
+ , m_pOutFormatNode(nullptr)
+ , m_pCurrentStyle(nullptr)
+ , m_pEscher(nullptr)
+ , m_nTextTyp(0)
+ , m_bStyDef(false)
+ , m_bBreakBefore(false)
+ , m_bOutKF(false)
+ , m_bOutFlyFrameAttrs(false)
+ , m_bOutPageDescs(false)
+ , m_bOutFirstPage(false)
+ , m_bOutTable(false)
+ , m_bOutGrf(false)
+ , m_bInWriteEscher(false)
+ , m_bStartTOX(false)
+ , m_bInWriteTOX(false)
+ , m_bFootnoteAtTextEnd(false)
+ , m_bEndAtTextEnd(false)
+ , m_bHasHdr(false)
+ , m_bHasFtr(false)
+ , m_bSubstituteBullets(true)
+ , m_bTabInTOC(false)
+ , m_bHideTabLeaderAndPageNumbers(false)
+ , m_bExportModeRTF(false)
+ , m_bFontSizeWritten(false)
+ , m_bAddFootnoteTab(false)
+ , m_rDoc(rDocument)
+ , m_nCurStart(pCurrentPam->GetPoint()->GetNodeIndex())
+ , m_nCurEnd(pCurrentPam->GetMark()->GetNodeIndex())
+ , m_pCurPam(pCurrentPam)
+ , m_pOrigPam(pOriginalPam)
+{
+}
+
+MSWordExportBase::~MSWordExportBase()
+{
+ if (m_pUsedNumTable) // all used NumRules
+ {
+ // clear the part of the list array that was copied from the document
+ // - it's an auto delete array, so the rest of the array which are
+ // duplicated lists that were added during the export will be deleted.
+ m_pUsedNumTable->erase(m_pUsedNumTable->begin(), m_pUsedNumTable->begin() + m_pUsedNumTable->size() - m_nUniqueList);
+ m_pUsedNumTable.reset();
+ }
+ m_oOLEExp.reset();
+ m_pOCXExp.reset();
+}
+
+WW8Export::WW8Export( SwWW8Writer *pWriter,
+ SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam,
+ bool bDot )
+ : MSWordExportBase( rDocument, pCurrentPam, pOriginalPam )
+ , m_pTableStrm(nullptr)
+ , m_pDataStrm(nullptr)
+ , m_bDot(bDot)
+ , m_pWriter(pWriter)
+ , m_pAttrOutput(new WW8AttributeOutput(*this))
+{
+}
+
+WW8Export::~WW8Export()
+{
+}
+
+AttributeOutputBase& WW8Export::AttrOutput() const
+{
+ return *m_pAttrOutput;
+}
+
+MSWordSections& WW8Export::Sections() const
+{
+ return *m_pSepx;
+}
+
+SwWW8Writer::SwWW8Writer(std::u16string_view rFltName, const OUString& rBaseURL)
+ : m_pExport( nullptr ),
+ mpMedium( nullptr )
+{
+ assert(rFltName == FILTER_WW8); // WW6/7 export was removed
+ (void)rFltName;
+ SetBaseURL( rBaseURL );
+}
+
+SwWW8Writer::~SwWW8Writer()
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell& rDoc, SotStorage& rStor, sal_Bool bSaveInto, const OUString& rStorageName )
+{
+ SvxImportMSVBasic aTmp( rDoc, rStor );
+ return sal_uInt32(aTmp.SaveOrDelMSVBAStorage( bSaveInto, rStorageName ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void ExportDOC( std::u16string_view rFltName, const OUString& rBaseURL, WriterRef& xRet )
+{
+ xRet = new SwWW8Writer( rFltName, rBaseURL );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8( SfxObjectShell &rDocS )
+{
+ return sal_uInt32(SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( rDocS ));
+}
+
+bool WW8_WrPlcFootnoteEdn::WriteText( WW8Export& rWrt )
+{
+ bool bRet = false;
+ if (TXT_FTN == m_nTyp)
+ {
+ bRet = WriteGenericText( rWrt, TXT_FTN, rWrt.m_pFib->m_ccpFootnote );
+ rWrt.m_pFieldFootnote->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
+ rWrt.m_pFib->m_ccpText );
+ }
+ else
+ {
+ bRet = WriteGenericText( rWrt, TXT_EDN, rWrt.m_pFib->m_ccpEdn );
+ rWrt.m_pFieldEdn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
+ rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote
+ + rWrt.m_pFib->m_ccpHdr + rWrt.m_pFib->m_ccpAtn );
+ }
+ return bRet;
+}
+
+void WW8_WrPlcFootnoteEdn::WritePlc( WW8Export& rWrt ) const
+{
+ if( TXT_FTN == m_nTyp )
+ {
+ WriteGenericPlc( rWrt, TXT_FTN, rWrt.m_pFib->m_fcPlcffndText,
+ rWrt.m_pFib->m_lcbPlcffndText, rWrt.m_pFib->m_fcPlcffndRef,
+ rWrt.m_pFib->m_lcbPlcffndRef );
+ }
+ else
+ {
+ WriteGenericPlc( rWrt, TXT_EDN, rWrt.m_pFib->m_fcPlcfendText,
+ rWrt.m_pFib->m_lcbPlcfendText, rWrt.m_pFib->m_fcPlcfendRef,
+ rWrt.m_pFib->m_lcbPlcfendRef );
+ }
+}
+
+bool WW8_WrPlcAnnotations::WriteText( WW8Export& rWrt )
+{
+ bool bRet = WriteGenericText( rWrt, TXT_ATN, rWrt.m_pFib->m_ccpAtn );
+ rWrt.m_pFieldAtn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
+ rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote
+ + rWrt.m_pFib->m_ccpHdr );
+ return bRet;
+}
+
+void WW8_WrPlcAnnotations::WritePlc( WW8Export& rWrt ) const
+{
+ WriteGenericPlc( rWrt, TXT_ATN, rWrt.m_pFib->m_fcPlcfandText,
+ rWrt.m_pFib->m_lcbPlcfandText, rWrt.m_pFib->m_fcPlcfandRef,
+ rWrt.m_pFib->m_lcbPlcfandRef );
+}
+
+void WW8_WrPlcTextBoxes::WritePlc( WW8Export& rWrt ) const
+{
+ if( TXT_TXTBOX == m_nTyp )
+ {
+ WriteGenericPlc( rWrt, m_nTyp, rWrt.m_pFib->m_fcPlcftxbxBkd,
+ rWrt.m_pFib->m_lcbPlcftxbxBkd, rWrt.m_pFib->m_fcPlcftxbxText,
+ rWrt.m_pFib->m_lcbPlcftxbxText );
+ }
+ else
+ {
+ WriteGenericPlc( rWrt, m_nTyp, rWrt.m_pFib->m_fcPlcfHdrtxbxBkd,
+ rWrt.m_pFib->m_lcbPlcfHdrtxbxBkd, rWrt.m_pFib->m_fcPlcfHdrtxbxText,
+ rWrt.m_pFib->m_lcbPlcfHdrtxbxText );
+ }
+}
+
+void WW8Export::RestoreMacroCmds()
+{
+ m_pFib->m_fcCmds = m_pTableStrm->Tell();
+
+ uno::Reference < embed::XStorage > xSrcRoot(m_rDoc.GetDocShell()->GetStorage());
+ try
+ {
+ uno::Reference < io::XStream > xSrcStream =
+ xSrcRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READ );
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( xSrcStream );
+
+ if ( pStream && ERRCODE_NONE == pStream->GetError())
+ {
+ m_pFib->m_lcbCmds = pStream->TellEnd();
+ pStream->Seek(0);
+
+ std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[m_pFib->m_lcbCmds] );
+ bool bReadOk = checkRead(*pStream, pBuffer.get(), m_pFib->m_lcbCmds);
+ if (bReadOk)
+ m_pTableStrm->WriteBytes(pBuffer.get(), m_pFib->m_lcbCmds);
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+
+ // set len to FIB
+ m_pFib->m_lcbCmds = m_pTableStrm->Tell() - m_pFib->m_fcCmds;
+}
+
+void WW8SHDLong::Write( WW8Export& rExport )
+{
+ rExport.InsUInt32( m_cvFore );
+ rExport.InsUInt32( m_cvBack );
+ rExport.InsUInt16( 0 ); // ipat
+}
+
+void WW8Export::WriteFormData( const ::sw::mark::IFieldmark& rFieldmark )
+{
+ const ::sw::mark::IFieldmark* pFieldmark = &rFieldmark;
+ const ::sw::mark::ICheckboxFieldmark* pAsCheckbox = dynamic_cast< const ::sw::mark::ICheckboxFieldmark* >( pFieldmark );
+
+ if ( ! ( rFieldmark.GetFieldname() == ODF_FORMTEXT ||
+ rFieldmark.GetFieldname() == ODF_FORMDROPDOWN ||
+ rFieldmark.GetFieldname() == ODF_FORMCHECKBOX ) )
+ {
+ SAL_WARN( "sw.ww8", "unknown field type" );
+ return;
+ }
+
+ int type = 0; // TextFieldmark
+ if ( pAsCheckbox )
+ type = 1;
+ if ( rFieldmark.GetFieldname() == ODF_FORMDROPDOWN )
+ type=2;
+
+ OUString ffname = rFieldmark.GetName();
+ if (ffname.getLength() > 20)
+ ffname = ffname.copy(0, 20);
+
+ sal_uInt64 nDataStt = m_pDataStrm->Tell();
+ m_pChpPlc->AppendFkpEntry(Strm().Tell());
+
+ WriteChar(0x01);
+ static sal_uInt8 aArr1[] =
+ {
+ 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation
+
+ 0x06, 0x08, 0x01, // sprmCFData
+ 0x55, 0x08, 0x01, // sprmCFSpec
+ 0x02, 0x08, 0x01 // sprmCFFieldVanish
+ };
+ sal_uInt8* pDataAdr = aArr1 + 2;
+ Set_UInt32(pDataAdr, nDataStt);
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aArr1 ), aArr1 );
+
+ struct FFDataHeader
+ {
+ sal_uInt32 version;
+ sal_uInt16 bits;
+ sal_uInt16 cch;
+ sal_uInt16 hps;
+ FFDataHeader() : version( 0xFFFFFFFF ), bits(0), cch(0), hps(0) {}
+ };
+
+ FFDataHeader aFieldHeader;
+ aFieldHeader.bits |= (type & 0x03);
+
+ sal_Int32 ffres = 0; // rFieldmark.GetFFRes();
+ if ( pAsCheckbox && pAsCheckbox->IsChecked() )
+ ffres = 1;
+ else if ( type == 2 )
+ {
+ ::sw::mark::IFieldmark::parameter_map_t::const_iterator pResParameter = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT);
+ if(pResParameter != rFieldmark.GetParameters()->end())
+ pResParameter->second >>= ffres;
+ else
+ ffres = 0;
+ }
+ aFieldHeader.bits |= ( (ffres<<2) & 0x7C );
+
+ OUString ffdeftext;
+ OUString ffformat;
+ OUString ffhelptext = rFieldmark.GetFieldHelptext();
+ if ( ffhelptext.getLength() > 255 )
+ ffhelptext = ffhelptext.copy(0, 255);
+ OUString ffstattext;
+ OUString ffentrymcr;
+ OUString ffexitmcr;
+
+ ::sw::mark::IFieldmark::parameter_map_t::const_iterator pParameter
+ = rFieldmark.GetParameters()->find("Type");
+ if (type == 0) // iTypeText
+ {
+ sal_uInt16 nType = 0;
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aType;
+ pParameter->second >>= aType;
+ if ( aType == "number" ) nType = 1;
+ else if ( aType == "date" ) nType = 2;
+ else if ( aType == "currentTime" ) nType = 3;
+ else if ( aType == "currentDate" ) nType = 4;
+ else if ( aType == "calculated" ) nType = 5;
+ aFieldHeader.bits |= nType<<11; // FFDataBits-F 00111000 00000000
+ }
+
+ if ( nType < 3 || nType == 5 ) // not currentTime or currentDate
+ {
+ pParameter = rFieldmark.GetParameters()->find("Content");
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aDefaultText;
+ pParameter->second >>= aDefaultText;
+ const sal_Int32 nLen = std::min( sal_Int32(255), aDefaultText.getLength() );
+ ffdeftext = aDefaultText.copy (0, nLen);
+ }
+ }
+
+ pParameter = rFieldmark.GetParameters()->find("MaxLength");
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ sal_uInt16 nLength = 0;
+ pParameter->second >>= nLength;
+ nLength = std::min( sal_uInt16(32767), nLength );
+ aFieldHeader.cch = nLength;
+ }
+
+ pParameter = rFieldmark.GetParameters()->find("Format");
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aFormat;
+ pParameter->second >>= aFormat;
+ const sal_Int32 nLen = std::min( sal_Int32(64), aFormat.getLength() );
+ ffformat = aFormat.copy(0, nLen);
+ }
+ }
+
+ pParameter = rFieldmark.GetParameters()->find("Help"); //help
+ if ( ffhelptext.isEmpty() && pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aHelpText;
+ pParameter->second >>= aHelpText;
+ const sal_Int32 nLen = std::min( sal_Int32(255), aHelpText.getLength() );
+ ffhelptext = aHelpText.copy (0, nLen);
+ }
+ if ( !ffhelptext.isEmpty() )
+ aFieldHeader.bits |= 0x1<<7;
+
+ pParameter = rFieldmark.GetParameters()->find("Description"); // doc tooltip
+ if ( pParameter == rFieldmark.GetParameters()->end() )
+ pParameter = rFieldmark.GetParameters()->find("Hint"); //docx tooltip
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aStatusText;
+ pParameter->second >>= aStatusText;
+ const sal_Int32 nLen = std::min( sal_Int32(138), aStatusText.getLength() );
+ ffstattext = aStatusText.copy (0, nLen);
+ }
+ if ( !ffstattext.isEmpty() )
+ aFieldHeader.bits |= 0x1<<8;
+
+ pParameter = rFieldmark.GetParameters()->find("EntryMacro");
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aEntryMacro;
+ pParameter->second >>= aEntryMacro;
+ const sal_Int32 nLen = std::min( sal_Int32(32), aEntryMacro.getLength() );
+ ffentrymcr = aEntryMacro.copy (0, nLen);
+ }
+
+ pParameter = rFieldmark.GetParameters()->find("ExitMacro");
+ if ( pParameter != rFieldmark.GetParameters()->end() )
+ {
+ OUString aExitMacro;
+ pParameter->second >>= aExitMacro;
+ const sal_Int32 nLen = std::min( sal_Int32(32), aExitMacro.getLength() );
+ ffexitmcr = aExitMacro.copy (0, nLen);
+ }
+
+ std::vector< OUString > aListItems;
+ if (type==2)
+ {
+ aFieldHeader.bits |= 0x8000; // ffhaslistbox
+ const ::sw::mark::IFieldmark::parameter_map_t* const pParameters = rFieldmark.GetParameters();
+ ::sw::mark::IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
+ if(pListEntries != pParameters->end())
+ {
+ uno::Sequence< OUString > vListEntries;
+ pListEntries->second >>= vListEntries;
+ aListItems.reserve(vListEntries.getLength());
+ copy(std::cbegin(vListEntries), std::cend(vListEntries), back_inserter(aListItems));
+ }
+ }
+
+ const sal_uInt8 aFieldData[] =
+ {
+ 0x44,0, // the start of "next" data
+ 0,0,0,0,0,0,0,0,0,0, // PIC-Structure! /10
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
+ 0,0,0,0, // / /4
+ };
+ sal_uInt32 slen = sizeof(sal_uInt32)
+ + sizeof(aFieldData)
+ + sizeof( aFieldHeader.version ) + sizeof( aFieldHeader.bits ) + sizeof( aFieldHeader.cch ) + sizeof( aFieldHeader.hps )
+ + 2*ffname.getLength() + 4
+ + 2*ffformat.getLength() + 4
+ + 2*ffhelptext.getLength() + 4
+ + 2*ffstattext.getLength() + 4
+ + 2*ffentrymcr.getLength() + 4
+ + 2*ffexitmcr.getLength() + 4;
+ if ( type )
+ slen += 2; // wDef
+ else
+ slen += 2*ffdeftext.getLength() + 4; //xstzTextDef
+ if ( type==2 ) {
+ slen += 2; // sttb ( fExtend )
+ slen += 4; // for num of list items
+ const int items = aListItems.size();
+ for( int i = 0; i < items; i++ ) {
+ OUString item = aListItems[i];
+ slen += 2 * item.getLength() + 2;
+ }
+ }
+
+ m_pDataStrm->WriteUInt32( slen );
+
+ int len = sizeof( aFieldData );
+ OSL_ENSURE( len == 0x44-sizeof(sal_uInt32), "SwWW8Writer::WriteFormData(..) - wrong aFieldData length" );
+ m_pDataStrm->WriteBytes( aFieldData, len );
+
+ m_pDataStrm->WriteUInt32( aFieldHeader.version ).WriteUInt16( aFieldHeader.bits ).WriteUInt16( aFieldHeader.cch ).WriteUInt16( aFieldHeader.hps );
+
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffname, true ); // Form field name
+
+ if ( !type )
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffdeftext, true );
+ if ( type )
+ m_pDataStrm->WriteUInt16( 0 );
+
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffformat, true );
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffhelptext, true );
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffstattext, true );
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffentrymcr, true );
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffexitmcr, true );
+ if (type==2) {
+ m_pDataStrm->WriteUInt16( 0xFFFF );
+ const int items=aListItems.size();
+ m_pDataStrm->WriteUInt32( items );
+ for(int i=0;i<items;i++) {
+ OUString item=aListItems[i];
+ SwWW8Writer::WriteString_xstz( *m_pDataStrm, item, false );
+ }
+ }
+}
+
+void WW8Export::WriteHyperlinkData( const sw::mark::IFieldmark& /*rFieldmark*/ )
+{
+ //@TODO implement me !!!
+}
+
+void WW8AttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
+{
+ SVBT16 nStyle;
+ ShortToSVBT16( m_rWW8Export.m_nStyleBeforeFly, nStyle );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<OutWW8_TableNodeInfoInner>" << pNodeInfoInner->toString());
+#endif
+
+ m_rWW8Export.m_pO->clear();
+
+ sal_uInt32 nShadowsBefore = pNodeInfoInner->getShadowsBefore();
+ if (nShadowsBefore > 0)
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t
+ pTmpNodeInfoInner = std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
+
+ pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
+ pTmpNodeInfoInner->setEndOfCell(true);
+
+ for (sal_uInt32 n = 0; n < nShadowsBefore; ++n)
+ {
+ m_rWW8Export.WriteCR(pTmpNodeInfoInner);
+
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
+ TableInfoCell(pTmpNodeInfoInner);
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry
+ ( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+
+ m_rWW8Export.m_pO->clear();
+ }
+ }
+
+ if (pNodeInfoInner->isEndOfCell())
+ {
+ SAL_INFO( "sw.ww8", "<endOfCell/>" );
+
+ m_rWW8Export.WriteCR(pNodeInfoInner);
+
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
+ TableInfoCell(pNodeInfoInner);
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+
+ m_rWW8Export.m_pO->clear();
+ }
+
+ sal_uInt32 nShadowsAfter = pNodeInfoInner->getShadowsAfter();
+ if (nShadowsAfter > 0)
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t
+ pTmpNodeInfoInner= std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
+
+ pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
+ pTmpNodeInfoInner->setEndOfCell(true);
+
+ for (sal_uInt32 n = 0; n < nShadowsAfter; ++n)
+ {
+ m_rWW8Export.WriteCR(pTmpNodeInfoInner);
+
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
+ TableInfoCell(pTmpNodeInfoInner);
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+
+ m_rWW8Export.m_pO->clear();
+ }
+ }
+
+ if (pNodeInfoInner->isEndOfLine())
+ {
+ SAL_INFO( "sw.ww8", "<endOfLine/>" );
+
+ TableRowEnd(pNodeInfoInner->getDepth());
+
+ ShortToSVBT16(0, nStyle);
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
+ TableInfoRow(pNodeInfoInner);
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+
+ m_rWW8Export.m_pO->clear();
+ }
+ SAL_INFO( "sw.ww8", "</OutWW8_TableNodeInfoInner>" );
+}
+
+void MSWordExportBase::OutputStartNode( const SwStartNode & rNode)
+{
+
+ ww8::WW8TableNodeInfo::Pointer_t pNodeInfo =
+ m_pTableInfo->getTableNodeInfo( &rNode );
+
+ if (pNodeInfo)
+ {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", pNodeInfo->toString());
+#endif
+ const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
+ ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aIt(aInners.rbegin());
+ ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aEnd(aInners.rend());
+ while (aIt != aEnd)
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t pInner = aIt->second;
+
+ AttrOutput().TableNodeInfoInner(pInner);
+ ++aIt;
+ }
+ }
+ SAL_INFO( "sw.ww8", "</OutWW8_SwStartNode>" );
+}
+
+void MSWordExportBase::OutputEndNode( const SwEndNode &rNode )
+{
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", "<OutWW8_SwEndNode>" << dbg_out(&rNode));
+#endif
+
+ ww8::WW8TableNodeInfo::Pointer_t pNodeInfo = m_pTableInfo->getTableNodeInfo( &rNode );
+
+ if (pNodeInfo)
+ {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.ww8", pNodeInfo->toString());
+#endif
+ const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
+ for (const auto& rEntry : aInners)
+ {
+ ww8::WW8TableNodeInfoInner::Pointer_t pInner = rEntry.second;
+ AttrOutput().TableNodeInfoInner(pInner);
+ }
+ }
+ SAL_INFO( "sw.ww8", "</OutWW8_SwEndNode>" );
+}
+
+const NfKeywordTable & MSWordExportBase::GetNfKeywordTable()
+{
+ if (m_pKeyMap == nullptr)
+ {
+ m_pKeyMap = std::make_shared<NfKeywordTable>();
+ NfKeywordTable & rKeywordTable = *m_pKeyMap;
+ rKeywordTable[NF_KEY_D] = "d";
+ rKeywordTable[NF_KEY_DD] = "dd";
+ rKeywordTable[NF_KEY_DDD] = "ddd";
+ rKeywordTable[NF_KEY_DDDD] = "dddd";
+ rKeywordTable[NF_KEY_M] = "M";
+ rKeywordTable[NF_KEY_MM] = "MM";
+ rKeywordTable[NF_KEY_MMM] = "MMM";
+ rKeywordTable[NF_KEY_MMMM] = "MMMM";
+ rKeywordTable[NF_KEY_NN] = "ddd";
+ rKeywordTable[NF_KEY_NNN] = "dddd";
+ rKeywordTable[NF_KEY_NNNN] = "dddd";
+ rKeywordTable[NF_KEY_YY] = "yy";
+ rKeywordTable[NF_KEY_YYYY] = "yyyy";
+ rKeywordTable[NF_KEY_H] = "H";
+ rKeywordTable[NF_KEY_HH] = "HH";
+ rKeywordTable[NF_KEY_MI] = "m";
+ rKeywordTable[NF_KEY_MMI] = "mm";
+ rKeywordTable[NF_KEY_S] = "s";
+ rKeywordTable[NF_KEY_SS] = "ss";
+ rKeywordTable[NF_KEY_AMPM] = "AM/PM";
+ }
+
+ return *m_pKeyMap;
+}
+
+OUString MSWordExportBase::BookmarkToWord(const OUString& rBookmark, bool* pIsMove, bool* pIsFrom)
+{
+ OUString sLookup = rBookmark;
+ if (pIsMove)
+ {
+ static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix = u"__RefMoveFrom__";
+ static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix = u"__RefMoveTo__";
+ if (rBookmark.startsWith(MoveFrom_Bookmark_NamePrefix, &sLookup))
+ {
+ *pIsMove = true;
+ *pIsFrom = true;
+ }
+ else if (rBookmark.startsWith(MoveTo_Bookmark_NamePrefix, &sLookup))
+ {
+ *pIsMove = true;
+ *pIsFrom = false;
+ }
+ }
+ if (auto it = m_aBookmarkToWord.find(sLookup); it != m_aBookmarkToWord.end())
+ return it->second;
+
+ OUString sRet
+ = INetURLObject::encode(sLookup.replace(' ', '_'), // Spaces are prohibited in bookmark name
+ INetURLObject::PART_REL_SEGMENT_EXTRA,
+ INetURLObject::EncodeMechanism::All, RTL_TEXTENCODING_ASCII_US);
+ // Unicode letters are allowed
+ sRet = INetURLObject::decode(sRet, INetURLObject::DecodeMechanism::Unambiguous,
+ RTL_TEXTENCODING_UTF8);
+
+ /*#i15387#*/
+ // Word has 40 character limit for bookmarks: [MS-OE376] Part 4 Sect. 2.13.6.2, bookmarkStart
+ if (sRet.getLength() > 40)
+ {
+ // Generate a unique bookmark name
+ sRet = sRet.copy(0, 40);
+ for (sal_uInt32 n = 1; n; ++n)
+ {
+ if (m_aWordBookmarks.find(sRet) == m_aWordBookmarks.end())
+ break;
+ auto num = OUString::number(n, 36);
+ sRet = sRet.subView(0, 40 - num.length) + num;
+ }
+ }
+
+ m_aBookmarkToWord[sLookup] = sRet;
+ m_aWordBookmarks.insert(sRet);
+ return sRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx
new file mode 100644
index 0000000000..fcc39c44aa
--- /dev/null
+++ b/sw/source/filter/ww8/wrtww8.hxx
@@ -0,0 +1,1683 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WRTWW8_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WRTWW8_HXX
+
+#include <sot/storage.hxx>
+#include <svl/nfkeytab.hxx>
+#include <tools/solar.h>
+#include <tools/gen.hxx>
+#include <editeng/editdata.hxx>
+#include <filter/msfilter/ww8fields.hxx>
+#include <filter/msfilter/msoleexp.hxx>
+
+#include <shellio.hxx>
+
+#include "ww8struc.hxx"
+#include "ww8scan.hxx"
+#include "types.hxx"
+#include "writerhelper.hxx"
+#include <msfilter.hxx>
+#include <expfld.hxx>
+#include "WW8TableInfo.hxx"
+#include <calbck.hxx>
+#include <IDocumentRedlineAccess.hxx>
+
+#include <vcl/graph.hxx>
+
+#include <optional>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+#include <cstddef>
+#include <memory>
+#include <map>
+#include <string_view>
+#include <vector>
+#include <unordered_map>
+
+
+class SvxBrushItem;
+class EditTextObject;
+
+// some forward declarations
+class SwWW8AttrIter;
+namespace msfilter
+{
+ class MSCodec_Std97;
+}
+
+namespace editeng { class SvxBorderLine; }
+
+class AttributeOutputBase;
+class DocxAttributeOutput;
+class RtfAttributeOutput;
+class BitmapPalette;
+class SwEscherEx;
+class DateTime;
+namespace vcl { class Font; }
+class MSWordExportBase;
+class SdrObject;
+class SdrTextObj;
+class SfxItemSet;
+class SvStream;
+class SvxFontItem;
+class SvxBoxItem;
+class SwAttrSet;
+class SwCharFormat;
+class SwContentNode;
+class SwField;
+class SwFormat;
+class SwFormatContent;
+class SwFormatFootnote;
+class SwFrameFormat;
+class SwGrfNode;
+class SwNumFormat;
+class SwNumRule;
+class SwNumRuleTable;
+class SwPageDesc;
+class SwFormatPageDesc;
+class SwOLENode;
+class SwPostItField;
+class SwRedlineData;
+class SwSectionFormat;
+class SwSectionNode;
+class SwTableNode;
+class SwTOXType;
+class SwTextFormatColl;
+class SwTextNode;
+class SwWW8WrGrf;
+class SwWW8Writer;
+class MSWordStyles;
+class WW8AttributeOutput;
+class WW8Export;
+class MSWordAttrIter;
+class WW8_WrFkp;
+class WW8_WrPlc0;
+class WW8_WrPlc1;
+class WW8_WrPlcField;
+class WW8_WrMagicTable;
+class WW8_WrPlcFootnoteEdn;
+class WW8_WrPlcPn;
+class WW8_WrPlcAnnotations;
+class WW8_WrtFactoids;
+class MSWordSections;
+class WW8_WrPlcTextBoxes;
+class WW8_WrPct; // administration
+class WW8_WrtBookmarks;
+class WW8_WrtRedlineAuthor;
+class SwMSConvertControls;
+class WW8_WrPc;
+struct WW8_PdAttrDesc;
+class SvxBrushItem;
+namespace sw::mark { class IFieldmark; }
+namespace com::sun::star::embed { class XEmbeddedObject; }
+
+typedef std::map<const css::embed::XEmbeddedObject*, sal_Int32> WW8OleMap;
+typedef o3tl::sorted_vector< sal_Int32 > SwSoftPageBreakList;
+
+#define GRF_MAGIC_1 0x12 // 3 magic bytes for PicLocFc attribute
+#define GRF_MAGIC_2 0x34
+#define GRF_MAGIC_3 0x56
+#define GRF_MAGIC_321 0x563412L
+
+#define OLE_PREVIEW_AS_EMF //If we want to export ole2 previews as emf in ww8+
+
+enum class FieldFlags : sal_uInt8 // for InsertField- Method
+{
+ NONE = 0x00,
+ Start = 0x01,
+ CmdStart = 0x02,
+ CmdEnd = 0x04,
+ End = 0x10,
+ Close = 0x20,
+ All = 0x37
+};
+namespace o3tl {
+ template<> struct typed_flags<FieldFlags> : is_typed_flags<FieldFlags, 0x37> {};
+}
+
+enum TextTypes //enums for TextTypes
+{
+ TXT_MAINTEXT = 0, /*TXT_FTNEDN = 1,*/ TXT_HDFT = 2, TXT_FTN = 3,
+ TXT_EDN = 4, TXT_ATN = 5, TXT_TXTBOX = 6, TXT_HFTXTBOX= 7
+};
+
+/**
+enum to state the present state of the fly
+*/
+enum FlyProcessingState
+{
+ FLY_NONE,
+ FLY_PROCESSED,
+ FLY_POSTPONED,
+ FLY_NOT_PROCESSED
+};
+
+struct WW8_SepInfo
+{
+ const SwPageDesc* pPageDesc;
+ const SwSectionFormat* pSectionFormat;
+ const SwNode* pPDNd;
+ sal_uLong nLnNumRestartNo;
+ ::std::optional<sal_uInt16> oPgRestartNo;
+ bool bIsFirstParagraph;
+
+ WW8_SepInfo( const SwPageDesc* pPD, const SwSectionFormat* pFormat,
+ sal_uLong nLnRestart, ::std::optional<sal_uInt16> oPgRestart = std::nullopt,
+ const SwNode* pNd = nullptr, bool bIsFirstPara = false )
+ : pPageDesc( pPD ), pSectionFormat( pFormat ), pPDNd( pNd ),
+ nLnNumRestartNo( nLnRestart ), oPgRestartNo( oPgRestart ),
+ bIsFirstParagraph( bIsFirstPara )
+ {}
+
+ bool IsProtected() const;
+};
+
+/// Class to collect and output the sections/headers/footers.
+// Plc for PageDescs -> Sepx ( Section Extensions )
+class MSWordSections
+{
+protected:
+ bool mbDocumentIsProtected;
+ std::vector<WW8_SepInfo> m_aSects;
+
+ void CheckForFacinPg( const WW8Export& rWrt ) const;
+ void NeedsDocumentProtected(const WW8_SepInfo &rInfo);
+
+ //No copy, no assign
+ MSWordSections( const MSWordSections& );
+ MSWordSections& operator=( const MSWordSections& );
+public:
+ explicit MSWordSections( MSWordExportBase& rExport );
+ virtual ~MSWordSections();
+
+ virtual bool HeaderFooterWritten();
+
+ void AppendSection( const SwPageDesc* pPd,
+ const SwSectionFormat* pSectionFormat,
+ sal_uLong nLnNumRestartNo,
+ bool bIsFirstParagraph = false );
+ void AppendSection( const SwFormatPageDesc& rPd,
+ const SwNode& rNd,
+ const SwSectionFormat* pSectionFormat,
+ sal_uLong nLnNumRestartNo );
+
+ /// Number of columns based on the most recent WW8_SepInfo.
+ sal_uInt16 CurrentNumberOfColumns( const SwDoc &rDoc ) const;
+
+ /// Number of columns of the provided WW8_SepInfo.
+ static const SwFormatCol& GetFormatCol(const SwDoc &rDoc, const WW8_SepInfo& rInfo);
+
+ bool DocumentIsProtected() const { return mbDocumentIsProtected; }
+
+ /// The most recent WW8_SepInfo.
+ const WW8_SepInfo* CurrentSectionInfo();
+
+ static void SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
+ sal_uInt8 nFlag );
+ static void SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
+ sal_uInt8 nFlag );
+
+ /// Should we output borders?
+ static bool HasBorderItem( const SwFormat& rFormat );
+};
+
+class WW8_WrPlcSepx : public MSWordSections
+{
+ std::vector<WW8_CP> m_aCps;
+ std::vector< std::shared_ptr<WW8_PdAttrDesc> > m_SectionAttributes;
+ // hack to prevent adding sections in endnotes
+ bool m_bHeaderFooterWritten;
+ std::unique_ptr<WW8_WrPlc0> m_pTextPos; // Position of the headers/footers
+
+ WW8_WrPlcSepx( const WW8_WrPlcSepx& ) = delete;
+ WW8_WrPlcSepx& operator=( const WW8_WrPlcSepx& ) = delete;
+
+public:
+ explicit WW8_WrPlcSepx( MSWordExportBase& rExport );
+ virtual ~WW8_WrPlcSepx() override;
+
+ virtual bool HeaderFooterWritten() override; // override
+
+ void AppendSep( WW8_CP nStartCp,
+ const SwPageDesc* pPd,
+ const SwSectionFormat* pSectionFormat,
+ sal_uLong nLnNumRestartNo );
+ void AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPd,
+ const SwNode& rNd,
+ const SwSectionFormat* pSectionFormat,
+ sal_uLong nLnNumRestartNo );
+ void Finish( WW8_CP nEndCp ) { m_aCps.push_back( nEndCp ); }
+
+ bool WriteKFText( WW8Export& rWrt );
+ void WriteSepx( SvStream& rStrm ) const;
+ void WritePlcSed( WW8Export& rWrt ) const;
+ void WritePlcHdd( WW8Export& rWrt ) const;
+
+private:
+ void WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt );
+public:
+ void OutHeaderFooter(WW8Export& rWrt, bool bHeader,
+ const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags, sal_uInt8 nFlag, sal_uInt8 nBreakCode);
+};
+
+// class WW8_WrPct to construct the piece table
+class WW8_WrPct
+{
+ std::vector<std::unique_ptr<WW8_WrPc>> m_Pcts;
+ WW8_FC m_nOldFc;
+public:
+ explicit WW8_WrPct(WW8_FC nStartFc);
+ ~WW8_WrPct();
+ void AppendPc(WW8_FC nStartFc);
+ void WritePc( WW8Export& rWrt );
+ void SetParaBreak();
+ WW8_CP Fc2Cp( sal_uLong nFc ) const;
+};
+
+/// Collects and outputs fonts.
+class wwFont
+{
+//In some future land the stream could be converted to a nice stream interface
+//and we could have harmony
+private:
+ sal_uInt8 maWW8_FFN[6] = {};
+ OUString msFamilyNm;
+ OUString msAltNm;
+ bool mbAlt;
+ FontPitch mePitch;
+ FontFamily meFamily;
+ rtl_TextEncoding meChrSet;
+public:
+ wwFont( std::u16string_view rFamilyName, FontPitch ePitch, FontFamily eFamily,
+ rtl_TextEncoding eChrSet);
+ void Write( SvStream *pTableStram ) const;
+ void WriteDocx( DocxAttributeOutput* rAttrOutput ) const;
+ void WriteRtf( const RtfAttributeOutput* rAttrOutput ) const;
+ OUString const & GetFamilyName() const { return msFamilyNm; }
+ friend bool operator < (const wwFont &r1, const wwFont &r2);
+};
+
+class wwFontHelper
+{
+private:
+ /// Keep track of fonts that need to be exported.
+ std::map<wwFont, sal_uInt16> maFonts;
+
+ /// Convert from fast insertion map to linear vector in the order that we want to write.
+ std::vector< const wwFont* > AsVector() const;
+
+public:
+ wwFontHelper() : m_bLoadAllFonts(false) {}
+ /// rDoc used only to get the initial standard font(s) in use.
+ void InitFontTable(const SwDoc& rDoc);
+ sal_uInt16 GetId(const SvxFontItem& rFont);
+ sal_uInt16 GetId(const wwFont& rFont);
+ void WriteFontTable( SvStream *pTableStream, WW8Fib& pFib );
+ void WriteFontTable( DocxAttributeOutput& rAttrOutput );
+ void WriteFontTable( const RtfAttributeOutput& rAttrOutput );
+
+ /// If true, all fonts are loaded before processing the document.
+ bool m_bLoadAllFonts: 1;
+};
+
+class DrawObj
+{
+public:
+ WW8_CP mnCp; // CP-Pos of references
+ sal_uInt32 mnShapeId; // ShapeId for the SwFrameFormats
+ ww8::Frame maContent; // the frame itself
+ Point maParentPos; // Points
+ sal_Int32 mnThick; // Border Thicknesses
+ SvxFrameDirection mnDirection; // If BiDi or not
+ unsigned int mnHdFtIndex; // 0 for main text, +1 for each subsequent
+ // msword hd/ft
+
+ DrawObj(const ww8::Frame &rContent, WW8_CP nCp, Point aParentPos, SvxFrameDirection nDir,
+ unsigned int nHdFtIndex)
+ : mnCp(nCp), mnShapeId(0), maContent(rContent), maParentPos(aParentPos),
+ mnThick(0), mnDirection(nDir), mnHdFtIndex(nHdFtIndex) {}
+ void SetShapeDetails(sal_uInt32 nId, sal_Int32 nThick);
+};
+
+typedef std::vector<DrawObj> DrawObjVector;
+typedef std::vector<DrawObj *> DrawObjPointerVector;
+
+class PlcDrawObj // PC for DrawObjects and Text-/OLE-/GRF-Boxes
+{
+private:
+ DrawObjVector maDrawObjs; // vector of drawobjs
+protected:
+ virtual void RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart,
+ sal_uInt32 nLen) const = 0;
+ virtual WW8_CP GetCpOffset(const WW8Fib &rFib) const = 0;
+public:
+ PlcDrawObj() {}
+ void WritePlc( WW8Export& rWrt ) const;
+ bool Append( WW8Export const &, WW8_CP nCp, const ww8::Frame& rFormat,
+ const Point& rNdTopLeft );
+ int size() { return maDrawObjs.size(); };
+ DrawObjVector &GetObjArr() { return maDrawObjs; }
+ virtual ~PlcDrawObj();
+private:
+ PlcDrawObj(const PlcDrawObj&) = delete;
+ PlcDrawObj& operator=(const PlcDrawObj&) = delete;
+};
+
+class MainTextPlcDrawObj : public PlcDrawObj
+{
+public:
+ MainTextPlcDrawObj() {}
+private:
+ virtual void RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart,
+ sal_uInt32 nLen) const override;
+ virtual WW8_CP GetCpOffset(const WW8Fib &) const override;
+private:
+ MainTextPlcDrawObj(const MainTextPlcDrawObj&) = delete;
+ MainTextPlcDrawObj& operator=(const MainTextPlcDrawObj&) = delete;
+};
+
+class HdFtPlcDrawObj : public PlcDrawObj
+{
+public:
+ HdFtPlcDrawObj() {}
+private:
+ virtual void RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart,
+ sal_uInt32 nLen) const override;
+ virtual WW8_CP GetCpOffset(const WW8Fib &rFib) const override;
+private:
+ HdFtPlcDrawObj(const HdFtPlcDrawObj&) = delete;
+ HdFtPlcDrawObj& operator=(const HdFtPlcDrawObj&) = delete;
+};
+
+typedef std::pair<OUString, SwNodeOffset> aBookmarkPair;
+
+class WW8_WrtRedlineAuthor : public sw::util::WrtRedlineAuthor
+{
+ public:
+ virtual void Write(Writer &rWrt) override;
+};
+
+/** Structure that is used to save some of the WW8Export/DocxExport data.
+
+ It is used to be able to recurse inside of the WW8Export/DocxExport (eg.
+ for the needs of the tables) - you need to tall WriteText() from there with
+ new values of PaM etc.
+
+ It must contain all the stuff that might be saved either in WW8Export or in
+ DocxExport, because it makes no sense to do it abstract, and specialize it
+ for each of the cases. If you implement other *Export, just add the needed
+ members here, and store them in the appropriate SaveData() method.
+ */
+struct MSWordSaveData
+{
+ Point* pOldFlyOffset;
+ RndStdIds eOldAnchorType;
+ std::unique_ptr<ww::bytes> pOOld; ///< WW8Export only
+ std::shared_ptr<SwUnoCursor> pOldPam;
+ SwPaM* pOldEnd;
+ SwNodeOffset nOldStart, nOldEnd;
+ const ww8::Frame* pOldFlyFormat;
+ const SwPageDesc* pOldPageDesc;
+
+ bool bOldWriteAll : 1; ///< WW8Export only
+ bool bOldOutTable : 1;
+ bool bOldFlyFrameAttrs : 1;
+ bool bOldStartTOX : 1;
+ bool bOldInWriteTOX : 1;
+ // m_bOutPageDescs does not have to be saved in MSWordExportBase::SaveData
+ // since it is only modified when outputting special texts.
+};
+
+/// Base class for WW8Export and DocxExport
+class MSWordExportBase
+{
+public:
+ wwFontHelper m_aFontHelper;
+ std::vector<SwNodeOffset> m_aChapterFieldLocs;
+ OUString m_aMainStg;
+ std::vector<const SwTOXType*> m_aTOXArr;
+ const SfxItemSet* m_pISet; // for double attributes
+ const SwFrameFormat* m_pFirstPageFormat = nullptr;
+ std::unique_ptr<WW8_WrPct> m_pPiece; // Pointer to Piece-Table
+ std::unique_ptr<SwNumRuleTable> m_pUsedNumTable; // all used NumRules
+ /// overriding numdef index -> (existing numdef index, abstractnumdef index)
+ std::map<size_t, std::pair<size_t, size_t>> m_OverridingNums;
+ /// list-id -> abstractnumdef index
+ std::map<OUString, size_t> m_Lists;
+
+ /// Map of maps for list levels overrides
+ /// listid -> level number -> restart value
+ std::map < size_t, std::map<size_t, size_t> > m_ListLevelOverrides;
+
+ const SwTextNode *m_pTopNodeOfHdFtPage; ///< Top node of host page when in hd/ft
+ std::stack< sal_Int32 > m_aCurrentCharPropStarts; ///< To remember the position in a run.
+ std::unique_ptr<WW8_WrtBookmarks> m_pBkmks;
+ std::unique_ptr<WW8_WrtRedlineAuthor> m_pRedlAuthors;
+ std::shared_ptr<NfKeywordTable> m_pKeyMap;
+ std::optional<SvxMSExportOLEObjects> m_oOLEExp;
+ std::unique_ptr<SwMSConvertControls> m_pOCXExp;
+ WW8OleMap m_aOleMap; // To remember all already exported ole objects
+ ww8::WW8TableInfo::Pointer_t m_pTableInfo;
+
+ sal_uInt16 m_nCharFormatStart;
+ sal_uInt16 m_nFormatCollStart;
+ sal_uInt16 m_nStyleBeforeFly; ///< style number of the node
+ ///< to which the Fly is connected
+ sal_uInt16 m_nLastFormatId; ///< Style of last TextNode in normal range
+ sal_uInt16 m_nUniqueList; ///< current number for creating unique list names
+ unsigned int m_nHdFtIndex;
+
+ RedlineFlags m_nOrigRedlineFlags; ///< Remember the original redline mode
+ bool m_bOrigShowChanges; ///< Remember the original Show Changes mode
+
+public:
+ /* implicit bookmark vector containing pairs of node indexes and bookmark names */
+ std::vector<aBookmarkPair> m_aImplicitBookmarks;
+ std::unordered_map<OUString, OUString> m_TOXMarkBookmarksByURL;
+ std::unordered_map<SwTOXMark const*, OUString> m_TOXMarkBookmarksByTOXMark;
+ ww8::Frames m_aFrames; // The floating frames in this document
+ const SwPageDesc *m_pCurrentPageDesc;
+ const SwPageDesc* m_pPreviousSectionPageDesc;
+ bool m_bFirstTOCNodeWithSection;
+ std::unique_ptr<WW8_WrPlcPn> m_pPapPlc;
+ std::unique_ptr<WW8_WrPlcPn> m_pChpPlc;
+ MSWordAttrIter* m_pChpIter;
+ std::unique_ptr<MSWordStyles> m_pStyles;
+ std::unique_ptr<WW8_WrPlcAnnotations> m_pAtn;
+ std::unique_ptr<WW8_WrtFactoids> m_pFactoids;
+ std::unique_ptr<WW8_WrPlcTextBoxes> m_pTextBxs, m_pHFTextBxs;
+
+ struct LinkedTextboxInfo //help analyze textbox flow links
+ {
+ sal_Int32 nId;
+ sal_Int32 nSeq;
+ OUString sNextChain;
+ OUString sPrevChain;
+ LinkedTextboxInfo(): nId(0), nSeq(0) {}
+ };
+ std::map<OUString,LinkedTextboxInfo> m_aLinkedTextboxesHelper;
+ bool m_bLinkedTextboxesHelperInitialized = false;
+ sal_Int32 m_nLinkedTextboxesChainId=0;
+
+ const ww8::Frame *m_pParentFrame; // If set we are exporting content inside
+ // a frame, e.g. a graphic node
+
+ Point* m_pFlyOffset; // for adjusting of character-bound Fly in the Writer,
+ RndStdIds m_eNewAnchorType; // that is paragraph-bound in the WW.
+
+ std::unique_ptr<WW8_WrPlcField> m_pFieldMain; // fields in MainText
+ std::unique_ptr<WW8_WrPlcField> m_pFieldHdFt; // fields in Header/Footer
+ std::unique_ptr<WW8_WrPlcField> m_pFieldFootnote; // fields in FootNotes
+ std::unique_ptr<WW8_WrPlcField> m_pFieldEdn; // fields in EndNotes
+ std::unique_ptr<WW8_WrPlcField> m_pFieldAtn; // fields in Annotations
+ std::unique_ptr<WW8_WrPlcField> m_pFieldTextBxs; // fields in textboxes
+ std::unique_ptr<WW8_WrPlcField> m_pFieldHFTextBxs; // fields in header/footer textboxes
+ std::unique_ptr<WW8_WrMagicTable> m_pMagicTable; // keeps track of table cell positions, and
+ // marks those that contain graphics,
+ // which is required to make word display
+ // graphics inside tables
+ std::unique_ptr<SwWW8WrGrf> m_pGrf;
+ const SwAttrSet* m_pStyAttr; // StyleAttr for Tabs
+ const sw::BroadcastingModify* m_pOutFormatNode; // write Format or Node
+ const SwFormat *m_pCurrentStyle; // iff bStyDef=true, then this store the current style
+
+ std::unique_ptr<MainTextPlcDrawObj> m_pSdrObjs; // Draw-/Fly-Objects
+ std::unique_ptr<HdFtPlcDrawObj> m_pHFSdrObjs; // Draw-/Fly-Objects in header or footer
+
+ SwEscherEx* m_pEscher; // escher export class
+ // #i43447# - removed
+// SwTwips nFlyWidth, nFlyHeight; // for adaptation of graphics
+
+ sal_uInt8 m_nTextTyp;
+
+ bool m_bStyDef : 1; // should Style be written?
+ bool m_bBreakBefore : 1; // Breaks are being written 2 times
+ bool m_bOutKF : 1; // Header/Footer texts are being written
+ bool m_bOutFlyFrameAttrs : 1; // Frame-attr of Flys are being written
+ bool m_bOutPageDescs : 1; ///< PageDescs (section properties) are being written
+ bool m_bOutFirstPage : 1; // write Attrset of FirstPageDesc
+ bool m_bOutTable : 1; // table is being written
+ // ( is reset e.g. for Flys in a table )
+ bool m_bOutGrf : 1; // graphics are being written
+ bool m_bInWriteEscher : 1; // in write textboxes
+ bool m_bStartTOX : 1; // true: a TOX is started
+ bool m_bInWriteTOX : 1; // true: all content are in a TOX
+ bool m_bFootnoteAtTextEnd : 1; // true: all FTN at Textend
+ bool m_bEndAtTextEnd : 1; // true: all END at Textend
+ bool m_bHasHdr : 1;
+ bool m_bHasFtr : 1;
+ bool m_bSubstituteBullets : 1; // true: SubstituteBullet() gets called
+ bool m_bTabInTOC : 1; //true for TOC field flag 'w'
+
+ bool m_bHideTabLeaderAndPageNumbers : 1 ; // true: the 'z' field of TOC is set.
+ bool m_bExportModeRTF;
+ /// Is font size written already as part of the current character properties?
+ bool m_bFontSizeWritten;
+ bool m_bAddFootnoteTab; // only one aesthetic spacing tab per footnote
+
+ SwDoc& m_rDoc;
+ SwNodeOffset m_nCurStart, m_nCurEnd;
+ std::shared_ptr<SwUnoCursor> & m_pCurPam;
+ SwPaM *m_pOrigPam;
+
+ /// Stack to remember the nesting (see MSWordSaveData for more)
+ std::stack< MSWordSaveData > m_aSaveData;
+
+ /// Used to split the runs according to the bookmarks start and ends
+ typedef std::vector< ::sw::mark::IMark* > IMarkVector;
+ IMarkVector m_rSortedBookmarksStart;
+ IMarkVector m_rSortedBookmarksEnd;
+ IMarkVector m_rSortedAnnotationMarksStart;
+ IMarkVector m_rSortedAnnotationMarksEnd;
+
+public:
+ /// The main function to export the document.
+ ErrCode ExportDocument( bool bWriteAll );
+
+ /// Iterate through the nodes and call the appropriate OutputNode() on them.
+ void WriteText();
+
+ /// Return whether currently exported node is in table.
+ bool IsInTable() const;
+
+ /// Set the pCurPam appropriately and call WriteText().
+ ///
+ /// Used to export paragraphs in footnotes/endnotes/etc.
+ void WriteSpecialText( SwNodeOffset nStart, SwNodeOffset nEnd, sal_uInt8 nTTyp );
+
+ /// Export the pool items to attributes (through an attribute output class).
+ void ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars = false );
+
+ /// Return the numeric id of the numbering rule
+ sal_uInt16 GetNumberingId( const SwNumRule& rNumRule );
+
+ /// Return the numeric id of the style.
+ sal_uInt16 GetId( const SwTextFormatColl& rColl ) const;
+
+ /// Return the numeric id of the style.
+ sal_uInt16 GetId( const SwCharFormat* pFormat ) const;
+
+ sal_uInt16 GetId( const SwTOXType& rTOXType );
+
+ /// Return the numeric id of the font (and add it to the font list if needed)
+ sal_uInt16 GetId( const SvxFontItem& rFont)
+ {
+ return m_aFontHelper.GetId(rFont);
+ }
+ /// @overload
+ void GetId( const wwFont& rFont)
+ {
+ m_aFontHelper.GetId(rFont);
+ }
+
+ const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const;
+ template<class T> const T& GetItem( TypedWhichId<T> nWhich ) const
+ {
+ return static_cast<const T&>(GetItem(sal_uInt16(nWhich)));
+ }
+
+ /// Find the reference.
+ bool HasRefToAttr(const OUString& rName);
+ bool HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo);
+
+ /// Find the bookmark name.
+ OUString GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo );
+
+ /// Find out which style we should use in OOXML
+ OUString GetStyleRefName(const OUString& rName);
+
+ /// Use OutputItem() on an item set according to the parameters.
+ void OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript, bool bExportParentItemSet );
+
+ SvxFrameDirection GetDefaultFrameDirection( ) const;
+
+ /// Right to left?
+ SvxFrameDirection TrueFrameDirection( const SwFrameFormat& rFlyFormat ) const;
+
+ /// Right to left?
+ SvxFrameDirection GetCurrentPageDirection() const;
+
+ /// In case of numbering restart.
+
+ /// List is set to restart at a particular value so for export make a
+ /// completely new list based on this one and export that instead,
+ /// which duplicates words behaviour in this respect.
+ sal_uInt16 DuplicateNumRule(const SwNumRule* pRule, sal_uInt8 nLevel, sal_uInt16 nVal);
+ SwNumRule * DuplicateNumRuleImpl(const SwNumRule *pRule);
+
+ /// check if a new abstractNum is needed for this list
+ sal_uInt16 DuplicateAbsNum(OUString const& rListId,
+ SwNumRule const& rAbstractRule);
+
+
+ /// Create a overriding numbering definition (if it does not yet exist)
+ /// @return index of the overriding numbering definition
+ sal_uInt16 OverrideNumRule(SwNumRule const& rExistingRule,
+ OUString const& rListId,
+ SwNumRule const& rAbstractRule);
+
+ /// Store list level overrides (restart of list)
+ void AddListLevelOverride(sal_uInt16 nListId,
+ sal_uInt16 nLevelNum,
+ sal_uInt16 nStartAt);
+
+ /// Access to the attribute output class.
+ virtual AttributeOutputBase& AttrOutput() const = 0;
+
+ /// Access to the sections/headers/footres.
+ virtual MSWordSections& Sections() const = 0;
+
+ /// Determines if the import filter already quoted fields or not.
+ virtual bool FieldsQuoted() const = 0;
+
+ /// Determines the Section Breaks are to be added for TOX Section
+ virtual bool AddSectionBreaksForTOX() const = 0;
+
+ /// Used to filter out attributes that can be e.g. written to .doc but not to .docx
+ virtual bool ignoreAttributeForStyleDefaults( sal_uInt16 /*nWhich*/ ) const { return false; }
+
+ /// If saving page break is preferred as a paragraph attribute (yes) or as a special character (no).
+ virtual bool PreferPageBreakBefore() const = 0;
+
+ /// Guess the script (asian/western).
+ ///
+ /// Sadly word does not have two different sizes for asian font size and
+ /// western font size, it has two different fonts, but not sizes, so we
+ /// have to use our guess as to the script used and disable the export of
+ /// one type. The same occurs for font weight and posture (bold and
+ /// italic).
+ ///
+ /// In addition WW7- has only one character language identifier while WW8+
+ /// has two
+ virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) = 0;
+
+ virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pSwRedline = nullptr ) = 0;
+
+ virtual void AppendBookmark( const OUString& rName ) = 0;
+
+ virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) = 0;
+
+ virtual void AppendSmartTags(SwTextNode& /*rTextNode*/) { }
+
+ //For i120928,add this interface to export graphic of bullet
+ virtual void ExportGrfBullet(const SwTextNode& rNd) = 0;
+
+ // FIXME probably a hack...
+ virtual void WriteCR( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t() ) = 0;
+
+ // FIXME definitely a hack, must not be here - it can't do anything
+ // sensible for docx
+ virtual void WriteChar( sal_Unicode c ) = 0;
+
+ /// Output attributes.
+ void OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat = false );
+
+ /// Getter for pISet.
+ const SfxItemSet* GetCurItemSet() const { return m_pISet; }
+
+ /// Setter for pISet.
+ void SetCurItemSet( const SfxItemSet* pS ) { m_pISet = pS; }
+
+ /// Remember some of the members so that we can recurse in WriteText().
+ virtual void SaveData( SwNodeOffset nStt, SwNodeOffset nEnd );
+
+ /// Restore what was saved in SaveData().
+ virtual void RestoreData();
+
+ /// The return value indicates, if a follow page desc is written.
+ bool OutputFollowPageDesc( const SfxItemSet* pSet,
+ const SwTextNode* pNd );
+
+ /// Write header/footer text.
+ void WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader);
+
+ /// Format of the section.
+ static const SwSectionFormat* GetSectionFormat( const SwNode& rNd );
+
+ /// Line number of the section start.
+ static sal_uLong GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd );
+
+ /// Start new section.
+ void OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen = false );
+
+ /// Write section properties.
+ ///
+ /// pA is ignored for docx.
+ void SectionProperties( const WW8_SepInfo& rSectionInfo, WW8_PdAttrDesc* pA = nullptr );
+
+ /// Output the numbering table.
+ virtual void WriteNumbering() = 0;
+
+ /// Write static data of SwNumRule - LSTF
+ void NumberingDefinitions();
+
+ /// Write all Levels for all SwNumRules - LVLF
+ void AbstractNumberingDefinitions();
+
+ /// Write one numbering level
+ void NumberingLevel(SwNumRule const& rRule, sal_uInt8 nLvl);
+
+ // Convert the bullet according to the font.
+ void SubstituteBullet( OUString& rNumStr, rtl_TextEncoding& rChrSet,
+ OUString& rFontName ) const;
+
+ /// Setup the pA's info.
+ virtual void SetupSectionPositions( WW8_PdAttrDesc* /*pA*/ ) {}
+
+ /// Top node of host page when in header/footer.
+ void SetHdFtPageRoot( const SwTextNode *pNd ) { m_pTopNodeOfHdFtPage = pNd; }
+
+ /// Top node of host page when in header/footer.
+ const SwTextNode *GetHdFtPageRoot() const { return m_pTopNodeOfHdFtPage; }
+
+ /// Output the actual headers and footers.
+ virtual void WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
+ const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat,
+ sal_uInt8 nBreakCode, bool bEvenAndOddHeaders) = 0;
+
+ /// Write the field
+ virtual void OutputField( const SwField* pField, ww::eField eFieldType,
+ const OUString& rFieldCmd, FieldFlags nMode = FieldFlags::All ) = 0;
+
+ /// Write the data of the form field
+ virtual void WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) = 0;
+ virtual void WriteHyperlinkData( const ::sw::mark::IFieldmark& rFieldmark ) = 0;
+
+ virtual void DoComboBox(const OUString &rName,
+ const OUString &rHelp,
+ const OUString &ToolTip,
+ const OUString &rSelected,
+ const css::uno::Sequence<OUString> &rListItems) = 0;
+
+ virtual void DoFormText(const SwInputField * pField) = 0;
+
+ static bool NoPageBreakSection( const SfxItemSet *pSet );
+
+ // Compute the number format for WW dates
+ bool GetNumberFormat(const SwField& rField, OUString& rStr);
+
+ virtual sal_uInt64 ReplaceCr( sal_uInt8 nChar ) = 0;
+
+ const SfxPoolItem* HasItem( sal_uInt16 nWhich ) const;
+ template<class T> const T* HasItem(TypedWhichId<T> nWhich) const
+ {
+ return static_cast<const T*>(HasItem(sal_uInt16(nWhich)));
+ }
+
+ /// Returns the index of a picture bullet, used in numberings.
+ int GetGrfIndex(const SvxBrushItem& rBrush);
+
+ tools::Long GetParaTabStopOffset() const;
+
+ enum ExportFormat { DOC = 0, RTF = 1, DOCX = 2};
+ virtual ExportFormat GetExportFormat() const = 0;
+
+protected:
+ /// Format-dependent part of the actual export.
+ virtual ErrCode ExportDocument_Impl() = 0;
+
+ /// Get the next position in the text node to output
+ sal_Int32 GetNextPos( SwWW8AttrIter const * pAttrIter, const SwTextNode& rNode, sal_Int32 nCurrentPos );
+
+ /// Update the information for GetNextPos().
+ void UpdatePosition( SwWW8AttrIter* pAttrIter, sal_Int32 nCurrentPos );
+
+ /// Output SwTextNode
+ virtual void OutputTextNode( SwTextNode& );
+
+ /// Setup the chapter fields (maChapterFieldLocs).
+ void GatherChapterFields();
+
+ void AddLinkTarget( std::u16string_view rURL );
+ void CollectOutlineBookmarks( const SwDoc &rDoc );
+
+ bool SetCurrentPageDescFromNode(const SwNode &rNd);
+ bool ContentContainsChapterField(const SwFormatContent &rContent) const;
+ bool FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const;
+
+ virtual void SectionBreaksAndFrames( const SwTextNode& rNode ) = 0;
+
+ virtual void PrepareNewPageDesc( const SfxItemSet* pSet,
+ const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc,
+ bool bExtraPageBreak = false ) = 0;
+
+ /// Return value indicates if an inherited outline numbering is suppressed.
+ virtual bool DisallowInheritingOutlineNumbering(const SwFormat &rFormat) = 0;
+
+ /// Output SwStartNode
+ void OutputStartNode( const SwStartNode& );
+
+ /// Output SwEndNode
+ virtual void OutputEndNode( const SwEndNode& );
+
+ /// Output SwGrfNode
+ virtual void OutputGrfNode( const SwGrfNode& ) = 0;
+
+ /// Output SwOLENode
+ virtual void OutputOLENode( const SwOLENode& ) = 0;
+
+ virtual void OutputLinkedOLE( const OUString& ) = 0;
+
+ /// Output SwSectionNode
+ void OutputSectionNode( const SwSectionNode& );
+ static void UpdateTocSectionNodeProperties(const SwSectionNode& rSectionNode);
+
+ virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) = 0;
+
+ /// Call the right (virtual) function according to the type of the item.
+ ///
+ /// One of OutputTextNode(), OutputGrfNode(), or OutputOLENode()
+ void OutputContentNode( SwContentNode& );
+
+ /// Find the nearest bookmark from the current position.
+ ///
+ /// Returns false when there is no bookmark.
+ bool NearestBookmark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly );
+
+ void GetSortedBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen );
+
+ bool GetBookmarks( const SwTextNode& rNd, sal_Int32 nStt, sal_Int32 nEnd,
+ IMarkVector& rArr );
+
+ /// Find the nearest annotation mark from the current position.
+ ///
+ void NearestAnnotationMark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly );
+
+ void GetSortedAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen );
+
+ bool GetAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nStt, sal_Int32 nEnd,
+ IMarkVector& rArr );
+
+ const NfKeywordTable & GetNfKeywordTable();
+
+ void SetCurPam(SwNodeOffset nStt, SwNodeOffset nEnd);
+
+ /// Get background color of the document, if there is one.
+ std::unique_ptr<SvxBrushItem> getBackground();
+ /// Populates m_vecBulletPic with all the bullet graphics used by numberings.
+ int CollectGrfsOfBullets();
+ /// Write the numbering picture bullets.
+ void BulletDefinitions();
+
+ bool NeedSectionBreak( const SwNode& rNd ) const;
+ bool NeedTextNodeSplit( const SwTextNode& rNd, SwSoftPageBreakList& pList ) const;
+
+ std::vector<const Graphic*> m_vecBulletPic; ///< Vector to record all the graphics of bullets
+
+public:
+ MSWordExportBase(SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam);
+ virtual ~MSWordExportBase();
+
+ // TODO move as much as possible here from WW8Export! ;-)
+
+ static void CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft );
+
+ OUString BookmarkToWord(const OUString& rBookmark, bool* pIsMove = nullptr,
+ bool* pIsFrom = nullptr);
+
+private:
+ MSWordExportBase( const MSWordExportBase& ) = delete;
+ MSWordExportBase& operator=( const MSWordExportBase& ) = delete;
+
+ std::unordered_map<OUString, OUString> m_aBookmarkToWord;
+ o3tl::sorted_vector<OUString> m_aWordBookmarks;
+};
+
+/// The writer class that gets called for the WW8 filter.
+class SwWW8Writer: public StgWriter
+{
+// friends to get access to m_pExport
+// FIXME avoid that, this is probably not what we want
+// (if yes, remove the friends, and provide here a GetExport() method)
+friend void WW8_WrtRedlineAuthor::Write(Writer &rWrt);
+
+ WW8Export *m_pExport;
+ SfxMedium *mpMedium;
+
+public:
+ SwWW8Writer(std::u16string_view rFltName, const OUString& rBaseURL);
+ virtual ~SwWW8Writer() override;
+
+ virtual ErrCodeMsg WriteStorage() override;
+ virtual ErrCodeMsg WriteMedium( SfxMedium& ) override;
+
+ // TODO most probably we want to be able to get these in
+ // MSExportFilterBase
+ using Writer::getIDocumentSettingAccess;
+
+public:
+ static void InsUInt16(ww::bytes &rO, sal_uInt16 n);
+ static void InsUInt32(ww::bytes &rO, sal_uInt32 n);
+ static void InsAsString16(ww::bytes &rO, const OUString& rStr);
+ static void InsAsString8(ww::bytes & O, std::u16string_view rStr,
+ rtl_TextEncoding eCodeSet);
+
+ static sal_uLong FillUntil( SvStream& rStrm, sal_uLong nEndPos = 0 );
+ static void FillCount( SvStream& rStrm, sal_uLong nCount );
+
+ static void WriteShort( SvStream& rStrm, sal_Int16 nVal ) { rStrm.WriteInt16( nVal ); }
+ static void WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal );
+
+ static void WriteLong( SvStream& rStrm, sal_Int32 nVal ) { rStrm.WriteInt32( nVal ); }
+ static void WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal );
+
+ static void WriteString16(SvStream& rStrm, const OUString& rStr,
+ bool bAddZero);
+ static void WriteString8(SvStream& rStrm, std::u16string_view rStr,
+ bool bAddZero, rtl_TextEncoding eCodeSet);
+
+ static void WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero);
+
+ bool InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec );
+
+ using StgWriter::Write;
+ virtual ErrCodeMsg Write( SwPaM&, SfxMedium&, const OUString* ) override;
+ //Seems not an expected to provide method to access the private member
+ SfxMedium* GetMedia() { return mpMedium; }
+
+private:
+ SwWW8Writer(const SwWW8Writer&) = delete;
+ SwWW8Writer& operator=(const SwWW8Writer&) = delete;
+ ErrCode WriteStorageImpl();
+};
+
+/// Exporter of the binary Word file formats.
+class WW8Export : public MSWordExportBase
+{
+public:
+ std::unique_ptr<ww::bytes> m_pO; ///< Buffer
+
+ SvStream *m_pTableStrm, *m_pDataStrm; ///< Streams for WW97 Export
+
+ std::unique_ptr<WW8Fib> m_pFib; ///< File Information Block
+ std::unique_ptr<WW8Dop> m_pDop; ///< Document Properties
+ std::unique_ptr<WW8_WrPlcFootnoteEdn> m_pFootnote; ///< Footnotes - structure to remember them, and output
+ std::unique_ptr<WW8_WrPlcFootnoteEdn> m_pEdn; ///< Endnotes - structure to remember them, and output
+ std::unique_ptr<WW8_WrPlcSepx> m_pSepx; ///< Sections/headers/footers
+
+ bool m_bDot; ///< Template or document.
+
+protected:
+ SwWW8Writer *m_pWriter; ///< Pointer to the writer
+ std::unique_ptr<WW8AttributeOutput> m_pAttrOutput; ///< Converting attributes to stream data
+
+private:
+ tools::SvRef<SotStorage> m_xEscherStg; /// memory leak #i120098#, to hold the reference to unnamed SotStorage obj
+
+public:
+ /// Access to the attribute output class.
+ virtual AttributeOutputBase& AttrOutput() const override;
+
+ /// Access to the sections/headers/footres.
+ virtual MSWordSections& Sections() const override;
+
+ virtual bool PreferPageBreakBefore() const override { return true; }
+
+ virtual bool FieldsQuoted() const override { return false; }
+
+ virtual bool AddSectionBreaksForTOX() const override { return false; }
+private:
+ /// Format-dependent part of the actual export.
+ virtual ErrCode ExportDocument_Impl() override;
+
+ void PrepareStorage();
+ void WriteFkpPlcUsw();
+ void WriteMainText();
+ void StoreDoc1();
+
+ /// Output the numbering table.
+ virtual void WriteNumbering() override;
+
+ void OutOverrideListTab();
+ void OutListNamesTab();
+
+ void RestoreMacroCmds();
+
+ void DoComboBox(css::uno::Reference<css::beans::XPropertySet> const & xPropSet);
+
+public:
+
+ /// Setup the pA's info.
+ virtual void SetupSectionPositions( WW8_PdAttrDesc* pA ) override;
+
+ bool MiserableFormFieldExportHack(const SwFrameFormat& rFrameFormat);
+
+ SwMSConvertControls& GetOCXExp() { return *m_pOCXExp; }
+ void ExportDopTypography(WW8DopTypography &rTypo);
+
+ sal_uInt16 AddRedlineAuthor( std::size_t nId );
+
+ void WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pO = nullptr );
+ void WritePostItBegin( ww::bytes* pO = nullptr );
+ const SvxBrushItem* GetCurrentPageBgBrush() const;
+ std::shared_ptr<SvxBrushItem> TrueFrameBgBrush(const SwFrameFormat &rFlyFormat) const;
+
+ void AppendFlyInFlys(const ww8::Frame& rFrameFormat, const Point& rNdTopLeft);
+ void WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp);
+ void WriteSdrTextObj(const SdrTextObj& rObj, sal_uInt8 nTyp);
+
+ sal_uInt32 GetSdrOrdNum( const SwFrameFormat& rFormat ) const;
+ void CreateEscher();
+ void WriteEscher();
+
+ /// Write the field
+ virtual void OutputField( const SwField* pField, ww::eField eFieldType,
+ const OUString& rFieldCmd, FieldFlags nMode = FieldFlags::All ) override;
+
+ void StartCommentOutput( std::u16string_view rName );
+ void EndCommentOutput( std::u16string_view rName );
+ void OutGrf(const ww8::Frame &rFrame);
+ bool TestOleNeedsGraphic(const SwAttrSet& rSet, tools::SvRef<SotStorage> const& xOleStg,
+ const tools::SvRef<SotStorage>& xObjStg, OUString const& rStorageName,
+ SwOLENode* pOLENd);
+
+ virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pRedlineData = nullptr ) override;
+ virtual void AppendBookmark( const OUString& rName ) override;
+ void AppendBookmarkEndWithCorrection( const OUString& rName );
+
+ virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) override;
+
+ virtual void AppendSmartTags(SwTextNode& rTextNode) override;
+
+ virtual void ExportGrfBullet(const SwTextNode& rNd) override;
+ void OutGrfBullets(const ww8::Frame &rFrame);
+
+ void MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo);
+
+ void WriteAsStringTable(const std::vector<OUString>&, sal_Int32& rfcSttbf,
+ sal_Int32& rlcbSttbf);
+
+ virtual sal_uInt64 ReplaceCr( sal_uInt8 nChar ) override;
+
+ virtual void WriteCR( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t() ) override;
+ void WriteChar( sal_Unicode c ) override;
+
+ void OutSwString(const OUString&, sal_Int32 nStt, sal_Int32 nLen);
+
+ WW8_CP Fc2Cp( sal_uLong nFc ) const { return m_pPiece->Fc2Cp( nFc ); }
+
+ // some partly static semi-internal function declarations
+
+ void OutSprmBytes( sal_uInt8* pBytes, sal_uInt16 nSiz )
+ { m_pO->insert( m_pO->end(), pBytes, pBytes+nSiz ); }
+
+ virtual void SectionBreaksAndFrames( const SwTextNode& rNode ) override;
+
+ /// Helper method for OutputSectionBreaks() and OutputFollowPageDesc().
+ // #i76300#
+ virtual void PrepareNewPageDesc( const SfxItemSet* pSet,
+ const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc,
+ bool bExtraPageBreak = false ) override;
+
+ static void Out_BorderLine(ww::bytes& rO, const ::editeng::SvxBorderLine* pLine,
+ sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9,
+ bool bShadow);
+
+ void Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow);
+ static void Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * rBox );
+ void Out_CellRangeBorders(const SvxBoxItem * pBox, sal_uInt8 nStart,
+ sal_uInt8 nLimit);
+ static bool TransBrush(const Color& rCol, WW8_SHD& rShd);
+ static WW8_BRCVer9 TranslateBorderLine(const ::editeng::SvxBorderLine& pLine,
+ sal_uInt16 nDist, bool bShadow);
+
+ // #i77805# - new return value indicates, if an inherited outline numbering is suppressed
+ virtual bool DisallowInheritingOutlineNumbering(const SwFormat &rFormat) override;
+
+ unsigned int GetHdFtIndex() const { return m_nHdFtIndex; }
+ void SetHdFtIndex(unsigned int nHdFtIndex) { m_nHdFtIndex = nHdFtIndex; }
+ void IncrementHdFtIndex() { ++m_nHdFtIndex; }
+
+ /**
+ * Converts the SVX numbering type to MSONFC.
+ *
+ * This is used for section, footnote and endnote numbering purposes.
+ */
+ static sal_uInt8 GetNumId( sal_uInt16 eNumType );
+
+ /// Guess the script (asian/western).
+ virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) override;
+
+ SwTwips CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const;
+
+ /// Nasty swap for bidi if necessary
+ void MiserableRTLFrameFormatHack(SwTwips &rLeft, SwTwips &rRight,
+ const ww8::Frame &rFrameFormat);
+
+ void InsUInt16( sal_uInt16 n ) { SwWW8Writer::InsUInt16( *m_pO, n ); }
+ void InsInt16(sal_Int16 n) { InsUInt16(sal_uInt16(n)); }
+ void InsUInt32( sal_uInt32 n ) { SwWW8Writer::InsUInt32( *m_pO, n ); }
+ void WriteStringAsPara( const OUString& rText );
+
+ /// Setup the exporter.
+ WW8Export( SwWW8Writer *pWriter,
+ SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam,
+ bool bDot );
+ virtual ~WW8Export() override;
+
+ virtual void DoComboBox(const OUString &rName,
+ const OUString &rHelp,
+ const OUString &ToolTip,
+ const OUString &rSelected,
+ const css::uno::Sequence<OUString> &rListItems) override;
+
+ virtual void DoFormText(const SwInputField * pField) override;
+
+ void GetCurrentItems(ww::bytes &rItems) const;
+
+ /// Write the data of the form field
+ virtual void WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) override;
+ virtual void WriteHyperlinkData( const ::sw::mark::IFieldmark& rFieldmark ) override;
+
+ /// Fields.
+ WW8_WrPlcField* CurrentFieldPlc() const;
+
+ SwWW8Writer& GetWriter() const { return *m_pWriter; }
+ SvStream& Strm() const { return m_pWriter->Strm(); }
+
+ /// Remember some of the members so that we can recurse in WriteText().
+ virtual void SaveData( SwNodeOffset nStt, SwNodeOffset nEnd ) override;
+
+ /// Restore what was saved in SaveData().
+ virtual void RestoreData() override;
+
+ /// Output the actual headers and footers.
+ virtual void WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
+ const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat,
+ sal_uInt8 nBreakCode, bool bEvenAndOddHeaders) override;
+
+ virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOC; }
+
+protected:
+ /// Output SwGrfNode
+ virtual void OutputGrfNode( const SwGrfNode& ) override;
+
+ /// Output SwOLENode
+ virtual void OutputOLENode( const SwOLENode& ) override;
+
+ virtual void OutputLinkedOLE( const OUString& ) override;
+
+ virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) override;
+
+private:
+ WW8Export(const WW8Export&) = delete;
+ WW8Export& operator=(const WW8Export&) = delete;
+};
+
+class WW8_WrPlcSubDoc // double Plc for Footnotes/Endnotes and Postits
+{
+private:
+ WW8_WrPlcSubDoc(const WW8_WrPlcSubDoc&) = delete;
+ WW8_WrPlcSubDoc& operator=(const WW8_WrPlcSubDoc&) = delete;
+protected:
+ std::vector<WW8_CP> m_aCps;
+ std::vector<const void*> m_aContent; // PTRARR of SwFormatFootnote/PostIts/..
+ std::vector<const SwFrameFormat*> m_aSpareFormats; // a backup for aContent: if there's no SdrObject, stores the fmt directly here
+ std::unique_ptr<WW8_WrPlc0> m_pTextPos; // positions of the individual texts
+
+ WW8_WrPlcSubDoc();
+ virtual ~WW8_WrPlcSubDoc();
+
+ bool WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp, WW8_CP& rCount );
+ void WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp, WW8_FC& rTextStt,
+ sal_Int32& rTextCnt, WW8_FC& rRefStt, sal_Int32& rRefCnt ) const;
+
+ virtual const std::vector<sal_uInt32>* GetShapeIdArr() const;
+};
+
+// double Plc for Footnotes/Endnotes
+class WW8_WrPlcFootnoteEdn : public WW8_WrPlcSubDoc
+{
+private:
+ sal_uInt8 m_nTyp;
+
+ WW8_WrPlcFootnoteEdn(const WW8_WrPlcFootnoteEdn&) = delete;
+ WW8_WrPlcFootnoteEdn& operator=(WW8_WrPlcFootnoteEdn const &) = delete;
+public:
+ explicit WW8_WrPlcFootnoteEdn( sal_uInt8 nTTyp ) : m_nTyp( nTTyp ) {}
+
+ bool WriteText( WW8Export& rWrt );
+ void WritePlc( WW8Export& rWrt ) const;
+
+ void Append( WW8_CP nCp, const SwFormatFootnote& rFootnote );
+};
+
+struct WW8_Annotation
+{
+ const OutlinerParaObject* mpRichText;
+ OUString msSimpleText;
+ OUString msOwner;
+ OUString m_sInitials;
+ DateTime maDateTime;
+ WW8_CP m_nRangeStart, m_nRangeEnd;
+ bool m_bIgnoreEmpty = true;
+ WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd);
+ explicit WW8_Annotation(const SwRedlineData* pRedline);
+ /// An annotation has a range if start != end or the m_bIgnoreEmpty flag is cleared.
+ bool HasRange() const;
+};
+
+class WW8_WrPlcAnnotations : public WW8_WrPlcSubDoc // double Plc for Postits
+{
+private:
+ WW8_WrPlcAnnotations(const WW8_WrPlcAnnotations&) = delete;
+ WW8_WrPlcAnnotations& operator=(WW8_WrPlcAnnotations const &) = delete;
+ o3tl::sorted_vector<const SwRedlineData*> maProcessedRedlines;
+
+ std::map<const OUString, std::pair<WW8_CP, bool>> m_aRangeStartPositions;
+public:
+ WW8_WrPlcAnnotations() {}
+ virtual ~WW8_WrPlcAnnotations() override;
+
+ void AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp, bool bIgnoreEmpty);
+ void Append( WW8_CP nCp, const SwPostItField* pPostIt );
+ void Append( WW8_CP nCp, const SwRedlineData* pRedLine );
+ bool IsNewRedlineComment( const SwRedlineData* pRedLine );
+ bool WriteText( WW8Export& rWrt );
+ void WritePlc( WW8Export& rWrt ) const;
+};
+
+class WW8_WrPlcTextBoxes : public WW8_WrPlcSubDoc // double Plc for Textboxes
+{ // Frame/DrawTextboxes!
+private:
+ sal_uInt8 m_nTyp;
+ std::vector<sal_uInt32> m_aShapeIds; // VARARR of ShapeIds for the SwFrameFormats
+ virtual const std::vector<sal_uInt32>* GetShapeIdArr() const override;
+
+ WW8_WrPlcTextBoxes(const WW8_WrPlcTextBoxes&) = delete;
+ WW8_WrPlcTextBoxes& operator=(WW8_WrPlcTextBoxes const &) = delete;
+public:
+ explicit WW8_WrPlcTextBoxes( sal_uInt8 nTTyp ) : m_nTyp( nTTyp ) {}
+
+ bool WriteText( WW8Export& rWrt );
+ void WritePlc( WW8Export& rWrt ) const;
+ void Append( const SdrObject& rObj, sal_uInt32 nShapeId );
+ void Append( const SwFrameFormat* pFormat, sal_uInt32 nShapeId );
+ sal_uInt16 Count() const { return m_aContent.size(); }
+ sal_uInt16 GetPos( const void* p ) const
+ {
+ std::vector<const void*>::const_iterator it
+ = std::find( m_aContent.begin(), m_aContent.end(), p );
+ return it == m_aContent.end() ? USHRT_MAX : it - m_aContent.begin();
+ }
+};
+
+class WW8_WrPlcPn // Plc for Page Numbers
+{
+private:
+ WW8Export& m_rWrt;
+ // Plc for Chpx and Papx ( incl PN-Plc )
+ std::vector<std::unique_ptr<WW8_WrFkp>> m_Fkps;
+ sal_uInt16 m_nFkpStartPage;
+ ePLCFT m_ePlc;
+
+ WW8_WrPlcPn(const WW8_WrPlcPn&) = delete;
+ WW8_WrPlcPn& operator=(const WW8_WrPlcPn&) = delete;
+public:
+ WW8_WrPlcPn( WW8Export& rWrt, ePLCFT ePl, WW8_FC nStartFc );
+ ~WW8_WrPlcPn();
+ void AppendFkpEntry(WW8_FC nEndFc,short nVarLen = 0,const sal_uInt8* pSprms = nullptr);
+ void WriteFkps();
+ void WritePlc();
+ sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen);
+};
+
+// class WW8_WrPlc1 is only used for fields
+class WW8_WrPlc1
+{
+private:
+ std::vector<WW8_CP> m_aPos;
+ std::unique_ptr<sal_uInt8[]> m_pData; // content ( structures )
+ sal_uLong m_nDataLen;
+ sal_uInt16 m_nStructSiz;
+
+ WW8_WrPlc1(const WW8_WrPlc1&) = delete;
+ WW8_WrPlc1& operator=(const WW8_WrPlc1&) = delete;
+protected:
+ sal_uInt16 Count() const { return m_aPos.size(); }
+ void Write( SvStream& rStrm );
+ WW8_CP Prev() const;
+public:
+ explicit WW8_WrPlc1( sal_uInt16 nStructSz );
+ ~WW8_WrPlc1();
+ void Append( WW8_CP nCp, const void* pData );
+ void Finish( sal_uLong nLastCp, sal_uLong nStartCp );
+};
+
+// class WW8_WrPlcField is for fields
+class WW8_WrPlcField : public WW8_WrPlc1
+{
+private:
+ sal_uInt8 m_nTextTyp;
+ sal_uInt16 m_nResults;
+
+ WW8_WrPlcField(const WW8_WrPlcField&) = delete;
+ WW8_WrPlcField& operator=(const WW8_WrPlcField&) = delete;
+public:
+ WW8_WrPlcField( sal_uInt16 nStructSz, sal_uInt8 nTTyp )
+ : WW8_WrPlc1( nStructSz ), m_nTextTyp( nTTyp ), m_nResults(0)
+ {}
+ void Write( WW8Export& rWrt );
+ void ResultAdded() { ++m_nResults; }
+ sal_uInt16 ResultCount() const { return m_nResults; }
+};
+
+class WW8_WrMagicTable : public WW8_WrPlc1
+{
+private:
+ WW8_WrMagicTable(const WW8_WrMagicTable&) = delete;
+ WW8_WrMagicTable& operator=(const WW8_WrMagicTable&) = delete;
+public:
+ WW8_WrMagicTable() : WW8_WrPlc1( 4 ) {Append(0,0);}
+ void Append( WW8_CP nCp, sal_uLong nData );
+ void Write( WW8Export& rWrt );
+};
+
+class GraphicDetails
+{
+public:
+ ww8::Frame maFly; // surrounding FlyFrames
+ sal_uInt64 mnPos; // FilePos of the graphics
+ sal_uInt16 mnWid; // Width of the graphics
+ sal_uInt16 mnHei; // Height of the graphics
+
+ GraphicDetails(const ww8::Frame &rFly, sal_uInt16 nWid, sal_uInt16 nHei)
+ : maFly(rFly), mnPos(0), mnWid(nWid), mnHei(nHei)
+ {}
+
+ bool operator==(const GraphicDetails& rIn) const
+ {
+ return (
+ (mnWid == rIn.mnWid) && (mnHei == rIn.mnHei) &&
+ (maFly.RefersToSameFrameAs(rIn.maFly))
+ );
+ }
+};
+
+// class SwWW8WrGrf collects graphics and issues them
+class SwWW8WrGrf
+{
+private:
+ /// for access to the variables
+ WW8Export& m_rWrt;
+
+ std::vector<GraphicDetails> maDetails;
+ sal_uInt16 mnIdx; // index in file positions
+
+ static void WritePICFHeader(SvStream& rStrm, const ww8::Frame &rFly,
+ sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight,
+ const SwAttrSet* pAttrSet = nullptr);
+ void WriteGraphicNode(SvStream& rStrm, const GraphicDetails &rItem);
+ void WriteGrfFromGrfNode(SvStream& rStrm, const SwGrfNode &rNd,
+ const ww8::Frame &rFly, sal_uInt16 nWidth, sal_uInt16 nHeight);
+
+ static void WritePICBulletFHeader(SvStream& rStrm, const Graphic &rGrf, sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight);
+ void WriteGrfForBullet(SvStream& rStrm, const Graphic &rGrf, sal_uInt16 nWidth, sal_uInt16 nHeight);
+
+ SwWW8WrGrf(const SwWW8WrGrf&) = delete;
+ SwWW8WrGrf& operator=(const SwWW8WrGrf&) = delete;
+public:
+ explicit SwWW8WrGrf( WW8Export& rW ) : m_rWrt( rW ), mnIdx( 0 ) {}
+ void Insert(const ww8::Frame &rFly);
+ void Write();
+ sal_uLong GetFPos()
+ { return (mnIdx < maDetails.size()) ? maDetails[mnIdx++].mnPos : 0; }
+};
+
+/** The class MSWordAttrIter is a helper class to build the Fkp.chpx.
+ This is a base class to output the SwTextAttrs and the EditEngineTextAttrs.
+*/
+class MSWordAttrIter
+{
+private:
+ MSWordAttrIter* m_pOld;
+ MSWordAttrIter(const MSWordAttrIter&) = delete;
+ MSWordAttrIter& operator=(const MSWordAttrIter&) = delete;
+protected:
+ MSWordExportBase& m_rExport;
+public:
+ explicit MSWordAttrIter( MSWordExportBase& rExport );
+ virtual ~MSWordAttrIter();
+
+ virtual const SfxPoolItem* HasTextItem( sal_uInt16 nWhich ) const = 0;
+ template<class T> const T* HasTextItem( TypedWhichId<T> nWhich ) const
+ {
+ return static_cast<const T*>(HasTextItem(sal_uInt16(nWhich)));
+ }
+ virtual const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const = 0;
+};
+
+/// Used to export formatted text associated to drawings.
+class MSWord_SdrAttrIter : public MSWordAttrIter
+{
+private:
+ const EditTextObject* m_pEditObj;
+ const SfxItemPool* m_pEditPool;
+ std::vector<EECharAttrib> m_aTextAtrArr;
+ std::vector<const EECharAttrib*> m_aChrTextAtrArr;
+ std::vector<rtl_TextEncoding> m_aChrSetArr;
+ sal_Int32 m_nPara;
+ sal_Int32 m_nCurrentSwPos;
+ sal_Int32 m_nTmpSwPos; // for HasItem()
+ rtl_TextEncoding m_eNdChrSet;
+ sal_uInt16 m_nScript;
+ sal_uInt8 mnTyp;
+
+ sal_Int32 SearchNext( sal_Int32 nStartPos );
+ void SetCharSet(const EECharAttrib& rTextAttr, bool bStart);
+
+ void SetItemsThatDifferFromStandard(bool bCharAttr, SfxItemSet& rSet);
+
+ MSWord_SdrAttrIter(const MSWord_SdrAttrIter&) = delete;
+ MSWord_SdrAttrIter& operator=(const MSWord_SdrAttrIter&) = delete;
+public:
+ MSWord_SdrAttrIter( MSWordExportBase& rWr, const EditTextObject& rEditObj,
+ sal_uInt8 nType );
+ void NextPara( sal_Int32 nPar );
+ void OutParaAttr(bool bCharAttr, const std::set<sal_uInt16>* pWhichsToIgnore = nullptr);
+ void OutEEField(const SfxPoolItem& rHt);
+
+ bool IsTextAttr(sal_Int32 nSwPos);
+
+ void NextPos() { if ( m_nCurrentSwPos < SAL_MAX_INT32 ) m_nCurrentSwPos = SearchNext( m_nCurrentSwPos + 1 ); }
+
+ void OutAttr( sal_Int32 nSwPos );
+ virtual const SfxPoolItem* HasTextItem( sal_uInt16 nWhich ) const override;
+ virtual const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const override;
+ sal_Int32 WhereNext() const { return m_nCurrentSwPos; }
+ rtl_TextEncoding GetNextCharSet() const;
+ rtl_TextEncoding GetNodeCharSet() const { return m_eNdChrSet; }
+};
+
+// class SwWW8AttrIter is a helper for constructing the Fkp.chpx.
+// Only character attributes are considered; paragraph attributes do not need this treatment.
+// The paragraph and text attributes of the Writer are passed, and
+// Where() returns the next position where the attributes change.
+// IsTextAtr() tells if, at the position returned by Where(), there is
+// an attribute without end and with \xff in the text.
+// Using OutAttr(), the attributes on the passed SwPos are returned.
+class SwWW8AttrIter : public MSWordAttrIter
+{
+private:
+ const SwTextNode& m_rNode;
+
+ sw::util::CharRuns maCharRuns;
+ sw::util::CharRuns::const_iterator maCharRunIter;
+
+ rtl_TextEncoding meChrSet;
+ sal_uInt16 mnScript;
+ bool mbCharIsRTL;
+
+ const SwRangeRedline* m_pCurRedline;
+ sal_Int32 m_nCurrentSwPos;
+ SwRedlineTable::size_type m_nCurRedlinePos;
+
+ bool mbParaIsRTL;
+
+ const SwFormatDrop &mrSwFormatDrop;
+
+ ww8::Frames maFlyFrames; // #i2916#
+ ww8::FrameIter maFlyIter;
+
+ sal_Int32 SearchNext( sal_Int32 nStartPos );
+
+ void OutSwFormatRefMark(const SwFormatRefMark& rAttr);
+
+ void IterToCurrent();
+
+ SwWW8AttrIter(const SwWW8AttrIter&) = delete;
+ SwWW8AttrIter& operator=(const SwWW8AttrIter&) = delete;
+
+ void handleToggleProperty(SfxItemSet& rExportSet, const SwFormatCharFormat& rCharFormatItem);
+public:
+ SwWW8AttrIter( MSWordExportBase& rWr, const SwTextNode& rNd );
+
+ bool IsTextAttr( sal_Int32 nSwPos ) const;
+ bool IsExportableAttr(sal_Int32 nSwPos) const;
+ bool IncludeEndOfParaCRInRedlineProperties(sal_Int32 nPos) const;
+ bool IsDropCap( int nSwPos );
+ bool RequiresImplicitBookmark();
+
+ void NextPos() { if ( m_nCurrentSwPos < SAL_MAX_INT32 ) m_nCurrentSwPos = SearchNext( m_nCurrentSwPos + 1 ); }
+
+ void OutAttr(sal_Int32 nSwPos, bool bWriteCombinedChars);
+ virtual const SfxPoolItem* HasTextItem( sal_uInt16 nWhich ) const override;
+ virtual const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const override;
+ int OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos);
+ const SwRedlineData* GetParagraphLevelRedline( );
+ const SwRedlineData* GetRunLevelRedline( sal_Int32 nPos );
+ FlyProcessingState OutFlys(sal_Int32 nSwPos);
+ bool HasFlysAt(sal_Int32 nSwPos) const;
+
+ sal_Int32 WhereNext() const { return m_nCurrentSwPos; }
+ sal_uInt16 GetScript() const { return mnScript; }
+ bool IsParaRTL() const { return mbParaIsRTL; }
+ rtl_TextEncoding GetCharSet() const { return meChrSet; }
+ OUString GetSnippet(const OUString &rStr, sal_Int32 nCurrentPos,
+ sal_Int32 nLen) const;
+ const SwFormatDrop& GetSwFormatDrop() const { return mrSwFormatDrop; }
+
+ bool IsWatermarkFrame();
+ bool IsAnchorLinkedToThisNode( SwNodeOffset nNodePos );
+
+ void SplitRun( sal_Int32 nSplitEndPos );
+
+ const SwTextNode& GetNode() const { return m_rNode; }
+};
+
+/// Class to collect and output the styles table.
+class MSWordStyles
+{
+ MSWordExportBase& m_rExport;
+ sal_uInt16 m_aHeadingParagraphStyles[MAXLEVEL];
+
+ struct MapEntry
+ {
+ const SwFormat* format = nullptr;
+ const SwNumRule* num_rule = nullptr;
+ /// We need to build style id's for DOCX export; ideally we should roundtrip that, but this is good enough.
+ sal_uInt16 ww_id = ww::stiUser;
+ OUString ww_name;
+ OString style_id;
+
+ MapEntry() = default;
+ MapEntry(const SwFormat* f) : format(f) { if (f) ww_id = GetWWId(*f); }
+ MapEntry(const SwNumRule* r) : num_rule(r) {}
+ };
+ std::vector<MapEntry> m_aStyles; ///< Slot <-> Character/paragraph/list style array.
+ bool m_bListStyles; ///< If list styles are requested to be exported as well.
+
+ /// Create the style table, called from the constructor.
+ void BuildStylesTable();
+
+ /// Generate proper Word names, taking mapping between special types into account
+ void BuildWwNames();
+
+ /// Based on style names, fill in unique, MS-like names.
+ void BuildStyleIds();
+
+ /// Return information about one style.
+ void GetStyleData( const SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext, sal_uInt16& nLink );
+
+ /// Outputs attributes of one style.
+ void WriteProperties( const SwFormat* pFormat, bool bPap, sal_uInt16 nPos, bool bInsDefCharSiz );
+
+ static sal_uInt16 GetWWId( const SwFormat& rFormat );
+
+ void SetStyleDefaults( const SwFormat& rFormat, bool bPap );
+
+ /// Outputs one style - called (in a loop) from OutputStylesTable().
+ void OutputStyle( sal_uInt16 nSlot );
+
+ MSWordStyles( const MSWordStyles& ) = delete;
+ MSWordStyles& operator=( const MSWordStyles& ) = delete;
+
+public:
+ MSWordStyles( MSWordExportBase& rExport, bool bListStyles = false );
+ ~MSWordStyles();
+
+ /// Output the styles table.
+ void OutputStylesTable();
+
+ /// Get slot of the style (rFormat).
+ sal_uInt16 GetSlot( const SwFormat* pFormat ) const;
+
+ /// create style id using only ASCII characters of the style name
+ static OString CreateStyleId(std::u16string_view aName);
+
+ /// Get styleId of the nSlot-th style (nSlot is its position in m_aStyles).
+ OString const & GetStyleId(sal_uInt16 nSlot) const;
+ /// the awful TOC field references names, not styleIds
+ OUString GetStyleWWName(SwFormat const* pFormat) const;
+
+ const SwFormat* GetSwFormat(sal_uInt16 nSlot) const { return m_aStyles[nSlot].format; }
+ /// Get numbering rule of the nSlot-th style
+ const SwNumRule* GetSwNumRule(sal_uInt16 nSlot) const { return m_aStyles[nSlot].num_rule; }
+ sal_uInt16 GetHeadingParagraphStyleId(sal_uInt16 nLevel) const { return m_aHeadingParagraphStyles[ nLevel ]; }
+};
+
+#define MSWORD_MAX_STYLES_LIMIT 4091
+
+sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat);
+// A bit of a bag on the side for now
+OUString FieldString(ww::eField eIndex);
+
+class WW8SHDLong
+{
+ sal_uInt32 m_cvFore;
+ sal_uInt32 m_cvBack;
+
+public:
+ WW8SHDLong() : m_cvFore(0), m_cvBack(0) {}
+
+ void Write(WW8Export & rExport);
+ void setCvFore(sal_uInt32 cvFore) { m_cvFore = cvFore; }
+ void setCvBack(sal_uInt32 cvBack) { m_cvBack = cvBack; }
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WRTWW8_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/wrtww8gr.cxx b/sw/source/filter/ww8/wrtww8gr.cxx
new file mode 100644
index 0000000000..824794430e
--- /dev/null
+++ b/sw/source/filter/ww8/wrtww8gr.cxx
@@ -0,0 +1,872 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <sal/log.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/gdimtf.hxx>
+#include <svl/itemiter.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <svtools/embedhlp.hxx>
+
+#include <hintids.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <filter/msfilter/msoleexp.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svx/svdoole2.hxx>
+
+#include <unotools/ucbstreamhelper.hxx>
+#include <fmtanchr.hxx>
+#include <ndgrf.hxx>
+#include <frmfmt.hxx>
+#include <grfatr.hxx>
+#include <ndole.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+
+#include "sprmids.hxx"
+
+#include <doc.hxx>
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "ww8struc.hxx"
+#include "wrtww8.hxx"
+#include "ww8par.hxx"
+#include "escher.hxx"
+//Added for i120568
+#include "ww8attributeoutput.hxx"
+#include <fmturl.hxx>
+
+#include <IDocumentDrawModelAccess.hxx>
+#include <drawdoc.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+
+// TODO:
+// 5. convert the MapModes that Widows can't handle
+
+// OutGrf () is called for every GrfNode in the document. A PicLocFc-Sprm
+// will be inserted, which contains a magic number instead of an address.
+// The GrfNode-Ptr is saved in Graf-Class (used later for output of
+// the graphic and patching of the PicLocFc attributes)
+
+void WW8Export::OutputGrfNode( const SwGrfNode& /*rNode*/ )
+{
+ SAL_INFO("sw", "WW8Export::OutputGrfNode( const SwGrfNode& )" );
+ OSL_ENSURE( m_pParentFrame, "frame not set!" );
+ if ( m_pParentFrame )
+ {
+ OutGrf( *m_pParentFrame );
+ m_pFib->m_fHasPic = true;
+ }
+}
+
+bool WW8Export::TestOleNeedsGraphic(const SwAttrSet& rSet, tools::SvRef<SotStorage> const& xOleStg,
+ const tools::SvRef<SotStorage>& xObjStg,
+ OUString const& rStorageName, SwOLENode* pOLENd)
+{
+ bool bGraphicNeeded = false;
+ SfxItemIter aIter( rSet );
+ for (auto pItem = aIter.GetCurItem(); !bGraphicNeeded && pItem; pItem = aIter.NextItem())
+ {
+ switch (pItem->Which())
+ {
+ /*
+ For an inline object these properties are irrelevant because they
+ will be the same as the defaults that msword applies in their
+ absence, so if that is all that there is for these inline objects
+ then if there turns out to be enough information in the object
+ itself to regenerate the correct size and preview of the object
+ then we will not need to provide an additional graphics preview in
+ the data stream, which can save a lot of disk space.
+ */
+ case RES_FRM_SIZE:
+ case RES_CNTNT:
+ case RES_VERT_ORIENT:
+ case RES_ANCHOR:
+ break;
+ default:
+ bGraphicNeeded = true;
+ }
+ }
+
+ /*
+ Now we must see if the object contains a preview itself which is equal to
+ the preview that we are currently using. If the graphics are equal then we
+ don't need to store another preview
+ */
+ GDIMetaFile aWMF;
+ tools::Long nX=0,nY=0;
+ if (!bGraphicNeeded && SwWW8ImplReader::ImportOleWMF(xOleStg,aWMF,nX,nY))
+ {
+ // bGraphicNeeded set to true is right / fixes #i51670#.
+ bGraphicNeeded = true;
+ tools::Rectangle aRect( Point(), Size( nX, nY ) );
+ Graphic aGraph(aWMF);
+
+ ErrCode nErr = ERRCODE_NONE;
+ sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT;
+ if ( pOLENd )
+ nAspect = pOLENd->GetAspect();
+ rtl::Reference<SdrOle2Obj> pRet = SvxMSDffManager::CreateSdrOLEFromStorage(
+ *m_rDoc.getIDocumentDrawModelAccess().GetOrCreateDrawModel(),
+ rStorageName,
+ xObjStg,
+ m_rDoc.GetDocStorage(),
+ aGraph,
+ aRect,
+ tools::Rectangle(),
+ nullptr,
+ nErr,
+ 0,
+ nAspect,
+ m_pWriter->GetBaseURL());
+
+ if (pRet)
+ {
+ uno::Reference< embed::XEmbeddedObject > xObj = pOLENd->GetOLEObj().GetOleRef();
+ if ( xObj.is() )
+ {
+ std::unique_ptr<SvStream> pGraphicStream;
+ comphelper::EmbeddedObjectContainer aCnt( m_rDoc.GetDocStorage() );
+ try
+ {
+ uno::Reference< embed::XEmbedPersist > xPersist(
+ xObj,
+ uno::UNO_QUERY_THROW );
+
+ // it makes no sense to search the object in the container by reference since the object was created
+ // outside of the container and was not inserted there, only the name makes sense
+ pGraphicStream =
+ ::utl::UcbStreamHelper::CreateStream( aCnt.GetGraphicStream( xPersist->getEntryName() ) );
+ }
+ catch( const uno::Exception& )
+ {}
+
+ OSL_ENSURE( pGraphicStream && !pGraphicStream->GetError(), "No graphic stream available!" );
+ if ( pGraphicStream && !pGraphicStream->GetError() )
+ {
+ Graphic aGr1;
+ GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
+ if( rGF.ImportGraphic( aGr1, u"", *pGraphicStream ) == ERRCODE_NONE )
+ {
+ Graphic aGr2;
+ pGraphicStream =
+ ::utl::UcbStreamHelper::CreateStream( aCnt.GetGraphicStream( pRet->GetObjRef() ) );
+ if( pGraphicStream && rGF.ImportGraphic( aGr2, u"", *pGraphicStream ) == ERRCODE_NONE )
+ {
+ if ( aGr1 == aGr2 )
+ bGraphicNeeded = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ bGraphicNeeded = true;
+ return bGraphicNeeded;
+}
+
+void WW8Export::OutputOLENode( const SwOLENode& rOLENode )
+{
+ SAL_INFO("sw", "WW8Export::OutputOLENode( const SwOLENode& rOLENode )" );
+ sal_uInt8 *pSpecOLE;
+ sal_uInt8 *pDataAdr;
+ short nSize;
+ static sal_uInt8 aSpecOLE_WW8[] = {
+ 0x03, 0x6a, 0, 0, 0, 0, // sprmCPicLocation
+ 0x0a, 0x08, 1, // sprmCFOLE2
+ 0x56, 0x08, 1 // sprmCFObj
+ };
+
+ pSpecOLE = aSpecOLE_WW8;
+ nSize = sizeof( aSpecOLE_WW8 );
+ pDataAdr = pSpecOLE + 2; //WW6 sprm is 1 but has 1 byte len as well.
+
+ tools::SvRef<SotStorage> xObjStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
+
+ if( !xObjStg.is() )
+ return;
+
+ uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef());
+ if( !xObj.is() )
+ return;
+
+ const embed::XEmbeddedObject *pObj = xObj.get();
+ //Don't want to use pointer ids, as is traditional, because we need
+ //to put this into a 32bit value, and on 64bit the bottom bits
+ //might collide and two unrelated ole objects end up considered the
+ //same. Don't want to simply start at 0 which is a special value
+ sal_Int32 nPictureId = SAL_MAX_INT32 - m_aOleMap.size();
+ WW8OleMap::value_type entry = std::make_pair(pObj, nPictureId);
+ std::pair<WW8OleMap::iterator, bool> aRes = m_aOleMap.insert(entry);
+ bool bIsNotDuplicate = aRes.second; //.second is false when element already existed
+ nPictureId = aRes.first->second;
+ Set_UInt32(pDataAdr, nPictureId);
+ OUString sStorageName = "_" + OUString::number( nPictureId );
+ tools::SvRef<SotStorage> xOleStg = xObjStg->OpenSotStorage( sStorageName );
+ if( !xOleStg.is() )
+ return;
+
+ /*
+ If this object storage has been written already don't
+ waste time rewriting it
+ */
+ if (bIsNotDuplicate)
+ {
+ sal_Int64 nAspect = rOLENode.GetAspect();
+ svt::EmbeddedObjectRef aObjRef( xObj, nAspect );
+ m_oOLEExp->ExportOLEObject( aObjRef, *xOleStg );
+ if ( nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ OUString aObjInfo( "\3ObjInfo" );
+ if ( !xOleStg->IsStream( aObjInfo ) )
+ {
+ const sal_uInt8 pObjInfoData[] = { 0x40, 0x00, 0x03, 0x00 };
+ tools::SvRef<SotStorageStream> rObjInfoStream = xOleStg->OpenSotStream( aObjInfo );
+ if ( rObjInfoStream.is() && !rObjInfoStream->GetError() )
+ {
+ rObjInfoStream->WriteBytes(pObjInfoData, sizeof(pObjInfoData));
+ xOleStg->Commit();
+ }
+ }
+ }
+ }
+
+ // write as embedded field - the other things will be done
+ // in the escher export
+ OUString sServer = FieldString(ww::eEMBED) + xOleStg->GetUserName() + " ";
+
+ OutputField(nullptr, ww::eEMBED, sServer, FieldFlags::Start |
+ FieldFlags::CmdStart | FieldFlags::CmdEnd);
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(),
+ nSize, pSpecOLE );
+
+ bool bEndCR = true;
+ /*
+ In the word filter we only need a preview image for
+ floating images, and then only (the usual case) if the
+ object doesn't contain enough information to reconstruct
+ what we need.
+
+ We don't need a graphic for inline objects, so we don't
+ even need the overhead of a graphic in that case.
+ */
+ bool bGraphicNeeded = false;
+
+ if (m_pParentFrame)
+ {
+ bGraphicNeeded = true;
+
+ if (m_pParentFrame->IsInline())
+ {
+ const SwAttrSet& rSet =
+ m_pParentFrame->GetFrameFormat().GetAttrSet();
+ bEndCR = false;
+ bGraphicNeeded = TestOleNeedsGraphic(rSet,
+ xOleStg, xObjStg, sStorageName, const_cast<SwOLENode*>(&rOLENode));
+ }
+ }
+
+ if (!bGraphicNeeded)
+ WriteChar(0x1);
+ else
+ {
+ /*
+ ##897##
+ We need to insert the graphic representation of
+ this object for the inline case, otherwise word
+ has no place to find the dimensions of the ole
+ object, and will not be able to draw it
+ */
+ OutGrf(*m_pParentFrame);
+ }
+
+ OutputField(nullptr, ww::eEMBED, OUString(),
+ FieldFlags::End | FieldFlags::Close);
+
+ if (bEndCR) //No newline in inline case
+ WriteCR();
+}
+
+void WW8Export::OutputLinkedOLE( const OUString& rOleId )
+{
+ uno::Reference< embed::XStorage > xDocStg = m_rDoc.GetDocStorage();
+ uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement( "OLELinks", embed::ElementModes::READ );
+ tools::SvRef<SotStorage> xObjSrc = SotStorage::OpenOLEStorage( xOleStg, rOleId, StreamMode::READ );
+
+ tools::SvRef<SotStorage> xObjStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
+
+ if( !(xObjStg.is() && xObjSrc.is()) )
+ return;
+
+ tools::SvRef<SotStorage> xOleDst = xObjStg->OpenSotStorage( rOleId );
+ if ( xOleDst.is() )
+ xObjSrc->CopyTo( xOleDst.get() );
+
+ if ( xOleDst->GetError( ) )
+ return;
+
+ xOleDst->Commit();
+
+ // Output the cPicLocation attribute
+ std::unique_ptr<ww::bytes> pBuf( new ww::bytes );
+ SwWW8Writer::InsUInt16( *pBuf, NS_sprm::CPicLocation::val );
+ SwWW8Writer::InsUInt32( *pBuf, o3tl::toInt32(rOleId.subView( 1 )) );
+
+ SwWW8Writer::InsUInt16( *pBuf, NS_sprm::CFOle2::val );
+ pBuf->push_back( 1 );
+
+ SwWW8Writer::InsUInt16( *pBuf, NS_sprm::CFSpec::val );
+ pBuf->push_back( 1 );
+
+ SwWW8Writer::InsUInt16( *pBuf, NS_sprm::CFObj::val );
+ pBuf->push_back( 1 );
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), pBuf->size(), pBuf->data() );
+}
+
+void WW8Export::OutGrf(const ww8::Frame &rFrame)
+{
+ //Added for i120568,the hyperlink info within a graphic whose anchor type is "As character"
+ //will be exported to ensure the fidelity
+ const SwFormatURL& rURL = rFrame.GetFrameFormat().GetAttrSet().GetURL();
+ bool bURLStarted = false;
+ if( !rURL.GetURL().isEmpty() && rFrame.GetWriterType() == ww8::Frame::eGraphic)
+ {
+ bURLStarted = true;
+ m_pAttrOutput->StartURL( rURL.GetURL(), rURL.GetTargetFrameName() );
+ }
+
+ // Store the graphic settings in GrfNode so they may be written-out later
+ m_pGrf->Insert(rFrame);
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), m_pO->size(), m_pO->data() );
+ m_pO->clear();
+
+ // #i29408#
+ // linked, as-character anchored graphics have to be exported as fields.
+ const SwGrfNode* pGrfNd = rFrame.IsInline() && rFrame.GetContent()
+ ? rFrame.GetContent()->GetGrfNode() : nullptr;
+ if ( pGrfNd && pGrfNd->IsLinkedFile() )
+ {
+ OUString sStr;
+ pGrfNd->GetFileFilterNms(&sStr, nullptr);
+ sStr = FieldString(ww::eINCLUDEPICTURE) + " \"" + sStr + "\" \\d";
+
+ OutputField( nullptr, ww::eINCLUDEPICTURE, sStr,
+ FieldFlags::Start | FieldFlags::CmdStart | FieldFlags::CmdEnd );
+ }
+
+ WriteChar( char(1) ); // paste graphic symbols in the main text
+
+ sal_uInt8 aArr[ 18 ];
+ sal_uInt8* pArr = aArr;
+
+ const SwFrameFormat &rFlyFormat = rFrame.GetFrameFormat();
+ const RndStdIds eAn = rFlyFormat.GetAttrSet().GetAnchor(false).GetAnchorId();
+ if (eAn == RndStdIds::FLY_AS_CHAR)
+ {
+ sal_Int16 eVert = rFlyFormat.GetVertOrient().GetVertOrient();
+ if ((eVert == text::VertOrientation::CHAR_CENTER) || (eVert == text::VertOrientation::LINE_CENTER))
+ {
+ bool bVert = false;
+ //The default for word in vertical text mode is to center,
+ //otherwise a sub/super script hack is employed
+ if (auto pTextNd = dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) )
+ {
+ SwPosition aPos(*pTextNd);
+ bVert = m_rDoc.IsInVerticalText(aPos);
+ }
+ if (!bVert)
+ {
+ SwTwips nHeight = rFlyFormat.GetFrameSize().GetHeight();
+ nHeight/=20; //nHeight was in twips, want it in half points, but
+ //then half of total height.
+ tools::Long nFontHeight = GetItem(RES_CHRATR_FONTSIZE).GetHeight();
+ nHeight-=nFontHeight/20;
+
+ Set_UInt16( pArr, NS_sprm::CHpsPos::val );
+ Set_UInt16( pArr, - static_cast<sal_Int16>(nHeight));
+ }
+ }
+ }
+
+ // sprmCFSpec
+ Set_UInt16( pArr, 0x855 );
+ Set_UInt8( pArr, 1 );
+
+ // sprmCPicLocation
+ Set_UInt16( pArr, NS_sprm::CPicLocation::val );
+ Set_UInt32( pArr, GRF_MAGIC_321 );
+
+ // vary Magic, so that different graphic attributes will not be merged
+ static sal_uInt8 nAttrMagicIdx = 0;
+ --pArr;
+ Set_UInt8( pArr, nAttrMagicIdx++ );
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
+
+ // #i75464#
+ // Check, if graphic isn't exported as-character anchored.
+ // Otherwise, an additional paragraph is exported for a graphic, which is
+ // forced to be treated as inline, because it's anchored inside another frame.
+ if ( !rFrame.IsInline() &&
+ ( (eAn == RndStdIds::FLY_AT_PARA) ||
+ (eAn == RndStdIds::FLY_AT_PAGE) ) )
+ {
+ WriteChar( char(0x0d) ); // close the surrounding frame with CR
+
+ static sal_uInt8 nSty[2] = { 0, 0 };
+ m_pO->insert( m_pO->end(), nSty, nSty+2 ); // Style #0
+ bool bOldGrf = m_bOutGrf;
+ m_bOutGrf = true;
+
+ OutputFormat( rFrame.GetFrameFormat(), false, false, true ); // Fly-Attrs
+
+ m_bOutGrf = bOldGrf;
+ m_pPapPlc->AppendFkpEntry( Strm().Tell(), m_pO->size(), m_pO->data() );
+ m_pO->clear();
+ }
+ // #i29408#
+ // linked, as-character anchored graphics have to be exported as fields.
+ else if ( pGrfNd && pGrfNd->IsLinkedFile() )
+ {
+ OutputField( nullptr, ww::eINCLUDEPICTURE, OUString(), FieldFlags::Close );
+ }
+ //Added for i120568,the hyperlink info within a graphic whose anchor type is
+ //"As character" will be exported to ensure the fidelity
+ if( bURLStarted )
+ m_pAttrOutput->EndURL(false);
+}
+
+void SwWW8WrGrf::Insert(const ww8::Frame &rFly)
+{
+ const Size aSize( rFly.GetLayoutSize() );
+ const sal_uInt16 nWidth = static_cast< sal_uInt16 >(aSize.Width());
+ const sal_uInt16 nHeight = static_cast< sal_uInt16 >(aSize.Height());
+ maDetails.emplace_back(rFly, nWidth, nHeight);
+}
+
+void SwWW8WrGrf::WritePICFHeader(SvStream& rStrm, const ww8::Frame &rFly,
+ sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight, const SwAttrSet* pAttrSet)
+{
+ sal_Int16 nXSizeAdd = 0, nYSizeAdd = 0;
+ sal_Int16 nCropL = 0, nCropR = 0, nCropT = 0, nCropB = 0;
+
+ // write Crop-Attribute content in Header ( if available )
+ const SwCropGrf* pCropItem;
+ if (pAttrSet && (pCropItem
+ = pAttrSet->GetItemIfSet(RES_GRFATR_CROPGRF, false)))
+ {
+ nCropL = static_cast<sal_Int16>(pCropItem->GetLeft());
+ nCropR = static_cast<sal_Int16>(pCropItem->GetRight());
+ nCropT = static_cast<sal_Int16>(pCropItem->GetTop());
+ nCropB = static_cast<sal_Int16>(pCropItem->GetBottom());
+ nXSizeAdd = nXSizeAdd - static_cast<sal_Int16>( pCropItem->GetLeft() + pCropItem->GetRight() );
+ nYSizeAdd = nYSizeAdd - static_cast<sal_Int16>( pCropItem->GetTop() + pCropItem->GetBottom() );
+ }
+
+ Size aGrTwipSz(rFly.GetSize());
+ sal_uInt16 nHdrLen = 0x44;
+
+ sal_uInt8 aArr[ 0x44 ] = { 0 };
+
+ sal_uInt8* pArr = aArr + 0x2E; // Do borders first
+
+ const SwAttrSet& rAttrSet = rFly.GetFrameFormat().GetAttrSet();
+ if (const SvxBoxItem* pBox = rAttrSet.GetItemIfSet(RES_BOX, false))
+ {
+ bool bShadow = false; // Shadow ?
+ if (const SvxShadowItem* pSI = rAttrSet.GetItem<SvxShadowItem>(RES_SHADOW))
+ {
+ bShadow = (pSI->GetLocation() != SvxShadowLocation::NONE) &&
+ (pSI->GetWidth() != 0);
+ }
+
+ static const SvxBoxItemLine aLnArr[4] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
+ for(const SvxBoxItemLine & i : aLnArr)
+ {
+ const ::editeng::SvxBorderLine* pLn = pBox->GetLine( i );
+ WW8_BRC aBrc;
+ if (pLn)
+ {
+ WW8_BRCVer9 aBrc90 = WW8Export::TranslateBorderLine( *pLn,
+ pBox->GetDistance( i ), bShadow );
+ sal_uInt8 ico = msfilter::util::TransColToIco(msfilter::util::BGRToRGB(
+ aBrc90.cv()));
+ aBrc = WW8_BRC(aBrc90.dptLineWidth(), aBrc90.brcType(), ico,
+ aBrc90.dptSpace(), aBrc90.fShadow(), aBrc90.fFrame());
+ }
+
+ // use importer logic to determine how large the exported
+ // border will really be in word and adjust accordingly
+ short nSpacing;
+ short nThick = aBrc.DetermineBorderProperties(&nSpacing);
+ switch (i)
+ {
+ case SvxBoxItemLine::TOP:
+ case SvxBoxItemLine::BOTTOM:
+ nHeight -= bShadow ? nThick*2 : nThick;
+ nHeight = nHeight - nSpacing;
+ break;
+ case SvxBoxItemLine::LEFT:
+ case SvxBoxItemLine::RIGHT:
+ default:
+ nWidth -= bShadow ? nThick*2 : nThick;
+ nWidth = nWidth - nSpacing;
+ break;
+ }
+ memcpy( pArr, &aBrc.aBits1, 2);
+ pArr+=2;
+
+ memcpy( pArr, &aBrc.aBits2, 2);
+ pArr+=2;
+ }
+ }
+
+ pArr = aArr + 4; // skip lcb
+ Set_UInt16( pArr, nHdrLen ); // set cbHeader
+
+ Set_UInt16( pArr, mm ); // set mm
+
+ /*
+ Just in case our original size is too big to fit inside a ushort we can
+ substitute the final size and lose on retaining the scaling factor but
+ still keep the correct display size anyway.
+ */
+ const bool bIsSubstitutedSize = (aGrTwipSz.Width() > SHRT_MAX) || (aGrTwipSz.Height() > SHRT_MAX) ||
+ aGrTwipSz.IsEmpty();
+ if ( bIsSubstitutedSize )
+ {
+ aGrTwipSz.setWidth( nWidth );
+ aGrTwipSz.setHeight( nHeight );
+ }
+ using namespace sw::types;
+ // set xExt & yExt
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Width())));
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Height())));
+ pArr += 16;
+ // skip hMF & rcWinMF
+ // set dxaGoal & dyaGoal
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Width()));
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Height()));
+
+ if ( aGrTwipSz.Width() + nXSizeAdd ) // set mx
+ {
+ if ( !bIsSubstitutedSize )
+ {
+ const double fVal = nWidth * 1000.0 / (aGrTwipSz.Width() + nXSizeAdd );
+ Set_UInt16( pArr, o3tl::narrowing<sal_uInt16>(::rtl::math::round(fVal)) );
+ }
+ else
+ {
+ Set_UInt16( pArr, 1000 );
+ }
+ }
+ else
+ {
+ pArr += 2;
+ }
+
+ if ( aGrTwipSz.Height() + nYSizeAdd ) // set my
+ {
+ if ( !bIsSubstitutedSize )
+ {
+ const double fVal = nHeight * 1000.0 / (aGrTwipSz.Height() + nYSizeAdd);
+ Set_UInt16( pArr, o3tl::narrowing<sal_uInt16>(::rtl::math::round(fVal)) );
+ }
+ else
+ {
+ Set_UInt16( pArr, 1000 );
+ }
+ }
+ else
+ {
+ pArr += 2;
+ }
+
+ if ( !bIsSubstitutedSize )
+ {
+ Set_UInt16( pArr, nCropL ); // set dxaCropLeft
+ Set_UInt16( pArr, nCropT ); // set dyaCropTop
+ Set_UInt16( pArr, nCropR ); // set dxaCropRight
+ Set_UInt16( pArr, nCropB ); // set dyaCropBottom
+ }
+
+ rStrm.WriteBytes(aArr, nHdrLen);
+}
+
+void SwWW8WrGrf::WriteGrfFromGrfNode(SvStream& rStrm, const SwGrfNode &rGrfNd,
+ const ww8::Frame &rFly, sal_uInt16 nWidth, sal_uInt16 nHeight)
+{
+ if (rGrfNd.IsLinkedFile()) // Linked File
+ {
+ OUString aFileN;
+ rGrfNd.GetFileFilterNms( &aFileN, nullptr );
+
+ sal_uInt16 const mm = 94; // 94 = BMP, GIF
+
+ WritePICFHeader(rStrm, rFly, mm, nWidth, nHeight,
+ rGrfNd.GetpSwAttrSet());
+ rStrm.WriteUChar( aFileN.getLength() ); // write Pascal-String
+ SwWW8Writer::WriteString8(rStrm, aFileN, false,
+ RTL_TEXTENCODING_MS_1252);
+ }
+ else // Embedded File or DDE or something like that
+ {
+ WritePICFHeader(rStrm, rFly, 0x64, nWidth, nHeight,
+ rGrfNd.GetpSwAttrSet());
+ SwBasicEscherEx aInlineEscher(&rStrm, m_rWrt);
+ aInlineEscher.WriteGrfFlyFrame(rFly.GetFrameFormat(), 0x401);
+ aInlineEscher.WritePictures();
+ }
+}
+//For i120928,export graphic info of bullet
+void SwWW8WrGrf::WritePICBulletFHeader(SvStream& rStrm, const Graphic &rGrf,
+ sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight)
+{
+ sal_Int16 nXSizeAdd = 0, nYSizeAdd = 0;
+
+ Size aGrTwipSz(rGrf.GetPrefSize());
+ sal_uInt16 nHdrLen = 0x44;
+
+ sal_uInt8 aArr[ 0x44 ] = { 0 };
+
+ sal_uInt8* pArr = aArr + 0x2E; //Do borders first
+
+ static const SvxBoxItemLine aLnArr[4] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
+ SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
+ for(const SvxBoxItemLine & i : aLnArr)
+ {
+ WW8_BRC aBrc;
+
+ short nSpacing;
+ short nThick = aBrc.DetermineBorderProperties(&nSpacing);
+ switch (i)
+ {
+ case SvxBoxItemLine::TOP:
+ case SvxBoxItemLine::BOTTOM:
+ nHeight -= nThick;
+ nHeight = nHeight - nSpacing;
+ break;
+ case SvxBoxItemLine::LEFT:
+ case SvxBoxItemLine::RIGHT:
+ default:
+ nWidth -= nThick;
+ nWidth = nWidth - nSpacing;
+ break;
+ }
+ memcpy( pArr, &aBrc.aBits1, 2);
+ pArr+=2;
+
+ memcpy(pArr, &aBrc.aBits2, 2);
+ pArr+=2;
+ }
+
+ pArr = aArr + 4; //skip lcb
+ Set_UInt16( pArr, nHdrLen ); // set cbHeader
+
+ Set_UInt16( pArr, mm ); // set mm
+
+ if ( (convertTwipToMm100(aGrTwipSz.Width()) > USHRT_MAX ) || ( convertTwipToMm100(aGrTwipSz.Height()) > USHRT_MAX )
+ || aGrTwipSz.IsEmpty() )
+ {
+ aGrTwipSz.setWidth( nWidth );
+ aGrTwipSz.setHeight( nHeight );
+ }
+ using namespace sw::types;
+ // set xExt & yExt
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Width())));
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Height())));
+ pArr += 16;
+ // skip hMF & rcWinMF
+ // set dxaGoal & dyaGoal
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Width()));
+ Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Height()));
+
+ if( aGrTwipSz.Width() + nXSizeAdd ) // set mx
+ {
+ double fVal = nWidth * 1000.0 / (aGrTwipSz.Width() + nXSizeAdd);
+ Set_UInt16( pArr, o3tl::narrowing<sal_uInt16>(::rtl::math::round(fVal)) );
+ }
+ else
+ pArr += 2;
+
+ if( aGrTwipSz.Height() + nYSizeAdd ) // set my
+ {
+ double fVal = nHeight * 1000.0 / (aGrTwipSz.Height() + nYSizeAdd);
+ Set_UInt16( pArr, o3tl::narrowing<sal_uInt16>(::rtl::math::round(fVal)) );
+ }
+ else
+ pArr += 2;
+
+ Set_UInt16( pArr, 0 ); // set dxaCropLeft
+ Set_UInt16( pArr, 0 ); // set dyaCropTop
+ Set_UInt16( pArr, 0 ); // set dxaCropRight
+ Set_UInt16( pArr, 0 ); // set dyaCropBottom
+
+ rStrm.WriteBytes(aArr, nHdrLen);
+}
+
+void SwWW8WrGrf::WriteGrfForBullet(SvStream& rStrm, const Graphic &rGrf, sal_uInt16 nWidth, sal_uInt16 nHeight)
+{
+ WritePICBulletFHeader(rStrm,rGrf, 0x64,nWidth,nHeight);
+ SwBasicEscherEx aInlineEscher(&rStrm, m_rWrt);
+ aInlineEscher.WriteGrfBullet(rGrf);
+ aInlineEscher.WritePictures();
+}
+
+void SwWW8WrGrf::WriteGraphicNode(SvStream& rStrm, const GraphicDetails &rItem)
+{
+ sal_uInt16 nWidth = rItem.mnWid;
+ sal_uInt16 nHeight = rItem.mnHei;
+ sal_uInt64 nPos = rStrm.Tell(); // store start of graphic
+
+ const ww8::Frame &rFly = rItem.maFly;
+ switch (rFly.GetWriterType())
+ {
+ case ww8::Frame::eGraphic:
+ {
+ const SwNode *pNode = rItem.maFly.GetContent();
+ const SwGrfNode *pNd = pNode ? pNode->GetGrfNode() : nullptr;
+ OSL_ENSURE(pNd, "Impossible");
+ if (pNd)
+ WriteGrfFromGrfNode(rStrm, *pNd, rItem.maFly, nWidth, nHeight);
+ }
+ break;
+ //For i120928,add branch to export graphic of bullet
+ case ww8::Frame::eBulletGrf:
+ {
+ if (rItem.maFly.HasGraphic())
+ {
+ const Graphic& rGrf = rItem.maFly.GetGraphic();
+ WriteGrfForBullet(rStrm, rGrf, nWidth, nHeight);
+ }
+ }
+ break;
+
+ case ww8::Frame::eOle:
+ {
+ const SwNode *pNode = rItem.maFly.GetContent();
+ const SwOLENode *pNd = pNode ? pNode->GetOLENode() : nullptr;
+ OSL_ENSURE(pNd, "Impossible");
+ if (pNd)
+ {
+#ifdef OLE_PREVIEW_AS_EMF
+ //Convert this ole2 preview in ww8+ to an EMF for better unicode
+ //support (note that at this moment this breaks StarSymbol
+ //using graphics because I need to embed starsymbol in exported
+ //documents.
+ WritePICFHeader(rStrm, rFly, 0x64, nWidth, nHeight,
+ pNd->GetpSwAttrSet());
+ SwBasicEscherEx aInlineEscher(&rStrm, m_rWrt);
+ aInlineEscher.WriteOLEFlyFrame(rFly.GetFrameFormat(), 0x401);
+ aInlineEscher.WritePictures();
+#else
+ // cast away const
+ SwOLENode *pOleNd = const_cast<SwOLENode*>(pNd);
+ SwOLEObj& rSObj= pOleNd->GetOLEObj();
+
+ // TODO/LATER: do we need to load object?
+ Graphic* pGr = SdrOle2Obj::GetGraphicFromObject( pOleNd->GetDoc()->GetDocStorage(), rObj );
+
+ //TODO/LATER: do we really want to use GDIMetafile?!
+ GDIMetaFile aMtf;
+ if ( pGr )
+ aMtf = pGr->GetGDIMetaFile();
+
+ Size aS(aMtf.GetPrefSize());
+ aMtf.WindStart();
+ aMtf.Play(Application::GetDefaultDevice(), Point(0, 0),
+ Size(2880, 2880));
+
+ WritePICFHeader(rStrm, rFly, 8, nWidth, nHeight,
+ pNd->GetpSwAttrSet());
+ WriteWindowMetafileBits(rStrm, aMtf);
+ delete pGr;
+#endif
+ }
+ }
+ break;
+ case ww8::Frame::eDrawing:
+ case ww8::Frame::eTextBox:
+ case ww8::Frame::eFormControl:
+ /*
+ #i3958# We only export an empty dummy picture frame here, this is
+ what word does the escher export should contain an anchored to
+ character element which is drawn over this dummy and the whole
+ shebang surrounded with a SHAPE field. This isn't *my* hack :-),
+ it's what word does.
+ */
+ {
+ WritePICFHeader(rStrm, rFly, 0x64, nWidth, nHeight);
+ SwBasicEscherEx aInlineEscher(&rStrm, m_rWrt);
+ aInlineEscher.WriteEmptyFlyFrame(rFly.GetFrameFormat(), 0x401);
+ }
+ break;
+ default:
+ OSL_ENSURE(false, "Some inline export not implemented");
+ break;
+ }
+
+ sal_uInt64 nPos2 = rStrm.Tell(); // store the end
+ rStrm.Seek( nPos );
+ rStrm.WriteUInt32(nPos2 - nPos); // patch graphic length in the header
+ rStrm.Seek( nPos2 ); // restore Pos
+}
+
+// SwWW8WrGrf::Write() is called after the text.
+// It writes out all the graphics and remembers the file locations of the graphics,
+// so when writing the attributes of the items it can be patched into PicLocFc-SPRMs.
+// The search in the attributes for the Magic sal_uLong and patching
+// happens when writing the attributes. Class SwWW8WrGrf provides with
+// GetFPos() sequentially the positions
+void SwWW8WrGrf::Write()
+{
+ SvStream& rStrm = *m_rWrt.m_pDataStrm;
+ auto aEnd = maDetails.end();
+ for (auto aIter = maDetails.begin(); aIter != aEnd; ++aIter)
+ {
+ sal_uInt64 nPos = rStrm.Tell(); // align to 4 Bytes
+ if( nPos & 0x3 )
+ SwWW8Writer::FillCount( rStrm, 4 - ( nPos & 0x3 ) );
+
+ auto aIter2 = std::find(maDetails.begin(), aIter, *aIter);
+ if (aIter2 != aIter)
+ {
+ aIter->mnPos = aIter2->mnPos;
+ }
+ else
+ {
+ aIter->mnPos = rStrm.Tell();
+ WriteGraphicNode(rStrm, *aIter);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx
new file mode 100644
index 0000000000..4949c7ffe6
--- /dev/null
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -0,0 +1,6020 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*
+ * This file contains methods for the WW8 output
+ * (nodes, attributes, formats and chars).
+ */
+
+
+#include <algorithm>
+#include <hintids.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+#include <svl/grabbagitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/pbinitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflgrit.hxx>
+#include <o3tl/string_view.hxx>
+#include <fmtfld.hxx>
+#include <fchrfmt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtornt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtclds.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtftn.hxx>
+#include <fmtflcnt.hxx>
+#include <frmatr.hxx>
+#include <swtable.hxx>
+#include <fmtinfmt.hxx>
+#include <txtftn.hxx>
+#include <poolfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <list.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <paratr.hxx>
+#include <fldbas.hxx>
+#include <docufld.hxx>
+#include <expfld.hxx>
+#include <pagedesc.hxx>
+#include <ndtxt.hxx>
+#include <swrect.hxx>
+#include <redline.hxx>
+#include <reffld.hxx>
+#include <ftninfo.hxx>
+#include <charfmt.hxx>
+#include <section.hxx>
+#include <fmtline.hxx>
+#include <tox.hxx>
+#include <fmtftntx.hxx>
+#include <breakit.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <unotools/localedatawrapper.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <tgrditem.hxx>
+#include <flddropdown.hxx>
+#include <chpfld.hxx>
+#include <fmthdft.hxx>
+#include <authfld.hxx>
+#include <dbfld.hxx>
+#include <docsh.hxx>
+
+#include "sprmids.hxx"
+
+#include <fmtcntnt.hxx>
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "wrtww8.hxx"
+#include "ww8par.hxx"
+#include "ww8attributeoutput.hxx"
+#include "fields.hxx"
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/fltrcfg.hxx>
+
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+using namespace nsSwDocInfoSubType;
+using namespace sw::util;
+using namespace sw::types;
+
+bool WW8Export::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich )
+{
+ bool bRet = true;
+ if ( nScript == i18n::ScriptType::ASIAN )
+ {
+ //for asian in ww8, there is only one fontsize
+ //and one fontstyle (posture/weight) for ww6
+ //there is the additional problem that there
+ //is only one font setting for all three scripts
+ switch ( nWhich )
+ {
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ bRet = false;
+ break;
+ case RES_CHRATR_LANGUAGE:
+ case RES_CHRATR_CTL_FONT:
+ case RES_CHRATR_CTL_FONTSIZE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ case RES_CHRATR_CTL_POSTURE:
+ case RES_CHRATR_CTL_WEIGHT:
+ default:
+ break;
+ }
+ }
+ else if ( nScript == i18n::ScriptType::COMPLEX )
+ {
+ //Complex is ok in ww8, but for ww6 there is only
+ //one font, one fontsize, one fontsize (weight/posture)
+ //and only one language
+ }
+ else
+ {
+ //for western in ww8, there is only one fontsize
+ //and one fontstyle (posture/weight) for ww6
+ //there is the additional problem that there
+ //is only one font setting for all three scripts
+ switch ( nWhich )
+ {
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ bRet = false;
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ case RES_CHRATR_CTL_FONT:
+ case RES_CHRATR_CTL_FONTSIZE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ case RES_CHRATR_CTL_POSTURE:
+ case RES_CHRATR_CTL_WEIGHT:
+ default:
+ break;
+ }
+ }
+ return bRet;
+}
+
+
+void MSWordExportBase::ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars )
+{
+ for ( const auto& rItem : rItems )
+ {
+ const SfxPoolItem *pItem = rItem.second;
+ sal_uInt16 nWhich = pItem->Which();
+ if ( ( isCHRATR( nWhich ) || isTXTATR( nWhich ) ) && CollapseScriptsforWordOk( nScript, nWhich ) )
+ {
+ //In the id definition, RES_TXTATR_INETFMT must precede RES_TXTATR_CHARFMT, so that link style can overwrite char style.
+ //and in #i24291# it describes "All we want to do is ensure for now is that if a charfmt exist in the character
+ //properties that it rises to the top and is exported first."
+ //In bug 119649, it is in such situation, so we need to ignore the link style when doing ms word filter exports and
+ //add the second judgement for #i24291# definition.
+ if (nWhich == RES_TXTATR_CHARFMT)
+ {
+ const SfxPoolItem* pINetItem = SearchPoolItems(rItems, RES_TXTATR_INETFMT);
+
+ if (pINetItem)
+ {
+ const SwFormatINetFormat& rINet = pINetItem->StaticWhichCast(RES_TXTATR_INETFMT);
+ const SwCharFormat* pINetFormat = GetSwCharFormat(rINet, m_rDoc);
+ if (!pINetFormat)
+ continue;
+
+ const SwCharFormat* pFormat = pItem->StaticWhichCast(RES_TXTATR_CHARFMT).GetCharFormat();
+ ww8::PoolItems aCharItems, aINetItems;
+ GetPoolItems(pFormat->GetAttrSet(), aCharItems, false);
+ GetPoolItems(pINetFormat->GetAttrSet(), aINetItems, false);
+ for (const auto& rCharItem : aCharItems)
+ {
+ const SfxPoolItem* pCharItem = rCharItem.second;
+ sal_uInt16 nCharWhich = pCharItem->Which();
+ if (!SearchPoolItems(aINetItems, nCharWhich) && !SearchPoolItems(rItems, nCharWhich))
+ AttrOutput().OutputItem(*pCharItem);
+ }
+ continue;
+ }
+ }
+
+ // tdf#38778 Fix output of the font in DOC run for fields
+ if (pFont &&
+ nWhich == RES_TXTATR_FIELD)
+ {
+ AttrOutput().OutputItem( *pFont );
+ }
+
+ // tdf#66401 For Combined Characters in docx, MS Word uses half the normal font-size for the field's
+ // font-size, but only for <w:sz>. Therefore, we check if we are currently writing a field of type
+ // Combined Characters and if so, we half the font size.
+ if (bWriteCombChars &&
+ nWhich == RES_CHRATR_FONTSIZE)
+ {
+ SvxFontHeightItem fontHeight(item_cast<SvxFontHeightItem>( *pItem ));
+ fontHeight.SetHeight( fontHeight.GetHeight() / 2 );
+
+ AttrOutput().OutputItem( fontHeight );
+ }
+ else if (nWhich == RES_CHRATR_COLOR)
+ {
+ const SvxColorItem& rColor = pItem->StaticWhichCast(RES_CHRATR_COLOR);
+ const SfxPoolItem* pBackgroundItem = SearchPoolItems(rItems, RES_CHRATR_BACKGROUND);
+ if (rColor.GetValue() == COL_AUTO && pBackgroundItem)
+ {
+ const SvxBrushItem& rBrushBackground = pBackgroundItem->StaticWhichCast(RES_CHRATR_BACKGROUND);
+ SvxColorItem aForeground(rBrushBackground.GetColor().IsDark() ? COL_WHITE : COL_BLACK, RES_CHRATR_COLOR);
+ AttrOutput().OutputItem(aForeground);
+ }
+ else
+ {
+ // default
+ AttrOutput().OutputItem( *pItem );
+ }
+ }
+ else if (nWhich == RES_CHRATR_HIGHLIGHT)
+ {
+ const SvxBrushItem& rBrush = pItem->StaticWhichCast(RES_CHRATR_HIGHLIGHT);
+ // The UI easily adds unnecessary highlights, so identify and avoid exporting those.
+ // Highlight is not valid in character styles, so must not check there.
+ // Check the (para) style hierarchy to find the nearest defined highlight.
+ const SfxPoolItem* pInherited = nullptr;
+ if ( auto pNd = dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) ) //paragraph
+ pInherited = static_cast<SwTextFormatColl&>( pNd->GetAnyFormatColl() ).GetAttrSet().GetItem(nWhich);
+ else if ( m_bStyDef && m_pCurrentStyle && m_pCurrentStyle->DerivedFrom() ) //style
+ pInherited = &m_pCurrentStyle->DerivedFrom()->GetFormatAttr(nWhich);
+
+ // Ignore highlight if style already sets the same one.
+ // Also ignore a transparent highlight if there is no inherited highlight to cancel
+ if ( (pInherited && *pInherited != *pItem) || (!pInherited && rBrush.GetColor() != COL_TRANSPARENT) )
+ AttrOutput().OutputItem( *pItem );
+ }
+ else
+ {
+ AttrOutput().OutputItem( *pItem );
+ }
+ }
+ }
+}
+
+/*
+ * Output format as follows:
+ * - output the attributes; without parents!
+ */
+
+void MSWordExportBase::OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript,
+ bool bExportParentItemSet )
+{
+ if( !(bExportParentItemSet || rSet.Count()) )
+ return;
+
+ m_pISet = &rSet; // for double attributes
+
+ // If frame dir is set, but not adjust, then force adjust as well
+ if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, bExportParentItemSet ) )
+ {
+ // No explicit adjust set ?
+ if ( SfxItemState::SET != rSet.GetItemState( RES_PARATR_ADJUST, bExportParentItemSet ) )
+ {
+ const SvxAdjustItem* pItem = rSet.GetItem( RES_PARATR_ADJUST, bExportParentItemSet );
+ if ( nullptr != pItem )
+ {
+ // then set the adjust used by the parent format
+ AttrOutput().OutputItem( *pItem );
+ }
+ }
+ }
+
+ const SwNumRuleItem* pRuleItem;
+ if ( bPapFormat && (pRuleItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, bExportParentItemSet )) )
+ {
+ AttrOutput().OutputItem( *pRuleItem );
+
+ // switch off the numbering?
+ const SfxPoolItem* pLRItem;
+ if ( pRuleItem->GetValue().isEmpty() &&
+ SfxItemState::SET != rSet.GetItemState(RES_MARGIN_FIRSTLINE, false) &&
+ (pLRItem = rSet.GetItemIfSet(RES_MARGIN_FIRSTLINE)) )
+ {
+ // set the LR-Space of the parentformat!
+ AttrOutput().OutputItem( *pLRItem );
+ }
+ if ( pRuleItem->GetValue().isEmpty() &&
+ SfxItemState::SET != rSet.GetItemState(RES_MARGIN_TEXTLEFT, false) &&
+ (pLRItem = rSet.GetItemIfSet(RES_MARGIN_TEXTLEFT)) )
+ {
+ // set the LR-Space of the parentformat!
+ AttrOutput().OutputItem( *pLRItem );
+ }
+ }
+
+ ww8::PoolItems aItems;
+ GetPoolItems( rSet, aItems, bExportParentItemSet );
+ if ( bChpFormat )
+ ExportPoolItemsToCHP(aItems, nScript, nullptr);
+ if ( bPapFormat )
+ {
+ const bool bAlreadyOutputBrushItem = AttrOutput().MaybeOutputBrushItem(rSet);
+
+ for ( const auto& rItem : aItems )
+ {
+ const SfxPoolItem* pItem = rItem.second;
+ sal_uInt16 nWhich = pItem->Which();
+ // Handle fill attributes just like frame attributes for now.
+ if ( (nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END && nWhich != RES_PARATR_NUMRULE ) ||
+ (nWhich >= XATTR_FILL_FIRST && nWhich < XATTR_FILL_LAST))
+ AttrOutput().OutputItem( *pItem );
+ }
+
+ // Has to be called after RES_PARATR_GRABBAG is processed.
+ const XFillStyleItem* pFill(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
+ if (!bAlreadyOutputBrushItem && pFill
+ && (pFill->GetValue() == drawing::FillStyle_SOLID || pFill->GetValue() == drawing::FillStyle_NONE)
+ && !rSet.GetItem(RES_BACKGROUND, false))
+ {
+ const bool bFillStyleNone = pFill->GetValue() == drawing::FillStyle_NONE;
+ // No need to write out a NONE background if it can't inherit something else, or if it already inherits a NONE.
+ std::unique_ptr<SvxBrushItem> pInherited;
+ if (bFillStyleNone)
+ {
+ if ( auto pNd = dynamic_cast<const SwContentNode*>(m_pOutFormatNode)) //paragraph
+ pInherited = getSvxBrushItemFromSourceSet(static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet(), RES_BACKGROUND);
+ else if (m_bStyDef && m_pCurrentStyle && m_pCurrentStyle->DerivedFrom()) //style
+ pInherited = getSvxBrushItemFromSourceSet(m_pCurrentStyle->DerivedFrom()->GetAttrSet(), RES_BACKGROUND);
+ }
+ // Construct an SvxBrushItem, as expected by the exporters.
+ std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
+ if (!bFillStyleNone || (pInherited && *pInherited != *aBrush))
+ AttrOutput().OutputItem(*aBrush);
+ }
+#if 0
+ else
+ {
+ // note: *does not work* due to undocumented Word behavior: must be before a:ln element at least
+ AttrOutput().MaybeOutputBrushItem(rSet);
+ }
+#endif
+ }
+ m_pISet = nullptr; // for double attributes
+}
+
+void MSWordExportBase::GatherChapterFields()
+{
+ //If the header/footer contains a chapter field
+ SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter );
+ pType->GatherNodeIndex(m_aChapterFieldLocs);
+}
+
+bool MSWordExportBase::ContentContainsChapterField(const SwFormatContent &rContent) const
+{
+ bool bRet = false;
+ if ( const SwNodeIndex* pSttIdx = rContent.GetContentIdx() )
+ {
+ SwNodeIndex aIdx( *pSttIdx, 1 );
+ SwNodeIndex aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
+ SwNodeOffset nStart = aIdx.GetIndex();
+ SwNodeOffset nEnd = aEnd.GetIndex();
+ //If the header/footer contains a chapter field
+ bRet = std::any_of(m_aChapterFieldLocs.cbegin(), m_aChapterFieldLocs.cend(),
+ [nStart, nEnd](SwNodeOffset i) { return ( nStart <= i ) && ( i <= nEnd ); });
+ }
+ return bRet;
+}
+
+bool MSWordExportBase::FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const
+{
+ if ( m_aChapterFieldLocs.empty() )
+ return false;
+
+ const SwFrameFormat *pFormat = nullptr;
+
+ pFormat = rFormat.GetHeader().GetHeaderFormat();
+ if ( pFormat && ContentContainsChapterField( pFormat->GetContent() ) )
+ return true;
+
+ pFormat = rFormat.GetFooter().GetFooterFormat();
+ return pFormat && ContentContainsChapterField( pFormat->GetContent() );
+}
+
+bool MSWordExportBase::SetCurrentPageDescFromNode(const SwNode &rNd)
+{
+ bool bNewPageDesc = false;
+ const SwPageDesc* pCurrent = SwPageDesc::GetPageDescOfNode(rNd);
+ OSL_ENSURE(pCurrent && m_pCurrentPageDesc, "Not possible surely");
+ if (m_pCurrentPageDesc && pCurrent)
+ {
+ if (pCurrent != m_pCurrentPageDesc)
+ {
+ if (m_pCurrentPageDesc->GetFollow() != pCurrent)
+ bNewPageDesc = true;
+ else
+ {
+ const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster();
+ const SwFrameFormat& rFollowFormat = pCurrent->GetMaster();
+
+ bNewPageDesc = !IsPlausableSingleWordSection(rTitleFormat,
+ rFollowFormat);
+ }
+ m_pCurrentPageDesc = pCurrent;
+ }
+ else
+ {
+ const SwFrameFormat &rFormat = pCurrent->GetMaster();
+ bNewPageDesc = FormatHdFtContainsChapterField(rFormat);
+ }
+ }
+ return bNewPageDesc;
+}
+
+/**
+ * WW only knows Break-After (page break and section breaks),
+ * whereas in SW page breaks exist both "before" and "after" and PageDesc exists
+ * only "before". Therefore the breaks are iterated two times, namely before
+ * and after every line.
+ * Depending on the break type they're set before or after the line.
+ * Only functions can be called, which do not write in output area pO,
+ * because that one only exits once for CHP and PAP and therefore end up in
+ * the wrong one.
+ */
+void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen )
+{
+ if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs )
+ return;
+
+ m_bBreakBefore = true;
+ bool bNewPageDesc = false;
+ const SwFormatPageDesc *pPgDesc=nullptr;
+ bool bExtraPageBreakBeforeSectionBreak = false;
+
+ //Output a sectionbreak if there's a new pagedescriptor. Otherwise output a
+ //pagebreak if there is a pagebreak here, unless the new page (follow
+ //style) is different to the current one, in which case plump for a
+ //section.
+ bool bBreakSet = false;
+
+ const SwPageDesc * pPageDesc = rNd.FindPageDesc();
+
+ // Even if m_pCurrentPageDesc != pPageDesc ,it might be because of the different header & footer types.
+ if (m_pCurrentPageDesc != pPageDesc)
+ {
+ if (isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() ))
+ {
+ /*
+ * If Table cell is open and page header types are different
+ * set pSet to NULL as we don't want to add any section breaks inside a table.
+ */
+ pSet = nullptr;
+ }
+ else if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster()))
+ {
+ bBreakSet = true;
+ bNewPageDesc = true;
+ m_pCurrentPageDesc = pPageDesc;
+ }
+ }
+
+ if ( pSet && pSet->Count() )
+ {
+ const SwFormatPageDesc * pItem = pSet->GetItemIfSet( RES_PAGEDESC, false );
+ if ( pItem && pItem->GetRegisteredIn() != nullptr)
+ {
+ bBreakSet = true;
+ // Avoid unnecessary section breaks if possible. LO can't notice identical
+ // sections during import, so minimize unnecessary duplication
+ // by substituting a simple page break when the resulting section is identical,
+ // unless this is needed to re-number the page.
+ // DOCX only.
+ if (!bNewPageDesc && !pItem->GetNumOffset() && !PreferPageBreakBefore()
+ && m_pCurrentPageDesc && m_pCurrentPageDesc->GetFollow() == pItem->GetPageDesc())
+ {
+ // A section break on the very first paragraph is ignored by LO/Word
+ // and should NOT be turned into a page break.
+ SwNodeIndex aDocEnd(m_rDoc.GetNodes().GetEndOfContent());
+ SwNodeIndex aStart(*aDocEnd.GetNode().StartOfSectionNode());
+ // Set aStart to the first content node in the section
+ m_rDoc.GetNodes().GoNext(&aStart);
+ assert(aStart <= aDocEnd && "impossible: end section must have one content node");
+ if (rNd.GetIndex() > aStart.GetNode().GetIndex())
+ AttrOutput().OutputItem(SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
+ }
+ else
+ bNewPageDesc = true;
+
+ pPgDesc = pItem;
+ m_pCurrentPageDesc = pPgDesc->GetPageDesc();
+
+ // tdf#121666: nodes that have pagebreak + sectionbreak may need to export both breaks
+ // tested / implemented with docx format only.
+ // If other formats (rtf /doc) need similar fix, then that may can be done similar way.
+ if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
+ {
+ bExtraPageBreakBeforeSectionBreak = true;
+ }
+ }
+ else if ( const SvxFormatBreakItem* pBreak = pSet->GetItemIfSet( RES_BREAK, false ) )
+ {
+ // Word does not like hard break attributes in some table cells
+ bool bRemoveHardBreakInsideTable = false;
+ if ( m_bOutTable )
+ {
+ const SwTableNode* pTableNode = rNd.FindTableNode();
+ if ( pTableNode )
+ {
+ const SwTableBox* pBox = rNd.GetTableBox();
+ const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
+ // but only for non-complex tables
+ if ( pLine && !pLine->GetUpper() )
+ {
+ // check if box is not first in that line:
+ if ( 0 < pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
+ {
+ bRemoveHardBreakInsideTable = true;
+ }
+ }
+ }
+ }
+ bBreakSet = true;
+
+ if ( !bRemoveHardBreakInsideTable )
+ {
+ OSL_ENSURE(m_pCurrentPageDesc, "should not be possible");
+ /*
+ If because of this pagebreak the page desc following the page
+ break is the follow style of the current page desc then output a
+ section break using that style instead. At least in those cases
+ we end up with the same style in word and writer, nothing can be
+ done when it happens when we get a new pagedesc because we
+ overflow from the first page style.
+ */
+ if ( m_pCurrentPageDesc )
+ {
+ // #i76301# - assure that there is a page break before set at the node.
+ if ( pBreak->GetBreak() == SvxBreak::PageBefore )
+ {
+ bNewPageDesc |= SetCurrentPageDescFromNode( rNd );
+ }
+ }
+
+ // If the columns are different in LO's adjacent sections, create a new MS section
+ if (!bNewPageDesc && pBreak->GetBreak() == SvxBreak::PageBefore
+ && Sections().CurrentSectionInfo())
+ {
+ const SwSectionFormat* pSectionFormat = MSWordExportBase::GetSectionFormat(rNd);
+ if (pSectionFormat)
+ {
+ const SwFormatCol& rNewSect = pSectionFormat->GetFormatAttr(RES_COL);
+ const SwFormatCol& rPrevSect
+ = MSWordSections::GetFormatCol(m_rDoc,
+ *Sections().CurrentSectionInfo());
+ if (rNewSect.GetNumCols() != rPrevSect.GetNumCols()
+ || !rNewSect.IsOrtho() || !rPrevSect.IsOrtho()
+ || rNewSect.GetLineStyle() != rPrevSect.GetLineStyle()
+ || rNewSect.GetLineWidth() != rPrevSect.GetLineWidth()
+ || rNewSect.GetLineColor() != rPrevSect.GetLineColor()
+ || rNewSect.GetLineHeight() != rPrevSect.GetLineHeight()
+ || rNewSect.GetLineAdj() != rPrevSect.GetLineAdj())
+ {
+ bNewPageDesc = true;
+ }
+ }
+ }
+
+ if ( !bNewPageDesc )
+ AttrOutput().OutputItem( *pBreak );
+ }
+ }
+ }
+
+ /*
+ #i9301#
+ No explicit page break, lets see if the style had one and we've moved to a
+ new page style because of it, if we have to then we take the opportunity to
+ set the equivalent word section here. We *could* do it for every paragraph
+ that moves onto a new page because of layout, but that would be insane.
+ */
+ bool bHackInBreak = false;
+ if ( !bBreakSet )
+ {
+ if ( const SwContentNode *pNd = rNd.GetContentNode() )
+ {
+ const SvxFormatBreakItem &rBreak = pNd->GetAttr( RES_BREAK );
+ if ( rBreak.GetBreak() == SvxBreak::PageBefore )
+ bHackInBreak = true;
+ else
+ { // Even a pagedesc item is set, the break item can be set 'NONE',
+ // but a pagedesc item is an implicit page break before...
+ const SwFormatPageDesc &rPageDesc = pNd->GetAttr( RES_PAGEDESC );
+ if ( rPageDesc.KnowsPageDesc() )
+ bHackInBreak = true;
+ }
+ }
+ }
+
+ if ( bHackInBreak )
+ {
+ OSL_ENSURE( m_pCurrentPageDesc, "should not be possible" );
+ if ( m_pCurrentPageDesc )
+ bNewPageDesc = SetCurrentPageDescFromNode( rNd );
+ }
+
+ if ( bNewPageDesc && m_pCurrentPageDesc )
+ {
+ PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc, bExtraPageBreakBeforeSectionBreak );
+ }
+ m_bBreakBefore = false;
+}
+
+// #i76300#
+bool MSWordExportBase::OutputFollowPageDesc( const SfxItemSet* pSet, const SwTextNode* pNd )
+{
+ bool bRet = false;
+
+ if ( pNd &&
+ m_pCurrentPageDesc &&
+ m_pCurrentPageDesc != m_pCurrentPageDesc->GetFollow() )
+ {
+ PrepareNewPageDesc( pSet, *pNd, nullptr, m_pCurrentPageDesc->GetFollow() );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+const SwSectionFormat* MSWordExportBase::GetSectionFormat( const SwNode& rNd )
+{
+ const SwSectionFormat* pFormat = nullptr;
+ const SwSectionNode* pSect = rNd.FindSectionNode();
+ if ( pSect &&
+ SectionType::Content == pSect->GetSection().GetType() )
+ {
+ pFormat = pSect->GetSection().GetFormat();
+ }
+
+ return pFormat;
+}
+
+sal_uLong MSWordExportBase::GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd )
+{
+ const SwFormatLineNumber* pNItem = nullptr;
+ if ( pSet )
+ {
+ pNItem = & pSet->Get( RES_LINENUMBER );
+ }
+ else if ( const SwContentNode *pNd = rNd.GetContentNode() )
+ {
+ pNItem = &pNd->GetAttr( RES_LINENUMBER );
+ }
+
+ return pNItem? pNItem->GetStartValue() : 0;
+}
+
+void WW8Export::PrepareNewPageDesc( const SfxItemSet*pSet,
+ const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc,
+ bool /*bExtraPageBreak*/ )
+{
+ // The PageDescs will only be inserted in WW8Writer::pSepx with the corresponding
+ // position by the occurrences of PageDesc attributes. The construction and
+ // output of the attributes and header/footer of the PageDesc are done
+ // after the main text and its attributes.
+
+ sal_uLong nFcPos = ReplaceCr( msword::PageBreak ); // Page/Section-Break
+
+ // actually nothing is outputted here, rather the arrays aCps, aSects
+ // accordingly completed
+ if ( !nFcPos )
+ return;
+
+ const SwSectionFormat* pFormat = GetSectionFormat( rNd );
+ const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd );
+
+ OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." );
+
+ if ( pNewPgDescFormat )
+ {
+ m_pSepx->AppendSep( Fc2Cp( nFcPos ), *pNewPgDescFormat, rNd, pFormat, nLnNm );
+ }
+ else if ( pNewPgDesc )
+ {
+ m_pSepx->AppendSep( Fc2Cp( nFcPos ), pNewPgDesc, rNd, pFormat, nLnNm );
+ }
+}
+
+void MSWordExportBase::CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft )
+{
+ const SvxTabStopItem *pItem = rSet.GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
+ if (!pItem)
+ return;
+
+ // then it must be corrected for the output
+ SvxTabStopItem aTStop(*pItem);
+ for ( sal_uInt16 nCnt = 0; nCnt < aTStop.Count(); ++nCnt )
+ {
+ SvxTabStop& rTab = const_cast<SvxTabStop&>(aTStop[ nCnt ]);
+ if ( SvxTabAdjust::Default != rTab.GetAdjustment() &&
+ rTab.GetTabPos() >= nAbsLeft )
+ {
+ rTab.GetTabPos() -= nAbsLeft;
+ }
+ else
+ {
+ aTStop.Remove( nCnt );
+ --nCnt;
+ }
+ }
+ rSet.Put( aTStop );
+}
+
+tools::Long MSWordExportBase::GetParaTabStopOffset() const
+{
+ tools::Long nOffset = 0;
+ // Tabs are absolute by default.
+ if (m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT))
+ {
+ // don't do it for editengine text, it doesn't implement this anyway
+ if (!m_pISet || m_pISet->GetRanges()[0].first < RES_WHICHHINT_END)
+ {
+ nOffset = GetItem(RES_MARGIN_TEXTLEFT).GetTextLeft();
+ }
+ }
+ return nOffset;
+}
+
+sal_uInt8 WW8Export::GetNumId( sal_uInt16 eNumType )
+{
+ sal_uInt8 nRet = 0;
+ switch( eNumType )
+ {
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break;
+ case SVX_NUM_ROMAN_UPPER: nRet = 1; break;
+ case SVX_NUM_ROMAN_LOWER: nRet = 2; break;
+ case style::NumberingType::TEXT_NUMBER: nRet = 5; break;
+ case style::NumberingType::TEXT_CARDINAL: nRet = 6; break;
+ case style::NumberingType::TEXT_ORDINAL: nRet = 7; break;
+ case style::NumberingType::AIU_HALFWIDTH_JA: nRet = 12; break;
+ case style::NumberingType::IROHA_HALFWIDTH_JA: nRet = 13; break;
+ case style::NumberingType::FULLWIDTH_ARABIC: nRet = 14; break;
+ case style::NumberingType::NUMBER_TRADITIONAL_JA: nRet = 16; break;
+ case style::NumberingType::CIRCLE_NUMBER: nRet = 18; break;
+ case style::NumberingType::AIU_FULLWIDTH_JA: nRet = 20; break;
+ case style::NumberingType::IROHA_FULLWIDTH_JA: nRet = 21; break;
+
+ case SVX_NUM_BITMAP:
+ case SVX_NUM_CHAR_SPECIAL: nRet = 23; break;
+ case style::NumberingType::HANGUL_SYLLABLE_KO: nRet = 24; break;// ganada
+ case style::NumberingType::HANGUL_JAMO_KO: nRet = 25; break;// chosung
+ case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: nRet = 24; break;
+ case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: nRet = 25; break;
+ case style::NumberingType::TIAN_GAN_ZH: nRet = 30; break;
+ case style::NumberingType::DI_ZI_ZH: nRet = 31; break;
+ case style::NumberingType::NUMBER_UPPER_ZH_TW: nRet = 34;break;
+ case style::NumberingType::NUMBER_UPPER_ZH: nRet = 38; break;
+ case style::NumberingType::NUMBER_DIGITAL_KO: nRet = 41; break;
+ case style::NumberingType::NUMBER_HANGUL_KO: nRet = 42; break;
+ case style::NumberingType::NUMBER_LEGAL_KO: nRet = 43; break;
+ case style::NumberingType::NUMBER_DIGITAL2_KO: nRet = 44; break;
+ case style::NumberingType::NUMBER_HEBREW: nRet = 45; break;
+ case style::NumberingType::CHARS_ARABIC: nRet = 46; break;
+ case style::NumberingType::CHARS_HEBREW: nRet = 47; break;
+ case style::NumberingType::CHARS_ARABIC_ABJAD: nRet = 48; break;
+ case style::NumberingType::CHARS_PERSIAN:
+ case style::NumberingType::CHARS_NEPALI: nRet = 49; break;
+ case style::NumberingType::CHARS_THAI: nRet = 53; break;
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU:
+ case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU: nRet = 58; break;
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU:
+ case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU: nRet = 59; break;
+ // nothing, WW does the same (undocumented)
+ case SVX_NUM_NUMBER_NONE: nRet = 0xff; break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ // 0x09, msonfcChiManSty
+ nRet = 9;
+ break;
+ case SVX_NUM_ARABIC_ZERO:
+ // 0x16, msonfcArabicLZ
+ nRet = 22;
+ break;
+ }
+ return nRet;
+}
+
+void WW8AttributeOutput::OutlineNumbering(sal_uInt8 /*nLvl*/)
+{
+ // Handled by ParaOutlineLevel and ParaNumRule
+}
+
+// #i77805#
+bool WW8Export::DisallowInheritingOutlineNumbering(const SwFormat &rFormat)
+{
+ bool bRet( false );
+
+ //If there is no numbering on this fmt, but its parent was outline
+ //numbered, then in writer this is no inheritied, but in word it would
+ //be, so we must export "no numbering" and "body level" to make word
+ //behave like writer (see #i25755)
+ if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
+ {
+ if (const SwFormat *pParent = rFormat.DerivedFrom())
+ {
+ if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle())
+ {
+ SwWW8Writer::InsUInt16(*m_pO, NS_sprm::POutLvl::val);
+ m_pO->push_back(sal_uInt8(9));
+ SwWW8Writer::InsUInt16(*m_pO, NS_sprm::PIlfo::val);
+ SwWW8Writer::InsUInt16(*m_pO, 0);
+
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void MSWordExportBase::OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat )
+{
+ bool bCallOutSet = true;
+ const sw::BroadcastingModify* pOldMod = m_pOutFormatNode;
+ m_pOutFormatNode = &rFormat;
+
+ switch( rFormat.Which() )
+ {
+ case RES_CONDTXTFMTCOLL:
+ case RES_TXTFMTCOLL:
+ if( bPapFormat )
+ {
+ int nLvl = MAXLEVEL;
+
+ if (static_cast<const SwTextFormatColl&>(rFormat).IsAssignedToListLevelOfOutlineStyle())
+ nLvl = static_cast<const SwTextFormatColl&>(rFormat).GetAssignedOutlineStyleLevel();
+
+ if (nLvl >= 0 && nLvl < MAXLEVEL)
+ {
+ //if outline numbered
+ // if Write StyleDefinition then write the OutlineRule
+ const SwNumFormat& rNFormat = m_rDoc.GetOutlineNumRule()->Get( o3tl::narrowing<sal_uInt16>( nLvl ) );
+ if ( m_bStyDef )
+ AttrOutput().OutlineNumbering(static_cast<sal_uInt8>(nLvl));
+
+ if ( rNFormat.GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION &&
+ rNFormat.GetAbsLSpace() )
+ {
+ SfxItemSet aSet( rFormat.GetAttrSet() );
+ SvxFirstLineIndentItem firstLine(aSet.Get(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(aSet.Get(RES_MARGIN_TEXTLEFT));
+
+ leftMargin.SetTextLeft(leftMargin.GetTextLeft() + rNFormat.GetAbsLSpace());
+ firstLine.SetTextFirstLineOffset(GetWordFirstLineOffset(rNFormat));
+
+ aSet.Put(firstLine);
+ aSet.Put(leftMargin);
+ CorrectTabStopInSet( aSet, rNFormat.GetAbsLSpace() );
+ OutputItemSet( aSet, bPapFormat, bChpFormat,
+ i18n::ScriptType::LATIN, m_bExportModeRTF);
+ bCallOutSet = false;
+ }
+ }
+ else
+ {
+ //otherwise we might have to remove outline numbering from
+ //what gets exported if the parent style was outline numbered
+ // #i77805#
+ // If inherited outline numbering is suppress, the left/right
+ // margins has to be exported explicitly.
+ if ( m_bStyDef && DisallowInheritingOutlineNumbering(rFormat) )
+ {
+ SfxItemSet aSet( rFormat.GetAttrSet() );
+ SvxFirstLineIndentItem const& rFirstLine(aSet.Get(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem const& rLeftMargin(aSet.Get(RES_MARGIN_TEXTLEFT));
+ aSet.Put(rFirstLine);
+ aSet.Put(rLeftMargin);
+ OutputItemSet( aSet, bPapFormat, bChpFormat,
+ css::i18n::ScriptType::LATIN, m_bExportModeRTF);
+ bCallOutSet = false;
+ }
+ }
+ }
+ break;
+
+ case RES_CHRFMT:
+ break;
+ case RES_FLYFRMFMT:
+ if (bFlyFormat)
+ {
+ OSL_ENSURE(m_pParentFrame, "No parent frame, all broken");
+
+ if (m_pParentFrame)
+ {
+ const SwFrameFormat &rFrameFormat = m_pParentFrame->GetFrameFormat();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet(m_rDoc.GetAttrPool());
+ aSet.Set(rFrameFormat.GetAttrSet());
+
+ // Fly as character becomes a paragraph bound
+ // now set the distance to paragraph margin
+ if (m_pFlyOffset)
+ {
+ aSet.Put(SwFormatHoriOrient(m_pFlyOffset->X()));
+ aSet.Put(SwFormatVertOrient(m_pFlyOffset->Y()));
+ SwFormatAnchor aAnchor(rFrameFormat.GetAnchor());
+ aAnchor.SetType(m_eNewAnchorType);
+ aSet.Put(aAnchor);
+ }
+
+ if (SfxItemState::SET != aSet.GetItemState(RES_SURROUND))
+ aSet.Put(SwFormatSurround(css::text::WrapTextMode_NONE));
+
+ const XFillStyleItem* pXFillStyleItem(rFrameFormat.GetAttrSet().GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
+ if (pXFillStyleItem)
+ {
+ switch (pXFillStyleItem->GetValue())
+ {
+ case drawing::FillStyle_NONE:
+ break;
+ case drawing::FillStyle_SOLID:
+ {
+ // Construct an SvxBrushItem, as expected by the exporters.
+ aSet.Put(getSvxBrushItemFromSourceSet(rFrameFormat.GetAttrSet(), RES_BACKGROUND));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ m_bOutFlyFrameAttrs = true;
+ //script doesn't matter if not exporting chp
+ OutputItemSet(aSet, true, false,
+ i18n::ScriptType::LATIN, m_bExportModeRTF);
+ m_bOutFlyFrameAttrs = false;
+
+ bCallOutSet = false;
+ }
+ }
+ break;
+ case RES_FRMFMT:
+ break;
+ default:
+ OSL_ENSURE( false, "Which format is exported here?" );
+ break;
+ }
+
+ if( bCallOutSet )
+ OutputItemSet( rFormat.GetAttrSet(), bPapFormat, bChpFormat,
+ i18n::ScriptType::LATIN, m_bExportModeRTF);
+ m_pOutFormatNode = pOldMod;
+}
+
+bool MSWordExportBase::HasRefToAttr(const OUString& rName)
+{
+ SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
+ std::vector<SwGetRefField*> vpRFields;
+ pType->GatherRefFields(vpRFields, REF_SETREFATTR);
+ return std::any_of(vpRFields.begin(), vpRFields.end(),
+ [rName](SwGetRefField* pF) { return rName == pF->GetSetRefName(); });
+}
+
+bool MSWordExportBase::HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo)
+{
+ SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
+ std::vector<SwGetRefField*> vpRFields;
+ pType->GatherRefFields(vpRFields, isEndNote ? REF_ENDNOTE : REF_FOOTNOTE);
+ return std::any_of(vpRFields.begin(), vpRFields.end(),
+ [nSeqNo](SwGetRefField* pF) { return nSeqNo == pF->GetSeqNo(); });
+}
+
+OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo )
+{
+ OUString sRet;
+ switch ( nTyp )
+ {
+ case REF_SETREFATTR:
+ if ( pName )
+ {
+ sRet = "Ref_" + *pName;
+ }
+ break;
+ case REF_SEQUENCEFLD:
+ {
+ assert(pName);
+ sRet = "Ref_" + *pName;
+ break;
+ }
+ case REF_BOOKMARK:
+ if ( pName )
+ sRet = *pName;
+ break;
+ case REF_OUTLINE:
+ break; // ???
+ case REF_FOOTNOTE:
+ sRet = "_RefF" + OUString::number( nSeqNo );
+ break;
+ case REF_ENDNOTE:
+ sRet = "_RefE" + OUString::number( nSeqNo );
+ break;
+ }
+ return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly
+}
+
+OUString MSWordExportBase::GetStyleRefName(const OUString& rName)
+{
+ SwTextFormatColls* pTextFormatColls = m_rDoc.GetTextFormatColls();
+ SwTextFormatColl* pTextFormat = pTextFormatColls->FindFormatByName(rName);
+
+ if (pTextFormat == nullptr)
+ return "\"" + rName + "\"";
+ // Didn't find the style, just keep the original name
+
+ return "\"" + m_pStyles->GetStyleWWName(pTextFormat) + "\"";
+}
+
+/* File CHRATR.HXX: */
+void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript )
+{
+ if (bIsRTL)
+ {
+ if( m_rWW8Export.m_rDoc.GetDocumentType() != SwDoc::DOCTYPE_MSWORD )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CFBiDi::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(1) );
+ }
+ }
+
+ // #i46087# patch from james_clark; complex texts needs the undocumented SPRM CComplexScript with param 0x81.
+ if (nScript == i18n::ScriptType::COMPLEX && !bIsRTL)
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CFComplexScripts::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x81) );
+ m_rWW8Export.m_pDop->bUseThaiLineBreakingRules = true;
+ }
+}
+
+void WW8AttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
+{
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() - (mbOnTOXEnding?2:0), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+ mbOnTOXEnding = false;
+ m_rWW8Export.m_pO->clear();
+
+ if ( pTextNodeInfoInner )
+ {
+ if ( pTextNodeInfoInner->isEndOfLine() )
+ {
+ TableRowEnd( pTextNodeInfoInner->getDepth() );
+
+ SVBT16 nSty;
+ ShortToSVBT16( 0, nSty );
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // Style #
+ TableInfoRow( pTextNodeInfoInner );
+ m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data());
+ m_rWW8Export.m_pO->clear();
+ //For Bug 119650, should break the properties of CHP PLC after a paragraph end.
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data());
+ }
+ }
+
+ // Clear bookmarks of the current paragraph
+ m_aBookmarksOfParagraphStart.clear();
+ m_aBookmarksOfParagraphEnd.clear();
+}
+
+void WW8AttributeOutput::StartRunProperties()
+{
+ WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
+ m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
+}
+
+void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ )
+{
+ if (pRedlineData)
+ {
+ const OUString &rComment = pRedlineData->GetComment();
+ //Only possible to export to main text
+ if (!rComment.isEmpty() && (m_rWW8Export.m_nTextTyp == TXT_MAINTEXT) &&
+ // tdf#153016 don't export the new automatic comments added by tdf#148032
+ rComment != SwResId(STR_REDLINE_COMMENT_DELETED) &&
+ rComment != SwResId(STR_REDLINE_COMMENT_ADDED))
+ {
+ if (m_rWW8Export.m_pAtn->IsNewRedlineComment(pRedlineData))
+ {
+ m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pRedlineData );
+ m_rWW8Export.WritePostItBegin( m_rWW8Export.m_pO.get() );
+ }
+ }
+ }
+
+ /// Insert bookmarks started at this run
+ auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos);
+ for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
+ {
+ GetExport().AppendBookmark(GetExport().BookmarkToWord(aIter->second));
+ }
+}
+
+void WW8AttributeOutput::OnTOXEnding()
+{
+ mbOnTOXEnding = true;
+}
+
+void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, sal_Int32 /*nLen*/, bool bLastRun )
+{
+ /// Insert bookmarks ended after this run
+ auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos);
+ for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
+ {
+ if(bLastRun)
+ GetExport().AppendBookmarkEndWithCorrection(GetExport().BookmarkToWord(aIter->second));
+ else
+ GetExport().AppendBookmark(GetExport().BookmarkToWord(aIter->second));
+ }
+}
+
+void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
+{
+ Redline( pRedlineData );
+
+ WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
+ sal_uInt16 nNewFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
+
+ bool bExportedFieldResult = ( m_nFieldResults != nNewFieldResults );
+
+ // If we have exported a field result, then we will have been forced to
+ // split up the text into a 0x13, 0x14, <result> 0x15 sequence with the
+ // properties forced out at the end of the result, so the 0x15 itself
+ // should remain clean of all other attributes to avoid #iXXXXX#
+ if ( !bExportedFieldResult )
+ {
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
+ m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+ }
+ m_rWW8Export.m_pO->clear();
+}
+
+void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet, const OUString& /*rSymbolFont*/ )
+{
+ RawText(rText, eCharSet);
+}
+
+void WW8AttributeOutput::RawText(const OUString& rText, rtl_TextEncoding)
+{
+ m_rWW8Export.OutSwString(rText, 0, rText.getLength());
+}
+
+void WW8AttributeOutput::OutputFKP(bool bForce)
+{
+ if (!m_rWW8Export.m_pO->empty() || bForce)
+ {
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
+ m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
+ m_rWW8Export.m_pO->clear();
+ }
+}
+
+void WW8AttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
+{
+ OSL_ENSURE( m_rWW8Export.m_pO->empty(), " pO is not empty at line end" );
+
+ SVBT16 nSty;
+ ShortToSVBT16( nStyle, nSty );
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // style #
+}
+
+void WW8AttributeOutput::OutputWW8Attribute( sal_uInt8 nId, bool bVal )
+{
+ m_rWW8Export.InsUInt16( 8 == nId ? NS_sprm::CFDStrike::val : NS_sprm::CFBold::val + nId );
+
+ m_rWW8Export.m_pO->push_back( bVal ? 1 : 0 );
+}
+
+void WW8AttributeOutput::OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal )
+{
+ OSL_ENSURE( nId <= 1, "out of range" );
+ if (nId > 1)
+ return;
+
+ m_rWW8Export.InsUInt16( NS_sprm::CFBoldBi::val + nId );
+ m_rWW8Export.m_pO->push_back( bVal ? 1 : 0 );
+}
+
+void WW8AttributeOutput::CharFont( const SvxFontItem& rFont )
+{
+ sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
+
+ m_rWW8Export.InsUInt16( NS_sprm::CRgFtc0::val );
+ m_rWW8Export.InsUInt16( nFontID );
+ m_rWW8Export.InsUInt16( NS_sprm::CRgFtc2::val );
+
+ m_rWW8Export.InsUInt16( nFontID );
+}
+
+void WW8AttributeOutput::CharFontCTL( const SvxFontItem& rFont )
+{
+ sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
+ m_rWW8Export.InsUInt16( NS_sprm::CFtcBi::val );
+ m_rWW8Export.InsUInt16( nFontID );
+}
+
+void WW8AttributeOutput::CharFontCJK( const SvxFontItem& rFont )
+{
+ sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
+ m_rWW8Export.InsUInt16( NS_sprm::CRgFtc1::val );
+ m_rWW8Export.InsUInt16( nFontID );
+}
+
+void WW8AttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
+{
+ OutputWW8AttributeCTL( 0, WEIGHT_BOLD == rWeight.GetWeight());
+}
+
+void WW8AttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture )
+{
+ OutputWW8AttributeCTL( 1, ITALIC_NONE != rPosture.GetPosture() );
+}
+
+void WW8AttributeOutput::CharPosture( const SvxPostureItem& rPosture )
+{
+ OutputWW8Attribute( 1, ITALIC_NONE != rPosture.GetPosture() );
+}
+
+void WW8AttributeOutput::CharWeight( const SvxWeightItem& rWeight )
+{
+ OutputWW8Attribute( 0, WEIGHT_BOLD == rWeight.GetWeight() );
+}
+
+// Shadowed and Contour are not in WW-UI. JP: ??
+void WW8AttributeOutput::CharContour( const SvxContourItem& rContour )
+{
+ OutputWW8Attribute( 3, rContour.GetValue() );
+}
+
+void WW8AttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
+{
+ OutputWW8Attribute( 4, rShadow.GetValue() );
+}
+
+void WW8AttributeOutput::CharKerning( const SvxKerningItem& rKerning )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::CDxaSpace::val );
+
+ m_rWW8Export.InsUInt16( rKerning.GetValue() );
+}
+
+void WW8AttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::CHpsKern::val );
+
+ m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 );
+}
+
+void WW8AttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::CSfxText::val );
+ // At the moment the only animated text effect we support is blinking
+ m_rWW8Export.m_pO->push_back( rBlink.GetValue() ? 2 : 0 );
+}
+
+void WW8AttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossed )
+{
+ FontStrikeout eSt = rCrossed.GetStrikeout();
+ if ( STRIKEOUT_DOUBLE == eSt )
+ {
+ OutputWW8Attribute( 8, true );
+ return;
+ }
+ if ( STRIKEOUT_NONE != eSt )
+ {
+ OutputWW8Attribute( 2, true );
+ return;
+ }
+
+ // otherwise both off
+ OutputWW8Attribute( 8, false );
+ OutputWW8Attribute( 2, false );
+}
+
+void WW8AttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
+{
+ SvxCaseMap eSt = rCaseMap.GetValue();
+ switch ( eSt )
+ {
+ case SvxCaseMap::SmallCaps:
+ OutputWW8Attribute( 5, true );
+ return;
+ case SvxCaseMap::Uppercase:
+ OutputWW8Attribute( 6, true );
+ return;
+ case SvxCaseMap::Capitalize:
+ // no such feature in word
+ break;
+ default:
+ // otherwise both off
+ OutputWW8Attribute( 5, false );
+ OutputWW8Attribute( 6, false );
+ return;
+ }
+}
+
+void WW8AttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
+{
+ OutputWW8Attribute( 7, rHidden.GetValue() );
+}
+
+void WW8AttributeOutput::CharBorder( const SvxBorderLine* pAllBorder, const sal_uInt16 /*nDist*/, const bool bShadow )
+{
+ WW8Export::Out_BorderLine( *m_rWW8Export.m_pO, pAllBorder, 0, NS_sprm::CBrc80::val, NS_sprm::CBrc::val, bShadow );
+}
+
+void WW8AttributeOutput::CharHighlight( const SvxBrushItem& rBrush )
+{
+ sal_uInt8 nColor = msfilter::util::TransColToIco( rBrush.GetColor() );
+ m_rWW8Export.InsUInt16( NS_sprm::CHighlight::val );
+ m_rWW8Export.m_pO->push_back( nColor );
+}
+
+void WW8AttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::CKul::val );
+
+ // FIXME: this should likely be a StaticWhichCast(), but some we put something dirty in RES_CHRATR_WORDLINEMODE apparently
+ const auto pItem = m_rWW8Export.HasItem(RES_CHRATR_WORDLINEMODE);
+ bool bWord = false;
+ if(pItem)
+ {
+ const auto pWordline = pItem->DynamicWhichCast(RES_CHRATR_WORDLINEMODE);
+ if(pWordline)
+ bWord = pWordline->GetValue();
+ else
+ SAL_WARN("sw.ww8", "m_rWW8Export has an RES_CHRATR_WORDLINEMODE item, but it's of the wrong type.");
+ }
+
+ // WW95 - parameters: 0 = none, 1 = single, 2 = by Word,
+ // 3 = double, 4 = dotted, 5 = hidden
+ // WW97 - additional parameters:
+ // 6 = thick, 7 = dash, 8 = dot(not used)
+ // 9 = dotdash 10 = dotdotdash, 11 = wave
+ sal_uInt8 b = 0;
+ switch ( rUnderline.GetLineStyle() )
+ {
+ case LINESTYLE_SINGLE:
+ b = bWord ? 2 : 1;
+ break;
+ case LINESTYLE_BOLD:
+ b = 6;
+ break;
+ case LINESTYLE_DOUBLE:
+ b = 3;
+ break;
+ case LINESTYLE_DOTTED:
+ b = 4;
+ break;
+ case LINESTYLE_DASH:
+ b = 7;
+ break;
+ case LINESTYLE_DASHDOT:
+ b = 9;
+ break;
+ case LINESTYLE_DASHDOTDOT:
+ b = 10;
+ break;
+ case LINESTYLE_WAVE:
+ b = 11;
+ break;
+ // new in WW2000
+ case LINESTYLE_BOLDDOTTED:
+ b = 20;
+ break;
+ case LINESTYLE_BOLDDASH:
+ b = 23;
+ break;
+ case LINESTYLE_LONGDASH:
+ b = 39;
+ break;
+ case LINESTYLE_BOLDLONGDASH:
+ b = 55;
+ break;
+ case LINESTYLE_BOLDDASHDOT:
+ b = 25;
+ break;
+ case LINESTYLE_BOLDDASHDOTDOT:
+ b = 26;
+ break;
+ case LINESTYLE_BOLDWAVE:
+ b = 27;
+ break;
+ case LINESTYLE_DOUBLEWAVE:
+ b = 43;
+ break;
+ case LINESTYLE_NONE:
+ b = 0;
+ break;
+ default:
+ OSL_ENSURE( rUnderline.GetLineStyle() == LINESTYLE_NONE, "Unhandled underline type" );
+ break;
+ }
+
+ m_rWW8Export.m_pO->push_back( b );
+ Color aColor = rUnderline.GetColor();
+ if( aColor != COL_TRANSPARENT )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CCvUl::val );
+
+ m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( aColor ) );
+ }
+}
+
+void WW8AttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
+{
+ sal_uInt16 nId = 0;
+ switch ( rLanguage.Which() )
+ {
+ case RES_CHRATR_LANGUAGE:
+ nId = NS_sprm::CRgLid0_80::val;
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ nId = NS_sprm::CRgLid1_80::val;
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ nId = NS_sprm::CLidBi::val;
+ break;
+ }
+
+ if ( !nId )
+ return;
+
+ // use sprmCRgLid0_80 rather than sprmCLid
+ m_rWW8Export.InsUInt16( nId );
+ m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
+
+ // Word 2000 and above apparently require both old and new versions of
+ // these sprms to be set, without it spellchecking doesn't work
+ if ( nId == NS_sprm::CRgLid0_80::val )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CRgLid0::val );
+ m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
+ }
+ else if ( nId == NS_sprm::CRgLid1_80::val )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CRgLid1::val );
+ m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
+ }
+}
+
+void WW8AttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
+{
+ sal_uInt8 b = 0xFF;
+ short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
+ if ( !nEsc )
+ {
+ b = 0;
+ nEsc = 0;
+ nProp = 100;
+ }
+ else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
+ {
+ if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
+ b = 2;
+ else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
+ b = 1;
+ }
+ else if ( DFLT_ESC_AUTO_SUPER == nEsc )
+ {
+ // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
+ // The ascent is generally about 80% of the total font height.
+ // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
+ nEsc = .8 * (100 - nProp);
+ }
+ else if ( DFLT_ESC_AUTO_SUB == nEsc )
+ {
+ // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
+ // The descent is generally about 20% of the total font height.
+ // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
+ nEsc = .2 * -(100 - nProp);
+ }
+
+ if ( 0xFF != b )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CIss::val );
+
+ m_rWW8Export.m_pO->push_back( b );
+ }
+
+ if ( 0 != b && 0xFF != b )
+ return;
+
+ double fHeight = m_rWW8Export.GetItem( RES_CHRATR_FONTSIZE ).GetHeight();
+ m_rWW8Export.InsUInt16( NS_sprm::CHpsPos::val );
+
+ m_rWW8Export.InsUInt16(static_cast<short>( round(fHeight * nEsc / 1000) ));
+
+ if( 100 != nProp || !b )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CHps::val );
+ m_rWW8Export.InsUInt16(msword_cast<sal_uInt16>( round(fHeight * nProp / 1000) ));
+ }
+}
+
+void WW8AttributeOutput::CharFontSize( const SvxFontHeightItem& rHeight )
+{
+ sal_uInt16 nId = 0;
+ switch ( rHeight.Which() )
+ {
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_CJK_FONTSIZE:
+ nId = NS_sprm::CHps::val;
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ nId = NS_sprm::CHpsBi::val;
+ break;
+ }
+
+ if ( nId )
+ {
+ m_rWW8Export.InsUInt16( nId );
+
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(( rHeight.GetHeight() + 5 ) / 10 ) );
+ }
+}
+
+void WW8AttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::CCharScale::val );
+ m_rWW8Export.InsUInt16( rScaleWidth.GetValue() );
+}
+
+void WW8AttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
+{
+ sal_uInt16 nId;
+ switch ( rRelief.GetValue() )
+ {
+ case FontRelief::Embossed: nId = NS_sprm::CFEmboss::val; break;
+ case FontRelief::Engraved: nId = NS_sprm::CFImprint::val; break;
+ default: nId = 0; break;
+ }
+
+ if( nId )
+ {
+ m_rWW8Export.InsUInt16( nId );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x81) );
+ }
+ else
+ {
+ // switch both flags off
+ m_rWW8Export.InsUInt16( NS_sprm::CFEmboss::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x0) );
+ m_rWW8Export.InsUInt16( NS_sprm::CFImprint::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x0) );
+ }
+}
+
+void WW8AttributeOutput::CharBidiRTL( const SfxPoolItem& rHt )
+{
+ const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
+ if( rAttr.GetValue() == 1 )
+ {
+ m_rWW8Export.InsUInt16(0x85a);
+ m_rWW8Export.m_pO->push_back(sal_uInt8(1));
+ }
+}
+
+void WW8AttributeOutput::CharIdctHint( const SfxPoolItem& rHt )
+{
+ const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
+ m_rWW8Export.InsUInt16(0x286F);
+ m_rWW8Export.m_pO->push_back(static_cast<sal_uInt8>(rAttr.GetValue()));
+}
+
+void WW8AttributeOutput::CharRotate( const SvxCharRotateItem& rRotate )
+{
+ // #i28331# - check that a Value is set
+ if ( !rRotate.GetValue() )
+ return;
+
+ if (m_rWW8Export.IsInTable())
+ return;
+
+ // #i36867 In word the text in a table is rotated via the TC or NS_sprm::TTextFlow::val
+ // This means you can only rotate all or none of the text adding NS_sprm::CFELayout::val
+ // here corrupts the table, hence !m_rWW8Export.bIsInTable
+
+ m_rWW8Export.InsUInt16( NS_sprm::CFELayout::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x06) ); //len 6
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x01) );
+
+ m_rWW8Export.InsUInt16( rRotate.IsFitToLine() ? 1 : 0 );
+ static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aZeroArr, aZeroArr+3);
+}
+
+void WW8AttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
+{
+ sal_uInt8 nVal;
+ const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
+ if (v == FontEmphasisMark::NONE)
+ nVal = 0;
+ else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
+ nVal = 2;
+ else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
+ nVal = 3;
+ else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
+ nVal = 4;
+ else
+ // case 1:
+ nVal = 1;
+
+ m_rWW8Export.InsUInt16( NS_sprm::CKcd::val );
+ m_rWW8Export.m_pO->push_back( nVal );
+}
+
+/**
+ * TransBrush converts SW-Brushes to WW. The result is WW8_SHD.
+ * Non-standard colours of SW won't be converted now to the mixed values
+ * ( 0 .. 95% ) of WW.
+ * Also if transparent, e.g. for tables a transparent brush is returned
+ *
+ * @return real brush ( not transparent )
+ */
+bool WW8Export::TransBrush(const Color& rCol, WW8_SHD& rShd)
+{
+ if( rCol.IsTransparent() )
+ rShd = WW8_SHD(); // all zeros: transparent
+ else
+ {
+ rShd.SetFore( 0);
+ rShd.SetBack( msfilter::util::TransColToIco( rCol ) );
+ rShd.SetStyle( 0 );
+ }
+ return !rCol.IsTransparent();
+}
+
+static sal_uInt32 SuitableBGColor(Color nIn)
+{
+ if (nIn == COL_AUTO)
+ return 0xFF000000;
+ return wwUtility::RGBToBGR(nIn);
+}
+
+void WW8AttributeOutput::CharColor( const SvxColorItem& rColor )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::CIco::val );
+
+ sal_uInt8 nColor = msfilter::util::TransColToIco( rColor.GetValue() );
+ m_rWW8Export.m_pO->push_back( nColor );
+
+ if (nColor)
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CCv::val );
+ m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( rColor.GetValue() ) );
+ }
+}
+
+void WW8AttributeOutput::CharBackground( const SvxBrushItem& rBrush )
+{
+ WW8_SHD aSHD;
+
+ WW8Export::TransBrush( rBrush.GetColor(), aSHD );
+ // sprmCShd80
+ m_rWW8Export.InsUInt16( NS_sprm::CShd80::val );
+ m_rWW8Export.InsUInt16( aSHD.GetValue() );
+
+ //Quite a few unknowns, some might be transparency or something
+ //of that nature...
+ m_rWW8Export.InsUInt16( NS_sprm::CShd::val );
+ m_rWW8Export.m_pO->push_back( 10 );
+ m_rWW8Export.InsUInt32( 0xFF000000 );
+ m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) );
+ m_rWW8Export.InsUInt16( 0x0000);
+}
+
+namespace sw { namespace util {
+
+const SwCharFormat* GetSwCharFormat(const SwFormatINetFormat& rINet, SwDoc& rDoc)
+{
+ if (rINet.GetValue().isEmpty())
+ return nullptr;
+
+ const sal_uInt16 nId = rINet.GetINetFormatId();
+ const OUString& rStr = rINet.GetINetFormat();
+ if (rStr.isEmpty())
+ {
+ OSL_ENSURE( false, "WW8AttributeOutput::TextINetFormat(..) - missing unvisited character format at hyperlink attribute" );
+ }
+
+ return IsPoolUserFormat( nId )
+ ? rDoc.FindCharFormatByName( rStr )
+ : rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
+}
+
+} }
+
+void WW8AttributeOutput::TextINetFormat( const SwFormatINetFormat& rINet )
+{
+ const SwCharFormat* pFormat = GetSwCharFormat(rINet, m_rWW8Export.m_rDoc);
+ if (!pFormat)
+ return;
+
+ m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
+
+ m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pFormat ) );
+}
+
+// #i43956# - add optional parameter <pLinkStr>
+// It's needed to write the hyperlink data for a certain cross-reference
+// - it contains the name of the link target, which is a bookmark.
+// add optional parameter <bIncludeEmptyPicLocation>
+// It is needed to write an empty picture location for page number field separators
+static void InsertSpecialChar( WW8Export& rWrt, sal_uInt8 c,
+ OUString const * pLinkStr,
+ bool bIncludeEmptyPicLocation = false )
+{
+ ww::bytes aItems;
+ rWrt.GetCurrentItems(aItems);
+
+ if (c == 0x13)
+ rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell());
+ else
+ rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
+
+ rWrt.WriteChar(c);
+
+ // store empty sprmCPicLocation for field separator
+ if ( bIncludeEmptyPicLocation &&
+ ( c == 0x13 || c == 0x14 || c == 0x15 ) )
+ {
+ SwWW8Writer::InsUInt16( aItems, NS_sprm::CPicLocation::val );
+ SwWW8Writer::InsUInt32( aItems, 0x00000000 );
+ }
+
+ // #i43956# - write hyperlink data and attributes
+ if ( c == 0x01 && pLinkStr)
+ {
+ // write hyperlink data to data stream
+ SvStream& rStrm = *rWrt.m_pDataStrm;
+ // position of hyperlink data
+ const sal_uInt64 nLinkPosInDataStrm = rStrm.Tell();
+ // write empty header
+ const sal_uInt16 nEmptyHdrLen = 0x44;
+ sal_uInt8 aEmptyHeader[ nEmptyHdrLen ] = { 0 };
+ aEmptyHeader[ 4 ] = 0x44;
+ rStrm.WriteBytes( aEmptyHeader, nEmptyHdrLen );
+ // writer fixed header
+ const sal_uInt16 nFixHdrLen = 0x19;
+ sal_uInt8 const aFixHeader[ nFixHdrLen ] =
+ {
+ 0x08, 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE,
+ 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9,
+ 0x0B, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00,
+ };
+ rStrm.WriteBytes( aFixHeader, nFixHdrLen );
+ // write reference string including length+1
+ sal_uInt32 nStrLen( pLinkStr->getLength() + 1 );
+ SwWW8Writer::WriteLong( rStrm, nStrLen );
+ SwWW8Writer::WriteString16( rStrm, *pLinkStr, false );
+ // write additional two NULL Bytes
+ SwWW8Writer::WriteLong( rStrm, 0 );
+ // write length of hyperlink data
+ const sal_uInt64 nCurrPos = rStrm.Tell();
+ rStrm.Seek( nLinkPosInDataStrm );
+ rStrm.WriteUInt32(nCurrPos - nLinkPosInDataStrm);
+ rStrm.Seek( nCurrPos );
+
+ // write attributes of hyperlink character 0x01
+ SwWW8Writer::InsUInt16( aItems, NS_sprm::CFFldVanish::val );
+ aItems.push_back( sal_uInt8(0x81) );
+ SwWW8Writer::InsUInt16( aItems, NS_sprm::CPicLocation::val );
+ SwWW8Writer::InsUInt32( aItems, nLinkPosInDataStrm );
+ SwWW8Writer::InsUInt16( aItems, NS_sprm::CFData::val );
+ aItems.push_back( sal_uInt8(0x01) );
+ }
+
+ //Technically we should probably Remove all attribs
+ //here for the 0x13, 0x14, 0x15, but our import
+ //is slightly lacking
+ //aItems.Remove(0, aItems.Count());
+ // fSpec-Attribute true
+ SwWW8Writer::InsUInt16( aItems, NS_sprm::CFSpec::val );
+ aItems.push_back( 1 );
+
+ rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
+}
+
+static OUString lcl_GetExpandedField(const SwField &rField)
+{
+ //replace LF 0x0A with VT 0x0B
+ return rField.ExpandField(true, nullptr).replace(0x0A, 0x0B);
+}
+
+WW8_WrPlcField* WW8Export::CurrentFieldPlc() const
+{
+ WW8_WrPlcField* pFieldP = nullptr;
+ switch (m_nTextTyp)
+ {
+ case TXT_MAINTEXT:
+ pFieldP = m_pFieldMain.get();
+ break;
+ case TXT_HDFT:
+ pFieldP = m_pFieldHdFt.get();
+ break;
+ case TXT_FTN:
+ pFieldP = m_pFieldFootnote.get();
+ break;
+ case TXT_EDN:
+ pFieldP = m_pFieldEdn.get();
+ break;
+ case TXT_ATN:
+ pFieldP = m_pFieldAtn.get();
+ break;
+ case TXT_TXTBOX:
+ pFieldP = m_pFieldTextBxs.get();
+ break;
+ case TXT_HFTXTBOX:
+ pFieldP = m_pFieldHFTextBxs.get();
+ break;
+ default:
+ OSL_ENSURE( false, "what type of SubDoc is that?" );
+ }
+ return pFieldP;
+}
+
+void WW8Export::OutputField( const SwField* pField, ww::eField eFieldType,
+ const OUString& rFieldCmd, FieldFlags nMode )
+{
+ OUString sFieldCmd(rFieldCmd);
+ switch (eFieldType)
+ {
+ // map fields that are not supported in WW8 as of Word 2003
+ case ww::eBIBLIOGRAPHY:
+ eFieldType = ww::eQUOTE;
+ assert(rFieldCmd == FieldString(ww::eBIBLIOGRAPHY));
+ sFieldCmd = FieldString(ww::eQUOTE);
+ break;
+ case ww::eCITATION:
+ eFieldType = ww::eQUOTE;
+ assert(o3tl::starts_with(o3tl::trim(rFieldCmd), u"CITATION"));
+ sFieldCmd = rFieldCmd.replaceFirst(FieldString(ww::eCITATION),
+ FieldString(ww::eQUOTE));
+ break;
+ default:
+ break;
+ }
+
+ assert(eFieldType <= 0x5F); // 95 is the highest documented one
+
+ WW8_WrPlcField* pFieldP = CurrentFieldPlc();
+
+ const bool bIncludeEmptyPicLocation = ( eFieldType == ww::ePAGE );
+ if (FieldFlags::Start & nMode)
+ {
+ sal_uInt8 aField13[2] = { 0x13, 0x00 }; // will change
+ //#i3958#, Needed to make this field work correctly in Word 2000
+ if (eFieldType == ww::eSHAPE)
+ aField13[0] |= 0x80;
+ aField13[1] = static_cast< sal_uInt8 >(eFieldType); // add type
+ pFieldP->Append( Fc2Cp( Strm().Tell() ), aField13 );
+ InsertSpecialChar( *this, 0x13, nullptr, bIncludeEmptyPicLocation );
+ }
+ if (FieldFlags::CmdStart & nMode)
+ {
+ SwWW8Writer::WriteString16(Strm(), sFieldCmd, false);
+ // #i43956# - write hyperlink character including
+ // attributes and corresponding binary data for certain reference fields.
+ bool bHandleBookmark = false;
+
+ if (pField)
+ {
+ if (pField->GetTyp()->Which() == SwFieldIds::GetRef &&
+ ( eFieldType == ww::ePAGEREF || eFieldType == ww::eREF ||
+ eFieldType == ww::eNOTEREF || eFieldType == ww::eFOOTREF ))
+ bHandleBookmark = true;
+ }
+
+ if ( bHandleBookmark )
+ {
+ // retrieve reference destination - the name of the bookmark
+ OUString aLinkStr;
+ const sal_uInt16 nSubType = pField->GetSubType();
+ const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
+ if ( nSubType == REF_SETREFATTR ||
+ nSubType == REF_BOOKMARK )
+ {
+ const OUString& aRefName(rRField.GetSetRefName());
+ aLinkStr = GetBookmarkName( nSubType, &aRefName, 0 );
+ }
+ else if ( nSubType == REF_FOOTNOTE ||
+ nSubType == REF_ENDNOTE )
+ {
+ aLinkStr = GetBookmarkName( nSubType, nullptr, rRField.GetSeqNo() );
+ }
+ else if ( nSubType == REF_SEQUENCEFLD )
+ {
+ aLinkStr = pField->GetPar2();
+ }
+ // insert hyperlink character including attributes and data.
+ InsertSpecialChar( *this, 0x01, &aLinkStr );
+ }
+ }
+ if (FieldFlags::CmdEnd & nMode)
+ {
+ static const sal_uInt8 aField14[2] = { 0x14, 0xff };
+ pFieldP->Append( Fc2Cp( Strm().Tell() ), aField14 );
+ pFieldP->ResultAdded();
+ InsertSpecialChar( *this, 0x14, nullptr, bIncludeEmptyPicLocation );
+ }
+ if (FieldFlags::End & nMode)
+ {
+ OUString sOut;
+ if( pField )
+ sOut = lcl_GetExpandedField(*pField);
+ else
+ sOut = sFieldCmd;
+ if( !sOut.isEmpty() )
+ {
+ SwWW8Writer::WriteString16(Strm(), sOut, false);
+
+ if (pField)
+ {
+ if (pField->GetTyp()->Which() == SwFieldIds::Input &&
+ eFieldType == ww::eFORMTEXT)
+ {
+ sal_uInt8 aArr[12];
+ sal_uInt8 *pArr = aArr;
+
+ Set_UInt16( pArr, NS_sprm::CPicLocation::val );
+ Set_UInt32( pArr, 0x0 );
+
+ Set_UInt16( pArr, NS_sprm::CFSpec::val );
+ Set_UInt8( pArr, 1 );
+
+ Set_UInt16( pArr, NS_sprm::CFNoProof::val );
+ Set_UInt8( pArr, 1 );
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
+ }
+ }
+ }
+ }
+ if (!(FieldFlags::Close & nMode))
+ return;
+
+ sal_uInt8 aField15[2] = { 0x15, 0x80 };
+
+ if (pField)
+ {
+ if (pField->GetTyp()->Which() == SwFieldIds::Input &&
+ eFieldType == ww::eFORMTEXT)
+ {
+ sal_uInt16 nSubType = pField->GetSubType();
+
+ if (nSubType == REF_SEQUENCEFLD)
+ aField15[0] |= (0x4 << 5);
+ }
+ // This ought to apply to any field, but just to be safe, start off with DATE/TIME only.
+ if (pField->GetTyp()->Which() == SwFieldIds::DateTime
+ && (pField->GetSubType() & FIXEDFLD))
+ {
+ //bit 5 - Locked: do not recalculate field
+ aField15[1] |= 0x10;
+ }
+ }
+
+ pFieldP->Append( Fc2Cp( Strm().Tell() ), aField15 );
+ InsertSpecialChar( *this, 0x15, nullptr, bIncludeEmptyPicLocation );
+}
+
+void WW8Export::StartCommentOutput(std::u16string_view rName)
+{
+ const OUString sStr{ FieldString(ww::eQUOTE) + "[" + rName + "] " };
+ OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::Start | FieldFlags::CmdStart);
+}
+
+void WW8Export::EndCommentOutput(std::u16string_view rName)
+{
+ const OUString sStr{ OUString::Concat(" [") + rName + "] " };
+ OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::CmdEnd | FieldFlags::End |
+ FieldFlags::Close);
+}
+
+sal_uInt16 MSWordExportBase::GetId( const SwTOXType& rTOXType )
+{
+ std::vector<const SwTOXType*>::iterator it
+ = std::find( m_aTOXArr.begin(), m_aTOXArr.end(), &rTOXType );
+ if ( it != m_aTOXArr.end() )
+ {
+ return it - m_aTOXArr.begin();
+ }
+ m_aTOXArr.push_back( &rTOXType );
+ return m_aTOXArr.size() - 1;
+}
+
+// return values: 1 - no PageNum,
+// 2 - TabStop before PageNum,
+// 3 - Text before PageNum - rText hold the text
+// 4 - no Text and no TabStop before PageNum
+static int lcl_CheckForm( const SwForm& rForm, sal_uInt8 nLvl, OUString& rText )
+{
+ int nRet = 4;
+ rText.clear();
+
+ // #i21237#
+ SwFormTokens aPattern = rForm.GetPattern(nLvl);
+ SwFormTokens::iterator aIt = aPattern.begin();
+ FormTokenType eTType;
+
+ // #i61362#
+ if (! aPattern.empty())
+ {
+ bool bPgNumFnd = false;
+
+ // #i21237#
+ while( ++aIt != aPattern.end() && !bPgNumFnd )
+ {
+ eTType = aIt->eTokenType;
+
+ switch( eTType )
+ {
+ case TOKEN_PAGE_NUMS:
+ bPgNumFnd = true;
+ break;
+
+ case TOKEN_TAB_STOP:
+ nRet = 2;
+ break;
+ case TOKEN_TEXT:
+ {
+ nRet = 3;
+ sal_Int32 nCount = std::min<sal_Int32>(5, aIt->sText.getLength());
+ rText = aIt->sText.copy(0, nCount); // #i21237#
+ break;
+ }
+ case TOKEN_LINK_START:
+ case TOKEN_LINK_END:
+ break;
+
+ default:
+ nRet = 4;
+ break;
+ }
+ }
+
+ if( !bPgNumFnd )
+ nRet = 1;
+ }
+
+ return nRet;
+}
+
+static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl)
+{
+ bool bRes = false;
+ for (sal_uInt16 nI = 1; nI <= nTOXLvl; ++nI)
+ {
+ // #i21237#
+ SwFormTokens aPattern = rForm.GetPattern(nI);
+
+ if ( !aPattern.empty() )
+ {
+ SwFormTokens::iterator aIt = aPattern.begin();
+
+ FormTokenType eTType;
+
+ // #i21237#
+ while ( ++aIt != aPattern.end() )
+ {
+ eTType = aIt->eTokenType;
+ switch (eTType)
+ {
+ case TOKEN_LINK_START:
+ case TOKEN_LINK_END:
+ bRes = true;
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ return bRes;
+}
+
+void AttributeOutputBase::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter)
+{
+ if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF
+ return;
+
+ const SwpHints* pTextAttrs = rNode.GetpSwpHints();
+ if (!pTextAttrs)
+ return;
+
+ for( size_t i = 0; i < pTextAttrs->Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextAttrs->Get(i);
+ if (pHt->GetAttr().Which() == RES_TXTATR_FIELD)
+ {
+ const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr());
+ const SwField* pField = rField.GetField();
+ // Need to have bookmarks only for sequence fields
+ if (pField && pField->GetTyp()->Which() == SwFieldIds::SetExp && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ)
+ {
+ const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber();
+ const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName();
+ const SwFieldTypes* pFieldTypes = GetExport().m_rDoc.getIDocumentFieldsAccess().GetFieldTypes();
+ bool bHaveFullBkm = false;
+ bool bHaveLabelAndNumberBkm = false;
+ bool bHaveCaptionOnlyBkm = false;
+ bool bHaveNumberOnlyBkm = false;
+ bool bRunSplittedAtSep = false;
+ for( auto const & pFieldType : *pFieldTypes )
+ {
+ if( SwFieldIds::GetRef == pFieldType->Which() )
+ {
+ SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType );
+ for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() )
+ {
+ SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField());
+ // If we have a reference to the current sequence field
+ if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName)
+ {
+ // Need to create a separate run for separator character
+ SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs
+ const OUString& aText = rNode.GetText();
+ const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName());
+ const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart());
+ bool bCategoryFirst = nCategoryStart < pHt->GetStart();
+ sal_Int32 nSeparatorPos = 0;
+ if (bCategoryFirst)
+ {
+ nSeparatorPos = aLocalAttrIter.WhereNext();
+ while (nSeparatorPos <= nPosBeforeSeparator)
+ {
+ aLocalAttrIter.NextPos();
+ nSeparatorPos = aLocalAttrIter.WhereNext();
+ }
+ }
+ else
+ {
+ nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength();
+ }
+ sal_Int32 nRefTextPos = 0;
+ if(nSeparatorPos < aText.getLength())
+ {
+ nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), GetExport().m_rDoc, nSeparatorPos);
+ if(nRefTextPos != nSeparatorPos)
+ {
+ if(!bRunSplittedAtSep)
+ {
+ if(!bCategoryFirst)
+ rAttrIter.SplitRun(nSeparatorPos);
+ rAttrIter.SplitRun(nRefTextPos);
+ bRunSplittedAtSep = true;
+ }
+ if(!bCategoryFirst)
+ aLocalAttrIter.SplitRun(nSeparatorPos);
+ aLocalAttrIter.SplitRun(nRefTextPos);
+ }
+ else if (bCategoryFirst)
+ {
+ if(!bRunSplittedAtSep)
+ {
+ rAttrIter.SplitRun(nSeparatorPos);
+ bRunSplittedAtSep = true;
+ }
+ aLocalAttrIter.SplitRun(nSeparatorPos);
+ }
+ }
+ // Generate bookmarks on the right position
+ OUString sName("Ref_" + pRefField->GetSetRefName() + OUString::number(pRefField->GetSeqNo()));
+ switch (pRefField->GetFormat())
+ {
+ case REF_PAGE:
+ case REF_PAGE_PGDESC:
+ case REF_CONTENT:
+ case REF_UPDOWN:
+ if(!bHaveFullBkm)
+ {
+ sal_Int32 nLastAttrStart = 0;
+ sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
+ while (nActAttr < rNode.GetText().getLength())
+ {
+ nLastAttrStart = nActAttr;
+ aLocalAttrIter.NextPos();
+ nActAttr = aLocalAttrIter.WhereNext();
+ }
+ WriteBookmarkInActParagraph( sName + "_full", std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart );
+ bHaveFullBkm = true;
+ }
+ break;
+ case REF_ONLYNUMBER:
+ {
+ if(!bHaveLabelAndNumberBkm)
+ {
+ sName += "_label_and_number";
+ if(bCategoryFirst)
+ WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) );
+ else
+ {
+ // Find the last run which contains category text
+ SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode );
+ sal_Int32 nCatLastRun = 0;
+ sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext();
+ while (nNextAttr < nSeparatorPos)
+ {
+ nCatLastRun = nNextAttr;
+ aLocalAttrIter2.NextPos();
+ nNextAttr = aLocalAttrIter2.WhereNext();
+ }
+ WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun );
+ }
+ bHaveLabelAndNumberBkm = true;
+ }
+ break;
+ }
+ case REF_ONLYCAPTION:
+ {
+ if(!bHaveCaptionOnlyBkm)
+ {
+ // Find last run
+ sal_Int32 nLastAttrStart = 0;
+ sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
+ while (nActAttr < rNode.GetText().getLength())
+ {
+ nLastAttrStart = nActAttr;
+ aLocalAttrIter.NextPos();
+ nActAttr = aLocalAttrIter.WhereNext();
+ }
+ WriteBookmarkInActParagraph( sName + "_caption_only", nRefTextPos, nLastAttrStart );
+ bHaveCaptionOnlyBkm = true;
+ }
+ break;
+ }
+ case REF_ONLYSEQNO:
+ {
+ if(!bHaveNumberOnlyBkm)
+ {
+ WriteBookmarkInActParagraph( sName + "_number_only", pHt->GetStart(), pHt->GetStart() );
+ bHaveNumberOnlyBkm = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return;
+ }
+ }
+ }
+}
+
+static auto GetSeparatorForLocale() -> OUString
+{
+ switch (sal_uInt16(MsLangId::getSystemLanguage()))
+ {
+ case sal_uInt16(LANGUAGE_GERMAN):
+ case sal_uInt16(LANGUAGE_GERMAN_AUSTRIAN):
+ case sal_uInt16(LANGUAGE_GERMAN_LIECHTENSTEIN):
+ case sal_uInt16(LANGUAGE_GERMAN_LUXEMBOURG):
+ case sal_uInt16(LANGUAGE_GERMAN_SWISS):
+ return ";";
+ default:
+ return ",";
+ }
+}
+
+void AttributeOutputBase::StartTOX( const SwSection& rSect )
+{
+ if ( const SwTOXBase* pTOX = rSect.GetTOXBase() )
+ {
+ static const char sEntryEnd[] = "\" ";
+
+ ww::eField eCode = ww::eTOC;
+ OUString sStr = pTOX ->GetMSTOCExpression();
+ if ( sStr.isEmpty() )
+ {
+ OUString sUserTypeName;
+ auto aType = pTOX->GetType();
+ // user index, it needs INDEX with \f
+ if ( TOX_USER == aType )
+ {
+ sUserTypeName = pTOX->GetTOXType()->GetTypeName();
+ if ( !sUserTypeName.isEmpty() )
+ aType = TOX_INDEX;
+ }
+ switch (aType)
+ {
+ case TOX_INDEX:
+ eCode = ww::eINDEX;
+ sStr = FieldString(eCode);
+
+ {
+ const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
+ const SwColumns& rColumns = rCol.GetColumns();
+ sal_Int32 nCol = rColumns.size();
+
+ if ( 0 < nCol )
+ {
+ // Add a continuous section break
+ if( GetExport().AddSectionBreaksForTOX() )
+ {
+ SwSection *pParent = rSect.GetParent();
+ WW8_SepInfo rInfo(&GetExport().m_rDoc.GetPageDesc(0),
+ pParent ? pParent->GetFormat() : nullptr, 0/*nRstLnNum*/);
+ GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
+ }
+
+ sStr += "\\c \"" + OUString::number( nCol ) + "\"";
+ }
+ }
+
+ if (pTOX->GetTOXForm().IsCommaSeparated())
+ sStr += "\\r ";
+
+ if (SwTOIOptions::AlphaDelimiter & pTOX->GetOptions())
+ sStr += "\\h \"A\" ";
+
+ if (!sUserTypeName.isEmpty())
+ {
+ sStr += "\\f \"" + sUserTypeName + "\"";
+ }
+
+ if (!pTOX->GetTOXForm().IsCommaSeparated())
+ {
+ // In case of Run-in style no separators are added.
+ OUString aFillText;
+ for (sal_uInt8 n = 1; n <= 3; ++n)
+ {
+ OUString aText;
+ int nRet = ::lcl_CheckForm(pTOX->GetTOXForm(), n, aText);
+
+ if( 3 == nRet )
+ aFillText = aText;
+ else if ((4 == nRet) || (2 == nRet))
+ aFillText = "\t";
+ else
+ aFillText.clear();
+ }
+ sStr += "\\e \"" + aFillText + sEntryEnd;
+ }
+ break;
+
+ case TOX_ILLUSTRATIONS:
+ case TOX_OBJECTS:
+ case TOX_TABLES:
+ if (!pTOX->IsFromObjectNames())
+ {
+ sStr = FieldString(eCode) + "\\c ";
+ const OUString& seqName = pTOX->GetSequenceName();
+ if(!seqName.isEmpty())
+ {
+ sStr += "\"" + seqName + sEntryEnd;
+ }
+ OUString aText;
+ int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), 1, aText );
+ if (1 == nRet)
+ sStr += "\\n ";
+ else if( 3 == nRet || 4 == nRet )
+ {
+ sStr += "\\p \"" + aText + sEntryEnd;
+ }
+ }
+ if (lcl_IsHyperlinked(pTOX->GetTOXForm(), 1))
+ {
+ sStr += "\\h ";
+ }
+ if (pTOX->GetCreateType() & SwTOXElement::Template)
+ {
+ OUString const& rStyle(pTOX->GetStyleNames(0));
+ assert(rStyle.indexOf(TOX_STYLE_DELIMITER) == -1);
+ SwTextFormatColl const*const pColl = GetExport().m_rDoc.FindTextFormatCollByName(rStyle);
+ if (pColl)
+ {
+ OUString const converted(GetExport().m_pStyles->GetStyleWWName(pColl));
+ if (!converted.isEmpty())
+ {
+ sStr += "\\t \"" + converted + sEntryEnd;
+ }
+ }
+ }
+ break;
+
+ case TOX_AUTHORITIES:
+ eCode = ww::eBIBLIOGRAPHY;
+ sStr = FieldString(eCode);
+ break;
+ // case TOX_USER:
+ // case TOX_CONTENT:
+ default:
+ {
+ sStr = FieldString(eCode);
+
+ OUString sTOption;
+ // tdf#153082 Word's separator interpretation in DOCX
+ // fields varies by system locale.
+ auto const tsep(GetSeparatorForLocale());
+ sal_uInt16 n, nTOXLvl = pTOX->GetLevel();
+ if( !nTOXLvl )
+ ++nTOXLvl;
+
+ if(SwTOXElement::TableLeader & pTOX->GetCreateType())
+ {
+ sStr +="\\z " ;
+ GetExport( ).m_bHideTabLeaderAndPageNumbers = true ;
+ }
+ if(SwTOXElement::TableInToc & pTOX->GetCreateType())
+ {
+ sStr +="\\w " ;
+ GetExport( ).m_bTabInTOC = true ;
+ }
+ if(SwTOXElement::Newline & pTOX->GetCreateType())
+ {
+ sStr +="\\x " ;
+ }
+ if( SwTOXElement::Mark & pTOX->GetCreateType() )
+ {
+ sStr += "\\f ";
+
+ if( TOX_USER == pTOX->GetType() )
+ {
+ sStr += "\""
+ + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *pTOX->GetTOXType() ) ))
+ + sEntryEnd;
+ }
+ }
+ if(SwTOXElement::Bookmark & pTOX->GetCreateType())
+ {
+ sStr += "\\b \"" + pTOX->GetBookmarkName() + sEntryEnd;
+ }
+
+ if( SwTOXElement::OutlineLevel & pTOX->GetCreateType() )
+ {
+ // Take the TOC value of the max level to evaluate to as
+ // the starting point for the \o flag, but reduce it to the
+ // value of the highest outline level filled by a *standard*
+ // Heading 1 - 9 style because \o "Builds a table of
+ // contents from paragraphs formatted with built-in heading
+ // styles". And afterward fill in any outline styles left
+ // uncovered by that range to the \t flag
+
+ // i.e. for
+ // Heading 1
+ // Heading 2
+ // custom-style
+ // Heading 4
+ // output
+ // \o 1-2 \tcustom-style,3,Heading 3,4
+
+ // Search over all the outline styles used and figure out
+ // what is the minimum outline level (if any) filled by a
+ // non-standard style for that level, i.e. ignore headline
+ // styles 1-9 and find the lowest valid outline level
+ sal_uInt8 nPosOfLowestNonStandardLvl = MAXLEVEL;
+ const SwTextFormatColls& rColls = *GetExport().m_rDoc.GetTextFormatColls();
+ for( n = rColls.size(); n; )
+ {
+ const SwTextFormatColl* pColl = rColls[ --n ];
+ sal_uInt16 nPoolId = pColl->GetPoolFormatId();
+ if (
+ //Is a Non-Standard Outline Style
+ (RES_POOLCOLL_HEADLINE1 > nPoolId || RES_POOLCOLL_HEADLINE9 < nPoolId) &&
+ //Has a valid outline level
+ (pColl->IsAssignedToListLevelOfOutlineStyle()) &&
+ // Is less than the lowest known non-standard level
+ (pColl->GetAssignedOutlineStyleLevel() < nPosOfLowestNonStandardLvl)
+ )
+ {
+ nPosOfLowestNonStandardLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
+ }
+ }
+
+ sal_uInt8 nMaxMSAutoEvaluate = nPosOfLowestNonStandardLvl < nTOXLvl ? nPosOfLowestNonStandardLvl : static_cast<sal_uInt8>(nTOXLvl);
+
+ //output \o 1-X where X is the highest normal outline style to be included in the toc
+ if ( nMaxMSAutoEvaluate )
+ {
+ if (nMaxMSAutoEvaluate > WW8ListManager::nMaxLevel)
+ nMaxMSAutoEvaluate = WW8ListManager::nMaxLevel;
+
+ sStr += "\\o \"1-" + OUString::number(nMaxMSAutoEvaluate) + sEntryEnd;
+ }
+
+ //collect up any other styles in the writer TOC which will
+ //not already appear in the MS TOC and place then into the
+ //\t option
+ if( nMaxMSAutoEvaluate < nTOXLvl )
+ {
+ // collect this templates into the \t option
+ for( n = rColls.size(); n;)
+ {
+ const SwTextFormatColl* pColl = rColls[ --n ];
+ if (!pColl->IsAssignedToListLevelOfOutlineStyle())
+ continue;
+ sal_uInt8 nTestLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
+ if (nTestLvl < nTOXLvl && nTestLvl >= nMaxMSAutoEvaluate)
+ {
+ if (!sTOption.isEmpty())
+ sTOption += tsep;
+ sTOption += pColl->GetName() + tsep + OUString::number(nTestLvl + 1);
+ }
+ }
+ }
+ }
+
+ if( SwTOXElement::ParagraphOutlineLevel & pTOX->GetCreateType() )
+ {
+ sStr +="\\u " ;
+ }
+
+ if( SwTOXElement::Template & pTOX->GetCreateType() )
+ {
+ // #i99641# - Consider additional styles regardless of TOX-outlinelevel
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ const OUString& rStyles = pTOX->GetStyleNames( n );
+ if( !rStyles.isEmpty() )
+ {
+ sal_Int32 nPos = 0;
+ const OUString sLvl{tsep + OUString::number(n + 1)};
+ do {
+ const OUString sStyle( rStyles.getToken( 0, TOX_STYLE_DELIMITER, nPos ));
+ if( !sStyle.isEmpty() )
+ {
+ SwTextFormatColl* pColl = GetExport().m_rDoc.FindTextFormatCollByName(sStyle);
+ if (pColl)
+ {
+ OUString const converted(GetExport().m_pStyles->GetStyleWWName(pColl));
+ if (!converted.isEmpty() &&
+ (!pColl->IsAssignedToListLevelOfOutlineStyle()
+ || pColl->GetAssignedOutlineStyleLevel() < nTOXLvl))
+ {
+ if( !sTOption.isEmpty() )
+ sTOption += tsep;
+ sTOption += converted + sLvl;
+ }
+ }
+ }
+ } while( -1 != nPos );
+ }
+ }
+ }
+
+ // No 'else' branch; why the below snippet is a block I have no idea.
+ {
+ OUString aFillText;
+ sal_uInt8 nNoPgStt = MAXLEVEL, nNoPgEnd = MAXLEVEL;
+ bool bFirstFillText = true, bOnlyText = true;
+ for( n = 0; n < nTOXLvl; ++n )
+ {
+ OUString aText;
+ int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(),
+ static_cast< sal_uInt8 >(n+1), aText );
+ if( 1 == nRet )
+ {
+ bOnlyText = false;
+ if( MAXLEVEL == nNoPgStt )
+ nNoPgStt = static_cast< sal_uInt8 >(n+1);
+ }
+ else
+ {
+ if( MAXLEVEL != nNoPgStt &&
+ MAXLEVEL == nNoPgEnd )
+ nNoPgEnd = sal_uInt8(n);
+
+ bOnlyText = bOnlyText && 3 == nRet;
+ if( 3 == nRet || 4 == nRet )
+ {
+ if( bFirstFillText )
+ aFillText = aText;
+ else if( aFillText != aText )
+ aFillText.clear();
+ bFirstFillText = false;
+ }
+ }
+ }
+ if( MAXLEVEL != nNoPgStt )
+ {
+ if (WW8ListManager::nMaxLevel < nNoPgEnd)
+ nNoPgEnd = WW8ListManager::nMaxLevel;
+ sStr += "\\n "
+ + OUString::number( nNoPgStt )
+ + "-"
+ + OUString::number( nNoPgEnd )
+ + " ";
+ }
+ if( bOnlyText )
+ {
+ sStr += "\\p \"" + aFillText + sEntryEnd;
+ }
+ }
+
+ if( !sTOption.isEmpty() )
+ {
+ sStr += "\\t \"" + sTOption + sEntryEnd;
+ }
+
+ if (lcl_IsHyperlinked(pTOX->GetTOXForm(), nTOXLvl))
+ sStr += "\\h";
+ break;
+ }
+ }
+ }
+
+ if (!sStr.isEmpty())
+ {
+ GetExport( ).m_bInWriteTOX = true;
+ if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
+ { // tdf#129574: required for RTF; doesn't work with DOCX
+ StartRun(nullptr, -42, true);
+ }
+ GetExport( ).OutputField( nullptr, eCode, sStr, FieldFlags::Start | FieldFlags::CmdStart |
+ FieldFlags::CmdEnd );
+ if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
+ {
+ EndRun(nullptr, -42, -1, true);
+ }
+ }
+ }
+
+ GetExport( ).m_bStartTOX = false;
+}
+
+void AttributeOutputBase::EndTOX( const SwSection& rSect,bool bCareEnd )
+{
+ const SwTOXBase* pTOX = rSect.GetTOXBase();
+ if ( pTOX )
+ {
+ ww::eField eCode = TOX_INDEX == pTOX->GetType() ? ww::eINDEX : ww::eTOC;
+ GetExport( ).OutputField( nullptr, eCode, OUString(), FieldFlags::Close );
+
+ if ( pTOX->GetType() == TOX_INDEX && GetExport().AddSectionBreaksForTOX() )
+ {
+ const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
+ const SwColumns& rColumns = rCol.GetColumns();
+ sal_Int32 nCol = rColumns.size();
+
+ if ( 0 < nCol )
+ {
+ WW8_SepInfo rInfo( &GetExport().m_rDoc.GetPageDesc( 0 ), rSect.GetFormat(), 0/*nRstLnNum*/ );
+ GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
+ }
+ }
+ }
+ GetExport( ).m_bInWriteTOX = false;
+ GetExport( ).m_bHideTabLeaderAndPageNumbers = false;
+ if (bCareEnd)
+ OnTOXEnding();
+}
+
+bool MSWordExportBase::GetNumberFormat(const SwField& rField, OUString& rStr)
+{
+ // Returns a date or time format string by using the US NfKeywordTable
+ bool bHasFormat = false;
+ SvNumberFormatter* pNFormatr = m_rDoc.GetNumberFormatter();
+ sal_uInt32 nFormatIdx = rField.GetFormat();
+ const SvNumberformat* pNumFormat = pNFormatr->GetEntry( nFormatIdx );
+ if( pNumFormat )
+ {
+ LanguageType nLng = rField.GetLanguage();
+ SAL_WARN_IF(nLng == LANGUAGE_DONTKNOW, "sw.ww8", "unexpected LANGUAGE_DONTKNOW");
+ if (nLng == LANGUAGE_NONE || nLng == LANGUAGE_DONTKNOW)
+ {
+ nLng = pNumFormat->GetLanguage();
+ }
+ LocaleDataWrapper aLocDat(pNFormatr->GetComponentContext(),
+ LanguageTag(nLng));
+
+ OUString sFormat(pNumFormat->GetMappedFormatstring(GetNfKeywordTable(),
+ aLocDat));
+
+ if (!sFormat.isEmpty())
+ {
+ sw::ms::SwapQuotesInField(sFormat);
+
+ rStr = "\\@\"" + sFormat + "\" " ;
+ bHasFormat = true;
+ }
+ }
+ return bHasFormat;
+}
+
+void AttributeOutputBase::GetNumberPara( OUString& rStr, const SwField& rField )
+{
+ switch(rField.GetFormat())
+ {
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ rStr += "\\* ALPHABETIC ";
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ rStr += "\\* alphabetic ";
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ rStr += "\\* ROMAN ";
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ rStr += "\\* roman ";
+ break;
+ case SVX_NUM_TEXT_NUMBER:
+ rStr += "\\* Ordinal ";
+ break;
+ case SVX_NUM_TEXT_ORDINAL:
+ rStr += "\\* Ordtext ";
+ break;
+ case SVX_NUM_TEXT_CARDINAL:
+ rStr += "\\* Cardtext ";
+ break;
+ default:
+ OSL_ENSURE(rField.GetFormat() == SVX_NUM_ARABIC,
+ "Unknown numbering type exported as default of Arabic");
+ [[fallthrough]];
+ case SVX_NUM_ARABIC:
+ rStr += "\\* ARABIC ";
+ break;
+ case SVX_NUM_PAGEDESC:
+ //Nothing, use word's default
+ break;
+ }
+}
+
+void WW8Export::WritePostItBegin( ww::bytes* pOut )
+{
+ sal_uInt8 aArr[ 3 ];
+ sal_uInt8* pArr = aArr;
+
+ // sprmCFSpec true
+ Set_UInt16( pArr, NS_sprm::CFSpec::val );
+ Set_UInt8( pArr, 1 );
+
+ m_pChpPlc->AppendFkpEntry( Strm().Tell() );
+ WriteChar( 0x05 ); // Annotation reference
+
+ if( pOut )
+ pOut->insert( pOut->end(), aArr, pArr );
+ else
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
+}
+
+OUString FieldString(ww::eField eIndex)
+{
+ if (const char *pField = ww::GetEnglishFieldName(eIndex))
+ return " " + OUString::createFromAscii(pField) + " ";
+ return " ";
+}
+
+void WW8AttributeOutput::HiddenField( const SwField& rField )
+{
+ //replace LF 0x0A with VT 0x0B
+ const OUString sExpand(rField.GetPar2().replace(0x0A, 0x0B));
+
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell());
+ SwWW8Writer::WriteString16(m_rWW8Export.Strm(), sExpand, false);
+ static sal_uInt8 aArr[] =
+ {
+ 0x3C, 0x08, 0x1
+ };
+ m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), sizeof(aArr), aArr);
+}
+
+void WW8AttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
+{
+ const SwSetExpField* pSet = static_cast<const SwSetExpField*>(&rField);
+ const OUString &rVar = pSet->GetPar2();
+
+ sal_uLong nFrom = m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell());
+
+ GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Start |
+ FieldFlags::CmdStart | FieldFlags::CmdEnd);
+
+ /*
+ Is there a bookmark at the start position of this field, if so
+ move it to the 0x14 of the result of the field. This is what word
+ does. MoveFieldMarks moves any bookmarks at this position to
+ the beginning of the field result, and marks the bookmark as a
+ fieldbookmark which is to be ended before the field end mark
+ instead of after it like a normal bookmark.
+ */
+ m_rWW8Export.MoveFieldMarks(nFrom,m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell()));
+
+ if (!rVar.isEmpty())
+ {
+ SwWW8Writer::WriteString16(m_rWW8Export.Strm(), rVar, false);
+ }
+ GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Close);
+}
+
+void WW8AttributeOutput::PostitField( const SwField* pField )
+{
+ const SwPostItField *pPField = static_cast<const SwPostItField*>(pField);
+ m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pPField );
+ m_rWW8Export.WritePostItBegin( m_rWW8Export.m_pO.get() );
+}
+
+bool WW8AttributeOutput::DropdownField( const SwField* pField )
+{
+ const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(pField);
+ uno::Sequence<OUString> aItems =
+ rField2.GetItemSequence();
+ GetExport().DoComboBox(rField2.GetName(),
+ rField2.GetHelp(),
+ rField2.GetToolTip(),
+ rField2.GetSelectedItem(), aItems);
+ return false;
+}
+
+bool WW8AttributeOutput::PlaceholderField( const SwField* )
+{
+ return true; // expand to text?
+}
+
+void WW8AttributeOutput::RefField( const SwField &rField, const OUString &rRef)
+{
+ const OUString sStr{ FieldString( ww::eREF ) + "\"" + rRef + "\" " };
+ m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Start |
+ FieldFlags::CmdStart | FieldFlags::CmdEnd );
+ const OUString sVar = lcl_GetExpandedField( rField );
+ if ( !sVar.isEmpty() )
+ {
+ SwWW8Writer::WriteString16( m_rWW8Export.Strm(), sVar, false );
+ }
+ m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Close );
+}
+
+void WW8AttributeOutput::WriteExpand( const SwField* pField )
+{
+ SwWW8Writer::WriteString16( m_rWW8Export.Strm(), lcl_GetExpandedField( *pField ), false );
+}
+
+namespace
+{
+// Escapes a token string for storing in Word formats. Its import counterpart
+// is lcl_ExtractToken in writerfilter/source/dmapper/DomainMapper_Impl.cxx
+OUString EscapeToken(const OUString& rCommand)
+{
+ bool bWasEscaped = false;
+
+ const int nBufferLen = rCommand.getLength()*1.5;
+ OUStringBuffer sResult(nBufferLen);
+ sResult.append('"'); // opening quote
+ for (sal_Int32 i = 0; i < rCommand.getLength(); ++i)
+ {
+ sal_Unicode ch = rCommand[i];
+ switch (ch)
+ {
+ case '\\':
+ case '"':
+ // Backslashes and doublequotes must be escaped
+ bWasEscaped = true;
+ sResult.append('\\');
+ break;
+ case ' ':
+ // Spaces require quotation
+ bWasEscaped = true;
+ break;
+ }
+ sResult.append(ch);
+ }
+
+ if (bWasEscaped)
+ {
+ sResult.append('"'); // closing quote
+ return sResult.makeStringAndClear();
+ }
+ // No escapement/quotation was required
+ return rCommand;
+}
+}
+
+void AttributeOutputBase::TextField( const SwFormatField& rField )
+{
+ const SwField* pField = rField.GetField();
+ bool bWriteExpand = false;
+ const sal_uInt16 nSubType = pField->GetSubType();
+
+ switch (pField->GetTyp()->Which())
+ {
+ case SwFieldIds::GetExp:
+ if (nSubType == nsSwGetSetExpType::GSE_STRING)
+ {
+ const SwGetExpField *pGet = static_cast<const SwGetExpField*>(pField);
+ RefField( *pGet, pGet->GetFormula() );
+ }
+ else
+ bWriteExpand = true;
+ break;
+ case SwFieldIds::SetExp:
+ if (nsSwGetSetExpType::GSE_SEQ == nSubType)
+ {
+ OUString sStr;
+ if (GetExport().FieldsQuoted())
+ sStr = FieldString(ww::eSEQ) + pField->GetTyp()->GetName() + " ";
+ else
+ sStr = FieldString(ww::eSEQ) + "\"" + pField->GetTyp()->GetName() +"\" ";
+ GetNumberPara( sStr, *pField );
+ GetExport().OutputField(pField, ww::eSEQ, sStr);
+ }
+ else if (nSubType & nsSwGetSetExpType::GSE_STRING)
+ {
+ bool bShowAsWell = false;
+ ww::eField eFieldNo;
+ const SwSetExpField *pSet = static_cast<const SwSetExpField*>(pField);
+ const OUString sVar = pSet->GetPar2();
+ OUString sStr;
+ if (pSet->GetInputFlag())
+ {
+ sStr = FieldString(ww::eASK) + "\""
+ + pSet->GetPar1() + "\" "
+ + pSet->GetPromptText() + " \\d "
+ + sVar;
+ eFieldNo = ww::eASK;
+ }
+ else
+ {
+ sStr = FieldString(ww::eSET)
+ + pSet->GetPar1() + " \""
+ + sVar + "\" ";
+ eFieldNo = ww::eSET;
+ bShowAsWell = (nSubType & nsSwExtendedSubType::SUB_INVISIBLE) == 0;
+ }
+
+ SetField( *pField, eFieldNo, sStr );
+
+ if (bShowAsWell)
+ RefField( *pSet, pSet->GetPar1() );
+ }
+ else
+ bWriteExpand = true;
+ break;
+ case SwFieldIds::PageNumber:
+ {
+ OUString sStr = FieldString(ww::ePAGE);
+ GetNumberPara(sStr, *pField);
+ GetExport().OutputField(pField, ww::ePAGE, sStr);
+ }
+ break;
+ case SwFieldIds::Filename:
+ {
+ OUString sStr = FieldString(ww::eFILENAME);
+ if (pField->GetFormat() == FF_PATHNAME)
+ sStr += "\\p ";
+ GetExport().OutputField(pField, ww::eFILENAME, sStr);
+ }
+ break;
+ case SwFieldIds::Database:
+ {
+ OUString sStr = FieldString(ww::eMERGEFIELD)
+ + EscapeToken(static_cast<SwDBFieldType *>(pField->GetTyp())->GetColumnName()) + " ";
+ GetExport().OutputField(pField, ww::eMERGEFIELD, sStr);
+ }
+ break;
+ case SwFieldIds::DatabaseName:
+ {
+ SwDBData aData = GetExport().m_rDoc.GetDBData();
+ const OUString sStr = FieldString(ww::eDATABASE)
+ + aData.sDataSource
+ + OUStringChar(DB_DELIM)
+ + aData.sCommand;
+ GetExport().OutputField(pField, ww::eDATABASE, sStr);
+ }
+ break;
+ case SwFieldIds::Author:
+ {
+ ww::eField eField =
+ ((AF_SHORTCUT & pField->GetFormat()) ? ww::eUSERINITIALS : ww::eUSERNAME);
+ GetExport().OutputField(pField, eField, FieldString(eField));
+ }
+ break;
+ case SwFieldIds::TemplateName:
+ GetExport().OutputField(pField, ww::eTEMPLATE, FieldString(ww::eTEMPLATE));
+ break;
+ case SwFieldIds::DocInfo: // Last printed, last edited,...
+ if( DI_SUB_FIXED & nSubType )
+ bWriteExpand = true;
+
+ {
+ OUString sStr;
+ ww::eField eField(ww::eNONE);
+ switch (0xff & nSubType)
+ {
+ case DI_TITLE:
+ eField = ww::eTITLE;
+ break;
+ case DI_SUBJECT:
+ eField = ww::eSUBJECT;
+ break;
+ case DI_KEYS:
+ eField = ww::eKEYWORDS;
+ break;
+ case DI_COMMENT:
+ eField = ww::eCOMMENTS;
+ break;
+ case DI_DOCNO:
+ eField = ww::eREVNUM;
+ break;
+ case DI_EDIT:
+ eField = ww::eEDITTIME;
+ break;
+ case DI_CREATE:
+ if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
+ eField = ww::eAUTHOR;
+ else if (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty())
+ eField = ww::eCREATEDATE;
+
+ // Create author/time are always imported as fixed. Safe to ignore on export
+ bWriteExpand = false;
+ break;
+
+ case DI_CHANGE:
+ if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
+ {
+ eField = ww::eLASTSAVEDBY;
+ bWriteExpand=false;
+ }
+ else if (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty())
+ eField = ww::eSAVEDATE;
+ break;
+
+ case DI_PRINT:
+ if (DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK) &&
+ (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty()))
+ eField = ww::ePRINTDATE;
+ break;
+ case DI_CUSTOM:
+ eField = ww::eDOCPROPERTY;
+ {
+ const SwDocInfoField * pDocInfoField =
+ dynamic_cast<const SwDocInfoField *> (pField);
+
+ if (pDocInfoField != nullptr)
+ sStr = "\"" + pDocInfoField->GetName() + "\"";
+
+ bWriteExpand = false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!bWriteExpand && eField != ww::eNONE)
+ {
+ GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
+ }
+ else
+ bWriteExpand = true;
+ }
+ break;
+ case SwFieldIds::DateTime:
+ {
+ OUString sStr;
+ if (!GetExport().GetNumberFormat(*pField, sStr))
+ bWriteExpand = true;
+ else
+ {
+ ww::eField eField = (DATEFLD & nSubType) ? ww::eDATE : ww::eTIME;
+ GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
+ }
+ }
+ break;
+ case SwFieldIds::DocStat:
+ {
+ ww::eField eField = ww::eNONE;
+
+ switch (nSubType)
+ {
+ case DS_PAGE:
+ eField = ww::eNUMPAGES;
+ break;
+ case DS_WORD:
+ eField = ww::eNUMWORDS;
+ break;
+ case DS_CHAR:
+ eField = ww::eNUMCHARS;
+ break;
+ }
+
+ if (eField != ww::eNONE)
+ {
+ OUString sStr = FieldString(eField);
+ GetNumberPara(sStr, *pField);
+ GetExport().OutputField(pField, eField, sStr);
+ }
+ else
+ bWriteExpand = true;
+ }
+ break;
+ case SwFieldIds::ExtUser:
+ {
+ ww::eField eField = ww::eNONE;
+ switch (0xFF & nSubType)
+ {
+ case EU_FIRSTNAME:
+ case EU_NAME:
+ eField = ww::eUSERNAME;
+ break;
+ case EU_SHORTCUT:
+ eField = ww::eUSERINITIALS;
+ break;
+ case EU_STREET:
+ case EU_COUNTRY:
+ case EU_ZIP:
+ case EU_CITY:
+ eField = ww::eUSERADDRESS;
+ break;
+ }
+
+ if (eField != ww::eNONE)
+ {
+ GetExport().OutputField(pField, eField, FieldString(eField));
+ }
+ else
+ bWriteExpand = true;
+ }
+ break;
+ case SwFieldIds::TableOfAuthorities:
+ {
+ OUString sRet(static_cast<SwAuthorityField const*>(pField)
+ ->ExpandCitation(AUTH_FIELD_IDENTIFIER, nullptr));
+ // FIXME: DomainMapper_Impl::CloseFieldCommand() stuffs fully formed
+ // field instructions in here, but if the field doesn't originate
+ // from those filters it won't have that
+ if (!o3tl::starts_with(o3tl::trim(sRet), u"CITATION"))
+ {
+ sRet = FieldString(ww::eCITATION) + " \"" + sRet + "\"";
+ }
+ GetExport().OutputField( pField, ww::eCITATION, sRet );
+ }
+ break;
+ case SwFieldIds::Postit:
+ //Sadly only possible for word in main document text
+ if (GetExport().m_nTextTyp == TXT_MAINTEXT)
+ {
+ PostitField( pField );
+ }
+ break;
+ case SwFieldIds::Input:
+ {
+ const SwInputField * pInputField = dynamic_cast<const SwInputField *>(pField);
+
+ if (pInputField && pInputField->isFormField())
+ GetExport().DoFormText(pInputField);
+ else
+ {
+ const OUString sStr = FieldString(ww::eFILLIN) + "\""
+ + pField->GetPar2() + "\"";
+
+ GetExport().OutputField(pField, ww::eFILLIN, sStr);
+ }
+ }
+ break;
+ case SwFieldIds::GetRef:
+ {
+ ww::eField eField = ww::eNONE;
+ OUString sStr;
+ const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
+ switch (nSubType)
+ {
+ case REF_SETREFATTR:
+ case REF_BOOKMARK:
+ switch (pField->GetFormat())
+ {
+ case REF_PAGE_PGDESC:
+ case REF_PAGE:
+ eField = ww::ePAGEREF;
+ break;
+ default:
+ eField = ww::eREF;
+ break;
+ }
+ {
+ const OUString& aRefName(rRField.GetSetRefName());
+ sStr = FieldString(eField)
+ + GetExport().GetBookmarkName(nSubType, &aRefName, 0);
+ }
+ switch (pField->GetFormat())
+ {
+ case REF_NUMBER:
+ sStr += " \\r";
+ break;
+ case REF_NUMBER_NO_CONTEXT:
+ sStr += " \\n";
+ break;
+ case REF_NUMBER_FULL_CONTEXT:
+ sStr += " \\w";
+ break;
+ }
+ break;
+ case REF_SEQUENCEFLD:
+ {
+ // Not implemented for RTF
+ if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
+ break;
+
+ switch (pField->GetFormat())
+ {
+ case REF_PAGE:
+ case REF_PAGE_PGDESC:
+ eField = ww::ePAGEREF;
+ break;
+ default:
+ eField = ww::eREF;
+ break;
+ }
+ // Generate a unique bookmark name
+ {
+ OUString sName{rRField.GetSetRefName() + OUString::number(rRField.GetSeqNo())};
+ switch (pField->GetFormat())
+ {
+ case REF_PAGE:
+ case REF_PAGE_PGDESC:
+ case REF_CONTENT:
+ case REF_UPDOWN:
+ sName += "_full";
+ break;
+ case REF_ONLYNUMBER:
+ sName += "_label_and_number";
+ break;
+ case REF_ONLYCAPTION:
+ sName += "_caption_only";
+ break;
+ case REF_ONLYSEQNO:
+ sName += "_number_only";
+ break;
+ default: // Ignore other types of reference fields
+ eField = ww::eNONE;
+ break;
+ }
+ sStr = FieldString(eField) + GetExport().GetBookmarkName(nSubType, &sName, 0);
+ }
+ switch (pField->GetFormat())
+ {
+ case REF_NUMBER:
+ sStr += " \\r";
+ break;
+ case REF_NUMBER_NO_CONTEXT:
+ sStr += " \\n";
+ break;
+ case REF_NUMBER_FULL_CONTEXT:
+ sStr += " \\w";
+ break;
+ }
+ break;
+ }
+ case REF_FOOTNOTE:
+ case REF_ENDNOTE:
+ switch (pField->GetFormat())
+ {
+ case REF_PAGE_PGDESC:
+ case REF_PAGE:
+ eField = ww::ePAGEREF;
+ break;
+ case REF_UPDOWN:
+ eField = ww::eREF;
+ break;
+ default:
+ eField =
+ REF_ENDNOTE == nSubType ? ww::eNOTEREF : ww::eFOOTREF;
+ break;
+ }
+ sStr = FieldString(eField)
+ + GetExport().GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo());
+ break;
+ case REF_STYLE:
+ sStr = FieldString(ww::eSTYLEREF)
+ + GetExport().GetStyleRefName(pField->GetPar1());
+ eField = ww::eSTYLEREF;
+ break;
+ }
+
+ OUString sExtraFlags = "\\h "; // by default, include a hyperlink
+
+ switch (eField)
+ {
+ case ww::eNONE:
+ bWriteExpand = true;
+ break;
+ case ww::eSTYLEREF:
+ sExtraFlags = ""; // styleref fields do not work if they have a hyperlink
+
+ {
+ sal_uInt16 stylerefFlags = static_cast<const SwGetRefField*>(pField)->GetFlags();
+ if ((stylerefFlags & REFFLDFLAG_STYLE_FROM_BOTTOM) == REFFLDFLAG_STYLE_FROM_BOTTOM) {
+ sExtraFlags += "\\l ";
+ }
+ if ((stylerefFlags & REFFLDFLAG_STYLE_HIDE_NON_NUMERICAL) == REFFLDFLAG_STYLE_HIDE_NON_NUMERICAL) {
+ sExtraFlags += "\\t ";
+ }
+ }
+
+ [[fallthrough]];
+ default:
+ switch (pField->GetFormat())
+ {
+ case REF_NUMBER:
+ sStr += " \\r " + sExtraFlags;
+ break;
+ case REF_NUMBER_FULL_CONTEXT:
+ sStr += " \\w " + sExtraFlags;
+ break;
+ case REF_UPDOWN:
+ sStr += " \\p " + sExtraFlags;
+ break;
+ case REF_NUMBER_NO_CONTEXT:
+ case REF_CHAPTER:
+ sStr += " \\n " + sExtraFlags;
+ break;
+ default:
+ sStr += " " + sExtraFlags;
+ break;
+ }
+ GetExport().OutputField(pField, eField, sStr);
+ }
+ }
+ break;
+ case SwFieldIds::CombinedChars:
+ {
+ /*
+ We need a font size to fill in the defaults, if these are overridden
+ (as they generally are) by character properties then those properties
+ win.
+
+ The fontsize that is used in MS for determining the defaults is always
+ the CJK fontsize even if the text is not in that language, in OOo the
+ largest fontsize used in the field is the one we should take, but
+ whatever we do, word will actually render using the fontsize set for
+ CJK text. Nevertheless we attempt to guess whether the script is in
+ asian or western text based up on the first character and use the
+ font size of that script as our default.
+ */
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( pField->GetPar1(), 0);
+
+ TypedWhichId<SvxFontHeightItem> nFontHeightWhich = GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript);
+ tools::Long nHeight = GetExport().GetItem(nFontHeightWhich).GetHeight();
+
+ nHeight = (nHeight + 10) / 20; //Font Size in points;
+
+ /*
+ Divide the combined char string into its up and down part. Get the
+ font size and fill in the defaults as up == half the font size and
+ down == a fifth the font size
+ */
+ const sal_Int32 nAbove = (pField->GetPar1().getLength()+1)/2;
+ const OUString sStr = FieldString(ww::eEQ)
+ + "\\o (\\s\\up "
+ + OUString::number(nHeight/2)
+ + "("
+ + pField->GetPar1().subView(0, nAbove)
+ + "), \\s\\do "
+ + OUString::number(nHeight/5)
+ + "("
+ + pField->GetPar1().subView(nAbove)
+ + "))";
+ GetExport().OutputField(pField, ww::eEQ, sStr);
+ }
+ break;
+ case SwFieldIds::Dropdown:
+ bWriteExpand = DropdownField( pField );
+ break;
+ case SwFieldIds::Chapter:
+ bWriteExpand = true;
+
+ if (rField.GetTextField())
+ {
+ const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot();
+ if (!pTextNd)
+ {
+ pTextNd = GetExport().m_pCurPam->GetPointNode().GetTextNode();
+ }
+
+ if (pTextNd)
+ {
+ SwChapterField aCopy(*static_cast<const SwChapterField*>(pField));
+ aCopy.ChangeExpansion(*pTextNd, false);
+
+ OUString sStr;
+ if (GetExport().m_bOutKF) {
+ // In headers and footers, use the chapter number as the style name
+ sStr = FieldString(ww::eSTYLEREF)
+ + " "
+ + OUString::number(aCopy.GetLevel() + 1)
+ + " \\* MERGEFORMAT ";
+ } else {
+ // Otherwise, get the style of the text and use it as the style name
+ const SwTextNode* pOutlineNd = pTextNd->FindOutlineNodeOfLevel(aCopy.GetLevel());
+
+ if (!pOutlineNd) break;
+ // Sometimes we can't find the outline node, in that case let's just fallback to exporting the text
+
+ sStr = FieldString(ww::eSTYLEREF)
+ + GetExport().GetStyleRefName(pOutlineNd->GetFormatColl()->GetName());
+ }
+
+ GetExport().OutputField(pField, ww::eSTYLEREF, sStr);
+ bWriteExpand = false;
+ }
+ }
+ break;
+ case SwFieldIds::HiddenText:
+ {
+ OUString sExpand(pField->GetPar2());
+ if (!sExpand.isEmpty())
+ {
+ auto eSubType = static_cast<SwFieldTypesEnum>(pField->GetSubType());
+ if (eSubType == SwFieldTypesEnum::ConditionalText)
+ {
+ OUString aCond = pField->GetPar1();
+ OUString aTrueFalse = pField->GetPar2();
+ sal_Int32 nPos = aTrueFalse.indexOf('|');
+ OUString aTrue;
+ OUString aFalse;
+ if (nPos == -1)
+ {
+ aTrue = aTrueFalse;
+ }
+ else
+ {
+ aTrue = aTrueFalse.subView(0, nPos);
+ aFalse = aTrueFalse.subView(nPos + 1);
+ }
+ if (aTrue.getLength() > 1 && aTrue.startsWith("\"") && aTrue.endsWith("\""))
+ aTrue = aTrue.copy(1, aTrue.getLength() - 2);
+ if (aFalse.getLength() > 1 && aFalse.startsWith("\"") && aFalse.endsWith("\""))
+ aFalse = aFalse.copy(1, aFalse.getLength() - 2);
+
+ // Substitute a single quote for an illegal double quote if one exists
+ OUString aCmd = FieldString(ww::eIF) + aCond + " \""
+ + aTrue.replaceAll("\"", "'") + "\" \"" + aFalse.replaceAll("\"", "'")
+ + "\"";
+ GetExport().OutputField(pField, ww::eIF, aCmd);
+ }
+ else
+ HiddenField(*pField);
+ }
+ }
+ break;
+ case SwFieldIds::JumpEdit:
+ bWriteExpand = PlaceholderField( pField );
+ break;
+ case SwFieldIds::Macro:
+ {
+ const OUString sStr = " MACROBUTTON "
+ + pField->GetPar1().replaceFirst("StarOffice.Standard.Modul1.", "")
+ + " "
+ + lcl_GetExpandedField(*pField);
+ GetExport().OutputField( pField, ww::eMACROBUTTON, sStr );
+ }
+ break;
+ case SwFieldIds::Table:
+ {
+ ww::eField eField = ww::eEquals;
+ OUString aExpand = OUString::Concat(" =") + o3tl::trim(pField->GetFieldName());
+ GetExport().OutputField(pField, eField, aExpand);
+ }
+ break;
+ case SwFieldIds::User:
+ {
+ ww::eField eField = ww::eDOCVARIABLE;
+ OUString aExpand = FieldString(eField) + pField->GetPar1() + " ";
+ GetExport().OutputField(pField, eField, aExpand);
+ }
+ break;
+ default:
+ bWriteExpand = true;
+ break;
+ }
+
+ if (bWriteExpand)
+ WriteExpand( pField );
+}
+
+void AttributeOutputBase::TextFlyContent( const SwFormatFlyCnt& rFlyContent )
+{
+ if ( auto pTextNd = dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) )
+ {
+ Point const origin;
+ Point aLayPos = pTextNd->FindLayoutRect( false, &origin ).Pos();
+
+ SwPosition aPos( *pTextNd );
+ ww8::Frame aFrame( *rFlyContent.GetFrameFormat(), std::move(aPos) );
+
+ OutputFlyFrame_Impl( aFrame, aLayPos );
+ }
+}
+
+// TOXMarks are still missing
+
+// WW allows detailed settings for hyphenation only for the whole document.
+// One could implement following mimic: The values of the style "Standard" will
+// be set in the Document Properties ( DOP ) if they exist.
+
+// ACK. This suggestion fits exactly to our implementation of the import,
+// therefore I'll implement that right now. (KHZ, 07/15/2000)
+void WW8AttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
+{
+ // sprmPFNoAutoHyph
+ m_rWW8Export.InsUInt16( NS_sprm::PFNoAutoHyph::val );
+
+ m_rWW8Export.m_pO->push_back( rHyphenZone.IsHyphen() ? 0 : 1 );
+}
+
+void WW8AttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::PFAutoSpaceDE::val );
+ m_rWW8Export.m_pO->push_back( rScriptSpace.GetValue() ? 1 : 0 );
+}
+
+void WW8AttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::PFOverflowPunct::val );
+ m_rWW8Export.m_pO->push_back( rItem.GetValue() ? 1 : 0 );
+}
+
+void WW8AttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
+{
+ m_rWW8Export.InsUInt16( NS_sprm::PFKinsoku::val );
+ m_rWW8Export.m_pO->push_back( rItem.GetValue() ? 1 : 0 );
+}
+
+void WW8AttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
+{
+ // sprmPFUsePgsuSettings
+
+ m_rWW8Export.InsUInt16( NS_sprm::PFUsePgsuSettings::val );
+ m_rWW8Export.m_pO->push_back( rGrid.GetValue() ? 1 : 0 );
+}
+
+void WW8AttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
+{
+ // sprmPWAlignFont
+
+ m_rWW8Export.InsUInt16( NS_sprm::PWAlignFont::val );
+
+ SvxParaVertAlignItem::Align nAlign = rAlign.GetValue();
+ sal_uInt16 nVal;
+ switch ( nAlign )
+ {
+ case SvxParaVertAlignItem::Align::Baseline:
+ nVal = 2;
+ break;
+ case SvxParaVertAlignItem::Align::Top:
+ nVal = 0;
+ break;
+ case SvxParaVertAlignItem::Align::Center:
+ nVal = 1;
+ break;
+ case SvxParaVertAlignItem::Align::Bottom:
+ nVal = 3;
+ break;
+ case SvxParaVertAlignItem::Align::Automatic:
+ nVal = 4;
+ break;
+ default:
+ nVal = 4;
+ OSL_FAIL( "Unknown vert alignment" );
+ break;
+ }
+ m_rWW8Export.InsUInt16( nVal );
+}
+
+// NoHyphen: I didn't find an equal in the SW UI and WW UI
+
+// RefMark, NoLineBreakHere are still missing
+
+void WW8Export::WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pOutArr )
+{
+ ww::bytes aAttrArr;
+ const bool bAutoNum = rFootnote.GetNumStr().isEmpty();
+ if( bAutoNum )
+ {
+ static const sal_uInt8 aSpec[] =
+ {
+ 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation
+ 0x55, 0x08, 1 // sprmCFSpec
+ };
+
+ aAttrArr.insert(aAttrArr.end(), aSpec, aSpec+sizeof(aSpec));
+ }
+
+ // sprmCIstd
+ const SwEndNoteInfo* pInfo;
+ if( rFootnote.IsEndNote() )
+ pInfo = &m_rDoc.GetEndNoteInfo();
+ else
+ pInfo = &m_rDoc.GetFootnoteInfo();
+ const SwCharFormat* pCFormat = pOutArr
+ ? pInfo->GetAnchorCharFormat( m_rDoc )
+ : pInfo->GetCharFormat( m_rDoc );
+ SwWW8Writer::InsUInt16( aAttrArr, NS_sprm::CIstd::val );
+ SwWW8Writer::InsUInt16( aAttrArr, GetId( pCFormat ) );
+
+ // fSpec-Attribute true
+ // For Auto-Number a special character must go
+ // into the text and therefore a fSpec attribute
+ m_pChpPlc->AppendFkpEntry( Strm().Tell() );
+ if( bAutoNum )
+ WriteChar( 0x02 ); // auto number character
+ else
+ // user numbering
+ OutSwString(rFootnote.GetNumStr(), 0, rFootnote.GetNumStr().getLength());
+
+ if( pOutArr )
+ {
+ // insert at start of array, so the "hard" attribute overrule the
+ // attributes of the character template
+ pOutArr->insert( pOutArr->begin(), aAttrArr.begin(), aAttrArr.end() );
+ }
+ else
+ {
+ std::unique_ptr<ww::bytes> pOwnOutArr(new ww::bytes);
+
+ // insert at start of array, so the "hard" attribute overrule the
+ // attributes of the character template
+ pOwnOutArr->insert(pOwnOutArr->begin(), aAttrArr.begin(), aAttrArr.end());
+
+ // write for the ftn number in the content, the font of the anchor
+ const SwTextFootnote* pTextFootnote = rFootnote.GetTextFootnote();
+ if( pTextFootnote )
+ {
+ std::unique_ptr<ww::bytes> pOld = std::move(m_pO);
+ m_pO = std::move(pOwnOutArr);
+ SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONT> aSet( m_rDoc.GetAttrPool() );
+
+ pCFormat = pInfo->GetCharFormat( m_rDoc );
+
+ pTextFootnote->GetTextNode().GetParaAttr(aSet,
+ pTextFootnote->GetStart(), pTextFootnote->GetStart() + 1, true);
+ if (aSet.Count())
+ {
+ m_pAttrOutput->OutputItem( aSet.Get( RES_CHRATR_FONT ) );
+ }
+ else
+ {
+ m_pAttrOutput->OutputItem( pCFormat->GetAttrSet().Get(RES_CHRATR_FONT) );
+ }
+ pOwnOutArr = std::move(m_pO);
+ m_pO = std::move(pOld);
+ }
+ m_pChpPlc->AppendFkpEntry( Strm().Tell(), pOwnOutArr->size(),
+ pOwnOutArr->data() );
+ }
+}
+
+static bool lcl_IsAtTextEnd(const SwFormatFootnote& rFootnote)
+{
+ bool bRet = true;
+ if( rFootnote.GetTextFootnote() )
+ {
+ sal_uInt16 nWh = rFootnote.IsEndNote() ? sal_uInt16(RES_END_AT_TXTEND)
+ : sal_uInt16(RES_FTN_AT_TXTEND);
+ const SwSectionNode* pSectNd = rFootnote.GetTextFootnote()->GetTextNode().
+ FindSectionNode();
+ while( pSectNd && FTNEND_ATPGORDOCEND ==
+ static_cast<const SwFormatFootnoteEndAtTextEnd&>(pSectNd->GetSection().GetFormat()->
+ GetFormatAttr( nWh)).GetValue() )
+ pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
+
+ if (!pSectNd)
+ bRet = false; // the is ftn/end collected at Page- or Doc-End
+ }
+ return bRet;
+}
+
+void AttributeOutputBase::TextFootnote( const SwFormatFootnote& rFootnote )
+{
+ sal_uInt16 nTyp;
+ if ( rFootnote.IsEndNote() )
+ {
+ nTyp = REF_ENDNOTE;
+ if ( GetExport().m_bEndAtTextEnd )
+ GetExport().m_bEndAtTextEnd = lcl_IsAtTextEnd( rFootnote );
+ }
+ else
+ {
+ nTyp = REF_FOOTNOTE;
+ if ( GetExport().m_bFootnoteAtTextEnd )
+ GetExport().m_bFootnoteAtTextEnd = lcl_IsAtTextEnd( rFootnote );
+ }
+
+ // if any reference to this footnote/endnote then insert an internal
+ // Bookmark.
+ OUString sBkmkNm;
+ if ( GetExport().HasRefToFootOrEndnote( rFootnote.IsEndNote(), rFootnote.GetTextFootnote()->GetSeqRefNo()))
+ {
+ sBkmkNm = GetExport().GetBookmarkName(nTyp, nullptr,
+ rFootnote.GetTextFootnote()->GetSeqRefNo() );
+ GetExport().AppendBookmark( sBkmkNm );
+ }
+
+ TextFootnote_Impl( rFootnote );
+
+ if ( !sBkmkNm.isEmpty() )
+ GetExport().AppendBookmark( sBkmkNm ); // FIXME: Why is it added twice? Shouldn't this one go to WW8AttributeOutput::TextFootnote_Impl()?
+}
+
+void WW8AttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
+{
+ WW8_WrPlcFootnoteEdn* pFootnoteEnd;
+ if ( rFootnote.IsEndNote() || GetExport().m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER )
+ pFootnoteEnd = m_rWW8Export.m_pEdn.get();
+ else
+ pFootnoteEnd = m_rWW8Export.m_pFootnote.get();
+
+ pFootnoteEnd->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), rFootnote );
+ m_rWW8Export.WriteFootnoteBegin( rFootnote, m_rWW8Export.m_pO.get() );
+}
+
+void WW8AttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
+{
+ if( rCharFormat.GetCharFormat() )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
+
+ m_rWW8Export.InsUInt16( m_rWW8Export.GetId( rCharFormat.GetCharFormat() ) );
+ }
+}
+
+/*
+ See ww8par6.cxx Read_DoubleLine for some more info
+ */
+void WW8AttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
+{
+ // #i28331# - check that bOn is set
+ if ( !rTwoLines.GetValue() )
+ return;
+
+ m_rWW8Export.InsUInt16( NS_sprm::CFELayout::val );
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x06) ); //len 6
+ m_rWW8Export.m_pO->push_back( sal_uInt8(0x02) );
+
+ sal_Unicode cStart = rTwoLines.GetStartBracket();
+ sal_Unicode cEnd = rTwoLines.GetEndBracket();
+
+ /*
+ As per usual we have problems. We can have separate left and right brackets
+ in OOo, it doesn't appear that you can in word. Also in word there appear
+ to only be a limited number of possibilities, we can use pretty much
+ anything.
+
+ So if we have none, we export none, if either bracket is set to a known
+ word type we export both as that type (with the bracket winning out in
+ the case of a conflict simply being the order of test here.
+
+ Upshot being a documented created in word will be reexported with no
+ ill effects.
+ */
+
+ sal_uInt16 nType;
+ if (!cStart && !cEnd)
+ nType = 0;
+ else if ((cStart == '{') || (cEnd == '}'))
+ nType = 4;
+ else if ((cStart == '<') || (cEnd == '>'))
+ nType = 3;
+ else if ((cStart == '[') || (cEnd == ']'))
+ nType = 2;
+ else
+ nType = 1;
+ m_rWW8Export.InsUInt16( nType );
+ static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
+ m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aZeroArr, aZeroArr+3);
+}
+
+void AttributeOutputBase::FormatLineNumberingBase(const SwFormatLineNumber& rNumbering)
+{
+ // always write out suppressLineNumberings - even if it matches the parent,
+ // so that even if the parent is modified, special styles/situations won't lose that suppression
+ if (!rNumbering.IsCount())
+ {
+ FormatLineNumbering(rNumbering);
+ return;
+ }
+
+ // Don't spam suppressLineNumberings = false - that is the default when not specified at all
+ if (auto pNd = dynamic_cast<const SwContentNode*>(GetExport().m_pOutFormatNode)) //paragraph
+ {
+ // useless IsCount can be added to paragraph when specifying MID_LINENUMBER_STARTVALUE
+ const auto& rSet = static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet();
+ const SwFormatLineNumber& rInherited = rSet.GetLineNumber();
+ if (rInherited.IsCount() && rInherited.GetStartValue() != rNumbering.GetStartValue())
+ return; // same IsCount as parent style
+ }
+ else if (GetExport().m_bStyDef) //style
+ {
+ if (GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom())
+ {
+ const auto& rSet = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet();
+ if (rSet.GetLineNumber().IsCount())
+ return; // same as parent style
+ }
+ else
+ return; // same as default value
+ }
+
+ FormatLineNumbering(rNumbering);
+}
+
+void AttributeOutputBase::ParaOutlineLevelBase( const SfxUInt16Item& rItem )
+{
+ sal_uInt16 nOutLvl = rItem.GetValue();
+
+ // Do not write out default level (Body Text) if there is no inheritance, or if the level matches the inherited value
+ const SfxUInt16Item* pInherited = nullptr;
+ if (auto pNd = dynamic_cast<const SwContentNode*>(GetExport().m_pOutFormatNode)) //paragraph
+ pInherited = static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
+ else if (GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom()) //style
+ pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
+ if ((pInherited && pInherited->GetValue() == nOutLvl)
+ || (!pInherited && !nOutLvl))
+ return;
+
+ ParaOutlineLevel(rItem);
+}
+
+void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule )
+{
+ if (rNumRule.GetValue().isEmpty())
+ {
+ ParaNumRule_Impl(nullptr, 0, 0);
+ return;
+ }
+ const SwNumRule* pRule = GetExport().m_rDoc.FindNumRulePtr(
+ rNumRule.GetValue() );
+ if (!pRule)
+ return;
+
+ sal_uInt16 nNumId = GetExport().GetNumberingId(*pRule) + 1;
+ sal_uInt8 nLvl = 0;
+
+ const SwTextNode* pTextNd = dynamic_cast<const SwTextNode*>(GetExport().m_pOutFormatNode);
+ if (pTextNd)
+ {
+ if( pTextNd->IsCountedInList())
+ {
+ nLvl = std::clamp(pTextNd->GetActualListLevel(), 0, MAXLEVEL - 1);
+ const bool bListRestart = pTextNd->IsListRestart();
+
+ if (GetExport().GetExportFormat() == MSWordExportBase::DOCX) // FIXME
+ {
+ // tdf#95848 find the abstract list definition
+ OUString const listId(pTextNd->GetListId());
+ if (!listId.isEmpty()
+ && (listId != pRule->GetDefaultListId() // default list id uses the 1:1 mapping
+ || bListRestart) // or restarting previous list
+ )
+ {
+ SwList const*const pList(
+ GetExport().m_rDoc.getIDocumentListsAccess().getListByName(listId));
+ if (pList)
+ {
+ SwNumRule const*const pAbstractRule(
+ GetExport().m_rDoc.FindNumRulePtr(
+ pList->GetDefaultListStyleName()));
+ assert(pAbstractRule);
+ if (pAbstractRule == pRule && !bListRestart)
+ {
+ // different list, but no override
+ nNumId = GetExport().DuplicateAbsNum(listId, *pAbstractRule) + 1;
+ }
+ else
+ {
+ nNumId = GetExport().OverrideNumRule(
+ *pRule, listId, *pAbstractRule) + 1;
+
+ if (bListRestart)
+ {
+ // For restarted lists we should also keep value for
+ // future w:lvlOverride / w:startOverride
+ GetExport().AddListLevelOverride(nNumId-1, pTextNd->GetActualListLevel(),
+ pTextNd->GetActualListStartValue());
+ }
+ }
+ }
+ }
+ }
+ else if (bListRestart)
+ {
+ sal_uInt16 nStartWith = static_cast<sal_uInt16>(pTextNd->GetActualListStartValue());
+ nNumId = GetExport().DuplicateNumRule(pRule, nLvl, nStartWith);
+ if (USHRT_MAX != nNumId)
+ ++nNumId;
+ }
+ }
+ else
+ {
+ // #i44815# adjust numbering for numbered paragraphs
+ // without number. These paragraphs will receive a
+ // list id 0, which WW interprets as 'no number'.
+ nNumId = 0;
+ }
+ }
+ else if ( auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
+ {
+ if (pC->IsAssignedToListLevelOfOutlineStyle())
+ nLvl = static_cast< sal_uInt8 >( pC->GetAssignedOutlineStyleLevel() );
+ else
+ {
+ const SfxItemSet* pSet = GetExport().m_pISet;
+ if (pSet && pSet->HasItem(RES_PARATR_LIST_LEVEL))
+ {
+ const SfxInt16Item* pItem = pSet->GetItem(RES_PARATR_LIST_LEVEL);
+ nLvl = pItem->GetValue();
+ }
+ }
+ }
+
+ if ( nLvl >= WW8ListManager::nMaxLevel )
+ nLvl = WW8ListManager::nMaxLevel - 1;
+
+ ParaNumRule_Impl( pTextNd, nLvl, nNumId);
+}
+
+void WW8AttributeOutput::ParaNumRule_Impl(const SwTextNode* /*pTextNd*/,
+ sal_Int32 const nLvl, sal_Int32 const nNumId)
+{
+ if (USHRT_MAX == nNumId)
+ return;
+
+ // write sprmPIlvl and sprmPIlfo
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::PIlvl::val );
+ m_rWW8Export.m_pO->push_back( ::sal::static_int_cast<sal_uInt8>(nLvl) );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::PIlfo::val );
+ SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, ::sal::static_int_cast<sal_uInt16>(nNumId) );
+}
+
+/* File FRMATR.HXX */
+
+void WW8AttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
+{
+ if( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
+ {
+ if( m_rWW8Export.m_bOutGrf )
+ return; // Fly around graphic -> Auto-size
+
+ //???? What about percentages ???
+ if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed)
+ {
+ //"sprmPDxaWidth"
+ m_rWW8Export.InsUInt16( NS_sprm::PDxaWidth::val );
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rSize.GetWidth()) );
+ }
+
+ if ( rSize.GetHeight() )
+ {
+ // sprmPWHeightAbs
+ m_rWW8Export.InsUInt16( NS_sprm::PWHeightAbs::val );
+
+ sal_uInt16 nH = 0;
+ switch ( rSize.GetHeightSizeType() )
+ {
+ case SwFrameSize::Variable: break;
+ case SwFrameSize::Fixed: nH = o3tl::narrowing<sal_uInt16>(rSize.GetHeight()) & 0x7fff; break;
+ default: nH = o3tl::narrowing<sal_uInt16>(rSize.GetHeight()) | 0x8000; break;
+ }
+ m_rWW8Export.InsUInt16( nH );
+ }
+ }
+ else if( m_rWW8Export.m_bOutPageDescs ) // PageDesc : width + height
+ {
+ if( m_rWW8Export.m_pCurrentPageDesc->GetLandscape() )
+ {
+ /*sprmSBOrientation*/
+ m_rWW8Export.InsUInt16( NS_sprm::SBOrientation::val );
+ m_rWW8Export.m_pO->push_back( 2 );
+ }
+
+ /*sprmSXaPage*/
+ m_rWW8Export.InsUInt16( NS_sprm::SXaPage::val );
+ m_rWW8Export.InsUInt16(
+ msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetWidth())));
+
+ /*sprmSYaPage*/
+ m_rWW8Export.InsUInt16( NS_sprm::SYaPage::val );
+ m_rWW8Export.InsUInt16(
+ msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetHeight())));
+ }
+}
+
+// FillOrder is still missing
+
+/**
+ * ReplaceCr() is used for Pagebreaks and Pagedescs. An already written CR
+ * will be replaced by a break character. Replace must be called right after
+ * the writing of CR.
+ *
+ * @return FilePos + 1 of the replaced CR or 0 if nothing was replaced.
+ */
+sal_uInt64 WW8Export::ReplaceCr( sal_uInt8 nChar )
+{
+ OSL_ENSURE( nChar, "replaced with 0 crashes WW97/95" );
+
+ bool bReplaced = false;
+ SvStream& rStrm = Strm();
+ sal_uInt64 nRetPos = 0, nPos = rStrm.Tell();
+ //If there is at least two characters already output
+ if (nPos - 2 >= o3tl::make_unsigned(m_pFib->m_fcMin))
+ {
+ sal_uInt16 nUCode=0;
+
+ rStrm.SeekRel(-2);
+ rStrm.ReadUInt16( nUCode );
+ //If the last char was a cr
+ if (nUCode == 0x0d) // CR ?
+ {
+ if ((nChar == 0x0c) &&
+ (nPos - 4 >= o3tl::make_unsigned(m_pFib->m_fcMin)))
+ {
+ rStrm.SeekRel(-4);
+ rStrm.ReadUInt16( nUCode );
+ }
+ else
+ {
+ rStrm.SeekRel(-2);
+ nUCode = 0x0;
+ }
+ //And the para is not of len 0, then replace this cr with the mark
+ //#120140# If there is a cr before a column break, need replace the cr. So remove the "nChar==0x0e" check.
+ if( nUCode == 0x0d )
+ bReplaced = false;
+ else
+ {
+ bReplaced = true;
+ WriteChar(nChar);
+ nRetPos = nPos;
+ }
+ }
+ else if ((nUCode == 0x0c) && (nChar == 0x0e))
+ {
+ // a column break after a section has no effect in writer
+ bReplaced = true;
+ }
+ rStrm.Seek( nPos );
+ }
+ else
+ bReplaced = true;
+
+ if (!bReplaced)
+ {
+ // then write as normal char
+ WriteChar(nChar);
+ m_pPiece->SetParaBreak();
+ m_pPapPlc->AppendFkpEntry(rStrm.Tell());
+ m_pChpPlc->AppendFkpEntry(rStrm.Tell());
+ nRetPos = rStrm.Tell();
+ }
+ return nRetPos;
+}
+
+void WW8AttributeOutput::TableRowEnd(sal_uInt32 nDepth)
+{
+ if ( nDepth == 1 )
+ m_rWW8Export.WriteChar( 0x07 );
+ else if ( nDepth > 1 )
+ m_rWW8Export.WriteChar( 0x0d );
+
+ //Technically in a word document this is a different value for a row ends
+ //that are not row ends directly after a cell with a graphic. But it
+ //doesn't seem to make a difference
+ //pMagicTable->Append(Fc2Cp(Strm().Tell()),0x1B6);
+}
+
+void AttributeOutputBase::FormatPageDescription( const SwFormatPageDesc& rPageDesc )
+{
+ if ( GetExport().m_bStyDef )
+ if (auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
+ {
+ if ( (SfxItemState::SET != pC->GetItemState( RES_BREAK, false ) ) && rPageDesc.KnowsPageDesc() )
+ FormatBreak( SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) );
+ }
+}
+
+void WW8AttributeOutput::PageBreakBefore( bool bBreak )
+{
+ // sprmPPageBreakBefore/sprmPFPageBreakBefore
+ m_rWW8Export.InsUInt16( NS_sprm::PFPageBreakBefore::val );
+
+ m_rWW8Export.m_pO->push_back( bBreak ? 1 : 0 );
+}
+
+/**
+ * breaks write nothing in the output field rWrt.pO,
+ * but only in the text stream (requirement so they can
+ * be called from Out_Break...)
+ */
+void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak )
+{
+ if ( GetExport().m_bStyDef )
+ {
+ switch ( rBreak.GetBreak() )
+ {
+ case SvxBreak::NONE:
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageBoth:
+ PageBreakBefore( rBreak.GetValue() != SvxBreak::NONE );
+ break;
+ default:
+ break;
+ }
+ }
+ else if ( !GetExport().m_pParentFrame )
+ {
+ sal_uInt8 nC = 0;
+ bool bBefore = false;
+ // #i76300# - Note: Can only be <true>, if <bBefore> equals <false>.
+ bool bCheckForFollowPageDesc = false;
+
+ switch ( rBreak.GetBreak() )
+ {
+ case SvxBreak::NONE: // disabled
+ if ( !GetExport().m_bBreakBefore )
+ PageBreakBefore( false );
+ return;
+
+ case SvxBreak::ColumnBefore: // ColumnBreak
+ bBefore = true;
+ [[fallthrough]];
+ case SvxBreak::ColumnAfter:
+ case SvxBreak::ColumnBoth:
+ if ( GetExport().m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK )
+ || GetExport().Sections().CurrentNumberOfColumns( GetExport().m_rDoc ) > 1 )
+ {
+ nC = msword::ColumnBreak;
+ }
+ break;
+
+ case SvxBreak::PageBefore: // PageBreak
+ // From now on(fix for #i77900#) we prefer to save a page break
+ // as paragraph attribute (if the exporter is OK with that),
+ // this has to be done after the export of the paragraph ( =>
+ // !GetExport().bBreakBefore )
+ if (GetExport().PreferPageBreakBefore())
+ {
+ if (!GetExport().m_bBreakBefore)
+ PageBreakBefore(true);
+ }
+ else
+ {
+ bBefore = true;
+ nC = msword::PageBreak;
+ }
+ break;
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ nC = msword::PageBreak;
+ // #i76300# - check for follow page description,
+ // if current writing attributes of a paragraph.
+ if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) &&
+ GetExport().GetCurItemSet() )
+ {
+ bCheckForFollowPageDesc = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if ( ( bBefore == GetExport().m_bBreakBefore ) && nC )
+ {
+ // #i76300#
+ bool bFollowPageDescWritten = false;
+ if ( bCheckForFollowPageDesc )
+ {
+ bFollowPageDescWritten =
+ GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(),
+ dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) );
+ }
+ if ( !bFollowPageDescWritten )
+ {
+ SectionBreak(nC, !bBefore);
+ }
+ }
+ }
+}
+
+void WW8AttributeOutput::SectionBreak( sal_uInt8 nC, bool /*bBreakAfter*/, const WW8_SepInfo* /*pSectionInfo*/, bool /*bExtraPageBreak*/ )
+{
+ m_rWW8Export.ReplaceCr( nC );
+}
+
+sal_uInt32 AttributeOutputBase::GridCharacterPitch( const SwTextGridItem& rGrid ) const
+{
+ MSWordStyles * pStyles = GetExport().m_pStyles.get();
+ const SwFormat * pSwFormat = pStyles->GetSwFormat(0);
+
+ sal_uInt32 nPageCharSize = 0;
+
+ if (pSwFormat != nullptr)
+ {
+ nPageCharSize = pSwFormat->GetFormatAttr(RES_CHRATR_FONTSIZE).GetHeight();
+ }
+ sal_uInt16 nPitch = rGrid.IsSquaredMode() ? rGrid.GetBaseHeight() :
+ rGrid.GetBaseWidth( );
+
+ sal_Int32 nCharWidth = nPitch - nPageCharSize;
+ sal_Int32 nFraction = nCharWidth % 20;
+ if ( nCharWidth < 0 )
+ nFraction = 20 + nFraction;
+ nFraction = ( nFraction * 0xFFF ) / 20;
+ nFraction = ( nFraction & 0x00000FFF );
+
+ sal_Int32 nMain = nCharWidth / 20;
+ if ( nCharWidth < 0 )
+ nMain -= 1;
+ nMain = nMain * 0x1000;
+ nMain = ( nMain & 0xFFFFF000 );
+
+ return sal_uInt32( nFraction + nMain );
+}
+
+void WW8AttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
+{
+ if (!m_rWW8Export.m_bOutPageDescs)
+ return;
+
+ sal_uInt16 nGridType = 0;
+ switch ( rGrid.GetGridType() )
+ {
+ default:
+ OSL_FAIL("Unknown grid type");
+ [[fallthrough]];
+ case GRID_NONE:
+ nGridType = 0;
+ break;
+ case GRID_LINES_ONLY:
+ nGridType = 2;
+ break;
+ case GRID_LINES_CHARS:
+ if ( rGrid.IsSnapToChars() )
+ nGridType = 3;
+ else
+ nGridType = 1;
+ break;
+ }
+ m_rWW8Export.InsUInt16( NS_sprm::SClm::val );
+ m_rWW8Export.InsUInt16( nGridType );
+
+ sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
+ m_rWW8Export.InsUInt16( NS_sprm::SDyaLinePitch::val );
+ m_rWW8Export.InsUInt16( nHeight );
+
+ m_rWW8Export.InsUInt16( NS_sprm::SDxtCharSpace::val );
+ m_rWW8Export.InsUInt32( GridCharacterPitch( rGrid ) );
+}
+
+void WW8AttributeOutput::FormatPaperBin( const SvxPaperBinItem& rPaperBin )
+{
+ if ( !m_rWW8Export.m_bOutPageDescs )
+ return;
+
+ sal_uInt16 nVal;
+ switch ( rPaperBin.GetValue() )
+ {
+ case 0: nVal = 15; break; // Automatically select
+ case 1: nVal = 1; break; // Upper paper tray
+ case 2: nVal = 4; break; // Manual paper feed
+ default: nVal = 0; break;
+ }
+
+ if ( nVal )
+ {
+ m_rWW8Export.InsUInt16( m_rWW8Export.m_bOutFirstPage
+ ? NS_sprm::SDmBinFirst::val : NS_sprm::SDmBinOther::val );
+
+ m_rWW8Export.InsUInt16( nVal );
+ }
+}
+
+void WW8AttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine)
+{
+ // sprmPDxaLeft1
+ m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
+ m_rWW8Export.InsUInt16( rFirstLine.GetTextFirstLineOffset() );
+}
+
+void WW8AttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin)
+{ // normal paragraphs
+ // sprmPDxaLeft
+ m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rTextLeftMargin.GetTextLeft()) );
+}
+
+void WW8AttributeOutput::FormatRightMargin(SvxRightMarginItem const& rRightMargin)
+{
+ // (paragraph case, this will be an else branch once others are converted)
+#if 0
+ else
+#endif
+ { // normal paragraphs
+ // sprmPDxaRight
+ m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rRightMargin.GetRight()) );
+ }
+}
+
+void WW8AttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLR )
+{
+ // Flys are still missing ( see RTF )
+
+ if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
+ {
+ // sprmPDxaFromText10
+ m_rWW8Export.InsUInt16( NS_sprm::LN_PDxaFromText10 );
+ // use average, since WW only knows one value
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>( ( rLR.GetLeft() + rLR.GetRight() ) / 2 ) );
+ }
+ else if ( m_rWW8Export.m_bOutPageDescs ) // PageDescs
+ {
+ m_pageMargins.nLeft = 0;
+ m_pageMargins.nRight = 0;
+
+ if ( const SvxBoxItem* pBoxItem = m_rWW8Export.HasItem( RES_BOX ) )
+ {
+ m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
+ m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
+ }
+
+ m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft());
+ m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight());
+ sal_uInt16 nGutter = rLR.GetGutterMargin();
+
+ // sprmSDxaLeft
+ m_rWW8Export.InsUInt16( NS_sprm::SDxaLeft::val );
+ m_rWW8Export.InsUInt16( m_pageMargins.nLeft );
+
+ // sprmSDxaRight
+ m_rWW8Export.InsUInt16( NS_sprm::SDxaRight::val );
+ m_rWW8Export.InsUInt16( m_pageMargins.nRight );
+
+ if (nGutter)
+ {
+ // sprmSDzaGutter
+ m_rWW8Export.InsUInt16(NS_sprm::SDzaGutter::val);
+ m_rWW8Export.InsUInt16(nGutter);
+ }
+ }
+ else
+ { // normal paragraphs
+ // sprmPDxaLeft
+ m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rLR.GetTextLeft()) );
+
+ // sprmPDxaRight
+ m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rLR.GetRight()) );
+
+ // sprmPDxaLeft1
+ m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
+ m_rWW8Export.InsUInt16( rLR.GetTextFirstLineOffset() );
+ }
+}
+
+void WW8AttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
+{
+ if (!rRtlGutter.GetValue())
+ {
+ return;
+ }
+
+ // sprmSFRTLGutter
+ m_rWW8Export.InsUInt16(NS_sprm::SFRTLGutter::val);
+ m_rWW8Export.m_pO->push_back(1);
+}
+
+void WW8AttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
+{
+ // Write the linebreak itself.
+ m_rWW8Export.WriteChar(0x0b);
+
+ // sprmCLbcCRJ
+ m_rWW8Export.InsUInt16(NS_sprm::CLbcCRJ::val);
+ m_rWW8Export.m_pO->push_back(rLineBreak.GetEnumValue());
+}
+
+void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
+{
+ // Flys are still missing ( see RTF )
+
+ if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
+ {
+ // sprmPDyaFromText
+ m_rWW8Export.InsUInt16( NS_sprm::PDyaFromText::val );
+ // use average, since WW only knows one value
+ m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>( ( rUL.GetUpper() + rUL.GetLower() ) / 2 ) );
+ }
+ else if ( m_rWW8Export.m_bOutPageDescs ) // Page-UL
+ {
+ OSL_ENSURE( m_rWW8Export.GetCurItemSet(), "Impossible" );
+ if ( !m_rWW8Export.GetCurItemSet() )
+ return;
+
+ HdFtDistanceGlue aDistances( *m_rWW8Export.GetCurItemSet() );
+
+ if ( aDistances.HasHeader() )
+ {
+ //sprmSDyaHdrTop
+ m_rWW8Export.InsUInt16( NS_sprm::SDyaHdrTop::val );
+ m_rWW8Export.InsUInt16( aDistances.m_DyaHdrTop );
+ }
+
+ // sprmSDyaTop
+ m_rWW8Export.InsUInt16( NS_sprm::SDyaTop::val );
+ m_rWW8Export.InsUInt16( aDistances.m_DyaTop );
+ m_pageMargins.nTop = aDistances.m_DyaTop;
+
+ if ( aDistances.HasFooter() )
+ {
+ //sprmSDyaHdrBottom
+ m_rWW8Export.InsUInt16( NS_sprm::SDyaHdrBottom::val );
+ m_rWW8Export.InsUInt16( aDistances.m_DyaHdrBottom );
+ }
+
+ //sprmSDyaBottom
+ m_rWW8Export.InsUInt16( NS_sprm::SDyaBottom::val );
+ m_rWW8Export.InsUInt16( aDistances.m_DyaBottom );
+ m_pageMargins.nBottom = aDistances.m_DyaBottom;
+ }
+ else
+ {
+ // sprmPDyaBefore
+ m_rWW8Export.InsUInt16( NS_sprm::PDyaBefore::val );
+ m_rWW8Export.InsUInt16( rUL.GetUpper() );
+ // sprmPDyaAfter
+ m_rWW8Export.InsUInt16( NS_sprm::PDyaAfter::val );
+ m_rWW8Export.InsUInt16( rUL.GetLower() );
+ // sprmPFContextualSpacing
+
+ // Write out Contextual Spacing = false if it would have inherited a true.
+ const SvxULSpaceItem* pInherited = nullptr;
+ if (!rUL.GetContext())
+ {
+ if (auto pNd = dynamic_cast<const SwContentNode*>(m_rWW8Export.m_pOutFormatNode)) //paragraph
+ pInherited = &static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetULSpace();
+ else if (m_rWW8Export.m_bStyDef && m_rWW8Export.m_pCurrentStyle && m_rWW8Export.m_pCurrentStyle->DerivedFrom()) //style
+ pInherited = &m_rWW8Export.m_pCurrentStyle->DerivedFrom()->GetULSpace();
+ }
+ if (rUL.GetContext() || (pInherited && pInherited->GetContext()))
+ {
+ m_rWW8Export.InsUInt16(NS_sprm::PFContextualSpacing::val);
+ m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(rUL.GetContext()) );
+ }
+ }
+}
+
+// print, opaque, protect are still missing
+
+void WW8AttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
+{
+ if ( m_rWW8Export.m_bOutFlyFrameAttrs )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::PWr::val );
+
+ m_rWW8Export.m_pO->push_back(
+ ( css::text::WrapTextMode_NONE != rSurround.GetSurround() ) ? 2 : 1 );
+ }
+}
+
+void WW8AttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
+{
+
+ //!!!! anchor type and corresponding borders are still missing
+ if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
+ return;
+
+ short nPos;
+ switch( rFlyVert.GetVertOrient() )
+ {
+ case text::VertOrientation::NONE:
+ nPos = static_cast<short>(rFlyVert.GetPos());
+ break;
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ nPos = -8;
+ break;
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ nPos = -12;
+ break;
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::LINE_TOP:
+ default:
+ nPos = -4;
+ break;
+ }
+
+ // sprmPDyaAbs
+ m_rWW8Export.InsUInt16( NS_sprm::PDyaAbs::val );
+ m_rWW8Export.InsUInt16( nPos );
+}
+
+void WW8AttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
+{
+ if ( !m_rWW8Export.m_pParentFrame )
+ {
+ OSL_ENSURE( m_rWW8Export.m_pParentFrame, "HoriOrient without mpParentFrame !!" );
+ return;
+ }
+
+ //!!!! anchor type and corresponding borders are still missing
+ if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
+ return;
+
+ short nPos;
+ switch( rFlyHori.GetHoriOrient() )
+ {
+ case text::HoriOrientation::NONE:
+ nPos = static_cast<short>(rFlyHori.GetPos());
+ if( !nPos )
+ nPos = 1; // WW: 0 is reserved
+ break;
+ case text::HoriOrientation::LEFT:
+ nPos = rFlyHori.IsPosToggle() ? -12 : 0;
+ break;
+ case text::HoriOrientation::RIGHT:
+ nPos = rFlyHori.IsPosToggle() ? -16 : -8;
+ break;
+ case text::HoriOrientation::CENTER:
+ case text::HoriOrientation::FULL: // FULL only for tables
+ default:
+ nPos = -4;
+ break;
+ }
+
+ // sprmPDxaAbs
+ m_rWW8Export.InsUInt16( NS_sprm::PDxaAbs::val );
+ m_rWW8Export.InsUInt16( nPos );
+}
+
+void WW8AttributeOutput::FormatAnchor( const SwFormatAnchor& rAnchor )
+{
+ OSL_ENSURE( m_rWW8Export.m_pParentFrame, "Anchor without mpParentFrame !!" );
+
+ if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
+ return;
+
+ sal_uInt8 nP = 0;
+ switch ( rAnchor.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ // vertical: page | horizontal: page
+ nP |= (1 << 4) | (2 << 6);
+ break;
+ // in case of Fly as characters: set paragraph-bound!!!
+ case RndStdIds::FLY_AT_FLY:
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AS_CHAR:
+ // vertical: page | horizontal: page
+ nP |= (2 << 4) | (0 << 6);
+ break;
+ default:
+ break;
+ }
+
+ // sprmPPc
+ m_rWW8Export.InsUInt16( NS_sprm::PPc::val );
+ m_rWW8Export.m_pO->push_back( nP );
+}
+
+void WW8AttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
+{
+ // WW cannot have background in a section
+ if ( m_rWW8Export.m_bOutPageDescs )
+ return;
+
+ WW8_SHD aSHD;
+ WW8Export::TransBrush( rBrush.GetColor(), aSHD );
+
+ m_rWW8Export.InsUInt16( NS_sprm::PShd80::val );
+ m_rWW8Export.InsUInt16( aSHD.GetValue() );
+
+ m_rWW8Export.InsUInt16( NS_sprm::PShd::val );
+ m_rWW8Export.m_pO->push_back( 10 ); //size of operand: MUST be 10
+ m_rWW8Export.InsUInt32( 0xFF000000 ); //cvFore: Foreground BGR = cvAuto
+ m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); //cvBack
+ m_rWW8Export.InsUInt16( 0x0000 ); //iPat: specifies the pattern used for shading = clear/100% background
+}
+
+void WW8AttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
+{
+ // WW cannot have background in a section
+ if ( m_rWW8Export.m_bOutPageDescs )
+ return;
+
+ // see MSWordExportBase::OutputItemSet for how _SOLID is handled
+ if ( rFillStyle.GetValue() != drawing::FillStyle_NONE )
+ return;
+
+ //Shd80Nil
+ m_rWW8Export.InsUInt16( NS_sprm::PShd80::val );
+ m_rWW8Export.InsUInt16( 0xffff );
+
+ //cvAuto
+ m_rWW8Export.InsUInt16( NS_sprm::PShd::val );
+ m_rWW8Export.m_pO->push_back( 10 );
+ m_rWW8Export.InsUInt32( 0xFF000000 );
+ m_rWW8Export.InsUInt32( 0xFF000000 );
+ m_rWW8Export.InsUInt16( 0x0000 );
+}
+
+void WW8AttributeOutput::FormatFillGradient( const XFillGradientItem& /*rFillGradient*/ )
+{
+}
+
+WW8_BRCVer9 WW8Export::TranslateBorderLine(const SvxBorderLine& rLine,
+ sal_uInt16 nDist, bool bShadow)
+{
+ sal_uInt32 nColBGR = 0;
+ sal_uInt16 nWidth = ::editeng::ConvertBorderWidthToWord(
+ rLine.GetBorderLineStyle(), rLine.GetWidth());
+ sal_uInt8 brcType = 0;
+
+ if( nWidth ) // line ?
+ {
+ // BRC.brcType
+ brcType = 0;
+ // All the border types values are available on
+ // http://msdn.microsoft.com/en-us/library/dd908142%28v=office.12%29.aspx
+ switch (rLine.GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ {
+ if ( rLine.GetWidth( ) == SvxBorderLineWidth::Hairline )
+ brcType = 5;
+ else
+ brcType = 1;
+ }
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ brcType = 6;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ brcType = 7;
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ brcType = 3;
+ break;
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ brcType = 11;
+ break;
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ brcType = 14;
+ break;
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ brcType = 17;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ brcType = 12;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ brcType = 15;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ brcType = 18;
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ brcType = 24;
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ brcType = 25;
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ brcType = 26;
+ break;
+ case SvxBorderLineStyle::INSET:
+ brcType = 27;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ brcType = 22;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ brcType = 8;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ brcType = 9;
+ break;
+ default:
+ break;
+ }
+
+ // convert width from twips (1/20 pt) to eighths of a point
+ nWidth = (( nWidth * 8 ) + 10 ) / 20;
+ if( 0xff < nWidth )
+ nWidth = 0xff;
+
+ if( 0 == nWidth ) // really thin line
+ nWidth = 1; // don't omit
+
+ // BRC.cv
+ nColBGR = wwUtility::RGBToBGR(rLine.GetColor().GetRGBColor());
+ }
+
+ // BRC.dptSpace
+ sal_uInt16 nLDist = rtl::math::round(nDist / 20.0); // unit of measurement: pt
+ if( nLDist > 0x1f )
+ nLDist = 0x1f;
+
+ return WW8_BRCVer9(nColBGR, sal_uInt8(nWidth), brcType, sal_uInt8(nLDist),
+ bShadow, false);
+}
+
+/**
+ * Gets passed a WW8Bytes*, so the function also can be used for the table border.
+ *
+ * @param nSprmNo If nSprmNo == 0, then the opcode isn't outputted.
+ * @param bShadow SHOULDN'T be set for table cells !
+ */
+void WW8Export::Out_BorderLine(ww::bytes& rO, const SvxBorderLine* pLine,
+ sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, bool bShadow)
+{
+ OSL_ENSURE( ( nSprmNo == 0 ) ||
+ ( nSprmNo >= 38 && nSprmNo <= 41 ) ||
+ ( nSprmNo >= NS_sprm::PBrcTop80::val
+ && nSprmNo <= NS_sprm::PBrcRight80::val ) ||
+ ( nSprmNo >= NS_sprm::SBrcTop80::val
+ && nSprmNo <= NS_sprm::SBrcRight80::val ),
+ "Sprm for border out is of range" );
+
+ WW8_BRCVer9 aBrcVer9;
+ WW8_BRC aBrcVer8;
+
+ if( pLine && pLine->GetBorderLineStyle() != SvxBorderLineStyle::NONE )
+ {
+ aBrcVer9 = TranslateBorderLine( *pLine, nDist, bShadow );
+ sal_uInt8 ico = msfilter::util::TransColToIco( msfilter::util::BGRToRGB(aBrcVer9.cv()) );
+ aBrcVer8 = WW8_BRC( aBrcVer9.dptLineWidth(), aBrcVer9.brcType(), ico,
+ aBrcVer9.dptSpace(), aBrcVer9.fShadow(), aBrcVer9.fFrame() );
+ }
+
+ // WW97-SprmIds
+ if ( nSprmNo != 0 )
+ SwWW8Writer::InsUInt16( rO, nSprmNo );
+
+ rO.insert( rO.end(), aBrcVer8.aBits1, aBrcVer8.aBits2+2 );
+
+ if ( nSprmNoVer9 != 0 )
+ {
+ SwWW8Writer::InsUInt16( rO, nSprmNoVer9 );
+ rO.push_back(sizeof(WW8_BRCVer9));
+ rO.insert( rO.end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4);
+ }
+}
+
+/**
+ * is for all boxes except in tables. pO of the WW8Writer is used
+ *
+ * @param rBox
+ * @param bShadow
+ */
+void WW8Export::Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow)
+{
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+ static const sal_uInt16 aPBrc[] =
+ {
+ // WW8 SPRMs
+ NS_sprm::PBrcTop80::val, NS_sprm::PBrcLeft80::val,
+ NS_sprm::PBrcBottom80::val, NS_sprm::PBrcRight80::val,
+ // WW9 SPRMs
+ NS_sprm::PBrcTop::val, NS_sprm::PBrcLeft::val,
+ NS_sprm::PBrcBottom::val, NS_sprm::PBrcRight::val
+ };
+ static const sal_uInt16 aSBrc[] =
+ {
+ // WW8 SPRMs
+ NS_sprm::SBrcTop80::val, NS_sprm::SBrcLeft80::val,
+ NS_sprm::SBrcBottom80::val, NS_sprm::SBrcRight80::val,
+ // WW9 SPRMs
+ NS_sprm::SBrcTop::val, NS_sprm::SBrcLeft::val,
+ NS_sprm::SBrcBottom::val, NS_sprm::SBrcRight::val
+ };
+
+ const SvxBoxItemLine* pBrd = aBorders;
+ for( sal_uInt16 i = 0; i < 4; ++i, ++pBrd )
+ {
+ const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
+
+ sal_uInt16 nSprmNo, nSprmNoVer9 = 0;
+ if (m_bOutPageDescs)
+ {
+ nSprmNo = aSBrc[i];
+ nSprmNoVer9 = aSBrc[i+4];
+ }
+ else
+ {
+ nSprmNo = aPBrc[i];
+ nSprmNoVer9 = aPBrc[i+4];
+ }
+
+ Out_BorderLine( *m_pO, pLn, rBox.GetDistance( *pBrd ), nSprmNo,
+ nSprmNoVer9, bShadow );
+ }
+}
+
+/**
+ * FormatBox2() is for TC structures in tables. The Sprm opcode isn't written
+ * because it is packed into the TC structure without opcode.
+ * dxpSpace always becomes 0, because WW requires that in tables
+ * ( table borders otherwise will fray )
+ *
+ * @param rO A WW8Bytes pointer is passed in as output parameter
+ */
+void WW8Export::Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * pBox )
+{
+ // possible and maybe better would be 0xffff
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+ static const SvxBorderLine aBorderLine;
+
+ for(const SvxBoxItemLine & rBorder : aBorders)
+ {
+ const SvxBorderLine* pLn;
+ if (pBox != nullptr)
+ pLn = pBox->GetLine( rBorder );
+ else
+ pLn = & aBorderLine;
+
+ Out_BorderLine(rO, pLn, 0, 0, 0, false);
+ }
+}
+
+void WW8Export::Out_CellRangeBorders( const SvxBoxItem * pBox, sal_uInt8 nStart,
+ sal_uInt8 nLimit )
+{
+ if ( !pBox )
+ return;
+
+ static const SvxBoxItemLine aBorders[] =
+ {
+ SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
+ };
+
+ for( int i = 0; i < 4; ++i )
+ {
+ const SvxBorderLine* pLn = pBox->GetLine( aBorders[i] );
+ if (!pLn)
+ continue;
+
+ InsUInt16( NS_sprm::TSetBrc::val );
+ m_pO->push_back( 11 );
+ m_pO->push_back( nStart );
+ m_pO->push_back( nLimit );
+ m_pO->push_back( 1<<i );
+ WW8_BRCVer9 aBrcVer9 = TranslateBorderLine( *pLn, 0, false );
+ m_pO->insert( m_pO->end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4 );
+ }
+}
+
+void WW8AttributeOutput::FormatBox( const SvxBoxItem& rBox )
+{
+ // Fly around graphic -> here no border, because the
+ // graphics header already has the border
+ if ( m_rWW8Export.m_bOutGrf )
+ return;
+
+ bool bShadow = false;
+ const SvxShadowItem* pShadowItem = m_rWW8Export.HasItem( RES_SHADOW );
+ if ( pShadowItem )
+ {
+ bShadow = ( pShadowItem->GetLocation() != SvxShadowLocation::NONE )
+ && ( pShadowItem->GetWidth() != 0 );
+ }
+
+ SvxBoxItem aBox(rBox);
+ if (m_rWW8Export.m_bOutPageDescs)
+ {
+ editeng::WordBorderDistances aDistances;
+ editeng::BorderDistancesToWord(aBox, m_pageMargins, aDistances);
+
+ aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP);
+ aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT);
+ aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM);
+ aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT);
+
+ m_bFromEdge = aDistances.bFromEdge;
+ }
+
+ m_rWW8Export.Out_SwFormatBox( aBox, bShadow );
+}
+
+SwTwips WW8Export::CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const
+{
+ const SwFrameFormat* pFormat = m_pCurrentPageDesc ? &m_pCurrentPageDesc->GetMaster()
+ : &m_rDoc.GetPageDesc(0).GetMaster();
+
+ const SvxLRSpaceItem& rLR = pFormat->GetLRSpace();
+ SwTwips nPageSize = pFormat->GetFrameSize().GetWidth();
+ rLeft = rLR.GetLeft();
+ rRight = rLR.GetRight();
+ return nPageSize;
+}
+
+void WW8AttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize )
+{
+ // CColumns
+ m_rWW8Export.InsUInt16( NS_sprm::SCcolumns::val );
+ m_rWW8Export.InsUInt16( nCols - 1 );
+
+ // DxaColumns
+ m_rWW8Export.InsUInt16( NS_sprm::SDxaColumns::val );
+ m_rWW8Export.InsUInt16( rCol.GetGutterWidth( true ) );
+
+ // LBetween
+ m_rWW8Export.InsUInt16( NS_sprm::SLBetween::val );
+ m_rWW8Export.m_pO->push_back( COLADJ_NONE == rCol.GetLineAdj( )? 0 : 1 );
+
+ const SwColumns & rColumns = rCol.GetColumns( );
+
+ // FEvenlySpaced
+ m_rWW8Export.InsUInt16( NS_sprm::SFEvenlySpaced::val );
+ m_rWW8Export.m_pO->push_back( bEven ? 1 : 0 );
+
+ if ( bEven )
+ return;
+
+ for ( sal_uInt16 n = 0; n < nCols; ++n )
+ {
+ //sprmSDxaColWidth
+ m_rWW8Export.InsUInt16( NS_sprm::SDxaColWidth::val );
+ m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(n) );
+ m_rWW8Export.InsUInt16( rCol.
+ CalcPrtColWidth( n,
+ o3tl::narrowing<sal_uInt16>(nPageSize) ) );
+
+ if ( n + 1 != nCols )
+ {
+ //sprmSDxaColSpacing
+ m_rWW8Export.InsUInt16( NS_sprm::SDxaColSpacing::val );
+ m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(n) );
+ m_rWW8Export.InsUInt16( rColumns[n].GetRight( ) +
+ rColumns[n + 1].GetLeft( ) );
+ }
+ }
+}
+
+void AttributeOutputBase::FormatColumns( const SwFormatCol& rCol )
+{
+ const SwColumns& rColumns = rCol.GetColumns();
+
+ sal_uInt16 nCols = rColumns.size();
+ if (1 >= nCols || GetExport( ).m_bOutFlyFrameAttrs)
+ return;
+
+ // get the page width without borders !!
+
+ const SwFrameFormat* pFormat = GetExport( ).m_pCurrentPageDesc ? &GetExport( ).m_pCurrentPageDesc->GetMaster() : &const_cast<const SwDoc&>(GetExport().m_rDoc).GetPageDesc(0).GetMaster();
+ const SvxFrameDirectionItem &frameDirection = pFormat->GetFrameDir();
+ SwTwips nPageSize;
+ if ( frameDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB || frameDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB )
+ {
+ const SvxULSpaceItem &rUL = pFormat->GetULSpace();
+ nPageSize = pFormat->GetFrameSize().GetHeight();
+ nPageSize -= rUL.GetUpper() + rUL.GetLower();
+
+ const SwFormatHeader *header = pFormat->GetAttrSet().GetItem(RES_HEADER);
+ if ( header )
+ {
+ const SwFrameFormat *headerFormat = header->GetHeaderFormat();
+ if (headerFormat)
+ {
+ nPageSize -= headerFormat->GetFrameSize().GetHeight();
+ }
+ }
+ const SwFormatFooter *footer = pFormat->GetAttrSet().GetItem(RES_FOOTER);
+ if ( footer )
+ {
+ const SwFrameFormat *footerFormat = footer->GetFooterFormat();
+ if ( footerFormat )
+ {
+ nPageSize -= footerFormat->GetFrameSize().GetHeight();
+ }
+ }
+ }
+ else
+ {
+ const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
+ nPageSize = pFormat->GetFrameSize().GetWidth();
+ nPageSize -= rLR.GetLeft() + rLR.GetRight();
+ //i120133: The Section width should consider page indent value.
+ nPageSize -= rCol.GetAdjustValue();
+
+ }
+
+ // look if all columns are equal
+ bool bEven = rCol.IsOrtho();
+ if (!bEven)
+ {
+ bEven = true;
+ sal_uInt16 n;
+ sal_uInt16 nColWidth = rCol.CalcPrtColWidth( 0, o3tl::narrowing<sal_uInt16>(nPageSize) );
+ for ( n = 1; n < nCols; n++ )
+ {
+ short nDiff = nColWidth -
+ rCol.CalcPrtColWidth( n, o3tl::narrowing<sal_uInt16>(nPageSize) );
+
+ if ( nDiff > 10 || nDiff < -10 ) // Tolerance: 10 tw
+ {
+ bEven = false;
+ break;
+ }
+ }
+ }
+
+ FormatColumns_Impl( nCols, rCol, bEven, nPageSize );
+}
+
+// "Paragraphs together"
+void WW8AttributeOutput::FormatKeep( const SvxFormatKeepItem& rKeep )
+{
+ // sprmFKeepFollow
+ m_rWW8Export.InsUInt16( NS_sprm::PFKeepFollow::val );
+
+ m_rWW8Export.m_pO->push_back( rKeep.GetValue() ? 1 : 0 );
+}
+
+// exclude a paragraph from Line Numbering
+void WW8AttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
+{
+ // sprmPFNoLineNumb
+ m_rWW8Export.InsUInt16( NS_sprm::PFNoLineNumb::val );
+
+ m_rWW8Export.m_pO->push_back( rNumbering.IsCount() ? 0 : 1 );
+}
+
+/* File PARATR.HXX */
+
+void WW8AttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
+{
+ // sprmPDyaLine
+ m_rWW8Export.InsUInt16( NS_sprm::PDyaLine::val );
+
+ m_rWW8Export.InsUInt16( nSpace );
+ m_rWW8Export.InsUInt16( nMulti );
+}
+
+void AttributeOutputBase::ParaLineSpacing( const SvxLineSpacingItem& rSpacing )
+{
+ short nSpace = 240, nMulti = 0;
+
+ switch ( rSpacing.GetLineSpaceRule() )
+ {
+ default:
+ break;
+ case SvxLineSpaceRule::Fix: // Fix
+ nSpace = -static_cast<short>(rSpacing.GetLineHeight());
+ break;
+ case SvxLineSpaceRule::Min: // At least
+ nSpace = static_cast<short>(rSpacing.GetLineHeight());
+ break;
+ case SvxLineSpaceRule::Auto:
+ {
+ if( rSpacing.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) // Leading
+ {
+ // doesn't exist in WW - how do you get the MaxLineHeight?
+ nSpace = rSpacing.GetInterLineSpace();
+ sal_uInt16 nScript =
+ i18n::ScriptType::LATIN;
+ const SwAttrSet *pSet = nullptr;
+ if ( auto pFormat = dynamic_cast< const SwFormat *>( GetExport().m_pOutFormatNode ) )
+ {
+ pSet = &pFormat->GetAttrSet();
+ }
+ else if ( auto pNd = dynamic_cast< const SwTextNode *>( GetExport().m_pOutFormatNode ) )
+ {
+ pSet = &pNd->GetSwAttrSet();
+ nScript = g_pBreakIt->GetBreakIter()->getScriptType(pNd->GetText(), 0);
+ }
+ OSL_ENSURE( pSet, "No attrset for lineheight :-(" );
+ if ( pSet )
+ {
+ nSpace = nSpace + static_cast<short>( AttrSetToLineHeight( GetExport().m_rDoc.getIDocumentSettingAccess(),
+ *pSet, *Application::GetDefaultDevice(), nScript ) );
+ }
+ }
+ else // Proportional
+ {
+ if ( rSpacing.GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off )
+ nSpace = static_cast<short>( ( 240L * rSpacing.GetPropLineSpace() ) / 100L );
+ nMulti = 1;
+ }
+ }
+ break;
+ }
+ // if nSpace is negative, it is a fixed size in 1/20 of a point
+ // if nSpace is positive and nMulti is 1, it is 1/240 of a single line height
+ // otherwise, it is a minimum size in 1/20 of a point
+ ParaLineSpacing_Impl( nSpace, nMulti );
+}
+
+void WW8AttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
+{
+ // sprmPJc
+ sal_uInt8 nAdj;
+ sal_uInt8 nAdjBiDi;
+ switch ( rAdjust.GetAdjust() )
+ {
+ case SvxAdjust::Left:
+ nAdj = 0;
+ nAdjBiDi = 2;
+ break;
+ case SvxAdjust::Right:
+ nAdj = 2;
+ nAdjBiDi = 0;
+ break;
+ case SvxAdjust::BlockLine:
+ case SvxAdjust::Block:
+ nAdj = nAdjBiDi = rAdjust.GetLastBlock() == SvxAdjust::Block ? 4 : 3;
+ break;
+ case SvxAdjust::Center:
+ nAdj = nAdjBiDi = 1;
+ break;
+ default:
+ return; // not a supported Attribute
+ }
+
+ m_rWW8Export.InsUInt16(NS_sprm::PJc80::val);
+ m_rWW8Export.m_pO->push_back(nAdj);
+
+ /*
+ Sadly for left to right paragraphs both these values are the same,
+ for right to left paragraphs the bidi one is the reverse of the
+ normal one.
+ */
+ m_rWW8Export.InsUInt16(NS_sprm::PJc::val); //bidi version ?
+ bool bBiDiSwap = false;
+ if (m_rWW8Export.m_pOutFormatNode)
+ {
+ SvxFrameDirection nDirection = SvxFrameDirection::Horizontal_LR_TB;
+ if (auto pTN = dynamic_cast<const SwTextNode*>(m_rWW8Export.m_pOutFormatNode))
+ {
+ SwPosition aPos(*pTN);
+ nDirection = m_rWW8Export.m_rDoc.GetTextDirection(aPos);
+ }
+ else if (auto pC = dynamic_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode))
+ {
+ const SvxFrameDirectionItem &rItem = pC->GetFormatAttr(RES_FRAMEDIR);
+ nDirection = rItem.GetValue();
+ }
+ if ( ( nDirection == SvxFrameDirection::Horizontal_RL_TB ) ||
+ ( nDirection == SvxFrameDirection::Environment && AllSettings::GetLayoutRTL() ) )
+ {
+ bBiDiSwap = true;
+ }
+ }
+
+ if (bBiDiSwap)
+ m_rWW8Export.m_pO->push_back(nAdjBiDi);
+ else
+ m_rWW8Export.m_pO->push_back(nAdj);
+}
+
+void WW8AttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
+{
+ sal_uInt16 nTextFlow=0;
+ bool bBiDi = false;
+ SvxFrameDirection nDir = rDirection.GetValue();
+
+ if ( nDir == SvxFrameDirection::Environment )
+ nDir = GetExport( ).GetDefaultFrameDirection( );
+
+
+ switch ( nDir )
+ {
+ default:
+ //Can't get an unknown type here
+ OSL_FAIL("Unknown frame direction");
+ [[fallthrough]];
+ case SvxFrameDirection::Horizontal_LR_TB:
+ nTextFlow = 0;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ nTextFlow = 0;
+ bBiDi = true;
+ break;
+ case SvxFrameDirection::Vertical_LR_TB: //word doesn't have this
+ case SvxFrameDirection::Vertical_RL_TB:
+ nTextFlow = 1;
+ break;
+ }
+
+ if ( m_rWW8Export.m_bOutPageDescs )
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::STextFlow::val );
+ m_rWW8Export.InsUInt16( nTextFlow );
+ m_rWW8Export.InsUInt16( NS_sprm::SFBiDi::val );
+ m_rWW8Export.m_pO->push_back( bBiDi ? 1 : 0 );
+ }
+ else if ( !m_rWW8Export.m_bOutFlyFrameAttrs ) //paragraph/style
+ {
+ m_rWW8Export.InsUInt16( NS_sprm::PFBiDi::val );
+ m_rWW8Export.m_pO->push_back( bBiDi ? 1 : 0 );
+ }
+}
+
+void WW8AttributeOutput::ParaGrabBag(const SfxGrabBagItem& /*rItem*/)
+{
+}
+
+void WW8AttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/)
+{
+}
+
+void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
+{
+ sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel));
+ // Outline Level: in LO Body Text = 0, in MS Body Text = 9
+ nOutLvl = nOutLvl ? nOutLvl - 1 : 9;
+ m_rWW8Export.InsUInt16( NS_sprm::POutLvl::val );
+ m_rWW8Export.m_pO->push_back( nOutLvl );
+}
+
+// "Separate paragraphs"
+void WW8AttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
+{
+ // sprmPFKeep
+ m_rWW8Export.InsUInt16( NS_sprm::PFKeep::val );
+ m_rWW8Export.m_pO->push_back( rSplit.GetValue() ? 0 : 1 );
+}
+
+/**
+ * Only convert the item "SvxWidowItem" and not the orphans, because
+ * in WW only one attribute "paragraph control" exists for both and
+ * in SW probably both or none is set by the user.
+ */
+void WW8AttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
+{
+ // sprmPFWidowControl
+ m_rWW8Export.InsUInt16( NS_sprm::PFWidowControl::val );
+ m_rWW8Export.m_pO->push_back( rWidows.GetValue() ? 1 : 0 );
+}
+
+namespace {
+
+class SwWW8WrTabu
+{
+ std::unique_ptr<sal_uInt8[]> m_pDel; // DelArray
+ std::unique_ptr<sal_uInt8[]> m_pAddPos; // AddPos-Array
+ std::unique_ptr<sal_uInt8[]> m_pAddTyp; // AddTyp-Array
+ sal_uInt16 m_nAdd; // number of tabs to be added
+ sal_uInt16 m_nDel; // number of tabs to be deleted
+
+ SwWW8WrTabu(const SwWW8WrTabu&) = delete;
+ SwWW8WrTabu& operator=(const SwWW8WrTabu&) = delete;
+
+public:
+ SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax);
+
+ void Add(const SvxTabStop &rTS, tools::Long nAdjustment);
+ void Del(const SvxTabStop &rTS, tools::Long nAdjustment);
+ void PutAll(WW8Export& rWW8Wrt);
+};
+
+}
+
+SwWW8WrTabu::SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax)
+ : m_nAdd(0), m_nDel(0)
+{
+ if (nDelMax)
+ m_pDel.reset( new sal_uInt8[nDelMax * 2] );
+ m_pAddPos.reset( new sal_uInt8[nAddMax * 2] );
+ m_pAddTyp.reset( new sal_uInt8[nAddMax] );
+}
+
+/**
+ * insert a tab in the WW structure
+ */
+void SwWW8WrTabu::Add(const SvxTabStop & rTS, tools::Long nAdjustment)
+{
+ // insert tab position
+ ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
+ m_pAddPos.get() + (m_nAdd * 2));
+
+ // insert tab type
+ sal_uInt8 nPara = 0;
+ switch (rTS.GetAdjustment())
+ {
+ case SvxTabAdjust::Right:
+ nPara = 2;
+ break;
+ case SvxTabAdjust::Center:
+ nPara = 1;
+ break;
+ case SvxTabAdjust::Decimal:
+ /*
+ There is nothing we can do btw the decimal separator has been
+ customized, but if you think different remember that different
+ locales have different separators, i.e. german is a , while english
+ is a .
+ */
+ nPara = 3;
+ break;
+ default:
+ break;
+ }
+
+ switch( rTS.GetFill() )
+ {
+ case '.': // dotted leader
+ nPara |= 1 << 3;
+ break;
+ case '_': // Single line leader
+ nPara |= 3 << 3;
+ break;
+ case '-': // hyphenated leader
+ nPara |= 2 << 3;
+ break;
+ case '=': // heavy line leader
+ nPara |= 4 << 3;
+ break;
+ }
+
+ m_pAddTyp[m_nAdd] = nPara;
+ ++m_nAdd;
+}
+
+/**
+ * Insert a to be deleted tab in the WW structure
+ */
+void SwWW8WrTabu::Del(const SvxTabStop &rTS, tools::Long nAdjustment)
+{
+ // insert tab position
+ ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
+ m_pDel.get() + (m_nDel * 2));
+ ++m_nDel;
+}
+
+/**
+ * Writes the attribute to rWrt.pO
+ */
+void SwWW8WrTabu::PutAll(WW8Export& rWrt)
+{
+ if (!m_nAdd && !m_nDel) //If it's a no-op
+ return;
+ OSL_ENSURE(m_nAdd <= 255, "more than 255 added tabstops?");
+ OSL_ENSURE(m_nDel <= 255, "more than 244 removed tabstops?");
+ if (m_nAdd > 255)
+ m_nAdd = 255;
+ if (m_nDel > 255)
+ m_nDel = 255;
+
+ sal_uInt16 nSiz = 2 * m_nDel + 3 * m_nAdd + 2;
+ if (nSiz > 255)
+ nSiz = 255;
+
+ rWrt.InsUInt16(NS_sprm::PChgTabsPapx::val);
+ // insert cch
+ rWrt.m_pO->push_back(msword_cast<sal_uInt8>(nSiz));
+ // write DelArr
+ rWrt.m_pO->push_back(msword_cast<sal_uInt8>(m_nDel));
+ rWrt.OutSprmBytes(m_pDel.get(), m_nDel * 2);
+ // write InsArr
+ rWrt.m_pO->push_back(msword_cast<sal_uInt8>(m_nAdd));
+ rWrt.OutSprmBytes(m_pAddPos.get(), 2 * m_nAdd); // AddPosArray
+ rWrt.OutSprmBytes(m_pAddTyp.get(), m_nAdd); // AddTypArray
+}
+
+static void ParaTabStopAdd( WW8Export& rWrt,
+ const SvxTabStopItem& rTStops,
+ const tools::Long nLParaMgn )
+{
+ SwWW8WrTabu aTab( 0, rTStops.Count());
+
+ for( sal_uInt16 n = 0; n < rTStops.Count(); n++ )
+ {
+ const SvxTabStop& rTS = rTStops[n];
+ // ignore default tabs
+ if (SvxTabAdjust::Default != rTS.GetAdjustment())
+ aTab.Add(rTS, nLParaMgn);
+ }
+ aTab.PutAll( rWrt );
+}
+
+static bool lcl_IsEqual(tools::Long nOneLeft, const SvxTabStop &rOne,
+ tools::Long nTwoLeft, const SvxTabStop &rTwo)
+{
+ return(
+ nOneLeft == nTwoLeft &&
+ rOne.GetAdjustment() == rTwo.GetAdjustment() &&
+ rOne.GetDecimal() == rTwo.GetDecimal() &&
+ rOne.GetFill() == rTwo.GetFill()
+ );
+}
+
+static void ParaTabStopDelAdd( WW8Export& rWrt,
+ const SvxTabStopItem& rTStyle,
+ const tools::Long nLStypeMgn,
+ const SvxTabStopItem& rTNew,
+ const tools::Long nLParaMgn )
+{
+ SwWW8WrTabu aTab(rTStyle.Count(), rTNew.Count());
+
+ sal_uInt16 nO = 0; // rTStyle Index
+ sal_uInt16 nN = 0; // rTNew Index
+
+ do {
+ const SvxTabStop* pTO;
+ tools::Long nOP;
+ if( nO < rTStyle.Count() ) // old not yet at the end?
+ {
+ pTO = &rTStyle[ nO ];
+ nOP = pTO->GetTabPos() + nLStypeMgn;
+ if( SvxTabAdjust::Default == pTO->GetAdjustment() )
+ {
+ nO++; // ignore default tab
+ continue;
+ }
+ }
+ else
+ {
+ pTO = nullptr;
+ nOP = LONG_MAX;
+ }
+
+ const SvxTabStop* pTN;
+ tools::Long nNP;
+ if( nN < rTNew.Count() ) // new not yet at the end
+ {
+ pTN = &rTNew[ nN ];
+ nNP = pTN->GetTabPos() + nLParaMgn;
+ if( SvxTabAdjust::Default == pTN->GetAdjustment() )
+ {
+ nN++; // ignore default tab
+ continue;
+ }
+ }
+ else
+ {
+ pTN = nullptr;
+ nNP = LONG_MAX;
+ }
+
+ if( nOP == LONG_MAX && nNP == LONG_MAX )
+ break; // everything done
+
+ if( nOP < nNP ) // next tab is old
+ {
+ assert(pTO);
+ aTab.Del(*pTO, nLStypeMgn); // must be deleted
+ nO++;
+ }
+ else if( nNP < nOP ) // next tab is new
+ {
+ assert(pTN);
+ aTab.Add(*pTN, nLParaMgn); // must be inserted
+ nN++;
+ }
+ else if (lcl_IsEqual(nOP, *pTO, nNP, *pTN)) // tabs are equal
+ {
+ nO++; // nothing to do
+ nN++;
+ }
+ else // tabs same position, different type
+ {
+ aTab.Del(*pTO, nLStypeMgn); // delete old one
+ aTab.Add(*pTN, nLParaMgn); // insert new one
+ nO++;
+ nN++;
+ }
+ } while( true );
+
+ aTab.PutAll( rWrt );
+}
+
+void WW8AttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStops )
+{
+ const bool bTabsRelativeToIndex = m_rWW8Export.m_pCurPam->GetDoc().getIDocumentSettingAccess().get( DocumentSettingId::TABS_RELATIVE_TO_INDENT );
+
+ tools::Long nCurrentLeft = 0;
+ if ( bTabsRelativeToIndex )
+ {
+ if (const SfxPoolItem* pItem = m_rWW8Export.HasItem(RES_MARGIN_TEXTLEFT))
+ {
+ if (const auto pLeft = pItem->DynamicWhichCast(RES_MARGIN_TEXTLEFT))
+ {
+ nCurrentLeft = pLeft->GetTextLeft();
+ }
+ else
+ // FIXME: This fails in sw.ww8export/testCommentExport::Load_Verify_Reload_Verify
+ SAL_WARN("sw.ww8", "m_rWW8Export has an RES_LR_SPACE item, but it's of the wrong type.");
+ }
+ }
+
+ // #i100264#
+ if ( m_rWW8Export.m_bStyDef &&
+ m_rWW8Export.m_pCurrentStyle != nullptr &&
+ m_rWW8Export.m_pCurrentStyle->DerivedFrom() != nullptr )
+ {
+ SvxTabStopItem aParentTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ const SwFormat *pParentStyle = m_rWW8Export.m_pCurrentStyle->DerivedFrom();
+ {
+ if (const SvxTabStopItem* pParentTabs = pParentStyle->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP))
+ {
+ aParentTabs.Insert( pParentTabs );
+ }
+ }
+
+ // #i120938# - consider left indentation of style and its parent style
+ tools::Long nParentLeft = 0;
+ if ( bTabsRelativeToIndex )
+ {
+ SvxTextLeftMarginItem const& rLeftMargin(pParentStyle->GetAttrSet().Get(RES_MARGIN_TEXTLEFT));
+ nParentLeft = rLeftMargin.GetTextLeft();
+ }
+
+ ParaTabStopDelAdd( m_rWW8Export, aParentTabs, nParentLeft, rTabStops, nCurrentLeft );
+ return;
+ }
+
+ const SvxTabStopItem* pStyleTabs = nullptr;
+ if ( !m_rWW8Export.m_bStyDef && m_rWW8Export.m_pStyAttr )
+ {
+ pStyleTabs = m_rWW8Export.m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
+ }
+
+ if ( !pStyleTabs )
+ {
+ ParaTabStopAdd(m_rWW8Export, rTabStops, nCurrentLeft);
+ }
+ else
+ {
+ tools::Long nStyleLeft = 0;
+ if ( bTabsRelativeToIndex )
+ {
+ SvxTextLeftMarginItem const& rLeftMargin(m_rWW8Export.m_pStyAttr->Get(RES_MARGIN_TEXTLEFT));
+ nStyleLeft = rLeftMargin.GetTextLeft();
+ }
+
+ ParaTabStopDelAdd( m_rWW8Export,
+ *pStyleTabs, nStyleLeft,
+ rTabStops, nCurrentLeft);
+ }
+}
+
+void AttributeOutputBase::OutputItem( const SfxPoolItem& rHt )
+{
+ // FIXME maybe use 'item_cast', like 'item_cast<SvxCharHiddenItem>( rHt )'?
+ switch ( rHt.Which() )
+ {
+ case RES_CHRATR_CASEMAP:
+ CharCaseMap(rHt.StaticWhichCast(RES_CHRATR_CASEMAP));
+ break;
+ case RES_CHRATR_COLOR:
+ CharColor(rHt.StaticWhichCast(RES_CHRATR_COLOR));
+ break;
+ case RES_CHRATR_CONTOUR:
+ CharContour(rHt.StaticWhichCast(RES_CHRATR_CONTOUR));
+ break;
+ case RES_CHRATR_CROSSEDOUT:
+ CharCrossedOut(rHt.StaticWhichCast(RES_CHRATR_CROSSEDOUT));
+ break;
+ case RES_CHRATR_ESCAPEMENT:
+ CharEscapement(rHt.StaticWhichCast(RES_CHRATR_ESCAPEMENT));
+ break;
+ case RES_CHRATR_FONT:
+ CharFont(rHt.StaticWhichCast(RES_CHRATR_FONT));
+ break;
+ case RES_CHRATR_FONTSIZE:
+ CharFontSize(rHt.StaticWhichCast(RES_CHRATR_FONTSIZE));
+ break;
+ case RES_CHRATR_KERNING:
+ CharKerning(rHt.StaticWhichCast(RES_CHRATR_KERNING));
+ break;
+ case RES_CHRATR_LANGUAGE:
+ CharLanguage(rHt.StaticWhichCast(RES_CHRATR_LANGUAGE));
+ break;
+ case RES_CHRATR_POSTURE:
+ CharPosture(rHt.StaticWhichCast(RES_CHRATR_POSTURE));
+ break;
+ case RES_CHRATR_SHADOWED:
+ CharShadow(rHt.StaticWhichCast(RES_CHRATR_SHADOWED));
+ break;
+ case RES_CHRATR_UNDERLINE:
+ CharUnderline(rHt.StaticWhichCast(RES_CHRATR_UNDERLINE));
+ break;
+ case RES_CHRATR_WEIGHT:
+ CharWeight(rHt.StaticWhichCast(RES_CHRATR_WEIGHT));
+ break;
+ case RES_CHRATR_AUTOKERN:
+ CharAutoKern(rHt.StaticWhichCast(RES_CHRATR_AUTOKERN));
+ break;
+ case RES_CHRATR_BLINK:
+ CharAnimatedText(rHt.StaticWhichCast(RES_CHRATR_BLINK));
+ break;
+ case RES_CHRATR_BACKGROUND:
+ CharBackgroundBase(rHt.StaticWhichCast(RES_CHRATR_BACKGROUND));
+ break;
+
+ case RES_CHRATR_CJK_FONT:
+ CharFontCJK( static_cast< const SvxFontItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ CharFontSizeCJK( static_cast< const SvxFontHeightItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ CharLanguageCJK( static_cast< const SvxLanguageItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CJK_POSTURE:
+ CharPostureCJK( static_cast< const SvxPostureItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CJK_WEIGHT:
+ CharWeightCJK( static_cast< const SvxWeightItem& >( rHt ) );
+ break;
+
+ case RES_CHRATR_CTL_FONT:
+ CharFontCTL( static_cast< const SvxFontItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ CharFontSizeCTL( static_cast< const SvxFontHeightItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ CharLanguageCTL( static_cast< const SvxLanguageItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CTL_POSTURE:
+ CharPostureCTL( static_cast< const SvxPostureItem& >( rHt ) );
+ break;
+ case RES_CHRATR_CTL_WEIGHT:
+ CharWeightCTL( static_cast< const SvxWeightItem& >( rHt ) );
+ break;
+
+ case RES_CHRATR_ROTATE:
+ CharRotate(rHt.StaticWhichCast(RES_CHRATR_ROTATE));
+ break;
+ case RES_CHRATR_EMPHASIS_MARK:
+ CharEmphasisMark(rHt.StaticWhichCast(RES_CHRATR_EMPHASIS_MARK));
+ break;
+ case RES_CHRATR_TWO_LINES:
+ CharTwoLines(rHt.StaticWhichCast(RES_CHRATR_TWO_LINES));
+ break;
+ case RES_CHRATR_SCALEW:
+ CharScaleWidth(rHt.StaticWhichCast(RES_CHRATR_SCALEW));
+ break;
+ case RES_CHRATR_RELIEF:
+ CharRelief(rHt.StaticWhichCast(RES_CHRATR_RELIEF));
+ break;
+ case RES_CHRATR_HIDDEN:
+ CharHidden(rHt.StaticWhichCast(RES_CHRATR_HIDDEN));
+ break;
+ case RES_CHRATR_BOX:
+ FormatCharBorder(rHt.StaticWhichCast(RES_CHRATR_BOX));
+ break;
+ case RES_CHRATR_HIGHLIGHT:
+ CharHighlight(rHt.StaticWhichCast(RES_CHRATR_HIGHLIGHT));
+ break;
+ case RES_CHRATR_BIDIRTL:
+ CharBidiRTL( rHt );
+ break;
+ case RES_CHRATR_IDCTHINT:
+ CharIdctHint( rHt );
+ break;
+ case RES_TXTATR_INETFMT:
+ TextINetFormat( static_cast< const SwFormatINetFormat& >( rHt ) );
+ break;
+ case RES_TXTATR_CHARFMT:
+ TextCharFormat( static_cast< const SwFormatCharFormat& >( rHt ) );
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ TextField( static_cast< const SwFormatField& >( rHt ) );
+ break;
+
+ case RES_TXTATR_FLYCNT:
+ TextFlyContent( static_cast< const SwFormatFlyCnt& >( rHt ) );
+ break;
+ case RES_TXTATR_FTN:
+ TextFootnote( static_cast< const SwFormatFootnote& >( rHt ) );
+ break;
+
+ case RES_PARATR_LINESPACING:
+ ParaLineSpacing( static_cast< const SvxLineSpacingItem& >( rHt ) );
+ break;
+ case RES_PARATR_ADJUST:
+ ParaAdjust( static_cast< const SvxAdjustItem& >( rHt ) );
+ break;
+ case RES_PARATR_SPLIT:
+ ParaSplit( static_cast< const SvxFormatSplitItem& >( rHt ) );
+ break;
+ case RES_PARATR_WIDOWS:
+ ParaWidows( static_cast< const SvxWidowsItem& >( rHt ) );
+ break;
+ case RES_PARATR_TABSTOP:
+ ParaTabStop( static_cast< const SvxTabStopItem& >( rHt ) );
+ break;
+ case RES_PARATR_HYPHENZONE:
+ ParaHyphenZone( static_cast< const SvxHyphenZoneItem& >( rHt ) );
+ break;
+ case RES_PARATR_NUMRULE:
+ ParaNumRule( static_cast< const SwNumRuleItem& >( rHt ) );
+ break;
+ case RES_PARATR_SCRIPTSPACE:
+ ParaScriptSpace( static_cast< const SfxBoolItem& >( rHt ) );
+ break;
+ case RES_PARATR_HANGINGPUNCTUATION:
+ ParaHangingPunctuation( static_cast< const SfxBoolItem& >( rHt ) );
+ break;
+ case RES_PARATR_FORBIDDEN_RULES:
+ ParaForbiddenRules( static_cast< const SfxBoolItem& >( rHt ) );
+ break;
+ case RES_PARATR_VERTALIGN:
+ ParaVerticalAlign( static_cast< const SvxParaVertAlignItem& >( rHt ) );
+ break;
+ case RES_PARATR_SNAPTOGRID:
+ ParaSnapToGrid( static_cast< const SvxParaGridItem& >( rHt ) );
+ break;
+
+ case RES_FRM_SIZE:
+ FormatFrameSize( static_cast< const SwFormatFrameSize& >( rHt ) );
+ break;
+ case RES_PAPER_BIN:
+ FormatPaperBin( static_cast< const SvxPaperBinItem& >( rHt ) );
+ break;
+ case RES_MARGIN_FIRSTLINE:
+ FormatFirstLineIndent(static_cast<const SvxFirstLineIndentItem &>(rHt));
+ break;
+ case RES_MARGIN_TEXTLEFT:
+ FormatTextLeftMargin(static_cast<const SvxTextLeftMarginItem &>(rHt));
+ break;
+ case RES_MARGIN_RIGHT:
+ FormatRightMargin(static_cast<const SvxRightMarginItem &>(rHt));
+ break;
+ case RES_LR_SPACE:
+ FormatLRSpace( static_cast< const SvxLRSpaceItem& >( rHt ) );
+ break;
+ case RES_UL_SPACE:
+ FormatULSpace( static_cast< const SvxULSpaceItem& >( rHt ) );
+ break;
+ case RES_PAGEDESC:
+ FormatPageDescription( static_cast< const SwFormatPageDesc& >( rHt ) );
+ break;
+ case RES_BREAK:
+ FormatBreak( static_cast< const SvxFormatBreakItem& >( rHt ) );
+ break;
+ case RES_SURROUND:
+ FormatSurround( static_cast< const SwFormatSurround& >( rHt ) );
+ break;
+ case RES_VERT_ORIENT:
+ FormatVertOrientation( static_cast< const SwFormatVertOrient& >( rHt ) );
+ break;
+ case RES_HORI_ORIENT:
+ FormatHorizOrientation( static_cast< const SwFormatHoriOrient& >( rHt ) );
+ break;
+ case RES_ANCHOR:
+ FormatAnchor( static_cast< const SwFormatAnchor& >( rHt ) );
+ break;
+ case RES_BACKGROUND:
+ FormatBackground( static_cast< const SvxBrushItem& >( rHt ) );
+ break;
+ case XATTR_FILLSTYLE:
+ FormatFillStyle( static_cast< const XFillStyleItem& >( rHt ) );
+ break;
+ case XATTR_FILLGRADIENT:
+ FormatFillGradient( static_cast< const XFillGradientItem& >( rHt ) );
+ break;
+ case RES_BOX:
+ FormatBox( static_cast< const SvxBoxItem& >( rHt ) );
+ break;
+ case RES_COL:
+ FormatColumns( static_cast< const SwFormatCol& >( rHt ) );
+ break;
+ case RES_KEEP:
+ FormatKeep( static_cast< const SvxFormatKeepItem& >( rHt ) );
+ break;
+ case RES_TEXTGRID:
+ FormatTextGrid( static_cast< const SwTextGridItem& >( rHt ) );
+ break;
+ case RES_LINENUMBER:
+ FormatLineNumberingBase(static_cast<const SwFormatLineNumber&>(rHt));
+ break;
+ case RES_FRAMEDIR:
+ FormatFrameDirection( static_cast< const SvxFrameDirectionItem& >( rHt ) );
+ break;
+ case RES_PARATR_GRABBAG:
+ ParaGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
+ break;
+ case RES_PARATR_OUTLINELEVEL:
+ ParaOutlineLevelBase(static_cast<const SfxUInt16Item&>(rHt));
+ break;
+ case RES_CHRATR_GRABBAG:
+ CharGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
+ break;
+ case RES_RTL_GUTTER:
+ SectionRtlGutter(static_cast<const SfxBoolItem&>(rHt));
+ break;
+ case RES_TXTATR_LINEBREAK:
+ TextLineBreak(static_cast<const SwFormatLineBreak&>(rHt));
+ break;
+
+ default:
+ SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
+ break;
+ }
+}
+
+void AttributeOutputBase::OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault )
+{
+ // based on OutputItemSet() from wrt_fn.cxx
+
+ const SfxItemPool& rPool = *rSet.GetPool();
+ const SfxItemSet* pSet = &rSet;
+ if ( !pSet->Count() )
+ {
+ while ( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() )
+ ;
+
+ if ( !pSet )
+ return;
+ }
+
+ const SfxPoolItem* pItem;
+ if ( !pSet->GetParent() )
+ {
+ assert(rSet.Count() && "Was already handled or?");
+ SfxItemIter aIter( *pSet );
+ pItem = aIter.GetCurItem();
+ do {
+ OutputItem( *pItem );
+ } while ((pItem = aIter.NextItem()));
+ }
+ else
+ {
+ SfxWhichIter aIter( *pSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ if ( SfxItemState::SET == aIter.GetItemState( true/*bDeep*/, &pItem ) &&
+ ( !bTestForDefault ||
+ nWhich == RES_UL_SPACE ||
+ nWhich == RES_LR_SPACE ||
+ *pItem != rPool.GetDefaultItem( nWhich ) ||
+ ( pSet->GetParent() && *pItem != pSet->GetParent()->Get( nWhich ) ) ) )
+ {
+ OutputItem( *pItem );
+ }
+ nWhich = aIter.NextWhich();
+ }
+ }
+}
+
+void AttributeOutputBase::FormatCharBorder( const SvxBoxItem& rBox )
+{
+ // Get one of the borders (if there is any border then in docx also will be)
+ const SvxBorderLine* pBorderLine = nullptr;
+ sal_uInt16 nDist = 0;
+ if( rBox.GetTop() )
+ {
+ pBorderLine = rBox.GetTop();
+ nDist = rBox.GetDistance( SvxBoxItemLine::TOP );
+ }
+ else if( rBox.GetLeft() )
+ {
+ pBorderLine = rBox.GetLeft();
+ nDist = rBox.GetDistance( SvxBoxItemLine::LEFT );
+ }
+ else if( rBox.GetBottom() )
+ {
+ pBorderLine = rBox.GetBottom();
+ nDist = rBox.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ else if( rBox.GetRight() )
+ {
+ pBorderLine = rBox.GetRight();
+ nDist = rBox.GetDistance( SvxBoxItemLine::RIGHT );
+ }
+
+ // RTF: avoid regressions since RTF doesn't know how to export a border_NONE style-override
+ if( pBorderLine || GetExport().GetExportFormat() != MSWordExportBase::ExportFormat::RTF )
+ {
+ const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW );
+ const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
+ const bool bShadow = pBorderLine &&
+ pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE &&
+ pShadowItem->GetWidth() > 0;
+
+ CharBorder( pBorderLine, nDist, bShadow );
+ }
+}
+
+/*
+ * This function is used to check if the current SwTextNode (paragraph) has a redline object
+ * that is attached to the paragraph marker.
+ * This is done by checking if the range (SwPaM) of the redline is :
+ * - Start = the last character of the current paragraph
+ * - End = the first character of the next paragraph
+ */
+const SwRedlineData* AttributeOutputBase::GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType)
+{
+ // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos'
+ for(SwRangeRedline* pRedl : GetExport().m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ // Only check redlines that are of type 'Delete'
+ if ( pRedl->GetRedlineData().GetType() != aRedlineType )
+ continue;
+
+ SwNodeOffset uStartNodeIndex = pRedl->Start()->GetNodeIndex();
+ SwNodeOffset uEndNodeIndex = pRedl->End()->GetNodeIndex();
+ SwNodeOffset uNodeIndex = rNode.GetIndex();
+
+ if( uStartNodeIndex <= uNodeIndex && uNodeIndex < uEndNodeIndex )
+ return &( pRedl->GetRedlineData() );
+ }
+ return nullptr;
+}
+
+void AttributeOutputBase::CharBackgroundBase( const SvxBrushItem& rBrush )
+{
+ bool bConvertToShading = SvtFilterOptions::Get().IsCharBackground2Shading();
+ bool bHasShadingMarker = false;
+
+ // MS Word doesn't support highlight in character styles. Always export those as shading.
+ if ( !bConvertToShading && GetExport().m_bStyDef )
+ {
+ const SwFormat* pFormat = dynamic_cast<const SwFormat*>( GetExport().m_pOutFormatNode );
+ bConvertToShading = pFormat && pFormat->Which() == RES_CHRFMT;
+ }
+
+ // Check shading marker
+ const SfxPoolItem* pItem = GetExport().HasItem(RES_CHRATR_GRABBAG);
+ if( pItem )
+ {
+ const SfxGrabBagItem aGrabBag = static_cast< const SfxGrabBagItem& >(*pItem);
+ const std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
+ auto aIterator = rMap.find("CharShadingMarker");
+ if( aIterator != rMap.end() )
+ {
+ aIterator->second >>= bHasShadingMarker;
+ }
+ }
+
+ if( bConvertToShading || bHasShadingMarker )
+ {
+ CharBackground(rBrush);
+ }
+ else
+ {
+ // Don't create a duplicate entry when converting to highlight. An existing one has priority.
+ // Character runs seem to need a different method to detect duplicates? Just continue to ignore that situation.
+ if (GetExport().m_aCurrentCharPropStarts.size() || !GetExport().HasItem(RES_CHRATR_HIGHLIGHT))
+ CharHighlight(rBrush);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx
new file mode 100644
index 0000000000..eb20f529c5
--- /dev/null
+++ b/sw/source/filter/ww8/ww8attributeoutput.hxx
@@ -0,0 +1,518 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX
+
+#include "attributeoutputbase.hxx"
+#include "wrtww8.hxx"
+#include <editeng/boxitem.hxx>
+#include <sfx2/docfile.hxx>
+
+class WW8AttributeOutput : public AttributeOutputBase
+{
+public:
+ /// Export the state of RTL/CJK.
+ virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) override;
+
+ /// Start of the paragraph.
+ virtual sal_Int32 StartParagraph( ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, bool /*bGenerateParaId*/ ) override { return 0; }
+
+ /// End of the paragraph.
+ virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override;
+
+ /// Called in order to output section breaks.
+ virtual void SectionBreaks(const SwNode& /*rNode*/) override {}
+
+ /// Called before we start outputting the attributes.
+ virtual void StartParagraphProperties() override {}
+
+ /// Called after we end outputting the attributes.
+ virtual void EndParagraphProperties(const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/, const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/, const SwRedlineData* /*pRedlineParagraphMarkerInserted*/) override {}
+
+ /// Empty paragraph.
+ virtual void EmptyParagraph() override;
+
+ /// Start of the text run.
+ ///
+ virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) override;
+
+ virtual void OnTOXEnding() override;
+
+ /// End of the text run.
+ ///
+ /// No-op for binary filters.
+ virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool bLastRun = false) override;
+
+ /// Before we start outputting the attributes.
+ virtual void StartRunProperties() override;
+
+ /// After we end outputting the attributes.
+ virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override;
+
+ /// Output text.
+ virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8, const OUString& rSymbolFont = OUString() ) override;
+
+ /// Output text (without markup).
+ virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override;
+
+ /// Output ruby start.
+ virtual void StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) override;
+
+ /// Output ruby end.
+ virtual void EndRuby(const SwTextNode& rNode, sal_Int32 nPos) override;
+
+ /// Output URL start.
+ virtual bool StartURL( const OUString &rUrl, const OUString &rTarget ) override;
+
+ /// Output URL end.
+ virtual bool EndURL(bool) override;
+
+ virtual void FieldVanish(const OUString& rText, ww::eField eType, OUString const* pBookmarkName) override;
+
+ /// Output redlining.
+ virtual void Redline( const SwRedlineData* pRedline ) override;
+
+ virtual void FormatDrop( const SwTextNode& rNode, const SwFormatDrop &rSwFormatDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override;
+
+ /// Output FKP (Formatted disK Page) - necessary for binary formats only.
+ /// FIXME having it in AttributeOutputBase is probably a hack, it
+ /// should be in WW8AttributeOutput only...
+ virtual void OutputFKP(bool bForce) override;
+
+ /// Output style.
+ virtual void ParagraphStyle( sal_uInt16 nStyle ) override;
+
+ virtual void TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ void TablePositioning(SwFrameFormat* pFlyFormat);
+ virtual void TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override;
+ virtual void TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) override;
+ virtual void TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override;
+ virtual void TableRowEnd( sal_uInt32 nDepth ) override;
+
+ /// Start of the styles table.
+ virtual void StartStyles() override;
+
+ /// End of the styles table.
+ virtual void EndStyles( sal_uInt16 nNumberOfStyles ) override;
+
+ /// Write default style.
+ virtual void DefaultStyle() override;
+
+ /// Start of a style in the styles table.
+ virtual void StartStyle( const OUString& rName, StyleType eType,
+ sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nLink, sal_uInt16 nWwIdi, sal_uInt16 nSlot,
+ bool bAutoUpdate ) override;
+
+ /// End of a style in the styles table.
+ virtual void EndStyle() override;
+
+ /// Start of (paragraph or run) properties of a style.
+ virtual void StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) override;
+
+ /// End of (paragraph or run) properties of a style.
+ virtual void EndStyleProperties( bool bParProp ) override;
+
+ /// Numbering rule and Id.
+ virtual void OutlineNumbering(sal_uInt8 nLvl) override;
+
+ /// Page break
+ /// As a paragraph property - the paragraph should be on the next page.
+ virtual void PageBreakBefore( bool bBreak ) override;
+
+ /// Write a section break
+ /// msword::ColumnBreak or msword::PageBreak
+ virtual void SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr, bool bExtraPageBreak = false ) override;
+
+ // preserve DOC page vertical alignment
+ virtual void TextVerticalAdjustment( const css::drawing::TextVerticalAdjust ) override;
+
+ /// Start of the section properties.
+ virtual void StartSection() override;
+
+ // footnote / endnote section properties
+ virtual void SectFootnoteEndnotePr() override;
+
+ /// End of the section properties.
+ ///
+ /// No-op for binary filters.
+ virtual void EndSection() override {}
+
+ /// Protection of forms.
+ virtual void SectionFormProtection( bool bProtected ) override;
+
+ /// Numbering of the lines in the document.
+ virtual void SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) override;
+
+ /// Has different headers/footers for the title page.
+ virtual void SectionTitlePage() override;
+
+ /// Description of the page borders.
+ virtual void SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* pFirstPageFormat ) override;
+
+ /// Columns populated from right/numbers on the right side?
+ virtual void SectionBiDi( bool bBiDi ) override;
+
+ /// The style of the page numbers.
+ ///
+ virtual void SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) override;
+
+ /// The type of breaking.
+ virtual void SectionType( sal_uInt8 nBreakCode ) override;
+
+ /// Definition of a numbering instance.
+ virtual void NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) override;
+
+ /// All the numbering level information.
+ virtual void NumberingLevel( sal_uInt8 nLevel,
+ sal_uInt16 nStart,
+ sal_uInt16 nNumberingType,
+ SvxAdjust eAdjust,
+ const sal_uInt8 *pNumLvlPos,
+ sal_uInt8 nFollow,
+ const wwFont *pFont,
+ const SfxItemSet *pOutSet,
+ sal_Int16 nIndentAt,
+ sal_Int16 nFirstLineIndex,
+ sal_Int16 nListTabPos,
+ const OUString &rNumberingString,
+ const SvxBrushItem* pBrush, //For i120928,transfer graphic of bullet
+ bool isLegal) override;
+
+protected:
+ /// Output frames - the implementation.
+ void OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) override;
+
+ /// Sfx item Sfx item RES_CHRATR_CASEMAP
+ virtual void CharCaseMap( const SvxCaseMapItem& ) override;
+
+ /// Sfx item Sfx item RES_CHRATR_COLOR
+ virtual void CharColor( const SvxColorItem& ) override;
+
+ /// Sfx item Sfx item RES_CHRATR_CONTOUR
+ virtual void CharContour( const SvxContourItem& ) override;
+
+ /// Sfx item RES_CHRATR_CROSSEDOUT
+ virtual void CharCrossedOut( const SvxCrossedOutItem& rHt ) override;
+
+ /// Sfx item RES_CHRATR_ESCAPEMENT
+ virtual void CharEscapement( const SvxEscapementItem& ) override;
+
+ /// Sfx item RES_CHRATR_FONT
+ virtual void CharFont( const SvxFontItem& ) override;
+
+ /// Sfx item RES_CHRATR_FONTSIZE
+ virtual void CharFontSize( const SvxFontHeightItem& ) override;
+
+ /// Sfx item RES_CHRATR_KERNING
+ virtual void CharKerning( const SvxKerningItem& ) override;
+
+ /// Sfx item RES_CHRATR_LANGUAGE
+ virtual void CharLanguage( const SvxLanguageItem& ) override;
+
+ /// Sfx item RES_CHRATR_POSTURE
+ virtual void CharPosture( const SvxPostureItem& ) override;
+
+ /// Sfx item RES_CHRATR_SHADOWED
+ virtual void CharShadow( const SvxShadowedItem& ) override;
+
+ /// Sfx item RES_CHRATR_UNDERLINE
+ virtual void CharUnderline( const SvxUnderlineItem& ) override;
+
+ /// Sfx item RES_CHRATR_WEIGHT
+ virtual void CharWeight( const SvxWeightItem& ) override;
+
+ /// Sfx item RES_CHRATR_AUTOKERN
+ virtual void CharAutoKern( const SvxAutoKernItem& ) override;
+
+ /// Sfx item RES_CHRATR_BLINK
+ virtual void CharAnimatedText( const SvxBlinkItem& ) override;
+
+ /// Sfx item RES_CHRATR_BACKGROUND
+ virtual void CharBackground( const SvxBrushItem& ) override;
+
+ /// Sfx item RES_CHRATR_CJK_FONT
+ virtual void CharFontCJK( const SvxFontItem& ) override;
+
+ /// Sfx item RES_CHRATR_CJK_FONTSIZE
+ virtual void CharFontSizeCJK( const SvxFontHeightItem& rHt ) override { CharFontSize( rHt ); }
+
+ /// Sfx item RES_CHRATR_CJK_LANGUAGE
+ virtual void CharLanguageCJK( const SvxLanguageItem& rHt ) override { CharLanguage( rHt ); }
+
+ /// Sfx item RES_CHRATR_CJK_POSTURE
+ virtual void CharPostureCJK( const SvxPostureItem& rHt ) override { CharPosture( rHt ); }
+
+ /// Sfx item RES_CHRATR_CJK_WEIGHT
+ virtual void CharWeightCJK( const SvxWeightItem& rHt ) override { CharWeight( rHt ); }
+
+ /// Sfx item RES_CHRATR_CTL_FONT
+ virtual void CharFontCTL( const SvxFontItem& ) override;
+
+ /// Sfx item RES_CHRATR_CTL_FONTSIZE
+ virtual void CharFontSizeCTL( const SvxFontHeightItem& rHt ) override { CharFontSize( rHt ); }
+
+ /// Sfx item RES_CHRATR_CTL_LANGUAGE
+ virtual void CharLanguageCTL( const SvxLanguageItem& rHt ) override { CharLanguage( rHt ); }
+
+ /// Sfx item RES_CHRATR_CTL_POSTURE
+ virtual void CharPostureCTL( const SvxPostureItem& ) override;
+
+ /// Sfx item RES_CHRATR_CTL_WEIGHT
+ virtual void CharWeightCTL( const SvxWeightItem& ) override;
+
+ /// Sfx item RES_CHRATR_BidiRTL
+ virtual void CharBidiRTL( const SfxPoolItem& rHt ) override;
+
+ /// Sfx item RES_CHRATR_IdctHint
+ virtual void CharIdctHint( const SfxPoolItem& rHt ) override;
+
+ /// Sfx item RES_CHRATR_ROTATE
+ virtual void CharRotate( const SvxCharRotateItem& ) override;
+
+ /// Sfx item RES_CHRATR_EMPHASIS_MARK
+ virtual void CharEmphasisMark( const SvxEmphasisMarkItem& rHt ) override;
+
+ /// Sfx item RES_CHRATR_TWO_LINES
+ virtual void CharTwoLines( const SvxTwoLinesItem& ) override;
+
+ /// Sfx item RES_CHRATR_SCALEW
+ virtual void CharScaleWidth( const SvxCharScaleWidthItem& ) override;
+
+ /// Sfx item RES_CHRATR_RELIEF
+ virtual void CharRelief( const SvxCharReliefItem& ) override;
+
+ /// Sfx item RES_CHRATR_HIDDEN
+ virtual void CharHidden( const SvxCharHiddenItem& ) override;
+
+ /// Sfx item RES_CHRATR_BOX
+ virtual void CharBorder( const ::editeng::SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) override;
+
+ /// Sfx item RES_CHRATR_HIGHLIGHT
+ virtual void CharHighlight( const SvxBrushItem& ) override;
+
+ /// Sfx item RES_TXTATR_INETFMT
+ virtual void TextINetFormat( const SwFormatINetFormat& ) override;
+
+ /// Sfx item RES_TXTATR_CHARFMT
+ virtual void TextCharFormat( const SwFormatCharFormat& ) override;
+
+ /// Sfx item RES_TXTATR_FTN
+ virtual void TextFootnote_Impl( const SwFormatFootnote& ) override;
+
+ /// Sfx item RES_PARATR_LINESPACING
+ virtual void ParaLineSpacing_Impl( short nSpace, short nMulti ) override;
+
+ /// Sfx item RES_PARATR_ADJUST
+ virtual void ParaAdjust( const SvxAdjustItem& rHt ) override;
+
+ /// Sfx item RES_PARATR_SPLIT
+ virtual void ParaSplit( const SvxFormatSplitItem& ) override;
+
+ /// Sfx item RES_PARATR_WIDOWS
+ virtual void ParaWidows( const SvxWidowsItem& rHt ) override;
+
+ /// Sfx item RES_PARATR_TABSTOP
+ virtual void ParaTabStop( const SvxTabStopItem& rHt ) override;
+
+ /// Sfx item RES_PARATR_HYPHENZONE
+ virtual void ParaHyphenZone( const SvxHyphenZoneItem& ) override;
+
+ /// Sfx item RES_PARATR_NUMRULE
+ virtual void ParaNumRule_Impl( const SwTextNode *pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) override;
+
+ /// Sfx item RES_PARATR_SCRIPTSPACE
+ virtual void ParaScriptSpace( const SfxBoolItem& ) override;
+
+ /// Sfx item RES_PARATR_HANGINGPUNCTUATION
+ virtual void ParaHangingPunctuation( const SfxBoolItem& ) override;
+
+ /// Sfx item RES_PARATR_FORBIDDEN_RULES
+ virtual void ParaForbiddenRules( const SfxBoolItem& ) override;
+
+ /// Sfx item RES_PARATR_VERTALIGN
+ virtual void ParaVerticalAlign( const SvxParaVertAlignItem& ) override;
+
+ /// Sfx item RES_PARATR_SNAPTOGRID
+ virtual void ParaSnapToGrid( const SvxParaGridItem& ) override;
+
+ /// Sfx item RES_FRM_SIZE
+ virtual void FormatFrameSize( const SwFormatFrameSize& ) override;
+
+ /// Sfx item RES_PAPER_BIN
+ virtual void FormatPaperBin( const SvxPaperBinItem& ) override;
+
+ /// Sfx item RES_MARGIN_FIRSTLINE
+ virtual void FormatFirstLineIndent(const SvxFirstLineIndentItem & rFirstLine) override;
+ /// Sfx item RES_MARGIN_TEXTLEFT
+ virtual void FormatTextLeftMargin(const SvxTextLeftMarginItem & rTextLeftMargin) override;
+ /// Sfx item RES_MARGIN_RIGHT
+ virtual void FormatRightMargin(const SvxRightMarginItem & rRightMargin) override;
+
+ /// Sfx item RES_LR_SPACE
+ virtual void FormatLRSpace( const SvxLRSpaceItem& ) override;
+
+ /// Sfx item RES_UL_SPACE
+ virtual void FormatULSpace( const SvxULSpaceItem& rHt ) override;
+
+ /// Sfx item RES_SURROUND
+ virtual void FormatSurround( const SwFormatSurround& ) override;
+
+ /// Sfx item RES_VERT_ORIENT
+ virtual void FormatVertOrientation( const SwFormatVertOrient& ) override;
+
+ /// Sfx item RES_HORI_ORIENT
+ virtual void FormatHorizOrientation( const SwFormatHoriOrient& ) override;
+
+ /// Sfx item RES_ANCHOR
+ virtual void FormatAnchor( const SwFormatAnchor& ) override;
+
+ /// Sfx item RES_BACKGROUND
+ virtual void FormatBackground( const SvxBrushItem& ) override;
+
+ /// Sfx item RES_FILL_STYLE
+ virtual void FormatFillStyle( const XFillStyleItem& ) override;
+
+ /// Sfx item RES_FILL_GRADIENT
+ virtual void FormatFillGradient( const XFillGradientItem& ) override;
+
+ /// Sfx item RES_BOX
+ virtual void FormatBox( const SvxBoxItem& ) override;
+
+ /// Sfx item RES_COL
+ virtual void FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) override;
+
+ /// Sfx item RES_KEEP
+ virtual void FormatKeep( const SvxFormatKeepItem& ) override;
+
+ /// Sfx item RES_TEXTGRID
+ virtual void FormatTextGrid( const SwTextGridItem& ) override;
+
+ /// Sfx item RES_LINENUMBER
+ virtual void FormatLineNumbering( const SwFormatLineNumber& ) override;
+
+ /// Sfx item RES_FRAMEDIR
+ virtual void FormatFrameDirection( const SvxFrameDirectionItem& ) override;
+
+ /// Sfx item RES_PARATR_GRABBAG
+ virtual void ParaGrabBag( const SfxGrabBagItem& ) override;
+
+ /// Sfx item RES_TXTATR_GRABBAG
+ virtual void CharGrabBag( const SfxGrabBagItem& ) override;
+
+ // Sfx item RES_PARATR_OUTLINELEVEL
+ virtual void ParaOutlineLevel( const SfxUInt16Item& ) override;
+
+ /// Write the expanded field
+ virtual void WriteExpand( const SwField* pField ) override;
+
+ virtual void RefField ( const SwField& rField, const OUString& rRef ) override;
+ virtual void HiddenField( const SwField& rField ) override;
+ virtual void SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) override;
+ virtual void PostitField( const SwField* pField ) override;
+ virtual bool DropdownField( const SwField* pField ) override;
+ virtual bool PlaceholderField( const SwField* pField ) override;
+
+ virtual bool AnalyzeURL( const OUString& rURL, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) override;
+
+ virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) override;
+
+ void SectionRtlGutter( const SfxBoolItem& rRtlGutter) override;
+
+ void TextLineBreak(const SwFormatLineBreak& rLineBreak) override;
+
+public:
+ explicit WW8AttributeOutput( WW8Export &rWW8Export )
+ : AttributeOutputBase(rWW8Export.GetWriter().GetMedia()->GetURLObject().GetMainURL(
+ INetURLObject::DecodeMechanism::NONE))
+ , m_rWW8Export(rWW8Export)
+ , m_nPOPosStdLen1(0)
+ , m_nPOPosStdLen2(0)
+ , m_nStyleStartSize(0)
+ , m_nStyleLenPos(0)
+ , m_nStyleCountPos(0)
+ , m_nFieldResults(0)
+ , mbOnTOXEnding(false)
+ {
+ }
+
+ /// Return the right export class.
+ virtual WW8Export& GetExport() override { return m_rWW8Export; }
+
+protected:
+ /// Output the bold etc. attributes
+ void OutputWW8Attribute( sal_uInt8 nId, bool bVal );
+
+ /// Output the bold etc. attributes, the Complex Text Layout version
+ void OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal );
+
+ void TableCellBorders(
+ ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
+
+private:
+
+ /// Reference to the export, where to get the data from
+ WW8Export &m_rWW8Export;
+
+ /// For output of styles.
+ ///
+ /// We have to remember these positions between the StartStyle() and
+ /// EndStyle().
+ sal_uInt16 m_nPOPosStdLen1, m_nPOPosStdLen2;
+
+ /// For output of styles.
+ ///
+ /// We have to remember this position between StartStyleProperties() and
+ /// EndStyleProperties().
+ sal_uInt16 m_nStyleStartSize, m_nStyleLenPos;
+
+ /// For output of styles.
+ ///
+ /// Used between StartStyles() and EndStyles().
+ sal_uLong m_nStyleCountPos;
+
+ /// For output of run properties.
+ ///
+ /// We have to remember the number of field results, and do not export end
+ /// of the field results if we were forced to split text.
+ sal_uInt16 m_nFieldResults;
+
+ bool mbOnTOXEnding;
+
+ /// Bookmarks of the current paragraph
+ std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphStart;
+ std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphEnd;
+
+ editeng::WordPageMargins m_pageMargins;
+ bool m_bFromEdge = false;
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8glsy.cxx b/sw/source/filter/ww8/ww8glsy.cxx
new file mode 100644
index 0000000000..838102e68c
--- /dev/null
+++ b/sw/source/filter/ww8/ww8glsy.cxx
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <tools/urlobj.hxx>
+#include <svl/urihelper.hxx>
+#include <osl/diagnose.h>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <poolfmt.hxx>
+#include <shellio.hxx>
+#include <docsh.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <frameformats.hxx>
+#include "ww8glsy.hxx"
+#include "ww8par.hxx"
+
+WW8Glossary::WW8Glossary(tools::SvRef<SotStorageStream> &refStrm, sal_uInt8 nVersion, SotStorage *pStg)
+ : m_rStrm(refStrm)
+ , m_xStg(pStg)
+ , m_nStrings(0)
+{
+ refStrm->SetEndian(SvStreamEndian::LITTLE);
+ WW8Fib aWwFib(*refStrm, nVersion);
+
+ if (aWwFib.m_nFibBack >= 0x6A) //Word97
+ {
+ m_xTableStream = pStg->OpenSotStream(
+ aWwFib.m_fWhichTableStm ? SL::a1Table : SL::a0Table,
+ StreamMode::STD_READ);
+
+ if (m_xTableStream.is() && ERRCODE_NONE == m_xTableStream->GetError())
+ {
+ m_xTableStream->SetEndian(SvStreamEndian::LITTLE);
+ m_xGlossary = std::make_shared<WW8GlossaryFib>(*refStrm, nVersion, aWwFib);
+ }
+ }
+}
+
+bool WW8Glossary::HasBareGraphicEnd(SwDoc *pDoc, SwNode const &rIdx)
+{
+ bool bRet=false;
+ for( sal_uInt16 nCnt = pDoc->GetSpzFrameFormats()->size(); nCnt; )
+ {
+ const SwFrameFormat* pFrameFormat = (*pDoc->GetSpzFrameFormats())[ --nCnt ];
+ if ( RES_FLYFRMFMT != pFrameFormat->Which() &&
+ RES_DRAWFRMFMT != pFrameFormat->Which() )
+ continue;
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
+ rIdx == *pAnchorNode )
+ {
+ bRet=true;
+ break;
+ }
+ }
+ return bRet;
+}
+
+bool WW8Glossary::MakeEntries(SwDoc *pD, SwTextBlocks &rBlocks,
+ bool bSaveRelFile, const std::vector<OUString>& rStrings,
+ const std::vector<ww::bytes>& rExtra)
+{
+ // this code will be called after reading all text into the
+ // empty sections
+ const OUString aOldURL( rBlocks.GetBaseURL() );
+ bool bRet=false;
+ if( bSaveRelFile )
+ {
+ rBlocks.SetBaseURL(
+ URIHelper::SmartRel2Abs(
+ INetURLObject(), rBlocks.GetFileName(),
+ URIHelper::GetMaybeFileHdl()));
+ }
+ else
+ rBlocks.SetBaseURL( OUString() );
+
+ SwNodeIndex aDocEnd( pD->GetNodes().GetEndOfContent() );
+ SwNodeIndex aStart( *aDocEnd.GetNode().StartOfSectionNode(), 1 );
+
+ // search the first NormalStartNode
+ while( !( aStart.GetNode().IsStartNode() && SwNormalStartNode ==
+ aStart.GetNode().GetStartNode()->GetStartNodeType()) &&
+ aStart < aDocEnd )
+ ++aStart;
+
+ if( aStart < aDocEnd )
+ {
+ SwTextFormatColl* pColl = pD->getIDocumentStylePoolAccess().GetTextCollFromPool
+ (RES_POOLCOLL_STANDARD, false);
+ sal_uInt16 nGlosEntry = 0;
+ SwContentNode* pCNd = nullptr;
+ do {
+ SwPaM aPam( aStart );
+ {
+ SwPosition& rPos = *aPam.GetPoint();
+ rPos.Adjust(SwNodeOffset(1));
+ pCNd = rPos.GetNode().GetTextNode();
+ if( nullptr == pCNd )
+ {
+ pCNd = pD->GetNodes().MakeTextNode( rPos.GetNode(), pColl );
+ rPos.Assign(*pCNd);
+ }
+ }
+ aPam.SetMark();
+ {
+ SwPosition& rPos = *aPam.GetPoint();
+ rPos.Assign(aStart.GetNode().EndOfSectionIndex() - 1);
+ if(( nullptr == ( pCNd = rPos.GetNode().GetContentNode() ) )
+ || HasBareGraphicEnd(pD,rPos.GetNode()))
+ {
+ rPos.Adjust(SwNodeOffset(1));
+ pCNd = pD->GetNodes().MakeTextNode( rPos.GetNode(), pColl );
+ rPos.Assign(*pCNd);
+ }
+ }
+ aPam.GetPoint()->SetContent( pCNd->Len() );
+
+ // now we have the right selection for one entry. Copy this to
+ // the defined TextBlock, but only if it is not an autocorrection
+ // entry (== -1) otherwise the group indicates the group in the
+ // sttbfglsystyle list that this entry belongs to. Unused at the
+ // moment
+ const ww::bytes &rData = rExtra[nGlosEntry];
+ sal_uInt16 n = SVBT16ToUInt16( &(rData[2]) );
+ if(n != 0xFFFF)
+ {
+ rBlocks.ClearDoc();
+ const OUString &rLNm = rStrings[nGlosEntry];
+
+ OUString sShortcut = rLNm;
+
+ // Need to check make sure the shortcut is not already being used
+ sal_Int32 nStart = 0;
+ sal_uInt16 nCurPos = rBlocks.GetIndex( sShortcut );
+ while( sal_uInt16(-1) != nCurPos )
+ {
+ sShortcut = rLNm + OUString::number(++nStart); // add a Number to it
+ nCurPos = rBlocks.GetIndex( sShortcut );
+ }
+
+ if( rBlocks.BeginPutDoc( sShortcut, sShortcut )) // Make the shortcut and the name the same
+
+ {
+ SwDoc* pGlDoc = rBlocks.GetDoc();
+ SwNodeIndex aIdx( pGlDoc->GetNodes().GetEndOfContent(),
+ -1 );
+ pCNd = aIdx.GetNode().GetContentNode();
+ SwPosition aPos(aIdx, pCNd, pCNd ? pCNd->Len() : 0);
+ pD->getIDocumentContentOperations().CopyRange(aPam, aPos, SwCopyFlags::CheckPosInFly);
+ rBlocks.PutDoc();
+ }
+ }
+ aStart = aStart.GetNode().EndOfSectionIndex() + 1;
+ ++nGlosEntry;
+ } while( aStart.GetNode().IsStartNode() &&
+ SwNormalStartNode == aStart.GetNode().
+ GetStartNode()->GetStartNodeType());
+ bRet=true;
+ }
+
+// this code will be called after reading all text into the empty sections
+
+ rBlocks.SetBaseURL( aOldURL );
+ return bRet;
+}
+
+bool WW8Glossary::Load( SwTextBlocks &rBlocks, bool bSaveRelFile )
+{
+ bool bRet=false;
+ if (m_xGlossary && m_xGlossary->IsGlossaryFib() && rBlocks.StartPutMuchBlockEntries())
+ {
+ //read the names of the autotext entries
+ std::vector<OUString> aStrings;
+ std::vector<ww::bytes> aData;
+
+ rtl_TextEncoding eStructCharSet =
+ WW8Fib::GetFIBCharset(m_xGlossary->m_chseTables, m_xGlossary->m_lid);
+
+ WW8ReadSTTBF(true, *m_xTableStream, m_xGlossary->m_fcSttbfglsy,
+ m_xGlossary->m_lcbSttbfglsy, 0, eStructCharSet, aStrings, &aData );
+
+ m_rStrm->Seek(0);
+
+ m_nStrings = static_cast< sal_uInt16 >(aStrings.size());
+ if ( 0 != m_nStrings )
+ {
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ if (xDocSh->DoInitNew())
+ {
+ SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc();
+
+ SwNodeIndex aIdx(
+ *pD->GetNodes().GetEndOfContent().StartOfSectionNode(), 1);
+ if( !aIdx.GetNode().IsTextNode() )
+ {
+ OSL_ENSURE( false, "Where is the TextNode?" );
+ pD->GetNodes().GoNext( &aIdx );
+ }
+ SwPaM aPamo( aIdx );
+ std::unique_ptr<SwWW8ImplReader> xRdr(new SwWW8ImplReader(
+ m_xGlossary->m_nVersion, m_xStg.get(), m_rStrm.get(), *pD, rBlocks.GetBaseURL(),
+ true, false, *aPamo.GetPoint()));
+ xRdr->LoadDoc(this);
+ bRet = MakeEntries(pD, rBlocks, bSaveRelFile, aStrings, aData);
+ }
+ xDocSh->DoClose();
+ rBlocks.EndPutMuchBlockEntries();
+ }
+ }
+ return bRet;
+}
+
+sal_uInt32 WW8GlossaryFib::FindGlossaryFibOffset(const WW8Fib &rFib)
+{
+ sal_uInt32 nGlossaryFibOffset = 0;
+ if ( rFib.m_fDot ) // it's a template
+ {
+ if ( rFib.m_pnNext )
+ nGlossaryFibOffset = ( rFib.m_pnNext * 512 );
+ }
+ return nGlossaryFibOffset;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8glsy.hxx b/sw/source/filter/ww8/ww8glsy.hxx
new file mode 100644
index 0000000000..35babac1d0
--- /dev/null
+++ b/sw/source/filter/ww8/ww8glsy.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8GLSY_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8GLSY_HXX
+
+#include <memory>
+#include <sot/storage.hxx>
+
+#include <doc.hxx>
+#include "ww8scan.hxx"
+
+class SwTextBlocks;
+class SwNodeIndex;
+
+/*
+ * GlossaryFib takes the document fib and finds the glossary fib which may
+ * not exist. The glossary fib has the offsets into the autotext subdocument
+ * which is at the end of template .dot's
+ */
+class WW8GlossaryFib : public WW8Fib
+{
+public:
+ WW8GlossaryFib( SvStream& rStrm, sal_uInt8 nWantedVersion, const WW8Fib &rFib)
+ : WW8Fib(rStrm, nWantedVersion, FindGlossaryFibOffset(rFib)) {}
+ // fGlsy will indicate whether this has AutoText or not
+ bool IsGlossaryFib() const {
+ return m_fGlsy;
+ }
+private:
+ static sal_uInt32 FindGlossaryFibOffset(const WW8Fib &rFib);
+};
+
+/*
+ * Imports glossaries from word, given the document it gets the usual word
+ * doc information, then the glossary fib and uses the usual reader class to
+ * wrap the autotext into a star doc. Afterwards taking each section entry and
+ * making it a single star autotext entry.
+ *
+ * ToDo currently all autotext entries become resource hungry star autotext
+ * formatted text, need to use a flag in the ww8reader class to determine if
+ * an entry is formatted or not.
+ */
+class WW8Glossary
+{
+public:
+ WW8Glossary( tools::SvRef<SotStorageStream> &refStrm, sal_uInt8 nVersion, SotStorage *pStg);
+ bool Load( SwTextBlocks &rBlocks, bool bSaveRelFile );
+ std::shared_ptr<WW8GlossaryFib>& GetFib()
+ {
+ return m_xGlossary;
+ }
+ sal_uInt16 GetNoStrings() const
+ {
+ return m_nStrings;
+ }
+
+private:
+ std::shared_ptr<WW8GlossaryFib> m_xGlossary;
+ tools::SvRef<SotStorageStream> m_xTableStream;
+ tools::SvRef<SotStorageStream> &m_rStrm;
+ tools::SvRef<SotStorage> m_xStg;
+ sal_uInt16 m_nStrings;
+
+ static bool MakeEntries(SwDoc *pD, SwTextBlocks &rBlocks, bool bSaveRelFile,
+ const std::vector<OUString>& rStrings,
+ const std::vector<ww::bytes>& rExtra);
+ static bool HasBareGraphicEnd(SwDoc *pD, SwNode const &rIdx);
+
+ WW8Glossary(const WW8Glossary&) = delete;
+ WW8Glossary& operator=(const WW8Glossary&) = delete;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8graf.cxx b/sw/source/filter/ww8/ww8graf.cxx
new file mode 100644
index 0000000000..8a9dd11971
--- /dev/null
+++ b/sw/source/filter/ww8/ww8graf.cxx
@@ -0,0 +1,3244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <svl/urihelper.hxx>
+#include <hintids.hxx>
+#include <osl/endian.h>
+#include <sal/log.hxx>
+#include <editeng/lrspitem.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/sxctitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <editeng/editeng.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdocirc.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <svx/xfltrit.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include <grfatr.hxx>
+#include <fmtornt.hxx>
+#include <fmtcntnt.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <ndgrf.hxx>
+#include <dcontact.hxx>
+#include <docsh.hxx>
+#include <mdiexp.hxx>
+#include "ww8struc.hxx"
+#include "ww8scan.hxx"
+#include "ww8par.hxx"
+#include "ww8par2.hxx"
+#include "ww8graf.hxx"
+#include <fmtinfmt.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <fmtfollowtextflow.hxx>
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <editeng/editobj.hxx>
+#include <math.h>
+#include <fmturl.hxx>
+#include <o3tl/enumrange.hxx>
+#include <o3tl/safeint.hxx>
+#include <memory>
+#include <optional>
+#include <filter/msfilter/escherex.hxx>
+#include <utility>
+#include "sprmids.hxx"
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+using namespace sw::types;
+using namespace sw::util;
+
+// helper methods
+static Color WW8TransCol(SVBT32 nWC)
+{
+#if 1 // 1 = use predefined color, 0 = ignore
+
+ // color table to convert RGB values to pre-defined colors
+ // (to make the writer UI show the right color names)
+ // the table is split in base 3, the greys are missing as
+ // they don't fit into that system (4 values: bw, wb, 2 * grey)
+ static const Color eColA[] = { // B G R B G R B G R
+ COL_BLACK, COL_RED, COL_LIGHTRED, // 0 0 0, 0 0 1, 0 0 2
+ COL_GREEN, COL_BROWN, COL_BLACK, // 0 1 0, 0 1 1, 0 1 2
+ COL_LIGHTGREEN, COL_BLACK, COL_YELLOW, // 0 2 0, 0 2 1, 0 2 2
+ COL_BLUE, COL_MAGENTA, COL_BLACK, // 1 0 0, 1 0 1, 1 0 2
+ COL_CYAN, COL_LIGHTGRAY, COL_BLACK, // 1 1 0, 1 1 1, 1 1 2
+ COL_BLACK, COL_BLACK, COL_BLACK, // 1 2 0, 1 2 1, 1 2 2
+ COL_LIGHTBLUE, COL_BLACK, COL_LIGHTMAGENTA, // 2 0 0, 2 0 1, 2 0 2
+ COL_BLACK, COL_BLACK, COL_BLACK, // 2 1 0, 2 1 1, 2 1 2
+ COL_LIGHTCYAN, COL_BLACK, COL_WHITE }; // 2 2 0, 2 2 1, 2 2 2
+
+ // In nWC[3] is a byte that's not described in the WW documentation.
+ // Its meaning appears to be the following: For 0, it's a normal color
+ // whose RGB values are in nWC[0..2]. If nWC[3] is 0x1, 0x7d or 0x83,
+ // it's a grey value whose black portion is given in 0.5% in nWC[0].
+ // I guess that BIT(0) in nWC[3] is relevant for distinguishing RGB/Grey.
+
+ if( !( nWC[3] & 0x1 ) && // not special (grey)
+ ( ( nWC[0] == 0 || nWC[0]== 0x80 || nWC[0] == 0xff ) // R
+ && ( nWC[1] == 0 || nWC[1]== 0x80 || nWC[1] == 0xff ) // G
+ && ( nWC[2] == 0 || nWC[2]== 0x80 || nWC[2] == 0xff ) ) ){// B
+ int nIdx = 0; // and now: Idx-calculation in base 3
+ for (int i = 2; i >= 0; i--)
+ {
+ nIdx *= 3;
+ if (nWC[i])
+ nIdx += ((nWC[i] == 0xff) ? 2 : 1);
+ }
+ if (eColA[nIdx] != COL_BLACK)
+ return eColA[nIdx]; // default color
+ }
+#endif
+
+ if (nWC[3] & 0x1)
+ {
+ // Special color gray
+ sal_uInt8 u = static_cast<sal_uInt8>( static_cast<sal_uLong>( 200 - nWC[0] ) * 256 / 200 );
+ return Color(u, u, u);
+ }
+
+ // User-Color
+ return Color(nWC[0], nWC[1], nWC[2]);
+}
+
+void wwFrameNamer::SetUniqueGraphName(SwFrameFormat *pFrameFormat, std::u16string_view rFixed)
+{
+ if (mbIsDisabled || rFixed.empty())
+ return;
+
+ pFrameFormat->SetFormatName(msSeed+OUString::number(++mnImportedGraphicsCount) + ": " + rFixed);
+}
+
+// ReadGrafStart reads object data and if necessary creates an anchor
+bool SwWW8ImplReader::ReadGrafStart(void* pData, short nDataSiz,
+ WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ if (SVBT16ToUInt16(pHd->cb) < sizeof(WW8_DPHEAD) + nDataSiz)
+ {
+ OSL_ENSURE( false, "+graphic element: too short?" );
+ m_pStrm->SeekRel(SVBT16ToUInt16(pHd->cb) - sizeof(WW8_DPHEAD));
+ return false;
+ }
+
+ bool bCouldRead = checkRead(*m_pStrm, pData, nDataSiz);
+ OSL_ENSURE(bCouldRead, "Short Graphic header");
+ if (!bCouldRead)
+ return false;
+
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
+ aAnchor.SetAnchor( m_pPaM->GetPoint() );
+ rSet.Put( aAnchor );
+
+ m_nDrawXOfs2 = m_nDrawXOfs;
+ m_nDrawYOfs2 = m_nDrawYOfs;
+
+ return true;
+}
+
+// SetStdAttr() sets standard attributes
+static void SetStdAttr( SfxItemSet& rSet, WW8_DP_LINETYPE& rL,
+ WW8_DP_SHADOW const & rSh )
+{
+ if( SVBT16ToUInt16( rL.lnps ) == 5 ){ // invisible
+ rSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ }else{ // visible
+ Color aCol( WW8TransCol( rL.lnpc ) ); // line color
+ rSet.Put( XLineColorItem( OUString(), aCol ) );
+ rSet.Put( XLineWidthItem( SVBT16ToUInt16( rL.lnpw ) ) );
+ // line thickness
+ if( SVBT16ToUInt16( rL.lnps ) >= 1
+ && SVBT16ToUInt16(rL.lnps ) <= 4 ){ // line style
+ rSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) );
+ sal_Int16 nLen = SVBT16ToUInt16( rL.lnpw );
+ XDash aD( css::drawing::DashStyle_RECT, 1, 2 * nLen, 1, 5 * nLen, 5 * nLen );
+ switch( SVBT16ToUInt16( rL.lnps ) ){
+ case 1: aD.SetDots( 0 ); // Dash
+ aD.SetDashLen( 6 * nLen );
+ aD.SetDistance( 4 * nLen );
+ break;
+ case 2: aD.SetDashes( 0 ); break; // Dot
+ case 3: break; // Dash Dot
+ case 4: aD.SetDots( 2 ); break; // Dash Dot Dot
+ }
+ rSet.Put( XLineDashItem( OUString(), aD ) );
+ }else{
+ rSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); // needed for TextBox
+ }
+ }
+ if( SVBT16ToUInt16( rSh.shdwpi ) ){ // shadow
+ rSet.Put(makeSdrShadowItem(true));
+ rSet.Put( makeSdrShadowXDistItem( SVBT16ToUInt16( rSh.xaOffset ) ) );
+ rSet.Put( makeSdrShadowYDistItem( SVBT16ToUInt16( rSh.yaOffset ) ) );
+ }
+}
+
+// SetFill() sets fill attributes such as fore- and background color and
+// pattern by reducing to a color
+// SetFill() doesn't yet set a pattern, because Sdr can't easily do that
+// and the Sdr hatching (XDash) isn't finished yet.
+// Instead, a mixed color will be picked that's between the selected ones.
+static void SetFill( SfxItemSet& rSet, WW8_DP_FILL& rFill )
+{
+ static const sal_uInt8 nPatA[] =
+ {
+ 0, 0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80,
+ 90, 50, 50, 50, 50, 50, 50, 33, 33, 33, 33, 33, 33
+ };
+ sal_uInt16 nPat = SVBT16ToUInt16(rFill.flpp);
+
+ if (nPat == 0) // transparent
+ rSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ else
+ {
+ rSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); // necessary for textbox
+ if (nPat <= 1 || (SAL_N_ELEMENTS(nPatA) <= nPat))
+ {
+ // Solid background or unknown
+ rSet.Put(XFillColorItem(OUString(), WW8TransCol(rFill.dlpcBg)));
+ }
+ else
+ { // Brush -> color mix
+ Color aB( WW8TransCol( rFill.dlpcBg ) );
+ Color aF( WW8TransCol( rFill.dlpcFg ) );
+ aB.SetRed( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetRed()) * nPatA[nPat]
+ + static_cast<sal_uLong>(aB.GetRed()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
+ aB.SetGreen( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetGreen()) * nPatA[nPat]
+ + static_cast<sal_uLong>(aB.GetGreen()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
+ aB.SetBlue( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetBlue()) * nPatA[nPat]
+ + static_cast<sal_uLong>(aB.GetBlue()) * ( 100 - nPatA[nPat] ) ) / 100 ) );
+ rSet.Put( XFillColorItem( OUString(), aB ) );
+ }
+ }
+}
+
+static void SetLineEndAttr( SfxItemSet& rSet, WW8_DP_LINEEND const & rLe,
+ WW8_DP_LINETYPE const & rLt )
+{
+ sal_uInt16 aSB = SVBT16ToUInt16( rLe.aStartBits );
+ if( aSB & 0x3 )
+ {
+ ::basegfx::B2DPolygon aPolygon;
+ aPolygon.append(::basegfx::B2DPoint(0.0, 330.0));
+ aPolygon.append(::basegfx::B2DPoint(100.0, 0.0));
+ aPolygon.append(::basegfx::B2DPoint(200.0, 330.0));
+ aPolygon.setClosed(true);
+ rSet.Put( XLineEndItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) );
+ sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw )
+ * ( ( aSB >> 2 & 0x3 ) + ( aSB >> 4 & 0x3 ) );
+ if( nSiz < 220 ) nSiz = 220;
+ rSet.Put(XLineEndWidthItem(nSiz));
+ rSet.Put(XLineEndCenterItem(false));
+ }
+
+ sal_uInt16 aEB = SVBT16ToUInt16( rLe.aEndBits );
+ if( !(aEB & 0x3) ) return;
+
+ ::basegfx::B2DPolygon aPolygon;
+ aPolygon.append(::basegfx::B2DPoint(0.0, 330.0));
+ aPolygon.append(::basegfx::B2DPoint(100.0, 0.0));
+ aPolygon.append(::basegfx::B2DPoint(200.0, 330.0));
+ aPolygon.setClosed(true);
+ rSet.Put( XLineStartItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) );
+ sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw )
+ * ( ( aEB >> 2 & 0x3 ) + ( aEB >> 4 & 0x3 ) );
+ if( nSiz < 220 ) nSiz = 220;
+ rSet.Put(XLineStartWidthItem(nSiz));
+ rSet.Put(XLineStartCenterItem(false));
+}
+
+// start of routines for the different objects
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ WW8_DP_LINE aLine;
+
+ if( !ReadGrafStart( static_cast<void*>(&aLine), sizeof( aLine ), pHd, rSet ) )
+ return nullptr;
+
+ Point aP[2];
+ {
+ Point& rP0 = aP[0];
+ Point& rP1 = aP[1];
+
+ rP0.setX( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2 );
+ rP0.setY( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
+ rP1 = rP0;
+ rP0.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.xaStart )) );
+ rP0.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.yaStart )) );
+ rP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.xaEnd )) );
+ rP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.yaEnd )) );
+ }
+
+ ::basegfx::B2DPolygon aPolygon;
+ aPolygon.append(::basegfx::B2DPoint(aP[0].X(), aP[0].Y()));
+ aPolygon.append(::basegfx::B2DPoint(aP[1].X(), aP[1].Y()));
+ rtl::Reference<SdrObject> pObj = new SdrPathObj(
+ *m_pDrawModel,
+ SdrObjKind::Line,
+ ::basegfx::B2DPolyPolygon(aPolygon));
+
+ SetStdAttr( rSet, aLine.aLnt, aLine.aShd );
+ SetLineEndAttr( rSet, aLine.aEpp, aLine.aLnt );
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadRect(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ WW8_DP_RECT aRect;
+
+ if( !ReadGrafStart( static_cast<void*>(&aRect), sizeof( aRect ), pHd, rSet ) )
+ return nullptr;
+
+ Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
+ Point aP1( aP0 );
+ aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
+ aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
+
+ rtl::Reference<SdrObject> pObj = new SdrRectObj(
+ *m_pDrawModel,
+ tools::Rectangle(aP0, aP1));
+
+ SetStdAttr( rSet, aRect.aLnt, aRect.aShd );
+ SetFill( rSet, aRect.aFill );
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadEllipse(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ WW8_DP_ELLIPSE aEllipse;
+
+ if( !ReadGrafStart( static_cast<void*>(&aEllipse), sizeof( aEllipse ), pHd, rSet ) )
+ return nullptr;
+
+ Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
+ Point aP1( aP0 );
+ aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
+ aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
+
+ rtl::Reference<SdrObject> pObj = new SdrCircObj(
+ *m_pDrawModel,
+ SdrCircKind::Full,
+ tools::Rectangle(aP0, aP1));
+
+ SetStdAttr( rSet, aEllipse.aLnt, aEllipse.aShd );
+ SetFill( rSet, aEllipse.aFill );
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadArc(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ WW8_DP_ARC aArc;
+
+ if( !ReadGrafStart( static_cast<void*>(&aArc), sizeof( aArc ), pHd, rSet ) )
+ return nullptr;
+
+ Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
+ Point aP1( aP0 );
+ aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) * 2 );
+ aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) * 2 );
+
+ short nA[] = { 2, 3, 1, 0 };
+ short nW = nA[ ( ( aArc.fLeft & 1 ) << 1 ) + ( aArc.fUp & 1 ) ];
+ if( !aArc.fLeft ){
+ aP0.AdjustY( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
+ aP1.AdjustY( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
+ }
+ if( aArc.fUp ){
+ aP0.AdjustX( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
+ aP1.AdjustX( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
+ }
+
+ rtl::Reference<SdrObject> pObj = new SdrCircObj(
+ *m_pDrawModel,
+ SdrCircKind::Section,
+ tools::Rectangle(aP0, aP1),
+ Degree100(nW * 9000),
+ Degree100(( ( nW + 1 ) & 3 ) * 9000));
+
+ SetStdAttr( rSet, aArc.aLnt, aArc.aShd );
+ SetFill( rSet, aArc.aFill );
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadPolyLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ WW8_DP_POLYLINE aPoly;
+
+ if( !ReadGrafStart( static_cast<void*>(&aPoly), sizeof( aPoly ), pHd, rSet ) )
+ return nullptr;
+
+ sal_uInt16 nCount = SVBT16ToUInt16( aPoly.aBits1 ) >> 1 & 0x7fff;
+ std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]);
+
+ bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4); // read points
+ OSL_ENSURE(bCouldRead, "Short PolyLine header");
+ if (!bCouldRead)
+ return nullptr;
+
+ tools::Polygon aP( nCount );
+ Point aPt;
+ for (sal_uInt16 i=0; i<nCount; ++i)
+ {
+ aPt.setX( SVBT16ToUInt16( xP[i << 1] ) + m_nDrawXOfs2
+ + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) );
+ aPt.setY( SVBT16ToUInt16( xP[( i << 1 ) + 1] ) + m_nDrawYOfs2
+ + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) );
+ aP[i] = aPt;
+ }
+ xP.reset();
+
+ rtl::Reference<SdrObject> pObj = new SdrPathObj(
+ *m_pDrawModel,
+ (SVBT16ToUInt16(aPoly.aBits1) & 0x1) ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ ::basegfx::B2DPolyPolygon(aP.getB2DPolygon()));
+
+ SetStdAttr( rSet, aPoly.aLnt, aPoly.aShd );
+ SetFill( rSet, aPoly.aFill );
+
+ return pObj;
+}
+
+static ESelection GetESelection(EditEngine const &rDrawEditEngine, tools::Long nCpStart, tools::Long nCpEnd)
+{
+ sal_Int32 nPCnt = rDrawEditEngine.GetParagraphCount();
+ sal_Int32 nSP = 0;
+ sal_Int32 nEP = 0;
+ while( (nSP < nPCnt)
+ && (nCpStart >= rDrawEditEngine.GetTextLen( nSP ) + 1) )
+ {
+ nCpStart -= rDrawEditEngine.GetTextLen( nSP ) + 1;
+ nSP++;
+ }
+ // at the end, switch to the new line only 1 character later as
+ // otherwise line attributes reach one line too far
+ while( (nEP < nPCnt)
+ && (nCpEnd > rDrawEditEngine.GetTextLen( nEP ) + 1) )
+ {
+ nCpEnd -= rDrawEditEngine.GetTextLen( nEP ) + 1;
+ nEP++;
+ }
+ return ESelection( nSP, nCpStart, nEP, nCpEnd );
+}
+
+// InsertTxbxStyAttrs() sets style attributes into the passed ItemSet.
+// SW styles are used since import-WW-styles are already destroyed.
+// SW styles are examined in depth first search order (with parent styles)
+// for the attributes given in aSrcTab. They're cloned, and the clones'
+// Which-IDs are changed according to the aDstTab table so that the
+// EditEngine will not ignore them.
+// Both Paragraph and character attributes are stuffed into the ItemSet.
+void SwWW8ImplReader::InsertTxbxStyAttrs( SfxItemSet& rS, sal_uInt16 nColl )
+{
+ SwWW8StyInf * pStyInf = GetStyle(nColl);
+ if( !(pStyInf != nullptr && pStyInf->m_pFormat && pStyInf->m_bColl) )
+ return;
+
+ const SfxPoolItem* pItem;
+ for( sal_uInt16 i = POOLATTR_BEGIN; i < POOLATTR_END; i++ )
+ {
+ // If we are set in the source and not set in the destination
+ // then add it in.
+ if ( SfxItemState::SET == pStyInf->m_pFormat->GetItemState(
+ i, true, &pItem ) )
+ {
+ SfxItemPool *pEditPool = rS.GetPool();
+ sal_uInt16 nWhich = i;
+ sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich);
+ if (
+ nSlotId && nWhich != nSlotId &&
+ 0 != (nWhich = pEditPool->GetWhich(nSlotId)) &&
+ nWhich != nSlotId &&
+ ( SfxItemState::SET != rS.GetItemState(nWhich, false) )
+ )
+ {
+ rS.Put( pItem->CloneSetWhich(nWhich) );
+ }
+ }
+ }
+
+}
+
+static void lcl_StripFields(OUString &rString, WW8_CP &rNewStartCp)
+{
+ sal_Int32 nStartPos = 0;
+ for (;;)
+ {
+ nStartPos = rString.indexOf(0x13, nStartPos);
+ if (nStartPos<0)
+ return;
+
+ const sal_Unicode cStops[] = {0x14, 0x15, 0};
+ const sal_Int32 nStopPos = comphelper::string::indexOfAny(rString, cStops, nStartPos);
+ if (nStopPos<0)
+ {
+ rNewStartCp += rString.getLength()-nStartPos;
+ rString = rString.copy(0, nStartPos);
+ return;
+ }
+
+ const bool was0x14 = rString[nStopPos]==0x14;
+ rString = rString.replaceAt(nStartPos, nStopPos+1-nStartPos, u"");
+ rNewStartCp += nStopPos-nStartPos;
+
+ if (was0x14)
+ {
+ ++rNewStartCp;
+ nStartPos = rString.indexOf(0x15, nStartPos);
+ if (nStartPos<0)
+ return;
+ rString = rString.replaceAt(nStartPos, 1, u"");
+ }
+ }
+}
+
+namespace {
+
+class Chunk
+{
+private:
+ OUString msURL;
+ tools::Long mnStartPos; // 0x13
+ tools::Long mnEndPos; // 0x15
+public:
+ explicit Chunk(tools::Long nStart, OUString aURL)
+ : msURL(std::move(aURL)), mnStartPos(nStart), mnEndPos(0) {}
+
+ void SetEndPos(tools::Long nEnd) { mnEndPos = nEnd; }
+ tools::Long GetStartPos() const {return mnStartPos;}
+ tools::Long GetEndPos() const {return mnEndPos;}
+ const OUString &GetURL() const {return msURL;}
+ void Adjust(sal_Int32 nAdjust)
+ {
+ mnStartPos-=nAdjust;
+ mnEndPos-=nAdjust;
+ }
+};
+
+ bool IsValidSel(const EditEngine& rEngine, const ESelection& rSel)
+ {
+ const auto nParaCount = rEngine.GetParagraphCount();
+ if (rSel.nStartPara < nParaCount && rSel.nEndPara < nParaCount)
+ return rSel.nStartPos >= 0 && rSel.nEndPos >= 0;
+ return false;
+ }
+}
+
+// InsertAttrsAsDrawingAttrs() sets attributes between StartCp and EndCp.
+// Style attributes are set as hard, paragraph and character attributes.
+void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp,
+ ManTypes eType, bool bONLYnPicLocFc)
+{
+ /*
+ Save and create new plcxman for this drawing object, of the type that
+ will include the para end mark inside a paragraph property range, as
+ drawing boxes have real paragraph marks as part of their text, while
+ normal writer has separate nodes for each paragraph and so has no actual
+ paragraph mark as part of the paragraph text.
+ */
+ WW8ReaderSave aSave(this);
+ m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), eType, nStartCp, true);
+
+ WW8_CP nStart = m_xPlcxMan->Where();
+ WW8_CP nNext, nStartReplace=0;
+
+ bool bDoingSymbol = false;
+ sal_Unicode cReplaceSymbol = m_cSymbol;
+
+ std::optional<SfxItemSet> pS(m_pDrawEditEngine->GetEmptyItemSet());
+ WW8PLCFManResult aRes;
+
+ std::deque<Chunk> aChunks;
+
+ // Here store stack location
+ size_t nCurrentCount = m_xCtrlStck->size();
+ while (nStart < nEndCp)
+ {
+ // nStart is the beginning of the attributes for this range, and
+ // may be before the text itself. So watch out for that
+ WW8_CP nTextStart = nStart;
+ if (nTextStart < nStartCp)
+ nTextStart = nStartCp;
+
+ // get position of next SPRM
+ bool bStartAttr = m_xPlcxMan->Get(&aRes);
+ m_nCurrentColl = m_xPlcxMan->GetColl();
+ if (aRes.nSprmId)
+ {
+ if( bONLYnPicLocFc )
+ {
+ if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) )
+ {
+ Read_PicLoc(aRes.nSprmId, aRes.pMemPos +
+ m_oSprmParser->DistanceToData(aRes.nSprmId), 4);
+ // Ok, that's what we were looking for. Now let's get
+ // out of here!
+ break;
+ }
+ }
+ else if ((eFTN > aRes.nSprmId) || (0x0800 <= aRes.nSprmId))
+ {
+ // Here place them onto our usual stack and we will pop them
+ // off and convert them later
+ if (bStartAttr)
+ {
+ ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
+ if (!bDoingSymbol && m_bSymbol)
+ {
+ bDoingSymbol = true;
+ nStartReplace = nTextStart;
+ cReplaceSymbol = m_cSymbol;
+ }
+ }
+ else
+ {
+ EndSprm( aRes.nSprmId );
+ if (!m_bSymbol && bDoingSymbol)
+ {
+ bDoingSymbol = false;
+
+ ESelection aReplaceSel(GetESelection(*m_pDrawEditEngine, nStartReplace - nStartCp,
+ nTextStart - nStartCp));
+
+ sal_Int32 nParaCount = m_pDrawEditEngine->GetParagraphCount();
+ bool bBadSelection = aReplaceSel.nStartPara >= nParaCount || aReplaceSel.nEndPara >= nParaCount;
+
+ SAL_WARN_IF(bBadSelection, "sw.ww8", "editengine has different amount of text than expected");
+
+ if (!bBadSelection)
+ {
+ sal_Int32 nCount = nTextStart - nStartReplace;
+ OUStringBuffer sTemp(nCount);
+ comphelper::string::padToLength(sTemp, nCount, cReplaceSymbol);
+ m_pDrawEditEngine->QuickInsertText(sTemp.makeStringAndClear(), aReplaceSel);
+ }
+ }
+ }
+ }
+ else if (aRes.nSprmId == eFLD)
+ {
+ if (bStartAttr)
+ {
+ size_t nCount = m_xCtrlStck->size();
+ if (m_aFieldStack.empty() && Read_Field(&aRes))
+ {
+ OUString sURL;
+ for (size_t nI = m_xCtrlStck->size(); nI > nCount; --nI)
+ {
+ const SfxPoolItem *pItem = ((*m_xCtrlStck)[nI-1]).m_pAttr.get();
+ sal_uInt16 nWhich = pItem->Which();
+ if (nWhich == RES_TXTATR_INETFMT)
+ {
+ const SwFormatINetFormat *pURL =
+ static_cast<const SwFormatINetFormat *>(pItem);
+ sURL = pURL->GetValue();
+ }
+ m_xCtrlStck->DeleteAndDestroy(nI-1);
+ }
+ aChunks.emplace_back(nStart, sURL);
+ }
+ }
+ else
+ {
+ if (!m_aFieldStack.empty() && End_Field() && !aChunks.empty())
+ aChunks.back().SetEndPos(nStart+1);
+ }
+ }
+ }
+
+ m_xPlcxMan->advance();
+ nNext = m_xPlcxMan->Where();
+
+ const WW8_CP nEnd = ( nNext < nEndCp ) ? nNext : nEndCp;
+ if (!bONLYnPicLocFc && nNext != nStart && nEnd >= nStartCp)
+ {
+ SfxItemPool *pEditPool = pS->GetPool();
+
+ // Here read current properties and convert them into pS
+ // and put those attrs into the draw box if they can be converted
+ // to draw attributes
+ if (m_xCtrlStck->size() - nCurrentCount)
+ {
+ for (size_t i = nCurrentCount; i < m_xCtrlStck->size(); ++i)
+ {
+ const SfxPoolItem *pItem = ((*m_xCtrlStck)[i]).m_pAttr.get();
+ sal_uInt16 nWhich = pItem->Which();
+ if( nWhich < RES_FLTRATTR_BEGIN ||
+ nWhich >= RES_FLTRATTR_END )
+ {
+ sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich);
+ if (nWhich == RES_CHRATR_BACKGROUND || nWhich == RES_CHRATR_HIGHLIGHT)
+ {
+ Color aColor(static_cast<const SvxBrushItem*>(pItem)->GetColor());
+ pS->Put(SvxColorItem(aColor, EE_CHAR_BKGCOLOR));
+ }
+ else if (
+ nSlotId && nWhich != nSlotId &&
+ 0 != (nWhich = pEditPool->GetWhich(nSlotId)) &&
+ nWhich != nSlotId
+ )
+ {
+ pS->Put( pItem->CloneSetWhich(nWhich) );
+ }
+ }
+ }
+ }
+ // Fill in the remainder from the style
+ InsertTxbxStyAttrs(*pS, m_nCurrentColl);
+
+ if( pS->Count() )
+ {
+ m_pDrawEditEngine->QuickSetAttribs( *pS,
+ GetESelection(*m_pDrawEditEngine, nTextStart - nStartCp, nEnd - nStartCp ) );
+ pS.emplace(m_pDrawEditEngine->GetEmptyItemSet());
+ }
+ }
+ nStart = nNext;
+ }
+ pS.reset();
+
+ // pop off as far as recorded location just in case there were some left
+ // unclosed
+ for (size_t nI = m_xCtrlStck->size(); nI > nCurrentCount; --nI)
+ m_xCtrlStck->DeleteAndDestroy(nI-1);
+
+ auto aEnd = aChunks.end();
+ for (auto aIter = aChunks.begin(); aIter != aEnd; ++aIter)
+ {
+ ESelection aSel(GetESelection(*m_pDrawEditEngine, aIter->GetStartPos()-nStartCp,
+ aIter->GetEndPos()-nStartCp));
+ if (!IsValidSel(*m_pDrawEditEngine, aSel))
+ continue;
+ OUString aString(m_pDrawEditEngine->GetText(aSel));
+ const sal_Int32 nOrigLen = aString.getLength();
+ WW8_CP nDummy(0);
+ lcl_StripFields(aString, nDummy);
+
+ sal_Int32 nChanged;
+ if (!aIter->GetURL().isEmpty())
+ {
+ SvxURLField aURL(aIter->GetURL(), aString, SvxURLFormat::AppDefault);
+ m_pDrawEditEngine->QuickInsertField(SvxFieldItem(aURL, EE_FEATURE_FIELD), aSel);
+ nChanged = nOrigLen - 1;
+ }
+ else
+ {
+ m_pDrawEditEngine->QuickInsertText(aString, aSel);
+ nChanged = nOrigLen - aString.getLength();
+ }
+ for (auto aIter2 = aIter+1; aIter2 != aEnd; ++aIter2)
+ aIter2->Adjust(nChanged);
+ }
+
+ /*
+ Don't worry about the new pPlcxMan, the restore removes it when
+ replacing the current one with the old one.
+ */
+ aSave.Restore(this);
+}
+
+bool SwWW8ImplReader::GetTxbxTextSttEndCp(WW8_CP& rStartCp, WW8_CP& rEndCp,
+ sal_uInt16 nTxBxS, sal_uInt16 nSequence)
+{
+ // grab the TextBox-PLCF quickly
+ WW8PLCFspecial* pT = m_xPlcxMan ? m_xPlcxMan->GetTxbx() : nullptr;
+ if( !pT )
+ {
+ OSL_ENSURE( false, "+where's the text graphic (1)?" );
+ return false;
+ }
+
+ // if applicable first find the right TextBox-Story
+ bool bCheckTextBoxStory = ( nTxBxS && pT->GetIMax() >= nTxBxS );
+ if( bCheckTextBoxStory )
+ pT->SetIdx( nTxBxS-1 );
+
+ // then determine start and end
+ void* pT0;
+ if (!pT->Get(rStartCp, pT0) || rStartCp < 0)
+ {
+ OSL_ENSURE( false, "+where's the text graphic (2)?" );
+ return false;
+ }
+
+ if( bCheckTextBoxStory )
+ {
+ bool bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable ));
+ while( bReusable )
+ {
+ pT->advance();
+ if( !pT->Get( rStartCp, pT0 ) )
+ {
+ OSL_ENSURE( false, "+where's the text graphic (2a)?" );
+ return false;
+ }
+ bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable ));
+ }
+ }
+ pT->advance();
+ if (!pT->Get(rEndCp, pT0) || rEndCp < 0)
+ {
+ OSL_ENSURE( false, "+where's the text graphic (3)?" );
+ return false;
+ }
+
+ // find the right page in the break table (if necessary)
+ if( bCheckTextBoxStory )
+ {
+ // special case: entire chain should be determined - done!
+ if( USHRT_MAX > nSequence )
+ {
+ tools::Long nMinStartCp = rStartCp;
+ tools::Long nMaxEndCp = rEndCp;
+ // quickly grab the TextBox-Break-Descriptor-PLCF
+ pT = m_xPlcxMan->GetTxbxBkd();
+ if (!pT) // It can occur on occasion, Caolan
+ return false;
+
+ // find first entry for this TextBox story
+ if( !pT->SeekPos( rStartCp ) )
+ {
+ OSL_ENSURE( false, "+where's the text graphic (4)" );
+ return false;
+ }
+ // if needed skip the appropriate number of entries
+ for (sal_uInt16 iSequence = 0; iSequence < nSequence; ++iSequence)
+ pT->advance();
+ // and determine actual start and end
+ if( (!pT->Get( rStartCp, pT0 ))
+ || ( nMinStartCp > rStartCp ) )
+ {
+ OSL_ENSURE( false, "+where's the text graphic (5)?" );
+ return false;
+ }
+ if( rStartCp >= nMaxEndCp )
+ rEndCp = rStartCp; // not an error: empty string
+ else
+ {
+ pT->advance();
+ if ( (!pT->Get(rEndCp, pT0)) || (nMaxEndCp < rEndCp-1) )
+ {
+ OSL_ENSURE( false, "+where's the text graphic (6)?" );
+ return false;
+ }
+ rEndCp -= 1;
+ }
+ }
+ else
+ rEndCp -= 1;
+ }
+ else
+ rEndCp -= 1;
+ return true;
+}
+
+// TxbxText() grabs the text from the WW file and returns that along with
+// the StartCp and the corrected (by -2, or -1 for version 8) EndCp.
+sal_Int32 SwWW8ImplReader::GetRangeAsDrawingString(OUString& rString, tools::Long nStartCp, tools::Long nEndCp, ManTypes eType)
+{
+ WW8_CP nOffset = 0;
+ m_xWwFib->GetBaseCp(eType, &nOffset); //TODO: check return value
+
+ OSL_ENSURE(nStartCp <= nEndCp, "+where's the graphic text (7)?");
+ if (nStartCp == nEndCp)
+ rString.clear(); // empty string: entirely possible
+ else if (nStartCp < nEndCp)
+ {
+ // read the text: can be split into multiple pieces
+ const sal_Int32 nLen = m_xSBase->WW8ReadString(*m_pStrm, rString,
+ nStartCp + nOffset, nEndCp - nStartCp, GetCurrentCharSet());
+ OSL_ENSURE(nLen, "+where's the text graphic (8)?");
+ if (nLen>0)
+ {
+ if( rString[nLen-1]==0x0d )
+ rString = rString.copy(0, nLen-1);
+
+ rString = rString.replace( 0xb, 0xa );
+ return nLen;
+ }
+ }
+ return 0;
+}
+
+//EditEngine::InsertText will replace dos lines resulting in a shorter
+//string than is passed in, so inserting attributes based on the original
+//string len can fail. So here replace the dos line ends similar to
+//how EditEngine does it, but preserve the length and replace the extra
+//chars with placeholders, record the position of the placeholders and
+//remove those extra chars after attributes have been inserted
+static std::vector<sal_Int32> replaceDosLineEndsButPreserveLength(OUString &rIn)
+{
+ OUStringBuffer aNewData(rIn);
+ std::vector<sal_Int32> aDosLineEndDummies;
+ sal_Int32 i = 0;
+ sal_Int32 nStrLen = rIn.getLength();
+ while (i < nStrLen)
+ {
+ // \r or \n causes linebreak
+ if (rIn[i] == '\r' || rIn[i] == '\n')
+ {
+ // skip char if \r\n or \n\r
+ if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) &&
+ (rIn[i] != rIn[i+1]) )
+ {
+ ++i;
+ aDosLineEndDummies.push_back(i);
+ aNewData[i] = 0;
+ }
+ }
+ ++i;
+ }
+ rIn = aNewData.makeStringAndClear();
+ return aDosLineEndDummies;
+}
+
+static void removePositions(EditEngine &rDrawEditEngine, const std::vector<sal_Int32>& rDosLineEndDummies)
+{
+ for (auto aIter = rDosLineEndDummies.rbegin(); aIter != rDosLineEndDummies.rend(); ++aIter)
+ {
+ sal_Int32 nCharPos(*aIter);
+ rDrawEditEngine.QuickDelete(GetESelection(rDrawEditEngine, nCharPos, nCharPos+1));
+ }
+}
+
+std::optional<OutlinerParaObject> SwWW8ImplReader::ImportAsOutliner(OUString &rString, WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType)
+{
+ std::optional<OutlinerParaObject> pRet;
+
+ sal_Int32 nLen = GetRangeAsDrawingString(rString, nStartCp, nEndCp, eType);
+ if (nLen > 0)
+ {
+ if (m_bFuzzing && rString.getLength() > 1024)
+ {
+ SAL_WARN("sw.ww8", "Truncating long EditEngine strings when fuzzing for performance");
+ rString = rString.copy(0, 1024);
+ }
+
+ if (!m_pDrawEditEngine)
+ {
+ m_pDrawEditEngine.reset(new EditEngine(nullptr));
+ }
+
+ //replace dos line endings with editeng ones, replace any extra chars with
+ //placeholders to keep the inserted string len in sync with the attribute cps
+ //and record in aDosLineEnds the superfluous positions
+ OUString sEEString(rString);
+ std::vector<sal_Int32> aDosLineEnds(replaceDosLineEndsButPreserveLength(sEEString));
+ m_pDrawEditEngine->SetText(sEEString);
+ InsertAttrsAsDrawingAttrs(nStartCp, nStartCp+nLen, eType);
+ //remove any superfluous placeholders of replaceDosLineEndsButPreserveLength
+ //after attributes have been inserted
+ removePositions(*m_pDrawEditEngine, aDosLineEnds);
+
+ // Annotations typically begin with a (useless) 0x5
+ if ((eType == MAN_AND) && m_pDrawEditEngine->GetTextLen())
+ {
+ ESelection aFirstChar(0, 0, 0, 1);
+ if (m_pDrawEditEngine->GetText( aFirstChar ) == "\x05")
+ m_pDrawEditEngine->QuickDelete(aFirstChar);
+ }
+
+ std::unique_ptr<EditTextObject> pTemporaryText = m_pDrawEditEngine->CreateTextObject();
+ pRet.emplace( std::move(pTemporaryText) );
+ pRet->SetOutlinerMode( OutlinerMode::TextObject );
+
+ m_pDrawEditEngine->SetText( OUString() );
+ m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet());
+
+ // Strip out fields, leaving the result
+ WW8_CP nDummy(0);
+ lcl_StripFields(rString, nDummy);
+ // Strip out word's special characters for the simple string
+ rString = rString.replaceAll("\x01", "");
+ rString = rString.replaceAll("\x05", "");
+ rString = rString.replaceAll("\x08", "");
+ rString = rString.replaceAll("\007\007", "\007\012");
+ rString = rString.replace(0x7, ' ');
+ }
+
+ return pRet;
+}
+
+// InsertTxbxText() adds the Text and the Attributes for TextBoxes and CaptionBoxes
+void SwWW8ImplReader::InsertTxbxText(SdrTextObj* pTextObj,
+ Size const * pObjSiz, sal_uInt16 nTxBxS, sal_uInt16 nSequence, tools::Long nPosCp,
+ SwFrameFormat const * pOldFlyFormat, bool bMakeSdrGrafObj, bool& rbEraseTextObj,
+ bool* pbTestTxbxContainsText, tools::Long* pnStartCp, tools::Long* pnEndCp,
+ bool* pbContainsGraphics, SvxMSDffImportRec const * pRecord)
+{
+ SwFrameFormat* pFlyFormat = nullptr;
+ sal_uInt64 nOld = m_pStrm->Tell();
+
+ ManTypes eType = m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX;
+
+ rbEraseTextObj = false;
+
+ OUString aString;
+ WW8_CP nStartCp, nEndCp;
+ bool bContainsGraphics = false;
+ bool bTextWasRead = GetTxbxTextSttEndCp(nStartCp, nEndCp, nTxBxS, nSequence) &&
+ GetRangeAsDrawingString(aString, nStartCp, nEndCp, eType) > 0;
+
+ if (!m_pDrawEditEngine)
+ {
+ m_pDrawEditEngine.reset(new EditEngine(nullptr));
+ }
+ if( pObjSiz )
+ m_pDrawEditEngine->SetPaperSize( *pObjSiz );
+
+ if (m_bFuzzing && aString.getLength() > 1024)
+ {
+ SAL_WARN("sw.ww8", "Truncating long EditEngine strings when fuzzing for performance");
+ aString = aString.copy(0, 1024);
+ }
+
+ const OUString aOrigString(aString);
+ if( bTextWasRead )
+ {
+ WW8_CP nNewStartCp = nStartCp;
+ lcl_StripFields(aString, nNewStartCp);
+
+ if (aString.getLength()!=1)
+ {
+ bContainsGraphics = aString.indexOf(0x1)<0 || aString.indexOf(0x8)<0;
+ }
+ else // May be a single graphic or object
+ {
+ bool bDone = true;
+ switch( aString[0] )
+ {
+ case 0x1:
+ if (!pbTestTxbxContainsText)
+ {
+ WW8ReaderSave aSave(this, nNewStartCp -1);
+ bool bOldEmbeddObj = m_bEmbeddObj;
+ // bEmbeddObj Ordinarily would have been set by field
+ // parse, but this is impossible here so...
+ m_bEmbeddObj = true;
+
+ // 1st look for OLE- or Graph-Indicator Sprms
+ WW8PLCFx_Cp_FKP* pChp = m_xPlcxMan->GetChpPLCF();
+ WW8PLCFxDesc aDesc;
+ pChp->GetSprms( &aDesc );
+ WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, *m_oSprmParser);
+
+ for( int nLoop = 0; nLoop < 2; ++nLoop )
+ {
+ while (aSprmIter.GetSprms())
+ {
+ const sal_uInt8 *const pParams(aSprmIter.GetCurrentParams());
+ if (nullptr == pParams)
+ break;
+ sal_uInt16 nCurrentId = aSprmIter.GetCurrentId();
+ switch( nCurrentId )
+ {
+ case 75:
+ case 118:
+ case 0x080A:
+ case 0x0856:
+ Read_Obj(nCurrentId, pParams, 1);
+ break;
+ case 68: // Read_Pic()
+ case 0x6A03:
+ case NS_sprm::LN_CObjLocation:
+ Read_PicLoc(nCurrentId, pParams, 1);
+ break;
+ }
+ aSprmIter.advance();
+ }
+
+ if( !nLoop )
+ {
+ pChp->GetPCDSprms( aDesc );
+ aSprmIter.SetSprms( aDesc.pMemPos,
+ aDesc.nSprmsLen );
+ }
+ }
+ aSave.Restore(this);
+ m_bEmbeddObj=bOldEmbeddObj;
+
+ // then import either an OLE of a Graphic
+ if( m_bObj )
+ {
+ if( bMakeSdrGrafObj && pTextObj &&
+ pTextObj->getParentSdrObjectFromSdrObject() )
+ {
+ // use SdrOleObj/SdrGrafObj instead of
+ // SdrTextObj in this Group
+
+ Graphic aGraph;
+ rtl::Reference<SdrObject> pNew = ImportOleBase(aGraph);
+
+ if( !pNew )
+ {
+ pNew = new SdrGrafObj(*m_pDrawModel);
+ static_cast<SdrGrafObj*>(pNew.get())->SetGraphic(aGraph);
+ }
+
+ GraphicCtor();
+
+ pNew->SetLogicRect( pTextObj->GetCurrentBoundRect() );
+ pNew->SetLayer( pTextObj->GetLayer() );
+
+ pTextObj->getParentSdrObjectFromSdrObject()->GetSubList()->
+ ReplaceObject(pNew.get(), pTextObj->GetOrdNum());
+ }
+ else
+ pFlyFormat = ImportOle();
+ m_bObj = false;
+ }
+ else
+ {
+ InsertAttrsAsDrawingAttrs(nNewStartCp, nNewStartCp+1,
+ eType, true);
+ pFlyFormat = ImportGraf(bMakeSdrGrafObj ? pTextObj : nullptr,
+ pOldFlyFormat);
+ }
+ }
+ break;
+ case 0x8:
+ if ( (!pbTestTxbxContainsText) && (!m_bObj) )
+ pFlyFormat = Read_GrafLayer( nPosCp );
+ break;
+ default:
+ bDone = false;
+ break;
+ }
+
+ if( bDone )
+ {
+ if( pFlyFormat && pRecord )
+ {
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END>
+ aFlySet( m_rDoc.GetAttrPool() );
+
+ tools::Rectangle aInnerDist( pRecord->nDxTextLeft,
+ pRecord->nDyTextTop,
+ pRecord->nDxTextRight,
+ pRecord->nDyTextBottom );
+ MatchSdrItemsIntoFlySet( pTextObj,
+ aFlySet,
+ pRecord->eLineStyle,
+ pRecord->eLineDashing,
+ pRecord->eShapeType,
+ aInnerDist );
+
+ pFlyFormat->SetFormatAttr( aFlySet );
+
+ MapWrapIntoFlyFormat(*pRecord, *pFlyFormat);
+ }
+ aString.clear();
+ rbEraseTextObj = (nullptr != pFlyFormat);
+ }
+ }
+ }
+
+ if( pnStartCp )
+ *pnStartCp = nStartCp;
+ if( pnEndCp )
+ *pnEndCp = nEndCp;
+
+ if( pbTestTxbxContainsText )
+ *pbTestTxbxContainsText = bTextWasRead && ! rbEraseTextObj;
+ else if( !rbEraseTextObj )
+ {
+ if( bTextWasRead )
+ {
+ m_pDrawEditEngine->SetText(aOrigString);
+ InsertAttrsAsDrawingAttrs(nStartCp, nEndCp, eType);
+ }
+
+ bool bVertical = pTextObj->IsVerticalWriting();
+ OutlinerParaObject aOp(m_pDrawEditEngine->CreateTextObject());
+ aOp.SetOutlinerMode( OutlinerMode::TextObject );
+ aOp.SetVertical( bVertical );
+ pTextObj->NbcSetOutlinerParaObject( std::move(aOp) );
+ pTextObj->SetVerticalWriting(bVertical);
+
+ // For the next TextBox also remove the old paragraph attributes
+ // and styles, otherwise the next box will start with the wrong
+ // attributes.
+ // Course of action: delete text = reduce to one paragraph
+ // and on this one delete the paragraph attributes
+ // and styles
+ m_pDrawEditEngine->SetText( OUString() );
+ m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet());
+ }
+
+ m_pStrm->Seek( nOld );
+ if (pbContainsGraphics)
+ *pbContainsGraphics = bContainsGraphics;
+}
+
+bool SwWW8ImplReader::TxbxChainContainsRealText(sal_uInt16 nTxBxS, tools::Long& rStartCp,
+ tools::Long& rEndCp)
+{
+ bool bErase, bContainsText;
+ InsertTxbxText( nullptr,nullptr,nTxBxS,USHRT_MAX,0,nullptr,false, bErase, &bContainsText,
+ &rStartCp, &rEndCp );
+ return bContainsText;
+}
+
+// TextBoxes only for Ver67 !!
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadTextBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ bool bDummy;
+ WW8_DP_TXTBOX aTextB;
+
+ if( !ReadGrafStart( static_cast<void*>(&aTextB), sizeof( aTextB ), pHd, rSet ) )
+ return nullptr;
+
+ Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2,
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 );
+ Point aP1( aP0 );
+ aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) );
+ aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
+
+ rtl::Reference<SdrRectObj> pObj = new SdrRectObj(
+ *m_pDrawModel,
+ SdrObjKind::Text,
+ tools::Rectangle(aP0, aP1));
+
+ pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1));
+ Size aSize( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ,
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) );
+
+ tools::Long nStartCpFly,nEndCpFly;
+ bool bContainsGraphics;
+ InsertTxbxText(pObj.get(), &aSize, 0, 0, 0, nullptr, false,
+ bDummy,nullptr,&nStartCpFly,&nEndCpFly,&bContainsGraphics);
+
+ SetStdAttr( rSet, aTextB.aLnt, aTextB.aShd );
+ SetFill( rSet, aTextB.aFill );
+
+ rSet.Put( SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ) );
+ rSet.Put( makeSdrTextAutoGrowWidthItem(false));
+ rSet.Put( makeSdrTextAutoGrowHeightItem(false));
+ rSet.Put( makeSdrTextLeftDistItem( MIN_BORDER_DIST*2 ) );
+ rSet.Put( makeSdrTextRightDistItem( MIN_BORDER_DIST*2 ) );
+ rSet.Put( makeSdrTextUpperDistItem( MIN_BORDER_DIST ) );
+ rSet.Put( makeSdrTextLowerDistItem( MIN_BORDER_DIST ) );
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadCaptionBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ static const SdrCaptionType aCaptA[] = { SdrCaptionType::Type1, SdrCaptionType::Type2,
+ SdrCaptionType::Type3, SdrCaptionType::Type4 };
+
+ WW8_DP_CALLOUT_TXTBOX aCallB;
+
+ if( !ReadGrafStart( static_cast<void*>(&aCallB), sizeof( aCallB ), pHd, rSet ) )
+ return nullptr;
+
+ sal_uInt16 nCount = SVBT16ToUInt16( aCallB.dpPolyLine.aBits1 ) >> 1 & 0x7fff;
+ if (nCount < 1)
+ {
+ SAL_WARN("sw.ww8", "Short CaptionBox header");
+ return nullptr;
+ }
+
+ std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]);
+
+ bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4); // read points
+ if (!bCouldRead)
+ {
+ SAL_WARN("sw.ww8", "Short CaptionBox header");
+ return nullptr;
+ }
+
+ sal_uInt8 nTyp = static_cast<sal_uInt8>(nCount) - 1;
+ if( nTyp == 1 && SVBT16ToUInt16( xP[0] ) == SVBT16ToUInt16( xP[2] ) )
+ nTyp = 0;
+
+ Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) +
+ static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.xa )) + m_nDrawXOfs2,
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ))
+ + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.ya )) + m_nDrawYOfs2 );
+ Point aP1( aP0 );
+ aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dxa )) );
+ aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dya )) );
+ Point aP2( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa ))
+ + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadPolyLine.xa ))
+ + m_nDrawXOfs2 + static_cast<sal_Int16>(SVBT16ToUInt16( xP[0] )),
+ static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ))
+ + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadPolyLine.ya ))
+ + m_nDrawYOfs2 + static_cast<sal_Int16>(SVBT16ToUInt16( xP[1] )) );
+ xP.reset();
+
+ rtl::Reference<SdrCaptionObj> pObj = new SdrCaptionObj(
+ *m_pDrawModel,
+ tools::Rectangle(aP0, aP1),
+ aP2);
+
+ pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1));
+ Size aSize( static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dxa )),
+ static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dya )) );
+ bool bEraseThisObject;
+
+ InsertTxbxText(pObj.get(), &aSize, 0, 0, 0, nullptr, false, bEraseThisObject );
+
+ if( SVBT16ToUInt16( aCallB.dptxbx.aLnt.lnps ) != 5 ) // Is border visible ?
+ SetStdAttr( rSet, aCallB.dptxbx.aLnt, aCallB.dptxbx.aShd );
+ else // no -> take lines
+ SetStdAttr( rSet, aCallB.dpPolyLine.aLnt, aCallB.dptxbx.aShd );
+ SetFill( rSet, aCallB.dptxbx.aFill );
+ rSet.Put(SdrCaptionTypeItem(aCaptA[nTyp % SAL_N_ELEMENTS(aCaptA)]));
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadGroup(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet)
+{
+ sal_Int16 nGrouped;
+
+ if( !ReadGrafStart( static_cast<void*>(&nGrouped), sizeof( nGrouped ), pHd, rSet ) )
+ return nullptr;
+
+#ifdef OSL_BIGENDIAN
+ nGrouped = (sal_Int16)OSL_SWAPWORD( nGrouped );
+#endif
+
+ m_nDrawXOfs = m_nDrawXOfs + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa ));
+ m_nDrawYOfs = m_nDrawYOfs + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ));
+
+ rtl::Reference<SdrObject> pObj = new SdrObjGroup(*m_pDrawModel);
+
+ short nLeft = static_cast<sal_Int16>(SVBT16ToUInt16( pHd->cb )) - sizeof( WW8_DPHEAD );
+ for (int i = 0; i < nGrouped && nLeft >= static_cast<short>(sizeof(WW8_DPHEAD)); ++i)
+ {
+ SfxAllItemSet aSet(m_pDrawModel->GetItemPool());
+ if (rtl::Reference<SdrObject> pObject = ReadGrafPrimitive(nLeft, aSet))
+ {
+ // first add and then set ItemSet
+ SdrObjList *pSubGroup = pObj->GetSubList();
+ OSL_ENSURE(pSubGroup, "Why no sublist available?");
+ if (pSubGroup)
+ pSubGroup->InsertObject(pObject.get(), 0);
+ pObject->SetMergedItemSetAndBroadcast(aSet);
+ }
+ }
+
+ m_nDrawXOfs = m_nDrawXOfs - static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa ));
+ m_nDrawYOfs = m_nDrawYOfs - static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya ));
+
+ return pObj;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ReadGrafPrimitive(short& rLeft, SfxAllItemSet &rSet)
+{
+ // This whole archaic word 6 graphic import can probably be refactored
+ // into an object hierarchy with a little effort.
+ rtl::Reference<SdrObject> pRet;
+ WW8_DPHEAD aHd; // Read Draw-Primitive-Header
+ bool bCouldRead = checkRead(*m_pStrm, &aHd, sizeof(WW8_DPHEAD)) &&
+ SVBT16ToUInt16(aHd.cb) >= sizeof(WW8_DPHEAD);
+ OSL_ENSURE(bCouldRead, "Graphic Primitive header short read" );
+ if (!bCouldRead)
+ {
+ rLeft=0;
+ return pRet;
+ }
+
+ if( rLeft >= SVBT16ToUInt16(aHd.cb) ) // precautions
+ {
+ rSet.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH));
+ switch (SVBT16ToUInt16(aHd.dpk) & 0xff )
+ {
+ case 0:
+ pRet = ReadGroup(&aHd, rSet);
+ break;
+ case 1:
+ pRet = ReadLine(&aHd, rSet);
+ break;
+ case 2:
+ pRet = ReadTextBox(&aHd, rSet);
+ break;
+ case 3:
+ pRet = ReadRect(&aHd, rSet);
+ break;
+ case 4:
+ pRet = ReadEllipse(&aHd, rSet);
+ break;
+ case 5:
+ pRet = ReadArc(&aHd, rSet);
+ break;
+ case 6:
+ pRet = ReadPolyLine(&aHd, rSet);
+ break;
+ case 7:
+ pRet = ReadCaptionBox(&aHd, rSet);
+ break;
+ default: // unknown
+ m_pStrm->SeekRel(SVBT16ToUInt16(aHd.cb) - sizeof(WW8_DPHEAD));
+ break;
+ }
+ }
+ else
+ {
+ OSL_ENSURE( false, "+Grafik-Overlap" );
+ }
+ rLeft = rLeft - SVBT16ToUInt16( aHd.cb );
+ return pRet;
+}
+
+void SwWW8ImplReader::ReadGrafLayer1(WW8PLCFspecial& rPF, tools::Long nGrafAnchorCp)
+{
+ rPF.SeekPos(nGrafAnchorCp);
+ WW8_FC nStartFc;
+ void* pF0;
+ if (!rPF.Get(nStartFc, pF0))
+ {
+ OSL_ENSURE( false, "+Where is the graphic (2) ?" );
+ return;
+ }
+ WW8_FDOA* pF = static_cast<WW8_FDOA*>(pF0);
+ if( !SVBT32ToUInt32( pF->fc ) )
+ {
+ OSL_ENSURE( false, "+Where is the graphic (3) ?" );
+ return;
+ }
+
+ sal_uInt32 nPosFc = SVBT32ToUInt32(pF->fc);
+
+ //skip duplicate graphics when fuzzing
+ if (m_bFuzzing)
+ {
+ if (!m_aGrafPosSet.insert(nPosFc).second)
+ return;
+ }
+
+ bool bCouldSeek = checkSeek(*m_pStrm, nPosFc);
+ OSL_ENSURE(bCouldSeek, "Invalid graphic offset");
+ if (!bCouldSeek)
+ return;
+
+ // read Draw-Header
+ WW8_DO aDo;
+ bool bCouldRead = checkRead(*m_pStrm, &aDo, sizeof(WW8_DO));
+ OSL_ENSURE(bCouldRead, "Short graphic header");
+ if (!bCouldRead)
+ return;
+
+ short nLeft = SVBT16ToUInt16( aDo.cb ) - sizeof( WW8_DO );
+ while (nLeft > static_cast<short>(sizeof(WW8_DPHEAD)))
+ {
+ SfxAllItemSet aSet( m_pDrawModel->GetItemPool() );
+ if (rtl::Reference<SdrObject> pObject = ReadGrafPrimitive(nLeft, aSet))
+ {
+ m_xWWZOrder->InsertDrawingObject(pObject.get(), SVBT16ToUInt16(aDo.dhgt));
+
+ tools::Rectangle aRect(pObject->GetSnapRect());
+
+ const sal_uInt32 nCntRelTo = 3;
+
+ // Adjustment is horizontally relative to...
+ static const sal_Int16 aHoriRelOriTab[nCntRelTo] =
+ {
+ text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
+ text::RelOrientation::PAGE_FRAME, // 1 is page margin
+ text::RelOrientation::FRAME, // 2 is relative to paragraph
+ };
+
+ // Adjustment is vertically relative to...
+ static const sal_Int16 aVertRelOriTab[nCntRelTo] =
+ {
+ text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
+ text::RelOrientation::PAGE_FRAME, // 1 is page margin
+ text::RelOrientation::FRAME, // 2 is relative to paragraph
+ };
+
+ const int nXAlign = aDo.bx < nCntRelTo ? aDo.bx : 0;
+ const int nYAlign = aDo.by < nCntRelTo ? aDo.by : 0;
+
+ aSet.Put(SwFormatHoriOrient(aRect.Left(), text::HoriOrientation::NONE,
+ aHoriRelOriTab[ nXAlign ]));
+ aSet.Put(SwFormatVertOrient(aRect.Top(), text::VertOrientation::NONE,
+ aVertRelOriTab[ nYAlign ]));
+
+ SwFrameFormat *pFrame = m_rDoc.getIDocumentContentOperations().InsertDrawObj( *m_pPaM, *pObject, aSet );
+ pObject->SetMergedItemSet(aSet);
+
+ if (SwDrawFrameFormat *pDrawFrame = dynamic_cast<SwDrawFrameFormat*>(pFrame))
+ {
+ pDrawFrame->PosAttrSet();
+ }
+
+ AddAutoAnchor(pFrame);
+ }
+ }
+}
+
+sal_Int32 SwMSDffManager::GetEscherLineMatch(MSO_LineStyle eStyle,
+ MSO_SPT eShapeType, sal_Int32 &rThick)
+{
+ sal_Int32 nOutsideThick = 0;
+ /*
+ Note: In contrast to the regular WinWord table and frame border width,
+ where the overall border width has to be calculated from the width of *one*
+ line, the data from ESCHER already contains the overall width [twips]!
+
+ The WinWord default is 15 tw. We take for this our 20 tw line.
+ (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
+ 0.05 pt hairline.) The hairline we only set by WinWord width up to max.
+ 0.5 pt.
+ */
+ switch( eStyle )
+ {
+ case mso_lineTriple:
+ case mso_lineSimple:
+ nOutsideThick = eShapeType != mso_sptTextBox ? rThick : rThick/2;
+ break;
+ case mso_lineDouble:
+ if (eShapeType == mso_sptTextBox)
+ {
+ nOutsideThick = rThick/6;
+ rThick = rThick*2/3;
+ }
+ else
+ nOutsideThick = rThick*2/3;
+ break;
+ case mso_lineThickThin:
+ if (eShapeType == mso_sptTextBox)
+ {
+ nOutsideThick = rThick*3/10;
+ rThick = rThick*4/5;
+ }
+ else
+ nOutsideThick = rThick*4/5;
+ break;
+ case mso_lineThinThick:
+ {
+ if (eShapeType == mso_sptTextBox)
+ {
+ nOutsideThick = rThick/10;
+ rThick = rThick*3/5;
+ }
+ else
+ nOutsideThick = rThick*3/5;
+ }
+ break;
+ default:
+ break;
+ }
+ return nOutsideThick;
+}
+
+// Returns the thickness of the line outside the frame, the logic of
+// words positioning of borders around floating objects is that of a
+// disturbed mind.
+sal_Int32 SwWW8ImplReader::MatchSdrBoxIntoFlyBoxItem(const Color& rLineColor,
+ MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, sal_Int32 &rLineThick,
+ SvxBoxItem& rBox )
+{
+ sal_Int32 nOutsideThick = 0;
+ if( !rLineThick )
+ return nOutsideThick;
+
+ SvxBorderLineStyle nIdx = SvxBorderLineStyle::NONE;
+
+ sal_Int32 nLineThick=rLineThick;
+ nOutsideThick = SwMSDffManager::GetEscherLineMatch(eLineStyle,
+ eShapeType, rLineThick);
+
+ /*
+ Note: In contrast to the regular WinWord table and frame border width,
+ where the overall border width has to be calculated from the width of *one*
+ line, the data from ESCHER already contains the overall width [twips]!
+
+ The WinWord default is 15 tw. We take for this our 20 tw line.
+ (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our
+ 0.05 pt hairline.) The hairline we only set by WinWord width up to max.
+ 0.5 pt.
+ */
+ switch( +eLineStyle )
+ {
+ // first the single lines
+ case mso_lineSimple:
+ nIdx = SvxBorderLineStyle::SOLID;
+ break;
+ // second the double lines
+ case mso_lineDouble:
+ nIdx = SvxBorderLineStyle::DOUBLE;
+ break;
+ case mso_lineThickThin:
+ nIdx = SvxBorderLineStyle::THICKTHIN_SMALLGAP;
+ break;
+ case mso_lineThinThick:
+ nIdx = SvxBorderLineStyle::THINTHICK_SMALLGAP;
+ break;
+ // We have no triple border, use double instead.
+ case mso_lineTriple:
+ nIdx = SvxBorderLineStyle::DOUBLE;
+ break;
+ // no line style is set
+ case MSO_LineStyle(USHRT_MAX):
+ break;
+ // erroneously not implemented line style is set
+ default:
+ OSL_ENSURE(false, "eLineStyle is not (yet) implemented!");
+ break;
+ }
+
+ switch( eDashing )
+ {
+ case mso_lineDashGEL:
+ nIdx = SvxBorderLineStyle::DASHED;
+ break;
+ case mso_lineDotGEL:
+ nIdx = SvxBorderLineStyle::DOTTED;
+ break;
+ default:
+ break;
+ }
+
+ if (SvxBorderLineStyle::NONE != nIdx)
+ {
+ SvxBorderLine aLine;
+ aLine.SetColor( rLineColor );
+
+ aLine.SetWidth( nLineThick ); // No conversion here, nLineThick is already in twips
+ aLine.SetBorderLineStyle(nIdx);
+
+ for(SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>())
+ {
+ // aLine is cloned by SetLine
+ rBox.SetLine(&aLine, nLine);
+ }
+ }
+
+ return nOutsideThick;
+}
+
+#define WW8ITEMVALUE(ItemSet,Id,Cast) ItemSet.GetItem<Cast>(Id)->GetValue()
+
+void SwWW8ImplReader::MatchSdrItemsIntoFlySet( SdrObject const * pSdrObj,
+ SfxItemSet& rFlySet, MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType,
+ tools::Rectangle& rInnerDist )
+{
+ /*
+ attributes to be set on the frame
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ SwFormatFrameSize if not set, set here
+ SvxLRSpaceItem set here
+ SvxULSpaceItem set here
+ SvxOpaqueItem (Currently not possible for frames! khz 10.2.1999)
+ SwFormatSurround already set
+ SwFormatVertOrient already set
+ SwFormatHoriOrient already set
+ SwFormatAnchor already set
+ SvxBoxItem set here
+ SvxBrushItem set here
+ SvxShadowItem set here
+ */
+
+ // 1. GraphicObject of documents?
+ GraphicCtor();
+
+ const SfxItemSet& rOldSet = pSdrObj->GetMergedItemSet();
+
+ // some Items can be taken over directly
+ static sal_uInt16 const aDirectMatch[]
+ {
+ RES_LR_SPACE, // outer spacing left/right: SvxLRSpaceItem
+ RES_UL_SPACE // outer spacing top/bottom: SvxULSpaceItem
+ };
+ const SfxPoolItem* pPoolItem;
+ for(sal_uInt16 i : aDirectMatch)
+ if( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) )
+ {
+ rFlySet.Put( *pPoolItem );
+ }
+
+ // take new XATTR items directly. Skip old RES_BACKGROUND if new FILLSTYLE taken.
+ bool bSkipResBackground = false;
+ SfxItemPool* pPool = rFlySet.GetPool();
+ if ( pPool )
+ {
+ for ( sal_uInt16 i = XATTR_START; i < XATTR_END; ++i )
+ {
+ // Not all Fly types support XATTRs - skip unsupported attributes
+ SfxItemPool* pAttrPool = pPool->GetMasterPool();
+ while ( pAttrPool && !pAttrPool->IsInRange(i) )
+ pAttrPool = pAttrPool->GetSecondaryPool();
+ if ( !pAttrPool )
+ continue;
+
+ if ( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) )
+ {
+ rFlySet.Put( *pPoolItem );
+ if ( i == XATTR_FILLSTYLE )
+ {
+ const drawing::FillStyle eFill = static_cast<const XFillStyleItem*>(pPoolItem)->GetValue();
+ // Transparency forced in certain situations when fillstyle is none - use old logic for that case still
+ // which is especially needed for export purposes (tdf112618).
+ if ( eFill != drawing::FillStyle_NONE )
+ bSkipResBackground = true;
+ }
+ }
+ }
+ }
+
+ // now calculate the borders and build the box: The unit is needed for the
+ // frame SIZE!
+ SvxBoxItem aBox(rFlySet.Get(RES_BOX));
+ // dashed or solid becomes solid
+ // WW-default: 0.75 pt = 15 twips
+ sal_Int32 nLineThick = 15, nOutside=0;
+
+ // check if LineStyle is *really* set!
+
+ SfxItemState eState = rOldSet.GetItemState(XATTR_LINESTYLE);
+ if( eState == SfxItemState::SET )
+ {
+ // Now, that we know there is a line style we will make use the
+ // parameter given to us when calling the method... :-)
+ const Color aLineColor = rOldSet.Get(XATTR_LINECOLOR).GetColorValue();
+ nLineThick = WW8ITEMVALUE(rOldSet, XATTR_LINEWIDTH, XLineWidthItem);
+
+ if( !nLineThick )
+ nLineThick = 1; // for Writer, zero is "no border", so set a minimal value
+
+ nOutside = MatchSdrBoxIntoFlyBoxItem(aLineColor, eLineStyle,
+ eDashing, eShapeType, nLineThick, aBox);
+ }
+
+ rInnerDist.AdjustLeft(nLineThick );
+ rInnerDist.AdjustTop(nLineThick );
+ rInnerDist.AdjustRight(nLineThick );
+ rInnerDist.AdjustBottom(nLineThick );
+
+ rInnerDist.AdjustLeft( -(aBox.CalcLineWidth( SvxBoxItemLine::LEFT )) );
+ rInnerDist.AdjustTop( -(aBox.CalcLineWidth( SvxBoxItemLine::TOP )) );
+ rInnerDist.AdjustRight( -(aBox.CalcLineWidth( SvxBoxItemLine::RIGHT )) );
+ rInnerDist.AdjustBottom( -(aBox.CalcLineWidth( SvxBoxItemLine::BOTTOM )) );
+
+ // set distances from box's border to text contained within the box
+ if( 0 < rInnerDist.Left() )
+ aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Left()), SvxBoxItemLine::LEFT );
+ if( 0 < rInnerDist.Top() )
+ aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Top()), SvxBoxItemLine::TOP );
+ if( 0 < rInnerDist.Right() )
+ aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Right()), SvxBoxItemLine::RIGHT );
+ if( 0 < rInnerDist.Bottom() )
+ aBox.SetDistance( o3tl::narrowing<sal_uInt16>(rInnerDist.Bottom()), SvxBoxItemLine::BOTTOM );
+
+ bool bFixSize = !(WW8ITEMVALUE(rOldSet, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ SdrOnOffItem));
+
+ // Size: SwFormatFrameSize
+ if( SfxItemState::SET != rFlySet.GetItemState(RES_FRM_SIZE, false) )
+ {
+ const tools::Rectangle& rSnapRect = pSdrObj->GetSnapRect();
+ // if necessary adapt width and position of the framework: The
+ // recorded interior is to remain equally large despite thick edges.
+ rFlySet.Put( SwFormatFrameSize(bFixSize ? SwFrameSize::Fixed : SwFrameSize::Variable,
+ rSnapRect.GetWidth() + 2*nOutside,
+ rSnapRect.GetHeight() + 2*nOutside) );
+ }
+ else // If a size is set, adjust it to consider border thickness
+ {
+ SwFormatFrameSize aSize = rFlySet.Get(RES_FRM_SIZE);
+
+ SwFormatFrameSize aNewSize(bFixSize ? SwFrameSize::Fixed : SwFrameSize::Variable,
+ aSize.GetWidth() + 2*nOutside,
+ aSize.GetHeight() + 2*nOutside);
+ aNewSize.SetWidthSizeType(aSize.GetWidthSizeType());
+ rFlySet.Put( aNewSize );
+ }
+
+ // Sadly word puts escher borders outside the graphic, but orients the
+ // graphic in relation to the top left inside the border. We don't
+ if (nOutside)
+ {
+ SwFormatHoriOrient aHori = rFlySet.Get(RES_HORI_ORIENT);
+ aHori.SetPos(MakeSafePositioningValue(aHori.GetPos()-nOutside));
+ rFlySet.Put(aHori);
+
+ SwFormatVertOrient aVert = rFlySet.Get(RES_VERT_ORIENT);
+ aVert.SetPos(aVert.GetPos()-nOutside);
+ rFlySet.Put(aVert);
+ }
+
+ // now set the border
+ rFlySet.Put( aBox );
+
+ // shadow of the box: SvxShadowItem
+ if( WW8ITEMVALUE(rOldSet, SDRATTR_SHADOW, SdrOnOffItem) )
+ {
+ SvxShadowItem aShadow( RES_SHADOW );
+
+ const Color aShdColor = rOldSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue();
+ const sal_Int32 nShdDistX = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWXDIST,
+ SdrMetricItem);
+ const sal_Int32 nShdDistY = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWYDIST,
+ SdrMetricItem);
+
+ aShadow.SetColor( aShdColor );
+
+ aShadow.SetWidth(writer_cast<sal_uInt16>((std::abs( nShdDistX) +
+ std::abs( nShdDistY )) / 2 ));
+
+ SvxShadowLocation eShdPosi;
+ if( 0 <= nShdDistX )
+ {
+ if( 0 <= nShdDistY )
+ eShdPosi = SvxShadowLocation::BottomRight;
+ else
+ eShdPosi = SvxShadowLocation::TopRight;
+ }
+ else
+ {
+ if( 0 <= nShdDistY )
+ eShdPosi = SvxShadowLocation::BottomLeft;
+ else
+ eShdPosi = SvxShadowLocation::TopLeft;
+ }
+ aShadow.SetLocation( eShdPosi );
+
+ rFlySet.Put( aShadow );
+ }
+ SvxBrushItem aBrushItem(COL_WHITE, RES_BACKGROUND);
+ bool bBrushItemOk = false;
+ sal_uInt8 nTrans = 0;
+
+ // Separate transparency
+ eState = rOldSet.GetItemState(XATTR_FILLTRANSPARENCE);
+ if (!bSkipResBackground && eState == SfxItemState::SET)
+ {
+ sal_uInt16 nRes = WW8ITEMVALUE(rOldSet, XATTR_FILLTRANSPARENCE,
+ XFillTransparenceItem);
+ nTrans = sal_uInt8((nRes * 0xFE) / 100);
+ aBrushItem.GetColor().SetAlpha(255 - nTrans);
+ bBrushItemOk = true;
+ }
+
+ // Background: SvxBrushItem
+ const XFillStyleItem* pFillStyleItem = rOldSet.GetItemIfSet(XATTR_FILLSTYLE);
+ if (!bSkipResBackground && pFillStyleItem)
+ {
+ const drawing::FillStyle eFill = pFillStyleItem->GetValue();
+
+ switch (eFill)
+ {
+ default:
+ case drawing::FillStyle_NONE:
+ // Writer graphics don't have it yet
+ if (eShapeType != mso_sptPictureFrame)
+ {
+ aBrushItem.GetColor().SetAlpha(1);
+ bBrushItemOk = true;
+ }
+ break;
+ case drawing::FillStyle_SOLID:
+ case drawing::FillStyle_GRADIENT:
+ {
+ const Color aColor =
+ rOldSet.Get(XATTR_FILLCOLOR).GetColorValue();
+ aBrushItem.SetColor(aColor);
+
+ if (bBrushItemOk) // has trans
+ aBrushItem.GetColor().SetAlpha(255 - nTrans);
+
+ bBrushItemOk = true;
+ }
+ break;
+ case drawing::FillStyle_HATCH:
+ break;
+ case drawing::FillStyle_BITMAP:
+ {
+ GraphicObject aGrfObj(rOldSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
+ const bool bTile(WW8ITEMVALUE(rOldSet, XATTR_FILLBMP_TILE, XFillBmpTileItem));
+
+ if(bBrushItemOk) // has trans
+ {
+ GraphicAttr aAttr(aGrfObj.GetAttr());
+
+ aAttr.SetAlpha(255 - nTrans);
+ aGrfObj.SetAttr(aAttr);
+ }
+
+ aBrushItem.SetGraphicObject(aGrfObj);
+ aBrushItem.SetGraphicPos(bTile ? GPOS_TILED : GPOS_AREA);
+ bBrushItemOk = true;
+ }
+ break;
+ }
+ }
+
+ if (bBrushItemOk)
+ rFlySet.Put(aBrushItem);
+}
+
+void SwWW8ImplReader::AdjustLRWrapForWordMargins(
+ const SvxMSDffImportRec &rRecord, SvxLRSpaceItem &rLR)
+{
+ sal_uInt32 nXRelTo = SvxMSDffImportRec::RELTO_DEFAULT;
+ if ( rRecord.nXRelTo )
+ {
+ nXRelTo = *rRecord.nXRelTo;
+ }
+
+ // Left adjustments - if horizontally aligned to left of
+ // margin or column then remove the left wrapping
+ if (rRecord.nXAlign == 1)
+ {
+ if ((nXRelTo == 0) || (nXRelTo == 2))
+ rLR.SetLeft(sal_uInt16(0));
+ }
+
+ // Right adjustments - if horizontally aligned to right of
+ // margin or column then remove the right wrapping
+ if (rRecord.nXAlign == 3)
+ {
+ if ((nXRelTo == 0) || (nXRelTo == 2))
+ rLR.SetRight(sal_uInt16(0));
+ }
+
+ // Inside margin, remove left wrapping
+ if ((rRecord.nXAlign == 4) && (nXRelTo == 0))
+ {
+ rLR.SetLeft(sal_uInt16(0));
+ }
+
+ // Outside margin, remove left wrapping
+ if ((rRecord.nXAlign == 5) && (nXRelTo == 0))
+ {
+ rLR.SetRight(sal_uInt16(0));
+ }
+}
+
+void SwWW8ImplReader::AdjustULWrapForWordMargins(
+ const SvxMSDffImportRec &rRecord, SvxULSpaceItem &rUL)
+{
+ sal_uInt32 nYRelTo = SvxMSDffImportRec::RELTO_DEFAULT;
+ if ( rRecord.nYRelTo )
+ {
+ nYRelTo = *rRecord.nYRelTo;
+ }
+
+ // Top adjustment - remove upper wrapping if aligned to page
+ // printable area or to page
+ if (rRecord.nYAlign == 1)
+ {
+ if ((nYRelTo == 0) || (nYRelTo == 1))
+ rUL.SetUpper(sal_uInt16(0));
+ }
+
+ // Bottom adjustment - remove bottom wrapping if aligned to page or
+ // printable area or to page
+ if (rRecord.nYAlign == 3)
+ {
+ if ((nYRelTo == 0) || (nYRelTo == 1))
+ rUL.SetLower(sal_uInt16(0));
+ }
+
+ // Remove top margin if aligned vertically inside margin
+ if ((rRecord.nYAlign == 4) && (nYRelTo == 0))
+ rUL.SetUpper(sal_uInt16(0));
+}
+
+void SwWW8ImplReader::MapWrapIntoFlyFormat(const SvxMSDffImportRec& rRecord,
+ SwFrameFormat& rFlyFormat)
+{
+ if (rRecord.nDxWrapDistLeft || rRecord.nDxWrapDistRight)
+ {
+ SvxLRSpaceItem aLR(writer_cast<sal_uInt16>(rRecord.nDxWrapDistLeft),
+ writer_cast<sal_uInt16>(rRecord.nDxWrapDistRight), 0, RES_LR_SPACE);
+ AdjustLRWrapForWordMargins(rRecord, aLR);
+ rFlyFormat.SetFormatAttr(aLR);
+ }
+ if (rRecord.nDyWrapDistTop || rRecord.nDyWrapDistBottom)
+ {
+ SvxULSpaceItem aUL(writer_cast<sal_uInt16>(rRecord.nDyWrapDistTop),
+ writer_cast<sal_uInt16>(rRecord.nDyWrapDistBottom), RES_UL_SPACE);
+ AdjustULWrapForWordMargins(rRecord, aUL);
+ rFlyFormat.SetFormatAttr(aUL);
+ }
+
+ // If we are contoured and have a custom polygon...
+ if (rRecord.pWrapPolygon && rFlyFormat.GetSurround().IsContour())
+ {
+ if (SwNoTextNode* pNd = GetNoTextNodeFromSwFrameFormat(rFlyFormat))
+ {
+ /*
+ Gather round children and hear of a tale that will raise the
+ hairs on the back of your neck this dark halloween night.
+
+ There is a polygon in word that describes the wrapping around
+ the graphic.
+
+ Here are some sample values for the simplest case of a square
+ around some solid coloured graphics
+
+ X Y Pixel size of graphic
+ TopLeft -54 21600 400x400
+ Bottom Right 0 21546
+
+ TopLeft -108 21600 200x200
+ Bottom Right 0 21492
+
+ TopLeft -216 21600 100x100
+ Bottom Right 0 21384
+
+ TopLeft -432 21600 50x50
+ Bottom Right 0 21168
+
+ TopLeft -76 21600 283x212
+ Bottom Right 0 21498
+
+ So given that the size of the values remains pretty much the
+ same despite the size of the graphic, we can tell that the
+ polygon is measured in units that are independent of the
+ graphic. But why does the left corner move a different value
+ to the left each time, and why does the bottom move upwards
+ each time, when the right and top remain at the same value ?
+
+ I have no idea, but clearly once we calculate the values out
+ we see that the left margin is always a fixed realworld
+ distance from the true left and the polygon bottom is the same
+ fixed value from the bottom. i.e. 15twips.
+
+ So here we take our word provided polygon, shift it to the
+ right by 15twips and rescale it widthwise to shrink the width
+ a little to fit the now moved right margin back to where it
+ was, and stretch the height a little to make the bottom move
+ down the missing 15twips then we get a polygon that matches
+ what I actually see in word
+ */
+
+ tools::PolyPolygon aPoly(*rRecord.pWrapPolygon);
+ const Size &rSize = pNd->GetTwipSize();
+ /*
+ Move to the left by 15twips, and rescale to
+ a) shrink right bound back to orig position
+ b) stretch bottom bound to where I think it should have been
+ in the first place
+ */
+ Fraction aMoveHack(ww::nWrap100Percent, rSize.Width());
+ aMoveHack *= Fraction(15, 1);
+ tools::Long nMove(aMoveHack);
+ aPoly.Move(nMove, 0);
+
+ Fraction aHackX(ww::nWrap100Percent, ww::nWrap100Percent + nMove);
+ Fraction aHackY(ww::nWrap100Percent, ww::nWrap100Percent - nMove);
+ aPoly.Scale(double(aHackX), double(aHackY));
+
+ // Turn polygon back into units that match the graphic's
+ const Size &rOrigSize = pNd->GetGraphic().GetPrefSize();
+ Fraction aMapPolyX(rOrigSize.Width(), ww::nWrap100Percent);
+ Fraction aMapPolyY(rOrigSize.Height(), ww::nWrap100Percent);
+ aPoly.Scale(double(aMapPolyX), double(aMapPolyY));
+
+ // #i47277# - contour is already in unit of the
+ // graphic preferred unit. Thus, call method <SetContour(..)>
+ pNd->SetContour(&aPoly);
+ }
+ }
+ else if (rFlyFormat.GetSurround().IsContour())
+ {
+ const SdrObject* pSdrObj = rFlyFormat.FindSdrObject();
+ SdrObjKind eKind = pSdrObj ? pSdrObj->GetObjIdentifier() : SdrObjKind::Graphic;
+ switch (eKind)
+ {
+ case SdrObjKind::Text:
+ break;
+ case SdrObjKind::SwFlyDrawObjIdentifier:
+ default:
+ {
+ // Contour is enabled, but no polygon is set: disable contour, because Word does not
+ // Writer-style auto-contour in that case.
+ SwFormatSurround aSurround(rFlyFormat.GetSurround());
+ aSurround.SetContour(false);
+ rFlyFormat.SetFormatAttr(aSurround);
+ }
+ }
+ }
+}
+
+static sal_Int32 lcl_ConvertCrop(sal_uInt32 const nCrop, sal_Int32 const nSize)
+{
+ // cast to sal_Int32 to handle negative crop properly
+ sal_Int32 const nIntegral(static_cast<sal_Int32>(nCrop) >> 16);
+ // fdo#77454: heuristic to detect mangled values written by old OOo/LO
+ if (abs(nIntegral) >= 50) // FIXME: what's a good cut-off?
+ {
+ SAL_INFO("sw.ww8", "ignoring suspiciously large crop: " << nIntegral);
+ return 0;
+ }
+ return (nIntegral * nSize) + (((nCrop & 0xffff) * nSize) >> 16);
+}
+
+void SwWW8ImplReader::SetAttributesAtGrfNode(const SvxMSDffImportRec& rRecord,
+ const SwFrameFormat& rFlyFormat, WW8_FSPA const *pF)
+{
+ const SwNodeIndex* pIdx = rFlyFormat.GetContent(false).GetContentIdx();
+ SwGrfNode *const pGrfNd(
+ pIdx ? m_rDoc.GetNodes()[pIdx->GetIndex() + 1]->GetGrfNode() : nullptr);
+ if (!pGrfNd)
+ return;
+
+ Size aSz(pGrfNd->GetTwipSize());
+ // use type <sal_uInt64> instead of sal_uLong to get correct results
+ // in the following calculations.
+ sal_uInt64 nHeight = aSz.Height();
+ sal_uInt64 nWidth = aSz.Width();
+ if (!nWidth && pF)
+ nWidth = o3tl::saturating_sub(pF->nXaRight, pF->nXaLeft);
+ else if (!nHeight && pF)
+ nHeight = o3tl::saturating_sub(pF->nYaBottom, pF->nYaTop);
+
+ if (rRecord.nCropFromTop || rRecord.nCropFromBottom ||
+ rRecord.nCropFromLeft || rRecord.nCropFromRight)
+ {
+ SwCropGrf aCrop; // Cropping is stored in 'fixed floats'
+ // 16.16 (fraction times total
+ if (rRecord.nCropFromTop) // image width or height resp.)
+ {
+ aCrop.SetTop(lcl_ConvertCrop(rRecord.nCropFromTop, nHeight));
+ }
+ if (rRecord.nCropFromBottom)
+ {
+ aCrop.SetBottom(lcl_ConvertCrop(rRecord.nCropFromBottom, nHeight));
+ }
+ if (rRecord.nCropFromLeft)
+ {
+ aCrop.SetLeft(lcl_ConvertCrop(rRecord.nCropFromLeft, nWidth));
+ }
+ if (rRecord.nCropFromRight)
+ {
+ aCrop.SetRight(lcl_ConvertCrop(rRecord.nCropFromRight, nWidth));
+ }
+
+ pGrfNd->SetAttr( aCrop );
+ }
+
+ bool bFlipH(rRecord.nFlags & ShapeFlag::FlipH);
+ bool bFlipV(rRecord.nFlags & ShapeFlag::FlipV);
+ if ( bFlipH || bFlipV )
+ {
+ SwMirrorGrf aMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
+ if( bFlipH )
+ {
+ if( bFlipV )
+ aMirror.SetValue(MirrorGraph::Both);
+ else
+ aMirror.SetValue(MirrorGraph::Vertical);
+ }
+ else
+ aMirror.SetValue(MirrorGraph::Horizontal);
+
+ pGrfNd->SetAttr( aMirror );
+ }
+
+ if (!rRecord.pObj)
+ return;
+
+ const SfxItemSet& rOldSet = rRecord.pObj->GetMergedItemSet();
+ // contrast
+ if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFCONTRAST,
+ SdrGrafContrastItem))
+ {
+ SwContrastGrf aContrast(
+ WW8ITEMVALUE(rOldSet,
+ SDRATTR_GRAFCONTRAST, SdrGrafContrastItem));
+ pGrfNd->SetAttr( aContrast );
+ }
+
+ // luminance
+ if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFLUMINANCE,
+ SdrGrafLuminanceItem))
+ {
+ SwLuminanceGrf aLuminance(WW8ITEMVALUE(rOldSet,
+ SDRATTR_GRAFLUMINANCE, SdrGrafLuminanceItem));
+ pGrfNd->SetAttr( aLuminance );
+ }
+ // gamma
+ if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA, SdrGrafGamma100Item))
+ {
+ double fVal = WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA,
+ SdrGrafGamma100Item);
+ pGrfNd->SetAttr(SwGammaGrf(fVal/100.));
+ }
+
+ // drawmode
+ auto nGrafMode = rOldSet.GetItem<SdrGrafModeItem>(SDRATTR_GRAFMODE)->GetValue();
+ if ( nGrafMode != GraphicDrawMode::Standard)
+ {
+ SwDrawModeGrf aDrawMode( nGrafMode );
+ pGrfNd->SetAttr( aDrawMode );
+ }
+}
+
+SdrObject* SwWW8ImplReader::CreateContactObject(SwFrameFormat* pFlyFormat)
+{
+ if (pFlyFormat)
+ {
+ SdrObject* pNewObject = m_bNewDoc ? nullptr : pFlyFormat->FindRealSdrObject();
+ if (!pNewObject)
+ pNewObject = pFlyFormat->FindSdrObject();
+ if (!pNewObject )
+ if (auto pFlyFrameFormat = dynamic_cast<SwFlyFrameFormat *>( pFlyFormat ))
+ {
+ SwFlyDrawContact* pContactObject = pFlyFrameFormat->GetOrCreateContact();
+ pNewObject = pContactObject->GetMaster();
+ }
+ return pNewObject;
+ }
+ return nullptr;
+}
+
+// Miserable miserable hack to fudge word's graphic layout in RTL mode to ours.
+bool SwWW8ImplReader::MiserableRTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth,
+ sal_Int16 eHoriOri, sal_Int16 eHoriRel)
+{
+ if (!IsRightToLeft())
+ return false;
+ return RTLGraphicsHack(rLeft, nWidth, eHoriOri, eHoriRel,
+ m_aSectionManager.GetPageLeft(),
+ m_aSectionManager.GetPageRight(),
+ m_aSectionManager.GetPageWidth());
+}
+
+RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FSPA& rFSPA,
+ SfxItemSet &rFlySet)
+{
+ bool bCurSectionVertical = m_aSectionManager.CurrentSectionIsVertical();
+
+ if (!rRecord.nXRelTo)
+ {
+ rRecord.nXRelTo = sal_Int32(rFSPA.nbx);
+ }
+ if (!rRecord.nYRelTo)
+ {
+ rRecord.nYRelTo = sal_Int32(rFSPA.nby);
+ }
+
+ // nXAlign - abs. Position, Left, Centered, Right, Inside, Outside
+ // nYAlign - abs. Position, Top, Centered, Bottom, Inside, Outside
+
+ // nXRelTo - Page printable area, Page, Column, Character
+ // nYRelTo - Page printable area, Page, Paragraph, Line
+
+ const sal_uInt32 nCntXAlign = 6;
+ const sal_uInt32 nCntYAlign = 6;
+
+ const sal_uInt32 nCntRelTo = 4;
+
+ sal_uInt32 nXAlign = nCntXAlign > rRecord.nXAlign ? rRecord.nXAlign : 1;
+ sal_uInt32 nYAlign = nCntYAlign > rRecord.nYAlign ? rRecord.nYAlign : 1;
+
+ // #i52565# - try to handle special case for objects in tables regarding its X Rel
+
+ // if X and Y Rel values are on default take it as a hint, that they have not been set
+ // by <SwMSDffManager::ProcessObj(..)>
+ const bool bXYRelHaveDefaultValues = *rRecord.nXRelTo == 2 && *rRecord.nYRelTo == 2;
+ if (bXYRelHaveDefaultValues && m_nInTable > 0 && !bCurSectionVertical)
+ {
+ if (sal_uInt32(rFSPA.nby) != rRecord.nYRelTo)
+ rRecord.nYRelTo = sal_uInt32(rFSPA.nby);
+ }
+
+ sal_uInt32 nXRelTo = (rRecord.nXRelTo && nCntRelTo > rRecord.nXRelTo) ? *rRecord.nXRelTo : 1;
+ sal_uInt32 nYRelTo = (rRecord.nYRelTo && nCntRelTo > rRecord.nYRelTo) ? *rRecord.nYRelTo : 1;
+
+ RndStdIds eAnchor = IsInlineEscherHack() ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_CHAR; // #i43718#
+
+ SwFormatAnchor aAnchor( eAnchor );
+ aAnchor.SetAnchor( m_pPaM->GetPoint() );
+ rFlySet.Put( aAnchor );
+
+ // #i18732#
+ // Given new layout where everything is changed to be anchored to
+ // character the following 4 tables may need to be changed.
+
+ // horizontal Adjustment
+ static const sal_Int16 aHoriOriTab[ nCntXAlign ] =
+ {
+ text::HoriOrientation::NONE, // From left position
+ text::HoriOrientation::LEFT, // left
+ text::HoriOrientation::CENTER, // centered
+ text::HoriOrientation::RIGHT, // right
+ // #i36649#
+ // - inside -> text::HoriOrientation::LEFT and outside -> text::HoriOrientation::RIGHT
+ text::HoriOrientation::LEFT, // inside
+ text::HoriOrientation::RIGHT // outside
+ };
+
+ // generic vertical Adjustment
+ static const sal_Int16 aVertOriTab[ nCntYAlign ] =
+ {
+ text::VertOrientation::NONE, // From Top position
+ text::VertOrientation::TOP, // top
+ text::VertOrientation::CENTER, // centered
+ text::VertOrientation::BOTTOM, // bottom
+ text::VertOrientation::LINE_TOP, // inside (obscure)
+ text::VertOrientation::LINE_BOTTOM // outside (obscure)
+ };
+
+ // #i22673# - to-line vertical alignment
+ static const sal_Int16 aToLineVertOriTab[ nCntYAlign ] =
+ {
+ text::VertOrientation::NONE, // below
+ text::VertOrientation::LINE_BOTTOM, // top
+ text::VertOrientation::LINE_CENTER, // centered
+ text::VertOrientation::LINE_TOP, // bottom
+ text::VertOrientation::LINE_BOTTOM, // inside (obscure)
+ text::VertOrientation::LINE_TOP // outside (obscure)
+ };
+
+ // Adjustment is horizontally relative to...
+ static const sal_Int16 aHoriRelOriTab[nCntRelTo] =
+ {
+ text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
+ text::RelOrientation::PAGE_FRAME, // 1 is page margin
+ text::RelOrientation::FRAME, // 2 is relative to column
+ text::RelOrientation::CHAR // 3 is relative to character
+ };
+
+ // Adjustment is vertically relative to...
+ // #i22673# - adjustment for new vertical alignment at top of line.
+ static const sal_Int16 aVertRelOriTab[nCntRelTo] =
+ {
+ text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin
+ text::RelOrientation::PAGE_FRAME, // 1 is page margin
+ text::RelOrientation::FRAME, // 2 is relative to paragraph
+ text::RelOrientation::TEXT_LINE // 3 is relative to line
+ };
+
+ // If the image is inline, then the relative orientation means nothing,
+ // so set it up so that if the user changes it into an anchor, it positions usefully.
+ sal_Int16 eHoriOri
+ = IsInlineEscherHack() ? text::HoriOrientation::CENTER : aHoriOriTab[ nXAlign ];
+ sal_Int16 eHoriRel
+ = IsInlineEscherHack() ? text::RelOrientation::FRAME : aHoriRelOriTab[nXRelTo];
+
+ // #i36649# - adjustments for certain alignments
+ if (eHoriOri == text::HoriOrientation::LEFT && eHoriRel == text::RelOrientation::PAGE_FRAME)
+ {
+ // convert 'left to page' to 'from left -<width> to page text area'
+ eHoriOri = text::HoriOrientation::NONE;
+ eHoriRel = text::RelOrientation::PAGE_PRINT_AREA;
+ const tools::Long nWidth = rFSPA.nXaRight - rFSPA.nXaLeft;
+ rFSPA.nXaLeft = -nWidth;
+ rFSPA.nXaRight = 0;
+ }
+ else if (eHoriOri == text::HoriOrientation::RIGHT && eHoriRel == text::RelOrientation::PAGE_FRAME)
+ {
+ // convert 'right to page' to 'from left 0 to right page border'
+ eHoriOri = text::HoriOrientation::NONE;
+ eHoriRel = text::RelOrientation::PAGE_RIGHT;
+ const tools::Long nWidth = rFSPA.nXaRight - rFSPA.nXaLeft;
+ rFSPA.nXaLeft = 0;
+ rFSPA.nXaRight = nWidth;
+ }
+
+ // #i24255# - position of floating screen objects in
+ // R2L layout are given in L2R layout, thus convert them of all
+ // floating screen objects, which are imported.
+ {
+ // Miserable miserable hack.
+ SwTwips nWidth = o3tl::saturating_sub(rFSPA.nXaRight, rFSPA.nXaLeft);
+ SwTwips nLeft = rFSPA.nXaLeft;
+ if (MiserableRTLGraphicsHack(nLeft, nWidth, eHoriOri,
+ eHoriRel))
+ {
+ rFSPA.nXaLeft = nLeft;
+ rFSPA.nXaRight = rFSPA.nXaLeft + nWidth;
+ }
+ }
+
+ // if the object is anchored inside a table cell, is horizontal aligned
+ // at frame|character and has wrap through, but its attribute
+ // 'layout in table cell' isn't set, convert its horizontal alignment to page text area.
+ // #i84783# - use new method <IsObjectLayoutInTableCell()>
+ if (m_nInTable &&
+ (eHoriRel == text::RelOrientation::FRAME || eHoriRel == text::RelOrientation::CHAR) &&
+ rFSPA.nwr == 3 &&
+ !IsObjectLayoutInTableCell(rRecord.nGroupShapeBooleanProperties))
+ {
+ eHoriRel = text::RelOrientation::PAGE_PRINT_AREA;
+ }
+
+ // Writer honours this wrap distance when aligned as "left" or "right",
+ // Word doesn't. Writer doesn't honour it when its "from left".
+ if (eHoriOri == text::HoriOrientation::LEFT)
+ rRecord.nDxWrapDistLeft = 0;
+ else if (eHoriOri == text::HoriOrientation::RIGHT)
+ rRecord.nDxWrapDistRight = 0;
+
+ sal_Int16 eVertRel;
+
+ eVertRel = aVertRelOriTab[ nYRelTo ]; // #i18732#
+ if (bCurSectionVertical && nYRelTo == 2)
+ eVertRel = text::RelOrientation::PAGE_PRINT_AREA;
+ // #i22673# - fill <eVertOri> in dependence of <eVertRel>
+ sal_Int16 eVertOri;
+ if (eVertRel == text::RelOrientation::TEXT_LINE)
+ {
+ eVertOri = aToLineVertOriTab[ nYAlign ];
+ }
+ else
+ {
+ eVertOri = aVertOriTab[ nYAlign ];
+ }
+
+ // Below line in word is a positive value, while in writer its
+ // negative
+ tools::Long nYPos = rFSPA.nYaTop;
+ // #i22673#
+ if ((eVertRel == text::RelOrientation::TEXT_LINE) && (eVertOri == text::VertOrientation::NONE))
+ nYPos = -nYPos;
+
+ SwFormatHoriOrient aHoriOri(MakeSafePositioningValue(bCurSectionVertical ? nYPos : rFSPA.nXaLeft),
+ bCurSectionVertical ? eVertOri : eHoriOri,
+ bCurSectionVertical ? eVertRel : eHoriRel);
+ if (4 <= nXAlign)
+ aHoriOri.SetPosToggle(true);
+ rFlySet.Put(aHoriOri);
+
+ rFlySet.Put(SwFormatVertOrient(MakeSafePositioningValue(!bCurSectionVertical ? nYPos : -rFSPA.nXaRight),
+ !bCurSectionVertical ? eVertOri : eHoriOri,
+ !bCurSectionVertical ? eVertRel : eHoriRel));
+
+ return eAnchor;
+}
+
+// #i84783#
+bool SwWW8ImplReader::IsObjectLayoutInTableCell(const sal_uInt32 nGroupShapeBooleanProperties) const
+{
+ bool bIsObjectLayoutInTableCell = false;
+
+ if ( m_bVer8 )
+ {
+ sal_uInt16 nWWVersion = m_xWwFib->m_nProduct & 0xE000;
+ if (nWWVersion == 0)
+ {
+ // 0 nProduct can happen for Word >97 as well, check cswNew in this case instead.
+ if (m_xWwFib->m_cswNew > 0)
+ {
+ // This is Word >=2000.
+ nWWVersion = 0x2000;
+ }
+ }
+
+ switch ( nWWVersion )
+ {
+ case 0x0000: // version 8 aka Microsoft Word 97
+ {
+ bIsObjectLayoutInTableCell = false;
+ OSL_ENSURE(nGroupShapeBooleanProperties == 0,
+ "no explicit object attribute layout in table cell expected." );
+ }
+ break;
+ case 0x2000: // version 9 aka Microsoft Word 2000
+ case 0x4000: // version 10 aka Microsoft Word 2002
+ case 0x6000: // version 11 aka Microsoft Word 2003
+ case 0x8000: // version 12 aka Microsoft Word 2007
+ case 0xC000: // version 14 aka Microsoft Word 2010
+ case 0xE000: // version 15 aka Microsoft Word 2013
+ {
+ // Documented in [MS-ODRAW], 2.3.4.44 "Group Shape Boolean Properties".
+ bool fUsefLayoutInCell = (nGroupShapeBooleanProperties & 0x80000000) >> 31;
+ bool fLayoutInCell = (nGroupShapeBooleanProperties & 0x8000) >> 15;
+ // If unspecified, defaults to true
+ bIsObjectLayoutInTableCell = !fUsefLayoutInCell || fLayoutInCell;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "unknown version." );
+ }
+ }
+ }
+
+ return bIsObjectLayoutInTableCell;
+}
+
+SwFrameFormat* SwWW8ImplReader::Read_GrafLayer( tools::Long nGrafAnchorCp )
+{
+ if( m_nIniFlags & WW8FL_NO_GRAFLAYER )
+ return nullptr;
+
+ ::SetProgressState(m_nProgress, m_pDocShell); // Update
+
+ m_nDrawCpO = 0;
+ m_bDrawCpOValid = m_xWwFib->GetBaseCp(m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX, &m_nDrawCpO);
+
+ GraphicCtor();
+
+ WW8PLCFspecial* pPF = m_xPlcxMan->GetFdoa();
+ if( !pPF )
+ {
+ OSL_ENSURE( false, "Where is the graphic (1) ?" );
+ return nullptr;
+ }
+
+ if( m_bVer67 )
+ {
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+
+ m_nDrawXOfs = m_nDrawYOfs = 0;
+ ReadGrafLayer1(*pPF, nGrafAnchorCp);
+
+ m_pStrm->Seek( nOldPos );
+ return nullptr;
+ }
+
+ // Normal case of Word 8+ version stuff
+ pPF->SeekPos( nGrafAnchorCp );
+
+ WW8_FC nStartFc;
+ void* pF0;
+ if (!pPF->Get(nStartFc, pF0))
+ {
+ OSL_ENSURE( false, "+Where is the graphic (2) ?" );
+ return nullptr;
+ }
+
+ WW8_FSPA_SHADOW& rFS = *static_cast<WW8_FSPA_SHADOW*>(pF0);
+ WW8_FSPA aFSFA;
+ WW8FSPAShadowToReal(rFS, aFSFA);
+ if (!aFSFA.nSpId)
+ {
+ OSL_ENSURE( false, "+Where is the graphic (3) ?" );
+ return nullptr;
+ }
+
+ if (!m_xMSDffManager->GetModel())
+ m_xMSDffManager->SetModel(m_pDrawModel, 1440);
+
+ tools::Rectangle aRect(aFSFA.nXaLeft, aFSFA.nYaTop, aFSFA.nXaRight, aFSFA.nYaBottom);
+ SvxMSDffImportData aData( aRect );
+
+ rtl::Reference<SdrObject> pObject;
+ bool bOk = (m_xMSDffManager->GetShape(aFSFA.nSpId, pObject, aData) && pObject);
+
+ if (!bOk)
+ {
+ OSL_ENSURE( false, "Where is the Shape ?" );
+ return nullptr;
+ }
+
+ // tdf#118375 Word relates position to the unrotated rectangle,
+ // Writer uses the rotated one.
+ if (pObject->GetRotateAngle())
+ {
+ tools::Rectangle aObjSnapRect(pObject->GetSnapRect()); // recalculates the SnapRect
+ aFSFA.nXaLeft = aObjSnapRect.Left();
+ aFSFA.nYaTop = aObjSnapRect.Top();
+ aFSFA.nXaRight = aObjSnapRect.Right();
+ aFSFA.nYaBottom = aObjSnapRect.Bottom();
+ }
+
+ bool bDone = false;
+ rtl::Reference<SdrObject> pOurNewObject;
+ bool bReplaceable = false;
+
+ switch (pObject->GetObjIdentifier())
+ {
+ case SdrObjKind::Graphic:
+ bReplaceable = true;
+ bDone = true;
+ break;
+ case SdrObjKind::OLE2:
+ bReplaceable = true;
+ break;
+ default:
+ break;
+
+ }
+
+ // when in a header or footer word appears to treat all elements as wrap through
+
+ // determine wrapping mode
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END> aFlySet(m_rDoc.GetAttrPool());
+ Reader::ResetFrameFormatAttrs(aFlySet); // tdf#122425: Explicitly remove borders and spacing
+ css::text::WrapTextMode eSurround = css::text::WrapTextMode_PARALLEL;
+ bool bContour = false;
+ switch (aFSFA.nwr)
+ {
+ case 0: // 0 like 2, but doesn't require absolute object
+ case 2: // 2 wrap around absolute object
+ eSurround = css::text::WrapTextMode_PARALLEL;
+ break;
+ case 1: // 1 no text next to shape
+ eSurround = css::text::WrapTextMode_NONE;
+ break;
+ case 3: // 3 wrap as if no object present
+ // Special case: on export, inline images are wrapped through as a hack for old formats.
+ // That is irrelevant for Writer, so instead use the default wrap in that case,
+ // so that when the user changes it into an anchor, it wraps nicely, and not through.
+ if (!IsInlineEscherHack())
+ eSurround = css::text::WrapTextMode_THROUGH;
+ break;
+ case 4: // 4 wrap tightly around object
+ case 5: // 5 wrap tightly, but allow holes
+ eSurround = css::text::WrapTextMode_PARALLEL;
+ bContour = true;
+ break;
+ }
+
+ // if mode 2 or 4 also regard the additional parameters
+ if ((2 == aFSFA.nwr) || (4 == aFSFA.nwr))
+ {
+ switch (aFSFA.nwrk)
+ {
+ // 0 wrap both sides
+ case 0:
+ eSurround = css::text::WrapTextMode_PARALLEL;
+ break;
+ // 1 wrap only on left
+ case 1:
+ eSurround = css::text::WrapTextMode_LEFT;
+ break;
+ // 2 wrap only on right
+ case 2:
+ eSurround = css::text::WrapTextMode_RIGHT;
+ break;
+ // 3 wrap only on largest side
+ case 3:
+ eSurround = css::text::WrapTextMode_DYNAMIC;
+ break;
+ }
+ }
+
+ SwFormatSurround aSur( eSurround );
+ aSur.SetContour( bContour );
+ aSur.SetOutside(true); // Winword can only do outside contours
+ aFlySet.Put( aSur );
+
+ // now position imported object correctly and so on (can be a whole group)
+
+ OSL_ENSURE(!((aData.size() != 1) && bReplaceable),
+ "Replaceable drawing with > 1 entries ?");
+
+ if (aData.size() != 1)
+ bReplaceable = false;
+
+ /*
+ Get the record for top level object, so we can get the word anchoring
+ and wrapping information for it.
+ */
+ SvxMSDffImportRec* pRecord = aData.find(pObject.get());
+ OSL_ENSURE(pRecord, "how did that happen?");
+ if (!pRecord)
+ {
+ // remove old object from the Z-Order list
+ m_xMSDffManager->RemoveFromShapeOrder(pObject.get());
+ return nullptr;
+ }
+ const bool bLayoutInTableCell =
+ m_nInTable && IsObjectLayoutInTableCell(pRecord->nGroupShapeBooleanProperties);
+
+ // #i18732# - Switch on 'follow text flow', if object is laid out
+ // inside table cell
+ if (bLayoutInTableCell)
+ {
+ SwFormatFollowTextFlow aFollowTextFlow( true );
+ aFlySet.Put( aFollowTextFlow );
+ }
+
+ // #i21847#
+ // Some shapes are set to *hidden*, don't import those ones.
+ if (pRecord->bHidden)
+ {
+ // remove old object from the Z-Order list
+ m_xMSDffManager->RemoveFromShapeOrder(pObject.get());
+ return nullptr;
+ }
+
+ sal_uInt16 nCount = pObject->GetUserDataCount();
+ if(nCount)
+ {
+ OUString lnName, aObjName, aTarFrame;
+ for (sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ SdrObjUserData* pData = pObject->GetUserData( i );
+ if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
+ && pData->GetId() == SW_UD_IMAPDATA)
+ {
+ SwMacroInfo* macInf = dynamic_cast<SwMacroInfo*>(pData);
+ if (macInf && macInf->GetShapeId() == aFSFA.nSpId)
+ {
+ lnName = macInf->GetHlink();
+ aObjName = macInf->GetName();
+ aTarFrame = macInf->GetTarFrame();
+ break;
+ }
+ }
+ }
+ std::unique_ptr<SwFormatURL> pFormatURL(new SwFormatURL());
+ pFormatURL->SetURL( lnName, false );
+ if (!aObjName.isEmpty())
+ pFormatURL->SetName(aObjName);
+ if (!aTarFrame.isEmpty())
+ pFormatURL->SetTargetFrameName(aTarFrame);
+ pFormatURL->SetMap(nullptr);
+ aFlySet.Put(std::move(pFormatURL));
+ }
+
+ // If we are to be "below text" then we are not to be opaque
+ // #i14045# MM If we are in a header or footer then make the object transparent
+ // Not exactly like word but close enough for now
+
+ // both flags <bBelowText> and <bDrawHell> have to be set to move object into the background.
+ // #i46794# - it reveals that value of flag <bBelowText> can be neglected.
+ const bool bMoveToBackground = pRecord->bDrawHell ||
+ ((m_bIsHeader || m_bIsFooter) && aFSFA.nwr == 3);
+ if ( bMoveToBackground )
+ aFlySet.Put(SvxOpaqueItem(RES_OPAQUE,false));
+
+ OUString aObjName = pObject->GetName();
+
+ bool bDrawObj = false;
+ bool bFrame = false;
+
+ SwFrameFormat* pRetFrameFormat = nullptr;
+ if (bReplaceable)
+ {
+ // Single graphics or ole objects
+ pRetFrameFormat = ImportReplaceableDrawables(pObject, pOurNewObject, *pRecord, aFSFA, aFlySet);
+ }
+ else
+ {
+ bDrawObj = true;
+
+ // Drawing objects, (e.g. ovals or drawing groups)
+ if (aFSFA.bRcaSimple)
+ {
+ aFSFA.nbx = WW8_FSPA::RelPageBorder;
+ aFSFA.nby = WW8_FSPA::RelPageBorder;
+ }
+
+ RndStdIds eAnchor = ProcessEscherAlign(*pRecord, aFSFA, aFlySet);
+
+ // Should we, and is it possible to make this into a writer textbox
+ if ((!(m_nIniFlags1 & WW8FL_NO_FLY_FOR_TXBX)) && pRecord->bReplaceByFly)
+ {
+ pRetFrameFormat
+ = ConvertDrawTextToFly(pObject, pOurNewObject, *pRecord, eAnchor, aFSFA, aFlySet);
+ if (pRetFrameFormat)
+ {
+ bDone = true;
+ bDrawObj = false;
+ bFrame = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ sw::util::SetLayer aSetLayer(m_rDoc);
+ if ( bMoveToBackground )
+ aSetLayer.SendObjectToHell(*pObject);
+ else
+ aSetLayer.SendObjectToHeaven(*pObject);
+
+ if (!IsInlineEscherHack())
+ {
+ /* Need to make sure that the correct layer ordering is applied. */
+ // pass information, if object is in page header|footer to method.
+ m_xWWZOrder->InsertEscherObject(pObject.get(), aFSFA.nSpId, pRecord->bDrawHell,
+ m_bIsHeader || m_bIsFooter);
+ }
+ else
+ {
+ m_xWWZOrder->InsertTextLayerObject(pObject.get());
+ }
+
+ pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pObject, aFlySet );
+
+ OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() ==
+ eAnchor, "Not the anchor type requested!");
+
+ /*
+ Insert text if necessary into textboxes contained in groups.
+ */
+ for (const auto& it : aData)
+ {
+ pRecord = it.get();
+ if (pRecord->pObj && pRecord->aTextId.nTxBxS)
+ { // #i52825# pRetFrameFormat can be NULL
+ pRetFrameFormat = MungeTextIntoDrawBox(
+ *pRecord, nGrafAnchorCp, pRetFrameFormat);
+ }
+ }
+ }
+ }
+
+ SwDrawFrameFormat* pDrawFrameFormat = dynamic_cast<SwDrawFrameFormat*>(pRetFrameFormat);
+ // #i44344#, #i44681# - positioning attributes already set
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ if (!IsInlineEscherHack() && pRetFrameFormat)
+ MapWrapIntoFlyFormat(*pRecord, *pRetFrameFormat);
+
+ // Set frame name with object name
+ if (pRetFrameFormat /*#i52825# */)
+ {
+ if (!aObjName.isEmpty())
+ pRetFrameFormat->SetFormatName( aObjName );
+ if (pRetFrameFormat->GetName().isEmpty())
+ {
+ if (bDrawObj)
+ pRetFrameFormat->SetFormatName(m_rDoc.GetUniqueDrawObjectName());
+ else if (bFrame)
+ pRetFrameFormat->SetFormatName(m_rDoc.GetUniqueFrameName());
+ }
+ }
+ return AddAutoAnchor(pRetFrameFormat);
+}
+
+SwFrameFormat *SwWW8ImplReader::AddAutoAnchor(SwFrameFormat *pFormat)
+{
+ /*
+ * anchored to character at the current position will move along the
+ * paragraph as text is added because we are at the insertion point.
+ *
+ * Leave to later and set the correct location then.
+ */
+ if (pFormat && (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR))
+ {
+ m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), pFormat);
+ }
+ return pFormat;
+}
+
+SwFrameFormat* SwWW8ImplReader::MungeTextIntoDrawBox(SvxMSDffImportRec& rRecord,
+ tools::Long nGrafAnchorCp, SwFrameFormat* pRetFrameFormat)
+{
+ rtl::Reference<SdrObject> pTrueObject = rRecord.pObj;
+
+ rtl::Reference<SdrTextObj> pSdrTextObj;
+
+ // check for group object (e.g. two parentheses)
+ if (SdrObjGroup* pThisGroup = dynamic_cast<SdrObjGroup*>(rRecord.pObj.get()))
+ {
+ // Group objects don't have text. Insert a text object into
+ // the group for holding the text.
+ pSdrTextObj = new SdrRectObj(
+ *m_pDrawModel,
+ SdrObjKind::Text,
+ pThisGroup->GetCurrentBoundRect());
+
+ SfxItemSet aSet(m_pDrawModel->GetItemPool());
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aSet.Put(SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ));
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ aSet.Put(makeSdrTextAutoGrowWidthItem(false));
+ pSdrTextObj->SetMergedItemSet(aSet);
+ pSdrTextObj->NbcSetLayer( pThisGroup->GetLayer() );
+ pThisGroup->GetSubList()->NbcInsertObject(pSdrTextObj.get());
+ }
+ else
+ pSdrTextObj = DynCastSdrTextObj(rRecord.pObj.get());
+
+ if( pSdrTextObj )
+ {
+ Size aObjSize(pSdrTextObj->GetSnapRect().GetWidth(),
+ pSdrTextObj->GetSnapRect().GetHeight());
+
+ // Object is part of a group?
+ SdrObject* pGroupObject = pSdrTextObj->getParentSdrObjectFromSdrObject();
+
+ const size_t nOrdNum = pSdrTextObj->GetOrdNum();
+ bool bEraseThisObject;
+ InsertTxbxText(pSdrTextObj.get(), &aObjSize, rRecord.aTextId.nTxBxS, rRecord.aTextId.nSequence,
+ nGrafAnchorCp, pRetFrameFormat,
+ (pSdrTextObj.get() != pTrueObject.get()) || (nullptr != pGroupObject), bEraseThisObject,
+ nullptr, nullptr, nullptr, nullptr, &rRecord);
+
+ // was this object replaced ??
+ if (bEraseThisObject)
+ {
+ if( pGroupObject || (pSdrTextObj.get() != pTrueObject.get()) )
+ {
+ // Object is already replaced by a new SdrGrafObj (in the group
+ // and) the Drawing-Page.
+
+ SdrObject* pNewObj = pGroupObject ?
+ pGroupObject->GetSubList()->GetObj(nOrdNum) : pTrueObject.get();
+ if (pSdrTextObj.get() != pNewObj)
+ {
+ // Replace object in the Z-Order-List
+ m_xMSDffManager->ExchangeInShapeOrder(pSdrTextObj.get(), 0, pNewObj);
+ // and save the new object.
+ rRecord.pObj = pNewObj;
+ }
+ }
+ else
+ {
+ // remove the object from Z-Order list
+ m_xMSDffManager->RemoveFromShapeOrder( pSdrTextObj.get() );
+ // take the object from the drawing page
+ if( pSdrTextObj->getSdrPageFromSdrObject() )
+ m_pDrawPg->RemoveObject( pSdrTextObj->GetOrdNum() );
+ // and delete FrameFormat, because replaced by graphic
+ // (this also deletes the object)
+ m_rDoc.DelFrameFormat( pRetFrameFormat );
+ pRetFrameFormat = nullptr;
+ // also delete the object record
+ rRecord.pObj = nullptr;
+ }
+ }
+ else
+ {
+ // use ww8-default border distance
+ SfxItemSetFixed<SDRATTR_TEXT_LEFTDIST, SDRATTR_TEXT_LOWERDIST>
+ aItemSet(m_pDrawModel->GetItemPool());
+ aItemSet.Put(makeSdrTextLeftDistItem(rRecord.nDxTextLeft));
+ aItemSet.Put(makeSdrTextRightDistItem(rRecord.nDxTextRight));
+ aItemSet.Put(makeSdrTextUpperDistItem(rRecord.nDyTextTop));
+ aItemSet.Put(makeSdrTextLowerDistItem(rRecord.nDyTextBottom));
+ pSdrTextObj->SetMergedItemSetAndBroadcast(aItemSet);
+ }
+ }
+ return pRetFrameFormat;
+}
+
+SwFlyFrameFormat* SwWW8ImplReader::ConvertDrawTextToFly(rtl::Reference<SdrObject>& rpObject,
+ rtl::Reference<SdrObject>& rpOurNewObject,
+ const SvxMSDffImportRec& rRecord,
+ RndStdIds eAnchor, const WW8_FSPA& rF,
+ SfxItemSet &rFlySet)
+{
+ SwFlyFrameFormat* pRetFrameFormat = nullptr;
+ tools::Long nStartCp;
+ tools::Long nEndCp;
+
+ // Check if this textbox chain contains text as conversion of an empty
+ // chain would not make sense.
+ if (TxbxChainContainsRealText(rRecord.aTextId.nTxBxS, nStartCp, nEndCp))
+ {
+ // The Text is not read into SdrTextObj! Rather insert a frame and
+ // insert the text from nStartCp to nEndCp.
+
+ // More attributes can be used in a frame compared to the
+ // Edit-Engine, and it can contain field, OLEs or graphics...
+ tools::Rectangle aInnerDist(rRecord.nDxTextLeft, rRecord.nDyTextTop, rRecord.nDxTextRight,
+ rRecord.nDyTextBottom);
+
+ SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, rF.nXaRight - rF.nXaLeft,
+ rF.nYaBottom - rF.nYaTop);
+ aFrameSize.SetWidthSizeType(rRecord.bAutoWidth ? SwFrameSize::Variable
+ : SwFrameSize::Fixed);
+ rFlySet.Put(aFrameSize);
+
+ MatchSdrItemsIntoFlySet(rpObject.get(), rFlySet, rRecord.eLineStyle, rRecord.eLineDashing,
+ rRecord.eShapeType, aInnerDist);
+
+ SdrTextObj *pSdrTextObj = DynCastSdrTextObj(rpObject.get());
+ if (pSdrTextObj && pSdrTextObj->IsVerticalWriting())
+ rFlySet.Put(SvxFrameDirectionItem(SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR));
+
+ pRetFrameFormat = m_rDoc.MakeFlySection(eAnchor, m_pPaM->GetPoint(), &rFlySet);
+ OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() == eAnchor,
+ "Not the anchor type requested!");
+
+ // if everything is OK, find pointer on new object and correct
+ // Z-order list (or delete entry)
+ rpOurNewObject = CreateContactObject(pRetFrameFormat);
+
+ // remove old object from the Z-Order list
+ m_xMSDffManager->RemoveFromShapeOrder( rpObject.get() );
+
+ // and delete the object
+ rpObject.clear();
+ /*
+ NB: only query pOrgShapeObject starting here!
+ */
+
+ if (rpOurNewObject)
+ {
+ /*
+ We do not store our rpOutNewObject in the ShapeOrder because we
+ have a FrameFormat from which we can regenerate the contact object when
+ we need it. Because, we can have frames anchored to paragraphs in
+ header/footers and we can copy header/footers, if we do copy a
+ header/footer with a nonpage anchored frame in it then the contact
+ objects are invalidated. Under this condition the FrameFormat will be
+ updated to reflect this change and can be used to get a new
+ contact object, while a raw rpOutNewObject stored here becomes
+ deleted and useless.
+ */
+ m_xMSDffManager->StoreShapeOrder(rF.nSpId,
+ (static_cast<sal_uLong>(rRecord.aTextId.nTxBxS) << 16) +
+ rRecord.aTextId.nSequence, nullptr, pRetFrameFormat);
+
+ // The Contact object has to be inserted into the draw page, so
+ // SwWW8ImplReader::LoadDoc1() can determine the z-order.
+ if (!rpOurNewObject->IsInserted())
+ {
+ // pass information, if object is in page header|footer to method.
+ m_xWWZOrder->InsertEscherObject(rpOurNewObject.get(), rF.nSpId, rRecord.bDrawHell,
+ m_bIsHeader || m_bIsFooter);
+ }
+ }
+
+ // Box-0 receives the text for the whole chain!
+ if (!rRecord.aTextId.nSequence)
+ {
+ // save flags etc and reset them
+ WW8ReaderSave aSave( this );
+
+ MoveInsideFly(pRetFrameFormat);
+
+ m_xWWZOrder->InsideEscher(rF.nSpId);
+
+ // read in the text
+ m_bTxbxFlySection = true;
+ bool bJoined = ReadText(nStartCp, (nEndCp-nStartCp),
+ MAN_MAINTEXT == m_xPlcxMan->GetManType() ?
+ MAN_TXBX : MAN_TXBX_HDFT);
+
+ m_xWWZOrder->OutsideEscher();
+
+ MoveOutsideFly(pRetFrameFormat, aSave.GetStartPos(),!bJoined);
+
+ aSave.Restore( this );
+
+ StripNegativeAfterIndent(pRetFrameFormat);
+ }
+
+ }
+ return pRetFrameFormat;
+}
+
+void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec &rRecord, SfxItemSet &rFlySet)
+{
+ if (rRecord.bVFlip || rRecord.bHFlip)
+ {
+ MirrorGraph eType(MirrorGraph::Dont);
+ if (rRecord.bVFlip && rRecord.bHFlip)
+ eType = MirrorGraph::Both;
+ else if (rRecord.bVFlip)
+ eType = MirrorGraph::Horizontal;
+ else
+ eType = MirrorGraph::Vertical;
+ rFlySet.Put( SwMirrorGrf(eType) );
+ }
+}
+
+SwFlyFrameFormat* SwWW8ImplReader::ImportReplaceableDrawables(rtl::Reference<SdrObject> &rpObject,
+ rtl::Reference<SdrObject> &rpOurNewObject,
+ SvxMSDffImportRec& rRecord,
+ WW8_FSPA& rF,
+ SfxItemSet &rFlySet )
+{
+ SwFlyFrameFormat* pRetFrameFormat = nullptr;
+ sal_Int32 nWidthTw = o3tl::saturating_sub(rF.nXaRight, rF.nXaLeft);
+ if (0 > nWidthTw)
+ nWidthTw = 0;
+ sal_Int32 nHeightTw = o3tl::saturating_sub(rF.nYaBottom, rF.nYaTop);
+ if (0 > nHeightTw)
+ nHeightTw = 0;
+
+ ProcessEscherAlign(rRecord, rF, rFlySet);
+
+ rFlySet.Put(SwFormatFrameSize(SwFrameSize::Fixed, nWidthTw, nHeightTw));
+
+ SfxItemSetFixed<RES_GRFATR_BEGIN, RES_GRFATR_END-1> aGrSet(m_rDoc.GetAttrPool());
+
+ // Note that the escher inner distance only seems to be honoured in
+ // word for textboxes, not for graphics and ole objects.
+ tools::Rectangle aInnerDist(0, 0, 0, 0);
+
+ MatchSdrItemsIntoFlySet(rpObject.get(), rFlySet, rRecord.eLineStyle, rRecord.eLineDashing,
+ rRecord.eShapeType, aInnerDist);
+
+ MatchEscherMirrorIntoFlySet(rRecord, aGrSet);
+
+ OUString aObjectName(rpObject->GetName());
+ if (SdrObjKind::OLE2 == rpObject->GetObjIdentifier())
+ pRetFrameFormat = InsertOle(*static_cast<SdrOle2Obj*>(rpObject.get()), rFlySet, &aGrSet);
+ else
+ {
+ const SdrGrafObj *pGrf = static_cast<const SdrGrafObj*>(rpObject.get());
+ bool bDone = false;
+ if (pGrf->IsLinkedGraphic() && !pGrf->GetFileName().isEmpty())
+ {
+ GraphicType eType = pGrf->GetGraphicType();
+ OUString aGrfName(
+ URIHelper::SmartRel2Abs(
+ INetURLObject(m_sBaseURL), pGrf->GetFileName(),
+ URIHelper::GetMaybeFileHdl()));
+ // correction of fix for issue #i10939#:
+ // One of the two conditions have to be true to insert the graphic
+ // as a linked graphic -
+ if (GraphicType::NONE == eType || CanUseRemoteLink(aGrfName))
+ {
+ pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
+ *m_pPaM, aGrfName, OUString(), nullptr,
+ &rFlySet, &aGrSet, nullptr);
+ bDone = true;
+ }
+ }
+ if (!bDone)
+ {
+ const Graphic& rGraph = pGrf->GetGraphic();
+ pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
+ *m_pPaM, OUString(), OUString(), &rGraph,
+ &rFlySet, &aGrSet, nullptr);
+ }
+ }
+
+ if (pRetFrameFormat)
+ {
+ if (SdrObjKind::OLE2 != rpObject->GetObjIdentifier())
+ SetAttributesAtGrfNode(rRecord, *pRetFrameFormat, &rF);
+ // avoid multiple occurrences of the same graphic name
+ m_aGrfNameGenerator.SetUniqueGraphName(pRetFrameFormat, aObjectName);
+ }
+ // if everything is OK, determine pointer to new object and correct
+ // Z-Order-List accordingly (or delete entry)
+ rpOurNewObject = CreateContactObject(pRetFrameFormat);
+
+ // remove old object from Z-Order-List
+ m_xMSDffManager->RemoveFromShapeOrder( rpObject.get() );
+ // remove from Drawing-Page
+ if( rpObject->getSdrPageFromSdrObject() )
+ m_pDrawPg->RemoveObject( rpObject->GetOrdNum() );
+
+ // and delete the object
+ rpObject.clear();
+
+ /*
+ Warning: from now on query only pOrgShapeObject!
+ */
+
+ // add Contact-Object to the Z-Order-List and the page
+ if (rpOurNewObject)
+ {
+ if (!m_bHdFtFootnoteEdn)
+ m_xMSDffManager->StoreShapeOrder(rF.nSpId, 0, rpOurNewObject.get());
+
+ // The Contact-Object MUST be set in the Draw-Page, so that in
+ // SwWW8ImplReader::LoadDoc1() the Z-Order can be defined !!!
+ if (!rpOurNewObject->IsInserted())
+ {
+ // pass information, if object is in page header|footer to method.
+ m_xWWZOrder->InsertEscherObject(rpOurNewObject.get(), rF.nSpId, rRecord.bDrawHell,
+ m_bIsHeader || m_bIsFooter);
+ }
+ }
+ return pRetFrameFormat;
+}
+
+void SwWW8ImplReader::GraphicCtor() // For SVDraw and VCControls and Escher
+{
+ if (m_pDrawModel)
+ return;
+
+ m_rDoc.getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed
+ m_pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ OSL_ENSURE(m_pDrawModel, "Cannot create DrawModel");
+ m_pDrawPg = m_pDrawModel->GetPage(0);
+
+ m_xMSDffManager.reset(new SwMSDffManager(*this, m_bSkipImages));
+ m_xMSDffManager->SetModel(m_pDrawModel, 1440);
+ /*
+ Now the dff manager always needs a controls converter as well, but a
+ control converter may still exist without a dffmanager.
+ */
+ m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM));
+
+ m_xWWZOrder.reset(new wwZOrderer(sw::util::SetLayer(m_rDoc), m_pDrawPg,
+ m_xMSDffManager->GetShapeOrders()));
+}
+
+void SwWW8ImplReader::GraphicDtor()
+{
+ m_pDrawEditEngine.reset(); // maybe created by graphic
+ m_xWWZOrder.reset(); // same
+}
+
+void SwWW8FltAnchorStack::AddAnchor(const SwPosition& rPos, SwFrameFormat *pFormat)
+{
+ OSL_ENSURE(pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR,
+ "Don't use fltanchors with inline frames, slap!");
+ NewAttr(rPos, SwFltAnchor(pFormat));
+}
+
+void SwWW8FltAnchorStack::Flush()
+{
+ size_t nCnt = size();
+ for (size_t i=0; i < nCnt; ++i)
+ {
+ SwFltStackEntry &rEntry = (*this)[i];
+ SwPosition aDummy(rEntry.m_aMkPos.m_nNode);
+ SetAttrInDoc(aDummy, rEntry);
+ DeleteAndDestroy(i--);
+ --nCnt;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8graf.hxx b/sw/source/filter/ww8/ww8graf.hxx
new file mode 100644
index 0000000000..92f302545f
--- /dev/null
+++ b/sw/source/filter/ww8/ww8graf.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8GRAF_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8GRAF_HXX
+
+#include <vector>
+#include <stack>
+
+#include <filter/msfilter/msdffimp.hxx>
+#include <svx/svdpage.hxx>
+
+#include "writerhelper.hxx"
+#include "ww8struc.hxx"
+
+struct EscherShape
+{
+ sal_uLong mnEscherShapeOrder;
+ sal_uLong mnNoInlines;
+ bool mbInHellLayer;
+ bool mbInHeaderFooter;
+ EscherShape( sal_uLong nEscherShapeOrder,
+ bool bInHellLayer,
+ bool _bInHeaderFooter )
+ : mnEscherShapeOrder(nEscherShapeOrder),
+ mnNoInlines(0),
+ mbInHellLayer(bInHellLayer),
+ mbInHeaderFooter( _bInHeaderFooter )
+ {}
+};
+
+class wwZOrderer
+{
+private:
+ // consider that objects in page header/footer
+ // are always behind objects in page body. Thus, assure, that in vector
+ // <maEscherLayer> objects in page header|footer are inserted before
+ // objects in page body - see method <GetEscherObjectPos(..)>.
+ //No of objects in doc before starting (always 0 unless using file->insert
+ //and probably 0 then as well
+ std::vector<EscherShape> maEscherLayer;
+ typedef std::vector<EscherShape>::iterator myeiter;
+
+ std::vector<short> maDrawHeight;
+
+ std::stack<sal_uInt16> maIndexes;
+
+ sw::util::SetLayer maSetLayer;
+
+ sal_uLong mnNoInitialObjects;
+ sal_uLong mnInlines;
+ SdrPage* mpDrawPg;
+ const SvxMSDffShapeOrders *mpShapeOrders;
+
+ sal_uInt16 GetEscherObjectIdx(sal_uLong nSpId);
+ myeiter MapEscherIdxToIter(sal_uLong nIdx);
+ // new parameter <_bInHeaderFooter>, indicating
+ // that object is in header or footer
+ sal_uLong GetEscherObjectPos( sal_uLong nSpId,
+ const bool bInHellLayer,
+ const bool _bInHeaderFooter );
+ sal_uLong GetDrawingObjectPos(short nWwHeight);
+ void InsertObject(SdrObject *pObject, sal_uLong nPos);
+public:
+ wwZOrderer(const sw::util::SetLayer &rSetLayer, SdrPage* pDrawPg,
+ const SvxMSDffShapeOrders *pShapeOrders);
+ void InsertTextLayerObject(SdrObject* pObject);
+ /*
+ We should have separate ZOrder classes for 95- and 97+ and
+ instantiate the appropriate one at run time.
+ */
+ void InsertDrawingObject(SdrObject* pObj, short nWwHeight);
+ // new parameter <_bInHeaderFooter>, indicating that object is in header or footer
+ void InsertEscherObject( SdrObject* pObject,
+ sal_uLong nSpId,
+ const bool bInHellLayer,
+ const bool _bInHeaderFooter );
+ void InsideEscher(sal_uLong nIndex);
+ void OutsideEscher();
+};
+
+void WW8FSPAShadowToReal(const WW8_FSPA_SHADOW& rFSPAS, WW8_FSPA& rPic);
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8graf2.cxx b/sw/source/filter/ww8/ww8graf2.cxx
new file mode 100644
index 0000000000..35219922db
--- /dev/null
+++ b/sw/source/filter/ww8/ww8graf2.cxx
@@ -0,0 +1,781 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <iterator>
+#include <numeric>
+#include <hintids.hxx>
+#include <svl/urihelper.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include <grfatr.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <mdiexp.hxx>
+#include "writerwordglue.hxx"
+#include "ww8struc.hxx"
+#include "ww8scan.hxx"
+#include "ww8par.hxx"
+#include "ww8par2.hxx"
+#include "ww8graf.hxx"
+#include <vcl/gdimtf.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/wmf.hxx>
+
+using namespace ::com::sun::star;
+using namespace sw::types;
+
+wwZOrderer::wwZOrderer(const sw::util::SetLayer &rSetLayer, SdrPage* pDrawPg,
+ const SvxMSDffShapeOrders *pShapeOrders)
+ : maSetLayer(rSetLayer), mnInlines(0), mpDrawPg(pDrawPg),
+ mpShapeOrders(pShapeOrders)
+{
+ mnNoInitialObjects = mpDrawPg->GetObjCount();
+ OSL_ENSURE(mpDrawPg,"Missing draw page impossible!");
+}
+
+void wwZOrderer::InsideEscher(sal_uLong nSpId)
+{
+ maIndexes.push(GetEscherObjectIdx(nSpId));
+}
+
+void wwZOrderer::OutsideEscher()
+{
+ maIndexes.pop();
+}
+
+// consider new parameter <_bInHeaderFooter>
+void wwZOrderer::InsertEscherObject( SdrObject* pObject,
+ sal_uLong nSpId,
+ const bool bInHellLayer,
+ const bool _bInHeaderFooter )
+{
+ sal_uLong nInsertPos = GetEscherObjectPos( nSpId, bInHellLayer, _bInHeaderFooter );
+ InsertObject(pObject, nInsertPos + mnNoInitialObjects + mnInlines);
+}
+
+wwZOrderer::myeiter wwZOrderer::MapEscherIdxToIter(sal_uLong nIdx)
+{
+ return std::find_if(maEscherLayer.begin(), maEscherLayer.end(),
+ [nIdx](const EscherShape& rShape) { return rShape.mnEscherShapeOrder == nIdx; });
+}
+
+sal_uInt16 wwZOrderer::GetEscherObjectIdx(sal_uLong nSpId)
+{
+ sal_uInt16 nFound=0;
+ sal_uInt16 nShapeCount = mpShapeOrders ? mpShapeOrders->size() : 0;
+ // First, find out what position this shape is in the Escher order.
+ for (sal_uInt16 nShapePos=0; nShapePos < nShapeCount; nShapePos++)
+ {
+ const SvxMSDffShapeOrder& rOrder = *(*mpShapeOrders)[nShapePos];
+ if (rOrder.nShapeId == nSpId)
+ {
+ nFound = nShapePos;
+ break;
+ }
+ }
+ return nFound;
+}
+
+// consider new parameter <_bInHeaderFooter>
+sal_uLong wwZOrderer::GetEscherObjectPos( sal_uLong nSpId,
+ const bool bInHellLayer,
+ const bool _bInHeaderFooter )
+{
+ /*
+ EscherObjects have their own ordering which needs to be matched to
+ the actual ordering that should be used when inserting them into the
+ document.
+ */
+ sal_uInt16 nFound = GetEscherObjectIdx(nSpId);
+ // Match the ordering position from the ShapeOrders to the ordering of all
+ // objects in the document, there is a complexity when escherobjects
+ // contain inlines objects, we need to consider those as part of the
+ // escher count
+ sal_uLong nRet=0;
+ myeiter aIter = maEscherLayer.begin();
+ myeiter aEnd = maEscherLayer.end();
+ // skip objects in page header|footer,
+ // if current object isn't in page header|footer
+ if ( !_bInHeaderFooter )
+ {
+ while ( aIter != aEnd )
+ {
+ if ( !aIter->mbInHeaderFooter )
+ {
+ break;
+ }
+ nRet += aIter->mnNoInlines + 1;
+ ++aIter;
+ }
+ }
+ if (!bInHellLayer)
+ {
+ while (aIter != aEnd)
+ {
+ if (!aIter->mbInHellLayer || (_bInHeaderFooter && !aIter->mbInHeaderFooter))
+ break;
+ nRet += aIter->mnNoInlines + 1;
+ ++aIter;
+ }
+ }
+ while (aIter != aEnd)
+ {
+ // insert object in page header|footer
+ // before objects in page body
+ if ( _bInHeaderFooter && !aIter->mbInHeaderFooter )
+ {
+ break;
+ }
+ if ( bInHellLayer && !aIter->mbInHellLayer )
+ break;
+
+ if ( aIter->mnEscherShapeOrder > nFound )
+ break;
+ nRet += aIter->mnNoInlines+1;
+ ++aIter;
+ }
+ maEscherLayer.insert(aIter, EscherShape( nFound, bInHellLayer, _bInHeaderFooter ) );
+ return nRet;
+}
+
+// InsertObj() adds the object into the Sw-Page and memorize the Z-position
+// in a VarArr
+void wwZOrderer::InsertDrawingObject(SdrObject* pObj, short nWwHeight)
+{
+ sal_uLong nPos = GetDrawingObjectPos(nWwHeight);
+ if (nWwHeight & 0x2000) // Heaven ?
+ maSetLayer.SendObjectToHeaven(*pObj);
+ else
+ maSetLayer.SendObjectToHell(*pObj);
+
+ InsertObject(pObj, nPos + mnNoInitialObjects + mnInlines);
+}
+
+void wwZOrderer::InsertTextLayerObject(SdrObject* pObject)
+{
+ maSetLayer.SendObjectToHeaven(*pObject);
+ if (maIndexes.empty())
+ {
+ InsertObject(pObject, mnNoInitialObjects + mnInlines);
+ ++mnInlines;
+ }
+ else
+ {
+ //If we are inside an escher objects, place us just after that
+ //escher obj, and increment its inline count
+ sal_uInt16 nIdx = maIndexes.top();
+ myeiter aEnd = MapEscherIdxToIter(nIdx);
+
+ sal_uLong nInsertPos = std::accumulate(maEscherLayer.begin(), aEnd, sal_uLong(0),
+ [](const sal_uLong nPos, const EscherShape& rShape) { return nPos + rShape.mnNoInlines + 1; });
+
+ OSL_ENSURE(aEnd != maEscherLayer.end(), "Something very wrong here");
+ if (aEnd != maEscherLayer.end())
+ {
+ aEnd->mnNoInlines++;
+ nInsertPos += aEnd->mnNoInlines;
+ }
+
+ InsertObject(pObject, mnNoInitialObjects + mnInlines + nInsertPos);
+ }
+}
+
+/* Parallel to the Obj-array in the document I also build an array which
+ * contains the Ww-height (-> what covers what).
+ * Based on this VARARR the position where the insertion happens is
+ * determined.
+ * When inserting the offset in an existing document with a graphic layer the
+ * caller has to increment the index by mnNoInitialObjects, so that the new
+ * objects are added at the end (inserting is faster then)
+ */
+sal_uLong wwZOrderer::GetDrawingObjectPos(short nWwHeight)
+{
+ auto aIter = std::find_if(
+ maDrawHeight.begin(), maDrawHeight.end(),
+ [nWwHeight](short aHeight){ return (aHeight & 0x1fff) > (nWwHeight & 0x1fff); });
+
+ aIter = maDrawHeight.insert(aIter, nWwHeight);
+ return std::distance(maDrawHeight.begin(), aIter);
+}
+
+void wwZOrderer::InsertObject(SdrObject* pObject, sal_uLong nPos)
+{
+ if (!pObject->IsInserted())
+ {
+ mpDrawPg->InsertObject(pObject, nPos);
+ }
+}
+
+static void WW8PicShadowToReal( WW8_PIC_SHADOW const * pPicS, WW8_PIC* pPic );
+
+bool SwWW8ImplReader::GetPictGrafFromStream(Graphic& rGraphic, SvStream& rSrc)
+{
+ return ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(rGraphic, u"", rSrc);
+}
+
+bool SwWW8ImplReader::ReadGrafFile(OUString& rFileName, std::optional<Graphic>& roGraphic,
+ const WW8_PIC& rPic, SvStream* pSt, sal_uLong nFilePos, bool* pbInDoc)
+{ // Write the graphic to the file
+ *pbInDoc = true; // default
+
+ sal_uLong nPosFc = nFilePos + rPic.cbHeader;
+
+ switch (rPic.MFP.mm)
+ {
+ case 94: // BMP-file ( not embedded ) or GIF
+ case 99: // TIFF-file ( not embedded )
+ pSt->Seek(nPosFc);
+ // read name as P-string
+ rFileName = read_uInt8_PascalString(*pSt, m_eStructCharSet);
+ if (!rFileName.isEmpty())
+ rFileName = URIHelper::SmartRel2Abs(
+ INetURLObject(m_sBaseURL), rFileName,
+ URIHelper::GetMaybeFileHdl());
+ *pbInDoc = false; // Don't delete the file afterwards
+ return !rFileName.isEmpty(); // read was successful
+ }
+
+ //skip duplicate graphics when fuzzing
+ if (m_bFuzzing)
+ {
+ if (!m_aGrafPosSet.insert(nPosFc).second)
+ return false;
+ }
+
+ GDIMetaFile aWMF;
+ bool bOk = checkSeek(*pSt, nPosFc) && ReadWindowMetafile( *pSt, aWMF );
+
+ if (!bOk || pSt->GetError() || !aWMF.GetActionSize())
+ return false;
+
+ if (m_xWwFib->m_envr != 1) // !MAC as creator
+ {
+ roGraphic.emplace(aWMF);
+ return true;
+ }
+
+ // MAC - word as creator
+ // The WMF only says "Please use Word 6.0c" and Mac-Pict follows but without
+ // the first 512 Bytes which are not relevant in a MAC-PICT (they are not
+ // interpreted)
+ bOk = false;
+ tools::Long nData = rPic.lcb - ( pSt->Tell() - nPosFc );
+ if (nData > 0)
+ {
+ roGraphic.emplace();
+ bOk = SwWW8ImplReader::GetPictGrafFromStream(*roGraphic, *pSt);
+ if (!bOk)
+ roGraphic.reset();
+ }
+ return bOk; // Contains graphic
+}
+
+struct WW8PicDesc
+{
+ sal_Int16 nCL, nCR, nCT, nCB;
+ tools::Long nWidth, nHeight;
+
+ explicit WW8PicDesc( const WW8_PIC& rPic );
+};
+
+WW8PicDesc::WW8PicDesc( const WW8_PIC& rPic )
+ : nCL(rPic.dxaCropLeft),
+ nCR(rPic.dxaCropRight),
+ nCT(rPic.dyaCropTop),
+ nCB(rPic.dyaCropBottom)
+{
+ //See #i21190# before fiddling with this method
+ tools::Long nOriWidth = rPic.dxaGoal; //Size in 1/100 mm before crop
+ tools::Long nOriHeight = rPic.dyaGoal;
+
+
+ tools::Long nCurrentWidth = nOriWidth - (nCL + nCR); // Size after crop
+ tools::Long nCurrentHeight = nOriHeight - (nCT + nCB);
+ if (!nCurrentWidth)
+ nCurrentWidth = 1;
+ if (!nCurrentHeight)
+ nCurrentHeight = 1;
+ nWidth = nCurrentWidth * rPic.mx / 1000; // Writer Size
+ nHeight = nCurrentHeight * rPic.my / 1000;
+}
+
+void SwWW8ImplReader::ReplaceObj(const SdrObject &rReplaceObj,
+ SdrObject &rSubObj)
+{
+ // Insert SdrGrafObj instead of SdrTextObj into this group
+ if (SdrObject* pGroupObject = rReplaceObj.getParentSdrObjectFromSdrObject())
+ {
+ SdrObjList* pObjectList = pGroupObject->GetSubList();
+
+ rSubObj.SetLogicRect(rReplaceObj.GetCurrentBoundRect());
+ rSubObj.SetLayer(rReplaceObj.GetLayer());
+
+ // remove old object from group-list and add new one
+ // (this also exchanges it in the drawing page)
+ pObjectList->ReplaceObject(&rSubObj, rReplaceObj.GetOrdNum());
+ }
+ else
+ {
+ OSL_ENSURE( false, "Impossible!");
+ }
+}
+
+// MakeGrafNotInContent inserts a non character bound graphic
+// ( bGrafApo == true)
+SwFlyFrameFormat* SwWW8ImplReader::MakeGrafNotInContent(const WW8PicDesc& rPD,
+ const Graphic* pGraph, const OUString& rFileName, const SfxItemSet& rGrfSet)
+{
+
+ sal_uInt32 nWidth = rPD.nWidth;
+ sal_uInt32 nHeight = rPD.nHeight;
+
+ // Vertical shift through line spacing
+ sal_Int32 nNetHeight = nHeight + rPD.nCT + rPD.nCB;
+ if (m_xSFlyPara->nLineSpace && m_xSFlyPara->nLineSpace > nNetHeight)
+ m_xSFlyPara->nYPos =
+ o3tl::narrowing<sal_uInt16>( m_xSFlyPara->nYPos + m_xSFlyPara->nLineSpace - nNetHeight );
+
+ WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), true);
+
+ SwFormatAnchor aAnchor(WW8SwFlyPara::eAnchor);
+ aAnchor.SetAnchor(m_pPaM->GetPoint());
+ aFlySet.Put(aAnchor);
+
+ aFlySet.Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth, nHeight ) );
+
+ SwFlyFrameFormat *const pFlyFormat =
+ m_rDoc.getIDocumentContentOperations().InsertGraphic(
+ *m_pPaM, rFileName, OUString(), pGraph,
+ &aFlySet, &rGrfSet, nullptr);
+
+ // So the frames are generated when inserted in an existing doc:
+ if (m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() &&
+ (RndStdIds::FLY_AT_PARA == pFlyFormat->GetAnchor().GetAnchorId()))
+ {
+ pFlyFormat->MakeFrames();
+ }
+ return pFlyFormat;
+}
+
+// MakeGrafInContent inserts a character bound graphic
+SwFrameFormat* SwWW8ImplReader::MakeGrafInContent(const WW8_PIC& rPic,
+ const WW8PicDesc& rPD, const Graphic* pGraph, const OUString& rFileName,
+ const SfxItemSet& rGrfSet)
+{
+ WW8FlySet aFlySet(*this, m_pPaM, rPic, rPD.nWidth, rPD.nHeight);
+
+ SwFrameFormat* pFlyFormat = nullptr;
+
+ if (rFileName.isEmpty() && m_nObjLocFc) // then it should be an OLE-Object
+ pFlyFormat = ImportOle(pGraph, &aFlySet, &rGrfSet);
+
+ if( !pFlyFormat ) // then just as graphic
+ {
+
+ pFlyFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
+ *m_pPaM, rFileName, OUString(), pGraph, &aFlySet,
+ &rGrfSet, nullptr);
+ }
+
+ // Resize the frame to the size of the picture if graphic is inside a frame
+ // (only if auto-width)
+ if (m_xSFlyPara)
+ m_xSFlyPara->BoxUpWidth( rPD.nWidth );
+ return pFlyFormat;
+}
+
+SwFrameFormat* SwWW8ImplReader::ImportGraf1(WW8_PIC const & rPic, SvStream* pSt,
+ sal_uLong nFilePos )
+{
+ SwFrameFormat* pRet = nullptr;
+ if( pSt->eof() || rPic.fError || rPic.MFP.mm == 99 )
+ return nullptr;
+
+ OUString aFileName;
+ bool bInDoc;
+ std::optional<Graphic> oGraph;
+ bool bOk = ReadGrafFile(aFileName, oGraph, rPic, pSt, nFilePos, &bInDoc);
+
+ if (!bOk)
+ {
+ return nullptr; // Graphic could not be read correctly
+ }
+
+ WW8PicDesc aPD( rPic );
+
+ SwAttrSet aGrfSet( m_rDoc.GetAttrPool(), RES_GRFATR_BEGIN, RES_GRFATR_END-1);
+ if( aPD.nCL || aPD.nCR || aPD.nCT || aPD.nCB )
+ {
+ SwCropGrf aCrop( aPD.nCL, aPD.nCR, aPD.nCT, aPD.nCB) ;
+ aGrfSet.Put( aCrop );
+ }
+
+ if (m_xWFlyPara && m_xWFlyPara->bGrafApo)
+ pRet = MakeGrafNotInContent(aPD, oGraph ? &*oGraph : nullptr, aFileName, aGrfSet);
+ else
+ pRet = MakeGrafInContent(rPic, aPD, oGraph ? &*oGraph : nullptr, aFileName, aGrfSet);
+ return pRet;
+}
+
+bool SwWW8ImplReader::PicRead(SvStream *pDataStream, WW8_PIC *pPic,
+ bool bVer67)
+{
+ //Only the first 0x2e bytes are the same between version 6/7 and 8+
+ WW8_PIC_SHADOW aPicS{};
+ pDataStream->ReadBytes( &aPicS, sizeof( aPicS ) );
+ WW8PicShadowToReal( &aPicS, pPic );
+ for (WW8_BRC & i : pPic->rgbrc)
+ pDataStream->ReadBytes(&i, bVer67 ? 2 : 4);
+ pDataStream->ReadInt16( pPic->dxaOrigin );
+ pDataStream->ReadInt16( pPic->dyaOrigin );
+ bool bOk = pDataStream->good();
+ if (!bVer67)
+ pDataStream->SeekRel(2); //cProps
+ return bOk;
+}
+
+namespace
+{
+ SwNodeType GetNodeType(SwFrameFormat const &rSource)
+ {
+ const SwNodeIndex* pNodeIndex = rSource.GetContent().GetContentIdx();
+ if (!pNodeIndex)
+ return SwNodeType::NONE;
+ const SwNode& rCSttNd = pNodeIndex->GetNode();
+ SwNodeRange aRg(rCSttNd, SwNodeOffset(1), *rCSttNd.EndOfSectionNode());
+ return aRg.aStart.GetNode().GetNodeType();
+ }
+}
+
+SwFrameFormat* SwWW8ImplReader::ImportGraf(SdrTextObj const * pTextObj,
+ SwFrameFormat const * pOldFlyFormat)
+{
+ SwFrameFormat* pRet = nullptr;
+ if (
+ ((m_pStrm == m_pDataStream ) && !m_nPicLocFc) ||
+ (m_nIniFlags & WW8FL_NO_GRAF)
+ )
+ {
+ return nullptr;
+ }
+
+ ::SetProgressState(m_nProgress, m_pDocShell); // Update
+
+ GraphicCtor();
+
+ /*
+ * Little joke from Microsoft: sometimes a stream named DATA exists. This
+ * stream then contains the PICF and the corresponding graphic!
+ * We otherwise map the variable pDataStream to pStream.
+ */
+ auto nOldPos = m_pDataStream->Tell();
+ WW8_PIC aPic;
+ bool bValid = checkSeek(*m_pDataStream, m_nPicLocFc) &&
+ PicRead(m_pDataStream, &aPic, m_bVer67);
+
+ // Sanity check is needed because for example check boxes in field results
+ // contain a WMF-like struct
+ if (bValid && aPic.lcb >= 58)
+ {
+ if( m_pFlyFormatOfJustInsertedGraphic )
+ {
+ // We just added a graphic-link into the doc. Now we need to set
+ // its position and scale it.
+ WW8PicDesc aPD( aPic );
+
+ WW8FlySet aFlySet( *this, m_pPaM, aPic, aPD.nWidth, aPD.nHeight );
+
+ // the correct anchor is set in Read_F_IncludePicture and the
+ // current PaM point is after the position if it is anchored in
+ // content; because this anchor add a character into the textnode.
+ // #i2806#
+ if (RndStdIds::FLY_AS_CHAR ==
+ m_pFlyFormatOfJustInsertedGraphic->GetAnchor().GetAnchorId() )
+ {
+ aFlySet.ClearItem( RES_ANCHOR );
+ }
+
+ m_pFlyFormatOfJustInsertedGraphic->SetFormatAttr( aFlySet );
+
+ m_pFlyFormatOfJustInsertedGraphic = nullptr;
+ }
+ else if((0x64 == aPic.MFP.mm) || (0x66 == aPic.MFP.mm))
+ {
+ // linked graphic in ESCHER-Object
+ rtl::Reference<SdrObject> pObject;
+
+ WW8PicDesc aPD( aPic );
+ if (!m_xMSDffManager)
+ m_xMSDffManager.reset(new SwMSDffManager(*this, m_bSkipImages));
+ /* ##835##
+ * Disable use of main stream as fallback stream for inline direct
+ * blips as it is known that they are directly after the record
+ * header, testing for existence in main stream may lead to an
+ * incorrect fallback graphic being found if other escher graphics
+ * have been inserted in the document
+ */
+ m_xMSDffManager->DisableFallbackStream();
+ if (!m_xMSDffManager->GetModel())
+ m_xMSDffManager->SetModel(m_pDrawModel, 1440);
+
+ if (0x66 == aPic.MFP.mm)
+ {
+ //These ones have names prepended
+ sal_uInt8 nNameLen=0;
+ m_pDataStream->ReadUChar( nNameLen );
+ m_pDataStream->SeekRel( nNameLen );
+ }
+
+ tools::Rectangle aClientRect( 0,0, aPD.nWidth, aPD.nHeight);
+ SvxMSDffImportData aData( aClientRect );
+ pObject = m_xMSDffManager->ImportObj(*m_pDataStream, aData, aClientRect, tools::Rectangle(), /*nCalledByGroup*/0, /*pShapeId*/nullptr );
+ if (pObject)
+ {
+ // for the frame
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aAttrSet( m_rDoc.GetAttrPool() );
+
+ SvxMSDffImportRec const*const pRecord = (1 == aData.size())
+ ? aData.begin()->get() : nullptr;
+
+ if( pRecord )
+ {
+
+ // Horizontal rule may have its width given as % of page
+ // width (-1 is used if not given, 0 means the object has
+ // fixed width).
+ // Additionally, if it's a horizontal rule without width
+ // given, assume 100.0% width.
+ int relativeWidth = pRecord->relativeHorizontalWidth;
+ if( relativeWidth == -1 )
+ relativeWidth = pRecord->isHorizontalRule ? 1000 : 0;
+ if( relativeWidth != 0 )
+ {
+ const sal_Int16 nScale = aPic.dxaGoal ? aPic.dxaGoal : 1000;
+ aPic.mx = msword_cast<sal_uInt16>(
+ m_aSectionManager.GetPageWidth() -
+ m_aSectionManager.GetPageRight() -
+ m_aSectionManager.GetPageLeft()) * relativeWidth / nScale;
+ aPD = WW8PicDesc( aPic );
+ // This SetSnapRect() call adjusts the size of the
+ // object itself, no idea why it's this call (or even
+ // what the call actually does), but that's what
+ // ImportGraf() (called by ImportObj()) uses.
+ pObject->SetSnapRect( tools::Rectangle( 0, 0, aPD.nWidth, aPD.nHeight ));
+ }
+
+ // A graphic of this type in this location is always
+ // inline, and uses the pic in the same module as ww6
+ // graphics.
+ if (m_xWFlyPara && m_xWFlyPara->bGrafApo)
+ {
+ WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), true);
+
+ SwFormatAnchor aAnchor(WW8SwFlyPara::eAnchor);
+ aAnchor.SetAnchor(m_pPaM->GetPoint());
+ aFlySet.Put(aAnchor);
+
+ aAttrSet.Put(aFlySet);
+ }
+ else
+ {
+ WW8FlySet aFlySet( *this, m_pPaM, aPic, aPD.nWidth,
+ aPD.nHeight );
+
+ aAttrSet.Put(aFlySet);
+ }
+ // Modified for i120716,for graf importing from MS Word 2003
+ // binary format, there is no border distance.
+ tools::Rectangle aInnerDist(0,0,0,0);
+ MatchSdrItemsIntoFlySet( pObject.get(), aAttrSet,
+ pRecord->eLineStyle, pRecord->eLineDashing,
+ pRecord->eShapeType, aInnerDist );
+
+ // Set the size from the WinWord PIC-structure as graphic
+ // size
+ aAttrSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, aPD.nWidth,
+ aPD.nHeight ) );
+ }
+
+ // for the graphic
+ SfxItemSetFixed<RES_GRFATR_BEGIN, RES_GRFATR_END-1> aGrSet( m_rDoc.GetAttrPool() );
+
+ if( aPD.nCL || aPD.nCR || aPD.nCT || aPD.nCB )
+ {
+ SwCropGrf aCrop( aPD.nCL, aPD.nCR, aPD.nCT, aPD.nCB );
+ aGrSet.Put( aCrop );
+ }
+
+ if (pRecord)
+ MatchEscherMirrorIntoFlySet(*pRecord, aGrSet);
+
+ // if necessary adopt old AttrSet and correct horizontal
+ // positioning relation
+ if( pOldFlyFormat )
+ {
+ aAttrSet.Put( pOldFlyFormat->GetAttrSet() );
+ const SwFormatHoriOrient &rHori = pOldFlyFormat->GetHoriOrient();
+ if( text::RelOrientation::FRAME == rHori.GetRelationOrient() )
+ {
+ aAttrSet.Put( SwFormatHoriOrient( rHori.GetPos(),
+ text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA ) );
+ }
+ }
+
+ bool bTextObjWasGrouped = false;
+ if (pOldFlyFormat && pTextObj && pTextObj->getParentSdrObjectFromSdrObject())
+ bTextObjWasGrouped = true;
+
+ if (bTextObjWasGrouped)
+ ReplaceObj(*pTextObj, *pObject);
+ else
+ {
+ if (SdrObjKind::OLE2 == pObject->GetObjIdentifier())
+ {
+ // the size from BLIP, if there is any, should be already set
+ pRet = InsertOle(*static_cast<SdrOle2Obj*>(pObject.get()), aAttrSet, &aGrSet);
+ }
+ else
+ {
+ if (SdrGrafObj* pGraphObject = dynamic_cast<SdrGrafObj*>( pObject.get()) )
+ {
+ // Now add the link or rather the graphic to the doc
+ const Graphic& rGraph = pGraphObject->GetGraphic();
+
+ if (m_nObjLocFc) // is it an OLE-Object?
+ pRet = ImportOle(&rGraph, &aAttrSet, &aGrSet, pObject->GetBLIPSizeRectangle());
+
+ if (!pRet)
+ {
+ pRet = m_rDoc.getIDocumentContentOperations().InsertGraphic(
+ *m_pPaM, OUString(), OUString(),
+ &rGraph, &aAttrSet, &aGrSet, nullptr );
+ }
+ }
+ else
+ pRet = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pObject, aAttrSet );
+ }
+ }
+
+ // only if we made an *Insert*
+ if (pRet)
+ {
+ if (pRecord)
+ SetAttributesAtGrfNode(*pRecord, *pRet, nullptr);
+
+ OUString aObjectName(pObject->GetName());
+ if (aObjectName.isEmpty() || !m_rDoc.FindFlyByName(aObjectName, GetNodeType(*pRet)))
+ pRet->SetFormatName(aObjectName);
+ else
+ m_aGrfNameGenerator.SetUniqueGraphName(pRet, aObjectName);
+
+ // determine the pointer to the new object and update
+ // Z-order-list accordingly (or delete entry)
+ if (SdrObject* pOurNewObject = CreateContactObject(pRet))
+ {
+ if (pOurNewObject != pObject.get())
+ {
+ m_xMSDffManager->ExchangeInShapeOrder( pObject.get(), 0,
+ pOurNewObject );
+
+ // delete and destroy old SdrGrafObj from page
+ if (pObject->getSdrPageFromSdrObject())
+ m_pDrawPg->RemoveObject(pObject->GetOrdNum());
+ pObject.clear();
+ }
+ }
+ else
+ m_xMSDffManager->RemoveFromShapeOrder( pObject.get() );
+ }
+ else
+ m_xMSDffManager->RemoveFromShapeOrder( pObject.get() );
+
+ // also delete this from the page if not grouped
+ if (pTextObj && !bTextObjWasGrouped && pTextObj->getSdrPageFromSdrObject())
+ m_pDrawPg->RemoveObject( pTextObj->GetOrdNum() );
+ }
+ m_xMSDffManager->EnableFallbackStream();
+ }
+ else if (aPic.lcb >= 58)
+ pRet = ImportGraf1(aPic, m_pDataStream, m_nPicLocFc);
+ }
+ m_pDataStream->Seek( nOldPos );
+
+ if (pRet)
+ {
+ SdrObject* pOurNewObject = CreateContactObject(pRet);
+ m_xWWZOrder->InsertTextLayerObject(pOurNewObject);
+ }
+
+ return AddAutoAnchor(pRet);
+}
+
+void WW8PicShadowToReal( WW8_PIC_SHADOW const * pPicS, WW8_PIC * pPic )
+{
+ pPic->lcb = SVBT32ToUInt32( pPicS->lcb );
+ pPic->cbHeader = SVBT16ToUInt16( pPicS->cbHeader );
+ pPic->MFP.mm = SVBT16ToUInt16( pPicS->MFP.mm );
+ pPic->MFP.xExt = SVBT16ToUInt16( pPicS->MFP.xExt );
+ pPic->MFP.yExt = SVBT16ToUInt16( pPicS->MFP.yExt );
+ pPic->MFP.hMF = SVBT16ToUInt16( pPicS->MFP.hMF );
+ for( sal_uInt16 i = 0; i < 14 ; i++ )
+ pPic->rcWinMF[i] = pPicS->rcWinMF[i];
+ pPic->dxaGoal = SVBT16ToUInt16( pPicS->dxaGoal );
+ pPic->dyaGoal = SVBT16ToUInt16( pPicS->dyaGoal );
+ pPic->mx = SVBT16ToUInt16( pPicS->mx );
+ pPic->my = SVBT16ToUInt16( pPicS->my );
+ pPic->dxaCropLeft = SVBT16ToUInt16( pPicS->dxaCropLeft );
+ pPic->dyaCropTop = SVBT16ToUInt16( pPicS->dyaCropTop );
+ pPic->dxaCropRight = SVBT16ToUInt16( pPicS->dxaCropRight );
+ pPic->dyaCropBottom = SVBT16ToUInt16( pPicS->dyaCropBottom );
+ pPic->brcl = pPicS->aBits1 & 0x0f;
+ pPic->fFrameEmpty = (pPicS->aBits1 & 0x10) >> 4;
+ pPic->fBitmap = (pPicS->aBits1 & 0x20) >> 5;
+ pPic->fDrawHatch = (pPicS->aBits1 & 0x40) >> 6;
+ pPic->fError = (pPicS->aBits1 & 0x80) >> 7;
+ pPic->bpp = pPicS->aBits2;
+}
+
+void WW8FSPAShadowToReal(const WW8_FSPA_SHADOW& rFSPAS, WW8_FSPA& rFSPA)
+{
+ rFSPA.nSpId = SVBT32ToUInt32(rFSPAS.nSpId);
+ rFSPA.nXaLeft = SVBT32ToUInt32(rFSPAS.nXaLeft);
+ rFSPA.nYaTop = SVBT32ToUInt32(rFSPAS.nYaTop);
+ rFSPA.nXaRight = SVBT32ToUInt32(rFSPAS.nXaRight);
+ rFSPA.nYaBottom = SVBT32ToUInt32(rFSPAS.nYaBottom);
+
+ sal_uInt16 nBits = SVBT16ToUInt16(rFSPAS.aBits1);
+
+ rFSPA.bHdr = sal_uInt16(0 != (nBits & 0x0001));
+ rFSPA.nbx = (nBits & 0x0006) >> 1;
+ rFSPA.nby = (nBits & 0x0018) >> 3;
+ rFSPA.nwr = (nBits & 0x01E0) >> 5;
+ rFSPA.nwrk = (nBits & 0x1E00) >> 9;
+ rFSPA.bRcaSimple = sal_uInt16(0 != (nBits & 0x2000));
+ rFSPA.bBelowText = sal_uInt16(0 != (nBits & 0x4000));
+ rFSPA.bAnchorLock = sal_uInt16(0 != (nBits & 0x8000));
+ rFSPA.nTxbx = SVBT32ToUInt32(rFSPAS.nTxbx);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx
new file mode 100644
index 0000000000..9418fa0646
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par.cxx
@@ -0,0 +1,6759 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/packages/XPackageEncryption.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <i18nlangtag/languagetag.hxx>
+
+#include <unotools/configmgr.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <rtl/random.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <sfx2/docinf.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <comphelper/docpasswordrequest.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <comphelper/propertysequence.hxx>
+
+#include <editeng/outlobj.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/editeng.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svxerr.hxx>
+#include <filter/msfilter/mscodec.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtcfitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <fmtfld.hxx>
+#include <fmturl.hxx>
+#include <fmtinfmt.hxx>
+#include <reffld.hxx>
+#include <fmthdft.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtcnct.hxx>
+#include <fmtanchr.hxx>
+#include <fmtpdsc.hxx>
+#include <ftninfo.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <ndtxt.hxx>
+#include <pagedesc.hxx>
+#include <paratr.hxx>
+#include <poolfmt.hxx>
+#include <fmtclbl.hxx>
+#include <section.hxx>
+#include <docsh.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentExternalData.hxx>
+#include <../../core/inc/DocumentRedlineManager.hxx>
+#include <docufld.hxx>
+#include <swfltopt.hxx>
+#include <utility>
+#include <viewsh.hxx>
+#include <shellres.hxx>
+#include <swerror.h>
+#include <swtable.hxx>
+#include <fchrfmt.hxx>
+#include <charfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include "sprmids.hxx"
+
+#include "writerwordglue.hxx"
+
+#include <ndgrf.hxx>
+#include <editeng/editids.hrc>
+#include <fmtflcnt.hxx>
+#include <txatbase.hxx>
+
+#include "ww8par2.hxx"
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+
+#include <svl/lngmisc.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/processfactory.hxx>
+#include <basic/basmgr.hxx>
+
+#include "ww8toolbar.hxx"
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+
+#include <breakit.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <swdll.hxx>
+#include "WW8Sttbf.hxx"
+#include "WW8FibData.hxx"
+#include <unordered_set>
+#include <memory>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <unotools/pathoptions.hxx>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <comphelper/sequenceashashmap.hxx>
+#include <oox/ole/vbaproject.hxx>
+#include <oox/ole/olestorage.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <sfx2/DocumentMetadataAccess.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+using namespace sw::util;
+using namespace sw::types;
+using namespace nsHdFtFlags;
+
+static SwMacroInfo* GetMacroInfo( SdrObject* pObj )
+{
+ if ( pObj )
+ {
+ sal_uInt16 nCount = pObj->GetUserDataCount();
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ SdrObjUserData* pData = pObj->GetUserData( i );
+ if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw
+ && pData->GetId() == SW_UD_IMAPDATA)
+ {
+ return dynamic_cast<SwMacroInfo*>(pData);
+ }
+ }
+ SwMacroInfo* pData = new SwMacroInfo;
+ pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
+ return pData;
+ }
+
+ return nullptr;
+};
+
+static void lclGetAbsPath(OUString& rPath, sal_uInt16 nLevel, SwDocShell const * pDocShell)
+{
+ OUStringBuffer aTmpStr;
+ while( nLevel )
+ {
+ aTmpStr.append("../");
+ --nLevel;
+ }
+ if (!aTmpStr.isEmpty())
+ aTmpStr.append(rPath);
+ else
+ aTmpStr = rPath;
+
+ if (!aTmpStr.isEmpty())
+ {
+ bool bWasAbs = false;
+ rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ // full path as stored in SvxURLField must be encoded
+ }
+}
+
+namespace
+{
+ void lclIgnoreUString32(SvStream& rStrm)
+ {
+ sal_uInt32 nChars(0);
+ rStrm.ReadUInt32(nChars);
+ nChars *= 2;
+ rStrm.SeekRel(nChars);
+ }
+}
+
+void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr)
+{
+ // (0x01B8) HLINK
+ // const sal_uInt16 WW8_ID_HLINK = 0x01B8;
+ constexpr sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL.
+ constexpr sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path.
+ constexpr sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description.
+ constexpr sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark.
+ constexpr sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame.
+ constexpr sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path.
+
+ //sal_uInt8 maGuidStdLink[ 16 ] ={
+ // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
+
+ sal_uInt8 const aGuidUrlMoniker[ 16 ] = {
+ 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B };
+
+ sal_uInt8 const aGuidFileMoniker[ 16 ] = {
+ 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
+
+ sal_uInt8 aGuid[16];
+ sal_uInt32 nFlags(0);
+
+ rStrm.ReadBytes(aGuid, 16);
+ rStrm.SeekRel( 4 );
+ rStrm.ReadUInt32( nFlags );
+
+ std::unique_ptr< OUString > xLongName; // link / file name
+ std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
+ std::unique_ptr< OUString > xTextMark; // text mark
+
+ // description (ignore)
+ if( ::get_flag( nFlags, WW8_HLINK_DESCR ) )
+ lclIgnoreUString32( rStrm );
+
+ // target frame
+ if( ::get_flag( nFlags, WW8_HLINK_FRAME ) )
+ {
+ hlStr.tarFrame = read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm);
+ }
+
+ // UNC path
+ if( ::get_flag( nFlags, WW8_HLINK_UNC ) )
+ {
+ // MS-OSHARED: An unsigned integer that specifies the number of Unicode characters in the
+ // string field, including the null-terminating character.
+ sal_uInt32 nStrLen(0);
+ rStrm.ReadUInt32(nStrLen);
+ if (nStrLen)
+ {
+ xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
+ rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
+ lclGetAbsPath( *xLongName, 0 , pDocShell);
+ }
+ }
+ // file link or URL
+ else if( ::get_flag( nFlags, WW8_HLINK_BODY ) )
+ {
+ rStrm.ReadBytes(aGuid, 16);
+
+ if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 )
+ {
+ sal_uInt16 nLevel = 0; // counter for level to climb down in path
+ rStrm.ReadUInt16( nLevel );
+ // MS-OSHARED: An unsigned integer that specifies the number of
+ // ANSI characters in ansiPath, including the terminating NULL character
+ sal_uInt32 nUnits = 0;
+ rStrm.ReadUInt32(nUnits);
+ if (!nUnits)
+ xShortName.reset(new OUString);
+ else
+ {
+ OString sStr(read_uInt8s_ToOString(rStrm, nUnits - 1));
+ rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end
+ xShortName.reset(new OUString(sStr.getStr(), sStr.getLength(), GetCharSetFromLanguage()));
+ }
+ rStrm.SeekRel( 24 );
+
+ sal_uInt32 nStrLen(0);
+ rStrm.ReadUInt32( nStrLen );
+ if( nStrLen )
+ {
+ nStrLen = 0;
+ rStrm.ReadUInt32( nStrLen );
+ nStrLen /= 2;
+ rStrm.SeekRel( 2 );
+ // MS-OSHARED: This array MUST not include a terminating NULL character.
+ xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen)));
+ lclGetAbsPath( *xLongName, nLevel, pDocShell);
+ }
+ else
+ lclGetAbsPath( *xShortName, nLevel, pDocShell);
+ }
+ else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 )
+ {
+ // MS-OSHARED: An unsigned integer that specifies the size of this
+ // structure in bytes, excluding the size of the length field. The
+ // value of this field MUST be ... the byte size of the url
+ // field (including the terminating NULL character)
+ sal_uInt32 nStrLen(0);
+ rStrm.ReadUInt32( nStrLen );
+ nStrLen /= 2;
+ if (!nStrLen)
+ xLongName.reset(new OUString);
+ else
+ {
+ xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1)));
+ rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
+ }
+ if( !::get_flag( nFlags, WW8_HLINK_ABS ) )
+ lclGetAbsPath( *xLongName, 0 ,pDocShell);
+ }
+ else
+ {
+ SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID");
+ }
+ }
+
+ // text mark
+ if( ::get_flag( nFlags, WW8_HLINK_MARK ) )
+ {
+ xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm)));
+ }
+
+ if (!xLongName && xShortName)
+ xLongName.reset(new OUString(*xShortName));
+ else if (!xLongName && xTextMark)
+ xLongName.reset( new OUString );
+
+ if (xLongName)
+ {
+ if (xTextMark)
+ {
+ if (xLongName->isEmpty())
+ *xTextMark = xTextMark->replace('!', '.');
+ *xLongName += "#" + *xTextMark;
+ }
+ hlStr.hLinkAddr = *xLongName;
+ }
+}
+
+namespace {
+
+class BasicProjImportHelper
+{
+ SwDocShell& mrDocShell;
+ uno::Reference< uno::XComponentContext > mxCtx;
+public:
+ explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ),
+ mxCtx(comphelper::getProcessComponentContext())
+ {
+ }
+ bool import( const uno::Reference< io::XInputStream >& rxIn );
+ OUString getProjectName() const;
+};
+
+}
+
+bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn )
+{
+ bool bRet = false;
+ try
+ {
+ oox::ole::OleStorage root( mxCtx, rxIn, false );
+ oox::StorageRef vbaStg = root.openSubStorage( "Macros" , false );
+ if ( vbaStg )
+ {
+ oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), u"Writer" );
+ bRet = aVbaPrj.importVbaProject( *vbaStg );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ bRet = false;
+ }
+ return bRet;
+}
+
+OUString BasicProjImportHelper::getProjectName() const
+{
+ OUString sProjName( "Standard" );
+ uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY );
+ if ( xProps.is() )
+ {
+ try
+ {
+ uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW );
+ sProjName = xVBA->getProjectName();
+
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ return sProjName;
+}
+
+namespace {
+
+class Sttb : public TBBase
+{
+struct SBBItem
+{
+ sal_uInt16 cchData;
+ OUString data;
+ SBBItem() : cchData(0){}
+};
+ sal_uInt16 m_fExtend;
+ sal_uInt16 m_cData;
+ sal_uInt16 m_cbExtra;
+
+ std::vector< SBBItem > m_dataItems;
+
+ Sttb(Sttb const&) = delete;
+ Sttb& operator=(Sttb const&) = delete;
+
+public:
+ Sttb();
+
+ bool Read(SvStream &rS) override;
+ OUString getStringAtIndex( sal_uInt32 );
+};
+
+}
+
+Sttb::Sttb()
+ : m_fExtend(0)
+ , m_cData(0)
+ , m_cbExtra(0)
+{
+}
+
+bool Sttb::Read( SvStream& rS )
+{
+ SAL_INFO("sw.ww8", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUInt16( m_fExtend ).ReadUInt16( m_cData ).ReadUInt16( m_cbExtra );
+ if ( m_cData )
+ {
+ //if they are all going to be empty strings, how many could there be
+ const size_t nMaxPossibleRecords = rS.remainingSize() / sizeof(sal_uInt16);
+ if (m_cData > nMaxPossibleRecords)
+ return false;
+ for ( sal_Int32 index = 0; index < m_cData; ++index )
+ {
+ SBBItem aItem;
+ rS.ReadUInt16( aItem.cchData );
+ aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData);
+ m_dataItems.push_back( aItem );
+ }
+ }
+ return true;
+}
+
+OUString
+Sttb::getStringAtIndex( sal_uInt32 index )
+{
+ OUString aRet;
+ if ( index < m_dataItems.size() )
+ aRet = m_dataItems[ index ].data;
+ return aRet;
+
+}
+
+SwMSDffManager::SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages )
+ : SvxMSDffManager(*rRdr.m_pTableStream, rRdr.GetBaseURL(), rRdr.m_xWwFib->m_fcDggInfo,
+ rRdr.m_pDataStream, nullptr, 0, COL_WHITE, rRdr.m_pStrm, bSkipImages),
+ m_rReader(rRdr), m_pFallbackStream(nullptr)
+{
+ SetSvxMSDffSettings( GetSvxMSDffSettings() );
+ nSvxMSDffOLEConvFlags = SwMSDffManager::GetFilterFlags();
+}
+
+sal_uInt32 SwMSDffManager::GetFilterFlags()
+{
+ sal_uInt32 nFlags(0);
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ if (rOpt.IsMathType2Math())
+ nFlags |= OLE_MATHTYPE_2_STARMATH;
+ if (rOpt.IsExcel2Calc())
+ nFlags |= OLE_EXCEL_2_STARCALC;
+ if (rOpt.IsPowerPoint2Impress())
+ nFlags |= OLE_POWERPOINT_2_STARIMPRESS;
+ if (rOpt.IsWinWord2Writer())
+ nFlags |= OLE_WINWORD_2_STARWRITER;
+ return nFlags;
+}
+
+/*
+ * I would like to override the default OLE importing to add a test
+ * and conversion of OCX controls from their native OLE type into our
+ * native nonOLE Form Control Objects.
+ */
+// #i32596# - consider new parameter <_nCalledByGroup>
+rtl::Reference<SdrObject> SwMSDffManager::ImportOLE( sal_uInt32 nOLEId,
+ const Graphic& rGrf,
+ const tools::Rectangle& rBoundRect,
+ const tools::Rectangle& rVisArea,
+ const int _nCalledByGroup ) const
+{
+ // #i32596# - no import of OLE object, if it's inside a group.
+ // NOTE: This can be undone, if grouping of Writer fly frames is possible or
+ // if drawing OLE objects are allowed in Writer.
+ if ( _nCalledByGroup > 0 )
+ {
+ return nullptr;
+ }
+
+ rtl::Reference<SdrObject> pRet;
+ OUString sStorageName;
+ tools::SvRef<SotStorage> xSrcStg;
+ uno::Reference < embed::XStorage > xDstStg;
+ if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
+ {
+ tools::SvRef<SotStorage> xSrc = xSrcStg->OpenSotStorage( sStorageName );
+ OSL_ENSURE(m_rReader.m_xFormImpl, "No Form Implementation!");
+ css::uno::Reference< css::drawing::XShape > xShape;
+ if ( (!(m_rReader.m_bIsHeader || m_rReader.m_bIsFooter)) &&
+ m_rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true))
+ {
+ pRet = SdrObject::getSdrObjectFromXShape(xShape);
+ }
+ else
+ {
+ ErrCode nError = ERRCODE_NONE;
+ pRet = CreateSdrOLEFromStorage(
+ *pSdrModel,
+ sStorageName,
+ xSrcStg,
+ xDstStg,
+ rGrf,
+ rBoundRect,
+ rVisArea,
+ pStData,
+ nError,
+ nSvxMSDffOLEConvFlags,
+ css::embed::Aspects::MSOLE_CONTENT,
+ m_rReader.GetBaseURL());
+ }
+ }
+ return pRet;
+}
+
+void SwMSDffManager::DisableFallbackStream()
+{
+ OSL_ENSURE(!m_pFallbackStream,
+ "if you're recursive, you're broken");
+ m_pFallbackStream = pStData2;
+ m_aOldEscherBlipCache = aEscherBlipCache;
+ aEscherBlipCache.clear();
+ pStData2 = nullptr;
+}
+
+void SwMSDffManager::EnableFallbackStream()
+{
+ pStData2 = m_pFallbackStream;
+ aEscherBlipCache = m_aOldEscherBlipCache;
+ m_aOldEscherBlipCache.clear();
+ m_pFallbackStream = nullptr;
+}
+
+sal_uInt16 SwWW8ImplReader::GetToggleAttrFlags() const
+{
+ return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0;
+}
+
+sal_uInt16 SwWW8ImplReader::GetToggleBiDiAttrFlags() const
+{
+ return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0;
+}
+
+void SwWW8ImplReader::SetToggleAttrFlags(sal_uInt16 nFlags)
+{
+ if (m_xCtrlStck)
+ m_xCtrlStck->SetToggleAttrFlags(nFlags);
+}
+
+void SwWW8ImplReader::SetToggleBiDiAttrFlags(sal_uInt16 nFlags)
+{
+ if (m_xCtrlStck)
+ m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags);
+}
+
+rtl::Reference<SdrObject> SwMSDffManager::ProcessObj(SvStream& rSt,
+ DffObjData& rObjData,
+ SvxMSDffClientData& rData,
+ tools::Rectangle& rTextRect,
+ SdrObject* pObj1
+ )
+{
+ rtl::Reference<SdrObject> pObj = pObj1;
+ if( !rTextRect.IsEmpty() )
+ {
+ SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
+ std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec);
+
+ // fill Import Record with data
+ pImpRec->nShapeId = rObjData.nShapeId;
+ pImpRec->eShapeType = rObjData.eShapeType;
+
+ rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtClientAnchor,
+ SEEK_FROM_CURRENT_AND_RESTART );
+ if( rObjData.bClientAnchor )
+ ProcessClientAnchor( rSt,
+ maShapeRecords.Current()->nRecLen,
+ pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
+
+ rObjData.bClientData = maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtClientData,
+ SEEK_FROM_CURRENT_AND_RESTART );
+ if( rObjData.bClientData )
+ ProcessClientData( rSt,
+ maShapeRecords.Current()->nRecLen,
+ pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
+
+ pImpRec->nGroupShapeBooleanProperties = 0;
+
+ if( maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtUDefProp,
+ SEEK_FROM_CURRENT_AND_RESTART )
+ && maShapeRecords.Current()->nRecLen )
+ {
+ sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
+ auto nAvailableBytes = rSt.remainingSize();
+ if (nBytesLeft > nAvailableBytes)
+ {
+ SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available");
+ nBytesLeft = nAvailableBytes;
+ }
+ while( 5 < nBytesLeft )
+ {
+ sal_uInt16 nPID(0);
+ rSt.ReadUInt16(nPID);
+ sal_uInt32 nUDData(0);
+ rSt.ReadUInt32(nUDData);
+ if (!rSt.good())
+ break;
+ switch (nPID)
+ {
+ case 0x038F: pImpRec->nXAlign = nUDData; break;
+ case 0x0390:
+ pImpRec->nXRelTo = nUDData;
+ break;
+ case 0x0391: pImpRec->nYAlign = nUDData; break;
+ case 0x0392:
+ pImpRec->nYRelTo = nUDData;
+ break;
+ case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break;
+ case 0x0393:
+ // This seems to correspond to o:hrpct from .docx (even including
+ // the difference that it's in 0.1% even though the .docx spec
+ // says it's in 1%).
+ pImpRec->relativeHorizontalWidth = nUDData;
+ break;
+ case 0x0394:
+ // And this is really just a guess, but a mere presence of this
+ // flag makes a horizontal rule be as wide as the page (unless
+ // overridden by something), so it probably matches o:hr from .docx.
+ pImpRec->isHorizontalRule = true;
+ break;
+ }
+ nBytesLeft -= 6;
+ }
+ }
+
+ // Text Frame also Title or Outline
+ sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
+ if( nTextId )
+ {
+ SfxItemSet aSet( pSdrModel->GetItemPool() );
+
+ // Originally anything that as a mso_sptTextBox was created as a
+ // textbox, this was changed to be created as a simple
+ // rect to keep impress happy. For the rest of us we'd like to turn
+ // it back into a textbox again.
+ bool bIsSimpleDrawingTextBox = (pImpRec->eShapeType == mso_sptTextBox);
+ if (!bIsSimpleDrawingTextBox)
+ {
+ // Either
+ // a) it's a simple text object or
+ // b) it's a rectangle with text and square wrapping.
+ bIsSimpleDrawingTextBox =
+ (
+ (pImpRec->eShapeType == mso_sptTextSimple) ||
+ (
+ (pImpRec->eShapeType == mso_sptRectangle)
+ && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
+ )
+ );
+ }
+
+ // Distance of Textbox to its surrounding Autoshape
+ sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440);
+ sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440 );
+ sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720 );
+ sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720 );
+
+ ScaleEmu( nTextLeft );
+ ScaleEmu( nTextRight );
+ ScaleEmu( nTextTop );
+ ScaleEmu( nTextBottom );
+
+ Degree100 nTextRotationAngle;
+ bool bVerticalText = false;
+ if ( IsProperty( DFF_Prop_txflTextFlow ) )
+ {
+ MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue(
+ DFF_Prop_txflTextFlow, 0) & 0xFFFF);
+ switch( eTextFlow )
+ {
+ case mso_txflBtoT:
+ nTextRotationAngle = 9000_deg100;
+ break;
+ case mso_txflVertN:
+ case mso_txflTtoBN:
+ nTextRotationAngle = 27000_deg100;
+ break;
+ case mso_txflTtoBA:
+ bVerticalText = true;
+ break;
+ case mso_txflHorzA:
+ bVerticalText = true;
+ nTextRotationAngle = 9000_deg100;
+ break;
+ case mso_txflHorzN:
+ default :
+ break;
+ }
+ }
+
+ if (nTextRotationAngle)
+ {
+ if (nTextRotationAngle == 9000_deg100)
+ {
+ tools::Long nWidth = rTextRect.GetWidth();
+ rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
+ rTextRect.SetBottom( rTextRect.Top() + nWidth );
+
+ sal_Int32 nOldTextLeft = nTextLeft;
+ sal_Int32 nOldTextRight = nTextRight;
+ sal_Int32 nOldTextTop = nTextTop;
+ sal_Int32 nOldTextBottom = nTextBottom;
+
+ nTextLeft = nOldTextBottom;
+ nTextRight = nOldTextTop;
+ nTextTop = nOldTextLeft;
+ nTextBottom = nOldTextRight;
+ }
+ else if (nTextRotationAngle == 27000_deg100)
+ {
+ tools::Long nWidth = rTextRect.GetWidth();
+ rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
+ rTextRect.SetBottom( rTextRect.Top() + nWidth );
+
+ sal_Int32 nOldTextLeft = nTextLeft;
+ sal_Int32 nOldTextRight = nTextRight;
+ sal_Int32 nOldTextTop = nTextTop;
+ sal_Int32 nOldTextBottom = nTextBottom;
+
+ nTextLeft = nOldTextTop;
+ nTextRight = nOldTextBottom;
+ nTextTop = nOldTextRight;
+ nTextBottom = nOldTextLeft;
+ }
+ }
+
+ if (bIsSimpleDrawingTextBox)
+ {
+ pObj = new SdrRectObj(
+ *pSdrModel,
+ SdrObjKind::Text,
+ rTextRect);
+ }
+
+ // The vertical paragraph justification are contained within the
+ // BoundRect so calculate it here
+ tools::Rectangle aNewRect(rTextRect);
+ aNewRect.AdjustBottom( -(nTextTop + nTextBottom) );
+ aNewRect.AdjustRight( -(nTextLeft + nTextRight) );
+
+ // Only if it's a simple Textbox, Writer can replace the Object
+ // with a Frame, else
+ if( bIsSimpleDrawingTextBox )
+ {
+ std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec =
+ std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
+
+ SvxMSDffShapeInfos_ById::const_iterator const it =
+ GetShapeInfos()->find(xTmpRec);
+ if (it != GetShapeInfos()->end())
+ {
+ SvxMSDffShapeInfo& rInfo = **it;
+ pImpRec->bReplaceByFly = rInfo.bReplaceByFly;
+ }
+
+ ApplyAttributes(rSt, aSet, rObjData);
+ }
+
+ if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2)
+ {
+ aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
+ aSet.Put( makeSdrTextMinFrameHeightItem(
+ aNewRect.Bottom() - aNewRect.Top() ) );
+ aSet.Put( makeSdrTextMinFrameWidthItem(
+ aNewRect.Right() - aNewRect.Left() ) );
+ }
+ else
+ {
+ aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
+ aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
+ }
+
+ switch ( static_cast<MSO_WrapMode>(GetPropertyValue( DFF_Prop_WrapText, mso_wrapSquare )) )
+ {
+ case mso_wrapNone :
+ aSet.Put( makeSdrTextAutoGrowWidthItem( true ) );
+ pImpRec->bAutoWidth = true;
+ break;
+ case mso_wrapByPoints :
+ aSet.Put( makeSdrTextContourFrameItem( true ) );
+ break;
+ default:
+ ;
+ }
+
+ // Set distances on Textbox's margins
+ aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
+ aSet.Put( makeSdrTextRightDistItem( nTextRight ) );
+ aSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
+ aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
+ pImpRec->nDxTextLeft = nTextLeft;
+ pImpRec->nDyTextTop = nTextTop;
+ pImpRec->nDxTextRight = nTextRight;
+ pImpRec->nDyTextBottom = nTextBottom;
+
+ // Taking the correct default (which is mso_anchorTop)
+ sal_uInt32 eTextAnchor =
+ GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop );
+
+ SdrTextVertAdjust eTVA = bVerticalText
+ ? SDRTEXTVERTADJUST_BLOCK
+ : SDRTEXTVERTADJUST_CENTER;
+ SdrTextHorzAdjust eTHA = bVerticalText
+ ? SDRTEXTHORZADJUST_CENTER
+ : SDRTEXTHORZADJUST_BLOCK;
+
+ switch( eTextAnchor )
+ {
+ case mso_anchorTop:
+ {
+ if ( bVerticalText )
+ eTHA = SDRTEXTHORZADJUST_RIGHT;
+ else
+ eTVA = SDRTEXTVERTADJUST_TOP;
+ }
+ break;
+ case mso_anchorTopCentered:
+ {
+ if ( bVerticalText )
+ eTHA = SDRTEXTHORZADJUST_RIGHT;
+ else
+ eTVA = SDRTEXTVERTADJUST_TOP;
+ }
+ break;
+ case mso_anchorMiddle:
+ break;
+ case mso_anchorMiddleCentered:
+ break;
+ case mso_anchorBottom:
+ {
+ if ( bVerticalText )
+ eTHA = SDRTEXTHORZADJUST_LEFT;
+ else
+ eTVA = SDRTEXTVERTADJUST_BOTTOM;
+ }
+ break;
+ case mso_anchorBottomCentered:
+ {
+ if ( bVerticalText )
+ eTHA = SDRTEXTHORZADJUST_LEFT;
+ else
+ eTVA = SDRTEXTVERTADJUST_BOTTOM;
+ }
+ break;
+ default:
+ ;
+ }
+
+ aSet.Put( SdrTextVertAdjustItem( eTVA ) );
+ aSet.Put( SdrTextHorzAdjustItem( eTHA ) );
+
+ if (pObj != nullptr)
+ {
+ pObj->SetMergedItemSet(aSet);
+
+ if (bVerticalText)
+ {
+ SdrTextObj *pTextObj = DynCastSdrTextObj(pObj.get());
+ if (pTextObj)
+ pTextObj->SetVerticalWriting(true);
+ }
+
+ if ( bIsSimpleDrawingTextBox )
+ {
+ if ( nTextRotationAngle )
+ {
+ tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ?
+ rTextRect.GetWidth() : rTextRect.GetHeight();
+ nMinWH /= 2;
+ Point aPivot(rTextRect.TopLeft());
+ aPivot.AdjustX(nMinWH );
+ aPivot.AdjustY(nMinWH );
+ pObj->NbcRotate(aPivot, nTextRotationAngle);
+ }
+ }
+
+ if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj.get() ) )
+ {
+ SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj.get() );
+ if (pCustomShape)
+ {
+ double fExtraTextRotation = 0.0;
+ if ( mnFix16Angle && !( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 4 ) )
+ { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false
+ fExtraTextRotation = -mnFix16Angle.get();
+ }
+ if ( rObjData.nSpFlags & ShapeFlag::FlipV ) // sj: in ppt the text is flipped, whereas in word the text
+ { // remains unchanged, so we have to take back the flipping here
+ fExtraTextRotation += 18000.0; // because our core will flip text if the shape is flipped.
+ }
+ fExtraTextRotation += nTextRotationAngle.get();
+ if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) )
+ {
+ fExtraTextRotation /= 100.0;
+ SdrCustomShapeGeometryItem aGeometryItem( pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ css::beans::PropertyValue aPropVal;
+ aPropVal.Name = "TextRotateAngle";
+ aPropVal.Value <<= fExtraTextRotation;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ pCustomShape->SetMergedItem( aGeometryItem );
+ }
+ }
+ }
+ else if ( mnFix16Angle )
+ {
+ // rotate text with shape ?
+ pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle );
+ }
+ }
+ }
+ else if( !pObj )
+ {
+ // simple rectangular objects are ignored by ImportObj() :-(
+ // this is OK for Draw but not for Calc and Writer
+ // cause here these objects have a default border
+ pObj = new SdrRectObj(
+ *pSdrModel,
+ rTextRect);
+
+ SfxItemSet aSet( pSdrModel->GetItemPool() );
+ ApplyAttributes( rSt, aSet, rObjData );
+
+ SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, false );
+ if( SfxItemState::DEFAULT == eState )
+ aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
+ pObj->SetMergedItemSet(aSet);
+ }
+
+ // Means that fBehindDocument is set
+ if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20)
+ pImpRec->bDrawHell = true;
+ else
+ pImpRec->bDrawHell = false;
+ if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02)
+ pImpRec->bHidden = true;
+ pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 );
+
+ if ( nTextId )
+ {
+ pImpRec->aTextId.nTxBxS = o3tl::narrowing<sal_uInt16>( nTextId >> 16 );
+ pImpRec->aTextId.nSequence = o3tl::narrowing<sal_uInt16>(nTextId);
+ }
+
+ pImpRec->nDxWrapDistLeft = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistLeft, 114935),
+ o3tl::Length::emu, o3tl::Length::twip);
+ pImpRec->nDyWrapDistTop = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistTop, 0),
+ o3tl::Length::emu, o3tl::Length::twip);
+ pImpRec->nDxWrapDistRight
+ = o3tl::convert(GetPropertyValue(DFF_Prop_dxWrapDistRight, 114935), o3tl::Length::emu,
+ o3tl::Length::twip);
+ pImpRec->nDyWrapDistBottom = o3tl::convert(GetPropertyValue(DFF_Prop_dyWrapDistBottom, 0),
+ o3tl::Length::emu, o3tl::Length::twip);
+ // 16.16 fraction times total image width or height, as appropriate.
+
+ if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt))
+ {
+ pImpRec->pWrapPolygon.reset();
+
+ sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(0);
+ rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
+ bool bOk = false;
+ if (nNumElemVert && (nElemSizeVert == 8 || nElemSizeVert == 4))
+ {
+ //check if there is enough data in the file to make the
+ //record sane
+ // coverity[tainted_data : FALSE] - nElemSizeVert is either 8 or 4 so it has been sanitized
+ bOk = rSt.remainingSize() / nElemSizeVert >= nNumElemVert;
+ }
+ if (bOk)
+ {
+ pImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
+ for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
+ {
+ sal_Int32 nX(0), nY(0);
+ if (nElemSizeVert == 8)
+ rSt.ReadInt32( nX ).ReadInt32( nY );
+ else
+ {
+ sal_Int16 nSmallX(0), nSmallY(0);
+ rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
+ nX = nSmallX;
+ nY = nSmallY;
+ }
+ (*(pImpRec->pWrapPolygon))[i].setX( nX );
+ (*(pImpRec->pWrapPolygon))[i].setY( nY );
+ }
+ }
+ }
+
+ pImpRec->nCropFromTop = GetPropertyValue(
+ DFF_Prop_cropFromTop, 0 );
+ pImpRec->nCropFromBottom = GetPropertyValue(
+ DFF_Prop_cropFromBottom, 0 );
+ pImpRec->nCropFromLeft = GetPropertyValue(
+ DFF_Prop_cropFromLeft, 0 );
+ pImpRec->nCropFromRight = GetPropertyValue(
+ DFF_Prop_cropFromRight, 0 );
+
+ sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
+
+ if ( !IsHardAttribute( DFF_Prop_fLine ) &&
+ pImpRec->eShapeType == mso_sptPictureFrame )
+ {
+ nLineFlags &= ~0x08;
+ }
+
+ pImpRec->eLineStyle = (nLineFlags & 8)
+ ? static_cast<MSO_LineStyle>(GetPropertyValue(
+ DFF_Prop_lineStyle,
+ mso_lineSimple ))
+ : MSO_LineStyle(USHRT_MAX);
+ pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
+ DFF_Prop_lineDashing, mso_lineSolid ));
+
+ pImpRec->nFlags = rObjData.nSpFlags;
+
+ if( pImpRec->nShapeId )
+ {
+ auto nShapeId = pImpRec->nShapeId;
+ auto nShapeOrder = (static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16)
+ + pImpRec->aTextId.nSequence;
+ // Complement Import Record List
+ pImpRec->pObj = pObj;
+ rImportData.insert(std::move(pImpRec));
+
+ // Complement entry in Z Order List with a pointer to this Object
+ // Only store objects which are not deep inside the tree
+ if( ( rObjData.nCalledByGroup == 0 )
+ ||
+ ( (rObjData.nSpFlags & ShapeFlag::Group)
+ && (rObjData.nCalledByGroup < 2) )
+ )
+ {
+ StoreShapeOrder(nShapeId, nShapeOrder, pObj.get());
+ }
+ }
+ else
+ pImpRec.reset();
+ }
+
+ sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
+ if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) )
+ {
+ SvMemoryStream aMemStream;
+ struct HyperLinksTable hlStr;
+ aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
+
+ // copy from DFF stream to memory stream
+ std::vector< sal_uInt8 > aBuffer( nBufferSize );
+ if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize)
+ {
+ aMemStream.WriteBytes(aBuffer.data(), nBufferSize);
+ sal_uInt64 nStreamSize = aMemStream.TellEnd();
+ aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
+ bool bRet = 4 <= nStreamSize;
+ sal_uInt16 nRawRecId,nRawRecSize;
+ if( bRet )
+ aMemStream.ReadUInt16( nRawRecId ).ReadUInt16( nRawRecSize );
+ SwDocShell* pDocShell = m_rReader.m_pDocShell;
+ if (pDocShell)
+ {
+ m_rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr);
+ }
+ }
+
+ if (pObj && !hlStr.hLinkAddr.isEmpty())
+ {
+ SwMacroInfo* pInfo = GetMacroInfo( pObj.get() );
+ if( pInfo )
+ {
+ pInfo->SetShapeId( rObjData.nShapeId );
+ pInfo->SetHlink( hlStr.hLinkAddr );
+ if (!hlStr.tarFrame.isEmpty())
+ pInfo->SetTarFrame( hlStr.tarFrame );
+ OUString aNameStr = GetPropertyString( DFF_Prop_wzName, rSt );
+ if (!aNameStr.isEmpty())
+ pInfo->SetName( aNameStr );
+ }
+ }
+ }
+
+ return pObj;
+}
+
+/**
+ * Special FastSave - Attributes
+ */
+void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 0)
+ {
+ m_bCpxStyle = false;
+ return;
+ }
+ sal_uInt16 nColl = 0;
+ if (m_xWwFib->GetFIBVersion() <= ww::eWW2)
+ nColl = *pData;
+ else
+ nColl = SVBT16ToUInt16(pData);
+ if (nColl < m_vColl.size())
+ {
+ SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[nColl] );
+ m_bCpxStyle = true;
+ }
+}
+
+/**
+ * Read_Majority is for Majority (103) and Majority50 (108)
+ */
+void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short )
+{
+}
+
+/**
+ * Stack
+ */
+void SwWW8FltControlStack::NewAttr(const SwPosition& rPos,
+ const SfxPoolItem& rAttr)
+{
+ OSL_ENSURE(RES_TXTATR_FIELD != rAttr.Which(), "probably don't want to put"
+ "fields into the control stack");
+ OSL_ENSURE(RES_TXTATR_INPUTFIELD != rAttr.Which(), "probably don't want to put"
+ "input fields into the control stack");
+ OSL_ENSURE(RES_TXTATR_ANNOTATION != rAttr.Which(), "probably don't want to put"
+ "annotations into the control stack");
+ OSL_ENSURE(RES_FLTR_REDLINE != rAttr.Which(), "probably don't want to put"
+ "redlines into the control stack");
+ SwFltControlStack::NewAttr(rPos, rAttr);
+}
+
+SwFltStackEntry* SwWW8FltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId,
+ bool bTstEnd, tools::Long nHand, bool )
+{
+ SwFltStackEntry *pRet = nullptr;
+ // Doing a textbox, and using the control stack only as a temporary
+ // collection point for properties which will are not to be set into
+ // the real document
+ if (m_rReader.m_xPlcxMan && m_rReader.m_xPlcxMan->GetDoingDrawTextBox())
+ {
+ size_t nCnt = size();
+ for (size_t i=0; i < nCnt; ++i)
+ {
+ SwFltStackEntry& rEntry = (*this)[i];
+ if (nAttrId == rEntry.m_pAttr->Which())
+ {
+ DeleteAndDestroy(i--);
+ --nCnt;
+ }
+ }
+ }
+ else // Normal case, set the attribute into the document
+ pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand);
+ return pRet;
+}
+
+tools::Long GetListFirstLineIndent(const SwNumFormat &rFormat)
+{
+ OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
+ "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
+
+ SvxAdjust eAdj = rFormat.GetNumAdjust();
+ tools::Long nReverseListIndented;
+ if (eAdj == SvxAdjust::Right)
+ nReverseListIndented = -rFormat.GetCharTextDistance();
+ else if (eAdj == SvxAdjust::Center)
+ nReverseListIndented = rFormat.GetFirstLineOffset()/2;
+ else
+ nReverseListIndented = rFormat.GetFirstLineOffset();
+ return nReverseListIndented;
+}
+
+static tools::Long lcl_GetTrueMargin(SvxFirstLineIndentItem const& rFirstLine,
+ SvxTextLeftMarginItem const& rLeftMargin, const SwNumFormat &rFormat,
+ tools::Long &rFirstLinePos)
+{
+ OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
+ "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
+
+ const tools::Long nBodyIndent = rLeftMargin.GetTextLeft();
+ const tools::Long nFirstLineDiff = rFirstLine.GetTextFirstLineOffset();
+ rFirstLinePos = nBodyIndent + nFirstLineDiff;
+
+ const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace();
+ const tools::Long nReverseListIndented = GetListFirstLineIndent(rFormat);
+ tools::Long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented;
+
+ return std::max<tools::Long>(nExtraListIndent, 0);
+}
+
+// #i103711#
+// #i105414#
+void SyncIndentWithList( SvxFirstLineIndentItem & rFirstLine,
+ SvxTextLeftMarginItem & rLeftMargin,
+ const SwNumFormat &rFormat,
+ const bool bFirstLineOfstSet,
+ const bool bLeftIndentSet )
+{
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ tools::Long nWantedFirstLinePos;
+ tools::Long nExtraListIndent = lcl_GetTrueMargin(rFirstLine, rLeftMargin, rFormat, nWantedFirstLinePos);
+ rLeftMargin.SetTextLeft(nWantedFirstLinePos - nExtraListIndent);
+ rFirstLine.SetTextFirstLineOffset(0);
+ }
+ else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ if ( !bFirstLineOfstSet && bLeftIndentSet &&
+ rFormat.GetFirstLineIndent() != 0 )
+ {
+ rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
+ }
+ else if ( bFirstLineOfstSet && !bLeftIndentSet &&
+ rFormat.GetIndentAt() != 0 )
+ {
+ rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
+ }
+ else if (!bFirstLineOfstSet && !bLeftIndentSet )
+ {
+ if ( rFormat.GetFirstLineIndent() != 0 )
+ {
+ rFirstLine.SetTextFirstLineOffset(rFormat.GetFirstLineIndent());
+ }
+ if ( rFormat.GetIndentAt() != 0 )
+ {
+ rLeftMargin.SetTextLeft(rFormat.GetIndentAt());
+ }
+ }
+ }
+}
+
+const SwNumFormat* SwWW8FltControlStack::GetNumFormatFromStack(const SwPosition &rPos,
+ const SwTextNode &rTextNode)
+{
+ const SwNumFormat *pRet = nullptr;
+ const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE);
+ if (pItem && rTextNode.GetNumRule())
+ {
+ if (rTextNode.IsCountedInList())
+ {
+ OUString sName(static_cast<const SfxStringItem*>(pItem)->GetValue());
+ const SwNumRule *pRule = m_rDoc.FindNumRulePtr(sName);
+ if (pRule)
+ pRet = GetNumFormatFromSwNumRuleLevel(*pRule, rTextNode.GetActualListLevel());
+ }
+ }
+ return pRet;
+}
+
+void SwWW8ReferencedFltEndStack::SetAttrInDoc( const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry )
+{
+ switch( rEntry.m_pAttr->Which() )
+ {
+ case RES_FLTR_BOOKMARK:
+ {
+ // suppress insertion of bookmark, which is recognized as an internal bookmark used for table-of-content
+ // and which is not referenced.
+ bool bInsertBookmarkIntoDoc = true;
+
+ SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
+ if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() )
+ {
+ const OUString& rName = pFltBookmark->GetName();
+ std::set< OUString, SwWW8::ltstr >::const_iterator aResult = m_aReferencedTOCBookmarks.find(rName);
+ if ( aResult == m_aReferencedTOCBookmarks.end() )
+ {
+ bInsertBookmarkIntoDoc = false;
+ }
+ }
+ if ( bInsertBookmarkIntoDoc )
+ {
+ SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
+ }
+ break;
+ }
+ default:
+ SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry );
+ break;
+ }
+
+}
+
+void SwWW8FltControlStack::SetAttrInDoc(const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry)
+{
+ switch (rEntry.m_pAttr->Which())
+ {
+ case RES_LR_SPACE:
+ assert(false);
+ break;
+ case RES_MARGIN_FIRSTLINE:
+ case RES_MARGIN_TEXTLEFT:
+ {
+ /*
+ Loop over the affected nodes and
+ a) convert the word style absolute indent to indent relative
+ to any numbering indent active on the nodes
+ b) adjust the writer style tabstops relative to the old
+ paragraph indent to be relative to the new paragraph indent
+ */
+ SwPaM aRegion(rTmpPos);
+ if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck))
+ {
+ SvxFirstLineIndentItem firstLineNew(RES_MARGIN_FIRSTLINE);
+ SvxTextLeftMarginItem leftMarginNew(RES_MARGIN_TEXTLEFT);
+ if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
+ {
+ SvxFirstLineIndentItem const firstLineEntry(*static_cast<SvxFirstLineIndentItem*>(rEntry.m_pAttr.get()));
+ firstLineNew.SetTextFirstLineOffset(firstLineEntry.GetTextFirstLineOffset(), firstLineEntry.GetPropTextFirstLineOffset());
+ firstLineNew.SetAutoFirst(firstLineEntry.IsAutoFirst());
+ }
+ else
+ {
+ SvxTextLeftMarginItem const leftMarginEntry(*static_cast<SvxTextLeftMarginItem*>(rEntry.m_pAttr.get()));
+ leftMarginNew.SetTextLeft(leftMarginEntry.GetTextLeft(), leftMarginEntry.GetPropLeft());
+ }
+ SwNodeOffset nStart = aRegion.Start()->GetNodeIndex();
+ SwNodeOffset nEnd = aRegion.End()->GetNodeIndex();
+ for(; nStart <= nEnd; ++nStart)
+ {
+ SwNode* pNode = m_rDoc.GetNodes()[ nStart ];
+ if (!pNode || !pNode->IsTextNode())
+ continue;
+
+ SwContentNode* pNd = static_cast<SwContentNode*>(pNode);
+ SvxFirstLineIndentItem firstLineOld(pNd->GetAttr(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMarginOld(pNd->GetAttr(RES_MARGIN_TEXTLEFT));
+ if (rEntry.m_pAttr->Which() == RES_MARGIN_FIRSTLINE)
+ {
+ leftMarginNew.SetTextLeft(leftMarginOld.GetTextLeft(), leftMarginOld.GetPropLeft());
+ }
+ else
+ {
+ firstLineNew.SetTextFirstLineOffset(firstLineOld.GetTextFirstLineOffset(), firstLineOld.GetPropTextFirstLineOffset());
+ firstLineNew.SetAutoFirst(firstLineOld.IsAutoFirst());
+ }
+
+ SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode);
+
+ const SwNumFormat* pNum
+ = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode);
+ if (!pNum)
+ {
+ pNum = GetNumFormatFromTextNode(*pTextNode);
+ }
+
+ if ( pNum )
+ {
+ // #i103711#
+ const bool bFirstLineIndentSet =
+ ( m_rReader.m_aTextNodesHavingFirstLineOfstSet.end() !=
+ m_rReader.m_aTextNodesHavingFirstLineOfstSet.find( pNode ) );
+ // #i105414#
+ const bool bLeftIndentSet =
+ ( m_rReader.m_aTextNodesHavingLeftIndentSet.end() !=
+ m_rReader.m_aTextNodesHavingLeftIndentSet.find( pNode ) );
+ SyncIndentWithList(firstLineNew, leftMarginNew, *pNum,
+ bFirstLineIndentSet,
+ bLeftIndentSet );
+ }
+
+ if (firstLineNew != firstLineOld)
+ {
+ if (nStart == aRegion.Start()->GetNodeIndex())
+ {
+ pNd->SetAttr(firstLineNew);
+ }
+ }
+ if (leftMarginNew != leftMarginOld)
+ {
+ pNd->SetAttr(leftMarginNew);
+ }
+ }
+ }
+ }
+ break;
+
+ case RES_TXTATR_FIELD:
+ OSL_ENSURE(false, "What is a field doing in the control stack,"
+ "probably should have been in the endstack");
+ break;
+
+ case RES_TXTATR_ANNOTATION:
+ OSL_ENSURE(false, "What is an annotation doing in the control stack,"
+ "probably should have been in the endstack");
+ break;
+
+ case RES_TXTATR_INPUTFIELD:
+ OSL_ENSURE(false, "What is an input field doing in the control stack,"
+ "probably should have been in the endstack");
+ break;
+
+ case RES_TXTATR_INETFMT:
+ {
+ SwPaM aRegion(rTmpPos);
+ if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck))
+ {
+ SwFrameFormat *pFrame;
+ // If we have just one single inline graphic then
+ // don't insert a field for the single frame, set
+ // the frames hyperlink field attribute directly.
+ pFrame = SwWW8ImplReader::ContainsSingleInlineGraphic(aRegion);
+ if (nullptr != pFrame)
+ {
+ const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>(
+ rEntry.m_pAttr.get());
+ SwFormatURL aURL;
+ aURL.SetURL(pAttr->GetValue(), false);
+ aURL.SetTargetFrameName(pAttr->GetTargetFrame());
+ pFrame->SetFormatAttr(aURL);
+ }
+ else
+ {
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr);
+ }
+ }
+ }
+ break;
+ default:
+ SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry);
+ break;
+ }
+}
+
+const SfxPoolItem* SwWW8FltControlStack::GetFormatAttr(const SwPosition& rPos,
+ sal_uInt16 nWhich)
+{
+ const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich);
+ if (!pItem)
+ {
+ SwContentNode const*const pNd = rPos.GetNode().GetContentNode();
+ if (!pNd)
+ pItem = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
+ else
+ {
+ /*
+ If we're hunting for the indent on a paragraph and need to use the
+ parent style indent, then return the indent in msword format, and
+ not writer format, because that's the style that the filter works
+ in (naturally)
+ */
+ if (nWhich == RES_MARGIN_FIRSTLINE
+ || nWhich == RES_MARGIN_TEXTLEFT
+ || nWhich == RES_MARGIN_RIGHT)
+ {
+ SfxItemState eState = SfxItemState::DEFAULT;
+ if (const SfxItemSet *pSet = pNd->GetpSwAttrSet())
+ eState = pSet->GetItemState(nWhich, false);
+ if (eState != SfxItemState::SET && m_rReader.m_nCurrentColl < m_rReader.m_vColl.size())
+ {
+ switch (nWhich)
+ {
+ case RES_MARGIN_FIRSTLINE:
+ pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordFirstLine.get();
+ break;
+ case RES_MARGIN_TEXTLEFT:
+ pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordLeftMargin.get();
+ break;
+ case RES_MARGIN_RIGHT:
+ pItem = m_rReader.m_vColl[m_rReader.m_nCurrentColl].m_pWordRightMargin.get();
+ break;
+ }
+ }
+ }
+
+ /*
+ If we're hunting for a character property, try and exact position
+ within the text node for lookup
+ */
+ if (pNd->IsTextNode())
+ {
+ const sal_Int32 nPos = rPos.GetContentIndex();
+ m_xScratchSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), nWhich, nWhich));
+ if (pNd->GetTextNode()->GetParaAttr(*m_xScratchSet, nPos, nPos))
+ pItem = m_xScratchSet->GetItem(nWhich);
+ }
+
+ if (!pItem)
+ pItem = &pNd->GetAttr(nWhich);
+ }
+ }
+ return pItem;
+}
+
+const SfxPoolItem* SwWW8FltControlStack::GetStackAttr(const SwPosition& rPos,
+ sal_uInt16 nWhich)
+{
+ SwFltPosition aFltPos(rPos);
+
+ size_t nSize = size();
+ while (nSize)
+ {
+ const SwFltStackEntry& rEntry = (*this)[ --nSize ];
+ if (rEntry.m_pAttr->Which() == nWhich)
+ {
+ if ( (rEntry.m_bOpen) ||
+ (
+ (rEntry.m_aMkPos.m_nNode <= aFltPos.m_nNode) &&
+ (rEntry.m_aPtPos.m_nNode >= aFltPos.m_nNode) &&
+ (rEntry.m_aMkPos.m_nContent <= aFltPos.m_nContent) &&
+ (rEntry.m_aPtPos.m_nContent > aFltPos.m_nContent)
+ )
+ )
+ /*
+ * e.g. half-open range [0-3) so asking for properties at 3
+ * means props that end at 3 are not included
+ */
+ {
+ return rEntry.m_pAttr.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool SwWW8FltRefStack::IsFootnoteEdnBkmField(
+ const SwFormatField& rFormatField,
+ sal_uInt16& rBkmNo)
+{
+ const SwField* pField = rFormatField.GetField();
+ sal_uInt16 nSubType;
+ if(pField && (SwFieldIds::GetRef == pField->Which())
+ && ((REF_FOOTNOTE == (nSubType = pField->GetSubType())) || (REF_ENDNOTE == nSubType))
+ && !static_cast<const SwGetRefField*>(pField)->GetSetRefName().isEmpty())
+ {
+ const IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t ppBkmk =
+ pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() );
+ if(ppBkmk != pMarkAccess->getAllMarksEnd())
+ {
+ // find Sequence No of corresponding Foot-/Endnote
+ rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin();
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwWW8FltRefStack::SetAttrInDoc(const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry)
+{
+ switch (rEntry.m_pAttr->Which())
+ {
+ /*
+ Look up these in our lists of bookmarks that were changed to
+ variables, and replace the ref field with a var field, otherwise
+ do normal (?) strange stuff
+ */
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ {
+ SwPaM aPaM(rEntry.m_aMkPos.m_nNode.GetNode(), SwNodeOffset(1), rEntry.m_aMkPos.m_nContent);
+
+ SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.m_pAttr.get());
+ SwField* pField = rFormatField.GetField();
+
+ if (!RefToVar(pField, rEntry))
+ {
+ sal_uInt16 nBkmNo;
+ if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) )
+ {
+ ::sw::mark::IMark const * const pMark = m_rDoc.getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo];
+
+ const SwPosition& rBkMrkPos = pMark->GetMarkPos();
+
+ SwTextNode* pText = rBkMrkPos.GetNode().GetTextNode();
+ if( pText && rBkMrkPos.GetContentIndex() )
+ {
+ SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt(
+ rBkMrkPos.GetContentIndex()-1, RES_TXTATR_FTN );
+ if( pFootnote )
+ {
+ sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo();
+
+ static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo );
+
+ if( pFootnote->GetFootnote().IsEndNote() )
+ static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE);
+ }
+ }
+ }
+ }
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(aPaM, *rEntry.m_pAttr);
+ MoveAttrs(*aPaM.GetPoint());
+ }
+ break;
+ case RES_FLTR_TOX:
+ SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
+ break;
+ default:
+ case RES_FLTR_BOOKMARK:
+ OSL_ENSURE(false, "EndStck used with non field, not what we want");
+ SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry);
+ break;
+ }
+}
+
+/*
+ For styles we will do our tabstop arithmetic in word style and adjust them to
+ writer style after all the styles have been finished and the dust settles as
+ to what affects what.
+
+ For explicit attributes we turn the adjusted writer tabstops back into 0 based
+ word indexes and we'll turn them back into writer indexes when setting them
+ into the document. If explicit left indent exist which affects them, then this
+ is handled when the explicit left indent is set into the document
+*/
+void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 0)
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_TABSTOP);
+ return;
+ }
+
+ sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0;
+ const sal_uInt8* pDel = pData + 1; // Del - Array
+
+ sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0;
+ const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array
+
+ short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns;
+ if (nRequiredLength > nLen)
+ {
+ // would require more data than available to describe!
+ // discard invalid record
+ nIns = 0;
+ nDel = 0;
+ }
+
+ WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array
+
+ std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP));
+
+ const SwFormat * pSty = nullptr;
+ sal_uInt16 nTabBase;
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef
+ {
+ nTabBase = m_vColl[m_nCurrentColl].m_nBase;
+ if (nTabBase < m_vColl.size()) // Based On
+ pSty = m_vColl[nTabBase].m_pFormat;
+ }
+ else
+ { // Text
+ nTabBase = m_nCurrentColl;
+ if (m_nCurrentColl < m_vColl.size())
+ pSty = m_vColl[m_nCurrentColl].m_pFormat;
+ //TODO: figure out else here
+ }
+
+ bool bFound = false;
+ std::unordered_set<size_t> aLoopWatch;
+ while (pSty && !bFound)
+ {
+ const SvxTabStopItem* pTabs;
+ bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false,
+ &pTabs) == SfxItemState::SET;
+ if( bFound )
+ {
+ aAttr.reset(pTabs->Clone());
+ }
+ else
+ {
+ sal_uInt16 nOldTabBase = nTabBase;
+ // If based on another
+ if (nTabBase < m_vColl.size())
+ nTabBase = m_vColl[nTabBase].m_nBase;
+
+ if (
+ nTabBase < m_vColl.size() &&
+ nOldTabBase != nTabBase &&
+ nTabBase != ww::stiNil
+ )
+ {
+ // #i61789: Stop searching when next style is the same as the
+ // current one (prevent loop)
+ aLoopWatch.insert(reinterpret_cast<size_t>(pSty));
+ if (nTabBase < m_vColl.size())
+ pSty = m_vColl[nTabBase].m_pFormat;
+ //TODO figure out the else branch
+
+ if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) !=
+ aLoopWatch.end())
+ pSty = nullptr;
+ }
+ else
+ pSty = nullptr; // Give up on the search
+ }
+ }
+
+ SvxTabStop aTabStop;
+ for (short i=0; i < nDel; ++i)
+ {
+ sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2));
+ if( nPos != SVX_TAB_NOTFOUND )
+ aAttr->Remove( nPos );
+ }
+
+ for (short i=0; i < nIns; ++i)
+ {
+ short nPos = SVBT16ToUInt16(pIns + i*2);
+ aTabStop.GetTabPos() = nPos;
+ switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc
+ {
+ case 0:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Left;
+ break;
+ case 1:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Center;
+ break;
+ case 2:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Right;
+ break;
+ case 3:
+ aTabStop.GetAdjustment() = SvxTabAdjust::Decimal;
+ break;
+ case 4:
+ continue; // Ignore Bar
+ }
+
+ switch( pTyp[i].aBits1 >> 3 & 0x7 )
+ {
+ case 0:
+ aTabStop.GetFill() = ' ';
+ break;
+ case 1:
+ aTabStop.GetFill() = '.';
+ break;
+ case 2:
+ aTabStop.GetFill() = '-';
+ break;
+ case 3:
+ case 4:
+ aTabStop.GetFill() = '_';
+ break;
+ }
+
+ sal_uInt16 nPos2 = aAttr->GetPos( nPos );
+ if (nPos2 != SVX_TAB_NOTFOUND)
+ aAttr->Remove(nPos2); // Or else Insert() refuses
+ aAttr->Insert(aTabStop);
+ }
+
+ if (nIns || nDel)
+ NewAttr(*aAttr);
+ else
+ {
+ // Here we have a tab definition which inserts no extra tabs, or deletes
+ // no existing tabs. An older version of writer is probably the creator
+ // of the document :-( . So if we are importing a style we can just
+ // ignore it. But if we are importing into text we cannot as during
+ // text SwWW8ImplReader::Read_Tab is called at the begin and end of
+ // the range the attrib affects, and ignoring it would upset the
+ // balance
+ if (!m_pCurrentColl) // not importing into a style
+ {
+ SvxTabStopItem aOrig = pSty ?
+ pSty->GetFormatAttr(RES_PARATR_TABSTOP) :
+ m_rDoc.GetAttrPool().GetDefaultItem(RES_PARATR_TABSTOP);
+ NewAttr(aOrig);
+ }
+ }
+}
+
+/**
+ * DOP
+*/
+void SwWW8ImplReader::ImportDop()
+{
+ // correct the LastPrinted date in DocumentProperties
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocuProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocuProps.is(), "DocumentProperties is null");
+ if (xDocuProps.is())
+ {
+ DateTime aLastPrinted(
+ msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint));
+ ::util::DateTime uDT = aLastPrinted.GetUNODateTime();
+ xDocuProps->setPrintDate(uDT);
+ }
+
+ // COMPATIBILITY FLAGS START
+
+ // #i78951# - remember the unknown compatibility options
+ // so as to export them out
+ m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions1(m_xWDop->GetCompatibilityOptions());
+ m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions2(m_xWDop->GetCompatibilityOptions2());
+
+ // The distance between two paragraphs is the sum of the bottom distance of
+ // the first paragraph and the top distance of the second one
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX, m_xWDop->fDontUseHTMLAutoSpacing);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, true );
+ // move tabs on alignment
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_COMPAT, true);
+ // #i24363# tab stops relative to indent
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, false);
+ // tdf#117923
+ m_rDoc.getIDocumentSettingAccess().set(
+ DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, true);
+ m_rDoc.getIDocumentSettingAccess().set(
+ DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, true);
+ // tdf#128195
+ m_rDoc.getIDocumentSettingAccess().set(
+ DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, true);
+ m_rDoc.getIDocumentSettingAccess().set(
+ DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA, true);
+ m_rDoc.getIDocumentSettingAccess().set(
+ DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND, true);
+
+ // Import Default Tabs
+ tools::Long nDefTabSiz = m_xWDop->dxaTab;
+ if( nDefTabSiz < 56 )
+ nDefTabSiz = 709;
+
+ // We want exactly one DefaultTab
+ SvxTabStopItem aNewTab( 1, sal_uInt16(nDefTabSiz), SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ const_cast<SvxTabStop&>(aNewTab[0]).GetAdjustment() = SvxTabAdjust::Default;
+
+ m_rDoc.GetAttrPool().SetPoolDefaultItem( aNewTab );
+
+ // Import zoom factor
+ if (m_xWDop->wScaleSaved)
+ {
+ //Import zoom type
+ sal_Int16 nZoomType;
+ switch (m_xWDop->zkSaved) {
+ case 1: nZoomType = sal_Int16(SvxZoomType::WHOLEPAGE); break;
+ case 2: nZoomType = sal_Int16(SvxZoomType::PAGEWIDTH); break;
+ case 3: nZoomType = sal_Int16(SvxZoomType::OPTIMAL); break;
+ default: nZoomType = sal_Int16(SvxZoomType::PERCENT); break;
+ }
+ uno::Sequence<beans::PropertyValue> aViewProps( comphelper::InitPropertySequence({
+ { "ZoomFactor", uno::Any(sal_Int16(m_xWDop->wScaleSaved)) },
+ { "VisibleBottom", uno::Any(sal_Int32(0)) },
+ { "ZoomType", uno::Any(nZoomType) }
+ }));
+
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer();
+ xBox->insertByIndex(sal_Int32(0), uno::Any(aViewProps));
+ uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY);
+ xViewDataSupplier->setViewData(xBox);
+ }
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_VIRTUAL_DEVICE, !m_xWDop->fUsePrinterMetrics);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_FLY_OFFSETS, true );
+
+ // No vertical offsets would lead to e.g. overlap of table and fly frames.
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS, true );
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, !m_xWDop->fNoLeading);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_NUMBERING, false);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, false); // #i47448#
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, !m_xWDop->fExpShRtn); // #i49277#, #i56856#
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, false); // #i53199#
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_LINE_SPACING, false);
+
+ // #i25901# - set new compatibility option
+ // 'Add paragraph and table spacing at bottom of table cells'
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, true);
+
+ // #i11860# - set new compatibility option
+ // 'Use former object positioning' to <false>
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_OBJECT_POS, false);
+
+ // #i27767# - set new compatibility option
+ // 'Consider Wrapping mode when positioning object' to <true>
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, true);
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, false); // #i13832#, #i24135#
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true );
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, true); // #i3952#
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::INVERT_BORDER_SPACING, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::UNBREAKABLE_NUMBERINGS, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CLIPPED_PICTURES, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true);
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true);
+ // rely on default for HYPHENATE_URLS=false
+
+ IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
+ if (m_xWDop->fDontBreakWrappedTables)
+ {
+ rIDSA.set(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES, true);
+ }
+
+ // COMPATIBILITY FLAGS END
+
+ // Import magic doptypography information, if it's there
+ if (m_xWwFib->m_nFib > 105)
+ ImportDopTypography(m_xWDop->doptypography);
+
+ // disable form design mode to be able to use imported controls directly
+ // #i31239# always disable form design mode, not only in protected docs
+ uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY);
+ if (xDocProps.is())
+ {
+ uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo();
+ if (xInfo.is())
+ {
+ if (xInfo->hasPropertyByName("ApplyFormDesignMode"))
+ xDocProps->setPropertyValue("ApplyFormDesignMode", css::uno::Any(false));
+ }
+
+ // for the benefit of DOCX - if this is ever saved in that format.
+ comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
+ uno::Sequence<beans::PropertyValue> aCompatSetting( comphelper::InitPropertySequence({
+ { "name", uno::Any(OUString("compatibilityMode")) },
+ { "uri", uno::Any(OUString("http://schemas.microsoft.com/office/word")) },
+ { "val", uno::Any(OUString("11")) } //11: Use features specified in MS-DOC.
+ }));
+
+ uno::Sequence< beans::PropertyValue > aValue(comphelper::InitPropertySequence({
+ { "compatSetting", uno::Any(aCompatSetting) }
+ }));
+
+ aGrabBag["CompatSettings"] <<= aValue;
+ xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
+ }
+
+ // The password can force read-only, comments-only, fill-in-form-only, or require track-changes.
+ // Treat comments-only like read-only since Writer has no support for that.
+ // Still allow editing of form fields, without requiring the password.
+ // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.)
+ if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev)
+ m_pDocShell->SetModifyPasswordHash(m_xWDop->lKeyProtDoc);
+ else if ( xDocProps.is() )
+ {
+ comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag"));
+ aGrabBag["FormPasswordHash"] <<= m_xWDop->lKeyProtDoc;
+ xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList()));
+ }
+
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ if (rOpt.IsUseEnhancedFields())
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FORM, m_xWDop->fProtEnabled );
+
+ if (m_xWDop->iGutterPos)
+ {
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::GUTTER_AT_TOP, true);
+ }
+}
+
+void SwWW8ImplReader::ImportDopTypography(const WW8DopTypography &rTypo)
+{
+ switch (rTypo.m_iLevelOfKinsoku)
+ {
+ case 2: // custom
+ {
+ i18n::ForbiddenCharacters aForbidden(OUString(+rTypo.m_rgxchFPunct),
+ OUString(+rTypo.m_rgxchLPunct));
+ // unary + makes sure not to accidentally call the deleted
+ // OUString(ConstCharArrayDetector<...>::TypeUtf16) ctor that takes the full
+ // m_rgxchFPunct, m_rgxchLPunct arrays with embedded NULs, instead of just the
+ // prefix leading up to the first NUL
+ m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(rTypo.GetConvertedLang(),
+ aForbidden);
+ // Obviously cannot set the standard level 1 for japanese, so
+ // bail out now while we can.
+ if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ This MS hack means that level 2 of japanese is not in operation, so we put
+ in what we know are the MS defaults, there is a complementary reverse
+ hack in the writer. Its our default as well, but we can set it anyway
+ as a flag for later.
+ */
+ if (!rTypo.m_reserved2)
+ {
+ i18n::ForbiddenCharacters aForbidden(WW8DopTypography::JapanNotBeginLevel1,
+ WW8DopTypography::JapanNotEndLevel1);
+ m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(LANGUAGE_JAPANESE,aForbidden);
+ }
+
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, bool(rTypo.m_fKerningPunct));
+ m_rDoc.getIDocumentSettingAccess().setCharacterCompressionType(static_cast<CharCompressType>(rTypo.m_iJustification));
+}
+
+/**
+ * Footnotes and Endnotes
+ */
+WW8ReaderSave::WW8ReaderSave(SwWW8ImplReader* pRdr ,WW8_CP nStartCp) :
+ mxTmpPos(pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_pPaM->GetPoint())),
+ mxOldStck(std::move(pRdr->m_xCtrlStck)),
+ mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)),
+ mxOldRedlines(std::move(pRdr->m_xRedlineStack)),
+ mxOldPlcxMan(pRdr->m_xPlcxMan),
+ mpWFlyPara(std::move(pRdr->m_xWFlyPara)),
+ mpSFlyPara(std::move(pRdr->m_xSFlyPara)),
+ mpPreviousNumPaM(pRdr->m_pPreviousNumPaM),
+ mpPrevNumRule(pRdr->m_pPrevNumRule),
+ mxTableDesc(std::move(pRdr->m_xTableDesc)),
+ mnInTable(pRdr->m_nInTable),
+ mnCurrentColl(pRdr->m_nCurrentColl),
+ mcSymbol(pRdr->m_cSymbol),
+ mbIgnoreText(pRdr->m_bIgnoreText),
+ mbSymbol(pRdr->m_bSymbol),
+ mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn),
+ mbTxbxFlySection(pRdr->m_bTxbxFlySection),
+ mbAnl(pRdr->m_bAnl),
+ mbInHyperlink(pRdr->m_bInHyperlink),
+ mbPgSecBreak(pRdr->m_bPgSecBreak),
+ mbWasParaEnd(pRdr->m_bWasParaEnd),
+ mbHasBorder(pRdr->m_bHasBorder),
+ mbFirstPara(pRdr->m_bFirstPara)
+{
+ pRdr->m_bSymbol = false;
+ pRdr->m_bHdFtFootnoteEdn = true;
+ pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd
+ = pRdr->m_bHasBorder = false;
+ pRdr->m_bFirstPara = true;
+ pRdr->m_nInTable = 0;
+ pRdr->m_pPreviousNumPaM = nullptr;
+ pRdr->m_pPrevNumRule = nullptr;
+ pRdr->m_nCurrentColl = 0;
+
+ pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(pRdr->m_rDoc, pRdr->m_nFieldFlags,
+ *pRdr));
+
+ pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc));
+
+ pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(pRdr->m_rDoc, pRdr->m_nFieldFlags));
+
+ // Save the attribute manager: we need this as the newly created PLCFx Manager
+ // access the same FKPs as the old one and their Start-End position changes.
+ if (pRdr->m_xPlcxMan)
+ pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave);
+
+ if (nStartCp != -1)
+ {
+ pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(),
+ mxOldPlcxMan->GetManType(), nStartCp);
+ }
+
+ maOldApos.push_back(false);
+ maOldApos.swap(pRdr->m_aApos);
+ maOldFieldStack.swap(pRdr->m_aFieldStack);
+}
+
+void WW8ReaderSave::Restore( SwWW8ImplReader* pRdr )
+{
+ pRdr->m_xWFlyPara = std::move(mpWFlyPara);
+ pRdr->m_xSFlyPara = std::move(mpSFlyPara);
+ pRdr->m_pPreviousNumPaM = mpPreviousNumPaM;
+ pRdr->m_pPrevNumRule = mpPrevNumRule;
+ pRdr->m_xTableDesc = std::move(mxTableDesc);
+ pRdr->m_cSymbol = mcSymbol;
+ pRdr->m_bSymbol = mbSymbol;
+ pRdr->m_bIgnoreText = mbIgnoreText;
+ pRdr->m_bHdFtFootnoteEdn = mbHdFtFootnoteEdn;
+ pRdr->m_bTxbxFlySection = mbTxbxFlySection;
+ pRdr->m_nInTable = mnInTable;
+ pRdr->m_bAnl = mbAnl;
+ pRdr->m_bInHyperlink = mbInHyperlink;
+ pRdr->m_bWasParaEnd = mbWasParaEnd;
+ pRdr->m_bPgSecBreak = mbPgSecBreak;
+ pRdr->m_nCurrentColl = mnCurrentColl;
+ pRdr->m_bHasBorder = mbHasBorder;
+ pRdr->m_bFirstPara = mbFirstPara;
+
+ // Close all attributes as attributes could be created that extend the Fly
+ pRdr->DeleteCtrlStack();
+ pRdr->m_xCtrlStck = std::move(mxOldStck);
+
+ pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint());
+
+ // ofz#37322 drop m_oLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
+ // place, or somewhere close if that place got destroyed
+ std::shared_ptr<SwUnoCursor> xLastAnchorCursor(pRdr->m_oLastAnchorPos ? pRdr->m_rDoc.CreateUnoCursor(*pRdr->m_oLastAnchorPos) : nullptr);
+ pRdr->m_oLastAnchorPos.reset();
+
+ pRdr->m_xRedlineStack = std::move(mxOldRedlines);
+
+ if (xLastAnchorCursor)
+ pRdr->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
+
+ pRdr->DeleteAnchorStack();
+ pRdr->m_xAnchorStck = std::move(mxOldAnchorStck);
+
+ *pRdr->m_pPaM->GetPoint() = GetStartPos();
+
+ if (mxOldPlcxMan != pRdr->m_xPlcxMan)
+ pRdr->m_xPlcxMan = mxOldPlcxMan;
+ if (pRdr->m_xPlcxMan)
+ pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave);
+ pRdr->m_aApos.swap(maOldApos);
+ pRdr->m_aFieldStack.swap(maOldFieldStack);
+}
+
+void SwWW8ImplReader::Read_HdFtFootnoteText( const SwNodeIndex* pSttIdx,
+ WW8_CP nStartCp, WW8_CP nLen, ManTypes nType )
+{
+ if (nStartCp < 0 || nLen < 0)
+ return;
+
+ // Saves Flags (amongst other things) and resets them
+ WW8ReaderSave aSave( this );
+
+ m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
+
+ // Read Text for Header, Footer or Footnote
+ ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so
+ aSave.Restore( this );
+}
+
+/**
+ * Use authornames, if not available fall back to initials.
+ */
+tools::Long SwWW8ImplReader::Read_And(WW8PLCFManResult* pRes)
+{
+ WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn();
+ if (!pSD)
+ return 0;
+
+ const void* pData = pSD->GetData();
+ if (!pData)
+ return 0;
+
+ OUString sAuthor;
+ OUString sInitials;
+ if( m_bVer67 )
+ {
+ const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData);
+ const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst));
+ if (pA)
+ sAuthor = *pA;
+ else
+ {
+ const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0],
+ SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
+ sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252);
+ }
+ }
+ else
+ {
+ const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData);
+ {
+ const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]),
+ SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1);
+ OUStringBuffer aBuf;
+ aBuf.setLength(nLen);
+ for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx)
+ aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]);
+ sInitials = aBuf.makeStringAndClear();
+ }
+
+ if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)))
+ sAuthor = *pA;
+ else
+ sAuthor = sInitials;
+ }
+
+ sal_uInt32 nDateTime = 0;
+
+ if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments
+ {
+ sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs
+ if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex)
+ nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18)));
+ }
+
+ DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime);
+
+ OUString sText;
+ std::optional<OutlinerParaObject> pOutliner = ImportAsOutliner( sText, pRes->nCp2OrIdx,
+ pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND );
+
+ m_xFormatOfJustInsertedApo.reset();
+ SwPostItField aPostIt(
+ static_cast<SwPostItFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit)), sAuthor,
+ sText, sInitials, OUString(), aDate );
+ aPostIt.SetTextObject(std::move(pOutliner));
+
+ SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End());
+ m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN));
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(aEnd, SwFormatField(aPostIt));
+ m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN);
+ // If this is a range, make sure that it ends after the just inserted character, not before it.
+ m_xReffedStck->MoveAttrs(*aEnd.GetPoint(), SwFltControlStack::MoveAttrsMode::POSTIT_INSERTED);
+
+ return 0;
+}
+
+void SwWW8ImplReader::Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen,
+ SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth)
+{
+ const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx();
+ OSL_ENSURE(pSttIdx, "impossible");
+ if (!pSttIdx)
+ return;
+
+ SwPosition aTmpPos(*m_pPaM->GetPoint());
+
+ m_pPaM->GetPoint()->Assign( pSttIdx->GetIndex() + 1 );
+
+ // tdf#122425: Explicitly remove borders and spacing
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aFlySet(m_rDoc.GetAttrPool());
+ Reader::ResetFrameFormatAttrs(aFlySet);
+
+ SwFlyFrameFormat* pFrame
+ = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet);
+
+ SwFormatAnchor aAnch( pFrame->GetAnchor() );
+ aAnch.SetType( RndStdIds::FLY_AT_PARA );
+ pFrame->SetFormatAttr( aAnch );
+ SwFormatFrameSize aSz(SwFrameSize::Minimum, nPageWidth, MINLAY);
+ SwFrameSize eFrameSize = SwFrameSize::Minimum;
+ if( eFrameSize != aSz.GetWidthSizeType() )
+ aSz.SetWidthSizeType( eFrameSize );
+ pFrame->SetFormatAttr(aSz);
+ pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH));
+ pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO
+
+ // #i43427# - send frame for header/footer into background.
+ pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) );
+ SdrObject* pFrameObj = CreateContactObject( pFrame );
+ OSL_ENSURE( pFrameObj,
+ "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" );
+ if ( pFrameObj )
+ {
+ pFrameObj->SetOrdNum( 0 );
+ }
+ MoveInsideFly(pFrame);
+
+ const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx();
+
+ Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT);
+
+ MoveOutsideFly(pFrame, aTmpPos);
+}
+
+void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat)
+{
+ const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx();
+ if (!pSttIdx)
+ return;
+
+ SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position
+
+ Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT);
+
+ *m_pPaM->GetPoint() = aTmpPos;
+}
+
+bool SwWW8ImplReader::isValid_HdFt_CP(WW8_CP nHeaderCP) const
+{
+ // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd
+ return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0);
+}
+
+bool SwWW8ImplReader::HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt,
+ int nSect)
+{
+ if (m_xHdFt)
+ {
+ WW8_CP nStart, nLen;
+ sal_uInt8 nNumber = 5;
+
+ for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
+ {
+ if (nI & nWhichItems)
+ {
+ bool bOk = true;
+ if( m_bVer67 )
+ bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 );
+ else
+ {
+ m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
+ bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
+ }
+
+ if (bOk)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev,
+ const wwSection &rSection)
+{
+ sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt;
+ SwPageDesc *pPD = rSection.mpPage;
+
+ if( !m_xHdFt )
+ return;
+
+ WW8_CP nStart, nLen;
+ sal_uInt8 nNumber = 5;
+
+ // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST}
+ // corresponding to bit fields in grpfIhdt indicating which
+ // header/footer(s) are present in this section
+ for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- )
+ {
+ if (nI & grpfIhdt)
+ {
+ bool bOk = true;
+ if( m_bVer67 )
+ bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 );
+ else
+ {
+ m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen);
+ bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart);
+ }
+
+ bool bUseLeft
+ = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0;
+ bool bUseFirst
+ = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0;
+
+ // If we are loading a first-page header/footer which is not
+ // actually enabled in this section (it still needs to be
+ // loaded as it may be inherited by a later section)
+ bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage();
+
+ bool bFooter
+ = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0;
+
+ SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft()
+ : bUseFirst ? pPD->GetFirstMaster()
+ : pPD->GetMaster();
+
+ SwFrameFormat* pHdFtFormat;
+ // If we have empty first page header and footer.
+ bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST);
+ if (bFooter)
+ {
+ m_bIsFooter = true;
+ //#i17196# Cannot have left without right
+ if (!bDisabledFirst
+ && !pPD->GetMaster().GetFooter().GetFooterFormat())
+ pPD->GetMaster().SetFormatAttr(SwFormatFooter(true));
+ if (bUseLeft)
+ pPD->GetLeft().SetFormatAttr(SwFormatFooter(true));
+ if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
+ pPD->GetFirstMaster().SetFormatAttr(SwFormatFooter(true));
+ pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat());
+ }
+ else
+ {
+ m_bIsHeader = true;
+ //#i17196# Cannot have left without right
+ if (!bDisabledFirst
+ && !pPD->GetMaster().GetHeader().GetHeaderFormat())
+ pPD->GetMaster().SetFormatAttr(SwFormatHeader(true));
+ if (bUseLeft)
+ pPD->GetLeft().SetFormatAttr(SwFormatHeader(true));
+ if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst))
+ pPD->GetFirstMaster().SetFormatAttr(SwFormatHeader(true));
+ pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat());
+ }
+
+ if (bOk)
+ {
+ bool bHackRequired = false;
+ if (m_bIsHeader && rSection.IsFixedHeightHeader())
+ bHackRequired = true;
+ else if (m_bIsFooter && rSection.IsFixedHeightFooter())
+ bHackRequired = true;
+
+ if (bHackRequired)
+ {
+ Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat,
+ static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) );
+ }
+ else
+ Read_HdFtText(nStart, nLen, pHdFtFormat);
+ }
+ else if (pPrev)
+ CopyPageDescHdFt(pPrev, pPD, nI);
+
+ m_bIsHeader = m_bIsFooter = false;
+ }
+ }
+}
+
+bool wwSectionManager::SectionIsProtected(const wwSection &rSection) const
+{
+ return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() );
+}
+
+void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect,
+ const wwSection *pPrevious)
+{
+ // Header/Footer not present
+ if (!rSection.maSep.grpfIhdt)
+ return;
+
+ OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page");
+ if (rSection.mpPage)
+ {
+ mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr,
+ rSection);
+ }
+
+ // Header/Footer - Update Index
+ // So that the index is still valid later on
+ if (mrReader.m_xHdFt)
+ mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt);
+
+}
+
+void SwWW8ImplReader::AppendTextNode(SwPosition& rPos)
+{
+ SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
+
+ const SwNumRule* pRule = nullptr;
+
+ if (pText != nullptr)
+ pRule = sw::util::GetNumRuleFromTextNode(*pText);
+
+ // tdf#64222 / tdf#150613 filter out the "paragraph marker" formatting and
+ // set it as a separate paragraph property, just like we do for DOCX.
+ // This is only being used for numbering currently, so limiting to that context.
+ if (pRule)
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1, RES_TXTATR_CHARFMT,
+ RES_TXTATR_CHARFMT, RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
+ items(m_pPaM->GetDoc().GetAttrPool());
+
+ SfxWhichIter aIter(items);
+ for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
+ {
+ const SfxPoolItem* pItem = m_xCtrlStck->GetStackAttr(rPos, nWhich);
+ if (pItem)
+ items.Put(*pItem);
+ }
+ SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
+ item.SetStyleHandle(std::make_shared<SfxItemSet>(items));
+ pText->SetAttr(item);
+ }
+
+ if (
+ pRule && !m_xWDop->fDontUseHTMLAutoSpacing &&
+ (m_bParaAutoBefore || m_bParaAutoAfter)
+ )
+ {
+ // If after spacing is set to auto, set the after space to 0
+ if (m_bParaAutoAfter)
+ SetLowerSpacing(*m_pPaM, 0);
+
+ // If the previous textnode had numbering and
+ // and before spacing is set to auto, set before space to 0
+ if(m_pPrevNumRule && m_bParaAutoBefore)
+ SetUpperSpacing(*m_pPaM, 0);
+
+ // If the previous numbering rule was different we need
+ // to insert a space after the previous paragraph
+ if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM)
+ SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
+
+ // cache current paragraph
+ if(m_pPreviousNumPaM)
+ {
+ delete m_pPreviousNumPaM;
+ m_pPreviousNumPaM = nullptr;
+ }
+
+ m_pPreviousNumPaM = new SwPaM(*m_pPaM, m_pPaM);
+ m_pPrevNumRule = pRule;
+ }
+ else if(!pRule && m_pPreviousNumPaM)
+ {
+ // If the previous paragraph has numbering but the current one does not
+ // we need to add a space after the previous paragraph
+ SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
+ delete m_pPreviousNumPaM;
+ m_pPreviousNumPaM = nullptr;
+ m_pPrevNumRule = nullptr;
+ }
+ else
+ {
+ // clear paragraph cache
+ if(m_pPreviousNumPaM)
+ {
+ delete m_pPreviousNumPaM;
+ m_pPreviousNumPaM = nullptr;
+ }
+ m_pPrevNumRule = pRule;
+ }
+
+ // If this is the first paragraph in the document and
+ // Auto-spacing before paragraph is set,
+ // set the upper spacing value to 0
+ if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing)
+ SetUpperSpacing(*m_pPaM, 0);
+
+ m_bFirstPara = false;
+
+ m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos);
+
+ // We can flush all anchored graphics at the end of a paragraph.
+ m_xAnchorStck->Flush();
+}
+
+bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper )
+{
+ bool bRet = false;
+ const SwPosition* pSpacingPos = rMyPam.GetPoint();
+
+ const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE);
+
+ if(pULSpaceItem != nullptr)
+ {
+ SvxULSpaceItem aUL(*pULSpaceItem);
+
+ if(bIsUpper)
+ aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) );
+ else
+ aUL.SetLower( static_cast< sal_uInt16 >(nSpace) );
+
+ const sal_Int32 nEnd = pSpacingPos->GetContentIndex();
+ rMyPam.GetPoint()->SetContent(0);
+ m_xCtrlStck->NewAttr(*pSpacingPos, aUL);
+ rMyPam.GetPoint()->SetContent(nEnd);
+ m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE);
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool SwWW8ImplReader::SetLowerSpacing(SwPaM &rMyPam, int nSpace)
+{
+ return SetSpacing(rMyPam, nSpace, false);
+}
+
+bool SwWW8ImplReader::SetUpperSpacing(SwPaM &rMyPam, int nSpace)
+{
+ return SetSpacing(rMyPam, nSpace, true);
+}
+
+sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const
+{
+ if (m_bVer67)
+ return NS_sprm::v6::sprmPTtp;
+ return nLevel ? NS_sprm::PFInnerTtp::val : NS_sprm::PFTtp::val;
+}
+
+void SwWW8ImplReader::EndSpecial()
+{
+ // Frame/Table/Anl
+ if (m_bAnl)
+ StopAllAnl(); // -> bAnl = false
+
+ while(m_aApos.size() > 1)
+ {
+ StopTable();
+ m_aApos.pop_back();
+ --m_nInTable;
+ if (m_aApos[m_nInTable])
+ StopApo();
+ }
+
+ if (m_aApos[0])
+ StopApo();
+
+ OSL_ENSURE(!m_nInTable, "unclosed table!");
+}
+
+bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp)
+{
+ // Frame/Table/Anl
+ if (m_bInHyperlink)
+ return false;
+
+ rbReSync = false;
+
+ OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!");
+
+ // TabRowEnd
+ bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr);
+
+// Unfortunately, for every paragraph we need to check first whether
+// they contain a sprm 29 (0x261B), which starts an APO.
+// All other sprms then refer to that APO and not to the normal text
+// surrounding it.
+// The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13).
+
+// WW: Table in APO is possible (Both Start-Ends occur at the same time)
+// WW: APO in Table not possible
+
+// This mean that of a Table is the content of an APO, the APO start needs
+// to be edited first, so that the Table remains in the APO and not the
+// other way around.
+// At the End, however, we need to edit the Table End first as the APO
+// must end after that Table (or else we never find the APO End).
+
+// The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl.
+
+// If the Table is within an APO the TabRowEnd Area misses the
+// APO settings.
+// To not end the APO there, we do not call ProcessApo
+
+// KHZ: When there is a table inside the Apo the Apo-flags are also
+// missing for the 2nd, 3rd... paragraphs of each cell.
+
+// 1st look for in-table flag, for 2000+ there is a subtable flag to
+// be considered, the sprm 6649 gives the level of the table
+ sal_uInt8 nCellLevel = 0;
+
+ if (m_bVer67)
+ nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm);
+ else
+ {
+ nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm);
+ if (!nCellLevel)
+ nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm);
+ }
+ do
+ {
+ WW8_TablePos *pTabPos=nullptr;
+ WW8_TablePos aTabPos;
+ if(nCellLevel && !m_bVer67)
+ {
+ WW8PLCFxSave1 aSave;
+ m_xPlcxMan->GetPap()->Save( aSave );
+ rbReSync = true;
+ WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
+ WW8_CP nMyStartCp=nStartCp;
+
+ SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649);
+ if (aLevel.pSprm && aLevel.nRemainingData >= 1)
+ nCellLevel = *aLevel.pSprm;
+
+ bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1));
+
+ // Bad Table, remain unchanged in level, e.g. #i19667#
+ if (!bHasRowEnd)
+ nCellLevel = static_cast< sal_uInt8 >(m_nInTable);
+
+ if (bHasRowEnd && ParseTabPos(&aTabPos,pPap))
+ pTabPos = &aTabPos;
+
+ m_xPlcxMan->GetPap()->Restore( aSave );
+ }
+
+ // Then look if we are in an Apo
+
+ ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos);
+
+ // Look to see if we are in a Table, but Table in foot/end note not allowed
+ bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn;
+
+ bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn;
+
+ m_bWasTabRowEnd = false; // must be deactivated right here to prevent next
+ // WW8TabDesc::TableCellEnd() from making nonsense
+
+ if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop()))
+ bStopTab = bStartTab = true; // Required to stop and start table
+
+ // Test for Anl (Numbering) and process all events in the right order
+ if( m_bAnl && !bTableRowEnd )
+ {
+ SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13);
+ const sal_uInt8* pSprm13 = aSprm13.pSprm;
+ if (pSprm13 && aSprm13.nRemainingData >= 1)
+ { // Still Anl left?
+ sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 ));
+ if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change
+ || aApo.HasStartStop() // Forced Anl end
+ || bStopTab || bStartTab )
+ {
+ StopAnlToRestart(nT); // Anl-Restart (= change) over sprms
+ }
+ else
+ {
+ NextAnlLine( pSprm13 ); // Next Anl Line
+ }
+ }
+ else
+ { // Regular Anl end
+ StopAllAnl(); // Actual end
+ }
+ }
+ if (bStopTab)
+ {
+ StopTable();
+ m_aApos.pop_back();
+ --m_nInTable;
+ }
+ if (aApo.mbStopApo)
+ {
+ StopApo();
+ m_aApos[m_nInTable] = false;
+ }
+
+ if (aApo.mbStartApo)
+ {
+ m_aApos[m_nInTable] = StartApo(aApo, pTabPos);
+ // We need an ReSync after StartApo
+ // (actually only if the Apo extends past a FKP border)
+ rbReSync = true;
+ }
+ if (bStartTab)
+ {
+ WW8PLCFxSave1 aSave;
+ m_xPlcxMan->GetPap()->Save( aSave );
+
+ // Numbering for cell borders causes a crash -> no Anls in Tables
+ if (m_bAnl)
+ StopAllAnl();
+
+ if(m_nInTable < nCellLevel)
+ {
+ if (StartTable(nStartCp))
+ ++m_nInTable;
+ else
+ break;
+ m_aApos.push_back(false);
+ }
+
+ if(m_nInTable >= nCellLevel)
+ {
+ // We need an ReSync after StartTable
+ // (actually only if the Apo extends past a FKP border)
+ rbReSync = true;
+ m_xPlcxMan->GetPap()->Restore( aSave );
+ }
+ }
+ } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel));
+ return bTableRowEnd;
+}
+
+rtl_TextEncoding SwWW8ImplReader::GetCharSetFromLanguage()
+{
+ /*
+ #i22206#/#i52786#
+ The (default) character set used for a run of text is the default
+ character set for the version of Word that last saved the document.
+
+ This is a bit tentative, more might be required if the concept is correct.
+ When later version of word write older 6/95 documents the charset is
+ correctly set in the character runs involved, so it's hard to reproduce
+ documents that require this to be sure of the process involved.
+ */
+ const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_LANGUAGE);
+ LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
+ css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
+ return msfilter::util::getBestTextEncodingFromLocale(aLocale);
+}
+
+rtl_TextEncoding SwWW8ImplReader::GetCJKCharSetFromLanguage()
+{
+ /*
+ #i22206#/#i52786#
+ The (default) character set used for a run of text is the default
+ character set for the version of Word that last saved the document.
+
+ This is a bit tentative, more might be required if the concept is correct.
+ When later version of word write older 6/95 documents the charset is
+ correctly set in the character runs involved, so it's hard to reproduce
+ documents that require this to be sure of the process involved.
+ */
+ const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
+ LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM;
+ css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang));
+ return msfilter::util::getBestTextEncodingFromLocale(aLocale);
+}
+
+rtl_TextEncoding SwWW8ImplReader::GetCurrentCharSet()
+{
+ /*
+ #i2015
+ If the hard charset is set use it, if not see if there is an open
+ character run that has set the charset, if not then fallback to the
+ current underlying paragraph style.
+ */
+ rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
+ if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
+ {
+ if (!m_bVer67)
+ eSrcCharSet = GetCharSetFromLanguage();
+ else if (!m_aFontSrcCharSets.empty())
+ eSrcCharSet = m_aFontSrcCharSets.top();
+ if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
+ eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet();
+ if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
+ eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet();
+ if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
+ eSrcCharSet = GetCharSetFromLanguage();
+ }
+ return eSrcCharSet;
+}
+
+//Takashi Ono for CJK
+rtl_TextEncoding SwWW8ImplReader::GetCurrentCJKCharSet()
+{
+ /*
+ #i2015
+ If the hard charset is set use it, if not see if there is an open
+ character run that has set the charset, if not then fallback to the
+ current underlying paragraph style.
+ */
+ rtl_TextEncoding eSrcCharSet = m_eHardCharSet;
+ if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
+ {
+ if (!m_aFontSrcCJKCharSets.empty())
+ eSrcCharSet = m_aFontSrcCJKCharSets.top();
+ if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() )
+ eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet();
+ if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size())
+ eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet();
+ if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW)
+ eSrcCharSet = GetCJKCharSetFromLanguage();
+ }
+ return eSrcCharSet;
+}
+
+void SwWW8ImplReader::PostProcessAttrs()
+{
+ if (m_pPostProcessAttrsInfo == nullptr)
+ return;
+
+ SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(),
+ *pItem);
+ m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(),
+ pItem->Which());
+ }
+
+ m_pPostProcessAttrsInfo.reset();
+}
+
+/*
+ #i9240#
+ It appears that some documents that are in a baltic 8 bit encoding which has
+ some undefined characters can have use made of those characters, in which
+ case they default to CP1252. If not then it's perhaps that the font encoding
+ is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding
+ is always 1252.
+
+ So an encoding converter that on an undefined character attempts to
+ convert from 1252 on the undefined character
+*/
+static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter,
+ char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen)
+{
+ const sal_uInt32 nFlags =
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
+ RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
+ RTL_TEXTTOUNICODE_FLAGS_FLUSH;
+
+ const sal_uInt32 nFlags2 =
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE |
+ RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE |
+ RTL_TEXTTOUNICODE_FLAGS_FLUSH;
+
+ std::size_t nDestChars=0;
+ std::size_t nConverted=0;
+
+ do
+ {
+ sal_uInt32 nInfo = 0;
+ sal_Size nThisConverted=0;
+
+ nDestChars += rtl_convertTextToUnicode(hConverter, nullptr,
+ pIn+nConverted, nInLen-nConverted,
+ pOut+nDestChars, nOutLen-nDestChars,
+ nFlags, &nInfo, &nThisConverted);
+
+ OSL_ENSURE(nInfo == 0, "A character conversion failed!");
+
+ nConverted += nThisConverted;
+
+ if (
+ nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED ||
+ nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED
+ )
+ {
+ sal_Size nOtherConverted;
+ rtl_TextToUnicodeConverter hCP1252Converter =
+ rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252);
+ nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr,
+ pIn+nConverted, 1,
+ pOut+nDestChars, nOutLen-nDestChars,
+ nFlags2, &nInfo, &nOtherConverted);
+ rtl_destroyTextToUnicodeConverter(hCP1252Converter);
+ nConverted+=1;
+ }
+ } while (nConverted < nInLen);
+
+ return nDestChars;
+}
+
+bool SwWW8ImplReader::LangUsesHindiNumbers(LanguageType nLang)
+{
+ bool bResult = false;
+
+ switch (static_cast<sal_uInt16>(nLang))
+ {
+ case 0x1401: // Arabic(Algeria)
+ case 0x3c01: // Arabic(Bahrain)
+ case 0xc01: // Arabic(Egypt)
+ case 0x801: // Arabic(Iraq)
+ case 0x2c01: // Arabic (Jordan)
+ case 0x3401: // Arabic(Kuwait)
+ case 0x3001: // Arabic(Lebanon)
+ case 0x1001: // Arabic(Libya)
+ case 0x1801: // Arabic(Morocco)
+ case 0x2001: // Arabic(Oman)
+ case 0x4001: // Arabic(Qatar)
+ case 0x401: // Arabic(Saudi Arabia)
+ case 0x2801: // Arabic(Syria)
+ case 0x1c01: // Arabic(Tunisia)
+ case 0x3801: // Arabic(U.A.E)
+ case 0x2401: // Arabic(Yemen)
+ bResult = true;
+ break;
+ default:
+ break;
+ }
+
+ return bResult;
+}
+
+sal_Unicode SwWW8ImplReader::TranslateToHindiNumbers(sal_Unicode nChar)
+{
+ if (nChar >= 0x0030 && nChar <= 0x0039)
+ return nChar + 0x0630;
+
+ return nChar;
+}
+
+namespace
+{
+ OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen)
+ {
+ //if read len was in or around that of allocated len, just reuse pStr
+ if (nAllocLen < pStr->length + 256)
+ return OUString(pStr, SAL_NO_ACQUIRE);
+ //otherwise copy the shorter used section to release extra mem
+ OUString sRet(pStr->buffer, pStr->length);
+ rtl_uString_release(pStr);
+ return sRet;
+ }
+}
+
+/**
+ * Return value: true for non special chars
+ */
+bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs)
+{
+ sal_Int32 nRequestedStrLen = nEnd - rPos;
+
+ OSL_ENSURE(nRequestedStrLen, "String is 0");
+ if (nRequestedStrLen <= 0)
+ return true;
+
+ WW8_CP nCp;
+ const bool bFail = o3tl::checked_add(nCpOfs, rPos, nCp);
+ if (bFail)
+ {
+ rPos+=nRequestedStrLen;
+ return true;
+ }
+
+ sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCp, &m_bIsUnicode);
+ bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
+ OSL_ENSURE(bValidPos, "Document claimed to have more text than available");
+ if (!bValidPos)
+ {
+ // Swallow missing range, e.g. #i95550#
+ rPos+=nRequestedStrLen;
+ return true;
+ }
+
+ std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1);
+ OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available");
+ if (!nAvailableStrLen)
+ {
+ // Swallow missing range, e.g. #i95550#
+ rPos+=nRequestedStrLen;
+ return true;
+ }
+
+ sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen);
+
+ // Reset Unicode flag and correct FilePos if needed.
+ // Note: Seek is not expensive, as we're checking inline whether or not
+ // the correct FilePos has already been reached.
+ const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1);
+
+ rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() :
+ RTL_TEXTENCODING_MS_1252;
+ if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932)
+ {
+ /*
+ fdo#82904
+
+ Older documents exported as word 95 that use unicode aware fonts will
+ have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on
+ export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious
+ pain.
+
+ We will try and use a fallback encoding if the conversion from
+ RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document
+ which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form
+ valid RTL_TEXTENCODING_MS_932 by chance :-(
+
+ We're not the only ones that struggle with this: Here's the help from
+ MSOffice 2003 on the topic:
+
+ <<
+ Earlier versions of Microsoft Word were sometimes used in conjunction with
+ third-party language-processing add-in programs designed to support Chinese or
+ Korean on English versions of Microsoft Windows. Use of these add-ins sometimes
+ results in incorrect text display in more recent versions of Word.
+
+ However, you can set options to convert these documents so that text is
+ displayed correctly. On the Tools menu, click Options, and then click the
+ General tab. In the English Word 6.0/95 documents list, select Contain Asian
+ text (to have Word interpret the text as Asian code page data, regardless of
+ its font) or Automatically detect Asian text (to have Word attempt to determine
+ which parts of the text are meant to be Asian).
+ >>
+
+ What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if
+ the language is not Japanese
+ */
+
+ const SvxLanguageItem * pItem = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
+ if (pItem != nullptr && LANGUAGE_JAPANESE != pItem->GetLanguage())
+ {
+ SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding");
+ eSrcCharSet = GetCharSetFromLanguage();
+ }
+ }
+ const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() :
+ RTL_TEXTENCODING_MS_1252;
+
+ // allocate unicode string data
+ auto l = [](rtl_uString* p){rtl_uString_release(p);};
+ std::unique_ptr<rtl_uString, decltype(l)> xStr(rtl_uString_alloc(nStrLen), l);
+ sal_Unicode* pBuffer = xStr->buffer;
+ sal_Unicode* pWork = pBuffer;
+
+ std::unique_ptr<char[]> p8Bits;
+
+ rtl_TextToUnicodeConverter hConverter = nullptr;
+ if (!m_bIsUnicode || m_bVer67)
+ hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet);
+
+ if (!m_bIsUnicode)
+ p8Bits.reset( new char[nStrLen] );
+
+ // read the stream data
+ sal_uInt8 nBCode = 0;
+ sal_uInt16 nUCode;
+
+ LanguageType nCTLLang = LANGUAGE_SYSTEM;
+ const SvxLanguageItem * pItem = GetFormatAttr(RES_CHRATR_CTL_LANGUAGE);
+ if (pItem != nullptr)
+ nCTLLang = pItem->GetLanguage();
+
+ sal_Int32 nL2;
+ for (nL2 = 0; nL2 < nStrLen; ++nL2)
+ {
+ if (m_bIsUnicode)
+ m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes
+ else
+ {
+ m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte
+ nUCode = nBCode;
+ }
+
+ if (!m_pStrm->good())
+ {
+ rPos = WW8_CP_MAX-10; // -> eof or other error
+ return true;
+ }
+
+ if ((32 > nUCode) || (0xa0 == nUCode))
+ {
+ m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 );
+ break; // Special character < 32, == 0xa0 found
+ }
+
+ if (m_bIsUnicode)
+ {
+ if (!m_bVer67)
+ *pWork++ = nUCode;
+ else
+ {
+ if (nUCode >= 0x3000) //0x8000 ?
+ {
+ char aTest[2];
+ aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8);
+ aTest[1] = static_cast< char >(nUCode & 0x00FF);
+ OUString aTemp(aTest, 2, eSrcCJKCharSet);
+ OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory");
+ *pWork++ = aTemp[0];
+ }
+ else
+ {
+ char cTest = static_cast< char >(nUCode & 0x00FF);
+ pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1);
+ }
+ }
+ }
+ else
+ p8Bits[nL2] = nBCode;
+ }
+
+ if (nL2)
+ {
+ const sal_Int32 nEndUsed = !m_bIsUnicode
+ ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen)
+ : pWork - pBuffer;
+
+ if (m_bRegardHindiDigits && m_bBidi && LangUsesHindiNumbers(nCTLLang))
+ {
+ for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer)
+ *pBuffer = TranslateToHindiNumbers(*pBuffer);
+ }
+
+ xStr->buffer[nEndUsed] = 0;
+ xStr->length = nEndUsed;
+
+ emulateMSWordAddTextToParagraph(makeOUString(xStr.release(), nStrLen));
+ rPos += nL2;
+ if (!m_aApos.back()) // a para end in apo doesn't count
+ m_bWasParaEnd = false; // No CR
+ }
+
+ if (hConverter)
+ rtl_destroyTextToUnicodeConverter(hConverter);
+ return nL2 >= nStrLen;
+}
+
+#define MSASCII SAL_MAX_INT16
+
+namespace
+{
+ // We want to force weak chars inside 0x0020 to 0x007F to LATIN
+ sal_Int16 lcl_getScriptType(
+ const uno::Reference<i18n::XBreakIterator>& rBI,
+ const OUString &rString, sal_Int32 nPos)
+ {
+ sal_Int16 nScript = rBI->getScriptType(rString, nPos);
+ if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F)
+ nScript = MSASCII;
+ return nScript;
+ }
+
+ // We want to know about WEAK segments, so endOfScript isn't
+ // useful, and see lcl_getScriptType anyway
+ sal_Int32 lcl_endOfScript(
+ const uno::Reference<i18n::XBreakIterator>& rBI,
+ const OUString &rString, sal_Int32 nPos, sal_Int16 nScript)
+ {
+ while (nPos < rString.getLength())
+ {
+ sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos);
+ if (nScript != nNewScript)
+ break;
+ ++nPos;
+ }
+ return nPos;
+ }
+
+ sal_Int32 lcl_getWriterScriptType(
+ const uno::Reference<i18n::XBreakIterator>& rBI,
+ const OUString &rString, sal_Int32 nPos)
+ {
+ sal_Int16 nScript = i18n::ScriptType::WEAK;
+
+ if (rString.isEmpty())
+ return nScript;
+
+ while (nPos >= 0)
+ {
+ nScript = rBI->getScriptType(rString, nPos);
+ if (nScript != i18n::ScriptType::WEAK)
+ break;
+ --nPos;
+ }
+
+ return nScript;
+ }
+
+ bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB)
+ {
+ return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW);
+ }
+
+ bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB)
+ {
+ // Ignoring CharSet, and ignoring unknown pitch
+ return rA.GetFamilyName() == rB.GetFamilyName() &&
+ rA.GetStyleName() == rB.GetStyleName() &&
+ rA.GetFamily() == rB.GetFamily() &&
+ samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch());
+ }
+}
+
+// In writer we categorize text into CJK, CTL and "Western" for everything else.
+// Microsoft Word basically categorizes text into East Asian, Complex, ASCII,
+// NonEastAsian/HighAnsi, with some shared characters and some properties to
+// hint as to which way to bias those shared characters.
+
+// That's four categories, we however have three categories. Given that problem
+// here we would ideally find out "what would word do" to see what font/language
+// word would assign to characters based on the unicode range they fall into and
+// hack the word one onto the range we use. However it's unclear what word's
+// categorization is. So we don't do that here yet.
+
+// Additional to the categorization, when word encounters weak text for ambiguous
+// chars it uses idcthint to indicate which way to bias. We don't have an idcthint
+// feature in writer.
+
+// So what we currently do here then is to split our text into non-weak/weak
+// sections and uses word's idcthint to determine what font it would use and
+// force that on for the segment. Following what we *do* know about word's
+// categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in
+// word, something we map to LATIN, so we consider all weak chars in that range
+// to auto-bias to LATIN.
+
+// See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example
+void SwWW8ImplReader::emulateMSWordAddTextToParagraph(const OUString& rAddString)
+{
+ if (rAddString.isEmpty())
+ return;
+
+ if (m_bFuzzing)
+ {
+ simpleAddTextToParagraph(rAddString);
+ return;
+ }
+
+ uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter());
+ assert(xBI.is());
+
+ sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0);
+ sal_Int32 nLen = rAddString.getLength();
+
+ OUString sParagraphText;
+ const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
+ const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
+ if (pNd)
+ sParagraphText = pNd->GetText();
+ sal_Int32 nParaOffset = sParagraphText.getLength();
+ sParagraphText = sParagraphText + rAddString;
+
+ sal_Int32 nPos = 0;
+ while (nPos < nLen)
+ {
+ sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript);
+ if (nEnd < 0)
+ break;
+
+ OUString sChunk(rAddString.copy(nPos, nEnd-nPos));
+ const TypedWhichId<SvxFontItem> aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
+ const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr};
+ bool aForced[] = {false, false, false};
+
+ int nLclIdctHint = 0xFF;
+ if (nScript == i18n::ScriptType::WEAK)
+ {
+ const SfxInt16Item *pIdctHint = GetFormatAttr(RES_CHRATR_IDCTHINT);
+ nLclIdctHint = pIdctHint->GetValue();
+ }
+ else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font
+ nLclIdctHint = 0;
+
+ TypedWhichId<SvxFontItem> nForceFromFontId(0);
+ if (nLclIdctHint != 0xFF)
+ {
+ switch (nLclIdctHint)
+ {
+ case 0:
+ nForceFromFontId = RES_CHRATR_FONT;
+ break;
+ case 1:
+ nForceFromFontId = RES_CHRATR_CJK_FONT;
+ break;
+ case 2:
+ nForceFromFontId = RES_CHRATR_CTL_FONT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (sal_uInt16(nForceFromFontId) != 0)
+ {
+ // Now we know that word would use the nForceFromFontId font for this range
+ // Try and determine what script writer would assign this range to
+
+ sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText,
+ nPos + nParaOffset);
+
+ bool bWriterWillUseSameFontAsWordAutomatically = false;
+
+ if (nWriterScript != i18n::ScriptType::WEAK)
+ {
+ if (
+ (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) ||
+ (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) ||
+ (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT)
+ )
+ {
+ bWriterWillUseSameFontAsWordAutomatically = true;
+ }
+ else
+ {
+ const SvxFontItem *pSourceFont = GetFormatAttr(nForceFromFontId);
+ TypedWhichId<SvxFontItem> nDestId = aIds[nWriterScript-1];
+ const SvxFontItem *pDestFont = GetFormatAttr(nDestId);
+ bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont);
+ }
+ }
+
+ // Writer won't use the same font as word, so force the issue
+ if (!bWriterWillUseSameFontAsWordAutomatically)
+ {
+ const SvxFontItem *pSourceFont = GetFormatAttr(nForceFromFontId);
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
+ {
+ const SvxFontItem *pDestFont = GetFormatAttr(aIds[i]);
+ aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont;
+ if (aForced[i])
+ {
+ pOverriddenItems[i] =
+ static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i]));
+
+ SvxFontItem aForceFont(*pSourceFont);
+ aForceFont.SetWhich(aIds[i]);
+ m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont);
+ }
+ }
+ }
+ }
+
+ simpleAddTextToParagraph(sChunk);
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
+ {
+ if (aForced[i])
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]);
+ if (pOverriddenItems[i])
+ m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i]));
+ }
+ }
+
+ nPos = nEnd;
+ if (nPos < nLen)
+ nScript = lcl_getScriptType(xBI, rAddString, nPos);
+ }
+}
+
+namespace sw {
+
+auto FilterControlChars(std::u16string_view aString) -> OUString
+{
+ OUStringBuffer buf(aString.size());
+ for (size_t i = 0; i < aString.size(); ++i)
+ {
+ sal_Unicode const ch(aString[i]);
+ if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t')
+ {
+ buf.append(ch);
+ }
+ else
+ {
+ SAL_INFO("sw.ww8", "filtering control character");
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+} // namespace sw
+
+void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString)
+{
+ OUString const addString(sw::FilterControlChars(aAddString));
+
+ if (addString.isEmpty())
+ return;
+
+ const SwContentNode *pCntNd = m_pPaM->GetPointContentNode();
+ const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr;
+
+ OSL_ENSURE(pNd, "What the hell, where's my text node");
+
+ if (!pNd)
+ return;
+
+ const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength();
+ if (nCharsLeft > 0)
+ {
+ if (addString.getLength() <= nCharsLeft)
+ {
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
+ }
+ else
+ {
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft));
+ AppendTextNode(*m_pPaM->GetPoint());
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft));
+ }
+ }
+ else
+ {
+ AppendTextNode(*m_pPaM->GetPoint());
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString);
+ }
+
+ m_bReadTable = false;
+}
+
+/**
+ * Return value: true for para end
+ */
+bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd,
+ tools::Long nCpOfs)
+{
+ tools::Long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd;
+
+ if (m_bSymbol || m_bIgnoreText)
+ {
+ WW8_CP nRequested = nEnd - rPos;
+ if (m_bSymbol) // Insert special chars
+ {
+ sal_uInt64 nMaxPossible = m_pStrm->remainingSize();
+ if (o3tl::make_unsigned(nRequested) > nMaxPossible)
+ {
+ SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible);
+ nRequested = nMaxPossible;
+ }
+
+ if (!linguistic::IsControlChar(m_cSymbol)
+ || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t')
+ {
+ for (WW8_CP nCh = 0; nCh < nRequested; ++nCh)
+ {
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(m_cSymbol));
+ }
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CJK_FONT);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CTL_FONT);
+ }
+ }
+ m_pStrm->SeekRel(nRequested);
+ rPos = nEnd; // Ignore until attribute end
+ return false;
+ }
+
+ while (true)
+ {
+ if (ReadPlainChars(rPos, nEnd, nCpOfs))
+ return false; // Done
+
+ bool bStartLine = ReadChar(rPos, nCpOfs);
+ rPos++;
+ if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done
+ {
+ return bStartLine;
+ }
+ }
+}
+
+bool SwWW8ImplReader::HandlePageBreakChar()
+{
+ bool bParaEndAdded = false;
+ // #i1909# section/page breaks should not occur in tables, word
+ // itself ignores them in this case.
+ if (!m_nInTable)
+ {
+ bool IsTemp=true;
+ SwTextNode* pTemp = m_pPaM->GetPointNode().GetTextNode();
+ if (pTemp && pTemp->GetText().isEmpty()
+ && (m_bFirstPara || m_bFirstParaOfPage))
+ {
+ IsTemp = false;
+ AppendTextNode(*m_pPaM->GetPoint());
+ pTemp->SetAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
+ }
+
+ m_bPgSecBreak = true;
+ m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint());
+ /*
+ If it's a 0x0c without a paragraph end before it, act like a
+ paragraph end, but nevertheless, numbering (and perhaps other
+ similar constructs) do not exist on the para.
+ */
+ if (!m_bWasParaEnd && IsTemp)
+ {
+ bParaEndAdded = true;
+ if (0 >= m_pPaM->GetPoint()->GetContentIndex())
+ {
+ if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
+ {
+ pTextNode->SetAttr(
+ *GetDfltAttr(RES_PARATR_NUMRULE));
+ }
+ }
+ }
+ }
+ return bParaEndAdded;
+}
+
+bool SwWW8ImplReader::ReadChar(tools::Long nPosCp, tools::Long nCpOfs)
+{
+ bool bNewParaEnd = false;
+ // Reset Unicode flag and correct FilePos if needed.
+ // Note: Seek is not expensive, as we're checking inline whether or not
+ // the correct FilePos has already been reached.
+ std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode);
+ if (!checkSeek(*m_pStrm, nRequestedPos))
+ return false;
+
+ sal_uInt16 nWCharVal(0);
+ if( m_bIsUnicode )
+ m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes
+ else
+ {
+ sal_uInt8 nBCode(0);
+ m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte
+ nWCharVal = nBCode;
+ }
+
+ sal_Unicode cInsert = '\x0';
+ bool bParaMark = false;
+
+ if ( 0xc != nWCharVal )
+ m_bFirstParaOfPage = false;
+
+ switch (nWCharVal)
+ {
+ case 0:
+ if (!m_bFuzzing)
+ {
+ // Page number
+ SwPageNumberField aField(
+ static_cast<SwPageNumberFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(
+ SwFieldIds::PageNumber )), PG_RANDOM, SVX_NUM_ARABIC);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ }
+ else
+ {
+ // extremely slow, so skip for fuzzing, and insert a space instead
+ cInsert = ' ';
+ }
+ break;
+ case 0xe:
+ // if there is only one column word treats a column break like a pagebreak.
+ if (m_aSectionManager.CurrentSectionColCount() < 2)
+ bParaMark = HandlePageBreakChar();
+ else if (!m_nInTable)
+ {
+ // Always insert a txtnode for a column break, e.g. ##
+ SwContentNode *pCntNd=m_pPaM->GetPointContentNode();
+ if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed
+ AppendTextNode(*m_pPaM->GetPoint());
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
+ }
+ break;
+ case 0x7:
+ {
+ bNewParaEnd = true;
+ WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap();
+ //The last paragraph of each cell is terminated by a special
+ //paragraph mark called a cell mark. Following the cell mark
+ //that ends the last cell of a table row, the table row is
+ //terminated by a special paragraph mark called a row mark
+ //
+ //So the 0x7 should be right at the end of the previous
+ //range to be a real cell-end.
+ if (pPap->nOrigStartPos == nPosCp+1 ||
+ pPap->nOrigStartPos == WW8_CP_MAX)
+ {
+ TabCellEnd(); // Table cell/row end
+ }
+ else
+ bParaMark = true;
+ }
+ break;
+ case 0xf:
+ if( !m_bSpec ) // "Satellite"
+ cInsert = u'\x00a4';
+ break;
+ case 0x14:
+ if( !m_bSpec ) // "Para End" char
+ cInsert = u'\x00b5';
+ //TODO: should this be U+00B6 PILCROW SIGN rather than
+ // U+00B5 MICRO SIGN?
+ break;
+ case 0x15:
+ if( !m_bSpec ) // Juristenparagraph
+ {
+ cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp));
+ if (aItr == m_aTOXEndCps.end())
+ cInsert = u'\x00a7';
+ else
+ m_aTOXEndCps.erase(aItr);
+ }
+ break;
+ case 0x9:
+ cInsert = '\x9'; // Tab
+ break;
+ case 0xb:
+ cInsert = '\xa'; // Hard NewLine
+ break;
+ case 0xc:
+ bParaMark = HandlePageBreakChar();
+ break;
+ case 0x1e: // Non-breaking hyphen
+ m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDHYPHEN) );
+ break;
+ case 0x1f: // Non-required hyphens
+ m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_SOFTHYPHEN) );
+ break;
+ case 0xa0: // Non-breaking spaces
+ m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDBLANK) );
+ break;
+ case 0x1:
+ /*
+ Current thinking is that if bObj is set then we have a
+ straightforward "traditional" ole object, otherwise we have a
+ graphic preview of an associated ole2 object (or a simple
+ graphic of course)
+
+ normally in the canvas field, the code is 0x8 0x1.
+ in a special case, the code is 0x1 0x1, which yields a simple picture
+ */
+ {
+ bool bReadObj = IsInlineEscherHack();
+ if( bReadObj )
+ {
+ sal_uInt64 nCurPos = m_pStrm->Tell();
+ sal_uInt16 nWordCode(0);
+
+ if( m_bIsUnicode )
+ m_pStrm->ReadUInt16( nWordCode );
+ else
+ {
+ sal_uInt8 nByteCode(0);
+ m_pStrm->ReadUChar( nByteCode );
+ nWordCode = nByteCode;
+ }
+ if( nWordCode == 0x1 )
+ bReadObj = false;
+ m_pStrm->Seek( nCurPos );
+ }
+ if( !bReadObj )
+ {
+ SwFrameFormat *pResult = nullptr;
+ if (m_bObj)
+ pResult = ImportOle();
+ else if (m_bSpec)
+ {
+ SwFrameFormat* pAsCharFlyFormat =
+ m_rDoc.MakeFrameFormat(OUString(), m_rDoc.GetDfltFrameFormat());
+ SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
+ pAsCharFlyFormat->SetFormatAttr(aAnchor);
+ pResult = ImportGraf(nullptr, pAsCharFlyFormat);
+ m_rDoc.DelFrameFormat(pAsCharFlyFormat);
+ }
+
+
+ // If we have a bad 0x1 insert a space instead.
+ if (!pResult)
+ {
+ cInsert = ' ';
+ OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc,
+ "WW8: Please report this document, it may have a "
+ "missing graphic");
+ }
+ else
+ {
+ // reset the flags.
+ m_bObj = m_bEmbeddObj = false;
+ m_nObjLocFc = 0;
+ }
+ }
+ }
+ break;
+ case 0x8:
+ if( !m_bObj )
+ Read_GrafLayer( nPosCp );
+ break;
+ case 0xd:
+ bNewParaEnd = bParaMark = true;
+ if (m_nInTable > 1)
+ {
+ /*
+ #i9666#/#i23161#
+ Yes complex, if there is an entry in the undocumented PLCF
+ which I believe to be a record of cell and row boundaries
+ see if the magic bit which I believe to mean cell end is
+ set. I also think btw that the third byte of the 4 byte
+ value is the level of the cell
+ */
+ WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables();
+ if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) &&
+ pTest->Where() == nPosCp+1+nCpOfs)
+ {
+ WW8_FC nPos;
+ void *pData;
+ sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData))
+ : 0;
+ if (nData & 0x2) // Might be how it works
+ {
+ TabCellEnd();
+ bParaMark = false;
+ }
+ }
+ // tdf#106799: We expect TTP marks to be also cell marks,
+ // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell
+ else if (m_bWasTabCellEnd || m_bWasTabRowEnd)
+ {
+ TabCellEnd();
+ bParaMark = false;
+ }
+ }
+
+ m_bWasTabCellEnd = false;
+
+ break; // line end
+ case 0x5: // Annotation reference
+ case 0x13:
+ break;
+ case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later
+ if (!m_aFootnoteStack.empty())
+ cInsert = '?';
+ break;
+ default:
+ SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" );
+ break;
+ }
+
+ if( '\x0' != cInsert )
+ {
+ OUString sInsert(cInsert);
+ emulateMSWordAddTextToParagraph(sInsert);
+ }
+ if (!m_aApos.back()) // a para end in apo doesn't count
+ m_bWasParaEnd = bNewParaEnd;
+ return bParaMark;
+}
+
+void SwWW8ImplReader::ProcessCurrentCollChange(WW8PLCFManResult& rRes,
+ bool* pStartAttr, bool bCallProcessSpecial)
+{
+ sal_uInt16 nOldColl = m_nCurrentColl;
+ m_nCurrentColl = m_xPlcxMan->GetColl();
+
+ // Invalid Style-Id
+ if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl)
+ {
+ m_nCurrentColl = 0;
+ m_bParaAutoBefore = false;
+ m_bParaAutoAfter = false;
+ }
+ else
+ {
+ m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore;
+ m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter;
+ }
+
+ if (nOldColl >= m_vColl.size())
+ nOldColl = 0; // guess! TODO make sure this is what we want
+
+ bool bTabRowEnd = false;
+ if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink )
+ {
+ bool bReSync;
+ // Frame/Table/Autonumbering List Level
+ bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs());
+ if( bReSync )
+ *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribute-Pos again
+ }
+
+ if (!bTabRowEnd && StyleExists(m_nCurrentColl))
+ {
+ SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[ m_nCurrentColl ]);
+ ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags);
+ ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags,
+ m_vColl[m_nCurrentColl].m_n81BiDiFlags);
+ }
+}
+
+tools::Long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard)
+{
+ tools::Long nSkipChars = 0;
+ WW8PLCFManResult aRes;
+
+ OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
+ bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again
+ aRes.nCurrentCp = rTextPos; // Current Cp position
+
+ bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText;
+ if ( bNewSection ) // New Section
+ {
+ OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
+ // Create PageDesc and fill it
+ m_aSectionManager.CreateSep(rTextPos);
+ // -> 0xc was a Sectionbreak, but not a Pagebreak;
+ // Create PageDesc and fill it
+ m_bPgSecBreak = false;
+ OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
+ }
+
+ // New paragraph over Plcx.Fkp.papx
+ if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine )
+ {
+ ProcessCurrentCollChange( aRes, &bStartAttr,
+ MAN_MASK_NEW_PAP == (aRes.nFlags & MAN_MASK_NEW_PAP) &&
+ !m_bIgnoreText );
+ rbStartLine = false;
+ }
+
+ // position of last CP that's to be ignored
+ tools::Long nSkipPos = -1;
+
+ if( 0 < aRes.nSprmId ) // Ignore empty Attrs
+ {
+ if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) )
+ {
+ if( bStartAttr ) // WW attributes
+ {
+ if( aRes.nMemLen >= 0 )
+ ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId);
+ }
+ else
+ EndSprm( aRes.nSprmId ); // Switch off Attr
+ }
+ else if( aRes.nSprmId < 0x800 ) // Own helper attributes
+ {
+ if (bStartAttr)
+ {
+ nSkipChars = ImportExtSprm(&aRes);
+ if (
+ (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) ||
+ (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND)
+ )
+ {
+ WW8_CP nMaxLegalSkip = nTextEnd - rTextPos;
+ // Skip Field/Footnote-/End-Note here
+ rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip);
+ nSkipPos = rTextPos-1;
+ }
+ }
+ else
+ EndExtSprm( aRes.nSprmId );
+ }
+ }
+
+ sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode);
+ bool bValidPos = checkSeek(*m_pStrm, nRequestedPos);
+ SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region");
+
+ // Find next Attr position (and Skip attributes of field contents if needed)
+ if (nSkipChars && !m_bIgnoreText)
+ m_xCtrlStck->MarkAllAttrsOld();
+ bool bOldIgnoreText = m_bIgnoreText;
+ m_bIgnoreText = true;
+ sal_uInt16 nOldColl = m_nCurrentColl;
+ bool bDoPlcxManPlusPLus = true;
+ tools::Long nNext;
+ do
+ {
+ if( bDoPlcxManPlusPLus )
+ m_xPlcxMan->advance();
+ nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd;
+
+ if (m_pPostProcessAttrsInfo &&
+ m_pPostProcessAttrsInfo->mnCpStart == nNext)
+ {
+ m_pPostProcessAttrsInfo->mbCopy = true;
+ }
+
+ if( (0 <= nNext) && (nSkipPos >= nNext) )
+ {
+ if (nDepthGuard >= 1024)
+ {
+ SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit");
+ nNext = nTextEnd;
+ }
+ else
+ nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1);
+ bDoPlcxManPlusPLus = false;
+ m_bIgnoreText = true;
+ }
+
+ if (m_pPostProcessAttrsInfo &&
+ nNext > m_pPostProcessAttrsInfo->mnCpEnd)
+ {
+ m_pPostProcessAttrsInfo->mbCopy = false;
+ }
+ }
+ while( nSkipPos >= nNext );
+ m_bIgnoreText = bOldIgnoreText;
+ if( nSkipChars )
+ {
+ m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() );
+ if( nOldColl != m_xPlcxMan->GetColl() )
+ ProcessCurrentCollChange(aRes, nullptr, false);
+ }
+
+ return nNext;
+}
+
+void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine)
+{
+ // Do we have attributes?
+ if( rTextPos >= rNext )
+ {
+ do
+ {
+ rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine);
+ if (rTextPos == rNext && rTextPos >= nTextEnd)
+ break;
+ }
+ while( rTextPos >= rNext );
+
+ }
+ else if ( rbStartLine )
+ {
+ /* No attributes, but still a new line.
+ * If a line ends with a line break and paragraph attributes or paragraph templates
+ * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP)
+ * is false.
+ * Due to this we need to set the template here as a kind of special treatment.
+ */
+ if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size())
+ SetTextFormatCollAndListLevel(*m_pPaM, m_vColl[m_nCurrentColl]);
+ rbStartLine = false;
+ }
+}
+
+/**
+ * CloseAttrEnds to only read the attribute ends at the end of a text or a
+ * text area (Header, Footnote, ...).
+ * We ignore attribute starts and fields.
+ */
+void SwWW8ImplReader::CloseAttrEnds()
+{
+ // If there are any unclosed sprms then copy them to
+ // another stack and close the ones that must be closed
+ std::stack<sal_uInt16> aStack;
+ m_xPlcxMan->TransferOpenSprms(aStack);
+
+ while (!aStack.empty())
+ {
+ sal_uInt16 nSprmId = aStack.top();
+ if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId)))
+ EndSprm(nSprmId);
+ aStack.pop();
+ }
+
+ EndSpecial();
+}
+
+bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType)
+{
+ bool bJoined=false;
+
+ bool bStartLine = true;
+ short nCrCount = 0;
+ short nDistance = 0;
+
+ m_bWasParaEnd = false;
+ m_nCurrentColl = 0;
+ m_xCurrentItemSet.reset();
+ m_nCharFormat = -1;
+ m_bSpec = false;
+ m_bPgSecBreak = false;
+
+ m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp);
+ tools::Long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote
+
+ WW8_CP nNext = m_xPlcxMan->Where();
+ m_xPreviousNode.reset();
+ sal_uInt8 nDropLines = 0;
+ SwCharFormat* pNewSwCharFormat = nullptr;
+ const SwCharFormat* pFormat = nullptr;
+
+ bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode));
+ if (!bValidPos)
+ return false;
+
+ WW8_CP l = nStartCp;
+ const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp;
+ if (nTextLen > nMaxPossible)
+ {
+ SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long");
+ nTextLen = nMaxPossible;
+ }
+ WW8_CP nTextEnd = nStartCp+nTextLen;
+ while (l < nTextEnd)
+ {
+ ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too
+ OSL_ENSURE(m_pPaM->GetPointNode().GetTextNode(), "Missing txtnode");
+
+ if (m_pPostProcessAttrsInfo != nullptr)
+ PostProcessAttrs();
+
+ if (l >= nTextEnd)
+ break;
+
+ bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs);
+
+ // If the previous paragraph was a dropcap then do not
+ // create a new txtnode and join the two paragraphs together
+ if (bStartLine && !m_xPreviousNode) // Line end
+ {
+ bool bSplit = true;
+ if (m_bCareFirstParaEndInToc)
+ {
+ m_bCareFirstParaEndInToc = false;
+ if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
+ bSplit = false;
+ }
+ if (m_bCareLastParaEndInToc)
+ {
+ m_bCareLastParaEndInToc = false;
+ if (m_pPaM->End() && m_pPaM->End()->GetNode().GetTextNode() && m_pPaM->End()->GetNode().GetTextNode()->Len() == 0)
+ bSplit = false;
+ }
+ if (bSplit)
+ {
+ AppendTextNode(*m_pPaM->GetPoint());
+ }
+ }
+
+ if (SwTextNode* pPreviousNode = (bStartLine && m_xPreviousNode) ? m_xPreviousNode->GetTextNode() : nullptr)
+ {
+ SwTextNode* pEndNd = m_pPaM->GetPointNode().GetTextNode();
+ SAL_WARN_IF(!pEndNd, "sw.ww8", "didn't find textnode for dropcap");
+ if (pEndNd)
+ {
+ const sal_Int32 nDropCapLen = pPreviousNode->GetText().getLength();
+
+ // Need to reset the font size and text position for the dropcap
+ {
+ SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1);
+ m_xCtrlStck->Delete(aTmp);
+ }
+
+ // Get the default document dropcap which we can use as our template
+ const SwFormatDrop* defaultDrop = GetFormatAttr(RES_PARATR_DROP);
+ SwFormatDrop aDrop(*defaultDrop);
+
+ aDrop.GetLines() = nDropLines;
+ aDrop.GetDistance() = nDistance;
+ aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen);
+ // Word has no concept of a "whole word dropcap"
+ aDrop.GetWholeWord() = false;
+
+ if (pFormat)
+ aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat));
+ else if(pNewSwCharFormat)
+ aDrop.SetCharFormat(pNewSwCharFormat);
+
+ SwPosition aStart(*pEndNd);
+ m_xCtrlStck->NewAttr(aStart, aDrop);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_DROP);
+ }
+ m_xPreviousNode.reset();
+ }
+ else if (m_bDropCap)
+ {
+ // If we have found a dropcap store the textnode
+ m_xPreviousNode.reset(new TextNodeListener(m_pPaM->GetPointNode().GetTextNode()));
+
+ SprmResult aDCS;
+ if (m_bVer67)
+ aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46);
+ else
+ aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C);
+
+ if (aDCS.pSprm && aDCS.nRemainingData >= 1)
+ nDropLines = (*aDCS.pSprm) >> 3;
+ else // There is no Drop Cap Specifier hence no dropcap
+ m_xPreviousNode.reset();
+
+ SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F);
+ if (aDistance.pSprm && aDistance.nRemainingData >= 2)
+ nDistance = SVBT16ToUInt16(aDistance.pSprm);
+ else
+ nDistance = 0;
+
+ const SwFormatCharFormat *pSwFormatCharFormat = nullptr;
+
+ if (m_xCurrentItemSet)
+ pSwFormatCharFormat = &(m_xCurrentItemSet->Get(RES_TXTATR_CHARFMT));
+
+ if (pSwFormatCharFormat)
+ pFormat = pSwFormatCharFormat->GetCharFormat();
+
+ if (m_xCurrentItemSet && !pFormat)
+ {
+ OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++);
+ pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat());
+ m_xCurrentItemSet->ClearItem(RES_CHRATR_ESCAPEMENT);
+ pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet);
+ }
+
+ m_xCurrentItemSet.reset();
+ m_bDropCap=false;
+ }
+
+ if (bStartLine || m_bWasTabRowEnd)
+ {
+ // Call all 64 CRs; not for Header and the like
+ if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen)
+ {
+ if (nTextLen < WW8_CP_MAX/100)
+ m_nProgress = o3tl::narrowing<sal_uInt16>(l * 100 / nTextLen);
+ else
+ m_nProgress = o3tl::narrowing<sal_uInt16>(l / nTextLen * 100);
+ m_xProgress->Update(m_nProgress); // Update
+ }
+ }
+
+ // If we have encountered a 0x0c which indicates either section of
+ // pagebreak then look it up to see if it is a section break, and
+ // if it is not then insert a page break. If it is a section break
+ // it will be handled as such in the ReadAttrs of the next loop
+ if (m_bPgSecBreak)
+ {
+ // We need only to see if a section is ending at this cp,
+ // the plcf will already be sitting on the correct location
+ // if it is there.
+ WW8PLCFxDesc aTemp;
+ aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX;
+ if (m_xPlcxMan->GetSepPLCF())
+ m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp);
+ if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l))
+ {
+ // #i39251# - insert text node for page break, if no one inserted.
+ // #i43118# - refine condition: the anchor
+ // control stack has to have entries, otherwise it's not needed
+ // to insert a text node.
+ if (!bStartLine && !m_xAnchorStck->empty())
+ {
+ AppendTextNode(*m_pPaM->GetPoint());
+ }
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM,
+ SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
+ m_bFirstParaOfPage = true;
+ m_bPgSecBreak = false;
+ }
+ }
+ }
+
+ m_xPreviousNode.reset();
+
+ if (m_pPaM->GetPoint()->GetContentIndex())
+ AppendTextNode(*m_pPaM->GetPoint());
+
+ if (!m_bInHyperlink)
+ bJoined = JoinNode(*m_pPaM);
+
+ CloseAttrEnds();
+
+ m_xPlcxMan.reset();
+ return bJoined;
+}
+
+SwWW8ImplReader::SwWW8ImplReader(sal_uInt8 nVersionPara, SotStorage* pStorage,
+ SvStream* pSt, SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos)
+ : m_pDocShell(rD.GetDocShell())
+ , m_pStg(pStorage)
+ , m_pStrm(pSt)
+ , m_pTableStream(nullptr)
+ , m_pDataStream(nullptr)
+ , m_rDoc(rD)
+ , m_pPaM(nullptr)
+ , m_aSectionManager(*this)
+ , m_aExtraneousParas(rD)
+ , m_aInsertedTables(rD)
+ , m_aSectionNameGenerator(rD, "WW")
+ , m_aGrfNameGenerator(bNewDoc, OUString('G'))
+ , m_aParaStyleMapper(rD)
+ , m_aCharStyleMapper(rD)
+ , m_pFlyFormatOfJustInsertedGraphic(nullptr)
+ , m_pPreviousNumPaM(nullptr)
+ , m_pPrevNumRule(nullptr)
+ , m_pCurrentColl(nullptr)
+ , m_pDfltTextFormatColl(nullptr)
+ , m_pStandardFormatColl(nullptr)
+ , m_pDrawModel(nullptr)
+ , m_pDrawPg(nullptr)
+ , m_pNumFieldType(nullptr)
+ , m_sBaseURL(std::move(aBaseURL))
+ , m_nIniFlags(0)
+ , m_nIniFlags1(0)
+ , m_nFieldFlags(0)
+ , m_bRegardHindiDigits( false )
+ , m_bDrawCpOValid( false )
+ , m_nDrawCpO(0)
+ , m_nPicLocFc(0)
+ , m_nObjLocFc(0)
+ , m_nIniFlyDx(0)
+ , m_nIniFlyDy(0)
+ , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US)
+ , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US)
+ , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW)
+ , m_nProgress(0)
+ , m_nCurrentColl(0)
+ , m_nFieldNum(0)
+ , m_nLFOPosition(USHRT_MAX)
+ , m_nCharFormat(0)
+ , m_nDrawXOfs(0)
+ , m_nDrawYOfs(0)
+ , m_nDrawXOfs2(0)
+ , m_nDrawYOfs2(0)
+ , m_cSymbol(0)
+ , m_nWantedVersion(nVersionPara)
+ , m_nSwNumLevel(0xff)
+ , m_nWwNumType(0xff)
+ , m_pChosenWW8OutlineStyle(nullptr)
+ , m_nListLevel(MAXLEVEL)
+ , m_bNewDoc(bNewDoc)
+ , m_bSkipImages(bSkipImages)
+ , m_bReadNoTable(false)
+ , m_bPgSecBreak(false)
+ , m_bSpec(false)
+ , m_bObj(false)
+ , m_bTxbxFlySection(false)
+ , m_bHasBorder(false)
+ , m_bSymbol(false)
+ , m_bIgnoreText(false)
+ , m_nInTable(0)
+ , m_bWasTabRowEnd(false)
+ , m_bWasTabCellEnd(false)
+ , m_bAnl(false)
+ , m_bHdFtFootnoteEdn(false)
+ , m_bFootnoteEdn(false)
+ , m_bIsHeader(false)
+ , m_bIsFooter(false)
+ , m_bIsUnicode(false)
+ , m_bCpxStyle(false)
+ , m_bStyNormal(false)
+ , m_bWWBugNormal(false)
+ , m_bNoAttrImport(false)
+ , m_bInHyperlink(false)
+ , m_bWasParaEnd(false)
+ , m_bVer67(false)
+ , m_bVer6(false)
+ , m_bVer7(false)
+ , m_bVer8(false)
+ , m_bEmbeddObj(false)
+ , m_bCurrentAND_fNumberAcross(false)
+ , m_bNoLnNumYet(true)
+ , m_bFirstPara(true)
+ , m_bFirstParaOfPage(false)
+ , m_bParaAutoBefore(false)
+ , m_bParaAutoAfter(false)
+ , m_bDropCap(false)
+ , m_nDropCap(0)
+ , m_bBidi(false)
+ , m_bReadTable(false)
+ , m_bLoadingTOXCache(false)
+ , m_nEmbeddedTOXLevel(0)
+ , m_bLoadingTOXHyperlink(false)
+ , m_bCareFirstParaEndInToc(false)
+ , m_bCareLastParaEndInToc(false)
+ , m_bNotifyMacroEventRead(false)
+ , m_bFuzzing(utl::ConfigManager::IsFuzzing())
+{
+ m_pStrm->SetEndian( SvStreamEndian::LITTLE );
+ m_aApos.push_back(false);
+
+ mpCursor = m_rDoc.CreateUnoCursor(rPos);
+}
+
+SwWW8ImplReader::~SwWW8ImplReader()
+{
+}
+
+void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck)
+{
+ if( pStck )
+ {
+ pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
+ pStck->SetAttr( *m_pPaM->GetPoint(), 0, false);
+ }
+ else
+ {
+ OSL_ENSURE( false, "WW stack already deleted" );
+ }
+}
+
+void wwSectionManager::SetSegmentToPageDesc(const wwSection &rSection,
+ bool bIgnoreCols)
+{
+ SwPageDesc &rPage = *rSection.mpPage;
+
+ SetNumberingType(rSection, rPage);
+
+ SwFrameFormat &rFormat = rPage.GetMaster();
+
+ if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized
+ mrReader.GraphicCtor();
+
+ if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager)
+ {
+ tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size
+ SvxMSDffImportData aData(aRect);
+ rtl::Reference<SdrObject> pObject;
+ if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty())
+ {
+ // Only handle shape if it is a background shape
+ if (aData.begin()->get()->nFlags & ShapeFlag::Background)
+ {
+ SfxItemSetFixed<RES_BACKGROUND, RES_BACKGROUND,XATTR_START, XATTR_END>
+ aSet(rFormat.GetDoc()->GetAttrPool());
+ mrReader.MatchSdrItemsIntoFlySet(pObject.get(), aSet, mso_lineSimple,
+ mso_lineSolid, mso_sptRectangle, aRect);
+ if ( aSet.HasItem(RES_BACKGROUND) )
+ rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND));
+ else
+ rFormat.SetFormatAttr(aSet);
+ }
+ }
+ }
+ wwULSpaceData aULData;
+ GetPageULData(rSection, aULData);
+ SetPageULSpaceItems(rFormat, aULData, rSection);
+
+ rPage.SetVerticalAdjustment( rSection.mnVerticalAdjustment );
+
+ SetPage(rPage, rFormat, rSection, bIgnoreCols);
+
+ if (!(rSection.maSep.pgbApplyTo & 1))
+ SwWW8ImplReader::SetPageBorder(rFormat, rSection);
+ if (!(rSection.maSep.pgbApplyTo & 2))
+ SwWW8ImplReader::SetPageBorder(rPage.GetFirstMaster(), rSection);
+
+ mrReader.SetDocumentGrid(rFormat, rSection);
+}
+
+void wwSectionManager::SetUseOn(wwSection &rSection)
+{
+ bool bMirror = mrReader.m_xWDop->fMirrorMargins ||
+ mrReader.m_xWDop->doptypography.m_f2on1;
+
+ UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All;
+ UseOnPage eUse = eUseBase;
+ if (!mrReader.m_xWDop->fFacingPages)
+ eUse |= UseOnPage::HeaderShare | UseOnPage::FooterShare;
+ if (!rSection.HasTitlePage())
+ eUse |= UseOnPage::FirstShare;
+
+ OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set");
+ if (rSection.mpPage)
+ rSection.mpPage->WriteUseOn(eUse);
+}
+
+/**
+ * Set the page descriptor on this node, handle the different cases for a text
+ * node or a table
+ */
+static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc,
+ SwDoc &rDoc)
+{
+ /*
+ If it's a table here, apply the pagebreak to the table
+ properties, otherwise we add it to the para at this
+ position
+ */
+ if (rIdx.GetNode().IsTableNode())
+ {
+ SwTable& rTable =
+ rIdx.GetNode().GetTableNode()->GetTable();
+ SwFrameFormat* pApply = rTable.GetFrameFormat();
+ OSL_ENSURE(pApply, "impossible");
+ if (pApply)
+ pApply->SetFormatAttr(rPgDesc);
+ }
+ else
+ {
+ SwPaM aPage(rIdx);
+ rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc);
+ }
+}
+
+/**
+ * Map a word section to a writer page descriptor
+ */
+SwFormatPageDesc wwSectionManager::SetSwFormatPageDesc(mySegIter const &rIter,
+ mySegIter const &rStart, bool bIgnoreCols)
+{
+ if (mrReader.m_bNewDoc && rIter == rStart)
+ {
+ rIter->mpPage =
+ mrReader.m_rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD);
+ }
+ else
+ {
+ rIter->mpPage = mrReader.m_rDoc.MakePageDesc(
+ SwViewShell::GetShellRes()->GetPageDescName(mnDesc, ShellResource::NORMAL_PAGE),
+ nullptr, false);
+ }
+ OSL_ENSURE(rIter->mpPage, "no page!");
+ if (!rIter->mpPage)
+ return SwFormatPageDesc();
+
+ // Set page before hd/ft
+ const wwSection *pPrevious = nullptr;
+ if (rIter != rStart)
+ pPrevious = &(*(rIter-1));
+ SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious);
+ SetUseOn(*rIter);
+
+ // Set hd/ft after set page
+ SetSegmentToPageDesc(*rIter, bIgnoreCols);
+
+ SwFormatPageDesc aRet(rIter->mpPage);
+
+ rIter->mpPage->SetFollow(rIter->mpPage);
+
+ if (rIter->PageRestartNo())
+ aRet.SetNumOffset(rIter->PageStartAt());
+
+ ++mnDesc;
+ return aRet;
+}
+
+void wwSectionManager::InsertSegments()
+{
+ mySegIter aEnd = maSegments.end();
+ mySegIter aStart = maSegments.begin();
+ for (mySegIter aIter = aStart; aIter != aEnd; ++aIter)
+ {
+ // If the section is of type "New column" (0x01), then simply insert a column break.
+ // But only if there actually are columns on the page, otherwise a column break
+ // seems to be handled like a page break by MSO.
+ if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 )
+ {
+ SwPaM start( aIter->maStart );
+ mrReader.m_rDoc.getIDocumentContentOperations().InsertPoolItem( start, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK));
+ continue;
+ }
+
+ mySegIter aNext = aIter+1;
+ mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1;
+
+ // If two following sections are different in following properties, Word will interpret a continuous
+ // section break between them as if it was a section break next page.
+ bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) &&
+ (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape()));
+
+ bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible;
+ bool bInsertPageDesc = !bInsertSection;
+ bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this...
+
+ if (bInsertPageDesc)
+ {
+ /*
+ If a cont section follows this section then we won't be
+ creating a page desc with 2+ cols as we cannot host a one
+ col section in a 2+ col pagedesc and make it look like
+ word. But if the current section actually has columns then
+ we are forced to insert a section here as well as a page
+ descriptor.
+ */
+
+ bool bIgnoreCols = bInsertSection;
+ bool bThisAndNextAreCompatible = (aNext == aEnd) ||
+ ((aIter->GetPageWidth() == aNext->GetPageWidth()) &&
+ (aIter->GetPageHeight() == aNext->GetPageHeight()) &&
+ (aIter->IsLandScape() == aNext->IsLandScape()));
+
+ if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected)
+ {
+ bIgnoreCols = true;
+ if ((aIter->NoCols() > 1) || bProtected)
+ bInsertSection = true;
+ }
+
+ SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
+ if (!aDesc.GetPageDesc())
+ continue;
+
+ // special case handling for odd/even section break
+ // a) as before create a new page style for the section break
+ // b) set Layout of generated page style to right/left ( according
+ // to section break odd/even )
+ // c) create a new style to follow the break page style
+ if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 )
+ {
+ // SetSwFormatPageDesc calls some methods that could
+ // modify aIter (e.g. wwSection ).
+ // Since we call SetSwFormatPageDesc below to generate the
+ // 'Following' style of the Break style, it is safer
+ // to take a copy of the contents of aIter.
+ wwSection aTmpSection = *aIter;
+ // create a new following page style
+ SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols));
+ // restore any contents of aIter trashed by SetSwFormatPageDesc
+ *aIter = aTmpSection;
+
+ // Handle the section break
+ UseOnPage eUseOnPage = UseOnPage::Left;
+ if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break
+ eUseOnPage = UseOnPage::Right;
+
+ // Keep the share flags.
+ aDesc.GetPageDesc()->SetUseOn( eUseOnPage );
+ aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() );
+ }
+
+ // Avoid setting the page style at the very beginning since it is always the default style anyway,
+ // unless it is needed to specify a page number.
+ if (aIter != aStart || aDesc.GetNumOffset())
+ GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc);
+ }
+
+ SwTextNode* pTextNd = nullptr;
+ if (bInsertSection)
+ {
+ // Start getting the bounds of this section
+ SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM);
+ SwNodeIndex aAnchor(aSectPaM.GetPoint()->GetNode());
+ if (aNext != aEnd)
+ {
+ aAnchor = aNext->maStart;
+ aSectPaM.GetPoint()->Assign(aAnchor);
+ aSectPaM.Move(fnMoveBackward);
+ }
+
+ const SwPosition* pPos = aSectPaM.GetPoint();
+ SwTextNode const*const pSttNd = pPos->GetNode().GetTextNode();
+ const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr;
+ if (pTableNd)
+ {
+ pTextNd =
+ mrReader.m_rDoc.GetNodes().MakeTextNode(aAnchor.GetNode(),
+ mrReader.m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+
+ aSectPaM.GetPoint()->Assign(*pTextNd, 0);
+ }
+
+ aSectPaM.SetMark();
+
+ aSectPaM.GetPoint()->Assign(aIter->maStart);
+
+ bool bHasOwnHdFt = false;
+ /*
+ In this nightmare scenario the continuous section has its own
+ headers and footers so we will try and find a hard page break
+ between here and the end of the section and put the headers and
+ footers there.
+ */
+ if (!bInsertPageDesc)
+ {
+ bHasOwnHdFt =
+ mrReader.HasOwnHeaderFooter(
+ aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST),
+ aIter->maSep.grpfIhdt, std::distance(aStart, aIter)
+ );
+ }
+ if (bHasOwnHdFt)
+ {
+ // #i40766# Need to cache the page descriptor in case there is
+ // no page break in the section
+ SwPageDesc *pOrig = aIter->mpPage;
+ bool bFailed = true;
+ SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true));
+ if (aDesc.GetPageDesc())
+ {
+ SwNodeOffset nStart = aSectPaM.Start()->GetNodeIndex();
+ SwNodeOffset nEnd = aSectPaM.End()->GetNodeIndex();
+ for(; nStart <= nEnd; ++nStart)
+ {
+ SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart];
+ if (!pNode)
+ continue;
+ if (sw::util::HasPageBreak(*pNode))
+ {
+ SwNodeIndex aIdx(*pNode);
+ GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc);
+ bFailed = false;
+ break;
+ }
+ }
+ }
+ if(bFailed)
+ {
+ aIter->mpPage = pOrig;
+ }
+ }
+
+ // End getting the bounds of this section, quite a job eh?
+ SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter);
+ // The last section if continuous is always unbalanced
+ if (pRet)
+ {
+ // Set the columns to be UnBalanced if that compatibility option is set
+ if (mrReader.m_xWDop->fNoColumnBalance)
+ pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
+ else
+ {
+ // Otherwise set to unbalanced if the following section is
+ // not continuous, (which also means that the last section
+ // is unbalanced)
+ if (aNext == aEnd || !aNext->IsContinuous())
+ pRet->SetFormatAttr(SwFormatNoBalancedColumns(true));
+ }
+ }
+ }
+
+ if (pTextNd)
+ {
+ SwPaM aTest(*pTextNd);
+ mrReader.m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
+ pTextNd = nullptr;
+ }
+ }
+}
+
+void wwExtraneousParas::delete_all_from_doc()
+{
+ auto aEnd = m_aTextNodes.rend();
+ for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI)
+ {
+ ExtraTextNodeListener& rListener = const_cast<ExtraTextNodeListener&>(*aI);
+ SwTextNode* pTextNode = rListener.GetTextNode();
+ rListener.StopListening(pTextNode);
+
+ SwPaM aTest(*pTextNode);
+ m_rDoc.getIDocumentContentOperations().DelFullPara(aTest);
+ }
+ m_aTextNodes.clear();
+}
+
+void wwExtraneousParas::insert(SwTextNode *pTextNode)
+{
+ m_aTextNodes.emplace(pTextNode, this);
+}
+
+void wwExtraneousParas::remove_if_present(SwModify* pModify)
+{
+ auto it = std::find_if(m_aTextNodes.begin(), m_aTextNodes.end(),
+ [pModify](const ExtraTextNodeListener& rEntry) { return rEntry.GetTextNode() == pModify; });
+ if (it == m_aTextNodes.end())
+ return;
+ SAL_WARN("sw.ww8", "It is unexpected to drop a para scheduled for removal");
+ m_aTextNodes.erase(it);
+}
+
+TextNodeListener::TextNodeListener(SwTextNode* pTextNode)
+ : m_pTextNode(pTextNode)
+{
+ m_pTextNode->Add(this);
+}
+
+TextNodeListener::~TextNodeListener()
+{
+ if (!m_pTextNode)
+ return;
+ StopListening(m_pTextNode);
+}
+
+void TextNodeListener::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ // ofz#41398 drop a para scheduled for deletion if something else deletes it
+ // before wwExtraneousParas gets its chance to do so. Not the usual scenario,
+ // indicates an underlying bug.
+ if (pLegacy->GetWhich() == RES_OBJECTDYING)
+ removed(const_cast<SwModify*>(&rModify));
+}
+
+void TextNodeListener::StopListening(SwModify* pTextNode)
+{
+ pTextNode->Remove(this);
+ m_pTextNode = nullptr;
+}
+
+void TextNodeListener::removed(SwModify* pTextNode)
+{
+ StopListening(pTextNode);
+}
+
+void wwExtraneousParas::ExtraTextNodeListener::removed(SwModify* pTextNode)
+{
+ m_pOwner->remove_if_present(pTextNode);
+}
+
+void SwWW8ImplReader::StoreMacroCmds()
+{
+ if (!m_xWwFib->m_lcbCmds)
+ return;
+
+ bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds);
+ if (!bValidPos)
+ return;
+
+ uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage());
+
+ if (!xRoot.is())
+ return;
+
+ try
+ {
+ uno::Reference < io::XStream > xStream =
+ xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE );
+ std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream));
+
+ sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize());
+ std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]);
+ m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds);
+ xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds);
+ }
+ catch (...)
+ {
+ }
+}
+
+void SwWW8ImplReader::ReadDocVars()
+{
+ std::vector<OUString> aDocVarStrings;
+ std::vector<ww::bytes> aDocVarStringIds;
+ std::vector<OUString> aDocValueStrings;
+ WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser,
+ m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet,
+ aDocVarStrings, &aDocVarStringIds, &aDocValueStrings);
+ if (m_bVer67) return;
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
+ uno::Reference<beans::XPropertyContainer> xUserDefinedProps =
+ xDocProps->getUserDefinedProperties();
+ OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null");
+
+ for(size_t i=0; i<aDocVarStrings.size(); i++)
+ {
+ const OUString &rName = aDocVarStrings[i];
+ uno::Any aValue;
+ aValue <<= rName;
+ try {
+ xUserDefinedProps->addProperty( rName,
+ beans::PropertyAttribute::REMOVABLE,
+ aValue );
+ } catch (const uno::Exception &) {
+ // ignore
+ }
+ }
+}
+
+/**
+ * Document Info
+ */
+void SwWW8ImplReader::ReadDocInfo()
+{
+ if( !m_pStg )
+ return;
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
+
+ if (!xDocProps.is())
+ return;
+
+ if ( m_xWwFib->m_fDot )
+ {
+ SfxMedium* pMedium = m_pDocShell->GetMedium();
+ if ( pMedium )
+ {
+ const OUString& aName = pMedium->GetName();
+ INetURLObject aURL( aName );
+ OUString sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ if ( !sTemplateURL.isEmpty() )
+ xDocProps->setTemplateURL( sTemplateURL );
+ }
+ }
+ else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc
+ {
+ auto nCur = m_pTableStream->Tell();
+ Sttb aSttb;
+ // point at tgc record
+ if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream))
+ SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! ");
+ m_pTableStream->Seek( nCur ); // return to previous position, is that necessary?
+ OUString sPath = aSttb.getStringAtIndex( 0x1 );
+ OUString aURL;
+ // attempt to convert to url (won't work for obvious reasons on linux)
+ if ( !sPath.isEmpty() )
+ osl::FileBase::getFileURLFromSystemPath( sPath, aURL );
+ if (aURL.isEmpty())
+ xDocProps->setTemplateURL( aURL );
+ else
+ xDocProps->setTemplateURL( sPath );
+
+ }
+ sfx2::LoadOlePropertySet(xDocProps, m_pStg);
+}
+
+static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName )
+{
+ if ( !xPrjNameCache.is() )
+ return;
+
+ INetURLObject aObj;
+ aObj.SetURL( sTemplatePathOrURL );
+ bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
+ OUString aURL;
+ if ( bIsURL )
+ aURL = sTemplatePathOrURL;
+ else
+ {
+ osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL );
+ aObj.SetURL( aURL );
+ }
+ try
+ {
+ OUString templateNameWithExt = aObj.GetLastName();
+ sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' );
+ if ( nIndex != -1 )
+ {
+ OUString templateName = templateNameWithExt.copy( 0, nIndex );
+ xPrjNameCache->insertByName( templateName, uno::Any( sVBAProjName ) );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+namespace {
+
+class WW8Customizations
+{
+ SvStream* mpTableStream;
+ WW8Fib mWw8Fib;
+public:
+ WW8Customizations( SvStream*, WW8Fib const & );
+ void Import( SwDocShell* pShell );
+};
+
+}
+
+WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib )
+{
+}
+
+void WW8Customizations::Import( SwDocShell* pShell )
+{
+ if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) )
+ return;
+ try
+ {
+ Tcg aTCG;
+ sal_uInt64 nCur = mpTableStream->Tell();
+ if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record
+ {
+ SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! ");
+ return;
+ }
+ bool bReadResult = aTCG.Read( *mpTableStream );
+ mpTableStream->Seek( nCur ); // return to previous position, is that necessary?
+ if ( !bReadResult )
+ {
+ SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! ");
+ return;
+ }
+ aTCG.ImportCustomToolBar( *pShell );
+ }
+ catch(...)
+ {
+ SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically");
+ }
+}
+
+void SwWW8ImplReader::ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache )
+{
+ if (m_bFuzzing)
+ return;
+
+ SvtPathOptions aPathOpt;
+ const OUString& aAddinPath = aPathOpt.GetAddinPath();
+ uno::Sequence< OUString > sGlobalTemplates;
+
+ // first get the autoload addins in the directory STARTUP
+ uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext()));
+
+ if( xSFA->isFolder( aAddinPath ) )
+ sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false );
+
+ for ( const auto& rGlobalTemplate : std::as_const(sGlobalTemplates) )
+ {
+ INetURLObject aObj;
+ aObj.SetURL( rGlobalTemplate );
+ bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
+ OUString aURL;
+ if ( bIsURL )
+ aURL = rGlobalTemplate;
+ else
+ osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL );
+ if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.empty() && sCreatedFrom == aURL ) )
+ continue; // don't try and read the same document as ourselves
+
+ tools::SvRef<SotStorage> rRoot = new SotStorage( aURL, StreamMode::STD_READWRITE );
+
+ BasicProjImportHelper aBasicImporter( *m_pDocShell );
+ // Import vba via oox filter
+ aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
+ lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() );
+ // Read toolbars & menus
+ tools::SvRef<SotStorageStream> refMainStream = rRoot->OpenSotStream( "WordDocument");
+ refMainStream->SetEndian(SvStreamEndian::LITTLE);
+ WW8Fib aWwFib( *refMainStream, 8 );
+ tools::SvRef<SotStorageStream> xTableStream =
+ rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? SL::a1Table : SL::a0Table, StreamMode::STD_READ);
+
+ if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError())
+ {
+ xTableStream->SetEndian(SvStreamEndian::LITTLE);
+ WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib );
+ aGblCustomisations.Import( m_pDocShell );
+ }
+ }
+}
+
+ErrCode SwWW8ImplReader::CoreLoad(WW8Glossary const *pGloss)
+{
+ m_rDoc.SetDocumentType( SwDoc::DOCTYPE_MSWORD );
+ if (m_bNewDoc && m_pStg && !pGloss)
+ {
+ // Initialize RDF metadata, to be able to add statements during the import.
+ try
+ {
+ uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW);
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL));
+ uno::Reference<task::XInteractionHandler> xHandler;
+ xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.ww8", "failed to initialize RDF metadata");
+ }
+ ReadDocInfo();
+ }
+
+ auto pFibData = std::make_shared<::ww8::WW8FibData>();
+
+ if (m_xWwFib->m_fReadOnlyRecommended)
+ pFibData->setReadOnlyRecommended(true);
+ else
+ pFibData->setReadOnlyRecommended(false);
+
+ if (m_xWwFib->m_fWriteReservation)
+ pFibData->setWriteReservation(true);
+ else
+ pFibData->setWriteReservation(false);
+
+ m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData);
+
+ ::sw::tExternalDataPointer pSttbfAsoc
+ = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc);
+
+ m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc);
+
+ if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended)
+ {
+ SwDocShell * pDocShell = m_rDoc.GetDocShell();
+ if (pDocShell)
+ pDocShell->SetReadOnlyUI();
+ }
+
+ m_pPaM = mpCursor.get();
+
+ m_xCtrlStck.reset(new SwWW8FltControlStack(m_rDoc, m_nFieldFlags, *this));
+
+ m_xRedlineStack.reset(new sw::util::RedlineStack(m_rDoc));
+
+ /*
+ RefFieldStck: Keeps track of bookmarks which may be inserted as
+ variables instead.
+ */
+ m_xReffedStck.reset(new SwWW8ReferencedFltEndStack(m_rDoc, m_nFieldFlags));
+ m_xReffingStck.reset(new SwWW8FltRefStack(m_rDoc, m_nFieldFlags));
+
+ m_xAnchorStck.reset(new SwWW8FltAnchorStack(m_rDoc, m_nFieldFlags));
+
+ size_t nPageDescOffset = m_rDoc.GetPageDescCnt();
+
+ RedlineFlags eMode = RedlineFlags::ShowInsert | RedlineFlags::ShowDelete;
+
+ m_oSprmParser.emplace(*m_xWwFib);
+
+ // Set handy helper variables
+ m_bVer6 = (6 == m_xWwFib->m_nVersion);
+ m_bVer7 = (7 == m_xWwFib->m_nVersion);
+ m_bVer67 = m_bVer6 || m_bVer7;
+ m_bVer8 = (8 == m_xWwFib->m_nVersion);
+
+ m_eTextCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chse, m_xWwFib->m_lid);
+ m_eStructCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chseTables, m_xWwFib->m_lid);
+
+ m_bWWBugNormal = m_xWwFib->m_nProduct == 0xc03d;
+
+ m_xProgress.reset(new ImportProgress(m_pDocShell, 0, 100));
+
+ // read Font Table
+ m_xFonts.reset(new WW8Fonts(*m_pTableStream, *m_xWwFib));
+
+ // Document Properties
+ m_xWDop.reset(new WW8Dop(*m_pTableStream, m_xWwFib->m_nFib, m_xWwFib->m_fcDop,
+ m_xWwFib->m_lcbDop));
+
+ if (m_bNewDoc)
+ ImportDop();
+
+ /*
+ Import revisioning data: author names
+ */
+ if( m_xWwFib->m_lcbSttbfRMark )
+ {
+ ReadRevMarkAuthorStrTabl(*m_pTableStream,
+ m_xWwFib->m_fcSttbfRMark,
+ m_xWwFib->m_lcbSttbfRMark, m_rDoc);
+ }
+
+ // Initialize our String/ID map for Linked Sections
+ std::vector<OUString> aLinkStrings;
+ std::vector<ww::bytes> aStringIds;
+
+ WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcSttbFnm,
+ m_xWwFib->m_lcbSttbFnm, m_bVer67 ? 2 : 0, m_eStructCharSet,
+ aLinkStrings, &aStringIds);
+
+ for (size_t i=0; i < aLinkStrings.size() && i < aStringIds.size(); ++i)
+ {
+ const ww::bytes& stringId = aStringIds[i];
+ if (stringId.size() < sizeof(WW8_STRINGID))
+ {
+ SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: WW8_STRINGID is too short");
+ continue;
+ }
+ const WW8_STRINGID *stringIdStruct = reinterpret_cast<const WW8_STRINGID*>(stringId.data());
+ m_aLinkStringMap[SVBT16ToUInt16(stringIdStruct->nStringId)] = aLinkStrings[i];
+ }
+
+ ReadDocVars(); // import document variables as meta information.
+
+ m_xProgress->Update(m_nProgress); // Update
+
+ m_xLstManager.reset(new WW8ListManager(*m_pTableStream, *this));
+
+ /*
+ first (1) import all styles (see WW8PAR2.CXX)
+ BEFORE the import of the lists !!
+ */
+ m_xProgress->Update(m_nProgress); // Update
+ m_xStyles.reset(new WW8RStyle(*m_xWwFib, this)); // Styles
+ m_xStyles->Import();
+
+ /*
+ In the end: (also see WW8PAR3.CXX)
+
+ Go through all Styles and attach respective List Format
+ AFTER we imported the Styles and AFTER we imported the Lists!
+ */
+ m_xProgress->Update(m_nProgress); // Update
+ m_xStyles->PostProcessStyles();
+
+ if (!m_vColl.empty())
+ SetOutlineStyles();
+
+ m_xSBase.reset(new WW8ScannerBase(m_pStrm,m_pTableStream,m_pDataStream, m_xWwFib.get()));
+
+ if (m_xSBase->AreThereFootnotes())
+ {
+ static const SwFootnoteNum eNumA[4] =
+ {
+ FTNNUM_DOC, FTNNUM_CHAPTER, FTNNUM_PAGE, FTNNUM_DOC
+ };
+
+ SwFootnoteInfo aInfo = m_rDoc.GetFootnoteInfo(); // Copy-Ctor private
+
+ aInfo.m_ePos = FTNPOS_PAGE;
+ aInfo.m_eNum = eNumA[m_xWDop->rncFootnote];
+ sal_uInt16 nfcFootnoteRef = m_xWDop->nfcFootnoteRef;
+ aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcFootnoteRef));
+ if( m_xWDop->nFootnote )
+ aInfo.m_nFootnoteOffset = m_xWDop->nFootnote - 1;
+ m_rDoc.SetFootnoteInfo( aInfo );
+ }
+ if (m_xSBase->AreThereEndnotes())
+ {
+ SwEndNoteInfo aInfo = m_rDoc.GetEndNoteInfo(); // Same as for Footnote
+ sal_uInt16 nfcEdnRef = m_xWDop->nfcEdnRef;
+ aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcEdnRef));
+ if( m_xWDop->nEdn )
+ aInfo.m_nFootnoteOffset = m_xWDop->nEdn - 1;
+ m_rDoc.SetEndNoteInfo( aInfo );
+
+ if (m_xSBase->GetEndnoteCount() > 2)
+ {
+ // This compatibility flag only works in easy cases, disable it for anything non-trivial
+ // for now.
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, false);
+ }
+ }
+
+ if (m_xWwFib->m_lcbPlcfhdd)
+ m_xHdFt.reset(new WW8PLCF_HdFt(m_pTableStream, *m_xWwFib, *m_xWDop));
+
+ if (!m_bNewDoc)
+ {
+ // inserting into an existing document:
+ // As only complete paragraphs are inserted, the current one
+ // needs to be split - once or even twice.
+ const SwPosition* pPos = m_pPaM->GetPoint();
+
+ // split current paragraph to get new paragraph for the insertion
+ m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
+
+ // another split, if insertion position was not at the end of the current paragraph.
+ SwTextNode const*const pTextNd = pPos->GetNode().GetTextNode();
+ if ( pTextNd->GetText().getLength() )
+ {
+ m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false );
+ // move PaM back to the newly empty paragraph
+ m_pPaM->Move( fnMoveBackward );
+ }
+
+ // suppress insertion of tables inside footnotes.
+ const SwNodeOffset nNd = pPos->GetNodeIndex();
+ m_bReadNoTable = ( nNd < m_rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
+ m_rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() < nNd );
+ }
+
+ m_xProgress->Update(m_nProgress); // Update
+
+ // loop for each glossary entry and add dummy section node
+ if (pGloss)
+ {
+ WW8PLCF aPlc(*m_pTableStream, m_xWwFib->m_fcPlcfglsy, m_xWwFib->m_lcbPlcfglsy, 0);
+
+ WW8_CP nStart, nEnd;
+ void* pDummy;
+
+ for (int i = 0; i < pGloss->GetNoStrings(); ++i, aPlc.advance())
+ {
+ SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfContent());
+ SwTextFormatColl* pColl =
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,
+ false);
+ SwStartNode *pNode =
+ m_rDoc.GetNodes().MakeTextSection(aIdx.GetNode(),
+ SwNormalStartNode,pColl);
+ m_pPaM->GetPoint()->Assign( pNode->GetIndex()+1 );
+ aPlc.Get( nStart, nEnd, pDummy );
+ ReadText(nStart,nEnd-nStart-1,MAN_MAINTEXT);
+ }
+ }
+ else // ordinary case
+ {
+ if (m_bNewDoc && m_pStg) /*meaningless for a glossary */
+ {
+ m_pDocShell->SetIsTemplate( m_xWwFib->m_fDot ); // point at tgc record
+ uno::Reference<document::XDocumentPropertiesSupplier> const
+ xDocPropSupp(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW );
+
+ OUString sCreatedFrom = xDocProps->getTemplateURL();
+ uno::Reference< container::XNameContainer > xPrjNameCache;
+ uno::Reference< lang::XMultiServiceFactory> xSF(m_pDocShell->GetModel(), uno::UNO_QUERY);
+ if ( xSF.is() )
+ xPrjNameCache.set( xSF->createInstance( "ooo.vba.VBAProjectNameProvider" ), uno::UNO_QUERY );
+
+ // Read Global templates
+ ReadGlobalTemplateSettings( sCreatedFrom, xPrjNameCache );
+
+ // Create and insert Word vba Globals
+ uno::Any aGlobs;
+ uno::Sequence< uno::Any > aArgs{ uno::Any(m_pDocShell->GetModel()) };
+ try
+ {
+ aGlobs <<= ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( "ooo.vba.word.Globals", aArgs );
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: ooo.vba.word.Globals is not available");
+ }
+
+#if HAVE_FEATURE_SCRIPTING
+ if (!m_bFuzzing)
+ {
+ BasicManager *pBasicMan = m_pDocShell->GetBasicManager();
+ if (pBasicMan)
+ pBasicMan->SetGlobalUNOConstant( "VBAGlobals", aGlobs );
+ }
+#endif
+ BasicProjImportHelper aBasicImporter( *m_pDocShell );
+ // Import vba via oox filter
+ bool bRet = aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() );
+
+ lcl_createTemplateToProjectEntry( xPrjNameCache, sCreatedFrom, aBasicImporter.getProjectName() );
+ WW8Customizations aCustomisations( m_pTableStream, *m_xWwFib );
+ aCustomisations.Import( m_pDocShell );
+
+ if( bRet )
+ m_rDoc.SetContainsMSVBasic(true);
+
+ StoreMacroCmds();
+ }
+ ReadText(0, m_xWwFib->m_ccpText, MAN_MAINTEXT);
+ }
+
+ m_xProgress->Update(m_nProgress); // Update
+
+ if (m_pDrawPg && m_xMSDffManager && m_xMSDffManager->GetShapeOrders())
+ {
+ // Helper array to chain the inserted frames (instead of SdrTextObj)
+ SvxMSDffShapeTxBxSort aTxBxSort;
+
+ // Ensure correct z-order of read Escher objects
+ sal_uInt16 nShapeCount = m_xMSDffManager->GetShapeOrders()->size();
+
+ for (sal_uInt16 nShapeNum=0; nShapeNum < nShapeCount; nShapeNum++)
+ {
+ SvxMSDffShapeOrder *pOrder =
+ (*m_xMSDffManager->GetShapeOrders())[nShapeNum].get();
+ // Insert Pointer into new Sort array
+ if (pOrder->nTxBxComp && pOrder->pFly)
+ aTxBxSort.insert(pOrder);
+ }
+ // Chain Frames
+ if( !aTxBxSort.empty() )
+ {
+ SwFormatChain aChain;
+ for( SvxMSDffShapeTxBxSort::iterator it = aTxBxSort.begin(); it != aTxBxSort.end(); ++it )
+ {
+ SvxMSDffShapeOrder *pOrder = *it;
+
+ // Initialize FlyFrame Formats
+ SwFlyFrameFormat* pFlyFormat = pOrder->pFly;
+ SwFlyFrameFormat* pNextFlyFormat = nullptr;
+ SwFlyFrameFormat* pPrevFlyFormat = nullptr;
+
+ // Determine successor, if we can
+ SvxMSDffShapeTxBxSort::iterator tmpIter1 = it;
+ ++tmpIter1;
+ if( tmpIter1 != aTxBxSort.end() )
+ {
+ SvxMSDffShapeOrder *pNextOrder = *tmpIter1;
+ if ((0xFFFF0000 & pOrder->nTxBxComp)
+ == (0xFFFF0000 & pNextOrder->nTxBxComp))
+ pNextFlyFormat = pNextOrder->pFly;
+ }
+ // Determine predecessor, if we can
+ if( it != aTxBxSort.begin() )
+ {
+ SvxMSDffShapeTxBxSort::iterator tmpIter2 = it;
+ --tmpIter2;
+ SvxMSDffShapeOrder *pPrevOrder = *tmpIter2;
+ if ((0xFFFF0000 & pOrder->nTxBxComp)
+ == (0xFFFF0000 & pPrevOrder->nTxBxComp))
+ pPrevFlyFormat = pPrevOrder->pFly;
+ }
+ // If successor or predecessor present, insert the
+ // chain at the FlyFrame Format
+ if (pNextFlyFormat || pPrevFlyFormat)
+ {
+ aChain.SetNext( pNextFlyFormat );
+ aChain.SetPrev( pPrevFlyFormat );
+ pFlyFormat->SetFormatAttr( aChain );
+ }
+ }
+ }
+ }
+
+ bool isHideRedlines(false);
+
+ if (m_bNewDoc)
+ {
+ if( m_xWDop->fRevMarking )
+ eMode |= RedlineFlags::On;
+ isHideRedlines = !m_xWDop->fRMView;
+ }
+
+ m_aInsertedTables.DelAndMakeTableFrames();
+ m_aSectionManager.InsertSegments();
+
+ m_vColl.clear();
+
+ m_xStyles.reset();
+
+ m_xFormImpl.reset();
+ GraphicDtor();
+ m_xMSDffManager.reset();
+ m_xHdFt.reset();
+ m_xSBase.reset();
+ m_xWDop.reset();
+ m_xFonts.reset();
+ m_xAtnNames.reset();
+ m_oSprmParser.reset();
+ m_xProgress.reset();
+
+ m_pDataStream = nullptr;
+ m_pTableStream = nullptr;
+
+ DeleteCtrlStack();
+ DeleteAnchorStack();
+ DeleteRefStacks();
+ m_oLastAnchorPos.reset();//ensure this is deleted before UpdatePageDescs
+ // ofz#10994 remove any trailing fly paras before processing redlines
+ m_xWFlyPara.reset();
+ // ofz#12660 remove any trailing fly paras before deleting extra paras
+ m_xSFlyPara.reset();
+ // remove extra paragraphs after attribute ctrl
+ // stacks etc. are destroyed, and before fields
+ // are updated
+ m_aExtraneousParas.delete_all_from_doc();
+ m_xRedlineStack->closeall(*m_pPaM->GetPoint());
+
+ // For i120928,achieve the graphics from the special bookmark with is for graphic bullet
+ {
+ std::vector<const SwGrfNode*> vecBulletGrf;
+ std::vector<SwFrameFormat*> vecFrameFormat;
+
+ IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess();
+ if ( pMarkAccess )
+ {
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark( "_PictureBullets" );
+ if ( ppBkmk != pMarkAccess->getBookmarksEnd() &&
+ IDocumentMarkAccess::GetType(**ppBkmk) == IDocumentMarkAccess::MarkType::BOOKMARK )
+ {
+ SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().GetNode().GetTextNode();
+
+ if ( pTextNode )
+ {
+ const SwpHints* pHints = pTextNode->GetpSwpHints();
+ for( size_t nHintPos = 0; pHints && nHintPos < pHints->Count(); ++nHintPos)
+ {
+ const SwTextAttr *pHt = pHints->Get(nHintPos);
+ if (pHt->Which() != RES_TXTATR_FLYCNT)
+ continue;
+ const sal_Int32 st = pHt->GetStart();
+ if (st >= (*ppBkmk)->GetMarkStart().GetContentIndex())
+ {
+ SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
+ vecFrameFormat.push_back(pFrameFormat);
+ const SwNodeIndex* pNdIdx = pFrameFormat->GetContent().GetContentIdx();
+ const SwNodes* pNodesArray = (pNdIdx != nullptr)
+ ? &(pNdIdx->GetNodes())
+ : nullptr;
+ const SwGrfNode *pGrf = (pNodesArray != nullptr)
+ ? (*pNodesArray)[pNdIdx->GetIndex() + 1]->GetGrfNode()
+ : nullptr;
+ vecBulletGrf.push_back(pGrf);
+ }
+ }
+ // update graphic bullet information
+ size_t nCount = m_xLstManager->GetWW8LSTInfoNum();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SwNumRule* pRule = m_xLstManager->GetNumRule(i);
+ for (sal_uInt16 j = 0; j < MAXLEVEL; ++j)
+ {
+ SwNumFormat aNumFormat(pRule->Get(j));
+ const sal_Int16 nType = aNumFormat.GetNumberingType();
+ const sal_uInt16 nGrfBulletCP = aNumFormat.GetGrfBulletCP();
+ if ( nType == SVX_NUM_BITMAP
+ && vecBulletGrf.size() > nGrfBulletCP
+ && vecBulletGrf[nGrfBulletCP] != nullptr )
+ {
+ Graphic aGraphic = vecBulletGrf[nGrfBulletCP]->GetGrf();
+ SvxBrushItem aBrush(aGraphic, GPOS_AREA, SID_ATTR_BRUSH);
+ const vcl::Font& aFont = numfunc::GetDefBulletFont();
+ int nHeight = aFont.GetFontHeight() * 12;
+ Size aPrefSize( aGraphic.GetPrefSize());
+ if (aPrefSize.Height() * aPrefSize.Width() != 0 )
+ {
+ int nWidth = (nHeight * aPrefSize.Width()) / aPrefSize.Height();
+ Size aSize(nWidth, nHeight);
+ aNumFormat.SetGraphicBrush(&aBrush, &aSize);
+ }
+ else
+ {
+ aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aNumFormat.SetBulletChar(0x2190);
+ }
+ pRule->Set( j, aNumFormat );
+ }
+ }
+ }
+ // Remove additional pictures
+ for (SwFrameFormat* p : vecFrameFormat)
+ {
+ m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat(p);
+ }
+ }
+ }
+ }
+ m_xLstManager.reset();
+ }
+
+ m_oPosAfterTOC.reset();
+ m_xRedlineStack.reset();
+ mpCursor.reset();
+ m_pPaM = nullptr;
+
+ UpdateFields();
+
+ // delete the pam before the call for hide all redlines (Bug 73683)
+ if (m_bNewDoc)
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(eMode);
+
+ UpdatePageDescs(m_rDoc, nPageDescOffset);
+
+ // can't set it on the layout or view shell because it doesn't exist yet
+ m_rDoc.GetDocumentRedlineManager().SetHideRedlines(isHideRedlines);
+
+ return ERRCODE_NONE;
+}
+
+ErrCode SwWW8ImplReader::SetSubStreams(tools::SvRef<SotStorageStream> &rTableStream,
+ tools::SvRef<SotStorageStream> &rDataStream)
+{
+ ErrCode nErrRet = ERRCODE_NONE;
+ // 6 stands for "6 OR 7", 7 stands for "ONLY 7"
+ switch (m_xWwFib->m_nVersion)
+ {
+ case 6:
+ case 7:
+ m_pTableStream = m_pStrm;
+ m_pDataStream = m_pStrm;
+ break;
+ case 8:
+ if(!m_pStg)
+ {
+ OSL_ENSURE( m_pStg, "Version 8 always needs to have a Storage!!" );
+ nErrRet = ERR_SWG_READ_ERROR;
+ break;
+ }
+
+ rTableStream = m_pStg->OpenSotStream(
+ m_xWwFib->m_fWhichTableStm ? SL::a1Table : SL::a0Table,
+ StreamMode::STD_READ);
+
+ m_pTableStream = rTableStream.get();
+ m_pTableStream->SetEndian( SvStreamEndian::LITTLE );
+
+ rDataStream = m_pStg->OpenSotStream(SL::aData, StreamMode::STD_READ);
+
+ if (rDataStream.is() && ERRCODE_NONE == rDataStream->GetError())
+ {
+ m_pDataStream = rDataStream.get();
+ m_pDataStream->SetEndian(SvStreamEndian::LITTLE);
+ }
+ else
+ m_pDataStream = m_pStrm;
+ break;
+ default:
+ // Program error!
+ OSL_ENSURE( false, "We forgot to encode nVersion!" );
+ nErrRet = ERR_SWG_READ_ERROR;
+ break;
+ }
+ return nErrRet;
+}
+
+namespace
+{
+ SvStream* MakeTemp(std::optional<utl::TempFileFast>& roTempFile)
+ {
+ roTempFile.emplace();
+ return roTempFile->GetStream(StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
+ }
+
+#define WW_BLOCKSIZE 0x200
+
+ void DecryptRC4(msfilter::MSCodec97& rCtx, SvStream &rIn, SvStream &rOut)
+ {
+ const std::size_t nLen = rIn.TellEnd();
+ rIn.Seek(0);
+
+ sal_uInt8 in[WW_BLOCKSIZE];
+ for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
+ {
+ std::size_t nBS = std::min<size_t>(nLen - nI, WW_BLOCKSIZE);
+ nBS = rIn.ReadBytes(in, nBS);
+ rCtx.InitCipher(nBlock);
+ rCtx.Decode(in, nBS, in, nBS);
+ rOut.WriteBytes(in, nBS);
+ }
+ }
+
+ void DecryptXOR(msfilter::MSCodec_XorWord95 &rCtx, SvStream &rIn, SvStream &rOut)
+ {
+ std::size_t nSt = rIn.Tell();
+ std::size_t nLen = rIn.TellEnd();
+
+ rCtx.InitCipher();
+ rCtx.Skip(nSt);
+
+ sal_uInt8 in[0x4096];
+ for (std::size_t nI = nSt; nI < nLen; nI += 0x4096)
+ {
+ std::size_t nBS = std::min<size_t>(nLen - nI, 0x4096 );
+ nBS = rIn.ReadBytes(in, nBS);
+ rCtx.Decode(in, nBS);
+ rOut.WriteBytes(in, nBS);
+ }
+ }
+
+ // moan, copy and paste :-(
+ OUString QueryPasswordForMedium(SfxMedium& rMedium)
+ {
+ OUString aPassw;
+
+ if (const SfxStringItem* pPasswordItem = rMedium.GetItemSet().GetItemIfSet(SID_PASSWORD))
+ aPassw = pPasswordItem->GetValue();
+ else
+ {
+ try
+ {
+ uno::Reference< task::XInteractionHandler > xHandler( rMedium.GetInteractionHandler() );
+ if( xHandler.is() )
+ {
+ rtl::Reference<::comphelper::DocPasswordRequest> pRequest = new ::comphelper::DocPasswordRequest(
+ ::comphelper::DocPasswordRequestType::MS, task::PasswordRequestMode_PASSWORD_ENTER,
+ INetURLObject(rMedium.GetOrigURL())
+ .GetLastName(INetURLObject::DecodeMechanism::WithCharset));
+
+ xHandler->handle( pRequest );
+
+ if( pRequest->isPassword() )
+ aPassw = pRequest->getPassword();
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+
+ return aPassw;
+ }
+
+ uno::Sequence< beans::NamedValue > InitXorWord95Codec( ::msfilter::MSCodec_XorWord95& rCodec, SfxMedium& rMedium, WW8Fib const * pWwFib )
+ {
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionData = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
+ aEncryptionData.realloc( 0 );
+
+ if ( !aEncryptionData.hasElements() )
+ {
+ OUString sUniPassword = QueryPasswordForMedium( rMedium );
+
+ OString sPassword(OUStringToOString(sUniPassword,
+ WW8Fib::GetFIBCharset(pWwFib->m_chseTables, pWwFib->m_lid)));
+
+ sal_Int32 nLen = sPassword.getLength();
+ if( nLen <= 15 )
+ {
+ sal_uInt8 pPassword[16];
+ memcpy(pPassword, sPassword.getStr(), nLen);
+ memset(pPassword+nLen, 0, sizeof(pPassword)-nLen);
+
+ rCodec.InitKey( pPassword );
+ aEncryptionData = rCodec.GetEncryptionData();
+
+ // the export supports RC4 algorithm only, so we have to
+ // generate the related EncryptionData as well, so that Save
+ // can export the document without asking for a password;
+ // as result there will be EncryptionData for both algorithms
+ // in the MediaDescriptor
+ ::msfilter::MSCodec_Std97 aCodec97;
+
+ rtlRandomPool aRandomPool = rtl_random_createPool();
+ sal_uInt8 pDocId[ 16 ];
+ rtl_random_getBytes( aRandomPool, pDocId, 16 );
+
+ rtl_random_destroyPool( aRandomPool );
+
+ sal_uInt16 pStd97Pass[16] = {};
+ for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
+ pStd97Pass[nChar] = sUniPassword[nChar];
+
+ aCodec97.InitKey( pStd97Pass, pDocId );
+
+ // merge the EncryptionData, there should be no conflicts
+ ::comphelper::SequenceAsHashMap aEncryptionHash( aEncryptionData );
+ aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
+ aEncryptionHash >> aEncryptionData;
+ }
+ }
+
+ return aEncryptionData;
+ }
+
+ uno::Sequence< beans::NamedValue > Init97Codec(msfilter::MSCodec97& rCodec, sal_uInt8 const pDocId[16], SfxMedium& rMedium)
+ {
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionData = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
+ aEncryptionData.realloc( 0 );
+
+ if ( !aEncryptionData.hasElements() )
+ {
+ OUString sUniPassword = QueryPasswordForMedium( rMedium );
+
+ sal_Int32 nLen = sUniPassword.getLength();
+ if ( nLen <= 15 )
+ {
+ sal_uInt16 pPassword[16] = {};
+ for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
+ pPassword[nChar] = sUniPassword[nChar];
+
+ rCodec.InitKey( pPassword, pDocId );
+ aEncryptionData = rCodec.GetEncryptionData();
+ }
+ }
+
+ return aEncryptionData;
+ }
+}
+
+//TO-DO: merge this with lclReadFilepass8_Strong in sc which uses a different
+//stream thing
+static bool lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo &info, SvStream &rStream)
+{
+ //It is possible there are other variants in existence but these
+ //are the defaults I get with Word 2013
+
+ rStream.ReadUInt32(info.header.flags);
+ if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
+ return false;
+
+ sal_uInt32 nHeaderSize(0);
+ rStream.ReadUInt32(nHeaderSize);
+ sal_uInt32 actualHeaderSize = sizeof(info.header);
+
+ if (nHeaderSize < actualHeaderSize)
+ return false;
+
+ rStream.ReadUInt32(info.header.flags);
+ rStream.ReadUInt32(info.header.sizeExtra);
+ rStream.ReadUInt32(info.header.algId);
+ rStream.ReadUInt32(info.header.algIdHash);
+ rStream.ReadUInt32(info.header.keyBits);
+ rStream.ReadUInt32(info.header.providedType);
+ rStream.ReadUInt32(info.header.reserved1);
+ rStream.ReadUInt32(info.header.reserved2);
+
+ rStream.SeekRel(nHeaderSize - actualHeaderSize);
+
+ rStream.ReadUInt32(info.verifier.saltSize);
+ if (info.verifier.saltSize != msfilter::SALT_LENGTH)
+ return false;
+ rStream.ReadBytes(&info.verifier.salt, sizeof(info.verifier.salt));
+ rStream.ReadBytes(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
+
+ rStream.ReadUInt32(info.verifier.encryptedVerifierHashSize);
+ if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
+ return false;
+ rStream.ReadBytes(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
+
+ // check flags and algorithm IDs, required are AES128 and SHA-1
+ if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
+ return false;
+
+ if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
+ return false;
+
+ if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
+ return false;
+
+ // hash algorithm ID 0 defaults to SHA-1 too
+ if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
+ return false;
+
+ return true;
+}
+
+ErrCode SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss)
+{
+ ErrCode nErrRet = ERRCODE_NONE;
+ if (pGloss)
+ m_xWwFib = pGloss->GetFib();
+ else
+ m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
+
+ if (m_xWwFib->m_nFibError)
+ nErrRet = ERR_SWG_READ_ERROR;
+
+ tools::SvRef<SotStorageStream> xTableStream, xDataStream;
+
+ if (!nErrRet)
+ nErrRet = SetSubStreams(xTableStream, xDataStream);
+
+ std::optional<utl::TempFileFast> oTempMain;
+ std::optional<utl::TempFileFast> oTempTable;
+ std::optional<utl::TempFileFast> oTempData;
+ SvStream* pDecryptMain = nullptr;
+ SvStream* pDecryptTable = nullptr;
+ SvStream* pDecryptData = nullptr;
+
+ bool bDecrypt = false;
+ enum {RC4CryptoAPI, RC4, XOR, Other} eAlgo = Other;
+ if (m_xWwFib->m_fEncrypted && !nErrRet)
+ {
+ if (!pGloss)
+ {
+ bDecrypt = true;
+ if (8 != m_xWwFib->m_nVersion)
+ eAlgo = XOR;
+ else
+ {
+ if (m_xWwFib->m_nKey != 0)
+ eAlgo = XOR;
+ else
+ {
+ m_pTableStream->Seek(0);
+ sal_uInt32 nEncType(0);
+ m_pTableStream->ReadUInt32(nEncType);
+ if (nEncType == msfilter::VERSION_INFO_1997_FORMAT)
+ eAlgo = RC4;
+ else if (nEncType == msfilter::VERSION_INFO_2007_FORMAT || nEncType == msfilter::VERSION_INFO_2007_FORMAT_SP2)
+ eAlgo = RC4CryptoAPI;
+ }
+ }
+ }
+ }
+
+ if (bDecrypt)
+ {
+ nErrRet = ERRCODE_SVX_WRONGPASS;
+ SfxMedium* pMedium = m_pDocShell->GetMedium();
+
+ if ( pMedium )
+ {
+ switch (eAlgo)
+ {
+ default:
+ nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
+ break;
+ case XOR:
+ {
+ msfilter::MSCodec_XorWord95 aCtx;
+ uno::Sequence< beans::NamedValue > aEncryptionData = InitXorWord95Codec(aCtx, *pMedium, m_xWwFib.get());
+
+ // if initialization has failed the EncryptionData should be empty
+ if (aEncryptionData.hasElements() && aCtx.VerifyKey(m_xWwFib->m_nKey, m_xWwFib->m_nHash))
+ {
+ nErrRet = ERRCODE_NONE;
+ pDecryptMain = MakeTemp(oTempMain);
+
+ m_pStrm->Seek(0);
+ size_t nUnencryptedHdr =
+ (8 == m_xWwFib->m_nVersion) ? 0x44 : 0x34;
+ std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
+ nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
+ pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
+ pIn.reset();
+
+ DecryptXOR(aCtx, *m_pStrm, *pDecryptMain);
+
+ if (!m_pTableStream || m_pTableStream == m_pStrm)
+ m_pTableStream = pDecryptMain;
+ else
+ {
+ pDecryptTable = MakeTemp(oTempTable);
+ DecryptXOR(aCtx, *m_pTableStream, *pDecryptTable);
+ m_pTableStream = pDecryptTable;
+ }
+
+ if (!m_pDataStream || m_pDataStream == m_pStrm)
+ m_pDataStream = pDecryptMain;
+ else
+ {
+ pDecryptData = MakeTemp(oTempData);
+ DecryptXOR(aCtx, *m_pDataStream, *pDecryptData);
+ m_pDataStream = pDecryptData;
+ }
+
+ pMedium->GetItemSet().ClearItem( SID_PASSWORD );
+ pMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+ }
+ }
+ break;
+ case RC4:
+ case RC4CryptoAPI:
+ {
+ std::unique_ptr<msfilter::MSCodec97> xCtx;
+ msfilter::RC4EncryptionInfo info;
+ bool bCouldReadHeaders;
+
+ if (eAlgo == RC4)
+ {
+ xCtx.reset(new msfilter::MSCodec_Std97);
+ assert(sizeof(info.verifier.encryptedVerifierHash) >= RTL_DIGEST_LENGTH_MD5);
+ bCouldReadHeaders =
+ checkRead(*m_pTableStream, info.verifier.salt, sizeof(info.verifier.salt)) &&
+ checkRead(*m_pTableStream, info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)) &&
+ checkRead(*m_pTableStream, info.verifier.encryptedVerifierHash, RTL_DIGEST_LENGTH_MD5);
+ }
+ else
+ {
+ xCtx.reset(new msfilter::MSCodec_CryptoAPI);
+ bCouldReadHeaders = lclReadCryptoAPIHeader(info, *m_pTableStream);
+ }
+
+ // if initialization has failed the EncryptionData should be empty
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if (bCouldReadHeaders)
+ aEncryptionData = Init97Codec(*xCtx, info.verifier.salt, *pMedium);
+ else
+ nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT;
+ if (aEncryptionData.hasElements() && xCtx->VerifyKey(info.verifier.encryptedVerifier,
+ info.verifier.encryptedVerifierHash))
+ {
+ nErrRet = ERRCODE_NONE;
+
+ pDecryptMain = MakeTemp(oTempMain);
+
+ m_pStrm->Seek(0);
+ std::size_t nUnencryptedHdr = 0x44;
+ std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]);
+ nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr);
+
+ DecryptRC4(*xCtx, *m_pStrm, *pDecryptMain);
+
+ pDecryptMain->Seek(0);
+ pDecryptMain->WriteBytes(pIn.get(), nUnencryptedHdr);
+ pIn.reset();
+
+ pDecryptTable = MakeTemp(oTempTable);
+ DecryptRC4(*xCtx, *m_pTableStream, *pDecryptTable);
+ m_pTableStream = pDecryptTable;
+
+ if (!m_pDataStream || m_pDataStream == m_pStrm)
+ m_pDataStream = pDecryptMain;
+ else
+ {
+ pDecryptData = MakeTemp(oTempData);
+ DecryptRC4(*xCtx, *m_pDataStream, *pDecryptData);
+ m_pDataStream = pDecryptData;
+ }
+
+ pMedium->GetItemSet().ClearItem( SID_PASSWORD );
+ pMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+ }
+ }
+ break;
+ }
+ }
+
+ if (nErrRet == ERRCODE_NONE)
+ {
+ m_pStrm = pDecryptMain;
+
+ m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion);
+ if (m_xWwFib->m_nFibError)
+ nErrRet = ERR_SWG_READ_ERROR;
+ }
+ }
+
+ if (!nErrRet)
+ nErrRet = CoreLoad(pGloss);
+
+ oTempMain.reset();
+ oTempTable.reset();
+ oTempData.reset();
+
+ m_xWwFib.reset();
+ return nErrRet;
+}
+
+void SwWW8ImplReader::SetOutlineStyles()
+{
+ // If we are inserted into a document then don't clobber existing outline
+ // levels.
+ sal_uInt16 nOutlineStyleListLevelWithAssignment = 0;
+ if (!m_bNewDoc)
+ {
+ ww8::ParaStyles aOutLined(sw::util::GetParaStyles(m_rDoc));
+ sw::util::SortByAssignedOutlineStyleListLevel(aOutLined);
+ ww8::ParaStyles::reverse_iterator aEnd = aOutLined.rend();
+ for ( ww8::ParaStyles::reverse_iterator aIter = aOutLined.rbegin(); aIter < aEnd; ++aIter)
+ {
+ if ((*aIter)->IsAssignedToListLevelOfOutlineStyle())
+ nOutlineStyleListLevelWithAssignment |= 1 << (*aIter)->GetAssignedOutlineStyleLevel();
+ else
+ break;
+ }
+ }
+
+ // Check applied WW8 list styles at WW8 Built-In Heading Styles
+ // - Choose the list style which occurs most often as the one which provides
+ // the list level properties for the Outline Style.
+ // - Populate temporary list of WW8 Built-In Heading Styles for further
+ // iteration
+ std::vector<SwWW8StyInf*> aWW8BuiltInHeadingStyles;
+ {
+ sal_uInt16 nStyle = 0;
+ std::map<const SwNumRule*, int> aWW8ListStyleCounts;
+ std::map<const SwNumRule*, bool> aPreventUseAsChapterNumbering;
+ for (SwWW8StyInf& rSI : m_vColl)
+ {
+ // Copy inherited numbering info since LO drops inheritance after ChapterNumbering
+ // and only applies listLevel via style with the selected ChapterNumbering LFO.
+ bool bReRegister = false;
+ if (rSI.m_nBase && rSI.m_nBase < m_vColl.size()
+ && m_vColl[rSI.m_nBase].HasWW8OutlineLevel())
+ {
+ if (rSI.m_nLFOIndex == USHRT_MAX)
+ {
+ rSI.m_nLFOIndex = m_vColl[rSI.m_nBase].m_nLFOIndex;
+
+ // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
+ // A style never inherits numbering from Chapter Numbering.
+ if (rSI.m_nLFOIndex != USHRT_MAX)
+ {
+ const SwNumRule* pNumRule = m_vColl[rSI.m_nBase].m_pOutlineNumrule;
+ if (pNumRule)
+ aPreventUseAsChapterNumbering[pNumRule] = true;
+ }
+ }
+ if (rSI.m_nListLevel == MAXLEVEL)
+ rSI.m_nListLevel = m_vColl[rSI.m_nBase].m_nListLevel;
+ if (rSI.mnWW8OutlineLevel == MAXLEVEL)
+ rSI.mnWW8OutlineLevel = m_vColl[rSI.m_nBase].mnWW8OutlineLevel;
+ bReRegister = true;
+ }
+
+ // Undefined listLevel is treated as the first level with valid numbering rule.
+ if (rSI.m_nLFOIndex < USHRT_MAX && rSI.m_nListLevel == MAXLEVEL)
+ {
+ rSI.m_nListLevel = 0;
+ bReRegister = true;
+ }
+
+ if (bReRegister)
+ RegisterNumFormatOnStyle(nStyle);
+
+ ++nStyle; // increment before the first "continue";
+
+ if (!rSI.m_bColl || !rSI.IsWW8BuiltInHeadingStyle() || !rSI.HasWW8OutlineLevel())
+ {
+ continue;
+ }
+
+ // When ANYTHING is wrong or strange, prohibit eligibility for ChapterNumbering.
+ if (rSI.IsOutlineNumbered() && rSI.m_nListLevel != rSI.mnWW8OutlineLevel)
+ {
+ aPreventUseAsChapterNumbering[rSI.m_pOutlineNumrule] = true;
+ }
+
+ aWW8BuiltInHeadingStyles.push_back(&rSI);
+
+ const SwNumRule* pWW8ListStyle = rSI.GetOutlineNumrule();
+ if (pWW8ListStyle != nullptr)
+ {
+ std::map<const SwNumRule*, int>::iterator aCountIter
+ = aWW8ListStyleCounts.find(pWW8ListStyle);
+ if (aCountIter == aWW8ListStyleCounts.end())
+ {
+ aWW8ListStyleCounts[pWW8ListStyle] = 1;
+ }
+ else
+ {
+ ++(aCountIter->second);
+ }
+ }
+ }
+
+ int nCurrentMaxCount = 0;
+ for (const auto& rEntry : aWW8ListStyleCounts)
+ {
+ if (aPreventUseAsChapterNumbering[rEntry.first])
+ continue;
+
+ if (rEntry.second > nCurrentMaxCount)
+ {
+ nCurrentMaxCount = rEntry.second;
+ m_pChosenWW8OutlineStyle = rEntry.first;
+ }
+ }
+ }
+
+ // - set list level properties of Outline Style - ODF's list style applied
+ // by default to headings
+ // - assign corresponding Heading Paragraph Styles to the Outline Style
+ // - If a heading Paragraph Styles is not applying the WW8 list style which
+ // had been chosen as
+ // the one which provides the list level properties for the Outline Style,
+ // its assignment to
+ // the Outline Style is removed. A potential applied WW8 list style is
+ // assigned directly and
+ // its default outline level is applied.
+ SwNumRule aOutlineRule(*m_rDoc.GetOutlineNumRule());
+ if (m_pChosenWW8OutlineStyle)
+ {
+ for (int i = 0; i < WW8ListManager::nMaxLevel; ++i)
+ {
+ // Don't clobber existing outline levels.
+ const sal_uInt16 nLevel = 1 << i;
+ if (!(nOutlineStyleListLevelWithAssignment & nLevel))
+ aOutlineRule.Set(i, m_pChosenWW8OutlineStyle->Get(i));
+ }
+ }
+
+ for (const SwWW8StyInf* pStyleInf : aWW8BuiltInHeadingStyles)
+ {
+ const sal_uInt16 nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
+ = 1 << pStyleInf->mnWW8OutlineLevel;
+ if (nOutlineStyleListLevelOfWW8BuiltInHeadingStyle
+ & nOutlineStyleListLevelWithAssignment)
+ {
+ continue;
+ }
+
+ // in case that there are more styles on this level ignore them
+ nOutlineStyleListLevelWithAssignment
+ |= nOutlineStyleListLevelOfWW8BuiltInHeadingStyle;
+
+ SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pStyleInf->m_pFormat);
+ if (pStyleInf->GetOutlineNumrule() != m_pChosenWW8OutlineStyle
+ || (pStyleInf->m_nListLevel < WW8ListManager::nMaxLevel
+ && pStyleInf->mnWW8OutlineLevel != pStyleInf->m_nListLevel))
+ {
+ // WW8 Built-In Heading Style does not apply the chosen one.
+ // --> delete assignment to OutlineStyle, but keep its current
+ // outline level
+ pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
+ // Apply existing WW8 list style a normal list style at the
+ // Paragraph Style
+ if (pStyleInf->GetOutlineNumrule() != nullptr)
+ {
+ pTextFormatColl->SetFormatAttr(
+ SwNumRuleItem(pStyleInf->GetOutlineNumrule()->GetName()));
+ }
+ // apply default outline level of WW8 Built-in Heading Style
+ const sal_uInt8 nOutlineLevel
+ = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(
+ pStyleInf->mnWW8OutlineLevel);
+ pTextFormatColl->SetFormatAttr(
+ SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel));
+ }
+ else
+ {
+ pTextFormatColl->AssignToListLevelOfOutlineStyle(
+ pStyleInf->mnWW8OutlineLevel);
+ }
+ }
+
+ if (m_pChosenWW8OutlineStyle)
+ {
+ m_rDoc.SetOutlineNumRule(aOutlineRule);
+ }
+}
+
+const OUString* SwWW8ImplReader::GetAnnotationAuthor(sal_uInt16 nIdx)
+{
+ if (!m_xAtnNames && m_xWwFib->m_lcbGrpStAtnOwners)
+ {
+ // Determine authors: can be found in the TableStream
+ m_xAtnNames.emplace();
+ SvStream& rStrm = *m_pTableStream;
+
+ auto nOldPos = rStrm.Tell();
+ bool bValidPos = checkSeek(rStrm, m_xWwFib->m_fcGrpStAtnOwners);
+ if (bValidPos)
+ {
+ tools::Long nRead = 0, nCount = m_xWwFib->m_lcbGrpStAtnOwners;
+ while (nRead < nCount && rStrm.good())
+ {
+ if( m_bVer67 )
+ {
+ m_xAtnNames->push_back(read_uInt8_PascalString(rStrm,
+ RTL_TEXTENCODING_MS_1252));
+ nRead += m_xAtnNames->rbegin()->getLength() + 1; // Length + sal_uInt8 count
+ }
+ else
+ {
+ m_xAtnNames->push_back(read_uInt16_PascalString(rStrm));
+ // Unicode: double the length + sal_uInt16 count
+ nRead += (m_xAtnNames->rbegin()->getLength() + 1)*2;
+ }
+ }
+ }
+ rStrm.Seek( nOldPos );
+ }
+
+ const OUString *pRet = nullptr;
+ if (m_xAtnNames && nIdx < m_xAtnNames->size())
+ pRet = &((*m_xAtnNames)[nIdx]);
+ return pRet;
+}
+
+void SwWW8ImplReader::GetSmartTagInfo(SwFltRDFMark& rMark)
+{
+ if (!m_pSmartTagData && m_xWwFib->m_lcbFactoidData)
+ {
+ m_pSmartTagData.reset(new WW8SmartTagData);
+ m_pSmartTagData->Read(*m_pTableStream, m_xWwFib->m_fcFactoidData, m_xWwFib->m_lcbFactoidData);
+ }
+
+ if (!m_pSmartTagData)
+ return;
+
+ // Check if the handle is a valid smart tag bookmark index.
+ size_t nIndex = rMark.GetHandle();
+ if (nIndex >= m_pSmartTagData->m_aPropBags.size())
+ return;
+
+ // Check if the smart tag bookmark refers to a valid factoid type.
+ const MSOPropertyBag& rPropertyBag = m_pSmartTagData->m_aPropBags[rMark.GetHandle()];
+ auto& rFactoidTypes = m_pSmartTagData->m_aPropBagStore.m_aFactoidTypes;
+ auto itPropertyBag = std::find_if(rFactoidTypes.begin(), rFactoidTypes.end(),
+ [&rPropertyBag](const MSOFactoidType& rType) { return rType.m_nId == rPropertyBag.m_nId; });
+ if (itPropertyBag == rFactoidTypes.end())
+ return;
+
+ // Check if the factoid is an RDF one.
+ const MSOFactoidType& rFactoidType = *itPropertyBag;
+ if (rFactoidType.m_aUri != "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
+ return;
+
+ // Finally put the relevant attributes to the mark.
+ std::vector< std::pair<OUString, OUString> > aAttributes;
+ for (const MSOProperty& rProperty : rPropertyBag.m_aProperties)
+ {
+ OUString aKey;
+ OUString aValue;
+ if (rProperty.m_nKey < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
+ aKey = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nKey];
+ if (rProperty.m_nValue < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size())
+ aValue = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nValue];
+ if (!aKey.isEmpty() && !aValue.isEmpty())
+ aAttributes.emplace_back(aKey, aValue);
+ }
+ rMark.SetAttributes(std::move(aAttributes));
+}
+
+ErrCode SwWW8ImplReader::LoadDoc(WW8Glossary *pGloss)
+{
+ ErrCode nErrRet = ERRCODE_NONE;
+
+ {
+ static const char* aNames[ 13 ] = {
+ "WinWord/WW", "WinWord/WW8", "WinWord/WWFT",
+ "WinWord/WWFLX", "WinWord/WWFLY",
+ "WinWord/WWF",
+ "WinWord/WWFA0", "WinWord/WWFA1", "WinWord/WWFA2",
+ "WinWord/WWFB0", "WinWord/WWFB1", "WinWord/WWFB2",
+ "WinWord/RegardHindiDigits"
+ };
+ sal_uInt64 aVal[ 13 ];
+
+ SwFilterOptions aOpt( 13, aNames, aVal );
+
+ m_nIniFlags = aVal[ 0 ];
+ m_nIniFlags1= aVal[ 1 ];
+ // Moves Flys by x twips to the right or left
+ m_nIniFlyDx = aVal[ 3 ];
+ m_nIniFlyDy = aVal[ 4 ];
+
+ m_nFieldFlags = aVal[ 5 ];
+ m_nFieldTagAlways[0] = aVal[ 6 ];
+ m_nFieldTagAlways[1] = aVal[ 7 ];
+ m_nFieldTagAlways[2] = aVal[ 8 ];
+ m_nFieldTagBad[0] = aVal[ 9 ];
+ m_nFieldTagBad[1] = aVal[ 10 ];
+ m_nFieldTagBad[2] = aVal[ 11 ];
+ m_bRegardHindiDigits = aVal[ 12 ] > 0;
+ }
+
+ sal_uInt16 nMagic(0);
+ m_pStrm->ReadUInt16( nMagic );
+
+ // Remember: 6 means "6 OR 7", 7 means "JUST 7"
+ switch (m_nWantedVersion)
+ {
+ case 6:
+ case 7:
+ if (
+ 0xa59b != nMagic && 0xa59c != nMagic &&
+ 0xa5dc != nMagic && 0xa5db != nMagic &&
+ (nMagic < 0xa697 || nMagic > 0xa699)
+ )
+ {
+ // Test for own 97 fake!
+ if (m_pStg && 0xa5ec == nMagic)
+ {
+ sal_uInt64 nCurPos = m_pStrm->Tell();
+ if (checkSeek(*m_pStrm, nCurPos + 2))
+ {
+ sal_uInt32 nfcMin(0);
+ m_pStrm->ReadUInt32( nfcMin );
+ if (0x300 != nfcMin)
+ nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
+ }
+ m_pStrm->Seek( nCurPos );
+ }
+ else
+ nErrRet = ERR_WW6_NO_WW6_FILE_ERR;
+ }
+ break;
+ case 8:
+ if (0xa5ec != nMagic)
+ nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
+ break;
+ default:
+ nErrRet = ERR_WW8_NO_WW8_FILE_ERR;
+ OSL_ENSURE( false, "We forgot to encode nVersion!" );
+ break;
+ }
+
+ if (!nErrRet)
+ nErrRet = LoadThroughDecryption(pGloss);
+
+ m_rDoc.PropagateOutlineRule();
+
+ return nErrRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOC()
+{
+ return new WW8Reader;
+}
+
+namespace
+{
+ class FontCacheGuard
+ {
+ public:
+ ~FontCacheGuard()
+ {
+ FlushFontCache();
+ }
+ };
+}
+
+bool TestImportDOC(SvStream &rStream, const OUString &rFltName)
+{
+ FontCacheGuard aFontCacheGuard;
+ std::unique_ptr<Reader> xReader(ImportDOC());
+
+ tools::SvRef<SotStorage> xStorage;
+ xReader->m_pStream = &rStream;
+ if (rFltName != "WW6")
+ {
+ try
+ {
+ xStorage = tools::SvRef<SotStorage>(new SotStorage(rStream));
+ if (xStorage->GetError())
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+ xReader->m_pStorage = xStorage.get();
+ }
+ xReader->SetFltName(rFltName);
+
+ SwGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ xDocSh->DoInitNew();
+ SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc();
+
+ SwPaM aPaM(pD->GetNodes().GetEndOfContent(), SwNodeOffset(-1));
+ pD->SetInReading(true);
+ bool bRet = xReader->Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE;
+ pD->SetInReading(false);
+
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW8(SvStream &rStream)
+{
+ return TestImportDOC(rStream, "CWW8");
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW6(SvStream &rStream)
+{
+ return TestImportDOC(rStream, "CWW6");
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW2(SvStream &rStream)
+{
+ return TestImportDOC(rStream, "WW6");
+}
+
+ErrCode WW8Reader::OpenMainStream( tools::SvRef<SotStorageStream>& rRef, sal_uInt16& rBuffSize )
+{
+ ErrCode nRet = ERR_SWG_READ_ERROR;
+ OSL_ENSURE(m_pStorage, "Where is my Storage?");
+ rRef = m_pStorage->OpenSotStream( "WordDocument", StreamMode::READ | StreamMode::SHARE_DENYALL);
+
+ if( rRef.is() )
+ {
+ if( ERRCODE_NONE == rRef->GetError() )
+ {
+ sal_uInt16 nOld = rRef->GetBufferSize();
+ rRef->SetBufferSize( rBuffSize );
+ rBuffSize = nOld;
+ nRet = ERRCODE_NONE;
+ }
+ else
+ nRet = rRef->GetError();
+ }
+ return nRet;
+}
+
+static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, std::u16string_view sPrefix)
+{
+ SvStorageInfoList aElements;
+ pStorage->FillInfoList(&aElements);
+ for (const auto & aElement : aElements)
+ {
+ OUString sStreamFullName = sPrefix.size() ? OUString::Concat(sPrefix) + "/" + aElement.GetName() : aElement.GetName();
+ if (aElement.IsStorage())
+ {
+ tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
+ lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
+ }
+ else
+ {
+ // Read stream
+ tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (rStream.is())
+ {
+ sal_Int32 nStreamSize = rStream->GetSize();
+ css::uno::Sequence< sal_Int8 > oData;
+ oData.realloc(nStreamSize);
+ sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
+ if (nStreamSize == nReadBytes)
+ aStreamsData[sStreamFullName] <<= oData;
+ }
+ }
+ }
+}
+
+ErrCode WW8Reader::DecryptDRMPackage()
+{
+ // We have DRM encrypted storage. We should try to decrypt it first, if we can
+ uno::Sequence< uno::Any > aArguments;
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (!xPackageEncryption.is())
+ {
+ // We do not know how to decrypt this
+ return ERRCODE_IO_ACCESSDENIED;
+ }
+
+ comphelper::SequenceAsHashMap aStreamsData;
+ lcl_getListOfStreams(m_pStorage.get(), aStreamsData, u"");
+
+ try {
+ uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
+ if (!xPackageEncryption->readEncryptionInfo(aStreams))
+ {
+ // We failed with decryption
+ return ERRCODE_IO_ACCESSDENIED;
+ }
+
+ tools::SvRef<SotStorageStream> rContentStream = m_pStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (!rContentStream.is())
+ {
+ return ERRCODE_IO_NOTEXISTS;
+ }
+
+ mDecodedStream = std::make_shared<SvMemoryStream>();
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
+ uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*mDecodedStream));
+
+ if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
+ {
+ // We failed with decryption
+ return ERRCODE_IO_ACCESSDENIED;
+ }
+
+ mDecodedStream->Seek(0);
+
+ // Further reading is done from new document
+ m_pStorage = new SotStorage(*mDecodedStream);
+
+ // Set the media descriptor data
+ uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData("");
+ m_pMedium->GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+ catch (const std::exception&)
+ {
+ return ERRCODE_IO_ACCESSDENIED;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCodeMsg WW8Reader::Read(SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & /* FileName */)
+{
+ sal_uInt16 nOldBuffSize = 32768;
+ bool bNew = !m_bInsertMode; // New Doc (no inserting)
+
+ tools::SvRef<SotStorageStream> refStrm; // So that no one else can steal the Stream
+ SvStream* pIn = m_pStream;
+
+ ErrCode nRet = ERRCODE_NONE;
+ sal_uInt8 nVersion = 8;
+
+ const OUString sFltName = GetFltName();
+ if ( sFltName=="WW6" )
+ {
+ if (m_pStream)
+ nVersion = 6;
+ else
+ {
+ OSL_ENSURE(false, "WinWord 95 Reader-Read without Stream");
+ nRet = ERR_SWG_READ_ERROR;
+ }
+ }
+ else
+ {
+ if ( sFltName=="CWW6" )
+ nVersion = 6;
+ else if ( sFltName=="CWW7" )
+ nVersion = 7;
+
+ if( m_pStorage.is() )
+ {
+ // Check if we have special encrypted content
+ tools::SvRef<SotStorageStream> rRef = m_pStorage->OpenSotStream("\006DataSpaces/DataSpaceInfo/\011DRMDataSpace", StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (rRef.is())
+ {
+ nRet = DecryptDRMPackage();
+ }
+ nRet = OpenMainStream(refStrm, nOldBuffSize);
+ pIn = refStrm.get();
+ }
+ else
+ {
+ OSL_ENSURE(false, "WinWord 95/97 Reader-Read without Storage");
+ nRet = ERR_SWG_READ_ERROR;
+ }
+ }
+
+ if( !nRet )
+ {
+ std::unique_ptr<SwWW8ImplReader> pRdr(new SwWW8ImplReader(nVersion, m_pStorage.get(), pIn, rDoc,
+ rBaseURL, bNew, m_bSkipImages, *rPaM.GetPoint()));
+ if (bNew)
+ {
+ rPaM.GetBound().nContent.Assign(nullptr, 0);
+ rPaM.GetBound(false).nContent.Assign(nullptr, 0);
+ }
+ try
+ {
+ nRet = pRdr->LoadDoc();
+ }
+ catch( const std::exception& )
+ {
+ nRet = ERR_WW8_NO_WW8_FILE_ERR;
+ }
+
+ if( refStrm.is() )
+ {
+ refStrm->SetBufferSize( nOldBuffSize );
+ refStrm.clear();
+ }
+ else
+ {
+ pIn->ResetError();
+ }
+
+ }
+ return nRet;
+}
+
+SwReaderType WW8Reader::GetReaderType()
+{
+ return SwReaderType::Storage | SwReaderType::Stream;
+}
+
+bool WW8Reader::HasGlossaries() const
+{
+ return true;
+}
+
+bool WW8Reader::ReadGlossaries(SwTextBlocks& rBlocks, bool bSaveRelFiles) const
+{
+ bool bRet=false;
+
+ WW8Reader *pThis = const_cast<WW8Reader *>(this);
+
+ sal_uInt16 nOldBuffSize = 32768;
+ tools::SvRef<SotStorageStream> refStrm;
+ if (!pThis->OpenMainStream(refStrm, nOldBuffSize))
+ {
+ WW8Glossary aGloss( refStrm, 8, m_pStorage.get() );
+ bRet = aGloss.Load( rBlocks, bSaveRelFiles );
+ }
+ return bRet;
+}
+
+bool SwMSDffManager::GetOLEStorageName(sal_uInt32 nOLEId, OUString& rStorageName,
+ tools::SvRef<SotStorage>& rSrcStorage, uno::Reference < embed::XStorage >& rDestStorage) const
+{
+ bool bRet = false;
+
+ sal_Int32 nPictureId = 0;
+ if (m_rReader.m_pStg)
+ {
+ // Via the TextBox-PLCF we get the right char Start-End positions
+ // We should then find the EmbeddedField and the corresponding Sprms
+ // in that Area.
+ // We only need the Sprm for the Picture Id.
+ sal_uInt64 nOldPos = m_rReader.m_pStrm->Tell();
+ {
+ // #i32596# - consider return value of method
+ // <rReader.GetTxbxTextSttEndCp(..)>. If it returns false, method
+ // wasn't successful. Thus, continue in this case.
+ // Note: Ask MM for initialization of <nStartCp> and <nEndCp>.
+ // Note: Ask MM about assertions in method <rReader.GetTxbxTextSttEndCp(..)>.
+ WW8_CP nStartCp, nEndCp;
+ if ( m_rReader.m_bDrawCpOValid && m_rReader.GetTxbxTextSttEndCp(nStartCp, nEndCp,
+ o3tl::narrowing<sal_uInt16>((nOLEId >> 16) & 0xFFFF),
+ o3tl::narrowing<sal_uInt16>(nOLEId & 0xFFFF)) )
+ {
+ WW8PLCFxSaveAll aSave;
+ m_rReader.m_xPlcxMan->SaveAllPLCFx( aSave );
+
+ nStartCp += m_rReader.m_nDrawCpO;
+ nEndCp += m_rReader.m_nDrawCpO;
+ WW8PLCFx_Cp_FKP* pChp = m_rReader.m_xPlcxMan->GetChpPLCF();
+ wwSprmParser aSprmParser(*m_rReader.m_xWwFib);
+ while (nStartCp <= nEndCp && !nPictureId)
+ {
+ if (!pChp->SeekPos( nStartCp))
+ break;
+ WW8PLCFxDesc aDesc;
+ pChp->GetSprms( &aDesc );
+
+ if (aDesc.nSprmsLen && aDesc.pMemPos) // Attributes present
+ {
+ auto nLen = aDesc.nSprmsLen;
+ const sal_uInt8* pSprm = aDesc.pMemPos;
+
+ while (nLen >= 2 && !nPictureId)
+ {
+ sal_uInt16 nId = aSprmParser.GetSprmId(pSprm);
+ sal_Int32 nSL = aSprmParser.GetSprmSize(nId, pSprm, nLen);
+
+ if( nLen < nSL )
+ break; // Not enough Bytes left
+
+ if (0x6A03 == nId)
+ {
+ nPictureId = SVBT32ToUInt32(pSprm +
+ aSprmParser.DistanceToData(nId));
+ bRet = true;
+ }
+ pSprm += nSL;
+ nLen -= nSL;
+ }
+ }
+ nStartCp = aDesc.nEndPos;
+ }
+
+ m_rReader.m_xPlcxMan->RestoreAllPLCFx( aSave );
+ }
+ }
+ m_rReader.m_pStrm->Seek( nOldPos );
+ }
+
+ if( bRet )
+ {
+ rStorageName = "_";
+ rStorageName += OUString::number(nPictureId);
+ rSrcStorage = m_rReader.m_pStg->OpenSotStorage(SL::aObjectPool);
+ if (!m_rReader.m_pDocShell)
+ bRet=false;
+ else
+ rDestStorage = m_rReader.m_pDocShell->GetStorage();
+ }
+ return bRet;
+}
+
+/**
+ * When reading a single Box (which possibly is part of a group), we do
+ * not yet have enough information to decide whether we need it as a TextField
+ * or not.
+ * So convert all of them as a precaution.
+ * FIXME: Actually implement this!
+ */
+bool SwMSDffManager::ShapeHasText(sal_uLong, sal_uLong) const
+{
+ return true;
+}
+
+bool SwWW8ImplReader::InEqualOrHigherApo(int nLvl) const
+{
+ if (nLvl)
+ --nLvl;
+ // #i60827# - check size of <maApos> to assure that <maApos.begin() + nLvl> can be performed.
+ if ( sal::static_int_cast< sal_Int32>(nLvl) >= sal::static_int_cast< sal_Int32>(m_aApos.size()) )
+ {
+ return false;
+ }
+ auto aIter = std::find(m_aApos.begin() + nLvl, m_aApos.end(), true);
+ return aIter != m_aApos.end();
+}
+
+bool SwWW8ImplReader::InEqualApo(int nLvl) const
+{
+ // If we are in a table, see if an apo was inserted at the level below the table.
+ if (nLvl)
+ --nLvl;
+ if (nLvl < 0 || o3tl::make_unsigned(nLvl) >= m_aApos.size())
+ return false;
+ return m_aApos[nLvl];
+}
+
+namespace sw::hack
+{
+ Position::Position(const SwPosition &rPos)
+ : maPtNode(rPos.GetNode()), mnPtContent(rPos.GetContentIndex())
+ {
+ }
+
+ Position::operator SwPosition() const
+ {
+ return SwPosition(maPtNode, maPtNode.GetNode().GetContentNode(), mnPtContent);
+ }
+}
+
+SwMacroInfo::SwMacroInfo()
+ : SdrObjUserData( SdrInventor::ScOrSwDraw, SW_UD_IMAPDATA )
+ , mnShapeId(-1)
+{
+}
+
+SwMacroInfo::~SwMacroInfo()
+{
+}
+
+std::unique_ptr<SdrObjUserData> SwMacroInfo::Clone( SdrObject* /*pObj*/ ) const
+{
+ return std::unique_ptr<SdrObjUserData>(new SwMacroInfo( *this ));
+}
+
+std::unique_ptr<SfxItemSet> SwWW8ImplReader::SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet)
+{
+ std::unique_ptr<SfxItemSet> xRet(std::move(m_xCurrentItemSet));
+ m_xCurrentItemSet = std::move(pItemSet);
+ return xRet;
+}
+
+void SwWW8ImplReader::NotifyMacroEventRead()
+{
+ if (m_bNotifyMacroEventRead)
+ return;
+ uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel());
+ comphelper::DocumentInfo::notifyMacroEventRead(xModel);
+ m_bNotifyMacroEventRead = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par.hxx b/sw/source/filter/ww8/ww8par.hxx
new file mode 100644
index 0000000000..f98bcc2ff5
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par.hxx
@@ -0,0 +1,1974 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8PAR_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8PAR_HXX
+
+#include <rtl/ustring.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include <filter/msfilter/util.hxx>
+#include <editeng/frmdir.hxx>
+#include <svl/zforlist.hxx>
+#include <fltshell.hxx>
+
+#include <svx/svdobj.hxx>
+
+#include <utility>
+#include <vector>
+#include <stack>
+#include <string_view>
+#include <deque>
+#include <map>
+#include <memory>
+#include <optional>
+
+#include "ww8struc.hxx"
+#include "ww8scan.hxx"
+#include "ww8glsy.hxx"
+#include "ww8graf.hxx"
+#include "wrtww8.hxx"
+#include <msfilter.hxx>
+#include <xmloff/odffields.hxx>
+#include <IMark.hxx>
+
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <swtypes.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <ndtxt.hxx>
+#include <editeng/lrspitem.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <oox/ole/olehelper.hxx>
+
+#define SW_UD_IMAPDATA 2
+
+class SwDoc;
+class SwPaM;
+class SfxPoolItem;
+class SwTextFormatColl;
+class SwPageDesc;
+class SvxBoxItem;
+class SwFormat;
+class SwNodeIndex;
+class SwFlyFrameFormat;
+class SwAttrSet;
+class SwNumRule;
+class SwFrameFormat;
+class Writer;
+class SwFormatField;
+class WW8Fib;
+class WW8PLCFMan;
+struct WW8PLCFManResult;
+class WW8RStyle;
+class WW8PLCF_HdFt;
+class WW8ScannerBase;
+struct WW8FlyPara;
+struct WW8SwFlyPara;
+struct WW8_PIC;
+class WW8TabDesc;
+struct WW8_SHD;
+struct WW8_OLST;
+class SwNumFormat;
+struct WW8_ANLD;
+struct WW8_ANLV;
+struct WW8_DO;
+struct WW8_DPHEAD;
+struct WW8_FSPA;
+class SdrModel;
+class SdrPage;
+class SdrObject;
+class SdrTextObj;
+class SdrUnoObj;
+class Size;
+class EditEngine;
+struct SwPosition;
+class WW8ReaderSave;
+struct WW8PicDesc;
+class Graphic;
+class SwFieldType;
+class SotStorage;
+class SwAttrSet;
+class GDIMetaFile;
+struct ESelection;
+class SfxItemSet;
+class OutlinerParaObject;
+enum class SwLineBreakClear;
+
+namespace com::sun::star{
+ namespace beans{ class XPropertySet;}
+ namespace form { class XFormComponent;}
+ namespace drawing{class XShape;}
+ namespace lang{class XMultiServiceFactory;}
+}
+
+// defines only for the WW8-variable of the INI file
+#define WW8FL_NO_STYLES 2
+#define WW8FL_NO_GRAF 0x80
+
+#define WW8FL_NO_OUTLINE 0x1000
+#define WW8FL_NO_IMPLPASP 0x4000 // no implicit para space
+#define WW8FL_NO_GRAFLAYER 0x8000
+
+// Add-on-filter-flags, valid from Winword 8 on
+#define WW8FL_NO_FLY_FOR_TXBX 1
+
+// List-Manager (from Ver8 on)
+
+struct WW8LFOInfo;
+
+class WW8Reader : public StgReader
+{
+ std::shared_ptr<SvStream> mDecodedStream;
+ virtual ErrCodeMsg Read(SwDoc &, const OUString& rBaseURL, SwPaM &, const OUString &) override;
+ ErrCode OpenMainStream( tools::SvRef<SotStorageStream>& rRef, sal_uInt16& rBuffSize );
+ ErrCode DecryptDRMPackage();
+public:
+ WW8Reader() {}
+ virtual SwReaderType GetReaderType() override;
+
+ virtual bool HasGlossaries() const override;
+ virtual bool ReadGlossaries( SwTextBlocks&, bool bSaveRelFiles ) const override;
+};
+
+class SwWW8ImplReader;
+struct WW8LSTInfo;
+class WW8ListManager
+{
+public:
+ WW8ListManager(SvStream& rSt_, SwWW8ImplReader& rReader_);
+ //Min and Max possible List Levels in Word
+ enum ListLevel {nMinLevel=1, nMaxLevel=9};
+ //the rParaSprms returns back the original word paragraph indent
+ //sprms which were attached to the original numbering format
+ SwNumRule* GetNumRuleForActivation(sal_uInt16 nLFOPosition, const sal_uInt8 nLevel,
+ std::vector<sal_uInt8> &rParaSprms, SwTextNode *pNode=nullptr);
+ SwNumRule* CreateNextRule(bool bSimple);
+ ~WW8ListManager();
+ SwNumRule* GetNumRule(size_t i);
+ size_t GetWW8LSTInfoNum() const{return maLSTInfos.size();}
+ static SvxNumType GetSvxNumTypeFromMSONFC(sal_uInt16 nMSONFC);
+
+private:
+ wwSprmParser maSprmParser;
+ SwWW8ImplReader& m_rReader;
+ SwDoc& m_rDoc;
+ const WW8Fib& m_rFib;
+ SvStream& m_rSt;
+ std::vector<std::unique_ptr<WW8LSTInfo>> maLSTInfos;
+ std::vector<std::unique_ptr<WW8LFOInfo>> m_LFOInfos;// D. from PLF LFO, sorted exactly like in the WW8 Stream
+ sal_uInt16 m_nUniqueList; // current number for creating unique list names
+ SprmResult GrpprlHasSprm(sal_uInt16 nId, sal_uInt8& rSprms, sal_uInt8 nLen);
+ WW8LSTInfo* GetLSTByListId( sal_uInt32 nIdLst ) const;
+ //the rParaSprms returns back the original word paragraph indent
+ //sprms which are attached to this numbering level
+ bool ReadLVL(SwNumFormat& rNumFormat, std::unique_ptr<SfxItemSet>& rpItemSet, sal_uInt16 nLevelStyle,
+ bool bSetStartNo, sal_uInt16 nLevel, ww::bytes &rParaSprms);
+
+ // character attributes from GrpprlChpx
+ typedef std::unique_ptr<SfxItemSet> WW8aISet[nMaxLevel];
+ // character style pointer
+ typedef SwCharFormat* WW8aCFormat[nMaxLevel];
+
+ void AdjustLVL(sal_uInt8 nLevel, SwNumRule& rNumRule, WW8aISet const & rListItemSet,
+ WW8aCFormat& aCharFormat, bool& bNewCharFormatCreated,
+ const OUString& aPrefix = OUString());
+
+ void ImplDestroy();
+
+ WW8ListManager(const WW8ListManager&) = delete;
+ WW8ListManager& operator=(const WW8ListManager&) = delete;
+ sal_uInt16 m_nLastLFOPosition;
+};
+
+struct WW8FlyPara
+{ // WinWord-attributes
+ // Attention: *DO NOT* reorder, since parts will be
+ // compared using memcmp
+ bool bVer67;
+ sal_Int16 nTDxaAbs, nTDyaAbs; // raw position
+ sal_Int16 nSp45, nSp28; // width / height
+ sal_Int16 nLeftMargin, nRightMargin, nUpperMargin, nLowerMargin; // borders
+ sal_uInt8 nTPc; // raw binding + alignment
+ sal_uInt8 nPWr; // Wrap-Mode ( 1 / 2; 0 = no Apo ? )
+ WW8_BRCVer9_5 brc; // borders Top, Left, Bottom, Right, Between
+ bool bBorderLines; // border lines
+ bool bGrafApo; // true: this frame is only used to position
+ // the contained graphics *not* as a character
+ bool mbVertSet; // true if vertical positioning has been set
+
+ WW8FlyPara(bool bIsVer67, const WW8FlyPara* pSrc = nullptr);
+ bool operator==(const WW8FlyPara& rSrc) const;
+ void Read(sal_uInt8 nSprmTPc, WW8PLCFx_Cp_FKP* pPap);
+ void ReadFull(sal_uInt8 nOrigSprmTPc, SwWW8ImplReader* pIo);
+ void Read(sal_uInt8 nSprmTPc, WW8RStyle const * pStyle);
+ void ApplyTabPos(const WW8_TablePos *pTabPos);
+ bool IsEmpty() const;
+};
+
+class SwWW8StyInf
+{
+ OUString m_sWWStyleName;
+ sal_uInt16 m_nWWStyleId;
+public:
+ rtl_TextEncoding m_eLTRFontSrcCharSet; // rtl_TextEncoding for the font
+ rtl_TextEncoding m_eRTLFontSrcCharSet; // rtl_TextEncoding for the font
+ rtl_TextEncoding m_eCJKFontSrcCharSet; // rtl_TextEncoding for the font
+ SwFormat* m_pFormat;
+ std::shared_ptr<WW8FlyPara> m_xWWFly;
+ SwNumRule* m_pOutlineNumrule;
+ tools::Long m_nFilePos;
+ sal_uInt16 m_nBase;
+ sal_uInt16 m_nFollow;
+ sal_uInt16 m_nLFOIndex;
+ sal_uInt8 m_nListLevel;
+
+ // WW8 outline level is zero-based:
+ // 0: outline level 1
+ // 1: outline level 2
+ // ...
+ // 8: outline level 9
+ // 9: body text
+ sal_uInt8 mnWW8OutlineLevel;
+
+ sal_uInt16 m_n81Flags; // for bold, italic, ...
+ sal_uInt16 m_n81BiDiFlags; // for bold, italic, ...
+ std::shared_ptr<SvxFirstLineIndentItem> m_pWordFirstLine;
+ std::shared_ptr<SvxTextLeftMarginItem> m_pWordLeftMargin;
+ std::shared_ptr<SvxRightMarginItem> m_pWordRightMargin;
+ bool m_bValid; // empty of valid
+ bool m_bImported; // for recursive imports
+ bool m_bColl; // true-> pFormat is SwTextFormatColl
+ bool m_bImportSkipped; // only true if !bNewDoc && existing style
+ bool m_bHasStyNumRule; // true-> named NumRule in style
+ bool m_bHasBrokenWW6List; // true-> WW8+ style has a WW7- list
+ bool m_bListRelevantIndentSet; //true if this style's indent has
+ //been explicitly set, it's set to the value
+ //of pFormat->GetItemState(RES_LR_SPACE, false)
+ //if it was possible to get the ItemState
+ //for L of the LR space independently
+ bool m_bParaAutoBefore; // For Auto spacing before a paragraph
+ bool m_bParaAutoAfter; // For Auto Spacing after a paragraph
+ sal_Int16 m_nRelativeJustify;
+
+ SwWW8StyInf() :
+ m_sWWStyleName( OUString() ),
+ m_nWWStyleId( 0 ),
+ m_eLTRFontSrcCharSet(0),
+ m_eRTLFontSrcCharSet(0),
+ m_eCJKFontSrcCharSet(0),
+ m_pFormat( nullptr ),
+ m_pOutlineNumrule( nullptr ),
+ m_nFilePos( 0 ),
+ m_nBase( 0 ),
+ m_nFollow( 0 ),
+ m_nLFOIndex( USHRT_MAX ),
+ m_nListLevel(MAXLEVEL),
+ mnWW8OutlineLevel( MAXLEVEL ),
+ m_n81Flags( 0 ),
+ m_n81BiDiFlags(0),
+ m_pWordFirstLine(std::make_shared<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE)),
+ m_pWordLeftMargin(std::make_shared<SvxTextLeftMarginItem>(RES_MARGIN_TEXTLEFT)),
+ m_pWordRightMargin(std::make_shared<SvxRightMarginItem>(RES_MARGIN_RIGHT)),
+ m_bValid(false),
+ m_bImported(false),
+ m_bColl(false),
+ m_bImportSkipped(false),
+ m_bHasStyNumRule(false),
+ m_bHasBrokenWW6List(false),
+ m_bListRelevantIndentSet(false),
+ m_bParaAutoBefore(false),
+ m_bParaAutoAfter(false),
+ m_nRelativeJustify(-1)
+
+ {}
+
+ void SetOrgWWIdent( const OUString& rName, const sal_uInt16 nId )
+ {
+ m_sWWStyleName = rName;
+ m_nWWStyleId = nId;
+
+ // apply default WW8 outline level to WW8 Built-in Heading styles
+ if (IsWW8BuiltInHeadingStyle())
+ {
+ mnWW8OutlineLevel = m_nWWStyleId - 1;
+ }
+ }
+
+ const OUString& GetOrgWWName() const
+ {
+ return m_sWWStyleName;
+ }
+
+ bool HasWW8OutlineLevel() const
+ {
+ return (m_pFormat != nullptr && (MAXLEVEL > mnWW8OutlineLevel));
+ }
+
+ bool IsOutlineNumbered() const
+ {
+ return m_pOutlineNumrule && HasWW8OutlineLevel();
+ }
+
+ const SwNumRule* GetOutlineNumrule() const
+ {
+ return m_pOutlineNumrule;
+ }
+ rtl_TextEncoding GetCharSet() const;
+ rtl_TextEncoding GetCJKCharSet() const;
+
+ sal_uInt16 GetWWStyleId() const
+ {
+ return m_nWWStyleId;
+ }
+
+ bool IsWW8BuiltInHeadingStyle() const
+ {
+ return GetWWStyleId() >= 1 && GetWWStyleId() <= 9;
+ }
+
+ bool IsWW8BuiltInDefaultStyle() const
+ {
+ return GetWWStyleId() == 0;
+ }
+
+ static sal_uInt8
+ WW8OutlineLevelToOutlinelevel(const sal_uInt8 nWW8OutlineLevel)
+ {
+ if (nWW8OutlineLevel < MAXLEVEL)
+ {
+ if (nWW8OutlineLevel == 9)
+ {
+ return 0; // no outline level --> body text
+ }
+ else
+ {
+ return nWW8OutlineLevel + 1; // outline level 1..9
+ }
+ }
+
+ return 0;
+ }
+};
+
+// Stack
+
+class SwWW8FltControlStack : public SwFltControlStack
+{
+private:
+ SwWW8ImplReader& m_rReader;
+ std::unique_ptr<SfxItemSet> m_xScratchSet;
+ sal_uInt16 m_nToggleAttrFlags;
+ sal_uInt16 m_nToggleBiDiAttrFlags;
+ SwWW8FltControlStack(const SwWW8FltControlStack&) = delete;
+ SwWW8FltControlStack& operator=(const SwWW8FltControlStack&) = delete;
+ const SwNumFormat* GetNumFormatFromStack(const SwPosition &rPos,
+ const SwTextNode &rTextNode);
+protected:
+ virtual void SetAttrInDoc(const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry) override;
+
+public:
+ SwWW8FltControlStack(SwDoc& rDo, sal_uLong nFieldFl, SwWW8ImplReader& rReader_ )
+ : SwFltControlStack( rDo, nFieldFl ), m_rReader( rReader_ ),
+ m_nToggleAttrFlags(0), m_nToggleBiDiAttrFlags(0)
+ {}
+
+ void NewAttr(const SwPosition& rPos, const SfxPoolItem& rAttr);
+
+ virtual SwFltStackEntry* SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, bool bTstEnd=true, tools::Long nHand=LONG_MAX, bool consumedByField=false) override;
+
+ void SetToggleAttr(sal_uInt8 nId, bool bOn)
+ {
+ if( bOn )
+ m_nToggleAttrFlags |= (1 << nId);
+ else
+ m_nToggleAttrFlags &= ~(1 << nId);
+ }
+
+ sal_uInt16 GetToggleAttrFlags() const { return m_nToggleAttrFlags; }
+
+ void SetToggleBiDiAttr(sal_uInt8 nId, bool bOn)
+ {
+ if( bOn )
+ m_nToggleBiDiAttrFlags |= (1 << nId);
+ else
+ m_nToggleBiDiAttrFlags &= ~(1 << nId);
+ }
+
+ sal_uInt16 GetToggleBiDiAttrFlags() const { return m_nToggleBiDiAttrFlags; }
+ void SetToggleAttrFlags(sal_uInt16 nFlags) { m_nToggleAttrFlags = nFlags; }
+ void SetToggleBiDiAttrFlags(sal_uInt16 nFlags) {m_nToggleBiDiAttrFlags = nFlags;}
+
+ const SfxPoolItem* GetFormatAttr(const SwPosition& rPos, sal_uInt16 nWhich);
+ template<class T> const T* GetFormatAttr( const SwPosition& rPos, TypedWhichId<T> nWhich )
+ {
+ return static_cast<const T*>(GetFormatAttr(rPos, sal_uInt16(nWhich)));
+ }
+ const SfxPoolItem* GetStackAttr(const SwPosition& rPos, sal_uInt16 nWhich);
+};
+
+//The only thing this is for is RES_FLTR_ANCHOR, anything else is an error.
+//For graphics whose anchoring position would otherwise be automatically moved
+//along by the insertion of text.
+class SwWW8FltAnchorStack : public SwFltControlStack
+{
+public:
+ SwWW8FltAnchorStack(SwDoc& rDo, sal_uLong nFieldFl)
+ : SwFltControlStack( rDo, nFieldFl ) {}
+ void AddAnchor(const SwPosition& rPos,SwFrameFormat *pFormat);
+ void Flush();
+private:
+ SwWW8FltAnchorStack(const SwWW8FltAnchorStack&) = delete;
+ SwWW8FltAnchorStack& operator=(const SwWW8FltAnchorStack&) = delete;
+};
+
+
+namespace SwWW8
+{
+ struct ltstr
+ {
+ bool operator()(std::u16string_view r1, std::u16string_view r2) const
+ {
+ return o3tl::compareToIgnoreAsciiCase(r1, r2)<0;
+ }
+ };
+};
+
+class SwWW8ReferencedFltEndStack : public SwFltEndStack
+{
+public:
+ SwWW8ReferencedFltEndStack( SwDoc& rDo, sal_uLong nFieldFl )
+ : SwFltEndStack( rDo, nFieldFl )
+ , m_aReferencedTOCBookmarks()
+ {}
+
+ // Keep track of referenced TOC bookmarks in order to suppress the import
+ // of unreferenced ones.
+ std::set<OUString, SwWW8::ltstr> m_aReferencedTOCBookmarks;
+protected:
+ virtual void SetAttrInDoc( const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry ) override;
+};
+
+class SwWW8FltRefStack final : public SwFltEndStack
+{
+public:
+ SwWW8FltRefStack(SwDoc& rDo, sal_uLong nFieldFl)
+ : SwFltEndStack( rDo, nFieldFl )
+ , m_aFieldVarNames()
+ {}
+ bool IsFootnoteEdnBkmField(const SwFormatField& rFormatField, sal_uInt16& rBkmNo);
+
+ //Keep track of variable names created with fields, and the bookmark
+ //mapped to their position, hopefully the same, but very possibly
+ //an additional pseudo bookmark
+ std::map<OUString, OUString, SwWW8::ltstr> m_aFieldVarNames;
+private:
+ SwFltStackEntry *RefToVar(const SwField* pField,SwFltStackEntry& rEntry);
+ virtual void SetAttrInDoc(const SwPosition& rTmpPos,
+ SwFltStackEntry& rEntry) override;
+ SwWW8FltRefStack(const SwWW8FltRefStack&) = delete;
+ SwWW8FltRefStack& operator=(const SwWW8FltRefStack&) = delete;
+};
+
+template< typename Type >
+inline bool get_flag( Type nBitField, Type nMask )
+{ return (nBitField & nMask) != 0; }
+
+template< typename ReturnType, typename Type >
+inline ReturnType ulimit_cast( Type nValue, ReturnType nMax )
+{ return static_cast< ReturnType >( std::min< Type >( nValue, nMax ) ); }
+
+template< typename ReturnType, typename Type >
+inline ReturnType ulimit_cast( Type nValue )
+{ return ulimit_cast( nValue, std::numeric_limits< ReturnType >::max() ); }
+
+class SwMacroInfo : public SdrObjUserData
+{
+public:
+ SwMacroInfo();
+ virtual ~SwMacroInfo() override;
+
+ SwMacroInfo(SwMacroInfo const &) = default;
+ SwMacroInfo(SwMacroInfo &&) = default;
+ SwMacroInfo & operator =(SwMacroInfo const &) = delete; // due to SdrObjUserData
+ SwMacroInfo & operator =(SwMacroInfo &&) = delete; // due to SdrObjUserData
+
+ virtual std::unique_ptr<SdrObjUserData> Clone( SdrObject* pObj ) const override;
+
+ void SetHlink( const OUString& rHlink ) { maHlink = rHlink; }
+ const OUString& GetHlink() const { return maHlink; }
+ void SetTarFrame( const OUString& rTarFrame ) { maTarFrame = rTarFrame; }
+ const OUString& GetTarFrame() const { return maTarFrame; }
+ void SetShapeId( sal_Int32 rShapeId ) { mnShapeId = rShapeId; }
+ const sal_Int32& GetShapeId() const { return mnShapeId; }
+ void SetName( const OUString& rName ) { maNameStr = rName; }
+ const OUString& GetName() const { return maNameStr; }
+
+private:
+ sal_Int32 mnShapeId;
+ OUString maHlink;
+ OUString maNameStr;
+ OUString maTarFrame;
+};
+
+struct HyperLinksTable
+{
+ OUString hLinkAddr;
+ OUString tarFrame;
+};
+
+namespace sw
+{
+ namespace hack
+ {
+ class Position
+ {
+ private:
+ SwNodeIndex maPtNode;
+ sal_Int32 mnPtContent;
+ public:
+ explicit Position(const SwPosition &rPos);
+ operator SwPosition() const;
+ const SwNodeIndex& GetPtNode() const { return maPtNode; };
+ sal_Int32 GetPtContent() const { return mnPtContent; };
+ };
+ }
+
+ auto FilterControlChars(std::u16string_view aString) -> OUString;
+}
+
+class WW8FieldEntry
+{
+ private:
+ OUString msBookmarkName;
+ OUString msMarkType;
+ OUString msMarkCode;
+ ::sw::mark::IFieldmark::parameter_map_t maParams;
+
+ public:
+ sw::hack::Position maStartPos;
+ sal_uInt16 mnFieldId;
+ sal_uLong mnObjLocFc;
+ WW8FieldEntry(SwPosition const &rPos, sal_uInt16 nFieldId) noexcept;
+ WW8FieldEntry(const WW8FieldEntry &rOther) noexcept;
+ WW8FieldEntry &operator=(const WW8FieldEntry &rOther) noexcept;
+ void Swap(WW8FieldEntry &rOther) noexcept;
+
+ const SwNodeIndex& GetPtNode() const { return maStartPos.GetPtNode(); };
+ sal_Int32 GetPtContent() const { return maStartPos.GetPtContent(); };
+
+ const OUString& GetBookmarkName() const { return msBookmarkName;}
+ const OUString& GetBookmarkCode() const { return msMarkCode;}
+ void SetBookmarkName(const OUString& bookmarkName);
+ void SetBookmarkType(const OUString& bookmarkType);
+ void SetBookmarkCode(const OUString& bookmarkCode);
+ ::sw::mark::IFieldmark::parameter_map_t& getParameters() { return maParams;}
+};
+
+// mini marker for some flags
+
+class WW8ReaderSave
+{
+private:
+ WW8PLCFxSaveAll maPLCFxSave;
+ std::shared_ptr<SwUnoCursor> mxTmpPos;
+ std::deque<bool> maOldApos;
+ std::deque<WW8FieldEntry> maOldFieldStack;
+ std::unique_ptr<SwWW8FltControlStack> mxOldStck;
+ std::unique_ptr<SwWW8FltAnchorStack> mxOldAnchorStck;
+ std::unique_ptr<sw::util::RedlineStack> mxOldRedlines;
+ std::shared_ptr<WW8PLCFMan> mxOldPlcxMan;
+ std::unique_ptr<WW8FlyPara> mpWFlyPara;
+ std::unique_ptr<WW8SwFlyPara> mpSFlyPara;
+ SwPaM* mpPreviousNumPaM;
+ const SwNumRule* mpPrevNumRule;
+ std::unique_ptr<WW8TabDesc> mxTableDesc;
+ int mnInTable;
+ sal_uInt16 mnCurrentColl;
+ sal_Unicode mcSymbol;
+ bool mbIgnoreText;
+ bool mbSymbol;
+ bool mbHdFtFootnoteEdn;
+ bool mbTxbxFlySection;
+ bool mbAnl;
+ bool mbInHyperlink;
+ bool mbPgSecBreak;
+ bool mbWasParaEnd;
+ bool mbHasBorder;
+ bool mbFirstPara;
+public:
+ WW8ReaderSave(SwWW8ImplReader* pRdr, WW8_CP nStart=-1);
+ void Restore(SwWW8ImplReader* pRdr);
+ const SwPosition &GetStartPos() const { return *mxTmpPos->GetPoint(); }
+};
+
+enum class eF_ResT { OK, TEXT, TAGIGN, READ_FSPA };
+
+class SwWW8Shade
+{
+public:
+ Color m_aColor;
+ SwWW8Shade(bool bVer67, const WW8_SHD& rSHD);
+ SwWW8Shade(Color nFore, Color nBack, sal_uInt16 nIndex)
+ {
+ SetShade(nFore, nBack, nIndex);
+ }
+private:
+ void SetShade(Color nFore, Color nBack, sal_uInt16 nIndex);
+};
+
+// Formulas
+
+enum SwWw8ControlType
+{
+ WW8_CT_EDIT,
+ WW8_CT_CHECKBOX,
+ WW8_CT_DROPDOWN
+};
+
+class WW8FormulaControl
+{
+protected:
+ SwWW8ImplReader &mrRdr;
+
+ WW8FormulaControl(WW8FormulaControl const&) = delete;
+ WW8FormulaControl& operator=(WW8FormulaControl const&) = delete;
+
+public:
+ WW8FormulaControl(OUString aN, SwWW8ImplReader &rRdr)
+ : mrRdr(rRdr), mfUnknown(0), mfDropdownIndex(0),
+ mfToolTip(0), mfNoMark(0), mfType(0),
+ mfUnused(0), mhpsCheckBox(20), mnChecked(0), mnMaxLen(0),
+ mbHelp(false), msName(std::move( aN ))
+ {
+ }
+ sal_uInt8 mfUnknown:2;
+ sal_uInt8 mfDropdownIndex:6;
+ sal_uInt8 mfToolTip:1;
+ sal_uInt8 mfNoMark:1;
+ sal_uInt8 mfType:3;
+ sal_uInt8 mfUnused:3;
+
+ sal_uInt16 mhpsCheckBox;
+ sal_uInt16 mnChecked;
+
+ /// FFData.cch in the spec: maximum length, in characters, of the value of the textbox.
+ sal_uInt16 mnMaxLen;
+ OUString msTitle;
+ OUString msDefault;
+ OUString msFormatting;
+ bool mbHelp;
+ OUString msHelp;
+ OUString msToolTip;
+ OUString msEntryMcr;
+ OUString msExitMcr;
+ std::vector<OUString> maListEntries;
+ virtual ~WW8FormulaControl() {}
+ void FormulaRead(SwWw8ControlType nWhich,SvStream *pD);
+ virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> &rServiceFactory,
+ css::uno::Reference< css::form::XFormComponent> &rFComp,
+ css::awt::Size &rSz) = 0;
+ OUString msName;
+};
+
+class WW8FormulaCheckBox : public WW8FormulaControl
+{
+private:
+ WW8FormulaCheckBox(const WW8FormulaCheckBox&) = delete;
+ WW8FormulaCheckBox& operator=(const WW8FormulaCheckBox&) = delete;
+
+public:
+ explicit WW8FormulaCheckBox(SwWW8ImplReader &rR);
+
+ virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> &rServiceFactory,
+ css::uno::Reference< css::form::XFormComponent> &rFComp,
+ css::awt::Size &rSz) override;
+};
+
+class WW8FormulaListBox : public WW8FormulaControl
+{
+private:
+ WW8FormulaListBox(const WW8FormulaListBox&) = delete;
+ WW8FormulaListBox& operator=(const WW8FormulaListBox&) = delete;
+
+public:
+ explicit WW8FormulaListBox(SwWW8ImplReader &rR);
+
+ virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> &rServiceFactory,
+ css::uno::Reference< css::form::XFormComponent> &rFComp,
+ css::awt::Size &rSz) override;
+};
+
+class WW8FormulaEditBox : public WW8FormulaControl
+{
+private:
+ WW8FormulaEditBox(const WW8FormulaEditBox&) = delete;
+ WW8FormulaEditBox& operator=(const WW8FormulaEditBox&) = delete;
+public:
+ explicit WW8FormulaEditBox(SwWW8ImplReader &rR);
+ //no real implementation, return false
+ virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> & /* rServiceFactory */,
+ css::uno::Reference< css::form::XFormComponent> & /* rFComp */,
+ css::awt::Size & /* rSz */) override { return false; }
+};
+
+class SwMSConvertControls: public oox::ole::MSConvertOCXControls
+{
+public:
+ SwMSConvertControls( SfxObjectShell const *pDSh, SwPaM *pP );
+ void InsertFormula( WW8FormulaControl &rFormula);
+ virtual bool InsertControl(const css::uno::Reference< css::form::XFormComponent >& rFComp,
+ const css::awt::Size& rSize,
+ css::uno::Reference< css::drawing::XShape > *pShape, bool bFloatingCtrl) override;
+ void ExportControl(WW8Export &rWrt, const SdrUnoObj& rFormObj);
+ bool ReadOCXStream( tools::SvRef<SotStorage> const & rSrc1,
+ css::uno::Reference< css::drawing::XShape > *pShapeRef,
+ bool bFloatingCtrl=false );
+private:
+ SwPaM *m_pPaM;
+ sal_uInt32 mnObjectId;
+};
+
+class SwMSDffManager : public SvxMSDffManager
+{
+private:
+ SwWW8ImplReader& m_rReader;
+ SvStream *m_pFallbackStream;
+ std::unordered_map<sal_uInt32, Graphic> m_aOldEscherBlipCache;
+
+ virtual bool GetOLEStorageName( sal_uInt32 nOLEId, OUString& rStorageName,
+ tools::SvRef<SotStorage>& rSrcStorage, css::uno::Reference < css::embed::XStorage >& rDestStorage ) const override;
+ virtual bool ShapeHasText( sal_uLong nShapeId, sal_uLong nFilePos ) const override;
+ // #i32596# - new parameter <_nCalledByGroup>, which
+ // indicates, if the OLE object is imported inside a group object
+ virtual rtl::Reference<SdrObject> ImportOLE( sal_uInt32 nOLEId,
+ const Graphic& rGrf,
+ const tools::Rectangle& rBoundRect,
+ const tools::Rectangle& rVisArea,
+ const int _nCalledByGroup ) const override;
+
+ SwMSDffManager(const SwMSDffManager&) = delete;
+ SwMSDffManager& operator=(const SwMSDffManager&) = delete;
+public:
+ static sal_uInt32 GetFilterFlags();
+ static sal_Int32 GetEscherLineMatch(MSO_LineStyle eStyle, MSO_SPT eShapeType,
+ sal_Int32 &rThick);
+ SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages );
+ void DisableFallbackStream();
+ void EnableFallbackStream();
+protected:
+ virtual rtl::Reference<SdrObject> ProcessObj( SvStream& rSt, DffObjData& rObjData, SvxMSDffClientData& rData, tools::Rectangle& rTextRect, SdrObject* pObj ) override;
+};
+
+class wwSection
+{
+public:
+ explicit wwSection(const SwPosition &rPos);
+ SEPr maSep;
+ WW8_BRCVer9 m_brc[4];
+ SwNodeIndex maStart;
+ SwSection *mpSection;
+ SwPageDesc *mpPage;
+ SvxFrameDirection meDir;
+
+ sal_uInt32 m_nPgWidth;
+ sal_uInt32 m_nPgLeft;
+ sal_uInt32 m_nPgRight;
+ sal_uInt32 m_nPgGutter;
+ bool m_bRtlGutter = false;
+
+ css::drawing::TextVerticalAdjust mnVerticalAdjustment;
+ sal_uInt8 mnBorders;
+ bool mbHasFootnote;
+ void SetDirection();
+ bool IsContinuous() const { return maSep.bkc == 0; }
+ bool IsNotProtected() const { return maSep.fUnlocked != 0; }
+ bool IsVertical() const;
+ sal_Int16 NoCols() const { return maSep.ccolM1 + 1; }
+ sal_Int32 StandardColSeparation() const { return maSep.dxaColumns; }
+ bool HasTitlePage() const { return maSep.fTitlePage != 0; }
+ sal_uInt16 PageStartAt() const { return maSep.pgnStart; }
+ bool PageRestartNo() const { return maSep.fPgnRestart != 0; }
+ bool IsBiDi() const { return maSep.fBiDi != 0; }
+ sal_uInt32 GetPageWidth() const { return m_nPgWidth; }
+ sal_uInt32 GetTextAreaWidth() const
+ { return GetPageWidth() - GetPageLeft() - m_nPgGutter - GetPageRight(); }
+ sal_uInt32 GetPageHeight() const { return maSep.yaPage; }
+ sal_uInt32 GetPageLeft() const { return m_nPgLeft; }
+ sal_uInt32 GetPageRight() const { return m_nPgRight; }
+ bool IsLandScape() const { return maSep.dmOrientPage != 0; }
+ bool IsFixedHeightHeader() const { return maSep.dyaTop < 0; }
+ bool IsFixedHeightFooter() const { return maSep.dyaBottom < 0; }
+};
+
+class wwSectionManager
+{
+private:
+ /*
+ A queue of the ms sections in the document
+ */
+ SwWW8ImplReader& mrReader;
+ std::deque<wwSection> maSegments;
+ typedef std::deque<wwSection>::iterator mySegIter;
+
+ //Num of page desc's entered into the document
+ sal_uInt16 mnDesc;
+
+ struct wwULSpaceData
+ {
+ bool bHasHeader, bHasFooter;
+ sal_uInt32 nSwHLo, nSwFUp, nSwUp, nSwLo;
+ wwULSpaceData()
+ : bHasHeader(false)
+ , bHasFooter(false)
+ , nSwHLo(0)
+ , nSwFUp(0)
+ , nSwUp(0)
+ , nSwLo(0)
+ {}
+ };
+
+ void SetSegmentToPageDesc(const wwSection &rSection, bool bIgnoreCols);
+
+ void GetPageULData(const wwSection &rNewSection,
+ wwULSpaceData& rData) const;
+ static void SetPageULSpaceItems(SwFrameFormat &rFormat, wwULSpaceData const & rData,
+ const wwSection &rSection);
+
+ static void SetPage(SwPageDesc &rPageDesc, SwFrameFormat &rFormat,
+ const wwSection &rSection, bool bIgnoreCols);
+
+ static void SetNumberingType(const wwSection &rNewSection, SwPageDesc &rPageDesc);
+
+ void SetUseOn(wwSection &rSection);
+ void SetHdFt(wwSection const &rSection, int nSect, const wwSection *pPrevious);
+
+ SwSectionFormat *InsertSection(SwPaM const & rMyPaM, wwSection &rSection);
+ static bool SetCols(SwFrameFormat &rFormat, const wwSection &rSection,
+ sal_uInt32 nNetWidth);
+ bool SectionIsProtected(const wwSection &rSection) const;
+ static void SetLeftRight(wwSection &rSection);
+ /*
+ The segment we're inserting, the start of the segments container, and the
+ nodeindex of where we want the page break to (normally the segments start
+ position
+ */
+ SwFormatPageDesc SetSwFormatPageDesc(mySegIter const &rIter, mySegIter const &rStart,
+ bool bIgnoreCols);
+
+ wwSectionManager(const wwSectionManager&) = delete;
+ wwSectionManager& operator=(const wwSectionManager&) = delete;
+public:
+ explicit wwSectionManager(SwWW8ImplReader &rReader) : mrReader(rReader), mnDesc(0)
+ {}
+ void SetCurrentSectionHasFootnote();
+ void SetCurrentSectionVerticalAdjustment(const css::drawing::TextVerticalAdjust nVA);
+ bool CurrentSectionIsVertical() const;
+ bool CurrentSectionIsProtected() const;
+ void PrependedInlineNode(const SwPosition &rPos, const SwNode &rNode);
+ sal_uInt16 CurrentSectionColCount() const;
+ bool WillHavePageDescHere(const SwNode&) const;
+ void CreateSep(const tools::Long nTextPos);
+ void InsertSegments();
+ void JoinNode(const SwPosition &rPos, const SwNode &rNode);
+ sal_uInt32 GetPageLeft() const;
+ sal_uInt32 GetPageRight() const;
+ sal_uInt32 GetPageWidth() const;
+ sal_uInt32 GetWWPageTopMargin() const;
+ sal_uInt32 GetTextAreaWidth() const;
+};
+
+class TextNodeListener : public SwClient
+{
+ SwTextNode *m_pTextNode;
+
+protected:
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+public:
+ TextNodeListener(SwTextNode* pTextNode);
+ bool operator<(const TextNodeListener& rOther) const
+ {
+ return m_pTextNode->GetIndex() < rOther.m_pTextNode->GetIndex();
+ }
+ void StopListening(SwModify* pTextNode);
+ SwTextNode* GetTextNode() const { return m_pTextNode; }
+ virtual void removed(SwModify* pTextNode);
+ virtual ~TextNodeListener() override;
+};
+
+//Various writer elements like frames start off containing a blank paragraph,
+//sometimes this paragraph turns out to be extraneous, e.g. the frame should
+//only contain a table with no trailing paragraph.
+
+//We want to remove these extra paragraphs, but removing them during the parse
+//is problematic, because we don't want to remove any paragraphs that are still
+//addressed by property entries in a SwFltControlStack which have not yet been
+//committed to the document.
+
+//Safest thing is to not delete SwTextNodes from a document during import, and
+//remove these extraneous paragraphs at the end after all SwFltControlStack are
+//destroyed.
+class wwExtraneousParas
+{
+private:
+ class ExtraTextNodeListener : public TextNodeListener
+ {
+ private:
+ wwExtraneousParas* m_pOwner;
+ public:
+ ExtraTextNodeListener(SwTextNode* pTextNode, wwExtraneousParas* pOwner)
+ : TextNodeListener(pTextNode)
+ , m_pOwner(pOwner)
+ {
+ }
+ virtual void removed(SwModify* pTextNode) override;
+ };
+
+ /*
+ A vector of SwTextNodes to erase from a document after import is complete
+ */
+ std::set<ExtraTextNodeListener> m_aTextNodes;
+ SwDoc& m_rDoc;
+
+ void remove_if_present(SwModify* pModify);
+
+ wwExtraneousParas(wwExtraneousParas const&) = delete;
+ wwExtraneousParas& operator=(wwExtraneousParas const&) = delete;
+public:
+ explicit wwExtraneousParas(SwDoc &rDoc) : m_rDoc(rDoc) {}
+ ~wwExtraneousParas() { delete_all_from_doc(); }
+ void insert(SwTextNode *pTextNode);
+ void remove_if_present(SwTextNode *pTextNode)
+ {
+ remove_if_present(static_cast<SwModify*>(pTextNode));
+ }
+ void delete_all_from_doc();
+};
+
+class wwFrameNamer
+{
+private:
+ OUString msSeed;
+ sal_Int32 mnImportedGraphicsCount;
+ bool mbIsDisabled;
+
+ wwFrameNamer(wwFrameNamer const&) = delete;
+ wwFrameNamer& operator=(wwFrameNamer const&) = delete;
+
+public:
+ void SetUniqueGraphName(SwFrameFormat *pFrameFormat, std::u16string_view rFixedPart);
+ wwFrameNamer(bool bIsDisabled, OUString aSeed)
+ : msSeed(std::move(aSeed)), mnImportedGraphicsCount(0), mbIsDisabled(bIsDisabled)
+ {
+ }
+};
+
+class wwSectionNamer
+{
+private:
+ const SwDoc &mrDoc;
+ OUString msFileLinkSeed;
+ int mnFileSectionNo;
+ wwSectionNamer(const wwSectionNamer&) = delete;
+ wwSectionNamer& operator=(const wwSectionNamer&) = delete;
+public:
+ OUString UniqueName();
+ wwSectionNamer(const SwDoc &rDoc, OUString aSeed)
+ : mrDoc(rDoc), msFileLinkSeed(std::move(aSeed)), mnFileSectionNo(0)
+ { }
+};
+
+class FootnoteDescriptor
+{
+public:
+ ManTypes meType;
+ bool mbAutoNum;
+ WW8_CP mnStartCp;
+ WW8_CP mnLen;
+};
+
+struct ApoTestResults
+{
+ bool mbStartApo;
+ bool mbStopApo;
+ bool m_bHasSprmPWr;
+ bool m_bHasSprmPPc;
+ /// sprmPPc if m_bHasSprmPPc is true, sprmTPc otherwise.
+ sal_uInt8 m_nSprmPPc;
+ WW8FlyPara* mpStyleApo;
+ ApoTestResults()
+ : mbStartApo(false), mbStopApo(false), m_bHasSprmPWr(false)
+ , m_bHasSprmPPc(false), m_nSprmPPc(0), mpStyleApo(nullptr) {}
+ bool HasStartStop() const { return (mbStartApo || mbStopApo); }
+ bool HasFrame() const { return (m_bHasSprmPPc || m_bHasSprmPWr || mpStyleApo); }
+};
+
+struct ANLDRuleMap
+{
+ OUString msOutlineNumRule; // WinWord 6 numbering, variant 1
+ OUString msNumberingNumRule; // WinWord 6 numbering, variant 2
+ SwNumRule* GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType);
+ void SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType);
+};
+
+struct SprmReadInfo;
+class SwDocShell;
+struct WW8PostProcessAttrsInfo
+{
+ bool mbCopy;
+ WW8_CP mnCpStart;
+ WW8_CP mnCpEnd;
+ SwPaM mPaM;
+ SfxItemSet mItemSet;
+
+ WW8PostProcessAttrsInfo(WW8_CP nCpStart, WW8_CP nCpEnd, SwPaM & rPaM);
+};
+
+#define MAX_COL 64 // WW6-description: 32, WW6-UI: 31 & WW8-UI: 63!
+
+struct WW8TabBandDesc
+{
+ WW8TabBandDesc* pNextBand;
+ short nGapHalf;
+ short mnDefaultLeft;
+ short mnDefaultTop;
+ short mnDefaultRight;
+ short mnDefaultBottom;
+ bool mbHasSpacing;
+ short nLineHeight;
+ short nRows;
+ sal_uInt16 maDirections[MAX_COL + 1];
+ short nCenter[MAX_COL + 1]; // X-edge of all cells of this band
+ short nWidth[MAX_COL + 1]; // length of all cells of this band
+ short nWwCols; // sal_uInt8 would be sufficient, alignment -> short
+ short nSwCols; // SW: number of columns for the writer
+ bool bLEmptyCol; // SW: an additional empty column at the left
+ bool bREmptyCol; // SW: same at the right
+ bool bCantSplit;
+ WW8_TCell* pTCs;
+ sal_uInt8 nOverrideSpacing[MAX_COL + 1];
+ short nOverrideValues[MAX_COL + 1][4];
+ WW8_SHD* pSHDs;
+ Color* pNewSHDs;
+ WW8_BRCVer9 aDefBrcs[6];
+
+ bool bExist[MAX_COL]; // does this cell exist ?
+ sal_uInt8 nTransCell[MAX_COL + 2]; // translation WW-Index -> SW-Index
+
+ sal_uInt8 transCell(sal_uInt8 nWwCol) const
+ {
+ return nWwCol < SAL_N_ELEMENTS(nTransCell) ? nTransCell[nWwCol] : 0xFF;
+ }
+
+ WW8TabBandDesc();
+ WW8TabBandDesc(WW8TabBandDesc const & rBand); // deep copy
+ ~WW8TabBandDesc();
+ void ReadDef(bool bVer67, const sal_uInt8* pS, short nLen);
+ void ProcessDirection(const sal_uInt8* pParams);
+ void ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen);
+ void ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen);
+ void ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol);
+ void ProcessSprmTDelete(const sal_uInt8* pParamsTDelete);
+ void ProcessSprmTInsert(const sal_uInt8* pParamsTInsert);
+ void ProcessSpacing(const sal_uInt8* pParamsTInsert);
+ void ProcessSpecificSpacing(const sal_uInt8* pParamsTInsert);
+ void ReadShd(const sal_uInt8* pS );
+ void ReadNewShd(const sal_uInt8* pS, bool bVer67, sal_uInt8 nStart);
+
+ enum wwDIR {wwTOP = 0, wwLEFT = 1, wwBOTTOM = 2, wwRIGHT = 3};
+
+private:
+ WW8TabBandDesc & operator =(WW8TabBandDesc const &) = default; // only for use in copy ctor
+};
+
+// Storage-Reader
+
+typedef std::set<WW8_CP> cp_set;
+
+class SwWW8ImplReader
+{
+private:
+ SwDocShell *m_pDocShell; // The Real DocShell
+
+friend class WW8RStyle;
+friend class WW8TabDesc;
+friend class WW8ReaderSave;
+friend struct WW8FlyPara;
+friend struct WW8SwFlyPara;
+friend class WW8FlySet;
+friend class SwMSDffManager;
+friend class SwWW8FltControlStack;
+friend class WW8FormulaControl;
+friend class wwSectionManager;
+
+private:
+
+ SotStorage* m_pStg; // Input-Storage
+ SvStream* m_pStrm; // Input-(Storage)Stream
+ SvStream* m_pTableStream; // Input-(Storage)Stream
+ SvStream* m_pDataStream; // Input-(Storage)Stream
+
+// general stuff
+ SwDoc& m_rDoc;
+ std::shared_ptr<SwUnoCursor> mpCursor;
+ SwPaM* m_pPaM;
+
+ std::unique_ptr<SwWW8FltControlStack> m_xCtrlStck; // stack for the attributes
+
+ /*
+ This stack is for redlines, because their sequence of discovery can
+ be out of order of their order of insertion into the document.
+ */
+ std::unique_ptr<sw::util::RedlineStack> m_xRedlineStack;
+
+ /*
+ This stack is for fields that get referenced later, e.g. BookMarks and TOX.
+ They get inserted at the end of the document, it is the same stack for
+ headers/footers/main text/textboxes/tables etc...
+ */
+ std::unique_ptr<SwWW8ReferencedFltEndStack> m_xReffedStck;
+
+ /*
+ This stack is for fields whose true conversion cannot be determined until
+ the end of the document, it is the same stack for headers/footers/main
+ text/textboxes/tables etc... They are things that reference other things
+ e.g. NoteRef and Ref, they are processed after m_xReffedStck
+ */
+ std::unique_ptr<SwWW8FltRefStack> m_xReffingStck;
+
+ /*
+ For graphics anchors. Determines the graphics whose anchors are in the
+ current paragraph, and works around the difficulty in inserting a graphic
+ anchored to character before a character to be anchored to has been
+ inserted. Is emptied at the end of each paragraph.
+ */
+ std::unique_ptr<SwWW8FltAnchorStack> m_xAnchorStck;
+
+ /*
+ A stack of fields identifiers to keep track of any open fields that need
+ to be closed. Generally word fields are inserted as writer fields as soon
+ as they are encountered, and so their end point is normally unimportant.
+ But hyperlink fields need to be applied as attributes to text and it is
+ far easier and safer to set the end point of an attribute when we
+ encounter the end marker of the field instead of calculating in advance
+ where the end point will fall, to do so fully correctly duplicates the
+ main logic of the filter itself.
+ */
+ std::deque<WW8FieldEntry> m_aFieldStack;
+
+ /*
+ A stack of open footnotes. Should only be one in it at any time.
+ */
+ std::deque<FootnoteDescriptor> m_aFootnoteStack;
+
+ std::optional<SwLineBreakClear> m_oLineBreakClear;
+
+ /*
+ A queue of the ms sections in the document
+ */
+ wwSectionManager m_aSectionManager;
+
+ /*
+ A vector of surplus-to-requirements paragraph in the final document,
+ that exist because of quirks of the SwDoc document model and/or API,
+ which need to be removed.
+ */
+ wwExtraneousParas m_aExtraneousParas;
+
+ /*
+ A map of tables to their follow nodes for use in inserting tables into
+ already existing document, i.e. insert file
+ */
+ sw::util::InsertedTablesManager m_aInsertedTables;
+
+ /*
+ Creates unique names to give to (file link) sections (WW1/WW2/...)
+ */
+ wwSectionNamer m_aSectionNameGenerator;
+
+ /*
+ Knows how to split a series of bytes into sprms and their arguments
+ */
+ std::optional<wwSprmParser> m_oSprmParser;
+
+ /*
+ Creates unique names to give to graphics
+ */
+ wwFrameNamer m_aGrfNameGenerator;
+
+ /*
+ Knows which writer style a given word style should be imported as.
+ */
+ sw::util::ParaStyleMapper m_aParaStyleMapper;
+ sw::util::CharStyleMapper m_aCharStyleMapper;
+
+ /*
+ Stack of textencoding being used as we progress through the document text
+ */
+ std::stack<rtl_TextEncoding> m_aFontSrcCharSets;
+ std::stack<rtl_TextEncoding> m_aFontSrcCJKCharSets;
+
+ /*
+ Progress bar
+ */
+ std::unique_ptr<ImportProgress> m_xProgress;
+
+ std::unique_ptr<SwMSConvertControls> m_xFormImpl; // implementation of control
+
+ SwFlyFrameFormat* m_pFlyFormatOfJustInsertedGraphic;
+ std::unique_ptr<FrameDeleteWatch> m_xFormatOfJustInsertedApo;
+ SwPaM* m_pPreviousNumPaM;
+ const SwNumRule* m_pPrevNumRule;
+
+ //Keep track of APO environments
+ std::deque<bool> m_aApos;
+ /*
+ Keep track of generated Ruby character formats we can minimize the
+ number of character formats created
+ */
+ std::vector<const SwCharFormat*> m_aRubyCharFormats;
+
+ /*
+ For fuzzing keep track of source offset of inserted graphics
+ */
+ std::set<sal_uLong> m_aGrafPosSet;
+
+ std::unique_ptr<WW8PostProcessAttrsInfo> m_pPostProcessAttrsInfo;
+
+ std::shared_ptr<WW8Fib> m_xWwFib;
+ std::unique_ptr<WW8Fonts> m_xFonts;
+ std::unique_ptr<WW8Dop> m_xWDop;
+ std::unique_ptr<WW8ListManager> m_xLstManager;
+ std::unique_ptr<WW8ScannerBase> m_xSBase;
+ std::shared_ptr<WW8PLCFMan> m_xPlcxMan;
+ std::map<short, OUString> m_aLinkStringMap;
+
+ o3tl::sorted_vector<const SwNode*> m_aTextNodesHavingFirstLineOfstSet; // #i103711#
+ o3tl::sorted_vector<const SwNode*> m_aTextNodesHavingLeftIndentSet; // #i105414#
+
+ std::unique_ptr<WW8RStyle> m_xStyles; // pointer to the style reading class
+ SwFormat* m_pCurrentColl; // collection to be created now
+ // ( always 0 outside of a Style-Def )
+ std::unique_ptr<SfxItemSet> m_xCurrentItemSet;// character attributes to be read in now
+ // (always 0 outside of the WW8ListManager Ctor)
+ std::vector<SwWW8StyInf> m_vColl;
+ const SwTextFormatColl* m_pDfltTextFormatColl; // Default
+ SwFormat* m_pStandardFormatColl;// "Standard"
+
+ std::unique_ptr<WW8PLCF_HdFt> m_xHdFt; // pointer to Header / Footer - scanner class
+
+ std::unique_ptr<WW8FlyPara> m_xWFlyPara; // WW-parameter
+ std::unique_ptr<WW8SwFlyPara> m_xSFlyPara; // Sw parameters created from previous
+
+ std::unique_ptr<WW8TabDesc> m_xTableDesc; // description of table properties
+ //Keep track of tables within tables
+ std::stack<std::unique_ptr<WW8TabDesc>> m_aTableStack;
+
+ ANLDRuleMap m_aANLDRules;
+ std::unique_ptr<WW8_OLST> m_xNumOlst; // position in text
+
+ SdrModel* m_pDrawModel;
+ SdrPage* m_pDrawPg;
+ std::unique_ptr<EditEngine> m_pDrawEditEngine;
+ std::unique_ptr<wwZOrderer> m_xWWZOrder;
+
+ SwSetExpFieldType* m_pNumFieldType; // for number circle
+
+ std::unique_ptr<SwMSDffManager> m_xMSDffManager;
+
+ std::optional<std::vector<OUString>> m_xAtnNames;
+
+ std::unique_ptr<WW8SmartTagData> m_pSmartTagData;
+
+ /// Redlining Authors, map word author key to writer author value
+ std::map<sal_uInt16, std::size_t> m_aAuthorInfos;
+ OUString m_sBaseURL;
+
+ // Ini-Flags:
+ sal_uInt32 m_nIniFlags; // flags from writer.ini
+ sal_uInt32 m_nIniFlags1; // ditto ( additional flags )
+ sal_uInt32 m_nFieldFlags; // ditto for fields
+ sal_uInt32 m_nFieldTagAlways[3]; // ditto for tagging of fields
+ sal_uInt32 m_nFieldTagBad[3]; // ditto for tagging of fields that can't be imported
+ bool m_bRegardHindiDigits; // import digits in CTL scripts as Hindi numbers
+
+ bool m_bDrawCpOValid;
+ WW8_CP m_nDrawCpO; // start of Txbx-SubDocs
+
+ sal_uLong m_nPicLocFc; // Picture Location in File (FC)
+ sal_uLong m_nObjLocFc; // Object Location in File (FC)
+
+ sal_Int32 m_nIniFlyDx; // X-offset of Flys
+ sal_Int32 m_nIniFlyDy; // Y-offset of Flys
+
+ rtl_TextEncoding m_eTextCharSet; // Default charset for Text
+ rtl_TextEncoding m_eStructCharSet; // rtl_TextEncoding for structures
+ rtl_TextEncoding m_eHardCharSet; // Hard rtl_TextEncoding-Attribute
+ sal_uInt16 m_nProgress; // percentage for Progressbar
+ sal_uInt16 m_nCurrentColl; // per WW-count
+ sal_uInt16 m_nFieldNum; // serial number for that
+ sal_uInt16 m_nLFOPosition;
+
+ short m_nCharFormat; // per WW-count, <0 for none
+
+ short m_nDrawXOfs, m_nDrawYOfs;
+ short m_nDrawXOfs2, m_nDrawYOfs2;
+
+ sal_Unicode m_cSymbol; // symbol to be read now
+
+ sal_uInt8 m_nWantedVersion; // originally requested WW-Doc version by Writer
+
+ sal_uInt8 m_nSwNumLevel; // level number for outline / enumeration
+ sal_uInt8 m_nWwNumType; // outline / number / enumeration
+ const SwNumRule* m_pChosenWW8OutlineStyle; // copied to the "Outline" Chapter Numbering style
+ sal_uInt8 m_nListLevel;
+
+ bool m_bNewDoc; // new document?
+ bool m_bSkipImages; // skip images for text extraction/indexing
+ bool m_bReadNoTable; // no tables
+ bool m_bPgSecBreak; // Page- or Sectionbreak is still to be added
+ bool m_bSpec; // special char follows in text
+ bool m_bObj; // Obj in Text
+ bool m_bTxbxFlySection; // FlyFrame that was inserted as replacement for Winword Textbox
+ bool m_bHasBorder; // for bundling of the borders
+ bool m_bSymbol; // e.g. Symbol instead of Times
+ bool m_bIgnoreText; // e.g. for FieldVanish
+ int m_nInTable; // are we currently reading a table?
+ bool m_bWasTabRowEnd; // table : Row End Mark
+ bool m_bWasTabCellEnd; // table: Cell End Mark
+
+ bool m_bAnl; // enumeration in work
+ // Anl stands for "Autonumber level"
+
+ bool m_bHdFtFootnoteEdn; // special text: header/footer/and so on
+ bool m_bFootnoteEdn; // footnote or endnote
+ bool m_bIsHeader; // text is read from header ( line height )
+ bool m_bIsFooter; // text is read from footer ( line height )
+
+ bool m_bIsUnicode; // current piece of text is encoded as 2-byte Unicode
+ // please do NOT handle this as bit field!
+
+ bool m_bCpxStyle; // style in the complex part
+ bool m_bStyNormal; // style with Id 0 is currently read
+ bool m_bWWBugNormal; // WW-Version with Bug Dya in Style Normal
+ bool m_bNoAttrImport; // ignore attributes for ignoring styles
+ bool m_bInHyperlink; // special case for reading 0x01
+ // see also: SwWW8ImplReader::Read_F_Hyperlink()
+ bool m_bWasParaEnd;
+
+ // useful helper variables
+ bool m_bVer67; // ( (6 == nVersion) || (7 == nVersion) );
+ bool m_bVer6; // (6 == nVersion);
+ bool m_bVer7; // (7 == nVersion);
+ bool m_bVer8; // (8 == nVersion);
+
+ bool m_bEmbeddObj; // EmbeddField is being read
+
+ bool m_bCurrentAND_fNumberAcross; // current active Annotated List Descriptor - ROW flag
+
+ bool m_bNoLnNumYet; // no Line Numbering has been activated yet (we import
+ // the very 1st Line Numbering and ignore the rest)
+
+ bool m_bFirstPara; // first paragraph?
+ bool m_bFirstParaOfPage;
+ bool m_bParaAutoBefore;
+ bool m_bParaAutoAfter;
+
+ bool m_bDropCap;
+ sal_Int32 m_nDropCap;
+
+ bool m_bBidi;
+ bool m_bReadTable;
+ // Indicate that currently on loading a TOC, managed by Read_F_TOX() and End_Field()
+ bool m_bLoadingTOXCache;
+ int m_nEmbeddedTOXLevel;
+ // Indicate that current on loading a hyperlink, which is inside a TOC; Managed by Read_F_Hyperlink() and End_Field()
+ bool m_bLoadingTOXHyperlink;
+ // a document position recorded the after-position of TOC section, managed by Read_F_TOX() and End_Field()
+ std::optional<SwPaM> m_oPosAfterTOC;
+ // used for some dropcap tweaking
+ std::unique_ptr<TextNodeListener> m_xPreviousNode;
+
+ std::optional< SwPosition > m_oLastAnchorPos;
+
+ bool m_bCareFirstParaEndInToc;
+ bool m_bCareLastParaEndInToc;
+ cp_set m_aTOXEndCps;
+
+ bool m_bNotifyMacroEventRead;
+ bool m_bFuzzing;
+
+ const SprmReadInfo& GetSprmReadInfo(sal_uInt16 nId) const;
+
+ bool StyleExists(unsigned int nColl) const { return (nColl < m_vColl.size()); }
+ SwWW8StyInf *GetStyle(sal_uInt16 nColl) const;
+ void AppendTextNode(SwPosition& rPos);
+
+ void Read_HdFt(int nSect, const SwPageDesc *pPrev,
+ const wwSection &rSection);
+ void Read_HdFtText(WW8_CP nStartCp, WW8_CP nLen, SwFrameFormat const * pHdFtFormat);
+ void Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen,
+ SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth);
+
+ bool isValid_HdFt_CP(WW8_CP nHeaderCP) const;
+
+ bool HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt, int nSect);
+
+ void HandleLineNumbering(const wwSection &rSection);
+
+ void CopyPageDescHdFt( const SwPageDesc* pOrgPageDesc,
+ SwPageDesc* pNewPageDesc, sal_uInt8 nCode );
+
+ void DeleteStack(std::unique_ptr<SwFltControlStack> prStck);
+ void DeleteCtrlStack()
+ {
+ DeleteStack(std::move(m_xCtrlStck));
+ }
+ void DeleteRefStacks()
+ {
+ DeleteStack(std::move(m_xReffedStck));
+ DeleteStack(std::move(m_xReffingStck));
+ }
+ void DeleteAnchorStack()
+ {
+ DeleteStack(std::move(m_xAnchorStck));
+ }
+ void emulateMSWordAddTextToParagraph(const OUString& rAddString);
+ void simpleAddTextToParagraph(std::u16string_view aAddString);
+ bool HandlePageBreakChar();
+ bool ReadChar(tools::Long nPosCp, tools::Long nCpOfs);
+ bool ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs);
+ bool ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, tools::Long nTextEnd, tools::Long nCpOfs);
+ static bool LangUsesHindiNumbers(LanguageType nLang);
+ static sal_Unicode TranslateToHindiNumbers(sal_Unicode);
+
+ void SetDocumentGrid(SwFrameFormat &rFormat, const wwSection &rSection);
+
+ void ProcessCurrentCollChange(WW8PLCFManResult& rRes, bool* pStartAttr,
+ bool bCallProcessSpecial);
+ tools::Long ReadTextAttr(WW8_CP& rTextPos, tools::Long nTextEnd, bool& rbStartLine, int nDepthGuard = 0);
+ void ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, tools::Long nTextEnd, bool& rbStartLine);
+ void CloseAttrEnds();
+ bool ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType);
+
+ void ReadRevMarkAuthorStrTabl( SvStream& rStrm, sal_Int32 nTablePos,
+ sal_Int32 nTableSiz, SwDoc& rDoc );
+
+ void Read_HdFtFootnoteText(const SwNodeIndex* pSttIdx, WW8_CP nStartCp,
+ WW8_CP nLen, ManTypes nType);
+
+ void ImportTox( int nFieldId, const OUString& aStr );
+
+ void EndSprm( sal_uInt16 nId );
+ // #i103711#
+ // #i105414#
+ void NewAttr( const SfxPoolItem& rAttr,
+ const bool bFirstLineOfStSet = false,
+ const bool bLeftIndentSet = false );
+
+ bool GetFontParams(sal_uInt16, FontFamily&, OUString&, FontPitch&,
+ rtl_TextEncoding&);
+ bool SetNewFontAttr(sal_uInt16 nFCode, bool bSetEnums, sal_uInt16 nWhich);
+ void ResetCharSetVars();
+ void ResetCJKCharSetVars();
+
+ const SfxPoolItem* GetFormatAttr( sal_uInt16 nWhich );
+ template<class T> const T* GetFormatAttr( TypedWhichId<T> nWhich )
+ {
+ return static_cast<const T*>(GetFormatAttr(sal_uInt16(nWhich)));
+ }
+
+ bool JoinNode(SwPaM &rPam, bool bStealAttr = false);
+
+ static bool IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn = false);
+
+ //Set closest writer border equivalent into rBox from pbrc, optionally
+ //recording true winword dimensions in pSizeArray. nSetBorders to mark a
+ //border which has been previously set to a value and for which becoming
+ //empty is valid. Set bCheBtwn to work with paragraphs that have a special
+ //between paragraphs border
+
+ // Note #i20672# we can't properly support between lines so best to ignore
+ // them for now
+ static bool SetBorder(SvxBoxItem& rBox, const WW8_BRCVer9* pbrc,
+ short *pSizeArray=nullptr, sal_uInt8 nSetBorders=0xFF);
+ static void GetBorderDistance(const WW8_BRCVer9* pbrc, tools::Rectangle& rInnerDist);
+ static sal_uInt16 GetParagraphAutoSpace(bool fDontUseHTMLAutoSpacing);
+ static bool SetShadow(SvxShadowItem& rShadow, const short *pSizeArray,
+ const WW8_BRCVer9& aRightBrc);
+ //returns true is a shadow was set
+ static bool SetFlyBordersShadow(SfxItemSet& rFlySet, const WW8_BRCVer9 *pbrc,
+ short *SizeArray);
+ static void SetPageBorder(SwFrameFormat &rFormat, const wwSection &rSection);
+
+ static sal_Int32 MatchSdrBoxIntoFlyBoxItem( const Color& rLineColor,
+ MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, sal_Int32 &rLineWidth,
+ SvxBoxItem& rBox );
+ void MatchSdrItemsIntoFlySet( SdrObject const * pSdrObj, SfxItemSet &aFlySet,
+ MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, tools::Rectangle &rInnerDist );
+ static void AdjustLRWrapForWordMargins(const SvxMSDffImportRec &rRecord,
+ SvxLRSpaceItem &rLR);
+ static void AdjustULWrapForWordMargins(const SvxMSDffImportRec &rRecord,
+ SvxULSpaceItem &rUL);
+ static void MapWrapIntoFlyFormat(const SvxMSDffImportRec& rRecord, SwFrameFormat& rFlyFormat);
+
+ void SetAttributesAtGrfNode(const SvxMSDffImportRec& rRecord, const SwFrameFormat& rFlyFormat,
+ WW8_FSPA const* pF);
+
+ bool IsDropCap() const;
+ bool IsListOrDropcap() const { return (!m_xCurrentItemSet || m_bDropCap); };
+
+ //Apo == Absolutely Positioned Object, MSWord's old-style frames
+ std::unique_ptr<WW8FlyPara> ConstructApo(const ApoTestResults &rApo,
+ const WW8_TablePos *pTabPos);
+ bool StartApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos);
+ void StopApo();
+ bool TestSameApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos);
+ ApoTestResults TestApo(int nCellLevel, bool bTableRowEnd,
+ const WW8_TablePos *pTabPos);
+ static void StripNegativeAfterIndent(SwFrameFormat const *pFlyFormat);
+
+ void EndSpecial();
+ bool ProcessSpecial(bool &rbReSync, WW8_CP nStartCp);
+ sal_uInt16 TabRowSprm(int nLevel) const;
+
+ bool ReadGrafFile(OUString& rFileName, std::optional<Graphic>& rpGraphic,
+ const WW8_PIC& rPic, SvStream* pSt, sal_uLong nFilePos, bool* pDelIt);
+
+ static void ReplaceObj(const SdrObject &rReplaceTextObj,
+ SdrObject &rSubObj);
+
+ SwFlyFrameFormat* MakeGrafNotInContent(const WW8PicDesc& rPD,
+ const Graphic* pGraph, const OUString& rFileName,
+ const SfxItemSet& rGrfSet);
+
+ SwFrameFormat* MakeGrafInContent(const WW8_PIC& rPic, const WW8PicDesc& rPD,
+ const Graphic* pGraph, const OUString& rFileName,
+ const SfxItemSet& rGrfSet);
+
+ SwFrameFormat *AddAutoAnchor(SwFrameFormat *pFormat);
+ SwFrameFormat* ImportGraf1(WW8_PIC const & rPic, SvStream* pSt, sal_uLong nFilePos);
+ SwFrameFormat* ImportGraf(SdrTextObj const * pTextObj = nullptr, SwFrameFormat const * pFlyFormat = nullptr);
+
+ rtl::Reference<SdrObject> ImportOleBase( Graphic& rGraph, const Graphic* pGrf=nullptr,
+ const SfxItemSet* pFlySet=nullptr, const tools::Rectangle& aVisArea = tools::Rectangle() );
+
+ SwFrameFormat* ImportOle( const Graphic* = nullptr, const SfxItemSet* pFlySet = nullptr,
+ const SfxItemSet* pGrfSet = nullptr, const tools::Rectangle& aVisArea = tools::Rectangle() );
+ SwFlyFrameFormat* InsertOle(SdrOle2Obj &rObject, const SfxItemSet &rFlySet,
+ const SfxItemSet *rGrfSet);
+
+ bool ImportFormulaControl(WW8FormulaControl &rBox,WW8_CP nStart,
+ SwWw8ControlType nWhich);
+
+ void ImportDop();
+
+ //This converts MS Asian Typography information into OOo's
+ void ImportDopTypography(const WW8DopTypography &rTypo);
+
+ ErrCode LoadThroughDecryption(WW8Glossary *pGloss);
+ ErrCode SetSubStreams(tools::SvRef<SotStorageStream> &rTableStream, tools::SvRef<SotStorageStream> &rDataStream);
+ ErrCode CoreLoad(WW8Glossary const *pGloss);
+
+ void ReadDocVars();
+
+ bool StartTable(WW8_CP nStartCp);
+ bool InEqualApo(int nLvl) const;
+ bool InLocalApo() const { return InEqualApo(m_nInTable); }
+ bool InEqualOrHigherApo(int nLvl) const;
+ void TabCellEnd();
+ void StopTable();
+ bool IsInvalidOrToBeMergedTabCell() const;
+
+// Enumerations / lists ( Autonumbered List Data Descriptor )
+// list: ANLD ( Autonumbered List Data Descriptor )
+// one level: ANLV ( Autonumber Level Descriptor )
+
+// Chg7-8:
+// lists are separate structures in WW8 that are handled via the following three tables:
+// rglst, hpllfo and hsttbListNames
+// the corresponding structures are: LSTF, LVLF, LFO LFOLVL
+
+ void SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV const &rAV, const sal_uInt8* pText,
+ size_t nStart, size_t nElements, bool bOutline);
+ void SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel, bool bOutLine);
+ void SetNumOlst( SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel );
+ SwNumRule* GetStyRule();
+
+ void StartAnl(const sal_uInt8* pSprm13);
+ void NextAnlLine(const sal_uInt8* pSprm13);
+ void StopAllAnl(bool bGoBack = true);
+ void StopAnlToRestart(sal_uInt8 nType, bool bGoBack = true);
+
+// graphics layer
+
+ bool ReadGrafStart(void* pData, short nDataSiz, WW8_DPHEAD const * pHd,
+ SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadRect(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadEllipse(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadArc(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadPolyLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ void InsertTxbxStyAttrs( SfxItemSet& rS, sal_uInt16 nColl );
+ void InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType, bool bONLYnPicLocFc=false);
+
+ bool GetTxbxTextSttEndCp(WW8_CP& rStartCp, WW8_CP& rEndCp, sal_uInt16 nTxBxS,
+ sal_uInt16 nSequence);
+ sal_Int32 GetRangeAsDrawingString(OUString& rString, tools::Long StartCp, tools::Long nEndCp, ManTypes eType);
+ std::optional<OutlinerParaObject> ImportAsOutliner(OUString &rString, WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType);
+ void InsertTxbxText(SdrTextObj* pTextObj, Size const * pObjSiz,
+ sal_uInt16 nTxBxS, sal_uInt16 nSequence, tools::Long nPosCp, SwFrameFormat const * pFlyFormat,
+ bool bMakeSdrGrafObj, bool& rbEraseTextObj,
+ bool* pbTestTxbxContainsText = nullptr, tools::Long* pnStartCp = nullptr,
+ tools::Long* pnEndCp = nullptr, bool* pbContainsGraphics = nullptr,
+ SvxMSDffImportRec const * pRecord = nullptr);
+ bool TxbxChainContainsRealText( sal_uInt16 nTxBxS,
+ tools::Long& rStartCp,
+ tools::Long& rEndCp );
+ rtl::Reference<SdrObject> ReadTextBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadCaptionBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadGroup(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet);
+ rtl::Reference<SdrObject> ReadGrafPrimitive(short& rLeft, SfxAllItemSet &rSet);
+ void ReadGrafLayer1(WW8PLCFspecial& rPF, tools::Long nGrafAnchorCp);
+ SdrObject* CreateContactObject(SwFrameFormat* pFlyFormat);
+ RndStdIds ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FSPA& rFSPA, SfxItemSet& rFlySet);
+ bool MiserableRTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth,
+ sal_Int16 eHoriOri, sal_Int16 eHoriRel);
+ SwFrameFormat* Read_GrafLayer( tools::Long nGrafAnchorCp );
+ SwFlyFrameFormat* ImportReplaceableDrawables(rtl::Reference<SdrObject>& rpObject, rtl::Reference<SdrObject>& rpOurNewObject,
+ SvxMSDffImportRec& rRecord, WW8_FSPA& rF,
+ SfxItemSet& rFlySet);
+ SwFlyFrameFormat* ConvertDrawTextToFly(rtl::Reference<SdrObject>& rpObject, rtl::Reference<SdrObject>& rpOurNewObject,
+ const SvxMSDffImportRec& rRecord, RndStdIds eAnchor,
+ const WW8_FSPA& rF, SfxItemSet& rFlySet);
+ SwFrameFormat* MungeTextIntoDrawBox(SvxMSDffImportRec& rRecord, tools::Long nGrafAnchorCp,
+ SwFrameFormat* pRetFrameFormat);
+
+ void GraphicCtor();
+ void GraphicDtor();
+
+// other stuff
+ OUString GetFieldResult( WW8FieldDesc const * pF );
+ void MakeTagString( OUString& rStr, const OUString& rOrg );
+ void UpdateFields();
+ OUString ConvertFFileName(const OUString& rRaw);
+ WW8_CP Read_F_Tag(WW8FieldDesc* pF);
+ void InsertTagField( const sal_uInt16 nId, const OUString& rTagText );
+ tools::Long ImportExtSprm(WW8PLCFManResult* pRes);
+ void EndExtSprm(sal_uInt16 nSprmId);
+ void ReadDocInfo();
+
+// Ver8 lists
+
+ void RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel,
+ const bool bSetAttr = true);
+
+ void RegisterNumFormatOnStyle(sal_uInt16 nStyle);
+ void SetStylesList(sal_uInt16 nStyle, sal_uInt16 nCurrentLFO,
+ sal_uInt8 nCurrentLevel);
+ void RegisterNumFormat(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel);
+
+// to be replaced by calls in the appropriate extended SvxMSDffManager
+
+ const OUString* GetAnnotationAuthor(sal_uInt16 nIdx);
+
+ void GetSmartTagInfo(SwFltRDFMark& rMark);
+
+ // interfaces for the toggle attributes
+ void SetToggleAttr(sal_uInt8 nAttrId, bool bOn);
+ void SetToggleBiDiAttr(sal_uInt8 nAttrId, bool bOn);
+ void ChkToggleAttr_( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask );
+
+ void ChkToggleAttr( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask )
+ {
+ if( nOldStyle81Mask != nNewStyle81Mask &&
+ m_xCtrlStck->GetToggleAttrFlags() )
+ ChkToggleAttr_( nOldStyle81Mask, nNewStyle81Mask );
+ }
+
+ void ChkToggleBiDiAttr_( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask );
+
+ void ChkToggleBiDiAttr( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask )
+ {
+ if( nOldStyle81Mask != nNewStyle81Mask &&
+ m_xCtrlStck->GetToggleBiDiAttrFlags() )
+ ChkToggleBiDiAttr_( nOldStyle81Mask, nNewStyle81Mask );
+ }
+
+ void PopTableDesc();
+ void MoveInsideFly(const SwFrameFormat *pFlyFormat);
+ SwTwips MoveOutsideFly(SwFrameFormat *pFlyFormat, const SwPosition &rPos,
+ bool bTableJoin = true);
+
+ void SetOutlineStyles();
+
+ bool SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper);
+ bool SetUpperSpacing(SwPaM &pMyPam, int nSpace);
+ bool SetLowerSpacing(SwPaM &rMyPam, int nSpace);
+
+ bool IsInlineEscherHack() const
+ { return !m_aFieldStack.empty() && m_aFieldStack.back().mnFieldId == 95; };
+
+ void StoreMacroCmds();
+
+ // #i84783#
+ // determine object attribute "Layout in Table Cell"
+ bool IsObjectLayoutInTableCell(const sal_uInt32 nGroupShapeBooleanProperties) const;
+ void ReadGlobalTemplateSettings( std::u16string_view sCreatedFrom, const css::uno::Reference< css::container::XNameContainer >& xPrjNameMap );
+ SwWW8ImplReader(const SwWW8ImplReader &) = delete;
+ SwWW8ImplReader& operator=(const SwWW8ImplReader&) = delete;
+public: // really private, but can only be done public
+ ~SwWW8ImplReader();
+ sal_uInt16 GetToggleAttrFlags() const;
+ sal_uInt16 GetToggleBiDiAttrFlags() const;
+ void SetToggleAttrFlags(sal_uInt16 nFlags);
+ void SetToggleBiDiAttrFlags(sal_uInt16 nFlags);
+
+ tools::Long Read_Footnote(WW8PLCFManResult* pRes);
+ sal_uInt16 End_Footnote();
+ tools::Long Read_Field(WW8PLCFManResult* pRes);
+ sal_uInt16 End_Field();
+ tools::Long Read_Book(WW8PLCFManResult*);
+ tools::Long Read_And(WW8PLCFManResult* pRes);
+ tools::Long Read_AtnBook(WW8PLCFManResult*);
+ tools::Long Read_FactoidBook(WW8PLCFManResult*);
+
+ // attributes
+
+ void Read_Special(sal_uInt16, const sal_uInt8*, short nLen);
+ void Read_Obj(sal_uInt16, const sal_uInt8*, short nLen);
+ void Read_PicLoc(sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_BoldUsw(sal_uInt16 nId, const sal_uInt8*, short nLen);
+ void Read_Bidi(sal_uInt16 nId, const sal_uInt8*, short nLen);
+ void Read_BoldBiDiUsw(sal_uInt16 nId, const sal_uInt8*, short nLen);
+ void Read_AmbiguousSPRM(sal_uInt16 nId, const sal_uInt8*, short nLen);
+ void Read_SubSuper( sal_uInt16, const sal_uInt8*, short nLen );
+ bool ConvertSubToGraphicPlacement();
+ static SwFrameFormat *ContainsSingleInlineGraphic(const SwPaM &rRegion);
+ void Read_SubSuperProp( sal_uInt16, const sal_uInt8*, short nLen );
+ void Read_Underline( sal_uInt16, const sal_uInt8*, short nLen );
+ void Read_TextColor( sal_uInt16, const sal_uInt8*, short nLen );
+ void openFont(sal_uInt16 nFCode, sal_uInt16 nId);
+ void closeFont(sal_uInt16 nId);
+ void Read_FontCode( sal_uInt16, const sal_uInt8*, short nLen );
+ void Read_FontSize( sal_uInt16, const sal_uInt8*, short nLen );
+ void Read_CharSet(sal_uInt16 , const sal_uInt8* pData, short nLen);
+ void Read_Language( sal_uInt16, const sal_uInt8*, short nLen );
+ void Read_CColl( sal_uInt16, const sal_uInt8*, short nLen );
+ void Read_Kern( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_FontKern( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_Emphasis( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_ScaleWidth( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_Relief( sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_TextAnim( sal_uInt16, const sal_uInt8* pData, short nLen);
+
+ void Read_NoLineNumb( sal_uInt16 nId, const sal_uInt8* pData, short nLen );
+
+ void Read_LR( sal_uInt16 nId, const sal_uInt8*, short nLen );
+ void Read_UL( sal_uInt16 nId, const sal_uInt8*, short nLen );
+ void Read_ParaAutoBefore(sal_uInt16 , const sal_uInt8 *pData, short nLen);
+ void Read_ParaAutoAfter(sal_uInt16 , const sal_uInt8 *pData, short nLen);
+ void Read_ParaContextualSpacing( sal_uInt16 nId, const sal_uInt8* pData, short nLen );
+ void Read_LineBreakClear(sal_uInt16 nId, const sal_uInt8* pData, short nLen);
+ void Read_LineSpace( sal_uInt16, const sal_uInt8*, short nLen );
+
+ void SetRelativeJustify( bool bRel );
+ bool IsRelativeJustify();
+ bool IsRelativeJustify(sal_uInt16 nColl, o3tl::sorted_vector<sal_uInt16>& rVisitedStyles);
+ void Read_Justify(sal_uInt16, const sal_uInt8*, short nLen);
+
+ void Read_IdctHint(sal_uInt16, const sal_uInt8*, short nLen);
+ bool IsRightToLeft();
+ void Read_RTLJustify(sal_uInt16, const sal_uInt8*, short nLen);
+ void Read_Hyphenation( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_WidowControl( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_AlignFont( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_UsePgsuSettings( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_KeepLines( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_KeepParas( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_BreakBefore( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_ApoPPC(sal_uInt16, const sal_uInt8* pData, short);
+
+ void Read_BoolItem( sal_uInt16 nId, const sal_uInt8*, short nLen );
+
+ void Read_Border( sal_uInt16 nId, const sal_uInt8* pData, short nLen );
+ void Read_CharBorder(sal_uInt16 nId, const sal_uInt8* pData, short nLen );
+ void Read_Tab( sal_uInt16 nId, const sal_uInt8* pData, short nLen );
+ void Read_Symbol(sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_FieldVanish( sal_uInt16 nId, const sal_uInt8* pData, short nLen );
+
+ // Revision Marks ( == Redlining )
+
+ // insert or delete content (change char attributes resp.)
+ void Read_CRevisionMark(RedlineType eType, const sal_uInt8* pData, short nLen);
+ // insert new content
+ void Read_CFRMark(sal_uInt16 , const sal_uInt8* pData, short nLen);
+ // delete old content
+ void Read_CFRMarkDel(sal_uInt16 , const sal_uInt8* pData, short nLen);
+ // change properties of content (e.g. char formatting)
+ void Read_CPropRMark(sal_uInt16 , const sal_uInt8* pData, short nLen); // complex!
+
+ void Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen );
+ static bool ParseTabPos(WW8_TablePos *aTabPos, WW8PLCFx_Cp_FKP* pPap);
+ void Read_Shade( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen );
+
+ // outline level Ver8
+ void Read_POutLvl(sal_uInt16, const sal_uInt8* pData, short nLen);
+
+ void Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen );
+
+ void Read_CharShadow( sal_uInt16, const sal_uInt8* pData, short nLen );
+ void Read_CharHighlight( sal_uInt16, const sal_uInt8* pData, short nLen );
+ // Ver8-Listen
+
+ void Read_ListLevel( sal_uInt16 nId, const sal_uInt8* pData, short nLen);
+
+ /**
+ * read and interpret the sprmPIlfo used to determine which list
+ * contains the paragraph.
+ * @param nId unused (sprm value, 0x460b for sprmPIlfo).
+ * @param[in] pData operand.
+ * @param[in] nLen size of the operand (pData) in byte, should be 2.
+ * -1 to indicate the actual level is finished.
+ */
+ void Read_LFOPosition( sal_uInt16 nId, const sal_uInt8* pData, short nLen);
+ bool SetTextFormatCollAndListLevel(const SwPaM& rRg, SwWW8StyInf& rStyleInfo);
+
+ void Read_StyleCode(sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_Majority(sal_uInt16, const sal_uInt8* , short );
+ void Read_DoubleLine_Rotate( sal_uInt16, const sal_uInt8* pDATA, short nLen);
+
+ void Read_TextForeColor(sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_TextBackColor(sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_ParaBackColor(sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_ParaBiDi(sal_uInt16, const sal_uInt8* pData, short nLen);
+ static Color ExtractColour(const sal_uInt8* &rpData, bool bVer67);
+
+ void Read_TextVerticalAdjustment(sal_uInt16, const sal_uInt8* pData, short nLen);
+ void Read_UnderlineColor(sal_uInt16, const sal_uInt8* pData, short nLen);
+ tools::Long MapBookmarkVariables(const WW8FieldDesc* pF, OUString &rOrigName,
+ const OUString &rData);
+ OUString GetMappedBookmark(std::u16string_view rOrigName);
+
+ // fields
+ eF_ResT Read_F_Input(WW8FieldDesc*, OUString& rStr);
+ eF_ResT Read_F_InputVar(WW8FieldDesc*, OUString& rStr);
+ eF_ResT Read_F_ANumber( WW8FieldDesc*, OUString& );
+ eF_ResT Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr );
+ eF_ResT Read_F_Author( WW8FieldDesc*, OUString& );
+ eF_ResT Read_F_TemplName( WW8FieldDesc*, OUString& );
+ SvNumFormatType GetTimeDatePara(std::u16string_view aStr, sal_uInt32& rFormat, LanguageType &rLang,
+ int nWhichDefault, bool bHijri = false);
+ bool ForceFieldLanguage(SwField &rField, LanguageType nLang);
+ eF_ResT Read_F_DateTime( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_FileName( WW8FieldDesc*, OUString& rStr);
+ eF_ResT Read_F_Num( WW8FieldDesc* pF, OUString& );
+ eF_ResT Read_F_CurPage( WW8FieldDesc*, OUString& );
+ eF_ResT Read_F_Ref( WW8FieldDesc* pF, OUString& );
+
+ eF_ResT Read_F_Set( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_PgRef( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_NoteReference( WW8FieldDesc* pF, OUString& rStr );
+
+ eF_ResT Read_F_Tox( WW8FieldDesc* pF, OUString& rStr );
+ eF_ResT Read_F_Symbol( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_Embedd( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_FormTextBox( WW8FieldDesc* pF, OUString& rStr);
+ eF_ResT Read_F_FormCheckBox( WW8FieldDesc* pF, OUString& rStr );
+ eF_ResT Read_F_FormListBox( WW8FieldDesc* pF, OUString& rStr);
+ css::awt::Size MiserableDropDownFormHack(const OUString &rString,
+ css::uno::Reference<css::beans::XPropertySet> const & rPropSet);
+
+ eF_ResT Read_F_Macro( WW8FieldDesc*, OUString& rStr);
+ eF_ResT Read_F_DBField( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_DBNext( WW8FieldDesc*, OUString& );
+ eF_ResT Read_F_DBNum( WW8FieldDesc*, OUString& );
+ eF_ResT Read_F_Equation( WW8FieldDesc*, OUString& );
+ void Read_SubF_Ruby( msfilter::util::WW8ReadFieldParams& rReadParam);
+ eF_ResT Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_IncludeText( WW8FieldDesc*, OUString& rStr );
+ eF_ResT Read_F_Seq( WW8FieldDesc*, OUString& rStr );
+ /// Reads a STYLEREF field.
+ eF_ResT Read_F_Styleref(WW8FieldDesc*, OUString& rStr);
+
+ eF_ResT Read_F_OCX(WW8FieldDesc*, OUString&);
+ eF_ResT Read_F_Hyperlink(WW8FieldDesc*, OUString& rStr);
+ eF_ResT Read_F_Shape(WW8FieldDesc* pF, OUString& rStr);
+ eF_ResT Read_F_HTMLControl( WW8FieldDesc* pF, OUString& rStr);
+
+ short ImportSprm(const sal_uInt8* pPos, sal_Int32 nMemLen, sal_uInt16 nId = 0);
+
+ bool SearchRowEnd(WW8PLCFx_Cp_FKP* pPap,WW8_CP &rStartCp, int nLevel) const;
+
+ const WW8Fib& GetFib() const { return *m_xWwFib; }
+ SwDoc& GetDoc() const { return m_rDoc; }
+ sal_uInt16 GetCurrentColl() const { return m_nCurrentColl; }
+ void SetNCurrentColl( sal_uInt16 nColl ) { m_nCurrentColl = nColl; }
+ std::unique_ptr<SfxItemSet> SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet);
+ sal_uInt16 StyleUsingLFO( sal_uInt16 nLFOIndex ) const ;
+ const SwFormat* GetStyleWithOrgWWName( std::u16string_view rName ) const ;
+
+ static bool GetPictGrafFromStream(Graphic& rGraphic, SvStream& rSrc);
+ SAL_WARN_UNUSED_RESULT static bool PicRead(SvStream *pDataStream, WW8_PIC *pPic, bool bVer67);
+ static bool ImportOleWMF(const tools::SvRef<SotStorage>& xSrc1, GDIMetaFile& rWMF, tools::Long& rX,
+ tools::Long& rY);
+ static Color GetCol(sal_uInt8 nIco);
+
+ SwWW8ImplReader( sal_uInt8 nVersionPara, SotStorage* pStorage, SvStream* pSt,
+ SwDoc& rD, OUString aBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos );
+
+ const OUString& GetBaseURL() const { return m_sBaseURL; }
+ // load a complete doc file
+ ErrCode LoadDoc(WW8Glossary *pGloss=nullptr);
+ rtl_TextEncoding GetCurrentCharSet();
+ rtl_TextEncoding GetCurrentCJKCharSet();
+ rtl_TextEncoding GetCharSetFromLanguage();
+ rtl_TextEncoding GetCJKCharSetFromLanguage();
+
+ void PostProcessAttrs();
+ void ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr);
+ void NotifyMacroEventRead();
+};
+
+bool CanUseRemoteLink(const OUString &rGrfName);
+void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat);
+void SetStyleIndent(SwWW8StyInf &rStyleInfo, const SwNumFormat &rFormat);
+// #i103711#
+// #i105414#
+void SyncIndentWithList( SvxFirstLineIndentItem & rFirstLine,
+ SvxTextLeftMarginItem & rLeftMargin,
+ const SwNumFormat &rFormat,
+ const bool bFirstLineOfStSet,
+ const bool bLeftIndentSet );
+tools::Long GetListFirstLineIndent(const SwNumFormat &rFormat);
+OUString BookmarkToWriter(std::u16string_view rBookmark);
+bool RTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth,
+ sal_Int16 eHoriOri, sal_Int16 eHoriRel, SwTwips nPageLeft,
+ SwTwips nPageRight, SwTwips nPageSize);
+void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec &rRecord,
+ SfxItemSet &rFlySet);
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx
new file mode 100644
index 0000000000..6022564285
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par2.cxx
@@ -0,0 +1,4606 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/safeint.hxx>
+#include <o3tl/temporary.hxx>
+#include <tools/solar.h>
+#include <vcl/font.hxx>
+#include <hintids.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <msfilter.hxx>
+#include <pam.hxx>
+#include <deletelistener.hxx>
+#include <doc.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <poolfmt.hxx>
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <mdiexp.hxx>
+#include <txtftn.hxx>
+#include <frmfmt.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <fmtlsplt.hxx>
+#include <charfmt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <formatflysplit.hxx>
+#include <numrule.hxx>
+#include "sprmids.hxx"
+#include <wwstyles.hxx>
+#include "ww8struc.hxx"
+#include "ww8par.hxx"
+#include "ww8par2.hxx"
+
+#include <frmatr.hxx>
+#include <itabenum.hxx>
+#include <unocrsr.hxx>
+
+#include <iostream>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+WW8TabBandDesc::WW8TabBandDesc():
+ pNextBand(nullptr), nGapHalf(0), mnDefaultLeft(0), mnDefaultTop(0), mnDefaultRight(0),
+ mnDefaultBottom(0), mbHasSpacing(false), nLineHeight(0), nRows(0), nCenter{}, nWidth{},
+ nWwCols(0), nSwCols(0), bLEmptyCol(false), bREmptyCol(false), bCantSplit(false),
+ pTCs(nullptr), nOverrideSpacing{}, nOverrideValues{}, pSHDs(nullptr),
+ pNewSHDs(nullptr), bExist{}, nTransCell{}
+{
+ for (sal_uInt16 & rn : maDirections)
+ rn = 4;
+}
+
+WW8TabBandDesc::~WW8TabBandDesc()
+{
+ delete[] pTCs;
+ delete[] pSHDs;
+ delete[] pNewSHDs;
+}
+
+void sw::util::RedlineStack::close( const SwPosition& rPos,
+ RedlineType eType, WW8TabDesc* pTabDesc )
+{
+ // If the redline type is not found in the redline stack, we have to check if there has been
+ // a tabledesc and to check its saved redline stack, too. (#136939, #i68139)
+ if( !close( rPos, eType ) )
+ {
+ if( pTabDesc && pTabDesc->getOldRedlineStack() )
+ {
+ bool const bResult =
+ pTabDesc->getOldRedlineStack()->close(rPos, eType);
+ OSL_ENSURE( bResult, "close without open!");
+ }
+ }
+}
+
+void wwSectionManager::SetCurrentSectionHasFootnote()
+{
+ OSL_ENSURE(!maSegments.empty(),
+ "should not be possible, must be at least one segment");
+ if (!maSegments.empty())
+ maSegments.back().mbHasFootnote = true;
+}
+
+void wwSectionManager::SetCurrentSectionVerticalAdjustment(const drawing::TextVerticalAdjust nVA)
+{
+ OSL_ENSURE(!maSegments.empty(),
+ "should not be possible, must be at least one segment");
+ if ( !maSegments.empty() )
+ maSegments.back().mnVerticalAdjustment = nVA;
+}
+
+bool wwSectionManager::CurrentSectionIsVertical() const
+{
+ OSL_ENSURE(!maSegments.empty(),
+ "should not be possible, must be at least one segment");
+ if (!maSegments.empty())
+ return maSegments.back().IsVertical();
+ return false;
+}
+
+bool wwSectionManager::CurrentSectionIsProtected() const
+{
+ OSL_ENSURE(!maSegments.empty(),
+ "should not be possible, must be at least one segment");
+ if (!maSegments.empty())
+ return SectionIsProtected(maSegments.back());
+ return false;
+}
+
+sal_uInt32 wwSectionManager::GetPageLeft() const
+{
+ return !maSegments.empty() ? maSegments.back().m_nPgLeft : 0;
+}
+
+sal_uInt32 wwSectionManager::GetPageRight() const
+{
+ return !maSegments.empty() ? maSegments.back().m_nPgRight : 0;
+}
+
+sal_uInt32 wwSectionManager::GetPageWidth() const
+{
+ return !maSegments.empty() ? maSegments.back().GetPageWidth() : 0;
+}
+
+sal_uInt32 wwSectionManager::GetTextAreaWidth() const
+{
+ return !maSegments.empty() ? maSegments.back().GetTextAreaWidth() : 0;
+}
+
+sal_uInt32 wwSectionManager::GetWWPageTopMargin() const
+{
+ return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0;
+}
+
+namespace
+{
+bool IsInSplitFly(SwPaM& rPaM)
+{
+ SwNode& rNode = rPaM.GetPoint()->GetNode();
+ SwNodeOffset nNodeIndex = rNode.GetIndex();
+ SwNodes& rNodes = rNode.GetNodes();
+ if (nNodeIndex >= rNodes.GetEndOfAutotext().GetIndex()
+ || nNodeIndex < rNodes.GetEndOfInserts().GetIndex())
+ {
+ return false;
+ }
+
+ SwFrameFormat* pFlyFormat = rNode.StartOfSectionNode()->GetFlyFormat();
+ if (!pFlyFormat)
+ {
+ return false;
+ }
+
+ return pFlyFormat->GetFlySplit().GetValue();
+}
+}
+
+sal_uInt16 SwWW8ImplReader::End_Footnote()
+{
+ /*
+ Ignoring Footnote outside of the normal Text. People will put footnotes
+ into field results and field commands.
+ */
+ bool bSplitFly = IsInSplitFly(*m_pPaM);
+ if (m_bIgnoreText
+ || (m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras() && !bSplitFly))
+ {
+ return 0;
+ }
+
+ OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start");
+ if (m_aFootnoteStack.empty())
+ return 0;
+
+ bool bFtEdOk = false;
+ const FootnoteDescriptor &rDesc = m_aFootnoteStack.back();
+
+ //Get the footnote character and remove it from the txtnode. We'll
+ //replace it with the footnote
+ SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
+ sal_Int32 nPos = m_pPaM->GetPoint()->GetContentIndex();
+
+ OUString sChar;
+ SwTextFootnote* pFN = nullptr;
+ //There should have been a footnote char, we will replace this.
+ if (pText && nPos)
+ {
+ sChar += OUStringChar(pText->GetText()[--nPos]);
+ m_pPaM->SetMark();
+ m_pPaM->GetMark()->AdjustContent(-1);
+ std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_oLastAnchorPos ? m_rDoc.CreateUnoCursor(*m_oLastAnchorPos) : nullptr);
+ m_oLastAnchorPos.reset();
+ m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
+ m_pPaM->DeleteMark();
+ if (xLastAnchorCursor)
+ m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
+ SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN);
+ pFN = static_cast<SwTextFootnote*>(pText->InsertItem(aFootnote, nPos, nPos));
+ }
+ OSL_ENSURE(pFN, "Problems creating the footnote text");
+ if (pFN)
+ {
+ SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position
+ WW8PLCFxSaveAll aSave;
+ m_xPlcxMan->SaveAllPLCFx( aSave );
+ std::shared_ptr<WW8PLCFMan> xOldPlcxMan = m_xPlcxMan;
+
+ const SwNodeIndex* pSttIdx = pFN->GetStartNode();
+ assert(pSttIdx && "Problems creating footnote text");
+
+ pFN->SetSeqNo(m_rDoc.GetFootnoteIdxs().size());
+
+ bool bOld = m_bFootnoteEdn;
+ m_bFootnoteEdn = true;
+
+ SwFormatFootnote& rFormatFootnote = static_cast<SwFormatFootnote&>(pFN->GetAttr());
+
+ SvtDeleteListener aDeleteListener(rFormatFootnote.GetNotifier());
+
+ // read content of Ft-/End-Note
+ Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType);
+
+ m_bFootnoteEdn = bOld;
+
+ SAL_WARN_IF(aDeleteListener.WasDeleted(), "sw.ww8", "Footnode deleted during its import");
+ if (!aDeleteListener.WasDeleted())
+ {
+ bFtEdOk = true;
+
+ OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))),
+ "footnote autonumbering must be 0x02, and everything else must not be");
+
+ // If no automatic numbering use the following char from the main text
+ // as the footnote number
+ if (!rDesc.mbAutoNum)
+ pFN->SetNumber(0, 0, sChar);
+
+ /*
+ Delete the footnote char from the footnote if it's at the beginning
+ as usual. Might not be if the user has already deleted it, e.g.
+ #i14737#
+ */
+ SwPosition& rPaMPointPos = *m_pPaM->GetPoint();
+ rPaMPointPos.Assign(pSttIdx->GetIndex() + 1);
+ SwTextNode* pTNd = rPaMPointPos.GetNode().GetTextNode();
+ if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty())
+ {
+ const OUString &rText = pTNd->GetText();
+ if (rText[0] == sChar[0])
+ {
+ // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent
+ sal_Int32 nFirstLineIndent=0;
+ SfxItemSetFixed<RES_MARGIN_FIRSTLINE, RES_MARGIN_FIRSTLINE> aSet(m_rDoc.GetAttrPool());
+ if ( pTNd->GetAttr(aSet) )
+ {
+ const SvxFirstLineIndentItem *const pFirstLine(aSet.GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
+ if (pFirstLine)
+ {
+ nFirstLineIndent = pFirstLine->GetTextFirstLineOffset();
+ }
+ }
+
+ rPaMPointPos.SetContent(0);
+ m_pPaM->SetMark();
+ // Strip out aesthetic tabs we may have inserted on export #i24762#
+ if (nFirstLineIndent < 0 && rText.getLength() > 1 && rText[1] == 0x09)
+ m_pPaM->GetMark()->AdjustContent(1);
+ m_pPaM->GetMark()->AdjustContent(1);
+ m_xReffingStck->Delete(*m_pPaM);
+ m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM );
+ m_pPaM->DeleteMark();
+ }
+ }
+ }
+
+ *m_pPaM->GetPoint() = aTmpPos; // restore Cursor
+
+ m_xPlcxMan = xOldPlcxMan; // Restore attributes
+ m_xPlcxMan->RestoreAllPLCFx( aSave );
+ }
+
+ if (bFtEdOk)
+ m_aSectionManager.SetCurrentSectionHasFootnote();
+
+ m_aFootnoteStack.pop_back();
+ return 0;
+}
+
+tools::Long SwWW8ImplReader::Read_Footnote(WW8PLCFManResult* pRes)
+{
+ /*
+ Ignoring Footnote outside of the normal Text. People will put footnotes
+ into field results and field commands.
+ */
+ bool bSplitFly = IsInSplitFly(*m_pPaM);
+ if (m_bIgnoreText
+ || (m_pPaM->GetPoint()->GetNode() < m_rDoc.GetNodes().GetEndOfExtras() && !bSplitFly))
+ {
+ return 0;
+ }
+
+ FootnoteDescriptor aDesc;
+ aDesc.mbAutoNum = true;
+ if (eEDN == pRes->nSprmId)
+ {
+ aDesc.meType = MAN_EDN;
+ WW8PLCFx_SubDoc* pEndNote = m_xPlcxMan->GetEdn();
+ if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr)
+ aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
+ }
+ else
+ {
+ aDesc.meType = MAN_FTN;
+ WW8PLCFx_SubDoc* pFootNote = m_xPlcxMan->GetFootnote();
+ if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr)
+ aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData);
+ }
+
+ aDesc.mnStartCp = pRes->nCp2OrIdx;
+ aDesc.mnLen = pRes->nMemLen;
+
+ m_aFootnoteStack.push_back(aDesc);
+
+ return 0;
+}
+
+bool SwWW8ImplReader::SearchRowEnd(WW8PLCFx_Cp_FKP* pPap, WW8_CP &rStartCp,
+ int nLevel) const
+{
+ WW8PLCFxDesc aRes;
+ aRes.pMemPos = nullptr;
+ aRes.nEndPos = rStartCp;
+ std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
+
+ while (pPap->HasFkp() && rStartCp != WW8_CP_MAX)
+ {
+ if (pPap->Where() != WW8_CP_MAX)
+ {
+ SprmResult aSprmRes = pPap->HasSprm(TabRowSprm(nLevel));
+ const sal_uInt8* pB = aSprmRes.pSprm;
+ if (pB && aSprmRes.nRemainingData >= 1 && *pB == 1)
+ {
+ aSprmRes = pPap->HasSprm(0x6649);
+ const sal_uInt8 *pLevel = aSprmRes.pSprm;
+ if (pLevel && aSprmRes.nRemainingData >= 1)
+ {
+ if (nLevel + 1 == *pLevel)
+ return true;
+ }
+ else
+ {
+ OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm");
+ return true; // RowEnd found
+ }
+ }
+ }
+
+ aRes.nStartPos = aRes.nEndPos;
+ aRes.pMemPos = nullptr;
+ //Seek to our next block of properties
+ if (!(pPap->SeekPos(aRes.nStartPos)))
+ {
+ aRes.nEndPos = WW8_CP_MAX;
+ pPap->SetDirty(true);
+ }
+ pPap->GetSprms(&aRes);
+ pPap->SetDirty(false);
+ auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
+ if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
+ {
+ SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain");
+ break;
+ }
+ //Update our aRes to get the new starting point of the next properties
+ rStartCp = aRes.nEndPos;
+ }
+
+ return false;
+}
+
+ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd,
+ const WW8_TablePos *pTabPos)
+{
+ const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr;
+ ApoTestResults aRet;
+ // Frame in Style Definition (word appears to ignore them if inside an
+ // text autoshape)
+ sal_uInt16 const nStyle(m_xPlcxMan->GetColl());
+ if (!m_bTxbxFlySection && nStyle < m_vColl.size())
+ aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr;
+
+ /*
+ #i1140#
+ If I have a table and apply a style to one of its frames that should cause
+ a paragraph that it is applied to it to only exist as a separate floating
+ frame, then the behaviour depends on which cell that it has been applied
+ to. If it is the first cell of a row then the whole table row jumps into the
+ new frame, if it isn't then the paragraph attributes are applied
+ "except" for the floating frame stuff. i.e. it's ignored. So if there's a
+ table, and we're not in the first cell then we ignore the fact that the
+ paragraph style wants to be in a different frame.
+
+ This sort of mindbending inconsistency is surely why frames are deprecated
+ in word 97 onwards and hidden away from the user
+
+ #i1532# & #i5379#
+ If we are already a table in a frame then we must grab the para properties
+ to see if we are still in that frame.
+ */
+
+ aRet.m_bHasSprmPWr = m_xPlcxMan->HasParaSprm(m_bVer67 ? 37 : 0x2423).pSprm != nullptr; // sprmPWr
+ SprmResult aSrpmPPc = m_xPlcxMan->HasParaSprm(m_bVer67 ? 29 : 0x261B); // sprmPPc
+ const sal_uInt8 *pSrpmPPc = aSrpmPPc.pSprm;
+ aRet.m_bHasSprmPPc = pSrpmPPc != nullptr;
+ const sal_Int16 nTPc = aRet.mpStyleApo ? aRet.mpStyleApo->nTPc : 0;
+ aRet.m_nSprmPPc = (pSrpmPPc && aSrpmPPc.nRemainingData >= 1) ? *pSrpmPPc : nTPc;
+
+ // Is there some frame data here
+ bool bNowApo = aRet.HasFrame() || pTopLevelTable;
+ if (bNowApo)
+ {
+ if (!ConstructApo(aRet, pTabPos))
+ bNowApo = false;
+ }
+
+ bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd;
+ if (bTestAllowed)
+ {
+ //Test is allowed if there is no table.
+ //Otherwise only allowed if we are in the
+ //first paragraph of the first cell of a row.
+ //(And only if the row we are inside is at the
+ //same level as the previous row, think tables
+ //in tables)
+ if (nCellLevel == m_nInTable)
+ {
+
+ if (!m_nInTable)
+ bTestAllowed = true;
+ else
+ {
+ if (!m_xTableDesc)
+ {
+ OSL_ENSURE(m_xTableDesc, "What!");
+ bTestAllowed = false;
+ }
+ else
+ {
+ // #i39468#
+ // If current cell isn't valid, the test is allowed.
+ // The cell isn't valid, if e.g. there is a new row
+ // <pTableDesc->nCurrentRow> >= <pTableDesc->pTabLines->Count()>
+ bTestAllowed =
+ m_xTableDesc->GetCurrentCol() == 0 &&
+ ( !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) ||
+ m_xTableDesc->InFirstParaInCell() );
+ }
+ }
+ }
+ }
+
+ if (!bTestAllowed)
+ return aRet;
+
+ aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start
+ aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end
+
+ //If it happens that we are in a table, then if it's not the first cell
+ //then any attributes that might otherwise cause the contents to jump
+ //into another frame don't matter, a table row sticks together as one
+ //unit no matter what else happens. So if we are not in a table at
+ //all, or if we are in the first cell then test that the last frame
+ //data is the same as the current one
+ if (bNowApo && InEqualApo(nCellLevel))
+ {
+ // two bordering each other
+ if (!TestSameApo(aRet, pTabPos))
+ aRet.mbStopApo = aRet.mbStartApo = true;
+ }
+
+ return aRet;
+}
+
+// helper methods for outline, numbering and bullets
+
+static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel )
+{
+ static const SvxNumType eNumA[8] = { SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER,
+ SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N, SVX_NUM_ARABIC,
+ SVX_NUM_ARABIC, SVX_NUM_ARABIC };
+
+ static const SvxAdjust eAdjA[4] = { SvxAdjust::Left,
+ SvxAdjust::Right, SvxAdjust::Left, SvxAdjust::Left };
+ if (rAV.nfc < 8) {
+ rNum.SetNumberingType( eNumA[ rAV.nfc ] );
+ } else {
+ SvxNumType nType = SVX_NUM_ARABIC;
+ switch( rAV.nfc ) {
+ case 14:
+ case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break;
+ case 30:nType = SVX_NUM_TIAN_GAN_ZH; break;
+ case 31:nType = SVX_NUM_DI_ZI_ZH; break;
+ case 35:
+ case 36:
+ case 37:
+ case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break;
+ case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break;
+ case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break;
+ case 10:
+ case 11:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break;
+ case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break;
+ case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break;
+ case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break;
+ case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break;
+ case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break;
+ case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break;
+ case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break;
+ //case 42:
+ //case 43:
+ case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break;
+ default:
+ nType= SVX_NUM_ARABIC;break;
+ }
+ rNum.SetNumberingType( nType );
+ }
+
+ if ((rAV.aBits1 & 0x4) >> 2)
+ {
+ rNum.SetIncludeUpperLevels(nSwLevel + 1);
+ }
+ rNum.SetStart( SVBT16ToUInt16( rAV.iStartAt ) );
+ rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] );
+
+ rNum.SetCharTextDistance( SVBT16ToUInt16( rAV.dxaSpace ) );
+ sal_Int16 nIndent = std::abs(static_cast<sal_Int16>(SVBT16ToUInt16( rAV.dxaIndent )));
+ if( rAV.aBits1 & 0x08 ) //fHang
+ {
+ rNum.SetFirstLineOffset( -nIndent );
+ rNum.SetAbsLSpace( nIndent );
+ }
+ else
+ rNum.SetCharTextDistance( nIndent ); // width of number is missing
+
+ if( rAV.nfc == 5 || rAV.nfc == 7 )
+ {
+ OUString sP = "." + rNum.GetSuffix();
+ rNum.SetListFormat("", sP, nSwLevel); // ordinal number
+ }
+ else
+ rNum.SetListFormat("", "", nSwLevel);
+}
+
+void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV const &rAV,
+ const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline)
+{
+ if (nStart > nElements)
+ return;
+
+ pText += nStart;
+ nElements -= nStart;
+
+ bool bInsert = false; // Default
+ rtl_TextEncoding eCharSet = m_eStructCharSet;
+
+ const WW8_FFN* pF = m_xFonts->GetFont(SVBT16ToUInt16(rAV.ftc)); // FontInfo
+ bool bListSymbol = pF && ( pF->aFFNBase.chs == 2 ); // Symbol/WingDings/...
+
+ sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter;
+ OUStringBuffer sText(static_cast<sal_Int32>(nLen));
+ if (m_bVer67)
+ {
+ if (nLen > nElements)
+ {
+ SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
+ << nLen << " vs " << nElements << " max");
+ return;
+ }
+ sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet);
+ // ofz#23961 in case of multi-byte input encoding resulting in shorter
+ // output pad to full length with something semi-arbitrary
+ comphelper::string::padToLength(sText, nLen, cBulletChar);
+ }
+ else
+ {
+ if (nLen > nElements / 2)
+ {
+ SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range "
+ << nLen << " vs " << nElements / 2 << " max");
+ return;
+ }
+ for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2)
+ {
+ sText.append(static_cast<sal_Unicode>(SVBT16ToUInt16(*reinterpret_cast<SVBT16 const *>(pText))));
+ }
+ }
+
+ if( bOutline )
+ { // outline
+ if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show
+ || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none
+ {
+ // if self defined digits
+ bInsert = true; // then apply character
+
+ // replace by simple Bullet ?
+ if( bListSymbol )
+ {
+ // use cBulletChar for correct mapping on MAC
+ sText.setLength(0);
+ comphelper::string::padToLength(sText, rAV.cbTextBefore
+ + rAV.cbTextAfter, cBulletChar);
+ }
+ }
+ }
+ else
+ { // numbering / bullets
+ bInsert = true;
+ if( bListSymbol )
+ {
+ FontFamily eFamily;
+ OUString aName;
+ FontPitch ePitch;
+
+ if( GetFontParams( SVBT16ToUInt16( rAV.ftc ), eFamily, aName,
+ ePitch, eCharSet ) ){
+
+ vcl::Font aFont;
+ aFont.SetFamilyName( aName );
+ aFont.SetFamily( eFamily );
+
+ aFont.SetCharSet( eCharSet );
+ rNum.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+
+ rNum.SetBulletFont( &aFont );
+
+ // take only the very first character
+ if (rAV.cbTextBefore || rAV.cbTextAfter)
+ {
+ rNum.SetBulletChar(
+ OUString::unacquired(sText).iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
+ }
+ else
+ rNum.SetBulletChar( 0x2190 );
+ }
+ }
+ }
+ if( !bInsert )
+ return;
+
+ OUString sPrefix;
+ OUString sSuffix;
+ if (rAV.cbTextBefore)
+ {
+ sPrefix = sText.copy( 0, rAV.cbTextBefore ).makeStringAndClear();
+ }
+ if( rAV.cbTextAfter )
+ {
+ sSuffix = rNum.GetSuffix() + sText.subView( rAV.cbTextBefore, rAV.cbTextAfter);
+ }
+
+ rNum.SetListFormat(sPrefix, sSuffix, nLevel);
+
+// The characters before and after multiple digits do not apply because
+// those are handled differently by the writer and the result is in most
+// cases worse than without.
+}
+
+// SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules
+// which are provided by pNumR. This is used for everything beside
+// outline inside the text.
+void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel,
+ bool bOutLine)
+{
+ SwNumFormat aNF;
+ aNF.SetListFormat("", "", nSwLevel);
+ if (pAD)
+ { // there is an Anld-Sprm
+ m_bCurrentAND_fNumberAcross = 0 != pAD->fNumberAcross;
+ WW8_ANLV const &rAV = pAD->eAnlv;
+ SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format
+ SetAnlvStrings(aNF, nSwLevel, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest
+ }
+ pNumR->Set(nSwLevel, aNF);
+}
+
+// chapter numbering and bullets
+
+// Chapter numbering happens in the style definition.
+// Sprm 13 provides the level, Sprm 12 the content.
+
+SwNumRule* SwWW8ImplReader::GetStyRule()
+{
+ if( m_xStyles->mpStyRule ) // Bullet-Style already present
+ return m_xStyles->mpStyRule;
+
+ constexpr OUString aBaseName(u"WW8StyleNum"_ustr);
+ const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) );
+
+ // #i86652#
+ sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr, false,
+ SvxNumberFormat::LABEL_ALIGNMENT );
+ m_xStyles->mpStyRule = m_rDoc.GetNumRuleTable()[nRul];
+ // Auto == false-> numbering style
+ m_xStyles->mpStyRule->SetAutoRule(false);
+
+ return m_xStyles->mpStyRule;
+}
+
+// Sprm 13
+void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ m_nSwNumLevel = 0xff; // Default: invalid
+
+ if( nLen <= 0 )
+ return;
+
+ // StyleDef ?
+ if( m_pCurrentColl )
+ {
+ // only for SwTextFormatColl, not CharFormat
+ // WW: 0 = no Numbering
+ SwWW8StyInf * pColl = GetStyle(m_nCurrentColl);
+ if (pColl != nullptr && pColl->m_bColl && *pData)
+ {
+ // Range WW:1..9 -> SW:0..8 no bullets / numbering
+
+ if (*pData <= 9)
+ {
+ m_nSwNumLevel = *pData - 1;
+ if (!m_bNoAttrImport)
+ static_cast<SwTextFormatColl*>(m_pCurrentColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel );
+ // For WW-NoNumbering also NO_NUMBERING could be used.
+ // ( For normal numberierung NO_NUM has to be used:
+ // NO_NUM : pauses numbering,
+ // NO_NUMBERING : no numbering at all )
+
+ }
+ else if( *pData == 10 || *pData == 11 )
+ {
+ // remember type, the rest happens at Sprm 12
+ m_xStyles->mnWwNumLevel = *pData;
+ }
+ }
+ }
+ else
+ {
+ //Not StyleDef
+ if (!m_bAnl)
+ StartAnl(pData); // begin of outline / bullets
+ NextAnlLine(pData);
+ }
+}
+
+void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12
+{
+ SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
+ if( !m_pCurrentColl || nLen <= 0 // only for Styledef
+ || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat ->
+ || ( m_nIniFlags & WW8FL_NO_OUTLINE ) )
+ {
+ m_nSwNumLevel = 0xff;
+ return;
+ }
+
+ if (o3tl::make_unsigned(nLen) < sizeof(WW8_ANLD))
+ {
+ SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD));
+ m_nSwNumLevel = 0xff;
+ return;
+ }
+
+ if (m_nSwNumLevel <= 9) // Value range mapping WW:1..9 -> SW:0..8
+ {
+
+ // If NumRuleItems were set, either directly or through inheritance, disable them now
+ m_pCurrentColl->SetFormatAttr( SwNumRuleItem() );
+
+ constexpr OUString aName(u"Outline"_ustr);
+ SwNumRule aNR( m_rDoc.GetUniqueNumRuleName( &aName ),
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
+ OUTLINE_RULE );
+ aNR = *m_rDoc.GetOutlineNumRule();
+
+ SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true);
+
+ // Missing Levels need not be replenished
+ m_rDoc.SetOutlineNumRule( aNR );
+ }
+ else if( m_xStyles->mnWwNumLevel == 10 || m_xStyles->mnWwNumLevel == 11 ){
+ SwNumRule* pNR = GetStyRule();
+ SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false);
+ m_pCurrentColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) );
+
+ pStyInf = GetStyle(m_nCurrentColl);
+ if (pStyInf != nullptr)
+ pStyInf->m_bHasStyNumRule = true;
+ }
+}
+
+// Numbering / Bullets
+
+// SetNumOlst() carries the Numrules for this cell to SwNumFormat.
+// For this the info is fetched from OLST and not from ANLD ( see later )
+// ( only for outline inside text; Bullets / numbering use ANLDs )
+void SwWW8ImplReader::SetNumOlst(SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel)
+{
+ SwNumFormat aNF;
+ WW8_ANLV &rAV = pO->rganlv[nSwLevel];
+ SetBaseAnlv(aNF, rAV, nSwLevel);
+ // ... and then the Strings
+ int nTextOfs = 0;
+ sal_uInt8 i;
+ WW8_ANLV* pAV1; // search String-Positions
+ for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1)
+ nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter;
+
+ if (!m_bVer67)
+ nTextOfs *= 2;
+ SetAnlvStrings(aNF, nSwLevel, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply
+ pNumR->Set(nSwLevel, aNF);
+}
+
+// The OLST is at the beginning of each section that contains outlines.
+// The ANLDs that are connected to each outline-line contain only nonsense,
+// so the OLSTs are remembered for the section to have usable information
+// when outline-paragraphs occur.
+void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ m_xNumOlst.reset();
+ if (nLen <= 0)
+ return;
+
+ if (o3tl::make_unsigned(nLen) < sizeof(WW8_OLST))
+ {
+ SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST));
+ return;
+ }
+
+ m_xNumOlst.reset(new WW8_OLST);
+ *m_xNumOlst = *reinterpret_cast<WW8_OLST const *>(pData);
+}
+
+WW8LvlType GetNumType(sal_uInt8 nWwLevelNo)
+{
+ WW8LvlType nRet = WW8_None;
+ if( nWwLevelNo == 12 )
+ nRet = WW8_Pause;
+ else if( nWwLevelNo == 10 )
+ nRet = WW8_Numbering;
+ else if( nWwLevelNo == 11 )
+ nRet = WW8_Sequence;
+ else if( nWwLevelNo > 0 && nWwLevelNo <= 9 )
+ nRet = WW8_Outline;
+ return nRet;
+}
+
+SwNumRule *ANLDRuleMap::GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType)
+{
+ const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule;
+ if (rNumRule.isEmpty())
+ return nullptr;
+ return rDoc.FindNumRulePtr(rNumRule);
+}
+
+void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType)
+{
+ if (WW8_Numbering == nNumType)
+ msNumberingNumRule = rNumRule;
+ else
+ msOutlineNumRule = rNumRule;
+}
+
+// StartAnl is called at the beginning of a row area that contains
+// outline / numbering / bullets
+void SwWW8ImplReader::StartAnl(const sal_uInt8* pSprm13)
+{
+ m_bCurrentAND_fNumberAcross = false;
+
+ sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13));
+ if (nT == WW8_Pause || nT == WW8_None)
+ return;
+
+ m_nWwNumType = nT;
+ SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
+
+ // check for COL numbering:
+ SprmResult aS12; // sprmAnld
+ OUString sNumRule;
+
+ if (m_xTableDesc)
+ {
+ sNumRule = m_xTableDesc->GetNumRuleName();
+ if (!sNumRule.isEmpty())
+ {
+ pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
+ if (!pNumRule)
+ sNumRule.clear();
+ else
+ {
+ // this is ROW numbering ?
+ aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
+ if (aS12.pSprm && aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)) && 0 != reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
+ sNumRule.clear();
+ }
+ }
+ }
+
+ SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl);
+ if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule)
+ {
+ sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue();
+ pNumRule = m_rDoc.FindNumRulePtr(sNumRule);
+ if (!pNumRule)
+ sNumRule.clear();
+ }
+
+ if (sNumRule.isEmpty())
+ {
+ if (!pNumRule)
+ {
+ // #i86652#
+ pNumRule = m_rDoc.GetNumRuleTable()[
+ m_rDoc.MakeNumRule( sNumRule, nullptr, false,
+ SvxNumberFormat::LABEL_ALIGNMENT ) ];
+ }
+ if (m_xTableDesc)
+ {
+ if (!aS12.pSprm)
+ aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld
+ if (!aS12.pSprm || aS12.nRemainingData < sal_Int32(sizeof(WW8_ANLD)) || !reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross)
+ m_xTableDesc->SetNumRuleName(pNumRule->GetName());
+ }
+ }
+
+ m_bAnl = true;
+
+ sNumRule = pNumRule ? pNumRule->GetName() : OUString();
+ // set NumRules via stack
+ m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(),
+ SfxStringItem(RES_FLTR_NUMRULE, sNumRule));
+
+ m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType);
+}
+
+// NextAnlLine() is called once for every row of a
+// outline / numbering / bullet
+void SwWW8ImplReader::NextAnlLine(const sal_uInt8* pSprm13)
+{
+ if (!m_bAnl)
+ return;
+
+ SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType);
+
+ // pNd->UpdateNum without a set of rules crashes at the latest whilst storing as sdw3
+
+ // WW:10 = numbering -> SW:0 & WW:11 = bullets -> SW:0
+ if (*pSprm13 == 10 || *pSprm13 == 11)
+ {
+ m_nSwNumLevel = 0;
+ if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
+ {
+ // not defined yet
+ // sprmAnld o. 0
+ SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
+ if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
+ SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
+ }
+ }
+ else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8
+ {
+ m_nSwNumLevel = *pSprm13 - 1; // outline
+ // undefined
+ if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel))
+ {
+ if (m_xNumOlst) // there was a OLST
+ {
+ //Assure upper levels are set, #i9556#
+ for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI)
+ {
+ if (!pNumRule->GetNumFormat(nI))
+ SetNumOlst(pNumRule, m_xNumOlst.get(), nI);
+ }
+
+ SetNumOlst(pNumRule, m_xNumOlst.get(), m_nSwNumLevel);
+ }
+ else // no Olst -> use Anld
+ {
+ // sprmAnld
+ SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld);
+ if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)))
+ SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false);
+ }
+ }
+ }
+ else
+ m_nSwNumLevel = 0xff; // no number
+
+ SwTextNode* pNd = m_pPaM->GetPointNode().GetTextNode();
+ if (!pNd)
+ return;
+
+ if (m_nSwNumLevel < MAXLEVEL)
+ pNd->SetAttrListLevel( m_nSwNumLevel );
+ else
+ {
+ pNd->SetAttrListLevel(0);
+ pNd->SetCountedInList( false );
+ }
+}
+
+void SwWW8ImplReader::StopAllAnl(bool bGoBack)
+{
+ //Of course we're not restarting, but we'll make use of our knowledge
+ //of the implementation to do it.
+ StopAnlToRestart(WW8_None, bGoBack);
+}
+
+void SwWW8ImplReader::StopAnlToRestart(sal_uInt8 nNewType, bool bGoBack)
+{
+ if (bGoBack)
+ {
+ SwPosition aTmpPos(*m_pPaM->GetPoint());
+ m_pPaM->Move(fnMoveBackward, GoInContent);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
+ *m_pPaM->GetPoint() = aTmpPos;
+ }
+ else
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE);
+
+ m_aANLDRules.msNumberingNumRule.clear();
+ /*
+ #i18816#
+ my take on this problem is that moving either way from an outline to a
+ numbering doesn't halt the outline, while the numbering is always halted
+ */
+ bool bNumberingNotStopOutline =
+ (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) ||
+ ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline)));
+ if (!bNumberingNotStopOutline)
+ m_aANLDRules.msOutlineNumRule.clear();
+
+ m_nSwNumLevel = 0xff;
+ m_nWwNumType = WW8_None;
+ m_bAnl = false;
+}
+
+WW8TabBandDesc::WW8TabBandDesc( WW8TabBandDesc const & rBand )
+{
+ *this = rBand;
+ if( rBand.pTCs )
+ {
+ pTCs = reinterpret_cast<WW8_TCell *>(new char[nWwCols * sizeof (WW8_TCell)]);
+ // create uninitialized
+ memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) );
+ }
+ if( rBand.pSHDs )
+ {
+ pSHDs = new WW8_SHD[nWwCols];
+ memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) );
+ }
+ if( rBand.pNewSHDs )
+ {
+ pNewSHDs = new Color[nWwCols];
+ memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(Color));
+ }
+ memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs));
+}
+
+// ReadDef reads the cell position and the borders of a band
+void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS, short nLen)
+{
+ --nLen; //reduce len by expected nCols arg
+ if (nLen < 0)
+ return;
+ sal_uInt8 nCols = *pS; // number of cells
+
+ if (nCols > MAX_COL)
+ return;
+
+ nLen -= 2 * (nCols + 1); //reduce len by claimed amount of next x-borders arguments
+ if (nLen < 0)
+ return;
+
+ short nOldCols = nWwCols;
+ nWwCols = nCols;
+
+ const sal_uInt8* pT = &pS[1];
+ for (int i = 0; i <= nCols; i++, pT+=2)
+ nCenter[i] = static_cast<sal_Int16>(SVBT16ToUInt16( pT )); // X-borders
+
+ if( nCols != nOldCols ) // different column count
+ {
+ delete[] pTCs;
+ pTCs = nullptr;
+ delete[] pSHDs;
+ pSHDs = nullptr;
+ delete[] pNewSHDs;
+ pNewSHDs = nullptr;
+ }
+
+ short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved
+
+ if (!pTCs && nCols)
+ {
+ // create empty TCs
+ pTCs = new WW8_TCell[nCols];
+ }
+
+ short nColsToRead = std::min<short>(nFileCols, nCols);
+
+ if (nColsToRead <= 0)
+ return;
+
+ // read TCs
+
+ /*
+ Attention: Beginning with Ver8 there is an extra ushort per TC
+ added and the size of the border code is doubled.
+ Because of this a simple copy (pTCs[i] = *pTc;)
+ is not possible.
+ ---
+ Advantage: The work structure suits better.
+ */
+ WW8_TCell* pCurrentTC = pTCs;
+ if( bVer67 )
+ {
+ WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT);
+ for (int i = 0; i < nColsToRead; i++, ++pCurrentTC,++pTc)
+ {
+ // TC from file ?
+ sal_uInt8 aBits1 = pTc->aBits1Ver6;
+ pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 );
+ pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 );
+ pCurrentTC->rgbrc[ WW8_TOP ]
+ = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] ));
+ pCurrentTC->rgbrc[ WW8_LEFT ]
+ = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] ));
+ pCurrentTC->rgbrc[ WW8_BOT ]
+ = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] ));
+ pCurrentTC->rgbrc[ WW8_RIGHT ]
+ = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
+ if( ( pCurrentTC->bMerged )
+ && ( i > 0 ) )
+ {
+ // Cell merged -> remember
+ //bWWMergedVer6[i] = true;
+ pTCs[i-1].rgbrc[ WW8_RIGHT ]
+ = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] ));
+ // apply right border to previous cell
+ // bExist must not be set to false, because WW
+ // does not count this cells in text boxes...
+ }
+ }
+ }
+ else
+ {
+ WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT);
+ for (int k = 0; k < nColsToRead; ++k, ++pCurrentTC, ++pTc )
+ {
+ sal_uInt16 aBits1 = SVBT16ToUInt16( pTc->aBits1Ver8 );
+ pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 );
+ pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 );
+ pCurrentTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 );
+ pCurrentTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 );
+ pCurrentTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 );
+ pCurrentTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 );
+ pCurrentTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 );
+ pCurrentTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 );
+ // note: in aBits1 there are 7 bits unused,
+ // followed by another 16 unused bits
+
+ pCurrentTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]);
+ pCurrentTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]);
+ pCurrentTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]);
+ pCurrentTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]);
+ }
+ }
+
+ // #i25071 In '97 text direction appears to be only set using TC properties
+ // not with sprmTTextFlow so we need to cycle through the maDirections and
+ // double check any non-default directions
+ for (int k = 0; k < nCols; ++k)
+ {
+ if(maDirections[k] == 4)
+ {
+ if(pTCs[k].bVertical)
+ {
+ if(pTCs[k].bBackward)
+ maDirections[k] = 3;
+ else
+ maDirections[k] = 1;
+ }
+ }
+ }
+}
+
+void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen)
+{
+ if( !pParamsTSetBRC || !pTCs ) // set one or more cell border(s)
+ return;
+
+ if (nParamsLen < 3)
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+
+ sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed
+ sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1
+ sal_uInt8 nFlag = *(pParamsTSetBRC+2);
+
+ if (nitcFirst >= nWwCols)
+ return;
+
+ if (nitcLim > nWwCols)
+ nitcLim = nWwCols;
+
+ bool bChangeRight = (nFlag & 0x08) != 0;
+ bool bChangeBottom = (nFlag & 0x04) != 0;
+ bool bChangeLeft = (nFlag & 0x02) != 0;
+ bool bChangeTop = (nFlag & 0x01) != 0;
+
+ WW8_TCell* pCurrentTC = pTCs + nitcFirst;
+ WW8_BRCVer9 brcVer9;
+ if( nBrcVer == 6 )
+ {
+ if (nParamsLen < sizeof(WW8_BRCVer6) + 3)
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+ brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3)));
+ }
+ else if( nBrcVer == 8 )
+ {
+ static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
+ if (nParamsLen < sizeof(WW8_BRC) + 3)
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+ brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3));
+ }
+ else
+ {
+ if (nParamsLen < sizeof(WW8_BRCVer9) + 3)
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+ brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3);
+ }
+
+ for( int i = nitcFirst; i < nitcLim; ++i, ++pCurrentTC )
+ {
+ if( bChangeTop )
+ pCurrentTC->rgbrc[ WW8_TOP ] = brcVer9;
+ if( bChangeLeft )
+ pCurrentTC->rgbrc[ WW8_LEFT ] = brcVer9;
+ if( bChangeBottom )
+ pCurrentTC->rgbrc[ WW8_BOT ] = brcVer9;
+ if( bChangeRight )
+ pCurrentTC->rgbrc[ WW8_RIGHT ] = brcVer9;
+ }
+}
+
+void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen)
+{
+ // sprmTTableBorders
+ if( nBrcVer == 6 )
+ {
+ if (nParamsLen < sizeof(WW8_BRCVer6) * 6)
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+ WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams);
+ for (int i = 0; i < 6; ++i)
+ aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i]));
+ }
+ else if ( nBrcVer == 8 )
+ {
+ static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size");
+ if (nParamsLen < sizeof(WW8_BRC) * 6)
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+ for( int i = 0; i < 6; ++i )
+ aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]);
+ }
+ else
+ {
+ if (nParamsLen < sizeof( aDefBrcs ))
+ {
+ SAL_WARN("sw.ww8", "table border property is too short");
+ return;
+ }
+ memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) );
+ }
+}
+
+void WW8TabBandDesc::ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol)
+{
+ // sprmTDxaCol (opcode 0x7623) changes the width of cells
+ // whose index is within a certain range to be a certain value.
+
+ if( !(nWwCols && pParamsTDxaCol) ) // set one or more cell length(s)
+ return;
+
+ sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed
+ sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1
+ short nDxaCol = static_cast<sal_Int16>(SVBT16ToUInt16( pParamsTDxaCol + 2 ));
+
+ for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ )
+ {
+ const short nOrgWidth = nCenter[i+1] - nCenter[i];
+ const short nDelta = nDxaCol - nOrgWidth;
+ for( int j = i+1; j <= nWwCols; j++ )
+ {
+ nCenter[j] = nCenter[j] + nDelta;
+ }
+ }
+}
+
+void WW8TabBandDesc::ProcessSprmTInsert(const sal_uInt8* pParamsTInsert)
+{
+ if( !nWwCols || !pParamsTInsert ) // set one or more cell length(s)
+ return;
+
+ sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert
+ if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index
+ return;
+ sal_uInt8 nctc = pParamsTInsert[1]; // number of cells
+ sal_uInt16 ndxaCol = SVBT16ToUInt16( pParamsTInsert+2 );
+
+ short nNewWwCols;
+ if (nitcInsert > nWwCols)
+ {
+ nNewWwCols = nitcInsert+nctc;
+ //if new count would be outside max possible count, clip it, and calc a new replacement
+ //legal nctc
+ if (nNewWwCols > MAX_COL)
+ {
+ nNewWwCols = MAX_COL;
+ nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert);
+ }
+ }
+ else
+ {
+ nNewWwCols = nWwCols+nctc;
+ //if new count would be outside max possible count, clip it, and calc a new replacement
+ //legal nctc
+ if (nNewWwCols > MAX_COL)
+ {
+ nNewWwCols = MAX_COL;
+ nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols);
+ }
+ }
+
+ WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols];
+
+ if (pTCs)
+ {
+ memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) );
+ delete[] pTCs;
+ }
+ pTCs = pTC2s;
+
+ //If we have to move some cells
+ if (nitcInsert <= nWwCols)
+ {
+ // adjust the left x-position of the dummy at the very end
+ nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol;
+ for( int i = nWwCols-1; i >= nitcInsert; i--)
+ {
+ // adjust the left x-position
+ nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol;
+
+ // adjust the cell's borders
+ pTCs[i + nctc] = pTCs[i];
+ }
+ }
+
+ //if itcMac is larger than full size, fill in missing ones first
+ for( int i = nWwCols; i > nitcInsert+nWwCols; i--)
+ nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0;
+
+ //now add in our new cells
+ for( int j = 0;j < nctc; j++)
+ nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0;
+
+ nWwCols = nNewWwCols;
+
+}
+
+void WW8TabBandDesc::ProcessDirection(const sal_uInt8* pParams)
+{
+ sal_uInt8 nStartCell = *pParams++;
+ sal_uInt8 nEndCell = *pParams++;
+ sal_uInt16 nCode = SVBT16ToUInt16(pParams);
+
+ OSL_ENSURE(nStartCell < nEndCell, "not as I thought");
+ OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought");
+ if (nStartCell > MAX_COL)
+ return;
+ if (nEndCell > MAX_COL + 1)
+ nEndCell = MAX_COL + 1;
+
+ for (;nStartCell < nEndCell; ++nStartCell)
+ maDirections[nStartCell] = nCode;
+}
+
+void WW8TabBandDesc::ProcessSpacing(const sal_uInt8* pParams)
+{
+ sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
+ OSL_ENSURE(nLen == 6, "Unexpected spacing len");
+ if (nLen != 6)
+ return;
+ mbHasSpacing=true;
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt8 nWhichCell = *pParams;
+ OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!");
+#endif
+ ++pParams; //Skip which cell
+ ++pParams; //unknown byte
+
+ sal_uInt8 nSideBits = *pParams++;
+ OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
+ ++pParams; //unknown byte
+ sal_uInt16 nValue = SVBT16ToUInt16( pParams );
+ for (int i = wwTOP; i <= wwRIGHT; i++)
+ {
+ switch (nSideBits & (1 << i))
+ {
+ case 1 << wwTOP:
+ mnDefaultTop = nValue;
+ break;
+ case 1 << wwLEFT:
+ mnDefaultLeft = nValue;
+ break;
+ case 1 << wwBOTTOM:
+ mnDefaultBottom = nValue;
+ break;
+ case 1 << wwRIGHT:
+ mnDefaultRight = nValue;
+ break;
+ case 0:
+ break;
+ default:
+ OSL_ENSURE(false, "Impossible");
+ break;
+ }
+ }
+}
+
+void WW8TabBandDesc::ProcessSpecificSpacing(const sal_uInt8* pParams)
+{
+ sal_uInt8 nLen = pParams ? *(pParams - 1) : 0;
+ OSL_ENSURE(nLen == 6, "Unexpected spacing len");
+ if (nLen != 6)
+ return;
+
+ const sal_uInt8 nStartCell = *pParams++; // The first cell these margins could apply to.
+ const sal_uInt8 nEndCell = *pParams++; // The cell that does NOT apply these margins.
+ OSL_ENSURE(nStartCell < MAX_COL + 1, "Cell out of range in spacings");
+ if ( nStartCell >= nEndCell || nEndCell > MAX_COL+1 )
+ return;
+
+ sal_uInt8 nSideBits = *pParams++;
+ OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits");
+
+ const sal_uInt8 nSizeType = *pParams++; // Fts: FtsDxa(0x3) is the only type that mentions cellMargin
+ OSL_ENSURE(nSizeType == 0x3, "Unexpected non-twip value for margin width");
+ if ( nSizeType != 0x3 ) // i.e FtsNil: The size is wrong (or unconverted) and MUST be ignored
+ return;
+
+ sal_uInt16 nValue = SVBT16ToUInt16( pParams );
+
+ for (int nCell = nStartCell; nCell < nEndCell; ++nCell)
+ {
+ nOverrideSpacing[ nCell ] |= nSideBits;
+ OSL_ENSURE(nOverrideSpacing[ nCell ] < 0x10, "Unexpected value for nSideBits");
+
+ for (int i=0; i < 4; i++)
+ {
+ if (nSideBits & (1 << i))
+ nOverrideValues[ nCell ][ i ] = nValue;
+ }
+ }
+}
+
+void WW8TabBandDesc::ProcessSprmTDelete(const sal_uInt8* pParamsTDelete)
+{
+ if( !(nWwCols && pParamsTDelete) ) // set one or more cell length(s)
+ return;
+
+ sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted
+ if (nitcFirst >= nWwCols) // first index to delete from doesn't exist
+ return;
+ sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1
+ if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index
+ return;
+
+ /*
+ * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is
+ * greater than or equal to itcLim to be moved
+ */
+ int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted
+
+ if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim
+ {
+ WW8_TCell* pCurrentTC = pTCs + nitcFirst;
+ int i = 0;
+ while( i < nShlCnt )
+ {
+ // adjust the left x-position
+ nCenter[nitcFirst + i] = nCenter[nitcLim + i];
+
+ // adjust the cell's borders
+ *pCurrentTC = pTCs[ nitcLim + i];
+
+ ++i;
+ ++pCurrentTC;
+ }
+ // adjust the left x-position of the dummy at the very end
+ nCenter[nitcFirst + i] = nCenter[nitcLim + i];
+ }
+
+ short nCellsDeleted = nitcLim - nitcFirst;
+ //clip delete request to available number of cells
+ if (nCellsDeleted > nWwCols)
+ nCellsDeleted = nWwCols;
+ nWwCols -= nCellsDeleted;
+}
+
+// ReadShd reads the background color of a cell
+// ReadDef must be called before
+void WW8TabBandDesc::ReadShd(const sal_uInt8* pS )
+{
+ sal_uInt8 nLen = pS ? *(pS - 1) : 0;
+ if( !nLen )
+ return;
+
+ if( !pSHDs )
+ {
+ pSHDs = new WW8_SHD[nWwCols];
+ }
+
+ short nCount = nLen >> 1;
+ if (nCount > nWwCols)
+ nCount = nWwCols;
+
+ SVBT16 const * pShd;
+ int i;
+ for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ )
+ pSHDs[i].SetWWValue( *pShd );
+}
+
+void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67, sal_uInt8 nStart)
+{
+ sal_uInt8 nLen = pS ? *(pS - 1) : 0;
+ if (!nLen || nStart >= nWwCols)
+ return;
+
+ if (!pNewSHDs)
+ pNewSHDs = new Color[nWwCols];
+
+ short nCount = nLen / 10 + nStart; //10 bytes each
+ if (nCount > nWwCols)
+ nCount = nWwCols;
+
+ int i=nStart;
+ while (i < nCount)
+ pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67);
+
+ while (i < nWwCols)
+ pNewSHDs[i++] = COL_AUTO;
+}
+
+namespace
+{
+ SprmResult HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67)
+ {
+ if (bVer67)
+ return pPap->HasSprm(24);
+ SprmResult aRes = pPap->HasSprm(0x244B);
+ if (aRes.pSprm == nullptr)
+ aRes = pPap->HasSprm(0x2416);
+ return aRes;
+ }
+}
+
+namespace {
+
+enum wwTableSprm
+{
+ sprmNil,
+
+ sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi,
+ sprmTDefTable, sprmTDyaRowHeight, sprmTDefTableShd, sprmTDxaLeft,
+ sprmTSetBrc, sprmTSetBrc90, sprmTDxaCol, sprmTInsert, sprmTDelete,
+ sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90,
+ sprmTDefTableNewShd, sprmTDefTableNewShd2nd, sprmTDefTableNewShd3rd,
+ sprmTCellPadding, sprmTCellPaddingDefault
+};
+
+}
+
+static wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer)
+{
+ switch (eVer)
+ {
+ case ww::eWW8:
+ switch (nId)
+ {
+ case NS_sprm::TTableWidth::val:
+ return sprmTTableWidth;
+ case NS_sprm::TTextFlow::val:
+ return sprmTTextFlow;
+ case NS_sprm::TTableHeader::val:
+ return sprmTTableHeader;
+ case NS_sprm::TFCantSplit::val:
+ return sprmTFCantSplit;
+ case NS_sprm::TJc90::val:
+ return sprmTJc;
+ case NS_sprm::TFBiDi::val:
+ return sprmTFBiDi;
+ case NS_sprm::TDelete::val:
+ return sprmTDelete;
+ case NS_sprm::TInsert::val:
+ return sprmTInsert;
+ case NS_sprm::TDxaCol::val:
+ return sprmTDxaCol;
+ case NS_sprm::TDyaRowHeight::val:
+ return sprmTDyaRowHeight;
+ case NS_sprm::TDxaLeft::val:
+ return sprmTDxaLeft;
+ case NS_sprm::TDxaGapHalf::val:
+ return sprmTDxaGapHalf;
+ case NS_sprm::TTableBorders80::val:
+ return sprmTTableBorders;
+ case NS_sprm::TDefTable::val:
+ return sprmTDefTable;
+ case NS_sprm::TDefTableShd80::val:
+ return sprmTDefTableShd;
+ case NS_sprm::TDefTableShd::val:
+ return sprmTDefTableNewShd;
+ case NS_sprm::TDefTableShd2nd::val:
+ return sprmTDefTableNewShd2nd;
+ case NS_sprm::TDefTableShd3rd::val:
+ return sprmTDefTableNewShd3rd;
+ case NS_sprm::TTableBorders::val:
+ return sprmTTableBorders90;
+ case NS_sprm::TSetBrc80::val:
+ return sprmTSetBrc;
+ case NS_sprm::TSetBrc::val:
+ return sprmTSetBrc90;
+ case NS_sprm::TCellPadding::val:
+ return sprmTCellPadding;
+ case NS_sprm::TCellPaddingDefault::val:
+ return sprmTCellPaddingDefault;
+ }
+ break;
+ case ww::eWW7:
+ case ww::eWW6:
+ switch (nId)
+ {
+ case 182:
+ return sprmTJc;
+ case 183:
+ return sprmTDxaLeft;
+ case 184:
+ return sprmTDxaGapHalf;
+ case 186:
+ return sprmTTableHeader;
+ case 187:
+ return sprmTTableBorders;
+ case 189:
+ return sprmTDyaRowHeight;
+ case 190:
+ return sprmTDefTable;
+ case 191:
+ return sprmTDefTableShd;
+ case 193:
+ return sprmTSetBrc;
+ case 194:
+ return sprmTInsert;
+ case 195:
+ return sprmTDelete;
+ case 196:
+ return sprmTDxaCol;
+ }
+ break;
+ case ww::eWW1:
+ case ww::eWW2:
+ switch (nId)
+ {
+ case 146:
+ return sprmTJc;
+ case 147:
+ return sprmTDxaLeft;
+ case 148:
+ return sprmTDxaGapHalf;
+ case 153:
+ return sprmTDyaRowHeight;
+ case 154:
+ return sprmTDefTable;
+ case 155:
+ return sprmTDefTableShd;
+ case 157:
+ return sprmTSetBrc;
+ case 158:
+ return sprmTInsert;
+ case 159:
+ return sprmTDelete;
+ case 160:
+ return sprmTDxaCol;
+ }
+ break;
+ }
+ return sprmNil;
+}
+
+WW8TabDesc::WW8TabDesc(SwWW8ImplReader* pIoClass, WW8_CP nStartCp) :
+ m_pIo(pIoClass),
+ m_pFirstBand(nullptr),
+ m_pActBand(nullptr),
+ m_pTableNd(nullptr),
+ m_pTabLines(nullptr),
+ m_pTabLine(nullptr),
+ m_pTabBoxes(nullptr),
+ m_pTabBox(nullptr),
+ m_pCurrentWWCell(nullptr),
+ m_nRows(0),
+ m_nDefaultSwCols(0),
+ m_nBands(0),
+ m_nMinLeft(0),
+ m_nMaxRight(0),
+ m_nSwWidth(0),
+ m_nPreferredWidth(0),
+ m_nPercentWidth(0),
+ m_bOk(true),
+ m_bClaimLineFormat(false),
+ m_eOri(text::HoriOrientation::LEFT),
+ m_bIsBiDi(false),
+ m_nCurrentRow(0),
+ m_nCurrentBandRow(0),
+ m_nCurrentCol(0),
+ m_nRowsToRepeat(0),
+ m_pTable(nullptr),
+ m_pParentPos(nullptr),
+ m_pFlyFormat(nullptr),
+ m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>)
+{
+ m_pIo->m_bCurrentAND_fNumberAcross = false;
+
+ static const sal_Int16 aOriArr[] =
+ {
+ text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER
+ };
+
+ bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion());
+ WW8_TablePos aTabPos;
+
+ WW8PLCFxSave1 aSave;
+ m_pIo->m_xPlcxMan->GetPap()->Save( aSave );
+
+ WW8PLCFx_Cp_FKP* pPap = m_pIo->m_xPlcxMan->GetPapPLCF();
+
+ WW8TabBandDesc* pNewBand = new WW8TabBandDesc;
+
+ wwSprmParser aSprmParser(m_pIo->GetFib());
+
+ std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes;
+
+ // process pPap until end of table found
+ do
+ {
+ short nTabeDxaNew = SHRT_MAX;
+ bool bTabRowJustRead = false;
+ const sal_uInt8* pShadeSprm = nullptr;
+ const sal_uInt8* pNewShadeSprm[3] = {nullptr, nullptr, nullptr};
+ const sal_uInt8* pTableBorders = nullptr;
+ sal_uInt16 nTableBordersLen = 0;
+ const sal_uInt8* pTableBorders90 = nullptr;
+ sal_uInt16 nTableBorders90Len = 0;
+ // params, len
+ std::vector<std::pair<const sal_uInt8*, sal_uInt16>> aTSetBrcs, aTSetBrc90s;
+ WW8_TablePos *pTabPos = nullptr;
+
+ // search end of a tab row
+ if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable)))
+ {
+ m_bOk = false;
+ break;
+ }
+
+ // Get the SPRM chains:
+ // first from PAP and then from PCD (of the Piece Table)
+ WW8PLCFxDesc aDesc;
+ pPap->GetSprms( &aDesc );
+ WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser);
+
+ for (int nLoop = 0; nLoop < 2; ++nLoop)
+ {
+ const sal_uInt8* pParams;
+ while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetCurrentParams()))
+ {
+ sal_uInt16 nId = aSprmIter.GetCurrentId();
+ sal_Int32 nFixedLen = aSprmParser.DistanceToData(nId);
+ sal_Int32 nL = aSprmParser.GetSprmSize(nId, aSprmIter.GetSprms(), aSprmIter.GetRemLen());
+ sal_Int32 nLen = nL - nFixedLen;
+ wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion());
+ switch (eSprm)
+ {
+ case sprmTTableWidth:
+ {
+ const sal_uInt8 b0 = pParams[0];
+ const sal_uInt8 b1 = pParams[1];
+ const sal_uInt8 b2 = pParams[2];
+ if (b0 == 3) // Twips
+ m_nPreferredWidth = b2 * 0x100 + b1;
+ else if (b0 == 2) // percent in fiftieths of a percent
+ {
+ m_nPercentWidth = (b2 * 0x100 + b1);
+ // MS documentation: non-negative, and 600% max
+ if ( m_nPercentWidth >= 0 && m_nPercentWidth <= 30000 )
+ m_nPercentWidth *= .02;
+ else
+ m_nPercentWidth = 100;
+ }
+ }
+ break;
+ case sprmTTextFlow:
+ pNewBand->ProcessDirection(pParams);
+ break;
+ case sprmTFCantSplit:
+ pNewBand->bCantSplit = *pParams;
+ m_bClaimLineFormat = true;
+ break;
+ case sprmTTableBorders:
+ pTableBorders = pParams; // process at end
+ nTableBordersLen = nLen;
+ break;
+ case sprmTTableBorders90:
+ pTableBorders90 = pParams; // process at end
+ nTableBorders90Len = nLen;
+ break;
+ case sprmTTableHeader:
+ // tdf#105570
+ if ( m_nRowsToRepeat == m_nRows )
+ m_nRowsToRepeat = (m_nRows + 1);
+ break;
+ case sprmTJc:
+ // sprmTJc - Justification Code
+ if (m_nRows == 0)
+ m_eOri = aOriArr[*pParams & 0x3];
+ break;
+ case sprmTFBiDi:
+ m_bIsBiDi = SVBT16ToUInt16(pParams) != 0;
+ break;
+ case sprmTDxaGapHalf:
+ pNewBand->nGapHalf = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
+ break;
+ case sprmTDyaRowHeight:
+ pNewBand->nLineHeight = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
+ m_bClaimLineFormat = true;
+ break;
+ case sprmTDefTable:
+ pNewBand->ReadDef(bOldVer, pParams, nLen);
+ bTabRowJustRead = true;
+ break;
+ case sprmTDefTableShd:
+ pShadeSprm = pParams;
+ break;
+ case sprmTDefTableNewShd:
+ pNewShadeSprm[0] = pParams;
+ break;
+ case sprmTDefTableNewShd2nd:
+ pNewShadeSprm[1] = pParams;
+ break;
+ case sprmTDefTableNewShd3rd:
+ pNewShadeSprm[2] = pParams;
+ break;
+ case sprmTDxaLeft:
+ // our Writer cannot shift single table lines
+ // horizontally so we have to find the smallest
+ // parameter (meaning the left-most position) and then
+ // shift the whole table to that margin (see below)
+ {
+ short nDxaNew = static_cast<sal_Int16>(SVBT16ToUInt16( pParams ));
+ if( nDxaNew < nTabeDxaNew )
+ nTabeDxaNew = nDxaNew;
+ }
+ break;
+ case sprmTSetBrc:
+ aTSetBrcs.emplace_back(pParams, nLen); // process at end
+ break;
+ case sprmTSetBrc90:
+ aTSetBrc90s.emplace_back(pParams, nLen); // process at end
+ break;
+ case sprmTDxaCol:
+ pNewBand->ProcessSprmTDxaCol(pParams);
+ break;
+ case sprmTInsert:
+ pNewBand->ProcessSprmTInsert(pParams);
+ break;
+ case sprmTDelete:
+ pNewBand->ProcessSprmTDelete(pParams);
+ break;
+ case sprmTCellPaddingDefault:
+ pNewBand->ProcessSpacing(pParams);
+ break;
+ case sprmTCellPadding:
+ pNewBand->ProcessSpecificSpacing(pParams);
+ break;
+ default:
+ ;
+ }
+ aSprmIter.advance();
+ }
+
+ if( !nLoop )
+ {
+ pPap->GetPCDSprms( aDesc );
+ aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen );
+ }
+ }
+
+ // WW-Tables can contain Fly-changes. For this abort tables here
+ // and start again. *pPap is still before TabRowEnd, so TestApo()
+ // can be called with the last parameter set to false and therefore
+ // take effect.
+
+ if (bTabRowJustRead)
+ {
+ // Some SPRMs need to be processed *after* ReadDef is called
+ // so they were saved up until here
+ if (pShadeSprm)
+ pNewBand->ReadShd(pShadeSprm);
+ if (pNewShadeSprm[0])
+ pNewBand->ReadNewShd(pNewShadeSprm[0], bOldVer, /*nStart=*/0);
+ if (pNewShadeSprm[1])
+ pNewBand->ReadNewShd(pNewShadeSprm[1], bOldVer, /*nStart=*/22);
+ if (pNewShadeSprm[2])
+ pNewBand->ReadNewShd(pNewShadeSprm[2], bOldVer, /*nStart=*/44);
+ if (pTableBorders90)
+ pNewBand->ProcessSprmTTableBorders(9, pTableBorders90, nTableBorders90Len);
+ else if (pTableBorders)
+ pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8,
+ pTableBorders, nTableBordersLen);
+ for (const auto& a : aTSetBrcs)
+ pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, a.first, a.second);
+ for (const auto& a : aTSetBrc90s)
+ pNewBand->ProcessSprmTSetBRC(9, a.first, a.second);
+ }
+
+ if( nTabeDxaNew < SHRT_MAX )
+ {
+ short* pCenter = pNewBand->nCenter;
+ short firstDxaCenter = *pCenter;
+ for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter )
+ {
+ // #i30298# Use sprmTDxaLeft to adjust the left indent
+ // #i40461# Use dxaGapHalf during calculation
+ *pCenter +=
+ (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf));
+ }
+ }
+
+ if (!m_pActBand)
+ m_pActBand = m_pFirstBand = pNewBand;
+ else
+ {
+ m_pActBand->pNextBand = pNewBand;
+ m_pActBand = pNewBand;
+ }
+ m_nBands++;
+
+ pNewBand = new WW8TabBandDesc;
+
+ m_nRows++;
+ m_pActBand->nRows++;
+
+ //Seek our pap to its next block of properties
+ WW8PLCFxDesc aRes;
+ aRes.pMemPos = nullptr;
+ aRes.nStartPos = nStartCp;
+
+ if (!(pPap->SeekPos(aRes.nStartPos)))
+ {
+ aRes.nEndPos = WW8_CP_MAX;
+ pPap->SetDirty(true);
+ }
+ pPap->GetSprms(&aRes);
+ pPap->SetDirty(false);
+
+ //Are we at the end of available properties
+ if (
+ !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX ||
+ aRes.nStartPos == WW8_CP_MAX
+ )
+ {
+ m_bOk = false;
+ break;
+ }
+
+ //Are we still in a table cell
+ SprmResult aParamsRes = HasTabCellSprm(pPap, bOldVer);
+ const sal_uInt8* pParams = aParamsRes.pSprm;
+ SprmResult aLevelRes = pPap->HasSprm(0x6649);
+ const sal_uInt8 *pLevel = aLevelRes.pSprm;
+ // InTable
+ if (!pParams || aParamsRes.nRemainingData < 1 || (1 != *pParams) ||
+ (pLevel && aLevelRes.nRemainingData >= 1 && (*pLevel <= m_pIo->m_nInTable)))
+ {
+ break;
+ }
+
+ //Get the end of row new table positioning data
+ WW8_CP nMyStartCp=nStartCp;
+ if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable))
+ if (SwWW8ImplReader::ParseTabPos(&aTabPos, pPap))
+ pTabPos = &aTabPos;
+
+ //Move back to this cell
+ aRes.pMemPos = nullptr;
+ aRes.nStartPos = nStartCp;
+
+ // PlcxMan currently points too far ahead so we need to bring
+ // it back to where we are trying to make a table
+ m_pIo->m_xPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos;
+ m_pIo->m_xPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs;
+ if (!(pPap->SeekPos(aRes.nStartPos)))
+ {
+ aRes.nEndPos = WW8_CP_MAX;
+ pPap->SetDirty(true);
+ }
+ pPap->GetSprms(&aRes);
+ pPap->SetDirty(false);
+
+ //Does this row match up with the last row closely enough to be
+ //considered part of the same table
+ ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos);
+
+ /*
+ ##513##, #79474# If this is not sufficient, then we should look at
+ sprmPD{y|x}aAbs as our indicator that the following set of rows is not
+ part of this table, but instead is an absolutely positioned table
+ outside of this one
+ */
+ if (aApo.mbStopApo)
+ break;
+ if (aApo.mbStartApo)
+ {
+ //if there really is a fly here, and not a "null" fly then break.
+ if (m_pIo->ConstructApo(aApo, pTabPos))
+ break;
+ }
+
+ auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos));
+ if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop
+ {
+ SAL_WARN("sw.ww8", "WW8TabDesc, loop in paragraph property chain");
+ break;
+ }
+ nStartCp = aRes.nEndPos;
+ }
+ while(true);
+
+ if( m_bOk )
+ {
+ if( m_pActBand->nRows > 1 )
+ {
+ // last band has more than 1 cell
+ delete pNewBand;
+ pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new
+ m_pActBand->nRows--; // because of special treatment of border defaults
+ pNewBand->nRows = 1;
+ m_pActBand->pNextBand = pNewBand; // append at the end
+ m_nBands++;
+ pNewBand = nullptr; // do not delete
+ }
+ CalcDefaults();
+ }
+ delete pNewBand;
+
+ m_pIo->m_xPlcxMan->GetPap()->Restore( aSave );
+}
+
+WW8TabDesc::~WW8TabDesc()
+{
+ WW8TabBandDesc* pR = m_pFirstBand;
+ while(pR)
+ {
+ WW8TabBandDesc* pR2 = pR->pNextBand;
+ delete pR;
+ pR = pR2;
+ }
+
+ delete m_pParentPos;
+}
+
+void WW8TabDesc::CalcDefaults()
+{
+ short nMinCols = SHRT_MAX;
+ WW8TabBandDesc* pR;
+
+ m_nMinLeft = SHRT_MAX;
+ m_nMaxRight = SHRT_MIN;
+
+ /*
+ If we are an honestly inline centered table, then the normal rules of
+ engagement for left and right margins do not apply. The multiple rows are
+ centered regardless of the actual placement of rows, so we cannot have
+ mismatched rows as is possible in other configurations.
+
+ e.g. change the example bugdoc in word from text wrapping of none (inline)
+ to around (in frame (bApo)) and the table splits into two very disjoint
+ rows as the beginning point of each row are very different
+ */
+ if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER))
+ {
+ for (pR = m_pFirstBand; pR; pR = pR->pNextBand)
+ for( short i = pR->nWwCols; i >= 0; --i)
+ pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0];
+ }
+
+ // First loop: find outermost L and R borders
+ for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
+ {
+ if( pR->nCenter[0] < m_nMinLeft )
+ m_nMinLeft = pR->nCenter[0];
+
+ // Following adjustment moves a border and then uses it to find width
+ // of next cell, so collect current widths, to avoid situation when width
+ // adjustment to too narrow cell makes next cell have negative width
+ short nOrigWidth[MAX_COL + 1];
+ for( short i = 0; i < pR->nWwCols; i++ )
+ {
+ nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i];
+ }
+
+ for( short i = 0; i < pR->nWwCols; i++ )
+ {
+ /*
+ If the margins are so large as to make the displayable
+ area inside them smaller than the minimum allowed then adjust the
+ width to fit. But only do it if the two cells are not the exact
+ same value, if they are then the cell does not really exist and will
+ be blended together into the same cell through the use of the
+ nTrans(late) array.
+ #i28333# If the nGapHalf is greater than the cell width best to ignore it
+ */
+ int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i];
+ if (nCellWidth != nOrigWidth[i])
+ {
+ if (nOrigWidth[i] == 0)
+ nCellWidth = 0; // restore zero-width "cell"
+ else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i]))
+ nCellWidth = pR->nGapHalf + 1; // avoid false ignore
+ else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0))
+ nCellWidth = 1; // minimal non-zero width to minimize distortion
+ }
+ if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth)
+ {
+ nCellWidth = MINLAY + pR->nGapHalf * 2;
+ }
+ pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth;
+ }
+
+ if( pR->nCenter[pR->nWwCols] > m_nMaxRight )
+ m_nMaxRight = pR->nCenter[pR->nWwCols];
+ }
+ m_nSwWidth = m_nMaxRight - m_nMinLeft;
+
+ // If the table is right aligned we need to align all rows to the
+ // row that has the furthest right point
+
+ if(m_eOri == text::HoriOrientation::RIGHT)
+ {
+ for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
+ {
+ int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols];
+ for( short i = 0; i < pR->nWwCols + 1; i++ )
+ {
+ pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust);
+ }
+
+ }
+ }
+
+ // 2. pass: Detect number of writer columns. This can exceed the count
+ // of columns in WW by 2, because SW in contrast to WW does not provide
+ // fringed left and right borders and has to fill with empty boxes.
+ // Non existent cells can reduce the number of columns.
+
+ // 3. pass: Replace border with defaults if needed
+ for( pR = m_pFirstBand ; pR; pR = pR->pNextBand )
+ {
+ if( !pR->pTCs )
+ {
+ pR->pTCs = new WW8_TCell[ pR->nWwCols ];
+ }
+ for (int k = 0; k < pR->nWwCols; ++k)
+ {
+ WW8_TCell& rT = pR->pTCs[k];
+ for (int i = 0; i < 4; ++i)
+ {
+ if (rT.rgbrc[i].brcType()==0)
+ {
+ // if shadow is set, its invalid
+ int j = i;
+ switch( i )
+ {
+ case 0:
+ // outer top / horizontally inside
+ j = (pR == m_pFirstBand) ? 0 : 4;
+ break;
+ case 1:
+ // outer left / vertically inside
+ j = k ? 5 : 1;
+ break;
+ case 2:
+ // outer bottom / horizontally inside
+ j = pR->pNextBand ? 4 : 2;
+ break;
+ case 3:
+ // outer right / vertically inside
+ j = (k == pR->nWwCols - 1) ? 3 : 5;
+ break;
+ }
+ // merge with above defaults
+ rT.rgbrc[i] = pR->aDefBrcs[j];
+ }
+ }
+ }
+ }
+
+ for( pR = m_pFirstBand; pR; pR = pR->pNextBand )
+ {
+ pR->nSwCols = pR->nWwCols;
+ pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY;
+ pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols]) >= MINLAY;
+
+ short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol);
+ sal_uInt16 i;
+ sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0;
+ for (i = 0; i < pR->nWwCols; ++i)
+ {
+ pR->nTransCell[i] = static_cast<sal_Int8>(j);
+ if ( pR->nCenter[i] < pR->nCenter[i+1] )
+ {
+ pR->bExist[i] = true;
+ j++;
+ }
+ else
+ {
+ pR->bExist[i] = false;
+ nAddCols--;
+ }
+ }
+
+ OSL_ENSURE(i,"no columns in row ?");
+
+ /*
+ If the last cell was "false" then there is no valid cell following it,
+ so the default mapping forward won't work. So map it (and
+ contiguous invalid cells backwards to the last valid cell instead.)
+ */
+ if (i && !pR->bExist[i-1])
+ {
+ sal_uInt16 k=i-1;
+ while (k && !pR->bExist[k])
+ k--;
+ for (sal_uInt16 n=k+1;n<i;n++)
+ pR->nTransCell[n] = pR->nTransCell[k];
+ }
+
+ pR->nTransCell[i++] = static_cast<sal_Int8>(j++); // Can exceed by 2 among other
+ pR->nTransCell[i] = static_cast<sal_Int8>(j); // things because of bREmptyCol
+
+ pR->nSwCols = pR->nSwCols + nAddCols;
+ if( pR->nSwCols < nMinCols )
+ nMinCols = pR->nSwCols;
+ }
+
+ if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) ||
+ (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used
+ m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned
+
+ m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging
+ if( m_nDefaultSwCols == 0 )
+ m_bOk = false;
+ m_pActBand = m_pFirstBand;
+ m_nCurrentBandRow = 0;
+ OSL_ENSURE( m_pActBand, "pActBand is 0" );
+}
+
+void WW8TabDesc::SetSizePosition(SwFrameFormat* pFrameFormat)
+{
+ SwFrameFormat* pApply = pFrameFormat;
+ if (!pApply )
+ pApply = m_pTable->GetFrameFormat();
+ OSL_ENSURE(pApply,"No frame");
+ pApply->SetFormatAttr(m_aItemSet);
+ if (pFrameFormat)
+ {
+ SwFormatFrameSize aSize = pFrameFormat->GetFrameSize();
+ aSize.SetHeightSizeType(SwFrameSize::Minimum);
+ aSize.SetHeight(MINLAY);
+ pFrameFormat->SetFormatAttr(aSize);
+ m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL));
+ }
+}
+
+void wwSectionManager::PrependedInlineNode(const SwPosition &rPos,
+ const SwNode &rNode)
+{
+ OSL_ENSURE(!maSegments.empty(),
+ "should not be possible, must be at least one segment");
+ if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.GetNode()))
+ maSegments.back().maStart.Assign(rNode);
+}
+
+void WW8TabDesc::CreateSwTable()
+{
+ ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
+
+ // if there is already some content on the Node append new node to ensure
+ // that this content remains ABOVE the table
+ SwPosition* pPoint = m_pIo->m_pPaM->GetPoint();
+ bool bInsNode = pPoint->GetContentIndex() != 0;
+ bool bSetMinHeight = false;
+
+ /*
+ #i8062#
+ Set fly anchor to its anchor pos, so that if a table starts immediately
+ at this position a new node will be inserted before inserting the table.
+ */
+ SwFrameFormat* pFormat = (!bInsNode && m_pIo->m_xFormatOfJustInsertedApo)
+ ? m_pIo->m_xFormatOfJustInsertedApo->GetFormat() : nullptr;
+ if (pFormat)
+ {
+ const SwNode* pAnchorNode =
+ pFormat->GetAnchor().GetAnchorNode();
+ if (pAnchorNode && *pAnchorNode == pPoint->GetNode())
+ {
+ bInsNode = true;
+ bSetMinHeight = true;
+
+ SwFormatSurround aSur(pFormat->GetSurround());
+ aSur.SetAnchorOnly(true);
+ pFormat->SetFormatAttr(aSur);
+ }
+ }
+
+ if (bSetMinHeight)
+ {
+ // minimize Fontsize to minimize height growth of the header/footer
+ // set font size to 1 point to minimize y-growth of Hd/Ft
+ SvxFontHeightItem aSz(20, 100, RES_CHRATR_FONTSIZE);
+ m_pIo->NewAttr( aSz );
+ m_pIo->m_xCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE);
+ }
+
+ if (bInsNode)
+ m_pIo->AppendTextNode(*pPoint);
+
+ m_xTmpPos = m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_pPaM->GetPoint());
+
+ // Because SW cannot handle multi-page floating frames,
+ // _any unnecessary_ floating tables have been converted to inline.
+ tools::Long nLeft = 0;
+ if (m_pIo->m_xSFlyPara && !m_pIo->m_xSFlyPara->GetFlyFormat())
+ {
+ // Get the table orientation from the fly
+ // Do we also need to check m_pIo->m_xSFlyPara->bTogglePos/IsPosToggle()? [Probably not - layout-only concern]
+ const bool bAdjustMargin = m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME || m_pIo->m_xSFlyPara->nXPos;
+ const bool bIsInsideMargin = m_bIsBiDi ? m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::RIGHT
+ : m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::LEFT;
+ if ( bIsInsideMargin && bAdjustMargin )
+ m_eOri = text::HoriOrientation::LEFT_AND_WIDTH;
+ else if ( m_pIo->m_xSFlyPara->eHAlign != text::HoriOrientation::NONE )
+ m_eOri = m_pIo->m_xSFlyPara->eHAlign;
+ if ( m_eOri == text::HoriOrientation::LEFT_AND_WIDTH )
+ {
+ nLeft = m_pIo->m_xSFlyPara->nXPos;
+ if ( m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME )
+ {
+ if ( !m_bIsBiDi )
+ nLeft -= m_pIo->m_aSectionManager.GetPageLeft();
+ else
+ nLeft += m_pIo->m_aSectionManager.GetPageRight();
+ }
+ }
+ }
+
+ // The table is small: The number of columns is the lowest count of
+ // columns of the origin, because inserting is faster than deleting.
+ // The number of rows is the count of bands because (identically)
+ // rows of a band can be duplicated easy.
+ m_pTable = m_pIo->m_rDoc.InsertTable(
+ SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 0 ),
+ *m_xTmpPos->GetPoint(), m_nBands, m_nDefaultSwCols, m_eOri );
+
+ OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed");
+ if (!m_pTable || !m_pTable->GetFrameFormat())
+ return;
+
+ SwTableNode* pTableNode = m_pTable->GetTableNode();
+ OSL_ENSURE(pTableNode, "no table node!");
+ if (pTableNode)
+ {
+ m_pIo->m_aSectionManager.PrependedInlineNode(*m_pIo->m_pPaM->GetPoint(),
+ *pTableNode);
+ }
+
+ // Check if the node into which the table should be inserted already
+ // contains a Pagedesc. If so that Pagedesc would be moved to the
+ // row after the table, that would be wrong. So delete and
+ // set later to the table format.
+ if (SwTextNode *const pNd = m_xTmpPos->GetPoint()->GetNode().GetTextNode())
+ {
+ if (const SfxItemSet* pSet = pNd->GetpSwAttrSet())
+ {
+ std::unique_ptr<SfxPoolItem> pSetAttr;
+
+ if (const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet(RES_BREAK, false))
+ {
+ pSetAttr.reset(new SvxFormatBreakItem( *pBreakItem ));
+ pNd->ResetAttr( RES_BREAK );
+ }
+
+ // eventually set the PageDesc/Break now to the table
+ if (pSetAttr)
+ {
+ m_aItemSet.Put(std::move(pSetAttr));
+ }
+ }
+ }
+
+ // total width of table
+ if( m_nMaxRight - m_nMinLeft > MINLAY * m_nDefaultSwCols )
+ {
+ SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, m_nSwWidth);
+ // Don't set relative width if the table is in a floating frame
+ if ( m_nPercentWidth && (!m_pIo->m_xSFlyPara || !m_pIo->m_xSFlyPara->GetFlyFormat()) )
+ aFrameSize.SetWidthPercent(m_nPercentWidth);
+ m_pTable->GetFrameFormat()->SetFormatAttr(aFrameSize);
+ m_aItemSet.Put(aFrameSize);
+ }
+
+ SvxFrameDirectionItem aDirection(
+ m_bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
+ m_pTable->GetFrameFormat()->SetFormatAttr(aDirection);
+
+ if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri)
+ {
+ if (!m_pIo->m_nInTable && m_pIo->InLocalApo() && m_pIo->m_xSFlyPara &&
+ m_pIo->m_xSFlyPara->GetFlyFormat() && GetMinLeft())
+ {
+ //If we are inside a frame and we have a border, the frames
+ //placement does not consider the tables border, which word
+ //displays outside the frame, so adjust here.
+ SwFormatHoriOrient aHori(m_pIo->m_xSFlyPara->GetFlyFormat()->GetHoriOrient());
+ sal_Int16 eHori = aHori.GetHoriOrient();
+ if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) ||
+ (eHori == text::HoriOrientation::LEFT_AND_WIDTH))
+ {
+ //With multiple table, use last table settings. Perhaps
+ //the maximum is what word does ?
+ aHori.SetPos(m_pIo->m_xSFlyPara->nXPos + GetMinLeft());
+ aHori.SetHoriOrient(text::HoriOrientation::NONE);
+ m_pIo->m_xSFlyPara->GetFlyFormat()->SetFormatAttr(aHori);
+ }
+ }
+ else // Not directly in a floating frame.
+ {
+ //Historical note: If InLocalApo(), then this table is being placed in a floating
+ //frame, and the frame matches the left and right *lines* of the
+ //table, so the space to the left of the table isn't to be used
+ //inside the frame, in word the dialog involved greys out the
+ //ability to set the margin.
+ SvxLRSpaceItem aL( RES_LR_SPACE );
+
+ if (!m_bIsBiDi)
+ nLeft += GetMinLeft();
+ else
+ {
+ const short nTableWidth = m_nPreferredWidth ? m_nPreferredWidth : m_nSwWidth;
+ nLeft += m_pIo->m_aSectionManager.GetTextAreaWidth();
+ nLeft = nLeft - nTableWidth - GetMinLeft();
+ }
+ aL.SetLeft(nLeft);
+
+ m_aItemSet.Put(aL);
+ }
+ }
+
+ mxOldRedlineStack = std::move(m_pIo->m_xRedlineStack);
+ m_pIo->m_xRedlineStack.reset(new sw::util::RedlineStack(m_pIo->m_rDoc));
+}
+
+void WW8TabDesc::UseSwTable()
+{
+ // init global Vars
+ m_pTabLines = &m_pTable->GetTabLines();
+ m_nCurrentRow = m_nCurrentCol = m_nCurrentBandRow = 0;
+
+ m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]->
+ GetSttNd()->FindTableNode());
+ OSL_ENSURE( m_pTableNd, "Where is my table node" );
+
+ // #i69519# - Restrict rows to repeat to a decent value
+ if ( m_nRowsToRepeat == o3tl::narrowing<sal_uInt16>(m_nRows) )
+ m_nRowsToRepeat = 1;
+
+ m_pTableNd->GetTable().SetRowsToRepeat( m_nRowsToRepeat );
+ // insert extra cells if needed and something like this
+ AdjustNewBand();
+
+ WW8DupProperties aDup(m_pIo->m_rDoc, m_pIo->m_xCtrlStck.get());
+ m_pIo->m_xCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false);
+
+ // now set the correct PaM and prepare first merger group if any
+ SetPamInCell(m_nCurrentCol, true);
+ aDup.Insert(*m_pIo->m_pPaM->GetPoint());
+
+ m_pIo->m_bWasTabRowEnd = false;
+ m_pIo->m_bWasTabCellEnd = false;
+}
+
+void WW8TabDesc::MergeCells()
+{
+ short nRow;
+
+ for (m_pActBand=m_pFirstBand, nRow=0; m_pActBand; m_pActBand=m_pActBand->pNextBand)
+ {
+ // insert current box into merge group if appropriate.
+ // The algorithm must ensure proper row and column order in WW8SelBoxInfo!
+ if( m_pActBand->pTCs )
+ {
+ for( short j = 0; j < m_pActBand->nRows; j++, nRow++ )
+ for( short i = 0; i < m_pActBand->nWwCols; i++ )
+ {
+ WW8SelBoxInfo* pActMGroup = nullptr;
+
+ // start a new merge group if appropriate
+
+ OSL_ENSURE(nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()),
+ "Too few lines, table ended early");
+ if (nRow >= o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
+ return;
+ m_pTabLine = (*m_pTabLines)[ nRow ];
+ m_pTabBoxes = &m_pTabLine->GetTabBoxes();
+
+ sal_uInt16 nCol = m_pActBand->nTransCell[ i ];
+ if (!m_pActBand->bExist[i])
+ continue;
+ OSL_ENSURE(nCol < m_pTabBoxes->size(),
+ "Too few columns, table ended early");
+ if (nCol >= m_pTabBoxes->size())
+ return;
+ m_pTabBox = (*m_pTabBoxes)[nCol];
+ WW8_TCell& rCell = m_pActBand->pTCs[ i ];
+ // is this the left upper cell of a merge group ?
+
+ bool bMerge = false;
+ if ( rCell.bVertRestart && !rCell.bMerged )
+ bMerge = true;
+ else if (rCell.bFirstMerged && m_pActBand->bExist[i])
+ {
+ // Some tests to avoid merging cells which previously were
+ // declared invalid because of sharing the exact same dimensions
+ // as their previous cell
+
+ //If there's anything underneath/above we're ok.
+ if (rCell.bVertMerge || rCell.bVertRestart)
+ bMerge = true;
+ else
+ {
+ //If it's a hori merge only, and the only things in
+ //it are invalid cells then it's already taken care
+ //of, so don't merge.
+ for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
+ if (m_pActBand->pTCs[ i2 ].bMerged &&
+ !m_pActBand->pTCs[ i2 ].bFirstMerged )
+ {
+ if (m_pActBand->bExist[i2])
+ {
+ bMerge = true;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+
+ // remove numbering from cells that will be disabled in the merge
+ if( rCell.bVertMerge && !rCell.bVertRestart )
+ {
+ SwPaM aPam( *m_pTabBox->GetSttNd(), 0 );
+ aPam.GetPoint()->Adjust(SwNodeOffset(1));
+ SwTextNode* pNd = aPam.GetPointNode().GetTextNode();
+ while( pNd )
+ {
+ pNd->SetCountedInList( false );
+
+ aPam.GetPoint()->Adjust(SwNodeOffset(1));
+ pNd = aPam.GetPointNode().GetTextNode();
+ }
+ }
+
+ if (bMerge)
+ {
+ short nX1 = m_pActBand->nCenter[ i ];
+ short nWidth = m_pActBand->nWidth[ i ];
+
+ // 2. create current merge group
+ pActMGroup = new WW8SelBoxInfo( nX1, nWidth );
+
+ // determine size of new merge group
+ // before inserted the new merge group.
+ // Needed to correctly locked previously created merge groups.
+ // Calculate total width and set
+ short nSizCell = m_pActBand->nWidth[ i ];
+ for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ )
+ if (m_pActBand->pTCs[ i2 ].bMerged &&
+ !m_pActBand->pTCs[ i2 ].bFirstMerged )
+ {
+ nSizCell = nSizCell + m_pActBand->nWidth[ i2 ];
+ }
+ else
+ break;
+ pActMGroup->m_nGroupWidth = nSizCell;
+
+ // locked previously created merge groups,
+ // after determining the size for the new merge group.
+ // 1. If necessary close old merge group(s) that overlap
+ // the X-area of the new group
+ for (;;)
+ {
+ WW8SelBoxInfo* p = FindMergeGroup(
+ nX1, pActMGroup->m_nGroupWidth, false );
+ if (p == nullptr)
+ {
+ break;
+ }
+ p->m_bGroupLocked = true;
+ }
+
+ // 3. push to group array
+ m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup));
+ }
+
+ // if necessary add the current box to a merge group
+ // (that can be a newly created or another group)
+ UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i );
+ }
+ }
+ }
+}
+
+//There is a limbo area in word at the end of the row marker
+//where properties can live in word, there is no location in
+//writer equivalent, so try and park the cursor in the best
+//match, see #i23022#/#i18644#
+void WW8TabDesc::ParkPaM()
+{
+ SwTableBox *pTabBox2 = nullptr;
+ short nRow = m_nCurrentRow + 1;
+ if (nRow < o3tl::narrowing<sal_uInt16>(m_pTabLines->size()))
+ {
+ if (SwTableLine *pLine = (*m_pTabLines)[nRow])
+ {
+ SwTableBoxes &rBoxes = pLine->GetTabBoxes();
+ pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front();
+ }
+ }
+
+ if (!pTabBox2 || !pTabBox2->GetSttNd())
+ {
+ MoveOutsideTable();
+ return;
+ }
+
+ SwNodeOffset nSttNd = pTabBox2->GetSttIdx() + 1,
+ nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex();
+
+ if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
+ {
+ do
+ {
+ m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
+ }
+ while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
+
+ m_pIo->m_pPaM->GetPoint()->SetContent(0);
+ m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
+ }
+}
+
+void WW8TabDesc::MoveOutsideTable()
+{
+ OSL_ENSURE(m_xTmpPos && m_pIo, "I've forgotten where the table is anchored");
+ if (m_xTmpPos && m_pIo)
+ *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos->GetPoint();
+}
+
+void WW8TabDesc::FinishSwTable()
+{
+ m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint());
+
+ // ofz#38011 drop m_pLastAnchorPos during RedlineStack dtor and restore it afterwards to the same
+ // place, or somewhere close if that place got destroyed
+ std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pIo->m_oLastAnchorPos ? m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_oLastAnchorPos) : nullptr);
+ m_pIo->m_oLastAnchorPos.reset();
+
+ SwTableNode* pTableNode = m_pTable->GetTableNode();
+ SwDeleteListener aListener(*pTableNode);
+ m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack);
+
+ if (xLastAnchorCursor)
+ m_pIo->m_oLastAnchorPos.emplace(*xLastAnchorCursor->GetPoint());
+
+ WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_xCtrlStck.get());
+ m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false);
+
+ MoveOutsideTable();
+ m_xTmpPos.reset();
+
+ aDup.Insert(*m_pIo->m_pPaM->GetPoint());
+
+ m_pIo->m_bWasTabRowEnd = false;
+ m_pIo->m_bWasTabCellEnd = false;
+
+ m_pIo->m_aInsertedTables.InsertTable(*m_pTableNd, *m_pIo->m_pPaM);
+
+ if (aListener.WasDeleted())
+ throw std::runtime_error("table unexpectedly destroyed by applying redlines");
+
+ MergeCells();
+
+ // if needed group cells together that should be merged
+ if (m_MergeGroups.empty())
+ return;
+
+ // process all merge groups one by one
+ for (auto const& groupIt : m_MergeGroups)
+ {
+ if((1 < groupIt->size()) && groupIt->row(0)[0])
+ {
+ SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat();
+ pNewFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, groupIt->m_nGroupWidth, 0));
+ const sal_uInt16 nRowSpan = groupIt->rowsCount();
+ for (sal_uInt16 n = 0; n < nRowSpan; ++n)
+ {
+ auto& rRow = groupIt->row(n);
+ for (size_t i = 0; i<rRow.size(); ++i)
+ {
+ const sal_Int32 nRowSpanSet = (n == 0) && (i == 0) ?
+ nRowSpan :
+ (-1 * (nRowSpan - n));
+ SwTableBox* pCurrentBox = rRow[i];
+ pCurrentBox->setRowSpan(nRowSpanSet);
+
+ if (i == 0)
+ pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat));
+ else
+ {
+ SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat();
+ pFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, 0, 0));
+ }
+ }
+ }
+ }
+ }
+ m_pIo->m_xFormatOfJustInsertedApo.reset();
+ m_MergeGroups.clear();
+}
+
+// browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise
+
+// Parameter: nXcenter = center position of asking box
+// nWidth = width of asking box
+// bExact = flag, if box has to fit into group
+// or only has to touch
+
+WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact)
+{
+ if (!m_MergeGroups.empty())
+ {
+ // still valid area near the boundary
+ const short nTolerance = 4;
+ // box boundary
+ short nX2 = nX1 + nWidth;
+ // approximate group boundary
+ short nGrX1;
+ short nGrX2;
+
+ // improvement: search backwards
+ for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr)
+ {
+ // the currently inspected group
+ WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ];
+ if (!rActGroup.m_bGroupLocked)
+ {
+ // approximate group boundary with room (tolerance) to the *outside*
+ nGrX1 = rActGroup.m_nGroupXStart - nTolerance;
+ nGrX2 = rActGroup.m_nGroupXStart
+ + rActGroup.m_nGroupWidth + nTolerance;
+
+ // If box fits report success
+
+ if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) )
+ {
+ return &rActGroup;
+ }
+
+ // does the box share areas with the group?
+
+ if( !bExact )
+ {
+ // successful if nX1 *or* nX2 are inside the group
+ if( ( ( nX1 > nGrX1 )
+ && ( nX1 < nGrX2 - 2*nTolerance ) )
+ || ( ( nX2 > nGrX1 + 2*nTolerance )
+ && ( nX2 < nGrX2 ) )
+ // or nX1 and nX2 surround the group
+ || ( ( nX1 <=nGrX1 )
+ && ( nX2 >=nGrX2 ) ) )
+ {
+ return &rActGroup;
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool WW8TabDesc::IsValidCell(short nCol) const
+{
+ return (o3tl::make_unsigned(nCol) < SAL_N_ELEMENTS(m_pActBand->bExist)) &&
+ m_pActBand->bExist[nCol] &&
+ o3tl::make_unsigned(m_nCurrentRow) < m_pTabLines->size();
+}
+
+bool WW8TabDesc::InFirstParaInCell() const
+{
+ //e.g. #i19718#
+ if (!m_pTabBox || !m_pTabBox->GetSttNd())
+ {
+ OSL_FAIL("Problem with table");
+ return false;
+ }
+
+ if (!IsValidCell(GetCurrentCol()))
+ return false;
+
+ return m_pIo->m_pPaM->GetPoint()->GetNodeIndex() == m_pTabBox->GetSttIdx() + 1;
+}
+
+void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam)
+{
+ OSL_ENSURE( m_pActBand, "pActBand is 0" );
+ if (!m_pActBand)
+ return;
+
+ sal_uInt16 nCol = m_pActBand->transCell(nWwCol);
+
+ if (o3tl::make_unsigned(m_nCurrentRow) >= m_pTabLines->size())
+ {
+ OSL_ENSURE(false, "Actual row bigger than expected." );
+ if (bPam)
+ MoveOutsideTable();
+ return;
+ }
+
+ m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
+ m_pTabBoxes = &m_pTabLine->GetTabBoxes();
+
+ if (nCol >= m_pTabBoxes->size())
+ {
+ if (bPam)
+ {
+ // The first paragraph in a cell with upper autospacing has upper
+ // spacing set to 0
+ if (
+ m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara &&
+ !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing
+ )
+ {
+ m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
+ }
+
+ // The last paragraph in a cell with lower autospacing has lower
+ // spacing set to 0
+ if (m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
+ m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
+
+ ParkPaM();
+ }
+ return;
+ }
+ m_pTabBox = (*m_pTabBoxes)[nCol];
+ if( !m_pTabBox->GetSttNd() )
+ {
+ OSL_ENSURE(m_pTabBox->GetSttNd(), "Problems building the table");
+ if (bPam)
+ MoveOutsideTable();
+ return;
+ }
+ if (!bPam)
+ return;
+
+ m_pCurrentWWCell = &m_pActBand->pTCs[ nWwCol ];
+
+ // The first paragraph in a cell with upper autospacing has upper spacing set to 0
+ if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
+ m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0);
+
+ // The last paragraph in a cell with lower autospacing has lower spacing set to 0
+ if(m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing)
+ m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0);
+
+ //We need to set the pPaM on the first cell, invalid
+ //or not so that we can collect paragraph properties over
+ //all the cells, but in that case on the valid cell we do not
+ //want to reset the fmt properties
+ SwNodeOffset nSttNd = m_pTabBox->GetSttIdx() + 1,
+ nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex();
+ if (m_pIo->m_pPaM->GetPoint()->GetNodeIndex() != nSttNd)
+ {
+ do
+ {
+ m_pIo->m_pPaM->GetPoint()->Assign(nSttNd);
+ }
+ while (m_pIo->m_pPaM->GetPointNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd);
+ m_pIo->m_pPaM->GetPoint()->SetContent(0);
+ // Precautionally set now, otherwise the style is not set for cells
+ // that are inserted for margin balancing.
+ m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl));
+ // because this cells are invisible helper constructions only to simulate
+ // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel()
+ }
+
+ // Better to turn Snap to Grid off for all paragraphs in tables
+ SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint();
+ SwTextNode *pNd = pGridPos->GetNode().GetTextNode();
+ if(!pNd)
+ return;
+
+ const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID);
+ const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm);
+
+ if(!rSnapToGrid.GetValue())
+ return;
+
+ SvxParaGridItem aGridItem( rSnapToGrid );
+ aGridItem.SetValue(false);
+
+ const sal_Int32 nEnd = pGridPos->GetContentIndex();
+ pGridPos->SetContent(0);
+ m_pIo->m_xCtrlStck->NewAttr(*pGridPos, aGridItem);
+ pGridPos->SetContent(nEnd);
+ m_pIo->m_xCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID);
+}
+
+void WW8TabDesc::InsertCells( short nIns )
+{
+ m_pTabLine = (*m_pTabLines)[m_nCurrentRow];
+ m_pTabBoxes = &m_pTabLine->GetTabBoxes();
+ m_pTabBox = (*m_pTabBoxes)[0];
+
+ m_pIo->m_rDoc.GetNodes().InsBoxen( m_pTableNd, m_pTabLine, static_cast<SwTableBoxFormat*>(m_pTabBox->GetFrameFormat()),
+ const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns );
+ // The third parameter contains the FrameFormat of the boxes.
+ // Here it is possible to optimize to save (reduce) FrameFormats.
+}
+
+void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx)
+{
+ if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
+ return; // faked cells -> no border
+
+ SvxBoxItem aFormatBox( RES_BOX );
+ if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ?
+ {
+ WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
+ if (SwWW8ImplReader::IsBorder(pT->rgbrc))
+ SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc);
+ }
+
+ if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwTOP))
+ {
+ aFormatBox.SetDistance(
+ m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwTOP],
+ SvxBoxItemLine::TOP);
+ }
+ else
+ aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP);
+ if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwBOTTOM))
+ {
+ aFormatBox.SetDistance(
+ m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwBOTTOM],
+ SvxBoxItemLine::BOTTOM);
+ }
+ else
+ aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM);
+
+ // nGapHalf for WW is a *horizontal* gap between table cell and content.
+ short nLeftDist =
+ m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultLeft : m_pActBand->nGapHalf;
+ short nRightDist =
+ m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultRight : m_pActBand->nGapHalf;
+ if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwLEFT))
+ {
+ aFormatBox.SetDistance(
+ m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwLEFT],
+ SvxBoxItemLine::LEFT);
+ }
+ else
+ aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT);
+ if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwRIGHT))
+ {
+ aFormatBox.SetDistance(
+ m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwRIGHT],
+ SvxBoxItemLine::RIGHT);
+ }
+ else
+ aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT);
+
+ pBox->GetFrameFormat()->SetFormatAttr(aFormatBox);
+}
+
+void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx )
+{
+ if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
+ return; // faked cells -> no color
+
+ bool bFound=false;
+ if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO)
+ {
+ Color aColor(m_pActBand->pNewSHDs[nWwIdx]);
+ pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aColor, RES_BACKGROUND));
+ bFound = true;
+ }
+
+ //If there was no new shades, or no new shade setting
+ if (m_pActBand->pSHDs && !bFound)
+ {
+ WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx];
+ if (!rSHD.GetValue()) // auto
+ return;
+
+ SwWW8Shade aSh( m_pIo->m_bVer67, rSHD );
+ pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aSh.m_aColor, RES_BACKGROUND));
+ }
+}
+
+static SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi)
+{
+ SvxFrameDirection eDir = SvxFrameDirection::Environment;
+ // 1: Asian layout with rotated CJK characters
+ // 5: Asian layout
+ // 3: Western layout rotated by 90 degrees
+ // 4: Western layout
+ switch (nCode)
+ {
+ default:
+ OSL_ENSURE(eDir == SvxFrameDirection::Environment, "unknown direction code, maybe it's a bitfield");
+ [[fallthrough]];
+ case 3:
+ eDir = SvxFrameDirection::Vertical_LR_BT;
+ break;
+ case 5:
+ eDir = SvxFrameDirection::Vertical_RL_TB;
+ break;
+ case 1:
+ eDir = SvxFrameDirection::Vertical_RL_TB;
+ break;
+ case 4:
+ eDir = bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; // #i38158# - Consider RTL tables
+ break;
+ }
+ return eDir;
+}
+
+void WW8TabDesc::SetTabDirection(SwTableBox* pBox, short nWwIdx)
+{
+ if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols)
+ return;
+ SvxFrameDirectionItem aItem(MakeDirection(m_pActBand->maDirections[nWwIdx], m_bIsBiDi), RES_FRAMEDIR);
+ pBox->GetFrameFormat()->SetFormatAttr(aItem);
+}
+
+void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx )
+{
+ if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols )
+ return;
+
+ sal_Int16 eVertOri=text::VertOrientation::TOP;
+
+ if( m_pActBand->pTCs )
+ {
+ WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx];
+ switch (pT->nVertAlign)
+ {
+ case 0:
+ default:
+ eVertOri = text::VertOrientation::TOP;
+ break;
+ case 1:
+ eVertOri = text::VertOrientation::CENTER;
+ break;
+ case 2:
+ eVertOri = text::VertOrientation::BOTTOM;
+ break;
+ }
+ }
+
+ pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) );
+}
+
+void WW8TabDesc::AdjustNewBand()
+{
+ if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells
+ InsertCells( m_pActBand->nSwCols - m_nDefaultSwCols );
+
+ SetPamInCell( 0, false);
+ OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == o3tl::narrowing<sal_uInt16>(m_pActBand->nSwCols),
+ "Wrong column count in table" );
+
+ if( m_bClaimLineFormat )
+ {
+ m_pTabLine->ClaimFrameFormat(); // necessary because of cell height
+ SwFormatFrameSize aF( SwFrameSize::Minimum, 0, 0 ); // default
+
+ if (m_pActBand->nLineHeight == 0) // 0 = Auto
+ aF.SetHeightSizeType( SwFrameSize::Variable );
+ else
+ {
+ if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact
+ {
+ aF.SetHeightSizeType(SwFrameSize::Fixed);
+ m_pActBand->nLineHeight = -m_pActBand->nLineHeight;
+ }
+ if (m_pActBand->nLineHeight < MINLAY) // invalid cell height
+ m_pActBand->nLineHeight = MINLAY;
+
+ aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height
+ }
+ m_pTabLine->GetFrameFormat()->SetFormatAttr(aF);
+ }
+
+ //Word stores 1 for bCantSplit if the row cannot be split, we set true if
+ //we can split the row
+ bool bSetCantSplit = m_pActBand->bCantSplit;
+ m_pTabLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bSetCantSplit));
+
+ // if table is only a single row, and row is set as don't split, set the same value for the whole table.
+ if (bSetCantSplit && m_pTabLines->size() == 1)
+ m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatLayoutSplit(false));
+
+ short i; // SW-Index
+ short j; // WW-Index
+ short nW; // Width
+ SwFormatFrameSize aFS( SwFrameSize::Fixed );
+ j = m_pActBand->bLEmptyCol ? -1 : 0;
+
+ for( i = 0; i < m_pActBand->nSwCols; i++ )
+ {
+ // set cell width
+ if( j < 0 )
+ nW = m_pActBand->nCenter[0] - m_nMinLeft;
+ else
+ {
+ //Set j to first non invalid cell
+ while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j]))
+ j++;
+
+ if( j < m_pActBand->nWwCols )
+ nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
+ else
+ nW = m_nMaxRight - m_pActBand->nCenter[j];
+ m_pActBand->nWidth[ j ] = nW;
+ }
+
+ SwTableBox* pBox = (*m_pTabBoxes)[i];
+ // could be reduced further by intelligent moving of FrameFormats
+ pBox->ClaimFrameFormat();
+
+ SetTabBorders(pBox, j);
+
+ SvxBoxItem aCurrentBox(pBox->GetFrameFormat()->GetFormatAttr(RES_BOX));
+ pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox);
+
+ SetTabVertAlign(pBox, j);
+ SetTabDirection(pBox, j);
+ if( m_pActBand->pSHDs || m_pActBand->pNewSHDs)
+ SetTabShades(pBox, j);
+ j++;
+
+ aFS.SetWidth( nW );
+ pBox->GetFrameFormat()->SetFormatAttr( aFS );
+
+ // skip non existing cells
+ while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] )
+ {
+ m_pActBand->nWidth[j] = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j];
+ j++;
+ }
+ }
+}
+
+void WW8TabDesc::TableCellEnd()
+{
+ ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update
+
+ // new line/row
+ if( m_pIo->m_bWasTabRowEnd )
+ {
+ // bWasTabRowEnd will be deactivated in
+ // SwWW8ImplReader::ProcessSpecial()
+
+ sal_uInt16 iCol = GetLogicalWWCol();
+ if (iCol < m_aNumRuleNames.size())
+ {
+ m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol,
+ m_aNumRuleNames.end());
+ }
+
+ m_nCurrentCol = 0;
+ m_nCurrentRow++;
+ m_nCurrentBandRow++;
+ OSL_ENSURE( m_pActBand , "pActBand is 0" );
+ if( m_pActBand )
+ {
+ if( m_nCurrentRow >= m_nRows ) // nothing to at end of table
+ return;
+
+ bool bNewBand = m_nCurrentBandRow >= m_pActBand->nRows;
+ if( bNewBand )
+ { // new band needed ?
+ m_pActBand = m_pActBand->pNextBand;
+ m_nCurrentBandRow = 0;
+ OSL_ENSURE( m_pActBand, "pActBand is 0" );
+ AdjustNewBand();
+ }
+ else
+ {
+ SwTableBox* pBox = (*m_pTabBoxes)[0];
+ SwSelBoxes aBoxes;
+ m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) );
+ }
+ }
+ }
+ else
+ { // new column ( cell )
+ m_nCurrentCol++;
+ }
+ SetPamInCell(m_nCurrentCol, true);
+
+ // finish Annotated Level Numbering ?
+ if (m_pIo->m_bAnl && !m_pIo->m_bCurrentAND_fNumberAcross && m_pActBand)
+ m_pIo->StopAllAnl(IsValidCell(m_nCurrentCol));
+}
+
+// if necessary register the box for the merge group for this column
+void WW8TabDesc::UpdateTableMergeGroup( WW8_TCell const & rCell,
+ WW8SelBoxInfo* pActGroup,
+ SwTableBox* pActBox,
+ sal_uInt16 nCol )
+{
+ // check if the box has to be merged
+ // If cell is the first one to be merged, a new merge group has to be provided.
+ // E.g., it could be that a cell is the first one to be merged, but no
+ // new merge group is provided, because the potential other cell to be merged
+ // doesn't exist - see method <WW8TabDesc::MergeCells>.
+ if ( !(m_pActBand->bExist[ nCol ] &&
+ ( ( rCell.bFirstMerged && pActGroup ) ||
+ rCell.bMerged ||
+ rCell.bVertMerge ||
+ rCell.bVertRestart )) )
+ return;
+
+ // detect appropriate merge group
+ WW8SelBoxInfo* pTheMergeGroup = nullptr;
+ if( pActGroup )
+ // assign group
+ pTheMergeGroup = pActGroup;
+ else
+ {
+ // find group
+ pTheMergeGroup = FindMergeGroup(
+ m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true );
+ }
+ if( pTheMergeGroup )
+ {
+ // add current box to merge group
+ pTheMergeGroup->push_back(pActBox);
+ }
+}
+
+sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1
+{
+ sal_uInt16 nCol = 0;
+ if( m_pActBand && m_pActBand->pTCs)
+ {
+ for( sal_uInt16 iCol = 1; iCol <= m_nCurrentCol && iCol <= m_pActBand->nWwCols; ++iCol )
+ {
+ if( !m_pActBand->pTCs[ iCol-1 ].bMerged )
+ ++nCol;
+ }
+ }
+ return nCol;
+}
+
+// find name of numrule valid for current WW-COL
+OUString WW8TabDesc::GetNumRuleName() const
+{
+ sal_uInt16 nCol = GetLogicalWWCol();
+ if (nCol < m_aNumRuleNames.size())
+ return m_aNumRuleNames[nCol];
+ return OUString();
+}
+
+void WW8TabDesc::SetNumRuleName( const OUString& rName )
+{
+ sal_uInt16 nCol = GetLogicalWWCol();
+ for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize)
+ m_aNumRuleNames.emplace_back();
+ m_aNumRuleNames[nCol] = rName;
+}
+
+bool SwWW8ImplReader::StartTable(WW8_CP nStartCp)
+{
+ // Entering a table so make sure the FirstPara flag gets set
+ m_bFirstPara = true;
+ // no recursive table, not with InsertFile in table or foot note
+ if (m_bReadNoTable)
+ return false;
+
+ if (m_xTableDesc)
+ m_aTableStack.push(std::move(m_xTableDesc));
+
+ // #i33818# - determine absolute position object attributes,
+ // if possible. It's needed for nested tables.
+ std::unique_ptr<WW8FlyPara> pTableWFlyPara;
+ WW8SwFlyPara* pTableSFlyPara( nullptr );
+ // #i45301# - anchor nested table inside Writer fly frame
+ // only at-character, if absolute position object attributes are available.
+ // Thus, default anchor type is as-character anchored.
+ RndStdIds eAnchor( RndStdIds::FLY_AS_CHAR );
+ if ( m_nInTable )
+ {
+ WW8_TablePos* pNestedTabPos( nullptr );
+ WW8_TablePos aNestedTabPos;
+ WW8PLCFxSave1 aSave;
+ m_xPlcxMan->GetPap()->Save( aSave );
+ WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF();
+ WW8_CP nMyStartCp = nStartCp;
+ if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) &&
+ ParseTabPos( &aNestedTabPos, pPap ) )
+ {
+ pNestedTabPos = &aNestedTabPos;
+ }
+ m_xPlcxMan->GetPap()->Restore( aSave );
+ if ( pNestedTabPos )
+ {
+ ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos );
+ pTableWFlyPara = ConstructApo( aApo, pNestedTabPos );
+ if ( pTableWFlyPara )
+ {
+ // <WW8SwFlyPara> constructor has changed - new 4th parameter
+ // containing WW8 page top margin.
+ pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara,
+ m_aSectionManager.GetWWPageTopMargin(),
+ m_aSectionManager.GetTextAreaWidth(),
+ m_nIniFlyDx, m_nIniFlyDy);
+
+ // #i45301# - anchor nested table Writer fly frame
+ eAnchor = RndStdIds::FLY_AT_PARA;
+ }
+ }
+ }
+ // if first paragraph in table has break-before-page, transfer that setting to the table itself.
+ else if( StyleExists(m_nCurrentColl) )
+ {
+ const SwFormat* pStyleFormat = m_vColl[m_nCurrentColl].m_pFormat;
+ if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore )
+ NewAttr( pStyleFormat->GetBreak() );
+ }
+
+ m_xTableDesc.reset(new WW8TabDesc(this, nStartCp));
+
+ if( m_xTableDesc->Ok() )
+ {
+ int nNewInTable = m_nInTable + 1;
+
+ if ((eAnchor == RndStdIds::FLY_AT_PARA)
+ && !m_aTableStack.empty() && !InEqualApo(nNewInTable) )
+ {
+ m_xTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint());
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aItemSet(m_rDoc.GetAttrPool());
+ // #i33818# - anchor the Writer fly frame for the nested table at-character.
+ // #i45301#
+ SwFormatAnchor aAnchor( eAnchor );
+ aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
+ aItemSet.Put( aAnchor );
+ m_xTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor,
+ m_xTableDesc->m_pParentPos, &aItemSet);
+ OSL_ENSURE( m_xTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor,
+ "Not the anchor type requested!" );
+ MoveInsideFly(m_xTableDesc->m_pFlyFormat);
+ }
+ m_xTableDesc->CreateSwTable();
+ if (m_xTableDesc->m_pFlyFormat)
+ {
+ m_xTableDesc->SetSizePosition(m_xTableDesc->m_pFlyFormat);
+ // #i33818# - Use absolute position object attributes,
+ // if existing, and apply them to the created Writer fly frame.
+ if ( pTableWFlyPara && pTableSFlyPara )
+ {
+ WW8FlySet aFlySet( *this, pTableWFlyPara.get(), pTableSFlyPara, false );
+ // At-para, so it can split in the multi-page case.
+ SwFormatAnchor aAnchor(RndStdIds::FLY_AT_PARA);
+ aAnchor.SetAnchor( m_xTableDesc->m_pParentPos );
+ aFlySet.Put( aAnchor );
+ // Map a positioned inner table to a split fly.
+ aFlySet.Put(SwFormatFlySplit(true));
+ m_xTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet );
+ }
+ else
+ {
+ SwFormatHoriOrient aHori =
+ m_xTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient();
+ m_xTableDesc->m_pFlyFormat->SetFormatAttr(aHori);
+ m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ }
+ // #i33818# - The nested table doesn't have to leave
+ // the table cell. Thus, the Writer fly frame has to follow the text flow.
+ m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) );
+ }
+ else
+ m_xTableDesc->SetSizePosition(nullptr);
+ m_xTableDesc->UseSwTable();
+ }
+ else
+ PopTableDesc();
+
+ // #i33818#
+ delete pTableSFlyPara;
+
+ return m_xTableDesc != nullptr;
+}
+
+void SwWW8ImplReader::TabCellEnd()
+{
+ if (m_nInTable && m_xTableDesc)
+ m_xTableDesc->TableCellEnd();
+
+ m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag
+ m_bReadTable = false;
+}
+
+void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if( ( nLen > 0 ) && ( *pData == 1 ) )
+ m_bWasTabCellEnd = true;
+}
+
+void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25
+{
+ if( ( nLen > 0 ) && ( *pData == 1 ) )
+ m_bWasTabRowEnd = true;
+}
+
+void SwWW8ImplReader::PopTableDesc()
+{
+ if (m_xTableDesc && m_xTableDesc->m_pFlyFormat)
+ {
+ MoveOutsideFly(m_xTableDesc->m_pFlyFormat, *m_xTableDesc->m_pParentPos);
+ }
+
+ m_xTableDesc.reset();
+ if (!m_aTableStack.empty())
+ {
+ m_xTableDesc = std::move(m_aTableStack.top());
+ m_aTableStack.pop();
+ }
+}
+
+void SwWW8ImplReader::StopTable()
+{
+ OSL_ENSURE(m_xTableDesc, "Panic, stop table with no table!");
+ if (!m_xTableDesc)
+ return;
+
+ // We are leaving a table so make sure the next paragraph doesn't think
+ // it's the first paragraph
+ m_bFirstPara = false;
+
+ m_xTableDesc->FinishSwTable();
+ PopTableDesc();
+
+ m_bReadTable = true;
+}
+
+bool SwWW8ImplReader::IsInvalidOrToBeMergedTabCell() const
+{
+ if( !m_xTableDesc )
+ return false;
+
+ const WW8_TCell* pCell = m_xTableDesc->GetCurrentWWCell();
+
+ return !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() )
+ || ( pCell
+ && ( !pCell->bFirstMerged
+ && ( pCell->bMerged
+ || ( pCell->bVertMerge
+ && !pCell->bVertRestart
+ )
+ )
+ )
+ );
+}
+
+sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const
+{
+ sal_uInt16 nRes = USHRT_MAX;
+ if( !m_vColl.empty() )
+ {
+ for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
+ if( m_vColl[ nI ].m_bValid
+ && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) )
+ nRes = nI;
+ }
+ return nRes;
+}
+
+const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( std::u16string_view rName ) const
+{
+ SwFormat* pRet = nullptr;
+ if( !m_vColl.empty() )
+ {
+ for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ )
+ if( m_vColl[ nI ].m_bValid
+ && (rName == m_vColl[ nI ].GetOrgWWName()) )
+ {
+ pRet = m_vColl[ nI ].m_pFormat;
+ break;
+ }
+ }
+ return pRet;
+}
+
+
+SprmResult WW8RStyle::HasParaSprm(sal_uInt16 nId) const
+{
+ if( !mpParaSprms || !mnSprmsLen )
+ return SprmResult();
+
+ return maSprmParser.findSprmData(nId, mpParaSprms, mnSprmsLen);
+}
+
+void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap)
+{
+ if (!nLen)
+ return;
+
+ if( bPap )
+ {
+ mpParaSprms = pSprms; // for HasParaSprms()
+ mnSprmsLen = nLen;
+ }
+
+ WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser);
+ while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
+ {
+ mpIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
+ aSprmIter.advance();
+ }
+
+ mpParaSprms = nullptr;
+ mnSprmsLen = 0;
+}
+
+void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap)
+{
+ if (!nLen)
+ return;
+
+ if (checkSeek(*mpStStrm, nPosFc))
+ {
+ std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] );
+ nLen = mpStStrm->ReadBytes(pSprms.get(), nLen);
+ ImportSprms(pSprms.get(), nLen, bPap);
+ }
+}
+
+static short WW8SkipOdd(SvStream* pSt )
+{
+ if ( pSt->Tell() & 0x1 )
+ {
+ sal_uInt8 c;
+ return pSt->ReadBytes( &c, 1 );
+ }
+ return 0;
+}
+
+static short WW8SkipEven(SvStream* pSt )
+{
+ if (!(pSt->Tell() & 0x1))
+ {
+ sal_uInt8 c;
+ return pSt->ReadBytes( &c, 1 );
+ }
+ return 0;
+}
+
+short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd)
+{
+ if( 0 < nLen ) // Empty ?
+ {
+ if (bOdd)
+ nLen = nLen - WW8SkipEven( mpStStrm );
+ else
+ nLen = nLen - WW8SkipOdd( mpStStrm );
+
+ sal_Int16 cbUPX(0);
+ mpStStrm->ReadInt16( cbUPX );
+
+ nLen-=2;
+
+ if ( cbUPX > nLen )
+ cbUPX = nLen; // shrink cbUPX to nLen
+
+ if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) )
+ {
+ if( bPAP )
+ {
+ sal_uInt16 id;
+ mpStStrm->ReadUInt16( id );
+
+ cbUPX-= 2;
+ nLen-= 2;
+ }
+
+ if( 0 < cbUPX )
+ {
+ sal_uInt64 const nPos = mpStStrm->Tell(); // if something is interpreted wrong,
+ // this should make it work again
+ ImportSprms( nPos, cbUPX, bPAP );
+
+ if ( mpStStrm->Tell() != nPos + cbUPX )
+ mpStStrm->Seek( nPos+cbUPX );
+
+ nLen = nLen - cbUPX;
+ }
+ }
+ }
+ return nLen;
+}
+
+void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd)
+{
+ if( nLen <= 0 )
+ return;
+ if (bOdd)
+ nLen = nLen - WW8SkipEven( mpStStrm );
+ else
+ nLen = nLen - WW8SkipOdd( mpStStrm );
+
+ if( bPara ) // Grupx.Papx
+ nLen = ImportUPX(nLen, true, bOdd);
+ ImportUPX(nLen, false, bOdd); // Grupx.Chpx
+}
+
+WW8RStyle::WW8RStyle(WW8Fib& _rFib, SwWW8ImplReader* pI)
+ : WW8Style(*pI->m_pTableStream, _rFib)
+ , maSprmParser(_rFib)
+ , mpIo(pI)
+ , mpStStrm(pI->m_pTableStream)
+ , mpStyRule(nullptr)
+ , mpParaSprms(nullptr)
+ , mnSprmsLen(0)
+ , mnWwNumLevel(0)
+ , mbTextColChanged(false)
+ , mbFontChanged(false)
+ , mbCJKFontChanged(false)
+ , mbCTLFontChanged(false)
+ , mbFSizeChanged(false)
+ , mbFCTLSizeChanged(false)
+ , mbWidowsChanged(false)
+ , mbBidiChanged(false)
+{
+ mpIo->m_vColl.resize(m_cstd);
+}
+
+void WW8RStyle::Set1StyleDefaults()
+{
+ // see #i25247#, #i25561#, #i48064#, #i92341# for default font
+ if (!mbCJKFontChanged) // Style no CJK Font? set the default
+ mpIo->SetNewFontAttr(m_ftcFE, true, RES_CHRATR_CJK_FONT);
+
+ if (!mbCTLFontChanged) // Style no CTL Font? set the default
+ mpIo->SetNewFontAttr(m_ftcBi, true, RES_CHRATR_CTL_FONT);
+
+ // western 2nd to make western charset conversion the default
+ if (!mbFontChanged) // Style has no Font? set the default,
+ mpIo->SetNewFontAttr(m_ftcAsci, true, RES_CHRATR_FONT);
+
+ if( mpIo->m_bNoAttrImport )
+ return;
+
+ // Style has no text color set, winword default is auto
+ if ( !mbTextColChanged )
+ mpIo->m_pCurrentColl->SetFormatAttr(SvxColorItem(COL_AUTO, RES_CHRATR_COLOR));
+
+ // Style has no FontSize ? WinWord Default is 10pt for western and asian
+ if( !mbFSizeChanged )
+ {
+ SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
+ mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
+ aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE);
+ mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
+ }
+
+ // Style has no FontSize ? WinWord Default is 10pt for western and asian
+ if( !mbFCTLSizeChanged )
+ {
+ SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE);
+ aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE);
+ mpIo->m_pCurrentColl->SetFormatAttr(aAttr);
+ }
+
+ if( !mbWidowsChanged ) // Widows ?
+ {
+ mpIo->m_pCurrentColl->SetFormatAttr( SvxWidowsItem( 2, RES_PARATR_WIDOWS ) );
+ mpIo->m_pCurrentColl->SetFormatAttr( SvxOrphansItem( 2, RES_PARATR_ORPHANS ) );
+ }
+
+ // Word defaults to ltr, not inheriting from the environment like Writer. Regardless of
+ // the page/sections rtl setting, the standard/no-inherit styles lack of rtl still means ltr
+ if( !mbBidiChanged ) // likely, since no UI to change LTR except in default style
+ {
+ mpIo->m_pCurrentColl->SetFormatAttr(
+ SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
+ }
+}
+
+bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle,
+ sal_uInt16 nNextStyle,
+ std::map<OUString, sal_Int32>& rParaCollisions,
+ std::map<OUString, sal_Int32>& rCharCollisions)
+{
+ SwFormat* pColl;
+ bool bStyExist;
+
+ if (rSI.m_bColl)
+ {
+ // Para-Style
+ sw::util::ParaStyleMapper::StyleResult aResult =
+ mpIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rParaCollisions);
+ pColl = aResult.first;
+ bStyExist = aResult.second;
+ }
+ else
+ {
+ // Char-Style
+ sw::util::CharStyleMapper::StyleResult aResult =
+ mpIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti, rCharCollisions);
+ pColl = aResult.first;
+ bStyExist = aResult.second;
+ }
+
+ bool bImport = !bStyExist || mpIo->m_bNewDoc; // import content ?
+
+ // Do not override character styles the list import code created earlier.
+ if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num"))
+ bImport = false;
+
+ bool bOldNoImp = mpIo->m_bNoAttrImport;
+ rSI.m_bImportSkipped = !bImport;
+
+ if( !bImport )
+ mpIo->m_bNoAttrImport = true;
+ else
+ {
+ if (bStyExist)
+ {
+ pColl->ResetAllFormatAttr(); // #i73790# - method renamed
+ }
+ pColl->SetAuto(false); // suggested by JP
+ } // but changes the UI
+ mpIo->m_pCurrentColl = pColl;
+ rSI.m_pFormat = pColl; // remember translation WW->SW
+ rSI.m_bImportSkipped = !bImport;
+
+ // Set Based on style
+ sal_uInt16 j = rSI.m_nBase;
+ if (j != nThisStyle && j < m_cstd )
+ {
+ SwWW8StyInf* pj = &mpIo->m_vColl[j];
+ if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl)
+ {
+ rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on
+ rSI.m_eLTRFontSrcCharSet = pj->m_eLTRFontSrcCharSet;
+ rSI.m_eRTLFontSrcCharSet = pj->m_eRTLFontSrcCharSet;
+ rSI.m_eCJKFontSrcCharSet = pj->m_eCJKFontSrcCharSet;
+ rSI.m_n81Flags = pj->m_n81Flags;
+ rSI.m_n81BiDiFlags = pj->m_n81BiDiFlags;
+ if (!rSI.IsWW8BuiltInHeadingStyle())
+ {
+ rSI.mnWW8OutlineLevel = pj->mnWW8OutlineLevel;
+ }
+ rSI.m_bParaAutoBefore = pj->m_bParaAutoBefore;
+ rSI.m_bParaAutoAfter = pj->m_bParaAutoAfter;
+
+ if (pj->m_xWWFly)
+ rSI.m_xWWFly = std::make_shared<WW8FlyPara>(mpIo->m_bVer67, pj->m_xWWFly.get());
+ }
+ }
+ else if( mpIo->m_bNewDoc && bStyExist )
+ rSI.m_pFormat->SetDerivedFrom();
+
+ rSI.m_nFollow = nNextStyle; // remember Follow
+
+ mpStyRule = nullptr; // recreate if necessary
+ mbTextColChanged = mbFontChanged = mbCJKFontChanged = mbCTLFontChanged =
+ mbFSizeChanged = mbFCTLSizeChanged = mbWidowsChanged = false;
+ mpIo->SetNCurrentColl( nThisStyle );
+ mpIo->m_bStyNormal = nThisStyle == 0;
+ return bOldNoImp;
+}
+
+void WW8RStyle::PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp)
+{
+ // Reset attribute flags, because there are no style-ends.
+
+ mpIo->m_bHasBorder = mpIo->m_bSpec = mpIo->m_bObj = mpIo->m_bSymbol = false;
+ mpIo->m_nCharFormat = -1;
+
+ // if style is based on nothing or base ignored
+ if ((rSI.m_nBase >= m_cstd || mpIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl)
+ {
+ // If Char-Styles does not work
+ // -> set hard WW-Defaults
+ Set1StyleDefaults();
+ }
+
+ mpStyRule = nullptr; // to be on the safe side
+ mpIo->m_bStyNormal = false;
+ mpIo->SetNCurrentColl( 0 );
+ mpIo->m_bNoAttrImport = bOldNoImp;
+ // reset the list-remember-fields, if used when reading styles
+ mpIo->m_nLFOPosition = USHRT_MAX;
+ mpIo->m_nListLevel = MAXLEVEL;
+}
+
+void WW8RStyle::Import1Style(sal_uInt16 nNr,
+ std::map<OUString, sal_Int32>& rParaCollisions,
+ std::map<OUString, sal_Int32>& rCharCollisions)
+{
+ if (nNr >= mpIo->m_vColl.size())
+ return;
+
+ SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
+
+ if( rSI.m_bImported || !rSI.m_bValid )
+ return;
+
+ rSI.m_bImported = true; // set flag now to avoid endless loops
+
+ // valid and not NUL and not yet imported
+
+ if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
+ Import1Style(rSI.m_nBase, rParaCollisions, rCharCollisions);
+
+ mpStStrm->Seek( rSI.m_nFilePos );
+
+ sal_uInt16 nSkip;
+ OUString sName;
+
+ std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName)); // read Style
+
+ if (xStd)
+ rSI.SetOrgWWIdent( sName, xStd->sti );
+
+ // either no Name or unused Slot or unknown Style
+
+ if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) )
+ {
+ nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
+ mpStStrm->Seek(mpStStrm->Tell() + nSkip);
+ return;
+ }
+
+ bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti),
+ nNr, xStd->istdNext,
+ rParaCollisions, rCharCollisions);
+
+ // if something is interpreted wrong, this should make it work again
+ sal_uInt64 nPos = mpStStrm->Tell();
+
+ //Variable parts of the STD start at even byte offsets, but "inside
+ //the STD", which I take to meaning even in relation to the starting
+ //position of the STD, which matches findings in #89439#, generally it
+ //doesn't matter as the STSHI starts off nearly always on an even
+ //offset
+
+ //Import of the Style Contents
+ ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1);
+
+ PostStyle(rSI, bOldNoImp);
+
+ mpStStrm->Seek( nPos+nSkip );
+}
+
+void WW8RStyle::RecursiveReg(sal_uInt16 nNr)
+{
+ if (nNr >= mpIo->m_vColl.size())
+ return;
+
+ SwWW8StyInf &rSI = mpIo->m_vColl[nNr];
+ if( rSI.m_bImported || !rSI.m_bValid )
+ return;
+
+ rSI.m_bImported = true;
+
+ if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported )
+ RecursiveReg(rSI.m_nBase);
+
+ mpIo->RegisterNumFormatOnStyle(nNr);
+
+}
+
+/*
+ After all styles are imported then we can recursively apply numbering
+ styles to them, and change their tab stop settings if they turned out
+ to have special first line indentation.
+*/
+void WW8RStyle::PostProcessStyles()
+{
+ sal_uInt16 i;
+ /*
+ Clear all imported flags so that we can recursively apply numbering
+ formats and use it to mark handled ones
+ */
+ for (i=0; i < m_cstd; ++i)
+ mpIo->m_vColl[i].m_bImported = false;
+
+ /*
+ Register the num formats and tabstop changes on the styles recursively.
+ */
+
+ /*
+ In the same loop apply the tabstop changes required because we need to
+ change their location if there's a special indentation for the first line,
+ By avoiding making use of each styles margins during reading of their
+ tabstops we don't get problems with doubly adjusting tabstops that
+ are inheritied.
+ */
+ for (i=0; i < m_cstd; ++i)
+ {
+ if (mpIo->m_vColl[i].m_bValid)
+ {
+ RecursiveReg(i);
+ }
+ }
+}
+
+void WW8RStyle::ScanStyles() // investigate style dependencies
+{ // and detect Filepos for each Style
+ for (sal_uInt16 i = 0; i < m_cstd; ++i)
+ {
+ SwWW8StyInf &rSI = mpIo->m_vColl[i];
+
+ rSI.m_nFilePos = mpStStrm->Tell(); // remember FilePos
+ sal_uInt16 nSkip;
+ std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, nullptr)); // read STD
+ rSI.m_bValid = xStd != nullptr;
+ if (rSI.m_bValid)
+ {
+ rSI.m_nBase = xStd->istdBase; // remember Basis
+ rSI.m_bColl = xStd->sgc == 1; // Para-Style
+ }
+ else
+ rSI = SwWW8StyInf();
+
+ xStd.reset();
+ nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize());
+ mpStStrm->Seek(mpStStrm->Tell() + nSkip); // skip Names and Sprms
+ }
+}
+
+std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx)
+{
+ std::vector<sal_uInt8> aRet
+ {
+ 60,
+ static_cast< sal_uInt8 >(128 + rChpx.fBold),
+
+ 61,
+ static_cast< sal_uInt8 >(128 + rChpx.fItalic),
+
+ 62,
+ static_cast< sal_uInt8 >(128 + rChpx.fStrike),
+
+ 63,
+ static_cast< sal_uInt8 >(128 + rChpx.fOutline),
+
+ 65,
+ static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps),
+
+ 66,
+ static_cast< sal_uInt8 >(128 + rChpx.fCaps),
+
+ 67,
+ static_cast< sal_uInt8 >(128 + rChpx.fVanish)
+ };
+ if (rChpx.fsFtc)
+ {
+ aRet.push_back(68);
+ SVBT16 a;
+ ShortToSVBT16(rChpx.ftc, a);
+ aRet.push_back(a[1]);
+ aRet.push_back(a[0]);
+ }
+
+ if (rChpx.fsKul)
+ {
+ aRet.push_back(69);
+ aRet.push_back(rChpx.kul);
+ }
+
+ if (rChpx.fsLid)
+ {
+ aRet.push_back(72);
+ SVBT16 a;
+ ShortToSVBT16(rChpx.lid, a);
+ aRet.push_back(a[1]);
+ aRet.push_back(a[0]);
+ }
+
+ if (rChpx.fsIco)
+ {
+ aRet.push_back(73);
+ aRet.push_back(rChpx.ico);
+ }
+
+ if (rChpx.fsHps)
+ {
+ aRet.push_back(74);
+
+ SVBT16 a;
+ ShortToSVBT16(rChpx.hps, a);
+ aRet.push_back(a[0]);
+ }
+
+ if (rChpx.fsPos)
+ {
+ aRet.push_back(76);
+ aRet.push_back(rChpx.hpsPos);
+ }
+
+ aRet.push_back(80);
+ aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) );
+
+ aRet.push_back(81);
+ aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) );
+
+ if (rChpx.fsFtcBi)
+ {
+ aRet.push_back(82);
+ SVBT16 a;
+ ShortToSVBT16(rChpx.fsFtcBi, a);
+ aRet.push_back(a[1]);
+ aRet.push_back(a[0]);
+ }
+
+ if (rChpx.fsLidBi)
+ {
+ aRet.push_back(83);
+ SVBT16 a;
+ ShortToSVBT16(rChpx.lidBi, a);
+ aRet.push_back(a[1]);
+ aRet.push_back(a[0]);
+ }
+
+ if (rChpx.fsIcoBi)
+ {
+ aRet.push_back(84);
+ aRet.push_back(rChpx.icoBi);
+ }
+
+ if (rChpx.fsHpsBi)
+ {
+ aRet.push_back(85);
+ SVBT16 a;
+ ShortToSVBT16(rChpx.hpsBi, a);
+ aRet.push_back(a[1]);
+ aRet.push_back(a[0]);
+ }
+
+ return aRet;
+}
+
+Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize)
+{
+ Word2CHPX aChpx;
+
+ if (!nSize || !checkSeek(rSt, nOffset))
+ return aChpx;
+
+ const size_t nMaxByteCount = rSt.remainingSize();
+ if (!nMaxByteCount)
+ return aChpx;
+
+ if (nSize > nMaxByteCount)
+ {
+ SAL_WARN("sw.ww8", "ReadWord2Chpx: truncating out of range "
+ << nSize << " to " << nMaxByteCount);
+ nSize = nMaxByteCount;
+ }
+
+ sal_uInt8 nCount=0;
+
+ while (true)
+ {
+ sal_uInt8 nFlags8;
+ rSt.ReadUChar( nFlags8 );
+ nCount++;
+
+ if (!rSt.good())
+ break;
+
+ aChpx.fBold = nFlags8 & 0x01;
+ aChpx.fItalic = (nFlags8 & 0x02) >> 1;
+ aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2;
+ aChpx.fOutline = (nFlags8 & 0x08) >> 3;
+ aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4;
+ aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5;
+ aChpx.fCaps = (nFlags8 & 0x40) >> 6;
+ aChpx.fVanish = (nFlags8 & 0x80) >> 7;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( nFlags8 );
+ nCount++;
+
+ if (!rSt.good())
+ break;
+
+ aChpx.fRMark = nFlags8 & 0x01;
+ aChpx.fSpec = (nFlags8 & 0x02) >> 1;
+ aChpx.fStrike = (nFlags8 & 0x04) >> 2;
+ aChpx.fObj = (nFlags8 & 0x08) >> 3;
+ aChpx.fBoldBi = (nFlags8 & 0x10) >> 4;
+ aChpx.fItalicBi = (nFlags8 & 0x20) >> 5;
+ aChpx.fBiDi = (nFlags8 & 0x40) >> 6;
+ aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( nFlags8 );
+ nCount++;
+
+ if (!rSt.good())
+ break;
+
+ aChpx.fsIco = nFlags8 & 0x01;
+ aChpx.fsFtc = (nFlags8 & 0x02) >> 1;
+ aChpx.fsHps = (nFlags8 & 0x04) >> 2;
+ aChpx.fsKul = (nFlags8 & 0x08) >> 3;
+ aChpx.fsPos = (nFlags8 & 0x10) >> 4;
+ aChpx.fsSpace = (nFlags8 & 0x20) >> 5;
+ aChpx.fsLid = (nFlags8 & 0x40) >> 6;
+ aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( nFlags8 );
+ nCount++;
+
+ if (!rSt.good())
+ break;
+
+ aChpx.fsFtcBi = nFlags8 & 0x01;
+ aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1;
+ aChpx.fsLidBi = (nFlags8 & 0x04) >> 2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt16( aChpx.ftc );
+ nCount+=2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt16( aChpx.hps );
+ nCount+=2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( nFlags8 );
+ nCount++;
+
+ if (!rSt.good())
+ break;
+
+ aChpx.qpsSpace = nFlags8 & 0x3F;
+ aChpx.fSysVanish = (nFlags8 & 0x40) >> 6;
+ aChpx.fNumRun = (nFlags8 & 0x80) >> 7;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( nFlags8 );
+ nCount++;
+
+ if (!rSt.good())
+ break;
+
+ aChpx.ico = nFlags8 & 0x1F;
+ aChpx.kul = (nFlags8 & 0xE0) >> 5;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( aChpx.hpsPos );
+ nCount++;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUChar( aChpx.icoBi );
+ nCount++;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt16( aChpx.lid );
+ nCount+=2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt16( aChpx.ftcBi );
+ nCount+=2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt16( aChpx.hpsBi );
+ nCount+=2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt16( aChpx.lidBi );
+ nCount+=2;
+
+ if (nCount >= nSize) break;
+ rSt.ReadUInt32( aChpx.fcPic );
+ nCount+=4;
+
+ break;
+ }
+
+ rSt.SeekRel(nSize-nCount);
+ return aChpx;
+}
+
+namespace
+{
+ struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; };
+}
+
+void WW8RStyle::ImportOldFormatStyles()
+{
+ for (sal_uInt16 i=0; i < m_cstd; ++i)
+ {
+ mpIo->m_vColl[i].m_bColl = true;
+ //every chain must end eventually at the null style (style code 222)
+ mpIo->m_vColl[i].m_nBase = 222;
+ }
+
+ rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(
+ mpIo->m_xWwFib->m_chseTables, mpIo->m_xWwFib->m_lid);
+
+ sal_uInt16 cstcStd(0);
+ m_rStream.ReadUInt16( cstcStd );
+
+ size_t nMaxByteCount = m_rStream.remainingSize();
+ sal_uInt16 cbName(0);
+ m_rStream.ReadUInt16(cbName);
+ if (cbName > nMaxByteCount)
+ {
+ SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
+ << cbName << " to " << nMaxByteCount);
+ cbName = nMaxByteCount;
+ }
+ sal_uInt16 nByteCount = 2;
+ sal_uInt16 stcp=0;
+ while (nByteCount < cbName)
+ {
+ sal_uInt8 nCount(0);
+ m_rStream.ReadUChar( nCount );
+ nByteCount++;
+
+ sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
+ if (stc >=mpIo->m_vColl.size())
+ continue;
+
+ SwWW8StyInf &rSI = mpIo->m_vColl[stc];
+ OUString sName;
+
+ if (nCount != 0xFF) // undefined style
+ {
+ if (nCount != 0) // user style
+ {
+ OString aTmp = read_uInt8s_ToOString(m_rStream, nCount);
+ nByteCount += aTmp.getLength();
+ sName = OStringToOUString(aTmp, eStructChrSet);
+ }
+ rSI.m_bImported = true;
+ }
+
+ if (sName.isEmpty())
+ {
+ ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
+ if (const char *pStr = GetEnglishNameFromSti(eSti))
+ sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US);
+ }
+
+ if (sName.isEmpty())
+ sName = "Unknown Style: " + OUString::number(stc);
+
+ rSI.SetOrgWWIdent(sName, stc);
+ stcp++;
+ }
+
+ sal_uInt16 nStyles=stcp;
+
+ std::vector<pxoffset> aCHPXOffsets(stcp);
+ nMaxByteCount = m_rStream.remainingSize();
+ sal_uInt16 cbChpx(0);
+ m_rStream.ReadUInt16(cbChpx);
+ if (cbChpx > nMaxByteCount)
+ {
+ SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
+ << cbChpx << " to " << nMaxByteCount);
+ cbChpx = nMaxByteCount;
+ }
+ nByteCount = 2;
+ stcp=0;
+ std::vector< std::vector<sal_uInt8> > aConvertedChpx;
+ while (nByteCount < cbChpx)
+ {
+ if (stcp == aCHPXOffsets.size())
+ {
+ //more data than style slots, skip remainder
+ m_rStream.SeekRel(cbChpx-nByteCount);
+ break;
+ }
+
+ sal_uInt8 cb(0);
+ m_rStream.ReadUChar( cb );
+ nByteCount++;
+
+ aCHPXOffsets[stcp].mnSize = 0;
+
+ if (cb != 0xFF)
+ {
+ sal_uInt8 nRemainder = cb;
+
+ aCHPXOffsets[stcp].mnOffset = m_rStream.Tell();
+ aCHPXOffsets[stcp].mnSize = nRemainder;
+
+ Word2CHPX aChpx = ReadWord2Chpx(m_rStream, aCHPXOffsets[stcp].mnOffset,
+ aCHPXOffsets[stcp].mnSize);
+ aConvertedChpx.push_back( ChpxToSprms(aChpx) );
+
+ nByteCount += nRemainder;
+ }
+ else
+ aConvertedChpx.emplace_back( );
+
+ ++stcp;
+ }
+
+ std::vector<pxoffset> aPAPXOffsets(stcp);
+ nMaxByteCount = m_rStream.remainingSize();
+ sal_uInt16 cbPapx(0);
+ m_rStream.ReadUInt16(cbPapx);
+ if (cbPapx > nMaxByteCount)
+ {
+ SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range "
+ << cbPapx << " to " << nMaxByteCount);
+ cbPapx = nMaxByteCount;
+ }
+ nByteCount = 2;
+ stcp=0;
+ while (nByteCount < cbPapx)
+ {
+ if (stcp == aPAPXOffsets.size())
+ {
+ m_rStream.SeekRel(cbPapx-nByteCount);
+ break;
+ }
+
+ sal_uInt8 cb(0);
+ m_rStream.ReadUChar( cb );
+ nByteCount++;
+
+ aPAPXOffsets[stcp].mnSize = 0;
+
+ if (cb != 0xFF)
+ {
+ sal_uInt8 stc2(0);
+ m_rStream.ReadUChar( stc2 );
+ m_rStream.SeekRel(6);
+ nByteCount+=7;
+ sal_uInt8 nRemainder = cb-7;
+
+ aPAPXOffsets[stcp].mnOffset = m_rStream.Tell();
+ aPAPXOffsets[stcp].mnSize = nRemainder;
+
+ m_rStream.SeekRel(nRemainder);
+ nByteCount += nRemainder;
+ }
+
+ ++stcp;
+ }
+
+ sal_uInt16 iMac(0);
+ m_rStream.ReadUInt16( iMac );
+
+ if (iMac > nStyles) iMac = nStyles;
+
+ std::map<OUString, sal_Int32> aParaCollisions;
+ std::map<OUString, sal_Int32> aCharCollisions;
+
+ for (stcp = 0; stcp < iMac; ++stcp)
+ {
+ sal_uInt8 stcNext(0), stcBase(0);
+ m_rStream.ReadUChar( stcNext );
+ m_rStream.ReadUChar( stcBase );
+
+ sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255);
+
+ /*
+ #i64557# style based on itself
+ every chain must end eventually at the null style (style code 222)
+ */
+ if (stc == stcBase)
+ stcBase = 222;
+
+ SwWW8StyInf &rSI = mpIo->m_vColl[stc];
+ rSI.m_nBase = stcBase;
+
+ ww::sti eSti = ww::GetCanonicalStiFromStc(stc);
+
+ if (eSti == ww::stiNil)
+ continue;
+
+ if (stcp >= aPAPXOffsets.size())
+ continue;
+
+ rSI.m_bValid = true;
+
+ if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize)
+ mpIo->m_vColl[stc].m_bColl = false;
+
+ bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext,
+ aParaCollisions,
+ aCharCollisions);
+
+ ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize,
+ true);
+
+ if (!aConvertedChpx[stcp].empty())
+ ImportSprms(aConvertedChpx[stcp].data(),
+ static_cast< short >(aConvertedChpx[stcp].size()),
+ false);
+
+ PostStyle(rSI, bOldNoImp);
+ }
+}
+
+void WW8RStyle::ImportNewFormatStyles()
+{
+ ScanStyles(); // Scan Based On
+
+ std::map<OUString, sal_Int32> aParaCollisions;
+ std::map<OUString, sal_Int32> aCharCollisions;
+
+ for (sal_uInt16 i = 0; i < m_cstd; ++i) // import Styles
+ if (mpIo->m_vColl[i].m_bValid)
+ Import1Style(i, aParaCollisions, aCharCollisions);
+}
+
+void WW8RStyle::Import()
+{
+ mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
+ mpIo->m_pStandardFormatColl =
+ mpIo->m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
+
+ if( mpIo->m_nIniFlags & WW8FL_NO_STYLES )
+ return;
+
+ if (mpIo->m_xWwFib->GetFIBVersion() <= ww::eWW2)
+ ImportOldFormatStyles();
+ else
+ ImportNewFormatStyles();
+
+ for (sal_uInt16 i = 0; i < m_cstd; ++i)
+ {
+ // Follow chain
+ SwWW8StyInf* pi = &mpIo->m_vColl[i];
+ sal_uInt16 j = pi->m_nFollow;
+ if( j < m_cstd )
+ {
+ SwWW8StyInf* pj = &mpIo->m_vColl[j];
+ if ( j != i // rational Index ?
+ && pi->m_pFormat // Format ok ?
+ && pj->m_pFormat // Derived-Format ok ?
+ && pi->m_bColl // only possible for paragraph templates (WW)
+ && pj->m_bColl ){ // identical Type ?
+ static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl(
+ *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register
+ }
+ }
+ }
+
+ // Missing special handling for default character template
+ // "Absatz-Standardschriftart" ( Style-ID 65 ).
+ // That is empty by default ( WW6 dt and US ) and not changeable
+ // via WW-UI so this does not matter.
+ // This could be done by:
+ // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() );
+
+ // for e.g. tables an always valid Std-Style is necessary
+
+ if( mpIo->StyleExists(0) && !mpIo->m_vColl.empty() &&
+ mpIo->m_vColl[0].m_pFormat && mpIo->m_vColl[0].m_bColl && mpIo->m_vColl[0].m_bValid )
+ mpIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(mpIo->m_vColl[0].m_pFormat);
+ else
+ mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl();
+
+ // set Hyphenation flag on BASIC para-style
+ if (mpIo->m_bNewDoc && mpIo->m_pStandardFormatColl)
+ {
+ if (mpIo->m_xWDop->fAutoHyphen
+ && SfxItemState::SET != mpIo->m_pStandardFormatColl->GetItemState(
+ RES_PARATR_HYPHENZONE, false) )
+ {
+ SvxHyphenZoneItem aAttr(true, RES_PARATR_HYPHENZONE);
+ aAttr.GetMinLead() = 2;
+ aAttr.GetMinTrail() = 2;
+ aAttr.GetMaxHyphens() = 0;
+
+ mpIo->m_pStandardFormatColl->SetFormatAttr( aAttr );
+ }
+ }
+
+ // we do not read styles anymore:
+ mpIo->m_pCurrentColl = nullptr;
+}
+
+rtl_TextEncoding SwWW8StyInf::GetCharSet() const
+{
+ if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
+ return m_eRTLFontSrcCharSet;
+ return m_eLTRFontSrcCharSet;
+}
+
+rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const
+{
+ if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB))
+ return m_eRTLFontSrcCharSet;
+ return m_eCJKFontSrcCharSet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par2.hxx b/sw/source/filter/ww8/ww8par2.hxx
new file mode 100644
index 0000000000..db7f704476
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par2.hxx
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8PAR2_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8PAR2_HXX
+
+#include <fmtfsize.hxx>
+#include <svl/itemset.hxx>
+#include <svx/swframetypes.hxx>
+#include <swtable.hxx>
+
+#include "ww8scan.hxx"
+#include "ww8par.hxx"
+
+class WW8RStyle;
+
+class WW8DupProperties
+{
+public:
+ WW8DupProperties(SwDoc &rDoc, SwWW8FltControlStack *pStack);
+ void Insert(const SwPosition &rPos);
+private:
+ WW8DupProperties(const WW8DupProperties&) = delete;
+ WW8DupProperties& operator=(const WW8DupProperties&) = delete;
+ SwWW8FltControlStack* m_pCtrlStck;
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1> m_aChrSet,m_aParSet;
+};
+
+struct WW8SwFlyPara
+{
+private:
+ std::unique_ptr<FrameDeleteWatch> m_xFlyFormat;
+public:
+ // part 1: directly derived Sw attributes
+ sal_Int16 nXPos, nYPos; // Position
+ sal_Int16 nLeftMargin, nRightMargin; // borders
+ sal_Int16 nUpperMargin, nLowerMargin; // borders
+ sal_Int16 nWidth, nHeight; // size
+ sal_Int16 nNetWidth;
+
+ SwFrameSize eHeightFix; // height fixed or min
+ static constexpr RndStdIds eAnchor = RndStdIds::FLY_AT_PARA; // binding
+ short eHRel; // page or page border
+ short eVRel; // page or page border
+ sal_Int16 eVAlign; // up, down, centered
+ sal_Int16 eHAlign; // left, right, centered
+ css::text::WrapTextMode
+ eSurround; // wrap mode
+
+ sal_uInt8 nXBind, nYBind; // bound relative to what
+
+ // part 2: changes found during reading
+ tools::Long nNewNetWidth;
+ std::shared_ptr<SwUnoCursor> xMainTextPos; // to return to main text after apo
+ sal_uInt16 nLineSpace; // line space in tw for graf apos
+ bool bAutoWidth;
+ bool bTogglePos;
+
+ // add parameter <nWWPgTop> - WW8's page top margin
+ WW8SwFlyPara( SwPaM& rPaM,
+ SwWW8ImplReader& rIo,
+ WW8FlyPara& rWW,
+ const sal_uInt32 nWWPgTop,
+ const sal_uInt32 nPgWidth,
+ const sal_Int32 nIniFlyDx,
+ const sal_Int32 nIniFlyDy );
+
+ void BoxUpWidth( tools::Long nWidth );
+ SwFlyFrameFormat* GetFlyFormat() const;
+ void SetFlyFormat(SwFlyFrameFormat* pNewFlyFormat);
+
+ std::unique_ptr<SwWW8FltAnchorStack> xOldAnchorStck;
+};
+
+class WW8RStyle: public WW8Style
+{
+friend class SwWW8ImplReader;
+ wwSprmParser maSprmParser;
+ SwWW8ImplReader* mpIo; // parser class
+ SvStream* mpStStrm; // input file
+
+ SwNumRule* mpStyRule; // bullets and enumerations in styles
+
+ sal_uInt8* mpParaSprms; // all ParaSprms of the UPX if UPX.Papx
+ sal_uInt16 mnSprmsLen; // its length
+
+ sal_uInt8 mnWwNumLevel; // for bullets and enumerations in styles
+
+ bool mbTextColChanged;
+ bool mbFontChanged; // For Simulating Default-Font
+ bool mbCJKFontChanged; // For Simulating Default-CJK Font
+ bool mbCTLFontChanged; // For Simulating Default-CTL Font
+ bool mbFSizeChanged; // For Simulating Default-FontSize
+ bool mbFCTLSizeChanged; // For Simulating Default-CTL FontSize
+ bool mbWidowsChanged; // For Simulating Default-Widows / Orphans
+ bool mbBidiChanged; // For Simulating Default-LTR
+
+ void ImportSprms(std::size_t nPosFc, short nLen, bool bPap);
+ void ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap);
+ void ImportGrupx(short nLen, bool bPara, bool bOdd);
+ short ImportUPX(short nLen, bool bPAP, bool bOdd);
+
+ void Set1StyleDefaults();
+ void Import1Style(sal_uInt16 nNr,
+ std::map<OUString, sal_Int32>& rParaCollisions,
+ std::map<OUString, sal_Int32>& rCharCollisions);
+ void RecursiveReg(sal_uInt16 nNr);
+
+ void ImportNewFormatStyles();
+ void ScanStyles();
+ void ImportOldFormatStyles();
+
+ bool PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle, sal_uInt16 nNextStyle,
+ std::map<OUString, sal_Int32>& rParaCollisions,
+ std::map<OUString, sal_Int32>& rCharCollisions);
+ void PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp);
+
+ WW8RStyle(const WW8RStyle&) = delete;
+ WW8RStyle& operator=(const WW8RStyle&) = delete;
+public:
+ WW8RStyle( WW8Fib& rFib, SwWW8ImplReader* pI );
+ void Import();
+ void PostProcessStyles();
+ SprmResult HasParaSprm(sal_uInt16 nId) const;
+};
+
+class WW8FlySet: public SfxItemSetFixed<RES_FRMATR_BEGIN,RES_FRMATR_END-1>
+{
+private:
+ const WW8FlySet& operator=(const WW8FlySet&) = delete;
+ void Init(const SwWW8ImplReader& rReader, const SwPaM* pPaM);
+public:
+ WW8FlySet(SwWW8ImplReader& rReader, const WW8FlyPara* pFW,
+ const WW8SwFlyPara* pFS, bool bGraf);
+ WW8FlySet(SwWW8ImplReader& rReader, const SwPaM* pPaM, const WW8_PIC& rPic,
+ tools::Long nWidth, tools::Long nHeight);
+};
+
+// Gets filled in WW8TabDesc::MergeCells().
+// Algorithm must ensure proper row and column order in WW8SelBoxInfo!
+class WW8SelBoxInfo
+{
+private:
+ std::vector<std::vector<SwTableBox*> > m_vRows;
+
+ WW8SelBoxInfo(WW8SelBoxInfo const&) = delete;
+ WW8SelBoxInfo& operator=(WW8SelBoxInfo const&) = delete;
+
+public:
+ short m_nGroupXStart;
+ short m_nGroupWidth;
+ bool m_bGroupLocked;
+
+ WW8SelBoxInfo(short nXCenter, short nWidth)
+ : m_nGroupXStart( nXCenter ), m_nGroupWidth( nWidth ), m_bGroupLocked(false)
+ {}
+
+ size_t size() const
+ {
+ size_t nResult = 0;
+ for (auto& it : m_vRows)
+ nResult += it.size();
+ return nResult;
+ }
+
+ size_t rowsCount() const { return m_vRows.size(); }
+
+ const std::vector<SwTableBox*>& row( size_t nIndex ) { return m_vRows[nIndex]; }
+
+ void push_back( SwTableBox* pBox )
+ {
+ bool bDone = false;
+ for (auto& iRow : m_vRows)
+ if (iRow[0]->GetUpper() == pBox->GetUpper())
+ {
+ iRow.push_back(pBox);
+ bDone = true;
+ break;
+ }
+ if (!bDone)
+ {
+ const size_t sz = m_vRows.size();
+ m_vRows.resize(sz+1);
+ m_vRows[sz].push_back(pBox);
+ }
+ }
+};
+
+class WW8TabDesc
+{
+ std::vector<OUString> m_aNumRuleNames;
+ std::unique_ptr<sw::util::RedlineStack> mxOldRedlineStack;
+
+ SwWW8ImplReader* m_pIo;
+
+ WW8TabBandDesc* m_pFirstBand;
+ WW8TabBandDesc* m_pActBand;
+
+ std::shared_ptr<SwUnoCursor> m_xTmpPos;
+
+ SwTableNode* m_pTableNd; // table node
+ const SwTableLines* m_pTabLines; // row array of node
+ SwTableLine* m_pTabLine; // current row
+ SwTableBoxes* m_pTabBoxes; // boxes array in current row
+ SwTableBox* m_pTabBox; // current cell
+
+ std::vector<std::unique_ptr<WW8SelBoxInfo>> m_MergeGroups; // list of all cells to be merged
+
+ WW8_TCell* m_pCurrentWWCell;
+
+ short m_nRows;
+ short m_nDefaultSwCols;
+ short m_nBands;
+ short m_nMinLeft;
+ short m_nMaxRight;
+ short m_nSwWidth;
+ short m_nPreferredWidth;
+ short m_nPercentWidth;
+
+ bool m_bOk;
+ bool m_bClaimLineFormat;
+ sal_Int16 m_eOri;
+ bool m_bIsBiDi;
+ // 2. common admin info
+ short m_nCurrentRow;
+ short m_nCurrentBandRow; // SW: row of current band
+ // 3. admin info for writer
+ short m_nCurrentCol;
+
+ sal_uInt16 m_nRowsToRepeat;
+
+ // 4. methods
+
+ sal_uInt16 GetLogicalWWCol() const;
+ void SetTabBorders( SwTableBox* pBox, short nIdx );
+ void SetTabShades( SwTableBox* pBox, short nWwIdx );
+ void SetTabVertAlign( SwTableBox* pBox, short nWwIdx );
+ void SetTabDirection( SwTableBox* pBox, short nWwIdx );
+ void CalcDefaults();
+ void SetPamInCell(short nWwCol, bool bPam);
+ void InsertCells( short nIns );
+ void AdjustNewBand();
+
+ WW8SelBoxInfo* FindMergeGroup(short nX1, short nWidth, bool bExact);
+
+ // single box - maybe used in a merge group
+ // (the merge groups are processed later at once)
+ void UpdateTableMergeGroup(WW8_TCell const & rCell,
+ WW8SelBoxInfo* pActGroup, SwTableBox* pActBox, sal_uInt16 nCol );
+
+ WW8TabDesc(WW8TabDesc const&) = delete;
+ WW8TabDesc& operator=(WW8TabDesc const&) = delete;
+
+public:
+ const SwTable* m_pTable; // table
+ SwPosition* m_pParentPos;
+ SwFlyFrameFormat* m_pFlyFormat;
+ SfxItemSet m_aItemSet;
+ bool IsValidCell(short nCol) const;
+ bool InFirstParaInCell() const;
+
+ WW8TabDesc( SwWW8ImplReader* pIoClass, WW8_CP nStartCp );
+ bool Ok() const { return m_bOk; }
+ void CreateSwTable();
+ void UseSwTable();
+ void SetSizePosition(SwFrameFormat* pFrameFormat);
+ void TableCellEnd();
+ void MoveOutsideTable();
+ void ParkPaM();
+ void FinishSwTable();
+ void MergeCells();
+ short GetMinLeft() const { return m_nMinLeft; }
+ ~WW8TabDesc();
+
+ const WW8_TCell* GetCurrentWWCell() const { return m_pCurrentWWCell; }
+ short GetCurrentCol() const { return m_nCurrentCol; }
+ // find name of numrule valid for current WW-COL
+ OUString GetNumRuleName() const;
+ void SetNumRuleName( const OUString& rName );
+
+ sw::util::RedlineStack* getOldRedlineStack() { return mxOldRedlineStack.get(); }
+};
+
+enum WW8LvlType {WW8_None, WW8_Outline, WW8_Numbering, WW8_Sequence, WW8_Pause};
+
+WW8LvlType GetNumType(sal_uInt8 nWwLevelNo);
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par3.cxx b/sw/source/filter/ww8/ww8par3.cxx
new file mode 100644
index 0000000000..d0a294b144
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par3.cxx
@@ -0,0 +1,2589 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <string_view>
+#include <svl/itemiter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+#include <sal/log.hxx>
+
+#include <vcl/unohelp.hxx>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <algorithm>
+#include <hintids.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/postitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/temporary.hxx>
+#include <unotextrange.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <docsh.hxx>
+#include <numrule.hxx>
+#include <paratr.hxx>
+#include <charatr.hxx>
+#include <charfmt.hxx>
+#include <ndtxt.hxx>
+#include <expfld.hxx>
+#include <fmtfld.hxx>
+#include <flddropdown.hxx>
+#include "sprmids.hxx"
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "ww8par.hxx"
+#include "ww8par2.hxx"
+
+#include <IMark.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <rtl/character.hxx>
+#include <xmloff/odffields.hxx>
+#include <comphelper/string.hxx>
+
+using namespace com::sun::star;
+using namespace sw::util;
+using namespace sw::types;
+using namespace sw::mark;
+
+// UNO-Controls
+
+// OCX i.e. word 97 form controls
+eF_ResT SwWW8ImplReader::Read_F_OCX( WW8FieldDesc*, OUString& )
+{
+ if( m_bObj && m_nPicLocFc )
+ m_nObjLocFc = m_nPicLocFc;
+ m_bEmbeddObj = true;
+ return eF_ResT::TEXT;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_FormTextBox( WW8FieldDesc* pF, OUString& rStr )
+{
+ WW8FormulaEditBox aFormula(*this);
+
+ sal_Int32 const nPos(rStr.indexOf(0x01));
+ if (pF->nLCode && nPos != -1 && nPos < pF->nLCode) {
+ ImportFormulaControl(aFormula, pF->nSCode + nPos, WW8_CT_EDIT);
+ }
+
+ /*
+ Here we have a small complication. This formula control contains
+ the default text that is displayed if you edit the form field in
+ the "default text" area. But MSOffice does not display that
+ information, instead it display the result of the field,
+ MSOffice just uses the default text of the control as its
+ initial value for the displayed default text. So we will swap in
+ the field result into the formula here in place of the default
+ text.
+ */
+
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ const bool bUseEnhFields = rOpt.IsUseEnhancedFields();
+
+ if (!bUseEnhFields)
+ {
+ aFormula.msDefault = GetFieldResult(pF);
+
+ SwInputField aField(
+ static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
+ aFormula.msDefault,
+ aFormula.msTitle,
+ INP_TXT,
+ 0 );
+ aField.SetHelp(aFormula.msHelp);
+ aField.SetToolTip(aFormula.msToolTip);
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ return eF_ResT::OK;
+ }
+ else
+ {
+ WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
+ OUString aBookmarkName;
+ if (pB!=nullptr) {
+ WW8_CP currentCP=pF->nSCode;
+ WW8_CP currentLen=pF->nLen;
+
+ WW8_CP nEnd;
+ if (o3tl::checked_add(currentCP, currentLen-1, nEnd)) {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ }
+ else
+ {
+ sal_uInt16 bkmFindIdx;
+ OUString aBookmarkFind=pB->GetBookmark(currentCP-1, nEnd, bkmFindIdx);
+
+ if (!aBookmarkFind.isEmpty()) {
+ pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark bookmark as consumed, such that it'll not get inserted as a "normal" bookmark again
+ if (!aBookmarkFind.isEmpty()) {
+ aBookmarkName=aBookmarkFind;
+ }
+ }
+ }
+ }
+
+ if (pB!=nullptr && aBookmarkName.isEmpty()) {
+ aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
+ }
+
+ if (!aBookmarkName.isEmpty()) {
+ m_aFieldStack.back().SetBookmarkName(aBookmarkName);
+ m_aFieldStack.back().SetBookmarkType(ODF_FORMTEXT);
+ if ( aFormula.msToolTip.getLength() < 139 )
+ m_aFieldStack.back().getParameters()["Description"] <<= aFormula.msToolTip;
+ m_aFieldStack.back().getParameters()["Name"] <<= aFormula.msTitle;
+ if (aFormula.mnMaxLen && aFormula.mnMaxLen < 32768 )
+ m_aFieldStack.back().getParameters()["MaxLength"] <<= aFormula.mnMaxLen;
+
+ if ( aFormula.mfType == 1 )
+ m_aFieldStack.back().getParameters()["Type"] <<= OUString("number");
+ else if ( aFormula.mfType == 2 )
+ m_aFieldStack.back().getParameters()["Type"] <<= OUString("date");
+ else if ( aFormula.mfType == 3 )
+ m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentTime");
+ else if ( aFormula.mfType == 4 )
+ m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentDate");
+ else if ( aFormula.mfType == 5 )
+ m_aFieldStack.back().getParameters()["Type"] <<= OUString("calculated");
+ }
+ return eF_ResT::TEXT;
+ }
+}
+
+eF_ResT SwWW8ImplReader::Read_F_FormCheckBox( WW8FieldDesc* pF, OUString& rStr )
+{
+ WW8FormulaCheckBox aFormula(*this);
+
+ if (!m_xFormImpl)
+ m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM));
+
+ if (rStr[pF->nLCode-1]==0x01)
+ ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_CHECKBOX);
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ const bool bUseEnhFields = rOpt.IsUseEnhancedFields();
+
+ if (!bUseEnhFields)
+ {
+ m_xFormImpl->InsertFormula(aFormula);
+ return eF_ResT::OK;
+ }
+
+ OUString aBookmarkName;
+ WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
+ if (pB!=nullptr) {
+ WW8_CP currentCP=pF->nSCode;
+ WW8_CP currentLen=pF->nLen;
+
+ sal_uInt16 bkmFindIdx;
+ OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx);
+
+ if (!aBookmarkFind.isEmpty()) {
+ pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field
+ if (!aBookmarkFind.isEmpty()) {
+ aBookmarkName=aBookmarkFind;
+ }
+ }
+ }
+
+ if (pB!=nullptr && aBookmarkName.isEmpty()) {
+ aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
+ }
+
+ if (!aBookmarkName.isEmpty())
+ {
+ IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
+ IFieldmark* pFieldmark = pMarksAccess->makeNoTextFieldBookmark(
+ *m_pPaM, aBookmarkName, ODF_FORMCHECKBOX );
+ OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
+ if (pFieldmark!=nullptr) {
+ IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters();
+ ICheckboxFieldmark* pCheckboxFm = dynamic_cast<ICheckboxFieldmark*>(pFieldmark);
+ (*pParameters)[ODF_FORMCHECKBOX_HELPTEXT] <<= aFormula.msToolTip;
+
+ if(pCheckboxFm)
+ pCheckboxFm->SetChecked(aFormula.mnChecked != 0);
+ // set field data here...
+ }
+ }
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_FormListBox( WW8FieldDesc* pF, OUString& rStr)
+{
+ WW8FormulaListBox aFormula(*this);
+
+ if (pF->nLCode > 0 && rStr.getLength() >= pF->nLCode && rStr[pF->nLCode-1] == 0x01)
+ ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_DROPDOWN);
+
+ const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ bool bUseEnhFields = rOpt.IsUseEnhancedFields();
+
+ if (!bUseEnhFields)
+ {
+ SwDropDownField aField(static_cast<SwDropDownFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Dropdown)));
+
+ aField.SetName(aFormula.msTitle);
+ aField.SetHelp(aFormula.msHelp);
+ aField.SetToolTip(aFormula.msToolTip);
+
+ if (!aFormula.maListEntries.empty())
+ {
+ aField.SetItems(std::vector(aFormula.maListEntries));
+ int nIndex = aFormula.mfDropdownIndex < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : 0;
+ aField.SetSelectedItem(aFormula.maListEntries[nIndex]);
+ }
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ return eF_ResT::OK;
+ }
+ else
+ {
+ // TODO: review me
+ OUString aBookmarkName;
+ WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
+ if (pB!=nullptr)
+ {
+ WW8_CP currentCP=pF->nSCode;
+ WW8_CP currentLen=pF->nLen;
+
+ sal_uInt16 bkmFindIdx;
+ OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx);
+
+ if (!aBookmarkFind.isEmpty())
+ {
+ pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field
+ if (!aBookmarkFind.isEmpty())
+ aBookmarkName=aBookmarkFind;
+ }
+ }
+
+ if (pB!=nullptr && aBookmarkName.isEmpty())
+ aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
+
+ if (!aBookmarkName.isEmpty())
+ {
+ IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
+ IFieldmark *pFieldmark =
+ pMarksAccess->makeNoTextFieldBookmark( *m_pPaM, aBookmarkName, ODF_FORMDROPDOWN );
+ OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
+ if ( pFieldmark != nullptr )
+ {
+ uno::Sequence< OUString > vListEntries(aFormula.maListEntries.size());
+ std::copy(aFormula.maListEntries.begin(), aFormula.maListEntries.end(), vListEntries.getArray());
+ (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntries;
+ sal_Int32 nIndex = aFormula.mfDropdownIndex < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : -1;
+ if (nIndex >= 0)
+ (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_RESULT] <<= nIndex;
+ // set field data here...
+ }
+ }
+
+ return eF_ResT::OK;
+ }
+}
+
+eF_ResT SwWW8ImplReader::Read_F_HTMLControl(WW8FieldDesc*, OUString&)
+{
+ if( m_bObj && m_nPicLocFc )
+ m_nObjLocFc = m_nPicLocFc;
+ m_bEmbeddObj = true;
+ return eF_ResT::TEXT;
+}
+
+// Helper declarations
+
+// Style Id's for each level
+typedef sal_uInt16 WW8aIdSty[WW8ListManager::nMaxLevel];
+// Character Style Pointer
+typedef SwCharFormat* WW8aCFormat[WW8ListManager::nMaxLevel];
+
+namespace {
+
+struct WW8LST // only THOSE entries, WE need!
+{
+ WW8aIdSty aIdSty; // Style Id's for each level,
+ // nIStDNil if no style linked
+ sal_uInt32 nIdLst; // Unique List ID
+ sal_uInt32 nTplC; // Unique template code - What is this?
+ bool bSimpleList:1; // Flag: List only has ONE level
+ bool bRestartHdn:1; // WW6-Compatibility-Flag:
+ // true if the list should start numbering over
+}; // at the beginning of each section
+
+}
+
+const sal_uInt32 cbLSTF=28;
+
+namespace {
+
+struct WW8LFO // only THOSE entries, WE need!
+{
+ SwNumRule* pNumRule; // Parent NumRule
+ sal_uInt32 nIdLst; // Unique List ID
+ sal_uInt8 nLfoLvl; // count of levels whose format is overridden
+ bool bSimpleList;
+};
+
+struct WW8LVL // only THE entries, WE need!
+{
+ sal_Int32 nStartAt; // start at value for this value
+ sal_Int32 nV6DxaSpace;// Ver6-Compatible: min Space between Num and text::Paragraph
+ sal_Int32 nV6Indent; // Ver6-Compatible: Width of prefix text;
+ // Use definition of first line indent if appropriate!
+ // Paragraph attributes from GrpprlPapx
+ sal_uInt16 nDxaLeft; // left indent
+ short nDxaLeft1; // first line indent
+
+ sal_uInt8 nNFC; // number format code
+ // Offset of fieldcodes in Num-X-String
+ sal_uInt8 aOfsNumsXCH[WW8ListManager::nMaxLevel];
+ sal_uInt8 nLenGrpprlChpx; // length, in bytes, of the LVL's grpprlChpx
+ sal_uInt8 nLenGrpprlPapx; // length, in bytes, of the LVL's grpprlPapx
+ sal_uInt8 nAlign; // alignment (left, right, centered) of the number
+ bool bV6Prev; // Ver6-Compatible: number will include previous levels
+ bool bV6PrSp; // Ver6-Compatible: doesn't matter
+ bool bV6; // if true, pay attention to the V6-Compatible Entries!
+
+};
+
+struct WW8LFOLVL
+{
+ sal_Int32 nStartAt; // start-at value if bFormat==false and bStartAt == true
+ // (if bFormat==true, the start-at is stored in the LVL)
+ sal_uInt8 nLevel; // the level to be overridden
+ // this byte has not been packed into the following byte on _purpose_ !!
+ // (see comment of struct WW8LFOInfo)
+
+ bool bStartAt :1; // true if the start-at value is overridden
+ bool bFormat :1; // true if the formatting is overridden
+
+ WW8LFOLVL() :
+ nStartAt(1), nLevel(0), bStartAt(true), bFormat(false) {}
+};
+
+}
+
+// Data to be saved in ListInfo
+
+struct WW8LSTInfo // sorted by nIdLst (in WW8 used list-Id)
+{
+ std::vector<ww::bytes> maParaSprms;
+ WW8aIdSty aIdSty; // Style Id's for each level
+ WW8aCFormat aCharFormat = {}; // Character Style Pointer
+
+ SwNumRule* pNumRule; // Pointer to list-template in Writer
+ sal_uInt32 nIdLst; // WW8Id of this list
+ bool bSimpleList:1;// Flag, if this NumRule only uses one Level
+ bool bUsedInDoc :1;// Flag, if this NumRule is used in the Doc,
+ // or is supposed to be deleted on Reader-End
+
+ WW8LSTInfo(SwNumRule* pNumRule_, const WW8LST& aLST)
+ : pNumRule(pNumRule_), nIdLst(aLST.nIdLst),
+ bSimpleList(aLST.bSimpleList), bUsedInDoc(false)
+ {
+ memcpy( aIdSty, aLST.aIdSty, sizeof( aIdSty ));
+ }
+
+};
+
+// Data to be saved in ListenFormatOverrideInfos
+
+struct WW8LFOInfo // unordered, means ordered like in WW8 Stream
+{
+ std::vector<ww::bytes> maParaSprms;
+ std::vector<WW8LFOLVL> maOverrides;
+ SwNumRule* pNumRule; // Pointer to list template in Writer
+ // either List in LSTInfos or own List
+ // (in Ctor use the list from LSTInfos first)
+
+ sal_uInt32 nIdLst; // WW8-Id of the relevant list
+ sal_uInt8 nLfoLvl; // count of levels whose format is overridden
+ // yes we could include nLfoLvl (via :4) into the following byte,
+ // but it probably would be a source of error once MS increases their Listformat
+ // to more than 15 levels
+
+ bool bOverride :1;// Flag if NumRule is not included in maLSTInfos,
+ // but was created for m_LFOInfos
+ bool bUsedInDoc :1;// Flag if NumRule is used in Doc,
+ // or should be deleted on Reader-End
+ bool bLSTbUIDSet :1;// Flag, if bUsedInDoc is set in maLSTInfos
+
+ explicit WW8LFOInfo(const WW8LFO& rLFO);
+};
+
+WW8LFOInfo::WW8LFOInfo(const WW8LFO& rLFO)
+ : maParaSprms(WW8ListManager::nMaxLevel)
+ , maOverrides(WW8ListManager::nMaxLevel)
+ , pNumRule(rLFO.pNumRule)
+ , nIdLst(rLFO.nIdLst)
+ , nLfoLvl(rLFO.nLfoLvl)
+ , bOverride(rLFO.nLfoLvl != 0)
+ , bUsedInDoc(false)
+ , bLSTbUIDSet(false)
+{
+}
+
+// Helper methods
+
+// find Sprm-Parameter-Data, if Sprm is included in Grpprl
+SprmResult WW8ListManager::GrpprlHasSprm(sal_uInt16 nId, sal_uInt8& rSprms,
+ sal_uInt8 nLen)
+{
+ return maSprmParser.findSprmData(nId, &rSprms, nLen);
+}
+
+namespace {
+
+class ListWithId
+{
+private:
+ sal_uInt32 mnIdLst;
+public:
+ explicit ListWithId(sal_uInt32 nIdLst) : mnIdLst(nIdLst) {}
+ bool operator() (const std::unique_ptr<WW8LSTInfo>& pEntry) const
+ { return (pEntry->nIdLst == mnIdLst); }
+};
+
+}
+
+// Access via List-Id of LST Entry
+WW8LSTInfo* WW8ListManager::GetLSTByListId( sal_uInt32 nIdLst ) const
+{
+ auto aResult =
+ std::find_if(maLSTInfos.begin(),maLSTInfos.end(),ListWithId(nIdLst));
+ if (aResult == maLSTInfos.end())
+ return nullptr;
+ return aResult->get();
+}
+
+SvxNumType WW8ListManager::GetSvxNumTypeFromMSONFC(sal_uInt16 nNFC)
+{
+ SvxNumType nType(SVX_NUM_ARABIC);
+
+ switch (nNFC)
+ {
+ case 0:
+ nType = SVX_NUM_ARABIC;
+ break;
+ case 1:
+ nType = SVX_NUM_ROMAN_UPPER;
+ break;
+ case 2:
+ nType = SVX_NUM_ROMAN_LOWER;
+ break;
+ case 3:
+ nType = SVX_NUM_CHARS_UPPER_LETTER_N;
+ break;
+ case 4:
+ nType = SVX_NUM_CHARS_LOWER_LETTER_N;
+ break;
+ case 5: // ordinal
+ nType = SVX_NUM_TEXT_NUMBER;
+ break;
+ case 6: // cardinalText
+ nType = SVX_NUM_TEXT_CARDINAL;
+ break;
+ case 7: // ordinalText
+ nType = SVX_NUM_TEXT_ORDINAL;
+ break;
+ //case 8: // hex
+
+ case 9:
+ // 0x09, msonfcChiManSty
+ nType = SVX_NUM_SYMBOL_CHICAGO;
+ break;
+ //case 15: // decimalHalfWidth
+ //case 17: // japaneseDigitalTenThousand
+
+ case 18: // decimalEnclosedCircle
+ case 28: // decimalEnclosedCircleChinese
+ case 29: // ideographEnclosedCircle
+ nType = SVX_NUM_CIRCLE_NUMBER;
+ break;
+ case 22:
+ // 0x16, msonfcArabicLZ
+ nType = SVX_NUM_ARABIC_ZERO;
+ break;
+ case 23:
+ nType = SVX_NUM_CHAR_SPECIAL;
+
+ break;
+ case 255:
+ nType = SVX_NUM_NUMBER_NONE;
+ break;
+ case 14:
+ case 19:
+ nType = SVX_NUM_FULL_WIDTH_ARABIC;
+ break;
+ case 30:
+ nType = SVX_NUM_TIAN_GAN_ZH;
+ break;
+ case 31: // ideographZodiac
+ case 32: // ideographZodiacTraditional
+ nType = SVX_NUM_DI_ZI_ZH;
+ break;
+ case 33: // taiwaneseCounting
+ case 35:
+ case 36:
+ case 37:
+ case 11:
+ case 39:
+ nType = SVX_NUM_NUMBER_LOWER_ZH;
+ break;
+ case 34:
+ nType = SVX_NUM_NUMBER_UPPER_ZH_TW;
+ break;
+ case 38:
+ nType = SVX_NUM_NUMBER_UPPER_ZH;
+ break;
+ case 10:
+ case 16: // japaneseLegal
+ nType = SVX_NUM_NUMBER_TRADITIONAL_JA;
+ break;
+ case 20:
+ nType = SVX_NUM_AIU_FULLWIDTH_JA;
+ break;
+ case 12:
+ nType = SVX_NUM_AIU_HALFWIDTH_JA;
+ break;
+ case 21:
+ nType = SVX_NUM_IROHA_FULLWIDTH_JA;
+ break;
+ case 13:
+ nType = SVX_NUM_IROHA_HALFWIDTH_JA;
+ break;
+ case 24:
+ nType = SVX_NUM_HANGUL_SYLLABLE_KO;
+ break;
+ case 25:
+ nType = SVX_NUM_HANGUL_JAMO_KO;
+ break;
+ //case 26: // decimalEnclosedFullstop
+ //case 27: // decimalEnclosedParen
+ //case 40: // decimal (Chinese)
+
+ case 41: // koreanDigital
+ case 42: // koreanCounting
+ case 43: // koreanLegal
+ nType = SVX_NUM_NUMBER_HANGUL_KO;
+ break;
+ case 44: // koreanDigital2
+ nType = SVX_NUM_NUMBER_UPPER_KO;
+ break;
+ case 45: // hebrew1
+ nType = SVX_NUM_NUMBER_HEBREW;
+ break;
+ case 46: // arabicAlpha
+ nType = SVX_NUM_CHARS_ARABIC;
+ break;
+ case 47: // hebrew2
+ nType = SVX_NUM_CHARS_HEBREW;
+ break;
+ case 48: // arabicAbjad
+ nType = SVX_NUM_CHARS_ARABIC_ABJAD;
+ break;
+ case 49: // hindiVowels
+ nType = SVX_NUM_CHARS_NEPALI;
+ break;
+ //case 50: // hindiConsonants
+ //case 51: // hindiNumbers
+ //case 52: // hindiCounting
+
+ case 53: // thaiLetters
+ nType = SVX_NUM_CHARS_THAI;
+ break;
+ //case 54: // thaiNumbers
+ //case 55: // thaiCounting
+ //case 56: // vietnameseCounting
+ //case 57: // numberInDash
+
+ case 58: // russianLower
+ nType = SVX_NUM_CHARS_CYRILLIC_LOWER_LETTER_RU;
+ break;
+ case 59: // russianUpper
+ nType =SVX_NUM_CHARS_CYRILLIC_UPPER_LETTER_RU;
+ break;
+ default:
+ nType = SVX_NUM_ARABIC;
+ break;
+ }
+
+ return nType;
+}
+
+bool WW8ListManager::ReadLVL(SwNumFormat& rNumFormat, std::unique_ptr<SfxItemSet>& rpItemSet,
+ sal_uInt16 nLevelStyle, bool bSetStartNo, sal_uInt16 /*nLevel*/, ww::bytes &rParaSprms)
+{
+ sal_uInt8 aBits1(0);
+ SvxNumType nType(SVX_NUM_ARABIC);
+ SvxAdjust eAdj; // Alignment (Left/right/centered)
+ sal_UCS4 cBullet(0x2190); // default safe bullet
+
+ sal_Unicode cGrfBulletCP(USHRT_MAX);
+
+ WW8LVL aLVL = {};
+
+ // 1. read LVLF
+
+ m_rSt.ReadInt32( aLVL.nStartAt );
+ m_rSt.ReadUChar( aLVL.nNFC );
+ m_rSt.ReadUChar( aBits1 );
+ if( ERRCODE_NONE != m_rSt.GetError() ) return false;
+ aLVL.nAlign = (aBits1 & 0x03);
+ if( aBits1 & 0x10 ) aLVL.bV6Prev = true;
+ if( aBits1 & 0x20 ) aLVL.bV6PrSp = true;
+ if( aBits1 & 0x40 ) aLVL.bV6 = true;
+ bool bLVLOkB = true;
+ for(sal_uInt8 nLevelB = 0; nLevelB < nMaxLevel; ++nLevelB)
+ {
+ m_rSt.ReadUChar( aLVL.aOfsNumsXCH[ nLevelB ] );
+ if( ERRCODE_NONE != m_rSt.GetError() )
+ {
+ bLVLOkB = false;
+ break;
+ }
+ }
+
+ if( !bLVLOkB )
+ return false;
+
+ sal_uInt8 ixchFollow(0);
+ m_rSt.ReadUChar( ixchFollow );
+ m_rSt.ReadInt32( aLVL.nV6DxaSpace );
+ m_rSt.ReadInt32( aLVL.nV6Indent );
+ m_rSt.ReadUChar( aLVL.nLenGrpprlChpx );
+ m_rSt.ReadUChar( aLVL.nLenGrpprlPapx );
+ m_rSt.SeekRel( 2 );
+ if( ERRCODE_NONE != m_rSt.GetError()) return false;
+
+ // 2. read PAPx if needed and search for indent values
+
+ short nTabPos = 0; // #i86652# - read tab setting
+ if( aLVL.nLenGrpprlPapx )
+ {
+ sal_uInt8 aGrpprlPapx[ 255 ];
+ if (aLVL.nLenGrpprlPapx != m_rSt.ReadBytes(&aGrpprlPapx, aLVL.nLenGrpprlPapx))
+ return false;
+ // "sprmPDxaLeft" pap.dxaLeft;dxa;word;
+ SprmResult aSprm = GrpprlHasSprm(0x840F,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
+ if (!aSprm.pSprm)
+ aSprm = GrpprlHasSprm(0x845E,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
+
+ if (aSprm.pSprm && aSprm.nRemainingData >= 2)
+ {
+ const sal_uInt8 *pBegin = aSprm.pSprm - 2;
+ for(int i=0;i<4;++i)
+ rParaSprms.push_back(*pBegin++);
+ short nDxaLeft = SVBT16ToUInt16(aSprm.pSprm);
+ aLVL.nDxaLeft = (0 < nDxaLeft) ? o3tl::narrowing<sal_uInt16>(nDxaLeft)
+ : o3tl::narrowing<sal_uInt16>(-nDxaLeft);
+ }
+
+ // "sprmPDxaLeft1" pap.dxaLeft1;dxa;word;
+ aSprm = GrpprlHasSprm(0x8411,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
+ if (!aSprm.pSprm)
+ aSprm = GrpprlHasSprm(0x8460,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
+
+ if (aSprm.pSprm && aSprm.nRemainingData >= 2)
+ {
+ const sal_uInt8 *pBegin = aSprm.pSprm - 2;
+ for(int i=0;i<4;++i)
+ rParaSprms.push_back(*pBegin++);
+ aLVL.nDxaLeft1 = SVBT16ToUInt16(aSprm.pSprm);
+ }
+
+ // #i86652# - read tab setting
+ aSprm = GrpprlHasSprm(0xC615,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
+ const sal_uInt8* pSprm = aSprm.pSprm;
+ if (pSprm && aSprm.nRemainingData >= 5)
+ {
+ bool bDone = false;
+ if (*(pSprm-1) == 5)
+ {
+ if (*pSprm++ == 0) //nDel
+ {
+ if (*pSprm++ == 1) //nIns
+ {
+ nTabPos = SVBT16ToUInt16(pSprm);
+ pSprm+=2;
+ if (*pSprm == 6) //type
+ {
+ bDone = true;
+ }
+ }
+ }
+ }
+ OSL_ENSURE(bDone, "tab setting in numbering is "
+ "of unexpected configuration");
+ }
+ if ( rNumFormat.GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ // If there is a tab setting with a larger value, then use that.
+ // Ideally we would allow tabs to be used in numbering fields and set
+ // this on the containing paragraph which would make it actually work
+ // most of the time.
+ if ( nTabPos != 0 )
+ {
+ const sal_uInt16 nDesired = aLVL.nDxaLeft + aLVL.nDxaLeft1;
+
+ bool bDoAdjust = false;
+ if ( nDesired < aLVL.nDxaLeft )
+ {
+ if ( nDesired < nTabPos && nTabPos < aLVL.nDxaLeft )
+ {
+ bDoAdjust = true;
+ }
+ }
+ else
+ {
+ if ( aLVL.nDxaLeft < nTabPos && nTabPos < nDesired )
+ {
+ bDoAdjust = true;
+ }
+ }
+
+ if (bDoAdjust)
+ {
+ aLVL.nDxaLeft = (0 < nTabPos)
+ ? o3tl::narrowing<sal_uInt16>(nTabPos)
+ : o3tl::narrowing<sal_uInt16>(-nTabPos);
+
+ aLVL.nDxaLeft1 = nDesired - aLVL.nDxaLeft;
+ }
+ }
+ }
+ }
+
+ // 3. read CHPx if needed
+
+ sal_uInt16 nWitchPicIsBullet = USHRT_MAX;
+ bool bIsPicBullet = false;
+
+ if( aLVL.nLenGrpprlChpx )
+ {
+ sal_uInt8 aGrpprlChpx[ 255 ] = {};
+ if (aLVL.nLenGrpprlChpx != m_rSt.ReadBytes(&aGrpprlChpx, aLVL.nLenGrpprlChpx))
+ return false;
+
+ //For i120928,parse the graphic info of bullets
+ SprmResult aSprmWhichPis = GrpprlHasSprm(NS_sprm::CPbiIBullet::val, aGrpprlChpx[0],aLVL.nLenGrpprlChpx);
+ SprmResult aSprmIsPicBullet = GrpprlHasSprm(NS_sprm::CPbiGrf::val, aGrpprlChpx[0],aLVL.nLenGrpprlChpx);
+ if (aSprmWhichPis.pSprm && aSprmWhichPis.nRemainingData >= 1)
+ {
+ nWitchPicIsBullet = *aSprmWhichPis.pSprm;
+ }
+ if (aSprmIsPicBullet.pSprm && aSprmIsPicBullet.nRemainingData >= 1)
+ {
+ bIsPicBullet = (*aSprmIsPicBullet.pSprm) & 0x0001;
+ }
+
+ // create new Itemset for character attributes
+ rpItemSet.reset(new SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>( m_rDoc.GetAttrPool() ));
+
+ // Set Reader-ItemSet-Pointer to the newly created set
+ m_rReader.SetCurrentItemSet(std::move(rpItemSet));
+ // Set Reader-Style to Style of this Level
+ sal_uInt16 nOldColl = m_rReader.GetCurrentColl();
+ sal_uInt16 nNewColl = nLevelStyle;
+ if (ww::stiNil == nNewColl)
+ nNewColl = 0;
+ m_rReader.SetNCurrentColl( nNewColl );
+
+ // The Read_xy() methods in WW8PAR6.cxx are calling their respective
+ // NewAttr() or GetFormatAttr() which can determine, by using the assigned
+ // Reader-ItemSet-Pointer, whether this specific ItemSet is relevant
+ // and not a Stack or Style!
+ sal_uInt16 nOldFlags1 = m_rReader.GetToggleAttrFlags();
+ sal_uInt16 nOldFlags2 = m_rReader.GetToggleBiDiAttrFlags();
+
+ WW8SprmIter aSprmIter(&aGrpprlChpx[0], aLVL.nLenGrpprlChpx,
+ maSprmParser);
+ while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
+ {
+ m_rReader.ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
+ aSprmIter.advance();
+ }
+
+ // Reset Reader-ItemSet-Pointer and Reader-Style
+ rpItemSet = m_rReader.SetCurrentItemSet(nullptr);
+ m_rReader.SetNCurrentColl( nOldColl );
+ m_rReader.SetToggleAttrFlags(nOldFlags1);
+ m_rReader.SetToggleBiDiAttrFlags(nOldFlags2);
+ }
+
+ // 4. Read numbering String. Results in prefix and postfix
+
+ OUString sNumString(comphelper::string::sanitizeStringSurrogates(read_uInt16_PascalString(m_rSt)));
+
+ // 5. convert read values into Writer syntax
+
+ nType = GetSvxNumTypeFromMSONFC(aLVL.nNFC);
+ //For i120928,type info
+ if (bIsPicBullet)
+ {
+ nType = SVX_NUM_BITMAP;
+ }
+
+ if (style::NumberingType::CHAR_SPECIAL == nType)
+ {
+ cBullet = !sNumString.isEmpty()
+ ? sNumString.iterateCodePoints(&o3tl::temporary(sal_Int32(0))) : 0x2190;
+
+ if (!cBullet) // unsave control code?
+ cBullet = 0x2190;
+ }
+ else if (style::NumberingType::BITMAP == nType) //For i120928,position index info of graphic
+ {
+ cGrfBulletCP = nWitchPicIsBullet; // This is a bullet picture ID
+ }
+
+ switch( aLVL.nAlign )
+ {
+ case 0:
+ eAdj = SvxAdjust::Left;
+ break;
+ case 1:
+ eAdj = SvxAdjust::Center;
+ break;
+ case 2:
+ eAdj = SvxAdjust::Right;
+ break;
+ case 3:
+ // Writer here cannot do block justification
+ eAdj = SvxAdjust::Left;
+ break;
+ default:
+ // undefined value
+ OSL_ENSURE( false, "Value of aLVL.nAlign is not supported" );
+ // take default
+ eAdj = SvxAdjust::Left;
+ break;
+ }
+
+ // 6. Configure NumFormat
+ if( bSetStartNo && 0 <= aLVL.nStartAt)
+ rNumFormat.SetStart(o3tl::narrowing<sal_uInt16>(aLVL.nStartAt));
+ rNumFormat.SetNumberingType( nType );
+ rNumFormat.SetNumAdjust( eAdj );
+
+ if( style::NumberingType::CHAR_SPECIAL == nType )
+ {
+ // first character of the Prefix-Text is the Bullet
+ rNumFormat.SetBulletChar(cBullet);
+ // Don't forget: further below, after building styles
+ // Call SetBulletFont() !!!
+ }
+ //For i120928,position index info
+ else if (style::NumberingType::BITMAP == nType)
+ {
+ rNumFormat.SetGrfBulletCP(cGrfBulletCP);
+ }
+ else
+ {
+ // Replace symbols at aOfsNumsXCH offsets to %1%, %2% as supported by LO
+ OUString sListFormat = sNumString;
+ if (sListFormat.getLength())
+ {
+ sal_uInt32 nExtraOffset = 0;
+ sal_uInt8 nLevelB = 0;
+ while (nLevelB < nMaxLevel && aLVL.aOfsNumsXCH[nLevelB])
+ {
+ // Replacement symbol is read from source string from position taken from aOfsNumsXCH array
+ sal_uInt8 nOffset = aLVL.aOfsNumsXCH[nLevelB] + nExtraOffset - 1;
+ if (nOffset >= sListFormat.getLength())
+ {
+ SAL_WARN("sw.ww8", "List level reference is beyond the border. Ignored.");
+ nLevelB++;
+ continue;
+ }
+ sal_uInt8 nReplacement = sListFormat[nOffset] + 1;
+
+ OUString sReplacement("%" + OUString::number(nReplacement) + "%");
+ sListFormat = sListFormat.replaceAt(nOffset, 1, sReplacement);
+
+ // We need also update an offset, since we are replacing one symbol by at least two
+ nExtraOffset += sReplacement.getLength() - 1;
+ nLevelB++;
+ }
+ }
+
+ rNumFormat.SetListFormat(sListFormat);
+
+ // Total count of replacement holders is determining amount of required parent numbering to include
+ // TODO: not sure how "%" symbol is escaped. This is not supported yet
+ sal_Int16 nParentNum = comphelper::string::getTokenCount(sListFormat, '%');
+ rNumFormat.SetIncludeUpperLevels(nParentNum);
+ }
+
+ // #i89181#
+ if ( rNumFormat.GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ if (eAdj == SvxAdjust::Right)
+ {
+ rNumFormat.SetAbsLSpace(aLVL.nDxaLeft);
+ rNumFormat.SetFirstLineOffset(-aLVL.nDxaLeft);
+ rNumFormat.SetCharTextDistance(-aLVL.nDxaLeft1);
+ }
+ else
+ {
+ rNumFormat.SetAbsLSpace( aLVL.nDxaLeft );
+ rNumFormat.SetFirstLineOffset(aLVL.nDxaLeft1);
+ }
+ }
+ else
+ {
+ rNumFormat.SetIndentAt( aLVL.nDxaLeft );
+ rNumFormat.SetFirstLineIndent(aLVL.nDxaLeft1);
+ if ( !aLVL.bV6 )
+ rNumFormat.SetListtabPos( nTabPos );
+ else
+ rNumFormat.SetListtabPos( aLVL.nV6Indent );
+ SvxNumberFormat::LabelFollowedBy eNumLabelFollowedBy = SvxNumberFormat::LISTTAB;
+ switch ( ixchFollow )
+ {
+ case 0:
+ {
+ eNumLabelFollowedBy = SvxNumberFormat::LISTTAB;
+ }
+ break;
+ case 1:
+ {
+ eNumLabelFollowedBy = SvxNumberFormat::SPACE;
+ }
+ break;
+ case 2:
+ {
+ eNumLabelFollowedBy = SvxNumberFormat::NOTHING;
+ }
+ break;
+ }
+ rNumFormat.SetLabelFollowedBy( eNumLabelFollowedBy );
+ }
+
+ return true;
+}
+
+void WW8ListManager::AdjustLVL( sal_uInt8 nLevel, SwNumRule& rNumRule,
+ WW8aISet const & rListItemSet, WW8aCFormat& rCharFormat, bool& bNewCharFormatCreated,
+ const OUString& sPrefix )
+{
+ bNewCharFormatCreated = false;
+ sal_uInt8 nIdenticalItemSetLevel;
+ const SfxPoolItem* pItem;
+
+ SwNumFormat aNumFormat = rNumRule.Get( nLevel );
+
+ SfxItemSet* pThisLevelItemSet = rListItemSet[nLevel].get();
+
+ if( pThisLevelItemSet && pThisLevelItemSet->Count())
+ {
+ nIdenticalItemSetLevel = nMaxLevel;
+ SfxItemIter aIter( *pThisLevelItemSet );
+ for (sal_uInt8 nLowerLevel = 0; nLowerLevel < nLevel; ++nLowerLevel)
+ {
+ SfxItemSet* pLowerLevelItemSet = rListItemSet[nLowerLevel].get();
+ if( pLowerLevelItemSet
+ && (pLowerLevelItemSet->Count() == pThisLevelItemSet->Count()) )
+ {
+ nIdenticalItemSetLevel = nLowerLevel;
+ const SfxPoolItem* pItemIter = aIter.GetCurItem();
+ do
+ {
+ if( // search for appropriate pItem in pLowerLevelItemSet
+ (SfxItemState::SET != pLowerLevelItemSet->GetItemState(
+ pItemIter->Which(), false, &pItem ) )
+ || // use virtual "!=" Operator
+ (*pItem != *pItemIter) )
+ // if no Item with equal nWhich was found or Item value was not equal
+ // store inequality and break!
+ {
+ nIdenticalItemSetLevel = nMaxLevel;
+ break;
+ }
+ pItemIter = aIter.NextItem();
+ } while (pItemIter);
+
+ if( nIdenticalItemSetLevel != nMaxLevel )
+ break;
+ }
+ }
+
+ SwCharFormat* pFormat;
+ if (nMaxLevel == nIdenticalItemSetLevel)
+ {
+ // Define Style
+ const OUString aName( (!sPrefix.isEmpty() ? sPrefix : rNumRule.GetName())
+ + "z" + OUString::number( nLevel ) );
+
+ // remove const by casting
+ pFormat = m_rDoc.MakeCharFormat(aName, m_rDoc.GetDfltCharFormat());
+ bNewCharFormatCreated = true;
+ // Set Attributes
+ pFormat->SetFormatAttr( *pThisLevelItemSet );
+ }
+ else
+ {
+ // append Style
+ pFormat = rCharFormat[ nIdenticalItemSetLevel ];
+ }
+
+ // store
+ rCharFormat[ nLevel ] = pFormat;
+
+ // Append Style to NumFormat
+
+ aNumFormat.SetCharFormat( pFormat );
+ }
+
+ // if necessary: Append Bullet Font to NumFormat
+
+ if( SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType() )
+ {
+ SwCharFormat* pFormat = aNumFormat.GetCharFormat();
+ vcl::Font aFont;
+ if( !pFormat )
+ {
+ aFont = numfunc::GetDefBulletFont();
+ }
+ else
+ {
+ const SvxFontItem& rFontItem = pFormat->GetFont();
+ aFont.SetFamily( rFontItem.GetFamily() );
+ aFont.SetFamilyName( rFontItem.GetFamilyName() );
+ aFont.SetStyleName( rFontItem.GetStyleName() );
+ aFont.SetPitch( rFontItem.GetPitch() );
+ aFont.SetCharSet( rFontItem.GetCharSet() );
+ }
+ aNumFormat.SetBulletFont( &aFont );
+ }
+
+ // Set NumFormat in NumRule
+
+ rNumRule.Set(nLevel, aNumFormat);
+}
+
+SwNumRule* WW8ListManager::CreateNextRule(bool bSimple)
+{
+ // Used to build the Style Name
+ const OUString sPrefix("WW8Num" + OUString::number(m_nUniqueList++));
+ // #i86652#
+ sal_uInt16 nRul =
+ m_rDoc.MakeNumRule( m_rDoc.GetUniqueNumRuleName(&sPrefix), nullptr, false,
+ SvxNumberFormat::LABEL_ALIGNMENT );
+ SwNumRule* pMyNumRule = m_rDoc.GetNumRuleTable()[nRul];
+ pMyNumRule->SetAutoRule(false);
+ pMyNumRule->SetContinusNum(bSimple);
+ return pMyNumRule;
+}
+
+SwNumRule* WW8ListManager::GetNumRule(size_t i)
+{
+ if (i < maLSTInfos.size())
+ return maLSTInfos[i]->pNumRule;
+ else
+ return nullptr;
+}
+
+// public methods
+
+WW8ListManager::WW8ListManager(SvStream& rSt_, SwWW8ImplReader& rReader_)
+ : maSprmParser(rReader_.GetFib()), m_rReader(rReader_)
+ , m_rDoc(m_rReader.GetDoc())
+ , m_rFib(m_rReader.GetFib()), m_rSt(rSt_)
+ , m_nUniqueList(1)
+ , m_nLastLFOPosition(USHRT_MAX)
+{
+
+ // LST and LFO only since WW8
+ if( ( 8 > m_rFib.m_nVersion )
+ || ( m_rFib.m_fcPlcfLst == m_rFib.m_fcPlfLfo )
+ || ( m_rFib.m_lcbPlcfLst < 2 )
+ || ( m_rFib.m_lcbPlfLfo < 2) ) return; // no public lists
+
+ // create Arrays
+ bool bLVLOk = true;
+
+ sal_uInt64 nOriginalPos = m_rSt.Tell();
+
+ // 1. read PLCF LST and create list templates in Writer
+
+ bool bOk = checkSeek(m_rSt, m_rFib.m_fcPlcfLst);
+
+ if (!bOk)
+ return;
+
+ sal_uInt32 nRemainingPlcfLst = m_rFib.m_lcbPlcfLst;
+
+ sal_uInt16 nListCount(0);
+ m_rSt.ReadUInt16( nListCount );
+ nRemainingPlcfLst -= 2;
+ bOk = nListCount > 0;
+
+ if (!bOk)
+ return;
+
+ // 1.1 read all LST
+ const size_t nMinRecordSize = 10 + 2*nMaxLevel;
+ const size_t nMaxRecords = m_rSt.remainingSize() / nMinRecordSize;
+ if (nListCount > nMaxRecords)
+ {
+ SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nListCount << " claimed, truncating");
+ nListCount = nMaxRecords;
+ }
+ for (sal_uInt16 nList=0; nList < nListCount; ++nList)
+ {
+ if (nRemainingPlcfLst < cbLSTF)
+ break;
+
+ WW8LST aLST = {};
+
+ // 1.1.1 read Data
+
+ m_rSt.ReadUInt32( aLST.nIdLst );
+ m_rSt.ReadUInt32( aLST.nTplC );
+ for (sal_uInt16 & nLevel : aLST.aIdSty)
+ m_rSt.ReadUInt16( nLevel );
+
+ sal_uInt8 aBits1(0);
+ m_rSt.ReadUChar( aBits1 );
+
+ m_rSt.SeekRel( 1 );
+
+ if( aBits1 & 0x01 )
+ aLST.bSimpleList = true;
+ if( aBits1 & 0x02 )
+ aLST.bRestartHdn = true;
+
+ // 1.1.2 new NumRule inserted in Doc and WW8LSTInfo marked
+
+ /*
+ #i1869#
+ In word 2000 microsoft got rid of creating new "simple lists" with
+ only 1 level, all new lists are created with 9 levels. To hack it
+ so that the list types formerly known as simple lists still have
+ their own tab page to themselves one of the reserved bits is used
+ to show that a given list is to be in the simple list tabpage.
+ This has now nothing to do with the actual number of list level a
+ list has, only how many will be shown in the user interface.
+
+ i.e. create a simple list in 2000 and open it in 97 and 97 will
+ claim (correctly) that it is an outline list. We can set our
+ continuous flag in these lists to store this information.
+ */
+ SwNumRule* pMyNumRule = CreateNextRule(
+ aLST.bSimpleList || (aBits1 & 0x10));
+
+ WW8LSTInfo* pLSTInfo = new WW8LSTInfo(pMyNumRule, aLST);
+ maLSTInfos.emplace_back(pLSTInfo);
+
+ nRemainingPlcfLst -= cbLSTF;
+ }
+
+ // 1.2 read all LVL of all aLST
+
+ sal_uInt16 nLSTInfos = static_cast< sal_uInt16 >(maLSTInfos.size());
+ for (sal_uInt16 nList = 0; nList < nLSTInfos; ++nList)
+ {
+ WW8aISet aItemSet; // Character attributes from GrpprlChpx
+
+ WW8LSTInfo* pListInfo = maLSTInfos[nList].get();
+ if( !pListInfo || !pListInfo->pNumRule ) break;
+ SwNumRule& rMyNumRule = *pListInfo->pNumRule;
+
+ // 1.2.1 read specific LVL(s) for this aLST
+
+ sal_uInt16 nLvlCount = static_cast< sal_uInt16 >(pListInfo->bSimpleList ? nMinLevel : nMaxLevel);
+ pListInfo->maParaSprms.resize(nMaxLevel);
+ for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel)
+ {
+ SwNumFormat aNumFormat( rMyNumRule.Get( nLevel ) );
+ // read LVLF
+ bLVLOk = ReadLVL( aNumFormat, aItemSet[nLevel],
+ pListInfo->aIdSty[nLevel], true, nLevel,
+ pListInfo->maParaSprms[nLevel]);
+ if( !bLVLOk )
+ break;
+ // and set in rMyNumRule
+ rMyNumRule.Set( nLevel, aNumFormat );
+ }
+ if( !bLVLOk )
+ break;
+
+ // 1.2.2 compare ItemPools and CHPx Settings of different Levels
+ // and create Style(s) if necessary
+
+ for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel)
+ {
+ bool bDummy;
+ AdjustLVL( nLevel, rMyNumRule, aItemSet,
+ pListInfo->aCharFormat, bDummy );
+ }
+ }
+
+ // 2. read and save PLF LFO
+
+ bOk = checkSeek(m_rSt, m_rFib.m_fcPlfLfo);
+
+ if (!bOk)
+ return;
+
+ sal_Int32 nLfoCount(0);
+ m_rSt.ReadInt32( nLfoCount );
+ bOk = nLfoCount > 0;
+
+ if (!bOk)
+ return;
+
+ // 2.1 read all LFO
+
+ for (sal_Int32 nLfo = 0; nLfo < nLfoCount; ++nLfo)
+ {
+ bOk = false;
+
+ WW8LFO aLFO = {};
+
+ m_rSt.ReadUInt32( aLFO.nIdLst );
+ m_rSt.SeekRel( 8 );
+ m_rSt.ReadUChar( aLFO.nLfoLvl );
+ if (!m_rSt.good())
+ break;
+ m_rSt.SeekRel( 3 );
+ // as many Overrides as there are
+ if ((nMaxLevel < aLFO.nLfoLvl) || m_rSt.GetError())
+ break;
+
+ // get the Parent NumRule of the current List
+ WW8LSTInfo* pParentListInfo = GetLSTByListId(aLFO.nIdLst);
+ if (pParentListInfo)
+ {
+ // Save the NumRule in this first step
+ aLFO.pNumRule = pParentListInfo->pNumRule;
+
+ // are there multiple Levels in the List?
+ aLFO.bSimpleList = pParentListInfo->bSimpleList;
+ }
+ // store in Array
+ std::unique_ptr<WW8LFOInfo> pLFOInfo(new WW8LFOInfo(aLFO));
+ if (pParentListInfo)
+ {
+ //Copy the basic paragraph properties for each level from the
+ //original list into the list format override levels.
+ int nMaxSize = pParentListInfo->maParaSprms.size();
+ pLFOInfo->maParaSprms.resize(nMaxSize);
+ for (int i = 0; i < nMaxSize; ++i)
+ pLFOInfo->maParaSprms[i] = pParentListInfo->maParaSprms[i];
+ }
+ m_LFOInfos.push_back(std::move(pLFOInfo));
+ bOk = true;
+ }
+
+ if( bOk )
+ {
+
+ // 2.2 read specific LFOLVL for all LFO
+
+ size_t nLFOInfos = m_LFOInfos.size();
+ for (size_t nLfo = 0; nLfo < nLFOInfos; ++nLfo)
+ {
+ WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLfo];
+ // Do LFOLVL exist?
+ if( rLFOInfo.bOverride )
+ {
+ WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst);
+ if (!pParentListInfo)
+ break;
+
+ // 2.2.1 create new NumRule for this List
+
+ SwNumRule* pParentNumRule = rLFOInfo.pNumRule;
+ OSL_ENSURE(pParentNumRule, "ww: Impossible lists, please report");
+ if( !pParentNumRule )
+ break;
+ // create name-prefix for NumRule-Name
+ // and (if necessary) for Style-Name
+ const OUString sPrefix("WW8NumSt" + OUString::number( nLfo + 1 ));
+ // Now assign pNumRule its actual value!!!
+ // (it contained the parent NumRule up to this point)
+
+ // check if a Style is referencing this LFO
+ if( USHRT_MAX > m_rReader.StyleUsingLFO( nLfo ) )
+ {
+ sal_uInt16 nRul = m_rDoc.MakeNumRule(
+ m_rDoc.GetUniqueNumRuleName( &sPrefix ), pParentNumRule);
+ rLFOInfo.pNumRule = m_rDoc.GetNumRuleTable()[ nRul ];
+ rLFOInfo.pNumRule->SetAutoRule(false);
+ }
+ else
+ {
+ sal_uInt16 nRul = m_rDoc.MakeNumRule(
+ m_rDoc.GetUniqueNumRuleName(), pParentNumRule);
+ rLFOInfo.pNumRule = m_rDoc.GetNumRuleTable()[ nRul ];
+ rLFOInfo.pNumRule->SetAutoRule(true); // = default
+ }
+
+ // 2.2.2 read all LFOLVL (and LVL) for the new NumRule
+
+ WW8aISet aItemSet; // Character attributes from GrpprlChpx
+ WW8aCFormat aCharFormat = {}; // Character Style Pointer
+
+ //2.2.2.0 skip inter-group of override header ?
+ //See #i25438# for why I moved this here, compare
+ //that original bugdoc's binary to what it looks like
+ //when resaved with word, i.e. there is always a
+ //4 byte header, there might be more than one if
+ //that header was 0xFFFFFFFF, e.g. #114412# ?
+ sal_uInt32 nTest;
+ m_rSt.ReadUInt32( nTest );
+ do
+ {
+ nTest = 0;
+ m_rSt.ReadUInt32( nTest );
+ }
+ while (nTest == 0xFFFFFFFF);
+ m_rSt.SeekRel(-4);
+
+ for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel)
+ {
+ WW8LFOLVL aLFOLVL;
+ bLVLOk = false;
+
+ // 2.2.2.1 read LFOLVL
+
+ m_rSt.ReadInt32( aLFOLVL.nStartAt );
+ sal_uInt8 aBits1(0);
+ m_rSt.ReadUChar( aBits1 );
+ m_rSt.SeekRel( 3 );
+ if (m_rSt.GetError())
+ break;
+
+ // Note: MS writes the Override-Level-Number into 4 bit.
+ // We do not! (See comment at "struct WW8LFOInfo")
+ aLFOLVL.nLevel = aBits1 & 0x0F;
+ if( (0xFF > aBits1) &&
+ (nMaxLevel > aLFOLVL.nLevel) )
+ {
+ if (aBits1 & 0x10)
+ aLFOLVL.bStartAt = true;
+ else
+ aLFOLVL.bStartAt = false;
+
+ // 2.2.2.2 load dedicated LVL if necessary
+
+ SwNumFormat aNumFormat(
+ rLFOInfo.pNumRule->Get(aLFOLVL.nLevel));
+ if (aBits1 & 0x20)
+ {
+ aLFOLVL.bFormat = true;
+ // if bStartup is true, replace Startup-Level
+ // with the LVLF that is saved in the LVL
+ bLVLOk = nLevel < rLFOInfo.maParaSprms.size() &&
+ ReadLVL(aNumFormat, aItemSet[nLevel],
+ pParentListInfo->aIdSty[nLevel],
+ aLFOLVL.bStartAt, nLevel,
+ rLFOInfo.maParaSprms[nLevel]);
+
+ if (!bLVLOk)
+ break;
+ }
+ else if (aLFOLVL.bStartAt)
+ {
+ aNumFormat.SetStart(
+ writer_cast<sal_uInt16>(aLFOLVL.nStartAt));
+ }
+
+ // 2.2.2.3 Set NumFormat in NumRule
+
+ rLFOInfo.pNumRule->Set(aLFOLVL.nLevel, aNumFormat);
+ }
+ bLVLOk = true;
+
+ if (nMaxLevel > aLFOLVL.nLevel)
+ rLFOInfo.maOverrides[aLFOLVL.nLevel] = aLFOLVL;
+ }
+ if( !bLVLOk )
+ break;
+
+ // 2.2.3 adjust LVL of the new NumRule
+
+ bool bNewCharFormatCreated = false;
+ for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel)
+ {
+ AdjustLVL( nLevel, *rLFOInfo.pNumRule, aItemSet, aCharFormat,
+ bNewCharFormatCreated, sPrefix );
+ }
+ }
+ }
+ }
+ // and we're done!
+ m_rSt.Seek( nOriginalPos );
+}
+
+void WW8ListManager::ImplDestroy()
+{
+ /*
+ named lists remain in document
+ unused automatic lists are removed from document (DelNumRule)
+ */
+ for(auto & rpInfo : maLSTInfos)
+ {
+ if (rpInfo->pNumRule && !rpInfo->bUsedInDoc &&
+ rpInfo->pNumRule->IsAutoRule())
+ {
+ m_rDoc.DelNumRule(rpInfo->pNumRule->GetName());
+ }
+ rpInfo.reset();
+ }
+ for (auto aIter = m_LFOInfos.rbegin(); aIter != m_LFOInfos.rend(); ++aIter)
+ {
+ if ((*aIter)->bOverride
+ && (*aIter)->pNumRule
+ && !(*aIter)->bUsedInDoc
+ && (*aIter)->pNumRule->IsAutoRule())
+ {
+ m_rDoc.DelNumRule( (*aIter)->pNumRule->GetName() );
+ }
+ }
+}
+
+WW8ListManager::~WW8ListManager()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+static bool IsEqualFormatting(const SwNumRule &rOne, const SwNumRule &rTwo)
+{
+ bool bRet =
+ (
+ rOne.GetRuleType() == rTwo.GetRuleType() &&
+ rOne.IsContinusNum() == rTwo.IsContinusNum() &&
+ rOne.IsAbsSpaces() == rTwo.IsAbsSpaces() &&
+ rOne.GetPoolFormatId() == rTwo.GetPoolFormatId() &&
+ rOne.GetPoolHelpId() == rTwo.GetPoolHelpId() &&
+ rOne.GetPoolHlpFileId() == rTwo.GetPoolHlpFileId()
+ );
+
+ if (bRet)
+ {
+ for (sal_uInt8 n = 0; n < MAXLEVEL; ++n )
+ {
+ //The SvxNumberFormat compare, not the SwNumFormat compare
+ const SvxNumberFormat &rO = rOne.Get(n);
+ const SvxNumberFormat &rT = rTwo.Get(n);
+ if (rO != rT)
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+SwNumRule* WW8ListManager::GetNumRuleForActivation(sal_uInt16 nLFOPosition,
+ const sal_uInt8 nLevel, std::vector<sal_uInt8> &rParaSprms, SwTextNode *pNode)
+{
+ if (m_LFOInfos.size() <= nLFOPosition)
+ return nullptr;
+
+ WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLFOPosition];
+
+ bool bFirstUse = !rLFOInfo.bUsedInDoc;
+ rLFOInfo.bUsedInDoc = true;
+
+ if( !rLFOInfo.pNumRule )
+ return nullptr;
+
+ // #i25545#
+ // #i100132# - a number format does not have to exist on given list level
+ SwNumFormat aFormat(rLFOInfo.pNumRule->Get(nLevel));
+
+ if (m_rReader.IsRightToLeft() && m_nLastLFOPosition != nLFOPosition) {
+ if ( aFormat.GetNumAdjust() == SvxAdjust::Right)
+ aFormat.SetNumAdjust(SvxAdjust::Left);
+ else if ( aFormat.GetNumAdjust() == SvxAdjust::Left)
+ aFormat.SetNumAdjust(SvxAdjust::Right);
+ rLFOInfo.pNumRule->Set(nLevel, aFormat);
+ }
+ m_nLastLFOPosition = nLFOPosition;
+ /*
+ #i1869#
+ If this list has had its bits set in word 2000 to pretend that it is a
+ simple list from the point of view of the user, then it is almost
+ certainly a simple continuous list, and we will try to keep it like that.
+ Otherwise when we save again it will be shown as the true outline list
+ that it is, confusing the user that just wanted what they thought was a
+ simple list. On the other hand it is possible that some of the other levels
+ were used by the user, in which case we will not pretend anymore that it
+ is a simple list. Something that word 2000 does anyway, that 97 didn't, to
+ my bewilderment.
+ */
+ if (nLevel && rLFOInfo.pNumRule->IsContinusNum())
+ rLFOInfo.pNumRule->SetContinusNum(false);
+
+ if( (!rLFOInfo.bOverride) && (!rLFOInfo.bLSTbUIDSet) )
+ {
+ WW8LSTInfo* pParentListInfo = GetLSTByListId( rLFOInfo.nIdLst );
+ if( pParentListInfo )
+ pParentListInfo->bUsedInDoc = true;
+ rLFOInfo.bLSTbUIDSet = true;
+ }
+
+ if (rLFOInfo.maParaSprms.size() > nLevel)
+ rParaSprms = rLFOInfo.maParaSprms[nLevel];
+
+ SwNumRule *pRet = rLFOInfo.pNumRule;
+
+ bool bRestart(false);
+ sal_uInt16 nStart(0);
+ bool bNewstart(false);
+ /*
+ Note: If you fiddle with this then you have to make sure that #i18322#
+ #i13833#, #i20095# and #112466# continue to work
+
+ Check if there were overrides for this level
+ */
+ if (rLFOInfo.bOverride && nLevel < rLFOInfo.nLfoLvl)
+ {
+ WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst);
+ OSL_ENSURE(pParentListInfo, "ww: Impossible lists, please report");
+ if (pParentListInfo && pParentListInfo->pNumRule)
+ {
+ const WW8LFOLVL &rOverride = rLFOInfo.maOverrides[nLevel];
+ bool bNoChangeFromParent =
+ IsEqualFormatting(*pRet, *(pParentListInfo->pNumRule));
+
+ //If so then I think word still uses the parent (maybe)
+ if (bNoChangeFromParent)
+ {
+ pRet = pParentListInfo->pNumRule;
+
+ //did it not affect start at value ?
+ if (bFirstUse && rOverride.bStartAt)
+ {
+ const SwNumFormat &rFormat =
+ pParentListInfo->pNumRule->Get(nLevel);
+ if (
+ rFormat.GetStart() ==
+ rLFOInfo.maOverrides[nLevel].nStartAt
+ )
+ {
+ bRestart = true;
+ }
+ else
+ {
+ bNewstart = true;
+ nStart = writer_cast<sal_uInt16>
+ (rLFOInfo.maOverrides[nLevel].nStartAt);
+ }
+ }
+
+ pParentListInfo->bUsedInDoc = true;
+ }
+ }
+ }
+
+ if (pNode)
+ {
+ pNode->SetAttrListLevel(nLevel);
+
+ if (bRestart || bNewstart)
+ pNode->SetListRestart(true);
+ if (bNewstart)
+ pNode->SetAttrListRestartValue(nStart);
+ }
+ return pRet;
+}
+
+// SwWW8ImplReader: append a List to a Style or Paragraph
+
+bool SwWW8ImplReader::SetTextFormatCollAndListLevel(const SwPaM& rRg,
+ SwWW8StyInf& rStyleInfo)
+{
+ bool bRes = true;
+ if( rStyleInfo.m_pFormat && rStyleInfo.m_bColl )
+ {
+ bRes = m_rDoc.SetTextFormatColl(rRg, static_cast<SwTextFormatColl*>(rStyleInfo.m_pFormat));
+ SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode();
+ OSL_ENSURE( pTextNode, "No Text-Node at PaM-Position" );
+ if ( !pTextNode )
+ {
+ // make code robust
+ return bRes;
+ }
+
+ const SwNumRule * pNumRule = pTextNode->GetNumRule(); // #i27610#
+
+ if( !IsInvalidOrToBeMergedTabCell() &&
+ ! (pNumRule && pNumRule->IsOutlineRule()) ) // #i27610#
+ {
+ pTextNode->ResetAttr( RES_PARATR_NUMRULE );
+ }
+
+ if (USHRT_MAX > rStyleInfo.m_nLFOIndex && WW8ListManager::nMaxLevel
+ > rStyleInfo.m_nListLevel)
+ {
+ const bool bApplyListStyle = false;
+ RegisterNumFormatOnTextNode(rStyleInfo.m_nLFOIndex, rStyleInfo.m_nListLevel,
+ bApplyListStyle);
+ }
+ }
+ return bRes;
+}
+
+void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat)
+{
+ // #i86652#
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ const auto nAbsLSpace = rFormat.GetAbsLSpace();
+ const tools::Long nListFirstLineIndent = GetListFirstLineIndent(rFormat);
+ SvxFirstLineIndentItem firstLine(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT));
+ leftMargin.SetTextLeft(nAbsLSpace);
+ firstLine.SetTextFirstLineOffset(writer_cast<short>(nListFirstLineIndent));
+ rStyle.m_pFormat->SetFormatAttr(firstLine);
+ rStyle.m_pFormat->SetFormatAttr(leftMargin);
+ rStyle.m_bListRelevantIndentSet = true;
+ }
+}
+
+void SetStyleIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat)
+{
+ if ( rFormat.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) // #i86652#
+ return;
+
+ SvxFirstLineIndentItem firstLine(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE));
+ SvxTextLeftMarginItem leftMargin(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT));
+ if (rStyle.m_bListRelevantIndentSet)
+ {
+
+ SyncIndentWithList(firstLine, leftMargin, rFormat, false, false); // #i103711#, #i105414#
+ }
+ else
+ {
+ leftMargin.SetTextLeft(0);
+ firstLine.SetTextFirstLineOffset(0);
+ }
+ rStyle.m_pFormat->SetFormatAttr(firstLine);
+ rStyle.m_pFormat->SetFormatAttr(leftMargin);
+}
+
+void SwWW8ImplReader::SetStylesList(sal_uInt16 nStyle, sal_uInt16 nCurrentLFO,
+ sal_uInt8 nCurrentLevel)
+{
+ if (nStyle >= m_vColl.size())
+ return;
+
+ SwWW8StyInf &rStyleInf = m_vColl[nStyle];
+ if (!rStyleInf.m_bValid)
+ return;
+
+ OSL_ENSURE(m_pCurrentColl, "Cannot be called outside of style import");
+ // Phase 1: Numbering attributes when reading a StyleDef
+ if( !m_pCurrentColl )
+ return;
+
+ if (nCurrentLFO < USHRT_MAX)
+ rStyleInf.m_nLFOIndex = nCurrentLFO;
+ if (nCurrentLevel < MAXLEVEL)
+ rStyleInf.m_nListLevel = nCurrentLevel;
+
+ // only save the Parameters for now. The actual List will be appended
+ // at a later point, when the Listdefinitions is read...
+ if (rStyleInf.m_nLFOIndex < USHRT_MAX && rStyleInf.m_nListLevel < WW8ListManager::nMaxLevel)
+ {
+ std::vector<sal_uInt8> aParaSprms;
+ SwNumRule* pNmRule = m_xLstManager->GetNumRuleForActivation(
+ rStyleInf.m_nLFOIndex, rStyleInf.m_nListLevel, aParaSprms);
+ if (pNmRule)
+ UseListIndent(rStyleInf, pNmRule->Get(rStyleInf.m_nListLevel));
+ }
+}
+
+void SwWW8ImplReader::RegisterNumFormatOnStyle(sal_uInt16 nStyle)
+{
+
+ if (nStyle >= m_vColl.size())
+ return;
+
+ SwWW8StyInf &rStyleInf = m_vColl[nStyle];
+ if (!(rStyleInf.m_bValid && rStyleInf.m_pFormat))
+ return;
+
+ //Save old pre-list modified indent, which are the word indent values
+ rStyleInf.m_pWordFirstLine.reset(rStyleInf.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE).Clone());
+ rStyleInf.m_pWordLeftMargin.reset(rStyleInf.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT).Clone());
+ rStyleInf.m_pWordRightMargin.reset(rStyleInf.m_pFormat->GetFormatAttr(RES_MARGIN_RIGHT).Clone());
+
+ // Phase 2: refresh StyleDef after reading all Lists
+ if (rStyleInf.m_nLFOIndex >= USHRT_MAX || rStyleInf.m_nListLevel >= WW8ListManager::nMaxLevel)
+ return;
+
+ std::vector<sal_uInt8> aParaSprms;
+ SwNumRule* pNmRule = m_xLstManager->GetNumRuleForActivation(
+ rStyleInf.m_nLFOIndex, rStyleInf.m_nListLevel, aParaSprms);
+
+ if (pNmRule != nullptr)
+ {
+ if (rStyleInf.IsWW8BuiltInHeadingStyle()
+ && rStyleInf.HasWW8OutlineLevel())
+ {
+ rStyleInf.m_pOutlineNumrule = pNmRule;
+ }
+ else
+ {
+ rStyleInf.m_pFormat->SetFormatAttr(
+ SwNumRuleItem(pNmRule->GetName()));
+ rStyleInf.m_bHasStyNumRule = true;
+ }
+
+ SetStyleIndent(rStyleInf, pNmRule->Get(rStyleInf.m_nListLevel));
+ }
+}
+
+void SwWW8ImplReader::RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO,
+ sal_uInt8 nCurrentLevel,
+ const bool bSetAttr)
+{
+ // Note: the method appends NumRule to the Text Node if
+ // bSetAttr (of course the lists have to be read before)
+ // and only sets the Level. It does not check if there is a NumRule
+ // attached to the STYLE !!!
+
+ if (!m_xLstManager) // are all list declarations read?
+ return;
+
+ SwTextNode* pTextNd = m_pPaM->GetPointNode().GetTextNode();
+ OSL_ENSURE(pTextNd, "No Text-Node at PaM-Position");
+ if (!pTextNd)
+ return;
+
+ // WW8ListManager::nMaxLevel indicates body text, cancelling an inherited numbering.
+ if (nCurrentLFO < USHRT_MAX && nCurrentLevel == WW8ListManager::nMaxLevel)
+ {
+ pTextNd->SetAttr(SwNumRuleItem(OUString()));
+ return;
+ }
+
+ // Undefined listLevel is treated as the first level with valid numbering rule.
+ // TODO:This doesn't allow for inheriting from a style(HOW?), but it matches previous behaviour.
+ if (nCurrentLFO < USHRT_MAX && nCurrentLevel == MAXLEVEL)
+ nCurrentLevel = 0;
+
+ std::vector<sal_uInt8> aParaSprms;
+ const SwNumRule* pRule = bSetAttr ?
+ m_xLstManager->GetNumRuleForActivation( nCurrentLFO, nCurrentLevel,
+ aParaSprms, pTextNd) : nullptr;
+
+ if (pRule == nullptr && bSetAttr)
+ return;
+
+ if (bSetAttr && pTextNd->GetNumRule() != pRule
+ && (pTextNd->GetNumRule() != m_rDoc.GetOutlineNumRule()
+ || pRule != m_pChosenWW8OutlineStyle))
+ {
+ // Now this is either not a part of Chapter Numbering,
+ // or else it is using a different numRule than the one copied to Chapter Numbering.
+ OUString sName = pRule == m_pChosenWW8OutlineStyle ? m_rDoc.GetOutlineNumRule()->GetName()
+ : pRule->GetName();
+ pTextNd->SetAttr(SwNumRuleItem(sName));
+ }
+ pTextNd->SetAttrListLevel(nCurrentLevel);
+
+ // <IsCounted()> state of text node has to be adjusted accordingly.
+ if ( /*nCurrentLevel >= 0 &&*/ nCurrentLevel < MAXLEVEL )
+ {
+ pTextNd->SetCountedInList( true );
+ }
+
+ // #i99822#
+ // Direct application of the list level formatting no longer
+ // needed for list levels of mode LABEL_ALIGNMENT
+ bool bApplyListLevelIndentDirectlyAtPara(true);
+ {
+ if (pTextNd->GetNumRule() && nCurrentLevel < MAXLEVEL)
+ {
+ const SwNumFormat& rFormat = pTextNd->GetNumRule()->Get(nCurrentLevel);
+ if (rFormat.GetPositionAndSpaceMode()
+ == SvxNumberFormat::LABEL_ALIGNMENT)
+ {
+ bApplyListLevelIndentDirectlyAtPara = false;
+ }
+ }
+ }
+
+ if (!bApplyListLevelIndentDirectlyAtPara)
+ return;
+
+ auto pListIndent = std::make_unique<SfxItemSet>(m_rDoc.GetAttrPool(), svl::Items<RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT>);
+ const SfxPoolItem *pItem;
+ pItem = GetFormatAttr(RES_MARGIN_FIRSTLINE);
+ OSL_ENSURE(pItem, "impossible");
+ if (pItem)
+ pListIndent->Put(*pItem);
+ pItem = GetFormatAttr(RES_MARGIN_TEXTLEFT);
+ if (pItem)
+ pListIndent->Put(*pItem);
+
+ /*
+ Take the original paragraph sprms attached to this list level
+ formatting and apply them to the paragraph. I'm convinced that
+ this is exactly what word does.
+ */
+ if (short nLen = static_cast< short >(aParaSprms.size()))
+ {
+ std::unique_ptr<SfxItemSet> pOldCurrentItemSet(SetCurrentItemSet(std::move(pListIndent)));
+
+ sal_uInt8* pSprms1 = aParaSprms.data();
+ while (0 < nLen)
+ {
+ sal_uInt16 nL1 = ImportSprm(pSprms1, nLen);
+ nLen = nLen - nL1;
+ pSprms1 += nL1;
+ }
+
+ pListIndent = SetCurrentItemSet(std::move(pOldCurrentItemSet));
+ }
+
+ if (const SvxFirstLineIndentItem *const pFirstLine = pListIndent->GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE))
+ {
+ m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *pFirstLine);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_FIRSTLINE);
+ }
+ if (const SvxTextLeftMarginItem *const pLeftMargin = pListIndent->GetItem<SvxTextLeftMarginItem>(RES_MARGIN_TEXTLEFT))
+ {
+ m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *pLeftMargin);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_TEXTLEFT);
+ }
+}
+
+void SwWW8ImplReader::RegisterNumFormat(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel)
+{
+ // Are we reading the StyleDef ?
+ if (m_pCurrentColl)
+ SetStylesList( m_nCurrentColl , nCurrentLFO, nCurrentLevel);
+ else
+ RegisterNumFormatOnTextNode(nCurrentLFO, nCurrentLevel);
+}
+
+void SwWW8ImplReader::Read_ListLevel(sal_uInt16, const sal_uInt8* pData,
+ short nLen)
+{
+ if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
+ return;
+
+ if( nLen < 0 )
+ {
+ // the current level is finished, what should we do ?
+ m_nListLevel = MAXLEVEL;
+ if (m_xStyles && !m_bVer67)
+ m_xStyles->mnWwNumLevel = 0;
+ }
+ else
+ {
+ // security check
+ if( !pData )
+ return;
+
+ // the Streamdata is zero based
+ m_nListLevel = *pData;
+
+ if (m_xStyles && !m_bVer67)
+ {
+ /*
+ if this is the case, then if the numbering is actually stored in
+ winword 6 format, and its likely that sprmPIlvl has been abused
+ to set the ww6 list level information which we will need when we
+ reach the true ww6 list def. So set it now
+ */
+ m_xStyles->mnWwNumLevel = m_nListLevel;
+ }
+
+ // Treat an invalid level as body-level
+ if (WW8ListManager::nMaxLevel < m_nListLevel)
+ m_nListLevel = WW8ListManager::nMaxLevel;
+
+ RegisterNumFormat(m_nLFOPosition, m_nListLevel);
+ if (USHRT_MAX > m_nLFOPosition)
+ {
+ assert(false && "m_nLFOPosition is usually reset immediately, so we rarely ever get here.");
+ m_nLFOPosition = USHRT_MAX;
+ m_nListLevel = MAXLEVEL;
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_LFOPosition(sal_uInt16, const sal_uInt8* pData,
+ short nLen)
+{
+ if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
+ return;
+
+ if( nLen < 0 )
+ {
+ // the current level is finished, what should we do ?
+ m_nLFOPosition = USHRT_MAX;
+ m_nListLevel = MAXLEVEL;
+ }
+ else
+ {
+ // security check
+ if( !pData )
+ return;
+
+ short nData = SVBT16ToUInt16( pData );
+ if( 0 >= nData )
+ {
+ // disable the numbering/list style apply to the paragraph or the style
+
+ /*
+ If you have a paragraph in word with left and/or hanging indent
+ and remove its numbering, then the indentation appears to get
+ reset, but not back to the base style, instead it goes to a blank
+ setting.
+ Unless it's a broken ww6 list in 97 in which case more hackery is
+ required, some more details about broken ww6 list in
+ ww8par6.cxx#SwWW8ImplReader::Read_LR
+ */
+
+ if (m_pCurrentColl)
+ {
+ // here a "named" style is being configured
+
+ // disable the numbering/list in the style currently configured
+ m_pCurrentColl->SetFormatAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
+
+ // reset/blank the indent
+ m_pCurrentColl->SetFormatAttr(SvxFirstLineIndentItem(RES_MARGIN_FIRSTLINE));
+ m_pCurrentColl->SetFormatAttr(SvxTextLeftMarginItem(RES_MARGIN_TEXTLEFT));
+ m_pCurrentColl->SetFormatAttr(SvxRightMarginItem(RES_MARGIN_RIGHT));
+
+ // These sprmPIlfos are supposed to indicate "cancel" numbering.
+ // Since m_nLFOPosition is "data - 1", then zero becomes USHRT_MAX
+ // which is no good since that indicates "unspecified, available for inheritance".
+ // So instead use USHRT_MAX-1 for indicating an explicit "cancel numbering".
+ RegisterNumFormat(USHRT_MAX-1, MAXLEVEL);
+ }
+ else if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
+ {
+ // here a paragraph is being directly formatted
+
+ // empty the numbering/list style applied to the current paragraph
+ SwNumRuleItem aEmptyRule;
+ pTextNode->SetAttr( aEmptyRule );
+
+ // create an empty SvxLRSpaceItem
+ std::shared_ptr<SvxFirstLineIndentItem> pFirstLine(std::make_shared<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
+
+ // replace it with the one of the current node if it exist
+ if (const SvxFirstLineIndentItem * pItem = GetFormatAttr(RES_MARGIN_FIRSTLINE))
+ pFirstLine.reset(pItem->Clone());
+
+ // reset/blank the left indent (and only the left)
+ pFirstLine->SetTextFirstLineOffset(0);
+ SvxTextLeftMarginItem leftMargin(0, RES_MARGIN_TEXTLEFT);
+
+ // apply the modified SvxLRSpaceItem to the current paragraph
+ pTextNode->SetAttr(*pFirstLine);
+ pTextNode->SetAttr(leftMargin);
+ }
+
+ m_nLFOPosition = USHRT_MAX;
+ }
+ else // nData in (0..0x7FFF]
+ {
+ m_nLFOPosition = o3tl::narrowing<sal_uInt16>(nData)-1; // m_nLFOPosition in [0..0x7FFF)
+ /*
+ If we are a ww8+ style with ww7- style lists then there is a
+ bizarre broken word bug where when the list is removed from a para
+ the ww6 list first line indent still affects the first line
+ indentation. Setting this flag will allow us to recover from this
+ braindeadness
+ */
+ if (m_pCurrentColl && (m_nLFOPosition == 2047-1) && m_nCurrentColl < m_vColl.size())
+ m_vColl[m_nCurrentColl].m_bHasBrokenWW6List = true;
+
+ // here the stream data is 1-based, we subtract ONE
+ if (m_nLFOPosition != 2047-1) //Normal ww8+ list behaviour
+ {
+ RegisterNumFormat(m_nLFOPosition, m_nListLevel);
+ m_nLFOPosition = USHRT_MAX;
+ m_nListLevel = MAXLEVEL;
+ }
+ else if (m_xPlcxMan && m_xPlcxMan->HasParaSprm(NS_sprm::LN_PAnld).pSprm)
+ {
+ /*
+ #i8114# Horrific backwards compatible ww7- lists in ww8+
+ docs
+ */
+ m_nListLevel = std::min<sal_uInt8>(WW8ListManager::nMaxLevel, m_nListLevel);
+ Read_ANLevelNo(13 /*equiv ww7- sprm no*/, &m_nListLevel, 1);
+ }
+ }
+ }
+}
+
+// Reading Controls
+
+bool SwWW8ImplReader::ImportFormulaControl(WW8FormulaControl &aFormula,
+ WW8_CP nStart, SwWw8ControlType nWhich )
+{
+ bool bRet=false;
+ /*
+ * Save the reader state and process the sprms for this anchor cp.
+ * Doing so will set the nPicLocFc to the offset to find the hypertext
+ * data in the data stream.
+ */
+ WW8_CP nEndCp = nStart+1; //Only interested in the single 0x01 character
+
+ WW8ReaderSave aSave(this,nStart);
+
+ WW8PLCFManResult aRes;
+ nStart = m_xPlcxMan->Where();
+ while(nStart <= nEndCp)
+ {
+ if ( m_xPlcxMan->Get(&aRes)
+ && aRes.pMemPos && aRes.nSprmId )
+ {
+ //only interested in sprms which would set nPicLocFc
+ if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) )
+ {
+ Read_PicLoc( aRes.nSprmId, aRes.pMemPos +
+ m_oSprmParser->DistanceToData(aRes.nSprmId), 4);
+ break;
+ }
+ }
+ m_xPlcxMan->advance();
+ nStart = m_xPlcxMan->Where();
+ }
+ sal_uLong nOffset = m_nPicLocFc;
+ aSave.Restore(this);
+
+ sal_uInt64 nOldPos = m_pDataStream->Tell();
+ WW8_PIC aPic;
+ bool bValid = checkSeek(*m_pDataStream, nOffset) &&
+ PicRead(m_pDataStream, &aPic, m_bVer67);
+
+ if (bValid && aPic.lcb > 0x3A)
+ {
+ aFormula.FormulaRead(nWhich,m_pDataStream);
+ bRet = true;
+ }
+
+ /*
+ There is a problem with aPic, the WW8_PIC is always used even though it
+ is too big for the WW95 files, it needs to be modified to check the
+ version C.
+ */
+ m_pDataStream->Seek( nOldPos );
+ return bRet;
+}
+
+void SwMSConvertControls::InsertFormula(WW8FormulaControl &rFormula)
+{
+ const uno::Reference< lang::XMultiServiceFactory > & rServiceFactory =
+ GetServiceFactory();
+
+ if(!rServiceFactory.is())
+ return;
+
+ awt::Size aSz;
+ uno::Reference< form::XFormComponent> xFComp;
+
+ if (rFormula.Import(rServiceFactory, xFComp, aSz))
+ {
+ uno::Reference <drawing::XShape> xShapeRef;
+ if (InsertControl(xFComp, aSz, &xShapeRef, false))
+ GetShapes()->add(xShapeRef);
+ }
+}
+
+void WW8FormulaControl::FormulaRead(SwWw8ControlType nWhich,
+ SvStream *pDataStream)
+{
+ sal_uInt8 nField;
+
+ // The following is a FFData structure as described in
+ // Microsoft's DOC specification (chapter 2.9.78)
+ sal_uInt32 nVersion = 0;
+ pDataStream->ReadUInt32(nVersion);
+ // An unsigned integer that MUST be 0xFFFFFFFF
+ if (nVersion != 0xFFFFFFFF)
+ {
+ SAL_WARN("sw.ww8", "Parsing error: invalid header for FFData");
+ return; // bail out
+ }
+
+ // might be better to read the bits as a 16 bit word
+ // ( like it is in the spec. )
+ sal_uInt8 bits1 = 0;
+ pDataStream->ReadUChar( bits1 );
+ sal_uInt8 bits2 = 0;
+ pDataStream->ReadUChar( bits2 );
+
+ sal_uInt8 iType = ( bits1 & 0x3 );
+
+ // we should verify that bits.iType & nWhich concur
+ OSL_ENSURE( iType == nWhich, "something wrong, expect control type read from stream doesn't match nWhich passed in");
+ if ( iType != nWhich )
+ return; // bail out
+
+ sal_uInt8 iRes = (bits1 & 0x7C) >> 2;
+
+ pDataStream->ReadUInt16( mnMaxLen );
+
+ sal_uInt16 hps = 0;
+ pDataStream->ReadUInt16( hps );
+
+ // xstzName
+ msTitle = read_uInt16_BeltAndBracesString(*pDataStream);
+
+ if (nWhich == WW8_CT_EDIT)
+ { // Field is a textbox
+ // Default text
+ // xstzTextDef
+ msDefault = read_uInt16_BeltAndBracesString(*pDataStream);
+ }
+ else
+ {
+ // CheckBox or ComboBox
+ sal_uInt16 wDef = 0;
+ pDataStream->ReadUInt16( wDef );
+ mnChecked = wDef; // default
+ if (nWhich == WW8_CT_CHECKBOX)
+ {
+ if ( iRes != 25 )
+ mnChecked = iRes;
+ msDefault = ( wDef == 0 ) ? std::u16string_view( u"0" ) : std::u16string_view( u"1" );
+ }
+ }
+ // xstzTextFormat
+ msFormatting = read_uInt16_BeltAndBracesString(*pDataStream);
+ // xstzHelpText
+ msHelp = read_uInt16_BeltAndBracesString(*pDataStream);
+ // xstzStatText
+ msToolTip = read_uInt16_BeltAndBracesString(*pDataStream);
+
+ // xstzEntryMcr
+ msEntryMcr = read_uInt16_BeltAndBracesString(*pDataStream);
+ //xstzExitMcr
+ msExitMcr = read_uInt16_BeltAndBracesString(*pDataStream);
+
+ if (nWhich == WW8_CT_DROPDOWN)
+ {
+ bool bAllOk = true;
+ // SSTB (see Spec. 2.2.4)
+ sal_uInt16 fExtend = 0;
+ pDataStream->ReadUInt16( fExtend );
+ sal_uInt16 nStringsCnt = 0;
+
+ // Isn't it that if fExtend isn't 0xFFFF then fExtend actually
+ // doesn't exist and we really have just read nStringsCnt ( or cData )?
+ if (fExtend != 0xFFFF)
+ bAllOk = false;
+ pDataStream->ReadUInt16( nStringsCnt );
+
+ // I guess this should be zero ( and we should ensure that )
+ sal_uInt16 cbExtra = 0;
+ pDataStream->ReadUInt16( cbExtra );
+
+ OSL_ENSURE(bAllOk, "Unknown formfield dropdown list structure");
+ if (!bAllOk) //Not as expected, don't risk it at all.
+ nStringsCnt = 0;
+ const size_t nMinRecordSize = sizeof(sal_uInt16);
+ const size_t nMaxRecords = pDataStream->remainingSize() / nMinRecordSize;
+ if (nStringsCnt > nMaxRecords)
+ {
+ SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nStringsCnt << " claimed, truncating");
+ nStringsCnt = nMaxRecords;
+ }
+ maListEntries.reserve(nStringsCnt);
+ for (sal_uInt32 nI = 0; nI < nStringsCnt; ++nI)
+ {
+ OUString sEntry = read_uInt16_PascalString(*pDataStream);
+ maListEntries.push_back(sEntry);
+ }
+ }
+ mfDropdownIndex = iRes;
+
+ mbHelp = bits1 & 0x80;
+
+ nField = bits2;
+ mfToolTip = nField & 0x01;
+ mfNoMark = (nField & 0x02)>>1;
+ mfType = (nField & 0x38)>>3;
+ mfUnused = (nField & 0xE0)>>5;
+}
+
+WW8FormulaListBox::WW8FormulaListBox(SwWW8ImplReader &rR)
+ : WW8FormulaControl(SL::aListBox, rR)
+{
+}
+
+//Miserable hack to get a hardcoded guesstimate of the size of a list dropdown
+//box's first entry to set as the lists default size
+awt::Size SwWW8ImplReader::MiserableDropDownFormHack(const OUString &rString,
+ uno::Reference<beans::XPropertySet> const & rPropSet)
+{
+ awt::Size aRet;
+ struct CtrlFontMapEntry
+ {
+ sal_uInt16 nWhichId;
+ const char* pPropNm;
+ };
+ const CtrlFontMapEntry aMapTable[] =
+ {
+ { RES_CHRATR_COLOR, "TextColor" },
+ { RES_CHRATR_FONT, "FontName" },
+ { RES_CHRATR_FONTSIZE, "FontHeight" },
+ { RES_CHRATR_WEIGHT, "FontWeight" },
+ { RES_CHRATR_UNDERLINE, "FontUnderline" },
+ { RES_CHRATR_CROSSEDOUT, "FontStrikeout" },
+ { RES_CHRATR_POSTURE, "FontSlant" },
+ { 0, nullptr }
+ };
+
+ vcl::Font aFont;
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
+ rPropSet->getPropertySetInfo();
+
+ uno::Any aTmp;
+ for (const CtrlFontMapEntry* pMap = aMapTable; pMap->nWhichId; ++pMap)
+ {
+ bool bSet = true;
+ const SfxPoolItem* pItem = GetFormatAttr( pMap->nWhichId );
+ OSL_ENSURE(pItem, "Impossible");
+ if (!pItem)
+ continue;
+
+ switch ( pMap->nWhichId )
+ {
+ case RES_CHRATR_COLOR:
+ {
+ OUString aNm;
+ if (xPropSetInfo->hasPropertyByName(aNm = "TextColor"))
+ {
+ aTmp <<= static_cast<sal_Int32>(static_cast<const SvxColorItem*>(pItem)->GetValue());
+ rPropSet->setPropertyValue(aNm, aTmp);
+ }
+ }
+ aFont.SetColor(static_cast<const SvxColorItem*>(pItem)->GetValue());
+ break;
+ case RES_CHRATR_FONT:
+ {
+ const SvxFontItem *pFontItem = static_cast<const SvxFontItem *>(pItem);
+ OUString aNm;
+ if (xPropSetInfo->hasPropertyByName(aNm = "FontStyleName"))
+ {
+ aTmp <<= pFontItem->GetStyleName();
+ rPropSet->setPropertyValue( aNm, aTmp );
+ }
+ if (xPropSetInfo->hasPropertyByName(aNm = "FontFamily"))
+ {
+ aTmp <<= static_cast<sal_Int16>(pFontItem->GetFamily());
+ rPropSet->setPropertyValue( aNm, aTmp );
+ }
+ if (xPropSetInfo->hasPropertyByName(aNm = "FontCharset"))
+ {
+ aTmp <<= static_cast<sal_Int16>(pFontItem->GetCharSet());
+ rPropSet->setPropertyValue( aNm, aTmp );
+ }
+ if (xPropSetInfo->hasPropertyByName(aNm = "FontPitch"))
+ {
+ aTmp <<= static_cast<sal_Int16>(pFontItem->GetPitch());
+ rPropSet->setPropertyValue( aNm, aTmp );
+ }
+
+ aTmp <<= pFontItem->GetFamilyName();
+ aFont.SetFamilyName( pFontItem->GetFamilyName() );
+ aFont.SetStyleName( pFontItem->GetStyleName() );
+ aFont.SetFamily( pFontItem->GetFamily() );
+ aFont.SetCharSet( pFontItem->GetCharSet() );
+ aFont.SetPitch( pFontItem->GetPitch() );
+ }
+ break;
+
+ case RES_CHRATR_FONTSIZE:
+ {
+ Size aSize( aFont.GetFontSize().Width(),
+ static_cast<const SvxFontHeightItem*>(pItem)->GetHeight() );
+ aTmp <<= static_cast<float>(aSize.Height()) / 20.0;
+
+ aFont.SetFontSize(o3tl::convert(aSize, o3tl::Length::twip, o3tl::Length::mm100));
+ }
+ break;
+
+ case RES_CHRATR_WEIGHT:
+ aTmp <<= vcl::unohelper::ConvertFontWeight(
+ static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
+ aFont.SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
+ break;
+
+ case RES_CHRATR_UNDERLINE:
+ aTmp <<= static_cast<sal_Int16>(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
+ aFont.SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
+ break;
+
+ case RES_CHRATR_CROSSEDOUT:
+ aTmp <<= static_cast<sal_Int16>( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
+ aFont.SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
+ break;
+
+ case RES_CHRATR_POSTURE:
+ aTmp <<= static_cast<sal_Int16>( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
+ aFont.SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
+ break;
+
+ default:
+ bSet = false;
+ break;
+ }
+
+ if (bSet && xPropSetInfo->hasPropertyByName(OUString::createFromAscii(pMap->pPropNm)))
+ rPropSet->setPropertyValue(OUString::createFromAscii(pMap->pPropNm), aTmp);
+ }
+ // now calculate the size of the control
+ OutputDevice* pOut = Application::GetDefaultDevice();
+ OSL_ENSURE(pOut, "Impossible");
+ if (pOut)
+ {
+ pOut->Push( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE );
+ pOut->SetMapMode( MapMode( MapUnit::Map100thMM ));
+ pOut->SetFont( aFont );
+ aRet.Width = pOut->GetTextWidth(rString);
+ aRet.Width += 500; //plus size of button, total hack territory
+ aRet.Height = pOut->GetTextHeight();
+ pOut->Pop();
+ }
+ return aRet;
+}
+
+bool WW8FormulaListBox::Import(const uno::Reference <
+ lang::XMultiServiceFactory> &rServiceFactory,
+ uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz )
+{
+ uno::Reference<uno::XInterface> xCreate = rServiceFactory->createInstance("com.sun.star.form.component.ComboBox");
+ if( !xCreate.is() )
+ return false;
+
+ rFComp.set(xCreate, uno::UNO_QUERY);
+ if( !rFComp.is() )
+ return false;
+
+ uno::Reference<beans::XPropertySet> xPropSet(xCreate, uno::UNO_QUERY);
+
+ uno::Any aTmp;
+ if (!msTitle.isEmpty())
+ aTmp <<= msTitle;
+ else
+ aTmp <<= msName;
+ xPropSet->setPropertyValue("Name", aTmp );
+
+ if (!msToolTip.isEmpty())
+ {
+ aTmp <<= msToolTip;
+ xPropSet->setPropertyValue("HelpText", aTmp );
+ }
+
+ xPropSet->setPropertyValue("Dropdown", css::uno::Any(true));
+
+ if (!maListEntries.empty())
+ {
+ sal_uInt32 nLen = maListEntries.size();
+ uno::Sequence< OUString > aListSource(nLen);
+ auto aListSourceRange = asNonConstRange(aListSource);
+ for (sal_uInt32 nI = 0; nI < nLen; ++nI)
+ aListSourceRange[nI] = maListEntries[nI];
+ aTmp <<= aListSource;
+ xPropSet->setPropertyValue("StringItemList", aTmp );
+
+ if (mfDropdownIndex < nLen)
+ {
+ aTmp <<= aListSource[mfDropdownIndex];
+ }
+ else
+ {
+ aTmp <<= aListSource[0];
+ }
+
+ xPropSet->setPropertyValue("DefaultText", aTmp );
+
+ rSz = mrRdr.MiserableDropDownFormHack(maListEntries[0], xPropSet);
+ }
+ else
+ {
+ static constexpr OUStringLiteral aBlank =
+ u"\u2002\u2002\u2002\u2002\u2002";
+ rSz = mrRdr.MiserableDropDownFormHack(aBlank, xPropSet);
+ }
+
+ return true;
+}
+
+WW8FormulaCheckBox::WW8FormulaCheckBox(SwWW8ImplReader &rR)
+ : WW8FormulaControl(SL::aCheckBox, rR)
+{
+}
+
+static void lcl_AddToPropertyContainer
+(uno::Reference<beans::XPropertySet> const & xPropSet,
+ const OUString & rPropertyName, const OUString & rValue)
+{
+ uno::Reference<beans::XPropertySetInfo> xPropSetInfo =
+ xPropSet->getPropertySetInfo();
+ if (xPropSetInfo.is() &&
+ ! xPropSetInfo->hasPropertyByName(rPropertyName))
+ {
+ uno::Reference<beans::XPropertyContainer>
+ xPropContainer(xPropSet, uno::UNO_QUERY);
+ uno::Any aAny((OUString()));
+ xPropContainer->addProperty
+ (rPropertyName,
+ static_cast<sal_Int16>(beans::PropertyAttribute::BOUND |
+ beans::PropertyAttribute::REMOVABLE),
+ aAny);
+ }
+
+ uno::Any aAnyValue(rValue);
+ xPropSet->setPropertyValue(rPropertyName, aAnyValue );
+}
+
+bool WW8FormulaCheckBox::Import(const uno::Reference <
+ lang::XMultiServiceFactory> &rServiceFactory,
+ uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz )
+{
+ uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance("com.sun.star.form.component.CheckBox");
+ if( !xCreate.is() )
+ return false;
+
+ rFComp.set( xCreate, uno::UNO_QUERY );
+ if( !rFComp.is() )
+ return false;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xCreate, uno::UNO_QUERY );
+
+ rSz.Width = 16 * mhpsCheckBox;
+ rSz.Height = 16 * mhpsCheckBox;
+
+ uno::Any aTmp;
+ if (!msTitle.isEmpty())
+ aTmp <<= msTitle;
+ else
+ aTmp <<= msName;
+ xPropSet->setPropertyValue("Name", aTmp );
+
+ aTmp <<= static_cast<sal_Int16>(mnChecked);
+ xPropSet->setPropertyValue("DefaultState", aTmp);
+
+ if (!msToolTip.isEmpty())
+ lcl_AddToPropertyContainer(xPropSet, "HelpText", msToolTip);
+
+ if (!msHelp.isEmpty())
+ lcl_AddToPropertyContainer(xPropSet, "HelpF1Text", msHelp);
+
+ return true;
+
+}
+
+WW8FormulaEditBox::WW8FormulaEditBox(SwWW8ImplReader &rR)
+ : WW8FormulaControl(SL::aTextField ,rR)
+{
+}
+
+bool SwMSConvertControls::InsertControl(
+ const uno::Reference< form::XFormComponent > & rFComp,
+ const awt::Size& rSize, uno::Reference< drawing::XShape > *pShape,
+ bool bFloatingCtrl)
+{
+ const uno::Reference< container::XIndexContainer > &rComps = GetFormComps();
+ uno::Any aTmp( &rFComp, cppu::UnoType<form::XFormComponent>::get());
+ rComps->insertByIndex( rComps->getCount(), aTmp );
+
+ const uno::Reference< lang::XMultiServiceFactory > &rServiceFactory =
+ GetServiceFactory();
+ if( !rServiceFactory.is() )
+ return false;
+
+ uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance(
+ "com.sun.star.drawing.ControlShape");
+ if( !xCreate.is() )
+ return false;
+
+ uno::Reference< drawing::XShape > xShape(xCreate, uno::UNO_QUERY);
+
+ OSL_ENSURE(xShape.is(), "Did not get XShape");
+ xShape->setSize(rSize);
+
+ uno::Reference< beans::XPropertySet > xShapePropSet(
+ xCreate, uno::UNO_QUERY );
+
+ //I lay a small bet that this will change to
+ //sal_Int16 nTemp=TextContentAnchorType::AS_CHARACTER;
+ text::TextContentAnchorType nTemp;
+ if (bFloatingCtrl)
+ nTemp = text::TextContentAnchorType_AT_PARAGRAPH;
+ else
+ nTemp = text::TextContentAnchorType_AS_CHARACTER;
+
+ xShapePropSet->setPropertyValue("AnchorType", uno::Any(static_cast<sal_Int16>(nTemp)) );
+
+ xShapePropSet->setPropertyValue("VertOrient", uno::Any(sal_Int16(text::VertOrientation::TOP)) );
+
+ uno::Reference< text::XText > xDummyTextRef;
+ uno::Reference< text::XTextRange > xTextRg =
+ new SwXTextRange( *m_pPaM, xDummyTextRef );
+
+ aTmp <<= xTextRg;
+ xShapePropSet->setPropertyValue("TextRange", aTmp );
+
+ // Set the Control-Model for the Control-Shape
+ uno::Reference< drawing::XControlShape > xControlShape( xShape,
+ uno::UNO_QUERY );
+ uno::Reference< awt::XControlModel > xControlModel( rFComp,
+ uno::UNO_QUERY );
+ xControlShape->setControl( xControlModel );
+
+ if (pShape)
+ *pShape = xShape;
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par4.cxx b/sw/source/filter/ww8/ww8par4.cxx
new file mode 100644
index 0000000000..46bfa7325d
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par4.cxx
@@ -0,0 +1,547 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentContentOperations.hxx>
+#include "writerhelper.hxx"
+#include <com/sun/star/embed/XClassifiedObject.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+
+#include <cstddef>
+#include <osl/endian.h>
+#include <sot/storage.hxx>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <hintids.hxx>
+#include <svx/svdoole2.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include "sprmids.hxx"
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <sot/exchange.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <docsh.hxx>
+#include <mdiexp.hxx>
+#include <fltshell.hxx>
+#include <shellio.hxx>
+
+#include <vcl/wmf.hxx>
+#include <vcl/gdimtf.hxx>
+
+#include "ww8scan.hxx"
+#include "ww8par.hxx"
+#include "ww8par2.hxx"
+
+namespace {
+
+struct OLE_MFP
+{
+ sal_Int16 mm; // 0x6 int
+ sal_Int16 xExt; // 0x8 int in 1/100 mm
+ sal_Int16 yExt; // 0xa int in 1/100 mm
+ sal_Int16 hMF; // 0xc int
+};
+
+}
+
+using namespace ::com::sun::star;
+
+static bool SwWw8ReadScaling(tools::Long& rX, tools::Long& rY, tools::SvRef<SotStorage> const & rSrc1)
+{
+ // Getting the scaling factor:
+ // Information in the PIC-stream (by trying out)
+ // 0x0 (l)cb
+ // 0x08 .. 0x0a Flags ??
+ // 0x08 contains: 1 / 0
+ // 0x09 contains: 0,8,0x18
+ // 0x0a contains: always 8, MAP_ANISOTROPIC ???
+ // 0x0b contains: always 0
+ // 0x0c, 0x10 original size x,y in 1/100 mm
+ // 0x14, 0x16 original size x,y in tw
+ // 0x2c, 0x30 scaling x,y in per thousand
+ // 0x34, 0x38, 0x3c, 0x40 Crop Left, Top, Right, Bot in tw
+
+ tools::SvRef<SotStorageStream> xSrc3 = rSrc1->OpenSotStream( "\3PIC",
+ StreamMode::STD_READ );
+ SotStorageStream* pS = xSrc3.get();
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ OSL_ENSURE( pS->TellEnd() >= 76, "+OLE-PIC-Stream is shorter than 76 Byte" );
+
+ sal_Int32 nOrgWidth,
+ nOrgHeight,
+ nScaleX,
+ nScaleY,
+ nCropLeft,
+ nCropTop,
+ nCropRight,
+ nCropBottom;
+ pS->Seek( 0x14 );
+ pS->ReadInt32( nOrgWidth ) // Original Size in 1/100 mm
+ .ReadInt32( nOrgHeight );
+ pS->Seek( 0x2c );
+ pS->ReadInt32( nScaleX ) // Scaling in Promille
+ .ReadInt32( nScaleY )
+ .ReadInt32( nCropLeft ) // Cropping in 1/100 mm
+ .ReadInt32( nCropTop )
+ .ReadInt32( nCropRight )
+ .ReadInt32( nCropBottom );
+
+ rX = nOrgWidth - nCropLeft - nCropRight;
+ rY = nOrgHeight - nCropTop - nCropBottom;
+ if (10 > nScaleX || 65536 < nScaleX || 10 > nScaleY || 65536 < nScaleY)
+ {
+ OSL_ENSURE( !pS, "+OLE-scaling information in PIC-stream wrong" );
+ return false;
+ }
+ else
+ {
+ rX = (rX * nScaleX) / 1000;
+ rY = (rY * nScaleY) / 1000;
+ }
+ return true;
+}
+
+static bool SwWw6ReadMetaStream(GDIMetaFile& rWMF, OLE_MFP* pMfp,
+ tools::SvRef<SotStorage> const & rSrc1)
+{
+ tools::SvRef<SotStorageStream> xSrc2 = rSrc1->OpenSotStream( "\3META",
+ StreamMode::STD_READ );
+ SotStorageStream* pSt = xSrc2.get();
+ pSt->SetEndian( SvStreamEndian::LITTLE );
+ size_t const nRead = pSt->ReadBytes(pMfp, sizeof(*pMfp));
+ // read mini-placable-header
+ if (nRead != sizeof(*pMfp))
+ return false;
+
+#if defined OSL_BIGENDIAN
+ pMfp->mm = OSL_SWAPWORD( pMfp->mm );
+ pMfp->xExt = OSL_SWAPWORD( pMfp->xExt );
+ pMfp->yExt = OSL_SWAPWORD( pMfp->yExt );
+#endif // OSL_BIGENDIAN
+
+ if( pMfp->mm == 94 || pMfp->mm == 99 )
+ {
+ SAL_WARN("sw.ww8", "+OLE: wrong metafile type");
+ return false;
+ }
+ if( pMfp->mm != 8 )
+ {
+ SAL_WARN("sw.ww8", "OLE: wrong mMetafile type (not anisotropic)");
+ }
+ if( !pMfp->xExt || !pMfp->yExt )
+ {
+ SAL_WARN("sw.ww8", "+OLE: size of 0?");
+ return false;
+ }
+ bool bOk = ReadWindowMetafile( *pSt, rWMF ); // read WMF
+ // *pSt >> aWMF doesn't work without the placable header
+ if (!bOk || pSt->GetError() || rWMF.GetActionSize() == 0)
+ {
+ SAL_WARN("sw.ww8", "+OLE: could not read the metafile");
+ return false;
+ }
+
+ rWMF.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
+
+ // Scale MetaFile to new size and save new size to MetaFile
+ Size aOldSiz( rWMF.GetPrefSize() );
+ Size aNewSiz( pMfp->xExt, pMfp->yExt );
+ Fraction aFracX( aNewSiz.Width(), aOldSiz.Width() );
+ Fraction aFracY( aNewSiz.Height(), aOldSiz.Height() );
+
+ rWMF.Scale( aFracX, aFracY );
+ rWMF.SetPrefSize( aNewSiz );
+
+ return true;
+}
+
+static bool SwWw6ReadMacPICTStream(Graphic& rGraph, tools::SvRef<SotStorage> const & rSrc1)
+{
+ // 03-META-stream does not exist. Maybe a 03-PICT?
+ tools::SvRef<SotStorageStream> xSrc4 = rSrc1->OpenSotStream("\3PICT");
+ SotStorageStream* pStp = xSrc4.get();
+ pStp->SetEndian( SvStreamEndian::LITTLE );
+ sal_uInt8 aTestA[10]; // Does the 01Ole-stream even exist?
+ size_t const nReadTst = pStp->ReadBytes(aTestA, sizeof(aTestA));
+ if (nReadTst != sizeof(aTestA))
+ return false;
+
+ pStp->Seek( STREAM_SEEK_TO_BEGIN );
+
+ // Mac-Pict is in the 03PICT-StorageStream but without the first 512 Bytes
+ // which are not relevant in a MAC-PICT (they are not evaluated)
+ return SwWW8ImplReader::GetPictGrafFromStream(rGraph, *pStp);
+}
+
+SwFlyFrameFormat* SwWW8ImplReader::InsertOle(SdrOle2Obj &rObject,
+ const SfxItemSet &rFlySet, const SfxItemSet *rGrfSet)
+{
+ SfxObjectShell *pPersist = m_rDoc.GetPersist();
+ OSL_ENSURE(pPersist, "No persist, cannot insert objects correctly");
+ if (!pPersist)
+ return nullptr;
+
+ SwFlyFrameFormat *pRet = nullptr;
+
+ std::optional<SfxItemSet> pMathFlySet;
+ uno::Reference < embed::XClassifiedObject > xClass = rObject.GetObjRef();
+ if( xClass.is() )
+ {
+ SvGlobalName aClassName( xClass->getClassID() );
+ if (SotExchange::IsMath(aClassName))
+ {
+ // StarMath sets it own fixed size, so its counter productive to use
+ // the size Word says it is. i.e. Don't attempt to override its size.
+ pMathFlySet.emplace(rFlySet);
+ pMathFlySet->ClearItem(RES_FRM_SIZE);
+ }
+ }
+
+ /*
+ Take complete responsibility of the object away from SdrOle2Obj and to
+ me here locally. This utility class now owns the object.
+ */
+
+ // TODO/MBA: is the object inserted multiple times here? Testing!
+ // And is it a problem that we now use the same naming scheme as in the other apps?
+ sw::hack::DrawingOLEAdaptor aOLEObj(rObject, *pPersist);
+ OUString sNewName;
+ bool bSuccess = aOLEObj.TransferToDoc(sNewName);
+
+ OSL_ENSURE(bSuccess, "Insert OLE failed");
+ if (bSuccess)
+ {
+ const SfxItemSet *pFlySet = pMathFlySet ? &*pMathFlySet : &rFlySet;
+ pRet = m_rDoc.getIDocumentContentOperations().InsertOLE(*m_pPaM, sNewName, rObject.GetAspect(), pFlySet, rGrfSet);
+ }
+ return pRet;
+}
+
+SwFrameFormat* SwWW8ImplReader::ImportOle(const Graphic* pGrf,
+ const SfxItemSet* pFlySet, const SfxItemSet *pGrfSet, const tools::Rectangle& aVisArea )
+{
+ ::SetProgressState(m_nProgress, m_pDocShell); // Update
+ SwFrameFormat* pFormat = nullptr;
+
+ GraphicCtor();
+
+ Graphic aGraph;
+ rtl::Reference<SdrObject> pRet = ImportOleBase(aGraph, pGrf, pFlySet, aVisArea );
+
+ // create flyset
+ std::optional<SfxItemSet> pTempSet;
+ if( !pFlySet )
+ {
+ pTempSet.emplace( m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN,
+ RES_FRMATR_END-1> );
+
+ pFlySet = &*pTempSet;
+
+ // Remove distance/borders
+ Reader::ResetFrameFormatAttrs( *pTempSet );
+
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR );
+ aAnchor.SetAnchor( m_pPaM->GetPoint() );
+ pTempSet->Put( aAnchor );
+
+ const Size aSizeTwip = OutputDevice::LogicToLogic(
+ aGraph.GetPrefSize(), aGraph.GetPrefMapMode(), MapMode(MapUnit::MapTwip));
+
+ pTempSet->Put( SwFormatFrameSize( SwFrameSize::Fixed, aSizeTwip.Width(),
+ aSizeTwip.Height() ) );
+ pTempSet->Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
+
+ if (m_xSFlyPara)
+ {
+ // Resize the frame to the picture size if there is an OLE object
+ // in the frame (only if auto-width)
+ m_xSFlyPara->BoxUpWidth(aSizeTwip.Width());
+ }
+ }
+
+ if (pRet) // OLE object was inserted
+ {
+ if (SdrOle2Obj *pOleObj = dynamic_cast<SdrOle2Obj*>(pRet.get()))
+ {
+ pFormat = InsertOle(*pOleObj, *pFlySet, pGrfSet);
+ pRet.clear(); // we don't need this anymore
+ }
+ else
+ pFormat = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pRet, *pFlySet );
+ }
+ else if (
+ GraphicType::GdiMetafile == aGraph.GetType() ||
+ GraphicType::Bitmap == aGraph.GetType()
+ )
+ {
+ pFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic(
+ *m_pPaM, OUString(), OUString(), &aGraph, pFlySet,
+ pGrfSet, nullptr);
+ }
+ return pFormat;
+}
+
+bool SwWW8ImplReader::ImportOleWMF(const tools::SvRef<SotStorage>& xSrc1, GDIMetaFile& rWMF,
+ tools::Long& rX, tools::Long& rY)
+{
+ bool bOk = false;
+ OLE_MFP aMfp;
+ if( SwWw6ReadMetaStream( rWMF, &aMfp, xSrc1 ) )
+ {
+ // take scaling factor as found in PIC and apply it to graphic.
+ SwWw8ReadScaling( rX, rY, xSrc1 );
+ Size aFinalSize, aOrigSize;
+ aFinalSize.setWidth( rX );
+ aFinalSize.setHeight( rY );
+ aFinalSize = OutputDevice::LogicToLogic(
+ aFinalSize, MapMode(MapUnit::MapTwip), rWMF.GetPrefMapMode() );
+ aOrigSize = rWMF.GetPrefSize();
+ Fraction aScaleX(aFinalSize.Width(),aOrigSize.Width());
+ Fraction aScaleY(aFinalSize.Height(),aOrigSize.Height());
+ rWMF.Scale( aScaleX, aScaleY );
+ bOk = true;
+ }
+ return bOk;
+}
+
+rtl::Reference<SdrObject> SwWW8ImplReader::ImportOleBase( Graphic& rGraph,
+ const Graphic* pGrf, const SfxItemSet* pFlySet, const tools::Rectangle& aVisArea )
+{
+ if (!m_pStg)
+ {
+ SAL_WARN("sw.ww8", "no storage for ole objects");
+ return nullptr;
+ }
+
+ ::SetProgressState( m_nProgress, m_rDoc.GetDocShell() ); // Update
+
+ tools::Long nX=0, nY=0; // nX, nY is graphic size
+ bool bOleOk = true;
+
+ // results in the name "_4711"
+ OUString aSrcStgName = "_" + OUString::number( m_nObjLocFc );
+
+ tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(SL::aObjectPool);
+ tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( aSrcStgName );
+
+ if (pGrf)
+ {
+ rGraph = *pGrf;
+ const Size aSizeTwip = OutputDevice::LogicToLogic(
+ rGraph.GetPrefSize(), rGraph.GetPrefMapMode(), MapMode(MapUnit::MapTwip));
+ nX = aSizeTwip.Width();
+ nY = aSizeTwip.Height();
+ }
+ else
+ {
+ GDIMetaFile aWMF;
+
+ if (ImportOleWMF(xSrc1,aWMF,nX,nY))
+ rGraph = Graphic( aWMF );
+ else if( SwWw6ReadMacPICTStream( rGraph, xSrc1 ) )
+ {
+ // 03-META stream is not available. Maybe it's a 03-PICT?
+ const Size aSizeTwip = OutputDevice::LogicToLogic(
+ rGraph.GetPrefSize(), rGraph.GetPrefMapMode(), MapMode(MapUnit::MapTwip));
+ nX = aSizeTwip.Width();
+ nY = aSizeTwip.Height();
+ // PICT: no WMF available -> Graphic instead of OLE
+ bOleOk = false;
+ }
+ } // StorageStreams closed again
+
+ tools::Rectangle aRect(0, 0, nX, nY);
+
+ if (pFlySet)
+ {
+ if (const SwFormatFrameSize* pSize = pFlySet->GetItem<SwFormatFrameSize>(RES_FRM_SIZE, false))
+ {
+ aRect.SetSize(pSize->GetSize());
+ }
+ }
+
+ rtl::Reference<SdrObject> pRet;
+
+ if (!(m_bIsHeader || m_bIsFooter))
+ {
+ //Can't put them in headers/footers :-(
+ uno::Reference< drawing::XShape > xRef;
+ OSL_ENSURE(m_xFormImpl, "Impossible");
+ if (m_xFormImpl && m_xFormImpl->ReadOCXStream(xSrc1, &xRef))
+ {
+ pRet = SdrObject::getSdrObjectFromXShape(xRef);
+ OSL_ENSURE(pRet, "Impossible");
+ if (pRet)
+ pRet->SetLogicRect(aRect);
+ return pRet;
+ }
+ }
+
+ if (GraphicType::GdiMetafile == rGraph.GetType() ||
+ GraphicType::Bitmap == rGraph.GetType())
+ {
+ ::SetProgressState(m_nProgress, m_pDocShell); // Update
+
+ if (bOleOk)
+ {
+ sal_uInt64 nOldPos = m_pDataStream->Tell();
+ SvStream *pTmpData = nullptr;
+ if (m_nObjLocFc < m_pDataStream->TellEnd())
+ {
+ pTmpData = m_pDataStream;
+ pTmpData->Seek( m_nObjLocFc );
+ }
+
+ sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT;
+
+ {
+ tools::SvRef<SotStorageStream> xObjInfoSrc = xSrc1->OpenSotStream("\3ObjInfo",
+ StreamMode::STD_READ );
+ if ( xObjInfoSrc.is() && !xObjInfoSrc->GetError() )
+ {
+ sal_uInt8 nByte = 0;
+ xObjInfoSrc->ReadUChar( nByte );
+ if ( ( nByte >> 4 ) & embed::Aspects::MSOLE_ICON )
+ nAspect = embed::Aspects::MSOLE_ICON;
+ }
+ }
+
+ ErrCode nError = ERRCODE_NONE;
+ GraphicCtor();
+
+ pRet = SvxMSDffManager::CreateSdrOLEFromStorage(
+ *m_pDrawModel,
+ aSrcStgName,
+ xSrc0,
+ m_pDocShell->GetStorage(),
+ rGraph,
+ aRect,
+ aVisArea,
+ pTmpData,
+ nError,
+ SwMSDffManager::GetFilterFlags(),
+ nAspect,
+ GetBaseURL());
+ m_pDataStream->Seek( nOldPos );
+ }
+ }
+ return pRet;
+}
+
+void SwWW8ImplReader::ReadRevMarkAuthorStrTabl( SvStream& rStrm,
+ sal_Int32 nTablePos, sal_Int32 nTableSiz, SwDoc& rDocOut )
+{
+ std::vector<OUString> aAuthorNames;
+ WW8ReadSTTBF( !m_bVer67, rStrm, nTablePos, nTableSiz, m_bVer67 ? 2 : 0,
+ m_eStructCharSet, aAuthorNames );
+
+ sal_uInt16 nCount = static_cast< sal_uInt16 >(aAuthorNames.size());
+ for( sal_uInt16 nAuthor = 0; nAuthor < nCount; ++nAuthor )
+ {
+ // Store author in doc
+ std::size_t nSWId = rDocOut.getIDocumentRedlineAccess().InsertRedlineAuthor(aAuthorNames[nAuthor]);
+ // Store matchpair
+ m_aAuthorInfos[nAuthor] = nSWId;
+ }
+}
+
+/*
+ Revision Marks ( == Redlining )
+*/
+// insert or delete content (change char attributes resp.)
+void SwWW8ImplReader::Read_CRevisionMark(RedlineType eType,
+ const sal_uInt8* pData, short nLen )
+{
+ // there *must* be a SprmCIbstRMark[Del] and a SprmCDttmRMark[Del]
+ // pointing to the very same char position as our SprmCFRMark[Del]
+ if (!m_xPlcxMan)
+ return;
+ const sal_uInt8* pSprmCIbstRMark;
+ const sal_uInt8* pSprmCDttmRMark;
+ if( RedlineType::Format == eType )
+ {
+ pSprmCIbstRMark = nLen >= 3 ? pData+1 : nullptr;
+ pSprmCDttmRMark = nLen >= 7 ? pData+3 : nullptr;
+ }
+ else
+ {
+ /* It is possible to have a number of date stamps for the created time
+ * of the change, (possibly a word bug) so we must use the "get a full
+ * list" variant of HasCharSprm and take the last one as the true one.
+ */
+ std::vector<SprmResult> aResult;
+ bool bIns = (RedlineType::Insert == eType);
+ if( m_bVer67 )
+ {
+ m_xPlcxMan->HasCharSprm(69, aResult);
+ pSprmCIbstRMark = (aResult.empty() || aResult.back().nRemainingData < 2) ? nullptr : aResult.back().pSprm;
+ aResult.clear();
+ m_xPlcxMan->HasCharSprm(70, aResult);
+ pSprmCDttmRMark = (aResult.empty() || aResult.back().nRemainingData < 4) ? nullptr : aResult.back().pSprm;
+ }
+ else
+ {
+ m_xPlcxMan->HasCharSprm( bIns ? 0x4804 : 0x4863, aResult);
+ pSprmCIbstRMark = (aResult.empty() || aResult.back().nRemainingData < 2) ? nullptr : aResult.back().pSprm;
+ aResult.clear();
+ m_xPlcxMan->HasCharSprm( bIns ? 0x6805 : NS_sprm::CDttmRMarkDel::val, aResult);
+ pSprmCDttmRMark = (aResult.empty() || aResult.back().nRemainingData < 4) ? nullptr : aResult.back().pSprm;
+ }
+ }
+
+ if (nLen < 0)
+ m_xRedlineStack->close(*m_pPaM->GetPoint(), eType, m_xTableDesc.get());
+ else
+ {
+ // start of new revision mark, if not there default to first entry
+ sal_uInt16 nWWAutNo = pSprmCIbstRMark ? SVBT16ToUInt16(pSprmCIbstRMark) : 0;
+ sal_uInt32 nWWDate = pSprmCDttmRMark ? SVBT32ToUInt32(pSprmCDttmRMark): 0;
+ DateTime aStamp(msfilter::util::DTTM2DateTime(nWWDate));
+ std::size_t nAuthorNo = m_aAuthorInfos[nWWAutNo];
+ SwFltRedline aNewAttr(eType, nAuthorNo, aStamp);
+ NewAttr(aNewAttr);
+ }
+}
+
+// insert new content
+void SwWW8ImplReader::Read_CFRMark(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ Read_CRevisionMark( RedlineType::Insert, pData, nLen );
+}
+
+// delete old content
+void SwWW8ImplReader::Read_CFRMarkDel(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ Read_CRevisionMark( RedlineType::Delete, pData, nLen );
+}
+
+// change properties of content ( == char formatting)
+void SwWW8ImplReader::Read_CPropRMark(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ // complex (len is always 7)
+ // 1 byte - chp.fPropRMark
+ // 2 bytes - chp.ibstPropRMark
+ // 4 bytes - chp.dttmPropRMark;
+ Read_CRevisionMark( RedlineType::Format, pData, nLen );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par5.cxx b/sw/source/filter/ww8/ww8par5.cxx
new file mode 100644
index 0000000000..c87c33dde0
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par5.cxx
@@ -0,0 +1,3802 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <sal/types.h>
+#include <tools/solar.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/simplefileaccessinteraction.hxx>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <svl/cintitem.hxx>
+#include <svl/lngmisc.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <rtl/character.hxx>
+#include <unotools/charclass.hxx>
+
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <hintids.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/langitem.hxx>
+#include <fmtfld.hxx>
+#include <fmtanchr.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentState.hxx>
+#include <flddat.hxx>
+#include <docufld.hxx>
+#include <reffld.hxx>
+#include <IMark.hxx>
+#include <expfld.hxx>
+#include <dbfld.hxx>
+#include <tox.hxx>
+#include <section.hxx>
+#include <ndtxt.hxx>
+#include <fmtinfmt.hxx>
+#include <chpfld.hxx>
+#include <fmtruby.hxx>
+#include <charfmt.hxx>
+#include <breakit.hxx>
+#include <fmtclds.hxx>
+#include <poolfmt.hxx>
+#include <SwStyleNameMapper.hxx>
+
+#include "ww8scan.hxx"
+#include "ww8par.hxx"
+#include "writerhelper.hxx"
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <xmloff/odffields.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <string_view>
+
+#define MAX_FIELDLEN 64000
+
+#define WW8_TOX_LEVEL_DELIM ':'
+
+using namespace ::com::sun::star;
+using namespace msfilter::util;
+using namespace sw::util;
+using namespace sw::mark;
+using namespace nsSwDocInfoSubType;
+
+// Bookmarks
+namespace
+{
+ // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention
+ bool IsTOCBookmarkName(std::u16string_view rName)
+ {
+ return o3tl::starts_with(rName, u"_Toc") || o3tl::starts_with(rName, Concat2View(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"));
+ }
+
+ OUString EnsureTOCBookmarkName(const OUString& rName)
+ {
+ OUString sTmp = rName;
+ if ( IsTOCBookmarkName ( rName ) )
+ {
+ if ( ! rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
+ sTmp = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName;
+ }
+ return sTmp;
+ }
+}
+
+tools::Long SwWW8ImplReader::Read_Book(WW8PLCFManResult*)
+{
+ // should also work via pRes.nCo2OrIdx
+ WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
+ if( !pB )
+ {
+ OSL_ENSURE( pB, "WW8PLCFx_Book - Pointer does not exist" );
+ return 0;
+ }
+
+ eBookStatus eB = pB->GetStatus();
+ if (eB & BOOK_IGNORE)
+ return 0; // ignore bookmark
+
+ if (pB->GetIsEnd())
+ {
+ m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true,
+ pB->GetHandle(), (eB & BOOK_FIELD)!=0);
+ return 0;
+ }
+
+ // "_Hlt*" are unnecessary
+ const OUString* pName = pB->GetName();
+ // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available.
+ // So the hidden bookmarks inside for hyperlink jumping also should be kept.
+ if ( !pName ||
+ pName->startsWithIgnoreAsciiCase( "_Hlt" ) )
+ {
+ return 0;
+ }
+
+ // do NOT call ToUpper as the bookmark name can also be a hyperlink target!
+
+ OUString aVal;
+ if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::BOOK_TO_VAR_REF ) )
+ {
+ // set variable for translation bookmark
+ tools::Long nLen = pB->GetLen();
+ if( nLen > MAX_FIELDLEN )
+ nLen = MAX_FIELDLEN;
+
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+ m_xSBase->WW8ReadString( *m_pStrm, aVal, pB->GetStartPos(), nLen,
+ m_eStructCharSet );
+ m_pStrm->Seek( nOldPos );
+
+ // now here the implementation of the old "QuoteString" and
+ // I hope with a better performance as before. It's also only
+ // needed if the filterflags say we will convert bookmarks
+ // to SetExpFields! And this the exception!
+
+ bool bSetAsHex;
+ bool bAllowCr = SwFltGetFlag(m_nFieldFlags,
+ SwFltControlStack::ALLOW_FLD_CR);
+
+ for( sal_Int32 nI = 0;
+ nI < aVal.getLength() && aVal.getLength() < (MAX_FIELDLEN - 4);
+ ++nI )
+ {
+ const sal_Unicode cChar = aVal[nI];
+ switch( cChar )
+ {
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ if( bAllowCr )
+ {
+ aVal = aVal.replaceAt( nI, 1, u"\n" );
+ bSetAsHex = false;
+ }
+ else
+ bSetAsHex = true;
+ break;
+
+ case 0xFE:
+ case 0xFF:
+ bSetAsHex = true;
+ break;
+
+ default:
+ bSetAsHex = 0x20 > cChar;
+ break;
+ }
+
+ if( bSetAsHex )
+ {
+ //all Hex-Numbers with \x before
+ OUString sTmp( "\\x" );
+ if( cChar < 0x10 )
+ sTmp += "0";
+ sTmp += OUString::number( cChar, 16 );
+ aVal = aVal.replaceAt( nI, 1 , sTmp );
+ nI += sTmp.getLength() - 1;
+ }
+ }
+
+ if ( aVal.getLength() > (MAX_FIELDLEN - 4))
+ aVal = aVal.copy( 0, MAX_FIELDLEN - 4 );
+ }
+
+ //e.g. inserting bookmark around field result, so we need to put
+ //it around the entire writer field, as we don't have the separation
+ //of field and field result of word, see #i16941#
+ SwPosition aStart(*m_pPaM->GetPoint());
+ if (!m_aFieldStack.empty())
+ {
+ const WW8FieldEntry &rTest = m_aFieldStack.back();
+ aStart = rTest.maStartPos;
+ }
+
+ const OUString sOrigName = BookmarkToWriter(*pName);
+ m_xReffedStck->NewAttr( aStart,
+ SwFltBookmark( EnsureTOCBookmarkName( sOrigName ), aVal, pB->GetHandle(), IsTOCBookmarkName( sOrigName ) ));
+ return 0;
+}
+
+tools::Long SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult*)
+{
+ if (WW8PLCFx_AtnBook* pAtnBook = m_xPlcxMan->GetAtnBook())
+ {
+ if (pAtnBook->getIsEnd())
+ m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_ANNOTATIONMARK, true, pAtnBook->getHandle());
+ else
+ m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK, pAtnBook->getHandle()));
+ }
+ return 0;
+}
+
+tools::Long SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult*)
+{
+ if (WW8PLCFx_FactoidBook* pFactoidBook = m_xPlcxMan->GetFactoidBook())
+ {
+ if (pFactoidBook->getIsEnd())
+ m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_RDFMARK, true, pFactoidBook->getHandle());
+ else
+ {
+ SwFltRDFMark aMark;
+ aMark.SetHandle(pFactoidBook->getHandle());
+ GetSmartTagInfo(aMark);
+ m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), aMark);
+ }
+ }
+ return 0;
+}
+
+// general help methods to separate parameters
+
+/// translate FieldParameter names into the system character set and
+/// at the same time, double backslashes are converted into single ones
+OUString SwWW8ImplReader::ConvertFFileName(const OUString& rOrg)
+{
+ OUString aName = rOrg.replaceAll("\\\\", "\\");
+ aName = aName.replaceAll("%20", " ");
+
+ // remove attached quotation marks
+ if (aName.endsWith("\""))
+ aName = aName.copy(0, aName.getLength()-1);
+
+ // Need the more sophisticated url converter.
+ if (!aName.isEmpty())
+ aName = URIHelper::SmartRel2Abs(
+ INetURLObject(m_sBaseURL), aName, Link<OUString *, bool>(), false);
+
+ return aName;
+}
+
+namespace
+{
+ /// translate FieldParameter names into the
+ /// system character set and makes them uppercase
+ void ConvertUFName( OUString& rName )
+ {
+ rName = GetAppCharClass().uppercase( rName );
+ }
+}
+
+static void lcl_ConvertSequenceName(OUString& rSequenceName)
+{
+ ConvertUFName(rSequenceName);
+ if ('0' <= rSequenceName[0] && '9' >= rSequenceName[0])
+ rSequenceName = "_" + rSequenceName;
+}
+
+// FindParaStart() finds 1st Parameter that follows '\' and cToken
+// and returns start of this parameter or -1
+static sal_Int32 FindParaStart( std::u16string_view aStr, sal_Unicode cToken, sal_Unicode cToken2 )
+{
+ bool bStr = false; // ignore inside a string
+
+ for( size_t nBuf = 0; nBuf+1 < aStr.size(); nBuf++ )
+ {
+ if( aStr[ nBuf ] == '"' )
+ bStr = !bStr;
+
+ if( !bStr
+ && aStr[ nBuf ] == '\\'
+ && ( aStr[ nBuf + 1 ] == cToken
+ || aStr[ nBuf + 1 ] == cToken2 ) )
+ {
+ nBuf += 2;
+ // skip spaces between cToken and its parameters
+ while( nBuf < aStr.size()
+ && aStr[ nBuf ] == ' ' )
+ nBuf++;
+ // return start of parameters
+ return nBuf < aStr.size() ? nBuf : -1;
+ }
+ }
+ return -1;
+}
+
+// FindPara() finds the first parameter including '\' and cToken.
+// A new String will be allocated (has to be deallocated by the caller)
+// and everything that is part of the parameter will be returned.
+static OUString FindPara( std::u16string_view aStr, sal_Unicode cToken, sal_Unicode cToken2 )
+{
+ sal_Int32 n2; // end
+ sal_Int32 n = FindParaStart( aStr, cToken, cToken2 ); // start
+ if( n == -1)
+ return OUString();
+
+ if( aStr[ n ] == '"'
+ || aStr[ n ] == 132 )
+ { // Quotationmark in front of parameter
+ n++; // Skip quotationmark
+ n2 = n; // search for the end starting from here
+ while( n2 < sal_Int32(aStr.size())
+ && aStr[ n2 ] != 147
+ && aStr[ n2 ] != '"' )
+ n2++; // search end of parameter
+ }
+ else
+ { // no quotationmarks
+ n2 = n; // search for the end starting from here
+ while( n2 < sal_Int32(aStr.size())
+ && aStr[ n2 ] != ' ' )
+ n2++; // search end of parameter
+ }
+ return OUString(aStr.substr( n, n2-n ));
+}
+
+static SvxNumType GetNumTypeFromName(const OUString& rStr,
+ bool bAllowPageDesc = false)
+{
+ SvxNumType eTyp = bAllowPageDesc ? SVX_NUM_PAGEDESC : SVX_NUM_ARABIC;
+ if (rStr.isEmpty())
+ return eTyp;
+
+ if( rStr.startsWithIgnoreAsciiCase( "Arabi" ) ) // Arabisch, Arabic
+ eTyp = SVX_NUM_ARABIC;
+ else if( rStr.startsWith( "misch" ) ) // r"omisch
+ eTyp = SVX_NUM_ROMAN_LOWER;
+ else if( rStr.startsWith( "MISCH" ) ) // R"OMISCH
+ eTyp = SVX_NUM_ROMAN_UPPER;
+ else if( rStr.startsWithIgnoreAsciiCase( "alphabeti" ) )// alphabetisch, alphabetic
+ eTyp = ( rStr[0] == 'A' )
+ ? SVX_NUM_CHARS_UPPER_LETTER_N
+ : SVX_NUM_CHARS_LOWER_LETTER_N;
+ else if( rStr.startsWithIgnoreAsciiCase( "roman" ) ) // us
+ eTyp = ( rStr[0] == 'R' )
+ ? SVX_NUM_ROMAN_UPPER
+ : SVX_NUM_ROMAN_LOWER;
+ return eTyp;
+}
+
+static SvxNumType GetNumberPara(std::u16string_view aStr, bool bAllowPageDesc = false)
+{
+ OUString s( FindPara( aStr, '*', '*' ) ); // Type of number
+ SvxNumType aType = GetNumTypeFromName( s, bAllowPageDesc );
+ return aType;
+}
+
+bool SwWW8ImplReader::ForceFieldLanguage(SwField &rField, LanguageType nLang)
+{
+ bool bRet(false);
+
+ const SvxLanguageItem *pLang = GetFormatAttr(RES_CHRATR_LANGUAGE);
+ OSL_ENSURE(pLang, "impossible");
+ LanguageType nDefault = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
+
+ if (nLang != nDefault)
+ {
+ rField.SetAutomaticLanguage(false);
+ rField.SetLanguage(nLang);
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+static OUString GetWordDefaultDateStringAsUS(SvNumberFormatter* pFormatter, LanguageType nLang)
+{
+ //Get the system date in the correct final language layout, convert to
+ //a known language and modify the 2 digit year part to be 4 digit, and
+ //convert back to the correct language layout.
+ const sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, nLang);
+
+ SvNumberformat aFormat = *(pFormatter->GetEntry(nIndex));
+ aFormat.ConvertLanguage(*pFormatter, nLang, LANGUAGE_ENGLISH_US);
+
+ OUString sParams(aFormat.GetFormatstring());
+ // #i36594#
+ // Fix provided by mloiseleur@openoffice.org.
+ // A default date can have already 4 year digits, in some case
+ const sal_Int32 pos = sParams.indexOf("YYYY");
+ if ( pos == -1 )
+ {
+ sParams = sParams.replaceFirst("YY", "YYYY");
+ }
+ return sParams;
+}
+
+SvNumFormatType SwWW8ImplReader::GetTimeDatePara(std::u16string_view aStr, sal_uInt32& rFormat,
+ LanguageType &rLang, int nWhichDefault, bool bHijri)
+{
+ bool bRTL = false;
+ if (m_xPlcxMan && !m_bVer67)
+ {
+ SprmResult aResult = m_xPlcxMan->HasCharSprm(0x85A);
+ if (aResult.pSprm && aResult.nRemainingData >= 1 && *aResult.pSprm)
+ bRTL = true;
+ }
+ TypedWhichId<SvxLanguageItem> eLang = bRTL ? RES_CHRATR_CTL_LANGUAGE : RES_CHRATR_LANGUAGE;
+ const SvxLanguageItem *pLang = GetFormatAttr(eLang);
+ OSL_ENSURE(pLang, "impossible");
+ rLang = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US;
+
+ SvNumberFormatter* pFormatter = m_rDoc.GetNumberFormatter();
+ OUString sParams( FindPara( aStr, '@', '@' ) );// Date/Time
+ if (sParams.isEmpty())
+ {
+ bool bHasTime = false;
+ switch (nWhichDefault)
+ {
+ case ww::ePRINTDATE:
+ case ww::eSAVEDATE:
+ sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
+ sParams += " HH:MM:SS AM/PM";
+ bHasTime = true;
+ break;
+ case ww::eCREATEDATE:
+ sParams += "DD/MM/YYYY HH:MM:SS";
+ bHasTime = true;
+ break;
+ default:
+ case ww::eDATE:
+ sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang);
+ break;
+ }
+
+ if (bHijri)
+ sParams = "[~hijri]" + sParams;
+
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ rFormat = 0;
+
+ OUString sTemp(sParams);
+ pFormatter->PutandConvertEntry(sTemp, nCheckPos, nType, rFormat,
+ LANGUAGE_ENGLISH_US, rLang, false);
+ sParams = sTemp;
+
+ return bHasTime ? SvNumFormatType::DATETIME : SvNumFormatType::DATE;
+ }
+
+ sal_uLong nFormatIdx =
+ sw::ms::MSDateTimeFormatToSwFormat(sParams, pFormatter, rLang, bHijri,
+ GetFib().m_lid);
+ SvNumFormatType nNumFormatType = SvNumFormatType::UNDEFINED;
+ if (nFormatIdx)
+ nNumFormatType = pFormatter->GetType(nFormatIdx);
+ rFormat = nFormatIdx;
+
+ return nNumFormatType;
+}
+
+// Fields
+
+// Update respective fields after loading (currently references)
+void SwWW8ImplReader::UpdateFields()
+{
+ m_rDoc.getIDocumentState().SetUpdateExpFieldStat(true);
+ m_rDoc.SetInitDBFields(true); // Also update fields in the database
+}
+
+// Sanity check the PaM to see if it makes sense wrt sw::CalcBreaks
+static bool SanityCheck(const SwPaM& rFieldPam)
+{
+ SwNodeOffset const nEndNode(rFieldPam.End()->GetNodeIndex());
+ SwNodes const& rNodes(rFieldPam.GetPoint()->GetNodes());
+ SwNode *const pFinalNode(rNodes[nEndNode]);
+ if (pFinalNode->IsTextNode())
+ {
+ SwTextNode & rTextNode(*pFinalNode->GetTextNode());
+ return (rTextNode.Len() >= rFieldPam.End()->GetContentIndex());
+ }
+ return true;
+}
+
+sal_uInt16 SwWW8ImplReader::End_Field()
+{
+ sal_uInt16 nRet = 0;
+ WW8PLCFx_FLD* pF = m_xPlcxMan->GetField();
+ OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available");
+ WW8_CP nCP = 0;
+ if (!pF || !pF->EndPosIsFieldEnd(nCP))
+ return nRet;
+
+ const SvtFilterOptions &rOpt = SvtFilterOptions::Get();
+ bool bUseEnhFields = rOpt.IsUseEnhancedFields();
+
+ OSL_ENSURE(!m_aFieldStack.empty(), "Empty field stack");
+ if (!m_aFieldStack.empty())
+ {
+ /*
+ only hyperlinks currently need to be handled like this, for the other
+ cases we have inserted a field not an attribute with an unknown end
+ point
+ */
+ nRet = m_aFieldStack.back().mnFieldId;
+ switch (nRet)
+ {
+ case ww::eFORMTEXT:
+ if (bUseEnhFields && m_pPaM!=nullptr && m_pPaM->GetPoint()!=nullptr) {
+ SwPosition aEndPos = *m_pPaM->GetPoint();
+ SwPaM aFieldPam( m_aFieldStack.back().GetPtNode().GetNode(), m_aFieldStack.back().GetPtContent(), aEndPos.GetNode(), aEndPos.GetContentIndex());
+
+ IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
+ IFieldmark *pFieldmark = SanityCheck(aFieldPam) ? pMarksAccess->makeFieldBookmark(
+ aFieldPam, m_aFieldStack.back().GetBookmarkName(), ODF_FORMTEXT,
+ aFieldPam.Start() /*same pos as start!*/ ) : nullptr;
+ OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
+ if (pFieldmark!=nullptr) {
+ // adapt redline positions to inserted field mark start
+ // dummy char (assume not necessary for end dummy char)
+ m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start());
+ const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
+ pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
+ }
+ }
+ break;
+ // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field
+ case ww::eTOC://TOX
+ case ww::eINDEX://index
+ if (m_bLoadingTOXCache)
+ {
+ if (m_nEmbeddedTOXLevel > 0)
+ {
+ JoinNode(*m_pPaM);
+ --m_nEmbeddedTOXLevel;
+ }
+ else
+ {
+ m_aTOXEndCps.insert(nCP);
+ m_bLoadingTOXCache = false;
+ if ( m_pPaM->End() &&
+ m_pPaM->End()->GetNode().GetTextNode() &&
+ m_pPaM->End()->GetNode().GetTextNode()->Len() == 0 )
+ {
+ JoinNode(*m_pPaM);
+ }
+ else
+ {
+ m_bCareLastParaEndInToc = true;
+ }
+
+ if (m_oPosAfterTOC)
+ {
+ *m_pPaM = *m_oPosAfterTOC;
+ m_oPosAfterTOC.reset();
+ }
+ }
+ }
+ break;
+ case ww::ePAGEREF: //REF
+ if (m_bLoadingTOXCache && !m_bLoadingTOXHyperlink)
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_TXTATR_INETFMT);
+ }
+ break;
+ case ww::eHYPERLINK:
+ if (m_bLoadingTOXHyperlink)
+ m_bLoadingTOXHyperlink = false;
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_INETFMT);
+ break;
+ case ww::eMERGEINC:
+ case ww::eINCLUDETEXT:
+ {
+ //Move outside the section associated with this type of field
+ SwPosition aRestorePos(m_aFieldStack.back().maStartPos);
+
+ SwContentNode* pNd = aRestorePos.GetNode().GetContentNode();
+ sal_Int32 nMaxValidIndex = pNd ? pNd->Len() : 0;
+ if (aRestorePos.GetContentIndex() > nMaxValidIndex)
+ {
+ SAL_WARN("sw.ww8", "Attempt to restore to invalid content position");
+ aRestorePos.SetContent(nMaxValidIndex);
+ }
+
+ *m_pPaM->GetPoint() = aRestorePos;
+ break;
+ }
+ case ww::eIF: // IF-field
+ {
+ // conditional field parameters
+ OUString fieldDefinition = m_aFieldStack.back().GetBookmarkCode();
+
+ OUString paramCondition;
+ OUString paramTrue;
+ OUString paramFalse;
+
+ // ParseIfFieldDefinition expects: IF <some condition> "true result" "false result"
+ // while many fields include '\* MERGEFORMAT' after that.
+ // So first trim off the switches that are not supported anyway
+ sal_Int32 nLastIndex = fieldDefinition.lastIndexOf("\\*");
+ sal_Int32 nOtherIndex = fieldDefinition.lastIndexOf("\\#"); //number format
+ if (nOtherIndex > 0 && (nOtherIndex < nLastIndex || nLastIndex < 0))
+ nLastIndex = nOtherIndex;
+ nOtherIndex = fieldDefinition.lastIndexOf("\\@"); //date format
+ if (nOtherIndex > 0 && (nOtherIndex < nLastIndex || nLastIndex < 0))
+ nLastIndex = nOtherIndex;
+ nOtherIndex = fieldDefinition.lastIndexOf("\\!"); //locked result
+ if (nOtherIndex > 0 && (nOtherIndex < nLastIndex || nLastIndex < 0))
+ nLastIndex = nOtherIndex;
+ if (nLastIndex > 0)
+ fieldDefinition = fieldDefinition.copy(0, nLastIndex);
+
+ SwHiddenTextField::ParseIfFieldDefinition(fieldDefinition, paramCondition, paramTrue, paramFalse);
+
+ // create new field
+ SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText);
+ SwHiddenTextField aHTField(
+ static_cast<SwHiddenTextFieldType*>(pFieldType),
+ paramCondition,
+ paramTrue,
+ paramFalse,
+ SwFieldTypesEnum::ConditionalText);
+
+ // insert new field into document
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aHTField));
+ break;
+ }
+ default:
+ OUString aCode = m_aFieldStack.back().GetBookmarkCode();
+ if (!aCode.isEmpty() && !o3tl::starts_with(o3tl::trim(aCode), u"SHAPE"))
+ {
+ // Unhandled field with stored code
+ SwPosition aEndPos = *m_pPaM->GetPoint();
+ SwPaM aFieldPam(
+ m_aFieldStack.back().GetPtNode().GetNode(), m_aFieldStack.back().GetPtContent(),
+ aEndPos.GetNode(), aEndPos.GetContentIndex());
+
+ IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
+
+ IFieldmark* pFieldmark = pMarksAccess->makeFieldBookmark(
+ aFieldPam,
+ m_aFieldStack.back().GetBookmarkName(),
+ ODF_UNHANDLED,
+ aFieldPam.Start() /*same pos as start!*/ );
+ if ( pFieldmark )
+ {
+ // adapt redline positions to inserted field mark start
+ // dummy char (assume not necessary for end dummy char)
+ m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start());
+ const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters();
+ pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end());
+ OUString sFieldId = OUString::number( m_aFieldStack.back().mnFieldId );
+ pFieldmark->GetParameters()->insert(
+ std::pair< OUString, uno::Any > (
+ ODF_ID_PARAM,
+ uno::Any( sFieldId ) ) );
+ pFieldmark->GetParameters()->insert(
+ std::pair< OUString, uno::Any > (
+ ODF_CODE_PARAM,
+ uno::Any( aCode ) ) );
+
+ if ( m_aFieldStack.back().mnObjLocFc > 0 )
+ {
+ // Store the OLE object as an internal link
+ OUString sOleId = "_" +
+ OUString::number( m_aFieldStack.back().mnObjLocFc );
+
+ tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(SL::aObjectPool);
+ tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( sOleId, StreamMode::READ );
+
+ // Store it now!
+ uno::Reference< embed::XStorage > xDocStg = GetDoc().GetDocStorage();
+ if (xDocStg.is())
+ {
+ uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement(
+ "OLELinks", embed::ElementModes::WRITE );
+ tools::SvRef<SotStorage> xObjDst = SotStorage::OpenOLEStorage( xOleStg, sOleId );
+
+ if ( xObjDst.is() )
+ {
+ xSrc1->CopyTo( xObjDst.get() );
+
+ if ( !xObjDst->GetError() )
+ xObjDst->Commit();
+ }
+
+ uno::Reference< embed::XTransactedObject > xTransact( xOleStg, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+ }
+
+ // Store the OLE Id as a parameter
+ pFieldmark->GetParameters()->insert(
+ std::pair< OUString, uno::Any >(
+ ODF_OLE_PARAM, uno::Any( sOleId ) ) );
+ }
+ }
+ }
+
+ break;
+ }
+ m_aFieldStack.pop_back();
+ }
+ return nRet;
+}
+
+static bool AcceptableNestedField(sal_uInt16 nFieldCode)
+{
+ switch (nFieldCode)
+ {
+ case ww::eINDEX: // allow recursive field in TOC...
+ case ww::eTOC: // allow recursive field in TOC...
+ case ww::eMERGEINC:
+ case ww::eINCLUDETEXT:
+ case ww::eAUTOTEXT:
+ case ww::eHYPERLINK:
+ // Accept AutoTextList field as nested field.
+ // Thus, the field result is imported as plain text.
+ case ww::eAUTOTEXTLIST:
+ // tdf#129247 CONTROL contains a nested SHAPE field in the result
+ case ww::eCONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+WW8FieldEntry::WW8FieldEntry(SwPosition const &rPos, sal_uInt16 nFieldId) noexcept
+ : maStartPos(rPos), mnFieldId(nFieldId), mnObjLocFc(0)
+{
+}
+
+WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry &rOther) noexcept
+ : maStartPos(rOther.maStartPos), mnFieldId(rOther.mnFieldId), mnObjLocFc(rOther.mnObjLocFc)
+{
+}
+
+void WW8FieldEntry::Swap(WW8FieldEntry &rOther) noexcept
+{
+ std::swap(maStartPos, rOther.maStartPos);
+ std::swap(mnFieldId, rOther.mnFieldId);
+}
+
+WW8FieldEntry &WW8FieldEntry::operator=(const WW8FieldEntry &rOther) noexcept
+{
+ WW8FieldEntry aTemp(rOther);
+ Swap(aTemp);
+ return *this;
+}
+
+
+void WW8FieldEntry::SetBookmarkName(const OUString& bookmarkName)
+{
+ msBookmarkName=bookmarkName;
+}
+
+void WW8FieldEntry::SetBookmarkType(const OUString& bookmarkType)
+{
+ msMarkType=bookmarkType;
+}
+
+void WW8FieldEntry::SetBookmarkCode(const OUString& bookmarkCode)
+{
+ msMarkCode = bookmarkCode;
+}
+
+
+// Read_Field reads a field or returns 0 if the field cannot be read,
+// so that the calling function reads the field in text format.
+// Returnvalue: Total length of field
+tools::Long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes)
+{
+ typedef eF_ResT (SwWW8ImplReader::*FNReadField)( WW8FieldDesc*, OUString& );
+ enum Limits {eMax = 96};
+ static const FNReadField aWW8FieldTab[eMax+1] =
+ {
+ nullptr,
+ &SwWW8ImplReader::Read_F_Input,
+ nullptr,
+ &SwWW8ImplReader::Read_F_Ref, // 3
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_Set, // 6
+ nullptr,
+ &SwWW8ImplReader::Read_F_Tox, // 8
+ nullptr,
+ &SwWW8ImplReader::Read_F_Styleref, // 10
+ nullptr,
+ &SwWW8ImplReader::Read_F_Seq, // 12
+ &SwWW8ImplReader::Read_F_Tox, // 13
+ &SwWW8ImplReader::Read_F_DocInfo, // 14
+ &SwWW8ImplReader::Read_F_DocInfo, // 15
+ &SwWW8ImplReader::Read_F_DocInfo, // 16
+ &SwWW8ImplReader::Read_F_Author, // 17
+ &SwWW8ImplReader::Read_F_DocInfo, // 18
+ &SwWW8ImplReader::Read_F_DocInfo, // 19
+ &SwWW8ImplReader::Read_F_DocInfo, // 20
+ &SwWW8ImplReader::Read_F_DocInfo, // 21
+ &SwWW8ImplReader::Read_F_DocInfo, // 22
+ &SwWW8ImplReader::Read_F_DocInfo, // 23
+ &SwWW8ImplReader::Read_F_DocInfo, // 24
+ &SwWW8ImplReader::Read_F_DocInfo, // 25
+ &SwWW8ImplReader::Read_F_Num, // 26
+ &SwWW8ImplReader::Read_F_Num, // 27
+ &SwWW8ImplReader::Read_F_Num, // 28
+ &SwWW8ImplReader::Read_F_FileName, // 29
+ &SwWW8ImplReader::Read_F_TemplName, // 30
+ &SwWW8ImplReader::Read_F_DateTime, // 31
+ &SwWW8ImplReader::Read_F_DateTime, // 32
+ &SwWW8ImplReader::Read_F_CurPage, // 33
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_IncludeText, // 36
+ &SwWW8ImplReader::Read_F_PgRef, // 37
+ &SwWW8ImplReader::Read_F_InputVar, // 38
+ &SwWW8ImplReader::Read_F_Input, // 39
+ nullptr,
+ &SwWW8ImplReader::Read_F_DBNext, // 41
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_DBNum, // 44
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_Equation, // 49
+ nullptr,
+ &SwWW8ImplReader::Read_F_Macro, // 51
+ &SwWW8ImplReader::Read_F_ANumber, // 52
+ &SwWW8ImplReader::Read_F_ANumber, // 53
+ &SwWW8ImplReader::Read_F_ANumber, // 54
+ nullptr,
+
+ nullptr, // 56
+
+ &SwWW8ImplReader::Read_F_Symbol, // 57
+ &SwWW8ImplReader::Read_F_Embedd, // 58
+ &SwWW8ImplReader::Read_F_DBField, // 59
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_DocInfo, // 64 - DOCVARIABLE
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_IncludePicture, // 67
+ &SwWW8ImplReader::Read_F_IncludeText, // 68
+ nullptr,
+ &SwWW8ImplReader::Read_F_FormTextBox, // 70
+ &SwWW8ImplReader::Read_F_FormCheckBox, // 71
+ &SwWW8ImplReader::Read_F_NoteReference, // 72
+ nullptr, /*&SwWW8ImplReader::Read_F_Tox*/
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &SwWW8ImplReader::Read_F_FormListBox, // 83
+ nullptr, // 84
+ &SwWW8ImplReader::Read_F_DocInfo, // 85
+ nullptr, // 86
+ &SwWW8ImplReader::Read_F_OCX, // 87
+ &SwWW8ImplReader::Read_F_Hyperlink, // 88
+ nullptr, // 89
+ nullptr, // 90
+ &SwWW8ImplReader::Read_F_HTMLControl, // 91
+ nullptr, // 92
+ nullptr, // 93
+ nullptr, // 94
+ &SwWW8ImplReader::Read_F_Shape, // 95
+ nullptr // eMax - Dummy empty method
+ };
+ OSL_ENSURE( SAL_N_ELEMENTS( aWW8FieldTab ) == eMax+1, "FieldFunc table not right" );
+
+ WW8PLCFx_FLD* pF = m_xPlcxMan->GetField();
+ OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available");
+
+ if (!pF || !pF->StartPosIsFieldStart())
+ return 0;
+
+ bool bNested = false;
+ if (!m_aFieldStack.empty())
+ {
+ bNested = std::any_of(m_aFieldStack.cbegin(), m_aFieldStack.cend(),
+ [](const WW8FieldEntry& aField) { return !AcceptableNestedField(aField.mnFieldId); });
+ }
+
+ WW8FieldDesc aF;
+ bool bOk = pF->GetPara(pRes->nCp2OrIdx, aF);
+
+ OSL_ENSURE(bOk, "WW8: Bad Field!");
+ if (aF.nId == 33) aF.bCodeNest=false; // do not recurse into nested page fields
+ bool bCodeNest = aF.bCodeNest;
+ if ( aF.nId == 6 ) bCodeNest = false; // We can handle them and lose the inner data
+ if (aF.nId == 70) bCodeNest = false; // need to import 0x01 in FORMTEXT
+
+ m_aFieldStack.emplace_back(*m_pPaM->GetPoint(), aF.nId);
+
+ if (bNested)
+ return 0;
+
+ sal_uInt16 n = (aF.nId <= eMax) ? aF.nId : o3tl::narrowing<sal_uInt16>(eMax);
+ sal_uInt16 nI = n / 32; // # of sal_uInt32
+ sal_uInt32 nMask = 1 << ( n % 32 ); // Mask for bits
+
+ if (SAL_N_ELEMENTS(m_nFieldTagAlways) <= nI)
+ { // if indexes larger than 95 are needed, then a new configuration
+ // item has to be added, and nFieldTagAlways/nFieldTagBad expanded!
+ return aF.nLen;
+ }
+
+ if( m_nFieldTagAlways[nI] & nMask ) // Flag: Tag it
+ return Read_F_Tag( &aF ); // Result not as text
+
+ if( !bOk || !aF.nId ) // Field corrupted
+ return aF.nLen; // -> ignore
+
+ if( aF.nId > eMax - 1) // WW: Nested Field
+ {
+ if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
+ return Read_F_Tag( &aF ); // Result not as text
+ else
+ return aF.nLen;
+ }
+
+ //Only one type of field (hyperlink) in drawing textboxes exists
+ if (aF.nId != 88 && m_xPlcxMan->GetDoingDrawTextBox())
+ return aF.nLen;
+
+ bool bHasHandler = aWW8FieldTab[aF.nId] != nullptr;
+ if (aF.nId == 10) // STYLEREF
+ {
+ bool bHandledByChapter = false;
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+ OUString aStr;
+ aF.nLCode = m_xSBase->WW8ReadString(*m_pStrm, aStr, m_xPlcxMan->GetCpOfs() + aF.nSCode, aF.nLCode, m_eTextCharSet);
+ m_pStrm->Seek(nOldPos);
+
+ WW8ReadFieldParams aReadParam(aStr);
+ sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if (nRet == -2 && !aReadParam.GetResult().isEmpty())
+ // Single numeric argument: this can be handled by SwChapterField.
+ bHandledByChapter = rtl::isAsciiDigit(aReadParam.GetResult()[0]);
+
+ if (bHandledByChapter)
+ {
+ nRet = aReadParam.SkipToNextToken();
+ // Handle using SwChapterField only in case there is no \[a-z]
+ // switch after the field argument.
+ bHasHandler = nRet < 0 || nRet == '*';
+ }
+ }
+
+ // no routine available
+ if (!bHasHandler || bCodeNest)
+ {
+ if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad
+ return Read_F_Tag( &aF ); // Result not as text
+ // only read result
+ if (aF.bResNest && !AcceptableNestedField(aF.nId))
+ return aF.nLen; // Result nested -> unusable
+
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+ OUString aStr;
+ aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+
+ aF.nSCode, aF.nLCode, m_eTextCharSet );
+ m_pStrm->Seek( nOldPos );
+
+ // field codes which contain '/' or '.' are not displayed in WinWord
+ // skip if it is formula field or found space before. see #i119446, #i119585.
+ const sal_Int32 nDotPos = aStr.indexOf('.');
+ const sal_Int32 nSlashPos = aStr.indexOf('/');
+ sal_Int32 nSpacePos = aStr.indexOf( ' ', 1 );
+ if ( nSpacePos<0 )
+ nSpacePos = aStr.getLength();
+
+ if ( ( aStr.getLength() <= 1 || aStr[1] != '=') &&
+ (( nDotPos>=0 && nDotPos < nSpacePos ) ||
+ ( nSlashPos>=0 && nSlashPos < nSpacePos )))
+ return aF.nLen;
+ else
+ {
+ // Link fields aren't supported, but they are bound to an OLE object
+ // that needs to be roundtripped
+ if ( aF.nId == 56 )
+ m_bEmbeddObj = true;
+ // Field not supported: store the field code for later use
+ m_aFieldStack.back().SetBookmarkCode( aStr );
+
+ if (aF.nId == ww::eIF)
+ {
+ // In MS Word, the IF field is editable and requires a manual refresh
+ // so the last, saved result might not match either of the true or false options.
+ // But in LO the field is automatically updated and not editable,
+ // so the previous result is of no value to import since it could never be seen.
+ return aF.nLen;
+ }
+
+ return aF.nLen - aF.nLRes - 1; // skipped too many, the resulted field will be read like main text
+ }
+ }
+ else
+ { // read field
+ auto nOldPos = m_pStrm->Tell();
+ OUString aStr;
+ if ( aF.nId == 6 && aF.bCodeNest )
+ {
+ // TODO Extract the whole code string using the nested codes
+ aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs() +
+ aF.nSCode, aF.nSRes - aF.nSCode - 1, m_eTextCharSet );
+ }
+ else
+ {
+ aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+
+ aF.nSCode, aF.nLCode, m_eTextCharSet );
+ }
+
+ // #i51312# - graphics inside field code not supported by Writer.
+ // Thus, delete character 0x01, which stands for such a graphic.
+ if (aF.nId==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01.
+ {
+ aStr = aStr.replaceAll("\x01", "");
+ }
+
+ eF_ResT eRes = (this->*aWW8FieldTab[aF.nId])( &aF, aStr );
+ m_pStrm->Seek(nOldPos);
+
+ switch ( eRes )
+ {
+ case eF_ResT::OK:
+ return aF.nLen;
+ case eF_ResT::TEXT:
+ // skipped too many, the resulted field will be read like main text
+ // attributes can start at char 0x14 so skip one
+ // char more back == "-2"
+ if (aF.nLRes)
+ return aF.nLen - aF.nLRes - 2;
+ else
+ return aF.nLen;
+ case eF_ResT::TAGIGN:
+ if ( m_nFieldTagBad[nI] & nMask ) // Flag: Tag bad
+ return Read_F_Tag( &aF ); // Tag it
+ return aF.nLen; // or ignore
+ case eF_ResT::READ_FSPA:
+ return aF.nLen - aF.nLRes - 2; // position on char 1
+ default:
+ return aF.nLen; // ignore
+ }
+ }
+}
+
+// Tag fields
+
+// MakeTagString() returns the position of the first CR / end of line / page break
+// in pText and converts only up to this point.
+// If none of these special characters is found, the function returns 0.
+void SwWW8ImplReader::MakeTagString( OUString& rStr, const OUString& rOrg )
+{
+ bool bAllowCr = SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT )
+ || SwFltGetFlag( m_nFieldFlags, SwFltControlStack::ALLOW_FLD_CR );
+ sal_Unicode cChar;
+ rStr = rOrg;
+
+ for( sal_Int32 nI = 0;
+ nI < rStr.getLength() && rStr.getLength() < (MAX_FIELDLEN - 4); ++nI )
+ {
+ bool bSetAsHex = false;
+ cChar = rStr[ nI ];
+ switch( cChar )
+ {
+ case 132: // Exchange typographical quotation marks for normal ones
+ case 148:
+ case 147:
+ rStr = rStr.replaceAt( nI, 1, u"\"" );
+ break;
+ case 19:
+ rStr = rStr.replaceAt( nI, 1, u"{" );
+ break; // 19..21 to {|}
+ case 20:
+ rStr = rStr.replaceAt( nI, 1, u"|" );
+ break;
+ case 21:
+ rStr = rStr.replaceAt( nI, 1, u"}" );
+ break;
+ case '\\': // Tag \{|} with \ ...
+ case '{':
+ case '|':
+ case '}':
+ rStr = rStr.replaceAt( nI, 0, u"\\" );
+ ++nI;
+ break;
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ if( bAllowCr )
+ rStr = rStr.replaceAt( nI, 1, u"\n" );
+ else
+ bSetAsHex = true;
+ break;
+ case 0xFE:
+ case 0xFF:
+ bSetAsHex = true;
+ break;
+ default:
+ bSetAsHex = 0x20 > cChar;
+ break;
+ }
+
+ if( bSetAsHex )
+ {
+ //all Hex-Numbers with \x before
+ OUString sTmp( "\\x" );
+ if( cChar < 0x10 )
+ sTmp += "0";
+ sTmp += OUString::number( cChar, 16 );
+ rStr = rStr.replaceAt( nI, 1 , sTmp );
+ nI += sTmp.getLength() - 1;
+ }
+ }
+
+ if( rStr.getLength() > (MAX_FIELDLEN - 4))
+ rStr = rStr.copy( 0, MAX_FIELDLEN - 4 );
+}
+
+void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId, const OUString& rTagText )
+{
+ OUString aName("WwFieldTag");
+ if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_DO_ID ) ) // Number?
+ aName += OUString::number( nId ); // return it?
+
+ if( SwFltGetFlag(m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT))
+ {
+ aName += rTagText; // tag as text
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, aName,
+ SwInsertFlags::NOHINTEXPAND);
+ }
+ else
+ { // tag normally
+
+ SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ SwSetExpFieldType( &m_rDoc, aName, nsSwGetSetExpType::GSE_STRING ) );
+ SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), rTagText ); // SUB_INVISIBLE
+ sal_uInt16 nSubType = ( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_VISIBLE ) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE;
+ aField.SetSubType(nSubType | nsSwGetSetExpType::GSE_STRING);
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ }
+}
+
+WW8_CP SwWW8ImplReader::Read_F_Tag( WW8FieldDesc* pF )
+{
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+
+ WW8_CP nStart = pF->nSCode - 1; // starting with 0x19
+ WW8_CP nL = pF->nLen; // Total length with result and nest
+ if( nL > MAX_FIELDLEN )
+ nL = MAX_FIELDLEN; // MaxLength, by quoting
+ // max. 4 times as big
+ OUString sFText;
+ m_xSBase->WW8ReadString( *m_pStrm, sFText,
+ m_xPlcxMan->GetCpOfs() + nStart, nL, m_eStructCharSet);
+
+ OUString aTagText;
+ MakeTagString( aTagText, sFText );
+ InsertTagField( pF->nId, aTagText );
+
+ m_pStrm->Seek( nOldPos );
+ return pF->nLen;
+}
+
+// normal fields
+
+eF_ResT SwWW8ImplReader::Read_F_Input( WW8FieldDesc* pF, OUString& rStr )
+{
+ OUString aDef;
+ OUString aQ;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aQ.isEmpty() )
+ aQ = aReadParam.GetResult();
+ break;
+ case 'd':
+ case 'D':
+ if ( aReadParam.GoToTokenParam() )
+ aDef = aReadParam.GetResult();
+ break;
+ }
+ }
+ if( aDef.isEmpty() )
+ aDef = GetFieldResult( pF );
+
+ if ( pF->nId != 0x01 ) // 0x01 fields have no result
+ {
+ SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
+ aDef, aQ, INP_TXT, 0, false );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ }
+
+ return eF_ResT::OK;
+}
+
+// GetFieldResult allocates a string and reads the resulted field
+OUString SwWW8ImplReader::GetFieldResult( WW8FieldDesc const * pF )
+{
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+
+ WW8_CP nStart = pF->nSRes; // result start
+ WW8_CP nL = pF->nLRes; // result length
+ if( !nL )
+ return OUString(); // no result
+
+ if( nL > MAX_FIELDLEN )
+ nL = MAX_FIELDLEN; // MaxLength, by quoting
+ // max. 4 times as big
+
+ OUString sRes;
+ m_xSBase->WW8ReadString( *m_pStrm, sRes, m_xPlcxMan->GetCpOfs() + nStart,
+ nL, m_eStructCharSet );
+
+ m_pStrm->Seek( nOldPos );
+
+ //replace both CR 0x0D and VT 0x0B with LF 0x0A
+ // at least in the cases where the result is added to an SwInputField
+ // there must not be control characters in it
+ OUStringBuffer buf(sRes.getLength());
+ for (sal_Int32 i = 0; i < sRes.getLength(); ++i)
+ {
+ sal_Unicode const ch(sRes[i]);
+ if (!linguistic::IsControlChar(ch))
+ {
+ buf.append(ch);
+ }
+ else
+ {
+ switch (ch)
+ {
+ case 0x0B:
+ case '\r':
+ buf.append('\n');
+ break;
+ case '\n':
+ case '\t':
+ buf.append(ch);
+ break;
+ default:
+ SAL_INFO("sw.ww8", "GetFieldResult(): filtering control character");
+ break;
+ }
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+/*
+Bookmarks can be set with fields SET and ASK, and they can be referenced with
+REF. When set, they behave like variables in writer, otherwise they behave
+like normal bookmarks. We can check whether we should use a show variable
+instead of a normal bookmark ref by converting to "show variable" at the end
+of the document those refs which look for the content of a bookmark but whose
+bookmarks were set with SET or ASK. (See SwWW8FltRefStack)
+
+The other piece of the puzzle is that refs that point to the "location" of the
+bookmark will in word actually point to the last location where the bookmark
+was set with SET or ASK, not the actual bookmark. This is only noticeable when
+a document sets the bookmark more than once. This is because word places the
+true bookmark at the location of the last set, but the refs will display the
+position of the first set before the ref.
+
+So what we will do is
+
+1) keep a list of all bookmarks that were set, any bookmark names mentioned
+here that are referred by content will be converted to show variables.
+
+2) create pseudo bookmarks for every position that a bookmark is set with SET
+or ASK but has no existing bookmark. We can then keep a map from the original
+bookmark name to the new one. As we parse the document new pseudo names will
+replace the older ones, so the map always contains the bookmark of the
+location that msword itself would use.
+
+3) word's bookmarks are case insensitive, writers are not. So we need to
+map case different versions together, regardless of whether they are
+variables or not.
+
+4) when a reference is (first) SET or ASK, the bookmark associated with it
+is placed around the 0x14 0x15 result part of the field. We will fiddle
+the placement to be the writer equivalent of directly before and after
+the field, which gives the same effect and meaning, to do so we must
+get any bookmarks in the field range, and begin them immediately before
+the set/ask field, and end them directly afterwards. MapBookmarkVariables
+returns an identifier of the bookmark attribute to close after inserting
+the appropriate set/ask field.
+*/
+tools::Long SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc* pF,
+ OUString &rOrigName, const OUString &rData)
+{
+ OSL_ENSURE(m_xPlcxMan, "No pPlcxMan");
+ tools::Long nNo;
+ /*
+ If there was no bookmark associated with this set field, then we create a
+ pseudo one and insert it in the document.
+ */
+ sal_uInt16 nIndex;
+ m_xPlcxMan->GetBook()->MapName(rOrigName);
+ OUString sName = m_xPlcxMan->GetBook()->GetBookmark(
+ pF->nSCode, pF->nSCode + pF->nLen, nIndex);
+ if (!sName.isEmpty())
+ {
+ m_xPlcxMan->GetBook()->SetStatus(nIndex, BOOK_IGNORE);
+ nNo = nIndex;
+ }
+ else
+ {
+ nNo = m_xReffingStck->m_aFieldVarNames.size()+1;
+ sName = "WWSetBkmk" + OUString::number(nNo);
+ nNo += m_xPlcxMan->GetBook()->GetIMax();
+ }
+ m_xReffedStck->NewAttr(*m_pPaM->GetPoint(),
+ SwFltBookmark( BookmarkToWriter(sName), rData, nNo ));
+ m_xReffingStck->m_aFieldVarNames[rOrigName] = sName;
+ return nNo;
+}
+
+/*
+Word can set a bookmark with set or with ask, such a bookmark is equivalent to
+our variables, but until the end of a document we cannot be sure if a bookmark
+is a variable or not, at the end we will have a list of reference names which
+were set or asked, all bookmarks using the content of those bookmarks are
+converted to show variables, those that reference the position of the field
+can be left as references, because a bookmark is also inserted at the position
+of a set or ask field, either by word, or in some special cases by the import
+filter itself.
+*/
+SwFltStackEntry *SwWW8FltRefStack::RefToVar(const SwField* pField,
+ SwFltStackEntry &rEntry)
+{
+ SwFltStackEntry *pRet=nullptr;
+ if (pField && SwFieldIds::GetRef == pField->Which())
+ {
+ //Get the name of the ref field, and see if actually a variable
+ const OUString sName = pField->GetPar1();
+ std::map<OUString, OUString, SwWW8::ltstr>::const_iterator
+ aResult = m_aFieldVarNames.find(sName);
+
+ if (aResult != m_aFieldVarNames.end())
+ {
+ SwGetExpField aField( static_cast<SwGetExpFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp)), sName, nsSwGetSetExpType::GSE_STRING, 0);
+ SwFormatField aTmp(aField);
+ rEntry.m_pAttr.reset( aTmp.Clone() );
+ pRet = &rEntry;
+ }
+ }
+ return pRet;
+}
+
+OUString SwWW8ImplReader::GetMappedBookmark(std::u16string_view rOrigName)
+{
+ OUString sName(BookmarkToWriter(rOrigName));
+ OSL_ENSURE(m_xPlcxMan, "no pPlcxMan");
+ m_xPlcxMan->GetBook()->MapName(sName);
+
+ //See if there has been a variable set with this name, if so get
+ //the pseudo bookmark name that was set with it.
+ std::map<OUString, OUString, SwWW8::ltstr>::const_iterator aResult =
+ m_xReffingStck->m_aFieldVarNames.find(sName);
+
+ return (aResult == m_xReffingStck->m_aFieldVarNames.end())
+ ? sName : (*aResult).second;
+}
+
+// "ASK"
+eF_ResT SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc* pF, OUString& rStr )
+{
+ OUString sOrigName, aQ;
+ OUString aDef;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if (sOrigName.isEmpty())
+ sOrigName = aReadParam.GetResult();
+ else if (aQ.isEmpty())
+ aQ = aReadParam.GetResult();
+ break;
+ case 'd':
+ case 'D':
+ if ( aReadParam.GoToTokenParam() )
+ aDef = aReadParam.GetResult();
+ break;
+ }
+ }
+
+ if (sOrigName.isEmpty())
+ return eF_ResT::TAGIGN; // does not make sense without textmark
+
+ const OUString aResult(GetFieldResult(pF));
+
+ //#i24377#, munge Default Text into title as we have only one slot
+ //available for aResult and aDef otherwise
+ if (!aDef.isEmpty())
+ {
+ if (!aQ.isEmpty())
+ aQ += " - ";
+ aQ += aDef;
+ }
+
+ const tools::Long nNo = MapBookmarkVariables(pF, sOrigName, aResult);
+
+ SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ SwSetExpFieldType(&m_rDoc, sOrigName, nsSwGetSetExpType::GSE_STRING)));
+ SwSetExpField aField(pFT, aResult);
+ aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
+ aField.SetInputFlag(true);
+ aField.SetPromptText( aQ );
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+
+ m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
+ return eF_ResT::OK;
+}
+
+// "AUTONR"
+eF_ResT SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc*, OUString& rStr )
+{
+ if( !m_pNumFieldType ){ // 1st time
+ SwSetExpFieldType aT( &m_rDoc, "AutoNr", nsSwGetSetExpType::GSE_SEQ );
+ m_pNumFieldType = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aT ));
+ }
+ SwSetExpField aField( m_pNumFieldType, OUString(), GetNumberPara( rStr ) );
+ aField.SetValue( ++m_nFieldNum, nullptr );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ return eF_ResT::OK;
+}
+
+// "SEQ"
+eF_ResT SwWW8ImplReader::Read_F_Seq( WW8FieldDesc*, OUString& rStr )
+{
+ OUString aSequenceName;
+ OUString aBook;
+ bool bHidden = false;
+ bool bFormat = false;
+ bool bCountOn = true;
+ OUString sStart;
+ SvxNumType eNumFormat = SVX_NUM_ARABIC;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aSequenceName.isEmpty() )
+ aSequenceName = aReadParam.GetResult();
+ else if( aBook.isEmpty() )
+ aBook = aReadParam.GetResult();
+ break;
+
+ case 'h':
+ if( !bFormat )
+ bHidden = true; // activate hidden flag
+ break;
+
+ case '*':
+ bFormat = true; // activate format flag
+ if ( aReadParam.SkipToNextToken()!=-2 )
+ break;
+ if ( aReadParam.GetResult()!="MERGEFORMAT" && aReadParam.GetResult()!="CHARFORMAT" )
+ eNumFormat = GetNumTypeFromName( aReadParam.GetResult() );
+ break;
+
+ case 'r':
+ bCountOn = false;
+ if ( aReadParam.SkipToNextToken()==-2 )
+ sStart = aReadParam.GetResult();
+ break;
+
+ case 'c':
+ bCountOn = false;
+ break;
+
+ case 'n':
+ bCountOn = true; // Increase value by one (default)
+ break;
+
+ case 's': // Outline Level
+ //#i19682, what I have to do with this value?
+ break;
+ }
+ }
+ if (aSequenceName.isEmpty() && aBook.isEmpty())
+ return eF_ResT::TAGIGN;
+
+ SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ SwSetExpFieldType( &m_rDoc, aSequenceName, nsSwGetSetExpType::GSE_SEQ ) ) );
+ SwSetExpField aField( pFT, OUString(), eNumFormat );
+
+ //#i120654# Add bHidden for /h flag (/h: Hide the field result.)
+ if (bHidden)
+ aField.SetSubType(aField.GetSubType() | nsSwExtendedSubType::SUB_INVISIBLE);
+
+ if (!sStart.isEmpty())
+ aField.SetFormula( aSequenceName + "=" + sStart );
+ else if (!bCountOn)
+ aField.SetFormula(aSequenceName);
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_Styleref(WW8FieldDesc*, OUString& rString)
+{
+ WW8ReadFieldParams aReadParam(rString);
+ sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if (nRet != -2)
+ // \param was found, not normal text.
+ return eF_ResT::TAGIGN;
+
+ OUString aResult = aReadParam.GetResult();
+ sal_Int32 nResult = aResult.toInt32();
+ if (nResult < 1)
+ return eF_ResT::TAGIGN;
+
+ SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Chapter);
+ SwChapterField aField(static_cast<SwChapterFieldType*>(pFieldType), CF_TITLE);
+ aField.SetLevel(nResult - 1);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr )
+{
+ sal_uInt16 nSub=0;
+ // RegInfoFormat, DefaultFormat for DocInfoFields
+ sal_uInt16 nReg = DI_SUB_AUTHOR;
+ bool bDateTime = false;
+ const sal_uInt16 nFldLock = (pF->nOpt & 0x10) ? DI_SUB_FIXED : 0;
+
+ if( 85 == pF->nId )
+ {
+ OUString aDocProperty;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aDocProperty.isEmpty() )
+ aDocProperty = aReadParam.GetResult();
+ break;
+ case '*':
+ //Skip over MERGEFORMAT
+ (void)aReadParam.SkipToNextToken();
+ break;
+ }
+ }
+
+ aDocProperty = aDocProperty.replaceAll("\"", "");
+
+ /*
+ There are up to 26 fields that may be meant by 'DocumentProperty'.
+ Which of them is to be inserted here ?
+ This Problem can only be solved by implementing a name matching
+ method that compares the given Parameter String with the four
+ possible name sets (english, german, french, spanish)
+ */
+
+ static const char* aName10 = "\x0F"; // SW field code
+ static const char* aName11 // German
+ = "TITEL";
+ static const char* aName12 // French
+ = "TITRE";
+ static const char* aName13 // English
+ = "TITLE";
+ static const char* aName14 // Spanish
+ = "TITRO";
+ static const char* aName20 = "\x15"; // SW field code
+ static const char* aName21 // German
+ = "ERSTELLDATUM";
+ static const char* aName22 // French
+ = "CR\xC9\xC9";
+ static const char* aName23 // English
+ = "CREATED";
+ static const char* aName24 // Spanish
+ = "CREADO";
+ static const char* aName30 = "\x16"; // SW field code
+ static const char* aName31 // German
+ = "ZULETZTGESPEICHERTZEIT";
+ static const char* aName32 // French
+ = "DERNIERENREGISTREMENT";
+ static const char* aName33 // English
+ = "SAVED";
+ static const char* aName34 // Spanish
+ = "MODIFICADO";
+ static const char* aName40 = "\x17"; // SW field code
+ static const char* aName41 // German
+ = "ZULETZTGEDRUCKT";
+ static const char* aName42 // French
+ = "DERNI\xC8" "REIMPRESSION";
+ static const char* aName43 // English
+ = "LASTPRINTED";
+ static const char* aName44 // Spanish
+ = "HUPS PUPS";
+ static const char* aName50 = "\x18"; // SW field code
+ static const char* aName51 // German
+ = "\xDC" "BERARBEITUNGSNUMMER";
+ static const char* aName52 // French
+ = "NUM\xC9" "RODEREVISION";
+ static const char* aName53 // English
+ = "REVISIONNUMBER";
+ static const char* aName54 // Spanish
+ = "SNUBBEL BUBBEL";
+ static const sal_uInt16 nFieldCnt = 5;
+
+ // additional fields are to be coded soon!
+
+ static const sal_uInt16 nLangCnt = 4;
+ static const char *aNameSet_26[nFieldCnt][nLangCnt+1] =
+ {
+ {aName10, aName11, aName12, aName13, aName14},
+ {aName20, aName21, aName22, aName23, aName24},
+ {aName30, aName31, aName32, aName33, aName34},
+ {aName40, aName41, aName42, aName43, aName44},
+ {aName50, aName51, aName52, aName53, aName54}
+ };
+
+ bool bFieldFound= false;
+ sal_uInt16 nFIdx;
+ for(sal_uInt16 nLIdx=1; !bFieldFound && (nLangCnt > nLIdx); ++nLIdx)
+ {
+ for(nFIdx = 0; !bFieldFound && (nFieldCnt > nFIdx); ++nFIdx)
+ {
+ if( aDocProperty == OUString( aNameSet_26[nFIdx][nLIdx], strlen(aNameSet_26[nFIdx][nLIdx]),
+ RTL_TEXTENCODING_MS_1252 ) )
+ {
+ bFieldFound = true;
+ pF->nId = aNameSet_26[nFIdx][0][0];
+ }
+ }
+ }
+
+ if( !bFieldFound )
+ {
+ // LO always automatically updates a DocInfo field from the File-Properties-Custom Prop
+ // while MS Word requires the user to manually refresh the field (with F9).
+ // In other words, Word lets the field to be out of sync with the controlling variable.
+ // Marking as FIXEDFLD solves the automatic replacement problem, but of course prevents
+ // Writer from making any changes, even on an F9 refresh.
+ // TODO: Extend LO to allow a linked field that doesn't automatically update.
+ IDocumentContentOperations& rIDCO(m_rDoc.getIDocumentContentOperations());
+ const auto pType(static_cast<SwDocInfoFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo)));
+ const OUString sDisplayed = GetFieldResult(pF);
+ SwDocInfoField aField(pType, DI_CUSTOM | nReg, aDocProperty);
+
+ // If text already matches the DocProperty var, then safe to treat as refreshable field.
+ OUString sVariable = aField.ExpandField(/*bCache=*/false, nullptr);
+ if (sDisplayed.getLength() != sVariable.getLength())
+ {
+ sal_Int32 nLen = sVariable.indexOf('\x0');
+ if (nLen >= 0)
+ sVariable = sVariable.copy(0, nLen);
+ }
+ if (sDisplayed == sVariable)
+ rIDCO.InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ else
+ {
+ // They don't match, so use a fixed field to prevent LO from altering the contents.
+ SwDocInfoField aFixedField(pType, DI_CUSTOM | DI_SUB_FIXED | nReg, aDocProperty,
+ sDisplayed);
+ rIDCO.InsertPoolItem(*m_pPaM, SwFormatField(aFixedField));
+ }
+
+ return eF_ResT::OK;
+ }
+ }
+
+ switch( pF->nId )
+ {
+ case 14:
+ /* supports all INFO variables! */
+ nSub = DI_KEYS;
+ break;
+ case 15:
+ nSub = DI_TITLE;
+ break;
+ case 16:
+ nSub = DI_SUBJECT;
+ break;
+ case 18:
+ nSub = DI_KEYS;
+ break;
+ case 19:
+ nSub = DI_COMMENT;
+ break;
+ case 20:
+ // MS Word never updates this automatically, so mark as fixed for best compatibility
+ nSub = DI_CHANGE | DI_SUB_FIXED;
+ nReg = DI_SUB_AUTHOR;
+ break;
+ case 21:
+ // The real create date can never change, so mark as fixed for best compatibility
+ nSub = DI_CREATE | DI_SUB_FIXED;
+ nReg = DI_SUB_DATE;
+ bDateTime = true;
+ break;
+ case 23:
+ nSub = DI_PRINT | nFldLock;
+ nReg = DI_SUB_DATE;
+ bDateTime = true;
+ break;
+ case 24:
+ nSub = DI_DOCNO;
+ break;
+ case 22:
+ nSub = DI_CHANGE | nFldLock;
+ nReg = DI_SUB_DATE;
+ bDateTime = true;
+ break;
+ case 25:
+ nSub = DI_CHANGE | nFldLock;
+ nReg = DI_SUB_TIME;
+ bDateTime = true;
+ break;
+ case 64: // DOCVARIABLE
+ nSub = DI_CUSTOM;
+ break;
+ }
+
+ sal_uInt32 nFormat = 0;
+
+ LanguageType nLang(LANGUAGE_SYSTEM);
+ if (bDateTime)
+ {
+ SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, pF->nId);
+ switch (nDT)
+ {
+ case SvNumFormatType::DATE:
+ nReg = DI_SUB_DATE;
+ break;
+ case SvNumFormatType::TIME:
+ nReg = DI_SUB_TIME;
+ break;
+ case SvNumFormatType::DATETIME:
+ nReg = DI_SUB_DATE;
+ break;
+ default:
+ nReg = DI_SUB_DATE;
+ break;
+ }
+ }
+
+ OUString aData;
+ // Extract DOCVARIABLE varname
+ if ( 64 == pF->nId )
+ {
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1)
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aData.isEmpty() )
+ aData = aReadParam.GetResult();
+ break;
+ case '*':
+ //Skip over MERGEFORMAT
+ (void)aReadParam.SkipToNextToken();
+ break;
+ }
+ }
+
+ aData = aData.replaceAll("\"", "");
+ }
+
+ const auto pType(static_cast<SwDocInfoFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo)));
+ SwDocInfoField aField(pType, nSub|nReg, aData, GetFieldResult(pF), nFormat);
+ if (bDateTime)
+ ForceFieldLanguage(aField, nLang);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_Author(WW8FieldDesc* pF, OUString&)
+{
+ // SH: The SwAuthorField refers not to the original author but to the current user, better use DocInfo
+ SwDocInfoField aField( static_cast<SwDocInfoFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )),
+ DI_CREATE|DI_SUB_AUTHOR|DI_SUB_FIXED, OUString(), GetFieldResult(pF));
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc*, OUString& )
+{
+ SwTemplNameField aField( static_cast<SwTemplNameFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName )), FF_NAME );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ return eF_ResT::OK;
+}
+
+// Both the date and the time fields can be used for showing a date a time or both.
+eF_ResT SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc*pF, OUString& rStr )
+{
+ bool bHijri = false;
+ WW8ReadFieldParams aReadParam(rStr);
+ for (;;)
+ {
+ const sal_Int32 nTok = aReadParam.SkipToNextToken();
+ if ( nTok==-1 )
+ break;
+ switch (nTok)
+ {
+ default:
+ case 'l':
+ case -2:
+ break;
+ case 'h':
+ bHijri = true;
+ break;
+ case 's':
+ //Saka Calendar, should we do something with this ?
+ break;
+ }
+ }
+
+ sal_uInt32 nFormat = 0;
+
+ LanguageType nLang(LANGUAGE_SYSTEM);
+ SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, ww::eDATE, bHijri);
+
+ if( SvNumFormatType::UNDEFINED == nDT ) // no D/T-Formatstring
+ {
+ if (32 == pF->nId)
+ {
+ nDT = SvNumFormatType::TIME;
+ nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
+ NF_TIME_START, LANGUAGE_SYSTEM );
+ }
+ else
+ {
+ nDT = SvNumFormatType::DATE;
+ nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex(
+ NF_DATE_START, LANGUAGE_SYSTEM );
+ }
+ }
+
+ if (nDT & SvNumFormatType::DATE || nDT == SvNumFormatType::TIME)
+ {
+ SwDateTimeField aField(static_cast<SwDateTimeFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime)),
+ nDT & SvNumFormatType::DATE ? DATEFLD : TIMEFLD, nFormat);
+ if (pF->nOpt & 0x10) // Fixed field
+ {
+ double fSerial;
+ if (!m_rDoc.GetNumberFormatter()->IsNumberFormat(GetFieldResult(pF), nFormat, fSerial,
+ SvNumInputOptions::LAX_TIME))
+ return eF_ResT::TEXT; // just drop the field and insert the plain text.
+ aField.SetSubType(aField.GetSubType() | FIXEDFLD);
+ DateTime aSetDateTime(m_rDoc.GetNumberFormatter()->GetNullDate());
+ aSetDateTime.AddTime(fSerial);
+ aField.SetDateTime(aSetDateTime);
+ }
+ ForceFieldLanguage(aField, nLang);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ }
+
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_FileName(WW8FieldDesc*, OUString &rStr)
+{
+ SwFileNameFormat eType = FF_NAME;
+ WW8ReadFieldParams aReadParam(rStr);
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch (nRet)
+ {
+ case 'p':
+ eType = FF_PATHNAME;
+ break;
+ case '*':
+ //Skip over MERGEFORMAT
+ (void)aReadParam.SkipToNextToken();
+ break;
+ default:
+ OSL_ENSURE(false, "unknown option in FileName field");
+ break;
+ }
+ }
+
+ SwFileNameField aField(
+ static_cast<SwFileNameFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename)), eType);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_Num( WW8FieldDesc* pF, OUString& rStr )
+{
+ sal_uInt16 nSub = DS_PAGE; // page number
+ switch ( pF->nId ){
+ case 27: nSub = DS_WORD; break; // number of words
+ case 28: nSub = DS_CHAR; break; // number of characters
+ }
+ SwDocStatField aField( static_cast<SwDocStatFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocStat )), nSub,
+ GetNumberPara( rStr ) );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc*, OUString& rStr )
+{
+ // page number
+ SwPageNumberField aField( static_cast<SwPageNumberFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber )), PG_RANDOM,
+ GetNumberPara(rStr, true));
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ return eF_ResT::OK;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc*, OUString& rStr )
+{
+ //e.g. #i20118#
+ OUString aQ;
+ OUString aName;
+ sal_Int32 nSize = 0;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aQ.isEmpty() )
+ aQ = aReadParam.GetResult();
+ break;
+ case 'f':
+ case 'F':
+ if ( aReadParam.GoToTokenParam() )
+ aName = aReadParam.GetResult();
+ break;
+ case 's':
+ case 'S':
+ if ( aReadParam.GoToTokenParam() )
+ {
+ const OUString aSiz = aReadParam.GetResult();
+ if (!aSiz.isEmpty())
+ {
+ bool bFail = o3tl::checked_multiply<sal_Int32>(aSiz.toInt32(), 20, nSize); // pT -> twip
+ if (bFail)
+ nSize = -1;
+ }
+ }
+ break;
+ }
+ }
+ if( aQ.isEmpty() )
+ return eF_ResT::TAGIGN; // -> no 0-char in text
+
+ sal_Unicode const cChar = static_cast<sal_Unicode>(aQ.toInt32());
+ if (!linguistic::IsControlChar(cChar) || cChar == '\r' || cChar == '\n' || cChar == '\t')
+ {
+ if (!aName.isEmpty()) // Font Name set ?
+ {
+ SvxFontItem aFont(FAMILY_DONTKNOW, aName, OUString(),
+ PITCH_DONTKNOW, RTL_TEXTENCODING_SYMBOL, RES_CHRATR_FONT);
+ NewAttr(aFont); // new Font
+ }
+
+ if (nSize > 0) //#i20118#
+ {
+ SvxFontHeightItem aSz(nSize, 100, RES_CHRATR_FONTSIZE);
+ NewAttr(aSz);
+ }
+
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(cChar));
+
+ if (nSize > 0)
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONTSIZE);
+ if (!aName.isEmpty())
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT);
+ }
+ else
+ {
+ m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, "###");
+ }
+
+ return eF_ResT::OK;
+}
+
+// "EMBED"
+eF_ResT SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc*, OUString& rStr )
+{
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ // sHost
+ break;
+
+ case 's':
+ // use ObjectSize
+ break;
+ }
+ }
+
+ if( m_bObj && m_nPicLocFc )
+ m_nObjLocFc = m_nPicLocFc;
+ m_bEmbeddObj = true;
+ return eF_ResT::TEXT;
+}
+
+// "SET"
+eF_ResT SwWW8ImplReader::Read_F_Set( WW8FieldDesc* pF, OUString& rStr )
+{
+ OUString sOrigName;
+ OUString sVal;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if (sOrigName.isEmpty())
+ sOrigName = aReadParam.GetResult();
+ else if (sVal.isEmpty())
+ sVal = aReadParam.GetResult();
+ break;
+ }
+ }
+
+ const tools::Long nNo = MapBookmarkVariables(pF, sOrigName, sVal);
+
+ SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc, sOrigName,
+ nsSwGetSetExpType::GSE_STRING ) );
+ SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), sVal, ULONG_MAX );
+ aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING);
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+
+ m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo);
+
+ return eF_ResT::OK;
+}
+
+// "REF"
+eF_ResT SwWW8ImplReader::Read_F_Ref( WW8FieldDesc*, OUString& rStr )
+{ // Reference - Field
+ OUString sOrigBkmName;
+ REFERENCEMARK eFormat = REF_CONTENT;
+
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( sOrigBkmName.isEmpty() ) // get name of bookmark
+ sOrigBkmName = aReadParam.GetResult();
+ break;
+
+ /* References to numbers in Word could be either to a numbered
+ paragraph or to a chapter number. However Word does not seem to
+ have the capability we do, of referring to the chapter number some
+ other bookmark is in. As a result, cross-references to chapter
+ numbers in a word document will be cross-references to a numbered
+ paragraph, being the chapter heading paragraph. As it happens, our
+ cross-references to numbered paragraphs will do the right thing
+ when the target is a numbered chapter heading, so there is no need
+ for us to use the REF_CHAPTER bookmark format on import.
+ */
+ case 'n':
+ eFormat = REF_NUMBER_NO_CONTEXT;
+ break;
+ case 'r':
+ eFormat = REF_NUMBER;
+ break;
+ case 'w':
+ eFormat = REF_NUMBER_FULL_CONTEXT;
+ break;
+
+ case 'p':
+ eFormat = REF_UPDOWN;
+ break;
+ case 'h':
+ break;
+ default:
+ // unimplemented switch: just do 'nix nought nothing' :-)
+ break;
+ }
+ }
+
+ OUString sBkmName(GetMappedBookmark(sOrigBkmName));
+
+ // #i120879# add cross reference bookmark name prefix, if it
+ // matches internal TOC bookmark naming convention
+ if ( IsTOCBookmarkName( sBkmName ) )
+ {
+ sBkmName = EnsureTOCBookmarkName(sBkmName);
+ // track <sBookmarkName> as referenced TOC bookmark.
+ m_xReffedStck->m_aReferencedTOCBookmarks.insert( sBkmName );
+ }
+
+ SwGetRefField aField(
+ static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),
+ sBkmName,"",REF_BOOKMARK,0,0,eFormat);
+
+ if (eFormat == REF_CONTENT)
+ {
+ /*
+ If we are just inserting the contents of the bookmark, then it
+ is possible that the bookmark is actually a variable, so we
+ must store it until the end of the document to see if it was,
+ in which case we'll turn it into a show variable
+ */
+ m_xReffingStck->NewAttr( *m_pPaM->GetPoint(), SwFormatField(aField) );
+ m_xReffingStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_FIELD);
+ }
+ else
+ {
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ }
+ return eF_ResT::OK;
+}
+
+// Note Reference - Field
+eF_ResT SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc*, OUString& rStr )
+{
+ OUString aBkmName;
+ bool bAboveBelow = false;
+
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aBkmName.isEmpty() ) // get name of foot/endnote
+ aBkmName = aReadParam.GetResult();
+ break;
+ case 'r':
+ // activate flag 'Chapter Number'
+ break;
+ case 'p':
+ bAboveBelow = true;
+ break;
+ case 'h':
+ break;
+ default:
+ // unimplemented switch: just do 'nix nought nothing' :-)
+ break;
+ }
+ }
+
+ // set Sequence No of corresponding Foot-/Endnote to Zero
+ // (will be corrected in
+ SwGetRefField aField( static_cast<SwGetRefFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )), aBkmName, "", REF_FOOTNOTE, 0, 0,
+ REF_ONLYNUMBER );
+ m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField));
+ m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
+ if (bAboveBelow)
+ {
+ SwGetRefField aField2( static_cast<SwGetRefFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),aBkmName, "", REF_FOOTNOTE, 0, 0,
+ REF_UPDOWN );
+ m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField2));
+ m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD);
+ }
+ return eF_ResT::OK;
+}
+
+// "PAGEREF"
+eF_ResT SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc*, OUString& rStr )
+{
+ OUString sOrigName;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ else if ( nRet == -2 && sOrigName.isEmpty() )
+ {
+ sOrigName = aReadParam.GetResult();
+ }
+ }
+
+ const OUString sName(GetMappedBookmark(sOrigName));
+
+ // loading page reference field in TOX
+ if (m_bLoadingTOXCache)
+ {
+ // insert page ref representation as plain text --> return FLD_TEXT
+ // if there is no hyperlink settings for current toc and referenced bookmark is available,
+ // assign link to current ref area
+ if (!m_bLoadingTOXHyperlink && !sName.isEmpty())
+ {
+ // #i120879# add cross reference bookmark name prefix, if it
+ // matches internal TOC bookmark naming convention
+ OUString sBookmarkName;
+ if ( IsTOCBookmarkName( sName ) )
+ {
+ sBookmarkName = EnsureTOCBookmarkName(sName);
+ // track <sBookmarkName> as referenced TOC bookmark.
+ m_xReffedStck->m_aReferencedTOCBookmarks.insert( sBookmarkName );
+ }
+ else
+ {
+ sBookmarkName = sName;
+ }
+ OUString sURL = "#" + sBookmarkName;
+ SwFormatINetFormat aURL( sURL, "" );
+ static constexpr OUString sLinkStyle(u"Index Link"_ustr);
+ const sal_uInt16 nPoolId =
+ SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
+ aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId);
+ aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
+ m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
+ }
+ return eF_ResT::TEXT;
+ }
+
+ // #i120879# add cross reference bookmark name prefix, if it matches
+ // internal TOC bookmark naming convention
+ OUString sPageRefBookmarkName;
+ if ( IsTOCBookmarkName( sName ) )
+ {
+ sPageRefBookmarkName = EnsureTOCBookmarkName(sName);
+ // track <sPageRefBookmarkName> as referenced TOC bookmark.
+ m_xReffedStck->m_aReferencedTOCBookmarks.insert( sPageRefBookmarkName );
+ }
+ else
+ {
+ sPageRefBookmarkName = sName;
+ }
+ SwGetRefField aField( static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),
+ sPageRefBookmarkName, "", REF_BOOKMARK, 0, 0, REF_PAGE );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+
+ return eF_ResT::OK;
+}
+
+//helper function
+//For MS MacroButton field, the symbol in plain text is always "(" (0x28),
+//which should be mapped according to the macro type
+static bool ConvertMacroSymbol( std::u16string_view rName, OUString& rReference )
+{
+ bool bConverted = false;
+ if( rReference == "(" )
+ {
+ bConverted = true;
+ sal_Unicode cSymbol = sal_Unicode(); // silence false warning
+ if (rName == u"CheckIt")
+ cSymbol = 0xF06F;
+ else if (rName == u"UncheckIt")
+ cSymbol = 0xF0FE;
+ else if (rName == u"ShowExample")
+ cSymbol = 0xF02A;
+ //else if... : todo
+ else
+ bConverted = false;
+
+ if( bConverted )
+ rReference = OUString(cSymbol);
+ }
+ return bConverted;
+}
+
+// "MACROBUTTON"
+eF_ResT SwWW8ImplReader::Read_F_Macro( WW8FieldDesc*, OUString& rStr)
+{
+ OUString aName;
+ OUString aVText;
+ bool bNewVText = true;
+ bool bBracket = false;
+ WW8ReadFieldParams aReadParam( rStr );
+
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aName.isEmpty() )
+ aName = aReadParam.GetResult();
+ else if( aVText.isEmpty() || bBracket )
+ {
+ if( bBracket )
+ aVText += " ";
+ aVText += aReadParam.GetResult();
+ if (bNewVText)
+ {
+ bBracket = (aVText[0] == '[');
+ bNewVText = false;
+ }
+ else if( aVText.endsWith("]") )
+ bBracket = false;
+ }
+ break;
+ }
+ }
+ if( aName.isEmpty() )
+ return eF_ResT::TAGIGN; // makes no sense without Macro-Name
+
+ NotifyMacroEventRead();
+
+ //try converting macro symbol according to macro name
+ bool bApplyWingdings = ConvertMacroSymbol( aName, aVText );
+ aName = "StarOffice.Standard.Modul1." + aName;
+
+ SwMacroField aField( static_cast<SwMacroFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Macro )), aName, aVText );
+
+ if( !bApplyWingdings )
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ else
+ {
+ //set Wingdings font
+ sal_uInt16 i = 0;
+ for ( ; i < m_xFonts->GetMax(); i++ )
+ {
+ FontFamily eFamily;
+ OUString aFontName;
+ FontPitch ePitch;
+ rtl_TextEncoding eSrcCharSet;
+ if( GetFontParams( i, eFamily, aFontName, ePitch, eSrcCharSet )
+ && aFontName=="Wingdings" )
+ {
+ break;
+ }
+ }
+
+ if ( i < m_xFonts->GetMax() )
+ {
+
+ SetNewFontAttr( i, true, RES_CHRATR_FONT );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT );
+ ResetCharSetVars();
+ }
+ }
+
+ return eF_ResT::OK;
+}
+
+bool CanUseRemoteLink(const OUString &rGrfName)
+{
+ bool bUseRemote = false;
+ try
+ {
+ // Related: tdf#102499, add a default css::ucb::XCommandEnvironment
+ // in order to have https protocol manage certificates correctly
+ uno::Reference< task::XInteractionHandler > xIH(
+ task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr));
+
+ uno::Reference< ucb::XProgressHandler > xProgress;
+ rtl::Reference<::ucbhelper::CommandEnvironment> pCommandEnv =
+ new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH ), xProgress);
+
+ ::ucbhelper::Content aCnt(rGrfName,
+ static_cast< ucb::XCommandEnvironment* >(pCommandEnv.get()),
+ comphelper::getProcessComponentContext());
+
+ if ( !INetURLObject( rGrfName ).isAnyKnownWebDAVScheme() )
+ {
+ OUString aTitle;
+ aCnt.getPropertyValue("Title") >>= aTitle;
+ bUseRemote = !aTitle.isEmpty();
+ }
+ else
+ {
+ // is a link to a WebDAV resource
+ // need to use MediaType to check for link usability
+ OUString aMediaType;
+ aCnt.getPropertyValue("MediaType") >>= aMediaType;
+ bUseRemote = !aMediaType.isEmpty();
+ }
+ }
+ catch ( ... )
+ {
+ // this file did not exist, so we will not set this as graphiclink
+ bUseRemote = false;
+ }
+ return bUseRemote;
+}
+
+// "INCLUDEPICTURE"
+eF_ResT SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr )
+{
+ OUString aGrfName;
+ bool bEmbedded = true;
+
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if (aGrfName.isEmpty())
+ aGrfName = ConvertFFileName(aReadParam.GetResult());
+ break;
+
+ case 'd':
+ bEmbedded = false;
+ break;
+
+ case 'c':// skip the converter name
+ aReadParam.FindNextStringPiece();
+ break;
+ }
+ }
+
+ if (!bEmbedded)
+ bEmbedded = !CanUseRemoteLink(aGrfName);
+
+ if (!bEmbedded)
+ {
+ /*
+ Special case:
+
+ Now we write the Link into the Doc and remember the SwFlyFrameFormat.
+ Since we end on return FLD_READ_FSPA below, the skip value will be set
+ so that Char-1 will still be read.
+ When we then call SwWW8ImplReader::ImportGraf() it will then recognize
+ that we have inserted a graphic link and the suiting SwAttrSet will be
+ inserted into the frame format.
+ */
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFlySet( m_rDoc.GetAttrPool() );
+ aFlySet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
+ aFlySet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
+ m_pFlyFormatOfJustInsertedGraphic =
+ m_rDoc.getIDocumentContentOperations().InsertGraphic(*m_pPaM,
+ aGrfName,
+ OUString(),
+ nullptr, // Graphic*
+ &aFlySet,
+ nullptr, nullptr); // SwFrameFormat*
+ m_aGrfNameGenerator.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic,
+ INetURLObject(aGrfName).GetBase());
+ }
+ return eF_ResT::READ_FSPA;
+}
+
+OUString wwSectionNamer::UniqueName()
+{
+ const OUString aName(msFileLinkSeed + OUString::number(++mnFileSectionNo));
+ return mrDoc.GetUniqueSectionName(&aName);
+}
+
+// "INCLUDETEXT"
+eF_ResT SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc* /*pF*/, OUString& rStr )
+{
+ OUString aPara;
+ OUString aBook;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aPara.isEmpty() )
+ aPara = aReadParam.GetResult();
+ else if( aBook.isEmpty() )
+ aBook = aReadParam.GetResult();
+ break;
+ case '*':
+ //Skip over MERGEFORMAT
+ (void)aReadParam.SkipToNextToken();
+ break;
+ }
+ }
+ aPara = ConvertFFileName(aPara);
+
+ if (!aBook.isEmpty() && aBook[ 0 ] != '\\')
+ {
+ // Section from Source (no switch)?
+ ConvertUFName(aBook);
+ aPara += OUStringChar(sfx2::cTokenSeparator)
+ + OUStringChar(sfx2::cTokenSeparator) + aBook;
+ }
+
+ /*
+ ##509##
+ What we will do is insert a section to be linked to a file, but just in
+ case the file is not available we will fill in the section with the stored
+ content of this winword field as a fallback.
+ */
+ SwPosition aTmpPos(*m_pPaM->GetPoint());
+
+ SwSectionData aSection(SectionType::FileLink,
+ m_aSectionNameGenerator.UniqueName());
+ aSection.SetLinkFileName( aPara );
+ aSection.SetProtectFlag(true);
+
+ SwSection *const pSection =
+ m_rDoc.InsertSwSection(*m_pPaM, aSection, nullptr, nullptr, false);
+ OSL_ENSURE(pSection, "no section inserted");
+ if (!pSection)
+ return eF_ResT::TEXT;
+ const SwSectionNode* pSectionNode = pSection->GetFormat()->GetSectionNode();
+ OSL_ENSURE(pSectionNode, "no section node!");
+ if (!pSectionNode)
+ return eF_ResT::TEXT;
+
+ m_pPaM->GetPoint()->Assign( pSectionNode->GetIndex()+1 );
+
+ //we have inserted a section before this point, so adjust pos
+ //for future page/section segment insertion
+ m_aSectionManager.PrependedInlineNode(aTmpPos, m_pPaM->GetPointNode());
+
+ return eF_ResT::TEXT;
+}
+
+// "SERIALPRINT"
+eF_ResT SwWW8ImplReader::Read_F_DBField( WW8FieldDesc* pF, OUString& rStr )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) pF;
+ (void) rStr;
+#else
+ OUString aName;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( aName.isEmpty() )
+ aName = aReadParam.GetResult();
+ break;
+ }
+ }
+ SwDBFieldType aD( &m_rDoc, aName, SwDBData() ); // Database: nothing
+
+ SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aD );
+ SwDBField aField( static_cast<SwDBFieldType*>(pFT) );
+ aField.SetFieldCode( rStr );
+
+ OUString aResult;
+ m_xSBase->WW8ReadString( *m_pStrm, aResult, m_xPlcxMan->GetCpOfs()+
+ pF->nSRes, pF->nLRes, m_eTextCharSet );
+
+ aResult = aResult.replace( '\xb', '\n' );
+
+ aField.InitContent(aResult);
+
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField( aField ));
+#endif
+ return eF_ResT::OK;
+}
+
+// "NEXT"
+eF_ResT SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc*, OUString& )
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ SwDBNextSetFieldType aN;
+ SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
+ SwDBNextSetField aField( static_cast<SwDBNextSetFieldType*>(pFT), OUString(),
+ SwDBData() ); // Database: nothing
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+#endif
+ return eF_ResT::OK;
+}
+
+// "DATASET"
+eF_ResT SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc*, OUString& )
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ SwDBSetNumberFieldType aN;
+ SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN );
+ SwDBSetNumberField aField( static_cast<SwDBSetNumberFieldType*>(pFT),
+ SwDBData() ); // Datenbase: nothing
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) );
+#endif
+ return eF_ResT::OK;
+}
+
+/*
+ EQ , only the usage for
+ a. Combined Characters supported, must be exactly in the form that word
+ only accepts as combined characters, i.e.
+ eq \o(\s\up Y(XXX),\s\do Y(XXX))
+ b. Ruby Text supported, must be in the form that word recognizes as being
+ ruby text
+ ...
+*/
+eF_ResT SwWW8ImplReader::Read_F_Equation( WW8FieldDesc*, OUString& rStr )
+{
+ WW8ReadFieldParams aReadParam( rStr );
+ const sal_Int32 cChar = aReadParam.SkipToNextToken();
+ if ('o' == cChar || 'O' == cChar)
+ {
+ EquationResult aResult(ParseCombinedChars(rStr));
+
+ if (aResult.sType == "Input")
+ {
+ SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
+ aResult.sResult, aResult.sResult, INP_TXT, 0 );
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); // insert input field
+ }
+ else if (aResult.sType == "CombinedCharacters")
+ {
+ SwCombinedCharField aField(static_cast<SwCombinedCharFieldType*>(
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars)), aResult.sType);
+ m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
+ }
+ }
+ else if ('*' == cChar)
+ Read_SubF_Ruby(aReadParam);
+
+ return eF_ResT::OK;
+}
+
+void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams& rReadParam)
+{
+ sal_uInt16 nJustificationCode=0;
+ OUString sFontName;
+ sal_uInt32 nFontSize=0;
+ OUString sRuby;
+ OUString sText;
+ for (;;)
+ {
+ const sal_Int32 nRet = rReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ {
+ OUString sTemp = rReadParam.GetResult();
+ if( sTemp.startsWithIgnoreAsciiCase( "jc" ) )
+ {
+ sTemp = sTemp.copy(2);
+ nJustificationCode = o3tl::narrowing<sal_uInt16>(sTemp.toInt32());
+ }
+ else if( sTemp.startsWithIgnoreAsciiCase( "hps" ) )
+ {
+ sTemp = sTemp.copy(3);
+ nFontSize= static_cast<sal_uInt32>(sTemp.toInt32());
+ }
+ else if( sTemp.startsWithIgnoreAsciiCase( "Font:" ) )
+ {
+ sTemp = sTemp.copy(5);
+ sFontName = sTemp;
+ }
+ }
+ break;
+ case '*':
+ break;
+ case 'o':
+ for (;;)
+ {
+ const sal_Int32 nRes = rReadParam.SkipToNextToken();
+ if ( nRes==-1 )
+ break;
+ if ('u' == nRes)
+ {
+ if (-2 == rReadParam.SkipToNextToken() &&
+ rReadParam.GetResult().startsWithIgnoreAsciiCase("p"))
+ {
+ if (-2 == rReadParam.SkipToNextToken())
+ {
+ OUString sPart = rReadParam.GetResult();
+ sal_Int32 nBegin = sPart.indexOf('(');
+
+ //Word disallows brackets in this field,
+ sal_Int32 nEnd = sPart.indexOf(')');
+
+ if ((nBegin != -1) &&
+ (nEnd != -1) && (nBegin < nEnd))
+ {
+ sRuby = sPart.copy(nBegin+1,nEnd-nBegin-1);
+ }
+ if (-1 != nEnd)
+ {
+ nBegin = sPart.indexOf(',',nEnd);
+ if (-1 == nBegin)
+ {
+ nBegin = sPart.indexOf(';',nEnd);
+ }
+ nEnd = sPart.lastIndexOf(')');
+ }
+ if ((nBegin != -1) && (nEnd != -1) && (nBegin < nEnd))
+ {
+ sText = sPart.copy(nBegin+1,nEnd-nBegin-1);
+ sText = sw::FilterControlChars(sText);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ //Translate and apply
+ if (sRuby.isEmpty() || sText.isEmpty() || sFontName.isEmpty() || !nFontSize)
+ return;
+
+ css::text::RubyAdjust eRubyAdjust;
+ switch (nJustificationCode)
+ {
+ case 0:
+ eRubyAdjust = css::text::RubyAdjust_CENTER;
+ break;
+ case 1:
+ eRubyAdjust = css::text::RubyAdjust_BLOCK;
+ break;
+ case 2:
+ eRubyAdjust = css::text::RubyAdjust_INDENT_BLOCK;
+ break;
+ default:
+ case 3:
+ eRubyAdjust = css::text::RubyAdjust_LEFT;
+ break;
+ case 4:
+ eRubyAdjust = css::text::RubyAdjust_RIGHT;
+ break;
+ }
+
+ SwFormatRuby aRuby(sRuby);
+ const SwCharFormat *pCharFormat=nullptr;
+ //Make a guess at which of asian of western we should be setting
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(sRuby, 0);
+
+ //Check to see if we already have a ruby charstyle that this fits
+ for(const auto& rpCharFormat : m_aRubyCharFormats)
+ {
+ const SvxFontHeightItem &rFH =
+ rpCharFormat->GetFormatAttr(
+ GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
+ if (rFH.GetHeight() == nFontSize*10)
+ {
+ const SvxFontItem &rF = rpCharFormat->GetFormatAttr(
+ GetWhichOfScript(RES_CHRATR_FONT,nScript));
+ if (rF.GetFamilyName() == sFontName)
+ {
+ pCharFormat = rpCharFormat;
+ break;
+ }
+ }
+ }
+
+ //Create a new char style if necessary
+ if (!pCharFormat)
+ {
+ OUString aNm;
+ //Take this as the base name
+ SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT,aNm);
+ aNm+=OUString::number(m_aRubyCharFormats.size()+1);
+ SwCharFormat *pFormat = m_rDoc.MakeCharFormat(aNm, m_rDoc.GetDfltCharFormat());
+ SvxFontHeightItem aHeightItem(nFontSize*10, 100, RES_CHRATR_FONTSIZE);
+ SvxFontItem aFontItem(FAMILY_DONTKNOW,sFontName,
+ OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT);
+ aHeightItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript));
+ aFontItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONT,nScript));
+ pFormat->SetFormatAttr(aHeightItem);
+ pFormat->SetFormatAttr(aFontItem);
+ m_aRubyCharFormats.push_back(pFormat);
+ pCharFormat = pFormat;
+ }
+
+ //Set the charstyle and justification
+ aRuby.SetCharFormatName(pCharFormat->GetName());
+ aRuby.SetCharFormatId(pCharFormat->GetPoolFormatId());
+ aRuby.SetAdjustment(eRubyAdjust);
+
+ NewAttr(aRuby);
+ m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, sText );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CJK_RUBY );
+
+}
+
+// "table of ..." fields
+
+static void lcl_toxMatchACSwitch(SwDoc const & rDoc,
+ SwTOXBase& rBase,
+ WW8ReadFieldParams& rParam,
+ SwCaptionDisplay eCaptionType)
+{
+ if ( rParam.GoToTokenParam() )
+ {
+ SwTOXType* pType = const_cast<SwTOXType*>(rDoc.GetTOXType( TOX_ILLUSTRATIONS, 0));
+ rBase.RegisterToTOXType( *pType );
+ rBase.SetCaptionDisplay( eCaptionType );
+ // Read Sequence Name and store in TOXBase
+ OUString sSeqName( rParam.GetResult() );
+ lcl_ConvertSequenceName( sSeqName );
+ rBase.SetSequenceName( sSeqName );
+ }
+}
+
+static void EnsureMaxLevelForTemplates(SwTOXBase& rBase)
+{
+ //If the TOC contains Template entries at levels > the evaluation level
+ //that was initially taken from the max normal outline level of the word TOC
+ //then we cannot use that for the evaluation level because writer cuts off
+ //all styles above that level, while word just cuts off the "standard"
+ //outline styles, we have no option but to expand to the highest level
+ //Word included.
+ if ((rBase.GetLevel() != MAXLEVEL) && (SwTOXElement::Template & rBase.GetCreateType()))
+ {
+ for (sal_uInt16 nI = MAXLEVEL; nI > 0; --nI)
+ {
+ if (!rBase.GetStyleNames(nI-1).isEmpty())
+ {
+ rBase.SetLevel(nI);
+ break;
+ }
+ }
+ }
+}
+
+static void lcl_toxMatchTSwitch(SwWW8ImplReader const & rReader, SwTOXBase& rBase,
+ WW8ReadFieldParams& rParam)
+{
+ if ( !rParam.GoToTokenParam() )
+ return;
+
+ OUString sParams( rParam.GetResult() );
+ if( sParams.isEmpty() )
+ return;
+
+ sal_Int32 nIndex = 0;
+
+ // Delimiters between styles and style levels appears to allow both ; and ,
+
+ OUString sTemplate( sParams.getToken(0, ';', nIndex) );
+ if( -1 == nIndex )
+ {
+ nIndex=0;
+ sTemplate = sParams.getToken(0, ',', nIndex);
+ }
+ if( -1 == nIndex )
+ {
+ const SwFormat* pStyle = rReader.GetStyleWithOrgWWName(sTemplate);
+ if( pStyle )
+ sTemplate = pStyle->GetName();
+ // Store Style for Level 0 into TOXBase
+ rBase.SetStyleNames( sTemplate, 0 );
+ }
+ else while( -1 != nIndex )
+ {
+ sal_Int32 nOldIndex=nIndex;
+ sal_uInt16 nLevel = o3tl::narrowing<sal_uInt16>(
+ o3tl::toInt32(o3tl::getToken(sParams, 0, ';', nIndex)));
+ if( -1 == nIndex )
+ {
+ nIndex = nOldIndex;
+ nLevel = o3tl::narrowing<sal_uInt16>(
+ o3tl::toInt32(o3tl::getToken(sParams, 0, ',', nIndex)));
+ }
+
+ if( (0 < nLevel) && (MAXLEVEL >= nLevel) )
+ {
+ nLevel--;
+ // Store Style and Level into TOXBase
+ const SwFormat* pStyle
+ = rReader.GetStyleWithOrgWWName( sTemplate );
+
+ if( pStyle )
+ sTemplate = pStyle->GetName();
+
+ OUString sStyles( rBase.GetStyleNames( nLevel ) );
+ if( !sStyles.isEmpty() )
+ sStyles += OUStringChar(TOX_STYLE_DELIMITER);
+ sStyles += sTemplate;
+ rBase.SetStyleNames( sStyles, nLevel );
+ }
+ // read next style name...
+ nOldIndex = nIndex;
+ sTemplate = sParams.getToken(0, ';', nIndex);
+ if( -1 == nIndex )
+ {
+ nIndex=nOldIndex;
+ sTemplate = sParams.getToken(0, ',', nIndex);
+ }
+ }
+}
+
+sal_uInt16 wwSectionManager::CurrentSectionColCount() const
+{
+ sal_uInt16 nIndexCols = 1;
+ if (!maSegments.empty())
+ nIndexCols = maSegments.back().maSep.ccolM1 + 1;
+ return nIndexCols;
+}
+
+//Will there be a new pagebreak at this position (don't know what type
+//until later)
+bool wwSectionManager::WillHavePageDescHere(const SwNode& rNd) const
+{
+ bool bRet = false;
+ if (!maSegments.empty())
+ {
+ if (!maSegments.back().IsContinuous() &&
+ maSegments.back().maStart == rNd)
+ {
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+static sal_uInt16 lcl_GetMaxValidWordTOCLevel(const SwForm &rForm)
+{
+ // GetFormMax() returns level + 1, hence the -1
+ sal_uInt16 nRet = rForm.GetFormMax()-1;
+
+ // If the max of this type of TOC is greater than the max of a word
+ // possible toc, then clip to the word max
+ if (nRet > WW8ListManager::nMaxLevel)
+ nRet = WW8ListManager::nMaxLevel;
+
+ return nRet;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_Tox( WW8FieldDesc* pF, OUString& rStr )
+{
+ if (!m_bLoadingTOXCache)
+ {
+ m_bLoadingTOXCache = true;
+ }
+ else
+ {
+ // Embedded TOX --> continue reading its content, but no further TOX
+ // field
+ ++m_nEmbeddedTOXLevel;
+ return eF_ResT::TEXT;
+ }
+
+ if (pF->nLRes < 3)
+ return eF_ResT::TEXT; // ignore (#i25440#)
+
+ TOXTypes eTox; // create a ToxBase
+ switch( pF->nId )
+ {
+ case 8:
+ eTox = TOX_INDEX;
+ break;
+ case 13:
+ eTox = TOX_CONTENT;
+ break;
+ default:
+ eTox = TOX_USER;
+ break;
+ }
+
+ SwTOXElement nCreateOf = (eTox == TOX_CONTENT) ? SwTOXElement::OutlineLevel : SwTOXElement::Mark;
+
+ sal_uInt16 nIndexCols = 1;
+
+ const SwTOXType* pType = m_rDoc.GetTOXType( eTox, 0 );
+ SwForm aOrigForm(eTox);
+ std::shared_ptr<SwTOXBase> pBase = std::make_shared<SwTOXBase>( pType, aOrigForm, nCreateOf, OUString() );
+ pBase->SetProtected(m_aSectionManager.CurrentSectionIsProtected());
+ switch( eTox ){
+ case TOX_INDEX:
+ {
+ SwTOIOptions eOptions = SwTOIOptions::SameEntry | SwTOIOptions::CaseSensitive;
+
+ // We set SwTOXElement::OutlineLevel only if
+ // the parameter \o is within 1 to 9
+ // or the parameter \f exists
+ // or NO switch parameter are given at all.
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case 'c':
+ if ( aReadParam.GoToTokenParam() )
+ {
+ const OUString sParams( aReadParam.GetResult() );
+ // if NO OUString just ignore the \c
+ if( !sParams.isEmpty() )
+ {
+ nIndexCols = o3tl::narrowing<sal_uInt16>(sParams.toInt32());
+ }
+ }
+ break;
+ case 'e':
+ {
+ if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \e
+ {
+ OUString sDelimiter( aReadParam.GetResult() );
+ SwForm aForm( pBase->GetTOXForm() );
+
+ // Attention: if TOX_CONTENT brave
+ // GetFormMax() returns MAXLEVEL + 1 !!
+ sal_uInt16 nEnd = aForm.GetFormMax()-1;
+
+ for(sal_uInt16 nLevel = 1;
+ nLevel <= nEnd;
+ ++nLevel)
+ {
+ // Levels count from 1
+ // Level 0 is reserved for CAPTION
+
+ // Insert delimiter instead of tab in front of the page number if there is one:
+ FormTokenType ePrevType = TOKEN_END;
+ FormTokenType eType;
+ // -> #i21237#
+ SwFormTokens aPattern =
+ aForm.GetPattern(nLevel);
+ SwFormTokens::iterator aIt = aPattern.begin();
+ do
+ {
+ eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
+
+ if (eType == TOKEN_PAGE_NUMS)
+ {
+ if (TOKEN_TAB_STOP == ePrevType)
+ {
+ --aIt;
+
+ if(0x09 == sDelimiter[0])
+ aIt->eTabAlign = SvxTabAdjust::End;
+ else
+ {
+ SwFormToken aToken(TOKEN_TEXT);
+ aToken.sText = sDelimiter;
+ *aIt = aToken;
+ }
+ aForm.SetPattern(nLevel, std::move(aPattern));
+ }
+
+ eType = TOKEN_END;
+ }
+
+ ePrevType = eType;
+ }
+ while (TOKEN_END != eType);
+ // <- #i21237#
+ }
+ pBase->SetTOXForm( aForm );
+ }
+ }
+ break;
+ case 'h':
+ {
+ eOptions |= SwTOIOptions::AlphaDelimiter;
+ }
+ break;
+ }
+ }
+ pBase->SetOptions( eOptions );
+ }
+ break;
+
+ case TOX_CONTENT:
+ {
+ bool bIsHyperlink = false;
+ // We set SwTOXElement::OutlineLevel only if
+ // the parameter \o is within 1 to 9
+ // or the parameter \f exists
+ // or NO switch parameter are given at all.
+ SwTOXElement eCreateFrom = SwTOXElement::NONE;
+ sal_Int32 nMaxLevel = 0;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case 'h':
+ bIsHyperlink = true;
+ break;
+ case 'a':
+ case 'c':
+ lcl_toxMatchACSwitch(m_rDoc, *pBase, aReadParam,
+ ('c' == nRet)
+ ? CAPTION_COMPLETE
+ : CAPTION_TEXT );
+ break;
+ case 'o':
+ {
+ sal_Int32 nVal;
+ if( !aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
+ nVal = lcl_GetMaxValidWordTOCLevel(aOrigForm);
+ if( nMaxLevel < nVal )
+ nMaxLevel = nVal;
+ eCreateFrom |= SwTOXElement::OutlineLevel;
+ }
+ break;
+ case 'f':
+ eCreateFrom |= SwTOXElement::Mark;
+ break;
+ case 'l':
+ {
+ sal_Int32 nVal;
+ if( aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) )
+ {
+ if( nMaxLevel < nVal )
+ nMaxLevel = nVal;
+ eCreateFrom |= SwTOXElement::Mark;
+ }
+ }
+ break;
+ case 't': // paragraphs using special styles shall
+ // provide the TOX's content
+ lcl_toxMatchTSwitch(*this, *pBase, aReadParam);
+ eCreateFrom |= SwTOXElement::Template;
+ break;
+ case 'p':
+ {
+ if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \p
+ {
+ OUString sDelimiter( aReadParam.GetResult() );
+ SwForm aForm( pBase->GetTOXForm() );
+
+ // Attention: if TOX_CONTENT brave
+ // GetFormMax() returns MAXLEVEL + 1 !!
+ sal_uInt16 nEnd = aForm.GetFormMax()-1;
+
+ for(sal_uInt16 nLevel = 1;
+ nLevel <= nEnd;
+ ++nLevel)
+ {
+ // Levels count from 1
+ // Level 0 is reserved for CAPTION
+
+ // Insert delimiter instead of tab in front of the pagenumber if there is one:
+ FormTokenType ePrevType = TOKEN_END;
+ FormTokenType eType;
+
+ // -> #i21237#
+ SwFormTokens aPattern = aForm.GetPattern(nLevel);
+ SwFormTokens::iterator aIt = aPattern.begin();
+ do
+ {
+ eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
+
+ if (eType == TOKEN_PAGE_NUMS)
+ {
+ if (TOKEN_TAB_STOP == ePrevType)
+ {
+ --aIt;
+
+ SwFormToken aToken(TOKEN_TEXT);
+ aToken.sText = sDelimiter;
+
+ *aIt = aToken;
+ aForm.SetPattern(nLevel,
+ std::move(aPattern));
+ }
+ eType = TOKEN_END;
+ }
+ ePrevType = eType;
+ }
+ while( TOKEN_END != eType );
+ // <- #i21237#
+ }
+ pBase->SetTOXForm( aForm );
+ }
+ }
+ break;
+ case 'n': // don't print page numbers
+ {
+ // read START and END param
+ sal_Int32 nStart(0);
+ sal_Int32 nEnd(0);
+ if( !aReadParam.GetTokenSttFromTo( &nStart, &nEnd,
+ WW8ListManager::nMaxLevel ) )
+ {
+ nStart = 1;
+ nEnd = aOrigForm.GetFormMax()-1;
+ }
+ // remove page numbers from this levels
+ SwForm aForm( pBase->GetTOXForm() );
+ if (aForm.GetFormMax() <= nEnd)
+ nEnd = aForm.GetFormMax()-1;
+ for ( sal_Int32 nLevel = nStart; nLevel<=nEnd; ++nLevel )
+ {
+ // Levels count from 1
+ // Level 0 is reserved for CAPTION
+
+ // Remove pagenumber and if necessary the tab in front of it:
+ FormTokenType eType;
+ // -> #i21237#
+ SwFormTokens aPattern = aForm.GetPattern(nLevel);
+ SwFormTokens::iterator aIt = aPattern.begin();
+ do
+ {
+ eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType;
+
+ if (eType == TOKEN_PAGE_NUMS)
+ {
+ aIt = aPattern.erase(aIt);
+ --aIt;
+ if (
+ TOKEN_TAB_STOP ==
+ aIt->eTokenType
+ )
+ {
+ aPattern.erase(aIt);
+ aForm.SetPattern(nLevel, std::move(aPattern));
+ }
+ eType = TOKEN_END;
+ }
+ }
+ while (TOKEN_END != eType);
+ // <- #i21237#
+ }
+ pBase->SetTOXForm( aForm );
+ }
+ break;
+
+ /*
+ // the following switches are not (yet) supported
+ // by good old StarWriter:
+ case 'b':
+ case 's':
+ case 'd':
+ break;
+ */
+ }
+ }
+
+ // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens
+ // also include the hyperlinks and page references
+ SwFormToken aLinkStart(TOKEN_LINK_START);
+ SwFormToken aLinkEnd(TOKEN_LINK_END);
+ aLinkStart.sCharStyleName = "Index Link";
+ aLinkEnd.sCharStyleName = "Index Link";
+ SwForm aForm(pBase->GetTOXForm());
+ sal_uInt16 nEnd = aForm.GetFormMax()-1;
+
+ for(sal_uInt16 nLevel = 1; nLevel <= nEnd; ++nLevel)
+ {
+ SwFormTokens aPattern = aForm.GetPattern(nLevel);
+ if ( bIsHyperlink )
+ {
+ aPattern.insert(aPattern.begin(), aLinkStart);
+ }
+ else
+ {
+ auto aItr = std::find_if(aPattern.begin(), aPattern.end(),
+ [](const SwFormToken& rToken) { return rToken.eTokenType == TOKEN_PAGE_NUMS; });
+ if (aItr != aPattern.end())
+ aPattern.insert(aItr, aLinkStart);
+ }
+ aPattern.push_back(aLinkEnd);
+ aForm.SetPattern(nLevel, std::move(aPattern));
+ }
+ pBase->SetTOXForm(aForm);
+
+ if (!nMaxLevel)
+ nMaxLevel = WW8ListManager::nMaxLevel;
+ pBase->SetLevel(nMaxLevel);
+
+ const TOXTypes eType = pBase->GetTOXType()->GetType();
+ switch( eType )
+ {
+ case TOX_CONTENT:
+ {
+ //If we would be created from outlines, either explicitly or by default
+ //then see if we need extra styles added to the outlines
+ SwTOXElement eEffectivelyFrom = eCreateFrom != SwTOXElement::NONE ? eCreateFrom : SwTOXElement::OutlineLevel;
+ if (eEffectivelyFrom & SwTOXElement::OutlineLevel)
+ {
+ // #i19683# Insert a text token " " between the number and entry token.
+ // In an ideal world we could handle the tab stop between the number and
+ // the entry correctly, but I currently have no clue how to obtain
+ // the tab stop position. It is _not_ set at the paragraph style.
+ std::unique_ptr<SwForm> pForm;
+ for (const SwWW8StyInf & rSI : m_vColl)
+ {
+ if (rSI.IsOutlineNumbered())
+ {
+ sal_uInt16 nStyleLevel = rSI.mnWW8OutlineLevel;
+ const SwNumFormat& rFormat = rSI.GetOutlineNumrule()->Get( nStyleLevel );
+ if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() )
+ {
+ ++nStyleLevel;
+
+ if ( !pForm )
+ pForm.reset(new SwForm( pBase->GetTOXForm() ));
+
+ SwFormTokens aPattern = pForm->GetPattern(nStyleLevel);
+ SwFormTokens::iterator aIt =
+ find_if(aPattern.begin(), aPattern.end(),
+ SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
+
+ if ( aIt != aPattern.end() )
+ {
+ SwFormToken aNumberEntrySeparator( TOKEN_TEXT );
+ aNumberEntrySeparator.sText = " ";
+ aPattern.insert( ++aIt, aNumberEntrySeparator );
+ pForm->SetPattern( nStyleLevel, std::move(aPattern) );
+ }
+ }
+ }
+ }
+ if ( pForm )
+ {
+ pBase->SetTOXForm( *pForm );
+ }
+ }
+
+ if (eCreateFrom != SwTOXElement::NONE)
+ pBase->SetCreate(eCreateFrom);
+ EnsureMaxLevelForTemplates(*pBase);
+ }
+ break;
+ case TOX_ILLUSTRATIONS:
+ {
+ if( eCreateFrom == SwTOXElement::NONE )
+ eCreateFrom = SwTOXElement::Sequence;
+ pBase->SetCreate( eCreateFrom );
+
+ /*
+ We don't know until here if we are an illustration
+ or not, and so have being used a TOX_CONTENT so far
+ which has 10 levels, while TOX has only two, this
+ level is set only in the constructor of SwForm, so
+ create a new one and copy over anything that could
+ be set in the old one, and remove entries from the
+ pattern which do not apply to illustration indices
+ */
+ SwForm aOldForm( pBase->GetTOXForm() );
+ SwForm aNewForm( eType );
+ sal_uInt16 nNewEnd = aNewForm.GetFormMax()-1;
+
+ // #i21237#
+ for(sal_uInt16 nLevel = 1; nLevel <= nNewEnd; ++nLevel)
+ {
+ SwFormTokens aPattern = aOldForm.GetPattern(nLevel);
+ SwFormTokens::iterator new_end =
+ remove_if(aPattern.begin(), aPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO));
+ aPattern.erase(new_end, aPattern.end() ); // table index imported with wrong page number format
+ aForm.SetPattern( nLevel, std::move(aPattern) );
+ aForm.SetTemplate( nLevel, aOldForm.GetTemplate(nLevel) );
+ }
+
+ pBase->SetTOXForm( aNewForm );
+ }
+ break;
+ default:
+ OSL_ENSURE(false, "Unhandled toc options!");
+ break;
+ }
+ }
+ break;
+ case TOX_USER:
+ break;
+ default:
+ OSL_ENSURE(false, "Unhandled toc options!");
+ break;
+ } // ToxBase fertig
+
+ // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX
+ pBase->AdjustTabStops( m_rDoc );
+
+ //#i10028# inserting a toc implicitly acts like a parabreak in word and writer
+ if ( m_pPaM->End() &&
+ m_pPaM->End()->GetNode().GetTextNode() &&
+ m_pPaM->End()->GetNode().GetTextNode()->Len() != 0 )
+ {
+ m_bCareFirstParaEndInToc = true;
+ }
+
+ if (m_pPaM->GetPoint()->GetContentIndex())
+ AppendTextNode(*m_pPaM->GetPoint());
+
+ const SwPosition* pPos = m_pPaM->GetPoint();
+
+ SwFltTOX aFltTOX( pBase );
+
+ // test if there is already a break item on this node
+ if(SwContentNode* pNd = pPos->GetNode().GetContentNode())
+ {
+ const SfxItemSet* pSet = pNd->GetpSwAttrSet();
+ if( pSet )
+ {
+ if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
+ aFltTOX.SetHadBreakItem(true);
+ if (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false))
+ aFltTOX.SetHadPageDescItem(true);
+ }
+ }
+
+ //Will there be a new pagebreak at this position (don't know what type
+ //until later)
+ if (m_aSectionManager.WillHavePageDescHere(pPos->GetNode()))
+ aFltTOX.SetHadPageDescItem(true);
+
+ // Set start in stack
+ m_xReffedStck->NewAttr( *pPos, aFltTOX );
+
+ m_rDoc.InsertTableOf(*m_pPaM->GetPoint(), aFltTOX.GetBase());
+
+ //The TOC field representation contents should be inserted into TOC section, but not after TOC section.
+ //So we need update the document position when loading TOC representation and after loading TOC;
+ m_oPosAfterTOC.emplace(*m_pPaM, m_pPaM);
+ (*m_pPaM).Move(fnMoveBackward);
+ SwPaM aRegion(*m_pPaM, m_pPaM);
+
+ OSL_ENSURE(SwDoc::GetCurTOX(*aRegion.GetPoint()), "Misunderstood how toc works");
+ if (SwTOXBase* pBase2 = SwDoc::GetCurTOX(*aRegion.GetPoint()))
+ {
+ pBase2->SetMSTOCExpression(rStr);
+
+ if ( nIndexCols > 1 )
+ {
+ // Set the column number for index
+ SfxItemSetFixed<RES_COL, RES_COL> aSet( m_rDoc.GetAttrPool() );
+ SwFormatCol aCol;
+ aCol.Init( nIndexCols, 708, USHRT_MAX );
+ aSet.Put( aCol );
+ pBase2->SetAttrSet( aSet );
+ }
+
+ // inserting a toc inserts a section before this point, so adjust pos
+ // for future page/section segment insertion
+ m_aSectionManager.PrependedInlineNode( *m_oPosAfterTOC->GetPoint(), aRegion.GetPointNode() );
+ }
+
+ // Set end in stack
+ m_xReffedStck->SetAttr( *pPos, RES_FLTR_TOX );
+
+ if (!m_aApos.back()) //a para end in apo doesn't count
+ m_bWasParaEnd = true;
+
+ //Return FLD_TEXT, instead of FLD_OK
+ //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed
+ //FLD_OK means the current field loading is finished. The rest part should be ignored.
+ return eF_ResT::TEXT;
+}
+
+eF_ResT SwWW8ImplReader::Read_F_Shape(WW8FieldDesc* /*pF*/, OUString& /*rStr*/)
+{
+ /*
+ #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring
+ to be ignored followed by a 0x1 with an empty drawing. Detect in inserting
+ the drawing that we are in the Shape field and respond accordingly
+ */
+ return eF_ResT::TEXT;
+ }
+
+eF_ResT SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc* /*pF*/, OUString& rStr )
+{
+ OUString sURL, sTarget, sMark;
+
+ //HYPERLINK "filename" [switches]
+ rStr = comphelper::string::stripEnd(rStr, 1);
+
+ bool bOptions = false;
+ WW8ReadFieldParams aReadParam( rStr );
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if (sURL.isEmpty() && !bOptions)
+ sURL = ConvertFFileName(aReadParam.GetResult());
+ break;
+
+ case 'n':
+ sTarget = "_blank";
+ bOptions = true;
+ break;
+
+ case 'l':
+ bOptions = true;
+ if ( aReadParam.SkipToNextToken()==-2 )
+ {
+ sMark = aReadParam.GetResult();
+ if( sMark.endsWith("\""))
+ {
+ sMark = sMark.copy( 0, sMark.getLength() - 1 );
+ }
+ // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention
+ if ( IsTOCBookmarkName( sMark ) )
+ {
+ sMark = EnsureTOCBookmarkName(sMark);
+ // track <sMark> as referenced TOC bookmark.
+ m_xReffedStck->m_aReferencedTOCBookmarks.insert( sMark );
+ }
+
+ if (m_bLoadingTOXCache)
+ {
+ m_bLoadingTOXHyperlink = true; //on loading a TOC field nested hyperlink field
+ }
+ }
+ break;
+ case 't':
+ bOptions = true;
+ if ( aReadParam.SkipToNextToken()==-2 )
+ sTarget = aReadParam.GetResult();
+ break;
+ case 'h':
+ case 'm':
+ OSL_ENSURE( false, "Analysis still missing - unknown data" );
+ [[fallthrough]];
+ case 's': //worthless fake anchor option
+ bOptions = true;
+ break;
+ }
+ }
+
+ // use the result
+ OSL_ENSURE(!sURL.isEmpty() || !sMark.isEmpty(), "WW8: Empty URL");
+
+ if( !sMark.isEmpty() )
+ sURL += "#" + sMark;
+
+ SwFormatINetFormat aURL(sURL, sTarget);
+ // If on loading TOC field, change the default style into the "index link"
+ if (m_bLoadingTOXCache)
+ {
+ OUString sLinkStyle("Index Link");
+ sal_uInt16 nPoolId =
+ SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt );
+ aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId );
+ aURL.SetINetFormatAndId( sLinkStyle, nPoolId );
+ }
+
+ //As an attribute this needs to be closed, and that'll happen from
+ //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms
+ //between the start and begin, their hyperlinks will be set at that time
+ //as well.
+ m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL );
+ return eF_ResT::TEXT;
+}
+
+static void lcl_ImportTox(SwDoc &rDoc, SwPaM const &rPaM, const OUString &rStr, bool bIdx)
+{
+ TOXTypes eTox = ( !bIdx ) ? TOX_CONTENT : TOX_INDEX; // Default
+
+ sal_uInt16 nLevel = 1;
+
+ OUString sFieldText;
+ WW8ReadFieldParams aReadParam(rStr);
+ for (;;)
+ {
+ const sal_Int32 nRet = aReadParam.SkipToNextToken();
+ if ( nRet==-1 )
+ break;
+ switch( nRet )
+ {
+ case -2:
+ if( sFieldText.isEmpty() )
+ {
+ // PrimaryKey without ":", 2nd after
+ sFieldText = aReadParam.GetResult();
+ }
+ break;
+
+ case 'f':
+ if ( aReadParam.GoToTokenParam() )
+ {
+ const OUString sParams( aReadParam.GetResult() );
+ if( sParams[0]!='C' && sParams[0]!='c' )
+ eTox = TOX_USER;
+ }
+ break;
+
+ case 'l':
+ if ( aReadParam.GoToTokenParam() )
+ {
+ const OUString sParams( aReadParam.GetResult() );
+ // if NO String just ignore the \l
+ if( !sParams.isEmpty() && sParams[0]>'0' && sParams[0]<='9' )
+ {
+ nLevel = o3tl::narrowing<sal_uInt16>(sParams.toInt32());
+ }
+ }
+ break;
+ }
+ }
+
+ OSL_ENSURE( rDoc.GetTOXTypeCount( eTox ), "Doc.GetTOXTypeCount() == 0 :-(" );
+
+ const SwTOXType* pT = rDoc.GetTOXType( eTox, 0 );
+ SwTOXMark aM( pT );
+
+ if( eTox != TOX_INDEX )
+ aM.SetLevel( nLevel );
+ else
+ {
+ sal_Int32 nFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM );
+ if( -1 != nFnd ) // it exist levels
+ {
+ aM.SetPrimaryKey( sFieldText.copy( 0, nFnd ) );
+ sal_Int32 nScndFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM, nFnd+1 );
+ if( -1 != nScndFnd )
+ {
+ aM.SetSecondaryKey( sFieldText.copy( nFnd+1, nScndFnd - nFnd - 1 ));
+ nFnd = nScndFnd;
+ }
+ sFieldText = sFieldText.copy( nFnd+1 );
+ }
+ }
+
+ if (!sFieldText.isEmpty())
+ {
+ aM.SetAlternativeText( sFieldText );
+ rDoc.getIDocumentContentOperations().InsertPoolItem( rPaM, aM );
+ }
+}
+
+void SwWW8ImplReader::ImportTox( int nFieldId, const OUString& aStr )
+{
+ bool bIdx = (nFieldId != 9);
+ lcl_ImportTox(m_rDoc, *m_pPaM, aStr, bIdx);
+}
+
+void SwWW8ImplReader::Read_FieldVanish( sal_uInt16, const sal_uInt8*, short nLen )
+{
+ //Meaningless in a style
+ if (m_pCurrentColl || !m_xPlcxMan)
+ return;
+
+ const int nChunk = 64; //number of characters to read at one time
+
+ // Careful: MEMICMP doesn't work with fieldnames including umlauts!
+ const static char *aFieldNames[] = { "\x06""INHALT", "\x02""XE", // dt.
+ "\x02""TC" }; // us
+ const static sal_uInt8 aFieldId[] = { 9, 4, 9 };
+
+ if( nLen < 0 )
+ {
+ m_bIgnoreText = false;
+ return;
+ }
+
+ // our method was called from
+ // ''Skip attributes of field contents'' loop within ReadTextAttr()
+ if( m_bIgnoreText )
+ return;
+
+ m_bIgnoreText = true;
+ sal_uInt64 nOldPos = m_pStrm->Tell();
+
+ WW8_CP nStartCp = m_xPlcxMan->Where() + m_xPlcxMan->GetCpOfs();
+
+ OUString sFieldName;
+ sal_Int32 nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sFieldName, nStartCp,
+ nChunk, m_eStructCharSet );
+ nStartCp+=nFieldLen;
+
+ sal_Int32 nC = 0;
+ //If the first chunk did not start with a field start then
+ //reset the stream position and give up
+ if( !nFieldLen || sFieldName[nC]!=0x13 ) // Field Start Mark
+ {
+ // If Field End Mark found
+ if( nFieldLen && sFieldName[nC]==0x15 )
+ m_bIgnoreText = false;
+ m_pStrm->Seek( nOldPos );
+ return; // no field found
+ }
+
+ sal_Int32 nFnd;
+ //If this chunk does not contain a field end, keep reading chunks
+ //until we find one, or we run out of text,
+ for (;;)
+ {
+ nFnd = sFieldName.indexOf(0x15);
+ //found field end, we can stop now
+ if (nFnd != -1)
+ break;
+ OUString sTemp;
+ nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sTemp,
+ nStartCp, nChunk, m_eStructCharSet );
+ sFieldName+=sTemp;
+ nStartCp+=nFieldLen;
+ if (!nFieldLen)
+ break;
+ }
+
+ m_pStrm->Seek( nOldPos );
+
+ //if we have no 0x15 give up, otherwise erase everything from the 0x15
+ //onwards
+ if (nFnd<0)
+ return;
+
+ sFieldName = sFieldName.copy(0, nFnd);
+
+ nC++;
+ while ( sFieldName[nC]==' ' )
+ nC++;
+
+ for( int i = 0; i < 3; i++ )
+ {
+ const char* pName = aFieldNames[i];
+ const sal_Int32 nNameLen = static_cast<sal_Int32>(*pName++);
+ if( sFieldName.matchIgnoreAsciiCaseAsciiL( pName, nNameLen, nC ) )
+ {
+ ImportTox( aFieldId[i], sFieldName.copy( nC + nNameLen ) );
+ break; // no duplicates allowed
+ }
+ }
+ m_bIgnoreText = true;
+ m_pStrm->Seek( nOldPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx
new file mode 100644
index 0000000000..13270b28c1
--- /dev/null
+++ b/sw/source/filter/ww8/ww8par6.cxx
@@ -0,0 +1,6282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/grabbagitem.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+
+#include <hintids.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <officecfg/Office/Writer.hxx>
+#include "sortedarray.hxx"
+#include "sprmids.hxx"
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <pagedesc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <fchrfmt.hxx>
+#include <fmthdft.hxx>
+#include <fmtclds.hxx>
+#include <fmtftntx.hxx>
+#include <frmatr.hxx>
+#include <section.hxx>
+#include <lineinfo.hxx>
+#include <fmtline.hxx>
+#include <txatbase.hxx>
+#include <fmtflcnt.hxx>
+#include <tgrditem.hxx>
+#include <hfspacingitem.hxx>
+#include <swtable.hxx>
+#include <fltini.hxx>
+#include "writerhelper.hxx"
+#include "writerwordglue.hxx"
+#include "ww8scan.hxx"
+#include "ww8par2.hxx"
+#include "ww8graf.hxx"
+
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <formatflysplit.hxx>
+
+using namespace sw::util;
+using namespace sw::types;
+using namespace ::com::sun::star;
+using namespace nsHdFtFlags;
+
+// various
+
+// WW default for horizontal borders: 2.5 cm
+constexpr auto MM_250 = o3tl::convert(25, o3tl::Length::mm, o3tl::Length::twip); // 1417
+// WW default for lower border: 2.0 cm
+constexpr auto MM_200 = o3tl::convert(20, o3tl::Length::mm, o3tl::Length::twip); // 1134
+
+
+static sal_uInt8 lcl_ReadBorders(bool bVer67, WW8_BRCVer9* brc, WW8PLCFx_Cp_FKP* pPap,
+ const WW8RStyle* pSty = nullptr, const WW8PLCFx_SEPX* pSep = nullptr);
+
+Color SwWW8ImplReader::GetCol(sal_uInt8 nIco)
+{
+ static const Color eSwWW8ColA[] =
+ {
+ COL_AUTO, COL_BLACK, COL_LIGHTBLUE, COL_LIGHTCYAN, COL_LIGHTGREEN,
+ COL_LIGHTMAGENTA, COL_LIGHTRED, COL_YELLOW, COL_WHITE, COL_BLUE,
+ COL_CYAN, COL_GREEN, COL_MAGENTA, COL_RED, COL_BROWN, COL_GRAY,
+ COL_LIGHTGRAY
+ };
+ SAL_WARN_IF(
+ nIco >= SAL_N_ELEMENTS(eSwWW8ColA), "sw.ww8",
+ "ico " << sal_uInt32(nIco) << " >= " << SAL_N_ELEMENTS(eSwWW8ColA));
+ return nIco < SAL_N_ELEMENTS(eSwWW8ColA) ? eSwWW8ColA[nIco] : COL_AUTO;
+}
+
+static sal_uInt32 MSRoundTweak(sal_uInt32 x)
+{
+ return x;
+}
+
+// page attribute which are not handled via the attribute management but
+// using ...->HasSprm
+// (except OLST which stays a normal attribute)
+static short ReadSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, short nDefaultVal )
+{
+ SprmResult aRes = pSep->HasSprm(nId); // sprm here?
+ const sal_uInt8* pS = aRes.pSprm;
+ short nVal = (pS && aRes.nRemainingData >= 2) ? SVBT16ToInt16(pS) : nDefaultVal;
+ return nVal;
+}
+
+static sal_uInt16 ReadUSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, short nDefaultVal )
+{
+ SprmResult aRes = pSep->HasSprm(nId); // sprm here?
+ const sal_uInt8* pS = aRes.pSprm;
+ sal_uInt16 nVal = (pS && aRes.nRemainingData >= 2) ? SVBT16ToUInt16(pS) : nDefaultVal;
+ return nVal;
+}
+
+static sal_uInt8 ReadBSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, sal_uInt8 nDefaultVal )
+{
+ SprmResult aRes = pSep->HasSprm(nId); // sprm here?
+ const sal_uInt8* pS = aRes.pSprm;
+ sal_uInt8 nVal = (pS && aRes.nRemainingData >= 1) ? *pS : nDefaultVal;
+ return nVal;
+}
+
+void wwSection::SetDirection()
+{
+ //sprmSTextFlow
+ switch (maSep.wTextFlow)
+ {
+ default:
+ OSL_ENSURE(false, "Unknown layout type");
+ [[fallthrough]];
+ case 0:
+ meDir=SvxFrameDirection::Horizontal_LR_TB;
+ break;
+ case 1:
+ meDir=SvxFrameDirection::Vertical_RL_TB;
+ break;
+ case 2:
+ //asian letters are not rotated, western are. We can't import
+ //bottom to top going left to right, we can't do this in
+ //pages, (in drawboxes we could partly hack it with a rotated
+ //drawing box, though not frame)
+ meDir=SvxFrameDirection::Vertical_RL_TB;
+ break;
+ case 3:
+ //asian letters are not rotated, western are. We can't import
+ meDir=SvxFrameDirection::Vertical_RL_TB;
+ break;
+ case 4:
+ //asian letters are rotated, western not. We can't import
+ meDir=SvxFrameDirection::Horizontal_LR_TB;
+ break;
+ }
+
+ sal_uInt8 bRTLPgn = maSep.fBiDi;
+ if ((meDir == SvxFrameDirection::Horizontal_LR_TB) && bRTLPgn)
+ meDir = SvxFrameDirection::Horizontal_RL_TB;
+}
+
+bool wwSection::IsVertical() const
+{
+ return meDir == SvxFrameDirection::Vertical_RL_TB || meDir == SvxFrameDirection::Vertical_LR_TB;
+}
+
+/*
+ This is something of festering mapping, I'm open to better ways of doing it,
+ but primarily the grid in writer is different to that in word. In writer the
+ grid elements are squares with ruby rows inbetween. While in word there is no
+ ruby stuff, and the elements are rectangles. By misusing the ruby row I can
+ handle distortions in one direction, but its all a bit of a mess:
+*/
+void SwWW8ImplReader::SetDocumentGrid(SwFrameFormat &rFormat, const wwSection &rSection)
+{
+ if (m_bVer67)
+ return;
+
+ rFormat.SetFormatAttr(SvxFrameDirectionItem(rSection.meDir, RES_FRAMEDIR));
+
+ SwTwips nTextareaHeight = rFormat.GetFrameSize().GetHeight();
+ const SvxULSpaceItem &rUL = rFormat.GetFormatAttr(RES_UL_SPACE);
+ nTextareaHeight -= rUL.GetUpper();
+ nTextareaHeight -= rUL.GetLower();
+
+ SwTwips nTextareaWidth = rFormat.GetFrameSize().GetWidth();
+ const SvxLRSpaceItem &rLR = rFormat.GetFormatAttr(RES_LR_SPACE);
+ nTextareaWidth -= rLR.GetLeft();
+ nTextareaWidth -= rLR.GetRight();
+
+ if (rSection.IsVertical())
+ std::swap(nTextareaHeight, nTextareaWidth);
+
+ SwTextGridItem aGrid;
+ aGrid.SetDisplayGrid(false);
+ aGrid.SetPrintGrid(false);
+ SwTextGrid eType=GRID_NONE;
+
+ switch (rSection.maSep.clm)
+ {
+ case 0:
+ eType = GRID_NONE;
+ break;
+ default:
+ OSL_ENSURE(false, "Unknown grid type");
+ [[fallthrough]];
+ case 3:
+ eType = GRID_LINES_CHARS;
+ aGrid.SetSnapToChars(true);
+ break;
+ case 1:
+ eType = GRID_LINES_CHARS;
+ aGrid.SetSnapToChars(false);
+ break;
+ case 2:
+ eType = GRID_LINES_ONLY;
+ break;
+ }
+
+ aGrid.SetGridType(eType);
+
+ // seem to not add external leading in word, or the character would run across
+ // two line in some cases.
+ // TODO: tdf#85435 suggests that this is wrong
+ if (eType != GRID_NONE)
+ m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, false);
+
+ //force to set document as standard page mode
+ bool bSquaredMode = false;
+ m_rDoc.SetDefaultPageMode( bSquaredMode );
+ aGrid.SetSquaredMode( bSquaredMode );
+
+ //Get the size of word's default styles font
+ sal_uInt32 nCharWidth=240;
+ for (sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); ++nI)
+ {
+ if (m_vColl[nI].m_bValid && m_vColl[nI].m_pFormat &&
+ m_vColl[nI].IsWW8BuiltInDefaultStyle())
+ {
+ const SvxFontHeightItem& rFontHeightItem =
+ m_vColl[nI].m_pFormat->GetFormatAttr(RES_CHRATR_CJK_FONTSIZE);
+ nCharWidth = rFontHeightItem.GetHeight();
+ break;
+ }
+ }
+
+ //dxtCharSpace
+ if (rSection.maSep.dxtCharSpace)
+ {
+ sal_uInt32 nCharSpace = rSection.maSep.dxtCharSpace;
+ //main lives in top 20 bits, and is signed.
+ sal_Int32 nMain = (nCharSpace & 0xFFFFF000);
+ nMain/=0x1000;
+ nCharWidth += nMain*20;
+
+ int nFraction = (nCharSpace & 0x00000FFF);
+ nFraction = (nFraction*20)/0xFFF;
+ nCharWidth += nFraction;
+ }
+
+ aGrid.SetBaseWidth( writer_cast<sal_uInt16>(nCharWidth));
+
+ //sep.dyaLinePitch
+ sal_Int32 nLinePitch = rSection.maSep.dyaLinePitch;
+ if (nLinePitch >= 1 && nLinePitch <= 31680)
+ {
+ aGrid.SetLines(writer_cast<sal_uInt16>(nTextareaHeight/nLinePitch));
+ aGrid.SetBaseHeight(writer_cast<sal_uInt16>(nLinePitch));
+ }
+
+ aGrid.SetRubyHeight(0);
+
+ rFormat.SetFormatAttr(aGrid);
+}
+
+void SwWW8ImplReader::SetRelativeJustify( bool bRel )
+{
+ if ( m_pCurrentColl && StyleExists(m_nCurrentColl) ) // importing style
+ m_vColl[m_nCurrentColl].m_nRelativeJustify = bRel ? 1 : 0;
+ else if ( m_xPlcxMan && m_xPlcxMan->GetPap() ) // importing paragraph
+ m_xPlcxMan->GetPap()->nRelativeJustify = bRel ? 1 : 0;
+}
+
+bool SwWW8ImplReader::IsRelativeJustify()
+{
+ bool bRet = m_xWwFib->GetFIBVersion() >= ww::eWW8;
+ if ( bRet )
+ {
+ // if relativeJustify is undefined (-1), then check the parent style.
+ if ( m_pCurrentColl && StyleExists(m_nCurrentColl) )
+ {
+ sal_Int16 nRelative = m_vColl[m_nCurrentColl].m_nRelativeJustify;
+ if ( nRelative < 0 && m_nCurrentColl )
+ {
+ o3tl::sorted_vector<sal_uInt16> aVisitedStyles;
+ bRet = IsRelativeJustify(m_vColl[m_nCurrentColl].m_nBase, aVisitedStyles);
+ }
+ else
+ bRet = nRelative > 0;
+ }
+ else if ( m_xPlcxMan && m_xPlcxMan->GetPap() )
+ {
+ sal_Int16 nRelative = m_xPlcxMan->GetPap()->nRelativeJustify;
+ if ( nRelative < 0 )
+ {
+ o3tl::sorted_vector<sal_uInt16> aVisitedStyles;
+ bRet = IsRelativeJustify(m_nCurrentColl, aVisitedStyles);
+ }
+ else
+ bRet = nRelative > 0;
+ }
+ }
+
+ return bRet;
+}
+
+bool SwWW8ImplReader::IsRelativeJustify(sal_uInt16 nColl, o3tl::sorted_vector<sal_uInt16>& rVisitedStyles)
+{
+ assert( m_xWwFib->GetFIBVersion() >= ww::eWW8
+ && "pointless to search styles if relative justify is impossible");
+ bool bRet = true;
+ if ( StyleExists(nColl) )
+ {
+ rVisitedStyles.insert(nColl);
+ // if relativeJustify is undefined (-1), then check the parent style.
+ sal_Int16 nRelative = m_vColl[nColl].m_nRelativeJustify;
+ if ( nColl == 0 || nRelative >= 0 )
+ bRet = nRelative > 0;
+ else if (rVisitedStyles.find(m_vColl[nColl].m_nBase) == rVisitedStyles.end()) // detect loop in chain
+ bRet = IsRelativeJustify(m_vColl[nColl].m_nBase, rVisitedStyles);
+ }
+
+ return bRet;
+}
+
+void SwWW8ImplReader::Read_ParaBiDi(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FRAMEDIR);
+ else
+ {
+ SvxFrameDirection eDir =
+ *pData ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB;
+
+ // In eWW8+, justify can be absolute, or relative to BiDi
+ bool bBiDiSwap = IsRelativeJustify();
+ if ( bBiDiSwap )
+ {
+ // Only change if ParaBiDi doesn't match previous setting.
+ const bool bParentRTL = IsRightToLeft();
+ bBiDiSwap = (eDir == SvxFrameDirection::Horizontal_RL_TB && !bParentRTL)
+ || (eDir == SvxFrameDirection::Horizontal_LR_TB && bParentRTL);
+ }
+
+ if ( bBiDiSwap )
+ {
+ const SvxAdjustItem* pItem = GetFormatAttr(RES_PARATR_ADJUST);
+ if ( !pItem )
+ {
+ // no previous adjust: set appropriate default
+ if ( eDir == SvxFrameDirection::Horizontal_LR_TB )
+ NewAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
+ else
+ NewAttr( SvxAdjustItem( SvxAdjust::Right, RES_PARATR_ADJUST ) );
+ }
+ else
+ {
+ // previous adjust and bidi has changed: swap Left/Right
+ const SvxAdjust eJustify = pItem->GetAdjust();
+ if ( eJustify == SvxAdjust::Left )
+ NewAttr( SvxAdjustItem( SvxAdjust::Right, RES_PARATR_ADJUST ) );
+ else if ( eJustify == SvxAdjust::Right )
+ NewAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
+ }
+ }
+
+ NewAttr(SvxFrameDirectionItem(eDir, RES_FRAMEDIR));
+
+ if ( m_pCurrentColl && m_xStyles ) // in style definition
+ m_xStyles->mbBidiChanged = true;
+ }
+}
+
+bool wwSectionManager::SetCols(SwFrameFormat &rFormat, const wwSection &rSection,
+ sal_uInt32 nNetWidth)
+{
+ //sprmSCcolumns - number of columns - 1
+ const sal_Int16 nCols = rSection.NoCols();
+
+ if (nCols < 2) //check for no columns or other weird state
+ return false;
+
+ const sal_uInt16 nNetWriterWidth = writer_cast<sal_uInt16>(nNetWidth);
+ if (nNetWriterWidth == 0)
+ return false;
+
+ SwFormatCol aCol; // Create SwFormatCol
+
+ //sprmSDxaColumns - Default distance is 1.25 cm
+ sal_Int32 nColSpace = rSection.StandardColSeparation();
+
+ const SEPr& rSep = rSection.maSep;
+
+ // sprmSLBetween
+ if (rSep.fLBetween)
+ {
+ aCol.SetLineAdj(COLADJ_TOP); // Line
+ aCol.SetLineHeight(100);
+ aCol.SetLineColor(COL_BLACK);
+ aCol.SetLineWidth(1);
+ }
+
+ aCol.Init(nCols, writer_cast<sal_uInt16>(nColSpace), nNetWriterWidth);
+
+ // sprmSFEvenlySpaced
+ if (!rSep.fEvenlySpaced)
+ {
+ aCol.SetOrtho_(false);
+ const sal_uInt16 maxIdx = SAL_N_ELEMENTS(rSep.rgdxaColumnWidthSpacing);
+ for (sal_uInt16 i = 0, nIdx = 1; i < nCols && nIdx < maxIdx; i++, nIdx+=2 )
+ {
+ SwColumn* pCol = &aCol.GetColumns()[i];
+ const sal_Int32 nLeft = rSep.rgdxaColumnWidthSpacing[nIdx-1]/2;
+ const sal_Int32 nRight = rSep.rgdxaColumnWidthSpacing[nIdx+1]/2;
+ const sal_Int32 nWishWidth = rSep.rgdxaColumnWidthSpacing[nIdx]
+ + nLeft + nRight;
+ pCol->SetWishWidth(writer_cast<sal_uInt16>(nWishWidth));
+ pCol->SetLeft(writer_cast<sal_uInt16>(nLeft));
+ pCol->SetRight(writer_cast<sal_uInt16>(nRight));
+ }
+ aCol.SetWishWidth(nNetWriterWidth);
+ }
+ rFormat.SetFormatAttr(aCol);
+ return true;
+}
+
+void wwSectionManager::SetLeftRight(wwSection &rSection)
+{
+ // 3. LR-Margin
+ sal_uInt32 nWWLe = MSRoundTweak(rSection.maSep.dxaLeft);
+ sal_uInt32 nWWRi = MSRoundTweak(rSection.maSep.dxaRight);
+ sal_uInt32 nWWGu = rSection.maSep.dzaGutter;
+
+ /*
+ fRTLGutter is set if the gutter is on the right, the gutter is otherwise
+ placed on the left unless the global dop options are to put it on top, that
+ case is handled in GetPageULData.
+ */
+ if (rSection.maSep.fRTLGutter)
+ {
+ rSection.m_bRtlGutter = true;
+ }
+
+ // Left / Right
+ if ((rSection.m_nPgWidth - nWWLe - nWWRi) < MINLAY)
+ {
+ /*
+ There are some label templates which are "broken", they specify
+ margins which make no sense e.g. Left 16.10cm, Right 16.10cm. So the
+ space left between the margins is less than 0 In word the left margin
+ is honoured and if the right margin would be past the left margin is
+ left at the left margin position.
+
+ Now this will work fine for importing, layout and exporting, *but* the
+ page layout dialog has a hardcoded minimum page width of 0.5cm so it
+ will report a different value than what is actually being used. i.e.
+ it will add up the values to give a wider page than is actually being
+ used.
+ */
+ nWWRi = rSection.m_nPgWidth - nWWLe - MINLAY;
+ }
+
+ rSection.m_nPgLeft = nWWLe;
+ rSection.m_nPgRight = nWWRi;
+ rSection.m_nPgGutter = nWWGu;
+}
+
+void wwSectionManager::SetPage(SwPageDesc &rInPageDesc, SwFrameFormat &rFormat,
+ const wwSection &rSection, bool bIgnoreCols)
+{
+ // 1. orientation
+ rInPageDesc.SetLandscape(rSection.IsLandScape());
+
+ // 2. paper size
+ SwFormatFrameSize aSz( rFormat.GetFrameSize() );
+ aSz.SetWidth(rSection.GetPageWidth());
+ aSz.SetHeight(SvxPaperInfo::GetSloppyPaperDimension(rSection.GetPageHeight()));
+ rFormat.SetFormatAttr(aSz);
+
+ SvxLRSpaceItem aLR(rSection.GetPageLeft(), rSection.GetPageRight(), 0, RES_LR_SPACE);
+ aLR.SetGutterMargin(rSection.m_nPgGutter);
+ rFormat.SetFormatAttr(aLR);
+
+ SfxBoolItem aRtlGutter(RES_RTL_GUTTER, rSection.m_bRtlGutter);
+ rFormat.SetFormatAttr(aRtlGutter);
+
+ if (!bIgnoreCols)
+ SetCols(rFormat, rSection, rSection.GetTextAreaWidth());
+}
+
+namespace {
+// Returns corrected (ODF) margin size
+tools::Long SetBorderDistance(bool bFromEdge, SvxBoxItem& aBox, SvxBoxItemLine eLine, tools::Long nMSMargin)
+{
+ const editeng::SvxBorderLine* pLine = aBox.GetLine(eLine);
+ if (!pLine)
+ return nMSMargin;
+ sal_Int32 nNewMargin = nMSMargin;
+ sal_Int32 nNewDist = aBox.GetDistance(eLine);
+ sal_Int32 nLineWidth = pLine->GetScaledWidth();
+
+ editeng::BorderDistanceFromWord(bFromEdge, nNewMargin, nNewDist, nLineWidth);
+ aBox.SetDistance(nNewDist, eLine);
+
+ return nNewMargin;
+}
+}
+
+void SwWW8ImplReader::SetPageBorder(SwFrameFormat &rFormat, const wwSection &rSection)
+{
+ if (!IsBorder(rSection.m_brc))
+ return;
+
+ SfxItemSet aSet(rFormat.GetAttrSet());
+ short aSizeArray[5]={0};
+ SetFlyBordersShadow(aSet, rSection.m_brc, &aSizeArray[0]);
+ SvxLRSpaceItem aLR(aSet.Get(RES_LR_SPACE));
+ SvxULSpaceItem aUL(aSet.Get(RES_UL_SPACE));
+ SvxBoxItem aBox(aSet.Get(RES_BOX));
+ bool bFromEdge = rSection.maSep.pgbOffsetFrom == 1;
+
+ aLR.SetLeft(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::LEFT, aLR.GetLeft()));
+ aLR.SetRight(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::RIGHT, aLR.GetRight()));
+ aUL.SetUpper(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::TOP, aUL.GetUpper()));
+ aUL.SetLower(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::BOTTOM, aUL.GetLower()));
+
+ aSet.Put(aBox);
+ aSet.Put(aLR);
+ aSet.Put(aUL);
+ rFormat.SetFormatAttr(aSet);
+}
+
+void wwSectionManager::GetPageULData(const wwSection &rSection,
+ wwSectionManager::wwULSpaceData& rData) const
+{
+ sal_Int32 nWWUp = rSection.maSep.dyaTop;
+ sal_Int32 nWWLo = rSection.maSep.dyaBottom;
+ sal_uInt32 nWWHTop = rSection.maSep.dyaHdrTop;
+ sal_uInt32 nWWFBot = rSection.maSep.dyaHdrBottom;
+
+ /*
+ If there is gutter in 97+ and the dop says put it on top then get the
+ gutter distance and set it to the top margin. When we are "two pages
+ in one" the gutter is put at the top of odd pages, and bottom of
+ even pages, something we cannot do. So we will put it on top of all
+ pages, that way the pages are at least the right size.
+ */
+ if (!mrReader.m_bVer67 && mrReader.m_xWDop->iGutterPos &&
+ rSection.maSep.fRTLGutter)
+ {
+ nWWUp += rSection.maSep.dzaGutter;
+ }
+
+ /* Check whether this section has headers / footers */
+ sal_uInt16 nHeaderMask = WW8_HEADER_EVEN | WW8_HEADER_ODD;
+ sal_uInt16 nFooterMask = WW8_FOOTER_EVEN | WW8_FOOTER_ODD;
+ /* Ignore the presence of a first-page header/footer unless it is enabled */
+ if( rSection.HasTitlePage() )
+ {
+ nHeaderMask |= WW8_HEADER_FIRST;
+ nFooterMask |= WW8_FOOTER_FIRST;
+ }
+ rData.bHasHeader = (rSection.maSep.grpfIhdt & nHeaderMask) != 0;
+ rData.bHasFooter = (rSection.maSep.grpfIhdt & nFooterMask) != 0;
+
+ if( rData.bHasHeader )
+ {
+ rData.nSwUp = nWWHTop; // Header -> convert
+ // #i19922# - correction:
+ // consider that <nWWUp> can be negative, compare only if it's positive
+ if ( nWWUp > 0 &&
+ o3tl::make_unsigned(nWWUp) >= nWWHTop )
+ rData.nSwHLo = nWWUp - nWWHTop;
+ else
+ rData.nSwHLo = 0;
+
+ // #i19922# - minimum page header height is now 1mm
+ // use new constant <cMinHdFtHeight>
+ if (rData.nSwHLo < sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight))
+ rData.nSwHLo = sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight);
+ }
+ else // no header -> just use Up as-is
+ rData.nSwUp = std::abs(nWWUp);
+
+ if( rData.bHasFooter )
+ {
+ rData.nSwLo = nWWFBot; // footer -> convert
+ // #i19922# - correction: consider that <nWWLo> can be negative, compare only if it's positive
+ if ( nWWLo > 0 &&
+ o3tl::make_unsigned(nWWLo) >= nWWFBot )
+ rData.nSwFUp = nWWLo - nWWFBot;
+ else
+ rData.nSwFUp = 0;
+
+ // #i19922# - minimum page header height is now 1mm
+ // use new constant <cMinHdFtHeight>
+ if (rData.nSwFUp < sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight))
+ rData.nSwFUp = sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight);
+ }
+ else // no footer -> just use Lo as-is
+ rData.nSwLo = std::abs(nWWLo);
+}
+
+void wwSectionManager::SetPageULSpaceItems(SwFrameFormat &rFormat,
+ wwSectionManager::wwULSpaceData const & rData, const wwSection &rSection)
+{
+ if (rData.bHasHeader) // ... and set Header-Lower
+ {
+ // set header height to minimum
+ if (SwFrameFormat* pHdFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat()))
+ {
+ SvxULSpaceItem aHdUL(pHdFormat->GetULSpace());
+ if (!rSection.IsFixedHeightHeader()) //normal
+ {
+ pHdFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Minimum, 0, rData.nSwHLo));
+ // #i19922# - minimum page header height is now 1mm
+ // use new constant <cMinHdFtHeight>
+ aHdUL.SetLower( writer_cast<sal_uInt16>(rData.nSwHLo - cMinHdFtHeight) );
+ pHdFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
+ RES_HEADER_FOOTER_EAT_SPACING, true));
+ }
+ else
+ {
+ // Hack alert: these calculations are based on
+ // #112727# import negative height headers/footers as floating frames inside fixed height headers/footer
+ // #i48832# - set correct spacing between header and body.
+ const sal_Int32 nHdLowerSpace(std::max<sal_Int32>(0, std::abs(rSection.maSep.dyaTop) - rData.nSwUp - rData.nSwHLo));
+ pHdFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, 0, rData.nSwHLo + nHdLowerSpace));
+ aHdUL.SetLower( static_cast< sal_uInt16 >(nHdLowerSpace) );
+ pHdFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
+ RES_HEADER_FOOTER_EAT_SPACING, false));
+ }
+ pHdFormat->SetFormatAttr(aHdUL);
+ }
+ }
+
+ if (rData.bHasFooter) // ... and set footer-upper
+ {
+ if (SwFrameFormat* pFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat()))
+ {
+ SvxULSpaceItem aFtUL(pFtFormat->GetULSpace());
+ if (!rSection.IsFixedHeightFooter()) //normal
+ {
+ pFtFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Minimum, 0, rData.nSwFUp));
+ // #i19922# - minimum page header height is now 1mm
+ // use new constant <cMinHdFtHeight>
+ aFtUL.SetUpper( writer_cast<sal_uInt16>(rData.nSwFUp - cMinHdFtHeight) );
+ pFtFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
+ RES_HEADER_FOOTER_EAT_SPACING, true));
+ }
+ else
+ {
+ // #i48832# - set correct spacing between footer and body.
+ const sal_Int32 nFtUpperSpace(std::max<sal_Int32>(0, std::abs(rSection.maSep.dyaBottom) - rData.nSwLo - rData.nSwFUp));
+ pFtFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, 0, rData.nSwFUp + nFtUpperSpace));
+ aFtUL.SetUpper( static_cast< sal_uInt16 >(nFtUpperSpace) );
+ pFtFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem(
+ RES_HEADER_FOOTER_EAT_SPACING, false));
+ }
+ pFtFormat->SetFormatAttr(aFtUL);
+ }
+ }
+
+ SvxULSpaceItem aUL(writer_cast<sal_uInt16>(rData.nSwUp),
+ writer_cast<sal_uInt16>(rData.nSwLo), RES_UL_SPACE);
+ rFormat.SetFormatAttr(aUL);
+}
+
+SwSectionFormat *wwSectionManager::InsertSection(
+ SwPaM const & rMyPaM, wwSection &rSection)
+{
+ SwSectionData aSection( SectionType::Content,
+ mrReader.m_rDoc.GetUniqueSectionName() );
+
+ SfxItemSet aSet( mrReader.m_rDoc.GetAttrPool(), aFrameFormatSetRange );
+
+ bool bRTLPgn = !maSegments.empty() && maSegments.back().IsBiDi();
+ aSet.Put(SvxFrameDirectionItem(
+ bRTLPgn ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
+
+ if (2 == mrReader.m_xWDop->fpc)
+ aSet.Put( SwFormatFootnoteAtTextEnd(FTNEND_ATTXTEND));
+ if (0 == mrReader.m_xWDop->epc)
+ aSet.Put( SwFormatEndAtTextEnd(FTNEND_ATTXTEND));
+
+ aSection.SetProtectFlag(SectionIsProtected(rSection));
+
+ rSection.mpSection =
+ mrReader.m_rDoc.InsertSwSection( rMyPaM, aSection, nullptr, & aSet );
+ OSL_ENSURE(rSection.mpSection, "section not inserted!");
+ if (!rSection.mpSection)
+ return nullptr;
+
+ SwPageDesc *pPage = nullptr;
+ auto aIter = std::find_if(maSegments.rbegin(), maSegments.rend(),
+ [](const wwSection& rSegment) { return rSegment.mpPage != nullptr; });
+ if (aIter != maSegments.rend())
+ pPage = aIter->mpPage;
+
+ OSL_ENSURE(pPage, "no page outside this section!");
+
+ if (!pPage)
+ pPage = &mrReader.m_rDoc.GetPageDesc(0);
+
+ SwSectionFormat *pFormat = rSection.mpSection->GetFormat();
+ OSL_ENSURE(pFormat, "impossible");
+ if (!pFormat)
+ return nullptr;
+
+ SwFrameFormat& rFormat = pPage->GetMaster();
+ const SvxLRSpaceItem& rLR = rFormat.GetLRSpace();
+ tools::Long nPageLeft = rLR.GetLeft();
+ tools::Long nPageRight = rLR.GetRight();
+ tools::Long nSectionLeft = rSection.GetPageLeft() - nPageLeft;
+ tools::Long nSectionRight = rSection.GetPageRight() - nPageRight;
+ if ((nSectionLeft != 0) || (nSectionRight != 0))
+ {
+ SvxLRSpaceItem aLR(nSectionLeft, nSectionRight, 0, RES_LR_SPACE);
+ pFormat->SetFormatAttr(aLR);
+ }
+
+ SetCols(*pFormat, rSection, rSection.GetTextAreaWidth());
+ return pFormat;
+}
+
+void SwWW8ImplReader::HandleLineNumbering(const wwSection &rSection)
+{
+ // check if Line Numbering must be activated or reset
+ if (!(m_bNewDoc && rSection.maSep.nLnnMod))
+ return;
+
+ // restart-numbering-mode: 0 per page, 1 per section, 2 never restart
+ bool bRestartLnNumPerSection = (1 == rSection.maSep.lnc);
+
+ if (m_bNoLnNumYet)
+ {
+ SwLineNumberInfo aInfo( m_rDoc.GetLineNumberInfo() );
+
+ aInfo.SetPaintLineNumbers(true);
+
+ aInfo.SetRestartEachPage(rSection.maSep.lnc == 0);
+
+ // A value of 0 (auto) indicates that the application MUST automatically determine positioning.
+ if ( rSection.maSep.dxaLnn )
+ aInfo.SetPosFromLeft(writer_cast<sal_uInt16>(rSection.maSep.dxaLnn));
+
+ //Paint only for every n line
+ aInfo.SetCountBy(rSection.maSep.nLnnMod);
+
+ // to be defaulted features ( HARDCODED in MS Word 6,7,8,9 )
+ aInfo.SetCountBlankLines(true);
+ aInfo.SetCountInFlys(false);
+ aInfo.SetPos( LINENUMBER_POS_LEFT );
+ SvxNumberType aNumType; // this sets SVX_NUM_ARABIC per default
+ aInfo.SetNumType( aNumType );
+
+ m_rDoc.SetLineNumberInfo( aInfo );
+ m_bNoLnNumYet = false;
+ }
+
+ if ((0 < rSection.maSep.lnnMin) || bRestartLnNumPerSection)
+ {
+ SwFormatLineNumber aLN;
+ if (const SwFormatLineNumber* pLN = GetFormatAttr(RES_LINENUMBER))
+ {
+ aLN.SetCountLines( pLN->IsCount() );
+ }
+ aLN.SetStartValue(1 + rSection.maSep.lnnMin);
+ NewAttr(aLN);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_LINENUMBER);
+ }
+}
+
+wwSection::wwSection(const SwPosition &rPos) : maStart(rPos.GetNode())
+ , mpSection(nullptr)
+ , mpPage(nullptr)
+ , meDir(SvxFrameDirection::Horizontal_LR_TB)
+ , m_nPgWidth(SvxPaperInfo::GetPaperSize(PAPER_A4).Width())
+ , m_nPgLeft(MM_250)
+ , m_nPgRight(MM_250)
+ , m_nPgGutter(0)
+ , mnVerticalAdjustment(drawing::TextVerticalAdjust_TOP)
+ , mnBorders(0)
+ , mbHasFootnote(false)
+{
+}
+
+void wwSectionManager::SetNumberingType(const wwSection &rNewSection,
+ SwPageDesc &rPageDesc)
+{
+ // save page number format
+ static const SvxNumType aNumTyp[5] =
+ {
+ SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER,
+ SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N
+ };
+
+ SvxNumberType aType;
+ aType.SetNumberingType( aNumTyp[rNewSection.maSep.nfcPgn] );
+ rPageDesc.SetNumType(aType);
+}
+
+// CreateSep is called for every section change (even at the start of
+// the document. CreateSep also creates the pagedesc(s) and
+// fills it/them with attributes and KF texts.
+// This has become necessary because the translation of the various
+// page attributes is interconnected too much.
+void wwSectionManager::CreateSep(const tools::Long nTextPos)
+{
+ /*
+ #i1909# section/page breaks should not occur in tables or subpage
+ elements like frames. Word itself ignores them in this case. The bug is
+ more likely that this filter created such documents in the past!
+ */
+ if (mrReader.m_nInTable || mrReader.m_bTxbxFlySection || mrReader.InLocalApo())
+ return;
+
+ WW8PLCFx_SEPX* pSep = mrReader.m_xPlcxMan->GetSepPLCF();
+ OSL_ENSURE(pSep, "impossible!");
+ if (!pSep)
+ return;
+
+ if (!maSegments.empty() && mrReader.m_oLastAnchorPos && *mrReader.m_oLastAnchorPos == *mrReader.m_pPaM->GetPoint())
+ {
+ bool insert = true;
+ SwPaM pam( *mrReader.m_oLastAnchorPos );
+ if( pam.Move(fnMoveBackward, GoInNode))
+ if( SwTextNode* txtNode = pam.GetPoint()->GetNode().GetTextNode())
+ if( txtNode->Len() == 0 )
+ insert = false;
+ if( insert )
+ mrReader.AppendTextNode(*mrReader.m_pPaM->GetPoint());
+ }
+
+ ww::WordVersion eVer = mrReader.GetFib().GetFIBVersion();
+
+ // M.M. Create a linked section if the WkbPLCF
+ // has an entry for one at this cp
+ WW8PLCFspecial* pWkb = mrReader.m_xPlcxMan->GetWkbPLCF();
+ if (pWkb && pWkb->SeekPosExact(nTextPos) &&
+ pWkb->Where() == nTextPos)
+ {
+ void* pData;
+ WW8_CP nTest;
+ bool bSuccess = pWkb->Get(nTest, pData);
+ if (!bSuccess)
+ return;
+ OUString sSectionName = mrReader.m_aLinkStringMap[SVBT16ToUInt16( static_cast<WW8_WKB*>(pData)->nLinkId) ];
+ sSectionName = mrReader.ConvertFFileName(sSectionName);
+ SwSectionData aSection(SectionType::FileLink, sSectionName);
+ aSection.SetLinkFileName( sSectionName );
+ aSection.SetProtectFlag(true);
+ // #i19922# - improvement: return value of method <Insert> not used.
+ mrReader.m_rDoc.InsertSwSection(*mrReader.m_pPaM, aSection, nullptr, nullptr, false);
+ }
+
+ wwSection aLastSection(*mrReader.m_pPaM->GetPoint());
+ if (!maSegments.empty())
+ aLastSection = maSegments.back();
+
+ //Here
+ sal_uInt16 nLIdx = ( ( static_cast<sal_uInt16>(mrReader.m_xWwFib->m_lid) & 0xff ) == 0x9 ) ? 1 : 0;
+
+ //BEGIN read section values
+ wwSection aNewSection(*mrReader.m_pPaM->GetPoint());
+
+ static const sal_uInt16 aVer2Ids0[] =
+ {
+ /*sprmSBkc*/ 117,
+ /*sprmSFTitlePage*/ 118,
+ /*sprmSNfcPgn*/ 122,
+ /*sprmSCcolumns*/ 119,
+ /*sprmSDxaColumns*/ 120,
+ /*sprmSLBetween*/ 133
+ };
+
+ static const sal_uInt16 aVer67Ids0[] =
+ {
+ NS_sprm::v6::sprmSBkc,
+ NS_sprm::v6::sprmSFTitlePage,
+ NS_sprm::v6::sprmSNfcPgn,
+ NS_sprm::v6::sprmSCcolumns,
+ NS_sprm::v6::sprmSDxaColumns,
+ NS_sprm::v6::sprmSLBetween
+ };
+
+ static const sal_uInt16 aVer8Ids0[] =
+ {
+ NS_sprm::SBkc::val,
+ NS_sprm::SFTitlePage::val,
+ NS_sprm::SNfcPgn::val,
+ NS_sprm::SCcolumns::val,
+ NS_sprm::SDxaColumns::val,
+ NS_sprm::SLBetween::val
+ };
+
+ const sal_uInt16* pIds = eVer <= ww::eWW2 ? aVer2Ids0 : eVer <= ww::eWW7 ? aVer67Ids0 : aVer8Ids0;
+
+ SprmResult aRes = pSep->HasSprm(pIds[0]);
+ const sal_uInt8* pSprmBkc = aRes.pSprm;
+ if (!maSegments.empty())
+ {
+ // Type of break: break codes are:
+ // 0 No break
+ // 1 New column
+ // 2 New page
+ // 3 Even page
+ // 4 Odd page
+ if (pSprmBkc && aRes.nRemainingData >= 1)
+ aNewSection.maSep.bkc = *pSprmBkc;
+ }
+
+ // Has a table page
+ aNewSection.maSep.fTitlePage =
+ sal_uInt8(0 != ReadBSprm( pSep, pIds[1], 0 ));
+
+ // sprmSNfcPgn
+ aNewSection.maSep.nfcPgn = ReadBSprm( pSep, pIds[2], 0 );
+ if (aNewSection.maSep.nfcPgn > 4)
+ aNewSection.maSep.nfcPgn = 0;
+
+ aNewSection.maSep.fUnlocked = eVer > ww::eWW2 ? ReadBSprm(pSep, (eVer <= ww::eWW7 ? NS_sprm::v6::sprmSFProtected : NS_sprm::SFProtected::val), 0 ) : 0;
+
+ // sprmSFBiDi
+ aNewSection.maSep.fBiDi = eVer >= ww::eWW8 ? ReadBSprm(pSep, NS_sprm::SFBiDi::val, 0) : 0;
+
+ // Reading section property sprmSCcolumns - one less than the number of columns in the section.
+ // It must be less than MAX_NO_OF_SEP_COLUMNS according the WW8 specification.
+ aNewSection.maSep.ccolM1 = ReadSprm(pSep, pIds[3], 0 );
+ if ( aNewSection.maSep.ccolM1 >= MAX_NO_OF_SEP_COLUMNS )
+ {
+ // clip to max
+ aNewSection.maSep.ccolM1 = MAX_NO_OF_SEP_COLUMNS-1;
+ }
+
+ //sprmSDxaColumns - default distance 1.25 cm
+ aNewSection.maSep.dxaColumns = ReadUSprm( pSep, pIds[4], 708 );
+
+ // sprmSLBetween
+ aNewSection.maSep.fLBetween = ReadBSprm(pSep, pIds[5], 0 );
+
+ if (eVer >= ww::eWW6)
+ {
+ // sprmSFEvenlySpaced
+ aNewSection.maSep.fEvenlySpaced =
+ sal_uInt8(ReadBSprm(pSep, (eVer <= ww::eWW7 ? NS_sprm::v6::sprmSFEvenlySpaced : NS_sprm::SFEvenlySpaced::val), 1) != 0);
+
+ if (aNewSection.maSep.ccolM1 > 0 && !aNewSection.maSep.fEvenlySpaced)
+ {
+ int nColumnDataIdx = 0;
+ aNewSection.maSep.rgdxaColumnWidthSpacing[nColumnDataIdx] = 0;
+
+ const sal_uInt16 nColumnWidthSprmId = ( eVer <= ww::eWW7 ? NS_sprm::v6::sprmSDxaColWidth : NS_sprm::SDxaColWidth::val);
+ const sal_uInt16 nColumnSpacingSprmId = ( eVer <= ww::eWW7 ? NS_sprm::v6::sprmSDxaColSpacing : NS_sprm::SDxaColSpacing::val);
+ const sal_uInt8 nColumnCount = static_cast< sal_uInt8 >(aNewSection.maSep.ccolM1 + 1);
+ for ( sal_uInt8 nColumn = 0; nColumn < nColumnCount; ++nColumn )
+ {
+ //sprmSDxaColWidth
+ SprmResult aSWRes = pSep->HasSprm(nColumnWidthSprmId, nColumn);
+ const sal_uInt8* pSW = aSWRes.pSprm;
+
+ OSL_ENSURE( pSW, "+Sprm 136 (resp. 0xF203) (ColWidth) missing" );
+ sal_uInt16 nWidth = (pSW && aSWRes.nRemainingData >= 3) ? SVBT16ToUInt16(pSW + 1) : 1440;
+
+ aNewSection.maSep.rgdxaColumnWidthSpacing[++nColumnDataIdx] = nWidth;
+
+ if ( nColumn < nColumnCount - 1 )
+ {
+ //sprmSDxaColSpacing
+ SprmResult aSDRes = pSep->HasSprm(nColumnSpacingSprmId, nColumn);
+ const sal_uInt8* pSD = aSDRes.pSprm;
+
+ OSL_ENSURE( pSD, "+Sprm 137 (resp. 0xF204) (Colspacing) missing" );
+ if (pSD && aSDRes.nRemainingData >= 3)
+ {
+ nWidth = SVBT16ToUInt16(pSD + 1);
+ aNewSection.maSep.rgdxaColumnWidthSpacing[++nColumnDataIdx] = nWidth;
+ }
+ }
+ }
+ }
+ }
+
+ static const sal_uInt16 aVer2Ids1[] =
+ {
+ /*sprmSBOrientation*/ 137,
+ /*sprmSXaPage*/ 139,
+ /*sprmSYaPage*/ 140,
+ /*sprmSDxaLeft*/ 141,
+ /*sprmSDxaRight*/ 142,
+ /*sprmSDzaGutter*/ 145,
+ /*sprmSFPgnRestart*/ 125,
+ /*sprmSPgnStart*/ 136,
+ /*sprmSDmBinFirst*/ 115,
+ /*sprmSDmBinOther*/ 116
+ };
+
+ static const sal_uInt16 aVer67Ids1[] =
+ {
+ NS_sprm::v6::sprmSBOrientation,
+ NS_sprm::v6::sprmSXaPage,
+ NS_sprm::v6::sprmSYaPage,
+ NS_sprm::v6::sprmSDxaLeft,
+ NS_sprm::v6::sprmSDxaRight,
+ NS_sprm::v6::sprmSDzaGutter,
+ NS_sprm::v6::sprmSFPgnRestart,
+ NS_sprm::v6::sprmSPgnStart,
+ NS_sprm::v6::sprmSDmBinFirst,
+ NS_sprm::v6::sprmSDmBinOther
+ };
+
+ static const sal_uInt16 aVer8Ids1[] =
+ {
+ NS_sprm::SBOrientation::val,
+ NS_sprm::SXaPage::val,
+ NS_sprm::SYaPage::val,
+ NS_sprm::SDxaLeft::val,
+ NS_sprm::SDxaRight::val,
+ NS_sprm::SDzaGutter::val,
+ NS_sprm::SFPgnRestart::val,
+ NS_sprm::SPgnStart97::val,
+ NS_sprm::SDmBinFirst::val,
+ NS_sprm::SDmBinOther::val
+ };
+
+ pIds = eVer <= ww::eWW2 ? aVer2Ids1 : eVer <= ww::eWW7 ? aVer67Ids1 : aVer8Ids1;
+
+ // 1. orientation
+ aNewSection.maSep.dmOrientPage = ReadBSprm(pSep, pIds[0], 0);
+
+ // 2. paper size
+ aNewSection.maSep.xaPage = ReadUSprm(pSep, pIds[1], lLetterWidth);
+ aNewSection.m_nPgWidth = SvxPaperInfo::GetSloppyPaperDimension(aNewSection.maSep.xaPage);
+
+ aNewSection.maSep.yaPage = ReadUSprm(pSep, pIds[2], lLetterHeight);
+
+ // 3. LR borders
+ static const sal_uInt16 nLef[] = { MM_250, 1800 };
+ static const sal_uInt16 nRig[] = { MM_250, 1800 };
+
+ aNewSection.maSep.dxaLeft = ReadUSprm( pSep, pIds[3], nLef[nLIdx]);
+ aNewSection.maSep.dxaRight = ReadUSprm( pSep, pIds[4], nRig[nLIdx]);
+
+ // 2pages in 1sheet hackery ?
+ // #i31806# but only swap if 2page in 1sheet is enabled.
+ // it's not clear if dmOrientPage is the correct member to
+ // decide on this.
+ if(mrReader.m_xWDop->doptypography.m_f2on1 &&
+ aNewSection.maSep.dmOrientPage == 2)
+ std::swap(aNewSection.maSep.dxaLeft, aNewSection.maSep.dxaRight);
+
+ aNewSection.maSep.dzaGutter = ReadUSprm( pSep, pIds[5], 0);
+
+ aNewSection.maSep.fRTLGutter = static_cast<sal_uInt8>(
+ eVer >= ww::eWW8 ? ReadBSprm(pSep, NS_sprm::SFRTLGutter::val, 0) : 0);
+
+ // Page Number Restarts - sprmSFPgnRestart
+ aNewSection.maSep.fPgnRestart = ReadBSprm(pSep, pIds[6], 0);
+
+ aNewSection.maSep.pgnStart = ReadUSprm( pSep, pIds[7], 0 );
+
+ // if the document's first page number is unspecified, but it starts with an even page break,
+ // then set the first page number to two
+ if ( maSegments.empty() && !aNewSection.maSep.fPgnRestart && pSprmBkc && *pSprmBkc == 3 )
+ {
+ aNewSection.maSep.pgnStart = 2;
+ aNewSection.maSep.fPgnRestart = 1;
+ }
+
+ if (eVer >= ww::eWW6)
+ {
+ aRes = pSep->HasSprm(eVer <= ww::eWW7 ? NS_sprm::v6::sprmSiHeadingPgn : NS_sprm::SiHeadingPgn::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 1)
+ aNewSection.maSep.iHeadingPgn = *aRes.pSprm;
+
+ aRes = pSep->HasSprm(eVer <= ww::eWW7 ? NS_sprm::v6::sprmSScnsPgn : NS_sprm::ScnsPgn::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 1)
+ aNewSection.maSep.cnsPgn = *aRes.pSprm;
+ }
+
+ aRes = pSep->HasSprm(pIds[8]);
+ const sal_uInt8* pSprmSDmBinFirst = aRes.pSprm;
+ if (pSprmSDmBinFirst && aRes.nRemainingData >= 1)
+ aNewSection.maSep.dmBinFirst = *pSprmSDmBinFirst;
+
+ aRes = pSep->HasSprm(pIds[9]);
+ const sal_uInt8* pSprmSDmBinOther = aRes.pSprm;
+ if (pSprmSDmBinOther && aRes.nRemainingData >= 1)
+ aNewSection.maSep.dmBinOther = *pSprmSDmBinOther;
+
+ static const sal_uInt16 nTop[] = { MM_250, 1440 };
+ static const sal_uInt16 nBot[] = { MM_200, 1440 };
+
+ static const sal_uInt16 aVer2Ids2[] =
+ {
+ /*sprmSDyaTop*/ 143,
+ /*sprmSDyaBottom*/ 144,
+ /*sprmSDyaHdrTop*/ 131,
+ /*sprmSDyaHdrBottom*/ 132,
+ /*sprmSNLnnMod*/ 129,
+ /*sprmSLnc*/ 127,
+ /*sprmSDxaLnn*/ 130,
+ /*sprmSLnnMin*/ 135
+ };
+
+ static const sal_uInt16 aVer67Ids2[] =
+ {
+ NS_sprm::v6::sprmSDyaTop,
+ NS_sprm::v6::sprmSDyaBottom,
+ NS_sprm::v6::sprmSDyaHdrTop,
+ NS_sprm::v6::sprmSDyaHdrBottom,
+ NS_sprm::v6::sprmSNLnnMod,
+ NS_sprm::v6::sprmSLnc,
+ NS_sprm::v6::sprmSDxaLnn,
+ NS_sprm::v6::sprmSLnnMin
+ };
+ static const sal_uInt16 aVer8Ids2[] =
+ {
+ NS_sprm::SDyaTop::val,
+ NS_sprm::SDyaBottom::val,
+ NS_sprm::SDyaHdrTop::val,
+ NS_sprm::SDyaHdrBottom::val,
+ NS_sprm::SNLnnMod::val,
+ NS_sprm::SLnc::val,
+ NS_sprm::SDxaLnn::val,
+ NS_sprm::SLnnMin::val
+ };
+
+ pIds = eVer <= ww::eWW2 ? aVer2Ids2 : eVer <= ww::eWW7 ? aVer67Ids2 : aVer8Ids2;
+
+ aNewSection.maSep.dyaTop = ReadSprm( pSep, pIds[0], nTop[nLIdx] );
+ aNewSection.maSep.dyaBottom = ReadSprm( pSep, pIds[1], nBot[nLIdx] );
+ aNewSection.maSep.dyaHdrTop = ReadUSprm( pSep, pIds[2], 720 );
+ aNewSection.maSep.dyaHdrBottom = ReadUSprm( pSep, pIds[3], 720 );
+
+ if (eVer >= ww::eWW8)
+ {
+ aNewSection.maSep.wTextFlow = ReadUSprm(pSep, NS_sprm::STextFlow::val, 0);
+ aNewSection.maSep.clm = ReadUSprm( pSep, NS_sprm::SClm::val, 0 );
+ aNewSection.maSep.dyaLinePitch = ReadUSprm(pSep, NS_sprm::SDyaLinePitch::val, 360);
+ aRes = pSep->HasSprm(NS_sprm::SDxtCharSpace::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 4)
+ aNewSection.maSep.dxtCharSpace = SVBT32ToUInt32(aRes.pSprm);
+
+ //sprmSPgbProp
+ sal_uInt16 pgbProp = ReadSprm( pSep, NS_sprm::SPgbProp::val, 0 );
+ aNewSection.maSep.pgbApplyTo = pgbProp & 0x0007;
+ aNewSection.maSep.pgbPageDepth = (pgbProp & 0x0018) >> 3;
+ aNewSection.maSep.pgbOffsetFrom = (pgbProp & 0x00E0) >> 5;
+
+ aNewSection.mnBorders = ::lcl_ReadBorders(false, aNewSection.m_brc, nullptr, nullptr, pSep);
+ }
+
+ // check if Line Numbering must be activated or reset
+ SprmResult aSprmSNLnnMod = pSep->HasSprm(pIds[4]);
+ if (aSprmSNLnnMod.pSprm && aSprmSNLnnMod.nRemainingData >= 1)
+ aNewSection.maSep.nLnnMod = *aSprmSNLnnMod.pSprm;
+
+ SprmResult aSprmSLnc = pSep->HasSprm(pIds[5]);
+ if (aSprmSLnc.pSprm && aSprmSLnc.nRemainingData >= 1)
+ aNewSection.maSep.lnc = *aSprmSLnc.pSprm;
+
+ SprmResult aSprmSDxaLnn = pSep->HasSprm(pIds[6]);
+ if (aSprmSDxaLnn.pSprm && aSprmSDxaLnn.nRemainingData >= 2)
+ aNewSection.maSep.dxaLnn = SVBT16ToUInt16(aSprmSDxaLnn.pSprm);
+
+ SprmResult aSprmSLnnMin = pSep->HasSprm(pIds[7]);
+ if (aSprmSLnnMin.pSprm && aSprmSLnnMin.nRemainingData >= 1)
+ aNewSection.maSep.lnnMin = *aSprmSLnnMin.pSprm;
+
+ if (eVer <= ww::eWW7)
+ aNewSection.maSep.grpfIhdt = ReadBSprm(pSep, eVer <= ww::eWW2 ? 128 : 153, 0);
+ else if (mrReader.m_xHdFt)
+ {
+ aNewSection.maSep.grpfIhdt = WW8_HEADER_ODD | WW8_FOOTER_ODD
+ | WW8_HEADER_FIRST | WW8_FOOTER_FIRST;
+
+ // It is possible for a first page header to be provided
+ // for this section, but not actually shown in this section. In this
+ // case (aNewSection.maSep.grpfIhdt & WW8_HEADER_FIRST) will be nonzero
+ // but aNewSection.HasTitlePage() will be false.
+ // Likewise for first page footer.
+
+ if (mrReader.m_xWDop->fFacingPages)
+ aNewSection.maSep.grpfIhdt |= WW8_HEADER_EVEN | WW8_FOOTER_EVEN;
+
+ //See if we have a header or footer for each enabled possibility
+ //if we do not then we inherit the previous sections header/footer,
+ for (int nI = 0, nMask = 1; nI < 6; ++nI, nMask <<= 1)
+ {
+ if (aNewSection.maSep.grpfIhdt & nMask)
+ {
+ WW8_CP nStart, nLen;
+ mrReader.m_xHdFt->GetTextPosExact( static_cast< short >(nI + ( maSegments.size() + 1) * 6), nStart, nLen);
+ //No header or footer, inherit previous one, or set to zero
+ //if no previous one
+ if (!nLen)
+ {
+ if (
+ maSegments.empty() ||
+ !(maSegments.back().maSep.grpfIhdt & nMask)
+ )
+ {
+ aNewSection.maSep.grpfIhdt &= ~nMask;
+ }
+ }
+ }
+ }
+ }
+
+ SetLeftRight(aNewSection);
+ //END read section values
+
+ if (eVer >= ww::eWW8)
+ aNewSection.SetDirection();
+
+ mrReader.HandleLineNumbering(aNewSection);
+ maSegments.push_back(aNewSection);
+}
+
+void SwWW8ImplReader::CopyPageDescHdFt(const SwPageDesc* pOrgPageDesc,
+ SwPageDesc* pNewPageDesc, sal_uInt8 nCode )
+{
+ // copy odd header content section
+ if( nCode & WW8_HEADER_ODD )
+ {
+ m_rDoc.CopyHeader(pOrgPageDesc->GetMaster(),
+ pNewPageDesc->GetMaster() );
+ }
+ // copy odd footer content section
+ if( nCode & WW8_FOOTER_ODD )
+ {
+ m_rDoc.CopyFooter(pOrgPageDesc->GetMaster(),
+ pNewPageDesc->GetMaster());
+ }
+ // copy even header content section
+ if( nCode & WW8_HEADER_EVEN )
+ {
+ m_rDoc.CopyHeader(pOrgPageDesc->GetLeft(),
+ pNewPageDesc->GetLeft());
+ }
+ // copy even footer content section
+ if( nCode & WW8_FOOTER_EVEN )
+ {
+ m_rDoc.CopyFooter(pOrgPageDesc->GetLeft(),
+ pNewPageDesc->GetLeft());
+ }
+ // copy first page header content section
+ if( nCode & WW8_HEADER_FIRST )
+ {
+ m_rDoc.CopyHeader(pOrgPageDesc->GetFirstMaster(),
+ pNewPageDesc->GetFirstMaster());
+ }
+ // copy first page footer content section
+ if( nCode & WW8_FOOTER_FIRST )
+ {
+ m_rDoc.CopyFooter(pOrgPageDesc->GetFirstMaster(),
+ pNewPageDesc->GetFirstMaster());
+ }
+}
+
+// helper functions for graphics, Apos and tables
+
+// Read BoRder Control structure
+// nBrcVer should be set to the version of the BRC record being read (6, 8 or 9)
+// This will be converted to the latest format (9).
+static bool SetWW8_BRC(int nBrcVer, WW8_BRCVer9& rVar, const sal_uInt8* pS, size_t nLen)
+{
+
+ if( pS )
+ {
+ if (nBrcVer == 9 && nLen >= sizeof(WW8_BRCVer9))
+ rVar = *reinterpret_cast<const WW8_BRCVer9*>(pS);
+ else if (nBrcVer == 8 && nLen >= sizeof(WW8_BRC))
+ rVar = WW8_BRCVer9(*reinterpret_cast<const WW8_BRC*>(pS));
+ else if (nLen >= sizeof(WW8_BRCVer6)) // nBrcVer == 6
+ rVar = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<const WW8_BRCVer6*>(pS)));
+ }
+
+ return nullptr != pS;
+}
+
+static sal_uInt8 lcl_ReadBorders(bool bVer67, WW8_BRCVer9* brc, WW8PLCFx_Cp_FKP* pPap,
+ const WW8RStyle* pSty, const WW8PLCFx_SEPX* pSep)
+{
+
+//returns a sal_uInt8 filled with a bit for each position that had a sprm
+//setting that border
+
+ sal_uInt8 nBorder = 0;
+ if( pSep )
+ {
+ if( !bVer67 )
+ {
+ SprmResult a8Sprm[4];
+ if (pSep->Find4Sprms(
+ NS_sprm::SBrcTop80::val, NS_sprm::SBrcLeft80::val,
+ NS_sprm::SBrcBottom80::val, NS_sprm::SBrcRight80::val,
+ a8Sprm[0], a8Sprm[1], a8Sprm[2], a8Sprm[3]))
+ {
+ for( int i = 0; i < 4; ++i )
+ nBorder |= int(SetWW8_BRC(8, brc[i], a8Sprm[i].pSprm, a8Sprm[i].nRemainingData))<<i;
+ }
+
+ // Version 9 BRCs if present will override version 8
+ SprmResult a9Sprm[4];
+ if (pSep->Find4Sprms(
+ NS_sprm::SBrcTop::val, NS_sprm::SBrcLeft::val,
+ NS_sprm::SBrcBottom::val, NS_sprm::SBrcRight::val,
+ a9Sprm[0], a9Sprm[1], a9Sprm[2], a9Sprm[3]))
+ {
+ for( int i = 0; i < 4; ++i )
+ nBorder |= int(SetWW8_BRC(9, brc[i], a9Sprm[i].pSprm, a9Sprm[i].nRemainingData))<<i;
+ }
+ }
+ }
+ else
+ {
+
+ static const sal_uInt16 aVer67Ids[5] = {
+ NS_sprm::v6::sprmPBrcTop,
+ NS_sprm::v6::sprmPBrcLeft,
+ NS_sprm::v6::sprmPBrcBottom,
+ NS_sprm::v6::sprmPBrcRight,
+ NS_sprm::v6::sprmPBrcBetween
+ };
+
+ static const sal_uInt16 aVer8Ids[5] = {
+ NS_sprm::PBrcTop80::val,
+ NS_sprm::PBrcLeft80::val,
+ NS_sprm::PBrcBottom80::val,
+ NS_sprm::PBrcRight80::val,
+ NS_sprm::PBrcBetween80::val
+ };
+
+ static const sal_uInt16 aVer9Ids[5] = {
+ NS_sprm::PBrcTop::val,
+ NS_sprm::PBrcLeft::val,
+ NS_sprm::PBrcBottom::val,
+ NS_sprm::PBrcRight::val,
+ NS_sprm::PBrcBetween::val
+ };
+
+ if( pPap )
+ {
+ if (bVer67)
+ {
+ for( int i = 0; i < 5; ++i )
+ {
+ SprmResult aRes(pPap->HasSprm(aVer67Ids[i]));
+ nBorder |= int(SetWW8_BRC(6 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
+ }
+ }
+ else
+ {
+ for( int i = 0; i < 5; ++i )
+ {
+ SprmResult aRes(pPap->HasSprm(aVer8Ids[i]));
+ nBorder |= int(SetWW8_BRC(8 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
+ }
+ // Version 9 BRCs if present will override version 8
+ for( int i = 0; i < 5; ++i )
+ {
+ SprmResult aRes(pPap->HasSprm(aVer9Ids[i]));
+ nBorder |= int(SetWW8_BRC(9 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
+ }
+ }
+ }
+ else if( pSty )
+ {
+ if (bVer67)
+ {
+ for( int i = 0; i < 5; ++i )
+ {
+ SprmResult aRes(pSty->HasParaSprm(aVer67Ids[i]));
+ nBorder |= int(SetWW8_BRC(6 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
+ }
+ }
+ else
+ {
+ for( int i = 0; i < 5; ++i )
+ {
+ SprmResult aRes(pSty->HasParaSprm(aVer8Ids[i]));
+ nBorder |= int(SetWW8_BRC(8 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
+ }
+ // Version 9 BRCs if present will override version 8
+ for( int i = 0; i < 5; ++i )
+ {
+ SprmResult aRes(pSty->HasParaSprm(aVer9Ids[i]));
+ nBorder |= int(SetWW8_BRC(9 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i;
+ }
+ }
+ }
+ else {
+ OSL_ENSURE( pSty || pPap, "WW8PLCFx_Cp_FKP and WW8RStyle "
+ "and WW8PLCFx_SEPX is 0" );
+ }
+ }
+
+ return nBorder;
+}
+
+static void GetLineIndex(SvxBoxItem &rBox, short nLineThickness, short nSpace,
+ sal_uInt32 cv, short nIdx, SvxBoxItemLine nOOIndex, sal_uInt16 nWWIndex,
+ short *pSize)
+{
+ // LO cannot handle outset/inset (new in WW9 BRC) so fall back same as WW8
+ if ( nIdx == 0x1A || nIdx == 0x1B )
+ {
+ nIdx = (nIdx == 0x1A) ? 0x12 : 0x11;
+ cv = 0xc0c0c0;
+ }
+
+ SvxBorderLineStyle const eStyle(
+ ::editeng::ConvertBorderStyleFromWord(nIdx));
+
+ ::editeng::SvxBorderLine aLine;
+ aLine.SetBorderLineStyle( eStyle );
+ double const fConverted( (SvxBorderLineStyle::NONE == eStyle) ? 0.0 :
+ ::editeng::ConvertBorderWidthFromWord(eStyle, nLineThickness, nIdx));
+ aLine.SetWidth(fConverted);
+
+ //No AUTO for borders as yet, so if AUTO, use BLACK
+ Color col = (cv==0xff000000) ? COL_BLACK : msfilter::util::BGRToRGB(cv);
+
+ aLine.SetColor(col);
+
+ if (pSize)
+ pSize[nWWIndex] = fConverted + nSpace;
+
+ rBox.SetLine(&aLine, nOOIndex);
+ rBox.SetDistance(nSpace, nOOIndex);
+
+}
+
+static void Set1Border(SvxBoxItem &rBox, const WW8_BRCVer9& rBor, SvxBoxItemLine nOOIndex,
+ sal_uInt16 nWWIndex, short *pSize, const bool bIgnoreSpace)
+{
+ short nSpace;
+ short nLineThickness = rBor.DetermineBorderProperties(&nSpace);
+
+ GetLineIndex(rBox, nLineThickness, bIgnoreSpace ? 0 : nSpace,
+ rBor.cv(), rBor.brcType(), nOOIndex, nWWIndex, pSize );
+
+}
+
+static bool lcl_IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn = false)
+{
+ return pbrc[WW8_TOP ].brcType() || // brcType != 0
+ pbrc[WW8_LEFT ].brcType() ||
+ pbrc[WW8_BOT ].brcType() ||
+ pbrc[WW8_RIGHT].brcType() ||
+ (bChkBtwn && pbrc[WW8_BETW ].brcType());
+}
+
+bool SwWW8ImplReader::IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn)
+{
+ return lcl_IsBorder(pbrc, bChkBtwn);
+}
+
+bool SwWW8ImplReader::SetBorder(SvxBoxItem& rBox, const WW8_BRCVer9* pbrc,
+ short *pSizeArray, sal_uInt8 nSetBorders)
+{
+ bool bChange = false;
+ static const std::pair<sal_uInt16, SvxBoxItemLine> aIdArr[] =
+ {
+ { WW8_TOP, SvxBoxItemLine::TOP },
+ { WW8_LEFT, SvxBoxItemLine::LEFT },
+ { WW8_RIGHT, SvxBoxItemLine::RIGHT },
+ { WW8_BOT, SvxBoxItemLine::BOTTOM },
+ { WW8_BETW, SvxBoxItemLine::BOTTOM }
+ };
+
+ for( int i = 0; i < 4; ++i )
+ {
+ // filter out the invalid borders
+ const WW8_BRCVer9& rB = pbrc[ aIdArr[ i ].first ];
+ if( !rB.isNil() && rB.brcType() )
+ {
+ Set1Border(rBox, rB, aIdArr[i].second, aIdArr[i].first, pSizeArray, false);
+ bChange = true;
+ }
+ else if ( nSetBorders & (1 << aIdArr[i].first) )
+ {
+ /*
+ ##826##, ##653##
+
+ If a style has borders set,and the para attributes attempt remove
+ the borders, then this is perfectly acceptable, so we shouldn't
+ ignore this blank entry
+
+ nSetBorders has a bit set for each location that a sprm set a
+ border, so with a sprm set, but no border, then disable the
+ appropriate border
+ */
+ rBox.SetLine( nullptr, aIdArr[ i ].second );
+ }
+ }
+ return bChange;
+}
+
+bool SwWW8ImplReader::SetShadow(SvxShadowItem& rShadow, const short *pSizeArray,
+ const WW8_BRCVer9& aRightBrc)
+{
+ bool bRet = aRightBrc.fShadow() && pSizeArray && pSizeArray[WW8_RIGHT];
+ if (bRet)
+ {
+ rShadow.SetColor(COL_BLACK);
+ //i120718
+ short nVal = aRightBrc.DetermineBorderProperties();
+ //End
+ if (nVal < 0x10)
+ nVal = 0x10;
+ rShadow.SetWidth(nVal);
+ rShadow.SetLocation(SvxShadowLocation::BottomRight);
+ bRet = true;
+ }
+ return bRet;
+}
+
+void SwWW8ImplReader::GetBorderDistance(const WW8_BRCVer9* pbrc,
+ tools::Rectangle& rInnerDist)
+{
+ rInnerDist = tools::Rectangle( pbrc[ 1 ].dptSpace() * 20,
+ pbrc[ 0 ].dptSpace() * 20,
+ pbrc[ 3 ].dptSpace() * 20,
+ pbrc[ 2 ].dptSpace() * 20 );
+}
+
+bool SwWW8ImplReader::SetFlyBordersShadow(SfxItemSet& rFlySet,
+ const WW8_BRCVer9 *pbrc, short *pSizeArray)
+{
+ bool bShadowed = false;
+ if (IsBorder(pbrc))
+ {
+ SvxBoxItem aBox( RES_BOX );
+ SetBorder(aBox, pbrc, pSizeArray);
+
+ rFlySet.Put( aBox );
+
+ // fShadow
+ SvxShadowItem aShadow( RES_SHADOW );
+ if( SetShadow( aShadow, pSizeArray, pbrc[WW8_RIGHT] ))
+ {
+ bShadowed = true;
+ rFlySet.Put( aShadow );
+ }
+ }
+ return bShadowed;
+}
+
+// APOs
+
+ // for computing the minimal FrameSize
+#define MAX_BORDER_SIZE 210 // max. size of border
+#define MAX_EMPTY_BORDER 10 // for off-by-one errors, at least 1
+
+static void FlySecur1(short& rSize, const bool bBorder)
+{
+ short nMin = MINFLY +
+ (bBorder ? MAX_BORDER_SIZE : MAX_EMPTY_BORDER);
+
+ if ( rSize < nMin )
+ rSize = nMin;
+}
+
+static bool SetValSprm( sal_Int16* pVar, WW8PLCFx_Cp_FKP* pPap, sal_uInt16 nId )
+{
+ SprmResult aS = pPap->HasSprm(nId);
+ if (aS.pSprm && aS.nRemainingData >= 2)
+ *pVar = static_cast<sal_Int16>(SVBT16ToUInt16(aS.pSprm));
+ return aS.pSprm != nullptr;
+}
+
+static bool SetValSprm( sal_Int16* pVar, const WW8RStyle* pStyle, sal_uInt16 nId )
+{
+ SprmResult aS = pStyle->HasParaSprm(nId);
+ if (aS.pSprm && aS.nRemainingData >= 2)
+ *pVar = static_cast<sal_Int16>(SVBT16ToUInt16(aS.pSprm));
+ return aS.pSprm != nullptr;
+}
+
+/*
+#i1930 revealed that sprm 0x360D (sprmTPc) as used in tables can affect the frame
+around the table. Its full structure is not fully understood as yet.
+*/
+void WW8FlyPara::ApplyTabPos(const WW8_TablePos *pTabPos)
+{
+ if (pTabPos)
+ {
+ nTDxaAbs = pTabPos->nTDxaAbs;
+ nTDyaAbs = pTabPos->nTDyaAbs;
+ nTPc = pTabPos->nTPc;
+ nLeftMargin = pTabPos->nLeftMargin;
+ nRightMargin = pTabPos->nRightMargin;
+ nUpperMargin = pTabPos->nUpperMargin;
+ nLowerMargin = pTabPos->nLowerMargin;
+ nPWr = pTabPos->nPWr;
+ }
+}
+
+WW8FlyPara::WW8FlyPara(bool bIsVer67, const WW8FlyPara* pSrc /* = 0 */)
+{
+ if ( pSrc )
+ memcpy( this, pSrc, sizeof( WW8FlyPara ) ); // Copy-Ctor
+ else
+ {
+ nTDxaAbs = 0;
+ nTDyaAbs = 0;
+ nSp45 = 0;
+ nSp28 = 0;
+ nLeftMargin = 0;
+ nRightMargin = 0;
+ nUpperMargin = 0;
+ nLowerMargin = 0;
+ nTPc = 0;
+ nPWr = 2; // Default: wrapping
+ bBorderLines = false;
+ bGrafApo = false;
+ mbVertSet = false;
+ }
+ bVer67 = bIsVer67;
+}
+
+bool WW8FlyPara::operator==(const WW8FlyPara& rSrc) const
+{
+ /*
+ Compare the parts that word seems to compare for equivalence.
+ Interestingly being autoheight or absolute height (the & 0x7fff) doesn't
+ matter to word
+ */
+ return
+ (
+ (nTDxaAbs == rSrc.nTDxaAbs) &&
+ (nTDyaAbs == rSrc.nTDyaAbs) &&
+ ((nSp45 & 0x7fff) == (rSrc.nSp45 & 0x7fff)) &&
+ (nSp28 == rSrc.nSp28) &&
+ (nLeftMargin == rSrc.nLeftMargin) &&
+ (nRightMargin == rSrc.nRightMargin) &&
+ (nUpperMargin == rSrc.nUpperMargin) &&
+ (nLowerMargin == rSrc.nLowerMargin) &&
+ (nTPc == rSrc.nTPc) &&
+ (nPWr == rSrc.nPWr)
+ );
+}
+
+// Read for normal text
+void WW8FlyPara::Read(sal_uInt8 nOrigSprmTPc, WW8PLCFx_Cp_FKP* pPap)
+{
+ if( bVer67 )
+ {
+ SetValSprm( &nTDxaAbs, pPap, 26 ); // X-position //sprmPDxaAbs
+ //set in me or in parent style
+ mbVertSet |= SetValSprm( &nTDyaAbs, pPap, 27 ); // Y-position //sprmPDyaAbs
+ SetValSprm( &nSp45, pPap, 45 ); // height //sprmPWHeightAbs
+ SetValSprm( &nSp28, pPap, 28 ); // width //sprmPDxaWidth
+ SetValSprm( &nLeftMargin, pPap, 49 ); // L-border //sprmPDxaFromText
+ SetValSprm( &nRightMargin, pPap, 49 ); // R-border //sprmPDxaFromText
+ SetValSprm( &nUpperMargin, pPap, 48 ); // U-border //sprmPDyaFromText
+ SetValSprm( &nLowerMargin, pPap, 48 ); // D-border //sprmPDyaFromText
+
+ SprmResult aS = pPap->HasSprm(NS_sprm::v6::sprmPWr);
+ if (aS.pSprm && aS.nRemainingData >= 1)
+ nPWr = *aS.pSprm;
+ }
+ else
+ {
+ SetValSprm( &nTDxaAbs, pPap, NS_sprm::PDxaAbs::val ); // X-position
+ //set in me or in parent style
+ mbVertSet |= SetValSprm( &nTDyaAbs, pPap, NS_sprm::PDyaAbs::val ); // Y-position
+ SetValSprm( &nSp45, pPap, NS_sprm::PWHeightAbs::val ); // height
+ SetValSprm( &nSp28, pPap, NS_sprm::PDxaWidth::val ); // width
+ SetValSprm( &nLeftMargin, pPap, NS_sprm::PDxaFromText::val ); // L-border
+ SetValSprm( &nRightMargin, pPap, NS_sprm::PDxaFromText::val ); // R-border
+ SetValSprm( &nUpperMargin, pPap, NS_sprm::PDyaFromText::val ); // U-border
+ SetValSprm( &nLowerMargin, pPap, NS_sprm::PDyaFromText::val ); // D-border
+
+ SprmResult aS = pPap->HasSprm(NS_sprm::PWr::val); // wrapping
+ if (aS.pSprm && aS.nRemainingData >= 1)
+ nPWr = *aS.pSprm;
+ }
+
+ if( ::lcl_ReadBorders( bVer67, brc, pPap )) // borders
+ bBorderLines = ::lcl_IsBorder( brc );
+
+ /*
+ #i8798#
+ Appears that with no dyaAbs set then the actual vert anchoring set is
+ ignored and we remain relative to text, so if that is the case we are 0
+ from para anchor, so we update the frame to have explicitly this type of
+ anchoring
+ */
+ if (!mbVertSet)
+ nTPc = (nOrigSprmTPc & 0xCF) | 0x20;
+ else
+ nTPc = nOrigSprmTPc;
+}
+
+void WW8FlyPara::ReadFull(sal_uInt8 nOrigSprmTPc, SwWW8ImplReader* pIo)
+{
+ std::shared_ptr<WW8PLCFMan> xPlcxMan = pIo->m_xPlcxMan;
+ WW8PLCFx_Cp_FKP* pPap = xPlcxMan->GetPapPLCF();
+
+ Read(nOrigSprmTPc, pPap); // read Apo parameter
+
+ do{ // block for quick exit
+ if( nSp45 != 0 /* || nSp28 != 0 */ )
+ break; // bGrafApo only automatic for height
+ if( pIo->m_xWwFib->m_fComplex )
+ break; // (*pPap)++ does not work for FastSave
+ // -> for FastSave, no test for graphics APO
+ SvStream* pIoStrm = pIo->m_pStrm;
+ sal_uInt64 nPos = pIoStrm->Tell();
+ WW8PLCFxSave1 aSave;
+ xPlcxMan->GetPap()->Save( aSave );
+ bGrafApo = false;
+
+ do{ // block for quick exit
+ sal_uInt8 nText[2];
+
+ if (!checkRead(*pIoStrm, nText, 2)) // read text
+ break;
+
+ if( nText[0] != 0x01 || nText[1] != 0x0d )// only graphics + CR?
+ break; // no
+
+ pPap->advance(); // next line
+
+ // in APO ?
+ //sprmPPc
+ SprmResult aS = pPap->HasSprm( bVer67 ? NS_sprm::v6::sprmPPc : NS_sprm::PPc::val);
+
+ // no -> graphics Apo
+ if (!aS.pSprm || aS.nRemainingData < 1)
+ {
+ bGrafApo = true;
+ break; // end of APO
+ }
+
+ ww::WordVersion eVer = pIo->GetFib().GetFIBVersion();
+ WW8FlyPara *pNowStyleApo=nullptr;
+ sal_uInt16 nColl = pPap->GetIstd();
+
+ o3tl::sorted_vector<sal_uInt16> aSeenStyles;
+ ww::sti eSti = eVer < ww::eWW6 ? ww::GetCanonicalStiFromStc(nColl) : static_cast<ww::sti>(nColl);
+ while (eSti != ww::stiNil && static_cast<size_t>(nColl) < pIo->m_vColl.size() && nullptr == (pNowStyleApo = pIo->m_vColl[nColl].m_xWWFly.get()))
+ {
+ aSeenStyles.insert(nColl);
+
+ nColl = pIo->m_vColl[nColl].m_nBase;
+
+ if (aSeenStyles.find(nColl) != aSeenStyles.end())
+ {
+ SAL_WARN("sw.ww8", "loop in style chain");
+ break;
+ }
+
+ eSti = eVer < ww::eWW6 ? ww::GetCanonicalStiFromStc(nColl) : static_cast<ww::sti>(nColl);
+ }
+
+ WW8FlyPara aF(bVer67, pNowStyleApo);
+ // new FlaPara for comparison
+ aF.Read(*aS.pSprm, pPap); // WWPara for new Para
+ if( !( aF == *this ) ) // same APO? (or a new one?)
+ bGrafApo = true; // no -> 1-line APO
+ // -> graphics APO
+ }
+ while( false ); // block for quick exit
+
+ xPlcxMan->GetPap()->Restore( aSave );
+ pIoStrm->Seek( nPos );
+ }while( false ); // block for quick exit
+}
+
+// read for Apo definitions in Styledefs
+void WW8FlyPara::Read(sal_uInt8 nOrigSprmTPc, WW8RStyle const * pStyle)
+{
+ if (bVer67)
+ {
+ SetValSprm( &nTDxaAbs, pStyle, NS_sprm::v6::sprmPDxaAbs ); // X-position
+ //set in me or in parent style
+ mbVertSet |= SetValSprm(&nTDyaAbs, pStyle, NS_sprm::v6::sprmPDyaAbs); // Y-position
+ SetValSprm( &nSp45, pStyle, NS_sprm::v6::sprmPWHeightAbs ); // height
+ SetValSprm( &nSp28, pStyle, NS_sprm::v6::sprmPDxaWidth ); // width
+ SetValSprm( &nLeftMargin, pStyle, NS_sprm::v6::sprmPDxaFromText ); // L-border
+ SetValSprm( &nRightMargin, pStyle, NS_sprm::v6::sprmPDxaFromText ); // R-border
+ SetValSprm( &nUpperMargin, pStyle, NS_sprm::v6::sprmPDyaFromText ); // U-border
+ SetValSprm( &nLowerMargin, pStyle, NS_sprm::v6::sprmPDyaFromText ); // D-border
+
+ SprmResult aS = pStyle->HasParaSprm( NS_sprm::v6::sprmPWr ); // wrapping
+ if (aS.pSprm && aS.nRemainingData >= 1)
+ nPWr = *aS.pSprm;
+ }
+ else
+ {
+ SetValSprm( &nTDxaAbs, pStyle, NS_sprm::PDxaAbs::val ); // X-position
+ //set in me or in parent style
+ mbVertSet |= SetValSprm(&nTDyaAbs, pStyle, NS_sprm::PDyaAbs::val); // Y-position
+ SetValSprm( &nSp45, pStyle, NS_sprm::PWHeightAbs::val ); // height
+ SetValSprm( &nSp28, pStyle, NS_sprm::PDxaWidth::val ); // width
+ SetValSprm( &nLeftMargin, pStyle, NS_sprm::PDxaFromText::val ); // L-border
+ SetValSprm( &nRightMargin, pStyle, NS_sprm::PDxaFromText::val ); // R-border
+ SetValSprm( &nUpperMargin, pStyle, NS_sprm::PDyaFromText::val ); // U-border
+ SetValSprm( &nLowerMargin, pStyle, NS_sprm::PDyaFromText::val ); // D-border
+
+ SprmResult aS = pStyle->HasParaSprm( NS_sprm::PWr::val ); // wrapping
+ if (aS.pSprm && aS.nRemainingData >= 1)
+ nPWr = *aS.pSprm;
+ }
+
+ if (::lcl_ReadBorders(bVer67, brc, nullptr, pStyle)) // border
+ bBorderLines = ::lcl_IsBorder(brc);
+
+ /*
+ #i8798#
+ Appears that with no dyaAbs set then the actual vert anchoring set is
+ ignored and we remain relative to text, so if that is the case we are 0
+ from para anchor, so we update the frame to have explicitly this type of
+ anchoring
+ */
+ if (!mbVertSet)
+ nTPc = (nOrigSprmTPc & 0xCF) | 0x20;
+ else
+ nTPc = nOrigSprmTPc;
+}
+
+bool WW8FlyPara::IsEmpty() const
+{
+ WW8FlyPara aEmpty(bVer67);
+ /*
+ wr of 0 like 2 appears to me to be equivalent for checking here. See
+ #107103# if wrong, so given that the empty is 2, if we are 0 then set
+ empty to 0 to make 0 equiv to 2 for empty checking
+ */
+ OSL_ENSURE(aEmpty.nPWr == 2, "this is not what we expect for nPWr");
+ if (this->nPWr == 0)
+ aEmpty.nPWr = 0;
+ return aEmpty == *this;
+}
+
+// #i18732# - changes made on behalf of CMC
+WW8SwFlyPara::WW8SwFlyPara( SwPaM& rPaM,
+ SwWW8ImplReader& rIo,
+ WW8FlyPara& rWW,
+ const sal_uInt32 nWWPgTop,
+ const sal_uInt32 nPgWidth,
+ const sal_Int32 nIniFlyDx,
+ const sal_Int32 nIniFlyDy ):
+nXPos(0),
+nYPos(0),
+nLeftMargin(rWW.nLeftMargin),
+nRightMargin(rWW.nRightMargin),
+nUpperMargin(rWW.nUpperMargin),
+nLowerMargin(rWW.nLowerMargin),
+nWidth(rWW.nSp28),
+nHeight(rWW.nSp45),
+nNetWidth(rWW.nSp28),
+eHeightFix(SwFrameSize::Fixed),
+eHRel(text::RelOrientation::PAGE_FRAME),
+eVRel(text::RelOrientation::FRAME),
+eVAlign(text::VertOrientation::NONE),
+eHAlign(text::HoriOrientation::NONE),
+nXBind(( rWW.nTPc & 0xc0 ) >> 6),
+nYBind(( rWW.nTPc & 0x30 ) >> 4),
+nNewNetWidth(MINFLY),
+nLineSpace(0),
+bAutoWidth(false),
+bTogglePos(false)
+{
+ switch(rWW.nPWr)
+ {
+ case 0: // ST_Wrap: auto
+ eSurround = css::text::WrapTextMode_DYNAMIC;
+ break;
+ case 1: // ST_Wrap: notBeside
+ case 3: // ST_Wrap: none
+ eSurround = css::text::WrapTextMode_NONE;
+ break;
+ case 2: // ST_Wrap: around
+ case 4: // ST_Wrap: tight
+ eSurround = css::text::WrapTextMode_PARALLEL;
+ break;
+ case 5: // St_Wrap: through
+ eSurround = css::text::WrapTextMode_THROUGH;
+ break;
+ default:
+ eSurround = css::text::WrapTextMode_DYNAMIC;
+ }
+
+ /*
+ #95905#, #83307# seems to have gone away now, so re-enable parallel
+ wrapping support for frames in headers/footers. I don't know if we truly
+ have an explicitly specified behaviour for these circumstances.
+ */
+
+ if( nHeight & 0x8000 )
+ {
+ nHeight &= 0x7fff;
+ eHeightFix = SwFrameSize::Minimum;
+ }
+
+ if( nHeight <= MINFLY )
+ { // no data, or bad data
+ eHeightFix = SwFrameSize::Minimum;
+ nHeight = MINFLY;
+ }
+
+ if( nWidth <= 10 ) // auto width
+ {
+ bAutoWidth = true;
+ nWidth = nNetWidth =
+ msword_cast<sal_Int16>(nPgWidth ? nPgWidth : 2268); // 4 cm
+ }
+ if( nWidth <= MINFLY )
+ nWidth = nNetWidth = MINFLY; // minimum width
+
+ /*
+ See issue #i9178# for the 9 anchoring options, and make sure they stay
+ working if you modify the anchoring logic here.
+ */
+
+ // If the Fly is aligned left, right, up, or down,
+ // the outer text distance will be ignored, because otherwise
+ // the Fly will end up in the wrong position.
+ // The only problem is with inside/outside.
+
+ //#i53725# - absolute positioned objects have to be
+ // anchored at-paragraph to assure its correct anchor position.
+ rIo.m_oLastAnchorPos.emplace(*rPaM.GetPoint());
+
+ switch (nYBind)
+ {
+ case 0: //relative to margin
+ eVRel = text::RelOrientation::PAGE_PRINT_AREA;
+ break;
+ case 1: //relative to page
+ eVRel = text::RelOrientation::PAGE_FRAME;
+ break;
+ default: //relative to text
+ // put in initialization part eVRel = text::RelOrientation::FRAME;
+ break;
+ }
+
+// #i18732#
+ switch( rWW.nTDyaAbs ) // particular Y-positions ?
+ {
+ case 0: // inline
+ // Specifies that the parent object shall be vertically aligned in line
+ // with the surrounding text (i.e. shall not allow any text wrapping around it)
+ eVRel = text::RelOrientation::FRAME;
+ break;
+ case -4:
+ eVAlign = text::VertOrientation::TOP;
+ if (nYBind < 2)
+ nUpperMargin = 0;
+ break; // up
+ case -8:
+ eVAlign = text::VertOrientation::CENTER;
+ break; // centered
+ case -12:
+ eVAlign = text::VertOrientation::BOTTOM;
+ if (nYBind < 2)
+ nLowerMargin = 0;
+ break; // down
+ default:
+ nYPos = rWW.nTDyaAbs + static_cast<short>(nIniFlyDy);
+ break; // corrections from ini file
+ }
+
+ switch( rWW.nTDxaAbs ) // particular X-positions ?
+ {
+ case 0:
+ eHAlign = text::HoriOrientation::LEFT;
+ nLeftMargin = 0;
+ break; // left
+ case -4:
+ eHAlign = text::HoriOrientation::CENTER;
+ break; // centered
+ case -8:
+ eHAlign = text::HoriOrientation::RIGHT;
+ nRightMargin = 0;
+ break; // right
+ case -12:
+ eHAlign = text::HoriOrientation::LEFT;
+ bTogglePos = true;
+ break; // inside
+ case -16:
+ eHAlign = text::HoriOrientation::RIGHT;
+ bTogglePos = true;
+ break; // outside
+ default:
+ nXPos = rWW.nTDxaAbs + static_cast<short>(nIniFlyDx);
+ break; // corrections from ini file
+ }
+
+// #i18732#
+ switch (nXBind) // X - binding -> transform coordinates
+ {
+ case 0: //relative to column
+ eHRel = text::RelOrientation::FRAME;
+ break;
+ case 1: //relative to margin
+ eHRel = text::RelOrientation::PAGE_PRINT_AREA;
+ break;
+ default: //relative to page
+ // put in initialization part eHRel= text::RelOrientation::PAGE_FRAME;
+ break;
+ }
+
+ // #i36649# - adjustments for certain horizontal alignments
+ // Note: These special adjustments found by an investigation of documents
+ // containing frames with different left/right border distances and
+ // distances to text. The outcome is somehow strange.
+ // Note: These adjustments causes wrong horizontal positions for frames,
+ // which are aligned inside|outside to page|margin on even pages,
+ // the left and right border distances are different.
+ // no adjustments possible, if frame has automatic width.
+ // determine left border distance
+ sal_Int16 nLeBorderMgn( 0 );
+ if ( !bAutoWidth )
+ {
+ WW8_BRCVer9 &rBrc = rWW.brc[WW8_LEFT];
+ sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nLeBorderMgn);
+ nLeBorderMgn = nLeBorderMgn + nTemp;
+ }
+ // determine right border distance
+ sal_Int16 nRiBorderMgn( 0 );
+ if ( !bAutoWidth )
+ {
+ WW8_BRCVer9 &rBrc = rWW.brc[WW8_RIGHT];
+ sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nRiBorderMgn);
+ nRiBorderMgn = nRiBorderMgn + nTemp;
+ }
+ if ( !bAutoWidth && eHAlign == text::HoriOrientation::LEFT && eHRel == text::RelOrientation::PAGE_FRAME )
+ {
+ // convert 'left to page' to
+ // 'from left -<width>-<2*left border distance>-<right wrap distance>
+ // to page text area'
+ eHAlign = text::HoriOrientation::NONE;
+ eHRel = text::RelOrientation::PAGE_PRINT_AREA;
+ nXPos = -nWidth - (2*nLeBorderMgn) - rWW.nRightMargin;
+ // re-set left wrap distance
+ nLeftMargin = rWW.nLeftMargin;
+ }
+ else if ( !bAutoWidth && eHAlign == text::HoriOrientation::RIGHT && eHRel == text::RelOrientation::PAGE_FRAME )
+ {
+ // convert 'right to page' to
+ // 'from left <right border distance-left border distance>+<left wrap distance>
+ // to right page border'
+ eHAlign = text::HoriOrientation::NONE;
+ eHRel = text::RelOrientation::PAGE_RIGHT;
+ nXPos = ( nRiBorderMgn - nLeBorderMgn ) + rWW.nLeftMargin;
+ // re-set right wrap distance
+ nRightMargin = rWW.nRightMargin;
+ }
+ else if ( !bAutoWidth && eHAlign == text::HoriOrientation::LEFT && eHRel == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ // convert 'left to margin' to
+ // 'from left -<left border distance> to page text area'
+ eHAlign = text::HoriOrientation::NONE;
+ eHRel = text::RelOrientation::PAGE_PRINT_AREA;
+ nXPos = -nLeBorderMgn;
+ // re-set left wrap distance
+ nLeftMargin = rWW.nLeftMargin;
+ }
+ else if ( !bAutoWidth && eHAlign == text::HoriOrientation::RIGHT && eHRel == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ // convert 'right to margin' to
+ // 'from left -<width>-<left border distance> to right page border'
+ eHAlign = text::HoriOrientation::NONE;
+ eHRel = text::RelOrientation::PAGE_RIGHT;
+ nXPos = -nWidth - nLeBorderMgn;
+ // re-set right wrap distance
+ nRightMargin = rWW.nRightMargin;
+ }
+ else if (rWW.bBorderLines)
+ {
+ /*
+ #i582#
+ Word has a curious bug where the offset stored do not take into
+ account the internal distance from the corner both
+ */
+ WW8_BRCVer9 &rBrc = rWW.brc[WW8_LEFT];
+ sal_Int16 nLeLMgn = 0;
+ sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nLeLMgn);
+ nLeLMgn = nLeLMgn + nTemp;
+
+ if (nLeLMgn)
+ {
+ if (eHAlign == text::HoriOrientation::LEFT)
+ eHAlign = text::HoriOrientation::NONE;
+ nXPos = nXPos - nLeLMgn;
+ }
+ }
+
+ // adjustments for certain vertical alignments
+ if ( eVAlign == text::VertOrientation::NONE && eVRel == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ // convert "<X> from top page text area" to
+ // "<X + page top margin> from page"
+ eVRel = text::RelOrientation::PAGE_FRAME;
+ nYPos = static_cast< sal_Int16 >( nYPos + nWWPgTop );
+ }
+
+ FlySecur1( nWidth, rWW.bBorderLines ); // Do the borders match ?
+ FlySecur1( nHeight, rWW.bBorderLines );
+
+}
+
+// If a Fly in WW has automatic width, this has to be simulated
+// by modifying the Fly width (fixed in SW) afterwards.
+// This can increase or decrease the Fly width, because the default value
+// is set without knowledge of the contents.
+void WW8SwFlyPara::BoxUpWidth( tools::Long nInWidth )
+{
+ if( bAutoWidth && nInWidth > nNewNetWidth )
+ nNewNetWidth = nInWidth;
+}
+
+SwFlyFrameFormat* WW8SwFlyPara::GetFlyFormat() const
+{
+ if (!m_xFlyFormat)
+ return nullptr;
+ return static_cast<SwFlyFrameFormat*>(m_xFlyFormat->GetFormat());
+}
+
+void WW8SwFlyPara::SetFlyFormat(SwFlyFrameFormat* pNewFlyFormat)
+{
+ if (pNewFlyFormat)
+ m_xFlyFormat.reset(new FrameDeleteWatch(pNewFlyFormat));
+ else
+ m_xFlyFormat.reset();
+}
+
+// The class WW8FlySet is derived from SfxItemSetFixed and does not
+// provide more, but is easier to handle for me.
+// WW8FlySet-ctor for Apos and graphics Apos
+WW8FlySet::WW8FlySet(SwWW8ImplReader& rReader, const WW8FlyPara* pFW,
+ const WW8SwFlyPara* pFS, bool bGraf)
+ : SfxItemSetFixed(rReader.m_rDoc.GetAttrPool())
+{
+ Reader::ResetFrameFormatAttrs(*this); // remove distance/border
+ // position
+ Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
+
+/*Below can all go when we have from left in rtl mode*/
+ SwTwips nXPos = pFS->nXPos;
+ sal_Int16 eHRel = pFS->eHRel;
+ rReader.MiserableRTLGraphicsHack(nXPos, pFS->nWidth, pFS->eHAlign, eHRel);
+/*Above can all go when we have from left in rtl mode*/
+ Put( SwFormatHoriOrient(nXPos, pFS->eHAlign, pFS->eHRel, pFS->bTogglePos ));
+ Put( SwFormatVertOrient( pFS->nYPos, pFS->eVAlign, pFS->eVRel ) );
+
+ if (pFS->nLeftMargin || pFS->nRightMargin) // set borders
+ Put(SvxLRSpaceItem(pFS->nLeftMargin, pFS->nRightMargin, 0, RES_LR_SPACE));
+
+ if (pFS->nUpperMargin || pFS->nLowerMargin)
+ Put(SvxULSpaceItem(pFS->nUpperMargin, pFS->nLowerMargin, RES_UL_SPACE));
+
+ //we no longer need to hack around the header/footer problems
+ SwFormatSurround aSurround(pFS->eSurround);
+ if ( pFS->eSurround == css::text::WrapTextMode_DYNAMIC )
+ aSurround.SetAnchorOnly( true );
+ Put( aSurround );
+
+ short aSizeArray[5]={0};
+ SwWW8ImplReader::SetFlyBordersShadow(*this,pFW->brc,&aSizeArray[0]);
+
+ // the 5th parameter is always 0, thus we lose nothing due to the cast
+
+ // #i27767#
+ // #i35017# - constant name has changed
+ Put( SwFormatWrapInfluenceOnObjPos(
+ text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ) );
+
+ if( bGraf )
+ return;
+
+ Put( SwFormatAnchor(WW8SwFlyPara::eAnchor) );
+ // adjust size
+
+ //Ordinarily with frames, the border width and spacing is
+ //placed outside the frame, making it larger. With these
+ //types of frames, the left right thickness and space makes
+ //it wider, but the top bottom spacing and border thickness
+ //is placed inside.
+ Put( SwFormatFrameSize( pFS->eHeightFix, pFS->nWidth +
+ aSizeArray[WW8_LEFT] + aSizeArray[WW8_RIGHT],
+ pFS->nHeight));
+}
+
+// WW8FlySet-ctor for character bound graphics
+WW8FlySet::WW8FlySet( SwWW8ImplReader& rReader, const SwPaM* pPaM,
+ const WW8_PIC& rPic, tools::Long nWidth, tools::Long nHeight )
+ : SfxItemSetFixed<RES_FRMATR_BEGIN,RES_FRMATR_END-1>(rReader.m_rDoc.GetAttrPool())
+{
+ Init(rReader, pPaM);
+
+ Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
+
+ short aSizeArray[5]={0};
+ /*
+ If we have set borders then in word the graphic is displaced from the left
+ and top the width of the borders of those sides, and then the shadow
+ itself is drawn to the bottom and right of the displaced graphic. In word
+ the total size is that of the graphic plus the borders, plus the total
+ shadow around all edges, for this translation the top and left shadow
+ region is translated spacing around the graphic to those sides, and the
+ bottom and right shadow size is added to the graphic size.
+ */
+ WW8_BRCVer9 brcVer9[4];
+ for (int i = 0; i < 4; i++)
+ brcVer9[i] = WW8_BRCVer9(rPic.rgbrc[i]);
+ if (SwWW8ImplReader::SetFlyBordersShadow( *this, brcVer9, &aSizeArray[0]))
+ {
+ Put(SvxLRSpaceItem( aSizeArray[WW8_LEFT], 0, 0, RES_LR_SPACE ) );
+ Put(SvxULSpaceItem( aSizeArray[WW8_TOP], 0, RES_UL_SPACE ));
+ aSizeArray[WW8_RIGHT]*=2;
+ aSizeArray[WW8_BOT]*=2;
+ }
+
+ Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth+aSizeArray[WW8_LEFT]+
+ aSizeArray[WW8_RIGHT], nHeight+aSizeArray[WW8_TOP]
+ + aSizeArray[WW8_BOT]) );
+}
+
+void WW8FlySet::Init(const SwWW8ImplReader& rReader, const SwPaM* pPaM)
+{
+ Reader::ResetFrameFormatAttrs(*this); // remove distance/borders
+
+ Put(SvxLRSpaceItem(RES_LR_SPACE)); //inline writer ole2 objects start with 0.2cm l/r
+ SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR);
+
+ aAnchor.SetAnchor(pPaM->GetPoint());
+ Put(aAnchor);
+
+ //The horizontal default is on the baseline, the vertical is centered
+ //around the character center it appears
+ if (rReader.m_aSectionManager.CurrentSectionIsVertical())
+ Put(SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER,text::RelOrientation::CHAR));
+ else
+ Put(SwFormatVertOrient(0, text::VertOrientation::TOP, text::RelOrientation::FRAME));
+}
+
+WW8DupProperties::WW8DupProperties(SwDoc &rDoc, SwWW8FltControlStack *pStack)
+ : m_pCtrlStck(pStack),
+ m_aChrSet(rDoc.GetAttrPool()),
+ m_aParSet(rDoc.GetAttrPool())
+{
+ //Close any open character properties and duplicate them inside the
+ //first table cell
+ size_t nCnt = m_pCtrlStck->size();
+ for (size_t i=0; i < nCnt; ++i)
+ {
+ const SwFltStackEntry& rEntry = (*m_pCtrlStck)[ i ];
+ if (rEntry.m_bOpen)
+ {
+ if (isCHRATR(rEntry.m_pAttr->Which()))
+ {
+ m_aChrSet.Put( *rEntry.m_pAttr );
+
+ }
+ else if (isPARATR(rEntry.m_pAttr->Which()))
+ {
+ m_aParSet.Put( *rEntry.m_pAttr );
+ }
+ }
+ }
+}
+
+void WW8DupProperties::Insert(const SwPosition &rPos)
+{
+ for (const SfxItemSet* pSet : {&m_aChrSet, &m_aParSet})
+ {
+ if( pSet->Count() )
+ {
+ SfxItemIter aIter( *pSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ m_pCtrlStck->NewAttr(rPos, *pItem);
+ } while ((pItem = aIter.NextItem()));
+ }
+ }
+}
+
+void SwWW8ImplReader::MoveInsideFly(const SwFrameFormat *pFlyFormat)
+{
+ WW8DupProperties aDup(m_rDoc, m_xCtrlStck.get());
+
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), 0, false);
+
+ // set Pam in FlyFrame
+ const SwFormatContent& rContent = pFlyFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." );
+ m_pPaM->GetPoint()->Assign( rContent.GetContentIdx()->GetIndex() + 1 );
+
+ aDup.Insert(*m_pPaM->GetPoint());
+}
+
+SwTwips SwWW8ImplReader::MoveOutsideFly(SwFrameFormat *pFlyFormat,
+ const SwPosition &rPos, bool bTableJoin)
+{
+ SwTwips nRetWidth = 0;
+ if (!pFlyFormat)
+ return nRetWidth;
+ // Close all attributes, because otherwise attributes can appear
+ // that extend out of Flys
+ WW8DupProperties aDup(m_rDoc, m_xCtrlStck.get());
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), 0, false);
+
+ /*
+ #i1291
+ If this fly frame consists entirely of one table inside a frame
+ followed by an empty paragraph then we want to delete the empty
+ paragraph so as to get the frame to autoshrink to the size of the
+ table to emulate words behaviour closer.
+ */
+ if (bTableJoin)
+ {
+ const SwNodeIndex* pNodeIndex = pFlyFormat->GetContent().
+ GetContentIdx();
+ if (pNodeIndex)
+ {
+ SwNodeIndex aIdx( *pNodeIndex, 1 ),
+ aEnd( *pNodeIndex->GetNode().EndOfSectionNode() );
+
+ if (aIdx < aEnd)
+ {
+ if(aIdx.GetNode().IsTableNode())
+ {
+ SwTableNode *pTable = aIdx.GetNode().GetTableNode();
+ aIdx = *aIdx.GetNode().EndOfSectionNode();
+ ++aIdx;
+ if ( (aIdx < aEnd) && aIdx.GetNode().IsTextNode() )
+ {
+ SwTextNode *pNd = aIdx.GetNode().GetTextNode();
+ ++aIdx;
+ if (aIdx == aEnd && pNd && pNd->GetText().isEmpty())
+ {
+ //An extra pre-created by writer unused paragraph
+
+ //delete after import is complete rather than now
+ //to avoid the complication of managing uncommitted
+ //ctrlstack properties that refer to it.
+ m_aExtraneousParas.insert(pNd);
+
+ SwTable& rTable = pTable->GetTable();
+ SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
+
+ if (pTableFormat)
+ {
+ SwFormatFrameSize aSize = pTableFormat->GetFrameSize();
+ aSize.SetHeightSizeType(SwFrameSize::Minimum);
+ aSize.SetHeight(MINLAY);
+ pFlyFormat->SetFormatAttr(aSize);
+ SwFormatHoriOrient aHori = pTableFormat->GetHoriOrient();
+ // passing the table orientation of
+ // LEFT_AND_WIDTH to the frame seems to
+ // work better than FULL, especially if the
+ // table width exceeds the page width, however
+ // I am not brave enough to set it in all
+ // instances
+ pTableFormat->SetFormatAttr( SwFormatHoriOrient(0, ( aHori.GetHoriOrient() == text::HoriOrientation::LEFT_AND_WIDTH ) ? ::text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::FULL ) );
+ nRetWidth = aSize.GetWidth();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *m_pPaM->GetPoint() = rPos;
+ aDup.Insert(*m_pPaM->GetPoint());
+ return nRetWidth;
+}
+
+std::unique_ptr<WW8FlyPara> SwWW8ImplReader::ConstructApo(const ApoTestResults &rApo,
+ const WW8_TablePos *pTabPos)
+{
+ OSL_ENSURE(rApo.HasFrame() || pTabPos,
+ "If no frame found, *MUST* be in a table");
+
+ std::unique_ptr<WW8FlyPara> pRet(new WW8FlyPara(m_bVer67, rApo.mpStyleApo));
+
+ // find APO parameter and test for bGrafApo
+ if (rApo.HasFrame())
+ pRet->ReadFull(rApo.m_nSprmPPc, this);
+
+ pRet->ApplyTabPos(pTabPos);
+
+ if (pRet->IsEmpty())
+ {
+ pRet.reset();
+ }
+ return pRet;
+}
+
+bool SwWW8ImplReader::IsDropCap() const
+{
+ // Find the DCS (Drop Cap Specifier) for the paragraph
+ // if does not exist or if the first three bits are 0
+ // then there is no dropcap on the paragraph
+ WW8PLCFx_Cp_FKP *pPap = m_xPlcxMan ? m_xPlcxMan->GetPapPLCF() : nullptr;
+ if (pPap)
+ {
+ SprmResult aDCS;
+ if (m_bVer67)
+ aDCS = pPap->HasSprm(NS_sprm::v6::sprmPDcs);
+ else
+ aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PDcs::val);
+ if (aDCS.pSprm && aDCS.nRemainingData >= 2)
+ {
+ /*
+ fdct short :3 0007 drop cap type
+ 0 no drop cap
+ 1 normal drop cap
+ 2 drop cap in margin
+ */
+ short nDCS = SVBT16ToUInt16(aDCS.pSprm);
+ if (nDCS & 7)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwWW8ImplReader::StartApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos)
+{
+ m_xWFlyPara = ConstructApo(rApo, pTabPos);
+ if (!m_xWFlyPara)
+ return false;
+
+ // <WW8SwFlyPara> constructor has changed - new 4th parameter
+ // containing WW8 page top margin.
+ m_xSFlyPara.reset(new WW8SwFlyPara( *m_pPaM, *this, *m_xWFlyPara,
+ m_aSectionManager.GetWWPageTopMargin(),
+ m_aSectionManager.GetTextAreaWidth(),
+ m_nIniFlyDx, m_nIniFlyDy));
+
+ // If this paragraph is a Dropcap set the flag and we will deal with it later
+ if (IsDropCap())
+ {
+ m_bDropCap = true;
+ m_xCurrentItemSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_END - 1>));
+ return false;
+ }
+
+ if (!m_xWFlyPara->bGrafApo)
+ {
+
+ // Within the GrafApo text attributes have to be ignored, because
+ // they would apply to the following lines. The frame is only inserted
+ // if it is not merely positioning a single image. If it is an image
+ // frame, pWFlyPara and pSFlyPara are retained and the resulting
+ // attributes applied to the image when inserting the image.
+
+ WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), false);
+
+ // ofz#34749 we shouldn't anchor anything into an 'extra' paragraph scheduled for
+ // removal at end of import, but check if that scenario is happening
+ m_aExtraneousParas.remove_if_present(m_pPaM->GetPointNode().GetTextNode());
+
+ if (pTabPos)
+ {
+ if (m_xFormatOfJustInsertedApo)
+ {
+ // We just inserted a floating table and we'll insert a next one.
+ SwFrameFormat* pFormat = m_xFormatOfJustInsertedApo->GetFormat();
+ if (pFormat)
+ {
+ const SwNode* pAnchorNode = pFormat->GetAnchor().GetAnchorNode();
+ SwPosition* pPoint = m_pPaM->GetPoint();
+ if (pAnchorNode && *pAnchorNode == pPoint->GetNode())
+ {
+ // The two fly frames would have the same anchor position, leading to
+ // potentially overlapping text, prevent that.
+ AppendTextNode(*pPoint);
+ }
+ }
+ }
+
+ // Map a positioned table to a split fly.
+ aFlySet.Put(SwFormatFlySplit(true));
+
+ if (pTabPos->nTFNoAllowOverlap)
+ {
+ // text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE is not the default and is set in
+ // the WW8FlySet ctor already, keep that unchanged.
+ SwFormatWrapInfluenceOnObjPos aInfluence(aFlySet.Get(RES_WRAP_INFLUENCE_ON_OBJPOS));
+ aInfluence.SetAllowOverlap(false);
+ aFlySet.Put(aInfluence);
+ }
+ }
+
+ m_xSFlyPara->SetFlyFormat(m_rDoc.MakeFlySection(WW8SwFlyPara::eAnchor,
+ m_pPaM->GetPoint(), &aFlySet));
+ OSL_ENSURE(m_xSFlyPara->GetFlyFormat()->GetAnchor().GetAnchorId() ==
+ WW8SwFlyPara::eAnchor, "Not the anchor type requested!");
+
+ if (SwFlyFrameFormat* pFlyFormat = m_xSFlyPara->GetFlyFormat())
+ {
+ if (!m_pDrawModel)
+ GraphicCtor();
+
+ SdrObject* pOurNewObject = CreateContactObject(pFlyFormat);
+ m_xWWZOrder->InsertTextLayerObject(pOurNewObject);
+ }
+
+ if (RndStdIds::FLY_AS_CHAR != WW8SwFlyPara::eAnchor && m_xSFlyPara->GetFlyFormat())
+ {
+ m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), m_xSFlyPara->GetFlyFormat());
+ }
+
+ // remember Pos in body text
+ m_xSFlyPara->xMainTextPos = m_rDoc.CreateUnoCursor(*m_pPaM->GetPoint());
+
+ //remove fltanchors, otherwise they will be closed inside the
+ //frame, which makes no sense, restore them after the frame is
+ //closed
+ m_xSFlyPara->xOldAnchorStck = std::move(m_xAnchorStck);
+ m_xAnchorStck.reset(new SwWW8FltAnchorStack(m_rDoc, m_nFieldFlags));
+
+ if (SwFlyFrameFormat* pFlyFormat = m_xSFlyPara->GetFlyFormat())
+ MoveInsideFly(pFlyFormat);
+
+ // 1) ReadText() is not called recursively because the length of
+ // the Apo is unknown at that time, and ReadText() needs it.
+ // 2) the CtrlStck is not re-created.
+ // the Char attributes continue (trouble with Sw-attributes)
+ // Para attributes must be reset at the end of every paragraph,
+ // i.e. at the end of a paragraph there must not be para attributes
+ // on the stack
+ }
+ return true;
+}
+
+void wwSectionManager::JoinNode(const SwPosition &rPos, const SwNode &rNode)
+{
+ if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.GetNode()))
+ maSegments.back().maStart.Assign(rNode);
+}
+
+bool SwWW8ImplReader::JoinNode(SwPaM &rPam, bool bStealAttr)
+{
+ bool bRet = false;
+ rPam.GetPoint()->SetContent(0); // go to start of paragraph
+
+ SwNodeIndex aPref(rPam.GetPoint()->GetNode(), -1);
+
+ if (SwTextNode* pNode = aPref.GetNode().GetTextNode())
+ {
+ m_aSectionManager.JoinNode(*rPam.GetPoint(), aPref.GetNode());
+ rPam.GetPoint()->Assign(*pNode, pNode->GetText().getLength());
+ if (bStealAttr)
+ m_xCtrlStck->StealAttr(rPam.GetPoint()->GetNode());
+
+ if (m_oLastAnchorPos || m_xPreviousNode || (m_xSFlyPara && m_xSFlyPara->xMainTextPos))
+ {
+ SwNodeIndex aToBeJoined(aPref, 1);
+
+ if (m_oLastAnchorPos)
+ {
+ //If the last anchor pos is here, then clear the anchor pos.
+ //This "last anchor pos" is only used for fixing up the
+ //positions of things anchored to page breaks and here
+ //we are removing the last paragraph of a frame, so there
+ //cannot be a page break at this point so we can
+ //safely reset m_pLastAnchorPos to avoid any dangling
+ //SwContentIndex's pointing into the deleted paragraph
+ SwNodeIndex aLastAnchorPos(m_oLastAnchorPos->GetNode());
+ if (aLastAnchorPos == aToBeJoined)
+ m_oLastAnchorPos.reset();
+ }
+
+ if (m_xPreviousNode)
+ {
+ //If the drop character start pos is here, then clear it.
+ SwNodeIndex aDropCharPos(*m_xPreviousNode->GetTextNode());
+ if (aDropCharPos == aToBeJoined)
+ m_xPreviousNode.reset();
+ }
+
+ if (m_xSFlyPara && m_xSFlyPara->xMainTextPos)
+ {
+ // If an open apo pos is here, then clear it before
+ // JoinNext destroys it
+ SwNodeIndex aOpenApoPos(m_xSFlyPara->xMainTextPos->GetPoint()->GetNode());
+ if (aOpenApoPos == aToBeJoined)
+ m_xSFlyPara->xMainTextPos.reset();
+ }
+ }
+
+ pNode->JoinNext();
+
+ bRet = true;
+ }
+ return bRet;
+}
+
+//In auto-width word frames negative after-indent values are ignored
+void SwWW8ImplReader::StripNegativeAfterIndent(SwFrameFormat const *pFlyFormat)
+{
+ const SwNodeIndex* pSttNd = pFlyFormat->GetContent().GetContentIdx();
+ if (!pSttNd)
+ return;
+
+ SwNodeIndex aIdx(*pSttNd, 1);
+ SwNodeIndex aEnd(*pSttNd->GetNode().EndOfSectionNode());
+ while (aIdx < aEnd)
+ {
+ SwTextNode *pNd = aIdx.GetNode().GetTextNode();
+ if (pNd)
+ {
+ const SvxRightMarginItem & rRightMargin(pNd->GetAttr(RES_MARGIN_RIGHT));
+ if (rRightMargin.GetRight() < 0)
+ {
+ SvxRightMarginItem rightMargin(rRightMargin);
+ rightMargin.SetRight(0);
+ pNd->SetAttr(rightMargin);
+ }
+ }
+ ++aIdx;
+ }
+}
+
+void SwWW8ImplReader::StopApo()
+{
+ OSL_ENSURE(m_xWFlyPara, "no pWFlyPara to close");
+ if (!m_xWFlyPara)
+ return;
+ if (m_xWFlyPara->bGrafApo)
+ {
+ // image frame that has not been inserted: delete empty paragraph + attr
+ JoinNode(*m_pPaM, true);
+
+ }
+ else
+ {
+ if (!m_xSFlyPara->xMainTextPos)
+ {
+ OSL_ENSURE(m_xSFlyPara->xMainTextPos, "StopApo: xMainTextPos is nullptr");
+ return;
+ }
+
+ /*
+ What we are doing with this temporary nodeindex is as follows: The
+ stack of attributes normally only places them into the document when
+ the current insertion point has passed them by. Otherwise the end
+ point of the attribute gets pushed along with the insertion point. The
+ insertion point is moved and the properties committed during
+ MoveOutsideFly. We also may want to remove the final paragraph in the
+ frame, but we need to wait until the properties for that frame text
+ have been committed otherwise they will be lost. So we first get a
+ handle to the last the filter inserted. After the attributes are
+ committed, if that paragraph exists we join it with the para after it
+ that comes with the frame by default so that as normal we don't end up
+ with one more paragraph than we wanted.
+ */
+ SwNodeIndex aPref(m_pPaM->GetPoint()->GetNode(), -1);
+
+ SwTwips nNewWidth =
+ MoveOutsideFly(m_xSFlyPara->GetFlyFormat(), *m_xSFlyPara->xMainTextPos->GetPoint());
+ if (nNewWidth)
+ m_xSFlyPara->BoxUpWidth(nNewWidth);
+
+ Color aBg(ColorTransparency, 0xFE, 0xFF, 0xFF, 0xFF); //Transparent by default
+
+ SwTextNode* pNd = aPref.GetNode().GetTextNode();
+ SwTextNode* pJoinNext = nullptr;
+ if (pNd && m_xSFlyPara->GetFlyFormat())
+ {
+ /*
+ #i582#
+ Take the last paragraph background colour and fill the frame with
+ it. Otherwise, make it transparent, this appears to be how MSWord
+ works
+ */
+ const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_BACKGROUND);
+ const SvxBrushItem &rBrush = static_cast<const SvxBrushItem&>(rItm);
+ if (rBrush.GetColor() != COL_AUTO)
+ aBg = rBrush.GetColor();
+
+ if (m_oLastAnchorPos)
+ {
+ //If the last anchor pos is here, then clear the anchor pos.
+ //This "last anchor pos" is only used for fixing up the
+ //positions of things anchored to page breaks and here
+ //we are removing the last paragraph of a frame, so there
+ //cannot be a page break at this point so we can
+ //safely reset m_pLastAnchorPos to avoid any dangling
+ //SwContentIndex's pointing into the deleted paragraph
+ SwNodeIndex aLastAnchorPos(m_oLastAnchorPos->GetNode());
+ SwNodeIndex aToBeJoined(aPref, 1);
+ if (aLastAnchorPos == aToBeJoined)
+ m_oLastAnchorPos.reset();
+ }
+
+ //Get rid of extra empty paragraph
+ pJoinNext = pNd;
+ }
+
+ if (SwFlyFrameFormat* pFlyFormat = m_xSFlyPara->GetFlyFormat())
+ pFlyFormat->SetFormatAttr(SvxBrushItem(aBg, RES_BACKGROUND));
+
+ DeleteAnchorStack();
+ if (pJoinNext)
+ pJoinNext->JoinNext();
+
+ m_xAnchorStck = std::move(m_xSFlyPara->xOldAnchorStck);
+
+ // When inserting a graphic into the fly frame using the auto
+ // function, the extension of the SW-fly has to be set
+ // manually as the SW fly has no auto function to adjust the
+ // frame´s size.
+ if (m_xSFlyPara->nNewNetWidth > MINFLY && m_xSFlyPara->GetFlyFormat()) // BoxUpWidth ?
+ {
+ tools::Long nW = m_xSFlyPara->nNewNetWidth;
+ nW += m_xSFlyPara->nWidth - m_xSFlyPara->nNetWidth; // border for it
+ m_xSFlyPara->GetFlyFormat()->SetFormatAttr(
+ SwFormatFrameSize(m_xSFlyPara->eHeightFix, nW, m_xSFlyPara->nHeight));
+ }
+ /*
+ Word set *no* width meaning it's an automatic width. The
+ SwFlyPara reader will have already set a fallback width of the
+ printable regions width, so we should reuse it. Despite the related
+ problems with layout addressed with a hack in WW8FlyPara's constructor
+ #i27204# Added AutoWidth setting. Left the old CalculateFlySize in place
+ so that if the user unselects autowidth, the width doesn't max out
+ */
+ else if (!m_xWFlyPara->nSp28 && m_xSFlyPara->GetFlyFormat())
+ {
+ using namespace sw::util;
+ SfxItemSet aFlySet( m_xSFlyPara->GetFlyFormat()->GetAttrSet() );
+
+ SwFormatFrameSize aSize(aFlySet.Get(RES_FRM_SIZE));
+
+ aFlySet.ClearItem(RES_FRM_SIZE);
+
+ if (!m_bFuzzing)
+ {
+ CalculateFlySize(aFlySet, m_xSFlyPara->xMainTextPos->GetPoint()->GetNode(),
+ m_xSFlyPara->nWidth);
+ }
+
+ nNewWidth = aFlySet.Get(RES_FRM_SIZE).GetWidth();
+
+ aSize.SetWidth(nNewWidth);
+ aSize.SetWidthSizeType(SwFrameSize::Variable);
+
+ m_xSFlyPara->GetFlyFormat()->SetFormatAttr(aSize);
+ }
+
+ m_xSFlyPara->xMainTextPos.reset();
+// To create the SwFrames when inserting into an existing document, fltshell.cxx
+// will call pFlyFrame->MakeFrames() when setting the FltAnchor attribute
+
+ }
+
+ //#i8062#
+ if (m_xSFlyPara && m_xSFlyPara->GetFlyFormat())
+ m_xFormatOfJustInsertedApo.reset(new FrameDeleteWatch(m_xSFlyPara->GetFlyFormat()));
+
+ m_xSFlyPara.reset();
+ m_xWFlyPara.reset();
+}
+
+// TestSameApo() returns if it's the same Apo or a different one
+bool SwWW8ImplReader::TestSameApo(const ApoTestResults &rApo,
+ const WW8_TablePos *pTabPos)
+{
+ if (!m_xWFlyPara)
+ {
+ OSL_ENSURE(m_xWFlyPara, " Where is my pWFlyPara ? ");
+ return true;
+ }
+
+ // We need to a full comparison (excepting borders) to identify all
+ // combinations style/hard correctly. For this reason we create a
+ // temporary WW8FlyPara (depending on if style or not), apply the
+ // hard attributes and then compare.
+
+ // For comparison
+ WW8FlyPara aF(m_bVer67, rApo.mpStyleApo);
+ // WWPara for current para
+ if (rApo.HasFrame())
+ aF.Read(rApo.m_nSprmPPc, m_xPlcxMan->GetPapPLCF());
+ aF.ApplyTabPos(pTabPos);
+
+ return aF == *m_xWFlyPara;
+}
+
+void SwWW8ImplReader::NewAttr( const SfxPoolItem& rAttr,
+ const bool bFirstLineOfStSet,
+ const bool bLeftIndentSet )
+{
+ if( m_bNoAttrImport ) // for ignoring styles during doc inserts
+ return;
+
+ if (m_pCurrentColl)
+ {
+ OSL_ENSURE(rAttr.Which() != RES_FLTR_REDLINE, "redline in style!");
+ m_pCurrentColl->SetFormatAttr(rAttr);
+ }
+ else if (m_xCurrentItemSet)
+ {
+ m_xCurrentItemSet->Put(rAttr);
+ }
+ else if (rAttr.Which() == RES_FLTR_REDLINE)
+ {
+ m_xRedlineStack->open(*m_pPaM->GetPoint(), rAttr);
+ }
+ else
+ {
+ m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), rAttr);
+ // #i103711#
+ if ( bFirstLineOfStSet )
+ {
+ const SwNode* pNd = &(m_pPaM->GetPoint()->GetNode());
+ m_aTextNodesHavingFirstLineOfstSet.insert( pNd );
+ }
+ // #i105414#
+ if ( bLeftIndentSet )
+ {
+ const SwNode* pNd = &(m_pPaM->GetPoint()->GetNode());
+ m_aTextNodesHavingLeftIndentSet.insert( pNd );
+ }
+ }
+
+ if (m_pPostProcessAttrsInfo && m_pPostProcessAttrsInfo->mbCopy)
+ m_pPostProcessAttrsInfo->mItemSet.Put(rAttr);
+}
+
+// fetches attribute from FormatColl / Stack / Doc
+const SfxPoolItem* SwWW8ImplReader::GetFormatAttr( sal_uInt16 nWhich )
+{
+ const SfxPoolItem* pRet = nullptr;
+ if (m_pCurrentColl)
+ pRet = &(m_pCurrentColl->GetFormatAttr(nWhich));
+ else if (m_xCurrentItemSet)
+ {
+ pRet = m_xCurrentItemSet->GetItem(nWhich);
+ if (!pRet)
+ pRet = m_pStandardFormatColl ? &(m_pStandardFormatColl->GetFormatAttr(nWhich)) : nullptr;
+ if (!pRet)
+ pRet = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
+ }
+ else if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
+ {
+ pRet = m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), nWhich);
+ if (!pRet)
+ {
+ if (m_nCurrentColl < m_vColl.size() && m_vColl[m_nCurrentColl].m_pFormat &&
+ m_vColl[m_nCurrentColl].m_bColl)
+ {
+ pRet = &(m_vColl[m_nCurrentColl].m_pFormat->GetFormatAttr(nWhich));
+ }
+ }
+ if (!pRet)
+ pRet = m_pStandardFormatColl ? &(m_pStandardFormatColl->GetFormatAttr(nWhich)) : nullptr;
+ if (!pRet)
+ pRet = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich);
+ }
+ else
+ pRet = m_xCtrlStck->GetFormatAttr(*m_pPaM->GetPoint(), nWhich);
+ return pRet;
+}
+
+// The methods get as parameters the token id and the length of the following
+// parameters according to the table in WWScan.cxx.
+void SwWW8ImplReader::Read_Special(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 1)
+ {
+ m_bSpec = false;
+ return;
+ }
+ m_bSpec = ( *pData != 0 );
+}
+
+// Read_Obj is used for fObj and for fOle2 !
+void SwWW8ImplReader::Read_Obj(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 1)
+ m_bObj = false;
+ else
+ {
+ m_bObj = 0 != *pData;
+
+ if( m_bObj && m_nPicLocFc && m_bEmbeddObj )
+ {
+ if (!m_aFieldStack.empty() && m_aFieldStack.back().mnFieldId == 56)
+ {
+ // For LINK fields, store the nObjLocFc value in the field entry
+ m_aFieldStack.back().mnObjLocFc = m_nPicLocFc;
+ }
+ else
+ {
+ m_nObjLocFc = m_nPicLocFc;
+ }
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_PicLoc(sal_uInt16 , const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 4)
+ {
+ m_nPicLocFc = 0;
+ m_bSpec = false; // Is this always correct?
+ }
+ else
+ {
+ m_nPicLocFc = SVBT32ToUInt32( pData );
+ m_bSpec = true;
+
+ if( m_bObj && m_nPicLocFc && m_bEmbeddObj )
+ m_nObjLocFc = m_nPicLocFc;
+ }
+}
+
+void SwWW8ImplReader::Read_POutLvl(sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 0)
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_OUTLINELEVEL);
+ return;
+ }
+
+ if (m_pCurrentColl != nullptr)
+ {
+ SwWW8StyInf* pSI = GetStyle(m_nCurrentColl);
+ if (pSI && pSI->m_bColl && pSI->m_pFormat)
+ {
+ pSI->mnWW8OutlineLevel =
+ static_cast< sal_uInt8 >( ( (pData && nLen >= 1) ? *pData : 0 ) );
+ auto nLevel = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(pSI->mnWW8OutlineLevel);
+ if (nLevel == 0)
+ {
+ SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pSI->m_pFormat);
+ pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
+ }
+ NewAttr(SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nLevel));
+ }
+ }
+ else if (m_pPaM != nullptr)
+ {
+ const sal_uInt8 nOutlineLevel
+ = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(
+ static_cast<sal_uInt8>(((pData && nLen >= 1) ? *pData : 0)));
+ NewAttr(SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel));
+ }
+}
+
+void SwWW8ImplReader::Read_Symbol(sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if( m_bIgnoreText )
+ return;
+
+ if (nLen < (m_bVer67 ? 3 : 4))
+ {
+ //otherwise disable after we print the char
+ if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT );
+ m_bSymbol = false;
+ }
+ else
+ {
+ // Make new Font-Attribute
+ // (will be closed in SwWW8ImplReader::ReadChars() )
+
+ //Will not be added to the charencoding stack, for styles the real
+ //font setting will be put in as the styles charset, and for plain
+ //text encoding for symbols is moot. Drawing boxes will check bSymbol
+ //themselves so they don't need to add it to the stack either.
+ if (SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_FONT))
+ {
+ SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_CJK_FONT);
+ SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_CTL_FONT);
+ if( m_bVer67 )
+ {
+ //convert single byte from MS1252 to Unicode
+ m_cSymbol = OUString(
+ reinterpret_cast<const char*>(pData+2), 1,
+ RTL_TEXTENCODING_MS_1252).toChar();
+ }
+ else
+ {
+ //already is Unicode
+ m_cSymbol = SVBT16ToUInt16( pData+2 );
+ }
+ m_bSymbol = true;
+ }
+ }
+}
+
+SwWW8StyInf *SwWW8ImplReader::GetStyle(sal_uInt16 nColl) const
+{
+ return const_cast<SwWW8StyInf *>(nColl < m_vColl.size() ? &m_vColl[nColl] : nullptr);
+}
+
+// Read_BoldUsw for italic, bold, small caps, majuscule, struck out,
+// contour and shadow
+void SwWW8ImplReader::Read_BoldUsw( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ const int nContiguousWestern = 8;
+ const int nWestern = nContiguousWestern + 1;
+ const int nEastern = 2;
+ const int nCTL = 2;
+ const int nIds = nWestern + nEastern + nCTL;
+ static const sal_uInt16 nEndIds[ nIds ] =
+ {
+ RES_CHRATR_WEIGHT, RES_CHRATR_POSTURE,
+ RES_CHRATR_CROSSEDOUT, RES_CHRATR_CONTOUR,
+ RES_CHRATR_SHADOWED, RES_CHRATR_CASEMAP,
+ RES_CHRATR_CASEMAP, RES_CHRATR_HIDDEN,
+
+ RES_CHRATR_CROSSEDOUT,
+
+ RES_CHRATR_CJK_WEIGHT, RES_CHRATR_CJK_POSTURE,
+
+ RES_CHRATR_CTL_WEIGHT, RES_CHRATR_CTL_POSTURE
+ };
+
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+
+ sal_uInt8 nI;
+ // the attribute number for "double strike-through" breaks rank
+ if (NS_sprm::CFDStrike::val == nId)
+ nI = nContiguousWestern; // The out of sequence western id
+ else
+ {
+ // The contiguous western ids
+ if (eVersion <= ww::eWW2)
+ nI = static_cast< sal_uInt8 >(nId - 60);
+ else if (eVersion < ww::eWW8)
+ nI = static_cast< sal_uInt8 >(nId - NS_sprm::v6::sprmCFBold);
+ else
+ nI = static_cast< sal_uInt8 >(nId - NS_sprm::CFBold::val);
+ }
+
+ sal_uInt16 nMask = 1 << nI;
+
+ if (nLen < 1)
+ {
+ if (nI < 2)
+ {
+ if (eVersion <= ww::eWW6)
+ {
+ // reset the CTL Weight and Posture, because they are the same as their
+ // western equivalents in ww6
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nWestern + nEastern + nI ] );
+ }
+ // reset the CJK Weight and Posture, because they are the same as their
+ // western equivalents in word
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nWestern + nI ] );
+ }
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nI ] );
+ m_xCtrlStck->SetToggleAttr(nI, false);
+ return;
+ }
+ // value: 0 = off, 1 = on, 128 = like style, 129 contrary to style
+ bool bOn = *pData & 1;
+ SwWW8StyInf* pSI = GetStyle(m_nCurrentColl);
+ if (m_xPlcxMan && eVersion > ww::eWW2)
+ {
+ SprmResult aCharIstd =
+ m_xPlcxMan->GetChpPLCF()->HasSprm(m_bVer67 ? NS_sprm::v6::sprmCIstd : NS_sprm::CIstd::val);
+ if (aCharIstd.pSprm && aCharIstd.nRemainingData >= 2)
+ pSI = GetStyle(SVBT16ToUInt16(aCharIstd.pSprm));
+ }
+
+ if( m_pCurrentColl ) // StyleDef -> remember flags
+ {
+ if (pSI)
+ {
+ // The style based on has Bit 7 set ?
+ if (
+ pSI->m_nBase < m_vColl.size() && (*pData & 0x80) &&
+ (m_vColl[pSI->m_nBase].m_n81Flags & nMask)
+ )
+ {
+ bOn = !bOn; // invert
+ }
+
+ if (bOn)
+ pSI->m_n81Flags |= nMask; // set flag
+ else
+ pSI->m_n81Flags &= ~nMask; // delete flag
+ }
+ }
+ else
+ {
+
+ // in text -> look at flags
+ if( *pData & 0x80 ) // bit 7 set?
+ {
+ if (pSI && pSI->m_n81Flags & nMask) // and in StyleDef at ?
+ bOn = !bOn; // then invert
+ // remember on stack that this is a toggle-attribute
+ m_xCtrlStck->SetToggleAttr(nI, true);
+ }
+ }
+
+ SetToggleAttr( nI, bOn );
+}
+
+void SwWW8ImplReader::Read_Bidi(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 1) //Property end
+ {
+ m_bBidi = false;
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_CHRATR_BIDIRTL);
+ }
+ else //Property start
+ {
+ m_bBidi = true;
+ sal_uInt8 nBidi = *pData;
+ NewAttr( SfxInt16Item( RES_CHRATR_BIDIRTL, (nBidi!=0)? 1 : 0 ) );
+ }
+}
+
+/*
+ tdf#91916, #i8726, #i42685# there is an ambiguity
+ around certain properties as to what they mean,
+ which appears to be a problem with different versions
+ of the file format where properties conflict, i.e.
+
+ooo40606-2.doc, magic is a699
+ : 0x6f 0x4 0x0 0x71 0x4 0x0
+ooo40635-1.doc, magic is a699
+ : 0x6f 0x4 0x0 0x71 0x4 0x0
+ooo31093/SIMPCHIN.doc, magic is a699
+ : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0
+ : 0x6f 0x5 0x0 0x70 0x5 0x0
+ooo31093/TRADCHIN.doc, magic is a699
+ : 0x6f 0x1 0x0 0x70 0x0 0x0 0x71 0x1 0x0
+ooo31093/JAPANESE.doc, magic is a697
+ : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0
+ooo31093/KOREAN.doc, magic is a698
+ : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0
+ooo31093-1.doc, magic is a698
+ : 0x6f 0x5 0x0 0x70 0x5 0x0
+ooo31093-1.doc, magic is a698
+ : 0x6f 0x5 0x0 0x70 0x5 0x0
+
+meanwhile...
+
+ooo27954-1.doc, magic is a5dc
+ : 0x6f 0x1 0x81 0x71 0x2 0x4 0x0 0x74 0x2 0x20 0x0
+
+ooo33251-1.doc, magic is a5dc
+ : 0x6f 0x1 0x81 0x71 0x2 0x3 0x0 0x74 0x2 0x1c 0x0
+
+---
+
+So we have the same sprm values, but different payloads, where
+the a5dc versions appear to use a len argument, followed by len
+bytes, while the a698<->a699 versions use a 2byte argument
+
+commit c2213db9ed70c1fd546482d22e36e4029c10aa45
+
+ INTEGRATION: CWS tl28 (1.169.24); FILE MERGED
+ 2006/10/25 13:40:41 tl 1.169.24.2: RESYNC: (1.169-1.170); FILE MERGED
+ 2006/09/20 11:55:50 hbrinkm 1.169.24.1: #i42685# applied patch
+
+changed 0x6f and 0x70 from Read_BoldBiDiUsw to Read_FontCode for all versions.
+
+In the Word for Window 2 spec we have...
+ 78 //sprmCMajority
+ 80 //sprmCFBoldBi
+ 81 //sprmCFItalicBi
+ 82 //sprmCFtcBi
+ 83 //sprmClidBi
+ 84 //sprmCIcoBi
+ 85 //sprmCHpsBi
+as see in GetWW2SprmDispatcher, different numbers, but the sequence starts with
+the same sprmCMajority as appears before 0x6f in word 6/95
+
+I think the easiest explanation is that the CJK Word for Window 95, or whatever
+the product was went rogue, and did their own things with at least first three
+slots after sprmCMajority to do a different thing. I have no reason to think Tono
+was wrong with what they do in the a698<->a699 versions, but with magic
+a5dc they probably did mean sprmCFBoldBi, sprmCFItalicBi cause they have that 0x81
+pattern which has significance for those types of properties.
+*/
+void SwWW8ImplReader::Read_AmbiguousSPRM(sal_uInt16 nId, const sal_uInt8* pData,
+ short nLen)
+{
+ if (m_xWwFib->m_wIdent >= 0xa697 && m_xWwFib->m_wIdent <= 0xa699)
+ {
+ Read_FontCode(nId, pData, nLen);
+ }
+ else
+ {
+ Read_BoldBiDiUsw(nId, pData, nLen);
+ }
+}
+
+// Read_BoldUsw for BiDi Italic, Bold
+void SwWW8ImplReader::Read_BoldBiDiUsw(sal_uInt16 nId, const sal_uInt8* pData,
+ short nLen)
+{
+ static const sal_uInt16 nEndIds[2] =
+ {
+ RES_CHRATR_CTL_WEIGHT, RES_CHRATR_CTL_POSTURE,
+ };
+
+ sal_uInt8 nI;
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+ if (eVersion <= ww::eWW2)
+ nI = static_cast< sal_uInt8 >(nId - 80);
+ else if (eVersion < ww::eWW8)
+ nI = static_cast< sal_uInt8 >(nId - 111);
+ else
+ nI = static_cast< sal_uInt8 >(nId - NS_sprm::CFBoldBi::val);
+
+ OSL_ENSURE(nI <= 1, "not happening");
+ if (nI > 1)
+ return;
+
+ sal_uInt16 nMask = 1 << nI;
+
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),nEndIds[nI]);
+ m_xCtrlStck->SetToggleBiDiAttr(nI, false);
+ return;
+ }
+ bool bOn = *pData & 1;
+ SwWW8StyInf* pSI = GetStyle(m_nCurrentColl);
+ if (m_xPlcxMan)
+ {
+ SprmResult aCharIstd =
+ m_xPlcxMan->GetChpPLCF()->HasSprm(m_bVer67 ? NS_sprm::v6::sprmCIstd : NS_sprm::CIstd::val);
+ if (aCharIstd.pSprm && aCharIstd.nRemainingData >= 2)
+ pSI = GetStyle(SVBT16ToUInt16(aCharIstd.pSprm));
+ }
+
+ if (m_pCurrentColl && eVersion > ww::eWW2) // StyleDef -> remember flags
+ {
+ if (pSI)
+ {
+ if( pSI->m_nBase < m_vColl.size() // Style Based on
+ && ( *pData & 0x80 ) // bit 7 set?
+ && ( m_vColl[pSI->m_nBase].m_n81BiDiFlags & nMask ) ) // base mask?
+ bOn = !bOn; // invert
+
+ if( bOn )
+ pSI->m_n81BiDiFlags |= nMask; // set flag
+ else
+ pSI->m_n81BiDiFlags &= ~nMask; // delete flag
+ }
+ }
+ else
+ {
+
+ // in text -> look at flags
+ if (*pData & 0x80) // Bit 7 set?
+ {
+ if (pSI && pSI->m_n81BiDiFlags & nMask) // and in StyleDef at ?
+ bOn = !bOn; // then invert
+ // remember on stack that this is a toggle-attribute
+ m_xCtrlStck->SetToggleBiDiAttr(nI, true);
+ }
+ }
+
+ SetToggleBiDiAttr(nI, bOn);
+}
+
+void SwWW8ImplReader::SetToggleBiDiAttr(sal_uInt8 nAttrId, bool bOn)
+{
+ switch (nAttrId)
+ {
+ case 0:
+ {
+ SvxWeightItem aAttr( bOn ? WEIGHT_BOLD : WEIGHT_NORMAL, RES_CHRATR_WEIGHT );
+ aAttr.SetWhich( RES_CHRATR_CTL_WEIGHT );
+ NewAttr( aAttr );
+ }
+ break;
+ case 1:
+ {
+ SvxPostureItem aAttr( bOn ? ITALIC_NORMAL : ITALIC_NONE, RES_CHRATR_POSTURE );
+ aAttr.SetWhich( RES_CHRATR_CTL_POSTURE );
+ NewAttr( aAttr );
+ }
+ break;
+ default:
+ OSL_ENSURE(false, "Unhandled unknown bidi toggle attribute");
+ break;
+
+ }
+}
+
+void SwWW8ImplReader::SetToggleAttr(sal_uInt8 nAttrId, bool bOn)
+{
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+
+ switch (nAttrId)
+ {
+ case 0:
+ {
+ SvxWeightItem aAttr( bOn ? WEIGHT_BOLD : WEIGHT_NORMAL, RES_CHRATR_WEIGHT );
+ NewAttr( aAttr );
+ aAttr.SetWhich( RES_CHRATR_CJK_WEIGHT );
+ NewAttr( aAttr );
+ if (eVersion <= ww::eWW6)
+ {
+ aAttr.SetWhich( RES_CHRATR_CTL_WEIGHT );
+ NewAttr( aAttr );
+ }
+ }
+ break;
+ case 1:
+ {
+ SvxPostureItem aAttr( bOn ? ITALIC_NORMAL : ITALIC_NONE, RES_CHRATR_POSTURE );
+ NewAttr( aAttr );
+ aAttr.SetWhich( RES_CHRATR_CJK_POSTURE );
+ NewAttr( aAttr );
+ if (eVersion <= ww::eWW6)
+ {
+ aAttr.SetWhich( RES_CHRATR_CTL_POSTURE );
+ NewAttr( aAttr );
+ }
+ }
+ break;
+ case 2:
+ NewAttr(SvxCrossedOutItem(bOn ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT));
+ break;
+ case 3:
+ NewAttr( SvxContourItem( bOn, RES_CHRATR_CONTOUR ) );
+ break;
+ case 4:
+ NewAttr( SvxShadowedItem( bOn, RES_CHRATR_SHADOWED ) );
+ break;
+ case 5:
+ NewAttr( SvxCaseMapItem( bOn ? SvxCaseMap::SmallCaps
+ : SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP ) );
+ break;
+ case 6:
+ NewAttr( SvxCaseMapItem( bOn ? SvxCaseMap::Uppercase
+ : SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP ) );
+ break;
+ case 7:
+ if (m_pPaM->GetPoint()->GetContentIndex() == 0 && m_xFormatOfJustInsertedApo)
+ {
+ // We just inserted a frame and we're at the next paragraph start.
+ SwFrameFormat* pFormat = m_xFormatOfJustInsertedApo->GetFormat();
+ if (pFormat)
+ {
+ SwNode* pAnchorNode = pFormat->GetAnchor().GetAnchorNode();
+ if (pAnchorNode && *pAnchorNode == m_pPaM->GetPoint()->GetNode())
+ {
+ // The anchor paragraph would be hidden, leading to hiding the frame as
+ // well, prevent that.
+ break;
+ }
+ }
+ }
+
+ NewAttr(SvxCharHiddenItem(bOn, RES_CHRATR_HIDDEN));
+ break;
+ case 8:
+ NewAttr( SvxCrossedOutItem( bOn ? STRIKEOUT_DOUBLE
+ : STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT ) );
+ break;
+ default:
+ OSL_ENSURE(false, "Unhandled unknown toggle attribute");
+ break;
+ }
+}
+
+void SwWW8ImplReader::ChkToggleAttr_( sal_uInt16 nOldStyle81Mask,
+ sal_uInt16 nNewStyle81Mask )
+{
+ sal_uInt16 i = 1, nToggleAttrFlags = m_xCtrlStck->GetToggleAttrFlags();
+ for (sal_uInt8 n = 0; n < 7; ++n, i <<= 1)
+ {
+ if (
+ (i & nToggleAttrFlags) &&
+ ((i & nOldStyle81Mask) != (i & nNewStyle81Mask))
+ )
+ {
+ SetToggleAttr(n, (i & nOldStyle81Mask));
+ }
+ }
+}
+
+void SwWW8ImplReader::ChkToggleBiDiAttr_( sal_uInt16 nOldStyle81Mask,
+ sal_uInt16 nNewStyle81Mask )
+{
+ sal_uInt16 i = 1, nToggleAttrFlags = m_xCtrlStck->GetToggleBiDiAttrFlags();
+ for (sal_uInt8 n = 0; n < 7; ++n, i <<= 1)
+ {
+ if (
+ (i & nToggleAttrFlags) &&
+ ((i & nOldStyle81Mask) != (i & nNewStyle81Mask))
+ )
+ {
+ SetToggleBiDiAttr(n, (i & nOldStyle81Mask));
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_SubSuper( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_ESCAPEMENT );
+ return;
+ }
+
+ short nEs;
+ sal_uInt8 nProp;
+ switch( *pData )
+ {
+ case 1:
+ nEs = DFLT_ESC_AUTO_SUPER;
+ nProp = DFLT_ESC_PROP;
+ break;
+ case 2:
+ nEs = DFLT_ESC_AUTO_SUB;
+ nProp = DFLT_ESC_PROP;
+ break;
+ default:
+ nEs = 0;
+ nProp = 100;
+ break;
+ }
+ NewAttr( SvxEscapementItem( nEs, nProp, RES_CHRATR_ESCAPEMENT ) );
+}
+
+SwFrameFormat *SwWW8ImplReader::ContainsSingleInlineGraphic(const SwPaM &rRegion)
+{
+ /*
+ For inline graphics and objects word has a hacked in feature to use
+ subscripting to force the graphic into a centered position on the line, so
+ we must check when applying sub/super to see if it the subscript range
+ contains only a single graphic, and if that graphic is anchored as
+ RndStdIds::FLY_AS_CHAR and then we can change its anchoring to centered in the line.
+ */
+ SwFrameFormat *pRet=nullptr;
+ SwNodeIndex aBegin(rRegion.Start()->GetNode());
+ const sal_Int32 nBegin(rRegion.Start()->GetContentIndex());
+ SwNodeIndex aEnd(rRegion.End()->GetNode());
+ const sal_Int32 nEnd(rRegion.End()->GetContentIndex());
+ const SwTextNode* pTNd;
+ const SwTextAttr* pTFlyAttr;
+ if (
+ aBegin == aEnd && nBegin == nEnd - 1 &&
+ nullptr != (pTNd = aBegin.GetNode().GetTextNode()) &&
+ nullptr != (pTFlyAttr = pTNd->GetTextAttrForCharAt(nBegin, RES_TXTATR_FLYCNT))
+ )
+ {
+ const SwFormatFlyCnt& rFly = pTFlyAttr->GetFlyCnt();
+ SwFrameFormat *pFlyFormat = rFly.GetFrameFormat();
+ if (pFlyFormat &&
+ (RndStdIds::FLY_AS_CHAR == pFlyFormat->GetAnchor().GetAnchorId()))
+ {
+ pRet = pFlyFormat;
+ }
+ }
+ return pRet;
+}
+
+bool SwWW8ImplReader::ConvertSubToGraphicPlacement()
+{
+ /*
+ For inline graphics and objects word has a hacked in feature to use
+ subscripting to force the graphic into a centered position on the line, so
+ we must check when applying sub/super to see if it the subscript range
+ contains only a single graphic, and if that graphic is anchored as
+ RndStdIds::FLY_AS_CHAR and then we can change its anchoring to centered in the line.
+ */
+ bool bIsGraphicPlacementHack = false;
+ sal_uInt16 nPos;
+ if (m_xCtrlStck->GetFormatStackAttr(RES_CHRATR_ESCAPEMENT, &nPos))
+ {
+ SwPaM aRegion(*m_pPaM->GetPoint());
+
+ SwFltPosition aMkPos((*m_xCtrlStck)[nPos].m_aMkPos);
+ SwFltPosition aPtPos(*m_pPaM->GetPoint());
+
+ SwFrameFormat *pFlyFormat = nullptr;
+ if (SwFltStackEntry::MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck, aMkPos, aPtPos)
+ && nullptr != (pFlyFormat = ContainsSingleInlineGraphic(aRegion)))
+ {
+ m_xCtrlStck->DeleteAndDestroy(nPos);
+ pFlyFormat->SetFormatAttr(SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER, text::RelOrientation::CHAR));
+ bIsGraphicPlacementHack = true;
+ }
+ }
+ return bIsGraphicPlacementHack;
+}
+
+void SwWW8ImplReader::Read_SubSuperProp( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+
+ if (nLen < (eVersion <= ww::eWW2 ? 1 : 2))
+ {
+ if (!ConvertSubToGraphicPlacement())
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_ESCAPEMENT );
+ return;
+ }
+
+ // if the fontsize for these characters is specified, make sure it is updated first
+ if ( m_xPlcxMan )
+ {
+ const sal_uInt16 nFontsizeID = m_bVer67 ? NS_sprm::v6::sprmCHps : NS_sprm::CHps::val;
+ const SprmResult aFontsize = m_xPlcxMan->GetChpPLCF()->HasSprm( nFontsizeID, /*bFindFirst=*/false );
+ if ( aFontsize.pSprm && aFontsize.nRemainingData )
+ Read_FontSize(nFontsizeID, aFontsize.pSprm, aFontsize.nRemainingData);
+ }
+
+ // font position in HalfPoints
+ short nPos = eVersion <= ww::eWW2 ? static_cast< sal_Int8 >( *pData ) : SVBT16ToInt16( pData );
+ sal_Int32 nPos2 = nPos * ( 10 * 100 ); // HalfPoints in 100 * tw
+ const SvxFontHeightItem* pF = GetFormatAttr(RES_CHRATR_FONTSIZE);
+ OSL_ENSURE(pF, "Expected to have the fontheight available here");
+
+ // #i59022: Check ensure nHeight != 0. Div by zero otherwise.
+ sal_Int32 nHeight = 240;
+ if (pF != nullptr && pF->GetHeight() != 0)
+ nHeight = pF->GetHeight();
+ nPos2 /= nHeight; // ... now in % (rounded)
+ if( nPos2 > MAX_ESC_POS )
+ nPos2 = MAX_ESC_POS;
+ if( nPos2 < -MAX_ESC_POS )
+ nPos2 = -MAX_ESC_POS;
+ SvxEscapementItem aEs( static_cast<short>(nPos2), 100, RES_CHRATR_ESCAPEMENT );
+ NewAttr( aEs );
+}
+
+void SwWW8ImplReader::Read_Underline( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ FontLineStyle eUnderline = LINESTYLE_NONE;
+ bool bWordLine = false;
+ if (pData && nLen)
+ {
+ // Parameter: 0 = none, 1 = single, 2 = by Word,
+ // 3 = double, 4 = dotted, 5 = hidden
+ // 6 = thick, 7 = dash, 8 = dot(not used)
+ // 9 = dotdash 10 = dotdotdash 11 = wave
+ switch( *pData )
+ {
+ case 2: bWordLine = true;
+ [[fallthrough]];
+ case 1: eUnderline = LINESTYLE_SINGLE; break;
+ case 3: eUnderline = LINESTYLE_DOUBLE; break;
+ case 4: eUnderline = LINESTYLE_DOTTED; break;
+ case 7: eUnderline = LINESTYLE_DASH; break;
+ case 9: eUnderline = LINESTYLE_DASHDOT; break;
+ case 10:eUnderline = LINESTYLE_DASHDOTDOT; break;
+ case 6: eUnderline = LINESTYLE_BOLD; break;
+ case 11:eUnderline = LINESTYLE_WAVE; break;
+ case 20:eUnderline = LINESTYLE_BOLDDOTTED; break;
+ case 23:eUnderline = LINESTYLE_BOLDDASH; break;
+ case 39:eUnderline = LINESTYLE_LONGDASH; break;
+ case 55:eUnderline = LINESTYLE_BOLDLONGDASH; break;
+ case 25:eUnderline = LINESTYLE_BOLDDASHDOT; break;
+ case 26:eUnderline = LINESTYLE_BOLDDASHDOTDOT;break;
+ case 27:eUnderline = LINESTYLE_BOLDWAVE; break;
+ case 43:eUnderline = LINESTYLE_DOUBLEWAVE; break;
+ }
+ }
+
+ // if necessary, mix up stack and exit!
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_UNDERLINE );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_WORDLINEMODE );
+ }
+ else
+ {
+ NewAttr( SvxUnderlineItem( eUnderline, RES_CHRATR_UNDERLINE ));
+ if( bWordLine )
+ NewAttr(SvxWordLineModeItem(true, RES_CHRATR_WORDLINEMODE));
+ }
+}
+
+/*
+//The last three vary, measurements, rotation ? ?
+NoBracket 78 CA 06 - 02 00 00 02 34 52
+() 78 CA 06 - 02 01 00 02 34 52
+[] 78 CA 06 - 02 02 00 02 34 52
+<> 78 CA 06 - 02 03 00 02 34 52
+{} 78 CA 06 - 02 04 00 02 34 52
+*/
+void SwWW8ImplReader::Read_DoubleLine_Rotate( sal_uInt16, const sal_uInt8* pData,
+ short nLen )
+{
+ if (nLen < 0) // close the tag
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_TWO_LINES );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_ROTATE );
+ }
+ else if( pData && 6 == nLen )
+ {
+ switch( *pData )
+ {
+ case 2: // double line
+ {
+ sal_Unicode cStt = 0, cEnd = 0;
+ switch( SVBT16ToUInt16( pData+1 ) )
+ {
+ case 1: cStt = '('; cEnd = ')'; break;
+ case 2: cStt = '['; cEnd = ']'; break;
+ case 3: cStt = '<'; cEnd = '>'; break;
+ case 4: cStt = '{'; cEnd = '}'; break;
+ }
+ NewAttr( SvxTwoLinesItem( true, cStt, cEnd, RES_CHRATR_TWO_LINES ));
+ }
+ break;
+
+ case 1: // rotated characters
+ {
+ bool bFitToLine = 0 != *(pData+1);
+ NewAttr( SvxCharRotateItem( 900_deg10, bFitToLine, RES_CHRATR_ROTATE ));
+ }
+ break;
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_TextColor( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ //Has newer colour variant, ignore this old variant
+ if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::CCv::val).pSprm)
+ return;
+
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_COLOR );
+ else
+ {
+ sal_uInt8 b = *pData; // parameter: 0 = Auto, 1..16 colors
+
+ if( b > 16 ) // unknown -> Black
+ b = 0;
+
+ NewAttr( SvxColorItem(GetCol(b), RES_CHRATR_COLOR));
+ if (m_pCurrentColl && m_xStyles)
+ m_xStyles->mbTextColChanged = true;
+ }
+}
+
+void SwWW8ImplReader::Read_TextForeColor(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 4)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_COLOR );
+ else
+ {
+ Color aColor = msfilter::util::BGRToRGB(SVBT32ToUInt32(pData));
+
+ // At least when transparency is 0xff and the color is black, Word renders that as black.
+ if (aColor.IsTransparent() && aColor != COL_AUTO)
+ {
+ aColor.SetAlpha(255);
+ }
+
+ NewAttr(SvxColorItem(aColor, RES_CHRATR_COLOR));
+ if (m_pCurrentColl && m_xStyles)
+ m_xStyles->mbTextColChanged = true;
+ }
+}
+
+void SwWW8ImplReader::Read_UnderlineColor(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 0)
+ {
+ //because the UnderlineColor is not a standalone attribute in SW, it belongs to the underline attribute.
+ //And, the .doc file stores attributes separately, this attribute ends here, the "underline"
+ //attribute also terminates (if the character next owns underline, that will be a new underline attribute).
+ //so nothing is left to be done here.
+ return;
+ }
+ else
+ {
+ if ( m_pCurrentColl ) //importing style
+ {
+ if( SfxItemState::SET == m_pCurrentColl->GetItemState( RES_CHRATR_UNDERLINE, false ) )
+ {
+ if (nLen >= 4)
+ {
+ const SwAttrSet& aSet = m_pCurrentColl->GetAttrSet();
+ std::unique_ptr<SvxUnderlineItem> pUnderline(aSet.Get(RES_CHRATR_UNDERLINE, false).Clone());
+ pUnderline->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32(pData)) );
+ m_pCurrentColl->SetFormatAttr( *pUnderline );
+ }
+ }
+ }
+ else if (m_xCurrentItemSet)
+ {
+ if ( SfxItemState::SET == m_xCurrentItemSet->GetItemState( RES_CHRATR_UNDERLINE, false ) )
+ {
+ if (nLen >= 4)
+ {
+ std::unique_ptr<SvxUnderlineItem> pUnderline(m_xCurrentItemSet->Get(RES_CHRATR_UNDERLINE, false).Clone());
+ pUnderline->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32(pData)) );
+ m_xCurrentItemSet->Put( std::move(pUnderline) );
+ }
+ }
+ }
+ else
+ {
+ SvxUnderlineItem* pUnderlineAttr = const_cast<SvxUnderlineItem*>(static_cast<const SvxUnderlineItem*>(m_xCtrlStck->GetOpenStackAttr( *m_pPaM->GetPoint(), RES_CHRATR_UNDERLINE )));
+ if (pUnderlineAttr && nLen >= 4)
+ pUnderlineAttr->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32( pData ) ));
+ }
+ }
+}
+bool SwWW8ImplReader::GetFontParams( sal_uInt16 nFCode, FontFamily& reFamily,
+ OUString& rName, FontPitch& rePitch, rtl_TextEncoding& reCharSet )
+{
+ // the definitions that are the base for these tables are in windows.h
+ static const FontPitch ePitchA[] =
+ {
+ PITCH_DONTKNOW, PITCH_FIXED, PITCH_VARIABLE, PITCH_DONTKNOW
+ };
+
+ static const FontFamily eFamilyA[] =
+ {
+ FAMILY_DONTKNOW, FAMILY_ROMAN, FAMILY_SWISS, FAMILY_MODERN,
+ FAMILY_SCRIPT, FAMILY_DECORATIVE, FAMILY_DONTKNOW, FAMILY_DONTKNOW
+ };
+
+ const WW8_FFN* pF = m_xFonts->GetFont( nFCode ); // Info for it
+ if( !pF ) // font number unknown ?
+ return false; // then ignore
+
+ rName = pF->sFontname;
+
+ // pF->prg : Pitch
+ rePitch = ePitchA[pF->aFFNBase.prg];
+
+ // pF->chs: Charset
+ if( 77 == pF->aFFNBase.chs ) // Mac font in Mac Charset or
+ reCharSet = m_eTextCharSet; // translated to ANSI charset
+ else
+ {
+ // #i52786#, for word 67 we'll assume that ANSI is basically invalid,
+ // might be true for (above) mac as well, but would need a mac example
+ // that exercises this to be sure
+ if (m_bVer67 && pF->aFFNBase.chs == 0)
+ reCharSet = RTL_TEXTENCODING_DONTKNOW;
+ else
+ reCharSet = rtl_getTextEncodingFromWindowsCharset(pF->aFFNBase.chs);
+ }
+
+ // make sure Font Family Code is set correctly
+ // at least for the most important fonts
+ // ( might be set wrong when Doc was not created by
+ // Winword but by third party program like Applixware... )
+ if (rName.startsWithIgnoreAsciiCase("Tms Rmn") ||
+ rName.startsWithIgnoreAsciiCase("Timmons") ||
+ rName.startsWithIgnoreAsciiCase("CG Times") ||
+ rName.startsWithIgnoreAsciiCase("MS Serif") ||
+ rName.startsWithIgnoreAsciiCase("Garamond") ||
+ rName.startsWithIgnoreAsciiCase("Times Roman") ||
+ rName.startsWithIgnoreAsciiCase("Times New Roman"))
+ {
+ reFamily = FAMILY_ROMAN;
+ }
+ else if (rName.startsWithIgnoreAsciiCase("Helv") ||
+ rName.startsWithIgnoreAsciiCase("Arial") ||
+ rName.startsWithIgnoreAsciiCase("Univers") ||
+ rName.startsWithIgnoreAsciiCase("LinePrinter") ||
+ rName.startsWithIgnoreAsciiCase("Lucida Sans") ||
+ rName.startsWithIgnoreAsciiCase("Small Fonts") ||
+ rName.startsWithIgnoreAsciiCase("MS Sans Serif"))
+ {
+ reFamily = FAMILY_SWISS;
+ }
+ else
+ {
+ reFamily = eFamilyA[pF->aFFNBase.ff];
+ }
+
+ return true;
+}
+
+bool SwWW8ImplReader::SetNewFontAttr(sal_uInt16 nFCode, bool bSetEnums,
+ sal_uInt16 nWhich)
+{
+ FontFamily eFamily;
+ OUString aName;
+ FontPitch ePitch;
+ rtl_TextEncoding eSrcCharSet;
+
+ if( !GetFontParams( nFCode, eFamily, aName, ePitch, eSrcCharSet ) )
+ {
+ //If we fail (and are not doing a style) then put something into the
+ //character encodings stack anyway so that the property end that pops
+ //off the stack will keep in sync
+ if (!m_pCurrentColl && IsListOrDropcap())
+ {
+ if (nWhich == RES_CHRATR_CJK_FONT)
+ {
+ if (!m_aFontSrcCJKCharSets.empty())
+ {
+ eSrcCharSet = m_aFontSrcCJKCharSets.top();
+ }
+ else
+ {
+ eSrcCharSet = RTL_TEXTENCODING_DONTKNOW;
+ }
+
+ m_aFontSrcCJKCharSets.push(eSrcCharSet);
+ }
+ else
+ {
+ if (!m_aFontSrcCharSets.empty())
+ {
+ eSrcCharSet = m_aFontSrcCharSets.top();
+ }
+ else
+ {
+ eSrcCharSet = RTL_TEXTENCODING_DONTKNOW;
+ }
+
+ m_aFontSrcCharSets.push(eSrcCharSet);
+ }
+ }
+ return false;
+ }
+
+ rtl_TextEncoding eDstCharSet = eSrcCharSet;
+
+ SvxFontItem aFont( eFamily, aName, OUString(), ePitch, eDstCharSet, nWhich);
+
+ if( bSetEnums )
+ {
+ if( m_pCurrentColl && m_nCurrentColl < m_vColl.size() ) // StyleDef
+ {
+ switch(nWhich)
+ {
+ default:
+ case RES_CHRATR_FONT:
+ m_vColl[m_nCurrentColl].m_eLTRFontSrcCharSet = eSrcCharSet;
+ break;
+ case RES_CHRATR_CTL_FONT:
+ m_vColl[m_nCurrentColl].m_eRTLFontSrcCharSet = eSrcCharSet;
+ break;
+ case RES_CHRATR_CJK_FONT:
+ m_vColl[m_nCurrentColl].m_eCJKFontSrcCharSet = eSrcCharSet;
+ break;
+ }
+ }
+ else if (IsListOrDropcap())
+ {
+ //Add character text encoding to stack
+ if (nWhich == RES_CHRATR_CJK_FONT)
+ m_aFontSrcCJKCharSets.push(eSrcCharSet);
+ else
+ m_aFontSrcCharSets.push(eSrcCharSet);
+ }
+ }
+
+ NewAttr( aFont ); // ...and insert
+
+ return true;
+}
+
+void SwWW8ImplReader::ResetCharSetVars()
+{
+ OSL_ENSURE(!m_aFontSrcCharSets.empty(),"no charset to remove");
+ if (!m_aFontSrcCharSets.empty())
+ m_aFontSrcCharSets.pop();
+}
+
+void SwWW8ImplReader::ResetCJKCharSetVars()
+{
+ OSL_ENSURE(!m_aFontSrcCJKCharSets.empty(),"no charset to remove");
+ if (!m_aFontSrcCJKCharSets.empty())
+ m_aFontSrcCJKCharSets.pop();
+}
+
+void SwWW8ImplReader::openFont(sal_uInt16 nFCode, sal_uInt16 nId)
+{
+ if (SetNewFontAttr(nFCode, true, nId) && m_pCurrentColl && m_xStyles)
+ {
+ // remember for simulating default font
+ if (RES_CHRATR_CJK_FONT == nId)
+ m_xStyles->mbCJKFontChanged = true;
+ else if (RES_CHRATR_CTL_FONT == nId)
+ m_xStyles->mbCTLFontChanged = true;
+ else
+ m_xStyles->mbFontChanged = true;
+ }
+}
+
+void SwWW8ImplReader::closeFont(sal_uInt16 nId)
+{
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
+ if (nId == RES_CHRATR_CJK_FONT)
+ ResetCJKCharSetVars();
+ else
+ ResetCharSetVars();
+}
+
+/*
+ Turn font on or off:
+*/
+void SwWW8ImplReader::Read_FontCode( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ //Note: this function needs to be able to run multiple times on the same data.
+ //It is called by Read_SubSuperProp to ensure that the current fontsize is known.
+
+ if (m_bSymbol) // if bSymbol, the symbol's font
+ return;
+
+// (see sprmCSymbol) is valid!
+ switch( nId )
+ {
+ case 113: //WW7
+ case NS_sprm::CRgFtc2::val: //"Other" font, override with BiDi if it exists
+ case NS_sprm::CFtcBi::val: //BiDi Font
+ nId = RES_CHRATR_CTL_FONT;
+ break;
+ case NS_sprm::v6::sprmCFtc: //WW6
+ case 111: //WW7
+ case NS_sprm::CRgFtc0::val:
+ nId = RES_CHRATR_FONT;
+ break;
+ case 112: //WW7
+ case NS_sprm::CRgFtc1::val:
+ nId = RES_CHRATR_CJK_FONT;
+ break;
+ default:
+ return ;
+ }
+
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+
+ if (nLen < 2) // end of attribute
+ {
+ if (eVersion <= ww::eWW6)
+ {
+ closeFont(RES_CHRATR_CTL_FONT);
+ closeFont(RES_CHRATR_CJK_FONT);
+ }
+ closeFont(nId);
+ }
+ else
+ {
+ sal_uInt16 nFCode = SVBT16ToUInt16( pData ); // font number
+ openFont(nFCode, nId);
+ if (eVersion <= ww::eWW6)
+ {
+ openFont(nFCode, RES_CHRATR_CJK_FONT);
+ openFont(nFCode, RES_CHRATR_CTL_FONT);
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_FontSize( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ switch( nId )
+ {
+ case 74: // WW2
+ case NS_sprm::v6::sprmCHps:
+ case NS_sprm::CHps::val:
+ nId = RES_CHRATR_FONTSIZE;
+ break;
+ case 85: //WW2
+ case 116: //WW7
+ case NS_sprm::CHpsBi::val:
+ nId = RES_CHRATR_CTL_FONTSIZE;
+ break;
+ default:
+ return ;
+ }
+
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+
+ if (nLen < (eVersion <= ww::eWW2 ? 1 : 2)) // end of attribute
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
+ if (eVersion <= ww::eWW6) // reset additionally the CTL size
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_CTL_FONTSIZE );
+ if (RES_CHRATR_FONTSIZE == nId) // reset additionally the CJK size
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_CJK_FONTSIZE );
+ }
+ else
+ {
+ // Font-Size in half points e.g. 10 = 1440 / ( 72 * 2 )
+ sal_uLong nFSize = eVersion <= ww::eWW2 ? *pData : SVBT16ToUInt16(pData);
+ nFSize*= 10;
+
+ SvxFontHeightItem aSz( nFSize, 100, nId );
+ NewAttr( aSz );
+ if (RES_CHRATR_FONTSIZE == nId) // set additionally the CJK size
+ {
+ aSz.SetWhich( RES_CHRATR_CJK_FONTSIZE );
+ NewAttr( aSz );
+ }
+ if (eVersion <= ww::eWW6) // set additionally the CTL size
+ {
+ aSz.SetWhich( RES_CHRATR_CTL_FONTSIZE );
+ NewAttr( aSz );
+ }
+ if (m_pCurrentColl && m_xStyles) // Style-Def ?
+ {
+ // remember for simulating default font size
+ if (nId == RES_CHRATR_CTL_FONTSIZE)
+ m_xStyles->mbFCTLSizeChanged = true;
+ else
+ {
+ m_xStyles->mbFSizeChanged = true;
+ if (eVersion <= ww::eWW6)
+ m_xStyles->mbFCTLSizeChanged= true;
+ }
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_CharSet(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 1)
+ { // end of attribute
+ m_eHardCharSet = RTL_TEXTENCODING_DONTKNOW;
+ return;
+ }
+ sal_uInt8 nfChsDiff = *pData;
+
+ if (nfChsDiff && nLen >= 2)
+ m_eHardCharSet = rtl_getTextEncodingFromWindowsCharset( *(pData + 1) );
+ else
+ m_eHardCharSet = RTL_TEXTENCODING_DONTKNOW;
+}
+
+void SwWW8ImplReader::Read_Language( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ switch( nId )
+ {
+ case NS_sprm::v6::sprmCLid:
+ case NS_sprm::CRgLid0_80::val:
+ case NS_sprm::CRgLid0::val:
+ nId = RES_CHRATR_LANGUAGE;
+ break;
+ case NS_sprm::CRgLid1_80::val:
+ case NS_sprm::CRgLid1::val:
+ nId = RES_CHRATR_CJK_LANGUAGE;
+ break;
+ case 83: // WW2
+ case 114: // WW7
+ case NS_sprm::CLidBi::val:
+ nId = RES_CHRATR_CTL_LANGUAGE;
+ break;
+ default:
+ return;
+ }
+
+ if (nLen < 2) // end of attribute
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
+ else
+ {
+ sal_uInt16 nLang = SVBT16ToUInt16( pData ); // Language-Id
+ NewAttr(SvxLanguageItem(LanguageType(nLang), nId));
+ }
+}
+
+/*
+ Turn on character style:
+*/
+void SwWW8ImplReader::Read_CColl( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 2) // end of attribute
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CHARFMT );
+ m_nCharFormat = -1;
+ return;
+ }
+ sal_uInt16 nId = SVBT16ToUInt16( pData ); // Style-Id (NOT Sprm-Id!)
+
+ if( nId >= m_vColl.size() || !m_vColl[nId].m_pFormat // invalid Id?
+ || m_vColl[nId].m_bColl ) // or paragraph style?
+ return; // then ignore
+
+ // if current on loading a TOX field, and current trying to apply a hyperlink character style,
+ // just ignore. For the hyperlinks inside TOX in MS Word is not same with a common hyperlink
+ // Character styles: without underline and blue font color. And such type style will be applied in others
+ // processes.
+ if (m_bLoadingTOXCache && m_vColl[nId].GetWWStyleId() == ww::stiHyperlink)
+ {
+ return;
+ }
+
+ NewAttr( SwFormatCharFormat( static_cast<SwCharFormat*>(m_vColl[nId].m_pFormat) ) );
+ m_nCharFormat = static_cast<short>(nId);
+}
+
+/*
+ Narrower or wider than normal:
+*/
+void SwWW8ImplReader::Read_Kern( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 2) // end of attribute
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_KERNING );
+ return;
+ }
+ sal_Int16 nKern = SVBT16ToUInt16( pData ); // Kerning in Twips
+ NewAttr( SvxKerningItem( nKern, RES_CHRATR_KERNING ) );
+}
+
+void SwWW8ImplReader::Read_FontKern( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 2) // end of attribute
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_AUTOKERN );
+ return;
+ }
+ sal_Int16 nAutoKern = SVBT16ToUInt16( pData ); // Kerning in Twips
+ NewAttr(SvxAutoKernItem(static_cast<bool>(nAutoKern), RES_CHRATR_AUTOKERN));
+}
+
+void SwWW8ImplReader::Read_CharShadow( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ //Has newer colour variant, ignore this old variant
+ if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::CShd::val).pSprm)
+ return;
+
+ if (nLen < 2)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_BACKGROUND );
+ }
+ else
+ {
+ WW8_SHD aSHD;
+ aSHD.SetWWValue( *reinterpret_cast<SVBT16 const *>(pData) );
+ SwWW8Shade aSh( m_bVer67, aSHD );
+
+ NewAttr( SvxBrushItem( aSh.m_aColor, RES_CHRATR_BACKGROUND ));
+
+ // Add a marker to the grabbag indicating that character background was imported from MSO shading
+ SfxGrabBagItem aGrabBag = *GetFormatAttr(RES_CHRATR_GRABBAG);
+ std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
+ rMap.insert(std::pair<OUString, css::uno::Any>("CharShadingMarker",uno::Any(true)));
+ NewAttr(aGrabBag);
+ }
+}
+
+void SwWW8ImplReader::Read_TextBackColor(sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen <= 0)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_BACKGROUND );
+ }
+ else
+ {
+ OSL_ENSURE(nLen == 10, "Len of para back colour not 10!");
+ if (nLen != 10)
+ return;
+ Color aColour(ExtractColour(pData, m_bVer67));
+ NewAttr(SvxBrushItem(aColour, RES_CHRATR_BACKGROUND));
+
+ // Add a marker to the grabbag indicating that character background was imported from MSO shading
+ SfxGrabBagItem aGrabBag = *GetFormatAttr(RES_CHRATR_GRABBAG);
+ std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
+ rMap.insert(std::pair<OUString, css::uno::Any>("CharShadingMarker",uno::Any(true)));
+ NewAttr(aGrabBag);
+ }
+}
+
+void SwWW8ImplReader::Read_CharHighlight(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ // MS Word completely ignores character highlighting in character styles.
+ if ( m_pCurrentColl && m_pCurrentColl->Which() == RES_CHRFMT )
+ return;
+
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_HIGHLIGHT );
+ }
+ else
+ {
+ sal_uInt8 b = *pData; // Parameter: 0 = Auto, 1..16 colors
+
+ if( b > 16 ) // invalid -> Black
+ b = 0; // Auto -> Black
+
+ Color aCol(GetCol(b));
+ NewAttr( SvxBrushItem( aCol , RES_CHRATR_HIGHLIGHT ));
+ }
+}
+
+void SwWW8ImplReader::Read_NoLineNumb(sal_uInt16 , const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 0) // end of attribute
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_LINENUMBER );
+ return;
+ }
+ SwFormatLineNumber aLN;
+ if (const SwFormatLineNumber* pLN = GetFormatAttr(RES_LINENUMBER))
+ {
+ aLN.SetStartValue( pLN->GetStartValue() );
+ }
+
+ aLN.SetCountLines(pData && nLen >= 1 && (0 == *pData));
+ NewAttr( aLN );
+}
+
+static bool lcl_HasExplicitLeft(const WW8PLCFMan *pPlcxMan, bool bVer67)
+{
+ WW8PLCFx_Cp_FKP *pPap = pPlcxMan ? pPlcxMan->GetPapPLCF() : nullptr;
+ if (pPap)
+ {
+ if (bVer67)
+ return pPap->HasSprm(NS_sprm::v6::sprmPDxaLeft).pSprm;
+ else
+ return (pPap->HasSprm(NS_sprm::PDxaLeft80::val).pSprm || pPap->HasSprm(NS_sprm::PDxaLeft::val).pSprm);
+ }
+ return false;
+}
+
+// Sprm 16, 17
+void SwWW8ImplReader::Read_LR( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 2) // end of attribute
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_FIRSTLINE);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_TEXTLEFT);
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_RIGHT);
+ return;
+ }
+
+ short nPara = SVBT16ToUInt16( pData );
+
+ SfxPoolItem const* pItem(GetFormatAttr(RES_MARGIN_FIRSTLINE));
+ ::std::unique_ptr<SvxFirstLineIndentItem> pFirstLine(pItem
+ ? static_cast<SvxFirstLineIndentItem*>(pItem->Clone())
+ : new SvxFirstLineIndentItem(RES_MARGIN_FIRSTLINE));
+ pItem = GetFormatAttr(RES_MARGIN_TEXTLEFT);
+ ::std::unique_ptr<SvxTextLeftMarginItem> pLeftMargin(pItem
+ ? static_cast<SvxTextLeftMarginItem*>(pItem->Clone())
+ : new SvxTextLeftMarginItem(RES_MARGIN_TEXTLEFT));
+ pItem = GetFormatAttr(RES_MARGIN_RIGHT);
+ ::std::unique_ptr<SvxRightMarginItem> pRightMargin(pItem
+ ? static_cast<SvxRightMarginItem*>(pItem->Clone())
+ : new SvxRightMarginItem(RES_MARGIN_RIGHT));
+
+ // Fix the regression issue: #i99822#: Discussion?
+ // Since the list level formatting doesn't apply into paragraph style
+ // for list levels of mode LABEL_ALIGNMENT.(see ww8par3.cxx
+ // W8ImplReader::RegisterNumFormatOnTextNode).
+ // Need to apply the list format to the paragraph here.
+ SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode();
+ if (pTextNode
+ && pTextNode->AreListLevelIndentsApplicable() != ::sw::ListLevelIndents::No)
+ {
+ SwNumRule * pNumRule = pTextNode->GetNumRule();
+ if( pNumRule )
+ {
+ sal_uInt8 nLvl = static_cast< sal_uInt8 >(pTextNode->GetActualListLevel());
+ const SwNumFormat* pFormat = pNumRule->GetNumFormat( nLvl );
+ if ( pFormat && pFormat->GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ pLeftMargin->SetTextLeft(pFormat->GetIndentAt());
+ pFirstLine->SetTextFirstLineOffset(static_cast<short>(pFormat->GetFirstLineIndent()));
+ // make paragraph have hard-set indent attributes
+ pTextNode->SetAttr(*pLeftMargin);
+ pTextNode->SetAttr(*pFirstLine);
+ }
+ }
+ }
+
+ /*
+ The older word sprms mean left/right, while the new ones mean before/after.
+ Writer now also works with before after, so when we see old left/right and
+ we're RTL. We swap them
+ */
+ if (IsRightToLeft())
+ {
+ switch (nId)
+ {
+ //Left becomes after;
+ case NS_sprm::v6::sprmPDxaLeft:
+ nId = NS_sprm::v6::sprmPDxaRight;
+ break;
+ case NS_sprm::PDxaLeft80::val:
+ nId = NS_sprm::PDxaRight80::val;
+ break;
+ //Right becomes before;
+ case NS_sprm::v6::sprmPDxaRight:
+ nId = NS_sprm::v6::sprmPDxaLeft;
+ break;
+ case NS_sprm::PDxaRight80::val:
+ nId = NS_sprm::PDxaLeft80::val;
+ break;
+ }
+ }
+
+ bool bFirstLinOfstSet( false ); // #i103711#
+ bool bLeftIndentSet( false ); // #i105414#
+
+ switch (nId)
+ {
+ //sprmPDxaLeft
+ case NS_sprm::v6::sprmPDxaLeft:
+ case NS_sprm::PDxaLeft80::val:
+ case NS_sprm::PDxaLeft::val:
+ pLeftMargin->SetTextLeft(nPara);
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
+ {
+ m_vColl[m_nCurrentColl].m_bListRelevantIndentSet = true;
+ }
+ bLeftIndentSet = true; // #i105414#
+ break;
+ //sprmPDxaLeft1
+ case NS_sprm::v6::sprmPDxaLeft1:
+ case NS_sprm::PDxaLeft180::val:
+ case NS_sprm::PDxaLeft1::val:
+ /*
+ As part of an attempt to break my spirit ww 8+ formats can contain
+ ww 7- lists. If they do and the list is part of the style, then
+ when removing the list from a paragraph of that style there
+ appears to be a bug where the hanging indent value which the list
+ set is still factored into the left indent of the paragraph. Its
+ not listed in the winword dialogs, but it is clearly there. So if
+ our style has a broken ww 7- list and we know that the list has
+ been removed then we will factor the original list applied hanging
+ into our calculation.
+ */
+ if (m_xPlcxMan && m_nCurrentColl < m_vColl.size() && m_vColl[m_nCurrentColl].m_bHasBrokenWW6List)
+ {
+ SprmResult aIsZeroed = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PIlfo::val);
+ if (aIsZeroed.pSprm && aIsZeroed.nRemainingData >= 1 && *aIsZeroed.pSprm == 0)
+ {
+ const SvxFirstLineIndentItem & rFirstLine =
+ m_vColl[m_nCurrentColl].m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE);
+ nPara = nPara - rFirstLine.GetTextFirstLineOffset();
+ }
+ }
+
+ pFirstLine->SetTextFirstLineOffset(nPara);
+
+ if (!m_pCurrentColl)
+ {
+ if (const SwTextNode* pNode = m_pPaM->GetPointNode().GetTextNode())
+ {
+ if ( const SwNumFormat *pNumFormat = GetNumFormatFromTextNode(*pNode) )
+ {
+ if (!lcl_HasExplicitLeft(m_xPlcxMan.get(), m_bVer67))
+ {
+ pLeftMargin->SetTextLeft(pNumFormat->GetIndentAt());
+
+ // If have not explicit left, set number format list tab position is doc default tab
+ const SvxTabStopItem *pDefaultStopItem = m_rDoc.GetAttrPool().GetPoolDefaultItem(RES_PARATR_TABSTOP);
+ if ( pDefaultStopItem && pDefaultStopItem->Count() > 0 )
+ const_cast<SwNumFormat*>(pNumFormat)->SetListtabPos( const_cast<SvxTabStop&>((*pDefaultStopItem)[0]).GetTabPos() );
+ }
+ }
+ }
+ }
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
+ {
+ m_vColl[m_nCurrentColl].m_bListRelevantIndentSet = true;
+ }
+ bFirstLinOfstSet = true; // #i103711#
+ break;
+ //sprmPDxaRight
+ case NS_sprm::v6::sprmPDxaRight:
+ case NS_sprm::PDxaRight80::val:
+ case NS_sprm::PDxaRight::val:
+ pRightMargin->SetRight(nPara);
+ break;
+ default:
+ return;
+ }
+
+ NewAttr(*pFirstLine, bFirstLinOfstSet, false); // #i103711#, #i105414#
+ NewAttr(*pLeftMargin, false, bLeftIndentSet);
+ NewAttr(*pRightMargin, false, false);
+}
+
+// Sprm 20
+void SwWW8ImplReader::Read_LineSpace( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+// comment see Read_UL()
+ if (m_bStyNormal && m_bWWBugNormal)
+ return;
+
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+
+ if (nLen < (eVersion <= ww::eWW2 ? 3 : 4))
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_LINESPACING );
+ if( !( m_nIniFlags & WW8FL_NO_IMPLPASP ) )
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE );
+ return;
+ }
+
+ short nSpace = SVBT16ToUInt16( pData );
+ short nMulti = (eVersion <= ww::eWW2) ? 1 : SVBT16ToUInt16( pData + 2 );
+
+ SvxLineSpaceRule eLnSpc;
+ if( 0 > nSpace )
+ {
+ nSpace = -nSpace;
+ eLnSpc = SvxLineSpaceRule::Fix;
+ }
+ else
+ eLnSpc = SvxLineSpaceRule::Min;
+
+ // WW has implicit additional paragraph spacing depending on
+ // the line spacing. It is, for "exactly", 0.8 * line spacing "before"
+ // and 0.2 * line spacing "after".
+ // For "at least", it is 1 * line spacing "before" and 0 * line spacing "after".
+ // For "multiple", it is 0 "before" and min(0cm, FontSize*(nFach-1)) "after".
+
+ // SW also has implicit line spacing. It is, for "at least"
+ // 1 * line spacing "before" and 0 "after".
+ // For proportional, it is min(0cm, FontSize*(nFach-1)) both "before" and "after".
+
+ sal_uInt16 nSpaceTw = 0;
+
+ SvxLineSpacingItem aLSpc( LINE_SPACE_DEFAULT_HEIGHT, RES_PARATR_LINESPACING );
+
+ if( 1 == nMulti ) // MultilineSpace ( proportional )
+ {
+ tools::Long n = nSpace * 10 / 24; // WW: 240 = 100%, SW: 100 = 100%
+
+ // here n is in [0..13653]
+ aLSpc.SetPropLineSpace( o3tl::narrowing<sal_uInt16>(n) );
+ const SvxFontHeightItem* pH = GetFormatAttr( RES_CHRATR_FONTSIZE );
+ nSpaceTw = o3tl::narrowing<sal_uInt16>( n * pH->GetHeight() / 100 );
+ }
+ else // Fixed / Minimum
+ {
+ // for negative space, the distance is "exact", otherwise "at least"
+ nSpaceTw = o3tl::narrowing<sal_uInt16>(nSpace);
+ aLSpc.SetLineHeight( nSpaceTw );
+ aLSpc.SetLineSpaceRule( eLnSpc);
+ }
+ NewAttr( aLSpc );
+ if (m_xSFlyPara)
+ m_xSFlyPara->nLineSpace = nSpaceTw; // linespace for graphics APOs
+}
+
+//#i18519# AutoSpace value depends on Dop fDontUseHTMLAutoSpacing setting
+sal_uInt16 SwWW8ImplReader::GetParagraphAutoSpace(bool fDontUseHTMLAutoSpacing)
+{
+ if (fDontUseHTMLAutoSpacing)
+ return 100; //Seems to be always 5points in this case
+ else
+ return 280; //Seems to be always 14points in this case
+}
+
+void SwWW8ImplReader::Read_ParaAutoBefore(sal_uInt16, const sal_uInt8 *pData, short nLen)
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_UL_SPACE);
+ return;
+ }
+
+ if (*pData)
+ {
+ SvxULSpaceItem aUL(*GetFormatAttr(RES_UL_SPACE));
+ aUL.SetUpper(GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
+ NewAttr(aUL);
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
+ m_vColl[m_nCurrentColl].m_bParaAutoBefore = true;
+ else
+ m_bParaAutoBefore = true;
+ }
+ else
+ {
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
+ m_vColl[m_nCurrentColl].m_bParaAutoBefore = false;
+ else
+ m_bParaAutoBefore = false;
+ }
+}
+
+void SwWW8ImplReader::Read_ParaAutoAfter(sal_uInt16, const sal_uInt8 *pData, short nLen)
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_UL_SPACE);
+ return;
+ }
+
+ if (*pData)
+ {
+ SvxULSpaceItem aUL(*GetFormatAttr(RES_UL_SPACE));
+ aUL.SetLower(GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing));
+ NewAttr(aUL);
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
+ m_vColl[m_nCurrentColl].m_bParaAutoAfter = true;
+ else
+ m_bParaAutoAfter = true;
+ }
+ else
+ {
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size())
+ m_vColl[m_nCurrentColl].m_bParaAutoAfter = false;
+ else
+ m_bParaAutoAfter = false;
+ }
+}
+
+// Sprm 21, 22
+void SwWW8ImplReader::Read_UL( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ // A workaround for an error in WW: For nProduct == 0c03d, usually
+ // DyaAfter 240 (delta y distance after, comment of the translator)
+ // is incorrectly inserted into style "Normal", even though it isn't there.
+ // Using the ini flag WW8FL_NO_STY_DYA you can force this behavior for other
+ // WW versions as well.
+ // OSL_ENSURE( !bStyNormal || bWWBugNormal, "+This Document may point to a bug
+ // in the WW version used for creating it. If the Styles <Standard> resp.
+ // <Normal> differentiate between WW and SW in paragraph or line spacing,
+ // then please send this Document to SH.");
+ // bWWBugNormal is not a sufficient criterion for this distance being wrong.
+
+ if (nLen < 2)
+ {
+ // end of attribute
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE );
+ return;
+ }
+ short nPara = SVBT16ToUInt16( pData );
+ if( nPara < 0 )
+ nPara = -nPara;
+
+ SvxULSpaceItem aUL( *GetFormatAttr( RES_UL_SPACE ));
+
+ switch( nId )
+ {
+ //sprmPDyaBefore
+ case NS_sprm::v6::sprmPDyaBefore:
+ case NS_sprm::PDyaBefore::val:
+ aUL.SetUpper( nPara );
+ break;
+ //sprmPDyaAfter
+ case NS_sprm::v6::sprmPDyaAfter:
+ case NS_sprm::PDyaAfter::val:
+ aUL.SetLower( nPara );
+ break;
+ default:
+ return;
+ }
+
+ NewAttr( aUL );
+}
+
+void SwWW8ImplReader::Read_ParaContextualSpacing( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE );
+ return;
+ }
+ SvxULSpaceItem aUL( *GetFormatAttr( RES_UL_SPACE ));
+ aUL.SetContextValue(*pData != 0);
+ NewAttr( aUL );
+}
+
+void SwWW8ImplReader::Read_LineBreakClear(sal_uInt16 /*nId*/, const sal_uInt8* pData, short nLen)
+{
+ if (nLen == -1 && m_oLineBreakClear.has_value())
+ {
+ SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode();
+ sal_Int32 nPos = m_pPaM->GetPoint()->GetContentIndex();
+ if (!pText || !nPos)
+ {
+ // There should have been a linebreak char.
+ return;
+ }
+
+ // Replace the linebreak char with a clearing break.
+ --nPos;
+ m_pPaM->SetMark();
+ m_pPaM->GetMark()->AdjustContent(-1);
+ m_rDoc.getIDocumentContentOperations().DeleteRange(*m_pPaM);
+ m_pPaM->DeleteMark();
+ SwFormatLineBreak aLineBreak(*m_oLineBreakClear);
+ m_oLineBreakClear.reset();
+ pText->InsertItem(aLineBreak, nPos, nPos);
+ }
+
+ if (nLen < 1)
+ {
+ return;
+ }
+
+ sal_uInt8 nClear = pData[0];
+ if (nClear > 3)
+ {
+ return;
+ }
+
+ auto eClear = static_cast<SwLineBreakClear>(nClear);
+ m_oLineBreakClear = eClear;
+}
+
+void SwWW8ImplReader::Read_IdctHint( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ // sprmcidcthint (opcode 0x286f) specifies a script bias for the text in the run.
+ // for unicode characters that are shared between far east and non-far east scripts,
+ // this property determines what font and language the character will use.
+ // when this value is 0, text properties bias towards non-far east properties.
+ // when this value is 1, text properties bias towards far east properties.
+ // when this value is 2, text properties bias towards complex properties.
+ if (nLen < 1) //Property end
+ {
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_CHRATR_IDCTHINT);
+ }
+ else //Property start
+ {
+ NewAttr(SfxInt16Item(RES_CHRATR_IDCTHINT, *pData));
+ }
+}
+
+void SwWW8ImplReader::Read_Justify( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_ADJUST );
+ return;
+ }
+
+ SvxAdjust eAdjust(SvxAdjust::Left);
+ bool bDistributed = false;
+ switch (*pData)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ eAdjust = SvxAdjust::Center;
+ break;
+ case 2:
+ eAdjust = SvxAdjust::Right;
+ break;
+ case 3:
+ eAdjust = SvxAdjust::Block;
+ break;
+ case 4:
+ eAdjust = SvxAdjust::Block;
+ bDistributed = true;
+ break;
+ }
+ SvxAdjustItem aAdjust(eAdjust, RES_PARATR_ADJUST);
+ if (bDistributed)
+ aAdjust.SetLastBlock(SvxAdjust::Block);
+
+ NewAttr(aAdjust);
+ SetRelativeJustify( nId != NS_sprm::PJc80::val );
+}
+
+bool SwWW8ImplReader::IsRightToLeft()
+{
+ bool bRTL = false;
+ SprmResult aDir;
+ if (m_xPlcxMan)
+ aDir = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PFBiDi::val);
+ if (aDir.pSprm && aDir.nRemainingData >= 1)
+ bRTL = *aDir.pSprm != 0;
+ else
+ {
+ const SvxFrameDirectionItem* pItem = GetFormatAttr(RES_FRAMEDIR);
+ if (pItem && (pItem->GetValue() == SvxFrameDirection::Horizontal_RL_TB))
+ bRTL = true;
+ }
+ return bRTL;
+}
+
+void SwWW8ImplReader::Read_RTLJustify( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_ADJUST );
+ return;
+ }
+
+ //If we are in a ltr paragraph this is the same as normal Justify,
+ //If we are in a rtl paragraph the meaning is reversed.
+ if (!IsRightToLeft())
+ Read_Justify(nId, pData, nLen);
+ else
+ {
+ SvxAdjust eAdjust(SvxAdjust::Right);
+ bool bDistributed = false;
+ switch (*pData)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ eAdjust = SvxAdjust::Center;
+ break;
+ case 2:
+ eAdjust = SvxAdjust::Left;
+ break;
+ case 3:
+ eAdjust = SvxAdjust::Block;
+ break;
+ case 4:
+ eAdjust = SvxAdjust::Block;
+ bDistributed = true;
+ break;
+ }
+ SvxAdjustItem aAdjust(eAdjust, RES_PARATR_ADJUST);
+ if (bDistributed)
+ aAdjust.SetLastBlock(SvxAdjust::Block);
+
+ NewAttr(aAdjust);
+ SetRelativeJustify( true );
+ }
+}
+
+void SwWW8ImplReader::Read_BoolItem( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ switch( nId )
+ {
+ case NS_sprm::PFKinsoku::val:
+ nId = RES_PARATR_FORBIDDEN_RULES;
+ break;
+ case NS_sprm::PFOverflowPunct::val:
+ nId = RES_PARATR_HANGINGPUNCTUATION;
+ break;
+ case NS_sprm::PFAutoSpaceDE::val:
+ nId = RES_PARATR_SCRIPTSPACE;
+ break;
+ default:
+ OSL_ENSURE( false, "wrong Id" );
+ return ;
+ }
+
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId );
+ else
+ {
+ std::unique_ptr<SfxBoolItem> pI(static_cast<SfxBoolItem*>(GetDfltAttr( nId )->Clone()));
+ pI->SetValue( 0 != *pData );
+ NewAttr( *pI );
+ }
+}
+
+void SwWW8ImplReader::Read_Emphasis( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_EMPHASIS_MARK );
+ else
+ {
+ LanguageType nLang;
+ //Check to see if there is an up and coming cjk language property. If
+ //there is use it, if there is not fall back to the currently set one.
+ //Only the cjk language setting seems to matter to word, the western
+ //one is ignored
+ SprmResult aLang;
+ if (m_xPlcxMan)
+ aLang = m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::CRgLid1_80::val);
+
+ if (aLang.pSprm && aLang.nRemainingData >= 2)
+ nLang = LanguageType(SVBT16ToUInt16(aLang.pSprm));
+ else
+ {
+ const SvxLanguageItem * pLangItem = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE);
+ nLang = pLangItem->GetLanguage();
+ }
+
+ FontEmphasisMark nVal;
+ switch( *pData )
+ {
+ case 0:
+ nVal = FontEmphasisMark::NONE;
+ break;
+ case 2:
+ if (MsLangId::isKorean(nLang) || MsLangId::isTraditionalChinese(nLang))
+ nVal = (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove);
+ else if (nLang == LANGUAGE_JAPANESE)
+ nVal = (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove);
+ else
+ nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow);
+ break;
+ case 3:
+ nVal = (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove);
+ break;
+ case 4:
+ nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow);
+ break;
+ case 1:
+ if (MsLangId::isSimplifiedChinese(nLang))
+ nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow);
+ else
+ nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove);
+ break;
+ default:
+ nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove);
+ break;
+ }
+
+ NewAttr( SvxEmphasisMarkItem( nVal, RES_CHRATR_EMPHASIS_MARK ) );
+ }
+}
+
+void SwWW8ImplReader::Read_ScaleWidth( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 2)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_SCALEW );
+ else
+ {
+ sal_uInt16 nVal = SVBT16ToUInt16( pData );
+ //The number must be between 1 and 600
+ if (nVal < 1 || nVal > 600)
+ nVal = 100;
+ NewAttr( SvxCharScaleWidthItem( nVal, RES_CHRATR_SCALEW ) );
+ }
+}
+
+void SwWW8ImplReader::Read_Relief( sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_RELIEF );
+ else
+ {
+ if( *pData )
+ {
+// not so easy because this is also a toggle attribute!
+// 2 x emboss on -> no emboss !!!
+// the actual value must be searched over the stack / template
+
+ const SvxCharReliefItem* pOld = GetFormatAttr( RES_CHRATR_RELIEF );
+ FontRelief nNewValue = NS_sprm::CFImprint::val == nId ? FontRelief::Engraved
+ : ( NS_sprm::CFEmboss::val == nId ? FontRelief::Embossed
+ : FontRelief::NONE );
+ if( pOld->GetValue() == nNewValue )
+ {
+ if( FontRelief::NONE != nNewValue )
+ nNewValue = FontRelief::NONE;
+ }
+ NewAttr( SvxCharReliefItem( nNewValue, RES_CHRATR_RELIEF ));
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_TextAnim(sal_uInt16 /*nId*/, const sal_uInt8* pData, short nLen)
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_BLINK);
+ else
+ {
+ if (*pData)
+ {
+ bool bBlink;
+
+ // The 7 animated text effects available in word all get
+ // mapped to a blinking text effect in LibreOffice
+ // 0 no animation 1 Las Vegas lights
+ // 2 background blink 3 sparkle text
+ // 4 marching ants 5 marching red ants
+ // 6 shimmer
+ bBlink = *pData > 0 && *pData < 7;
+
+ NewAttr(SvxBlinkItem(bBlink, RES_CHRATR_BLINK));
+ }
+ }
+}
+
+SwWW8Shade::SwWW8Shade(bool bVer67, const WW8_SHD& rSHD)
+{
+ sal_uInt8 b = rSHD.GetFore();
+ OSL_ENSURE(b < 17, "ww8: colour out of range");
+ if (b >= 17)
+ b = 0;
+
+ Color nFore(SwWW8ImplReader::GetCol(b));
+
+ b = rSHD.GetBack();
+ OSL_ENSURE(b < 17, "ww8: colour out of range");
+ if( b >= 17 )
+ b = 0;
+
+ Color nBack(SwWW8ImplReader::GetCol(b));
+
+ b = rSHD.GetStyle(bVer67);
+
+ SetShade(nFore, nBack, b);
+}
+
+void SwWW8Shade::SetShade(Color nFore, Color nBack, sal_uInt16 nIndex)
+{
+ static const sal_uLong eMSGrayScale[] =
+ {
+ // Clear-Brush
+ 0, // 0 clear
+ // Solid-Brush
+ 1000, // 1 solid
+ // Percent values
+ 50, // 2 pct5
+ 100, // 3 pct10
+ 200, // 4 pct20
+ 250, // 5 pct25
+ 300, // 6 pct30
+ 400, // 7 pct40
+ 500, // 8 pct50
+ 600, // 9 pct60
+ 700, // 10 pct70
+ 750, // 11 pct75
+ 800, // 12 pct80
+ 900, // 13 pct90
+ // Special cases
+ 333, // 14 Dark Horizontal
+ 333, // 15 Dark Vertical
+ 333, // 16 Dark Forward Diagonal
+ 333, // 17 Dark Backward Diagonal
+ 333, // 18 Dark Cross
+ 333, // 19 Dark Diagonal Cross
+ 333, // 20 Horizontal
+ 333, // 21 Vertical
+ 333, // 22 Forward Diagonal
+ 333, // 23 Backward Diagonal
+ 333, // 24 Cross
+ 333, // 25 Diagonal Cross
+ // Undefined values in DOC spec-sheet
+ 500, // 26
+ 500, // 27
+ 500, // 28
+ 500, // 29
+ 500, // 30
+ 500, // 31
+ 500, // 32
+ 500, // 33
+ 500, // 34
+ // Different shading types
+ 25, // 35 [available in DOC, not available in DOCX]
+ 75, // 36 [available in DOC, not available in DOCX]
+ 125, // 37 pct12
+ 150, // 38 pct15
+ 175, // 39 [available in DOC, not available in DOCX]
+ 225, // 40 [available in DOC, not available in DOCX]
+ 275, // 41 [available in DOC, not available in DOCX]
+ 325, // 42 [available in DOC, not available in DOCX]
+ 350, // 43 pct35
+ 375, // 44 pct37
+ 425, // 45 [available in DOC, not available in DOCX]
+ 450, // 46 pct45
+ 475, // 47 [available in DOC, not available in DOCX]
+ 525, // 48 [available in DOC, not available in DOCX]
+ 550, // 49 pct55
+ 575, // 50 [available in DOC, not available in DOCX]
+ 625, // 51 pct62
+ 650, // 52 pct65
+ 675, // 53 [available in DOC, not available in DOCX]
+ 725, // 54 [available in DOC, not available in DOCX]
+ 775, // 55 [available in DOC, not available in DOCX]
+ 825, // 56 [available in DOC, not available in DOCX]
+ 850, // 57 pct85
+ 875, // 58 pct87
+ 925, // 59 [available in DOC, not available in DOCX]
+ 950, // 60 pct95
+ 975 // 61 [available in DOC, not available in DOCX]
+ };// 62
+
+ //NO auto for shading so Foreground: Auto = Black
+ if (nFore == COL_AUTO)
+ nFore = COL_BLACK;
+
+ //NO auto for shading so background: Auto = White
+ Color nUseBack = nBack;
+ if (nUseBack == COL_AUTO)
+ nUseBack = COL_WHITE;
+
+ if( nIndex >= SAL_N_ELEMENTS( eMSGrayScale ) )
+ nIndex = 0;
+
+ sal_uLong nWW8BrushStyle = eMSGrayScale[nIndex];
+
+ switch (nWW8BrushStyle)
+ {
+ case 0: // Null-Brush
+ m_aColor = nBack;
+ break;
+ default:
+ {
+ Color aForeColor(nFore);
+ Color aBackColor(nUseBack);
+
+ sal_uInt32 nRed = aForeColor.GetRed() * nWW8BrushStyle;
+ sal_uInt32 nGreen = aForeColor.GetGreen() * nWW8BrushStyle;
+ sal_uInt32 nBlue = aForeColor.GetBlue() * nWW8BrushStyle;
+ nRed += aBackColor.GetRed() * (1000 - nWW8BrushStyle);
+ nGreen += aBackColor.GetGreen()* (1000 - nWW8BrushStyle);
+ nBlue += aBackColor.GetBlue() * (1000 - nWW8BrushStyle);
+
+ m_aColor = Color( nRed/1000, nGreen/1000, nBlue/1000 );
+ }
+ break;
+ }
+}
+
+void SwWW8ImplReader::Read_Shade( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::PShd::val).pSprm)
+ return;
+
+ if (nLen < 2)
+ {
+ // end of attribute
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLSTYLE );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLCOLOR );
+ }
+ else
+ {
+ WW8_SHD aSHD;
+ aSHD.SetWWValue( *reinterpret_cast<SVBT16 const *>(pData) );
+ SwWW8Shade aSh( m_bVer67, aSHD );
+
+ NewAttr( XFillStyleItem(drawing::FillStyle_SOLID) );
+ NewAttr( XFillColorItem(OUString(), aSh.m_aColor) );
+ }
+}
+
+void SwWW8ImplReader::Read_ParaBackColor(sal_uInt16, const sal_uInt8* pData, short nLen)
+{
+ if (nLen <= 0)
+ {
+ // end of attribute
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLSTYLE );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLCOLOR );
+ }
+ else
+ {
+ OSL_ENSURE(nLen == 10, "Len of para back colour not 10!");
+ if (nLen != 10)
+ return;
+
+ const Color aColor = ExtractColour(pData, m_bVer67);
+ NewAttr( XFillColorItem(OUString(), aColor) );
+ if ( aColor == COL_AUTO )
+ NewAttr( XFillStyleItem(drawing::FillStyle_NONE) );
+ else
+ NewAttr( XFillStyleItem(drawing::FillStyle_SOLID) );
+ }
+}
+
+Color SwWW8ImplReader::ExtractColour(const sal_uInt8* &rpData, bool bVer67)
+{
+ OSL_ENSURE(!bVer67, "Impossible");
+ Color nFore = msfilter::util::BGRToRGB(SVBT32ToUInt32(rpData));
+ rpData+=4;
+ Color nBack = msfilter::util::BGRToRGB(SVBT32ToUInt32(rpData));
+ rpData+=4;
+ sal_uInt16 nIndex = SVBT16ToUInt16(rpData);
+ rpData+=2;
+ //Being a transparent background colour doesn't actually show the page
+ //background through, it merely acts like white
+ if (nBack == Color(ColorTransparency, 0xFF000000))
+ nBack = COL_AUTO;
+ OSL_ENSURE(nBack == COL_AUTO || !nBack.IsTransparent(),
+ "ww8: don't know what to do with such a transparent bg colour, report");
+ SwWW8Shade aShade(nFore, nBack, nIndex);
+ return aShade.m_aColor;
+}
+
+void SwWW8ImplReader::Read_TextVerticalAdjustment( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if( nLen <= 0 )
+ return;
+
+ drawing::TextVerticalAdjust nVA = drawing::TextVerticalAdjust_TOP;
+ switch( *pData )
+ {
+ case 1:
+ nVA = drawing::TextVerticalAdjust_CENTER;
+ break;
+ case 2: //justify
+ nVA = drawing::TextVerticalAdjust_BLOCK;
+ break;
+ case 3:
+ nVA = drawing::TextVerticalAdjust_BOTTOM;
+ break;
+ default:
+ break;
+ }
+ m_aSectionManager.SetCurrentSectionVerticalAdjustment( nVA );
+}
+
+void SwWW8ImplReader::Read_Border(sal_uInt16 , const sal_uInt8*, short nLen)
+{
+ if (nLen < 0)
+ {
+ if( m_bHasBorder )
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_BOX );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_SHADOW );
+ m_bHasBorder = false;
+ }
+ }
+ else if( !m_bHasBorder )
+ {
+ // the borders on all four sides are bundled. That
+ // simplifies the administration, i.e., the box does not have
+ // to be put on and removed from CtrlStack 4 times.
+ m_bHasBorder = true;
+
+ WW8_BRCVer9_5 aBrcs; // Top, Left, Bottom, Right, Between
+ sal_uInt8 nBorder;
+
+ if( m_pCurrentColl )
+ nBorder = ::lcl_ReadBorders(m_bVer67, aBrcs, nullptr, m_xStyles.get());
+ else
+ nBorder = ::lcl_ReadBorders(m_bVer67, aBrcs, m_xPlcxMan ? m_xPlcxMan->GetPapPLCF() : nullptr);
+
+ if( nBorder ) // Border
+ {
+ bool bIsB = IsBorder(aBrcs, true);
+ if (!InLocalApo() || !bIsB || (m_xWFlyPara && !m_xWFlyPara->bBorderLines))
+ {
+ // Do not turn *on* borders in APO, since otherwise
+ // I get the Fly border twice;
+ // but only when it is set on in the Fly, skip it;
+ // otherwise there is none at all!
+
+ // even if no border is set, the attribute has to be set,
+ // otherwise it's not possible to turn off the style attribute.
+ const SvxBoxItem* pBox = GetFormatAttr( RES_BOX );
+ std::shared_ptr<SvxBoxItem> aBox(std::make_shared<SvxBoxItem>(RES_BOX));
+ if (pBox)
+ aBox.reset(pBox->Clone());
+ short aSizeArray[5]={0};
+
+ SetBorder(*aBox, aBrcs, &aSizeArray[0], nBorder);
+
+ tools::Rectangle aInnerDist;
+ GetBorderDistance( aBrcs, aInnerDist );
+
+ if (nBorder & (1 << WW8_LEFT))
+ aBox->SetDistance( o3tl::narrowing<sal_uInt16>(aInnerDist.Left()), SvxBoxItemLine::LEFT );
+
+ if (nBorder & (1 << WW8_TOP))
+ aBox->SetDistance( o3tl::narrowing<sal_uInt16>(aInnerDist.Top()), SvxBoxItemLine::TOP );
+
+ if (nBorder & (1 << WW8_RIGHT))
+ aBox->SetDistance( o3tl::narrowing<sal_uInt16>(aInnerDist.Right()), SvxBoxItemLine::RIGHT );
+
+ if (nBorder & (1 << WW8_BOT))
+ aBox->SetDistance( o3tl::narrowing<sal_uInt16>(aInnerDist.Bottom()), SvxBoxItemLine::BOTTOM );
+
+ NewAttr( *aBox );
+
+ SvxShadowItem aS(RES_SHADOW);
+ // Word only allows shadows on visible borders
+ if ( aBox->CalcLineSpace( SvxBoxItemLine::RIGHT ) )
+ SetShadow( aS, &aSizeArray[0], aBrcs[WW8_RIGHT] );
+ NewAttr( aS );
+ }
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_CharBorder(sal_uInt16 nId, const sal_uInt8* pData, short nLen )
+{
+ //Ignore this old border type
+ //if (!bVer67 && pPlcxMan && pPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::CBrc::val))
+ // return;
+
+ if (nLen < 0)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_BOX );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_SHADOW );
+ }
+ else
+ {
+ const SvxBoxItem* pBox = GetFormatAttr( RES_CHRATR_BOX );
+ if( pBox )
+ {
+ std::unique_ptr<SvxBoxItem> aBoxItem(pBox->Clone());
+ WW8_BRCVer9 aBrc;
+ int nBrcVer = (nId == NS_sprm::CBrc::val) ? 9 : (m_bVer67 ? 6 : 8);
+
+ SetWW8_BRC(nBrcVer, aBrc, pData, nLen);
+
+ Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::TOP, 0, nullptr, true);
+ Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::BOTTOM, 0, nullptr, true);
+ Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::LEFT, 0, nullptr, true);
+ Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::RIGHT, 0, nullptr, true);
+ NewAttr( *aBoxItem );
+
+ short aSizeArray[WW8_RIGHT+1]={0}; aSizeArray[WW8_RIGHT] = 1;
+ SvxShadowItem aShadowItem(RES_CHRATR_SHADOW);
+ // Word only allows shadows on visible borders
+ if ( aBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) )
+ SetShadow( aShadowItem, &aSizeArray[0], aBrc );
+ NewAttr( aShadowItem );
+ }
+ }
+}
+
+void SwWW8ImplReader::Read_Hyphenation( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ // set Hyphenation flag
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_HYPHENZONE );
+ else
+ {
+ SvxHyphenZoneItem aAttr( *GetFormatAttr( RES_PARATR_HYPHENZONE ) );
+
+ aAttr.SetHyphen( 0 == *pData ); // sic !
+
+ if( !*pData )
+ {
+ aAttr.GetMinLead() = 2;
+ aAttr.GetMinTrail() = 2;
+ aAttr.GetMaxHyphens() = 0;
+ }
+
+ NewAttr( aAttr );
+ }
+}
+
+void SwWW8ImplReader::Read_WidowControl( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ {
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_WIDOWS );
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_ORPHANS );
+ }
+ else
+ {
+ sal_uInt8 nL = ( *pData & 1 ) ? 2 : 0;
+
+ NewAttr( SvxWidowsItem( nL, RES_PARATR_WIDOWS ) ); // Off -> nLines = 0
+ NewAttr( SvxOrphansItem( nL, RES_PARATR_ORPHANS ) );
+
+ if( m_pCurrentColl && m_xStyles ) // Style-Def ?
+ m_xStyles->mbWidowsChanged = true; // save for simulation
+ // Default-Widows
+ }
+}
+
+void SwWW8ImplReader::Read_UsePgsuSettings(sal_uInt16,const sal_uInt8* pData,short nLen)
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_SNAPTOGRID);
+ else
+ {
+ if(m_nInTable)
+ NewAttr( SvxParaGridItem(false, RES_PARATR_SNAPTOGRID) );
+ else
+ NewAttr( SvxParaGridItem(*pData, RES_PARATR_SNAPTOGRID) );
+ }
+}
+
+void SwWW8ImplReader::Read_AlignFont( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 2)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_VERTALIGN);
+ else
+ {
+ sal_uInt16 nVal = SVBT16ToUInt16( pData );
+ SvxParaVertAlignItem::Align nAlign;
+ switch (nVal)
+ {
+ case 0:
+ nAlign = SvxParaVertAlignItem::Align::Top;
+ break;
+ case 1:
+ nAlign = SvxParaVertAlignItem::Align::Center;
+ break;
+ case 2:
+ nAlign = SvxParaVertAlignItem::Align::Baseline;
+ break;
+ case 3:
+ nAlign = SvxParaVertAlignItem::Align::Bottom;
+ break;
+ case 4:
+ nAlign = SvxParaVertAlignItem::Align::Automatic;
+ break;
+ default:
+ nAlign = SvxParaVertAlignItem::Align::Automatic;
+ OSL_ENSURE(false,"Unknown paragraph vertical align");
+ break;
+ }
+ NewAttr( SvxParaVertAlignItem( nAlign, RES_PARATR_VERTALIGN ) );
+ }
+}
+
+void SwWW8ImplReader::Read_KeepLines( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_SPLIT );
+ else
+ NewAttr( SvxFormatSplitItem( ( *pData & 1 ) == 0, RES_PARATR_SPLIT ) );
+}
+
+void SwWW8ImplReader::Read_KeepParas( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_KEEP );
+ else
+ NewAttr( SvxFormatKeepItem( ( *pData & 1 ) != 0 , RES_KEEP) );
+}
+
+void SwWW8ImplReader::Read_BreakBefore( sal_uInt16, const sal_uInt8* pData, short nLen )
+{
+ if (nLen < 1)
+ m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_BREAK );
+ else
+ NewAttr( SvxFormatBreakItem(
+ ( *pData & 1 ) ? SvxBreak::PageBefore : SvxBreak::NONE, RES_BREAK ) );
+}
+
+void SwWW8ImplReader::Read_ApoPPC( sal_uInt16, const sal_uInt8* pData, short )
+{
+ if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // only for Styledef, otherwise solved differently
+ {
+ SwWW8StyInf& rSI = m_vColl[m_nCurrentColl];
+ if (!rSI.m_xWWFly)
+ rSI.m_xWWFly = std::make_shared<WW8FlyPara>(m_bVer67);
+ rSI.m_xWWFly->Read(*pData, m_xStyles.get());
+ if (rSI.m_xWWFly->IsEmpty())
+ {
+ m_vColl[m_nCurrentColl].m_xWWFly.reset();
+ }
+ }
+}
+
+bool SwWW8ImplReader::ParseTabPos(WW8_TablePos *pTabPos, WW8PLCFx_Cp_FKP* pPap)
+{
+ bool bRet = false;
+ memset(pTabPos, 0, sizeof(WW8_TablePos));
+ // sprmTPc contains a PositionCodeOperand structure that specifies the origin
+ // that is used to calculate the table position when it is absolutely positioned
+ SprmResult aRes = pPap->HasSprm(NS_sprm::TPc::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 1)
+ {
+ pTabPos->nTPc = *aRes.pSprm;
+ pTabPos->nPWr = 2; //Possible fail area, always parallel wrap
+ aRes = pPap->HasSprm(NS_sprm::TDxaAbs::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 2)
+ pTabPos->nTDxaAbs = SVBT16ToUInt16(aRes.pSprm);
+ aRes = pPap->HasSprm(NS_sprm::TDyaAbs::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 2)
+ pTabPos->nTDyaAbs = SVBT16ToUInt16(aRes.pSprm);
+ aRes = pPap->HasSprm(NS_sprm::TDxaFromText::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 2)
+ pTabPos->nLeftMargin = SVBT16ToUInt16(aRes.pSprm);
+ aRes = pPap->HasSprm(NS_sprm::TDxaFromTextRight::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 2)
+ pTabPos->nRightMargin = SVBT16ToUInt16(aRes.pSprm);
+ aRes = pPap->HasSprm(NS_sprm::TDyaFromText::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 2)
+ pTabPos->nUpperMargin = SVBT16ToUInt16(aRes.pSprm);
+ aRes = pPap->HasSprm(NS_sprm::TDyaFromTextBottom::val);
+ if (aRes.pSprm && aRes.nRemainingData >= 2)
+ pTabPos->nLowerMargin = SVBT16ToUInt16(aRes.pSprm);
+
+ aRes = pPap->HasSprm(NS_sprm::TFNoAllowOverlap::val);
+ if (aRes.pSprm)
+ {
+ // Remember the no-overlap request, to be consumed in SwWW8ImplReader::StartApo().
+ pTabPos->nTFNoAllowOverlap = *aRes.pSprm;
+ }
+
+ bRet = true;
+ }
+ return bRet;
+}
+
+// page attribute won't be used as attribute anymore
+// ( except OLST )
+tools::Long SwWW8ImplReader::ImportExtSprm(WW8PLCFManResult* pRes)
+{
+ // array for reading of the extended ( self-defined ) SPRMs
+ typedef tools::Long (SwWW8ImplReader::*FNReadRecordExt)(WW8PLCFManResult*);
+
+ static const FNReadRecordExt aWwSprmTab[] =
+ {
+ /* 0 (256) */ &SwWW8ImplReader::Read_Footnote, // FootNote
+ /* 1 (257) */ &SwWW8ImplReader::Read_Footnote, // EndNote
+ /* 2 (258) */ &SwWW8ImplReader::Read_Field, // Field
+ /* 3 (259) */ &SwWW8ImplReader::Read_Book, // Bookmark
+ /* 4 (260) */ &SwWW8ImplReader::Read_And, // Annotation
+ /* 5 (261) */ &SwWW8ImplReader::Read_AtnBook, // Annotationmark
+ /* 6 (262) */ &SwWW8ImplReader::Read_FactoidBook // Smart tag bookmark
+ };
+
+ if( pRes->nSprmId < 280 )
+ {
+ sal_uInt8 nIdx = static_cast< sal_uInt8 >(pRes->nSprmId - eFTN);
+ if( nIdx < SAL_N_ELEMENTS(aWwSprmTab)
+ && aWwSprmTab[nIdx] )
+ return (this->*aWwSprmTab[nIdx])(pRes);
+ else
+ return 0;
+ }
+ else
+ return 0;
+}
+
+void SwWW8ImplReader::EndExtSprm(sal_uInt16 nSprmId)
+{
+ typedef sal_uInt16 (SwWW8ImplReader::*FNReadRecordExt)();
+
+ static const FNReadRecordExt aWwSprmTab[] =
+ {
+ /* 0 (256) */ &SwWW8ImplReader::End_Footnote, // FootNote
+ /* 1 (257) */ &SwWW8ImplReader::End_Footnote, // EndNote
+ /* 2 (258) */ &SwWW8ImplReader::End_Field, // Field
+ /* 3 (259) */ nullptr, // Bookmark
+ /* 4 (260) */ nullptr // Annotation
+ };
+
+ sal_uInt8 nIdx = static_cast< sal_uInt8 >(nSprmId - eFTN);
+ if( nIdx < SAL_N_ELEMENTS(aWwSprmTab)
+ && aWwSprmTab[nIdx] )
+ (this->*aWwSprmTab[nIdx])();
+}
+
+// arrays for reading the SPRMs
+
+// function for reading of SPRMs. Par1: SprmId
+typedef void (SwWW8ImplReader::*FNReadRecord)( sal_uInt16, const sal_uInt8*, short );
+
+struct SprmReadInfo
+{
+ sal_uInt16 nId;
+ FNReadRecord pReadFnc;
+};
+
+static bool operator<(const SprmReadInfo &rFirst, const SprmReadInfo &rSecond)
+{
+ return (rFirst.nId < rSecond.nId);
+}
+
+typedef ww::SortedArray<SprmReadInfo> wwSprmDispatcher;
+
+static const wwSprmDispatcher *GetWW2SprmDispatcher()
+{
+ static SprmReadInfo aSprms[] =
+ {
+ {0, nullptr}, // "0" default resp. error
+ // will be skipped! ,
+ {2, &SwWW8ImplReader::Read_StyleCode}, //"sprmPIstd", pap.istd
+ //(style code)
+ {3, nullptr}, //"sprmPIstdPermute", pap.istd
+ //permutation
+ {4, nullptr}, //"sprmPIncLv1",
+ //pap.istddifference
+ {5, &SwWW8ImplReader::Read_Justify}, //"sprmPJc", pap.jc
+ //(justification)
+ {6, nullptr}, //"sprmPFSideBySide",
+ //pap.fSideBySide
+ {7, &SwWW8ImplReader::Read_KeepLines}, //"sprmPFKeep", pap.fKeep
+ {8, &SwWW8ImplReader::Read_KeepParas}, //"sprmPFKeepFollow ",
+ //pap.fKeepFollow
+ {9, &SwWW8ImplReader::Read_BreakBefore}, //"sprmPPageBreakBefore",
+ //pap.fPageBreakBefore
+ {10, nullptr}, //"sprmPBrcl", pap.brcl
+ {11, nullptr}, //"sprmPBrcp ", pap.brcp
+ {12, &SwWW8ImplReader::Read_ANLevelDesc}, //"sprmPAnld", pap.anld (ANLD
+ //structure)
+ {13, &SwWW8ImplReader::Read_ANLevelNo}, //"sprmPNLvlAnm", pap.nLvlAnm
+ //nn
+ {14, &SwWW8ImplReader::Read_NoLineNumb}, //"sprmPFNoLineNumb", ap.fNoLnn
+ {15, &SwWW8ImplReader::Read_Tab}, //"?sprmPChgTabsPapx",
+ //pap.itbdMac, ...
+ {16, &SwWW8ImplReader::Read_LR}, //"sprmPDxaRight", pap.dxaRight
+ {17, &SwWW8ImplReader::Read_LR}, //"sprmPDxaLeft", pap.dxaLeft
+ {18, nullptr}, //"sprmPNest", pap.dxaLeft
+ {19, &SwWW8ImplReader::Read_LR}, //"sprmPDxaLeft1", pap.dxaLeft1
+ {20, &SwWW8ImplReader::Read_LineSpace}, //"sprmPDyaLine", pap.lspd
+ //an LSPD
+ {21, &SwWW8ImplReader::Read_UL}, //"sprmPDyaBefore",
+ //pap.dyaBefore
+ {22, &SwWW8ImplReader::Read_UL}, //"sprmPDyaAfter", pap.dyaAfter
+ {23, nullptr}, //"?sprmPChgTabs", pap.itbdMac,
+ //pap.rgdxaTab, ...
+ {24, nullptr}, //"sprmPFInTable", pap.fInTable
+ {25, &SwWW8ImplReader::Read_TabRowEnd}, //"sprmPTtp", pap.fTtp
+ {26, nullptr}, //"sprmPDxaAbs", pap.dxaAbs
+ {27, nullptr}, //"sprmPDyaAbs", pap.dyaAbs
+ {28, nullptr}, //"sprmPDxaWidth", pap.dxaWidth
+ {29, &SwWW8ImplReader::Read_ApoPPC}, //"sprmPPc", pap.pcHorz,
+ //pap.pcVert
+ {30, nullptr}, //"sprmPBrcTop10", pap.brcTop
+ //BRC10
+ {31, nullptr}, //"sprmPBrcLeft10",
+ //pap.brcLeft BRC10
+ {32, nullptr}, //"sprmPBrcBottom10",
+ //pap.brcBottom BRC10
+ {33, nullptr}, //"sprmPBrcRight10",
+ //pap.brcRight BRC10
+ {34, nullptr}, //"sprmPBrcBetween10",
+ //pap.brcBetween BRC10
+ {35, nullptr}, //"sprmPBrcBar10", pap.brcBar
+ //BRC10
+ {36, nullptr}, //"sprmPFromText10",
+ //pap.dxaFromText dxa
+ {37, nullptr}, //"sprmPWr", pap.wr wr
+ {38, &SwWW8ImplReader::Read_Border}, //"sprmPBrcTop", pap.brcTop BRC
+ {39, &SwWW8ImplReader::Read_Border}, //"sprmPBrcLeft",
+ //pap.brcLeft BRC
+ {40, &SwWW8ImplReader::Read_Border}, //"sprmPBrcBottom",
+ //pap.brcBottom BRC
+ {41, &SwWW8ImplReader::Read_Border}, //"sprmPBrcRight",
+ //pap.brcRight BRC
+ {42, &SwWW8ImplReader::Read_Border}, //"sprmPBrcBetween",
+ //pap.brcBetween BRC
+ {43, nullptr}, //"sprmPBrcBar", pap.brcBar
+ //BRC word
+ {44, &SwWW8ImplReader::Read_Hyphenation}, //"sprmPFNoAutoHyph",
+ //pap.fNoAutoHyph
+ {45, nullptr}, //"sprmPWHeightAbs",
+ //pap.wHeightAbs w
+ {46, nullptr}, //"sprmPDcs", pap.dcs DCS
+ {47, &SwWW8ImplReader::Read_Shade}, //"sprmPShd", pap.shd SHD
+ {48, nullptr}, //"sprmPDyaFromText",
+ //pap.dyaFromText dya
+ {49, nullptr}, //"sprmPDxaFromText",
+ //pap.dxaFromText dxa
+ {50, nullptr}, //"sprmPFLocked", pap.fLocked
+ //0 or 1 byte
+ {51, &SwWW8ImplReader::Read_WidowControl}, //"sprmPFWidowControl",
+ //pap.fWidowControl 0 or 1 byte
+ {52, nullptr}, //"?sprmPRuler 52",
+ {53, nullptr}, //"??53",
+ {54, nullptr}, //"??54",
+ {55, nullptr}, //"??55",
+ {56, nullptr}, //"??56",
+ {57, nullptr}, //"??57",
+ {58, nullptr}, //"??58",
+ {59, nullptr}, //"??59",
+
+ {60, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFBold", chp.fBold 0,1,
+ //128, or 129 byte
+ {61, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFItalic", chp.fItalic
+ //0,1, 128, or 129 byte
+ {62, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFStrike", chp.fStrike
+ //0,1, 128, or 129 byte
+ {63, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFOutline", chp.fOutline
+ //0,1, 128, or 129 byte
+ {64, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFShadow", chp.fShadow
+ //0,1, 128, or 129 byte
+ {65, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFSmallCaps",
+ //chp.fSmallCaps 0,1, 128, or
+ //129 byte
+ {66, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFCaps", chp.fCaps 0,1,
+ //128, or 129 byte
+ {67, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFVanish", chp.fVanish
+ //0,1, 128, or 129 byte
+ {68, &SwWW8ImplReader::Read_FontCode}, //"sprmCFtc", chp.ftc ftc word
+ {69, &SwWW8ImplReader::Read_Underline}, // "sprmCKul", chp.kul kul byte
+ {70, nullptr}, //"sprmCSizePos", chp.hps,
+ //chp.hpsPos 3 bytes
+ {71, &SwWW8ImplReader::Read_Kern}, //"sprmCDxaSpace",
+ //chp.dxaSpace dxa word
+ {72, &SwWW8ImplReader::Read_Language}, //"sprmCLid", chp.lid LID word
+ {73, &SwWW8ImplReader::Read_TextColor}, //"sprmCIco", chp.ico ico byte
+ {74, &SwWW8ImplReader::Read_FontSize}, //"sprmCHps", chp.hps hps word!
+ {75, nullptr}, //"sprmCHpsInc", chp.hps byte
+ {76, &SwWW8ImplReader::Read_SubSuperProp}, //"sprmCHpsPos", chp.hpsPos
+ //hps byte
+ {77, nullptr}, //"sprmCHpsPosAdj", chp.hpsPos
+ //hps byte
+ {78, &SwWW8ImplReader::Read_Majority}, //"?sprmCMajority", chp.fBold,
+ //chp.fItalic, chp.fSmallCaps
+ {80, &SwWW8ImplReader::Read_BoldBiDiUsw}, //sprmCFBoldBi
+ {81, &SwWW8ImplReader::Read_BoldBiDiUsw}, //sprmCFItalicBi
+ {82, &SwWW8ImplReader::Read_FontCode}, //sprmCFtcBi
+ {83, &SwWW8ImplReader::Read_Language}, //sprmClidBi
+ {84, &SwWW8ImplReader::Read_TextColor}, //sprmCIcoBi
+ {85, &SwWW8ImplReader::Read_FontSize}, //sprmCHpsBi
+ {86, nullptr}, //sprmCFBiDi
+ {87, nullptr}, //sprmCFDiacColor
+ {94, nullptr}, //"sprmPicBrcl", pic.brcl brcl
+ //(see PIC structure
+ //definition) byte
+ {95, nullptr}, //"sprmPicScale", pic.mx,
+ //pic.my, pic.dxaCropleft,
+ {96, nullptr}, //"sprmPicBrcTop", pic.brcTop
+ //BRC word
+ {97, nullptr}, //"sprmPicBrcLeft",
+ //pic.brcLeft BRC word
+ {98, nullptr}, //"sprmPicBrcBottom",
+ //pic.brcBottom BRC word
+ {99, nullptr} //"sprmPicBrcRight",
+ };
+
+ static wwSprmDispatcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms));
+ return &aSprmSrch;
+}
+
+static const wwSprmDispatcher *GetWW6SprmDispatcher()
+{
+ static SprmReadInfo aSprms[] =
+ {
+ {0, nullptr}, // "0" default resp. error
+ // will be skipped!
+ {NS_sprm::v6::sprmPIstd, &SwWW8ImplReader::Read_StyleCode}, // pap.istd (style code)
+ {NS_sprm::v6::sprmPIstdPermute, nullptr}, // pap.istd permutation
+ {NS_sprm::v6::sprmPIncLv1, nullptr}, // pap.istddifference
+ {NS_sprm::v6::sprmPJc, &SwWW8ImplReader::Read_Justify}, // pap.jc (justification)
+ {NS_sprm::v6::sprmPFSideBySide, nullptr}, // pap.fSideBySide
+ {NS_sprm::v6::sprmPFKeep, &SwWW8ImplReader::Read_KeepLines}, // pap.fKeep
+ {NS_sprm::v6::sprmPFKeepFollow, &SwWW8ImplReader::Read_KeepParas}, // pap.fKeepFollow
+ {NS_sprm::v6::sprmPPageBreakBefore, &SwWW8ImplReader::Read_BreakBefore}, // pap.fPageBreakBefore
+ {NS_sprm::v6::sprmPBrcl, nullptr}, // pap.brcl
+ {NS_sprm::v6::sprmPBrcp, nullptr}, // pap.brcp
+ {NS_sprm::v6::sprmPAnld, &SwWW8ImplReader::Read_ANLevelDesc}, // pap.anld (ANLD structure)
+ {NS_sprm::v6::sprmPNLvlAnm, &SwWW8ImplReader::Read_ANLevelNo}, // pap.nLvlAnm nn
+ {NS_sprm::v6::sprmPFNoLineNumb, &SwWW8ImplReader::Read_NoLineNumb}, // ap.fNoLnn
+ {NS_sprm::v6::sprmPChgTabsPapx, &SwWW8ImplReader::Read_Tab}, // pap.itbdMac, ...
+ {NS_sprm::v6::sprmPDxaRight, &SwWW8ImplReader::Read_LR}, // pap.dxaRight
+ {NS_sprm::v6::sprmPDxaLeft, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft
+ {NS_sprm::v6::sprmPNest, nullptr}, // pap.dxaLeft
+ {NS_sprm::v6::sprmPDxaLeft1, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft1
+ {NS_sprm::v6::sprmPDyaLine, &SwWW8ImplReader::Read_LineSpace}, // pap.lspd an LSPD
+ {NS_sprm::v6::sprmPDyaBefore, &SwWW8ImplReader::Read_UL}, // pap.dyaBefore
+ {NS_sprm::v6::sprmPDyaAfter, &SwWW8ImplReader::Read_UL}, // pap.dyaAfter
+ {NS_sprm::v6::sprmPChgTabs, nullptr}, // pap.itbdMac, pap.rgdxaTab, ...
+ {NS_sprm::v6::sprmPFInTable, nullptr}, // pap.fInTable
+ {NS_sprm::v6::sprmPTtp, &SwWW8ImplReader::Read_TabRowEnd}, // pap.fTtp
+ {NS_sprm::v6::sprmPDxaAbs, nullptr}, // pap.dxaAbs
+ {NS_sprm::v6::sprmPDyaAbs, nullptr}, // pap.dyaAbs
+ {NS_sprm::v6::sprmPDxaWidth, nullptr}, // pap.dxaWidth
+ {NS_sprm::v6::sprmPPc, &SwWW8ImplReader::Read_ApoPPC}, // pap.pcHorz, pap.pcVert
+ {NS_sprm::v6::sprmPBrcTop10, nullptr}, // pap.brcTop BRC10
+ {NS_sprm::v6::sprmPBrcLeft10, nullptr}, // pap.brcLeft BRC10
+ {NS_sprm::v6::sprmPBrcBottom10, nullptr}, // pap.brcBottom BRC10
+ {NS_sprm::v6::sprmPBrcRight10, nullptr}, // pap.brcRight BRC10
+ {NS_sprm::v6::sprmPBrcBetween10, nullptr}, // pap.brcBetween BRC10
+ {NS_sprm::v6::sprmPBrcBar10, nullptr}, // pap.brcBar BRC10
+ {NS_sprm::v6::sprmPFromText10, nullptr}, // pap.dxaFromText dxa
+ {NS_sprm::v6::sprmPWr, nullptr}, // pap.wr wr
+ {NS_sprm::v6::sprmPBrcTop, &SwWW8ImplReader::Read_Border}, // pap.brcTop BRC
+ {NS_sprm::v6::sprmPBrcLeft, &SwWW8ImplReader::Read_Border}, // pap.brcLeft BRC
+ {NS_sprm::v6::sprmPBrcBottom, &SwWW8ImplReader::Read_Border}, // pap.brcBottom BRC
+ {NS_sprm::v6::sprmPBrcRight, &SwWW8ImplReader::Read_Border}, // pap.brcRight BRC
+ {NS_sprm::v6::sprmPBrcBetween, &SwWW8ImplReader::Read_Border}, // pap.brcBetween BRC
+ {NS_sprm::v6::sprmPBrcBar, nullptr}, // pap.brcBar BRC word
+ {NS_sprm::v6::sprmPFNoAutoHyph, &SwWW8ImplReader::Read_Hyphenation}, // pap.fNoAutoHyph
+ {NS_sprm::v6::sprmPWHeightAbs, nullptr}, // pap.wHeightAbs w
+ {NS_sprm::v6::sprmPDcs, nullptr}, // pap.dcs DCS
+ {NS_sprm::v6::sprmPShd, &SwWW8ImplReader::Read_Shade}, // pap.shd SHD
+ {NS_sprm::v6::sprmPDyaFromText, nullptr}, // pap.dyaFromText dya
+ {NS_sprm::v6::sprmPDxaFromText, nullptr}, // pap.dxaFromText dxa
+ {NS_sprm::v6::sprmPFLocked, nullptr}, // pap.fLocked 0 or 1 byte
+ {NS_sprm::v6::sprmPFWidowControl, &SwWW8ImplReader::Read_WidowControl}, // pap.fWidowControl 0 or 1 byte
+ {NS_sprm::v6::sprmPRuler, nullptr},
+ {53, nullptr}, //"??53",
+ {54, nullptr}, //"??54",
+ {55, nullptr}, //"??55",
+ {56, nullptr}, //"??56",
+ {57, nullptr}, //"??57",
+ {58, nullptr}, //"??58",
+ {59, nullptr}, //"??59",
+ {60, nullptr}, //"??60",
+ {61, nullptr}, //"??61",
+ {62, nullptr}, //"??62",
+ {63, nullptr}, //"??63",
+ {64, &SwWW8ImplReader::Read_ParaBiDi}, //"rtl bidi ?
+ {NS_sprm::v6::sprmCFStrikeRM, &SwWW8ImplReader::Read_CFRMarkDel}, // chp.fRMarkDel 1 or 0 bit
+ {NS_sprm::v6::sprmCFRMark, &SwWW8ImplReader::Read_CFRMark}, // chp.fRMark 1 or 0 bit
+ {NS_sprm::v6::sprmCFFldVanish, &SwWW8ImplReader::Read_FieldVanish}, // chp.fFieldVanish 1 or 0 bit
+ {NS_sprm::v6::sprmCPicLocation, &SwWW8ImplReader::Read_PicLoc}, // chp.fcPic and chp.fSpec
+ {NS_sprm::v6::sprmCIbstRMark, nullptr}, // chp.ibstRMark index into sttbRMark
+ {NS_sprm::v6::sprmCDttmRMark, nullptr}, // chp.dttm DTTM long
+ {NS_sprm::v6::sprmCFData, nullptr}, // chp.fData 1 or 0 bit
+ {NS_sprm::v6::sprmCRMReason, nullptr}, // chp.idslRMReason an index to a table
+ {NS_sprm::v6::sprmCChse, &SwWW8ImplReader::Read_CharSet}, // chp.fChsDiff and chp.chse 3 bytes
+ {NS_sprm::v6::sprmCSymbol, &SwWW8ImplReader::Read_Symbol}, // chp.fSpec, chp.chSym and chp.ftcSym
+ {NS_sprm::v6::sprmCFOle2, &SwWW8ImplReader::Read_Obj}, // chp.fOle2 1 or 0 bit
+ {76, nullptr}, //"??76",
+ {77, nullptr}, //"??77",
+ {78, nullptr}, //"??78",
+ {79, nullptr}, //"??79",
+ {NS_sprm::v6::sprmCIstd, &SwWW8ImplReader::Read_CColl}, // chp.istd istd, see stylesheet definition; short
+ {NS_sprm::v6::sprmCIstdPermute, nullptr}, // chp.istd permutation vector
+ {NS_sprm::v6::sprmCDefault, nullptr}, // whole CHP none variable length
+ {NS_sprm::v6::sprmCPlain, nullptr}, // whole CHP none 0
+ {84, nullptr}, //"??84",
+ {NS_sprm::v6::sprmCFBold, &SwWW8ImplReader::Read_BoldUsw}, // chp.fBold 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFItalic, &SwWW8ImplReader::Read_BoldUsw}, // chp.fItalic 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFStrike, &SwWW8ImplReader::Read_BoldUsw}, // chp.fStrike 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFOutline, &SwWW8ImplReader::Read_BoldUsw}, // chp.fOutline 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFShadow, &SwWW8ImplReader::Read_BoldUsw}, // chp.fShadow 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFSmallCaps, &SwWW8ImplReader::Read_BoldUsw}, // chp.fSmallCaps 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFCaps, &SwWW8ImplReader::Read_BoldUsw}, // chp.fCaps 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFVanish, &SwWW8ImplReader::Read_BoldUsw}, // chp.fVanish 0,1, 128, or 129 byte
+ {NS_sprm::v6::sprmCFtc, &SwWW8ImplReader::Read_FontCode}, // chp.ftc ftc word
+ {NS_sprm::v6::sprmCKul, &SwWW8ImplReader::Read_Underline}, // chp.kul kul byte
+ {NS_sprm::v6::sprmCSizePos, nullptr}, // chp.hps, chp.hpsPos 3 bytes
+ {NS_sprm::v6::sprmCDxaSpace, &SwWW8ImplReader::Read_Kern}, // chp.dxaSpace dxa word
+ {NS_sprm::v6::sprmCLid, &SwWW8ImplReader::Read_Language}, // chp.lid LID word
+ {NS_sprm::v6::sprmCIco, &SwWW8ImplReader::Read_TextColor}, // chp.ico ico byte
+ {NS_sprm::v6::sprmCHps, &SwWW8ImplReader::Read_FontSize}, // chp.hps hps word!
+ {NS_sprm::v6::sprmCHpsInc, nullptr}, // chp.hps byte
+ {NS_sprm::v6::sprmCHpsPos, &SwWW8ImplReader::Read_SubSuperProp}, // chp.hpsPos hps byte
+ {NS_sprm::v6::sprmCHpsPosAdj, nullptr}, // chp.hpsPos hps byte
+ {NS_sprm::v6::sprmCMajority, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fSmallCaps
+ {NS_sprm::v6::sprmCIss, &SwWW8ImplReader::Read_SubSuper}, // chp.iss iss byte
+ {NS_sprm::v6::sprmCHpsNew50, nullptr}, // chp.hps hps variable width, length always recorded as 2
+ {NS_sprm::v6::sprmCHpsInc1, nullptr}, // chp.hps complex variable width, length always recorded as 2
+ {NS_sprm::v6::sprmCHpsKern, &SwWW8ImplReader::Read_FontKern}, // chp.hpsKern hps short
+ {NS_sprm::v6::sprmCMajority50, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fSmallCaps, chp.fVanish, ...
+ {NS_sprm::v6::sprmCHpsMul, nullptr}, // chp.hps percentage to grow hps short
+ {NS_sprm::v6::sprmCCondHyhen, nullptr}, // chp.ysri ysri short
+ {111, &SwWW8ImplReader::Read_AmbiguousSPRM},//sprmCFBoldBi or font code
+ {112, &SwWW8ImplReader::Read_AmbiguousSPRM},//sprmCFItalicBi or font code
+ {113, &SwWW8ImplReader::Read_FontCode}, //sprmCFtcBi
+ {114, &SwWW8ImplReader::Read_Language}, //sprmClidBi
+ {115, &SwWW8ImplReader::Read_TextColor}, //sprmCIcoBi
+ {116, &SwWW8ImplReader::Read_FontSize}, //sprmCHpsBi
+ {NS_sprm::v6::sprmCFSpec, &SwWW8ImplReader::Read_Special}, // chp.fSpec 1 or 0 bit
+ {NS_sprm::v6::sprmCFObj, &SwWW8ImplReader::Read_Obj}, // chp.fObj 1 or 0 bit
+ {NS_sprm::v6::sprmPicBrcl, nullptr}, // pic.brcl brcl (see PIC structure definition) byte
+ {NS_sprm::v6::sprmPicScale, nullptr}, // pic.mx, pic.my, pic.dxaCropleft,
+ {NS_sprm::v6::sprmPicBrcTop, nullptr}, // pic.brcTop BRC word
+ {NS_sprm::v6::sprmPicBrcLeft, nullptr}, // pic.brcLeft BRC word
+ {NS_sprm::v6::sprmPicBrcBottom, nullptr}, // pic.brcBottom BRC word
+ {NS_sprm::v6::sprmPicBrcRight, nullptr}, // pic.brcRight BRC word
+ {125, nullptr}, //"??125",
+ {126, nullptr}, //"??126",
+ {127, nullptr}, //"??127",
+ {128, nullptr}, //"??128",
+ {129, nullptr}, //"??129",
+ {130, nullptr}, //"??130",
+ {NS_sprm::v6::sprmSScnsPgn, nullptr}, // sep.cnsPgn cns byte
+ {NS_sprm::v6::sprmSiHeadingPgn, nullptr}, // sep.iHeadingPgn heading number level byte
+ {NS_sprm::v6::sprmSOlstAnm, &SwWW8ImplReader::Read_OLST}, // sep.olstAnm OLST variable length
+ {134, nullptr}, //"??135",
+ {135, nullptr}, //"??135",
+ {NS_sprm::v6::sprmSDxaColWidth, nullptr}, // sep.rgdxaColWidthSpacing complex 3 bytes
+ {NS_sprm::v6::sprmSDxaColSpacing, nullptr}, // sep.rgdxaColWidthSpacing complex 3 bytes
+ {NS_sprm::v6::sprmSFEvenlySpaced, nullptr}, // sep.fEvenlySpaced 1 or 0 byte
+ {NS_sprm::v6::sprmSFProtected, nullptr}, // sep.fUnlocked 1 or 0 byte
+ {NS_sprm::v6::sprmSDmBinFirst, nullptr}, // sep.dmBinFirst word
+ {NS_sprm::v6::sprmSDmBinOther, nullptr}, // sep.dmBinOther word
+ {NS_sprm::v6::sprmSBkc, nullptr}, // sep.bkc bkc byte BreakCode
+ {NS_sprm::v6::sprmSFTitlePage, nullptr}, // sep.fTitlePage 0 or 1 byte
+ {NS_sprm::v6::sprmSCcolumns, nullptr}, // sep.ccolM1 # of cols - 1 word
+ {NS_sprm::v6::sprmSDxaColumns, nullptr}, // sep.dxaColumns dxa word
+ {NS_sprm::v6::sprmSFAutoPgn, nullptr}, // sep.fAutoPgn obsolete byte
+ {NS_sprm::v6::sprmSNfcPgn, nullptr}, // sep.nfcPgn nfc byte
+ {NS_sprm::v6::sprmSDyaPgn, nullptr}, // sep.dyaPgn dya short
+ {NS_sprm::v6::sprmSDxaPgn, nullptr}, // sep.dxaPgn dya short
+ {NS_sprm::v6::sprmSFPgnRestart, nullptr}, // sep.fPgnRestart 0 or 1 byte
+ {NS_sprm::v6::sprmSFEndnote, nullptr}, // sep.fEndnote 0 or 1 byte
+ {NS_sprm::v6::sprmSLnc, nullptr}, // sep.lnc lnc byte
+ {NS_sprm::v6::sprmSGprfIhdt, nullptr}, // sep.grpfIhdt grpfihdt byte
+ {NS_sprm::v6::sprmSNLnnMod, nullptr}, // sep.nLnnMod non-neg int. word
+ {NS_sprm::v6::sprmSDxaLnn, nullptr}, // sep.dxaLnn dxa word
+ {NS_sprm::v6::sprmSDyaHdrTop, nullptr}, // sep.dyaHdrTop dya word
+ {NS_sprm::v6::sprmSDyaHdrBottom, nullptr}, // sep.dyaHdrBottom dya word
+ {NS_sprm::v6::sprmSLBetween, nullptr}, // sep.fLBetween 0 or 1 byte
+ {NS_sprm::v6::sprmSVjc, nullptr}, // sep.vjc vjc byte
+ {NS_sprm::v6::sprmSLnnMin, nullptr}, // sep.lnnMin lnn word
+ {NS_sprm::v6::sprmSPgnStart, nullptr}, // sep.pgnStart pgn word
+ {NS_sprm::v6::sprmSBOrientation, nullptr}, // sep.dmOrientPage dm byte
+ {NS_sprm::v6::sprmSBCustomize, nullptr}, // ?
+ {NS_sprm::v6::sprmSXaPage, nullptr}, // sep.xaPage xa word
+ {NS_sprm::v6::sprmSYaPage, nullptr}, // sep.yaPage ya word
+ {NS_sprm::v6::sprmSDxaLeft, nullptr}, // sep.dxaLeft dxa word
+ {NS_sprm::v6::sprmSDxaRight, nullptr}, // sep.dxaRight dxa word
+ {NS_sprm::v6::sprmSDyaTop, nullptr}, // sep.dyaTop dya word
+ {NS_sprm::v6::sprmSDyaBottom, nullptr}, // sep.dyaBottom dya word
+ {NS_sprm::v6::sprmSDzaGutter, nullptr}, // sep.dzaGutter dza word
+ {NS_sprm::v6::sprmSDMPaperReq, nullptr}, // sep.dmPaperReq dm word
+ {172, nullptr}, //"??172",
+ {173, nullptr}, //"??173",
+ {174, nullptr}, //"??174",
+ {175, nullptr}, //"??175",
+ {176, nullptr}, //"??176",
+ {177, nullptr}, //"??177",
+ {178, nullptr}, //"??178",
+ {179, nullptr}, //"??179",
+ {180, nullptr}, //"??180",
+ {181, nullptr}, //"??181",
+ {NS_sprm::v6::sprmTJc, nullptr}, // tap.jc jc word (low order byte is significant)
+ {NS_sprm::v6::sprmTDxaLeft, nullptr}, // tap.rgdxaCenter dxa word
+ {NS_sprm::v6::sprmTDxaGapHalf, nullptr}, // tap.dxaGapHalf, tap.rgdxaCenter dxa word
+ {NS_sprm::v6::sprmTFCantSplit, nullptr}, // tap.fCantSplit 1 or 0 byte
+ {NS_sprm::v6::sprmTTableHeader, nullptr}, // tap.fTableHeader 1 or 0 byte
+ {NS_sprm::v6::sprmTTableBorders, nullptr}, // tap.rgbrcTable complex 12 bytes
+ {NS_sprm::v6::sprmTDefTable10, nullptr}, // tap.rgdxaCenter, tap.rgtc complex variable length
+ {NS_sprm::v6::sprmTDyaRowHeight, nullptr}, // tap.dyaRowHeight dya word
+ {NS_sprm::v6::sprmTDefTable, nullptr}, // tap.rgtc complex
+ {NS_sprm::v6::sprmTDefTableShd, nullptr}, // tap.rgshd complex
+ {NS_sprm::v6::sprmTTlp, nullptr}, // tap.tlp TLP 4 bytes
+ {NS_sprm::v6::sprmTSetBrc, nullptr}, // tap.rgtc[].rgbrc complex 5 bytes
+ {NS_sprm::v6::sprmTInsert, nullptr}, // tap.rgdxaCenter, tap.rgtc complex 4 bytes
+ {NS_sprm::v6::sprmTDelete, nullptr}, // tap.rgdxaCenter, tap.rgtc complex word
+ {NS_sprm::v6::sprmTDxaCol, nullptr}, // tap.rgdxaCenter complex 4 bytes
+ {NS_sprm::v6::sprmTMerge, nullptr}, // tap.fFirstMerged, tap.fMerged complex word
+ {NS_sprm::v6::sprmTSplit, nullptr}, // tap.fFirstMerged, tap.fMerged complex word
+ {NS_sprm::v6::sprmTSetBrc10, nullptr}, // tap.rgtc[].rgbrc complex 5 bytes
+ {NS_sprm::v6::sprmTSetShd, nullptr}, // tap.rgshd complex 4 bytes
+ {207, nullptr}, //dunno
+ };
+
+ static wwSprmDispatcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms));
+ return &aSprmSrch;
+}
+
+static const wwSprmDispatcher *GetWW8SprmDispatcher()
+{
+ static SprmReadInfo aSprms[] =
+ {
+ {0, nullptr}, // "0" default resp. error
+
+ {NS_sprm::PIstd::val, &SwWW8ImplReader::Read_StyleCode}, // pap.istd;istd (style code);short;
+ {NS_sprm::PIstdPermute::val, nullptr}, // pap.istd;permutation vector;
+ // variable length;
+ {NS_sprm::PIncLvl::val, nullptr}, // pap.istd, pap.lvl;difference between
+ // istd of base PAP and istd of
+ // PAP to be produced;byte;
+ {NS_sprm::PJc80::val, &SwWW8ImplReader::Read_Justify}, // pap.jc;jc (justification);byte;
+ {NS_sprm::LN_PFSideBySide, nullptr}, // pap.fSideBySide;0 or 1;byte;
+ {NS_sprm::PFKeep::val, &SwWW8ImplReader::Read_KeepLines}, // pap.fKeep;0 or 1;byte;
+ {NS_sprm::PFKeepFollow::val, &SwWW8ImplReader::Read_KeepParas}, // pap.fKeepFollow;0 or 1;byte;
+ {NS_sprm::PFPageBreakBefore::val, &SwWW8ImplReader::Read_BreakBefore}, // pap.fPageBreakBefore;0 or 1;byte;
+ {NS_sprm::LN_PBrcl, nullptr}, // pap.brcl;brcl;byte;
+ {NS_sprm::LN_PBrcp, nullptr}, // pap.brcp;brcp;byte;
+ {NS_sprm::PIlvl::val, &SwWW8ImplReader::Read_ListLevel}, // pap.ilvl;ilvl;byte;
+ {NS_sprm::PIlfo::val, &SwWW8ImplReader::Read_LFOPosition}, // pap.ilfo;ilfo (list index);short;
+ {NS_sprm::PFNoLineNumb::val, &SwWW8ImplReader::Read_NoLineNumb}, // pap.fNoLnn;0 or 1;byte;
+ {NS_sprm::PChgTabsPapx::val, &SwWW8ImplReader::Read_Tab}, // pap.itbdMac, pap.rgdxaTab, pap.rgtbd;
+ // complex;variable length
+ {NS_sprm::PDxaRight80::val, &SwWW8ImplReader::Read_LR}, // pap.dxaRight;dxa;word;
+ {NS_sprm::PDxaLeft80::val, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft;dxa;word;
+ {NS_sprm::PNest80::val, nullptr}, // pap.dxaLeft;dxa;word;
+ {NS_sprm::PDxaLeft180::val, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft1;dxa;word;
+ {NS_sprm::PDyaLine::val, &SwWW8ImplReader::Read_LineSpace}, // pap.lspd;an LSPD, a long word
+ // structure consisting of a short
+ // of dyaLine followed by a short
+ // of fMultLinespace;long;
+ {NS_sprm::PDyaBefore::val, &SwWW8ImplReader::Read_UL}, // pap.dyaBefore;dya;word;
+ {NS_sprm::PDyaAfter::val, &SwWW8ImplReader::Read_UL}, // pap.dyaAfter;dya;word;
+ {NS_sprm::PChgTabs::val, nullptr}, // pap.itbdMac, pap.rgdxaTab, pap.rgtbd;
+ // complex;variable length;
+ {NS_sprm::PFInTable::val, nullptr}, // pap.fInTable;0 or 1;byte;
+ {NS_sprm::PFTtp::val, &SwWW8ImplReader::Read_TabRowEnd}, // pap.fTtp;0 or 1;byte;
+ {NS_sprm::PDxaAbs::val, nullptr}, // pap.dxaAbs;dxa;word;
+ {NS_sprm::PDyaAbs::val, nullptr}, // pap.dyaAbs;dya;word;
+ {NS_sprm::PDxaWidth::val, nullptr}, // pap.dxaWidth;dxa;word;
+ {NS_sprm::PPc::val, &SwWW8ImplReader::Read_ApoPPC}, // pap.pcHorz, pap.pcVert;complex;byte;
+ {NS_sprm::LN_PBrcTop10, nullptr}, // pap.brcTop;BRC10;word;
+ {NS_sprm::LN_PBrcLeft10, nullptr}, // pap.brcLeft;BRC10;word;
+ {NS_sprm::LN_PBrcBottom10, nullptr}, // pap.brcBottom;BRC10;word;
+ {NS_sprm::LN_PBrcRight10, nullptr}, // pap.brcRight;BRC10;word;
+ {NS_sprm::LN_PBrcBetween10, nullptr}, // pap.brcBetween;BRC10;word;
+ {NS_sprm::LN_PBrcBar10, nullptr}, // pap.brcBar;BRC10;word;
+ {NS_sprm::LN_PDxaFromText10, nullptr}, // pap.dxaFromText;dxa;word;
+ {NS_sprm::PWr::val, nullptr}, // pap.wr;wr;byte;
+ {NS_sprm::PBrcTop80::val, &SwWW8ImplReader::Read_Border}, // pap.brcTop;BRC;long;
+ {NS_sprm::PBrcLeft80::val, &SwWW8ImplReader::Read_Border}, // pap.brcLeft;BRC;long;
+ {NS_sprm::PBrcBottom80::val, &SwWW8ImplReader::Read_Border}, // pap.brcBottom;BRC;long;
+ {NS_sprm::PBrcRight80::val, &SwWW8ImplReader::Read_Border}, // pap.brcRight;BRC;long;
+ {NS_sprm::PBrcBetween80::val, &SwWW8ImplReader::Read_Border}, // pap.brcBetween;BRC;long;
+ {NS_sprm::PBrcBar80::val, nullptr}, // pap.brcBar;BRC;long;
+ {NS_sprm::PFNoAutoHyph::val, &SwWW8ImplReader::Read_Hyphenation}, // pap.fNoAutoHyph;0 or 1;byte;
+ {NS_sprm::PWHeightAbs::val, nullptr}, // pap.wHeightAbs;w;word;
+ {NS_sprm::PDcs::val, nullptr}, // pap.dcs;DCS;short;
+ {NS_sprm::PShd80::val, &SwWW8ImplReader::Read_Shade}, // pap.shd;SHD;word;
+ {NS_sprm::PDyaFromText::val, nullptr}, // pap.dyaFromText;dya;word;
+ {NS_sprm::PDxaFromText::val, nullptr}, // pap.dxaFromText;dxa;word;
+ {NS_sprm::PFLocked::val, nullptr}, // pap.fLocked;0 or 1;byte;
+ {NS_sprm::PFWidowControl::val, &SwWW8ImplReader::Read_WidowControl}, // pap.fWidowControl;0 or 1;byte
+ {NS_sprm::LN_PRuler, nullptr}, // variable length;
+ {NS_sprm::PFKinsoku::val, &SwWW8ImplReader::Read_BoolItem}, // pap.fKinsoku;0 or 1;byte;
+ {NS_sprm::PFWordWrap::val, nullptr}, // pap.fWordWrap;0 or 1;byte;
+ {NS_sprm::PFOverflowPunct::val, &SwWW8ImplReader::Read_BoolItem}, // pap.fOverflowPunct; 0 or 1;byte;
+ {NS_sprm::PFTopLinePunct::val, nullptr}, // pap.fTopLinePunct;0 or 1;byte
+ {NS_sprm::PFAutoSpaceDE::val, &SwWW8ImplReader::Read_BoolItem}, // pap.fAutoSpaceDE;0 or 1;byte;
+ {NS_sprm::PFAutoSpaceDN::val, nullptr}, // pap.fAutoSpaceDN;0 or 1;byte;
+ {NS_sprm::PWAlignFont::val, &SwWW8ImplReader::Read_AlignFont}, // pap.wAlignFont;iFa;word;
+ {NS_sprm::PFrameTextFlow::val, nullptr}, // pap.fVertical pap.fBackward
+ // pap.fRotateFont;complex; word
+ {NS_sprm::LN_PISnapBaseLine, nullptr}, // obsolete, not applicable in
+ // Word97 and later versions;;byte;
+ {NS_sprm::LN_PAnld, &SwWW8ImplReader::Read_ANLevelDesc}, // pap.anld;;variable length;
+ {NS_sprm::LN_PPropRMark, nullptr}, // pap.fPropRMark;complex;
+ // variable length;
+ {NS_sprm::POutLvl::val, &SwWW8ImplReader::Read_POutLvl}, // pap.lvl;has no effect if pap.istd
+ // is < 1 or is > 9;byte;
+ {NS_sprm::PFBiDi::val, &SwWW8ImplReader::Read_ParaBiDi}, // ;;byte;
+ {NS_sprm::PFNumRMIns::val, nullptr}, // pap.fNumRMIns;1 or 0;bit;
+ {NS_sprm::LN_PCrLf, nullptr}, // ;;byte;
+ {NS_sprm::PNumRM::val, nullptr}, // pap.numrm;;variable length;
+ {NS_sprm::LN_PHugePapx, nullptr}, // ;fc in the data stream to locate
+ // the huge grpprl;long;
+ {NS_sprm::PHugePapx::val, nullptr}, // ;fc in the data stream to locate
+ // the huge grpprl;long;
+ {NS_sprm::PFUsePgsuSettings::val, &SwWW8ImplReader::Read_UsePgsuSettings}, // pap.fUsePgsuSettings;1 or 0;byte;
+ {NS_sprm::PFAdjustRight::val, nullptr}, // pap.fAdjustRight;1 or 0;byte;
+ {NS_sprm::CFRMarkDel::val, &SwWW8ImplReader::Read_CFRMarkDel}, // chp.fRMarkDel;1 or 0;bit;
+ {NS_sprm::CFRMarkIns::val, &SwWW8ImplReader::Read_CFRMark}, // chp.fRMark;1 or 0;bit;
+ {NS_sprm::CFFldVanish::val, &SwWW8ImplReader::Read_FieldVanish}, // chp.fFieldVanish;1 or 0;bit;
+ {NS_sprm::CPicLocation::val, &SwWW8ImplReader::Read_PicLoc}, // chp.fcPic and chp.fSpec;variable
+ // length, length recorded is always 4;
+ {NS_sprm::CIbstRMark::val, nullptr}, // chp.ibstRMark;index into
+ // sttbRMark;short;
+ {NS_sprm::CDttmRMark::val, nullptr}, // chp.dttmRMark;DTTM;long;
+ {NS_sprm::CFData::val, nullptr}, // chp.fData;1 or 0;bit;
+ {NS_sprm::CIdslRMark::val, nullptr}, // chp.idslRMReason;an index to
+ // a table of strings defined in
+ // Word 6.0 executables;short;
+ {NS_sprm::LN_CChs, &SwWW8ImplReader::Read_CharSet}, // chp.fChsDiff and chp.chse;3 bytes;
+ {NS_sprm::CSymbol::val, &SwWW8ImplReader::Read_Symbol}, // chp.fSpec, chp.xchSym and chp.ftcSym;
+ // variable length, length
+ // recorded is always 4;
+ {NS_sprm::CFOle2::val, &SwWW8ImplReader::Read_Obj}, // chp.fOle2;1 or 0;bit;
+ //NS_sprm::LN_CIdCharType, // obsolete: not applicable in Word97
+ // and later versions
+ {NS_sprm::CHighlight::val, &SwWW8ImplReader::Read_CharHighlight}, // chp.fHighlight, chp.icoHighlight;ico
+ // (fHighlight is set to 1 iff
+ // ico is not 0);byte;
+ {NS_sprm::LN_CObjLocation, &SwWW8ImplReader::Read_PicLoc}, // chp.fcObj;FC;long;
+ //NS_sprm::LN_CFFtcAsciSymb, ? ? ?,
+ {NS_sprm::CIstd::val, &SwWW8ImplReader::Read_CColl}, // chp.istd;istd,short;
+ {NS_sprm::CIstdPermute::val, nullptr}, // chp.istd;permutation vector;
+ // variable length;
+ {NS_sprm::LN_CDefault, nullptr}, // whole CHP;none;variable length;
+ {NS_sprm::CPlain::val, nullptr}, // whole CHP;none;length: 0;
+ {NS_sprm::CKcd::val, &SwWW8ImplReader::Read_Emphasis},
+ {NS_sprm::CFBold::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fBold;0,1, 128, or 129;byte;
+ {NS_sprm::CFItalic::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fItalic;0,1, 128, or 129; byte;
+ {NS_sprm::CFStrike::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fStrike;0,1, 128, or 129; byte;
+ {NS_sprm::CFOutline::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fOutline;0,1, 128, or 129; byte;
+ {NS_sprm::CFShadow::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fShadow;0,1, 128, or 129; byte;
+ {NS_sprm::CFSmallCaps::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fSmallCaps;0,1, 128, or 129;byte;
+ {NS_sprm::CFCaps::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fCaps;0,1, 128, or 129; byte;
+ {NS_sprm::CFVanish::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fVanish;0,1, 128, or 129; byte;
+ //NS_sprm::LN_CFtcDefault, 0, // ftc, only used internally, never
+ // stored in file;word;
+ {NS_sprm::CKul::val, &SwWW8ImplReader::Read_Underline}, // chp.kul;kul;byte;
+ {NS_sprm::LN_CSizePos, nullptr}, // chp.hps, chp.hpsPos;3 bytes;
+ {NS_sprm::CDxaSpace::val, &SwWW8ImplReader::Read_Kern}, // chp.dxaSpace;dxa;word;
+ {NS_sprm::LN_CLid, &SwWW8ImplReader::Read_Language}, // ;only used internally, never stored;
+ // word;
+ {NS_sprm::CIco::val, &SwWW8ImplReader::Read_TextColor}, // chp.ico;ico;byte;
+ {NS_sprm::CHps::val, &SwWW8ImplReader::Read_FontSize}, // chp.hps;hps;byte;
+ {NS_sprm::LN_CHpsInc, nullptr}, // chp.hps;byte;
+ {NS_sprm::CHpsPos::val, &SwWW8ImplReader::Read_SubSuperProp}, // chp.hpsPos;hps;byte;
+ {NS_sprm::LN_CHpsPosAdj, nullptr}, // chp.hpsPos;hps;byte;
+ {NS_sprm::CMajority::val, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fStrike,
+ // chp.fSmallCaps, chp.fVanish, chp.fCaps,
+ // chp.hps, chp.hpsPos, chp.dxaSpace,
+ // chp.kul, chp.ico, chp.rgftc, chp.rglid;
+ // complex;variable length, length byte
+ // plus size of following grpprl;
+ {NS_sprm::CIss::val, &SwWW8ImplReader::Read_SubSuper}, // chp.iss;iss;byte;
+ {NS_sprm::LN_CHpsNew50, nullptr}, // chp.hps;hps;variable width, length
+ // always recorded as 2;
+ {NS_sprm::LN_CHpsInc1, nullptr}, // chp.hps;complex; variable width,
+ // length always recorded as 2;
+ {NS_sprm::CHpsKern::val, &SwWW8ImplReader::Read_FontKern}, // chp.hpsKern;hps;short;
+ {NS_sprm::LN_CMajority50, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fStrike,
+ // chp.fSmallCaps, chp.fVanish, chp.fCaps,
+ // chp.ftc, chp.hps, chp.hpsPos, chp.kul,
+ // chp.dxaSpace, chp.ico;complex;
+ // variable length;
+ {NS_sprm::LN_CHpsMul, nullptr}, // chp.hps;percentage to grow hps;short;
+ {NS_sprm::CHresi::val, nullptr}, // ???? "sprmCYsri" chp.ysri;ysri;short;
+ {NS_sprm::CRgFtc0::val, &SwWW8ImplReader::Read_FontCode}, // chp.rgftc[0];ftc for ASCII text;short;
+ {NS_sprm::CRgFtc1::val, &SwWW8ImplReader::Read_FontCode}, // chp.rgftc[1];ftc for Far East text;
+ // short;
+ {NS_sprm::CRgFtc2::val, &SwWW8ImplReader::Read_FontCode}, // chp.rgftc[2];ftc for non-Far East text;
+ // short;
+ {NS_sprm::CCharScale::val, &SwWW8ImplReader::Read_ScaleWidth},
+ {NS_sprm::CFDStrike::val, &SwWW8ImplReader::Read_BoldUsw}, // chp.fDStrike;;byte;
+ {NS_sprm::CFImprint::val, &SwWW8ImplReader::Read_Relief}, // chp.fImprint;1 or 0;bit;
+ {NS_sprm::CFSpec::val, &SwWW8ImplReader::Read_Special}, // chp.fSpec;1 or 0;bit;
+ {NS_sprm::CFObj::val, &SwWW8ImplReader::Read_Obj}, // chp.fObj;1 or 0;bit;
+ {NS_sprm::CPropRMark90::val, &SwWW8ImplReader::Read_CPropRMark}, // chp.fPropRMark, chp.ibstPropRMark,
+ // chp.dttmPropRMark;Complex;variable
+ // length always recorded as 7 bytes;
+ {NS_sprm::CFEmboss::val, &SwWW8ImplReader::Read_Relief}, // chp.fEmboss;1 or 0;bit;
+ {NS_sprm::CSfxText::val, &SwWW8ImplReader::Read_TextAnim}, // chp.sfxtText;text animation;byte;
+ {NS_sprm::CFBiDi::val, &SwWW8ImplReader::Read_Bidi},
+ {NS_sprm::LN_CFDiacColor, nullptr},
+ {NS_sprm::CFBoldBi::val, &SwWW8ImplReader::Read_BoldBiDiUsw},
+ {NS_sprm::CFItalicBi::val, &SwWW8ImplReader::Read_BoldBiDiUsw},
+ {NS_sprm::CFtcBi::val, &SwWW8ImplReader::Read_FontCode},
+ {NS_sprm::CLidBi::val, &SwWW8ImplReader::Read_Language},
+ //NS_sprm::CIcoBi::val, ? ? ?,
+ {NS_sprm::CHpsBi::val, &SwWW8ImplReader::Read_FontSize},
+ {NS_sprm::CDispFldRMark::val, nullptr}, // chp.fDispFieldRMark,
+ // chp.ibstDispFieldRMark,
+ // chp.dttmDispFieldRMark;
+ // Complex;variable length
+ // always recorded as 39 bytes;
+ {NS_sprm::CIbstRMarkDel::val, nullptr}, // chp.ibstRMarkDel;index into
+ // sttbRMark;short;
+ {NS_sprm::CDttmRMarkDel::val, nullptr}, // chp.dttmRMarkDel;DTTM;long;
+ {NS_sprm::CBrc80::val, &SwWW8ImplReader::Read_CharBorder}, // chp.brc;BRC;long;
+ {NS_sprm::CBrc::val, &SwWW8ImplReader::Read_CharBorder}, // chp.brc;BRC;long;
+ {NS_sprm::CShd80::val, &SwWW8ImplReader::Read_CharShadow}, // chp.shd;SHD;short;
+ {NS_sprm::CIdslRMarkDel::val, nullptr}, // chp.idslRMReasonDel;an index to
+ // a table of strings defined in
+ // Word 6.0 executables;short;
+ {NS_sprm::CFUsePgsuSettings::val, nullptr}, // chp.fUsePgsuSettings; 1 or 0;bit;
+ {NS_sprm::LN_CCpg, nullptr}, // ;;word;
+ {NS_sprm::CRgLid0_80::val, &SwWW8ImplReader::Read_Language}, // chp.rglid[0];
+ // LID: for non-Far East text;word;
+ {NS_sprm::CRgLid1_80::val, &SwWW8ImplReader::Read_Language}, // chp.rglid[1];
+ // LID: for Far East text;word;
+ {NS_sprm::CIdctHint::val, &SwWW8ImplReader::Read_IdctHint}, // chp.idctHint;IDCT: byte;
+ {NS_sprm::LN_PicBrcl, nullptr}, // pic.brcl;brcl (see PIC structure
+ // definition);byte;
+ {NS_sprm::LN_PicScale, nullptr}, // pic.mx, pic.my, pic.dxaCropleft,
+ // pic.dyaCropTop pic.dxaCropRight,
+ // pic.dyaCropBottom;Complex;
+ // length byte plus 12 bytes;
+ {NS_sprm::PicBrcTop80::val, nullptr}, // pic.brcTop;BRC;long;
+ {NS_sprm::PicBrcLeft80::val, nullptr}, // pic.brcLeft;BRC;long;
+ {NS_sprm::PicBrcBottom80::val, nullptr}, // pic.brcBottom;BRC;long;
+ {NS_sprm::PicBrcRight80::val, nullptr}, // pic.brcRight;BRC;long;
+ {NS_sprm::ScnsPgn::val, nullptr}, // sep.cnsPgn;cns;byte;
+ {NS_sprm::SiHeadingPgn::val, nullptr}, // sep.iHeadingPgn;heading number level;
+ // byte;
+ {NS_sprm::LN_SOlstAnm, &SwWW8ImplReader::Read_OLST}, // sep.olstAnm;OLST;variable length;
+ {NS_sprm::SDxaColWidth::val, nullptr}, // sep.rgdxaColWidthSpacing;complex;
+ // 3 bytes;
+ {NS_sprm::SDxaColSpacing::val, nullptr}, // sep.rgdxaColWidthSpacing;complex;
+ // 3 bytes;
+ {NS_sprm::SFEvenlySpaced::val, nullptr}, // sep.fEvenlySpaced; 1 or 0;byte;
+ {NS_sprm::SFProtected::val, nullptr}, // sep.fUnlocked;1 or 0;byte;
+ {NS_sprm::SDmBinFirst::val, nullptr}, // sep.dmBinFirst;;word;
+ {NS_sprm::SDmBinOther::val, nullptr}, // sep.dmBinOther;;word;
+ {NS_sprm::SBkc::val, nullptr}, // sep.bkc;bkc;byte;
+ {NS_sprm::SFTitlePage::val, nullptr}, // sep.fTitlePage;0 or 1;byte;
+ {NS_sprm::SCcolumns::val, nullptr}, // sep.ccolM1;# of cols - 1;word;
+ {NS_sprm::SDxaColumns::val, nullptr}, // sep.dxaColumns;dxa;word;
+ {NS_sprm::LN_SFAutoPgn, nullptr}, // sep.fAutoPgn;obsolete;byte;
+ {NS_sprm::SNfcPgn::val, nullptr}, // sep.nfcPgn;nfc;byte;
+ {NS_sprm::LN_SDyaPgn, nullptr}, // sep.dyaPgn;dya;short;
+ {NS_sprm::LN_SDxaPgn, nullptr}, // sep.dxaPgn;dya;short;
+ {NS_sprm::SFPgnRestart::val, nullptr}, // sep.fPgnRestart;0 or 1;byte;
+ {NS_sprm::SFEndnote::val, nullptr}, // sep.fEndnote;0 or 1;byte;
+ {NS_sprm::SLnc::val, nullptr}, // sep.lnc;lnc;byte;
+ {NS_sprm::LN_SGprfIhdt, nullptr}, // sep.grpfIhdt;grpfihdt;byte;
+ {NS_sprm::SNLnnMod::val, nullptr}, // sep.nLnnMod;non-neg int.;word;
+ {NS_sprm::SDxaLnn::val, nullptr}, // sep.dxaLnn;dxa;word;
+ {NS_sprm::SDyaHdrTop::val, nullptr}, // sep.dyaHdrTop;dya;word;
+ {NS_sprm::SDyaHdrBottom::val, nullptr}, // sep.dyaHdrBottom;dya;word;
+ {NS_sprm::SLBetween::val, nullptr}, // sep.fLBetween;0 or 1;byte;
+ {NS_sprm::SVjc::val, &SwWW8ImplReader::Read_TextVerticalAdjustment}, // sep.vjc;vjc;byte;
+ {NS_sprm::SLnnMin::val, nullptr}, // sep.lnnMin;lnn;word;
+ {NS_sprm::SPgnStart97::val, nullptr}, // sep.pgnStart;pgn;word;
+ {NS_sprm::SBOrientation::val, nullptr}, // sep.dmOrientPage;dm;byte;
+ //NS_sprm::LN_SBCustomize, ? ? ?,
+ {NS_sprm::SXaPage::val, nullptr}, // sep.xaPage;xa;word;
+ {NS_sprm::SYaPage::val, nullptr}, // sep.yaPage;ya;word;
+ {0x2205, nullptr}, // ???? "sprmSDxaLeft" sep.dxaLeft;
+ // dxa;word;
+ {NS_sprm::SDxaLeft::val, nullptr}, // sep.dxaLeft;dxa;word;
+ {NS_sprm::SDxaRight::val, nullptr}, // sep.dxaRight;dxa;word;
+ {NS_sprm::SDyaTop::val, nullptr}, // sep.dyaTop;dya;word;
+ {NS_sprm::SDyaBottom::val, nullptr}, // sep.dyaBottom;dya;word;
+ {NS_sprm::SDzaGutter::val, nullptr}, // sep.dzaGutter;dza;word;
+ {NS_sprm::SDmPaperReq::val, nullptr}, // sep.dmPaperReq;dm;word;
+ {NS_sprm::LN_SPropRMark, nullptr}, // sep.fPropRMark, sep.ibstPropRMark,
+ // sep.dttmPropRMark;complex; variable
+ // length always recorded as 7 bytes;
+ //NS_sprm::SFBiDi::val, ? ? ?,
+ //NS_sprm::LN_SFFacingCol, ? ? ?,
+ {NS_sprm::SFRTLGutter::val, nullptr}, // set to 1 if gutter is on the right.
+ {NS_sprm::SBrcTop80::val, nullptr}, // sep.brcTop;BRC;long;
+ {NS_sprm::SBrcLeft80::val, nullptr}, // sep.brcLeft;BRC;long;
+ {NS_sprm::SBrcBottom80::val, nullptr}, // sep.brcBottom;BRC;long;
+ {NS_sprm::SBrcRight80::val, nullptr}, // sep.brcRight;BRC;long;
+ {NS_sprm::SPgbProp::val, nullptr}, // sep.pgbProp;word;
+ {NS_sprm::SDxtCharSpace::val, nullptr}, // sep.dxtCharSpace;dxt;long;
+ {NS_sprm::SDyaLinePitch::val, nullptr}, // sep.dyaLinePitch;dya;
+ // WRONG:long; RIGHT:short; !
+ //NS_sprm::SClm::val, ? ? ?,
+ {NS_sprm::STextFlow::val, nullptr}, // sep.wTextFlow;complex;short
+ {NS_sprm::TJc90::val, nullptr}, // tap.jc;jc;word
+ // (low order byte is significant);
+ {NS_sprm::TDxaLeft::val, nullptr}, // tap.rgdxaCenter;dxa;word;
+ {NS_sprm::TDxaGapHalf::val, nullptr}, // tap.dxaGapHalf,
+ // tap.rgdxaCenter;dxa;word;
+ {NS_sprm::TFCantSplit90::val, nullptr}, // tap.fCantSplit90;1 or 0;byte;
+ {NS_sprm::TTableHeader::val, nullptr}, // tap.fTableHeader;1 or 0;byte;
+ {NS_sprm::TFCantSplit::val, nullptr}, // tap.fCantSplit;1 or 0;byte;
+ {NS_sprm::TTableBorders80::val, nullptr}, // tap.rgbrcTable;complex;24 bytes;
+ {NS_sprm::LN_TDefTable10, nullptr}, // tap.rgdxaCenter, tap.rgtc;complex;
+ // variable length;
+ {NS_sprm::TDyaRowHeight::val, nullptr}, // tap.dyaRowHeight;dya;word;
+ {NS_sprm::TDefTable::val, nullptr}, // tap.rgtc;complex
+ {NS_sprm::TDefTableShd80::val, nullptr}, // tap.rgshd;complex
+ {NS_sprm::TTlp::val, nullptr}, // tap.tlp;TLP;4 bytes;
+ //NS_sprm::TFBiDi::val, ? ? ?,
+ //NS_sprm::LN_THTMLProps, ? ? ?,
+ {NS_sprm::TSetBrc80::val, nullptr}, // tap.rgtc[].rgbrc;complex;5 bytes;
+ {NS_sprm::TInsert::val, nullptr}, // tap.rgdxaCenter, tap.rgtc;complex;
+ // 4 bytes;
+ {NS_sprm::TDelete::val, nullptr}, // tap.rgdxaCenter, tap.rgtc;complex;
+ // word;
+ {NS_sprm::TDxaCol::val, nullptr}, // tap.rgdxaCenter;complex;4 bytes;
+ {NS_sprm::TMerge::val, nullptr}, // tap.fFirstMerged, tap.fMerged;
+ // complex; word;
+ {NS_sprm::TSplit::val, nullptr}, // tap.fFirstMerged, tap.fMerged;
+ // complex;word;
+ {NS_sprm::LN_TSetBrc10, nullptr}, // tap.rgtc[].rgbrc;complex;5 bytes;
+ {NS_sprm::LN_TSetShd80, nullptr}, // tap.rgshd;complex;4 bytes;
+ {NS_sprm::LN_TSetShdOdd80, nullptr}, // tap.rgshd;complex;4 bytes;
+ {NS_sprm::TTextFlow::val, nullptr}, // tap.rgtc[].fVertical
+ // tap.rgtc[].fBackward
+ // tap.rgtc[].fRotateFont
+ // 0 or 10 or 10 or 1;word;
+ //NS_sprm::LN_TDiagLine, ? ? ? ,
+ {NS_sprm::TVertMerge::val, nullptr}, // tap.rgtc[].vertMerge;complex;variable
+ // length always recorded as 2 bytes;
+ {NS_sprm::TVertAlign::val, nullptr}, // tap.rgtc[].vertAlign;complex;variable
+ // length always recorded as 3 bytes;
+ {NS_sprm::CFELayout::val, &SwWW8ImplReader::Read_DoubleLine_Rotate},
+ {NS_sprm::PItap::val, nullptr},
+ {NS_sprm::TTableWidth::val, nullptr}, // recorded as 3 bytes;
+ {NS_sprm::TDefTableShd::val, nullptr},
+ {NS_sprm::TTableBorders::val, nullptr},
+ {NS_sprm::TBrcTopCv::val, nullptr},
+ {NS_sprm::TBrcLeftCv::val, nullptr},
+ {NS_sprm::TBrcBottomCv::val, nullptr},
+ {NS_sprm::TBrcRightCv::val, nullptr},
+ {NS_sprm::TCellPaddingDefault::val, nullptr},
+ {NS_sprm::TCellPadding::val, nullptr},
+ {0xD238, nullptr}, // undocumented sep
+ {NS_sprm::PBrcTop::val, &SwWW8ImplReader::Read_Border},
+ {NS_sprm::PBrcLeft::val, &SwWW8ImplReader::Read_Border},
+ {NS_sprm::PBrcBottom::val, &SwWW8ImplReader::Read_Border},
+ {NS_sprm::PBrcRight::val, &SwWW8ImplReader::Read_Border},
+ {NS_sprm::PBrcBetween::val, &SwWW8ImplReader::Read_Border},
+ {NS_sprm::TWidthIndent::val, nullptr},
+ {NS_sprm::CRgLid0::val, &SwWW8ImplReader::Read_Language}, // chp.rglid[0];
+ // LID: for non-Far East text;
+ {NS_sprm::CRgLid1::val, nullptr}, // chp.rglid[1];
+ // LID: for Far East text
+ {0x6463, nullptr}, // undocumented
+ {NS_sprm::PJc::val, &SwWW8ImplReader::Read_RTLJustify},
+ {NS_sprm::PDxaLeft::val, &SwWW8ImplReader::Read_LR},
+ {NS_sprm::PDxaLeft1::val, &SwWW8ImplReader::Read_LR},
+ {NS_sprm::PDxaRight::val, &SwWW8ImplReader::Read_LR},
+ {NS_sprm::TFAutofit::val, nullptr},
+ {NS_sprm::TPc::val, nullptr},
+ {NS_sprm::TDxaAbs::val, nullptr},
+ {NS_sprm::TDyaAbs::val, nullptr},
+ {NS_sprm::TDxaFromText::val, nullptr},
+ {NS_sprm::SRsid::val, nullptr},
+ {NS_sprm::SFpc::val, nullptr},
+ {NS_sprm::PFInnerTableCell::val, &SwWW8ImplReader::Read_TabCellEnd},
+ {NS_sprm::PFInnerTtp::val, &SwWW8ImplReader::Read_TabRowEnd},
+ {NS_sprm::CRsidProp::val, nullptr},
+ {NS_sprm::CRsidText::val, nullptr},
+ {NS_sprm::CCv::val, &SwWW8ImplReader::Read_TextForeColor},
+ {NS_sprm::CCvUl::val, &SwWW8ImplReader::Read_UnderlineColor},
+ {NS_sprm::PShd::val, &SwWW8ImplReader::Read_ParaBackColor},
+ {NS_sprm::PRsid::val, nullptr},
+ {NS_sprm::TWidthBefore::val, nullptr},
+ {NS_sprm::TSetShdTable::val, nullptr},
+ {NS_sprm::TDefTableShdRaw::val, nullptr},
+ {NS_sprm::CShd::val, &SwWW8ImplReader::Read_TextBackColor},
+ {NS_sprm::SRncFtn::val, nullptr},
+ {NS_sprm::PFDyaBeforeAuto::val, &SwWW8ImplReader::Read_ParaAutoBefore},
+ {NS_sprm::PFDyaAfterAuto::val, &SwWW8ImplReader::Read_ParaAutoAfter},
+ {NS_sprm::PFContextualSpacing::val, &SwWW8ImplReader::Read_ParaContextualSpacing},
+ {NS_sprm::CLbcCRJ::val, &SwWW8ImplReader::Read_LineBreakClear},
+ };
+
+ static wwSprmDispatcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms));
+ return &aSprmSrch;
+}
+
+// helper routines : find SPRM
+
+const SprmReadInfo& SwWW8ImplReader::GetSprmReadInfo(sal_uInt16 nId) const
+{
+ ww::WordVersion eVersion = m_xWwFib->GetFIBVersion();
+ const wwSprmDispatcher *pDispatcher;
+ if (eVersion <= ww::eWW2)
+ pDispatcher = GetWW2SprmDispatcher();
+ else if (eVersion < ww::eWW8)
+ pDispatcher = GetWW6SprmDispatcher();
+ else
+ pDispatcher = GetWW8SprmDispatcher();
+
+ SprmReadInfo aSrch = {0, nullptr};
+ aSrch.nId = nId;
+ const SprmReadInfo* pFound = pDispatcher->search(aSrch);
+
+ if (!pFound)
+ {
+ aSrch.nId = 0;
+ pFound = pDispatcher->search(aSrch);
+ }
+
+ return *pFound;
+}
+
+// helper routines : SPRMs
+
+void SwWW8ImplReader::EndSprm( sal_uInt16 nId )
+{
+ if( ( nId > 255 ) && ( nId < 0x0800 ) ) return;
+
+ const SprmReadInfo& rSprm = GetSprmReadInfo( nId );
+
+ if (rSprm.pReadFnc)
+ (this->*rSprm.pReadFnc)( nId, nullptr, -1 );
+}
+
+short SwWW8ImplReader::ImportSprm(const sal_uInt8* pPos, sal_Int32 nMemLen, sal_uInt16 nId)
+{
+ if (!nId)
+ nId = m_oSprmParser->GetSprmId(pPos);
+
+ OSL_ENSURE( nId != 0xff, "Sprm FF !!!!" );
+
+ const SprmReadInfo& rSprm = GetSprmReadInfo(nId);
+
+ sal_Int32 nFixedLen = m_oSprmParser->DistanceToData(nId);
+ sal_Int32 nL = m_oSprmParser->GetSprmSize(nId, pPos, nMemLen);
+
+ if (rSprm.pReadFnc)
+ (this->*rSprm.pReadFnc)(nId, pPos + nFixedLen, nL - nFixedLen);
+
+ return nL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8scan.cxx b/sw/source/filter/ww8/ww8scan.cxx
new file mode 100644
index 0000000000..80f9102dcb
--- /dev/null
+++ b/sw/source/filter/ww8/ww8scan.cxx
@@ -0,0 +1,8540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "ww8scan.hxx"
+#include "ww8par.hxx"
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <algorithm>
+
+#include <i18nlangtag/mslangid.hxx>
+#include "sprmids.hxx"
+#include <rtl/tencinfo.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <swerror.h>
+
+#include <comphelper/string.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <i18nlangtag/lang.h>
+#include <o3tl/safeint.hxx>
+#include <tools/stream.hxx>
+
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star::lang;
+
+namespace
+{
+ /**
+ winword strings are typically Belt and Braces strings preceded with a
+ pascal style count, and ending with a c style 0 terminator. 16bit chars
+ and count for ww8+ and 8bit chars and count for ww7-. The count and 0
+ can be checked for integrity to catch errors (e.g. lotus created
+ documents) where in error 8bit strings are used instead of 16bits
+ strings for style names.
+ */
+ bool TestBeltAndBraces(SvStream& rStrm)
+ {
+ bool bRet = false;
+ sal_uInt64 nOldPos = rStrm.Tell();
+ sal_uInt16 nBelt(0);
+ rStrm.ReadUInt16( nBelt );
+ nBelt *= sizeof(sal_Unicode);
+ if (rStrm.good() && (rStrm.remainingSize() >= (nBelt + sizeof(sal_Unicode))))
+ {
+ rStrm.SeekRel(nBelt);
+ if (rStrm.good())
+ {
+ sal_Unicode cBraces(0);
+ rStrm.ReadUtf16( cBraces );
+ if (rStrm.good() && cBraces == 0)
+ bRet = true;
+ }
+ }
+ rStrm.Seek(nOldPos);
+ return bRet;
+ }
+}
+
+const wwSprmSearcher *wwSprmParser::GetWW2SprmSearcher()
+{
+ //double lock me
+ // WW2 Sprms
+ static const SprmInfoRow aSprms[] =
+ {
+ { 0, { 0, L_FIX} }, // "Default-sprm", will be skipped
+ { 2, { 1, L_FIX} }, // "sprmPIstd", pap.istd (style code)
+ { 3, { 0, L_VAR} }, // "sprmPIstdPermute pap.istd permutation
+ { 4, { 1, L_FIX} }, // "sprmPIncLv1" pap.istddifference
+ { 5, { 1, L_FIX} }, // "sprmPJc" pap.jc (justification)
+ { 6, { 1, L_FIX} }, // "sprmPFSideBySide" pap.fSideBySide
+ { 7, { 1, L_FIX} }, // "sprmPFKeep" pap.fKeep
+ { 8, { 1, L_FIX} }, // "sprmPFKeepFollow " pap.fKeepFollow
+ { 9, { 1, L_FIX} }, // "sprmPPageBreakBefore" pap.fPageBreakBefore
+ { 10, { 1, L_FIX} }, // "sprmPBrcl" pap.brcl
+ { 11, { 1, L_FIX} }, // "sprmPBrcp" pap.brcp
+ { 12, { 1, L_FIX} }, // "sprmPNfcSeqNumb" pap.nfcSeqNumb
+ { 13, { 1, L_FIX} }, // "sprmPNoSeqNumb" pap.nnSeqNumb
+ { 14, { 1, L_FIX} }, // "sprmPFNoLineNumb" pap.fNoLnn
+ { 15, { 0, L_VAR} }, // "?sprmPChgTabsPapx" pap.itbdMac, ...
+ { 16, { 2, L_FIX} }, // "sprmPDxaRight" pap.dxaRight
+ { 17, { 2, L_FIX} }, // "sprmPDxaLeft" pap.dxaLeft
+ { 18, { 2, L_FIX} }, // "sprmPNest" pap.dxaLeft
+ { 19, { 2, L_FIX} }, // "sprmPDxaLeft1" pap.dxaLeft1
+ { 20, { 2, L_FIX} }, // "sprmPDyaLine" pap.lspd an LSPD
+ { 21, { 2, L_FIX} }, // "sprmPDyaBefore" pap.dyaBefore
+ { 22, { 2, L_FIX} }, // "sprmPDyaAfter" pap.dyaAfter
+ { 23, { 0, L_VAR} }, // "?sprmPChgTabs" pap.itbdMac, pap.rgdxaTab, ...
+ { 24, { 1, L_FIX} }, // "sprmPFInTable" pap.fInTable
+ { 25, { 1, L_FIX} }, // "sprmPTtp" pap.fTtp
+ { 26, { 2, L_FIX} }, // "sprmPDxaAbs" pap.dxaAbs
+ { 27, { 2, L_FIX} }, // "sprmPDyaAbs" pap.dyaAbs
+ { 28, { 2, L_FIX} }, // "sprmPDxaWidth" pap.dxaWidth
+ { 29, { 1, L_FIX} }, // "sprmPPc" pap.pcHorz, pap.pcVert
+ { 30, { 2, L_FIX} }, // "sprmPBrcTop10" pap.brcTop BRC10
+ { 31, { 2, L_FIX} }, // "sprmPBrcLeft10" pap.brcLeft BRC10
+ { 32, { 2, L_FIX} }, // "sprmPBrcBottom10" pap.brcBottom BRC10
+ { 33, { 2, L_FIX} }, // "sprmPBrcRight10" pap.brcRight BRC10
+ { 34, { 2, L_FIX} }, // "sprmPBrcBetween10" pap.brcBetween BRC10
+ { 35, { 2, L_FIX} }, // "sprmPBrcBar10" pap.brcBar BRC10
+ { 36, { 2, L_FIX} }, // "sprmPFromText10" pap.dxaFromText dxa
+ { 37, { 1, L_FIX} }, // "sprmPWr" pap.wr wr
+ { 38, { 2, L_FIX} }, // "sprmPBrcTop" pap.brcTop BRC
+ { 39, { 2, L_FIX} }, // "sprmPBrcLeft" pap.brcLeft BRC
+ { 40, { 2, L_FIX} }, // "sprmPBrcBottom" pap.brcBottom BRC
+ { 41, { 2, L_FIX} }, // "sprmPBrcRight" pap.brcRight BRC
+ { 42, { 2, L_FIX} }, // "sprmPBrcBetween" pap.brcBetween BRC
+ { 43, { 2, L_FIX} }, // "sprmPBrcBar" pap.brcBar BRC word
+ { 44, { 1, L_FIX} }, // "sprmPFNoAutoHyph" pap.fNoAutoHyph
+ { 45, { 2, L_FIX} }, // "sprmPWHeightAbs" pap.wHeightAbs w
+ { 46, { 2, L_FIX} }, // "sprmPDcs" pap.dcs DCS
+ { 47, { 2, L_FIX} }, // "sprmPShd" pap.shd SHD
+ { 48, { 2, L_FIX} }, // "sprmPDyaFromText" pap.dyaFromText dya
+ { 49, { 2, L_FIX} }, // "sprmPDxaFromText" pap.dxaFromText dxa
+ { 50, { 1, L_FIX} }, // "sprmPFBiDi" pap.fBiDi 0 or 1 byte
+ { 51, { 1, L_FIX} }, // "sprmPFWidowControl" pap.fWidowControl 0 or 1 byte
+ { 52, { 0, L_FIX} }, // "?sprmPRuler 52"
+ { 53, { 1, L_FIX} }, // "sprmCFStrikeRM" chp.fRMarkDel 1 or 0 bit
+ { 54, { 1, L_FIX} }, // "sprmCFRMark" chp.fRMark 1 or 0 bit
+ { 55, { 1, L_FIX} }, // "sprmCFFieldVanish" chp.fFieldVanish 1 or 0 bit
+ { 57, { 0, L_VAR} }, // "sprmCDefault" whole CHP
+ { 58, { 0, L_FIX} }, // "sprmCPlain" whole CHP
+ { 60, { 1, L_FIX} }, // "sprmCFBold" chp.fBold 0,1, 128, or 129
+ { 61, { 1, L_FIX} }, // "sprmCFItalic" chp.fItalic 0,1, 128, or 129
+ { 62, { 1, L_FIX} }, // "sprmCFStrike" chp.fStrike 0,1, 128, or 129
+ { 63, { 1, L_FIX} }, // "sprmCFOutline" chp.fOutline 0,1, 128, or 129
+ { 64, { 1, L_FIX} }, // "sprmCFShadow" chp.fShadow 0,1, 128, or 129
+ { 65, { 1, L_FIX} }, // "sprmCFSmallCaps" chp.fSmallCaps 0,1, 128, or 129
+ { 66, { 1, L_FIX} }, // "sprmCFCaps" chp.fCaps 0,1, 128, or 129
+ { 67, { 1, L_FIX} }, // "sprmCFVanish" chp.fVanish 0,1, 128, or 129
+ { 68, { 2, L_FIX} }, // "sprmCFtc" chp.ftc ftc word
+ { 69, { 1, L_FIX} }, // "sprmCKul" chp.kul kul byte
+ { 70, { 3, L_FIX} }, // "sprmCSizePos" chp.hps, chp.hpsPos
+ { 71, { 2, L_FIX} }, // "sprmCDxaSpace" chp.dxaSpace dxa
+ { 72, { 2, L_FIX} }, // "sprmCLid" chp.lid LID
+ { 73, { 1, L_FIX} }, // "sprmCIco" chp.ico ico byte
+ { 74, { 1, L_FIX} }, // "sprmCHps" chp.hps hps !word!
+ { 75, { 1, L_FIX} }, // "sprmCHpsInc" chp.hps
+ { 76, { 1, L_FIX} }, // "sprmCHpsPos" chp.hpsPos hps !word!
+ { 77, { 1, L_FIX} }, // "sprmCHpsPosAdj" chp.hpsPos hps
+ { 78, { 0, L_VAR} }, // "?sprmCMajority" chp.fBold, chp.fItalic, ...
+ { 80, { 1, L_FIX} }, // "sprmCFBoldBi" chp.fBoldBi
+ { 81, { 1, L_FIX} }, // "sprmCFItalicBi" chp.fItalicBi
+ { 82, { 2, L_FIX} }, // "sprmCFtcBi" chp.ftcBi
+ { 83, { 2, L_FIX} }, // "sprmClidBi" chp.lidBi
+ { 84, { 1, L_FIX} }, // "sprmCIcoBi" chp.icoBi
+ { 85, { 1, L_FIX} }, // "sprmCHpsBi" chp.hpsBi
+ { 86, { 1, L_FIX} }, // "sprmCFBiDi" chp.fBiDi
+ { 87, { 1, L_FIX} }, // "sprmCFDiacColor" chp.fDiacUSico
+ { 94, { 1, L_FIX} }, // "sprmPicBrcl" pic.brcl brcl (see PIC definition)
+ { 95, {12, L_VAR} }, // "sprmPicScale" pic.mx, pic.my, pic.dxaCropleft,
+ { 96, { 2, L_FIX} }, // "sprmPicBrcTop" pic.brcTop BRC word
+ { 97, { 2, L_FIX} }, // "sprmPicBrcLeft" pic.brcLeft BRC word
+ { 98, { 2, L_FIX} }, // "sprmPicBrcBottom" pic.brcBottom BRC word
+ { 99, { 2, L_FIX} }, // "sprmPicBrcRight" pic.brcRight BRC word
+ {112, { 1, L_FIX} }, // "sprmSFRTLGutter", set to one if gutter is on
+ {114, { 1, L_FIX} }, // "sprmSFBiDi" ;;;
+ {115, { 2, L_FIX} }, // "sprmSDmBinFirst" sep.dmBinFirst word
+ {116, { 2, L_FIX} }, // "sprmSDmBinOther" sep.dmBinOther word
+ {117, { 1, L_FIX} }, // "sprmSBkc" sep.bkc bkc byte
+ {118, { 1, L_FIX} }, // "sprmSFTitlePage" sep.fTitlePage 0 or 1 byte
+ {119, { 2, L_FIX} }, // "sprmSCcolumns" sep.ccolM1 # of cols - 1 word
+ {120, { 2, L_FIX} }, // "sprmSDxaColumns" sep.dxaColumns dxa word
+ {121, { 1, L_FIX} }, // "sprmSFAutoPgn" sep.fAutoPgn obsolete byte
+ {122, { 1, L_FIX} }, // "sprmSNfcPgn" sep.nfcPgn nfc byte
+ {123, { 2, L_FIX} }, // "sprmSDyaPgn" sep.dyaPgn dya short
+ {124, { 2, L_FIX} }, // "sprmSDxaPgn" sep.dxaPgn dya short
+ {125, { 1, L_FIX} }, // "sprmSFPgnRestart" sep.fPgnRestart 0 or 1 byte
+ {126, { 1, L_FIX} }, // "sprmSFEndnote" sep.fEndnote 0 or 1 byte
+ {127, { 1, L_FIX} }, // "sprmSLnc" sep.lnc lnc byte
+ {128, { 1, L_FIX} }, // "sprmSGprfIhdt" sep.grpfIhdt grpfihdt
+ {129, { 2, L_FIX} }, // "sprmSNLnnMod" sep.nLnnMod non-neg int. word
+ {130, { 2, L_FIX} }, // "sprmSDxaLnn" sep.dxaLnn dxa word
+ {131, { 2, L_FIX} }, // "sprmSDyaHdrTop" sep.dyaHdrTop dya word
+ {132, { 2, L_FIX} }, // "sprmSDyaHdrBottom" sep.dyaHdrBottom dya word
+ {133, { 1, L_FIX} }, // "sprmSLBetween" sep.fLBetween 0 or 1 byte
+ {134, { 1, L_FIX} }, // "sprmSVjc" sep.vjc vjc byte
+ {135, { 2, L_FIX} }, // "sprmSLnnMin" sep.lnnMin lnn word
+ {136, { 2, L_FIX} }, // "sprmSPgnStart" sep.pgnStart pgn word
+ {137, { 1, L_FIX} }, // "sprmSBOrientation" sep.dmOrientPage dm byte
+ {138, { 1, L_FIX} }, // "sprmSFFacingCol" ;;;
+ {139, { 2, L_FIX} }, // "sprmSXaPage" sep.xaPage xa word
+ {140, { 2, L_FIX} }, // "sprmSYaPage" sep.yaPage ya word
+ {141, { 2, L_FIX} }, // "sprmSDxaLeft" sep.dxaLeft dxa word
+ {142, { 2, L_FIX} }, // "sprmSDxaRight" sep.dxaRight dxa word
+ {143, { 2, L_FIX} }, // "sprmSDyaTop" sep.dyaTop dya word
+ {144, { 2, L_FIX} }, // "sprmSDyaBottom" sep.dyaBottom dya word
+ {145, { 2, L_FIX} }, // "sprmSDzaGutter" sep.dzaGutter dza word
+ {146, { 2, L_FIX} }, // "sprmTJc" tap.jc jc (low order byte is significant)
+ {147, { 2, L_FIX} }, // "sprmTDxaLeft" tap.rgdxaCenter dxa word
+ {148, { 2, L_FIX} }, // "sprmTDxaGapHalf" tap.dxaGapHalf, tap.rgdxaCenter
+ {149, { 1, L_FIX} }, // "sprmTFBiDi" ;;;
+ {152, { 0, L_VAR2} },// "sprmTDefTable10" tap.rgdxaCenter, tap.rgtc complex
+ {153, { 2, L_FIX} }, // "sprmTDyaRowHeight" tap.dyaRowHeight dya word
+ {154, { 0, L_VAR2} },// "sprmTDefTable" tap.rgtc complex
+ {155, { 1, L_VAR} }, // "sprmTDefTableShd" tap.rgshd complex
+ {157, { 5, L_FIX} }, // "sprmTSetBrc" tap.rgtc[].rgbrc complex 5 bytes
+ {158, { 4, L_FIX} }, // "sprmTInsert" tap.rgdxaCenter,tap.rgtc complex
+ {159, { 2, L_FIX} }, // "sprmTDelete" tap.rgdxaCenter, tap.rgtc complex
+ {160, { 4, L_FIX} }, // "sprmTDxaCol" tap.rgdxaCenter complex
+ {161, { 2, L_FIX} }, // "sprmTMerge" tap.fFirstMerged, tap.fMerged complex
+ {162, { 2, L_FIX} }, // "sprmTSplit" tap.fFirstMerged, tap.fMerged complex
+ {163, { 5, L_FIX} }, // "sprmTSetBrc10" tap.rgtc[].rgbrc complex 5 bytes
+ {164, { 4, L_FIX} }, // "sprmTSetShd", tap.rgshd complex 4 bytes
+ };
+
+ static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms));
+ return &aSprmSrch;
+};
+
+const wwSprmSearcher *wwSprmParser::GetWW6SprmSearcher(const WW8Fib& rFib)
+{
+ //double lock me
+ // WW7- Sprms
+ static const SprmInfoRow aSprms[] =
+ {
+ { 0, { 0, L_FIX} }, // "Default-sprm", is skipped
+ {NS_sprm::v6::sprmPIstd, { 2, L_FIX} }, // pap.istd (style code)
+ {NS_sprm::v6::sprmPIstdPermute, { 3, L_VAR} }, // pap.istd permutation
+ {NS_sprm::v6::sprmPIncLv1, { 1, L_FIX} }, // pap.istddifference
+ {NS_sprm::v6::sprmPJc, { 1, L_FIX} }, // pap.jc (justification)
+ {NS_sprm::v6::sprmPFSideBySide, { 1, L_FIX} }, // pap.fSideBySide
+ {NS_sprm::v6::sprmPFKeep, { 1, L_FIX} }, // pap.fKeep
+ {NS_sprm::v6::sprmPFKeepFollow, { 1, L_FIX} }, // pap.fKeepFollow
+ {NS_sprm::v6::sprmPPageBreakBefore, { 1, L_FIX} }, // pap.fPageBreakBefore
+ {NS_sprm::v6::sprmPBrcl, { 1, L_FIX} }, // pap.brcl
+ {NS_sprm::v6::sprmPBrcp, { 1, L_FIX} }, // pap.brcp
+ {NS_sprm::v6::sprmPAnld, { 0, L_VAR} }, // pap.anld (ANLD structure)
+ {NS_sprm::v6::sprmPNLvlAnm, { 1, L_FIX} }, // pap.nLvlAnm nn
+ {NS_sprm::v6::sprmPFNoLineNumb, { 1, L_FIX} }, // pap.fNoLnn
+ {NS_sprm::v6::sprmPChgTabsPapx, { 0, L_VAR} }, // pap.itbdMac, ...
+ {NS_sprm::v6::sprmPDxaRight, { 2, L_FIX} }, // pap.dxaRight
+ {NS_sprm::v6::sprmPDxaLeft, { 2, L_FIX} }, // pap.dxaLeft
+ {NS_sprm::v6::sprmPNest, { 2, L_FIX} }, // pap.dxaLeft
+ {NS_sprm::v6::sprmPDxaLeft1, { 2, L_FIX} }, // pap.dxaLeft1
+ {NS_sprm::v6::sprmPDyaLine, { 4, L_FIX} }, // pap.lspd an LSPD
+ {NS_sprm::v6::sprmPDyaBefore, { 2, L_FIX} }, // pap.dyaBefore
+ {NS_sprm::v6::sprmPDyaAfter, { 2, L_FIX} }, // pap.dyaAfter
+ {NS_sprm::v6::sprmPChgTabs, { 0, L_VAR} }, // pap.itbdMac, pap.rgdxaTab, ...
+ {NS_sprm::v6::sprmPFInTable, { 1, L_FIX} }, // pap.fInTable
+ {NS_sprm::v6::sprmPTtp, { 1, L_FIX} }, // pap.fTtp
+ {NS_sprm::v6::sprmPDxaAbs, { 2, L_FIX} }, // pap.dxaAbs
+ {NS_sprm::v6::sprmPDyaAbs, { 2, L_FIX} }, // pap.dyaAbs
+ {NS_sprm::v6::sprmPDxaWidth, { 2, L_FIX} }, // pap.dxaWidth
+ {NS_sprm::v6::sprmPPc, { 1, L_FIX} }, // pap.pcHorz, pap.pcVert
+ {NS_sprm::v6::sprmPBrcTop10, { 2, L_FIX} }, // pap.brcTop BRC10
+ {NS_sprm::v6::sprmPBrcLeft10, { 2, L_FIX} }, // pap.brcLeft BRC10
+ {NS_sprm::v6::sprmPBrcBottom10, { 2, L_FIX} }, // pap.brcBottom BRC10
+ {NS_sprm::v6::sprmPBrcRight10, { 2, L_FIX} }, // pap.brcRight BRC10
+ {NS_sprm::v6::sprmPBrcBetween10, { 2, L_FIX} }, // pap.brcBetween BRC10
+ {NS_sprm::v6::sprmPBrcBar10, { 2, L_FIX} }, // pap.brcBar BRC10
+ {NS_sprm::v6::sprmPFromText10, { 2, L_FIX} }, // pap.dxaFromText dxa
+ {NS_sprm::v6::sprmPWr, { 1, L_FIX} }, // pap.wr wr
+ {NS_sprm::v6::sprmPBrcTop, { 2, L_FIX} }, // pap.brcTop BRC
+ {NS_sprm::v6::sprmPBrcLeft, { 2, L_FIX} }, // pap.brcLeft BRC
+ {NS_sprm::v6::sprmPBrcBottom, { 2, L_FIX} }, // pap.brcBottom BRC
+ {NS_sprm::v6::sprmPBrcRight, { 2, L_FIX} }, // pap.brcRight BRC
+ {NS_sprm::v6::sprmPBrcBetween, { 2, L_FIX} }, // pap.brcBetween BRC
+ {NS_sprm::v6::sprmPBrcBar, { 2, L_FIX} }, // pap.brcBar BRC word
+ {NS_sprm::v6::sprmPFNoAutoHyph, { 1, L_FIX} }, // pap.fNoAutoHyph
+ {NS_sprm::v6::sprmPWHeightAbs, { 2, L_FIX} }, // pap.wHeightAbs w
+ {NS_sprm::v6::sprmPDcs, { 2, L_FIX} }, // pap.dcs DCS
+ {NS_sprm::v6::sprmPShd, { 2, L_FIX} }, // pap.shd SHD
+ {NS_sprm::v6::sprmPDyaFromText, { 2, L_FIX} }, // pap.dyaFromText dya
+ {NS_sprm::v6::sprmPDxaFromText, { 2, L_FIX} }, // pap.dxaFromText dxa
+ {NS_sprm::v6::sprmPFLocked, { 1, L_FIX} }, // pap.fLocked 0 or 1 byte
+ {NS_sprm::v6::sprmPFWidowControl, { 1, L_FIX} }, // pap.fWidowControl 0 or 1 byte
+ {NS_sprm::v6::sprmPRuler, { 0, L_FIX} },
+ { 64, { 0, L_VAR} }, // rtl property ?
+ {NS_sprm::v6::sprmCFStrikeRM, { 1, L_FIX} }, // chp.fRMarkDel 1 or 0 bit
+ {NS_sprm::v6::sprmCFRMark, { 1, L_FIX} }, // chp.fRMark 1 or 0 bit
+ {NS_sprm::v6::sprmCFFldVanish, { 1, L_FIX} }, // chp.fFieldVanish 1 or 0 bit
+ {NS_sprm::v6::sprmCPicLocation, { 0, L_VAR} }, // chp.fcPic and chp.fSpec
+ {NS_sprm::v6::sprmCIbstRMark, { 2, L_FIX} }, // chp.ibstRMark index into sttbRMark
+ {NS_sprm::v6::sprmCDttmRMark, { 4, L_FIX} }, // chp.dttm DTTM long
+ {NS_sprm::v6::sprmCFData, { 1, L_FIX} }, // chp.fData 1 or 0 bit
+ {NS_sprm::v6::sprmCRMReason, { 2, L_FIX} }, // chp.idslRMReason an index to a table
+ {NS_sprm::v6::sprmCChse, { 3, L_FIX} }, // chp.fChsDiff and chp.chse
+ {NS_sprm::v6::sprmCSymbol, { 0, L_VAR} }, // chp.fSpec, chp.chSym and chp.ftcSym
+ {NS_sprm::v6::sprmCFOle2, { 1, L_FIX} }, // chp.fOle2 1 or 0 bit
+ { 77, { 0, L_VAR} }, // unknown
+ { 79, { 0, L_VAR} }, // unknown
+ {NS_sprm::v6::sprmCIstd, { 2, L_FIX} }, // chp.istd istd, see stylesheet definition
+ {NS_sprm::v6::sprmCIstdPermute, { 0, L_VAR} }, // chp.istd permutation vector
+ {NS_sprm::v6::sprmCDefault, { 0, L_VAR} }, // whole CHP
+ {NS_sprm::v6::sprmCPlain, { 0, L_FIX} }, // whole CHP
+ {NS_sprm::v6::sprmCFBold, { 1, L_FIX} }, // chp.fBold 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFItalic, { 1, L_FIX} }, // chp.fItalic 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFStrike, { 1, L_FIX} }, // chp.fStrike 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFOutline, { 1, L_FIX} }, // chp.fOutline 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFShadow, { 1, L_FIX} }, // chp.fShadow 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFSmallCaps, { 1, L_FIX} }, // chp.fSmallCaps 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFCaps, { 1, L_FIX} }, // chp.fCaps 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFVanish, { 1, L_FIX} }, // chp.fVanish 0,1, 128, or 129
+ {NS_sprm::v6::sprmCFtc, { 2, L_FIX} }, // chp.ftc ftc word
+ {NS_sprm::v6::sprmCKul, { 1, L_FIX} }, // chp.kul kul byte
+ {NS_sprm::v6::sprmCSizePos, { 3, L_FIX} }, // chp.hps, chp.hpsPos
+ {NS_sprm::v6::sprmCDxaSpace, { 2, L_FIX} }, // chp.dxaSpace dxa
+ {NS_sprm::v6::sprmCLid, { 2, L_FIX} }, // chp.lid LID
+ {NS_sprm::v6::sprmCIco, { 1, L_FIX} }, // chp.ico ico byte
+ {NS_sprm::v6::sprmCHps, { 2, L_FIX} }, // chp.hps hps !word!
+ {NS_sprm::v6::sprmCHpsInc, { 1, L_FIX} }, // chp.hps
+ {NS_sprm::v6::sprmCHpsPos, { 2, L_FIX} }, // chp.hpsPos hps !word!
+ {NS_sprm::v6::sprmCHpsPosAdj, { 1, L_FIX} }, // chp.hpsPos hps
+ {NS_sprm::v6::sprmCMajority, { 0, L_VAR} }, // chp.fBold, chp.fItalic, ...
+ {NS_sprm::v6::sprmCIss, { 1, L_FIX} }, // chp.iss iss
+ {NS_sprm::v6::sprmCHpsNew50, { 0, L_VAR} }, // chp.hps hps variable width
+ {NS_sprm::v6::sprmCHpsInc1, { 0, L_VAR} }, // chp.hps complex
+ {NS_sprm::v6::sprmCHpsKern, { 2, L_FIX} }, // chp.hpsKern hps
+ {NS_sprm::v6::sprmCMajority50, { 0, L_VAR} }, // chp.fBold, chp.fItalic, ...
+ {NS_sprm::v6::sprmCHpsMul, { 2, L_FIX} }, // chp.hps percentage to grow hps
+ {NS_sprm::v6::sprmCCondHyhen, { 2, L_FIX} }, // chp.ysri ysri
+ {111, { 0, L_VAR} }, // sprmCFBoldBi or font code
+ {112, { 0, L_VAR} }, // sprmCFItalicBi or font code
+ {113, { 0, L_VAR} }, // ww7 rtl font
+ {114, { 0, L_VAR} }, // ww7 lid
+ {115, { 0, L_VAR} }, // ww7 CJK font
+ {116, { 0, L_VAR} }, // ww7 fontsize
+ {NS_sprm::v6::sprmCFSpec, { 1, L_FIX} }, // chp.fSpec 1 or 0 bit
+ {NS_sprm::v6::sprmCFObj, { 1, L_FIX} }, // chp.fObj 1 or 0 bit
+ {NS_sprm::v6::sprmPicBrcl, { 1, L_FIX} }, // pic.brcl brcl (see PIC definition)
+ {NS_sprm::v6::sprmPicScale, {12, L_VAR} }, // pic.mx, pic.my, pic.dxaCropleft,
+ {NS_sprm::v6::sprmPicBrcTop, { 2, L_FIX} }, // pic.brcTop BRC word
+ {NS_sprm::v6::sprmPicBrcLeft, { 2, L_FIX} }, // pic.brcLeft BRC word
+ {NS_sprm::v6::sprmPicBrcBottom, { 2, L_FIX} }, // pic.brcBottom BRC word
+ {NS_sprm::v6::sprmPicBrcRight, { 2, L_FIX} }, // pic.brcRight BRC word
+ {NS_sprm::v6::sprmSScnsPgn, { 1, L_FIX} }, // sep.cnsPgn cns byte
+ {NS_sprm::v6::sprmSiHeadingPgn, { 1, L_FIX} }, // sep.iHeadingPgn
+ {NS_sprm::v6::sprmSOlstAnm, { 0, L_VAR} }, // sep.olstAnm OLST variable length
+ {NS_sprm::v6::sprmSDxaColWidth, { 3, L_FIX} }, // sep.rgdxaColWidthSpacing complex
+ {NS_sprm::v6::sprmSDxaColSpacing, { 3, L_FIX} }, // sep.rgdxaColWidthSpacing
+ {NS_sprm::v6::sprmSFEvenlySpaced, { 1, L_FIX} }, // sep.fEvenlySpaced 1 or 0
+ {NS_sprm::v6::sprmSFProtected, { 1, L_FIX} }, // sep.fUnlocked 1 or 0 byte
+ {NS_sprm::v6::sprmSDmBinFirst, { 2, L_FIX} }, // sep.dmBinFirst word
+ {NS_sprm::v6::sprmSDmBinOther, { 2, L_FIX} }, // sep.dmBinOther word
+ {NS_sprm::v6::sprmSBkc, { 1, L_FIX} }, // sep.bkc bkc byte
+ {NS_sprm::v6::sprmSFTitlePage, { 1, L_FIX} }, // sep.fTitlePage 0 or 1 byte
+ {NS_sprm::v6::sprmSCcolumns, { 2, L_FIX} }, // sep.ccolM1 # of cols - 1 word
+ {NS_sprm::v6::sprmSDxaColumns, { 2, L_FIX} }, // sep.dxaColumns dxa word
+ {NS_sprm::v6::sprmSFAutoPgn, { 1, L_FIX} }, // sep.fAutoPgn obsolete byte
+ {NS_sprm::v6::sprmSNfcPgn, { 1, L_FIX} }, // sep.nfcPgn nfc byte
+ {NS_sprm::v6::sprmSDyaPgn, { 2, L_FIX} }, // sep.dyaPgn dya short
+ {NS_sprm::v6::sprmSDxaPgn, { 2, L_FIX} }, // sep.dxaPgn dya short
+ {NS_sprm::v6::sprmSFPgnRestart, { 1, L_FIX} }, // sep.fPgnRestart 0 or 1 byte
+ {NS_sprm::v6::sprmSFEndnote, { 1, L_FIX} }, // sep.fEndnote 0 or 1 byte
+ {NS_sprm::v6::sprmSLnc, { 1, L_FIX} }, // sep.lnc lnc byte
+ {NS_sprm::v6::sprmSGprfIhdt, { 1, L_FIX} }, // sep.grpfIhdt grpfihdt
+ {NS_sprm::v6::sprmSNLnnMod, { 2, L_FIX} }, // sep.nLnnMod non-neg int. word
+ {NS_sprm::v6::sprmSDxaLnn, { 2, L_FIX} }, // sep.dxaLnn dxa word
+ {NS_sprm::v6::sprmSDyaHdrTop, { 2, L_FIX} }, // sep.dyaHdrTop dya word
+ {NS_sprm::v6::sprmSDyaHdrBottom, { 2, L_FIX} }, // sep.dyaHdrBottom dya word
+ {NS_sprm::v6::sprmSLBetween, { 1, L_FIX} }, // sep.fLBetween 0 or 1 byte
+ {NS_sprm::v6::sprmSVjc, { 1, L_FIX} }, // sep.vjc vjc byte
+ {NS_sprm::v6::sprmSLnnMin, { 2, L_FIX} }, // sep.lnnMin lnn word
+ {NS_sprm::v6::sprmSPgnStart, { 2, L_FIX} }, // sep.pgnStart pgn word
+ {NS_sprm::v6::sprmSBOrientation, { 1, L_FIX} }, // sep.dmOrientPage dm byte
+ {NS_sprm::v6::sprmSBCustomize, { 0, L_FIX} },
+ {NS_sprm::v6::sprmSXaPage, { 2, L_FIX} }, // sep.xaPage xa word
+ {NS_sprm::v6::sprmSYaPage, { 2, L_FIX} }, // sep.yaPage ya word
+ {NS_sprm::v6::sprmSDxaLeft, { 2, L_FIX} }, // sep.dxaLeft dxa word
+ {NS_sprm::v6::sprmSDxaRight, { 2, L_FIX} }, // sep.dxaRight dxa word
+ {NS_sprm::v6::sprmSDyaTop, { 2, L_FIX} }, // sep.dyaTop dya word
+ {NS_sprm::v6::sprmSDyaBottom, { 2, L_FIX} }, // sep.dyaBottom dya word
+ {NS_sprm::v6::sprmSDzaGutter, { 2, L_FIX} }, // sep.dzaGutter dza word
+ {NS_sprm::v6::sprmSDMPaperReq, { 2, L_FIX} }, // sep.dmPaperReq dm word
+ {179, { 0, L_VAR} }, // rtl property ?
+ {181, { 0, L_VAR} }, // rtl property ?
+ {NS_sprm::v6::sprmTJc, { 2, L_FIX} }, // tap.jc jc (low order byte is significant)
+ {NS_sprm::v6::sprmTDxaLeft, { 2, L_FIX} }, // tap.rgdxaCenter dxa word
+ {NS_sprm::v6::sprmTDxaGapHalf, { 2, L_FIX} }, // tap.dxaGapHalf, tap.rgdxaCenter
+ {NS_sprm::v6::sprmTFCantSplit, { 1, L_FIX} }, // tap.fCantSplit 1 or 0 byte
+ {NS_sprm::v6::sprmTTableHeader, { 1, L_FIX} }, // tap.fTableHeader 1 or 0 byte
+ {NS_sprm::v6::sprmTTableBorders, {12, L_FIX} }, // tap.rgbrcTable complex 12 bytes
+ {NS_sprm::v6::sprmTDefTable10, { 0, L_VAR2} }, // tap.rgdxaCenter, tap.rgtc complex
+ {NS_sprm::v6::sprmTDyaRowHeight, { 2, L_FIX} }, // tap.dyaRowHeight dya word
+ {NS_sprm::v6::sprmTDefTable, { 0, L_VAR2} }, // tap.rgtc complex
+ {NS_sprm::v6::sprmTDefTableShd, { 1, L_VAR} }, // tap.rgshd complex
+ {NS_sprm::v6::sprmTTlp, { 4, L_FIX} }, // tap.tlp TLP 4 bytes
+ {NS_sprm::v6::sprmTSetBrc, { 5, L_FIX} }, // tap.rgtc[].rgbrc complex 5 bytes
+ {NS_sprm::v6::sprmTInsert, { 4, L_FIX} }, // tap.rgdxaCenter,tap.rgtc complex
+ {NS_sprm::v6::sprmTDelete, { 2, L_FIX} }, // tap.rgdxaCenter, tap.rgtc complex
+ {NS_sprm::v6::sprmTDxaCol, { 4, L_FIX} }, // tap.rgdxaCenter complex
+ {NS_sprm::v6::sprmTMerge, { 2, L_FIX} }, // tap.fFirstMerged, tap.fMerged complex
+ {NS_sprm::v6::sprmTSplit, { 2, L_FIX} }, // tap.fFirstMerged, tap.fMerged complex
+ {NS_sprm::v6::sprmTSetBrc10, { 5, L_FIX} }, // tap.rgtc[].rgbrc complex 5 bytes
+ {NS_sprm::v6::sprmTSetShd, { 4, L_FIX} }, // tap.rgshd complex 4 bytes
+ {207, { 0, L_VAR} } // rtl property ?
+ };
+
+ if (rFib.m_wIdent >= 0xa697 && rFib.m_wIdent <= 0xa699)
+ {
+ //see Read_AmbiguousSPRM for this oddity
+ static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms), true);
+ return &aSprmSrch;
+ }
+
+ static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms));
+ return &aSprmSrch;
+};
+
+void wwSprmSearcher::patchCJKVariant()
+{
+ for (sal_uInt16 nId = 111; nId <= 113; ++nId)
+ {
+ SprmInfo& amb1 = map_[nId];
+ amb1.nLen = 2;
+ amb1.nVari = wwSprmParser::L_FIX;
+ }
+}
+
+template <class Sprm> static constexpr SprmInfoRow InfoRow()
+{
+ return { Sprm::val, { Sprm::len, Sprm::varlen ? wwSprmParser::L_VAR : wwSprmParser::L_FIX } };
+}
+
+const wwSprmSearcher *wwSprmParser::GetWW8SprmSearcher()
+{
+ //double lock me
+ //WW8+ Sprms
+ static const SprmInfoRow aSprms[] =
+ {
+ { 0, { 0, L_FIX} }, // "Default-sprm"/ is skipped
+ InfoRow<NS_sprm::PIstd>(), // pap.istd;istd (style code);short;
+ InfoRow<NS_sprm::PIstdPermute>(), // pap.istd;permutation vector
+ InfoRow<NS_sprm::PIncLvl>(), // pap.istd, pap.lvl;difference
+ // between istd of base PAP and istd of PAP to be
+ // produced
+ InfoRow<NS_sprm::PJc80>(), // pap.jc;jc (justification);byte;
+ {NS_sprm::LN_PFSideBySide, { 1, L_FIX} }, // "sprmPFSideBySide" pap.fSideBySide;0 or 1;byte;
+ InfoRow<NS_sprm::PFKeep>(), // pap.fKeep;0 or 1;byte;
+ InfoRow<NS_sprm::PFKeepFollow>(), // pap.fKeepFollow;0 or 1;byte;
+ InfoRow<NS_sprm::PFPageBreakBefore>(), // pap.fPageBreakBefore;
+ // 0 or 1
+ {NS_sprm::LN_PBrcl, { 1, L_FIX} }, // "sprmPBrcl" pap.brcl;brcl;byte;
+ {NS_sprm::LN_PBrcp, { 1, L_FIX} }, // "sprmPBrcp" pap.brcp;brcp;byte;
+ InfoRow<NS_sprm::PIlvl>(), // pap.ilvl;ilvl;byte;
+ InfoRow<NS_sprm::PIlfo>(), // pap.ilfo;ilfo (list index) ;short;
+ InfoRow<NS_sprm::PFNoLineNumb>(), // pap.fNoLnn;0 or 1;byte;
+ InfoRow<NS_sprm::PChgTabsPapx>(), // pap.itbdMac, pap.rgdxaTab,
+ // pap.rgtbd;complex
+ InfoRow<NS_sprm::PDxaRight80>(), // pap.dxaRight;dxa;word;
+ InfoRow<NS_sprm::PDxaLeft80>(), // pap.dxaLeft;dxa;word;
+ InfoRow<NS_sprm::PNest80>(), // pap.dxaLeft;dxa
+ InfoRow<NS_sprm::PDxaLeft180>(), // pap.dxaLeft1;dxa;word;
+ InfoRow<NS_sprm::PDyaLine>(), // pap.lspd;an LSPD, a long word
+ // structure consisting of a short of dyaLine
+ // followed by a short of fMultLinespace
+ InfoRow<NS_sprm::PDyaBefore>(), // pap.dyaBefore;dya;word;
+ InfoRow<NS_sprm::PDyaAfter>(), // pap.dyaAfter;dya;word;
+ InfoRow<NS_sprm::PChgTabs>(), // pap.itbdMac, pap.rgdxaTab,
+ // pap.rgtbd;complex
+ InfoRow<NS_sprm::PFInTable>(), // pap.fInTable;0 or 1;byte;
+ InfoRow<NS_sprm::PFTtp>(), // pap.fTtp;0 or 1;byte;
+ InfoRow<NS_sprm::PDxaAbs>(), // pap.dxaAbs;dxa;word;
+ InfoRow<NS_sprm::PDyaAbs>(), // pap.dyaAbs;dya;word;
+ InfoRow<NS_sprm::PDxaWidth>(), // pap.dxaWidth;dxa;word;
+ InfoRow<NS_sprm::PPc>(), // pap.pcHorz, pap.pcVert;complex
+ {NS_sprm::LN_PBrcTop10, { 2, L_FIX} }, // "sprmPBrcTop10" pap.brcTop;BRC10;word;
+ {NS_sprm::LN_PBrcLeft10, { 2, L_FIX} }, // "sprmPBrcLeft10" pap.brcLeft;BRC10;word;
+ {NS_sprm::LN_PBrcBottom10, { 2, L_FIX} }, // "sprmPBrcBottom10" pap.brcBottom;BRC10;word;
+ {NS_sprm::LN_PBrcRight10, { 2, L_FIX} }, // "sprmPBrcRight10" pap.brcRight;BRC10;word;
+ {NS_sprm::LN_PBrcBetween10, { 2, L_FIX} }, // "sprmPBrcBetween10" pap.brcBetween;BRC10;word;
+ {NS_sprm::LN_PBrcBar10, { 2, L_FIX} }, // "sprmPBrcBar10" pap.brcBar;BRC10;word;
+ {NS_sprm::LN_PDxaFromText10, { 2, L_FIX} }, // "sprmPDxaFromText10" pap.dxaFromText;dxa;word;
+ InfoRow<NS_sprm::PWr>(), // pap.wr;wr
+ InfoRow<NS_sprm::PBrcTop80>(), // pap.brcTop;BRC;long;
+ InfoRow<NS_sprm::PBrcLeft80>(), // pap.brcLeft;BRC;long;
+ InfoRow<NS_sprm::PBrcBottom80>(), // pap.brcBottom;BRC;long;
+ InfoRow<NS_sprm::PBrcRight80>(), // pap.brcRight;BRC;long;
+ InfoRow<NS_sprm::PBrcBetween80>(), // pap.brcBetween;BRC;long;
+ InfoRow<NS_sprm::PBrcBar80>(), // pap.brcBar;BRC;long;
+ InfoRow<NS_sprm::PFNoAutoHyph>(), // pap.fNoAutoHyph;0 or 1;byte;
+ InfoRow<NS_sprm::PWHeightAbs>(), // pap.wHeightAbs;w;word;
+ InfoRow<NS_sprm::PDcs>(), // pap.dcs;DCS;short;
+ InfoRow<NS_sprm::PShd80>(), // pap.shd;SHD;word;
+ InfoRow<NS_sprm::PDyaFromText>(), // pap.dyaFromText;dya;word;
+ InfoRow<NS_sprm::PDxaFromText>(), // pap.dxaFromText;dxa;word;
+ InfoRow<NS_sprm::PFLocked>(), // pap.fLocked;0 or 1;byte;
+ InfoRow<NS_sprm::PFWidowControl>(), // pap.fWidowControl;0 or 1
+ {NS_sprm::LN_PRuler, { 0, L_VAR} }, // "sprmPRuler" ;;variable length;
+ InfoRow<NS_sprm::PFKinsoku>(), // pap.fKinsoku;0 or 1;byte;
+ InfoRow<NS_sprm::PFWordWrap>(), // pap.fWordWrap;0 or 1;byte;
+ InfoRow<NS_sprm::PFOverflowPunct>(), // pap.fOverflowPunct;0 or 1
+ InfoRow<NS_sprm::PFTopLinePunct>(), // pap.fTopLinePunct;0 or 1
+ InfoRow<NS_sprm::PFAutoSpaceDE>(), // pap.fAutoSpaceDE;0 or 1
+ InfoRow<NS_sprm::PFAutoSpaceDN>(), // pap.fAutoSpaceDN;0 or 1
+ InfoRow<NS_sprm::PWAlignFont>(), // pap.wAlignFont;iFa
+ InfoRow<NS_sprm::PFrameTextFlow>(), // pap.fVertical pap.fBackward
+ // pap.fRotateFont;complex
+ {NS_sprm::LN_PISnapBaseLine, { 1, L_FIX} }, // "sprmPISnapBaseLine" obsolete: not applicable in
+ // Word97 and later versions;
+ {NS_sprm::LN_PAnld, { 0, L_VAR} }, // "sprmPAnld" pap.anld;;variable length;
+ {NS_sprm::LN_PPropRMark, { 0, L_VAR} }, // "sprmPPropRMark" pap.fPropRMark;complex
+ InfoRow<NS_sprm::POutLvl>(), // pap.lvl;has no effect if pap.istd
+ // is < 1 or is > 9
+ InfoRow<NS_sprm::PFBiDi>(), // ;;byte;
+ InfoRow<NS_sprm::PFNumRMIns>(), // pap.fNumRMIns;1 or 0;bit;
+ {NS_sprm::LN_PCrLf, { 1, L_FIX} }, // "sprmPCrLf" ;;byte;
+ InfoRow<NS_sprm::PNumRM>(), // pap.numrm;;variable length;
+ {NS_sprm::LN_PHugePapx, { 4, L_FIX} }, // "sprmPHugePapx" fc in the data stream to locate
+ // the huge grpprl
+ InfoRow<NS_sprm::PHugePapx>(), // fc in the data stream to locate
+ // the huge grpprl
+ InfoRow<NS_sprm::PFUsePgsuSettings>(), // pap.fUsePgsuSettings;
+ // 1 or 0
+ InfoRow<NS_sprm::PFAdjustRight>(), // pap.fAdjustRight;1 or 0;byte;
+ InfoRow<NS_sprm::CFRMarkDel>(), // chp.fRMarkDel;1 or 0;bit;
+ InfoRow<NS_sprm::CFRMarkIns>(), // chp.fRMark;1 or 0;bit;
+ InfoRow<NS_sprm::CFFldVanish>(), // chp.fFieldVanish;1 or 0;bit;
+ InfoRow<NS_sprm::CPicLocation>(), // chp.fcPic and chp.fSpec;
+ InfoRow<NS_sprm::CIbstRMark>(), // chp.ibstRMark;index into
+ // sttbRMark
+ InfoRow<NS_sprm::CDttmRMark>(), // chp.dttmRMark;DTTM;long;
+ InfoRow<NS_sprm::CFData>(), // chp.fData;1 or 0;bit;
+ InfoRow<NS_sprm::CIdslRMark>(), // chp.idslRMReason;an index to a
+ // table of strings defined in Word 6.0
+ // executables;short;
+ {NS_sprm::LN_CChs, { 1, L_FIX} }, // "sprmCChs" chp.fChsDiff and chp.chse;
+ InfoRow<NS_sprm::CSymbol>(), // chp.fSpec, chp.xchSym and
+ // chp.ftcSym
+ InfoRow<NS_sprm::CFOle2>(), // chp.fOle2;1 or 0;bit;
+ {NS_sprm::LN_CIdCharType, { 0, L_FIX} }, // "sprmCIdCharType" obsolete: not applicable in
+ // Word97 and later versions;
+ InfoRow<NS_sprm::CHighlight>(), // chp.fHighlight,
+ // chp.icoHighlight;ico (fHighlight is set to 1 iff
+ // ico is not 0)
+ {NS_sprm::LN_CObjLocation, { 4, L_FIX} }, // "sprmCObjLocation" chp.fcObj;FC;long;
+ {NS_sprm::LN_CFFtcAsciSymb, { 0, L_FIX} }, // "sprmCFFtcAsciSymb" ;;;
+ InfoRow<NS_sprm::CIstd>(), // chp.istd;istd, see stylesheet def
+ InfoRow<NS_sprm::CIstdPermute>(), // chp.istd;permutation vector
+ {NS_sprm::LN_CDefault, { 0, L_VAR} }, // "sprmCDefault" whole CHP;none;variable length;
+ InfoRow<NS_sprm::CPlain>(), // whole CHP;none;0;
+ InfoRow<NS_sprm::CKcd>(), // ;;;
+ InfoRow<NS_sprm::CFBold>(), // chp.fBold;0,1, 128, or 129
+ InfoRow<NS_sprm::CFItalic>(), // chp.fItalic;0,1, 128, or 129
+ InfoRow<NS_sprm::CFStrike>(), // chp.fStrike;0,1, 128, or 129
+ InfoRow<NS_sprm::CFOutline>(), // chp.fOutline;0,1, 128, or 129
+ InfoRow<NS_sprm::CFShadow>(), // chp.fShadow;0,1, 128, or 129
+ InfoRow<NS_sprm::CFSmallCaps>(), // chp.fSmallCaps;0,1, 128, or 129
+ InfoRow<NS_sprm::CFCaps>(), // chp.fCaps;0,1, 128, or 129
+ InfoRow<NS_sprm::CFVanish>(), // chp.fVanish;0,1, 128, or 129
+ {NS_sprm::LN_CFtcDefault, { 2, L_FIX} }, // "sprmCFtcDefault" ;ftc, only used internally
+ InfoRow<NS_sprm::CKul>(), // chp.kul;kul;byte;
+ {NS_sprm::LN_CSizePos, { 3, L_FIX} }, // "sprmCSizePos" chp.hps, chp.hpsPos;3 bytes;
+ InfoRow<NS_sprm::CDxaSpace>(), // chp.dxaSpace;dxa;word;
+ {NS_sprm::LN_CLid, { 2, L_FIX} }, // "sprmCLid" ;only used internally never stored
+ InfoRow<NS_sprm::CIco>(), // chp.ico;ico;byte;
+ InfoRow<NS_sprm::CHps>(), // chp.hps;hps
+ {NS_sprm::LN_CHpsInc, { 1, L_FIX} }, // "sprmCHpsInc" chp.hps;
+ InfoRow<NS_sprm::CHpsPos>(), // chp.hpsPos;hps;short; (doc wrong)
+ {NS_sprm::LN_CHpsPosAdj, { 1, L_FIX} }, // "sprmCHpsPosAdj" chp.hpsPos;hps
+ InfoRow<NS_sprm::CMajority>(), // chp.fBold, chp.fItalic,
+ // chp.fSmallCaps, chp.fVanish, chp.fStrike,
+ // chp.fCaps, chp.rgftc, chp.hps, chp.hpsPos,
+ // chp.kul, chp.dxaSpace, chp.ico,
+ // chp.rglid;complex;variable length, length byte
+ // plus size of following grpprl;
+ InfoRow<NS_sprm::CIss>(), // chp.iss;iss;byte;
+ {NS_sprm::LN_CHpsNew50, { 0, L_VAR} }, // "sprmCHpsNew50" chp.hps;hps;variable width
+ {NS_sprm::LN_CHpsInc1, { 0, L_VAR} }, // "sprmCHpsInc1" chp.hps;complex
+ InfoRow<NS_sprm::CHpsKern>(), // chp.hpsKern;hps;short;
+ {NS_sprm::LN_CMajority50, { 2, L_FIX} }, // "sprmCMajority50" chp.fBold, chp.fItalic,
+ // chp.fSmallCaps, chp.fVanish, chp.fStrike,
+ // chp.fCaps, chp.ftc, chp.hps, chp.hpsPos, chp.kul,
+ // chp.dxaSpace, chp.ico,;complex
+ {NS_sprm::LN_CHpsMul, { 2, L_FIX} }, // "sprmCHpsMul" chp.hps;percentage to grow hps
+ InfoRow<NS_sprm::CHresi>(), // chp.ysri;ysri;short;
+ InfoRow<NS_sprm::CRgFtc0>(), // chp.rgftc[0];ftc for ASCII text
+ InfoRow<NS_sprm::CRgFtc1>(), // chp.rgftc[1];ftc for Far East text
+ InfoRow<NS_sprm::CRgFtc2>(), // chp.rgftc[2];ftc for non-FE text
+ InfoRow<NS_sprm::CCharScale>(),
+ InfoRow<NS_sprm::CFDStrike>(), // chp.fDStrike;;byte;
+ InfoRow<NS_sprm::CFImprint>(), // chp.fImprint;1 or 0;bit;
+ InfoRow<NS_sprm::CFSpec>(), // chp.fSpec ;1 or 0;bit;
+ InfoRow<NS_sprm::CFObj>(), // chp.fObj;1 or 0;bit;
+ InfoRow<NS_sprm::CPropRMark90>(), // chp.fPropRMark,
+ // chp.ibstPropRMark, chp.dttmPropRMark;Complex
+ InfoRow<NS_sprm::CFEmboss>(), // chp.fEmboss;1 or 0;bit;
+ InfoRow<NS_sprm::CSfxText>(), // chp.sfxtText;text animation;byte;
+ InfoRow<NS_sprm::CFBiDi>(), // ;;;
+ {NS_sprm::LN_CFDiacColor, { 1, L_FIX} }, // "sprmCFDiacColor" ;;;
+ InfoRow<NS_sprm::CFBoldBi>(), // ;;;
+ InfoRow<NS_sprm::CFItalicBi>(), // ;;;
+ InfoRow<NS_sprm::CFtcBi>(),
+ InfoRow<NS_sprm::CLidBi>(), // ;;;
+ InfoRow<NS_sprm::CIcoBi>(), // ;;;
+ InfoRow<NS_sprm::CHpsBi>(), // ;;;
+ InfoRow<NS_sprm::CDispFldRMark>(), // chp.fDispFieldRMark,
+ // chp.ibstDispFieldRMark, chp.dttmDispFieldRMark ;
+ InfoRow<NS_sprm::CIbstRMarkDel>(), // chp.ibstRMarkDel;index into
+ // sttbRMark;short;
+ InfoRow<NS_sprm::CDttmRMarkDel>(), // chp.dttmRMarkDel;DTTM;long;
+ InfoRow<NS_sprm::CBrc80>(), // chp.brc;BRC;long;
+ InfoRow<NS_sprm::CShd80>(), // chp.shd;SHD;short;
+ InfoRow<NS_sprm::CIdslRMarkDel>(), // chp.idslRMReasonDel;an index
+ // to a table of strings defined in Word 6.0
+ // executables;short;
+ InfoRow<NS_sprm::CFUsePgsuSettings>(),
+ // chp.fUsePgsuSettings;1 or 0
+ {NS_sprm::LN_CCpg, { 2, L_FIX} }, // "sprmCCpg" ;;word;
+ InfoRow<NS_sprm::CRgLid0_80>(), // chp.rglid[0];LID: for non-FE text
+ InfoRow<NS_sprm::CRgLid1_80>(), // chp.rglid[1];LID: for Far East text
+ InfoRow<NS_sprm::CIdctHint>(), // chp.idctHint;IDCT:
+ {NS_sprm::LN_PicBrcl, { 1, L_FIX} }, // "sprmPicBrcl" pic.brcl;brcl (see PIC definition)
+ {NS_sprm::LN_PicScale, { 0, L_VAR} }, // "sprmPicScale" pic.mx, pic.my, pic.dxaCropleft,
+ // pic.dyaCropTop pic.dxaCropRight,
+ // pic.dyaCropBottom;Complex
+ InfoRow<NS_sprm::PicBrcTop80>(), // pic.brcTop;BRC;long;
+ InfoRow<NS_sprm::PicBrcLeft80>(), // pic.brcLeft;BRC;long;
+ InfoRow<NS_sprm::PicBrcBottom80>(), // pic.brcBottom;BRC;long;
+ InfoRow<NS_sprm::PicBrcRight80>(), // pic.brcRight;BRC;long;
+ InfoRow<NS_sprm::ScnsPgn>(), // sep.cnsPgn;cns;byte;
+ InfoRow<NS_sprm::SiHeadingPgn>(), // sep.iHeadingPgn;heading number
+ // level;byte;
+ {NS_sprm::LN_SOlstAnm, { 0, L_VAR} }, // "sprmSOlstAnm" sep.olstAnm;OLST;variable length;
+ InfoRow<NS_sprm::SDxaColWidth>(), // sep.rgdxaColWidthSpacing;
+ InfoRow<NS_sprm::SDxaColSpacing>(), // sep.rgdxaColWidthSpacing;
+ // complex
+ InfoRow<NS_sprm::SFEvenlySpaced>(), // sep.fEvenlySpaced;1 or 0
+ InfoRow<NS_sprm::SFProtected>(), // sep.fUnlocked;1 or 0;byte;
+ InfoRow<NS_sprm::SDmBinFirst>(), // sep.dmBinFirst;;word;
+ InfoRow<NS_sprm::SDmBinOther>(), // sep.dmBinOther;;word;
+ InfoRow<NS_sprm::SBkc>(), // sep.bkc;bkc;byte;
+ InfoRow<NS_sprm::SFTitlePage>(), // sep.fTitlePage;0 or 1;byte;
+ InfoRow<NS_sprm::SCcolumns>(), // sep.ccolM1;# of cols - 1;word;
+ InfoRow<NS_sprm::SDxaColumns>(), // sep.dxaColumns;dxa;word;
+ {NS_sprm::LN_SFAutoPgn, { 1, L_FIX} }, // "sprmSFAutoPgn" sep.fAutoPgn;obsolete;byte;
+ InfoRow<NS_sprm::SNfcPgn>(), // sep.nfcPgn;nfc;byte;
+ {NS_sprm::LN_SDyaPgn, { 2, L_FIX} }, // "sprmSDyaPgn" sep.dyaPgn;dya;short;
+ {NS_sprm::LN_SDxaPgn, { 2, L_FIX} }, // "sprmSDxaPgn" sep.dxaPgn;dya;short;
+ InfoRow<NS_sprm::SFPgnRestart>(), // sep.fPgnRestart;0 or 1;byte;
+ InfoRow<NS_sprm::SFEndnote>(), // sep.fEndnote;0 or 1;byte;
+ InfoRow<NS_sprm::SLnc>(), // sep.lnc;lnc;byte;
+ {NS_sprm::LN_SGprfIhdt, { 1, L_FIX} }, // "sprmSGprfIhdt" sep.grpfIhdt;grpfihdt
+ InfoRow<NS_sprm::SNLnnMod>(), // sep.nLnnMod;non-neg int.;word;
+ InfoRow<NS_sprm::SDxaLnn>(), // sep.dxaLnn;dxa;word;
+ InfoRow<NS_sprm::SDyaHdrTop>(), // sep.dyaHdrTop;dya;word;
+ InfoRow<NS_sprm::SDyaHdrBottom>(), // sep.dyaHdrBottom;dya;word;
+ InfoRow<NS_sprm::SLBetween>(), // sep.fLBetween;0 or 1;byte;
+ InfoRow<NS_sprm::SVjc>(), // sep.vjc;vjc;byte;
+ InfoRow<NS_sprm::SLnnMin>(), // sep.lnnMin;lnn;word;
+ InfoRow<NS_sprm::SPgnStart97>(), // sep.pgnStart;pgn;word;
+ InfoRow<NS_sprm::SBOrientation>(), // sep.dmOrientPage;dm;byte;
+ {NS_sprm::LN_SBCustomize, { 1, L_FIX} }, // "sprmSBCustomize" ;;;
+ InfoRow<NS_sprm::SXaPage>(), // sep.xaPage;xa;word;
+ InfoRow<NS_sprm::SYaPage>(), // sep.yaPage;ya;word;
+ InfoRow<NS_sprm::SDxaLeft>(), // sep.dxaLeft;dxa;word;
+ InfoRow<NS_sprm::SDxaRight>(), // sep.dxaRight;dxa;word;
+ InfoRow<NS_sprm::SDyaTop>(), // sep.dyaTop;dya;word;
+ InfoRow<NS_sprm::SDyaBottom>(), // sep.dyaBottom;dya;word;
+ InfoRow<NS_sprm::SDzaGutter>(), // sep.dzaGutter;dza;word;
+ InfoRow<NS_sprm::SDmPaperReq>(), // sep.dmPaperReq;dm;word;
+ {NS_sprm::LN_SPropRMark, { 0, L_VAR} }, // "sprmSPropRMark" sep.fPropRMark,
+ // sep.ibstPropRMark, sep.dttmPropRMark ;complex
+ InfoRow<NS_sprm::SFBiDi>(), // ;;;
+ {NS_sprm::LN_SFFacingCol, { 1, L_FIX} }, // "sprmSFFacingCol" ;;;
+ InfoRow<NS_sprm::SFRTLGutter>(), //, set to one if gutter is on
+ // right
+ InfoRow<NS_sprm::SBrcTop80>(), // sep.brcTop;BRC;long;
+ InfoRow<NS_sprm::SBrcLeft80>(), // sep.brcLeft;BRC;long;
+ InfoRow<NS_sprm::SBrcBottom80>(), // sep.brcBottom;BRC;long;
+ InfoRow<NS_sprm::SBrcRight80>(), // sep.brcRight;BRC;long;
+ InfoRow<NS_sprm::SPgbProp>(), // sep.pgbProp;;word;
+ InfoRow<NS_sprm::SDxtCharSpace>(), // sep.dxtCharSpace;dxt;long;
+ InfoRow<NS_sprm::SDyaLinePitch>(),
+ // sep.dyaLinePitch;dya; WRONG:long; RIGHT:short; !
+ InfoRow<NS_sprm::SClm>(), // ;;;
+ InfoRow<NS_sprm::STextFlow>(), // sep.wTextFlow;complex
+ InfoRow<NS_sprm::TJc90>(), // tap.jc;jc;word (low order byte is
+ // significant);
+ InfoRow<NS_sprm::TDxaLeft>(), // tap.rgdxaCenter
+ InfoRow<NS_sprm::TDxaGapHalf>(), // tap.dxaGapHalf,
+ // tap.rgdxaCenter
+ InfoRow<NS_sprm::TFCantSplit90>(), // tap.fCantSplit90;1 or 0;byte;
+ InfoRow<NS_sprm::TTableHeader>(), // tap.fTableHeader;1 or 0;byte;
+ InfoRow<NS_sprm::TFCantSplit>(), // tap.fCantSplit;1 or 0;byte;
+ InfoRow<NS_sprm::TTableBorders80>(), // tap.rgbrcTable;complex
+ {NS_sprm::LN_TDefTable10, { 0, L_VAR2} }, // "sprmTDefTable10" tap.rgdxaCenter,
+ // tap.rgtc;complex
+ InfoRow<NS_sprm::TDyaRowHeight>(), // tap.dyaRowHeight;dya;word;
+ {NS_sprm::LN_TDefTable, { 0, L_VAR2} }, // "sprmTDefTable" tap.rgtc;complex
+ InfoRow<NS_sprm::TDefTableShd80>(), // tap.rgshd;complex
+ InfoRow<NS_sprm::TTlp>(), // tap.tlp;TLP;4 bytes;
+ InfoRow<NS_sprm::TFBiDi>(), // ;;;
+ {NS_sprm::LN_THTMLProps, { 1, L_FIX} }, // "sprmTHTMLProps" ;;;
+ InfoRow<NS_sprm::TSetBrc80>(), // tap.rgtc[].rgbrc;complex
+ InfoRow<NS_sprm::TInsert>(), // tap.rgdxaCenter, tap.rgtc;complex
+ InfoRow<NS_sprm::TDelete>(), // tap.rgdxaCenter, tap.rgtc;complex
+ InfoRow<NS_sprm::TDxaCol>(), // tap.rgdxaCenter;complex
+ InfoRow<NS_sprm::TMerge>(), // tap.fFirstMerged, tap.fMerged;
+ InfoRow<NS_sprm::TSplit>(), // tap.fFirstMerged, tap.fMerged;
+ {NS_sprm::LN_TSetBrc10, { 0, L_VAR} }, // "sprmTSetBrc10" tap.rgtc[].rgbrc;complex
+ {NS_sprm::LN_TSetShd80, { 0, L_VAR} }, // "sprmTSetShd80" tap.rgshd;complex
+ {NS_sprm::LN_TSetShdOdd80, { 0, L_VAR} }, // "sprmTSetShdOdd80" tap.rgshd;complex
+ InfoRow<NS_sprm::TTextFlow>(), // tap.rgtc[].fVerticaltap,
+ // rgtc[].fBackwardtap, rgtc[].fRotateFont;0 or 10
+ // or 10 or 1;word;
+ {NS_sprm::LN_TDiagLine, { 1, L_FIX} }, // "sprmTDiagLine" ;;;
+ InfoRow<NS_sprm::TVertMerge>(), // tap.rgtc[].vertMerge
+ InfoRow<NS_sprm::TVertAlign>(), // tap.rgtc[].vertAlign
+ InfoRow<NS_sprm::CFELayout>(),
+ InfoRow<NS_sprm::PItap>(), // undocumented
+ InfoRow<NS_sprm::TTableWidth>(), // undocumented
+ InfoRow<NS_sprm::TDefTableShd>(),
+ InfoRow<NS_sprm::TTableBorders>(),
+ InfoRow<NS_sprm::TBrcTopCv>(), // undocumented
+ InfoRow<NS_sprm::TBrcLeftCv>(), // undocumented
+ InfoRow<NS_sprm::TBrcBottomCv>(), // undocumented
+ InfoRow<NS_sprm::TBrcRightCv>(), // undocumented
+ InfoRow<NS_sprm::TCellPadding>(), // undocumented
+ InfoRow<NS_sprm::TCellPaddingDefault>(), // undocumented
+ {0xD238, { 0, L_VAR} }, // undocumented sep
+ InfoRow<NS_sprm::PBrcTop>(),
+ InfoRow<NS_sprm::PBrcLeft>(),
+ InfoRow<NS_sprm::PBrcBottom>(),
+ InfoRow<NS_sprm::PBrcRight>(),
+ InfoRow<NS_sprm::PBrcBetween>(),
+ InfoRow<NS_sprm::TWidthIndent>(), // undocumented
+ InfoRow<NS_sprm::CRgLid0>(), // chp.rglid[0];LID: for non-FE text
+ InfoRow<NS_sprm::CRgLid1>(), // chp.rglid[1];LID: for Far East text
+ {0x6463, { 4, L_FIX} }, // undocumented
+ InfoRow<NS_sprm::PJc>(), // undoc, must be asian version of "sprmPJc"
+ InfoRow<NS_sprm::PDxaRight>(), // undoc, must be asian version of "sprmPDxaRight"
+ InfoRow<NS_sprm::PDxaLeft>(), // undoc, must be asian version of "sprmPDxaLeft"
+ InfoRow<NS_sprm::PDxaLeft1>(), // undoc, must be asian version of "sprmPDxaLeft1"
+ InfoRow<NS_sprm::TFAutofit>(), // undocumented
+ InfoRow<NS_sprm::TPc>(), // undocumented
+ InfoRow<NS_sprm::SRsid>(), // undocumented, sep, perhaps related to textgrids ?
+ InfoRow<NS_sprm::SFpc>(), // undocumented, sep
+ InfoRow<NS_sprm::PFInnerTableCell>(), // undocumented, subtable "sprmPFInTable" equiv ?
+ InfoRow<NS_sprm::PFInnerTtp>(), // undocumented, subtable "sprmPFTtp" equiv ?
+ InfoRow<NS_sprm::TDxaAbs>(), // undocumented
+ InfoRow<NS_sprm::TDyaAbs>(), // undocumented
+ InfoRow<NS_sprm::TDxaFromText>(), // undocumented
+ InfoRow<NS_sprm::CRsidProp>(), // undocumented
+ InfoRow<NS_sprm::CRsidText>(), // undocumented
+ InfoRow<NS_sprm::CCv>(), // text colour
+ InfoRow<NS_sprm::PShd>(), // undocumented, para back colour
+ InfoRow<NS_sprm::PRsid>(), // undocumented
+ InfoRow<NS_sprm::PTableProps>(), // undocumented
+ InfoRow<NS_sprm::TWidthBefore>(), // undocumented
+ InfoRow<NS_sprm::TSetShdTable>(), // undocumented, something to do with colour.
+ InfoRow<NS_sprm::TDefTableShdRaw>(), // undocumented, something to do with colour.
+ InfoRow<NS_sprm::CShd>(), // text backcolour
+ InfoRow<NS_sprm::SRncFtn>(), // undocumented, sep
+ InfoRow<NS_sprm::PFDyaBeforeAuto>(), // undocumented, para autobefore
+ InfoRow<NS_sprm::PFDyaAfterAuto>(), // undocumented, para autoafter
+ // "sprmPFContextualSpacing", don't add space between para of the same style
+ InfoRow<NS_sprm::PFContextualSpacing>(),
+ };
+
+ static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms));
+ return &aSprmSrch;
+};
+
+wwSprmParser::wwSprmParser(const WW8Fib& rFib) : meVersion(rFib.GetFIBVersion())
+{
+ OSL_ENSURE((meVersion >= ww::eWW1 && meVersion <= ww::eWW8),
+ "Impossible value for version");
+
+ mnDelta = (ww::IsSevenMinus(meVersion)) ? 0 : 1;
+
+ if (meVersion <= ww::eWW2)
+ mpKnownSprms = GetWW2SprmSearcher();
+ else if (meVersion < ww::eWW8)
+ mpKnownSprms = GetWW6SprmSearcher(rFib);
+ else
+ mpKnownSprms = GetWW8SprmSearcher();
+}
+
+SprmInfo wwSprmParser::GetSprmInfo(sal_uInt16 nId) const
+{
+ const SprmInfo* pFound = mpKnownSprms->search(nId);
+ if (pFound != nullptr)
+ {
+ return *pFound;
+ }
+
+ OSL_ENSURE(ww::IsEightPlus(meVersion),
+ "Unknown ww7- sprm, dangerous, report to development");
+
+ //All the unknown ww7 sprms appear to be variable (which makes sense)
+ SprmInfo aSrch = { 0, L_VAR };
+ if (ww::IsEightPlus(meVersion)) //We can recover perfectly in this case
+ {
+ aSrch.nVari = L_FIX;
+ switch (nId >> 13)
+ {
+ case 0:
+ case 1:
+ aSrch.nLen = 1;
+ break;
+ case 2:
+ aSrch.nLen = 2;
+ break;
+ case 3:
+ aSrch.nLen = 4;
+ break;
+ case 4:
+ case 5:
+ aSrch.nLen = 2;
+ break;
+ case 6:
+ aSrch.nLen = 0;
+ aSrch.nVari = L_VAR;
+ break;
+ case 7:
+ default:
+ aSrch.nLen = 3;
+ break;
+ }
+ }
+ return aSrch;
+}
+
+//-end
+
+static sal_uInt8 Get_Byte( sal_uInt8 *& p )
+{
+ sal_uInt8 n = *p;
+ p += 1;
+ return n;
+}
+
+static sal_uInt16 Get_UShort( sal_uInt8 *& p )
+{
+ const sal_uInt16 n = SVBT16ToUInt16( *reinterpret_cast<SVBT16*>(p) );
+ p += 2;
+ return n;
+}
+
+static sal_Int16 Get_Short( sal_uInt8 *& p )
+{
+ return Get_UShort(p);
+}
+
+static sal_uInt32 Get_ULong( sal_uInt8 *& p )
+{
+ sal_uInt32 n = SVBT32ToUInt32( *reinterpret_cast<SVBT32*>(p) );
+ p += 4;
+ return n;
+}
+
+static sal_Int32 Get_Long( sal_uInt8 *& p )
+{
+ return Get_ULong(p);
+}
+
+WW8SprmIter::WW8SprmIter(const sal_uInt8* pSprms_, sal_Int32 nLen_,
+ const wwSprmParser &rParser)
+ : mrSprmParser(rParser), m_pSprms( pSprms_), m_nRemLen( nLen_)
+{
+ UpdateMyMembers();
+}
+
+void WW8SprmIter::SetSprms(const sal_uInt8* pSprms_, sal_Int32 nLen_)
+{
+ m_pSprms = pSprms_;
+ m_nRemLen = nLen_;
+ UpdateMyMembers();
+}
+
+void WW8SprmIter::advance()
+{
+ if (m_nRemLen > 0 )
+ {
+ sal_uInt16 nSize = m_nCurrentSize;
+ if (nSize > m_nRemLen)
+ nSize = m_nRemLen;
+ m_pSprms += nSize;
+ m_nRemLen -= nSize;
+ UpdateMyMembers();
+ }
+}
+
+void WW8SprmIter::UpdateMyMembers()
+{
+ bool bValid = (m_pSprms && m_nRemLen >= mrSprmParser.MinSprmLen());
+
+ if (bValid)
+ {
+ m_nCurrentId = mrSprmParser.GetSprmId(m_pSprms);
+ m_nCurrentSize = mrSprmParser.GetSprmSize(m_nCurrentId, m_pSprms, m_nRemLen);
+ m_pCurrentParams = m_pSprms + mrSprmParser.DistanceToData(m_nCurrentId);
+ bValid = m_nCurrentSize <= m_nRemLen;
+ SAL_WARN_IF(!bValid, "sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong");
+ }
+
+ if (!bValid)
+ {
+ m_nCurrentId = 0;
+ m_pCurrentParams = nullptr;
+ m_nCurrentSize = 0;
+ m_nRemLen = 0;
+ }
+}
+
+SprmResult WW8SprmIter::FindSprm(sal_uInt16 nId, bool bFindFirst, const sal_uInt8* pNextByteMatch)
+{
+ SprmResult aRet;
+
+ while (GetSprms())
+ {
+ if (GetCurrentId() == nId)
+ {
+ sal_Int32 nFixedLen = mrSprmParser.DistanceToData(nId);
+ sal_Int32 nL = mrSprmParser.GetSprmSize(nId, GetSprms(), GetRemLen());
+ SprmResult aSprmResult(GetCurrentParams(), nL - nFixedLen);
+ // typically pNextByteMatch is nullptr and we just return the first match
+ // very occasionally we want one with a specific following byte
+ if ( !pNextByteMatch || (aSprmResult.nRemainingData >= 1 && *aSprmResult.pSprm == *pNextByteMatch) )
+ {
+ if ( bFindFirst )
+ return aSprmResult;
+ aRet = aSprmResult;
+ }
+ }
+ advance();
+ }
+
+ return aRet;
+}
+
+// temporary test
+// WW8PLCFx_PCDAttrs cling to WW8PLCF_Pcd and therefore do not have their own iterators.
+// All methods relating to iterators are therefore dummies.
+WW8PLCFx_PCDAttrs::WW8PLCFx_PCDAttrs(const WW8Fib& rFib,
+ WW8PLCFx_PCD* pPLCFx_PCD, const WW8ScannerBase* pBase)
+ : WW8PLCFx(rFib, true), m_pPcdI(pPLCFx_PCD->GetPLCFIter()),
+ m_pPcd(pPLCFx_PCD), mrGrpprls(pBase->m_aPieceGrpprls)
+{
+}
+
+sal_uInt32 WW8PLCFx_PCDAttrs::GetIdx() const
+{
+ return 0;
+}
+
+void WW8PLCFx_PCDAttrs::SetIdx(sal_uInt32)
+{
+}
+
+bool WW8PLCFx_PCDAttrs::SeekPos(WW8_CP )
+{
+ return true;
+}
+
+void WW8PLCFx_PCDAttrs::advance()
+{
+}
+
+WW8_CP WW8PLCFx_PCDAttrs::Where()
+{
+ return m_pPcd ? m_pPcd->Where() : WW8_CP_MAX;
+}
+
+void WW8PLCFx_PCDAttrs::GetSprms(WW8PLCFxDesc* p)
+{
+ void* pData;
+
+ p->bRealLineEnd = false;
+ if ( !m_pPcdI || !m_pPcdI->Get(p->nStartPos, p->nEndPos, pData) )
+ {
+ // PLCF fully processed
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ return;
+ }
+
+ const sal_uInt16 nPrm = SVBT16ToUInt16( static_cast<WW8_PCD*>(pData)->prm );
+ if ( nPrm & 1 )
+ {
+ // PRM Variant 2
+ const sal_uInt16 nSprmIdx = nPrm >> 1;
+
+ if( nSprmIdx >= mrGrpprls.size() )
+ {
+ // Invalid Index
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ return;
+ }
+ const sal_uInt8* pSprms = mrGrpprls[ nSprmIdx ].get();
+
+ p->nSprmsLen = SVBT16ToUInt16( pSprms ); // Length
+ pSprms += 2;
+ p->pMemPos = pSprms; // Position
+ }
+ else
+ {
+ // SPRM is stored directly into members var
+ /*
+ These are the attr that are in the piece-table instead of in the text!
+ */
+
+ if (IsSevenMinus(GetFIBVersion()))
+ {
+ m_aShortSprm[0] = static_cast<sal_uInt8>( ( nPrm & 0xfe) >> 1 );
+ m_aShortSprm[1] = static_cast<sal_uInt8>( nPrm >> 8 );
+ p->nSprmsLen = nPrm ? 2 : 0; // length
+
+ // store Position of internal mini storage in Data Pointer
+ p->pMemPos = m_aShortSprm;
+ }
+ else
+ {
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ sal_uInt8 nSprmListIdx = static_cast<sal_uInt8>((nPrm & 0xfe) >> 1);
+ if( nSprmListIdx )
+ {
+ // process Sprm Id Matching as explained in MS Documentation
+
+ // ''Property Modifier(variant 1) (PRM)''
+ // see file: s62f39.htm
+
+ // Since Sprm is 7 bits, rgsprmPrm can hold 0x80 entries.
+ static const sal_uInt16 aSprmId[0x80] =
+ {
+ // sprmNoop, sprmNoop, sprmNoop, sprmNoop
+ 0x0000,0x0000,0x0000,0x0000,
+ // sprmPIncLvl, sprmPJc, sprmPFSideBySide, sprmPFKeep
+ 0x2402,0x2403,NS_sprm::LN_PFSideBySide,0x2405,
+ // sprmPFKeepFollow, sprmPFPageBreakBefore, sprmPBrcl,
+ // sprmPBrcp
+ 0x2406,0x2407,NS_sprm::LN_PBrcl,NS_sprm::LN_PBrcp,
+ // sprmPIlvl, sprmNoop, sprmPFNoLineNumb, sprmNoop
+ 0x260A,0x0000,0x240C,0x0000,
+ // sprmNoop, sprmNoop, sprmNoop, sprmNoop
+ 0x0000,0x0000,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmNoop, sprmNoop
+ 0x0000,0x0000,0x0000,0x0000,
+ // sprmPFInTable, sprmPFTtp, sprmNoop, sprmNoop
+ 0x2416,0x2417,0x0000,0x0000,
+ // sprmNoop, sprmPPc, sprmNoop, sprmNoop
+ 0x0000,0x261B,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmNoop, sprmNoop
+ 0x0000,0x0000,0x0000,0x0000,
+ // sprmNoop, sprmPWr, sprmNoop, sprmNoop
+ 0x0000,0x2423,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmNoop, sprmNoop
+ 0x0000,0x0000,0x0000,0x0000,
+ // sprmPFNoAutoHyph, sprmNoop, sprmNoop, sprmNoop
+ 0x242A,0x0000,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmPFLocked, sprmPFWidowControl
+ 0x0000,0x0000,0x2430,0x2431,
+ // sprmNoop, sprmPFKinsoku, sprmPFWordWrap,
+ // sprmPFOverflowPunct
+ 0x0000,0x2433,0x2434,0x2435,
+ // sprmPFTopLinePunct, sprmPFAutoSpaceDE,
+ // sprmPFAutoSpaceDN, sprmNoop
+ 0x2436,0x2437,0x2438,0x0000,
+ // sprmNoop, sprmPISnapBaseLine, sprmNoop, sprmNoop
+ 0x0000,NS_sprm::LN_PISnapBaseLine,0x000,0x0000,
+ // sprmNoop, sprmCFStrikeRM, sprmCFRMark, sprmCFFieldVanish
+ 0x0000,0x0800,0x0801,0x0802,
+ // sprmNoop, sprmNoop, sprmNoop, sprmCFData
+ 0x0000,0x0000,0x0000,0x0806,
+ // sprmNoop, sprmNoop, sprmNoop, sprmCFOle2
+ 0x0000,0x0000,0x0000,0x080A,
+ // sprmNoop, sprmCHighlight, sprmCFEmboss, sprmCSfxText
+ 0x0000,0x2A0C,0x0858,0x2859,
+ // sprmNoop, sprmNoop, sprmNoop, sprmCPlain
+ 0x0000,0x0000,0x0000,0x2A33,
+ // sprmNoop, sprmCFBold, sprmCFItalic, sprmCFStrike
+ 0x0000,0x0835,0x0836,0x0837,
+ // sprmCFOutline, sprmCFShadow, sprmCFSmallCaps, sprmCFCaps,
+ 0x0838,0x0839,0x083a,0x083b,
+ // sprmCFVanish, sprmNoop, sprmCKul, sprmNoop,
+ 0x083C,0x0000,0x2A3E,0x0000,
+ // sprmNoop, sprmNoop, sprmCIco, sprmNoop,
+ 0x0000,0x0000,0x2A42,0x0000,
+ // sprmCHpsInc, sprmNoop, sprmCHpsPosAdj, sprmNoop,
+ NS_sprm::LN_CHpsInc,0x0000,NS_sprm::LN_CHpsPosAdj,0x0000,
+ // sprmCIss, sprmNoop, sprmNoop, sprmNoop,
+ 0x2A48,0x0000,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmNoop, sprmNoop,
+ 0x0000,0x0000,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmNoop, sprmCFDStrike,
+ 0x0000,0x0000,0x0000,0x2A53,
+ // sprmCFImprint, sprmCFSpec, sprmCFObj, sprmPicBrcl,
+ 0x0854,0x0855,0x0856,NS_sprm::LN_PicBrcl,
+ // sprmPOutLvl, sprmPFBiDi, sprmNoop, sprmNoop,
+ 0x2640,0x2441,0x0000,0x0000,
+ // sprmNoop, sprmNoop, sprmPPnbrRMarkNot
+ 0x0000,0x0000,0x0000,0x0000
+ };
+
+ // find real Sprm Id:
+ const sal_uInt16 nSprmId = aSprmId[ nSprmListIdx ];
+
+ if( nSprmId )
+ {
+ // move Sprm Id and Sprm Param to internal mini storage:
+ m_aShortSprm[0] = static_cast<sal_uInt8>( nSprmId & 0x00ff) ;
+ m_aShortSprm[1] = static_cast<sal_uInt8>( ( nSprmId & 0xff00) >> 8 );
+ m_aShortSprm[2] = static_cast<sal_uInt8>( nPrm >> 8 );
+
+ // store Sprm Length in member:
+ p->nSprmsLen = nPrm ? 3 : 0;
+
+ // store Position of internal mini storage in Data Pointer
+ p->pMemPos = m_aShortSprm;
+ }
+ }
+ }
+ }
+}
+
+WW8PLCFx_PCD::WW8PLCFx_PCD(const WW8Fib& rFib, WW8PLCFpcd* pPLCFpcd,
+ WW8_CP nStartCp, bool bVer67P)
+ : WW8PLCFx(rFib, false), m_nClipStart(-1)
+{
+ // construct own iterator
+ m_pPcdI.reset( new WW8PLCFpcd_Iter(*pPLCFpcd, nStartCp) );
+ m_bVer67= bVer67P;
+}
+
+WW8PLCFx_PCD::~WW8PLCFx_PCD()
+{
+}
+
+sal_uInt32 WW8PLCFx_PCD::GetIMax() const
+{
+ return m_pPcdI ? m_pPcdI->GetIMax() : 0;
+}
+
+sal_uInt32 WW8PLCFx_PCD::GetIdx() const
+{
+ return m_pPcdI ? m_pPcdI->GetIdx() : 0;
+}
+
+void WW8PLCFx_PCD::SetIdx(sal_uInt32 nIdx)
+{
+ if (m_pPcdI)
+ m_pPcdI->SetIdx( nIdx );
+}
+
+bool WW8PLCFx_PCD::SeekPos(WW8_CP nCpPos)
+{
+ return m_pPcdI && m_pPcdI->SeekPos( nCpPos );
+}
+
+WW8_CP WW8PLCFx_PCD::Where()
+{
+ return m_pPcdI ? m_pPcdI->Where() : WW8_CP_MAX;
+}
+
+tools::Long WW8PLCFx_PCD::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen )
+{
+ void* pData;
+ rLen = 0;
+
+ if ( !m_pPcdI || !m_pPcdI->Get(rStart, rEnd, pData) )
+ {
+ rStart = rEnd = WW8_CP_MAX;
+ return -1;
+ }
+ return m_pPcdI->GetIdx();
+}
+
+void WW8PLCFx_PCD::advance()
+{
+ OSL_ENSURE(m_pPcdI , "missing pPcdI");
+ if (m_pPcdI)
+ m_pPcdI->advance();
+}
+
+WW8_FC WW8PLCFx_PCD::CurrentPieceStartCp2Fc( WW8_CP nCp )
+{
+ WW8_CP nCpStart, nCpEnd;
+ void* pData;
+
+ if ( !m_pPcdI->Get(nCpStart, nCpEnd, pData) )
+ {
+ OSL_ENSURE( false, "CurrentPieceStartCp2Fc() with false Cp found (1)" );
+ return WW8_FC_MAX;
+ }
+
+ OSL_ENSURE( nCp >= nCpStart && nCp < nCpEnd,
+ "AktPieceCp2Fc() with false Cp found (2)" );
+
+ if( nCp < nCpStart )
+ nCp = nCpStart;
+ if( nCp >= nCpEnd )
+ nCp = nCpEnd - 1;
+
+ bool bIsUnicode = false;
+ WW8_FC nFC = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc );
+ if( !m_bVer67 )
+ nFC = WW8PLCFx_PCD::TransformPieceAddress( nFC, bIsUnicode );
+
+ WW8_CP nDistance;
+ bool bFail = o3tl::checked_sub(nCp, nCpStart, nDistance);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_FC_MAX;
+ }
+
+ if (bIsUnicode)
+ {
+ bFail = o3tl::checked_multiply<WW8_CP>(nDistance, 2, nDistance);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_FC_MAX;
+ }
+ }
+
+ WW8_FC nRet;
+ bFail = o3tl::checked_add(nFC, nDistance, nRet);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_FC_MAX;
+ }
+
+ return nRet;
+}
+
+void WW8PLCFx_PCD::CurrentPieceFc2Cp( WW8_CP& rStartPos, WW8_CP& rEndPos,
+ const WW8ScannerBase *pSBase )
+{
+ //No point going anywhere with this
+ if ((rStartPos == WW8_CP_MAX) && (rEndPos == WW8_CP_MAX))
+ return;
+
+ rStartPos = pSBase->WW8Fc2Cp( rStartPos );
+ rEndPos = pSBase->WW8Fc2Cp( rEndPos );
+}
+
+WW8_CP WW8PLCFx_PCD::CurrentPieceStartFc2Cp( WW8_FC nStartPos )
+{
+ WW8_CP nCpStart, nCpEnd;
+ void* pData;
+ if ( !m_pPcdI->Get( nCpStart, nCpEnd, pData ) )
+ {
+ OSL_ENSURE( false, "CurrentPieceStartFc2Cp() - error" );
+ return WW8_CP_MAX;
+ }
+ bool bIsUnicode = false;
+ sal_Int32 nFcStart = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc );
+ if( !m_bVer67 )
+ nFcStart = WW8PLCFx_PCD::TransformPieceAddress( nFcStart, bIsUnicode );
+
+ sal_Int32 nUnicodeFactor = bIsUnicode ? 2 : 1;
+
+ if( nStartPos < nFcStart )
+ nStartPos = nFcStart;
+
+ WW8_CP nCpLen;
+ bool bFail = o3tl::checked_sub(nCpEnd, nCpStart, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ WW8_CP nCpLenBytes;
+ bFail = o3tl::checked_multiply(nCpLen, nUnicodeFactor, nCpLenBytes);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ WW8_FC nFcLen;
+ bFail = o3tl::checked_add(nFcStart, nCpLenBytes, nFcLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ WW8_FC nFcEnd;
+ bFail = o3tl::checked_add(nFcStart, nFcLen, nFcEnd);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+
+ if (nStartPos >= nFcEnd)
+ nStartPos = nFcEnd - (1 * nUnicodeFactor);
+
+ WW8_FC nFcDiff = (nStartPos - nFcStart) / nUnicodeFactor;
+
+ WW8_FC nCpRet;
+ bFail = o3tl::checked_add(nCpStart, nFcDiff, nCpRet);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ return nCpRet;
+}
+
+// Helper routines for all
+
+// Convert BRC from WW6 to WW8 format
+WW8_BRC::WW8_BRC(const WW8_BRCVer6& brcVer6)
+{
+ sal_uInt8 _dptLineWidth = brcVer6.dxpLineWidth(),
+ _brcType = brcVer6.brcType();
+
+ if (_dptLineWidth > 5) // this signifies dashed(6) or dotted(7) line
+ {
+ _brcType = _dptLineWidth;
+ _dptLineWidth = 1;
+ }
+ _dptLineWidth *= 6; // convert units from 0.75pt to 1/8pt
+
+ *this = WW8_BRC(_dptLineWidth, _brcType, brcVer6.ico(), brcVer6.dxpSpace(),
+ brcVer6.fShadow(), false);
+}
+
+// Convert BRC from WW8 to WW9 format
+WW8_BRCVer9::WW8_BRCVer9(const WW8_BRC& brcVer8)
+{
+ if (brcVer8.isNil()) {
+ UInt32ToSVBT32(0, aBits1);
+ UInt32ToSVBT32(0xffffffff, aBits2);
+ }
+ else
+ {
+ sal_uInt32 _cv = brcVer8.ico() == 0 ? 0xff000000 // "auto" colour
+ : wwUtility::RGBToBGR(SwWW8ImplReader::GetCol(brcVer8.ico()));
+ *this = WW8_BRCVer9(_cv, brcVer8.dptLineWidth(), brcVer8.brcType(),
+ brcVer8.dptSpace(), brcVer8.fShadow(), brcVer8.fFrame());
+ }
+}
+
+short WW8_BRC::DetermineBorderProperties(short *pSpace) const
+{
+ WW8_BRCVer9 brcVer9(*this);
+ return brcVer9.DetermineBorderProperties(pSpace);
+}
+
+short WW8_BRCVer9::DetermineBorderProperties(short *pSpace) const
+{
+ /*
+ Word does not factor the width of the border into the width/height
+ stored in the information for graphic/table/object widths, so we need
+ to figure out this extra width here and utilize the returned size in
+ our calculations
+ */
+ short nMSTotalWidth;
+
+ //Specification in 8ths of a point, 1 Point = 20 Twips, so by 2.5
+ nMSTotalWidth = static_cast<short>(dptLineWidth()) * 20 / 8;
+
+ //Figure out the real size of the border according to word
+ switch (brcType())
+ {
+ //Note that codes over 25 are undocumented, and I can't create
+ //these 4 here in the wild.
+ case 2:
+ case 4:
+ case 5:
+ case 22:
+ OSL_FAIL("Can't create these from the menus, please report");
+ break;
+ default:
+ case 23: //Only 3pt in the menus, but honours the size setting.
+ break;
+ case 10:
+ /*
+ triple line is five times the width of an ordinary line,
+ except that the smallest 1/4 point size appears to have
+ exactly the same total border width as a 3/4 point size
+ ordinary line, i.e. three times the nominal line width. The
+ second smallest 1/2 point size appears to have exactly the
+ total border width as a 2 1/4 border, i.e 4.5 times the size.
+ */
+ if (nMSTotalWidth == 5)
+ nMSTotalWidth*=3;
+ else if (nMSTotalWidth == 10)
+ nMSTotalWidth = nMSTotalWidth*9/2;
+ else
+ nMSTotalWidth*=5;
+ break;
+ case 20:
+ /*
+ wave, the dimensions appear to be created by the drawing of
+ the wave, so we have only two possibilities in the menus, 3/4
+ point is equal to solid 3 point. This calculation seems to
+ match well to results.
+ */
+ nMSTotalWidth +=45;
+ break;
+ case 21:
+ /*
+ double wave, the dimensions appear to be created by the
+ drawing of the wave, so we have only one possibilities in the
+ menus, that of 3/4 point is equal to solid 3 point. This
+ calculation seems to match well to results.
+ */
+ nMSTotalWidth += 45*2;
+ break;
+ }
+
+ if (pSpace)
+ *pSpace = static_cast<short>(dptSpace()) * 20; // convert from points to twips
+ return nMSTotalWidth;
+}
+
+/*
+ * WW8Cp2Fc is a good method, a CP always maps to a FC
+ * WW8Fc2Cp on the other hand is more dubious, a random FC
+ * may not map to a valid CP. Try and avoid WW8Fc2Cp where
+ * possible
+ */
+WW8_CP WW8ScannerBase::WW8Fc2Cp( WW8_FC nFcPos ) const
+{
+ WW8_CP nFallBackCpEnd = WW8_CP_MAX;
+ if( nFcPos == WW8_FC_MAX )
+ return nFallBackCpEnd;
+
+ bool bIsUnicode;
+ if (m_pWw8Fib->m_nVersion >= 8)
+ bIsUnicode = false;
+ else
+ bIsUnicode = m_pWw8Fib->m_fExtChar;
+
+ if( m_pPieceIter ) // Complex File ?
+ {
+ sal_uInt32 nOldPos = m_pPieceIter->GetIdx();
+
+ for (m_pPieceIter->SetIdx(0);
+ m_pPieceIter->GetIdx() < m_pPieceIter->GetIMax(); m_pPieceIter->advance())
+ {
+ WW8_CP nCpStart, nCpEnd;
+ void* pData;
+ if( !m_pPieceIter->Get( nCpStart, nCpEnd, pData ) )
+ { // outside PLCFfpcd ?
+ OSL_ENSURE( false, "PLCFpcd-WW8Fc2Cp() went wrong" );
+ break;
+ }
+ sal_Int32 nFcStart = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc );
+ if (m_pWw8Fib->m_nVersion >= 8)
+ {
+ nFcStart = WW8PLCFx_PCD::TransformPieceAddress( nFcStart,
+ bIsUnicode );
+ }
+ else
+ {
+ bIsUnicode = m_pWw8Fib->m_fExtChar;
+ }
+
+ sal_Int32 nLen;
+ if (o3tl::checked_sub(nCpEnd, nCpStart, nLen))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ if (bIsUnicode)
+ {
+ if (o3tl::checked_multiply<WW8_CP>(nLen, 2, nLen))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ }
+
+ /*
+ If this cp is inside this piece, or it's the last piece and we are
+ on the very last cp of that piece
+ */
+ if (nFcPos >= nFcStart)
+ {
+ // found
+ WW8_FC nFcDiff;
+ if (o3tl::checked_sub(nFcPos, nFcStart, nFcDiff))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ if (bIsUnicode)
+ nFcDiff /= 2;
+ WW8_CP nTempCp;
+ if (o3tl::checked_add(nCpStart, nFcDiff, nTempCp))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ WW8_FC nFcEnd;
+ if (o3tl::checked_add(nFcStart, nLen, nFcEnd))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ if (nFcPos < nFcEnd)
+ {
+ m_pPieceIter->SetIdx( nOldPos );
+ return nTempCp;
+ }
+ else if (nFcPos == nFcEnd)
+ {
+ //Keep this cp as its on a piece boundary because we might
+ //need it if tests fail
+ nFallBackCpEnd = nTempCp;
+ }
+ }
+ }
+ // not found
+ m_pPieceIter->SetIdx( nOldPos ); // not found
+ /*
+ If it was not found, then this is because it has fallen between two
+ stools, i.e. either it is the last cp/fc of the last piece, or it is
+ the last cp/fc of a disjoint piece.
+ */
+ return nFallBackCpEnd;
+ }
+
+ WW8_FC nFcDiff;
+ if (o3tl::checked_sub(nFcPos, m_pWw8Fib->m_fcMin, nFcDiff))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ // No complex file
+ if (!bIsUnicode)
+ nFallBackCpEnd = nFcDiff;
+ else
+ nFallBackCpEnd = (nFcDiff + 1) / 2;
+
+ return nFallBackCpEnd;
+}
+
+// the fib of WinWord2 has a last entry of cpnBtePap of 2 byte sized type PN at
+// offset 324
+const int nSmallestPossibleFib = 326;
+
+WW8_FC WW8ScannerBase::WW8Cp2Fc(WW8_CP nCpPos, bool* pIsUnicode,
+ WW8_CP* pNextPieceCp, bool* pTestFlag) const
+{
+ if( pTestFlag )
+ *pTestFlag = true;
+ if( WW8_CP_MAX == nCpPos )
+ return WW8_CP_MAX;
+
+ bool bIsUnicode;
+ if( !pIsUnicode )
+ pIsUnicode = &bIsUnicode;
+
+ if (m_pWw8Fib->m_nVersion >= 8)
+ *pIsUnicode = false;
+ else
+ *pIsUnicode = m_pWw8Fib->m_fExtChar;
+
+ WW8_FC nRet;
+
+ if( m_pPieceIter )
+ {
+ // Complex File
+ if( pNextPieceCp )
+ *pNextPieceCp = WW8_CP_MAX;
+
+ if( !m_pPieceIter->SeekPos( nCpPos ) )
+ {
+ if( pTestFlag )
+ *pTestFlag = false;
+ else {
+ OSL_ENSURE( false, "Handed over wrong CP to WW8Cp2Fc()" );
+ }
+ return WW8_FC_MAX;
+ }
+ WW8_CP nCpStart, nCpEnd;
+ void* pData;
+ if( !m_pPieceIter->Get( nCpStart, nCpEnd, pData ) )
+ {
+ if( pTestFlag )
+ *pTestFlag = false;
+ else {
+ OSL_ENSURE( false, "PLCFfpcd-Get went wrong" );
+ }
+ return WW8_FC_MAX;
+ }
+ if( pNextPieceCp )
+ *pNextPieceCp = nCpEnd;
+
+ nRet = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc );
+ if (m_pWw8Fib->m_nVersion >= 8)
+ nRet = WW8PLCFx_PCD::TransformPieceAddress( nRet, *pIsUnicode );
+ else
+ *pIsUnicode = m_pWw8Fib->m_fExtChar;
+
+ WW8_CP nCpLen;
+ bool bFail = o3tl::checked_sub(nCpPos, nCpStart, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ if (*pIsUnicode)
+ {
+ bFail = o3tl::checked_multiply<WW8_CP>(nCpLen, 2, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ }
+
+ bFail = o3tl::checked_add(nRet, nCpLen, nRet);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ return nRet;
+ }
+
+ if (*pIsUnicode)
+ {
+ const bool bFail = o3tl::checked_multiply<WW8_CP>(nCpPos, 2, nCpPos);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+ }
+
+ // No complex file
+ const bool bFail = o3tl::checked_add(m_pWw8Fib->m_fcMin, nCpPos, nRet);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ // the text and the fib share the same stream, if the text is inside the fib
+ // then it's definitely a bad offset. The smallest FIB supported is that of
+ // WW2 which is 326 bytes in size
+ if (nRet < nSmallestPossibleFib)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return WW8_CP_MAX;
+ }
+
+ return nRet;
+}
+
+std::unique_ptr<WW8PLCFpcd> WW8ScannerBase::OpenPieceTable( SvStream* pStr, const WW8Fib* pWwF )
+{
+ if ( ((8 > m_pWw8Fib->m_nVersion) && !pWwF->m_fComplex) || !pWwF->m_lcbClx )
+ return nullptr;
+
+ if (pWwF->m_lcbClx < 0)
+ return nullptr;
+
+ WW8_FC nClxPos = pWwF->m_fcClx;
+
+ if (!checkSeek(*pStr, nClxPos))
+ return nullptr;
+
+ sal_Int32 nClxLen = pWwF->m_lcbClx;
+ sal_Int32 nLeft = nClxLen;
+
+ while (true)
+ {
+ sal_uInt8 clxt(2);
+ pStr->ReadUChar( clxt );
+ nLeft--;
+ if( 2 == clxt) // PLCFfpcd ?
+ break; // PLCFfpcd found
+ sal_uInt16 nLen(0);
+ pStr->ReadUInt16( nLen );
+ nLeft -= 2 + nLen;
+ if( nLeft < 0 )
+ return nullptr; // gone wrong
+ if( 1 == clxt ) // clxtGrpprl ?
+ {
+ if (m_aPieceGrpprls.size() == SHRT_MAX)
+ return nullptr;
+ if (nLen > pStr->remainingSize())
+ return nullptr;
+ std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[nLen+2]); // allocate
+ ShortToSVBT16(nLen, p.get()); // add length
+ if (!checkRead(*pStr, p.get()+2, nLen)) // read grpprl
+ {
+ return nullptr;
+ }
+ m_aPieceGrpprls.push_back(std::move(p)); // add to array
+ }
+ else
+ {
+ nLen = std::min<sal_uInt64>(nLen, pStr->remainingSize());
+ pStr->Seek(pStr->Tell() + nLen); // non-Grpprl left
+ }
+ }
+
+ // read Piece Table PLCF
+ sal_Int32 nPLCFfLen(0);
+ if (pWwF->GetFIBVersion() <= ww::eWW2)
+ {
+ sal_Int16 nWordTwoLen(0);
+ pStr->ReadInt16( nWordTwoLen );
+ nPLCFfLen = nWordTwoLen;
+ }
+ else
+ pStr->ReadInt32( nPLCFfLen );
+ OSL_ENSURE( 65536 > nPLCFfLen, "PLCFfpcd above 64 k" );
+ return std::make_unique<WW8PLCFpcd>( pStr, pStr->Tell(), nPLCFfLen, 8 );
+}
+
+WW8ScannerBase::WW8ScannerBase( SvStream* pSt, SvStream* pTableSt,
+ SvStream* pDataSt, WW8Fib* pWwFib )
+ : m_pWw8Fib(pWwFib)
+{
+ m_pPiecePLCF = OpenPieceTable( pTableSt, m_pWw8Fib ); // Complex
+ if( m_pPiecePLCF )
+ {
+ m_pPieceIter.reset(new WW8PLCFpcd_Iter( *m_pPiecePLCF ));
+ m_pPLCFx_PCD.reset( new WW8PLCFx_PCD(*pWwFib, m_pPiecePLCF.get(), 0,
+ IsSevenMinus(m_pWw8Fib->GetFIBVersion())));
+ m_pPLCFx_PCDAttrs.reset(new WW8PLCFx_PCDAttrs(*pWwFib,
+ m_pPLCFx_PCD.get(), this));
+ }
+ else
+ {
+ m_pPieceIter = nullptr;
+ m_pPLCFx_PCD = nullptr;
+ m_pPLCFx_PCDAttrs = nullptr;
+ }
+
+ // pChpPLCF and pPapPLCF may NOT be created before pPLCFx_PCD !!
+ m_pChpPLCF.reset(new WW8PLCFx_Cp_FKP( pSt, pTableSt, pDataSt, *this, CHP )); // CHPX
+ m_pPapPLCF.reset(new WW8PLCFx_Cp_FKP( pSt, pTableSt, pDataSt, *this, PAP )); // PAPX
+
+ m_pSepPLCF.reset(new WW8PLCFx_SEPX( pSt, pTableSt, *pWwFib, 0 )); // SEPX
+
+ // Footnotes
+ m_pFootnotePLCF.reset(new WW8PLCFx_SubDoc( pTableSt, *pWwFib, 0,
+ pWwFib->m_fcPlcffndRef, pWwFib->m_lcbPlcffndRef, pWwFib->m_fcPlcffndText,
+ pWwFib->m_lcbPlcffndText, 2 ));
+ // Endnotes
+ m_pEdnPLCF.reset(new WW8PLCFx_SubDoc( pTableSt, *pWwFib, 0,
+ pWwFib->m_fcPlcfendRef, pWwFib->m_lcbPlcfendRef, pWwFib->m_fcPlcfendText,
+ pWwFib->m_lcbPlcfendText, 2 ));
+ // Comments
+ m_pAndPLCF.reset(new WW8PLCFx_SubDoc( pTableSt, *pWwFib, 0,
+ pWwFib->m_fcPlcfandRef, pWwFib->m_lcbPlcfandRef, pWwFib->m_fcPlcfandText,
+ pWwFib->m_lcbPlcfandText, IsSevenMinus(pWwFib->GetFIBVersion()) ? 20 : 30));
+
+ // Fields Main Text
+ m_pFieldPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_MAINTEXT));
+ // Fields Header / Footer
+ m_pFieldHdFtPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_HDFT));
+ // Fields Footnote
+ m_pFieldFootnotePLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_FTN));
+ // Fields Endnote
+ m_pFieldEdnPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_EDN));
+ // Fields Comments
+ m_pFieldAndPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_AND));
+ // Fields in Textboxes in Main Text
+ m_pFieldTxbxPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_TXBX));
+ // Fields in Textboxes in Header / Footer
+ m_pFieldTxbxHdFtPLCF.reset(new WW8PLCFx_FLD(pTableSt,*pWwFib,MAN_TXBX_HDFT));
+
+ // Note: 6 stands for "6 OR 7", 7 stands for "ONLY 7"
+ switch( m_pWw8Fib->m_nVersion )
+ {
+ case 6:
+ case 7:
+ if( pWwFib->m_fcPlcfdoaMom && pWwFib->m_lcbPlcfdoaMom )
+ {
+ m_pMainFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfdoaMom,
+ pWwFib->m_lcbPlcfdoaMom, 6 ));
+ }
+ if( pWwFib->m_fcPlcfdoaHdr && pWwFib->m_lcbPlcfdoaHdr )
+ {
+ m_pHdFtFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfdoaHdr,
+ pWwFib->m_lcbPlcfdoaHdr, 6 ));
+ }
+ break;
+ case 8:
+ if( pWwFib->m_fcPlcfspaMom && pWwFib->m_lcbPlcfspaMom )
+ {
+ m_pMainFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfspaMom,
+ pWwFib->m_lcbPlcfspaMom, 26 ));
+ }
+ if( pWwFib->m_fcPlcfspaHdr && pWwFib->m_lcbPlcfspaHdr )
+ {
+ m_pHdFtFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfspaHdr,
+ pWwFib->m_lcbPlcfspaHdr, 26 ));
+ }
+ // PLCF for TextBox break-descriptors in the main text
+ if( pWwFib->m_fcPlcftxbxBkd && pWwFib->m_lcbPlcftxbxBkd )
+ {
+ m_pMainTxbxBkd.reset(new WW8PLCFspecial( pTableSt,
+ pWwFib->m_fcPlcftxbxBkd, pWwFib->m_lcbPlcftxbxBkd, 0));
+ }
+ // PLCF for TextBox break-descriptors in Header/Footer range
+ if( pWwFib->m_fcPlcfHdrtxbxBkd && pWwFib->m_lcbPlcfHdrtxbxBkd )
+ {
+ m_pHdFtTxbxBkd.reset(new WW8PLCFspecial( pTableSt,
+ pWwFib->m_fcPlcfHdrtxbxBkd, pWwFib->m_lcbPlcfHdrtxbxBkd, 0));
+ }
+ // Sub table cp positions
+ if (pWwFib->m_fcPlcfTch && pWwFib->m_lcbPlcfTch)
+ {
+ m_pMagicTables.reset(new WW8PLCFspecial( pTableSt,
+ pWwFib->m_fcPlcfTch, pWwFib->m_lcbPlcfTch, 4));
+ }
+ // Sub document cp positions
+ if (pWwFib->m_fcPlcfwkb && pWwFib->m_lcbPlcfwkb)
+ {
+ m_pSubdocs.reset(new WW8PLCFspecial( pTableSt,
+ pWwFib->m_fcPlcfwkb, pWwFib->m_lcbPlcfwkb, 12));
+ }
+ // Extended ATRD
+ if (pWwFib->m_fcAtrdExtra && pWwFib->m_lcbAtrdExtra)
+ {
+ sal_uInt64 const nOldPos = pTableSt->Tell();
+ if (checkSeek(*pTableSt, pWwFib->m_fcAtrdExtra) && (pTableSt->remainingSize() >= pWwFib->m_lcbAtrdExtra))
+ {
+ m_pExtendedAtrds.reset( new sal_uInt8[pWwFib->m_lcbAtrdExtra] );
+ pWwFib->m_lcbAtrdExtra = pTableSt->ReadBytes(m_pExtendedAtrds.get(), pWwFib->m_lcbAtrdExtra);
+ }
+ else
+ pWwFib->m_lcbAtrdExtra = 0;
+ pTableSt->Seek(nOldPos);
+ }
+
+ break;
+ default:
+ OSL_ENSURE( false, "nVersion not implemented!" );
+ break;
+ }
+
+ // PLCF for TextBox stories in main text
+ sal_uInt32 nLenTxBxS = (8 > m_pWw8Fib->m_nVersion) ? 0 : 22;
+ if( pWwFib->m_fcPlcftxbxText && pWwFib->m_lcbPlcftxbxText )
+ {
+ m_pMainTxbx.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcftxbxText,
+ pWwFib->m_lcbPlcftxbxText, nLenTxBxS ));
+ }
+
+ // PLCF for TextBox stories in Header/Footer range
+ if( pWwFib->m_fcPlcfHdrtxbxText && pWwFib->m_lcbPlcfHdrtxbxText )
+ {
+ m_pHdFtTxbx.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfHdrtxbxText,
+ pWwFib->m_lcbPlcfHdrtxbxText, nLenTxBxS ));
+ }
+
+ m_pBook.reset(new WW8PLCFx_Book(pTableSt, *pWwFib));
+ m_pAtnBook.reset(new WW8PLCFx_AtnBook(pTableSt, *pWwFib));
+ m_pFactoidBook.reset(new WW8PLCFx_FactoidBook(pTableSt, *pWwFib));
+}
+
+WW8ScannerBase::~WW8ScannerBase()
+{
+ m_aPieceGrpprls.clear();
+ m_pPLCFx_PCDAttrs.reset();
+ m_pPLCFx_PCD.reset();
+ m_pPieceIter.reset();
+ m_pPiecePLCF.reset();
+ m_pFactoidBook.reset();
+ m_pAtnBook.reset();
+ m_pBook.reset();
+ m_pFieldEdnPLCF.reset();
+ m_pFieldFootnotePLCF.reset();
+ m_pFieldAndPLCF.reset();
+ m_pFieldHdFtPLCF.reset();
+ m_pFieldPLCF.reset();
+ m_pFieldTxbxPLCF.reset();
+ m_pFieldTxbxHdFtPLCF.reset();
+ m_pEdnPLCF.reset();
+ m_pFootnotePLCF.reset();
+ m_pAndPLCF.reset();
+ m_pSepPLCF.reset();
+ m_pPapPLCF.reset();
+ m_pChpPLCF.reset();
+ m_pMainFdoa.reset();
+ m_pHdFtFdoa.reset();
+ m_pMainTxbx.reset();
+ m_pMainTxbxBkd.reset();
+ m_pHdFtTxbx.reset();
+ m_pHdFtTxbxBkd.reset();
+ m_pMagicTables.reset();
+ m_pSubdocs.reset();
+}
+
+// Fields
+
+static bool WW8SkipField(WW8PLCFspecial& rPLCF)
+{
+ void* pData;
+ WW8_CP nP;
+
+ if (!rPLCF.Get(nP, pData)) // End of PLCFspecial?
+ return false;
+
+ rPLCF.advance();
+
+ if((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) != 0x13 ) // No beginning?
+ return true; // Do not terminate on error
+
+ if( !rPLCF.Get( nP, pData ) )
+ return false;
+
+ while((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13 )
+ {
+ // still new (nested) beginnings ?
+ WW8SkipField( rPLCF ); // nested Field in description
+ if( !rPLCF.Get( nP, pData ) )
+ return false;
+ }
+
+ if((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x14 )
+ {
+
+ // Field Separator ?
+ rPLCF.advance();
+
+ if( !rPLCF.Get( nP, pData ) )
+ return false;
+
+ while ((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13)
+ {
+ // still new (nested) beginnings?
+ WW8SkipField( rPLCF ); // nested Field in Results
+ if( !rPLCF.Get( nP, pData ) )
+ return false;
+ }
+ }
+ rPLCF.advance();
+
+ return true;
+}
+
+static bool WW8GetFieldPara(WW8PLCFspecial& rPLCF, WW8FieldDesc& rF)
+{
+ void* pData;
+ sal_uInt32 nOldIdx = rPLCF.GetIdx();
+
+ rF.nLen = rF.nId = rF.nOpt = 0;
+ rF.bCodeNest = rF.bResNest = false;
+
+ if (!rPLCF.Get(rF.nSCode, pData) || rF.nSCode < 0) // end of PLCFspecial?
+ goto Err;
+
+ rPLCF.advance();
+
+ if (!pData || (static_cast<sal_uInt8*>(pData)[0] & 0x1f) != 0x13) // No beginning?
+ goto Err;
+
+ rF.nId = static_cast<sal_uInt8*>(pData)[1];
+
+ if( !rPLCF.Get( rF.nLCode, pData ) )
+ goto Err;
+
+ if (rF.nLCode < rF.nSCode)
+ goto Err;
+
+ rF.nSRes = rF.nLCode; // Default
+ rF.nSCode++; // without markers
+ rF.nLCode -= rF.nSCode; // Pos -> length
+
+ while((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13 )
+ {
+ // still new (nested) beginnings ?
+ WW8SkipField( rPLCF ); // nested Field in description
+ rF.bCodeNest = true;
+ if (!rPLCF.Get(rF.nSRes, pData) || rF.nSRes < 0)
+ goto Err;
+ }
+
+ if ((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x14 ) // Field Separator?
+ {
+ rPLCF.advance();
+
+ if (!rPLCF.Get(rF.nLRes, pData) || rF.nLRes < 0)
+ goto Err;
+
+ while((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13 )
+ {
+ // still new (nested) beginnings ?
+ WW8SkipField( rPLCF ); // nested Field in results
+ rF.bResNest = true;
+ if (!rPLCF.Get(rF.nLRes, pData) || rF.nLRes < 0)
+ goto Err;
+ }
+ WW8_CP nTmp;
+ if (o3tl::checked_sub<WW8_CP>(rF.nLRes, rF.nSCode, nTmp))
+ {
+ rF.nLen = 0;
+ goto Err;
+ }
+ if (o3tl::checked_add<WW8_CP>(nTmp, 2, rF.nLen)) // nLRes is still the final position
+ {
+ rF.nLen = 0;
+ goto Err;
+ }
+ rF.nLRes -= rF.nSRes; // now: nLRes = length
+ if (o3tl::checked_add<WW8_CP>(rF.nSRes, 1, rF.nSRes)) // Endpos including Markers
+ {
+ rF.nLen = 0;
+ goto Err;
+ }
+ rF.nLRes--;
+ }else{
+ rF.nLRes = 0; // no result found
+ WW8_CP nTmp;
+ if (o3tl::checked_sub<WW8_CP>(rF.nSRes, rF.nSCode, nTmp))
+ {
+ rF.nLen = 0;
+ goto Err;
+ }
+ if (o3tl::checked_add<WW8_CP>(nTmp, 2, rF.nLen)) // total length
+ {
+ rF.nLen = 0;
+ goto Err;
+ }
+ }
+
+ if (rF.nLen < 0)
+ {
+ rF.nLen = 0;
+ goto Err;
+ }
+
+ rPLCF.advance();
+ if((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x15 )
+ {
+ // Field end ?
+ // INDEX-Field has set Bit7?
+ rF.nOpt = static_cast<sal_uInt8*>(pData)[1]; // yes -> copy flags
+ }else{
+ rF.nId = 0; // no -> Field invalid
+ }
+
+ rPLCF.SetIdx( nOldIdx );
+ return true;
+Err:
+ rPLCF.SetIdx( nOldIdx );
+ return false;
+}
+
+OUString read_uInt8_BeltAndBracesString(SvStream& rStrm, rtl_TextEncoding eEnc)
+{
+ const OUString aRet = read_uInt8_lenPrefixed_uInt8s_ToOUString(rStrm, eEnc);
+ rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end
+ return aRet;
+}
+
+OUString read_uInt16_BeltAndBracesString(SvStream& rStrm)
+{
+ const OUString aRet = read_uInt16_PascalString(rStrm);
+ rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end
+ return aRet;
+}
+
+sal_Int32 WW8ScannerBase::WW8ReadString( SvStream& rStrm, OUString& rStr,
+ WW8_CP nCurrentStartCp, tools::Long nTotalLen, rtl_TextEncoding eEnc ) const
+{
+ // Read in plain text, which can extend over several pieces
+ rStr.clear();
+
+ if (nCurrentStartCp < 0 || nTotalLen < 0)
+ return 0;
+
+ WW8_CP nBehindTextCp = nCurrentStartCp + nTotalLen;
+ WW8_CP nNextPieceCp = nBehindTextCp; // Initialization, important for Ver6
+ tools::Long nTotalRead = 0;
+ do
+ {
+ bool bIsUnicode(false), bPosOk(false);
+ WW8_FC fcAct = WW8Cp2Fc(nCurrentStartCp,&bIsUnicode,&nNextPieceCp,&bPosOk);
+
+ // Probably aimed beyond file end, doesn't matter!
+ if( !bPosOk )
+ break;
+
+ bool bValid = checkSeek(rStrm, fcAct);
+ if (!bValid)
+ break;
+
+ WW8_CP nEnd = (nNextPieceCp < nBehindTextCp) ? nNextPieceCp
+ : nBehindTextCp;
+ WW8_CP nLen;
+ const bool bFail = o3tl::checked_sub(nEnd, nCurrentStartCp, nLen);
+ if (bFail)
+ break;
+
+ if( 0 >= nLen )
+ break;
+
+ rStr += bIsUnicode
+ ? read_uInt16s_ToOUString(rStrm, nLen)
+ : read_uInt8s_ToOUString(rStrm, nLen, eEnc);
+
+ nTotalRead += nLen;
+ nCurrentStartCp += nLen;
+ if ( nTotalRead != rStr.getLength() )
+ break;
+ }
+ while( nTotalRead < nTotalLen );
+
+ return rStr.getLength();
+}
+
+WW8PLCFspecial::WW8PLCFspecial(SvStream* pSt, sal_uInt32 nFilePos,
+ sal_uInt32 nPLCF, sal_uInt32 nStruct)
+ : m_nIdx(0), m_nStru(nStruct)
+{
+ const sal_uInt32 nValidMin=4;
+
+ sal_uInt64 const nOldPos = pSt->Tell();
+
+ bool bValid = checkSeek(*pSt, nFilePos);
+ std::size_t nRemainingSize = pSt->remainingSize();
+ if( nRemainingSize < nValidMin || nPLCF < nValidMin )
+ bValid = false;
+ nPLCF = bValid ? std::min(nRemainingSize, static_cast<std::size_t>(nPLCF)) : nValidMin;
+
+ // Pointer to Pos- and Struct-array
+ m_pPLCF_PosArray.reset( new sal_Int32[ ( nPLCF + 3 ) / 4 ] );
+ m_pPLCF_PosArray[0] = 0;
+
+ nPLCF = bValid ? pSt->ReadBytes(m_pPLCF_PosArray.get(), nPLCF) : nValidMin;
+
+ nPLCF = std::max(nPLCF, nValidMin);
+
+ m_nIMax = ( nPLCF - 4 ) / ( 4 + nStruct );
+#ifdef OSL_BIGENDIAN
+ for( m_nIdx = 0; m_nIdx <= m_nIMax; m_nIdx++ )
+ m_pPLCF_PosArray[m_nIdx] = OSL_SWAPDWORD( m_pPLCF_PosArray[m_nIdx] );
+ m_nIdx = 0;
+#endif // OSL_BIGENDIAN
+ if( nStruct ) // Pointer to content array
+ m_pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&m_pPLCF_PosArray[m_nIMax + 1]);
+ else
+ m_pPLCF_Contents = nullptr; // no content
+
+ pSt->Seek(nOldPos);
+}
+
+// WW8PLCFspecial::SeekPos() sets WW8PLCFspecial to position nPos, while also the entry is used
+// that begins before nPos and ends after nPos.
+// Suitable for normal attributes. However, the beginning of the attribute is not corrected onto
+// the position nPos.
+bool WW8PLCFspecial::SeekPos(tools::Long nP)
+{
+ if( nP < m_pPLCF_PosArray[0] )
+ {
+ m_nIdx = 0;
+ return false; // Not found: nP less than smallest entry
+ }
+
+ // Search from beginning?
+ if ((m_nIdx < 1) || (nP < m_pPLCF_PosArray[m_nIdx - 1]))
+ m_nIdx = 1;
+
+ tools::Long nI = m_nIdx;
+ tools::Long nEnd = m_nIMax;
+
+ for(int n = (1==m_nIdx ? 1 : 2); n; --n )
+ {
+ for( ; nI <=nEnd; ++nI)
+ { // search with an index that is incremented by 1
+ if( nP < m_pPLCF_PosArray[nI] )
+ { // found position
+ m_nIdx = nI - 1; // nI - 1 is the correct index
+ return true; // done
+ }
+ }
+ nI = 1;
+ nEnd = m_nIdx-1;
+ }
+ m_nIdx = m_nIMax; // not found, greater than all entries
+ return false;
+}
+
+// WW8PLCFspecial::SeekPosExact() like SeekPos(), but it is ensured that no attribute is cut,
+// i.e. the next given attribute begins at or after nPos.
+// Is used for fields and bookmarks.
+bool WW8PLCFspecial::SeekPosExact(tools::Long nP)
+{
+ if( nP < m_pPLCF_PosArray[0] )
+ {
+ m_nIdx = 0;
+ return false; // Not found: nP less than smallest entry
+ }
+ // Search from beginning?
+ if( nP <=m_pPLCF_PosArray[m_nIdx] )
+ m_nIdx = 0;
+
+ tools::Long nI = m_nIdx ? m_nIdx-1 : 0;
+ tools::Long nEnd = m_nIMax;
+
+ for(int n = (0==m_nIdx ? 1 : 2); n; --n )
+ {
+ for( ; nI < nEnd; ++nI)
+ {
+ if( nP <=m_pPLCF_PosArray[nI] )
+ { // found position
+ m_nIdx = nI; // nI is the correct index
+ return true; // done
+ }
+ }
+ nI = 0;
+ nEnd = m_nIdx;
+ }
+ m_nIdx = m_nIMax; // Not found, greater than all entries
+ return false;
+}
+
+bool WW8PLCFspecial::Get(WW8_CP& rPos, void*& rpValue) const
+{
+ return GetData( m_nIdx, rPos, rpValue );
+}
+
+bool WW8PLCFspecial::GetData(tools::Long nInIdx, WW8_CP& rPos, void*& rpValue) const
+{
+ if ( nInIdx >= m_nIMax )
+ {
+ rPos = WW8_CP_MAX;
+ return false;
+ }
+ rPos = m_pPLCF_PosArray[nInIdx];
+ rpValue = m_pPLCF_Contents ? static_cast<void*>(&m_pPLCF_Contents[nInIdx * m_nStru]) : nullptr;
+ return true;
+}
+
+// WW8PLCF e.g. for SEPX
+// Ctor for *others* than Fkps
+// With nStartPos < 0, the first element of PLCFs will be taken
+WW8PLCF::WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct,
+ WW8_CP nStartPos) : m_nIdx(0), m_nStru(nStruct)
+{
+ if (nPLCF < 0)
+ {
+ SAL_WARN("sw.ww8", "broken WW8PLCF, ignoring");
+ nPLCF = 0;
+ }
+ else
+ m_nIMax = (nPLCF - 4) / (4 + nStruct);
+
+ ReadPLCF(rSt, nFilePos, nPLCF);
+
+ if( nStartPos >= 0 )
+ SeekPos( nStartPos );
+}
+
+// Ctor *only* for Fkps
+// The last 2 parameters are needed for PLCF.Chpx and PLCF.Papx.
+// If ncpN != 0, then an incomplete PLCF will be completed. This is always required for WW6 with
+// lack of resources and for WordPad (W95).
+// With nStartPos < 0, the first element of the PLCFs is taken.
+WW8PLCF::WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct,
+ WW8_CP nStartPos, sal_Int32 nPN, sal_Int32 ncpN): m_nIdx(0),
+ m_nStru(nStruct)
+{
+ if (nPLCF < 0)
+ {
+ SAL_WARN("sw.ww8", "broken WW8PLCF, ignoring");
+ m_nIMax = SAL_MAX_INT32;
+ }
+ else
+ m_nIMax = (nPLCF - 4) / (4 + nStruct);
+
+ if( m_nIMax >= ncpN )
+ ReadPLCF(rSt, nFilePos, nPLCF);
+ else
+ GeneratePLCF(rSt, nPN, ncpN);
+
+ if( nStartPos >= 0 )
+ SeekPos( nStartPos );
+}
+
+void WW8PLCF::ReadPLCF(SvStream& rSt, WW8_FC nFilePos, sal_uInt32 nPLCF)
+{
+ sal_uInt64 const nOldPos = rSt.Tell();
+ bool bValid = nPLCF != 0 && checkSeek(rSt, nFilePos)
+ && (rSt.remainingSize() >= nPLCF);
+
+ if (bValid)
+ {
+ // Pointer to Pos-array
+ const size_t nEntries = (nPLCF + 3) / 4;
+ m_pPLCF_PosArray.reset(new WW8_CP[nEntries]);
+ bValid = checkRead(rSt, m_pPLCF_PosArray.get(), nPLCF);
+ size_t nBytesAllocated = nEntries * sizeof(WW8_CP);
+ if (bValid && nPLCF != nBytesAllocated)
+ {
+ sal_uInt8* pStartBlock = reinterpret_cast<sal_uInt8*>(m_pPLCF_PosArray.get());
+ memset(pStartBlock + nPLCF, 0, nBytesAllocated - nPLCF);
+ }
+ }
+
+ if (bValid)
+ {
+#ifdef OSL_BIGENDIAN
+ for( m_nIdx = 0; m_nIdx <= m_nIMax; m_nIdx++ )
+ m_pPLCF_PosArray[m_nIdx] = OSL_SWAPDWORD( m_pPLCF_PosArray[m_nIdx] );
+ m_nIdx = 0;
+#endif // OSL_BIGENDIAN
+ // Pointer to content array
+ m_pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&m_pPLCF_PosArray[m_nIMax + 1]);
+
+ TruncToSortedRange();
+ }
+
+ OSL_ENSURE(bValid, "Document has corrupt PLCF, ignoring it");
+
+ if (!bValid)
+ MakeFailedPLCF();
+
+ rSt.Seek(nOldPos);
+}
+
+void WW8PLCF::MakeFailedPLCF()
+{
+ m_nIMax = 0;
+ m_pPLCF_PosArray.reset( new WW8_CP[2] );
+ m_pPLCF_PosArray[0] = m_pPLCF_PosArray[1] = WW8_CP_MAX;
+ m_pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&m_pPLCF_PosArray[m_nIMax + 1]);
+}
+
+namespace
+{
+ sal_Int32 TruncToSortedRange(const sal_Int32* pPLCF_PosArray, sal_Int32 nIMax)
+ {
+ //Docs state that: ... all Plcs ... are sorted in ascending order.
+ //So ensure that here for broken documents.
+ for (auto nI = 0; nI < nIMax; ++nI)
+ {
+ if (pPLCF_PosArray[nI] > pPLCF_PosArray[nI+1])
+ {
+ SAL_WARN("sw.ww8", "Document has unsorted PLCF, truncated to sorted portion");
+ nIMax = nI;
+ break;
+ }
+ }
+ return nIMax;
+ }
+}
+
+void WW8PLCFpcd::TruncToSortedRange()
+{
+ m_nIMax = ::TruncToSortedRange(m_pPLCF_PosArray.get(), m_nIMax);
+}
+
+void WW8PLCF::TruncToSortedRange()
+{
+ m_nIMax = ::TruncToSortedRange(m_pPLCF_PosArray.get(), m_nIMax);
+}
+
+void WW8PLCF::GeneratePLCF(SvStream& rSt, sal_Int32 nPN, sal_Int32 ncpN)
+{
+ OSL_ENSURE( m_nIMax < ncpN, "Pcl.Fkp: Why is PLCF too big?" );
+
+ bool failure = false;
+ m_nIMax = ncpN;
+
+ if ((m_nIMax < 1) || (m_nIMax > (WW8_CP_MAX - 4) / (4 + m_nStru)) || nPN < 0)
+ failure = true;
+
+ if (!failure)
+ {
+ // Check arguments to ShortToSVBT16 in loop below will all be valid:
+ sal_Int32 nResult;
+ failure = o3tl::checked_add(nPN, ncpN, nResult) || nResult > SAL_MAX_UINT16;
+ }
+
+ if (!failure)
+ {
+ size_t nSiz = (4 + m_nStru) * m_nIMax + 4;
+ size_t nElems = ( nSiz + 3 ) / 4;
+ m_pPLCF_PosArray.reset( new WW8_CP[ nElems ] ); // Pointer to Pos-array
+
+ for (sal_Int32 i = 0; i < ncpN && !failure; ++i)
+ {
+ failure = true;
+ // construct FC entries
+ // first FC entry of each Fkp
+ if (!checkSeek(rSt, (nPN + i) << 9))
+ break;
+
+ WW8_CP nFc(0);
+ rSt.ReadInt32( nFc );
+ m_pPLCF_PosArray[i] = nFc;
+
+ failure = bool(rSt.GetError());
+ }
+ }
+
+ if (!failure)
+ {
+ do
+ {
+ failure = true;
+
+ std::size_t nLastFkpPos = nPN + m_nIMax - 1;
+ nLastFkpPos = nLastFkpPos << 9;
+ // number of FC entries of last Fkp
+ if (!checkSeek(rSt, nLastFkpPos + 511))
+ break;
+
+ sal_uInt8 nb(0);
+ rSt.ReadUChar( nb );
+ // last FC entry of last Fkp
+ if (!checkSeek(rSt, nLastFkpPos + nb * 4))
+ break;
+
+ WW8_CP nFc(0);
+ rSt.ReadInt32( nFc );
+ m_pPLCF_PosArray[m_nIMax] = nFc; // end of the last Fkp
+
+ failure = bool(rSt.GetError());
+ } while(false);
+ }
+
+ if (!failure)
+ {
+ // Pointer to content array
+ m_pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&m_pPLCF_PosArray[m_nIMax + 1]);
+ sal_uInt8* p = m_pPLCF_Contents;
+
+ for (sal_Int32 i = 0; i < ncpN; ++i) // construct PNs
+ {
+ ShortToSVBT16(o3tl::narrowing<sal_uInt16>(nPN + i), p);
+ p += m_nStru;
+ }
+ }
+
+ SAL_WARN_IF(failure, "sw.ww8", "Document has corrupt PLCF, ignoring it");
+
+ if (failure)
+ MakeFailedPLCF();
+}
+
+bool WW8PLCF::SeekPos(WW8_CP nPos)
+{
+ WW8_CP nP = nPos;
+
+ if( nP < m_pPLCF_PosArray[0] )
+ {
+ m_nIdx = 0;
+ // not found: nPos less than smallest entry
+ return false;
+ }
+
+ // Search from beginning?
+ if ((m_nIdx < 1) || (nP < m_pPLCF_PosArray[m_nIdx - 1]))
+ m_nIdx = 1;
+
+ sal_Int32 nI = m_nIdx;
+ sal_Int32 nEnd = m_nIMax;
+
+ for(int n = (1==m_nIdx ? 1 : 2); n; --n )
+ {
+ for( ; nI <=nEnd; ++nI) // search with an index that is incremented by 1
+ {
+ if( nP < m_pPLCF_PosArray[nI] ) // found position
+ {
+ m_nIdx = nI - 1; // nI - 1 is the correct index
+ return true; // done
+ }
+ }
+ nI = 1;
+ nEnd = m_nIdx-1;
+ }
+
+ m_nIdx = m_nIMax; // not found, greater than all entries
+ return false;
+}
+
+bool WW8PLCF::Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const
+{
+ if ( m_nIdx >= m_nIMax )
+ {
+ rStart = rEnd = WW8_CP_MAX;
+ return false;
+ }
+ rStart = m_pPLCF_PosArray[ m_nIdx ];
+ rEnd = m_pPLCF_PosArray[ m_nIdx + 1 ];
+ rpValue = static_cast<void*>(&m_pPLCF_Contents[m_nIdx * m_nStru]);
+ return true;
+}
+
+WW8_CP WW8PLCF::Where() const
+{
+ if ( m_nIdx >= m_nIMax )
+ return WW8_CP_MAX;
+
+ return m_pPLCF_PosArray[m_nIdx];
+}
+
+WW8PLCFpcd::WW8PLCFpcd(SvStream* pSt, sal_uInt32 nFilePos,
+ sal_uInt32 nPLCF, sal_uInt32 nStruct)
+ : m_nStru( nStruct )
+{
+ const sal_uInt32 nValidMin=4;
+
+ sal_uInt64 const nOldPos = pSt->Tell();
+
+ bool bValid = checkSeek(*pSt, nFilePos);
+ std::size_t nRemainingSize = pSt->remainingSize();
+ if( nRemainingSize < nValidMin || nPLCF < nValidMin )
+ bValid = false;
+ nPLCF = bValid ? std::min(nRemainingSize, static_cast<std::size_t>(nPLCF)) : nValidMin;
+
+ m_pPLCF_PosArray.reset( new WW8_CP[ ( nPLCF + 3 ) / 4 ] ); // Pointer to Pos-array
+ m_pPLCF_PosArray[0] = 0;
+
+ nPLCF = bValid ? pSt->ReadBytes(m_pPLCF_PosArray.get(), nPLCF) : nValidMin;
+ nPLCF = std::max(nPLCF, nValidMin);
+
+ m_nIMax = ( nPLCF - 4 ) / ( 4 + nStruct );
+#ifdef OSL_BIGENDIAN
+ for( tools::Long nI = 0; nI <= m_nIMax; nI++ )
+ m_pPLCF_PosArray[nI] = OSL_SWAPDWORD( m_pPLCF_PosArray[nI] );
+#endif // OSL_BIGENDIAN
+
+ // Pointer to content array
+ m_pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&m_pPLCF_PosArray[m_nIMax + 1]);
+ TruncToSortedRange();
+
+ pSt->Seek( nOldPos );
+}
+
+// If nStartPos < 0, the first element of PLCFs will be taken
+WW8PLCFpcd_Iter::WW8PLCFpcd_Iter( WW8PLCFpcd& rPLCFpcd, tools::Long nStartPos )
+ :m_rPLCF( rPLCFpcd ), m_nIdx( 0 )
+{
+ if( nStartPos >= 0 )
+ SeekPos( nStartPos );
+}
+
+bool WW8PLCFpcd_Iter::SeekPos(tools::Long nPos)
+{
+ tools::Long nP = nPos;
+
+ if( nP < m_rPLCF.m_pPLCF_PosArray[0] )
+ {
+ m_nIdx = 0;
+ return false; // not found: nPos less than smallest entry
+ }
+ // Search from beginning?
+ if ((m_nIdx < 1) || (nP < m_rPLCF.m_pPLCF_PosArray[m_nIdx - 1]))
+ m_nIdx = 1;
+
+ tools::Long nI = m_nIdx;
+ tools::Long nEnd = m_rPLCF.m_nIMax;
+
+ for(int n = (1==m_nIdx ? 1 : 2); n; --n )
+ {
+ for( ; nI <=nEnd; ++nI)
+ { // search with an index that is incremented by 1
+ if( nP < m_rPLCF.m_pPLCF_PosArray[nI] )
+ { // found position
+ m_nIdx = nI - 1; // nI - 1 is the correct index
+ return true; // done
+ }
+ }
+ nI = 1;
+ nEnd = m_nIdx-1;
+ }
+ m_nIdx = m_rPLCF.m_nIMax; // not found, greater than all entries
+ return false;
+}
+
+bool WW8PLCFpcd_Iter::Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const
+{
+ if( m_nIdx >= m_rPLCF.m_nIMax )
+ {
+ rStart = rEnd = WW8_CP_MAX;
+ return false;
+ }
+ rStart = m_rPLCF.m_pPLCF_PosArray[m_nIdx];
+ rEnd = m_rPLCF.m_pPLCF_PosArray[m_nIdx + 1];
+ rpValue = static_cast<void*>(&m_rPLCF.m_pPLCF_Contents[m_nIdx * m_rPLCF.m_nStru]);
+ return true;
+}
+
+sal_Int32 WW8PLCFpcd_Iter::Where() const
+{
+ if ( m_nIdx >= m_rPLCF.m_nIMax )
+ return SAL_MAX_INT32;
+
+ return m_rPLCF.m_pPLCF_PosArray[m_nIdx];
+}
+
+bool WW8PLCFx_Fc_FKP::WW8Fkp::Entry::operator<
+ (const WW8PLCFx_Fc_FKP::WW8Fkp::Entry& rSecond) const
+{
+ return (mnFC < rSecond.mnFC);
+}
+
+static bool IsReplaceAllSprm(sal_uInt16 nSpId)
+{
+ return (NS_sprm::LN_PHugePapx == nSpId || 0x6646 == nSpId);
+}
+
+static bool IsExpandableSprm(sal_uInt16 nSpId)
+{
+ return 0x646B == nSpId;
+}
+
+void WW8PLCFx_Fc_FKP::WW8Fkp::FillEntry(WW8PLCFx_Fc_FKP::WW8Fkp::Entry &rEntry,
+ std::size_t nDataOffset, sal_uInt16 nLen)
+{
+ bool bValidPos = (nDataOffset < sizeof(maRawData));
+
+ OSL_ENSURE(bValidPos, "sprm sequence offset is out of range, ignoring");
+
+ if (!bValidPos)
+ {
+ rEntry.mnLen = 0;
+ return;
+ }
+
+ const sal_uInt16 nAvailableData = sizeof(maRawData)-nDataOffset;
+ OSL_ENSURE(nLen <= nAvailableData, "sprm sequence len is out of range, clipping");
+ rEntry.mnLen = std::min(nLen, nAvailableData);
+ rEntry.mpData = maRawData + nDataOffset;
+}
+
+WW8PLCFx_Fc_FKP::WW8Fkp::WW8Fkp(const WW8Fib& rFib, SvStream* pSt,
+ SvStream* pDataSt, tools::Long _nFilePos, tools::Long nItemSiz, ePLCFT ePl,
+ WW8_FC nStartFc)
+ : m_nItemSize(nItemSiz), m_nFilePos(_nFilePos), mnIdx(0), m_ePLCF(ePl)
+ , mnMustRemainCached(0), maSprmParser(rFib)
+{
+ memset(maRawData, 0, 512);
+
+ const ww::WordVersion eVersion = rFib.GetFIBVersion();
+
+ sal_uInt64 const nOldPos = pSt->Tell();
+
+ bool bCouldSeek = checkSeek(*pSt, m_nFilePos);
+ bool bCouldRead = bCouldSeek && checkRead(*pSt, maRawData, 512);
+
+ mnIMax = bCouldRead ? maRawData[511] : 0;
+
+ sal_uInt8 *pStart = maRawData;
+ // Offset-Location in maRawData
+ const size_t nRawDataStart = (mnIMax + 1) * 4;
+
+ for (mnIdx = 0; mnIdx < mnIMax; ++mnIdx)
+ {
+ const size_t nRawDataOffset = nRawDataStart + mnIdx * m_nItemSize;
+
+ //clip to available data, corrupt fkp
+ if (nRawDataOffset >= 511)
+ {
+ mnIMax = mnIdx;
+ break;
+ }
+
+ unsigned int nOfs = maRawData[nRawDataOffset] * 2;
+ // nOfs in [0..0xff*2=510]
+
+ Entry aEntry(Get_Long(pStart));
+
+ if (nOfs)
+ {
+ switch (m_ePLCF)
+ {
+ case CHP:
+ {
+ aEntry.mnLen = maRawData[nOfs];
+
+ //len byte
+ std::size_t nDataOffset = nOfs + 1;
+
+ FillEntry(aEntry, nDataOffset, aEntry.mnLen);
+
+ if (aEntry.mnLen && eVersion <= ww::eWW2)
+ {
+ Word2CHPX aChpx = ReadWord2Chpx(*pSt, m_nFilePos + nOfs + 1, static_cast< sal_uInt8 >(aEntry.mnLen));
+ std::vector<sal_uInt8> aSprms = ChpxToSprms(aChpx);
+ aEntry.mnLen = static_cast< sal_uInt16 >(aSprms.size());
+ if (aEntry.mnLen)
+ {
+ aEntry.mpData = new sal_uInt8[aEntry.mnLen];
+ memcpy(aEntry.mpData, aSprms.data(), aEntry.mnLen);
+ aEntry.mbMustDelete = true;
+ }
+ }
+ break;
+ }
+ case PAP:
+ {
+ sal_uInt8 nDelta = 0;
+
+ aEntry.mnLen = maRawData[nOfs];
+ if (IsEightPlus(eVersion) && !aEntry.mnLen)
+ {
+ aEntry.mnLen = maRawData[nOfs+1];
+ nDelta++;
+ }
+ aEntry.mnLen *= 2;
+
+ //stylecode, std/istd
+ if (eVersion <= ww::eWW2)
+ {
+ if (aEntry.mnLen >= 1)
+ {
+ aEntry.mnIStd = *(maRawData+nOfs+1+nDelta);
+ aEntry.mnLen--; //style code
+ if (aEntry.mnLen >= 6)
+ {
+ aEntry.mnLen-=6; //PHE
+ //skip stc, len byte + 6 byte PHE
+ unsigned int nOffset = nOfs + 8;
+ if (nOffset >= 511) //Bad offset
+ aEntry.mnLen=0;
+ if (aEntry.mnLen) //start is ok
+ {
+ if (nOffset + aEntry.mnLen > 512) //Bad end, clip
+ aEntry.mnLen = 512 - nOffset;
+ aEntry.mpData = maRawData + nOffset;
+ }
+ }
+ else
+ aEntry.mnLen=0; //Too short
+ }
+ }
+ else
+ {
+ if (aEntry.mnLen >= 2)
+ {
+ //len byte + optional extra len byte
+ std::size_t nDataOffset = nOfs + 1 + nDelta;
+ aEntry.mnIStd = nDataOffset <= sizeof(maRawData)-sizeof(aEntry.mnIStd) ?
+ SVBT16ToUInt16(maRawData+nDataOffset) : 0;
+ aEntry.mnLen-=2; //istd
+ if (aEntry.mnLen)
+ {
+ //additional istd
+ nDataOffset += sizeof(aEntry.mnIStd);
+
+ FillEntry(aEntry, nDataOffset, aEntry.mnLen);
+ }
+ }
+ else
+ aEntry.mnLen=0; //Too short, ignore
+ }
+
+ const sal_uInt16 nSpId = aEntry.mnLen
+ ? maSprmParser.GetSprmId(aEntry.mpData) : 0;
+
+ /*
+ If we replace then we throw away the old data, if we
+ are expanding, then we tack the old data onto the end
+ of the new data
+ */
+ const bool bExpand = IsExpandableSprm(nSpId);
+ const sal_uInt8* pStartData
+ = aEntry.mpData == nullptr ? nullptr : aEntry.mpData + 2;
+ const sal_uInt8* pLastValidDataPos = maRawData + 512 - sizeof(sal_uInt32);
+ if (pStartData != nullptr && pStartData > pLastValidDataPos)
+ pStartData = nullptr;
+ if ((IsReplaceAllSprm(nSpId) || bExpand) && pStartData)
+ {
+ sal_uInt64 nCurr = pDataSt->Tell();
+ sal_uInt32 nPos = SVBT32ToUInt32(pStartData);
+ sal_uInt16 nLen(0);
+
+ bool bOk = checkSeek(*pDataSt, nPos);
+ if (bOk)
+ {
+ pDataSt->ReadUInt16( nLen );
+ bOk = nLen <= pDataSt->remainingSize();
+ }
+
+ if (bOk)
+ {
+ const sal_uInt16 nOrigLen = bExpand ? aEntry.mnLen : 0;
+ sal_uInt8 *pOrigData = bExpand ? aEntry.mpData : nullptr;
+
+ aEntry.mnLen = nLen;
+ aEntry.mpData =
+ new sal_uInt8[aEntry.mnLen + nOrigLen];
+ aEntry.mbMustDelete = true;
+ aEntry.mnLen =
+ pDataSt->ReadBytes(aEntry.mpData, aEntry.mnLen);
+
+ pDataSt->Seek( nCurr );
+
+ if (pOrigData)
+ {
+ memcpy(aEntry.mpData + aEntry.mnLen,
+ pOrigData, nOrigLen);
+ aEntry.mnLen = aEntry.mnLen + nOrigLen;
+ }
+ }
+ }
+ }
+ break;
+ default:
+ OSL_FAIL("sweet god, what have you done!");
+ break;
+ }
+ }
+
+ maEntries.push_back(aEntry);
+ }
+
+ //one more FC than grrpl entries
+ maEntries.emplace_back(Get_Long(pStart));
+
+ //we expect them sorted, but it appears possible for them to arrive unsorted
+ std::stable_sort(maEntries.begin(), maEntries.end());
+
+ mnIdx = 0;
+
+ if (nStartFc >= 0)
+ SeekPos(nStartFc);
+
+ pSt->Seek(nOldPos);
+}
+
+WW8PLCFx_Fc_FKP::WW8Fkp::Entry::Entry(const Entry &rEntry)
+ : mnFC(rEntry.mnFC), mnLen(rEntry.mnLen), mnIStd(rEntry.mnIStd),
+ mbMustDelete(rEntry.mbMustDelete)
+{
+ if (mbMustDelete)
+ {
+ mpData = new sal_uInt8[mnLen];
+ memcpy(mpData, rEntry.mpData, mnLen);
+ }
+ else
+ mpData = rEntry.mpData;
+}
+
+WW8PLCFx_Fc_FKP::WW8Fkp::Entry&
+ WW8PLCFx_Fc_FKP::WW8Fkp::Entry::operator=(const Entry &rEntry)
+{
+ if (this == &rEntry)
+ return *this;
+
+ if (mbMustDelete)
+ delete[] mpData;
+
+ mnFC = rEntry.mnFC;
+ mnLen = rEntry.mnLen;
+ mnIStd = rEntry.mnIStd;
+ mbMustDelete = rEntry.mbMustDelete;
+
+ if (rEntry.mbMustDelete)
+ {
+ mpData = new sal_uInt8[mnLen];
+ memcpy(mpData, rEntry.mpData, mnLen);
+ }
+ else
+ mpData = rEntry.mpData;
+
+ return *this;
+}
+
+WW8PLCFx_Fc_FKP::WW8Fkp::Entry::~Entry()
+{
+ if (mbMustDelete)
+ delete[] mpData;
+}
+
+void WW8PLCFx_Fc_FKP::WW8Fkp::Reset(WW8_FC nFc)
+{
+ SetIdx(0);
+ if (nFc >= 0)
+ SeekPos(nFc);
+}
+
+bool WW8PLCFx_Fc_FKP::WW8Fkp::SeekPos(WW8_FC nFc)
+{
+ if (nFc < maEntries[0].mnFC)
+ {
+ mnIdx = 0;
+ return false; // not found: nPos less than smallest entry
+ }
+
+ // Search from beginning?
+ if ((mnIdx < 1) || (nFc < maEntries[mnIdx - 1].mnFC))
+ mnIdx = 1;
+
+ sal_uInt8 nI = mnIdx;
+ sal_uInt8 nEnd = mnIMax;
+
+ for(sal_uInt8 n = (1==mnIdx ? 1 : 2); n; --n )
+ {
+ for( ; nI <=nEnd; ++nI)
+ { // search with an index that is incremented by 1
+ if (nFc < maEntries[nI].mnFC)
+ { // found position
+ mnIdx = nI - 1; // nI - 1 is the correct index
+ return true; // done
+ }
+ }
+ nI = 1;
+ nEnd = mnIdx-1;
+ }
+ mnIdx = mnIMax; // not found, greater than all entries
+ return false;
+}
+
+sal_uInt8* WW8PLCFx_Fc_FKP::WW8Fkp::Get(WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen)
+ const
+{
+ rLen = 0;
+
+ if (mnIdx >= mnIMax)
+ {
+ rStart = WW8_FC_MAX;
+ return nullptr;
+ }
+
+ rStart = maEntries[mnIdx].mnFC;
+ rEnd = maEntries[mnIdx + 1].mnFC;
+
+ sal_uInt8* pSprms = GetLenAndIStdAndSprms( rLen );
+ return pSprms;
+}
+
+void WW8PLCFx_Fc_FKP::WW8Fkp::SetIdx(sal_uInt8 nI)
+{
+ if (nI < mnIMax)
+ {
+ mnIdx = nI;
+ }
+}
+
+sal_uInt8* WW8PLCFx_Fc_FKP::WW8Fkp::GetLenAndIStdAndSprms(sal_Int32& rLen) const
+{
+ rLen = maEntries[mnIdx].mnLen;
+ return maEntries[mnIdx].mpData;
+}
+
+SprmResult WW8PLCFx_Fc_FKP::WW8Fkp::HasSprm( sal_uInt16 nId, bool bFindFirst )
+{
+ if (mnIdx >= mnIMax)
+ return SprmResult();
+
+ sal_Int32 nLen;
+ sal_uInt8* pSprms = GetLenAndIStdAndSprms( nLen );
+
+ WW8SprmIter aIter(pSprms, nLen, maSprmParser);
+ return aIter.FindSprm(nId, bFindFirst);
+}
+
+void WW8PLCFx_Fc_FKP::WW8Fkp::HasSprm(sal_uInt16 nId,
+ std::vector<SprmResult> &rResult)
+{
+ if (mnIdx >= mnIMax)
+ return;
+
+ sal_Int32 nLen;
+ sal_uInt8* pSprms = GetLenAndIStdAndSprms( nLen );
+
+ WW8SprmIter aIter(pSprms, nLen, maSprmParser);
+
+ while(aIter.GetSprms())
+ {
+ if (aIter.GetCurrentId() == nId)
+ {
+ sal_Int32 nFixedLen = maSprmParser.DistanceToData(nId);
+ sal_Int32 nL = maSprmParser.GetSprmSize(nId, aIter.GetSprms(), aIter.GetRemLen());
+ rResult.emplace_back(aIter.GetCurrentParams(), nL - nFixedLen);
+ }
+ aIter.advance();
+ };
+}
+
+ww::WordVersion WW8PLCFx::GetFIBVersion() const
+{
+ return mrFib.GetFIBVersion();
+}
+
+void WW8PLCFx::GetSprms( WW8PLCFxDesc* p )
+{
+ OSL_ENSURE( false, "Called wrong GetSprms" );
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ p->bRealLineEnd = false;
+}
+
+tools::Long WW8PLCFx::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen )
+{
+ OSL_ENSURE( false, "Called wrong GetNoSprms" );
+ rStart = rEnd = WW8_CP_MAX;
+ rLen = 0;
+ return 0;
+}
+
+// ...Idx2: Default: ignore
+sal_uInt32 WW8PLCFx::GetIdx2() const
+{
+ return 0;
+}
+
+void WW8PLCFx::SetIdx2(sal_uInt32)
+{
+}
+
+namespace {
+
+class SamePos
+{
+private:
+ tools::Long mnPo;
+public:
+ explicit SamePos(tools::Long nPo) : mnPo(nPo) {}
+ bool operator()(const std::unique_ptr<WW8PLCFx_Fc_FKP::WW8Fkp>& pFkp)
+ {return mnPo == pFkp->GetFilePos();}
+};
+
+}
+
+bool WW8PLCFx_Fc_FKP::NewFkp()
+{
+ WW8_CP nPLCFStart, nPLCFEnd;
+ void* pPage;
+
+ static const int WW8FkpSizeTabVer2[ PLCF_END ] =
+ {
+ 1, 1, 0 /*, 0, 0, 0*/
+ };
+ static const int WW8FkpSizeTabVer6[ PLCF_END ] =
+ {
+ 1, 7, 0 /*, 0, 0, 0*/
+ };
+ static const int WW8FkpSizeTabVer8[ PLCF_END ] =
+ {
+ 1, 13, 0 /*, 0, 0, 0*/
+ };
+ const int* pFkpSizeTab;
+
+ switch (GetFIBVersion())
+ {
+ case ww::eWW1:
+ case ww::eWW2:
+ pFkpSizeTab = WW8FkpSizeTabVer2;
+ break;
+ case ww::eWW6:
+ case ww::eWW7:
+ pFkpSizeTab = WW8FkpSizeTabVer6;
+ break;
+ case ww::eWW8:
+ pFkpSizeTab = WW8FkpSizeTabVer8;
+ break;
+ default:
+ // program error!
+ OSL_ENSURE( false, "nVersion not implemented!" );
+ return false;
+ }
+
+ if (!m_pPLCF->Get( nPLCFStart, nPLCFEnd, pPage ))
+ {
+ m_pFkp = nullptr;
+ return false; // PLCF completely processed
+ }
+ m_pPLCF->advance();
+ tools::Long nPo = SVBT16ToUInt16( static_cast<sal_uInt8 *>(pPage) );
+ nPo <<= 9; // shift as LONG
+
+ tools::Long nCurrentFkpFilePos = m_pFkp ? m_pFkp->GetFilePos() : -1;
+ if (nCurrentFkpFilePos == nPo)
+ m_pFkp->Reset(GetStartFc());
+ else
+ {
+ auto aIter =
+ std::find_if(maFkpCache.begin(), maFkpCache.end(), SamePos(nPo));
+ if (aIter != maFkpCache.end())
+ {
+ m_pFkp = aIter->get();
+ m_pFkp->Reset(GetStartFc());
+ }
+ else
+ {
+ m_pFkp = new WW8Fkp(GetFIB(), m_pFKPStrm, m_pDataStrm, nPo,
+ pFkpSizeTab[ m_ePLCF ], m_ePLCF, GetStartFc());
+ maFkpCache.push_back(std::unique_ptr<WW8Fkp>(m_pFkp));
+
+ if (maFkpCache.size() > eMaxCache)
+ {
+ WW8Fkp* pCachedFkp = maFkpCache.front().get();
+ if (!pCachedFkp->IsMustRemainCache())
+ {
+ maFkpCache.pop_front();
+ }
+ }
+ }
+ }
+
+ SetStartFc( -1 ); // only the first time
+ return true;
+}
+
+WW8PLCFx_Fc_FKP::WW8PLCFx_Fc_FKP(SvStream* pSt, SvStream* pTableSt,
+ SvStream* pDataSt, const WW8Fib& rFib, ePLCFT ePl, WW8_FC nStartFcL)
+ : WW8PLCFx(rFib, true), m_pFKPStrm(pSt), m_pDataStrm(pDataSt)
+ , m_pFkp(nullptr), m_ePLCF(ePl)
+{
+ SetStartFc(nStartFcL);
+ tools::Long nLenStruct = (8 > rFib.m_nVersion) ? 2 : 4;
+ if (ePl == CHP)
+ {
+ m_pPLCF.reset(new WW8PLCF(*pTableSt, rFib.m_fcPlcfbteChpx, rFib.m_lcbPlcfbteChpx,
+ nLenStruct, GetStartFc(), rFib.m_pnChpFirst, rFib.m_cpnBteChp));
+ }
+ else
+ {
+ m_pPLCF.reset(new WW8PLCF(*pTableSt, rFib.m_fcPlcfbtePapx, rFib.m_lcbPlcfbtePapx,
+ nLenStruct, GetStartFc(), rFib.m_pnPapFirst, rFib.m_cpnBtePap));
+ }
+}
+
+WW8PLCFx_Fc_FKP::~WW8PLCFx_Fc_FKP()
+{
+ maFkpCache.clear();
+ m_pPLCF.reset();
+ m_pPCDAttrs.reset();
+}
+
+sal_uInt32 WW8PLCFx_Fc_FKP::GetIdx() const
+{
+ sal_uInt32 u = m_pPLCF->GetIdx() << 8;
+ if (m_pFkp)
+ u |= m_pFkp->GetIdx();
+ return u;
+}
+
+void WW8PLCFx_Fc_FKP::SetIdx(sal_uInt32 nIdx)
+{
+ if( !( nIdx & 0xffffff00L ) )
+ {
+ m_pPLCF->SetIdx( nIdx >> 8 );
+ m_pFkp = nullptr;
+ }
+ else
+ { // there was a Fkp
+ // Set PLCF one position back to retrieve the address of the Fkp
+ m_pPLCF->SetIdx( ( nIdx >> 8 ) - 1 );
+ if (NewFkp()) // read Fkp again
+ {
+ sal_uInt8 nFkpIdx = static_cast<sal_uInt8>(nIdx & 0xff);
+ m_pFkp->SetIdx(nFkpIdx); // set Fkp-Pos again
+ }
+ }
+}
+
+bool WW8PLCFx_Fc_FKP::SeekPos(WW8_FC nFcPos)
+{
+ // StartPos for next Where()
+ SetStartFc( nFcPos );
+
+ // find StartPos for next pPLCF->Get()
+ bool bRet = m_pPLCF->SeekPos(nFcPos);
+
+ // make FKP invalid?
+ WW8_CP nPLCFStart, nPLCFEnd;
+ void* pPage;
+ if( m_pFkp && m_pPLCF->Get( nPLCFStart, nPLCFEnd, pPage ) )
+ {
+ tools::Long nPo = SVBT16ToUInt16( static_cast<sal_uInt8 *>(pPage) );
+ nPo <<= 9; // shift as LONG
+ if (nPo != m_pFkp->GetFilePos())
+ m_pFkp = nullptr;
+ else
+ m_pFkp->SeekPos( nFcPos );
+ }
+ return bRet;
+}
+
+WW8_FC WW8PLCFx_Fc_FKP::Where()
+{
+ if( !m_pFkp && !NewFkp() )
+ return WW8_FC_MAX;
+ WW8_FC nP = m_pFkp ? m_pFkp->Where() : WW8_FC_MAX;
+ if( nP != WW8_FC_MAX )
+ return nP;
+
+ m_pFkp = nullptr; // FKP finished -> get new
+ return Where(); // easiest way: do it recursively
+}
+
+sal_uInt8* WW8PLCFx_Fc_FKP::GetSprmsAndPos(WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen)
+{
+ rLen = 0; // Default
+ rStart = rEnd = WW8_FC_MAX;
+
+ if( !m_pFkp ) // Fkp not there ?
+ {
+ if( !NewFkp() )
+ return nullptr;
+ }
+
+ sal_uInt8* pPos = m_pFkp ? m_pFkp->Get( rStart, rEnd, rLen ) : nullptr;
+ if( rStart == WW8_FC_MAX ) //Not found
+ return nullptr;
+ return pPos;
+}
+
+void WW8PLCFx_Fc_FKP::advance()
+{
+ if( !m_pFkp && !NewFkp() )
+ return;
+
+ if (!m_pFkp)
+ return;
+
+ m_pFkp->advance();
+ if( m_pFkp->Where() == WW8_FC_MAX )
+ (void)NewFkp();
+}
+
+sal_uInt16 WW8PLCFx_Fc_FKP::GetIstd() const
+{
+ return m_pFkp ? m_pFkp->GetIstd() : 0xFFFF;
+}
+
+void WW8PLCFx_Fc_FKP::GetPCDSprms( WW8PLCFxDesc& rDesc )
+{
+ rDesc.pMemPos = nullptr;
+ rDesc.nSprmsLen = 0;
+ if( m_pPCDAttrs )
+ {
+ if( !m_pFkp )
+ {
+ OSL_FAIL("+Problem: GetPCDSprms: NewFkp necessary (not possible!)" );
+ if( !NewFkp() )
+ return;
+ }
+ m_pPCDAttrs->GetSprms(&rDesc);
+ }
+}
+
+SprmResult WW8PLCFx_Fc_FKP::HasSprm(sal_uInt16 nId, bool bFindFirst)
+{
+ // const would be nicer, but for that, NewFkp() would need to be replaced or eliminated
+ if( !m_pFkp )
+ {
+ OSL_FAIL( "+Motz: HasSprm: NewFkp needed ( no const possible )" );
+ // happens in BugDoc 31722
+ if( !NewFkp() )
+ return SprmResult();
+ }
+
+ if (!m_pFkp)
+ return SprmResult();
+
+ SprmResult aRes = m_pFkp->HasSprm(nId, bFindFirst);
+
+ if (!aRes.pSprm)
+ {
+ WW8PLCFxDesc aDesc;
+ GetPCDSprms( aDesc );
+
+ if (aDesc.pMemPos)
+ {
+ WW8SprmIter aIter(aDesc.pMemPos, aDesc.nSprmsLen,
+ m_pFkp->GetSprmParser());
+ aRes = aIter.FindSprm(nId, bFindFirst);
+ }
+ }
+
+ return aRes;
+}
+
+void WW8PLCFx_Fc_FKP::HasSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult)
+{
+ // const would be nicer, but for that, NewFkp() would need to be replaced or eliminated
+ if (!m_pFkp)
+ {
+ OSL_FAIL( "+Motz: HasSprm: NewFkp needed ( no const possible )" );
+ // happens in BugDoc 31722
+ if( !NewFkp() )
+ return;
+ }
+
+ if (!m_pFkp)
+ return;
+
+ m_pFkp->HasSprm(nId, rResult);
+
+ WW8PLCFxDesc aDesc;
+ GetPCDSprms( aDesc );
+
+ if (!aDesc.pMemPos)
+ return;
+
+ const wwSprmParser &rSprmParser = m_pFkp->GetSprmParser();
+ WW8SprmIter aIter(aDesc.pMemPos, aDesc.nSprmsLen, rSprmParser);
+ while(aIter.GetSprms())
+ {
+ if (aIter.GetCurrentId() == nId)
+ {
+ sal_Int32 nFixedLen = rSprmParser.DistanceToData(nId);
+ sal_Int32 nL = rSprmParser.GetSprmSize(nId, aIter.GetSprms(), aIter.GetRemLen());
+ rResult.emplace_back(aIter.GetCurrentParams(), nL - nFixedLen);
+ }
+ aIter.advance();
+ };
+}
+
+WW8PLCFx_Cp_FKP::WW8PLCFx_Cp_FKP( SvStream* pSt, SvStream* pTableSt,
+ SvStream* pDataSt, const WW8ScannerBase& rBase, ePLCFT ePl )
+ : WW8PLCFx_Fc_FKP(pSt, pTableSt, pDataSt, *rBase.m_pWw8Fib, ePl,
+ rBase.WW8Cp2Fc(0)), m_rSBase(rBase), m_nAttrStart(-1), m_nAttrEnd(-1),
+ m_bLineEnd(false),
+ m_bComplex( (7 < rBase.m_pWw8Fib->m_nVersion) || rBase.m_pWw8Fib->m_fComplex )
+{
+ ResetAttrStartEnd();
+
+ if (m_rSBase.m_pPiecePLCF)
+ m_pPcd.reset( new WW8PLCFx_PCD(GetFIB(), rBase.m_pPiecePLCF.get(), 0, IsSevenMinus(GetFIBVersion())) );
+
+ /*
+ Make a copy of the piece attributes for so that the calls to HasSprm on a
+ Fc_FKP will be able to take into account the current piece attributes,
+ despite the fact that such attributes can only be found through a cp based
+ mechanism.
+ */
+ if (m_pPcd)
+ {
+ m_pPCDAttrs.reset( m_rSBase.m_pPLCFx_PCDAttrs ? new WW8PLCFx_PCDAttrs(
+ *m_rSBase.m_pWw8Fib, m_pPcd.get(), &m_rSBase) : nullptr);
+ }
+
+ m_pPieceIter = m_rSBase.m_pPieceIter.get();
+}
+
+WW8PLCFx_Cp_FKP::~WW8PLCFx_Cp_FKP()
+{
+}
+
+void WW8PLCFx_Cp_FKP::ResetAttrStartEnd()
+{
+ m_nAttrStart = -1;
+ m_nAttrEnd = -1;
+ m_bLineEnd = false;
+}
+
+sal_uInt32 WW8PLCFx_Cp_FKP::GetPCDIdx() const
+{
+ return m_pPcd ? m_pPcd->GetIdx() : 0;
+}
+
+bool WW8PLCFx_Cp_FKP::SeekPos(WW8_CP nCpPos)
+{
+ if( m_pPcd ) // Complex
+ {
+ if( !m_pPcd->SeekPos( nCpPos ) ) // set piece
+ return false;
+ if (m_pPCDAttrs && !m_pPCDAttrs->GetIter()->SeekPos(nCpPos))
+ return false;
+ return WW8PLCFx_Fc_FKP::SeekPos(m_pPcd->CurrentPieceStartCp2Fc(nCpPos));
+ }
+ // NO piece table !!!
+ return WW8PLCFx_Fc_FKP::SeekPos( m_rSBase.WW8Cp2Fc(nCpPos) );
+}
+
+WW8_CP WW8PLCFx_Cp_FKP::Where()
+{
+ WW8_FC nFc = WW8PLCFx_Fc_FKP::Where();
+ if( m_pPcd )
+ return m_pPcd->CurrentPieceStartFc2Cp( nFc ); // identify piece
+ return m_rSBase.WW8Fc2Cp( nFc ); // NO piece table !!!
+}
+
+void WW8PLCFx_Cp_FKP::GetSprms(WW8PLCFxDesc* p)
+{
+ WW8_CP nOrigCp = p->nStartPos;
+
+ if (!GetDirty()) //Normal case
+ {
+ p->pMemPos = WW8PLCFx_Fc_FKP::GetSprmsAndPos(p->nStartPos, p->nEndPos,
+ p->nSprmsLen);
+ }
+ else
+ {
+ /*
+ For the odd case where we have a location in a fastsaved file which
+ does not have an entry in the FKP, perhaps its para end is in the next
+ piece, or perhaps the cp just doesn't exist at all in this document.
+ AdvSprm doesn't know so it sets the PLCF as dirty and we figure out
+ in this method what the situation is
+
+ It doesn't exist then the piece iterator will not be able to find it.
+ Otherwise our cool fastsave algorithm can be brought to bear on the
+ problem.
+ */
+ if( !m_pPieceIter )
+ return;
+ const sal_uInt32 nOldPos = m_pPieceIter->GetIdx();
+ bool bOk = m_pPieceIter->SeekPos(nOrigCp);
+ m_pPieceIter->SetIdx(nOldPos);
+ if (!bOk)
+ return;
+ }
+
+ if( m_pPcd ) // piece table available
+ {
+ // Init ( no ++ called, yet )
+ if( (m_nAttrStart > m_nAttrEnd) || (m_nAttrStart == -1) )
+ {
+ p->bRealLineEnd = (m_ePLCF == PAP);
+
+ if ( ((m_ePLCF == PAP ) || (m_ePLCF == CHP)) && (nOrigCp != WW8_CP_MAX) )
+ {
+ bool bIsUnicode=false;
+ /*
+ To find the end of a paragraph for a character in a
+ complex format file.
+
+ It is necessary to know the piece that contains the
+ character and the FC assigned to the character.
+ */
+
+ //We set the piece iterator to the piece that contains the
+ //character, now we have the correct piece for this character
+ sal_uInt32 nOldPos = m_pPieceIter->GetIdx();
+ p->nStartPos = nOrigCp;
+ m_pPieceIter->SeekPos( p->nStartPos);
+
+ //This is the FC assigned to the character, but we already
+ //have the result of the next stage, so we can skip this step
+ //WW8_FC nStartFc = rSBase.WW8Cp2Fc(p->nStartPos, &bIsUnicode);
+
+ /*
+ Using the FC of the character, first search the FKP that
+ describes the character to find the smallest FC in the rgfc
+ that is larger than the character FC.
+ */
+ //But the search has already been done, the next largest FC is
+ //p->nEndPos.
+ WW8_FC nOldEndPos = p->nEndPos;
+
+ /*
+ If the FC found in the FKP is less than or equal to the limit
+ FC of the piece, the end of the paragraph that contains the
+ character is at the FKP FC minus 1.
+ */
+ WW8_CP nCpStart, nCpEnd;
+ void* pData=nullptr;
+ bool bOk = m_pPieceIter->Get(nCpStart, nCpEnd, pData);
+
+ if (!bOk)
+ {
+ m_pPieceIter->SetIdx(nOldPos);
+ return;
+ }
+
+ WW8_FC nLimitFC = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc );
+ WW8_FC nBeginLimitFC = nLimitFC;
+ if (IsEightPlus(GetFIBVersion()))
+ {
+ nBeginLimitFC =
+ WW8PLCFx_PCD::TransformPieceAddress(nLimitFC,
+ bIsUnicode);
+ }
+
+ WW8_CP nCpLen;
+ bool bFail = o3tl::checked_sub(nCpEnd, nCpStart, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ m_pPieceIter->SetIdx(nOldPos);
+ return;
+ }
+
+ if (bIsUnicode)
+ {
+ bFail = o3tl::checked_multiply<WW8_CP>(nCpLen, 2, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ m_pPieceIter->SetIdx(nOldPos);
+ return;
+ }
+ }
+
+ bFail = o3tl::checked_add(nBeginLimitFC, nCpLen, nLimitFC);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ m_pPieceIter->SetIdx(nOldPos);
+ return;
+ }
+
+ if (nOldEndPos <= nLimitFC)
+ {
+ bFail = o3tl::checked_sub(nLimitFC, nOldEndPos, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ m_pPieceIter->SetIdx(nOldPos);
+ return;
+ }
+
+ nCpLen /= (bIsUnicode ? 2 : 1);
+
+ bFail = o3tl::checked_sub(nCpEnd, nCpLen, p->nEndPos);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ m_pPieceIter->SetIdx(nOldPos);
+ return;
+ }
+ }
+ else
+ {
+ p->nEndPos = nCpEnd;
+ if (m_ePLCF != CHP)
+ {
+ /*
+ If the FKP FC that was found was greater than the FC
+ of the end of the piece, scan piece by piece toward
+ the end of the document until a piece is found that
+ contains a paragraph end mark.
+ */
+
+ /*
+ It's possible to check if a piece contains a paragraph
+ mark by using the FC of the beginning of the piece to
+ search in the FKPs for the smallest FC in the FKP rgfc
+ that is greater than the FC of the beginning of the
+ piece. If the FC found is less than or equal to the
+ limit FC of the piece, then the character that ends
+ the paragraph is the character immediately before the
+ FKP fc
+ */
+
+ m_pPieceIter->advance();
+
+ for (;m_pPieceIter->GetIdx() < m_pPieceIter->GetIMax();
+ m_pPieceIter->advance())
+ {
+ if( !m_pPieceIter->Get( nCpStart, nCpEnd, pData ) )
+ {
+ OSL_ENSURE( false, "piece iter broken!" );
+ break;
+ }
+ bIsUnicode = false;
+ sal_Int32 nFcStart=SVBT32ToUInt32(static_cast<WW8_PCD*>(pData)->fc);
+
+ if (IsEightPlus(GetFIBVersion()))
+ {
+ nFcStart =
+ WW8PLCFx_PCD::TransformPieceAddress(
+ nFcStart,bIsUnicode );
+ }
+
+ bFail = o3tl::checked_sub(nCpEnd, nCpStart, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ continue;
+ }
+
+ if (bIsUnicode)
+ {
+ bFail = o3tl::checked_multiply<WW8_CP>(nCpLen, 2, nCpLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ continue;
+ }
+ }
+
+ bFail = o3tl::checked_add(nFcStart, nCpLen, nLimitFC);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ continue;
+ }
+
+ //if it doesn't exist, skip it
+ if (!SeekPos(nCpStart))
+ continue;
+
+ WW8_FC nOne,nSmallest;
+ p->pMemPos = WW8PLCFx_Fc_FKP::GetSprmsAndPos(nOne,
+ nSmallest, p->nSprmsLen);
+
+ if (nSmallest <= nLimitFC)
+ {
+ WW8_CP nCpDiff;
+ bFail = o3tl::checked_sub(nLimitFC, nSmallest, nCpDiff);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ continue;
+ }
+ if (bIsUnicode)
+ nCpDiff /= 2;
+
+ WW8_CP nEndPos;
+ bFail = o3tl::checked_sub(nCpEnd, nCpDiff, nEndPos);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nStartPos = p->nEndPos = WW8_FC_MAX;
+ continue;
+ }
+
+ OSL_ENSURE(nEndPos >= p->nStartPos, "EndPos before StartPos");
+
+ if (nEndPos >= p->nStartPos)
+ p->nEndPos = nEndPos;
+
+ break;
+ }
+ }
+ }
+ }
+ m_pPieceIter->SetIdx( nOldPos );
+ }
+ else
+ WW8PLCFx_PCD::CurrentPieceFc2Cp( p->nStartPos, p->nEndPos,&m_rSBase );
+ }
+ else
+ {
+ p->nStartPos = m_nAttrStart;
+ p->nEndPos = m_nAttrEnd;
+ p->bRealLineEnd = m_bLineEnd;
+ }
+ }
+ else // NO piece table !!!
+ {
+ p->nStartPos = m_rSBase.WW8Fc2Cp( p->nStartPos );
+ p->nEndPos = m_rSBase.WW8Fc2Cp( p->nEndPos );
+ p->bRealLineEnd = m_ePLCF == PAP;
+ }
+}
+
+void WW8PLCFx_Cp_FKP::advance()
+{
+ WW8PLCFx_Fc_FKP::advance();
+ // !pPcd: emergency break
+ if ( !m_bComplex || !m_pPcd )
+ return;
+
+ if( GetPCDIdx() >= m_pPcd->GetIMax() ) // End of PLCF
+ {
+ m_nAttrStart = m_nAttrEnd = WW8_CP_MAX;
+ return;
+ }
+
+ sal_Int32 nFkpLen; // Fkp entry
+ // get Fkp entry
+ WW8PLCFx_Fc_FKP::GetSprmsAndPos(m_nAttrStart, m_nAttrEnd, nFkpLen);
+
+ WW8PLCFx_PCD::CurrentPieceFc2Cp( m_nAttrStart, m_nAttrEnd, &m_rSBase );
+ m_bLineEnd = (m_ePLCF == PAP);
+}
+
+WW8PLCFx_SEPX::WW8PLCFx_SEPX(SvStream* pSt, SvStream* pTableSt,
+ const WW8Fib& rFib, WW8_CP nStartCp)
+ : WW8PLCFx(rFib, true), maSprmParser(rFib),
+ m_pStrm(pSt), m_nArrMax(256), m_nSprmSiz(0)
+{
+ if (rFib.m_lcbPlcfsed)
+ m_pPLCF.reset( new WW8PLCF(*pTableSt, rFib.m_fcPlcfsed, rFib.m_lcbPlcfsed,
+ GetFIBVersion() <= ww::eWW2 ? 6 : 12, nStartCp) );
+
+ m_pSprms.reset( new sal_uInt8[m_nArrMax] ); // maximum length
+}
+
+WW8PLCFx_SEPX::~WW8PLCFx_SEPX()
+{
+}
+
+sal_uInt32 WW8PLCFx_SEPX::GetIdx() const
+{
+ return m_pPLCF ? m_pPLCF->GetIdx() : 0;
+}
+
+void WW8PLCFx_SEPX::SetIdx(sal_uInt32 nIdx)
+{
+ if( m_pPLCF ) m_pPLCF->SetIdx( nIdx );
+}
+
+bool WW8PLCFx_SEPX::SeekPos(WW8_CP nCpPos)
+{
+ return m_pPLCF && m_pPLCF->SeekPos( nCpPos );
+}
+
+WW8_CP WW8PLCFx_SEPX::Where()
+{
+ return m_pPLCF ? m_pPLCF->Where() : 0;
+}
+
+void WW8PLCFx_SEPX::GetSprms(WW8PLCFxDesc* p)
+{
+ if( !m_pPLCF ) return;
+
+ void* pData;
+
+ p->bRealLineEnd = false;
+ if (!m_pPLCF->Get( p->nStartPos, p->nEndPos, pData ))
+ {
+ p->nStartPos = p->nEndPos = WW8_CP_MAX; // PLCF completely processed
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ }
+ else
+ {
+ sal_uInt32 nPo = SVBT32ToUInt32( static_cast<sal_uInt8*>(pData)+2 );
+ if (nPo == 0xFFFFFFFF || !checkSeek(*m_pStrm, nPo))
+ {
+ p->nStartPos = p->nEndPos = WW8_CP_MAX; // Sepx empty
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ }
+ else
+ {
+ // read len
+ if (GetFIBVersion() <= ww::eWW2) // eWW6 ?, docs say yes, but...
+ {
+ sal_uInt8 nSiz(0);
+ m_pStrm->ReadUChar( nSiz );
+ m_nSprmSiz = nSiz;
+ }
+ else
+ {
+ m_pStrm->ReadUInt16( m_nSprmSiz );
+ }
+
+ std::size_t nRemaining = m_pStrm->remainingSize();
+ if (m_nSprmSiz > nRemaining)
+ m_nSprmSiz = nRemaining;
+
+ if( m_nSprmSiz > m_nArrMax )
+ { // does not fit
+ m_nArrMax = m_nSprmSiz; // Get more memory
+ m_pSprms.reset( new sal_uInt8[m_nArrMax] );
+ }
+ m_nSprmSiz = m_pStrm->ReadBytes(m_pSprms.get(), m_nSprmSiz); // read Sprms
+
+ p->nSprmsLen = m_nSprmSiz;
+ p->pMemPos = m_pSprms.get(); // return Position
+ }
+ }
+}
+
+void WW8PLCFx_SEPX::advance()
+{
+ if (m_pPLCF)
+ m_pPLCF->advance();
+}
+
+SprmResult WW8PLCFx_SEPX::HasSprm(sal_uInt16 nId) const
+{
+ return HasSprm(nId, m_pSprms.get(), m_nSprmSiz);
+}
+
+SprmResult WW8PLCFx_SEPX::HasSprm( sal_uInt16 nId, const sal_uInt8* pOtherSprms,
+ tools::Long nOtherSprmSiz ) const
+{
+ SprmResult aRet;
+ if (m_pPLCF)
+ {
+ WW8SprmIter aIter(pOtherSprms, nOtherSprmSiz, maSprmParser);
+ aRet = aIter.FindSprm(nId, /*bFindFirst=*/true);
+ }
+ return aRet;
+}
+
+bool WW8PLCFx_SEPX::Find4Sprms(sal_uInt16 nId1,sal_uInt16 nId2,sal_uInt16 nId3,sal_uInt16 nId4,
+ SprmResult& r1, SprmResult& r2, SprmResult& r3, SprmResult& r4) const
+{
+ if( !m_pPLCF )
+ return false;
+
+ bool bFound = false;
+
+ sal_uInt8* pSp = m_pSprms.get();
+ size_t i = 0;
+ while (i + maSprmParser.MinSprmLen() <= m_nSprmSiz)
+ {
+ // Sprm found?
+ const sal_uInt16 nCurrentId = maSprmParser.GetSprmId(pSp);
+ sal_Int32 nRemLen = m_nSprmSiz - i;
+ const sal_Int32 x = maSprmParser.GetSprmSize(nCurrentId, pSp, nRemLen);
+ bool bValid = x <= nRemLen;
+ if (!bValid)
+ {
+ SAL_WARN("sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong");
+ break;
+ }
+ bool bOk = true;
+ if( nCurrentId == nId1 )
+ {
+ sal_Int32 nFixedLen = maSprmParser.DistanceToData(nId1);
+ r1 = SprmResult(pSp + nFixedLen, x - nFixedLen);
+ }
+ else if( nCurrentId == nId2 )
+ {
+ sal_Int32 nFixedLen = maSprmParser.DistanceToData(nId2);
+ r2 = SprmResult(pSp + nFixedLen, x - nFixedLen);
+ }
+ else if( nCurrentId == nId3 )
+ {
+ sal_Int32 nFixedLen = maSprmParser.DistanceToData(nId3);
+ r3 = SprmResult(pSp + nFixedLen, x - nFixedLen);
+ }
+ else if( nCurrentId == nId4 )
+ {
+ sal_Int32 nFixedLen = maSprmParser.DistanceToData(nId4);
+ r4 = SprmResult(pSp + nFixedLen, x - nFixedLen);
+ }
+ else
+ bOk = false;
+ bFound |= bOk;
+ // increment pointer so that it points to next SPRM
+ i += x;
+ pSp += x;
+ }
+ return bFound;
+}
+
+SprmResult WW8PLCFx_SEPX::HasSprm( sal_uInt16 nId, sal_uInt8 n2nd ) const
+{
+ SprmResult aRet;
+ if (m_pPLCF)
+ {
+ WW8SprmIter aIter(m_pSprms.get(), m_nSprmSiz, maSprmParser);
+ aRet = aIter.FindSprm(nId, /*bFindFirst=*/true, &n2nd);
+ }
+ return aRet;
+}
+
+WW8PLCFx_SubDoc::WW8PLCFx_SubDoc(SvStream* pSt, const WW8Fib& rFib,
+ WW8_CP nStartCp, tools::Long nFcRef, tools::Long nLenRef, tools::Long nFcText, tools::Long nLenText,
+ tools::Long nStruct)
+ : WW8PLCFx(rFib, true)
+{
+ if( nLenRef && nLenText )
+ {
+ m_pRef.reset(new WW8PLCF(*pSt, nFcRef, nLenRef, nStruct, nStartCp));
+ m_pText.reset(new WW8PLCF(*pSt, nFcText, nLenText, 0, nStartCp));
+ }
+}
+
+WW8PLCFx_SubDoc::~WW8PLCFx_SubDoc()
+{
+ m_pRef.reset();
+ m_pText.reset();
+}
+
+sal_uInt32 WW8PLCFx_SubDoc::GetIdx() const
+{
+ // Probably pText ... no need for it
+ if( m_pRef )
+ return ( m_pRef->GetIdx() << 16 | m_pText->GetIdx() );
+ return 0;
+}
+
+void WW8PLCFx_SubDoc::SetIdx(sal_uInt32 nIdx)
+{
+ if( m_pRef )
+ {
+ m_pRef->SetIdx( nIdx >> 16 );
+ // Probably pText ... no need for it
+ m_pText->SetIdx( nIdx & 0xFFFF );
+ }
+}
+
+bool WW8PLCFx_SubDoc::SeekPos( WW8_CP nCpPos )
+{
+ return m_pRef && m_pRef->SeekPos( nCpPos );
+}
+
+WW8_CP WW8PLCFx_SubDoc::Where()
+{
+ return m_pRef ? m_pRef->Where() : WW8_CP_MAX;
+}
+
+void WW8PLCFx_SubDoc::GetSprms(WW8PLCFxDesc* p)
+{
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ p->bRealLineEnd = false;
+
+ if (!m_pRef)
+ return;
+
+ sal_uInt32 nNr = m_pRef->GetIdx();
+
+ void *pData;
+ WW8_CP nFoo;
+ if (!m_pRef->Get(p->nStartPos, nFoo, pData))
+ {
+ p->nEndPos = p->nStartPos = WW8_CP_MAX;
+ return;
+ }
+
+ if (o3tl::checked_add<WW8_CP>(p->nStartPos, 1, p->nEndPos))
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ p->nEndPos = p->nStartPos = WW8_CP_MAX;
+ return;
+ }
+
+ if (!m_pText)
+ return;
+
+ m_pText->SetIdx(nNr);
+
+ if (!m_pText->Get(p->nCp2OrIdx, p->nSprmsLen, pData))
+ {
+ p->nEndPos = p->nStartPos = WW8_CP_MAX;
+ p->nSprmsLen = 0;
+ return;
+ }
+
+ if (p->nCp2OrIdx < 0 || p->nCp2OrIdx > p->nSprmsLen)
+ {
+ SAL_WARN("sw.ww8", "Document has invalid Cp or Idx, ignoring it");
+ p->nEndPos = p->nStartPos = WW8_CP_MAX;
+ p->nSprmsLen = 0;
+ return;
+ }
+
+ p->nSprmsLen -= p->nCp2OrIdx;
+}
+
+void WW8PLCFx_SubDoc::advance()
+{
+ if (m_pRef && m_pText)
+ {
+ m_pRef->advance();
+ m_pText->advance();
+ }
+}
+
+// fields
+WW8PLCFx_FLD::WW8PLCFx_FLD( SvStream* pSt, const WW8Fib& rMyFib, short nType)
+ : WW8PLCFx(rMyFib, true), m_rFib(rMyFib)
+{
+ WW8_FC nFc;
+ sal_Int32 nLen;
+
+ switch( nType )
+ {
+ case MAN_HDFT:
+ nFc = m_rFib.m_fcPlcffldHdr;
+ nLen = m_rFib.m_lcbPlcffldHdr;
+ break;
+ case MAN_FTN:
+ nFc = m_rFib.m_fcPlcffldFootnote;
+ nLen = m_rFib.m_lcbPlcffldFootnote;
+ break;
+ case MAN_EDN:
+ nFc = m_rFib.m_fcPlcffldEdn;
+ nLen = m_rFib.m_lcbPlcffldEdn;
+ break;
+ case MAN_AND:
+ nFc = m_rFib.m_fcPlcffldAtn;
+ nLen = m_rFib.m_lcbPlcffldAtn;
+ break;
+ case MAN_TXBX:
+ nFc = m_rFib.m_fcPlcffldTxbx;
+ nLen = m_rFib.m_lcbPlcffldTxbx;
+ break;
+ case MAN_TXBX_HDFT:
+ nFc = m_rFib.m_fcPlcffldHdrTxbx;
+ nLen = m_rFib.m_lcbPlcffldHdrTxbx;
+ break;
+ default:
+ nFc = m_rFib.m_fcPlcffldMom;
+ nLen = m_rFib.m_lcbPlcffldMom;
+ break;
+ }
+
+ if( nLen )
+ m_pPLCF.reset( new WW8PLCFspecial( pSt, nFc, nLen, 2 ) );
+}
+
+WW8PLCFx_FLD::~WW8PLCFx_FLD()
+{
+}
+
+sal_uInt32 WW8PLCFx_FLD::GetIdx() const
+{
+ return m_pPLCF ? m_pPLCF->GetIdx() : 0;
+}
+
+void WW8PLCFx_FLD::SetIdx(sal_uInt32 nIdx)
+{
+ if( m_pPLCF )
+ m_pPLCF->SetIdx( nIdx );
+}
+
+bool WW8PLCFx_FLD::SeekPos(WW8_CP nCpPos)
+{
+ return m_pPLCF && m_pPLCF->SeekPosExact( nCpPos );
+}
+
+WW8_CP WW8PLCFx_FLD::Where()
+{
+ return m_pPLCF ? m_pPLCF->Where() : WW8_CP_MAX;
+}
+
+bool WW8PLCFx_FLD::StartPosIsFieldStart()
+{
+ void* pData;
+ sal_Int32 nTest;
+ return m_pPLCF && m_pPLCF->Get(nTest, pData) && ((static_cast<sal_uInt8*>(pData)[0] & 0x1f) == 0x13);
+}
+
+bool WW8PLCFx_FLD::EndPosIsFieldEnd(WW8_CP& nCP)
+{
+ bool bRet = false;
+
+ if (m_pPLCF)
+ {
+ tools::Long n = m_pPLCF->GetIdx();
+
+ m_pPLCF->advance();
+
+ void* pData;
+ sal_Int32 nTest;
+ if ( m_pPLCF->Get(nTest, pData) && ((static_cast<sal_uInt8*>(pData)[0] & 0x1f) == 0x15) )
+ {
+ nCP = nTest;
+ bRet = true;
+ }
+
+ m_pPLCF->SetIdx(n);
+ }
+
+ return bRet;
+}
+
+void WW8PLCFx_FLD::GetSprms(WW8PLCFxDesc* p)
+{
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ p->bRealLineEnd = false;
+
+ if (!m_pPLCF)
+ {
+ p->nStartPos = WW8_CP_MAX; // there are no fields
+ return;
+ }
+
+ tools::Long n = m_pPLCF->GetIdx();
+
+ sal_Int32 nP;
+ void *pData;
+ if (!m_pPLCF->Get(nP, pData)) // end of PLCFspecial?
+ {
+ p->nStartPos = WW8_CP_MAX; // PLCF completely processed
+ return;
+ }
+
+ p->nStartPos = nP;
+
+ m_pPLCF->advance();
+ if (!m_pPLCF->Get(nP, pData)) // end of PLCFspecial?
+ {
+ p->nStartPos = WW8_CP_MAX; // PLCF completely processed
+ return;
+ }
+
+ p->nEndPos = nP;
+
+ m_pPLCF->SetIdx(n);
+
+ p->nCp2OrIdx = m_pPLCF->GetIdx();
+}
+
+void WW8PLCFx_FLD::advance()
+{
+ SAL_WARN_IF(!m_pPLCF, "sw.ww8", "Call without PLCFspecial field");
+ if( !m_pPLCF )
+ return;
+ m_pPLCF->advance();
+}
+
+bool WW8PLCFx_FLD::GetPara(tools::Long nIdx, WW8FieldDesc& rF)
+{
+ SAL_WARN_IF(!m_pPLCF, "sw.ww8", "Call without PLCFspecial field");
+ if( !m_pPLCF )
+ return false;
+
+ tools::Long n = m_pPLCF->GetIdx();
+ m_pPLCF->SetIdx(nIdx);
+
+ bool bOk = WW8GetFieldPara(*m_pPLCF, rF);
+
+ m_pPLCF->SetIdx(n);
+ return bOk;
+}
+
+// WW8PLCF_Book
+void WW8ReadSTTBF(bool bVer8, SvStream& rStrm, sal_uInt32 nStart, sal_Int32 nLen,
+ sal_uInt16 nExtraLen, rtl_TextEncoding eCS, std::vector<OUString> &rArray,
+ std::vector<ww::bytes>* pExtraArray, std::vector<OUString>* pValueArray)
+{
+ if (nLen==0) // Handle Empty STTBF
+ return;
+
+ sal_uInt64 const nOldPos = rStrm.Tell();
+ if (checkSeek(rStrm, nStart))
+ {
+ sal_uInt16 nLen2(0);
+ rStrm.ReadUInt16( nLen2 ); // bVer67: total length of structure
+ // bVer8 : count of strings
+
+ if( bVer8 )
+ {
+ sal_uInt16 nStrings(0);
+ bool bUnicode = (0xFFFF == nLen2);
+ if (bUnicode)
+ rStrm.ReadUInt16( nStrings );
+ else
+ nStrings = nLen2;
+
+ rStrm.ReadUInt16( nExtraLen );
+
+ const size_t nMinStringLen = bUnicode ? sizeof(sal_uInt16) : sizeof(sal_uInt8);
+ const size_t nMinRecordSize = nExtraLen + nMinStringLen;
+ assert(nMinRecordSize != 0 && "impossible to be zero");
+ const size_t nMaxPossibleStrings = rStrm.remainingSize() / nMinRecordSize;
+ if (nStrings > nMaxPossibleStrings)
+ {
+ SAL_WARN("sw.ww8", "STTBF claims " << nStrings << " entries, but only " << nMaxPossibleStrings << " are possible");
+ nStrings = nMaxPossibleStrings;
+ }
+
+ if (nExtraLen && nStrings)
+ {
+ const size_t nMaxExtraLen = (rStrm.remainingSize() - (nStrings * nMinStringLen)) / nStrings;
+ if (nExtraLen > nMaxExtraLen)
+ {
+ SAL_WARN("sw.ww8", "STTBF claims " << nMaxExtraLen << " extra len, but only " << nMaxExtraLen << " are possible");
+ nExtraLen = nMaxExtraLen;
+ }
+ }
+
+ for (sal_uInt16 i=0; i < nStrings; ++i)
+ {
+ if (bUnicode)
+ rArray.push_back(read_uInt16_PascalString(rStrm));
+ else
+ {
+ OString aTmp = read_uInt8_lenPrefixed_uInt8s_ToOString(rStrm);
+ rArray.push_back(OStringToOUString(aTmp, eCS));
+ }
+
+ // Skip the extra data
+ if (nExtraLen)
+ {
+ if (pExtraArray)
+ {
+ ww::bytes extraData(nExtraLen);
+ rStrm.ReadBytes(extraData.data(), nExtraLen);
+ pExtraArray->push_back(extraData);
+ }
+ else
+ rStrm.SeekRel( nExtraLen );
+ }
+ }
+ // read the value of the document variables, if requested.
+ if (pValueArray)
+ {
+ for (sal_uInt16 i=0; i < nStrings; ++i)
+ {
+ if( bUnicode )
+ pValueArray->push_back(read_uInt16_PascalString(rStrm));
+ else
+ {
+ OString aTmp = read_uInt8_lenPrefixed_uInt8s_ToOString(rStrm);
+ pValueArray->push_back(OStringToOUString(aTmp, eCS));
+ }
+ }
+ }
+ }
+ else
+ {
+ if( nLen2 != nLen )
+ {
+ OSL_ENSURE(nLen2 == nLen,
+ "Fib length and read length are different");
+ if (nLen > SAL_MAX_UINT16)
+ nLen = SAL_MAX_UINT16;
+ else if (nLen < 2 )
+ nLen = 2;
+ nLen2 = o3tl::narrowing<sal_uInt16>(nLen);
+ }
+ sal_uLong nRead = 0;
+ for( nLen2 -= 2; nRead < nLen2; )
+ {
+ sal_uInt8 nBChar(0);
+ rStrm.ReadUChar( nBChar );
+ ++nRead;
+ if (nBChar)
+ {
+ OString aTmp = read_uInt8s_ToOString(rStrm, nBChar);
+ nRead += aTmp.getLength();
+ rArray.push_back(OStringToOUString(aTmp, eCS));
+ }
+ else
+ rArray.emplace_back();
+
+ // Skip the extra data (for bVer67 versions this must come from
+ // external knowledge)
+ if (nExtraLen)
+ {
+ if (pExtraArray)
+ {
+ ww::bytes extraData(nExtraLen);
+ rStrm.ReadBytes(extraData.data(), nExtraLen);
+ pExtraArray->push_back(extraData);
+ }
+ else
+ rStrm.SeekRel( nExtraLen );
+ nRead+=nExtraLen;
+ }
+ }
+ }
+ }
+ rStrm.Seek(nOldPos);
+}
+
+WW8PLCFx_Book::WW8PLCFx_Book(SvStream* pTableSt, const WW8Fib& rFib)
+ : WW8PLCFx(rFib, false), m_nIsEnd(0), m_nBookmarkId(1)
+{
+ if( !rFib.m_fcPlcfbkf || !rFib.m_lcbPlcfbkf || !rFib.m_fcPlcfbkl ||
+ !rFib.m_lcbPlcfbkl || !rFib.m_fcSttbfbkmk || !rFib.m_lcbSttbfbkmk )
+ {
+ m_nIMax = 0;
+ }
+ else
+ {
+ m_pBook[0].reset( new WW8PLCFspecial(pTableSt,rFib.m_fcPlcfbkf,rFib.m_lcbPlcfbkf,4) );
+
+ m_pBook[1].reset( new WW8PLCFspecial(pTableSt,rFib.m_fcPlcfbkl,rFib.m_lcbPlcfbkl,0) );
+
+ rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(rFib.m_chseTables, rFib.m_lid);
+
+ WW8ReadSTTBF( (7 < rFib.m_nVersion), *pTableSt, rFib.m_fcSttbfbkmk,
+ rFib.m_lcbSttbfbkmk, 0, eStructChrSet, m_aBookNames );
+
+ m_nIMax = m_aBookNames.size();
+
+ if( m_pBook[0]->GetIMax() < m_nIMax ) // Count of Bookmarks
+ m_nIMax = m_pBook[0]->GetIMax();
+ if( m_pBook[1]->GetIMax() < m_nIMax )
+ m_nIMax = m_pBook[1]->GetIMax();
+ m_aStatus.resize(m_nIMax);
+ }
+}
+
+WW8PLCFx_Book::~WW8PLCFx_Book()
+{
+}
+
+sal_uInt32 WW8PLCFx_Book::GetIdx() const
+{
+ return m_nIMax ? m_pBook[0]->GetIdx() : 0;
+}
+
+void WW8PLCFx_Book::SetIdx(sal_uInt32 nI)
+{
+ if( m_nIMax )
+ m_pBook[0]->SetIdx( nI );
+}
+
+sal_uInt32 WW8PLCFx_Book::GetIdx2() const
+{
+ return m_nIMax ? ( m_pBook[1]->GetIdx() | ( m_nIsEnd ? 0x80000000 : 0 ) ) : 0;
+}
+
+void WW8PLCFx_Book::SetIdx2(sal_uInt32 nI)
+{
+ if( m_nIMax )
+ {
+ m_pBook[1]->SetIdx( nI & 0x7fffffff );
+ m_nIsEnd = o3tl::narrowing<sal_uInt16>( ( nI >> 31 ) & 1 ); // 0 or 1
+ }
+}
+
+bool WW8PLCFx_Book::SeekPos(WW8_CP nCpPos)
+{
+ if( !m_pBook[0] )
+ return false;
+
+ bool bOk = m_pBook[0]->SeekPosExact( nCpPos );
+ bOk &= m_pBook[1]->SeekPosExact( nCpPos );
+ m_nIsEnd = 0;
+
+ return bOk;
+}
+
+WW8_CP WW8PLCFx_Book::Where()
+{
+ return m_pBook[m_nIsEnd]->Where();
+}
+
+tools::Long WW8PLCFx_Book::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen )
+{
+ void* pData;
+ rEnd = WW8_CP_MAX;
+ rLen = 0;
+
+ if (!m_pBook[0] || !m_pBook[1] || !m_nIMax || (m_pBook[m_nIsEnd]->GetIdx()) >= m_nIMax)
+ {
+ rStart = rEnd = WW8_CP_MAX;
+ return -1;
+ }
+
+ (void)m_pBook[m_nIsEnd]->Get( rStart, pData ); // query position
+ return m_pBook[m_nIsEnd]->GetIdx();
+}
+
+// The operator ++ has a pitfall: If 2 bookmarks adjoin each other,
+// we should first go to the end of the first one
+// and then to the beginning of the second one.
+// But if 2 bookmarks with the length of 0 lie on top of each other,
+// we *must* first find the start and end of each bookmark.
+// The case of: ][
+// [...]
+// ][
+// is not solved yet.
+// Because I must jump back and forth in the start- and end-indices then.
+// This would require one more index or bitfield to remember
+// the already processed bookmarks.
+
+void WW8PLCFx_Book::advance()
+{
+ if( !(m_pBook[0] && m_pBook[1] && m_nIMax) )
+ return;
+
+ (*m_pBook[m_nIsEnd]).advance();
+
+ sal_uLong l0 = m_pBook[0]->Where();
+ sal_uLong l1 = m_pBook[1]->Where();
+ if( l0 < l1 )
+ m_nIsEnd = 0;
+ else if( l1 < l0 )
+ m_nIsEnd = 1;
+ else
+ {
+ const void * p = m_pBook[0]->GetData(m_pBook[0]->GetIdx());
+ tools::Long nPairFor = (p == nullptr) ? 0 : SVBT16ToUInt16(*static_cast<SVBT16 const *>(p));
+ if (nPairFor == m_pBook[1]->GetIdx())
+ m_nIsEnd = 0;
+ else
+ m_nIsEnd = m_nIsEnd ? 0 : 1;
+ }
+}
+
+tools::Long WW8PLCFx_Book::GetLen() const
+{
+ if( m_nIsEnd )
+ {
+ OSL_ENSURE( false, "Incorrect call (1) of PLCF_Book::GetLen()" );
+ return 0;
+ }
+ void * p;
+ WW8_CP nStartPos;
+ if( !m_pBook[0]->Get( nStartPos, p ) )
+ {
+ OSL_ENSURE( false, "Incorrect call (2) of PLCF_Book::GetLen()" );
+ return 0;
+ }
+ const sal_uInt16 nEndIdx = SVBT16ToUInt16( *static_cast<SVBT16*>(p) );
+ tools::Long nNum = m_pBook[1]->GetPos( nEndIdx );
+ nNum -= nStartPos;
+ return nNum;
+}
+
+void WW8PLCFx_Book::SetStatus(sal_uInt16 nIndex, eBookStatus eStat)
+{
+ SAL_WARN_IF(nIndex >= m_nIMax, "sw.ww8",
+ "bookmark index " << nIndex << " invalid");
+ eBookStatus eStatus = m_aStatus.at(nIndex);
+ m_aStatus[nIndex] = static_cast<eBookStatus>(eStatus | eStat);
+}
+
+eBookStatus WW8PLCFx_Book::GetStatus() const
+{
+ if (m_aStatus.empty())
+ return BOOK_NORMAL;
+ tools::Long nEndIdx = GetHandle();
+ return ( nEndIdx < m_nIMax ) ? m_aStatus[nEndIdx] : BOOK_NORMAL;
+}
+
+tools::Long WW8PLCFx_Book::GetHandle() const
+{
+ if( !m_pBook[0] || !m_pBook[1] )
+ return LONG_MAX;
+
+ if( m_nIsEnd )
+ return m_pBook[1]->GetIdx();
+ else
+ {
+ if (const void* p = m_pBook[0]->GetData(m_pBook[0]->GetIdx()))
+ return SVBT16ToUInt16( *static_cast<SVBT16 const *>(p) );
+ else
+ return LONG_MAX;
+ }
+}
+
+OUString WW8PLCFx_Book::GetBookmark(tools::Long nStart,tools::Long nEnd, sal_uInt16 &nIndex)
+{
+ bool bFound = false;
+ sal_uInt16 i = 0;
+ if (m_pBook[0] && m_pBook[1])
+ {
+ WW8_CP nStartCurrent, nEndCurrent;
+ while (sal::static_int_cast<decltype(m_aBookNames)::size_type>(i) < m_aBookNames.size())
+ {
+ void* p;
+ sal_uInt16 nEndIdx;
+
+ if( m_pBook[0]->GetData( i, nStartCurrent, p ) && p )
+ nEndIdx = SVBT16ToUInt16( *static_cast<SVBT16*>(p) );
+ else
+ {
+ OSL_ENSURE( false, "Bookmark-EndIdx not readable" );
+ nEndIdx = i;
+ }
+
+ nEndCurrent = m_pBook[1]->GetPos( nEndIdx );
+
+ if ((nStartCurrent >= nStart) && (nEndCurrent <= nEnd))
+ {
+ nIndex = i;
+ bFound=true;
+ break;
+ }
+ ++i;
+ }
+ }
+ return bFound ? m_aBookNames[i] : OUString();
+}
+
+OUString WW8PLCFx_Book::GetUniqueBookmarkName(const OUString &rSuggestedName)
+{
+ OUString aRet(rSuggestedName.isEmpty() ? OUString("Unnamed") : rSuggestedName);
+ size_t i = 0;
+ while (i < m_aBookNames.size())
+ {
+ if (aRet == m_aBookNames[i])
+ {
+ sal_Int32 len = aRet.getLength();
+ sal_Int32 p = len - 1;
+ while (p > 0 && aRet[p] >= '0' && aRet[p] <= '9')
+ --p;
+ aRet = aRet.subView(0, p+1) + OUString::number(m_nBookmarkId++);
+ i = 0; // start search from beginning
+ }
+ else
+ ++i;
+ }
+ return aRet;
+}
+
+void WW8PLCFx_Book::MapName(OUString& rName)
+{
+ if( !m_pBook[0] || !m_pBook[1] )
+ return;
+
+ size_t i = 0;
+ while (i < m_aBookNames.size())
+ {
+ if (rName.equalsIgnoreAsciiCase(m_aBookNames[i]))
+ {
+ rName = m_aBookNames[i];
+ break;
+ }
+ ++i;
+ }
+}
+
+const OUString* WW8PLCFx_Book::GetName() const
+{
+ const OUString *pRet = nullptr;
+ if (!m_nIsEnd && (m_pBook[0]->GetIdx() < m_nIMax))
+ pRet = &(m_aBookNames[m_pBook[0]->GetIdx()]);
+ return pRet;
+}
+
+WW8PLCFx_AtnBook::WW8PLCFx_AtnBook(SvStream* pTableSt, const WW8Fib& rFib)
+ : WW8PLCFx(rFib, /*bSprm=*/false),
+ m_bIsEnd(false)
+{
+ if (!rFib.m_fcPlcfAtnbkf || !rFib.m_lcbPlcfAtnbkf || !rFib.m_fcPlcfAtnbkl || !rFib.m_lcbPlcfAtnbkl)
+ {
+ m_nIMax = 0;
+ }
+ else
+ {
+ m_pBook[0].reset( new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfAtnbkf, rFib.m_lcbPlcfAtnbkf, 4) );
+ m_pBook[1].reset( new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfAtnbkl, rFib.m_lcbPlcfAtnbkl, 0) );
+
+ m_nIMax = m_pBook[0]->GetIMax();
+ if (m_pBook[1]->GetIMax() < m_nIMax)
+ m_nIMax = m_pBook[1]->GetIMax();
+ }
+}
+
+WW8PLCFx_AtnBook::~WW8PLCFx_AtnBook()
+{
+}
+
+sal_uInt32 WW8PLCFx_AtnBook::GetIdx() const
+{
+ return m_nIMax ? m_pBook[0]->GetIdx() : 0;
+}
+
+void WW8PLCFx_AtnBook::SetIdx(sal_uInt32 nI)
+{
+ if( m_nIMax )
+ m_pBook[0]->SetIdx( nI );
+}
+
+sal_uInt32 WW8PLCFx_AtnBook::GetIdx2() const
+{
+ if (m_nIMax)
+ return m_pBook[1]->GetIdx() | ( m_bIsEnd ? 0x80000000 : 0 );
+ else
+ return 0;
+}
+
+void WW8PLCFx_AtnBook::SetIdx2(sal_uInt32 nI)
+{
+ if( m_nIMax )
+ {
+ m_pBook[1]->SetIdx( nI & 0x7fffffff );
+ m_bIsEnd = static_cast<bool>(( nI >> 31 ) & 1);
+ }
+}
+
+bool WW8PLCFx_AtnBook::SeekPos(WW8_CP nCpPos)
+{
+ if (!m_pBook[0])
+ return false;
+
+ bool bOk = m_pBook[0]->SeekPosExact(nCpPos);
+ bOk &= m_pBook[1]->SeekPosExact(nCpPos);
+ m_bIsEnd = false;
+
+ return bOk;
+}
+
+WW8_CP WW8PLCFx_AtnBook::Where()
+{
+ return m_pBook[static_cast<int>(m_bIsEnd)]->Where();
+}
+
+tools::Long WW8PLCFx_AtnBook::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen )
+{
+ void* pData;
+ rEnd = WW8_CP_MAX;
+ rLen = 0;
+
+ if (!m_pBook[0] || !m_pBook[1] || !m_nIMax || (m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx()) >= m_nIMax)
+ {
+ rStart = rEnd = WW8_CP_MAX;
+ return -1;
+ }
+
+ (void)m_pBook[static_cast<int>(m_bIsEnd)]->Get(rStart, pData);
+ return m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx();
+}
+
+void WW8PLCFx_AtnBook::advance()
+{
+ if( !(m_pBook[0] && m_pBook[1] && m_nIMax) )
+ return;
+
+ (*m_pBook[static_cast<int>(m_bIsEnd)]).advance();
+
+ sal_uLong l0 = m_pBook[0]->Where();
+ sal_uLong l1 = m_pBook[1]->Where();
+ if( l0 < l1 )
+ m_bIsEnd = false;
+ else if( l1 < l0 )
+ m_bIsEnd = true;
+ else
+ {
+ const void * p = m_pBook[0]->GetData(m_pBook[0]->GetIdx());
+ tools::Long nPairFor = (p == nullptr) ? 0 : SVBT16ToUInt16(*static_cast<SVBT16 const *>(p));
+ if (nPairFor == m_pBook[1]->GetIdx())
+ m_bIsEnd = false;
+ else
+ m_bIsEnd = !m_bIsEnd;
+ }
+}
+
+tools::Long WW8PLCFx_AtnBook::getHandle() const
+{
+ if (!m_pBook[0] || !m_pBook[1])
+ return LONG_MAX;
+
+ if (m_bIsEnd)
+ return m_pBook[1]->GetIdx();
+ else
+ {
+ if (const void* p = m_pBook[0]->GetData(m_pBook[0]->GetIdx()))
+ return SVBT16ToUInt16(*static_cast<const SVBT16*>(p));
+ else
+ return LONG_MAX;
+ }
+}
+
+bool WW8PLCFx_AtnBook::getIsEnd() const
+{
+ return m_bIsEnd;
+}
+
+WW8PLCFx_FactoidBook::WW8PLCFx_FactoidBook(SvStream* pTableSt, const WW8Fib& rFib)
+ : WW8PLCFx(rFib, /*bSprm=*/false),
+ m_bIsEnd(false)
+{
+ if (!rFib.m_fcPlcfBkfFactoid || !rFib.m_lcbPlcfBkfFactoid || !rFib.m_fcPlcfBklFactoid || !rFib.m_lcbPlcfBklFactoid)
+ {
+ m_nIMax = 0;
+ }
+ else
+ {
+ m_pBook[0].reset(new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfBkfFactoid, rFib.m_lcbPlcfBkfFactoid, 6));
+ m_pBook[1].reset(new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfBklFactoid, rFib.m_lcbPlcfBklFactoid, 4));
+
+ m_nIMax = m_pBook[0]->GetIMax();
+ if (m_pBook[1]->GetIMax() < m_nIMax)
+ m_nIMax = m_pBook[1]->GetIMax();
+ }
+}
+
+WW8PLCFx_FactoidBook::~WW8PLCFx_FactoidBook()
+{
+}
+
+sal_uInt32 WW8PLCFx_FactoidBook::GetIdx() const
+{
+ return m_nIMax ? m_pBook[0]->GetIdx() : 0;
+}
+
+void WW8PLCFx_FactoidBook::SetIdx(sal_uInt32 nI)
+{
+ if (m_nIMax)
+ m_pBook[0]->SetIdx(nI);
+}
+
+sal_uInt32 WW8PLCFx_FactoidBook::GetIdx2() const
+{
+ if (m_nIMax)
+ return m_pBook[1]->GetIdx() | (m_bIsEnd ? 0x80000000 : 0);
+ else
+ return 0;
+}
+
+void WW8PLCFx_FactoidBook::SetIdx2(sal_uInt32 nI)
+{
+ if (m_nIMax)
+ {
+ m_pBook[1]->SetIdx(nI & 0x7fffffff);
+ m_bIsEnd = static_cast<bool>((nI >> 31) & 1);
+ }
+}
+
+bool WW8PLCFx_FactoidBook::SeekPos(WW8_CP nCpPos)
+{
+ if (!m_pBook[0])
+ return false;
+
+ bool bOk = m_pBook[0]->SeekPosExact(nCpPos);
+ bOk &= m_pBook[1]->SeekPosExact(nCpPos);
+ m_bIsEnd = false;
+
+ return bOk;
+}
+
+WW8_CP WW8PLCFx_FactoidBook::Where()
+{
+ return m_pBook[static_cast<int>(m_bIsEnd)]->Where();
+}
+
+tools::Long WW8PLCFx_FactoidBook::GetNoSprms(WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen)
+{
+ void* pData;
+ rEnd = WW8_CP_MAX;
+ rLen = 0;
+
+ if (!m_pBook[0] || !m_pBook[1] || !m_nIMax || (m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx()) >= m_nIMax)
+ {
+ rStart = rEnd = WW8_CP_MAX;
+ return -1;
+ }
+
+ (void)m_pBook[static_cast<int>(m_bIsEnd)]->Get(rStart, pData);
+ return m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx();
+}
+
+void WW8PLCFx_FactoidBook::advance()
+{
+ if (!(m_pBook[0] && m_pBook[1] && m_nIMax))
+ return;
+
+ (*m_pBook[static_cast<int>(m_bIsEnd)]).advance();
+
+ sal_uLong l0 = m_pBook[0]->Where();
+ sal_uLong l1 = m_pBook[1]->Where();
+ if (l0 < l1)
+ m_bIsEnd = false;
+ else if (l1 < l0)
+ m_bIsEnd = true;
+ else
+ {
+ const void * p = m_pBook[0]->GetData(m_pBook[0]->GetIdx());
+ tools::Long nPairFor = (p == nullptr) ? 0 : SVBT16ToUInt16(*static_cast<SVBT16 const *>(p));
+ if (nPairFor == m_pBook[1]->GetIdx())
+ m_bIsEnd = false;
+ else
+ m_bIsEnd = !m_bIsEnd;
+ }
+}
+
+tools::Long WW8PLCFx_FactoidBook::getHandle() const
+{
+ if (!m_pBook[0] || !m_pBook[1])
+ return LONG_MAX;
+
+ if (m_bIsEnd)
+ return m_pBook[1]->GetIdx();
+ else
+ {
+ if (const void* p = m_pBook[0]->GetData(m_pBook[0]->GetIdx()))
+ return SVBT16ToUInt16(*static_cast<const SVBT16*>(p));
+ else
+ return LONG_MAX;
+ }
+}
+
+bool WW8PLCFx_FactoidBook::getIsEnd() const
+{
+ return m_bIsEnd;
+}
+
+// In the end of a paragraph in WW6 the attribute extends after the <CR>.
+// This will be reset by one character to be used with SW,
+// if we don't expect trouble thereby.
+void WW8PLCFMan::AdjustEnds( WW8PLCFxDesc& rDesc )
+{
+ // might be necessary to do this for pChp and/or pSep as well,
+ // but its definitely the case for paragraphs that EndPos > StartPos
+ // for a well formed paragraph as those always have a paragraph
+ // <cr> in them
+ if (&rDesc == m_pPap && rDesc.bRealLineEnd)
+ {
+ if (rDesc.nStartPos == rDesc.nEndPos && rDesc.nEndPos != WW8_CP_MAX)
+ {
+ SAL_WARN("sw.ww8", "WW8PLCFxDesc End same as Start, abandoning to avoid looping");
+ rDesc.nEndPos = WW8_CP_MAX;
+ }
+ }
+
+ //Store old end position for supercool new property finder that uses
+ //cp instead of fc's as nature intended
+ rDesc.nOrigEndPos = rDesc.nEndPos;
+ rDesc.nOrigStartPos = rDesc.nStartPos;
+
+ /*
+ Normally given ^XXX{para end}^ we don't actually insert a para end
+ character into the document, so we clip the para end property one to the
+ left to make the para properties end when the paragraph text does. In a
+ drawing textbox we actually do insert a para end character, so we don't
+ clip it. Making the para end properties end after the para end char.
+ */
+ if (GetDoingDrawTextBox())
+ return;
+
+ if ( (&rDesc == m_pPap) && rDesc.bRealLineEnd )
+ {
+ if ( m_pPap->nEndPos != WW8_CP_MAX ) // Para adjust
+ {
+ m_nLineEnd = m_pPap->nEndPos;// nLineEnd points *after* the <CR>
+ m_pPap->nEndPos--; // shorten paragraph end by one character
+
+ // Is there already a sep end, which points to the current paragraph end?
+ // Then we also must shorten by one character
+ if( m_pSep->nEndPos == m_nLineEnd )
+ m_pSep->nEndPos--;
+ }
+ }
+ else if (&rDesc == m_pSep)
+ {
+ // Sep Adjust if end Char-Attr == paragraph end ...
+ if( (rDesc.nEndPos == m_nLineEnd) && (rDesc.nEndPos > rDesc.nStartPos) )
+ rDesc.nEndPos--; // ... then shorten by one character
+ }
+}
+
+void WW8PLCFxDesc::ReduceByOffset()
+{
+ SAL_WARN_IF(WW8_CP_MAX != nStartPos && nStartPos > nEndPos, "sw.ww8",
+ "End " << nEndPos << " before Start " << nStartPos);
+
+ if( nStartPos != WW8_CP_MAX )
+ {
+ /*
+ ##516##,##517##
+ Force the property change to happen at the beginning of this
+ subdocument, same as in GetNewNoSprms, except that the target type is
+ attributes attached to a piece that might span subdocument boundaries
+ */
+ if (nCpOfs > nStartPos)
+ nStartPos = 0;
+ else
+ nStartPos -= nCpOfs;
+ }
+ if (nEndPos != WW8_CP_MAX)
+ {
+ if (nCpOfs > nEndPos)
+ {
+ SAL_WARN("sw.ww8", "broken subdocument piece entry");
+ nEndPos = WW8_CP_MAX;
+ }
+ else
+ nEndPos -= nCpOfs;
+ }
+}
+
+void WW8PLCFMan::GetNewSprms( WW8PLCFxDesc& rDesc )
+{
+ rDesc.pPLCFx->GetSprms(&rDesc);
+ rDesc.ReduceByOffset();
+
+ rDesc.bFirstSprm = true;
+ AdjustEnds( rDesc );
+ rDesc.nOrigSprmsLen = rDesc.nSprmsLen;
+}
+
+void WW8PLCFMan::GetNewNoSprms( WW8PLCFxDesc& rDesc )
+{
+ rDesc.nCp2OrIdx = rDesc.pPLCFx->GetNoSprms(rDesc.nStartPos, rDesc.nEndPos,
+ rDesc.nSprmsLen);
+
+ SAL_WARN_IF(WW8_CP_MAX != rDesc.nStartPos && rDesc.nStartPos > rDesc.nEndPos, "sw.ww8",
+ "End " << rDesc.nEndPos << " before Start " << rDesc.nStartPos);
+
+ rDesc.ReduceByOffset();
+
+ rDesc.bFirstSprm = true;
+ rDesc.nOrigSprmsLen = rDesc.nSprmsLen;
+}
+
+sal_uInt16 WW8PLCFMan::GetId(const WW8PLCFxDesc* p) const
+{
+ sal_uInt16 nId = 0; // Id = 0 for empty attributes
+
+ if (p == m_pField)
+ nId = eFLD;
+ else if (p == m_pFootnote)
+ nId = eFTN;
+ else if (p == m_pEdn)
+ nId = eEDN;
+ else if (p == m_pAnd)
+ nId = eAND;
+ else if (p->nSprmsLen >= maSprmParser.MinSprmLen())
+ nId = maSprmParser.GetSprmId(p->pMemPos);
+
+ return nId;
+}
+
+WW8PLCFMan::WW8PLCFMan(const WW8ScannerBase* pBase, ManTypes nType, tools::Long nStartCp,
+ bool bDoingDrawTextBox)
+ : maSprmParser(*pBase->m_pWw8Fib),
+ m_nLineEnd(WW8_CP_MAX),
+ mbDoingDrawTextBox(bDoingDrawTextBox)
+{
+ m_pWwFib = pBase->m_pWw8Fib;
+
+ m_nManType = nType;
+
+ if( MAN_MAINTEXT == nType )
+ {
+ // search order of the attributes
+ m_nPLCF = MAN_PLCF_COUNT;
+ m_pField = &m_aD[0];
+ m_pBkm = &m_aD[1];
+ m_pEdn = &m_aD[2];
+ m_pFootnote = &m_aD[3];
+ m_pAnd = &m_aD[4];
+
+ m_pPcd = pBase->m_pPLCFx_PCD ? &m_aD[5] : nullptr;
+ //pPcdA index == pPcd index + 1
+ m_pPcdA = pBase->m_pPLCFx_PCDAttrs ? &m_aD[6] : nullptr;
+
+ m_pChp = &m_aD[7];
+ m_pPap = &m_aD[8];
+ m_pSep = &m_aD[9];
+ m_pAtnBkm = &m_aD[10];
+ m_pFactoidBkm = &m_aD[11];
+
+ m_pSep->pPLCFx = pBase->m_pSepPLCF.get();
+ m_pFootnote->pPLCFx = pBase->m_pFootnotePLCF.get();
+ m_pEdn->pPLCFx = pBase->m_pEdnPLCF.get();
+ m_pBkm->pPLCFx = pBase->m_pBook.get();
+ m_pAnd->pPLCFx = pBase->m_pAndPLCF.get();
+ m_pAtnBkm->pPLCFx = pBase->m_pAtnBook.get();
+ m_pFactoidBkm->pPLCFx = pBase->m_pFactoidBook.get();
+
+ }
+ else
+ {
+ // search order of the attributes
+ m_nPLCF = 7;
+ m_pField = &m_aD[0];
+ m_pBkm = pBase->m_pBook ? &m_aD[1] : nullptr;
+
+ m_pPcd = pBase->m_pPLCFx_PCD ? &m_aD[2] : nullptr;
+ //pPcdA index == pPcd index + 1
+ m_pPcdA= pBase->m_pPLCFx_PCDAttrs ? &m_aD[3] : nullptr;
+
+ m_pChp = &m_aD[4];
+ m_pPap = &m_aD[5];
+ m_pSep = &m_aD[6]; // Dummy
+
+ m_pAnd = m_pAtnBkm = m_pFactoidBkm = m_pFootnote = m_pEdn = nullptr; // not used at SpezText
+ }
+
+ m_pChp->pPLCFx = pBase->m_pChpPLCF.get();
+ m_pPap->pPLCFx = pBase->m_pPapPLCF.get();
+ if( m_pPcd )
+ m_pPcd->pPLCFx = pBase->m_pPLCFx_PCD.get();
+ if( m_pPcdA )
+ m_pPcdA->pPLCFx= pBase->m_pPLCFx_PCDAttrs.get();
+ if( m_pBkm )
+ m_pBkm->pPLCFx = pBase->m_pBook.get();
+
+ m_pMagicTables = pBase->m_pMagicTables.get();
+ m_pSubdocs = pBase->m_pSubdocs.get();
+ m_pExtendedAtrds = pBase->m_pExtendedAtrds.get();
+
+ switch( nType ) // field initialization
+ {
+ case MAN_HDFT:
+ m_pField->pPLCFx = pBase->m_pFieldHdFtPLCF.get();
+ m_pFdoa = pBase->m_pHdFtFdoa.get();
+ m_pTxbx = pBase->m_pHdFtTxbx.get();
+ m_pTxbxBkd = pBase->m_pHdFtTxbxBkd.get();
+ break;
+ case MAN_FTN:
+ m_pField->pPLCFx = pBase->m_pFieldFootnotePLCF.get();
+ m_pFdoa = m_pTxbx = m_pTxbxBkd = nullptr;
+ break;
+ case MAN_EDN:
+ m_pField->pPLCFx = pBase->m_pFieldEdnPLCF.get();
+ m_pFdoa = m_pTxbx = m_pTxbxBkd = nullptr;
+ break;
+ case MAN_AND:
+ m_pField->pPLCFx = pBase->m_pFieldAndPLCF.get();
+ m_pFdoa = m_pTxbx = m_pTxbxBkd = nullptr;
+ break;
+ case MAN_TXBX:
+ m_pField->pPLCFx = pBase->m_pFieldTxbxPLCF.get();
+ m_pTxbx = pBase->m_pMainTxbx.get();
+ m_pTxbxBkd = pBase->m_pMainTxbxBkd.get();
+ m_pFdoa = nullptr;
+ break;
+ case MAN_TXBX_HDFT:
+ m_pField->pPLCFx = pBase->m_pFieldTxbxHdFtPLCF.get();
+ m_pTxbx = pBase->m_pHdFtTxbx.get();
+ m_pTxbxBkd = pBase->m_pHdFtTxbxBkd.get();
+ m_pFdoa = nullptr;
+ break;
+ default:
+ m_pField->pPLCFx = pBase->m_pFieldPLCF.get();
+ m_pFdoa = pBase->m_pMainFdoa.get();
+ m_pTxbx = pBase->m_pMainTxbx.get();
+ m_pTxbxBkd = pBase->m_pMainTxbxBkd.get();
+ break;
+ }
+
+ WW8_CP cp = 0;
+ m_pWwFib->GetBaseCp(nType, &cp); //TODO: check return value
+ m_nCpO = cp;
+
+ if( nStartCp || m_nCpO )
+ SeekPos( nStartCp ); // adjust PLCFe at text StartPos
+
+ // initialization to the member vars Low-Level
+ GetChpPLCF()->ResetAttrStartEnd();
+ GetPapPLCF()->ResetAttrStartEnd();
+ for( sal_uInt16 i=0; i < m_nPLCF; ++i)
+ {
+ WW8PLCFxDesc* p = &m_aD[i];
+
+ /*
+ ##516##,##517##
+ For subdocuments we modify the cp of properties to be relative to
+ the beginning of subdocuments, we should also do the same for
+ piecetable changes, and piecetable properties, otherwise a piece
+ change that happens in a subdocument is lost.
+ */
+ p->nCpOfs = ( p == m_pChp || p == m_pPap || p == m_pBkm || p == m_pPcd ||
+ p == m_pPcdA ) ? m_nCpO : 0;
+
+ p->nCp2OrIdx = 0;
+ p->bFirstSprm = false;
+ p->xIdStack.reset();
+
+ if ((p == m_pChp) || (p == m_pPap))
+ p->nStartPos = p->nEndPos = nStartCp;
+ else
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ }
+
+ // initialization to the member vars High-Level
+ for( sal_uInt16 i=0; i<m_nPLCF; ++i){
+ WW8PLCFxDesc* p = &m_aD[i];
+
+ if( !p->pPLCFx )
+ {
+ p->nStartPos = p->nEndPos = WW8_CP_MAX;
+ continue;
+ }
+
+ if( p->pPLCFx->IsSprm() )
+ {
+ // Careful: nEndPos must be
+ p->xIdStack.emplace();
+ if ((p == m_pChp) || (p == m_pPap))
+ {
+ WW8_CP nTemp = p->nEndPos+p->nCpOfs;
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ p->nStartPos = nTemp;
+ if (!(*p->pPLCFx).SeekPos(p->nStartPos))
+ p->nEndPos = p->nStartPos = WW8_CP_MAX;
+ else
+ GetNewSprms( *p );
+ }
+ else
+ GetNewSprms( *p ); // initialized at all PLCFs
+ }
+ else if( p->pPLCFx )
+ GetNewNoSprms( *p );
+ }
+}
+
+WW8PLCFMan::~WW8PLCFMan()
+{
+ for( sal_uInt16 i=0; i<m_nPLCF; i++)
+ m_aD[i].xIdStack.reset();
+}
+
+// 0. which attr class,
+// 1. if it's an attr start,
+// 2. CP, where is next attr change
+sal_uInt16 WW8PLCFMan::WhereIdx(bool *const pbStart, WW8_CP *const pPos) const
+{
+ OSL_ENSURE(m_nPLCF,"What the hell");
+ WW8_CP nNext = WW8_CP_MAX; // search order:
+ sal_uInt16 nNextIdx = m_nPLCF;// first ending found ( CHP, PAP, ( SEP ) ),
+ bool bStart = true; // now find beginnings ( ( SEP ), PAP, CHP )
+ const WW8PLCFxDesc* pD;
+ for (sal_uInt16 i=0; i < m_nPLCF; ++i)
+ {
+ pD = &m_aD[i];
+ if (pD != m_pPcdA)
+ {
+ if( (pD->nEndPos < nNext) && (pD->nStartPos == WW8_CP_MAX) )
+ {
+ // otherwise start = end
+ nNext = pD->nEndPos;
+ nNextIdx = i;
+ bStart = false;
+ }
+ }
+ }
+ for (sal_uInt16 i=m_nPLCF; i > 0; --i)
+ {
+ pD = &m_aD[i-1];
+ if (pD != m_pPcdA && pD->nStartPos < nNext )
+ {
+ nNext = pD->nStartPos;
+ nNextIdx = i-1;
+ bStart = true;
+ }
+ }
+ if( pPos )
+ *pPos = nNext;
+ if( pbStart )
+ *pbStart = bStart;
+ return nNextIdx;
+}
+
+// gives the CP pos of the next attr change
+WW8_CP WW8PLCFMan::Where() const
+{
+ WW8_CP l;
+ WhereIdx(nullptr, &l);
+ return l;
+}
+
+void WW8PLCFMan::SeekPos( tools::Long nNewCp )
+{
+ m_pChp->pPLCFx->SeekPos( nNewCp + m_nCpO ); // create new attr
+ m_pPap->pPLCFx->SeekPos( nNewCp + m_nCpO );
+ m_pField->pPLCFx->SeekPos( nNewCp );
+ if( m_pPcd )
+ m_pPcd->pPLCFx->SeekPos( nNewCp + m_nCpO );
+ if( m_pBkm )
+ m_pBkm->pPLCFx->SeekPos( nNewCp + m_nCpO );
+}
+
+void WW8PLCFMan::SaveAllPLCFx( WW8PLCFxSaveAll& rSave ) const
+{
+ sal_uInt16 n=0;
+ if( m_pPcd )
+ m_pPcd->Save( rSave.aS[n++] );
+ if( m_pPcdA )
+ m_pPcdA->Save( rSave.aS[n++] );
+
+ for(sal_uInt16 i=0; i<m_nPLCF; ++i)
+ if( m_pPcd != &m_aD[i] && m_pPcdA != &m_aD[i] )
+ m_aD[i].Save( rSave.aS[n++] );
+}
+
+void WW8PLCFMan::RestoreAllPLCFx( const WW8PLCFxSaveAll& rSave )
+{
+ sal_uInt16 n=0;
+ if( m_pPcd )
+ m_pPcd->Restore( rSave.aS[n++] );
+ if( m_pPcdA )
+ m_pPcdA->Restore( rSave.aS[n++] );
+
+ for(sal_uInt16 i=0; i<m_nPLCF; ++i)
+ if( m_pPcd != &m_aD[i] && m_pPcdA != &m_aD[i] )
+ m_aD[i].Restore( rSave.aS[n++] );
+}
+
+namespace
+{
+ bool IsSizeLegal(tools::Long nSprmLen, sal_Int32 nSprmsLen)
+ {
+ if (nSprmLen > nSprmsLen)
+ {
+ SAL_WARN("sw.ww8", "Short sprm, len " << nSprmLen << " claimed, max possible is " << nSprmsLen);
+ return false;
+ }
+ return true;
+ }
+}
+
+bool WW8PLCFMan::IsSprmLegalForCategory(sal_uInt16 nSprmId, short nIdx) const
+{
+ const WW8PLCFxDesc* p = &m_aD[nIdx];
+ if (p != m_pSep) // just check sep for now
+ return true;
+
+ bool bRet;
+ ww::WordVersion eVersion = maSprmParser.GetFIBVersion();
+ if (eVersion <= ww::eWW2)
+ bRet = nSprmId >= 112 && nSprmId <= 145;
+ else if (eVersion < ww::eWW8)
+ bRet = nSprmId >= NS_sprm::v6::sprmSScnsPgn && nSprmId <= NS_sprm::v6::sprmSDMPaperReq;
+ else
+ {
+ /*
+ Sprm bits: 10-12 sgc sprm group; type of sprm (PAP, CHP, etc)
+
+ sgc value type of sprm
+ 1 PAP
+ 2 CHP
+ 3 PIC
+ 4 SEP
+ 5 TAP
+ */
+ auto nSGC = ((nSprmId & 0x1C00) >> 10);
+ bRet = nSGC == 4;
+ }
+ if (!bRet)
+ SAL_INFO("sw.ww8", "sprm, id " << nSprmId << " wrong category for section properties");
+ return bRet;
+}
+
+void WW8PLCFMan::GetSprmStart( short nIdx, WW8PLCFManResult* pRes ) const
+{
+ memset( pRes, 0, sizeof( WW8PLCFManResult ) );
+
+ // verifying !!!
+
+ pRes->nMemLen = 0;
+
+ const WW8PLCFxDesc* p = &m_aD[nIdx];
+
+ // first Sprm in a Group
+ if( p->bFirstSprm )
+ {
+ if( p == m_pPap )
+ pRes->nFlags |= MAN_MASK_NEW_PAP;
+ else if( p == m_pSep )
+ pRes->nFlags |= MAN_MASK_NEW_SEP;
+ }
+ pRes->pMemPos = p->pMemPos;
+ pRes->nSprmId = GetId(p);
+ pRes->nCp2OrIdx = p->nCp2OrIdx;
+ if ((p == m_pFootnote) || (p == m_pEdn) || (p == m_pAnd))
+ pRes->nMemLen = p->nSprmsLen;
+ else if (p->nSprmsLen >= maSprmParser.MinSprmLen()) //normal
+ {
+ // Length of actual sprm
+ pRes->nMemLen = maSprmParser.GetSprmSize(pRes->nSprmId, pRes->pMemPos, p->nSprmsLen);
+ if (!IsSizeLegal(pRes->nMemLen, p->nSprmsLen) || !IsSprmLegalForCategory(pRes->nSprmId, nIdx))
+ {
+ pRes->nSprmId = 0;
+ }
+ }
+}
+
+void WW8PLCFMan::GetSprmEnd( short nIdx, WW8PLCFManResult* pRes ) const
+{
+ memset( pRes, 0, sizeof( WW8PLCFManResult ) );
+
+ const WW8PLCFxDesc* p = &m_aD[nIdx];
+
+ if (!(p->xIdStack->empty()))
+ pRes->nSprmId = p->xIdStack->top(); // get end position
+ else
+ {
+ OSL_ENSURE( false, "No Id on the Stack" );
+ pRes->nSprmId = 0;
+ }
+}
+
+void WW8PLCFMan::GetNoSprmStart( short nIdx, WW8PLCFManResult* pRes ) const
+{
+ const WW8PLCFxDesc* p = &m_aD[nIdx];
+
+ pRes->nCpPos = p->nStartPos;
+ pRes->nMemLen = p->nSprmsLen;
+ pRes->nCp2OrIdx = p->nCp2OrIdx;
+
+ if( p == m_pField )
+ pRes->nSprmId = eFLD;
+ else if( p == m_pFootnote )
+ pRes->nSprmId = eFTN;
+ else if( p == m_pEdn )
+ pRes->nSprmId = eEDN;
+ else if( p == m_pBkm )
+ pRes->nSprmId = eBKN;
+ else if (p == m_pAtnBkm)
+ pRes->nSprmId = eATNBKN;
+ else if (p == m_pFactoidBkm)
+ pRes->nSprmId = eFACTOIDBKN;
+ else if( p == m_pAnd )
+ pRes->nSprmId = eAND;
+ else if( p == m_pPcd )
+ {
+ //We slave the piece table attributes to the piece table, the piece
+ //table attribute iterator contains the sprms for this piece.
+ GetSprmStart( nIdx+1, pRes );
+ }
+ else
+ pRes->nSprmId = 0; // default: not found
+}
+
+void WW8PLCFMan::GetNoSprmEnd( short nIdx, WW8PLCFManResult* pRes ) const
+{
+ pRes->nMemLen = -1; // end tag
+
+ if( &m_aD[nIdx] == m_pBkm )
+ pRes->nSprmId = eBKN;
+ else if (&m_aD[nIdx] == m_pAtnBkm)
+ pRes->nSprmId = eATNBKN;
+ else if (&m_aD[nIdx] == m_pFactoidBkm)
+ pRes->nSprmId = eFACTOIDBKN;
+ else if( &m_aD[nIdx] == m_pPcd )
+ {
+ //We slave the piece table attributes to the piece table, the piece
+ //table attribute iterator contains the sprms for this piece.
+ GetSprmEnd( nIdx+1, pRes );
+ }
+ else
+ pRes->nSprmId = 0;
+}
+
+void WW8PLCFMan::TransferOpenSprms(std::stack<sal_uInt16> &rStack)
+{
+ for (sal_uInt16 i = 0; i < m_nPLCF; ++i)
+ {
+ WW8PLCFxDesc* p = &m_aD[i];
+ if (!p || !p->xIdStack)
+ continue;
+ while (!p->xIdStack->empty())
+ {
+ rStack.push(p->xIdStack->top());
+ p->xIdStack->pop();
+ }
+ }
+}
+
+void WW8PLCFMan::AdvSprm(short nIdx, bool bStart)
+{
+ WW8PLCFxDesc* p = &m_aD[nIdx]; // determine sprm class(!)
+
+ p->bFirstSprm = false;
+ if( bStart )
+ {
+ const sal_uInt16 nLastId = GetId(p);
+
+ const sal_uInt16 nLastAttribStarted = IsSprmLegalForCategory(nLastId, nIdx) ? nLastId : 0;
+
+ p->xIdStack->push(nLastAttribStarted); // remember Id for attribute end
+
+ if( p->nSprmsLen )
+ { /*
+ Check, if we have to process more sprm(s).
+ */
+ if( p->pMemPos )
+ {
+ // Length of last sprm
+ const sal_Int32 nSprmL = maSprmParser.GetSprmSize(nLastId, p->pMemPos, p->nSprmsLen);
+
+ // Reduce length of all sprms by length of last sprm
+ p->nSprmsLen -= nSprmL;
+
+ // pos of next possible sprm
+ if (p->nSprmsLen < maSprmParser.MinSprmLen())
+ {
+ // preventively set to 0, because the end follows!
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ }
+ else
+ p->pMemPos += nSprmL;
+ }
+ else
+ p->nSprmsLen = 0;
+ }
+ if (p->nSprmsLen < maSprmParser.MinSprmLen())
+ p->nStartPos = WW8_CP_MAX; // the ending follows
+ }
+ else
+ {
+ if (!(p->xIdStack->empty()))
+ p->xIdStack->pop();
+ if (p->xIdStack->empty())
+ {
+ if ( (p == m_pChp) || (p == m_pPap) )
+ {
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ p->nStartPos = p->nOrigEndPos+p->nCpOfs;
+
+ /*
+ On failed seek we have run out of sprms, probably. But if it's
+ a fastsaved file (has pPcd) then we may be just in a sprm free
+ gap between pieces that have them, so set dirty flag in sprm
+ finder to consider than.
+ */
+ if (!(*p->pPLCFx).SeekPos(p->nStartPos))
+ {
+ p->nEndPos = WW8_CP_MAX;
+ p->pPLCFx->SetDirty(true);
+ }
+ if (!p->pPLCFx->GetDirty() || m_pPcd)
+ GetNewSprms( *p );
+ p->pPLCFx->SetDirty(false);
+
+ /*
+ #i2325#
+ To get the character and paragraph properties you first get
+ the pap and chp and then apply the fastsaved pPcd properties
+ to the range. If a pap or chp starts inside the pPcd range
+ then we must bring the current pPcd range to a halt so as to
+ end those sprms, then the pap/chp will be processed, and then
+ we must force a restart of the pPcd on that pap/chp starting
+ boundary. Doing that effectively means that the pPcd sprms will
+ be applied to the new range. Not doing it means that the pPcd
+ sprms will only be applied to the first pap/chp set of
+ properties contained in the pap/chp range.
+
+ So we bring the pPcd to a halt on this location here, by
+ settings its end to the current start, then store the starting
+ position of the current range to clipstart. The pPcd sprms
+ will end as normal (albeit earlier than originally expected),
+ and the existence of a clipstart will force the pPcd iterator
+ to reread the current set of sprms instead of advancing to its
+ next set. Then the clipstart will be set as the starting
+ position which will force them to be applied directly after
+ the pap and chps.
+ */
+ if (m_pPcd && ((p->nStartPos > m_pPcd->nStartPos) ||
+ (m_pPcd->nStartPos == WW8_CP_MAX)) &&
+ (m_pPcd->nEndPos != p->nStartPos))
+ {
+ m_pPcd->nEndPos = p->nStartPos;
+ static_cast<WW8PLCFx_PCD *>(m_pPcd->pPLCFx)->SetClipStart(
+ p->nStartPos);
+ }
+
+ }
+ else
+ {
+ p->pPLCFx->advance(); // next Group of Sprms
+ p->pMemPos = nullptr; // !!!
+ p->nSprmsLen = 0;
+ GetNewSprms( *p );
+ }
+ SAL_WARN_IF(p->nStartPos > p->nEndPos, "sw.ww8",
+ "End " << p->nEndPos << " before Start " << p->nStartPos);
+ }
+ }
+}
+
+void WW8PLCFMan::AdvNoSprm(short nIdx, bool bStart)
+{
+ /*
+ For the case of a piece table we slave the piece table attribute iterator
+ to the piece table and access it through that only. They are two separate
+ structures, but act together as one logical one. The attributes only go
+ to the next entry when the piece changes
+ */
+ WW8PLCFxDesc* p = &m_aD[nIdx];
+
+ if( p == m_pPcd )
+ {
+ AdvSprm(nIdx+1,bStart);
+ if( bStart )
+ p->nStartPos = m_aD[nIdx+1].nStartPos;
+ else
+ {
+ if (m_aD[nIdx+1].xIdStack->empty())
+ {
+ WW8PLCFx_PCD *pTemp = static_cast<WW8PLCFx_PCD*>(m_pPcd->pPLCFx);
+ /*
+ #i2325#
+ As per normal, go on to the next set of properties, i.e. we
+ have traversed over to the next piece. With a clipstart set
+ we are being told to reread the current piece sprms so as to
+ reapply them to a new chp or pap range.
+ */
+ if (pTemp->GetClipStart() == -1)
+ p->pPLCFx->advance();
+ p->pMemPos = nullptr;
+ p->nSprmsLen = 0;
+ GetNewSprms( m_aD[nIdx+1] );
+ GetNewNoSprms( *p );
+ if (pTemp->GetClipStart() != -1)
+ {
+ /*
+ #i2325#, now we will force our starting position to the
+ clipping start so as to force the application of these
+ sprms after the current pap/chp sprms so as to apply the
+ fastsave sprms to the current range.
+ */
+ p->nStartPos = pTemp->GetClipStart();
+ pTemp->SetClipStart(-1);
+ }
+ }
+ }
+ }
+ else
+ { // NoSprm without end
+ p->pPLCFx->advance();
+ p->pMemPos = nullptr; // MemPos invalid
+ p->nSprmsLen = 0;
+ GetNewNoSprms( *p );
+ }
+}
+
+void WW8PLCFMan::advance()
+{
+ bool bStart;
+ const sal_uInt16 nIdx = WhereIdx(&bStart);
+ if (nIdx < m_nPLCF)
+ {
+ WW8PLCFxDesc* p = &m_aD[nIdx];
+
+ p->bFirstSprm = true; // Default
+
+ if( p->pPLCFx->IsSprm() )
+ AdvSprm( nIdx, bStart );
+ else // NoSprm
+ AdvNoSprm( nIdx, bStart );
+ }
+}
+
+// return true for the beginning of an attribute or error,
+// false for the end of an attribute
+// remaining return values are delivered to the caller from WW8PclxManResults.
+bool WW8PLCFMan::Get(WW8PLCFManResult* pRes) const
+{
+ memset( pRes, 0, sizeof( WW8PLCFManResult ) );
+ bool bStart;
+ const sal_uInt16 nIdx = WhereIdx(&bStart);
+
+ if( nIdx >= m_nPLCF )
+ {
+ OSL_ENSURE( false, "Position not found" );
+ return true;
+ }
+
+ if( m_aD[nIdx].pPLCFx->IsSprm() )
+ {
+ if( bStart )
+ {
+ GetSprmStart( nIdx, pRes );
+ return true;
+ }
+ else
+ {
+ GetSprmEnd( nIdx, pRes );
+ return false;
+ }
+ }
+ else
+ {
+ if( bStart )
+ {
+ GetNoSprmStart( nIdx, pRes );
+ return true;
+ }
+ else
+ {
+ GetNoSprmEnd( nIdx, pRes );
+ return false;
+ }
+ }
+}
+
+sal_uInt16 WW8PLCFMan::GetColl() const
+{
+ if( m_pPap->pPLCFx )
+ return m_pPap->pPLCFx->GetIstd();
+ else
+ {
+ OSL_ENSURE( false, "GetColl without PLCF_Pap" );
+ return 0;
+ }
+}
+
+WW8PLCFx_FLD* WW8PLCFMan::GetField() const
+{
+ return static_cast<WW8PLCFx_FLD*>(m_pField->pPLCFx);
+}
+
+SprmResult WW8PLCFMan::HasParaSprm( sal_uInt16 nId ) const
+{
+ return static_cast<WW8PLCFx_Cp_FKP*>(m_pPap->pPLCFx)->HasSprm( nId );
+}
+
+SprmResult WW8PLCFMan::HasCharSprm( sal_uInt16 nId ) const
+{
+ return static_cast<WW8PLCFx_Cp_FKP*>(m_pChp->pPLCFx)->HasSprm( nId );
+}
+
+void WW8PLCFMan::HasCharSprm(sal_uInt16 nId,
+ std::vector<SprmResult> &rResult) const
+{
+ static_cast<WW8PLCFx_Cp_FKP*>(m_pChp->pPLCFx)->HasSprm(nId, rResult);
+}
+
+void WW8PLCFx::Save( WW8PLCFxSave1& rSave ) const
+{
+ rSave.nPLCFxPos = GetIdx();
+ rSave.nPLCFxPos2 = GetIdx2();
+ rSave.nPLCFxMemOfs = 0;
+ rSave.nStartFC = GetStartFc();
+}
+
+void WW8PLCFx::Restore( const WW8PLCFxSave1& rSave )
+{
+ SetIdx( rSave.nPLCFxPos );
+ SetIdx2( rSave.nPLCFxPos2 );
+ SetStartFc( rSave.nStartFC );
+}
+
+sal_uInt32 WW8PLCFx_Cp_FKP::GetIdx2() const
+{
+ return GetPCDIdx();
+}
+
+void WW8PLCFx_Cp_FKP::SetIdx2(sal_uInt32 nIdx)
+{
+ if( m_pPcd )
+ m_pPcd->SetIdx( nIdx );
+}
+
+void WW8PLCFx_Cp_FKP::Save( WW8PLCFxSave1& rSave ) const
+{
+ if (m_pFkp)
+ m_pFkp->IncMustRemainCache();
+ WW8PLCFx::Save( rSave );
+
+ rSave.nAttrStart = m_nAttrStart;
+ rSave.nAttrEnd = m_nAttrEnd;
+ rSave.bLineEnd = m_bLineEnd;
+}
+
+void WW8PLCFx_Cp_FKP::Restore( const WW8PLCFxSave1& rSave )
+{
+ WW8PLCFx::Restore( rSave );
+
+ m_nAttrStart = rSave.nAttrStart;
+ m_nAttrEnd = rSave.nAttrEnd;
+ m_bLineEnd = rSave.bLineEnd;
+
+ if (m_pFkp)
+ m_pFkp->DecMustRemainCache();
+}
+
+void WW8PLCFxDesc::Save( WW8PLCFxSave1& rSave ) const
+{
+ if( !pPLCFx )
+ return;
+
+ pPLCFx->Save( rSave );
+ if( !pPLCFx->IsSprm() )
+ return;
+
+ WW8PLCFxDesc aD;
+ aD.nStartPos = nOrigStartPos+nCpOfs;
+ aD.nCpOfs = rSave.nCpOfs = nCpOfs;
+ if (!(pPLCFx->SeekPos(aD.nStartPos)))
+ {
+ aD.nEndPos = WW8_CP_MAX;
+ pPLCFx->SetDirty(true);
+ }
+ pPLCFx->GetSprms(&aD);
+ pPLCFx->SetDirty(false);
+ aD.ReduceByOffset();
+ rSave.nStartCp = aD.nStartPos;
+ rSave.nPLCFxMemOfs = nOrigSprmsLen - nSprmsLen;
+}
+
+void WW8PLCFxDesc::Restore( const WW8PLCFxSave1& rSave )
+{
+ if( !pPLCFx )
+ return;
+
+ pPLCFx->Restore( rSave );
+ if( !pPLCFx->IsSprm() )
+ return;
+
+ WW8PLCFxDesc aD;
+ aD.nStartPos = rSave.nStartCp+rSave.nCpOfs;
+ nCpOfs = aD.nCpOfs = rSave.nCpOfs;
+ if (!(pPLCFx->SeekPos(aD.nStartPos)))
+ {
+ aD.nEndPos = WW8_CP_MAX;
+ pPLCFx->SetDirty(true);
+ }
+ pPLCFx->GetSprms(&aD);
+ pPLCFx->SetDirty(false);
+ aD.ReduceByOffset();
+
+ if (nOrigSprmsLen > aD.nSprmsLen)
+ {
+ //two entries exist for the same offset, cut and run
+ SAL_WARN("sw.ww8", "restored properties don't match saved properties, bailing out");
+ nSprmsLen = 0;
+ pMemPos = nullptr;
+ }
+ else
+ {
+ nSprmsLen = nOrigSprmsLen - rSave.nPLCFxMemOfs;
+ pMemPos = aD.pMemPos == nullptr ? nullptr : aD.pMemPos + rSave.nPLCFxMemOfs;
+ }
+}
+
+namespace
+{
+ sal_uInt32 Readcb(SvStream& rSt, ww::WordVersion eVer)
+ {
+ if (eVer <= ww::eWW2)
+ {
+ sal_uInt16 nShort(0);
+ rSt.ReadUInt16(nShort);
+ return nShort;
+ }
+ else
+ {
+ sal_uInt32 nLong(0);
+ rSt.ReadUInt32(nLong);
+ return nLong;
+ }
+ }
+}
+
+bool WW8Fib::GetBaseCp(ManTypes nType, WW8_CP * cp) const
+{
+ assert(cp != nullptr);
+ WW8_CP nOffset = 0;
+
+ switch (nType)
+ {
+ case MAN_TXBX_HDFT:
+ if (m_ccpTxbx < 0) {
+ return false;
+ }
+ nOffset = m_ccpTxbx;
+ [[fallthrough]];
+ case MAN_TXBX:
+ if (m_ccpEdn < 0 || m_ccpEdn > std::numeric_limits<WW8_CP>::max() - nOffset) {
+ return false;
+ }
+ nOffset += m_ccpEdn;
+ [[fallthrough]];
+ case MAN_EDN:
+ if (m_ccpAtn < 0 || m_ccpAtn > std::numeric_limits<WW8_CP>::max() - nOffset) {
+ return false;
+ }
+ nOffset += m_ccpAtn;
+ [[fallthrough]];
+ case MAN_AND:
+ if (m_ccpMcr < 0 || m_ccpMcr > std::numeric_limits<WW8_CP>::max() - nOffset) {
+ return false;
+ }
+ nOffset += m_ccpMcr;
+ /*
+ // fall through
+
+ A subdocument of this kind (MAN_MACRO) probably exists in some defunct
+ version of MSWord, but now ccpMcr is always 0. If some example that
+ uses this comes to light, this is the likely calculation required
+
+ case MAN_MACRO:
+ */
+ if (m_ccpHdr < 0 || m_ccpHdr > std::numeric_limits<WW8_CP>::max() - nOffset) {
+ return false;
+ }
+ nOffset += m_ccpHdr;
+ [[fallthrough]];
+ case MAN_HDFT:
+ if (m_ccpFootnote < 0 || m_ccpFootnote > std::numeric_limits<WW8_CP>::max() - nOffset) {
+ return false;
+ }
+ nOffset += m_ccpFootnote;
+ [[fallthrough]];
+ case MAN_FTN:
+ if (m_ccpText < 0 || m_ccpText > std::numeric_limits<WW8_CP>::max() - nOffset) {
+ return false;
+ }
+ nOffset += m_ccpText;
+ [[fallthrough]];
+ case MAN_MAINTEXT:
+ break;
+ }
+ *cp = nOffset;
+ return true;
+}
+
+ww::WordVersion WW8Fib::GetFIBVersion() const
+{
+ ww::WordVersion eVer = ww::eWW8;
+ /*
+ * Word for Windows 2 I think (1.X might work too if anyone has an example.
+ *
+ * 0xA59B for Word 1 for Windows
+ * 0xA59C for Word 1 for OS/2 "PM Word"
+ *
+ * Various pages claim that the fileformats of Word 1 and 2 for Windows are
+ * equivalent to Word for Macintosh 4 and 5. On the other hand
+ *
+ * wIdents for Word for Mac versions...
+ * 0xFE32 for Word 1
+ * 0xFE34 for Word 3
+ * 0xFE37 for Word 4 et 5.
+ *
+ * and this document
+ * http://cmsdoc.cern.ch/documents/docformat/CMS_CERN_LetterHead.word is
+ * claimed to be "Word 5 for Mac" by Office etc and has that wIdent, but
+ * its format isn't the same as that of Word 2 for windows. Nor is it
+ * the same as that of Word for DOS/PCWord 5
+ */
+ if (m_wIdent == 0xa59b || m_wIdent == 0xa59c)
+ eVer = ww::eWW1;
+ else if (m_wIdent == 0xa5db)
+ eVer = ww::eWW2;
+ else
+ {
+ switch (m_nVersion)
+ {
+ case 6:
+ eVer = ww::eWW6;
+ break;
+ case 7:
+ eVer = ww::eWW7;
+ break;
+ case 8:
+ eVer = ww::eWW8;
+ break;
+ }
+ }
+ return eVer;
+}
+
+WW8Fib::WW8Fib(SvStream& rSt, sal_uInt8 nWantedVersion, sal_uInt32 nOffset):
+ m_fDot(false), m_fGlsy(false), m_fComplex(false), m_fHasPic(false), m_cQuickSaves(0),
+ m_fEncrypted(false), m_fWhichTableStm(false), m_fReadOnlyRecommended(false),
+ m_fWriteReservation(false), m_fExtChar(false), m_fFarEast(false), m_fObfuscated(false),
+ m_fMac(false), m_fEmptySpecial(false), m_fLoadOverridePage(false), m_fFuturesavedUndo(false),
+ m_fWord97Saved(false), m_fWord2000Saved(false)
+ // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the
+ // above bit-field member initializations can be moved to the class definition
+{
+ // See [MS-DOC] 2.5.15 "How to read the FIB".
+ rSt.Seek( nOffset );
+ /*
+ note desired number, identify file version number
+ and check against desired number!
+ */
+ m_nVersion = nWantedVersion;
+ rSt.ReadUInt16( m_wIdent );
+ rSt.ReadUInt16( m_nFib );
+ rSt.ReadUInt16( m_nProduct );
+ if( ERRCODE_NONE != rSt.GetError() )
+ {
+ sal_Int16 nFibMin;
+ sal_Int16 nFibMax;
+ // note: 6 stands for "6 OR 7", 7 stands for "ONLY 7"
+ switch( m_nVersion )
+ {
+ case 6:
+ nFibMin = 0x0065; // from 101 WinWord 6.0
+ // 102 "
+ // and 103 WinWord 6.0 for Macintosh
+ // 104 "
+ nFibMax = 0x0069; // to 105 WinWord 95
+ break;
+ case 7:
+ nFibMin = 0x0069; // from 105 WinWord 95
+ nFibMax = 0x0069; // to 105 WinWord 95
+ break;
+ case 8:
+ nFibMin = 0x006A; // from 106 WinWord 97
+ nFibMax = 0x00c1; // to 193 WinWord 97 (?)
+ break;
+ default:
+ nFibMin = 0; // program error!
+ nFibMax = 0;
+ m_nFib = 1;
+ OSL_ENSURE( false, "nVersion not implemented!" );
+ break;
+ }
+ if ( (m_nFib < nFibMin) || (m_nFib > nFibMax) )
+ {
+ m_nFibError = ERR_SWG_READ_ERROR; // report error
+ return;
+ }
+ }
+
+ ww::WordVersion eVer = GetFIBVersion();
+
+ // helper vars for Ver67:
+ sal_Int16 pnChpFirst_Ver67=0;
+ sal_Int16 pnPapFirst_Ver67=0;
+ sal_Int16 cpnBteChp_Ver67=0;
+ sal_Int16 cpnBtePap_Ver67=0;
+
+ // read FIB
+ sal_uInt16 nTmpLid = 0;
+ rSt.ReadUInt16(nTmpLid);
+ m_lid = LanguageType(nTmpLid);
+ rSt.ReadInt16( m_pnNext );
+ sal_uInt8 aBits1(0);
+ rSt.ReadUChar( aBits1 );
+ sal_uInt8 aBits2(0);
+ rSt.ReadUChar( aBits2 );
+ rSt.ReadUInt16( m_nFibBack );
+ rSt.ReadUInt16( m_nHash );
+ rSt.ReadUInt16( m_nKey );
+ rSt.ReadUChar( m_envr );
+ sal_uInt8 aVer8Bits1(0); // only used starting with WinWord 8
+ rSt.ReadUChar( aVer8Bits1 ); // only have an empty reserve field under Ver67
+ // content from aVer8Bits1
+
+ // sal_uInt8 fMac :1;
+ // sal_uInt8 fEmptySpecial :1;
+ // sal_uInt8 fLoadOverridePage :1;
+ // sal_uInt8 fFuturesavedUndo :1;
+ // sal_uInt8 fWord97Saved :1;
+ // sal_uInt8 :3;
+ rSt.ReadUInt16( m_chse );
+ rSt.ReadUInt16( m_chseTables );
+ rSt.ReadInt32( m_fcMin );
+ rSt.ReadInt32( m_fcMac );
+
+// insertion for WW8
+ if (IsEightPlus(eVer))
+ {
+ rSt.ReadUInt16( m_csw );
+
+ // Marke: "rgsw" Beginning of the array of shorts
+ rSt.ReadUInt16( m_wMagicCreated );
+ rSt.ReadUInt16( m_wMagicRevised );
+ rSt.ReadUInt16( m_wMagicCreatedPrivate );
+ rSt.ReadUInt16( m_wMagicRevisedPrivate );
+ rSt.SeekRel( 9 * sizeof( sal_Int16 ) );
+
+ /*
+ // these are the 9 unused fields:
+ && (bVer67 || WW8ReadINT16( rSt, pnFbpChpFirst_W6 )) // 1
+ && (bVer67 || WW8ReadINT16( rSt, pnChpFirst_W6 )) // 2
+ && (bVer67 || WW8ReadINT16( rSt, cpnBteChp_W6 )) // 3
+ && (bVer67 || WW8ReadINT16( rSt, pnFbpPapFirst_W6 )) // 4
+ && (bVer67 || WW8ReadINT16( rSt, pnPapFirst_W6 )) // 5
+ && (bVer67 || WW8ReadINT16( rSt, cpnBtePap_W6 )) // 6
+ && (bVer67 || WW8ReadINT16( rSt, pnFbpLvcFirst_W6 )) // 7
+ && (bVer67 || WW8ReadINT16( rSt, pnLvcFirst_W6 )) // 8
+ && (bVer67 || WW8ReadINT16( rSt, cpnBteLvc_W6 )) // 9
+ */
+ sal_uInt16 nTmpFE = 0;
+ rSt.ReadUInt16(nTmpFE);
+ m_lidFE = LanguageType(nTmpFE);
+ rSt.ReadUInt16( m_clw );
+ }
+
+// end of the insertion for WW8
+
+ // Marke: "rglw" Beginning of the array of longs
+ rSt.ReadInt32( m_cbMac );
+
+ // ignore 2 longs, because they are unimportant
+ rSt.SeekRel( 2 * sizeof( sal_Int32) );
+
+ // skipping 2 more longs only at Ver67
+ if (IsSevenMinus(eVer))
+ rSt.SeekRel( 2 * sizeof( sal_Int32) );
+
+ rSt.ReadInt32( m_ccpText );
+ rSt.ReadInt32( m_ccpFootnote );
+ rSt.ReadInt32( m_ccpHdr );
+ rSt.ReadInt32( m_ccpMcr );
+ rSt.ReadInt32( m_ccpAtn );
+ rSt.ReadInt32( m_ccpEdn );
+ rSt.ReadInt32( m_ccpTxbx );
+ rSt.ReadInt32( m_ccpHdrTxbx );
+
+ // only skip one more long at Ver67
+ if (IsSevenMinus(eVer))
+ rSt.SeekRel( 1 * sizeof( sal_Int32) );
+ else
+ {
+// insertion for WW8
+ rSt.ReadInt32( m_pnFbpChpFirst );
+ rSt.ReadInt32( m_pnChpFirst );
+ rSt.ReadInt32( m_cpnBteChp );
+ rSt.ReadInt32( m_pnFbpPapFirst );
+ rSt.ReadInt32( m_pnPapFirst );
+ rSt.ReadInt32( m_cpnBtePap );
+ rSt.ReadInt32( m_pnFbpLvcFirst );
+ rSt.ReadInt32( m_pnLvcFirst );
+ rSt.ReadInt32( m_cpnBteLvc );
+ rSt.ReadInt32( m_fcIslandFirst );
+ rSt.ReadInt32( m_fcIslandLim );
+ rSt.ReadUInt16( m_cfclcb );
+
+ // Read cswNew to find out if nFib should be ignored.
+ sal_uInt64 nPos = rSt.Tell();
+ rSt.SeekRel(m_cfclcb * 8);
+ if (rSt.good() && rSt.remainingSize() >= 2)
+ {
+ rSt.ReadUInt16(m_cswNew);
+ }
+ rSt.Seek(nPos);
+ }
+
+// end of the insertion for WW8
+
+ // Marke: "rgfclcb" Beginning of array of FC/LCB pairs.
+ rSt.ReadInt32( m_fcStshfOrig );
+ m_lcbStshfOrig = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcStshf );
+ m_lcbStshf = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffndRef );
+ m_lcbPlcffndRef = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffndText );
+ m_lcbPlcffndText = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfandRef );
+ m_lcbPlcfandRef = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfandText );
+ m_lcbPlcfandText = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfsed );
+ m_lcbPlcfsed = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfpad );
+ m_lcbPlcfpad = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfphe );
+ m_lcbPlcfphe = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcSttbfglsy );
+ m_lcbSttbfglsy = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfglsy );
+ m_lcbPlcfglsy = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfhdd );
+ m_lcbPlcfhdd = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfbteChpx );
+ m_lcbPlcfbteChpx = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfbtePapx );
+ m_lcbPlcfbtePapx = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfsea );
+ m_lcbPlcfsea = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcSttbfffn );
+ m_lcbSttbfffn = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffldMom );
+ m_lcbPlcffldMom = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffldHdr );
+ m_lcbPlcffldHdr = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffldFootnote );
+ m_lcbPlcffldFootnote = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffldAtn );
+ m_lcbPlcffldAtn = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcffldMcr );
+ m_lcbPlcffldMcr = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcSttbfbkmk );
+ m_lcbSttbfbkmk = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfbkf );
+ m_lcbPlcfbkf = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfbkl );
+ m_lcbPlcfbkl = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcCmds );
+ m_lcbCmds = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfmcr );
+ m_lcbPlcfmcr = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcSttbfmcr );
+ m_lcbSttbfmcr = Readcb(rSt, eVer);
+ if (eVer >= ww::eWW2)
+ {
+ rSt.ReadInt32( m_fcPrDrvr );
+ m_lcbPrDrvr = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPrEnvPort );
+ m_lcbPrEnvPort = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPrEnvLand );
+ m_lcbPrEnvLand = Readcb(rSt, eVer);
+ }
+ else
+ {
+ rSt.ReadInt32( m_fcPrEnvPort );
+ m_lcbPrEnvPort = Readcb(rSt, eVer);
+ }
+ rSt.ReadInt32( m_fcWss );
+ m_lcbWss = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcDop );
+ m_lcbDop = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcSttbfAssoc );
+ m_lcbSttbfAssoc = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcClx );
+ m_lcbClx = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcPlcfpgdFootnote );
+ m_lcbPlcfpgdFootnote = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcAutosaveSource );
+ m_lcbAutosaveSource = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcGrpStAtnOwners );
+ m_lcbGrpStAtnOwners = Readcb(rSt, eVer);
+ rSt.ReadInt32( m_fcSttbfAtnbkmk );
+ m_lcbSttbfAtnbkmk = Readcb(rSt, eVer);
+
+ // only skip more shot at Ver67
+ if (IsSevenMinus(eVer))
+ {
+ if (eVer == ww::eWW1)
+ rSt.SeekRel(1*sizeof(sal_Int32));
+ rSt.SeekRel(1*sizeof(sal_Int16));
+
+ if (eVer >= ww::eWW2)
+ {
+ rSt.ReadInt16(pnChpFirst_Ver67);
+ rSt.ReadInt16(pnPapFirst_Ver67);
+ }
+ rSt.ReadInt16(cpnBteChp_Ver67);
+ rSt.ReadInt16(cpnBtePap_Ver67);
+ }
+
+ if (eVer > ww::eWW2)
+ {
+ rSt.ReadInt32( m_fcPlcfdoaMom );
+ rSt.ReadInt32( m_lcbPlcfdoaMom );
+ rSt.ReadInt32( m_fcPlcfdoaHdr );
+ rSt.ReadInt32( m_lcbPlcfdoaHdr );
+ rSt.ReadInt32( m_fcPlcfspaMom );
+ rSt.ReadInt32( m_lcbPlcfspaMom );
+ rSt.ReadInt32( m_fcPlcfspaHdr );
+ rSt.ReadInt32( m_lcbPlcfspaHdr );
+
+ rSt.ReadInt32( m_fcPlcfAtnbkf );
+ rSt.ReadInt32( m_lcbPlcfAtnbkf );
+ rSt.ReadInt32( m_fcPlcfAtnbkl );
+ rSt.ReadInt32( m_lcbPlcfAtnbkl );
+ rSt.ReadInt32( m_fcPms );
+ rSt.ReadInt32( m_lcbPMS );
+ rSt.ReadInt32( m_fcFormFieldSttbf );
+ rSt.ReadInt32( m_lcbFormFieldSttbf );
+ rSt.ReadInt32( m_fcPlcfendRef );
+ rSt.ReadInt32( m_lcbPlcfendRef );
+ rSt.ReadInt32( m_fcPlcfendText );
+ rSt.ReadInt32( m_lcbPlcfendText );
+ rSt.ReadInt32( m_fcPlcffldEdn );
+ rSt.ReadInt32( m_lcbPlcffldEdn );
+ rSt.ReadInt32( m_fcPlcfpgdEdn );
+ rSt.ReadInt32( m_lcbPlcfpgdEdn );
+ rSt.ReadInt32( m_fcDggInfo );
+ rSt.ReadInt32( m_lcbDggInfo );
+ rSt.ReadInt32( m_fcSttbfRMark );
+ rSt.ReadInt32( m_lcbSttbfRMark );
+ rSt.ReadInt32( m_fcSttbfCaption );
+ rSt.ReadInt32( m_lcbSttbfCaption );
+ rSt.ReadInt32( m_fcSttbAutoCaption );
+ rSt.ReadInt32( m_lcbSttbAutoCaption );
+ rSt.ReadInt32( m_fcPlcfwkb );
+ rSt.ReadInt32( m_lcbPlcfwkb );
+ rSt.ReadInt32( m_fcPlcfspl );
+ rSt.ReadInt32( m_lcbPlcfspl );
+ rSt.ReadInt32( m_fcPlcftxbxText );
+ rSt.ReadInt32( m_lcbPlcftxbxText );
+ rSt.ReadInt32( m_fcPlcffldTxbx );
+ rSt.ReadInt32( m_lcbPlcffldTxbx );
+ rSt.ReadInt32( m_fcPlcfHdrtxbxText );
+ rSt.ReadInt32( m_lcbPlcfHdrtxbxText );
+ rSt.ReadInt32( m_fcPlcffldHdrTxbx );
+ rSt.ReadInt32( m_lcbPlcffldHdrTxbx );
+ rSt.ReadInt32( m_fcStwUser );
+ rSt.ReadUInt32( m_lcbStwUser );
+ rSt.ReadInt32( m_fcSttbttmbd );
+ rSt.ReadUInt32( m_lcbSttbttmbd );
+ }
+
+ if( ERRCODE_NONE == rSt.GetError() )
+ {
+ // set bit flag
+ m_fDot = aBits1 & 0x01 ;
+ m_fGlsy = ( aBits1 & 0x02 ) >> 1;
+ m_fComplex = ( aBits1 & 0x04 ) >> 2;
+ m_fHasPic = ( aBits1 & 0x08 ) >> 3;
+ m_cQuickSaves = ( aBits1 & 0xf0 ) >> 4;
+ m_fEncrypted = aBits2 & 0x01 ;
+ m_fWhichTableStm= ( aBits2 & 0x02 ) >> 1;
+ m_fReadOnlyRecommended = (aBits2 & 0x4) >> 2;
+ m_fWriteReservation = (aBits2 & 0x8) >> 3;
+ m_fExtChar = ( aBits2 & 0x10 ) >> 4;
+ // dummy = ( aBits2 & 0x20 ) >> 5;
+ m_fFarEast = ( aBits2 & 0x40 ) >> 6; // #i90932#
+ // dummy = ( aBits2 & 0x80 ) >> 7;
+
+ /*
+ p.r.n. fill targeted variable with xxx_Ver67
+ or set flags
+ */
+ if (IsSevenMinus(eVer))
+ {
+ m_pnChpFirst = pnChpFirst_Ver67;
+ m_pnPapFirst = pnPapFirst_Ver67;
+ m_cpnBteChp = cpnBteChp_Ver67;
+ m_cpnBtePap = cpnBtePap_Ver67;
+ }
+ else if (IsEightPlus(eVer))
+ {
+ m_fMac = aVer8Bits1 & 0x01 ;
+ m_fEmptySpecial = ( aVer8Bits1 & 0x02 ) >> 1;
+ m_fLoadOverridePage = ( aVer8Bits1 & 0x04 ) >> 2;
+ m_fFuturesavedUndo = ( aVer8Bits1 & 0x08 ) >> 3;
+ m_fWord97Saved = ( aVer8Bits1 & 0x10 ) >> 4;
+ m_fWord2000Saved = ( aVer8Bits1 & 0x20 ) >> 5;
+
+ /*
+ especially for WW8:
+ identify the values for PLCF and PLF LFO
+ and PLCF for the textbox break descriptors
+ */
+ sal_uInt64 nOldPos = rSt.Tell();
+
+ rSt.Seek( 0x02da );
+ rSt.ReadInt32( m_fcSttbFnm );
+ rSt.ReadInt32( m_lcbSttbFnm );
+ rSt.ReadInt32( m_fcPlcfLst );
+ rSt.ReadInt32( m_lcbPlcfLst );
+ rSt.ReadInt32( m_fcPlfLfo );
+ rSt.ReadInt32( m_lcbPlfLfo );
+ rSt.ReadInt32( m_fcPlcftxbxBkd );
+ rSt.ReadInt32( m_lcbPlcftxbxBkd );
+ rSt.ReadInt32( m_fcPlcfHdrtxbxBkd );
+ rSt.ReadInt32( m_lcbPlcfHdrtxbxBkd );
+ if( ERRCODE_NONE != rSt.GetError() )
+ {
+ m_nFibError = ERR_SWG_READ_ERROR;
+ }
+
+ rSt.Seek( 0x372 ); // fcSttbListNames
+ rSt.ReadInt32( m_fcSttbListNames );
+ rSt.ReadInt32( m_lcbSttbListNames );
+
+ if (m_cfclcb > 93)
+ {
+ rSt.Seek( 0x382 ); // MagicTables
+ rSt.ReadInt32( m_fcPlcfTch );
+ rSt.ReadInt32( m_lcbPlcfTch );
+ }
+
+ if (m_cfclcb > 113)
+ {
+ rSt.Seek( 0x41A ); // new ATRD
+ rSt.ReadInt32( m_fcAtrdExtra );
+ rSt.ReadUInt32( m_lcbAtrdExtra );
+ }
+
+ // Factoid bookmarks
+ if (m_cfclcb > 134)
+ {
+ rSt.Seek(0x432);
+ rSt.ReadInt32(m_fcPlcfBkfFactoid);
+ rSt.ReadUInt32(m_lcbPlcfBkfFactoid);
+
+ rSt.Seek(0x442);
+ rSt.ReadInt32(m_fcPlcfBklFactoid);
+ rSt.ReadUInt32(m_lcbPlcfBklFactoid);
+
+ rSt.Seek(0x44a);
+ rSt.ReadInt32(m_fcFactoidData);
+ rSt.ReadUInt32(m_lcbFactoidData);
+ }
+
+ if( ERRCODE_NONE != rSt.GetError() )
+ m_nFibError = ERR_SWG_READ_ERROR;
+
+ rSt.Seek( 0x5bc ); // Actual nFib introduced in Word 2003
+ rSt.ReadUInt16( m_nFib_actual );
+
+ rSt.Seek( nOldPos );
+ }
+ }
+ else
+ {
+ m_nFibError = ERR_SWG_READ_ERROR; // report error
+ }
+}
+
+WW8Fib::WW8Fib(sal_uInt8 nVer, bool bDot):
+ m_nVersion(nVer), m_fDot(false), m_fGlsy(false), m_fComplex(false), m_fHasPic(false), m_cQuickSaves(0),
+ m_fEncrypted(false), m_fWhichTableStm(false), m_fReadOnlyRecommended(false),
+ m_fWriteReservation(false), m_fExtChar(false), m_fFarEast(false), m_fObfuscated(false),
+ m_fMac(false), m_fEmptySpecial(false), m_fLoadOverridePage(false), m_fFuturesavedUndo(false),
+ m_fWord97Saved(false), m_fWord2000Saved(false)
+ // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the
+ // above bit-field member initializations can be moved to the class definition
+{
+ if (8 == nVer)
+ {
+ m_fcMin = 0x800;
+ m_wIdent = 0xa5ec;
+ m_nFib = 0x0101;
+ m_nFibBack = 0xbf;
+ m_nProduct = 0x204D;
+ m_fDot = bDot;
+
+ m_csw = 0x0e; // Is this really necessary???
+ m_cfclcb = 0x88; // -""-
+ m_clw = 0x16; // -""-
+ m_pnFbpChpFirst = m_pnFbpPapFirst = m_pnFbpLvcFirst = 0x000fffff;
+ m_fExtChar = true;
+ m_fWord97Saved = m_fWord2000Saved = true;
+
+ // Just a fancy way to write 'Caolan80'.
+ m_wMagicCreated = 0x6143;
+ m_wMagicRevised = 0x6C6F;
+ m_wMagicCreatedPrivate = 0x6E61;
+ m_wMagicRevisedPrivate = 0x3038;
+ }
+ else
+ {
+ m_fcMin = 0x300;
+ m_wIdent = 0xa5dc;
+ m_nFib = m_nFibBack = 0x65;
+ m_nProduct = 0xc02d;
+ }
+
+ //If nFib is 0x00D9 or greater, then cQuickSaves MUST be 0xF
+ m_cQuickSaves = m_nFib >= 0x00D9 ? 0xF : 0;
+
+ // --> #i90932#
+ m_lid = LanguageType(0x409); // LANGUAGE_ENGLISH_US
+
+ LanguageType nLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+ m_fFarEast = MsLangId::isCJK(nLang);
+ if (m_fFarEast)
+ m_lidFE = nLang;
+ else
+ m_lidFE = m_lid;
+
+ LanguageTag aLanguageTag( m_lid );
+ LocaleDataWrapper aLocaleWrapper( std::move(aLanguageTag) );
+ m_nNumDecimalSep = aLocaleWrapper.getNumDecimalSep()[0];
+}
+
+
+void WW8Fib::WriteHeader(SvStream& rStrm)
+{
+ bool bVer8 = 8 == m_nVersion;
+
+ size_t nUnencryptedHdr = bVer8 ? 0x44 : 0x24;
+ std::unique_ptr<sal_uInt8[]> pDataPtr( new sal_uInt8[ nUnencryptedHdr ] );
+ sal_uInt8 *pData = pDataPtr.get();
+ memset( pData, 0, nUnencryptedHdr );
+
+ m_cbMac = rStrm.TellEnd();
+
+ Set_UInt16( pData, m_wIdent );
+ Set_UInt16( pData, m_nFib );
+ Set_UInt16( pData, m_nProduct );
+ Set_UInt16( pData, static_cast<sal_uInt16>(m_lid) );
+ Set_UInt16( pData, m_pnNext );
+
+ sal_uInt16 nBits16 = 0;
+ if( m_fDot ) nBits16 |= 0x0001;
+ if( m_fGlsy) nBits16 |= 0x0002;
+ if( m_fComplex ) nBits16 |= 0x0004;
+ if( m_fHasPic ) nBits16 |= 0x0008;
+ nBits16 |= (0xf0 & ( m_cQuickSaves << 4 ));
+ if( m_fEncrypted ) nBits16 |= 0x0100;
+ if( m_fWhichTableStm ) nBits16 |= 0x0200;
+
+ if (m_fReadOnlyRecommended)
+ nBits16 |= 0x0400;
+ if (m_fWriteReservation)
+ nBits16 |= 0x0800;
+
+ if( m_fExtChar ) nBits16 |= 0x1000;
+ if( m_fFarEast ) nBits16 |= 0x4000; // #i90932#
+ if( m_fObfuscated ) nBits16 |= 0x8000;
+ Set_UInt16( pData, nBits16 );
+
+ Set_UInt16( pData, m_nFibBack );
+ Set_UInt16( pData, m_nHash );
+ Set_UInt16( pData, m_nKey );
+ Set_UInt8( pData, m_envr );
+
+ sal_uInt8 nBits8 = 0;
+ if( bVer8 )
+ {
+ if( m_fMac ) nBits8 |= 0x0001;
+ if( m_fEmptySpecial ) nBits8 |= 0x0002;
+ if( m_fLoadOverridePage ) nBits8 |= 0x0004;
+ if( m_fFuturesavedUndo ) nBits8 |= 0x0008;
+ if( m_fWord97Saved ) nBits8 |= 0x0010;
+ if( m_fWord2000Saved ) nBits8 |= 0x0020;
+ }
+ // under Ver67 these are only reserved
+ Set_UInt8( pData, nBits8 );
+
+ Set_UInt16( pData, m_chse );
+ Set_UInt16( pData, m_chseTables );
+ Set_UInt32( pData, m_fcMin );
+ Set_UInt32( pData, m_fcMac );
+
+// insertion for WW8
+
+ // Marke: "rgsw" Beginning of the array of shorts
+ if( bVer8 )
+ {
+ Set_UInt16( pData, m_csw );
+ Set_UInt16( pData, m_wMagicCreated );
+ Set_UInt16( pData, m_wMagicRevised );
+ Set_UInt16( pData, m_wMagicCreatedPrivate );
+ Set_UInt16( pData, m_wMagicRevisedPrivate );
+ pData += 9 * sizeof( sal_Int16 );
+ Set_UInt16( pData, static_cast<sal_uInt16>(m_lidFE) );
+ Set_UInt16( pData, m_clw );
+ }
+
+// end of the insertion for WW8
+
+ // Marke: "rglw" Beginning of the array of longs
+ Set_UInt32( pData, m_cbMac );
+
+ rStrm.WriteBytes(pDataPtr.get(), nUnencryptedHdr);
+}
+
+void WW8Fib::Write(SvStream& rStrm)
+{
+ bool bVer8 = 8 == m_nVersion;
+
+ WriteHeader( rStrm );
+
+ size_t nUnencryptedHdr = bVer8 ? 0x44 : 0x24;
+
+ std::unique_ptr<sal_uInt8[]> pDataPtr( new sal_uInt8[ m_fcMin - nUnencryptedHdr ] );
+ sal_uInt8 *pData = pDataPtr.get();
+ memset( pData, 0, m_fcMin - nUnencryptedHdr );
+
+ m_cbMac = rStrm.TellEnd();
+
+ // ignore 2 longs, because they are unimportant
+ pData += 2 * sizeof( sal_Int32);
+
+ // skipping 2 more longs only at Ver67
+ if( !bVer8 )
+ pData += 2 * sizeof( sal_Int32);
+
+ Set_UInt32( pData, m_ccpText );
+ Set_UInt32( pData, m_ccpFootnote );
+ Set_UInt32( pData, m_ccpHdr );
+ Set_UInt32( pData, m_ccpMcr );
+ Set_UInt32( pData, m_ccpAtn );
+ Set_UInt32( pData, m_ccpEdn );
+ Set_UInt32( pData, m_ccpTxbx );
+ Set_UInt32( pData, m_ccpHdrTxbx );
+
+ // only skip one more long at Ver67
+ if( !bVer8 )
+ pData += 1 * sizeof( sal_Int32);
+
+// insertion for WW8
+ if( bVer8 )
+ {
+ Set_UInt32( pData, m_pnFbpChpFirst );
+ Set_UInt32( pData, m_pnChpFirst );
+ Set_UInt32( pData, m_cpnBteChp );
+ Set_UInt32( pData, m_pnFbpPapFirst );
+ Set_UInt32( pData, m_pnPapFirst );
+ Set_UInt32( pData, m_cpnBtePap );
+ Set_UInt32( pData, m_pnFbpLvcFirst );
+ Set_UInt32( pData, m_pnLvcFirst );
+ Set_UInt32( pData, m_cpnBteLvc );
+ Set_UInt32( pData, m_fcIslandFirst );
+ Set_UInt32( pData, m_fcIslandLim );
+ Set_UInt16( pData, m_cfclcb );
+ }
+// end of the insertion for WW8
+
+ // Marke: "rgfclcb" Beginning of array of FC/LCB pairs.
+ Set_UInt32( pData, m_fcStshfOrig );
+ Set_UInt32( pData, m_lcbStshfOrig );
+ Set_UInt32( pData, m_fcStshf );
+ Set_UInt32( pData, m_lcbStshf );
+ Set_UInt32( pData, m_fcPlcffndRef );
+ Set_UInt32( pData, m_lcbPlcffndRef );
+ Set_UInt32( pData, m_fcPlcffndText );
+ Set_UInt32( pData, m_lcbPlcffndText );
+ Set_UInt32( pData, m_fcPlcfandRef );
+ Set_UInt32( pData, m_lcbPlcfandRef );
+ Set_UInt32( pData, m_fcPlcfandText );
+ Set_UInt32( pData, m_lcbPlcfandText );
+ Set_UInt32( pData, m_fcPlcfsed );
+ Set_UInt32( pData, m_lcbPlcfsed );
+ Set_UInt32( pData, m_fcPlcfpad );
+ Set_UInt32( pData, m_lcbPlcfpad );
+ Set_UInt32( pData, m_fcPlcfphe );
+ Set_UInt32( pData, m_lcbPlcfphe );
+ Set_UInt32( pData, m_fcSttbfglsy );
+ Set_UInt32( pData, m_lcbSttbfglsy );
+ Set_UInt32( pData, m_fcPlcfglsy );
+ Set_UInt32( pData, m_lcbPlcfglsy );
+ Set_UInt32( pData, m_fcPlcfhdd );
+ Set_UInt32( pData, m_lcbPlcfhdd );
+ Set_UInt32( pData, m_fcPlcfbteChpx );
+ Set_UInt32( pData, m_lcbPlcfbteChpx );
+ Set_UInt32( pData, m_fcPlcfbtePapx );
+ Set_UInt32( pData, m_lcbPlcfbtePapx );
+ Set_UInt32( pData, m_fcPlcfsea );
+ Set_UInt32( pData, m_lcbPlcfsea );
+ Set_UInt32( pData, m_fcSttbfffn );
+ Set_UInt32( pData, m_lcbSttbfffn );
+ Set_UInt32( pData, m_fcPlcffldMom );
+ Set_UInt32( pData, m_lcbPlcffldMom );
+ Set_UInt32( pData, m_fcPlcffldHdr );
+ Set_UInt32( pData, m_lcbPlcffldHdr );
+ Set_UInt32( pData, m_fcPlcffldFootnote );
+ Set_UInt32( pData, m_lcbPlcffldFootnote );
+ Set_UInt32( pData, m_fcPlcffldAtn );
+ Set_UInt32( pData, m_lcbPlcffldAtn );
+ Set_UInt32( pData, m_fcPlcffldMcr );
+ Set_UInt32( pData, m_lcbPlcffldMcr );
+ Set_UInt32( pData, m_fcSttbfbkmk );
+ Set_UInt32( pData, m_lcbSttbfbkmk );
+ Set_UInt32( pData, m_fcPlcfbkf );
+ Set_UInt32( pData, m_lcbPlcfbkf );
+ Set_UInt32( pData, m_fcPlcfbkl );
+ Set_UInt32( pData, m_lcbPlcfbkl );
+ Set_UInt32( pData, m_fcCmds );
+ Set_UInt32( pData, m_lcbCmds );
+ Set_UInt32( pData, m_fcPlcfmcr );
+ Set_UInt32( pData, m_lcbPlcfmcr );
+ Set_UInt32( pData, m_fcSttbfmcr );
+ Set_UInt32( pData, m_lcbSttbfmcr );
+ Set_UInt32( pData, m_fcPrDrvr );
+ Set_UInt32( pData, m_lcbPrDrvr );
+ Set_UInt32( pData, m_fcPrEnvPort );
+ Set_UInt32( pData, m_lcbPrEnvPort );
+ Set_UInt32( pData, m_fcPrEnvLand );
+ Set_UInt32( pData, m_lcbPrEnvLand );
+ Set_UInt32( pData, m_fcWss );
+ Set_UInt32( pData, m_lcbWss );
+ Set_UInt32( pData, m_fcDop );
+ Set_UInt32( pData, m_lcbDop );
+ Set_UInt32( pData, m_fcSttbfAssoc );
+ Set_UInt32( pData, m_lcbSttbfAssoc );
+ Set_UInt32( pData, m_fcClx );
+ Set_UInt32( pData, m_lcbClx );
+ Set_UInt32( pData, m_fcPlcfpgdFootnote );
+ Set_UInt32( pData, m_lcbPlcfpgdFootnote );
+ Set_UInt32( pData, m_fcAutosaveSource );
+ Set_UInt32( pData, m_lcbAutosaveSource );
+ Set_UInt32( pData, m_fcGrpStAtnOwners );
+ Set_UInt32( pData, m_lcbGrpStAtnOwners );
+ Set_UInt32( pData, m_fcSttbfAtnbkmk );
+ Set_UInt32( pData, m_lcbSttbfAtnbkmk );
+
+ // only skip one more short at Ver67
+ if( !bVer8 )
+ {
+ pData += 1*sizeof( sal_Int16);
+ Set_UInt16( pData, o3tl::narrowing<sal_uInt16>(m_pnChpFirst) );
+ Set_UInt16( pData, o3tl::narrowing<sal_uInt16>(m_pnPapFirst) );
+ Set_UInt16( pData, o3tl::narrowing<sal_uInt16>(m_cpnBteChp) );
+ Set_UInt16( pData, o3tl::narrowing<sal_uInt16>(m_cpnBtePap) );
+ }
+
+ Set_UInt32( pData, m_fcPlcfdoaMom ); // only at Ver67, in Ver8 unused
+ Set_UInt32( pData, m_lcbPlcfdoaMom ); // only at Ver67, in Ver8 unused
+ Set_UInt32( pData, m_fcPlcfdoaHdr ); // only at Ver67, in Ver8 unused
+ Set_UInt32( pData, m_lcbPlcfdoaHdr ); // only at Ver67, in Ver8 unused
+
+ Set_UInt32( pData, m_fcPlcfspaMom ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_lcbPlcfspaMom ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_fcPlcfspaHdr ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_lcbPlcfspaHdr ); // in Ver67 empty reserve
+
+ Set_UInt32( pData, m_fcPlcfAtnbkf );
+ Set_UInt32( pData, m_lcbPlcfAtnbkf );
+ Set_UInt32( pData, m_fcPlcfAtnbkl );
+ Set_UInt32( pData, m_lcbPlcfAtnbkl );
+ Set_UInt32( pData, m_fcPms );
+ Set_UInt32( pData, m_lcbPMS );
+ Set_UInt32( pData, m_fcFormFieldSttbf );
+ Set_UInt32( pData, m_lcbFormFieldSttbf );
+ Set_UInt32( pData, m_fcPlcfendRef );
+ Set_UInt32( pData, m_lcbPlcfendRef );
+ Set_UInt32( pData, m_fcPlcfendText );
+ Set_UInt32( pData, m_lcbPlcfendText );
+ Set_UInt32( pData, m_fcPlcffldEdn );
+ Set_UInt32( pData, m_lcbPlcffldEdn );
+ Set_UInt32( pData, m_fcPlcfpgdEdn );
+ Set_UInt32( pData, m_lcbPlcfpgdEdn );
+ Set_UInt32( pData, m_fcDggInfo ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_lcbDggInfo ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_fcSttbfRMark );
+ Set_UInt32( pData, m_lcbSttbfRMark );
+ Set_UInt32( pData, m_fcSttbfCaption );
+ Set_UInt32( pData, m_lcbSttbfCaption );
+ Set_UInt32( pData, m_fcSttbAutoCaption );
+ Set_UInt32( pData, m_lcbSttbAutoCaption );
+ Set_UInt32( pData, m_fcPlcfwkb );
+ Set_UInt32( pData, m_lcbPlcfwkb );
+ Set_UInt32( pData, m_fcPlcfspl ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_lcbPlcfspl ); // in Ver67 empty reserve
+ Set_UInt32( pData, m_fcPlcftxbxText );
+ Set_UInt32( pData, m_lcbPlcftxbxText );
+ Set_UInt32( pData, m_fcPlcffldTxbx );
+ Set_UInt32( pData, m_lcbPlcffldTxbx );
+ Set_UInt32( pData, m_fcPlcfHdrtxbxText );
+ Set_UInt32( pData, m_lcbPlcfHdrtxbxText );
+ Set_UInt32( pData, m_fcPlcffldHdrTxbx );
+ Set_UInt32( pData, m_lcbPlcffldHdrTxbx );
+
+ if( bVer8 )
+ {
+ pData += 0x2da - 0x27a; // Pos + Offset (fcPlcfLst - fcStwUser)
+ Set_UInt32( pData, m_fcSttbFnm);
+ Set_UInt32( pData, m_lcbSttbFnm);
+ Set_UInt32( pData, m_fcPlcfLst );
+ Set_UInt32( pData, m_lcbPlcfLst );
+ Set_UInt32( pData, m_fcPlfLfo );
+ Set_UInt32( pData, m_lcbPlfLfo );
+ Set_UInt32( pData, m_fcPlcftxbxBkd );
+ Set_UInt32( pData, m_lcbPlcftxbxBkd );
+ Set_UInt32( pData, m_fcPlcfHdrtxbxBkd );
+ Set_UInt32( pData, m_lcbPlcfHdrtxbxBkd );
+
+ pData += 0x372 - 0x302; // Pos + Offset (fcSttbListNames - fcDocUndo)
+ Set_UInt32( pData, m_fcSttbListNames );
+ Set_UInt32( pData, m_lcbSttbListNames );
+
+ pData += 0x382 - 0x37A;
+ Set_UInt32( pData, m_fcPlcfTch );
+ Set_UInt32( pData, m_lcbPlcfTch );
+
+ pData += 0x3FA - 0x38A;
+ Set_UInt16( pData, sal_uInt16(0x0002));
+ Set_UInt16( pData, sal_uInt16(0x00D9));
+
+ pData += 0x41A - 0x3FE;
+ Set_UInt32( pData, m_fcAtrdExtra );
+ Set_UInt32( pData, m_lcbAtrdExtra );
+
+ pData += 0x42a - 0x422;
+ Set_UInt32(pData, m_fcSttbfBkmkFactoid);
+ Set_UInt32(pData, m_lcbSttbfBkmkFactoid);
+ Set_UInt32(pData, m_fcPlcfBkfFactoid);
+ Set_UInt32(pData, m_lcbPlcfBkfFactoid);
+
+ pData += 0x442 - 0x43A;
+ Set_UInt32(pData, m_fcPlcfBklFactoid);
+ Set_UInt32(pData, m_lcbPlcfBklFactoid);
+ Set_UInt32(pData, m_fcFactoidData);
+ Set_UInt32(pData, m_lcbFactoidData);
+
+ pData += 0x4BA - 0x452;
+ Set_UInt32(pData, m_fcPlcffactoid);
+ Set_UInt32(pData, m_lcbPlcffactoid);
+
+ pData += 0x4DA - 0x4c2;
+ Set_UInt32( pData, m_fcHplxsdr );
+ Set_UInt32( pData, 0);
+ }
+
+ rStrm.WriteBytes(pDataPtr.get(), m_fcMin - nUnencryptedHdr);
+}
+
+rtl_TextEncoding WW8Fib::GetFIBCharset(sal_uInt16 chs, LanguageType nLidLocale)
+{
+ OSL_ENSURE(chs <= 0x100, "overflowed winword charset set");
+ if (chs == 0x0100)
+ return RTL_TEXTENCODING_APPLE_ROMAN;
+ if (chs == 0 && static_cast<sal_uInt16>(nLidLocale) >= 999)
+ {
+ /*
+ nLidLocale:
+ language stamp -- localized version In pre-WinWord 2.0 files this
+ value was the nLocale. If value is < 999, then it is the nLocale,
+ otherwise it is the lid.
+ */
+ css::lang::Locale aLocale(LanguageTag::convertToLocale(nLidLocale));
+ return msfilter::util::getBestTextEncodingFromLocale(aLocale);
+ }
+ return rtl_getTextEncodingFromWindowsCharset(static_cast<sal_uInt8>(chs));
+}
+
+MSOFactoidType::MSOFactoidType()
+ : m_nId(0)
+{
+}
+
+namespace MSOPBString
+{
+static OUString Read(SvStream& rStream)
+{
+ OUString aRet;
+
+ sal_uInt16 nBuf(0);
+ rStream.ReadUInt16(nBuf);
+ sal_uInt16 nCch = nBuf & 0x7fff; // Bits 1..15.
+ bool bAnsiString = (nBuf & (1 << 15)) >> 15; // 16th bit.
+ if (bAnsiString)
+ aRet = OStringToOUString(read_uInt8s_ToOString(rStream, nCch), RTL_TEXTENCODING_ASCII_US);
+ else
+ aRet = read_uInt16s_ToOUString(rStream, nCch);
+
+ return aRet;
+}
+
+static void Write(std::u16string_view aString, SvStream& rStream)
+{
+ sal_uInt16 nBuf = 0;
+ nBuf |= sal_Int32(aString.size()); // cch, 0..14th bits.
+ nBuf |= 0x8000; // fAnsiString, 15th bit.
+ rStream.WriteUInt16(nBuf);
+ SwWW8Writer::WriteString8(rStream, aString, false, RTL_TEXTENCODING_ASCII_US);
+}
+};
+
+void MSOFactoidType::Read(SvStream& rStream)
+{
+ sal_uInt32 cbFactoid(0);
+ rStream.ReadUInt32(cbFactoid);
+ rStream.ReadUInt32(m_nId);
+ m_aUri = MSOPBString::Read(rStream);
+ m_aTag = MSOPBString::Read(rStream);
+ MSOPBString::Read(rStream); // rgbDownloadURL
+}
+
+void MSOFactoidType::Write(WW8Export& rExport)
+{
+ SvStream& rStream = *rExport.m_pTableStrm;
+
+ SvMemoryStream aStream;
+ aStream.WriteUInt32(m_nId); // id
+ MSOPBString::Write(m_aUri, aStream);
+ MSOPBString::Write(m_aTag, aStream);
+ MSOPBString::Write(u"", aStream); // rgbDownloadURL
+ rStream.WriteUInt32(aStream.Tell());
+ aStream.Seek(0);
+ rStream.WriteStream(aStream);
+}
+
+void MSOPropertyBagStore::Read(SvStream& rStream)
+{
+ sal_uInt32 cFactoidType(0);
+ rStream.ReadUInt32(cFactoidType);
+ for (sal_uInt32 i = 0; i < cFactoidType && rStream.good(); ++i)
+ {
+ MSOFactoidType aFactoidType;
+ aFactoidType.Read(rStream);
+ m_aFactoidTypes.push_back(aFactoidType);
+ }
+ sal_uInt16 cbHdr(0);
+ rStream.ReadUInt16(cbHdr);
+ SAL_WARN_IF(cbHdr != 0xc, "sw.ww8", "MSOPropertyBagStore::Read: unexpected cbHdr");
+ sal_uInt16 nVer(0);
+ rStream.ReadUInt16(nVer);
+ SAL_WARN_IF(nVer != 0x0100, "sw.ww8", "MSOPropertyBagStore::Read: unexpected nVer");
+ rStream.SeekRel(4); // cfactoid
+ sal_uInt32 nCste(0);
+ rStream.ReadUInt32(nCste);
+
+ //each string has a 2 byte len record at the start
+ const size_t nMaxPossibleRecords = rStream.remainingSize() / sizeof(sal_uInt16);
+ if (nCste > nMaxPossibleRecords)
+ {
+ SAL_WARN("sw.ww8", nCste << " records claimed, but max possible is " << nMaxPossibleRecords);
+ nCste = nMaxPossibleRecords;
+ }
+
+ for (sal_uInt32 i = 0; i < nCste; ++i)
+ {
+ OUString aString = MSOPBString::Read(rStream);
+ m_aStringTable.push_back(aString);
+ }
+}
+
+void MSOPropertyBagStore::Write(WW8Export& rExport)
+{
+ SvStream& rStream = *rExport.m_pTableStrm;
+ rStream.WriteUInt32(m_aFactoidTypes.size()); // cFactoidType
+ for (MSOFactoidType& rType : m_aFactoidTypes)
+ rType.Write(rExport);
+ rStream.WriteUInt16(0xc); // cbHdr
+ rStream.WriteUInt16(0x0100); // sVer
+ rStream.WriteUInt32(0); // cfactoid
+ rStream.WriteUInt32(m_aStringTable.size()); // cste
+ for (const OUString& rString : m_aStringTable)
+ MSOPBString::Write(rString, rStream);
+}
+
+MSOProperty::MSOProperty()
+ : m_nKey(0)
+ , m_nValue(0)
+{
+}
+
+void MSOProperty::Read(SvStream& rStream)
+{
+ rStream.ReadUInt32(m_nKey);
+ rStream.ReadUInt32(m_nValue);
+}
+
+void MSOProperty::Write(SvStream& rStream)
+{
+ rStream.WriteUInt32(m_nKey);
+ rStream.WriteUInt32(m_nValue);
+}
+
+MSOPropertyBag::MSOPropertyBag()
+ : m_nId(0)
+{
+}
+
+bool MSOPropertyBag::Read(SvStream& rStream)
+{
+ rStream.ReadUInt16(m_nId);
+ sal_uInt16 cProp(0);
+ rStream.ReadUInt16(cProp);
+ if (!rStream.good())
+ return false;
+ rStream.SeekRel(2); // cbUnknown
+ //each MSOProperty is 8 bytes in size
+ const size_t nMaxPossibleRecords = rStream.remainingSize() / 8;
+ if (cProp > nMaxPossibleRecords)
+ {
+ SAL_WARN("sw.ww8", cProp << " records claimed, but max possible is " << nMaxPossibleRecords);
+ cProp = nMaxPossibleRecords;
+ }
+ for (sal_uInt16 i = 0; i < cProp && rStream.good(); ++i)
+ {
+ MSOProperty aProperty;
+ aProperty.Read(rStream);
+ m_aProperties.push_back(aProperty);
+ }
+ return rStream.good();
+}
+
+void MSOPropertyBag::Write(WW8Export& rExport)
+{
+ SvStream& rStream = *rExport.m_pTableStrm;
+ rStream.WriteUInt16(m_nId);
+ rStream.WriteUInt16(m_aProperties.size());
+ rStream.WriteUInt16(0); // cbUnknown
+ for (MSOProperty& rProperty : m_aProperties)
+ rProperty.Write(rStream);
+}
+
+void WW8SmartTagData::Read(SvStream& rStream, WW8_FC fcFactoidData, sal_uInt32 lcbFactoidData)
+{
+ sal_uInt64 nOldPosition = rStream.Tell();
+ if (!checkSeek(rStream, fcFactoidData))
+ return;
+
+ m_aPropBagStore.Read(rStream);
+ while (rStream.good() && rStream.Tell() < fcFactoidData + lcbFactoidData)
+ {
+ MSOPropertyBag aPropertyBag;
+ if (!aPropertyBag.Read(rStream))
+ break;
+ m_aPropBags.push_back(aPropertyBag);
+ }
+
+ rStream.Seek(nOldPosition);
+}
+
+void WW8SmartTagData::Write(WW8Export& rExport)
+{
+ m_aPropBagStore.Write(rExport);
+ for (MSOPropertyBag& rPropertyBag : m_aPropBags)
+ rPropertyBag.Write(rExport);
+}
+
+WW8Style::WW8Style(SvStream& rStream, WW8Fib& rFibPara)
+ : m_rFib(rFibPara), m_rStream(rStream), m_cstd(0), m_cbSTDBaseInFile(0), m_fStdStylenamesWritten(0)
+ , m_stiMaxWhenSaved(0), m_istdMaxFixedWhenSaved(0), m_nVerBuiltInNamesWhenSaved(0)
+ , m_ftcAsci(0), m_ftcFE(0), m_ftcOther(0), m_ftcBi(0)
+{
+ if (!checkSeek(m_rStream, m_rFib.m_fcStshf))
+ return;
+
+ sal_uInt16 cbStshi = 0; // 2 bytes size of the following STSHI structure
+ sal_uInt32 nRemaining = m_rFib.m_lcbStshf;
+ const sal_uInt32 nMinValidStshi = 4;
+
+ if (m_rFib.GetFIBVersion() <= ww::eWW2)
+ {
+ cbStshi = 0;
+ m_cstd = 256;
+ }
+ else
+ {
+ if (m_rFib.m_nFib < 67) // old Version ? (need to find this again to fix)
+ cbStshi = nMinValidStshi;
+ else // new version
+ {
+ if (nRemaining < sizeof(cbStshi))
+ return;
+ // reads the length of the structure in the file
+ m_rStream.ReadUInt16( cbStshi );
+ nRemaining-=2;
+ }
+ }
+
+ cbStshi = std::min(static_cast<sal_uInt32>(cbStshi), nRemaining);
+ if (cbStshi < nMinValidStshi)
+ return;
+
+ const sal_uInt16 nRead = cbStshi;
+ do
+ {
+ m_rStream.ReadUInt16( m_cstd );
+
+ m_rStream.ReadUInt16( m_cbSTDBaseInFile );
+
+ if( 6 > nRead ) break;
+
+ sal_uInt16 a16Bit;
+ m_rStream.ReadUInt16( a16Bit );
+ m_fStdStylenamesWritten = a16Bit & 0x0001;
+
+ if( 8 > nRead ) break;
+ m_rStream.ReadUInt16( m_stiMaxWhenSaved );
+
+ if( 10 > nRead ) break;
+ m_rStream.ReadUInt16( m_istdMaxFixedWhenSaved );
+
+ if( 12 > nRead ) break;
+ m_rStream.ReadUInt16( m_nVerBuiltInNamesWhenSaved );
+
+ if( 14 > nRead ) break;
+ m_rStream.ReadUInt16( m_ftcAsci );
+
+ if( 16 > nRead ) break;
+ m_rStream.ReadUInt16( m_ftcFE );
+
+ if ( 18 > nRead ) break;
+ m_rStream.ReadUInt16( m_ftcOther );
+
+ m_ftcBi = m_ftcOther;
+
+ if ( 20 > nRead ) break;
+ m_rStream.ReadUInt16( m_ftcBi );
+
+ // p.r.n. ignore the rest
+ if( 20 < nRead )
+ m_rStream.SeekRel( nRead-20 );
+ }
+ while( false ); // trick: the block above will be passed through exactly one time
+ // and that's why we can early exit with "break".
+
+ nRemaining -= cbStshi;
+
+ //There will be stshi.cstd (cbSTD, STD) pairs in the file following the
+ //STSHI. Note that styles can be empty, i.e. cbSTD == 0
+ const sal_uInt32 nMinRecordSize = sizeof(sal_uInt16);
+ const sal_uInt16 nMaxPossibleRecords = nRemaining/nMinRecordSize;
+
+ OSL_ENSURE(m_cstd <= nMaxPossibleRecords,
+ "allegedly more styles that available data");
+ m_cstd = o3tl::sanitizing_min(m_cstd, nMaxPossibleRecords);
+}
+
+// Read1STDFixed() reads a style. If the style is completely existent,
+// so it has no empty slot, we should allocate memory and a pointer should
+// reference to STD (perhaps filled with 0). If the slot is empty,
+// it will return a null pointer.
+std::unique_ptr<WW8_STD> WW8Style::Read1STDFixed(sal_uInt16& rSkip)
+{
+ if (m_rStream.remainingSize() < 2)
+ {
+ rSkip = 0;
+ return nullptr;
+ }
+
+ std::unique_ptr<WW8_STD> pStd;
+
+ sal_uInt16 cbStd(0);
+ m_rStream.ReadUInt16(cbStd); // read length
+
+ const sal_uInt16 nRead = m_cbSTDBaseInFile;
+ if( cbStd >= m_cbSTDBaseInFile )
+ {
+ // Fixed part completely available
+
+ // read fixed part of STD
+ pStd.reset(new WW8_STD);
+ memset( pStd.get(), 0, sizeof( *pStd ) );
+
+ do
+ {
+ if( 2 > nRead ) break;
+
+ sal_uInt16 a16Bit = 0;
+ m_rStream.ReadUInt16( a16Bit );
+ pStd->sti = a16Bit & 0x0fff ;
+ pStd->fScratch = sal_uInt16(0 != ( a16Bit & 0x1000 ));
+ pStd->fInvalHeight = sal_uInt16(0 != ( a16Bit & 0x2000 ));
+ pStd->fHasUpe = sal_uInt16(0 != ( a16Bit & 0x4000 ));
+ pStd->fMassCopy = sal_uInt16(0 != ( a16Bit & 0x8000 ));
+
+ if( 4 > nRead ) break;
+ a16Bit = 0;
+ m_rStream.ReadUInt16( a16Bit );
+ pStd->sgc = a16Bit & 0x000f ;
+ pStd->istdBase = ( a16Bit & 0xfff0 ) >> 4;
+
+ if( 6 > nRead ) break;
+ a16Bit = 0;
+ m_rStream.ReadUInt16( a16Bit );
+ pStd->cupx = a16Bit & 0x000f ;
+ pStd->istdNext = ( a16Bit & 0xfff0 ) >> 4;
+
+ if( 8 > nRead ) break;
+ m_rStream.ReadUInt16( pStd->bchUpe );
+
+ // from Ver8 this two fields should be added:
+ if(10 > nRead ) break;
+ a16Bit = 0;
+ m_rStream.ReadUInt16( a16Bit );
+ pStd->fAutoRedef = a16Bit & 0x0001 ;
+ pStd->fHidden = ( a16Bit & 0x0002 ) >> 1;
+ // You never know: cautionary skipped
+ if (nRead > 10)
+ {
+ auto nSkip = std::min<sal_uInt64>(nRead - 10, m_rStream.remainingSize());
+ m_rStream.Seek(m_rStream.Tell() + nSkip);
+ }
+ }
+ while( false ); // trick: the block above will passed through exactly one time
+ // and can be left early with a "break"
+
+ if (!m_rStream.good() || !nRead)
+ {
+ pStd.reset(); // report error with NULL
+ }
+
+ rSkip = cbStd - m_cbSTDBaseInFile;
+ }
+ else
+ { // Fixed part too short
+ if( cbStd )
+ m_rStream.SeekRel( cbStd ); // skip leftovers
+ rSkip = 0;
+ }
+ return pStd;
+}
+
+std::unique_ptr<WW8_STD> WW8Style::Read1Style(sal_uInt16& rSkip, OUString* pString)
+{
+ // Attention: MacWord-Documents have their Stylenames
+ // always in ANSI, even if eStructCharSet == CHARSET_MAC !!
+
+ std::unique_ptr<WW8_STD> pStd = Read1STDFixed(rSkip); // read STD
+
+ // string desired?
+ if( pString )
+ { // real style?
+ if ( pStd )
+ {
+ sal_Int32 nLenStringBytes = 0;
+ switch( m_rFib.m_nVersion )
+ {
+ case 6:
+ case 7:
+ // read pascal string
+ *pString = read_uInt8_BeltAndBracesString(m_rStream, RTL_TEXTENCODING_MS_1252);
+ // leading len and trailing zero --> 2
+ nLenStringBytes = pString->getLength() + 2;
+ break;
+ case 8:
+ // handle Unicode-String with leading length short and
+ // trailing zero
+ if (TestBeltAndBraces(m_rStream))
+ {
+ *pString = read_uInt16_BeltAndBracesString(m_rStream);
+ nLenStringBytes = (pString->getLength() + 2) * 2;
+ }
+ else
+ {
+ /*
+ #i8114#
+ This is supposed to be impossible, it's just supposed
+ to be 16 bit count followed by the string and ending
+ in a 0 short. But "Lotus SmartSuite Product: Word Pro"
+ is creating invalid style names in ww7- format. So we
+ use the belt and braces of the ms strings to see if
+ they are not corrupt. If they are then we try them as
+ 8bit ones
+ */
+ *pString = read_uInt8_BeltAndBracesString(m_rStream,RTL_TEXTENCODING_MS_1252);
+ // leading len and trailing zero --> 2
+ nLenStringBytes = pString->getLength() + 2;
+ }
+ break;
+ default:
+ OSL_ENSURE(false, "It was forgotten to code nVersion!");
+ break;
+ }
+ if (nLenStringBytes > rSkip)
+ {
+ SAL_WARN("sw.ww8", "WW8Style structure corrupt");
+ nLenStringBytes = rSkip;
+ }
+ rSkip -= nLenStringBytes;
+ }
+ else
+ pString->clear(); // can not return a name
+ }
+ return pStd;
+}
+
+namespace {
+const sal_uInt16 maxStrSize = 65;
+
+struct WW8_FFN_Ver6
+{
+ WW8_FFN_BASE base;
+ // from Ver6
+ char szFfn[maxStrSize]; // 0x6 or 0x40 from Ver8 on zero terminated string that
+ // records name of font.
+ // Maximal size of szFfn is 65 characters.
+ // Attention: This array can also be smaller!!!
+ // Possibly followed by a second sz which records the
+ // name of an alternate font to use if the first named
+ // font does not exist on this system.
+};
+
+}
+
+// #i43762# check font name for illegal characters
+static void lcl_checkFontname( OUString& sString )
+{
+ // for efficiency, we'd like to use String methods as far as possible.
+ // Hence, we will:
+ // 1) convert all invalid chars to \u0001
+ // 2) then erase all \u0001 chars (if anywhere found), and
+ // 3) erase leading/trailing ';', in case a font name was
+ // completely removed
+
+ // convert all invalid chars to \u0001
+ OUStringBuffer aBuf(sString);
+ const sal_Int32 nLen = aBuf.getLength();
+ bool bFound = false;
+ for ( sal_Int32 n = 0; n < nLen; ++n )
+ {
+ if ( aBuf[n] < 0x20 )
+ {
+ aBuf[n] = 1;
+ bFound = true;
+ }
+ }
+ sString = aBuf.makeStringAndClear();
+
+ // if anything was found, remove \u0001 + leading/trailing ';'
+ if( bFound )
+ {
+ sString = comphelper::string::strip(sString.replaceAll("\001", ""), ';');
+ }
+}
+
+namespace
+{
+ sal_uInt16 calcMaxFonts(sal_uInt8 *p, sal_Int32 nFFn)
+ {
+ // Figure out the max number of fonts defined here
+ sal_uInt16 nMax = 0;
+ sal_Int32 nRemaining = nFFn;
+ while (nRemaining)
+ {
+ //p[0] is cbFfnM1, the alleged total length of FFN - 1.
+ //i.e. length after cbFfnM1
+ const sal_uInt16 cbFfnM1 = *p++;
+ --nRemaining;
+
+ if (cbFfnM1 > nRemaining)
+ break;
+
+ nMax++;
+ nRemaining -= cbFfnM1;
+ p += cbFfnM1;
+ }
+ return nMax;
+ }
+
+ template<typename T> bool readU8(
+ sal_uInt8 const * p, std::size_t offset, sal_uInt8 const * pEnd,
+ T * value)
+ {
+ assert(p <= pEnd);
+ assert(value != nullptr);
+ if (offset >= o3tl::make_unsigned(pEnd - p)) {
+ return false;
+ }
+ *value = p[offset];
+ return true;
+ }
+
+ bool readS16(
+ sal_uInt8 const * p, std::size_t offset, sal_uInt8 const * pEnd,
+ short * value)
+ {
+ assert(p <= pEnd);
+ assert(value != nullptr);
+ if (offset > o3tl::make_unsigned(pEnd - p)
+ || static_cast<std::size_t>(pEnd - p) - offset < 2)
+ {
+ return false;
+ }
+ *value = unsigned(p[offset]) + (unsigned(p[offset + 1]) << 8);
+ return true;
+ }
+
+ sal_Int32 getStringLengthWithMax(
+ sal_uInt8 const * p, std::size_t offset, sal_uInt8 const * pEnd, std::size_t maxchars)
+ {
+ assert(p <= pEnd);
+ assert(pEnd - p <= SAL_MAX_INT32);
+ if (offset >= o3tl::make_unsigned(pEnd - p)) {
+ return -1;
+ }
+ std::size_t nbytes = static_cast<std::size_t>(pEnd - p) - offset;
+ std::size_t nsearch = std::min(nbytes, maxchars + 1);
+ void const * p2 = std::memchr(p + offset, 0, nsearch);
+ if (p2 == nullptr) {
+ return -1;
+ }
+ return static_cast<sal_uInt8 const *>(p2) - (p + offset);
+ }
+}
+
+WW8Fonts::WW8Fonts( SvStream& rSt, WW8Fib const & rFib )
+{
+ // Attention: MacWord-Documents have their Fontnames
+ // always in ANSI, even if eStructCharSet == CHARSET_MAC !!
+ if( rFib.m_lcbSttbfffn <= 2 )
+ {
+ OSL_ENSURE( false, "font table is broken! (rFib.lcbSttbfffn < 2)" );
+ return;
+ }
+
+ if (!checkSeek(rSt, rFib.m_fcSttbfffn))
+ return;
+
+ sal_Int32 nFFn = rFib.m_lcbSttbfffn - 2;
+
+ const sal_uInt64 nMaxPossible = rSt.remainingSize();
+ if (o3tl::make_unsigned(nFFn) > nMaxPossible)
+ {
+ SAL_WARN("sw.ww8", "FFN structure longer than available data");
+ nFFn = nMaxPossible;
+ }
+
+ // allocate Font Array
+ std::vector<sal_uInt8> aA(nFFn);
+ memset(aA.data(), 0, nFFn);
+
+ ww::WordVersion eVersion = rFib.GetFIBVersion();
+
+ sal_uInt16 nMax(0);
+ if( eVersion >= ww::eWW8 )
+ {
+ // bVer8: read the count of strings in nMax
+ rSt.ReadUInt16(nMax);
+ }
+
+ // Ver8: skip undefined uint16
+ // Ver67: skip the herein stored total byte of structure
+ // - we already got that information in rFib.lcbSttbfffn
+ rSt.SeekRel( 2 );
+
+ // read all font information
+ nFFn = rSt.ReadBytes(aA.data(), nFFn);
+ sal_uInt8 * const pEnd = aA.data() + nFFn;
+ const sal_uInt16 nCalcMax = calcMaxFonts(aA.data(), nFFn);
+
+ if (eVersion < ww::eWW8)
+ nMax = nCalcMax;
+ else
+ {
+ //newer versions include supportive count of fonts, so take min of that
+ //and calced max
+ nMax = std::min(nMax, nCalcMax);
+ }
+
+ if (nMax)
+ {
+ // allocate Index Array
+ m_aFontA.resize(nMax);
+ WW8_FFN* p = m_aFontA.data();
+
+ if( eVersion <= ww::eWW2 )
+ {
+ sal_uInt8 const * pVer2 = aA.data();
+ sal_uInt16 i = 0;
+ for(; i<nMax; ++i, ++p)
+ {
+ if (!readU8(
+ pVer2, offsetof(WW8_FFN_BASE, cbFfnM1), pEnd,
+ &p->aFFNBase.cbFfnM1))
+ {
+ break;
+ }
+
+ p->aFFNBase.prg = 0;
+ p->aFFNBase.fTrueType = 0;
+ p->aFFNBase.ff = 0;
+
+ if (!(readU8(pVer2, 1, pEnd, &p->aFFNBase.wWeight)
+ && readU8(pVer2, 2, pEnd, &p->aFFNBase.chs)))
+ {
+ break;
+ }
+ /*
+ #i8726# 7- seems to encode the name in the same encoding as
+ the font, e.g load the doc in 97 and save to see the unicode
+ ver of the asian fontnames in that example to confirm.
+ */
+ rtl_TextEncoding eEnc = WW8Fib::GetFIBCharset(p->aFFNBase.chs, rFib.m_lid);
+ if ((eEnc == RTL_TEXTENCODING_SYMBOL) || (eEnc == RTL_TEXTENCODING_DONTKNOW))
+ eEnc = RTL_TEXTENCODING_MS_1252;
+
+ const size_t nStringOffset = 1 + 2;
+ sal_Int32 n = getStringLengthWithMax(pVer2, nStringOffset, pEnd, maxStrSize);
+ if (n == -1) {
+ break;
+ }
+ p->sFontname = OUString(
+ reinterpret_cast<char const *>(pVer2 + nStringOffset), n, eEnc);
+ pVer2 = pVer2 + p->aFFNBase.cbFfnM1 + 1;
+ }
+ nMax = i;
+ }
+ else if( eVersion < ww::eWW8 )
+ {
+ sal_uInt8 const * pVer6 = aA.data();
+ sal_uInt16 i = 0;
+ for(; i<nMax; ++i, ++p)
+ {
+ if (!readU8(
+ pVer6, offsetof(WW8_FFN_BASE, cbFfnM1), pEnd,
+ &p->aFFNBase.cbFfnM1))
+ {
+ break;
+ }
+ sal_uInt8 c2;
+ if (!readU8(pVer6, 1, pEnd, &c2)) {
+ break;
+ }
+
+ p->aFFNBase.prg = c2 & 0x02;
+ p->aFFNBase.fTrueType = (c2 & 0x04) >> 2;
+ // skip a reserve bit
+ p->aFFNBase.ff = (c2 & 0x70) >> 4;
+
+ if (!(readS16(
+ pVer6, offsetof(WW8_FFN_BASE, wWeight), pEnd,
+ &p->aFFNBase.wWeight)
+ && readU8(
+ pVer6, offsetof(WW8_FFN_BASE, chs), pEnd, &p->aFFNBase.chs)
+ && readU8(
+ pVer6, offsetof(WW8_FFN_BASE, ibszAlt), pEnd,
+ &p->aFFNBase.ibszAlt)))
+ {
+ break;
+ }
+ /*
+ #i8726# 7- seems to encode the name in the same encoding as
+ the font, e.g load the doc in 97 and save to see the unicode
+ ver of the asian fontnames in that example to confirm.
+ */
+ rtl_TextEncoding eEnc = WW8Fib::GetFIBCharset(p->aFFNBase.chs, rFib.m_lid);
+ if ((eEnc == RTL_TEXTENCODING_SYMBOL) || (eEnc == RTL_TEXTENCODING_DONTKNOW))
+ eEnc = RTL_TEXTENCODING_MS_1252;
+ const size_t nStringOffset = offsetof(WW8_FFN_Ver6, szFfn);
+ sal_Int32 n = getStringLengthWithMax(pVer6, nStringOffset, pEnd, maxStrSize);
+ if (n == -1) {
+ break;
+ }
+ p->sFontname = OUString(reinterpret_cast<char const*>(pVer6 + nStringOffset), n, eEnc);
+ if (p->aFFNBase.ibszAlt && p->aFFNBase.ibszAlt < maxStrSize) //don't start after end of string
+ {
+ const size_t nAltStringOffset = offsetof(WW8_FFN_Ver6, szFfn) + p->aFFNBase.ibszAlt;
+ n = getStringLengthWithMax(pVer6, nAltStringOffset, pEnd, maxStrSize);
+ if (n == -1) {
+ break;
+ }
+ p->sFontname += ";" + OUString(reinterpret_cast<char const*>(pVer6 + nAltStringOffset),
+ n, eEnc);
+ }
+ else
+ {
+ //#i18369# if it's a symbol font set Symbol as fallback
+ if (
+ RTL_TEXTENCODING_SYMBOL == WW8Fib::GetFIBCharset(p->aFFNBase.chs, rFib.m_lid)
+ && p->sFontname!="Symbol"
+ )
+ {
+ p->sFontname += ";Symbol";
+ }
+ }
+ pVer6 = pVer6 + p->aFFNBase.cbFfnM1 + 1;
+ }
+ nMax = i;
+ }
+ else
+ {
+ //count of bytes in minimum FontFamilyInformation payload
+ const sal_uInt8 cbMinFFNPayload = 41;
+ sal_uInt16 nValidFonts = 0;
+ sal_Int32 nRemainingFFn = nFFn;
+ sal_uInt8* pRaw = aA.data();
+ for (sal_uInt16 i=0; i < nMax && nRemainingFFn; ++i, ++p)
+ {
+ //pRaw[0] is cbFfnM1, the alleged total length of FFN - 1
+ //i.e. length after cbFfnM1
+ sal_uInt8 cbFfnM1 = *pRaw++;
+ --nRemainingFFn;
+
+ if (cbFfnM1 > nRemainingFFn)
+ break;
+
+ if (cbFfnM1 < cbMinFFNPayload)
+ break;
+
+ p->aFFNBase.cbFfnM1 = cbFfnM1;
+
+ sal_uInt8 *pVer8 = pRaw;
+
+ sal_uInt8 c2 = *pVer8++;
+ --cbFfnM1;
+
+ p->aFFNBase.prg = c2 & 0x02;
+ p->aFFNBase.fTrueType = (c2 & 0x04) >> 2;
+ // skip a reserve bit
+ p->aFFNBase.ff = (c2 & 0x70) >> 4;
+
+ p->aFFNBase.wWeight = SVBT16ToUInt16(*reinterpret_cast<SVBT16*>(pVer8));
+ pVer8+=2;
+ cbFfnM1-=2;
+
+ p->aFFNBase.chs = *pVer8++;
+ --cbFfnM1;
+
+ p->aFFNBase.ibszAlt = *pVer8++;
+ --cbFfnM1;
+
+ pVer8 += 10; //PANOSE
+ cbFfnM1-=10;
+ pVer8 += 24; //FONTSIGNATURE
+ cbFfnM1-=24;
+
+ assert(cbFfnM1 >= 2);
+
+ sal_uInt8 nMaxNullTerminatedPossible = cbFfnM1/2 - 1;
+ sal_Unicode *pPrimary = reinterpret_cast<sal_Unicode*>(pVer8);
+ pPrimary[nMaxNullTerminatedPossible] = 0;
+#ifdef OSL_BIGENDIAN
+ swapEndian(pPrimary);
+#endif
+ p->sFontname = pPrimary;
+ if (p->aFFNBase.ibszAlt && p->aFFNBase.ibszAlt < nMaxNullTerminatedPossible)
+ {
+ sal_Unicode *pSecondary = pPrimary + p->aFFNBase.ibszAlt;
+#ifdef OSL_BIGENDIAN
+ swapEndian(pSecondary);
+#endif
+ p->sFontname += OUString::Concat(";") + pSecondary;
+ }
+
+ // #i43762# check font name for illegal characters
+ lcl_checkFontname( p->sFontname );
+
+ // set pointer one font back to original array
+ pRaw += p->aFFNBase.cbFfnM1;
+ nRemainingFFn -= p->aFFNBase.cbFfnM1;
+ ++nValidFonts;
+ }
+ OSL_ENSURE(nMax == nValidFonts, "Font count differs with availability");
+ nMax = std::min(nMax, nValidFonts);
+ }
+ }
+ m_aFontA.resize(nMax);
+ m_aFontA.shrink_to_fit();
+}
+
+const WW8_FFN* WW8Fonts::GetFont( sal_uInt16 nNum ) const
+{
+ if (nNum >= m_aFontA.size())
+ return nullptr;
+
+ return &m_aFontA[nNum];
+}
+
+// Search after a header/footer for an index in the ww list from header/footer
+
+// specials for WinWord6 and -7:
+//
+// 1) At the start of reading we must build WWPLCF_HdFt with Fib and Dop
+// 2) The main text must be read sequentially over all sections
+// 3) For every header/footer in the main text, we must call UpdateIndex()
+// exactly once with the parameter from the attribute.
+// (per section can be maximally one). This call must take place *after*
+// the last call from GetTextPos().
+// 4) GetTextPos() can be called with exactly one flag
+// out of WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST} (Do not change!)
+// -> maybe we can get a right result then
+
+WW8PLCF_HdFt::WW8PLCF_HdFt( SvStream* pSt, WW8Fib const & rFib, WW8Dop const & rDop )
+ : m_aPLCF(*pSt, rFib.m_fcPlcfhdd , rFib.m_lcbPlcfhdd , 0)
+{
+ m_nIdxOffset = 0;
+
+ /*
+ This dop.grpfIhdt has a bit set for each special
+ footnote *and endnote!!* separator,continuation separator, and
+ continuation notice entry, the documentation does not mention the
+ endnote separators, the documentation also gets the index numbers
+ backwards when specifying which bits to test. The bottom six bits
+ of this value must be tested and skipped over. Each section's
+ grpfIhdt is then tested for the existence of the appropriate headers
+ and footers, at the end of each section the nIdxOffset must be updated
+ to point to the beginning of the next section's group of headers and
+ footers in this PLCF, UpdateIndex does that task.
+ */
+ for( sal_uInt8 nI = 0x1; nI <= 0x20; nI <<= 1 )
+ if( nI & rDop.grpfIhdt ) // bit set?
+ m_nIdxOffset++;
+}
+
+bool WW8PLCF_HdFt::GetTextPos(sal_uInt8 grpfIhdt, sal_uInt8 nWhich, WW8_CP& rStart,
+ WW8_CP& rLen)
+{
+ sal_uInt8 nI = 0x01;
+ short nIdx = m_nIdxOffset;
+ while (true)
+ {
+ if( nI & nWhich )
+ break; // found
+ if( grpfIhdt & nI )
+ nIdx++; // uninteresting Header / Footer
+ nI <<= 1; // text next bit
+ if( nI > 0x20 )
+ return false; // not found
+ }
+ // nIdx is HdFt-Index
+ WW8_CP nEnd;
+ void* pData;
+
+ m_aPLCF.SetIdx( nIdx ); // Lookup suitable CP
+ m_aPLCF.Get( rStart, nEnd, pData );
+ if (nEnd < rStart)
+ {
+ SAL_WARN("sw.ww8", "End " << nEnd << " before Start " << rStart);
+ return false;
+ }
+
+ bool bFail = o3tl::checked_sub(nEnd, rStart, rLen);
+ if (bFail)
+ {
+ SAL_WARN("sw.ww8", "broken offset, ignoring");
+ return false;
+ }
+
+ m_aPLCF.advance();
+
+ return true;
+}
+
+void WW8PLCF_HdFt::GetTextPosExact(short nIdx, WW8_CP& rStart, WW8_CP& rLen)
+{
+ WW8_CP nEnd;
+ void* pData;
+
+ m_aPLCF.SetIdx( nIdx ); // Lookup suitable CP
+ m_aPLCF.Get( rStart, nEnd, pData );
+ if (nEnd < rStart)
+ {
+ SAL_WARN("sw.ww8", "End " << nEnd << " before Start " << rStart);
+ rLen = 0;
+ return;
+ }
+ if (o3tl::checked_sub(nEnd, rStart, rLen))
+ {
+ SAL_WARN("sw.ww8", "GetTextPosExact overflow");
+ rLen = 0;
+ }
+}
+
+void WW8PLCF_HdFt::UpdateIndex( sal_uInt8 grpfIhdt )
+{
+ // Caution: Description is not correct
+ for( sal_uInt8 nI = 0x01; nI <= 0x20; nI <<= 1 )
+ if( nI & grpfIhdt )
+ m_nIdxOffset++;
+}
+
+WW8Dop::WW8Dop(SvStream& rSt, sal_Int16 nFib, sal_Int32 nPos, sal_uInt32 nSize):
+ fFacingPages(false), fWidowControl(false), fPMHMainDoc(false), grfSuppression(0), fpc(0),
+ grpfIhdt(0), rncFootnote(0), nFootnote(0), fOutlineDirtySave(false), fOnlyMacPics(false),
+ fOnlyWinPics(false), fLabelDoc(false), fHyphCapitals(false), fAutoHyphen(false),
+ fFormNoFields(false), fLinkStyles(false), fRevMarking(false), fBackup(false),
+ fExactCWords(false), fPagHidden(false), fPagResults(false), fLockAtn(false),
+ fMirrorMargins(false), fReadOnlyRecommended(false), fDfltTrueType(false),
+ fPagSuppressTopSpacing(false), fProtEnabled(false), fDispFormFieldSel(false), fRMView(false),
+ fRMPrint(false), fWriteReservation(false), fLockRev(false), fEmbedFonts(false),
+ copts_fNoTabForInd(false), copts_fNoSpaceRaiseLower(false), copts_fSuppressSpbfAfterPgBrk(false),
+ copts_fWrapTrailSpaces(false), copts_fMapPrintTextColor(false), copts_fNoColumnBalance(false),
+ copts_fConvMailMergeEsc(false), copts_fSuppressTopSpacing(false),
+ copts_fOrigWordTableRules(false), copts_fTransparentMetafiles(false),
+ copts_fShowBreaksInFrames(false), copts_fSwapBordersFacingPgs(false), copts_fExpShRtn(false),
+ rncEdn(0), nEdn(0), epc(0), fPrintFormData(false), fSaveFormData(false), fShadeFormData(false),
+ fWCFootnoteEdn(false), wvkSaved(0), wScaleSaved(0), zkSaved(0), fRotateFontW6(false),
+ iGutterPos(false), fNoTabForInd(false), fNoSpaceRaiseLower(false),
+ fSuppressSpbfAfterPageBreak(false), fWrapTrailSpaces(false), fMapPrintTextColor(false),
+ fNoColumnBalance(false), fConvMailMergeEsc(false), fSuppressTopSpacing(false),
+ fOrigWordTableRules(false), fTransparentMetafiles(false), fShowBreaksInFrames(false),
+ fSwapBordersFacingPgs(false), fCompatibilityOptions_Unknown1_13(false), fExpShRtn(false),
+ fCompatibilityOptions_Unknown1_15(false), fCompatibilityOptions_Unknown1_16(false),
+ fSuppressTopSpacingMac5(false), fTruncDxaExpand(false), fPrintBodyBeforeHdr(false),
+ fNoLeading(false), fCompatibilityOptions_Unknown1_21(false), fMWSmallCaps(false),
+ fCompatibilityOptions_Unknown1_23(false), fCompatibilityOptions_Unknown1_24(false),
+ fCompatibilityOptions_Unknown1_25(false), fCompatibilityOptions_Unknown1_26(false),
+ fCompatibilityOptions_Unknown1_27(false), fCompatibilityOptions_Unknown1_28(false),
+ fCompatibilityOptions_Unknown1_29(false), fCompatibilityOptions_Unknown1_30(false),
+ fCompatibilityOptions_Unknown1_31(false), fUsePrinterMetrics(false), lvl(0), fHtmlDoc(false),
+ fSnapBorder(false), fIncludeHeader(false), fIncludeFooter(false), fForcePageSizePag(false),
+ fMinFontSizePag(false), fHaveVersions(false), fAutoVersion(false),
+ fCompatibilityOptions_Unknown2_1(false), fCompatibilityOptions_Unknown2_2(false),
+ fDontUseHTMLAutoSpacing(false), fCompatibilityOptions_Unknown2_4(false),
+ fCompatibilityOptions_Unknown2_5(false), fCompatibilityOptions_Unknown2_6(false),
+ fCompatibilityOptions_Unknown2_7(false), fCompatibilityOptions_Unknown2_8(false),
+ fCompatibilityOptions_Unknown2_9(false), fCompatibilityOptions_Unknown2_10(false),
+ fDontBreakWrappedTables(false), fCompatibilityOptions_Unknown2_12(false),
+ fCompatibilityOptions_Unknown2_13(false), fCompatibilityOptions_Unknown2_14(false),
+ fCompatibilityOptions_Unknown2_15(false), fCompatibilityOptions_Unknown2_16(false),
+ fCompatibilityOptions_Unknown2_17(false), fCompatibilityOptions_Unknown2_18(false),
+ fCompatibilityOptions_Unknown2_19(false), fCompatibilityOptions_Unknown2_20(false),
+ fCompatibilityOptions_Unknown2_21(false), fCompatibilityOptions_Unknown2_22(false),
+ fCompatibilityOptions_Unknown2_23(false), fCompatibilityOptions_Unknown2_24(false),
+ fCompatibilityOptions_Unknown2_25(false), fCompatibilityOptions_Unknown2_26(false),
+ fCompatibilityOptions_Unknown2_27(false), fCompatibilityOptions_Unknown2_28(false),
+ fCompatibilityOptions_Unknown2_29(false), fCompatibilityOptions_Unknown2_30(false),
+ fCompatibilityOptions_Unknown2_31(false), fCompatibilityOptions_Unknown2_32(false),
+ fUnknown3(0), fUseBackGroundInAllmodes(false), fDoNotEmbedSystemFont(false), fWordCompat(false),
+ fLiveRecover(false), fEmbedFactoids(false), fFactoidXML(false), fFactoidAllDone(false),
+ fFolioPrint(false), fReverseFolio(false), iTextLineEnding(0), fHideFcc(false),
+ fAcetateShowMarkup(false), fAcetateShowAtn(false), fAcetateShowInsDel(false),
+ fAcetateShowProps(false)
+ // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the
+ // above bit-field member initializations can be moved to the class definition
+{
+ fDontUseHTMLAutoSpacing = true; //default
+ fAcetateShowAtn = true; //default
+ const sal_uInt32 nMaxDopSize = 0x268;
+ std::unique_ptr<sal_uInt8[]> pDataPtr( new sal_uInt8[ nMaxDopSize ] );
+ sal_uInt8* pData = pDataPtr.get();
+
+ sal_uInt32 nRead = std::min(nMaxDopSize, nSize);
+ if (nSize < 2 || !checkSeek(rSt, nPos) || nRead != rSt.ReadBytes(pData, nRead))
+ nDopError = ERR_SWG_READ_ERROR; // report error
+ else
+ {
+ if (nMaxDopSize > nRead)
+ memset( pData + nRead, 0, nMaxDopSize - nRead );
+
+ // interpret the data
+ sal_uInt32 a32Bit;
+ sal_uInt16 a16Bit;
+ sal_uInt8 a8Bit;
+
+ a16Bit = Get_UShort( pData ); // 0 0x00
+ fFacingPages = 0 != ( a16Bit & 0x0001 ) ;
+ fWidowControl = 0 != ( a16Bit & 0x0002 ) ;
+ fPMHMainDoc = 0 != ( a16Bit & 0x0004 ) ;
+ grfSuppression = ( a16Bit & 0x0018 ) >> 3;
+ fpc = ( a16Bit & 0x0060 ) >> 5;
+ grpfIhdt = ( a16Bit & 0xff00 ) >> 8;
+
+ a16Bit = Get_UShort( pData ); // 2 0x02
+ rncFootnote = a16Bit & 0x0003 ;
+ nFootnote = ( a16Bit & ~0x0003 ) >> 2 ;
+
+ a8Bit = Get_Byte( pData ); // 4 0x04
+ fOutlineDirtySave = 0 != ( a8Bit & 0x01 );
+
+ a8Bit = Get_Byte( pData ); // 5 0x05
+ fOnlyMacPics = 0 != ( a8Bit & 0x01 );
+ fOnlyWinPics = 0 != ( a8Bit & 0x02 );
+ fLabelDoc = 0 != ( a8Bit & 0x04 );
+ fHyphCapitals = 0 != ( a8Bit & 0x08 );
+ fAutoHyphen = 0 != ( a8Bit & 0x10 );
+ fFormNoFields = 0 != ( a8Bit & 0x20 );
+ fLinkStyles = 0 != ( a8Bit & 0x40 );
+ fRevMarking = 0 != ( a8Bit & 0x80 );
+
+ a8Bit = Get_Byte( pData ); // 6 0x06
+ fBackup = 0 != ( a8Bit & 0x01 );
+ fExactCWords = 0 != ( a8Bit & 0x02 );
+ fPagHidden = 0 != ( a8Bit & 0x04 );
+ fPagResults = 0 != ( a8Bit & 0x08 );
+ fLockAtn = 0 != ( a8Bit & 0x10 );
+ fMirrorMargins = 0 != ( a8Bit & 0x20 );
+ fReadOnlyRecommended = 0 != ( a8Bit & 0x40 );
+ fDfltTrueType = 0 != ( a8Bit & 0x80 );
+
+ a8Bit = Get_Byte( pData ); // 7 0x07
+ fPagSuppressTopSpacing = 0 != ( a8Bit & 0x01 );
+ fProtEnabled = 0 != ( a8Bit & 0x02 );
+ fDispFormFieldSel = 0 != ( a8Bit & 0x04 );
+ fRMView = 0 != ( a8Bit & 0x08 );
+ fRMPrint = 0 != ( a8Bit & 0x10 );
+ fWriteReservation = 0 != ( a8Bit & 0x20 );
+ fLockRev = 0 != ( a8Bit & 0x40 );
+ fEmbedFonts = 0 != ( a8Bit & 0x80 );
+
+ a8Bit = Get_Byte( pData ); // 8 0x08
+ copts_fNoTabForInd = 0 != ( a8Bit & 0x01 );
+ copts_fNoSpaceRaiseLower = 0 != ( a8Bit & 0x02 );
+ copts_fSuppressSpbfAfterPgBrk = 0 != ( a8Bit & 0x04 );
+ copts_fWrapTrailSpaces = 0 != ( a8Bit & 0x08 );
+ copts_fMapPrintTextColor = 0 != ( a8Bit & 0x10 );
+ copts_fNoColumnBalance = 0 != ( a8Bit & 0x20 );
+ copts_fConvMailMergeEsc = 0 != ( a8Bit & 0x40 );
+ copts_fSuppressTopSpacing = 0 != ( a8Bit & 0x80 );
+
+ a8Bit = Get_Byte( pData ); // 9 0x09
+ copts_fOrigWordTableRules = 0 != ( a8Bit & 0x01 );
+ copts_fTransparentMetafiles = 0 != ( a8Bit & 0x02 );
+ copts_fShowBreaksInFrames = 0 != ( a8Bit & 0x04 );
+ copts_fSwapBordersFacingPgs = 0 != ( a8Bit & 0x08 );
+ copts_fExpShRtn = 0 != ( a8Bit & 0x20 ); // #i56856#
+
+ dxaTab = Get_Short( pData ); // 10 0x0a
+ wSpare = Get_UShort( pData ); // 12 0x0c
+ dxaHotZ = Get_UShort( pData ); // 14 0x0e
+ cConsecHypLim = Get_UShort( pData ); // 16 0x10
+ wSpare2 = Get_UShort( pData ); // 18 0x12
+ dttmCreated = Get_Long( pData ); // 20 0x14
+ dttmRevised = Get_Long( pData ); // 24 0x18
+ dttmLastPrint = Get_Long( pData ); // 28 0x1c
+ nRevision = Get_Short( pData ); // 32 0x20
+ tmEdited = Get_Long( pData ); // 34 0x22
+ cWords = Get_Long( pData ); // 38 0x26
+ cCh = Get_Long( pData ); // 42 0x2a
+ cPg = Get_Short( pData ); // 46 0x2e
+ cParas = Get_Long( pData ); // 48 0x30
+
+ a16Bit = Get_UShort( pData ); // 52 0x34
+ rncEdn = a16Bit & 0x0003 ;
+ nEdn = ( a16Bit & ~0x0003 ) >> 2;
+
+ a16Bit = Get_UShort( pData ); // 54 0x36
+ epc = a16Bit & 0x0003 ;
+ nfcFootnoteRef = ( a16Bit & 0x003c ) >> 2;
+ nfcEdnRef = ( a16Bit & 0x03c0 ) >> 6;
+ fPrintFormData = 0 != ( a16Bit & 0x0400 );
+ fSaveFormData = 0 != ( a16Bit & 0x0800 );
+ fShadeFormData = 0 != ( a16Bit & 0x1000 );
+ fWCFootnoteEdn = 0 != ( a16Bit & 0x8000 );
+
+ cLines = Get_Long( pData ); // 56 0x38
+ cWordsFootnoteEnd = Get_Long( pData ); // 60 0x3c
+ cChFootnoteEdn = Get_Long( pData ); // 64 0x40
+ cPgFootnoteEdn = Get_Short( pData ); // 68 0x44
+ cParasFootnoteEdn = Get_Long( pData ); // 70 0x46
+ cLinesFootnoteEdn = Get_Long( pData ); // 74 0x4a
+ lKeyProtDoc = Get_Long( pData ); // 78 0x4e
+
+ a16Bit = Get_UShort( pData ); // 82 0x52
+ wvkSaved = a16Bit & 0x0007 ;
+ wScaleSaved = ( a16Bit & 0x0ff8 ) >> 3 ;
+ zkSaved = ( a16Bit & 0x3000 ) >> 12;
+ fRotateFontW6 = ( a16Bit & 0x4000 ) >> 14;
+ iGutterPos = ( a16Bit & 0x8000 ) >> 15;
+
+ if (nFib >= 103) // Word 6/32bit, 95, 97, 2000, 2002, 2003, 2007
+ {
+ a32Bit = Get_ULong( pData ); // 84 0x54
+ SetCompatibilityOptions(a32Bit);
+ }
+
+ //#i22436#, for all WW7- documents
+ if (nFib <= 104) // Word 95
+ fUsePrinterMetrics = true;
+
+ if (nFib > 105) // Word 97, 2000, 2002, 2003, 2007
+ {
+ adt = Get_Short( pData ); // 88 0x58
+
+ doptypography.ReadFromMem(pData); // 90 0x5a
+
+ memcpy( &dogrid, pData, sizeof( WW8_DOGRID )); // 400 0x190
+ pData += sizeof( WW8_DOGRID );
+
+ a16Bit = Get_UShort( pData ); // 410 0x19a
+ // the following 9 bit are uninteresting
+ fHtmlDoc = ( a16Bit & 0x0200 ) >> 9 ;
+ fSnapBorder = ( a16Bit & 0x0800 ) >> 11 ;
+ fIncludeHeader = ( a16Bit & 0x1000 ) >> 12 ;
+ fIncludeFooter = ( a16Bit & 0x2000 ) >> 13 ;
+ fForcePageSizePag = ( a16Bit & 0x4000 ) >> 14 ;
+ fMinFontSizePag = ( a16Bit & 0x8000 ) >> 15 ;
+
+ a16Bit = Get_UShort( pData ); // 412 0x19c
+ fHaveVersions = 0 != ( a16Bit & 0x0001 );
+ fAutoVersion = 0 != ( a16Bit & 0x0002 );
+
+ pData += 12; // 414 0x19e
+
+ cChWS = Get_Long( pData ); // 426 0x1aa
+ cChWSFootnoteEdn = Get_Long( pData ); // 430 0x1ae
+ grfDocEvents = Get_Long( pData ); // 434 0x1b2
+
+ pData += 4+30+8; // 438 0x1b6; 442 0x1ba; 472 0x1d8; 476 0x1dc
+
+ cDBC = Get_Long( pData ); // 480 0x1e0
+ cDBCFootnoteEdn = Get_Long( pData ); // 484 0x1e4
+
+ pData += 1 * sizeof( sal_Int32); // 488 0x1e8
+
+ nfcFootnoteRef = Get_Short( pData ); // 492 0x1ec
+ nfcEdnRef = Get_Short( pData ); // 494 0x1ee
+ hpsZoomFontPag = Get_Short( pData ); // 496 0x1f0
+ dywDispPag = Get_Short( pData ); // 498 0x1f2
+
+ if (nRead >= 516)
+ {
+ //500 -> 508, Appear to be repeated here in 2000+
+ pData += 8; // 500 0x1f4
+ a32Bit = Get_Long( pData ); // 508 0x1fc
+ SetCompatibilityOptions(a32Bit);
+ a32Bit = Get_Long( pData ); // 512 0x200
+
+ // i#78591#
+ SetCompatibilityOptions2(a32Bit);
+ }
+ if (nRead >= 550)
+ {
+ pData += 32;
+ a16Bit = Get_UShort( pData );
+ fDoNotEmbedSystemFont = ( a16Bit & 0x0001 );
+ fWordCompat = ( a16Bit & 0x0002 ) >> 1;
+ fLiveRecover = ( a16Bit & 0x0004 ) >> 2;
+ fEmbedFactoids = ( a16Bit & 0x0008 ) >> 3;
+ fFactoidXML = ( a16Bit & 0x00010 ) >> 4;
+ fFactoidAllDone = ( a16Bit & 0x0020 ) >> 5;
+ fFolioPrint = ( a16Bit & 0x0040 ) >> 6;
+ fReverseFolio = ( a16Bit & 0x0080 ) >> 7;
+ iTextLineEnding = ( a16Bit & 0x0700 ) >> 8;
+ fHideFcc = ( a16Bit & 0x0800 ) >> 11;
+ fAcetateShowMarkup = ( a16Bit & 0x1000 ) >> 12;
+ fAcetateShowAtn = ( a16Bit & 0x2000 ) >> 13;
+ fAcetateShowInsDel = ( a16Bit & 0x4000 ) >> 14;
+ fAcetateShowProps = ( a16Bit & 0x8000 ) >> 15;
+ }
+ if (nRead >= 600)
+ {
+ pData += 48;
+ a16Bit = Get_Short(pData);
+ fUseBackGroundInAllmodes = (a16Bit & 0x0080) >> 7;
+ }
+ }
+ }
+}
+
+WW8Dop::WW8Dop():
+ fFacingPages(false), fWidowControl(true), fPMHMainDoc(false), grfSuppression(0), fpc(1),
+ grpfIhdt(0), rncFootnote(0), nFootnote(1), fOutlineDirtySave(true), fOnlyMacPics(false),
+ fOnlyWinPics(false), fLabelDoc(false), fHyphCapitals(true), fAutoHyphen(false),
+ fFormNoFields(false), fLinkStyles(false), fRevMarking(false), fBackup(true),
+ fExactCWords(false), fPagHidden(true), fPagResults(true), fLockAtn(false),
+ fMirrorMargins(false), fReadOnlyRecommended(false), fDfltTrueType(true),
+ fPagSuppressTopSpacing(false), fProtEnabled(false), fDispFormFieldSel(false), fRMView(true),
+ fRMPrint(true), fWriteReservation(false), fLockRev(false), fEmbedFonts(false),
+ copts_fNoTabForInd(false), copts_fNoSpaceRaiseLower(false), copts_fSuppressSpbfAfterPgBrk(false),
+ copts_fWrapTrailSpaces(false), copts_fMapPrintTextColor(false), copts_fNoColumnBalance(false),
+ copts_fConvMailMergeEsc(false), copts_fSuppressTopSpacing(false),
+ copts_fOrigWordTableRules(false), copts_fTransparentMetafiles(false),
+ copts_fShowBreaksInFrames(false), copts_fSwapBordersFacingPgs(false), copts_fExpShRtn(false),
+ dxaTab(0x2d0), dxaHotZ(0x168), nRevision(1),
+ rncEdn(0), nEdn(1), epc(3), fPrintFormData(false), fSaveFormData(false), fShadeFormData(true),
+ fWCFootnoteEdn(false), wvkSaved(2), wScaleSaved(100), zkSaved(0), fRotateFontW6(false),
+ iGutterPos(false), fNoTabForInd(false), fNoSpaceRaiseLower(false),
+ fSuppressSpbfAfterPageBreak(false), fWrapTrailSpaces(false), fMapPrintTextColor(false),
+ fNoColumnBalance(false), fConvMailMergeEsc(false), fSuppressTopSpacing(false),
+ fOrigWordTableRules(false), fTransparentMetafiles(false), fShowBreaksInFrames(false),
+ fSwapBordersFacingPgs(false), fCompatibilityOptions_Unknown1_13(false), fExpShRtn(false),
+ fCompatibilityOptions_Unknown1_15(false), fCompatibilityOptions_Unknown1_16(false),
+ fSuppressTopSpacingMac5(false), fTruncDxaExpand(false), fPrintBodyBeforeHdr(false),
+ fNoLeading(true), fCompatibilityOptions_Unknown1_21(false), fMWSmallCaps(false),
+ fCompatibilityOptions_Unknown1_23(false), fCompatibilityOptions_Unknown1_24(false),
+ fCompatibilityOptions_Unknown1_25(false), fCompatibilityOptions_Unknown1_26(false),
+ fCompatibilityOptions_Unknown1_27(false), fCompatibilityOptions_Unknown1_28(false),
+ fCompatibilityOptions_Unknown1_29(false), fCompatibilityOptions_Unknown1_30(false),
+ fCompatibilityOptions_Unknown1_31(false), fUsePrinterMetrics(true), lvl(9), fHtmlDoc(false),
+ fSnapBorder(false), fIncludeHeader(true), fIncludeFooter(true), fForcePageSizePag(false),
+ fMinFontSizePag(false), fHaveVersions(false), fAutoVersion(false),
+ cChWS(0), cChWSFootnoteEdn(0), cDBC(0), cDBCFootnoteEdn(0), nfcEdnRef(2),
+ fCompatibilityOptions_Unknown2_1(false), fCompatibilityOptions_Unknown2_2(false),
+ fDontUseHTMLAutoSpacing(false), fCompatibilityOptions_Unknown2_4(false),
+ fCompatibilityOptions_Unknown2_5(false), fCompatibilityOptions_Unknown2_6(false),
+ fCompatibilityOptions_Unknown2_7(false), fCompatibilityOptions_Unknown2_8(false),
+ fCompatibilityOptions_Unknown2_9(false), fCompatibilityOptions_Unknown2_10(false),
+ fDontBreakWrappedTables(false), fCompatibilityOptions_Unknown2_12(false),
+ fCompatibilityOptions_Unknown2_13(false), fCompatibilityOptions_Unknown2_14(false),
+ fCompatibilityOptions_Unknown2_15(false), fCompatibilityOptions_Unknown2_16(false),
+ fCompatibilityOptions_Unknown2_17(false), fCompatibilityOptions_Unknown2_18(false),
+ fCompatibilityOptions_Unknown2_19(false), fCompatibilityOptions_Unknown2_20(false),
+ fCompatibilityOptions_Unknown2_21(false), fCompatibilityOptions_Unknown2_22(false),
+ fCompatibilityOptions_Unknown2_23(false), fCompatibilityOptions_Unknown2_24(false),
+ fCompatibilityOptions_Unknown2_25(false), fCompatibilityOptions_Unknown2_26(false),
+ fCompatibilityOptions_Unknown2_27(false), fCompatibilityOptions_Unknown2_28(false),
+ fCompatibilityOptions_Unknown2_29(false), fCompatibilityOptions_Unknown2_30(false),
+ fCompatibilityOptions_Unknown2_31(false), fCompatibilityOptions_Unknown2_32(false),
+ fUnknown3(0), fUseBackGroundInAllmodes(false), fDoNotEmbedSystemFont(false), fWordCompat(false),
+ fLiveRecover(false), fEmbedFactoids(false), fFactoidXML(false), fFactoidAllDone(false),
+ fFolioPrint(false), fReverseFolio(false), iTextLineEnding(0), fHideFcc(false),
+ fAcetateShowMarkup(false), fAcetateShowAtn(true), fAcetateShowInsDel(false),
+ fAcetateShowProps(false)
+ // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the
+ // above bit-field member initializations can be moved to the class definition
+{
+ /*
+ Writer acts like this all the time at the moment, ideally we need an
+ option for these two as well to import word docs that are not like
+ this by default
+ */
+ // put in initialization list
+ // fNoLeading = true;
+ //fUsePrinterMetrics = true;
+}
+
+void WW8Dop::SetCompatibilityOptions(sal_uInt32 a32Bit)
+{
+ fNoTabForInd = ( a32Bit & 0x00000001 ) ;
+ fNoSpaceRaiseLower = ( a32Bit & 0x00000002 ) >> 1 ;
+ fSuppressSpbfAfterPageBreak = ( a32Bit & 0x00000004 ) >> 2 ;
+ fWrapTrailSpaces = ( a32Bit & 0x00000008 ) >> 3 ;
+ fMapPrintTextColor = ( a32Bit & 0x00000010 ) >> 4 ;
+ fNoColumnBalance = ( a32Bit & 0x00000020 ) >> 5 ;
+ fConvMailMergeEsc = ( a32Bit & 0x00000040 ) >> 6 ;
+ fSuppressTopSpacing = ( a32Bit & 0x00000080 ) >> 7 ;
+ fOrigWordTableRules = ( a32Bit & 0x00000100 ) >> 8 ;
+ fTransparentMetafiles = ( a32Bit & 0x00000200 ) >> 9 ;
+ fShowBreaksInFrames = ( a32Bit & 0x00000400 ) >> 10 ;
+ fSwapBordersFacingPgs = ( a32Bit & 0x00000800 ) >> 11 ;
+ fCompatibilityOptions_Unknown1_13 = ( a32Bit & 0x00001000 ) >> 12 ;
+ fExpShRtn = ( a32Bit & 0x00002000 ) >> 13 ; // #i56856#
+ fCompatibilityOptions_Unknown1_15 = ( a32Bit & 0x00004000 ) >> 14 ;
+ fCompatibilityOptions_Unknown1_16 = ( a32Bit & 0x00008000 ) >> 15 ;
+ fSuppressTopSpacingMac5 = ( a32Bit & 0x00010000 ) >> 16 ;
+ fTruncDxaExpand = ( a32Bit & 0x00020000 ) >> 17 ;
+ fPrintBodyBeforeHdr = ( a32Bit & 0x00040000 ) >> 18 ;
+ fNoLeading = ( a32Bit & 0x00080000 ) >> 19 ;
+ fCompatibilityOptions_Unknown1_21 = ( a32Bit & 0x00100000 ) >> 20 ;
+ fMWSmallCaps = ( a32Bit & 0x00200000 ) >> 21 ;
+ fCompatibilityOptions_Unknown1_23 = ( a32Bit & 0x00400000 ) >> 22 ;
+ fCompatibilityOptions_Unknown1_24 = ( a32Bit & 0x00800800 ) >> 23 ;
+ fCompatibilityOptions_Unknown1_25 = ( a32Bit & 0x01000000 ) >> 24 ;
+ fCompatibilityOptions_Unknown1_26 = ( a32Bit & 0x02000000 ) >> 25 ;
+ fCompatibilityOptions_Unknown1_27 = ( a32Bit & 0x04000000 ) >> 26 ;
+ fCompatibilityOptions_Unknown1_28 = ( a32Bit & 0x08000000 ) >> 27 ;
+ fCompatibilityOptions_Unknown1_29 = ( a32Bit & 0x10000000 ) >> 28 ;
+ fCompatibilityOptions_Unknown1_30 = ( a32Bit & 0x20000000 ) >> 29 ;
+ fCompatibilityOptions_Unknown1_31 = ( a32Bit & 0x40000000 ) >> 30 ;
+
+ fUsePrinterMetrics = ( a32Bit & 0x80000000 ) >> 31 ;
+}
+
+sal_uInt32 WW8Dop::GetCompatibilityOptions() const
+{
+ sal_uInt32 a32Bit = 0;
+ if (fNoTabForInd) a32Bit |= 0x00000001;
+ if (fNoSpaceRaiseLower) a32Bit |= 0x00000002;
+ if (fSuppressSpbfAfterPageBreak) a32Bit |= 0x00000004;
+ if (fWrapTrailSpaces) a32Bit |= 0x00000008;
+ if (fMapPrintTextColor) a32Bit |= 0x00000010;
+ if (fNoColumnBalance) a32Bit |= 0x00000020;
+ if (fConvMailMergeEsc) a32Bit |= 0x00000040;
+ if (fSuppressTopSpacing) a32Bit |= 0x00000080;
+ if (fOrigWordTableRules) a32Bit |= 0x00000100;
+ if (fTransparentMetafiles) a32Bit |= 0x00000200;
+ if (fShowBreaksInFrames) a32Bit |= 0x00000400;
+ if (fSwapBordersFacingPgs) a32Bit |= 0x00000800;
+ if (fCompatibilityOptions_Unknown1_13) a32Bit |= 0x00001000;
+ if (fExpShRtn) a32Bit |= 0x00002000; // #i56856#
+ if (fCompatibilityOptions_Unknown1_15) a32Bit |= 0x00004000;
+ if (fCompatibilityOptions_Unknown1_16) a32Bit |= 0x00008000;
+ if (fSuppressTopSpacingMac5) a32Bit |= 0x00010000;
+ if (fTruncDxaExpand) a32Bit |= 0x00020000;
+ if (fPrintBodyBeforeHdr) a32Bit |= 0x00040000;
+ if (fNoLeading) a32Bit |= 0x00080000;
+ if (fCompatibilityOptions_Unknown1_21) a32Bit |= 0x00100000;
+ if (fMWSmallCaps) a32Bit |= 0x00200000;
+ if (fCompatibilityOptions_Unknown1_23) a32Bit |= 0x00400000;
+ if (fCompatibilityOptions_Unknown1_24) a32Bit |= 0x00800000;
+ if (fCompatibilityOptions_Unknown1_25) a32Bit |= 0x01000000;
+ if (fCompatibilityOptions_Unknown1_26) a32Bit |= 0x02000000;
+ if (fCompatibilityOptions_Unknown1_27) a32Bit |= 0x04000000;
+ if (fCompatibilityOptions_Unknown1_28) a32Bit |= 0x08000000;
+ if (fCompatibilityOptions_Unknown1_29) a32Bit |= 0x10000000;
+ if (fCompatibilityOptions_Unknown1_30) a32Bit |= 0x20000000;
+ if (fCompatibilityOptions_Unknown1_31) a32Bit |= 0x40000000;
+ if (fUsePrinterMetrics) a32Bit |= 0x80000000;
+ return a32Bit;
+}
+
+// i#78591#
+void WW8Dop::SetCompatibilityOptions2(sal_uInt32 a32Bit)
+{
+ fCompatibilityOptions_Unknown2_1 = ( a32Bit & 0x00000001 );
+ fCompatibilityOptions_Unknown2_2 = ( a32Bit & 0x00000002 ) >> 1 ;
+ fDontUseHTMLAutoSpacing = ( a32Bit & 0x00000004 ) >> 2 ;
+ fCompatibilityOptions_Unknown2_4 = ( a32Bit & 0x00000008 ) >> 3 ;
+ fCompatibilityOptions_Unknown2_5 = ( a32Bit & 0x00000010 ) >> 4 ;
+ fCompatibilityOptions_Unknown2_6 = ( a32Bit & 0x00000020 ) >> 5 ;
+ fCompatibilityOptions_Unknown2_7 = ( a32Bit & 0x00000040 ) >> 6 ;
+ fCompatibilityOptions_Unknown2_8 = ( a32Bit & 0x00000080 ) >> 7 ;
+ fCompatibilityOptions_Unknown2_9 = ( a32Bit & 0x00000100 ) >> 8 ;
+ fCompatibilityOptions_Unknown2_10 = ( a32Bit & 0x00000200 ) >> 9 ;
+ fDontBreakWrappedTables = ( a32Bit & 0x00000400 ) >> 10 ;
+ fCompatibilityOptions_Unknown2_12 = ( a32Bit & 0x00000800 ) >> 11 ;
+ fCompatibilityOptions_Unknown2_13 = ( a32Bit & 0x00001000 ) >> 12 ;
+ fCompatibilityOptions_Unknown2_14 = ( a32Bit & 0x00002000 ) >> 13 ;
+ fCompatibilityOptions_Unknown2_15 = ( a32Bit & 0x00004000 ) >> 14 ;
+ fCompatibilityOptions_Unknown2_16 = ( a32Bit & 0x00008000 ) >> 15 ;
+ fCompatibilityOptions_Unknown2_17 = ( a32Bit & 0x00010000 ) >> 16 ;
+ fCompatibilityOptions_Unknown2_18 = ( a32Bit & 0x00020000 ) >> 17 ;
+ fCompatibilityOptions_Unknown2_19 = ( a32Bit & 0x00040000 ) >> 18 ;
+ fCompatibilityOptions_Unknown2_20 = ( a32Bit & 0x00080000 ) >> 19 ;
+ fCompatibilityOptions_Unknown2_21 = ( a32Bit & 0x00100000 ) >> 20 ;
+ fCompatibilityOptions_Unknown2_22 = ( a32Bit & 0x00200000 ) >> 21 ;
+ fCompatibilityOptions_Unknown2_23 = ( a32Bit & 0x00400000 ) >> 22 ;
+ fCompatibilityOptions_Unknown2_24 = ( a32Bit & 0x00800800 ) >> 23 ;
+ fCompatibilityOptions_Unknown2_25 = ( a32Bit & 0x01000800 ) >> 24 ;
+ fCompatibilityOptions_Unknown2_26 = ( a32Bit & 0x02000800 ) >> 25 ;
+ fCompatibilityOptions_Unknown2_27 = ( a32Bit & 0x04000800 ) >> 26 ;
+ fCompatibilityOptions_Unknown2_28 = ( a32Bit & 0x08000800 ) >> 27 ;
+ fCompatibilityOptions_Unknown2_29 = ( a32Bit & 0x10000800 ) >> 28 ;
+ fCompatibilityOptions_Unknown2_30 = ( a32Bit & 0x20000800 ) >> 29 ;
+ fCompatibilityOptions_Unknown2_31 = ( a32Bit & 0x40000800 ) >> 30 ;
+ fCompatibilityOptions_Unknown2_32 = ( a32Bit & 0x80000000 ) >> 31 ;
+}
+
+sal_uInt32 WW8Dop::GetCompatibilityOptions2() const
+{
+ sal_uInt32 a32Bit = 0;
+ if (fCompatibilityOptions_Unknown2_1) a32Bit |= 0x00000001;
+ if (fCompatibilityOptions_Unknown2_2) a32Bit |= 0x00000002;
+ if (fDontUseHTMLAutoSpacing) a32Bit |= 0x00000004;
+ if (fCompatibilityOptions_Unknown2_4) a32Bit |= 0x00000008;
+ if (fCompatibilityOptions_Unknown2_5) a32Bit |= 0x00000010;
+ if (fCompatibilityOptions_Unknown2_6) a32Bit |= 0x00000020;
+ if (fCompatibilityOptions_Unknown2_7) a32Bit |= 0x00000040;
+ if (fCompatibilityOptions_Unknown2_8) a32Bit |= 0x00000080;
+ if (fCompatibilityOptions_Unknown2_9) a32Bit |= 0x00000100;
+ if (fCompatibilityOptions_Unknown2_10) a32Bit |= 0x00000200;
+ if (fDontBreakWrappedTables) a32Bit |= 0x00000400;
+ if (fCompatibilityOptions_Unknown2_12) a32Bit |= 0x00000800;
+ if (fCompatibilityOptions_Unknown2_13) a32Bit |= 0x00001000;
+ //#i42909# set thai "line breaking rules" compatibility option
+ // pflin, wonder whether bUseThaiLineBreakingRules is correct
+ // when importing word document.
+ if (bUseThaiLineBreakingRules) a32Bit |= 0x00002000;
+ else if (fCompatibilityOptions_Unknown2_14) a32Bit |= 0x00002000;
+ if (fCompatibilityOptions_Unknown2_15) a32Bit |= 0x00004000;
+ if (fCompatibilityOptions_Unknown2_16) a32Bit |= 0x00008000;
+ if (fCompatibilityOptions_Unknown2_17) a32Bit |= 0x00010000;
+ if (fCompatibilityOptions_Unknown2_18) a32Bit |= 0x00020000;
+ if (fCompatibilityOptions_Unknown2_19) a32Bit |= 0x00040000;
+ if (fCompatibilityOptions_Unknown2_20) a32Bit |= 0x00080000;
+ if (fCompatibilityOptions_Unknown2_21) a32Bit |= 0x00100000;
+ if (fCompatibilityOptions_Unknown2_22) a32Bit |= 0x00200000;
+ if (fCompatibilityOptions_Unknown2_23) a32Bit |= 0x00400000;
+ if (fCompatibilityOptions_Unknown2_24) a32Bit |= 0x00800000;
+ if (fCompatibilityOptions_Unknown2_25) a32Bit |= 0x01000000;
+ if (fCompatibilityOptions_Unknown2_26) a32Bit |= 0x02000000;
+ if (fCompatibilityOptions_Unknown2_27) a32Bit |= 0x04000000;
+ if (fCompatibilityOptions_Unknown2_28) a32Bit |= 0x08000000;
+ if (fCompatibilityOptions_Unknown2_29) a32Bit |= 0x10000000;
+ if (fCompatibilityOptions_Unknown2_30) a32Bit |= 0x20000000;
+ if (fCompatibilityOptions_Unknown2_31) a32Bit |= 0x40000000;
+ if (fCompatibilityOptions_Unknown2_32) a32Bit |= 0x80000000;
+ return a32Bit;
+}
+
+void WW8Dop::Write(SvStream& rStrm, WW8Fib& rFib) const
+{
+ const int nMaxDopLen = 610;
+ sal_uInt32 nLen = 8 == rFib.m_nVersion ? nMaxDopLen : 84;
+ rFib.m_fcDop = rStrm.Tell();
+ rFib.m_lcbDop = nLen;
+
+ sal_uInt8 aData[ nMaxDopLen ] = {};
+ sal_uInt8* pData = aData;
+
+ // analyse the data
+ sal_uInt16 a16Bit;
+ sal_uInt8 a8Bit;
+
+ a16Bit = 0; // 0 0x00
+ if (fFacingPages)
+ a16Bit |= 0x0001;
+ if (fWidowControl)
+ a16Bit |= 0x0002;
+ if (fPMHMainDoc)
+ a16Bit |= 0x0004;
+ a16Bit |= ( 0x0018 & (grfSuppression << 3));
+ a16Bit |= ( 0x0060 & (fpc << 5));
+ a16Bit |= ( 0xff00 & (grpfIhdt << 8));
+ Set_UInt16( pData, a16Bit );
+
+ a16Bit = 0; // 2 0x02
+ a16Bit |= ( 0x0003 & rncFootnote );
+ a16Bit |= ( ~0x0003 & (nFootnote << 2));
+ Set_UInt16( pData, a16Bit );
+
+ a8Bit = 0; // 4 0x04
+ if( fOutlineDirtySave ) a8Bit |= 0x01;
+ Set_UInt8( pData, a8Bit );
+
+ a8Bit = 0; // 5 0x05
+ if( fOnlyMacPics ) a8Bit |= 0x01;
+ if( fOnlyWinPics ) a8Bit |= 0x02;
+ if( fLabelDoc ) a8Bit |= 0x04;
+ if( fHyphCapitals ) a8Bit |= 0x08;
+ if( fAutoHyphen ) a8Bit |= 0x10;
+ if( fFormNoFields ) a8Bit |= 0x20;
+ if( fLinkStyles ) a8Bit |= 0x40;
+ if( fRevMarking ) a8Bit |= 0x80;
+ Set_UInt8( pData, a8Bit );
+
+ a8Bit = 0; // 6 0x06
+ if( fBackup ) a8Bit |= 0x01;
+ if( fExactCWords ) a8Bit |= 0x02;
+ if( fPagHidden ) a8Bit |= 0x04;
+ if( fPagResults ) a8Bit |= 0x08;
+ if( fLockAtn ) a8Bit |= 0x10;
+ if( fMirrorMargins ) a8Bit |= 0x20;
+ if( fReadOnlyRecommended ) a8Bit |= 0x40;
+ if( fDfltTrueType ) a8Bit |= 0x80;
+ Set_UInt8( pData, a8Bit );
+
+ a8Bit = 0; // 7 0x07
+ if( fPagSuppressTopSpacing ) a8Bit |= 0x01;
+ if( fProtEnabled ) a8Bit |= 0x02;
+ if( fDispFormFieldSel ) a8Bit |= 0x04;
+ if( fRMView ) a8Bit |= 0x08;
+ if( fRMPrint ) a8Bit |= 0x10;
+ if( fWriteReservation ) a8Bit |= 0x20;
+ if( fLockRev ) a8Bit |= 0x40;
+ if( fEmbedFonts ) a8Bit |= 0x80;
+ Set_UInt8( pData, a8Bit );
+
+ a8Bit = 0; // 8 0x08
+ if( copts_fNoTabForInd ) a8Bit |= 0x01;
+ if( copts_fNoSpaceRaiseLower ) a8Bit |= 0x02;
+ if( copts_fSuppressSpbfAfterPgBrk ) a8Bit |= 0x04;
+ if( copts_fWrapTrailSpaces ) a8Bit |= 0x08;
+ if( copts_fMapPrintTextColor ) a8Bit |= 0x10;
+ if( copts_fNoColumnBalance ) a8Bit |= 0x20;
+ if( copts_fConvMailMergeEsc ) a8Bit |= 0x40;
+ if( copts_fSuppressTopSpacing ) a8Bit |= 0x80;
+ Set_UInt8( pData, a8Bit );
+
+ a8Bit = 0; // 9 0x09
+ if( copts_fOrigWordTableRules ) a8Bit |= 0x01;
+ if( copts_fTransparentMetafiles ) a8Bit |= 0x02;
+ if( copts_fShowBreaksInFrames ) a8Bit |= 0x04;
+ if( copts_fSwapBordersFacingPgs ) a8Bit |= 0x08;
+ if( copts_fExpShRtn ) a8Bit |= 0x20; // #i56856#
+ Set_UInt8( pData, a8Bit );
+
+ Set_UInt16( pData, dxaTab ); // 10 0x0a
+ Set_UInt16( pData, wSpare ); // 12 0x0c
+ Set_UInt16( pData, dxaHotZ ); // 14 0x0e
+ Set_UInt16( pData, cConsecHypLim ); // 16 0x10
+ Set_UInt16( pData, wSpare2 ); // 18 0x12
+ Set_UInt32( pData, dttmCreated ); // 20 0x14
+ Set_UInt32( pData, dttmRevised ); // 24 0x18
+ Set_UInt32( pData, dttmLastPrint ); // 28 0x1c
+ Set_UInt16( pData, nRevision ); // 32 0x20
+ Set_UInt32( pData, tmEdited ); // 34 0x22
+ Set_UInt32( pData, cWords ); // 38 0x26
+ Set_UInt32( pData, cCh ); // 42 0x2a
+ Set_UInt16( pData, cPg ); // 46 0x2e
+ Set_UInt32( pData, cParas ); // 48 0x30
+
+ a16Bit = 0; // 52 0x34
+ a16Bit |= ( 0x0003 & rncEdn );
+ a16Bit |= (~0x0003 & ( nEdn << 2));
+ Set_UInt16( pData, a16Bit );
+
+ a16Bit = 0; // 54 0x36
+ a16Bit |= (0x0003 & epc );
+ a16Bit |= (0x003c & (nfcFootnoteRef << 2));
+ a16Bit |= (0x03c0 & (nfcEdnRef << 6));
+ if( fPrintFormData ) a16Bit |= 0x0400;
+ if( fSaveFormData ) a16Bit |= 0x0800;
+ if( fShadeFormData ) a16Bit |= 0x1000;
+ if( fWCFootnoteEdn ) a16Bit |= 0x8000;
+ Set_UInt16( pData, a16Bit );
+
+ Set_UInt32( pData, cLines ); // 56 0x38
+ Set_UInt32( pData, cWordsFootnoteEnd ); // 60 0x3c
+ Set_UInt32( pData, cChFootnoteEdn ); // 64 0x40
+ Set_UInt16( pData, cPgFootnoteEdn ); // 68 0x44
+ Set_UInt32( pData, cParasFootnoteEdn ); // 70 0x46
+ Set_UInt32( pData, cLinesFootnoteEdn ); // 74 0x4a
+ Set_UInt32( pData, lKeyProtDoc ); // 78 0x4e
+
+ a16Bit = 0; // 82 0x52
+ if (wvkSaved)
+ a16Bit |= 0x0007;
+ a16Bit |= (0x0ff8 & (wScaleSaved << 3));
+ a16Bit |= (0x3000 & (zkSaved << 12));
+ if (iGutterPos)
+ {
+ // Last bit: gutter at top.
+ a16Bit |= 0x8000;
+ }
+ Set_UInt16( pData, a16Bit );
+
+ if( 8 == rFib.m_nVersion )
+ {
+ Set_UInt32(pData, GetCompatibilityOptions()); // 84 0x54
+
+ Set_UInt16( pData, adt ); // 88 0x58
+
+ doptypography.WriteToMem(pData); // 400 0x190
+
+ memcpy( pData, &dogrid, sizeof( WW8_DOGRID ));
+ pData += sizeof( WW8_DOGRID );
+
+ a16Bit = 0x12; // set lvl to 9 // 410 0x19a
+ if( fHtmlDoc ) a16Bit |= 0x0200;
+ if( fSnapBorder ) a16Bit |= 0x0800;
+ if( fIncludeHeader ) a16Bit |= 0x1000;
+ if( fIncludeFooter ) a16Bit |= 0x2000;
+ if( fForcePageSizePag ) a16Bit |= 0x4000;
+ if( fMinFontSizePag ) a16Bit |= 0x8000;
+ Set_UInt16( pData, a16Bit );
+
+ a16Bit = 0; // 412 0x19c
+ if( fHaveVersions ) a16Bit |= 0x0001;
+ if( fAutoVersion ) a16Bit |= 0x0002;
+ Set_UInt16( pData, a16Bit );
+
+ pData += 12; // 414 0x19e
+
+ Set_UInt32( pData, cChWS ); // 426 0x1aa
+ Set_UInt32( pData, cChWSFootnoteEdn ); // 430 0x1ae
+ Set_UInt32( pData, grfDocEvents ); // 434 0x1b2
+
+ pData += 4+30+8; // 438 0x1b6; 442 0x1ba; 472 0x1d8; 476 0x1dc
+
+ Set_UInt32( pData, cDBC ); // 480 0x1e0
+ Set_UInt32( pData, cDBCFootnoteEdn ); // 484 0x1e4
+
+ pData += 1 * sizeof( sal_Int32); // 488 0x1e8
+
+ Set_UInt16( pData, nfcFootnoteRef ); // 492 0x1ec
+ Set_UInt16( pData, nfcEdnRef ); // 494 0x1ee
+ Set_UInt16( pData, hpsZoomFontPag ); // 496 0x1f0
+ Set_UInt16( pData, dywDispPag ); // 498 0x1f2
+
+ //500 -> 508, Appear to be repeated here in 2000+
+ pData += 8;
+ Set_UInt32(pData, GetCompatibilityOptions());
+ Set_UInt32(pData, GetCompatibilityOptions2());
+ pData += 32;
+
+ a16Bit = 0;
+ if (fEmbedFactoids)
+ a16Bit |= 0x8;
+ if (fAcetateShowMarkup)
+ a16Bit |= 0x1000;
+ //Word XP at least requires fAcetateShowMarkup to honour fAcetateShowAtn
+ if (fAcetateShowAtn)
+ {
+ a16Bit |= 0x1000;
+ a16Bit |= 0x2000;
+ }
+ Set_UInt16(pData, a16Bit);
+
+ pData += 48;
+ a16Bit = 0x0080;
+ Set_UInt16(pData, a16Bit);
+ }
+ rStrm.WriteBytes(aData, nLen);
+}
+
+void WW8DopTypography::ReadFromMem(sal_uInt8 *&pData)
+{
+ sal_uInt16 a16Bit = Get_UShort(pData);
+ m_fKerningPunct = (a16Bit & 0x0001);
+ m_iJustification = (a16Bit & 0x0006) >> 1;
+ m_iLevelOfKinsoku = (a16Bit & 0x0018) >> 3;
+ m_f2on1 = (a16Bit & 0x0020) >> 5;
+ m_reserved1 = (a16Bit & 0x03C0) >> 6;
+ m_reserved2 = (a16Bit & 0xFC00) >> 10;
+
+ m_cchFollowingPunct = Get_Short(pData);
+ m_cchLeadingPunct = Get_Short(pData);
+
+ sal_Int16 i;
+ for (i=0; i < nMaxFollowing; ++i)
+ m_rgxchFPunct[i] = Get_Short(pData);
+ for (i=0; i < nMaxLeading; ++i)
+ m_rgxchLPunct[i] = Get_Short(pData);
+
+ if (m_cchFollowingPunct >= 0 && m_cchFollowingPunct < nMaxFollowing)
+ m_rgxchFPunct[m_cchFollowingPunct]=0;
+ else
+ m_rgxchFPunct[nMaxFollowing - 1]=0;
+
+ if (m_cchLeadingPunct >= 0 && m_cchLeadingPunct < nMaxLeading)
+ m_rgxchLPunct[m_cchLeadingPunct]=0;
+ else
+ m_rgxchLPunct[nMaxLeading - 1]=0;
+
+}
+
+void WW8DopTypography::WriteToMem(sal_uInt8 *&pData) const
+{
+ sal_uInt16 a16Bit = sal_uInt16(m_fKerningPunct);
+ a16Bit |= (m_iJustification << 1) & 0x0006;
+ a16Bit |= (m_iLevelOfKinsoku << 3) & 0x0018;
+ a16Bit |= (int(m_f2on1) << 5) & 0x0020;
+ a16Bit |= (m_reserved1 << 6) & 0x03C0;
+ a16Bit |= (m_reserved2 << 10) & 0xFC00;
+ Set_UInt16(pData,a16Bit);
+
+ Set_UInt16(pData,m_cchFollowingPunct);
+ Set_UInt16(pData,m_cchLeadingPunct);
+
+ sal_Int16 i;
+ for (i=0; i < nMaxFollowing; ++i)
+ Set_UInt16(pData,m_rgxchFPunct[i]);
+ for (i=0; i < nMaxLeading; ++i)
+ Set_UInt16(pData,m_rgxchLPunct[i]);
+}
+
+LanguageType WW8DopTypography::GetConvertedLang() const
+{
+ LanguageType nLang;
+ //I have assumed people's republic/taiwan == simplified/traditional
+
+ //This isn't a documented issue, so we might have it all wrong,
+ //i.e. i.e. what's with the powers of two ?
+
+ /*
+ One example of 3 for reserved1 which was really Japanese, perhaps last bit
+ is for some other use ?, or redundant. If more examples trigger the assert
+ we might be able to figure it out.
+ */
+ switch(m_reserved1 & 0xE)
+ {
+ case 2: //Japan
+ nLang = LANGUAGE_JAPANESE;
+ break;
+ case 4: //Chinese (People's Republic)
+ nLang = LANGUAGE_CHINESE_SIMPLIFIED;
+ break;
+ case 6: //Korean
+ nLang = LANGUAGE_KOREAN;
+ break;
+ case 8: //Chinese (Taiwan)
+ nLang = LANGUAGE_CHINESE_TRADITIONAL;
+ break;
+ default:
+ OSL_ENSURE(false, "Unknown MS Asian Typography language, report");
+ nLang = LANGUAGE_CHINESE_SIMPLIFIED_LEGACY;
+ break;
+ case 0:
+ //And here we have the possibility that it says 2, but it's really
+ //a bug and only japanese level 2 has been selected after a custom
+ //version was chosen on last save!
+ nLang = LANGUAGE_JAPANESE;
+ break;
+ }
+ return nLang;
+}
+
+// Sprms
+
+sal_uInt16 wwSprmParser::GetSprmTailLen(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen)
+ const
+{
+ SprmInfo aSprm = GetSprmInfo(nId);
+ sal_uInt16 nL = 0; // number of Bytes to read
+
+ //sprmPChgTabs
+ switch( nId )
+ {
+ case 23:
+ case 0xC615:
+ if( pSprm[1 + mnDelta] != 255 )
+ nL = static_cast< sal_uInt16 >(pSprm[1 + mnDelta] + aSprm.nLen);
+ else
+ {
+ sal_uInt8 nDelIdx = 2 + mnDelta;
+ sal_uInt8 nDel = nDelIdx < nRemLen ? pSprm[nDelIdx] : 0;
+ sal_uInt8 nInsIdx = 3 + mnDelta + 4 * nDel;
+ sal_uInt8 nIns = nInsIdx < nRemLen ? pSprm[nInsIdx] : 0;
+
+ nL = 2 + 4 * nDel + 3 * nIns;
+ }
+ break;
+ default:
+ switch (aSprm.nVari)
+ {
+ case L_FIX:
+ nL = aSprm.nLen; // Excl. Token
+ break;
+ case L_VAR:
+ // Variable 1-Byte Length
+ // parameter length (i.e. excluding token and length byte)
+ nL = static_cast< sal_uInt16 >(pSprm[1 + mnDelta] + aSprm.nLen);
+ break;
+ case L_VAR2:
+ {
+ // Variable 2-Byte Length
+ // For sprmTDefTable and sprmTDefTable10, the length of the
+ // parameter plus 1 is recorded in the two bytes beginning
+ // at offset (WW7-) 1 or (WW8+) 2
+ sal_uInt8 nIndex = 1 + mnDelta;
+ sal_uInt16 nCount;
+ if (nIndex + 1 >= nRemLen)
+ {
+ SAL_WARN("sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong");
+ nCount = 0;
+ }
+ else
+ {
+ nCount = SVBT16ToUInt16(&pSprm[nIndex]);
+ SAL_WARN_IF(nCount < 1, "sw.ww8", "length should have been at least 1");
+ if (nCount)
+ --nCount;
+ }
+ nL = static_cast<sal_uInt16>(nCount + aSprm.nLen);
+ break;
+ }
+ default:
+ OSL_ENSURE(false, "Unknown sprm variant");
+ break;
+ }
+ break;
+ }
+ return nL;
+}
+
+// one or two bytes at the beginning at the sprm id
+sal_uInt16 wwSprmParser::GetSprmId(const sal_uInt8* pSp) const
+{
+ OSL_ENSURE(pSp, "Why GetSprmId with pSp of 0");
+ if (!pSp)
+ return 0;
+
+ sal_uInt16 nId = 0;
+
+ if (ww::IsSevenMinus(meVersion))
+ {
+ nId = *pSp; // [0..0xff]
+ }
+ else
+ {
+ nId = SVBT16ToUInt16(pSp);
+ if (0x0800 > nId)
+ nId = 0;
+ }
+
+ return nId;
+}
+
+// with tokens and length byte
+sal_Int32 wwSprmParser::GetSprmSize(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) const
+{
+ return GetSprmTailLen(nId, pSprm, nRemLen) + 1 + mnDelta + SprmDataOfs(nId);
+}
+
+sal_uInt8 wwSprmParser::SprmDataOfs(sal_uInt16 nId) const
+{
+ return GetSprmInfo(nId).nVari;
+}
+
+sal_Int32 wwSprmParser::DistanceToData(sal_uInt16 nId) const
+{
+ return 1 + mnDelta + SprmDataOfs(nId);
+}
+
+SprmResult wwSprmParser::findSprmData(sal_uInt16 nId, sal_uInt8* pSprms,
+ sal_Int32 nLen) const
+{
+ while (nLen >= MinSprmLen())
+ {
+ const sal_uInt16 nCurrentId = GetSprmId(pSprms);
+ // set pointer to data
+ sal_Int32 nSize = GetSprmSize(nCurrentId, pSprms, nLen);
+
+ bool bValid = nSize <= nLen;
+
+ SAL_WARN_IF(!bValid, "sw.ww8",
+ "sprm 0x" << std::hex << nCurrentId << std::dec << " longer than remaining bytes, " <<
+ nSize << " vs " << nLen << "doc or parser is wrong");
+
+ if (nCurrentId == nId && bValid) // Sprm found
+ {
+ sal_Int32 nFixedLen = DistanceToData(nId);
+ return SprmResult(pSprms + nFixedLen, nSize - nFixedLen);
+ }
+
+ //Clip to available size if wrong
+ nSize = std::min(nSize, nLen);
+ pSprms += nSize;
+ nLen -= nSize;
+ }
+ // Sprm not found
+ return SprmResult();
+}
+
+SEPr::SEPr() :
+ bkc(2), fTitlePage(0), fAutoPgn(0), nfcPgn(0), fUnlocked(0), cnsPgn(0),
+ fPgnRestart(0), fEndNote(1), lnc(0), grpfIhdt(0), nLnnMod(0), dxaLnn(0),
+ dxaPgn(720), dyaPgn(720), fLBetween(0), vjc(0), dmBinFirst(0),
+ dmBinOther(0), dmPaperReq(0), fPropRMark(0), ibstPropRMark(0),
+ dttmPropRMark(0), dxtCharSpace(0), dyaLinePitch(0), clm(0), reserved1(0),
+ dmOrientPage(0), iHeadingPgn(0), pgnStart(1), lnnMin(0), wTextFlow(0),
+ reserved2(0), pgbApplyTo(0), pgbPageDepth(0), pgbOffsetFrom(0),
+ xaPage(lLetterWidth), yaPage(lLetterHeight), xaPageNUp(lLetterWidth), yaPageNUp(lLetterHeight),
+ dxaLeft(1800), dxaRight(1800), dyaTop(1440), dyaBottom(1440), dzaGutter(0),
+ dyaHdrTop(720), dyaHdrBottom(720), ccolM1(0), fEvenlySpaced(1),
+ reserved3(0), fBiDi(0), fFacingCol(0), fRTLGutter(0), fRTLAlignment(0),
+ dxaColumns(720), dxaColumnWidth(0), dmOrientFirst(0), fLayout(0),
+ reserved4(0)
+{
+}
+
+bool checkRead(SvStream &rSt, void *pDest, sal_uInt32 nLength)
+{
+ return (rSt.ReadBytes(pDest, nLength) == static_cast<std::size_t>(nLength));
+}
+
+#ifdef OSL_BIGENDIAN
+void swapEndian(sal_Unicode *pString)
+{
+ for (sal_Unicode *pWork = pString; *pWork; ++pWork)
+ *pWork = OSL_SWAPWORD(*pWork);
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8scan.hxx b/sw/source/filter/ww8/ww8scan.hxx
new file mode 100644
index 0000000000..320d3e3cb1
--- /dev/null
+++ b/sw/source/filter/ww8/ww8scan.hxx
@@ -0,0 +1,1888 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8SCAN_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8SCAN_HXX
+
+#include <cassert>
+#include <cstddef>
+#include <deque>
+#include <memory>
+#include <optional>
+#include <stack>
+#include <unordered_map>
+#include <vector>
+
+#include <osl/endian.h>
+#include <tools/solar.h>
+#include <tools/stream.hxx>
+#include <rtl/ustring.hxx>
+
+#include "ww8struc.hxx"
+#include "types.hxx"
+
+class SvStream;
+
+//Commonly used string literals for stream and storage names in word docs
+namespace SL
+{
+ inline constexpr OUString aObjectPool = u"ObjectPool"_ustr;
+ inline constexpr OUString a1Table = u"1Table"_ustr;
+ inline constexpr OUString a0Table = u"0Table"_ustr;
+ inline constexpr OUString aData = u"Data"_ustr;
+ inline constexpr OUString aCheckBox = u"CheckBox"_ustr;
+ inline constexpr OUString aListBox = u"ListBox"_ustr;
+ inline constexpr OUString aTextField = u"TextField"_ustr;
+ inline constexpr OUString aMSMacroCmds = u"MSMacroCmds"_ustr;
+}
+
+struct SprmInfo
+{
+ unsigned int nLen : 6;
+ unsigned int nVari : 2;
+};
+
+struct SprmInfoRow {
+ sal_uInt16 nId; ///< A ww8 sprm is hardcoded as 16bits
+ SprmInfo info;
+};
+
+class wwSprmSearcher {
+public:
+ //see Read_AmbiguousSPRM for the bPatchCJK oddity
+ wwSprmSearcher(SprmInfoRow const * rows, std::size_t size, bool bPatchCJK = false) {
+ for (std::size_t i = 0; i != size; ++i) {
+ bool ins = map_.emplace(rows[i].nId, rows[i].info).second;
+ assert(ins); (void) ins;
+ }
+ if (bPatchCJK)
+ patchCJKVariant();
+ }
+
+ SprmInfo const * search(sal_uInt16 id) const {
+ Map::const_iterator i(map_.find(id));
+ return i == map_.end() ? nullptr : &i->second;
+ }
+
+private:
+ typedef std::unordered_map<sal_uInt16, SprmInfo> Map;
+
+ Map map_;
+
+ void patchCJKVariant();
+};
+
+class WW8Fib;
+
+struct SprmResult
+{
+ const sal_uInt8* pSprm;
+ sal_Int32 nRemainingData;
+ SprmResult()
+ : pSprm(nullptr)
+ , nRemainingData(0)
+ {
+ }
+ SprmResult(const sal_uInt8* pInSprm, sal_Int32 nInRemainingData)
+ : pSprm(pInSprm)
+ , nRemainingData(nInRemainingData)
+ {
+ }
+};
+
+/**
+ wwSprmParser knows how to take a sequence of bytes and split it up into
+ sprms and their arguments
+*/
+class wwSprmParser
+{
+private:
+ ww::WordVersion meVersion;
+ sal_uInt8 mnDelta;
+ const wwSprmSearcher *mpKnownSprms;
+ static const wwSprmSearcher* GetWW8SprmSearcher();
+ static const wwSprmSearcher* GetWW6SprmSearcher(const WW8Fib& rFib);
+ static const wwSprmSearcher* GetWW2SprmSearcher();
+
+ SprmInfo GetSprmInfo(sal_uInt16 nId) const;
+
+ sal_uInt8 SprmDataOfs(sal_uInt16 nId) const;
+
+public:
+ enum SprmType {L_FIX=0, L_VAR=1, L_VAR2=2};
+
+ //7- ids are very different to 8+ ones
+ explicit wwSprmParser(const WW8Fib& rFib);
+ /// Return the SPRM id at the beginning of this byte sequence
+ sal_uInt16 GetSprmId(const sal_uInt8* pSp) const;
+
+ sal_Int32 GetSprmSize(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) const;
+
+ /// Get known len of a sprms head, the bytes of the sprm id + any bytes
+ /// reserved to hold a variable length
+ sal_Int32 DistanceToData(sal_uInt16 nId) const;
+
+ /// Get len of a sprms data area, ignoring the bytes of the sprm id and
+ /// ignoring any len bytes. Reports the remaining data after those bytes
+ sal_uInt16 GetSprmTailLen(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) const;
+
+ /// The minimum acceptable sprm len possible for this type of parser
+ int MinSprmLen() const { return (IsSevenMinus(meVersion)) ? 2 : 3; }
+
+ /// Returns the offset to data of the first sprm of id nId, 0
+ // if not found. nLen must be the <= length of pSprms
+ SprmResult findSprmData(sal_uInt16 nId, sal_uInt8* pSprms, sal_Int32 nLen) const;
+
+ ww::WordVersion GetFIBVersion() const { return meVersion; }
+};
+
+//Read a Pascal-style, i.e. single byte string length followed
+//by string contents
+inline OUString read_uInt8_PascalString(SvStream& rStrm, rtl_TextEncoding eEnc)
+{
+ return read_uInt8_lenPrefixed_uInt8s_ToOUString(rStrm, eEnc);
+}
+
+inline OUString read_uInt16_PascalString(SvStream& rStrm)
+{
+ return read_uInt16_lenPrefixed_uInt16s_ToOUString(rStrm);
+}
+
+//Belt and Braces strings, i.e. Pascal-style strings followed by
+//null termination, Spolsky calls them "fucked strings" FWIW
+//http://www.joelonsoftware.com/articles/fog0000000319.html
+OUString read_uInt8_BeltAndBracesString(SvStream& rStrm, rtl_TextEncoding eEnc);
+OUString read_uInt16_BeltAndBracesString(SvStream& rStrm);
+
+//--Line above which the code has meaningful comments
+
+class WW8ScannerBase;
+class WW8PLCFspecial;
+struct WW8PLCFxDesc;
+class WW8PLCFx_PCD;
+
+/**
+ reads array of strings (see MS documentation: String Table stored in File)
+ returns NOT the original pascal strings but an array of converted char*
+
+ attention: the *extra data* of each string are SKIPPED and ignored
+ */
+void WW8ReadSTTBF(bool bVer8, SvStream& rStrm, sal_uInt32 nStart, sal_Int32 nLen,
+ sal_uInt16 nExtraLen, rtl_TextEncoding eCS, std::vector<OUString> &rArray,
+ std::vector<ww::bytes>* pExtraArray = nullptr, std::vector<OUString>* pValueArray = nullptr);
+
+struct WW8FieldDesc
+{
+ WW8_CP nLen; ///< total length (to skip over text)
+ WW8_CP nSCode; ///< start of instructions code
+ WW8_CP nLCode; ///< length
+ WW8_CP nSRes; ///< start of result
+ WW8_CP nLRes; ///< length ( == 0, if no result )
+ sal_uInt16 nId; ///< WW-id for fields
+ sal_uInt8 nOpt; ///< WW-Flags ( e.g.: changed by user )
+ bool bCodeNest:1; ///< instruction used recursively
+ bool bResNest:1; ///< instruction inserted into result
+};
+
+struct WW8PLCFxSave1
+{
+ sal_uInt32 nPLCFxPos;
+ sal_uInt32 nPLCFxPos2; ///< for PLCF_Cp_Fkp: PieceIter-Pos
+ tools::Long nPLCFxMemOfs;
+ WW8_CP nStartCp; ///< for cp based iterator like PAP and CHP
+ tools::Long nCpOfs;
+ WW8_FC nStartFC;
+ WW8_CP nAttrStart;
+ WW8_CP nAttrEnd;
+ bool bLineEnd;
+};
+
+/**
+ among others for fields, that is, the same number of attr as positions,
+ if Ctor-Param bNoEnd = false
+*/
+class WW8PLCFspecial // iterator for PLCFs
+{
+private:
+ std::unique_ptr<sal_Int32[]> m_pPLCF_PosArray; ///< pointer to Pos-array and to the whole structure
+ sal_uInt8* m_pPLCF_Contents; ///< pointer to content-array-part of Pos-array
+ tools::Long m_nIMax; ///< number of elements
+ tools::Long m_nIdx; ///< marker where we currently are
+ sal_uInt32 m_nStru;
+
+ WW8PLCFspecial(const WW8PLCFspecial&) = delete;
+ WW8PLCFspecial& operator=(const WW8PLCFspecial&) = delete;
+
+public:
+ WW8PLCFspecial(SvStream* pSt, sal_uInt32 nFilePos, sal_uInt32 nPLCF,
+ sal_uInt32 nStruct);
+ tools::Long GetIdx() const { return m_nIdx; }
+ void SetIdx( tools::Long nI ) { m_nIdx = nI; }
+ tools::Long GetIMax() const { return m_nIMax; }
+ bool SeekPos(tools::Long nPos); // walks over FC- or CP-value
+ // resp. next biggest value
+ bool SeekPosExact(tools::Long nPos);
+ sal_Int32 Where() const
+ { return ( m_nIdx >= m_nIMax ) ? SAL_MAX_INT32 : m_pPLCF_PosArray[m_nIdx]; }
+ bool Get(WW8_CP& rStart, void*& rpValue) const;
+ bool GetData(tools::Long nIdx, WW8_CP& rPos, void*& rpValue) const;
+
+ const void* GetData( tools::Long nInIdx ) const
+ {
+ return ( nInIdx >= m_nIMax ) ? nullptr
+ : static_cast<const void*>(&m_pPLCF_Contents[nInIdx * m_nStru]);
+ }
+ sal_Int32 GetPos( tools::Long nInIdx ) const
+ { return ( nInIdx >= m_nIMax ) ? SAL_MAX_INT32 : m_pPLCF_PosArray[nInIdx]; }
+
+ void advance()
+ {
+ if (m_nIdx <= m_nIMax)
+ ++m_nIdx;
+ }
+};
+
+/** simple Iterator for SPRMs */
+class WW8SprmIter
+{
+private:
+ const wwSprmParser &mrSprmParser;
+ // these members will be updated
+ const sal_uInt8* m_pSprms; // remaining part of the SPRMs ( == start of current SPRM)
+ const sal_uInt8* m_pCurrentParams; // start of current SPRM's parameters
+ sal_uInt16 m_nCurrentId;
+ sal_Int32 m_nCurrentSize;
+
+ sal_Int32 m_nRemLen; // length of remaining SPRMs (including current SPRM)
+
+ void UpdateMyMembers();
+
+public:
+ explicit WW8SprmIter(const sal_uInt8* pSprms_, sal_Int32 nLen_,
+ const wwSprmParser &rSprmParser);
+ void SetSprms(const sal_uInt8* pSprms_, sal_Int32 nLen_);
+ SprmResult FindSprm(sal_uInt16 nId, bool bFindFirst, const sal_uInt8* pNextByteMatch = nullptr);
+ void advance();
+ const sal_uInt8* GetSprms() const
+ { return ( m_pSprms && (0 < m_nRemLen) ) ? m_pSprms : nullptr; }
+ const sal_uInt8* GetCurrentParams() const { return m_pCurrentParams; }
+ sal_uInt16 GetCurrentId() const { return m_nCurrentId; }
+ sal_Int32 GetRemLen() const { return m_nRemLen; }
+
+private:
+ WW8SprmIter(const WW8SprmIter&) = delete;
+ WW8SprmIter& operator=(const WW8SprmIter&) = delete;
+};
+
+/* among others for FKPs to normal attr., i.e. one less attr than positions */
+class WW8PLCF // Iterator for PLCFs
+{
+private:
+ std::unique_ptr<WW8_CP[]> m_pPLCF_PosArray; // pointer to Pos-array and the whole structure
+ sal_uInt8* m_pPLCF_Contents; // pointer to content-array-part of Pos-array
+ sal_Int32 m_nIMax; // number of elements
+ sal_Int32 m_nIdx;
+ int m_nStru;
+
+ void ReadPLCF(SvStream& rSt, WW8_FC nFilePos, sal_uInt32 nPLCF);
+
+ /*
+ If a PLC is missing in the doc and the FKPs stand alone,
+ we create a PLC with this:
+ */
+ void GeneratePLCF(SvStream& rSt, sal_Int32 nPN, sal_Int32 ncpN);
+
+ void MakeFailedPLCF();
+
+ void TruncToSortedRange();
+public:
+ WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct,
+ WW8_CP nStartPos = -1);
+
+ /*
+ the following ctor generates a PLC from nPN and ncpN, if necessary
+ */
+ WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct,
+ WW8_CP nStartPos, sal_Int32 nPN, sal_Int32 ncpN);
+
+ sal_Int32 GetIdx() const { return m_nIdx; }
+ void SetIdx( sal_Int32 nI ) { m_nIdx = nI; }
+ sal_Int32 GetIMax() const { return m_nIMax; }
+ bool SeekPos(WW8_CP nPos);
+ WW8_CP Where() const;
+ bool Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const;
+ void advance() { if( m_nIdx < m_nIMax ) ++m_nIdx; }
+
+ const void* GetData( sal_Int32 nInIdx ) const
+ {
+ return ( nInIdx >= m_nIMax ) ? nullptr :
+ static_cast<const void*>(&m_pPLCF_Contents[nInIdx * m_nStru]);
+ }
+};
+
+/* for Piece Table (.i.e. FastSave Table) */
+class WW8PLCFpcd
+{
+ friend class WW8PLCFpcd_Iter;
+
+ std::unique_ptr<WW8_CP[]> m_pPLCF_PosArray; // pointer to Pos-array and the whole structure
+ sal_uInt8* m_pPLCF_Contents; // pointer to content-array-part of Pos-array
+ sal_Int32 m_nIMax;
+ sal_uInt32 m_nStru;
+
+ WW8PLCFpcd(const WW8PLCFpcd&) = delete;
+ WW8PLCFpcd& operator=(const WW8PLCFpcd&) = delete;
+
+ void TruncToSortedRange();
+
+public:
+ WW8PLCFpcd(SvStream* pSt, sal_uInt32 nFilePos, sal_uInt32 nPLCF,
+ sal_uInt32 nStruct);
+};
+
+/* multiple WW8PLCFpcd_Iter may point to the same WW8PLCFpcd !!! */
+class WW8PLCFpcd_Iter
+{
+private:
+ WW8PLCFpcd& m_rPLCF;
+ tools::Long m_nIdx;
+
+ WW8PLCFpcd_Iter(const WW8PLCFpcd_Iter&) = delete;
+ WW8PLCFpcd_Iter& operator=(const WW8PLCFpcd_Iter&) = delete;
+
+public:
+ WW8PLCFpcd_Iter( WW8PLCFpcd& rPLCFpcd, tools::Long nStartPos = -1 );
+ tools::Long GetIdx() const { return m_nIdx; }
+ void SetIdx( tools::Long nI ) { m_nIdx = nI; }
+ tools::Long GetIMax() const { return m_rPLCF.m_nIMax; }
+ bool SeekPos(tools::Long nPos);
+ sal_Int32 Where() const;
+ bool Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const;
+ void advance()
+ {
+ if( m_nIdx < m_rPLCF.m_nIMax )
+ ++m_nIdx;
+ }
+};
+
+// PLCF-type:
+enum ePLCFT{ CHP=0, PAP, SEP, /*HED, FNR, ENR,*/ PLCF_END };
+
+//It's hardcoded that eFTN be the first one: a very poor hack, needs to be fixed
+enum eExtSprm { eFTN = 256, eEDN = 257, eFLD = 258, eBKN = 259, eAND = 260, eATNBKN = 261, eFACTOIDBKN = 262 };
+
+/*
+ pure virtual:
+*/
+class WW8PLCFx // virtual iterator for Piece Table Exceptions
+{
+private:
+ const WW8Fib& mrFib;
+ bool m_bIsSprm; // PLCF of Sprms or other stuff ( Footnote, ... )
+ WW8_FC m_nStartFc;
+ bool m_bDirty;
+
+ WW8PLCFx(const WW8PLCFx&) = delete;
+ WW8PLCFx& operator=(const WW8PLCFx&) = delete;
+
+public:
+ WW8PLCFx(const WW8Fib& rFib, bool bSprm)
+ : mrFib(rFib)
+ , m_bIsSprm(bSprm)
+ , m_nStartFc(-1)
+ , m_bDirty(false)
+ {
+ }
+ virtual ~WW8PLCFx() {}
+ bool IsSprm() const { return m_bIsSprm; }
+ virtual sal_uInt32 GetIdx() const = 0;
+ virtual void SetIdx(sal_uInt32 nIdx) = 0;
+ virtual sal_uInt32 GetIdx2() const;
+ virtual void SetIdx2(sal_uInt32 nIdx);
+ virtual bool SeekPos(WW8_CP nCpPos) = 0;
+ virtual WW8_FC Where() = 0;
+ virtual void GetSprms( WW8PLCFxDesc* p );
+ virtual tools::Long GetNoSprms( WW8_CP& rStart, WW8_CP&, sal_Int32& rLen );
+ virtual void advance() = 0;
+ virtual sal_uInt16 GetIstd() const { return 0xffff; }
+ virtual void Save( WW8PLCFxSave1& rSave ) const;
+ virtual void Restore( const WW8PLCFxSave1& rSave );
+ ww::WordVersion GetFIBVersion() const;
+ const WW8Fib& GetFIB() const { return mrFib; }
+ void SetStartFc( WW8_FC nFc ) { m_nStartFc = nFc; }
+ WW8_FC GetStartFc() const { return m_nStartFc; }
+ void SetDirty(bool bIn) {m_bDirty=bIn;}
+ bool GetDirty() const {return m_bDirty;}
+};
+
+class WW8PLCFx_PCDAttrs : public WW8PLCFx
+{
+private:
+ WW8PLCFpcd_Iter* m_pPcdI;
+ WW8PLCFx_PCD* m_pPcd;
+ std::vector<std::unique_ptr<sal_uInt8[]>> const & mrGrpprls; // attribute of Piece-table
+ sal_uInt8 m_aShortSprm[4]; // mini storage: can contain ONE sprm with 1 byte param
+
+ WW8PLCFx_PCDAttrs(const WW8PLCFx_PCDAttrs&) = delete;
+ WW8PLCFx_PCDAttrs& operator=(const WW8PLCFx_PCDAttrs&) = delete;
+
+public:
+ WW8PLCFx_PCDAttrs(const WW8Fib& rFib, WW8PLCFx_PCD* pPLCFx_PCD,
+ const WW8ScannerBase* pBase );
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nI) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual void GetSprms( WW8PLCFxDesc* p ) override;
+ virtual void advance() override;
+
+ WW8PLCFpcd_Iter* GetIter() const { return m_pPcdI; }
+};
+
+class WW8PLCFx_PCD : public WW8PLCFx // iterator for Piece table
+{
+private:
+ std::unique_ptr<WW8PLCFpcd_Iter> m_pPcdI;
+ bool m_bVer67;
+ WW8_CP m_nClipStart;
+
+ WW8PLCFx_PCD(const WW8PLCFx_PCD&) = delete;
+ WW8PLCFx_PCD& operator=(const WW8PLCFx_PCD&) = delete;
+
+public:
+ WW8PLCFx_PCD(const WW8Fib& rFib, WW8PLCFpcd* pPLCFpcd,
+ WW8_CP nStartCp, bool bVer67P);
+ virtual ~WW8PLCFx_PCD() override;
+ sal_uInt32 GetIMax() const;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nI) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual tools::Long GetNoSprms( WW8_CP& rStart, WW8_CP&, sal_Int32& rLen ) override;
+ virtual void advance() override;
+ WW8_CP CurrentPieceStartFc2Cp( WW8_FC nStartPos );
+ WW8_FC CurrentPieceStartCp2Fc( WW8_CP nCp );
+ static void CurrentPieceFc2Cp(WW8_CP& rStartPos, WW8_CP& rEndPos,
+ const WW8ScannerBase *pSBase);
+ WW8PLCFpcd_Iter* GetPLCFIter() { return m_pPcdI.get(); }
+ void SetClipStart(WW8_CP nIn) { m_nClipStart = nIn; }
+ WW8_CP GetClipStart() const { return m_nClipStart; }
+
+ static sal_Int32 TransformPieceAddress(tools::Long nfc, bool& bIsUnicodeAddress)
+ {
+ bIsUnicodeAddress = 0 == (0x40000000 & nfc);
+ return bIsUnicodeAddress ? nfc : (nfc & 0x3fffFFFF) / 2;
+ }
+};
+
+/**
+ Iterator for Piece Table Exceptions of Fkps
+ works only with FCs, not with CPs ! ( Low-Level )
+*/
+class WW8PLCFx_Fc_FKP : public WW8PLCFx
+{
+public:
+ class WW8Fkp // Iterator for Formatted Disk Page
+ {
+ private:
+ class Entry
+ {
+ public:
+ WW8_FC mnFC;
+
+ sal_uInt8* mpData;
+ sal_uInt16 mnLen;
+ sal_uInt16 mnIStd; // only for Fkp.Papx (actually Style-Nr)
+ bool mbMustDelete;
+
+ explicit Entry(WW8_FC nFC) : mnFC(nFC), mpData(nullptr), mnLen(0),
+ mnIStd(0), mbMustDelete(false) {}
+ Entry(const Entry &rEntry);
+ ~Entry();
+ bool operator<(const Entry& rEntry) const;
+ Entry& operator=(const Entry& rEntry);
+ };
+
+ sal_uInt8 maRawData[512];
+ std::vector<Entry> maEntries;
+
+ tools::Long m_nItemSize; // either 1 Byte or a complete BX
+
+ // Offset in Stream where last read of 512 bytes took place
+ tools::Long m_nFilePos;
+ sal_uInt8 mnIdx; // Pos marker
+ ePLCFT m_ePLCF;
+ sal_uInt8 mnIMax; // number of entries
+ int mnMustRemainCached; // after SaveAllPLCFx, before RestoreAllPLCFx
+
+ wwSprmParser maSprmParser;
+
+ //Fill in an Entry with sanity testing
+ void FillEntry(Entry &rEntry, std::size_t nDataOffset, sal_uInt16 nLen);
+
+ public:
+ WW8Fkp (const WW8Fib& rFib, SvStream* pFKPStrm,
+ SvStream* pDataStrm, tools::Long _nFilePos, tools::Long nItemSiz, ePLCFT ePl,
+ WW8_FC nStartFc);
+ void Reset(WW8_FC nPos);
+ tools::Long GetFilePos() const { return m_nFilePos; }
+ sal_uInt8 GetIdx() const { return mnIdx; }
+ void SetIdx(sal_uInt8 nI);
+ bool SeekPos(WW8_FC nFc);
+ WW8_FC Where() const
+ {
+ return (mnIdx < mnIMax) ? maEntries[mnIdx].mnFC : WW8_FC_MAX;
+ }
+ void advance()
+ {
+ if (mnIdx < mnIMax)
+ ++mnIdx;
+ }
+ sal_uInt8* Get( WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen ) const;
+ sal_uInt16 GetIstd() const { return maEntries[mnIdx].mnIStd; }
+
+ /*
+ returns a real pointer to the Sprm of type nId,
+ if such a thing is in the Fkp.
+ */
+ sal_uInt8* GetLenAndIStdAndSprms(sal_Int32& rLen) const;
+
+ /*
+ calls GetLenAndIStdAndSprms()...
+ 2020 bFindFirst note: Normally the last SPRM takes effect, so I don't know why HasSprm always returned the first value!
+ I don't dare to change the default due to regression potential (and slower in the few cases looking for a boolean result),
+ but first thing to try is use FindFirst as false.
+ */
+ SprmResult HasSprm(sal_uInt16 nId, bool bFindFirst = true);
+ void HasSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult);
+
+ const wwSprmParser &GetSprmParser() const { return maSprmParser; }
+
+ void IncMustRemainCache() { ++mnMustRemainCached; }
+ bool IsMustRemainCache() const { return mnMustRemainCached > 0; }
+ void DecMustRemainCache() { --mnMustRemainCached; }
+ };
+
+private:
+ SvStream* m_pFKPStrm; // input file
+ SvStream* m_pDataStrm; // input file
+ std::unique_ptr<WW8PLCF> m_pPLCF;
+protected:
+ WW8Fkp* m_pFkp;
+private:
+
+ /*
+ Keep a cache of eMaxCache entries of previously seen pFkps, which
+ speeds up considerably table parsing and load save plcfs for what turn
+ out to be small text frames, which frames generally are
+
+ size : cache hits
+ cache all : 19168 pap, 48 chp
+ == 100 : 19166 pap, 48 chp
+ == 50 : 18918 pap, 48 chp
+ == 10 : 18549 pap, 47 chp
+ == 5 : 18515 pap, 47 chp
+ */
+ std::deque<std::unique_ptr<WW8Fkp>> maFkpCache;
+ enum Limits {eMaxCache = 50000};
+
+ bool NewFkp();
+
+ WW8PLCFx_Fc_FKP(const WW8PLCFx_Fc_FKP&) = delete;
+ WW8PLCFx_Fc_FKP& operator=(const WW8PLCFx_Fc_FKP&) = delete;
+
+protected:
+ ePLCFT m_ePLCF;
+ std::unique_ptr<WW8PLCFx_PCDAttrs> m_pPCDAttrs;
+
+public:
+ WW8PLCFx_Fc_FKP( SvStream* pSt, SvStream* pTableSt, SvStream* pDataSt,
+ const WW8Fib& rFib, ePLCFT ePl, WW8_FC nStartFcL );
+ virtual ~WW8PLCFx_Fc_FKP() override;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_FC nFcPos) override;
+ virtual WW8_FC Where() override;
+ sal_uInt8* GetSprmsAndPos( WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen );
+ virtual void advance() override;
+ virtual sal_uInt16 GetIstd() const override;
+ void GetPCDSprms( WW8PLCFxDesc& rDesc );
+ SprmResult HasSprm(sal_uInt16 nId, bool bFindFirst = true);
+ void HasSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult);
+ bool HasFkp() const { return (nullptr != m_pFkp); }
+};
+
+/// iterator for Piece Table Exceptions of Fkps works on CPs (high-level)
+class WW8PLCFx_Cp_FKP : public WW8PLCFx_Fc_FKP
+{
+private:
+ const WW8ScannerBase& m_rSBase;
+ std::unique_ptr<WW8PLCFx_PCD> m_pPcd;
+ WW8PLCFpcd_Iter *m_pPieceIter;
+ WW8_CP m_nAttrStart, m_nAttrEnd;
+ bool m_bLineEnd : 1;
+ bool m_bComplex : 1;
+
+ WW8PLCFx_Cp_FKP(const WW8PLCFx_Cp_FKP&) = delete;
+ WW8PLCFx_Cp_FKP& operator=(const WW8PLCFx_Cp_FKP&) = delete;
+
+public:
+ WW8PLCFx_Cp_FKP( SvStream* pSt, SvStream* pTableSt, SvStream* pDataSt,
+ const WW8ScannerBase& rBase, ePLCFT ePl );
+ virtual ~WW8PLCFx_Cp_FKP() override;
+ void ResetAttrStartEnd();
+ sal_uInt32 GetPCDIdx() const;
+ virtual sal_uInt32 GetIdx2() const override;
+ virtual void SetIdx2(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual void GetSprms( WW8PLCFxDesc* p ) override;
+ virtual void advance() override;
+ virtual void Save( WW8PLCFxSave1& rSave ) const override;
+ virtual void Restore( const WW8PLCFxSave1& rSave ) override;
+};
+
+/// Iterator for Piece Table Exceptions of Sepx
+class WW8PLCFx_SEPX : public WW8PLCFx
+{
+private:
+ wwSprmParser maSprmParser;
+ SvStream* m_pStrm;
+ std::unique_ptr<WW8PLCF> m_pPLCF;
+ std::unique_ptr<sal_uInt8[]> m_pSprms;
+ sal_uInt16 m_nArrMax;
+ sal_uInt16 m_nSprmSiz;
+
+ WW8PLCFx_SEPX(const WW8PLCFx_SEPX&) = delete;
+ WW8PLCFx_SEPX& operator=(const WW8PLCFx_SEPX&) = delete;
+
+public:
+ WW8PLCFx_SEPX( SvStream* pSt, SvStream* pTablexySt, const WW8Fib& rFib,
+ WW8_CP nStartCp );
+ virtual ~WW8PLCFx_SEPX() override;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual void GetSprms( WW8PLCFxDesc* p ) override;
+ virtual void advance() override;
+ SprmResult HasSprm( sal_uInt16 nId ) const;
+ SprmResult HasSprm( sal_uInt16 nId, sal_uInt8 n2nd ) const;
+ SprmResult HasSprm( sal_uInt16 nId, const sal_uInt8* pOtherSprms,
+ tools::Long nOtherSprmSiz ) const;
+ bool Find4Sprms(sal_uInt16 nId1, sal_uInt16 nId2, sal_uInt16 nId3, sal_uInt16 nId4,
+ SprmResult& r1, SprmResult& r2, SprmResult& r3, SprmResult& r4) const;
+};
+
+/// iterator for footnotes/endnotes and comments
+class WW8PLCFx_SubDoc : public WW8PLCFx
+{
+private:
+ std::unique_ptr<WW8PLCF> m_pRef;
+ std::unique_ptr<WW8PLCF> m_pText;
+
+ WW8PLCFx_SubDoc(const WW8PLCFx_SubDoc&) = delete;
+ WW8PLCFx_SubDoc& operator=(const WW8PLCFx_SubDoc&) = delete;
+
+public:
+ WW8PLCFx_SubDoc(SvStream* pSt, const WW8Fib& rFib, WW8_CP nStartCp,
+ tools::Long nFcRef, tools::Long nLenRef, tools::Long nFcText, tools::Long nLenText, tools::Long nStruc);
+ virtual ~WW8PLCFx_SubDoc() override;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+
+ // returns reference descriptors
+ const void* GetData() const
+ {
+ return m_pRef ? m_pRef->GetData( m_pRef->GetIdx() ) : nullptr;
+ }
+
+ virtual void GetSprms(WW8PLCFxDesc* p) override;
+ virtual void advance() override;
+ tools::Long Count() const { return m_pRef ? m_pRef->GetIMax() : 0; }
+};
+
+/// Iterator for fields
+class WW8PLCFx_FLD : public WW8PLCFx
+{
+private:
+ std::unique_ptr<WW8PLCFspecial> m_pPLCF;
+ const WW8Fib& m_rFib;
+ WW8PLCFx_FLD(const WW8PLCFx_FLD&) = delete;
+ WW8PLCFx_FLD& operator=(const WW8PLCFx_FLD &) = delete;
+
+public:
+ WW8PLCFx_FLD(SvStream* pSt, const WW8Fib& rMyFib, short nType);
+ virtual ~WW8PLCFx_FLD() override;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual void GetSprms(WW8PLCFxDesc* p) override;
+ virtual void advance() override;
+ bool StartPosIsFieldStart();
+ bool EndPosIsFieldEnd(WW8_CP&);
+ bool GetPara(tools::Long nIdx, WW8FieldDesc& rF);
+};
+
+enum eBookStatus { BOOK_NORMAL = 0, BOOK_IGNORE = 0x1, BOOK_FIELD = 0x2 };
+
+/// Iterator for Booknotes
+class WW8PLCFx_Book : public WW8PLCFx
+{
+private:
+ std::unique_ptr<WW8PLCFspecial> m_pBook[2]; // Start and End Position
+ std::vector<OUString> m_aBookNames; // Name
+ std::vector<eBookStatus> m_aStatus;
+ tools::Long m_nIMax; // Number of Booknotes
+ sal_uInt16 m_nIsEnd;
+ sal_Int32 m_nBookmarkId; // counter incremented by GetUniqueBookmarkName.
+
+ WW8PLCFx_Book(const WW8PLCFx_Book&) = delete;
+ WW8PLCFx_Book& operator=(const WW8PLCFx_Book&) = delete;
+
+public:
+ WW8PLCFx_Book(SvStream* pTableSt,const WW8Fib& rFib);
+ virtual ~WW8PLCFx_Book() override;
+ tools::Long GetIMax() const { return m_nIMax; }
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nI) override;
+ virtual sal_uInt32 GetIdx2() const override;
+ virtual void SetIdx2(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual tools::Long GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) override;
+ virtual void advance() override;
+ const OUString* GetName() const;
+ WW8_CP GetStartPos() const
+ { return m_nIsEnd ? WW8_CP_MAX : m_pBook[0]->Where(); }
+ tools::Long GetLen() const;
+ bool GetIsEnd() const { return m_nIsEnd != 0; }
+ tools::Long GetHandle() const;
+ void SetStatus( sal_uInt16 nIndex, eBookStatus eStat );
+ void MapName(OUString& rName);
+ OUString GetBookmark(tools::Long nStart,tools::Long nEnd, sal_uInt16 &nIndex);
+ eBookStatus GetStatus() const;
+ OUString GetUniqueBookmarkName(const OUString &rSuggestedName);
+};
+
+/// Handles the import of PlcfAtnBkf and PlcfAtnBkl: start / end position of annotation marks.
+class WW8PLCFx_AtnBook : public WW8PLCFx
+{
+private:
+ /// Start and end positions.
+ std::unique_ptr<WW8PLCFspecial> m_pBook[2];
+ /// Number of annotation marks
+ sal_Int32 m_nIMax;
+ bool m_bIsEnd;
+
+ WW8PLCFx_AtnBook(const WW8PLCFx_AtnBook&) = delete;
+ WW8PLCFx_AtnBook& operator=(const WW8PLCFx_AtnBook&) = delete;
+
+public:
+ WW8PLCFx_AtnBook(SvStream* pTableSt,const WW8Fib& rFib);
+ virtual ~WW8PLCFx_AtnBook() override;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nI) override;
+ virtual sal_uInt32 GetIdx2() const override;
+ virtual void SetIdx2(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual tools::Long GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) override;
+ virtual void advance() override;
+
+ /// Handle is the unique ID of an annotation mark.
+ tools::Long getHandle() const;
+ bool getIsEnd() const;
+};
+
+/// Handles the import of PlcfBkfFactoid and PlcfBklFactoid: start / end position of factoids.
+class WW8PLCFx_FactoidBook : public WW8PLCFx
+{
+private:
+ /// Start and end positions.
+ std::unique_ptr<WW8PLCFspecial> m_pBook[2];
+ /// Number of factoid marks
+ sal_Int32 m_nIMax;
+ bool m_bIsEnd;
+
+ WW8PLCFx_FactoidBook(const WW8PLCFx_FactoidBook&) = delete;
+ WW8PLCFx_FactoidBook& operator=(const WW8PLCFx_FactoidBook&) = delete;
+
+public:
+ WW8PLCFx_FactoidBook(SvStream* pTableSt,const WW8Fib& rFib);
+ virtual ~WW8PLCFx_FactoidBook() override;
+ virtual sal_uInt32 GetIdx() const override;
+ virtual void SetIdx(sal_uInt32 nI) override;
+ virtual sal_uInt32 GetIdx2() const override;
+ virtual void SetIdx2(sal_uInt32 nIdx) override;
+ virtual bool SeekPos(WW8_CP nCpPos) override;
+ virtual WW8_CP Where() override;
+ virtual tools::Long GetNoSprms(WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen) override;
+ virtual void advance() override;
+
+ /// Handle is the unique ID of a factoid mark.
+ tools::Long getHandle() const;
+ bool getIsEnd() const;
+};
+
+/*
+ this is what we use outside:
+*/
+struct WW8PLCFManResult
+{
+ WW8_CP nCpPos; // attribute starting position
+ tools::Long nMemLen; // length for previous
+ tools::Long nCp2OrIdx; // footnote-textpos or index in PLCF
+ WW8_CP nCurrentCp; // only used by caller
+ const sal_uInt8* pMemPos;// Mem-Pos for Sprms
+ sal_uInt16 nSprmId; // Sprm-Id ( 0 = invalid Id -> skip! )
+ // (2..255) or pseudo-Sprm-Id (256..260)
+ // from Winword-Ver8 Sprm-Id (800..) resp.
+ sal_uInt8 nFlags; // start of paragraph or section
+};
+
+enum ManMaskTypes
+{
+ MAN_MASK_NEW_PAP = 1, // new line
+ MAN_MASK_NEW_SEP = 2 // new section
+};
+
+enum ManTypes // enums for PLCFMan-ctor
+{
+ MAN_MAINTEXT = 0, MAN_FTN = 1, MAN_EDN = 2, MAN_HDFT = 3, MAN_AND = 4,
+ MAN_TXBX = 5, MAN_TXBX_HDFT = 6
+};
+
+/*
+ this is what the manager uses inside:
+*/
+struct WW8PLCFxDesc
+{
+ WW8PLCFx* pPLCFx;
+ std::optional<std::stack<sal_uInt16>> xIdStack; // memory for Attr-Id for Attr-end(s)
+ const sal_uInt8* pMemPos;// where are the Sprm(s)
+ tools::Long nOrigSprmsLen;
+
+ WW8_CP nStartPos;
+ WW8_CP nEndPos;
+
+ WW8_CP nOrigStartPos;
+ WW8_CP nOrigEndPos; // The ending character position of a paragraph is
+ // always one before the end reported in the FKP,
+ // also a character run that ends on the same location
+ // as the paragraph mark is adjusted to end just before
+ // the paragraph mark so as to handle their close
+ // first. The value being used to determining where the
+ // properties end is in nEndPos, but the original
+ // unadjusted end character position is important as
+ // it can be used as the beginning cp of the next set
+ // of properties
+
+ WW8_CP nCp2OrIdx; // where are the NoSprm(s)
+ sal_Int32 nSprmsLen; // how many bytes for further Sprms / length of footnote
+ tools::Long nCpOfs; // for Offset Header .. Footnote
+ bool bFirstSprm; // for recognizing the first Sprm of a group
+ bool bRealLineEnd; // false for Pap-Piece-end
+ sal_Int16 nRelativeJustify;
+ void Save( WW8PLCFxSave1& rSave ) const;
+ void Restore( const WW8PLCFxSave1& rSave );
+ //With nStartPos set to WW8_CP_MAX then in the case of a pap or chp
+ //GetSprms will not search for the sprms, but instead take the
+ //existing ones.
+ WW8PLCFxDesc()
+ : pPLCFx(nullptr)
+ , pMemPos(nullptr)
+ , nOrigSprmsLen(0)
+ , nStartPos(WW8_CP_MAX)
+ , nEndPos(WW8_CP_MAX)
+ , nOrigStartPos(WW8_CP_MAX)
+ , nOrigEndPos(WW8_CP_MAX)
+ , nCp2OrIdx(WW8_CP_MAX)
+ , nSprmsLen(0)
+ , nCpOfs(0)
+ , bFirstSprm(false)
+ , bRealLineEnd(false)
+ , nRelativeJustify(-1)
+ {
+ }
+ void ReduceByOffset();
+};
+
+struct WW8PLCFxSaveAll;
+class WW8PLCFMan
+{
+public:
+ enum WW8PLCFManLimits {MAN_PLCF_COUNT = 12};
+
+private:
+ wwSprmParser maSprmParser;
+ WW8_CP m_nCpO; //< Origin Cp -- the basis for nNewCp
+
+ WW8_CP m_nLineEnd; // points *after* the <CR>
+ sal_uInt16 m_nPLCF; // this many PLCFs are managed
+ ManTypes m_nManType;
+ bool mbDoingDrawTextBox; //Normally we adjust the end of attributes
+ //so that the end of a paragraph occurs
+ //before the para end mark, but for
+ //drawboxes we want the true offsets
+
+ WW8PLCFxDesc m_aD[MAN_PLCF_COUNT];
+ WW8PLCFxDesc *m_pChp, *m_pPap, *m_pSep, *m_pField, *m_pFootnote, *m_pEdn, *m_pBkm, *m_pPcd,
+ *m_pPcdA, *m_pAnd, *m_pAtnBkm, *m_pFactoidBkm;
+ WW8PLCFspecial *m_pFdoa, *m_pTxbx, *m_pTxbxBkd,*m_pMagicTables, *m_pSubdocs;
+ sal_uInt8* m_pExtendedAtrds;
+
+ const WW8Fib* m_pWwFib;
+
+ sal_uInt16 WhereIdx(bool* pbStart, WW8_CP * pPos=nullptr) const;
+ void AdjustEnds(WW8PLCFxDesc& rDesc);
+ void GetNewSprms(WW8PLCFxDesc& rDesc);
+ static void GetNewNoSprms(WW8PLCFxDesc& rDesc);
+ void GetSprmStart(short nIdx, WW8PLCFManResult* pRes) const;
+ void GetSprmEnd(short nIdx, WW8PLCFManResult* pRes) const;
+ void GetNoSprmStart(short nIdx, WW8PLCFManResult* pRes) const;
+ void GetNoSprmEnd(short nIdx, WW8PLCFManResult* pRes) const;
+ void AdvSprm(short nIdx, bool bStart);
+ void AdvNoSprm(short nIdx, bool bStart);
+ sal_uInt16 GetId(const WW8PLCFxDesc* p ) const;
+
+ bool IsSprmLegalForCategory(sal_uInt16 nSprmId, short nIdx) const;
+
+public:
+ WW8PLCFMan(const WW8ScannerBase* pBase, ManTypes nType, tools::Long nStartCp,
+ bool bDoingDrawTextBox = false);
+ ~WW8PLCFMan();
+
+ /*
+ Where asks on which following position any Attr changes...
+ */
+ WW8_CP Where() const;
+
+ bool Get(WW8PLCFManResult* pResult) const;
+ void advance();
+ sal_uInt16 GetColl() const; // index of actual Style
+ WW8PLCFx_FLD* GetField() const;
+ WW8PLCFx_SubDoc* GetEdn() const { return static_cast<WW8PLCFx_SubDoc*>(m_pEdn->pPLCFx); }
+ WW8PLCFx_SubDoc* GetFootnote() const { return static_cast<WW8PLCFx_SubDoc*>(m_pFootnote->pPLCFx); }
+ WW8PLCFx_SubDoc* GetAtn() const { return static_cast<WW8PLCFx_SubDoc*>(m_pAnd->pPLCFx); }
+ WW8PLCFx_Book* GetBook() const { return static_cast<WW8PLCFx_Book*>(m_pBkm->pPLCFx); }
+ WW8PLCFx_AtnBook* GetAtnBook() const { return static_cast<WW8PLCFx_AtnBook*>(m_pAtnBkm->pPLCFx); }
+ WW8PLCFx_FactoidBook* GetFactoidBook() const { return static_cast<WW8PLCFx_FactoidBook*>(m_pFactoidBkm->pPLCFx); }
+ tools::Long GetCpOfs() const { return m_pChp->nCpOfs; } // for Header/Footer...
+
+ /* asks, if *current paragraph* has an Sprm of this type */
+ SprmResult HasParaSprm(sal_uInt16 nId) const;
+
+ /* asks, if *current textrun* has an Sprm of this type */
+ SprmResult HasCharSprm(sal_uInt16 nId) const;
+ void HasCharSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult) const;
+
+ WW8PLCFx_Cp_FKP* GetChpPLCF() const
+ { return static_cast<WW8PLCFx_Cp_FKP*>(m_pChp->pPLCFx); }
+ WW8PLCFx_Cp_FKP* GetPapPLCF() const
+ { return static_cast<WW8PLCFx_Cp_FKP*>(m_pPap->pPLCFx); }
+ WW8PLCFx_SEPX* GetSepPLCF() const
+ { return static_cast<WW8PLCFx_SEPX*>(m_pSep->pPLCFx); }
+ WW8PLCFxDesc* GetPap() const { return m_pPap; }
+ void TransferOpenSprms(std::stack<sal_uInt16> &rStack);
+ void SeekPos( tools::Long nNewCp );
+ void SaveAllPLCFx( WW8PLCFxSaveAll& rSave ) const;
+ void RestoreAllPLCFx( const WW8PLCFxSaveAll& rSave );
+ WW8PLCFspecial* GetFdoa() const { return m_pFdoa; }
+ WW8PLCFspecial* GetTxbx() const { return m_pTxbx; }
+ WW8PLCFspecial* GetTxbxBkd() const { return m_pTxbxBkd; }
+ WW8PLCFspecial* GetMagicTables() const { return m_pMagicTables; }
+ WW8PLCFspecial* GetWkbPLCF() const { return m_pSubdocs; }
+ sal_uInt8* GetExtendedAtrds() const { return m_pExtendedAtrds; }
+ ManTypes GetManType() const { return m_nManType; }
+ bool GetDoingDrawTextBox() const { return mbDoingDrawTextBox; }
+};
+
+struct WW8PLCFxSaveAll
+{
+ WW8PLCFxSave1 aS[WW8PLCFMan::MAN_PLCF_COUNT] = {};
+ WW8PLCFxSaveAll() = default;
+};
+
+class WW8ScannerBase
+{
+friend WW8PLCFx_PCDAttrs::WW8PLCFx_PCDAttrs(const WW8Fib& rFib,
+ WW8PLCFx_PCD* pPLCFx_PCD, const WW8ScannerBase* pBase );
+friend WW8PLCFx_Cp_FKP::WW8PLCFx_Cp_FKP( SvStream*, SvStream*, SvStream*,
+ const WW8ScannerBase&, ePLCFT );
+
+friend WW8PLCFMan::WW8PLCFMan(const WW8ScannerBase*, ManTypes, tools::Long, bool);
+friend class SwWW8FltControlStack;
+
+private:
+ WW8Fib* m_pWw8Fib;
+ std::unique_ptr<WW8PLCFx_Cp_FKP> m_pChpPLCF; // Character-Attrs
+ std::unique_ptr<WW8PLCFx_Cp_FKP> m_pPapPLCF; // Paragraph-Attrs
+ std::unique_ptr<WW8PLCFx_SEPX> m_pSepPLCF; // Section-Attrs
+ std::unique_ptr<WW8PLCFx_SubDoc> m_pFootnotePLCF; // Footnotes
+ std::unique_ptr<WW8PLCFx_SubDoc> m_pEdnPLCF; // EndNotes
+ std::unique_ptr<WW8PLCFx_SubDoc> m_pAndPLCF; // Comments
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldPLCF; // Fields in Main Text
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldHdFtPLCF; // Fields in Header / Footer
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldTxbxPLCF; // Fields in Textboxes in Main Text
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldTxbxHdFtPLCF; // Fields in Textboxes in Header / Footer
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldFootnotePLCF; // Fields in Footnotes
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldEdnPLCF; // Fields in Endnotes
+ std::unique_ptr<WW8PLCFx_FLD> m_pFieldAndPLCF; // Fields in Comments
+ std::unique_ptr<WW8PLCFspecial> m_pMainFdoa; // Graphic Primitives in Main Text
+ std::unique_ptr<WW8PLCFspecial> m_pHdFtFdoa; // Graphic Primitives in Header / Footer
+ std::unique_ptr<WW8PLCFspecial> m_pMainTxbx; // Textboxes in Main Text
+ std::unique_ptr<WW8PLCFspecial> m_pMainTxbxBkd; // Break-Descriptors for them
+ std::unique_ptr<WW8PLCFspecial> m_pHdFtTxbx; // TextBoxes in Header / Footer
+ std::unique_ptr<WW8PLCFspecial> m_pHdFtTxbxBkd; // Break-Descriptors for previous
+ std::unique_ptr<WW8PLCFspecial> m_pMagicTables; // Break-Descriptors for them
+ std::unique_ptr<WW8PLCFspecial> m_pSubdocs; // subdoc references in master document
+ std::unique_ptr<sal_uInt8[]>
+ m_pExtendedAtrds; // Extended ATRDs
+ std::unique_ptr<WW8PLCFx_Book> m_pBook; // Bookmarks
+ std::unique_ptr<WW8PLCFx_AtnBook> m_pAtnBook; // Annotationmarks
+ /// Smart tag bookmarks.
+ std::unique_ptr<WW8PLCFx_FactoidBook> m_pFactoidBook;
+
+ std::unique_ptr<WW8PLCFpcd> m_pPiecePLCF; // for FastSave ( Basis-PLCF without iterator )
+ std::unique_ptr<WW8PLCFpcd_Iter> m_pPieceIter; // for FastSave ( iterator for previous )
+ std::unique_ptr<WW8PLCFx_PCD> m_pPLCFx_PCD; // ditto
+ std::unique_ptr<WW8PLCFx_PCDAttrs> m_pPLCFx_PCDAttrs;
+ std::vector<std::unique_ptr<sal_uInt8[]>> m_aPieceGrpprls; // attributes of Piece-Table
+
+ std::unique_ptr<WW8PLCFpcd> OpenPieceTable( SvStream* pStr, const WW8Fib* pWwF );
+
+ WW8ScannerBase(const WW8ScannerBase&) = delete;
+ WW8ScannerBase& operator=(const WW8ScannerBase&) = delete;
+
+public:
+ WW8ScannerBase( SvStream* pSt, SvStream* pTableSt, SvStream* pDataSt,
+ WW8Fib* pWwF );
+ ~WW8ScannerBase();
+ bool AreThereFootnotes() const { return m_pFootnotePLCF->Count() > 0; };
+ bool AreThereEndnotes() const { return m_pEdnPLCF->Count() > 0; };
+ tools::Long GetEndnoteCount() const { return m_pEdnPLCF->Count(); };
+
+ //If you use WW8Fc2Cp you are almost certainly doing the wrong thing
+ //when it comes to fastsaved files, avoid like the plague. For export
+ //given that we never write fastsaved files you can use it, otherwise
+ //I will beat you with a stick
+ WW8_CP WW8Fc2Cp(WW8_FC nFcPos) const ;
+ WW8_FC WW8Cp2Fc(WW8_CP nCpPos, bool* pIsUnicode = nullptr,
+ WW8_CP* pNextPieceCp = nullptr, bool* pTestFlag = nullptr) const;
+
+ sal_Int32 WW8ReadString(SvStream& rStrm, OUString& rStr, WW8_CP nCurrentStartCp,
+ tools::Long nTotalLen, rtl_TextEncoding eEnc ) const;
+
+};
+
+/** FIB - the File Information Block
+
+ The FIB contains a "magic word" and pointers to the various other parts of
+ the file, as well as information about the length of the file.
+ The FIB starts at the beginning of the file.
+*/
+class WW8Fib
+{
+private:
+ sal_Unicode m_nNumDecimalSep = u'\0';
+
+public:
+ /**
+ Program-Version asked for by us:
+ in Ctor we check if it matches the value of nFib
+
+ 6 == "WinWord 6 or WinWord 95",
+ 7 == "only WinWord 95"
+ 8 == "WinWord 97 or newer"
+ */
+ sal_uInt8 m_nVersion = 0;
+ /*
+ error status
+ */
+ ErrCode m_nFibError;
+ /*
+ data read from FIB by Ctor
+ (corresponds only approximately to the real structure
+ of the Winword-FIB)
+ */
+ sal_uInt16 m_wIdent = 0; // 0x0 int magic number
+ /*
+ File Information Block (FIB) values:
+ WinWord 1.0 = 33
+ WinWord 2.0 = 45
+ WinWord 6.0c for 16bit = 101
+ Word 6/32 bit = 104
+ Word 95 = 104
+ Word 97 = 193
+ Word 2000 = 217
+ Word 2002 = 257
+ Word 2003 = 268
+ Word 2007 = 274
+ */
+ sal_uInt16 m_nFib = 0; // 0x2 FIB version written
+ sal_uInt16 m_nProduct = 0; // 0x4 product version written by
+ LanguageType m_lid; // 0x6 language stamp---localized version;
+ WW8_PN m_pnNext = 0; // 0x8
+
+ bool m_fDot :1 /*= false*/; // 0xa 0001
+ bool m_fGlsy :1 /*= false*/;
+ bool m_fComplex :1 /*= false*/; // 0004 when 1, file is in complex, fast-saved format.
+ bool m_fHasPic :1 /*= false*/; // 0008 file contains 1 or more pictures
+ sal_uInt16 m_cQuickSaves :4 /*= 0*/; // 00F0 count of times file was quicksaved
+ bool m_fEncrypted :1 /*= false*/; //0100 1 if file is encrypted, 0 if not
+ bool m_fWhichTableStm :1 /*= false*/; //0200 When 0, this fib refers to the table stream
+ bool m_fReadOnlyRecommended :1 /*= false*/;
+ bool m_fWriteReservation :1 /*= false*/;
+ // named "0Table", when 1, this fib refers to the
+ // table stream named "1Table". Normally, a file
+ // will have only one table stream, but under unusual
+ // circumstances a file may have table streams with
+ // both names. In that case, this flag must be used
+ // to decide which table stream is valid.
+
+ bool m_fExtChar :1 /*= false*/; // 1000 =1, when using extended character set in file
+ bool m_fFarEast :1 /*= false*/; // 4000 =1, probably, when far-East language variants of Word is used to create a file #i90932#
+
+ bool m_fObfuscated :1 /*= false*/; // 8000=1. specifies whether the document is obfuscated using XOR obfuscation. otherwise this bit MUST be ignored.
+
+ sal_uInt16 m_nFibBack = 0; // 0xc
+ sal_uInt16 m_nHash = 0; // 0xe file encrypted hash
+ sal_uInt16 m_nKey = 0; // 0x10 file encrypted key
+ sal_uInt8 m_envr = 0; // 0x12 environment in which file was created
+ // 0 created by Win Word / 1 created by Mac Word
+ bool m_fMac :1 /*= false*/; // 0x13 when 1, this file was last saved in the Mac environment
+ bool m_fEmptySpecial :1 /*= false*/;
+ bool m_fLoadOverridePage :1 /*= false*/;
+ bool m_fFuturesavedUndo :1 /*= false*/;
+ bool m_fWord97Saved :1 /*= false*/;
+ bool m_fWord2000Saved :1 /*= false*/;
+ sal_uInt8 :2;
+
+ sal_uInt16 m_chse = 0; // 0x14 default extended character set id for text in document stream. (overridden by chp.chse)
+ // 0 = ANSI / 256 Macintosh character set.
+ sal_uInt16 m_chseTables = 0; // 0x16 default extended character set id for text in
+ // internal data structures: 0 = ANSI, 256 = Macintosh
+ WW8_FC m_fcMin = 0; // 0x18 file offset of first character of text
+ WW8_FC m_fcMac = 0; // 0x1c file offset of last character of text + 1
+
+ // start of WW8 section
+ sal_uInt16 m_csw = 0; // Count of fields in the array of "shorts"
+
+ // marker: "rgsw" Beginning of the array of shorts
+ sal_uInt16 m_wMagicCreated = 0; // unique number Identifying the File's creator
+ // 0x6A62 is the creator ID for Word and is reserved.
+ // Other creators should choose a different value.
+ sal_uInt16 m_wMagicRevised = 0; // identifies the File's last modifier
+ sal_uInt16 m_wMagicCreatedPrivate = 0; // private data
+ sal_uInt16 m_wMagicRevisedPrivate = 0; // private data
+
+ LanguageType m_lidFE; // Language id if document was written by Far East version
+ // of Word (i.e. FIB.fFarEast is on)
+ sal_uInt16 m_clw = 0; // Number of fields in the array of longs
+
+ // end of WW8 section
+
+ // Marker: "rglw" Beginning of the array of longs
+ WW8_FC m_cbMac = 0; // 0x20 file offset of last byte written to file + 1.
+
+ // WW8_FC u4[4]; // 0x24
+ WW8_CP m_ccpText = 0; // 0x34 length of main document text stream
+ WW8_CP m_ccpFootnote = 0; // 0x38 length of footnote subdocument text stream
+ WW8_CP m_ccpHdr = 0; // 0x3c length of header subdocument text stream
+ WW8_CP m_ccpMcr = 0; // 0x40 length of macro subdocument text stream
+ WW8_CP m_ccpAtn = 0; // 0x44 length of annotation subdocument text stream
+ WW8_CP m_ccpEdn = 0; // 0x48 length of endnote subdocument text stream
+ WW8_CP m_ccpTxbx = 0; // 0x4c length of textbox subdocument text stream
+ WW8_CP m_ccpHdrTxbx = 0; // 0x50 length of header textbox subdocument text stream
+
+ // start of WW8 section
+ sal_Int32 m_pnFbpChpFirst = 0; // when there was insufficient memory for Word to expand
+ // the PLCFbte at save time, the PLCFbte is written
+ // to the file in a linked list of 512-byte pieces
+ // starting with this pn.
+ sal_Int32 m_pnFbpPapFirst = 0; // when there was insufficient memory for Word to expand
+ // the PLCFbte at save time, the PLCFbte is written to
+ // the file in a linked list of 512-byte pieces
+ // starting with this pn
+
+ sal_Int32 m_pnFbpLvcFirst = 0; // when there was insufficient memory for Word to expand
+ // the PLCFbte at save time, the PLCFbte is written to
+ // the file in a linked list of 512-byte pieces
+ // starting with this pn
+ sal_Int32 m_pnLvcFirst = 0; // the page number of the lowest numbered page in the
+ // document that records LVC FKP information
+ sal_Int32 m_cpnBteLvc = 0; // count of LVC FKPs recorded in file. In non-complex
+ // files if the number of entries in the PLCFbtePapx is
+ // less than this, the PLCFbtePapx is incomplete.
+ sal_Int32 m_fcIslandFirst = 0; // ?
+ sal_Int32 m_fcIslandLim = 0; // ?
+ sal_uInt16 m_cfclcb = 0; // Number of fields in the array of FC/LCB pairs.
+ /// Specifies the count of 16-bit values corresponding to fibRgCswNew that follow.
+ sal_uInt16 m_cswNew = 0;
+
+ // end of WW8 section
+
+ // Marker: "rgfclcb" Beginning of array of FC/LCB pairs.
+ WW8_FC m_fcStshfOrig = 0; // file offset of original allocation for STSH in table
+ // stream. During fast save Word will attempt to reuse
+ // this allocation if STSH is small enough to fit.
+ sal_Int32 m_lcbStshfOrig = 0; // 0x5c count of bytes of original STSH allocation
+ WW8_FC m_fcStshf = 0; // 0x60 file offset of STSH in file.
+ sal_Int32 m_lcbStshf = 0; // 0x64 count of bytes of current STSH allocation
+ WW8_FC m_fcPlcffndRef = 0; // 0x68 file offset of footnote reference PLCF.
+ sal_Int32 m_lcbPlcffndRef = 0; // 0x6c count of bytes of footnote reference PLCF
+ // == 0 if no footnotes defined in document.
+
+ WW8_FC m_fcPlcffndText = 0; // 0x70 file offset of footnote text PLCF.
+ sal_Int32 m_lcbPlcffndText = 0; // 0x74 count of bytes of footnote text PLCF.
+ // == 0 if no footnotes defined in document
+
+ WW8_FC m_fcPlcfandRef = 0; // 0x78 file offset of annotation reference PLCF.
+ sal_Int32 m_lcbPlcfandRef = 0; // 0x7c count of bytes of annotation reference PLCF.
+
+ WW8_FC m_fcPlcfandText = 0; // 0x80 file offset of annotation text PLCF.
+ sal_Int32 m_lcbPlcfandText = 0; // 0x84 count of bytes of the annotation text PLCF
+
+ WW8_FC m_fcPlcfsed = 0; // 8x88 file offset of section descriptor PLCF.
+ sal_Int32 m_lcbPlcfsed = 0; // 0x8c count of bytes of section descriptor PLCF.
+
+ WW8_FC m_fcPlcfpad = 0; // 0x90 file offset of paragraph descriptor PLCF
+ sal_Int32 m_lcbPlcfpad = 0; // 0x94 count of bytes of paragraph descriptor PLCF.
+ // ==0 if file was never viewed in Outline view.
+ // Should not be written by third party creators
+
+ WW8_FC m_fcPlcfphe = 0; // 0x98 file offset of PLCF of paragraph heights.
+ sal_Int32 m_lcbPlcfphe = 0; // 0x9c count of bytes of paragraph height PLCF.
+ // ==0 when file is non-complex.
+
+ WW8_FC m_fcSttbfglsy = 0; // 0xa0 file offset of glossary string table.
+ sal_Int32 m_lcbSttbfglsy = 0; // 0xa4 count of bytes of glossary string table.
+ // == 0 for non-glossary documents.
+ // !=0 for glossary documents.
+
+ WW8_FC m_fcPlcfglsy = 0; // 0xa8 file offset of glossary PLCF.
+ sal_Int32 m_lcbPlcfglsy = 0; // 0xac count of bytes of glossary PLCF.
+ // == 0 for non-glossary documents.
+ // !=0 for glossary documents.
+
+ WW8_FC m_fcPlcfhdd = 0; // 0xb0 byte offset of header PLCF.
+ sal_Int32 m_lcbPlcfhdd = 0; // 0xb4 count of bytes of header PLCF.
+ // == 0 if document contains no headers
+
+ WW8_FC m_fcPlcfbteChpx = 0; // 0xb8 file offset of character property bin table.PLCF.
+ sal_Int32 m_lcbPlcfbteChpx = 0;// 0xbc count of bytes of character property bin table PLCF.
+
+ WW8_FC m_fcPlcfbtePapx = 0; // 0xc0 file offset of paragraph property bin table.PLCF.
+ sal_Int32 m_lcbPlcfbtePapx = 0;// 0xc4 count of bytes of paragraph property bin table PLCF.
+
+ WW8_FC m_fcPlcfsea = 0; // 0xc8 file offset of PLCF reserved for private use. The SEA is 6 bytes long.
+ sal_Int32 m_lcbPlcfsea = 0; // 0xcc count of bytes of private use PLCF.
+
+ WW8_FC m_fcSttbfffn = 0; // 0xd0 file offset of font information STTBF. See the FFN file structure definition.
+ sal_Int32 m_lcbSttbfffn = 0; // 0xd4 count of bytes in sttbfffn.
+
+ WW8_FC m_fcPlcffldMom = 0; // 0xd8 offset in doc stream to the PLCF of field positions in the main document.
+ sal_Int32 m_lcbPlcffldMom = 0; // 0xdc
+
+ WW8_FC m_fcPlcffldHdr = 0; // 0xe0 offset in doc stream to the PLCF of field positions in the header subdocument.
+ sal_Int32 m_lcbPlcffldHdr = 0; // 0xe4
+
+ WW8_FC m_fcPlcffldFootnote = 0; // 0xe8 offset in doc stream to the PLCF of field positions in the footnote subdocument.
+ sal_Int32 m_lcbPlcffldFootnote = 0; // 0xec
+
+ WW8_FC m_fcPlcffldAtn = 0; // 0xf0 offset in doc stream to the PLCF of field positions in the annotation subdocument.
+ sal_Int32 m_lcbPlcffldAtn = 0; // 0xf4
+
+ WW8_FC m_fcPlcffldMcr = 0; // 0xf8 offset in doc stream to the PLCF of field positions in the macro subdocument.
+ sal_Int32 m_lcbPlcffldMcr = 0; // 9xfc
+
+ WW8_FC m_fcSttbfbkmk = 0; // 0x100 offset in document stream of the STTBF that records bookmark names in the main document
+ sal_Int32 m_lcbSttbfbkmk = 0; // 0x104
+
+ WW8_FC m_fcPlcfbkf = 0; // 0x108 offset in document stream of the PLCF that records the beginning CP offsets of bookmarks in the main document. See BKF
+ sal_Int32 m_lcbPlcfbkf = 0; // 0x10c
+
+ WW8_FC m_fcPlcfbkl = 0; // 0x110 offset in document stream of the PLCF that records the ending CP offsets of bookmarks recorded in the main document. See the BKL structure definition.
+ sal_Int32 m_lcbPlcfbkl = 0; // 0x114 sal_Int32
+
+ WW8_FC m_fcCmds = 0; // 0x118 FC
+ sal_uInt32 m_lcbCmds = 0; // 0x11c
+
+ WW8_FC m_fcPlcfmcr = 0; // 0x120 FC
+ sal_Int32 m_lcbPlcfmcr = 0; // 0x124
+
+ WW8_FC m_fcSttbfmcr = 0; // 0x128 FC
+ sal_Int32 m_lcbSttbfmcr = 0; // 0x12c
+
+ WW8_FC m_fcPrDrvr = 0; // 0x130 file offset of the printer driver information (names of drivers, port etc...)
+ sal_Int32 m_lcbPrDrvr = 0; // 0x134 count of bytes of the printer driver information (names of drivers, port etc...)
+
+ WW8_FC m_fcPrEnvPort = 0; // 0x138 file offset of the print environment in portrait mode.
+ sal_Int32 m_lcbPrEnvPort = 0; // 0x13c count of bytes of the print environment in portrait mode.
+
+ WW8_FC m_fcPrEnvLand = 0; // 0x140 file offset of the print environment in landscape mode.
+ sal_Int32 m_lcbPrEnvLand = 0; // 0x144 count of bytes of the print environment in landscape mode.
+
+ WW8_FC m_fcWss = 0; // 0x148 file offset of Window Save State data structure. See WSS.
+ sal_Int32 m_lcbWss = 0; // 0x14c count of bytes of WSS. ==0 if unable to store the window state.
+
+ WW8_FC m_fcDop = 0; // 0x150 file offset of document property data structure.
+ sal_uInt32 m_lcbDop = 0; // 0x154 count of bytes of document properties.
+ // cbDOP is 84 when nFib < 103
+
+ WW8_FC m_fcSttbfAssoc = 0; // 0x158 offset to STTBF of associated strings. See STTBFASSOC.
+ sal_Int32 m_lcbSttbfAssoc = 0; // 0x15C
+
+ WW8_FC m_fcClx = 0; // 0x160 file offset of beginning of information for complex files.
+ sal_Int32 m_lcbClx = 0; // 0x164 count of bytes of complex file information. 0 if file is non-complex.
+
+ WW8_FC m_fcPlcfpgdFootnote = 0; // 0x168 file offset of page descriptor PLCF for footnote subdocument.
+ sal_Int32 m_lcbPlcfpgdFootnote = 0; // 0x16C count of bytes of page descriptor PLCF for footnote subdocument.
+ // ==0 if document has not been paginated. The length of the PGD is 8 bytes.
+
+ WW8_FC m_fcAutosaveSource = 0; // 0x170 file offset of the name of the original file.
+ sal_Int32 m_lcbAutosaveSource = 0; // 0x174 count of bytes of the name of the original file.
+
+ WW8_FC m_fcGrpStAtnOwners = 0; // 0x178 group of strings recording the names of the owners of annotations
+ sal_Int32 m_lcbGrpStAtnOwners = 0; // 0x17C count of bytes of the group of strings
+
+ WW8_FC m_fcSttbfAtnbkmk = 0; // 0x180 file offset of the sttbf that records names of bookmarks in the annotation subdocument
+ sal_Int32 m_lcbSttbfAtnbkmk = 0; // 0x184 length in bytes of the sttbf that records names of bookmarks in the annotation subdocument
+
+ // end of WW67 section
+
+ WW8_FC m_fcPlcfdoaMom = 0; // 0x192 file offset of the FDOA (drawn object) PLCF for main document.
+ // ==0 if document has no drawn objects. The length of the FDOA is 6 bytes.
+ // unused starting from Ver8
+ sal_Int32 m_lcbPlcfdoaMom = 0; // 0x196 length in bytes of the FDOA PLCF of the main document
+ // unused starting from Ver8
+ WW8_FC m_fcPlcfdoaHdr = 0; // 0x19A file offset of the FDOA (drawn object) PLCF for the header document.
+ // ==0 if document has no drawn objects. The length of the FDOA is 6 bytes.
+ // unused starting from Ver8
+ sal_Int32 m_lcbPlcfdoaHdr = 0; // 0x19E length in bytes of the FDOA PLCF of the header document
+ // unused starting from Ver8
+
+ WW8_FC m_fcPlcfspaMom = 0; // offset in table stream of the FSPA PLCF for main document.
+ // == 0 if document has no office art objects
+ // was empty reserve in Ver67
+ sal_Int32 m_lcbPlcfspaMom = 0; // length in bytes of the FSPA PLCF of the main document
+ // was empty reserve in Ver67
+ WW8_FC m_fcPlcfspaHdr = 0; // offset in table stream of the FSPA PLCF for header document.
+ // == 0 if document has no office art objects
+ // was empty reserve in Ver67
+ sal_Int32 m_lcbPlcfspaHdr = 0; // length in bytes of the FSPA PLCF of the header document
+ // was empty reserve in Ver67
+
+ WW8_FC m_fcPlcfAtnbkf = 0; // 0x1B2 file offset of BKF (bookmark first) PLCF of the annotation subdocument
+ sal_Int32 m_lcbPlcfAtnbkf = 0; // 0x1B6 length in bytes of BKF (bookmark first) PLCF of the annotation subdocument
+
+ WW8_FC m_fcPlcfAtnbkl = 0; // 0x1BA file offset of BKL (bookmark last) PLCF of the annotation subdocument
+ sal_Int32 m_lcbPlcfAtnbkl = 0; // 0x1BE length in bytes of BKL (bookmark first) PLCF of the annotation subdocument
+
+ WW8_FC m_fcPms = 0; // 0x1C2 file offset of PMS (Print Merge State) information block
+ sal_Int32 m_lcbPMS = 0; // 0x1C6 length in bytes of PMS
+
+ WW8_FC m_fcFormFieldSttbf = 0; // 0x1CA file offset of form field Sttbf which contains strings used in form field dropdown controls
+ sal_Int32 m_lcbFormFieldSttbf = 0; // 0x1CE length in bytes of form field Sttbf
+
+ WW8_FC m_fcPlcfendRef = 0; // 0x1D2 file offset of PLCFendRef which points to endnote references in the main document stream
+ sal_Int32 m_lcbPlcfendRef = 0; // 0x1D6
+
+ WW8_FC m_fcPlcfendText = 0; // 0x1DA file offset of PLCFendRef which points to endnote text in the endnote document
+ // stream which corresponds with the PLCFendRef
+ sal_Int32 m_lcbPlcfendText = 0; // 0x1DE
+
+ WW8_FC m_fcPlcffldEdn = 0; // 0x1E2 offset to PLCF of field positions in the endnote subdoc
+ sal_Int32 m_lcbPlcffldEdn = 0; // 0x1E6
+
+ WW8_FC m_fcPlcfpgdEdn = 0; // 0x1EA offset to PLCF of page boundaries in the endnote subdoc.
+ sal_Int32 m_lcbPlcfpgdEdn = 0; // 0x1EE
+
+ WW8_FC m_fcDggInfo = 0; // offset in table stream of the office art object table data.
+ // The format of office art object table data is found in a separate document.
+ // was empty reserve in Ver67
+ sal_Int32 m_lcbDggInfo = 0; // length in bytes of the office art object table data
+ // was empty reserve in Ver67
+
+ WW8_FC m_fcSttbfRMark = 0; // 0x1fa offset to STTBF that records the author abbreviations...
+ sal_Int32 m_lcbSttbfRMark = 0; // 0x1fe
+ WW8_FC m_fcSttbfCaption = 0; // 0x202 offset to STTBF that records caption titles...
+ sal_Int32 m_lcbSttbfCaption = 0; // 0x206
+ WW8_FC m_fcSttbAutoCaption = 0; // offset in table stream to the STTBF that records the object names and
+ // indices into the caption STTBF for objects which get auto captions.
+ sal_Int32 m_lcbSttbAutoCaption = 0; // 0x20e
+
+ WW8_FC m_fcPlcfwkb = 0; // 0x212 offset to PLCF that describes the boundaries of contributing documents...
+ sal_Int32 m_lcbPlcfwkb = 0; // 0x216
+
+ WW8_FC m_fcPlcfspl = 0; // offset in table stream of PLCF (of SPLS structures) that records spell check state
+ // was empty reserve in Ver67
+ sal_Int32 m_lcbPlcfspl = 0; // was empty reserve in Ver67
+
+ WW8_FC m_fcPlcftxbxText = 0; // 0x222 ...PLCF of beginning CP in the text box subdoc
+ sal_Int32 m_lcbPlcftxbxText = 0; // 0x226
+ WW8_FC m_fcPlcffldTxbx = 0; // 0x22a ...PLCF of field boundaries recorded in the textbox subdoc.
+ sal_Int32 m_lcbPlcffldTxbx = 0; // 0x22e
+ WW8_FC m_fcPlcfHdrtxbxText = 0;// 0x232 ...PLCF of beginning CP in the header text box subdoc
+ sal_Int32 m_lcbPlcfHdrtxbxText = 0;// 0x236
+ WW8_FC m_fcPlcffldHdrTxbx = 0;// 0x23a ...PLCF of field boundaries recorded in the header textbox subdoc.
+ sal_Int32 m_lcbPlcffldHdrTxbx = 0;// 0x23e
+ WW8_FC m_fcStwUser = 0;
+ sal_uInt32 m_lcbStwUser = 0;
+ WW8_FC m_fcSttbttmbd = 0;
+ sal_uInt32 m_lcbSttbttmbd = 0;
+
+ WW8_FC m_fcSttbFnm = 0; // 0x02da offset in the table stream of masters subdocument names
+ sal_Int32 m_lcbSttbFnm = 0; // 0x02de length
+
+ /*
+ special list handling for WW8
+ */
+ WW8_FC m_fcPlcfLst = 0; // 0x02e2 offset in the table stream of list format information.
+ sal_Int32 m_lcbPlcfLst = 0; // 0x02e6 length
+ WW8_FC m_fcPlfLfo = 0; // 0x02ea offset in the table stream of list format override information.
+ sal_Int32 m_lcbPlfLfo = 0; // 0x02ee length
+ /*
+ special Break handling for text-box-stories in WW8
+ */
+ WW8_FC m_fcPlcftxbxBkd = 0; // 0x02f2 PLCF for TextBox-Break-descriptors in the Maintext
+ sal_Int32 m_lcbPlcftxbxBkd = 0; // 0x02f6
+ WW8_FC m_fcPlcfHdrtxbxBkd = 0;// 0x02fa PLCF for TextBox-Break-descriptors in the Header-/Footer- area
+ sal_Int32 m_lcbPlcfHdrtxbxBkd = 0;// 0x02fe
+
+ // 0x302 - 372 == ignore
+ /*
+ ListNames (skip to here!)
+ */
+ WW8_FC m_fcSttbListNames = 0;// 0x0372 PLCF for Listname Table
+ sal_Int32 m_lcbSttbListNames = 0;// 0x0376
+
+ WW8_FC m_fcPlcfTch = 0;
+ sal_Int32 m_lcbPlcfTch = 0;
+
+ // 0x38A - 41A == ignore
+ WW8_FC m_fcAtrdExtra = 0;
+ sal_uInt32 m_lcbAtrdExtra = 0;
+
+ // 0x422 - 0x429 == ignore
+
+ /// 0x42a smart-tag bookmark string table offset.
+ WW8_FC m_fcSttbfBkmkFactoid = 0;
+ /// 0x42e smart-tag bookmark string table length.
+ sal_uInt32 m_lcbSttbfBkmkFactoid = 0;
+ /// 0x432 smart-tag bookmark starts offset.
+ WW8_FC m_fcPlcfBkfFactoid = 0;
+ /// 0x436 smart-tag bookmark ends length.
+ sal_uInt32 m_lcbPlcfBkfFactoid = 0;
+
+ // 0x43a - 0x441 == ignore
+
+ /// 0x442 smart-tag bookmark ends offset.
+ WW8_FC m_fcPlcfBklFactoid = 0;
+ /// 0x446 smart-tag bookmark ends length.
+ sal_uInt32 m_lcbPlcfBklFactoid = 0;
+ /// 0x44a smart tag data offset.
+ WW8_FC m_fcFactoidData = 0;
+ /// 0x44e smart tag data length.
+ sal_uInt32 m_lcbFactoidData = 0;
+
+ // 0x452 - 0x4b9 == ignore
+
+ /// 0x4ba Plcffactoid offset.
+ WW8_FC m_fcPlcffactoid = 0;
+ /// 0x4be Plcffactoid offset.
+ sal_uInt32 m_lcbPlcffactoid = 0;
+
+ // 0x4bf - 0x4d4 == ignore
+
+ WW8_FC m_fcHplxsdr = 0; //bizarrely, word xp seems to require this set to shows dates from AtrdExtra
+ sal_uInt32 m_lcbHplxsdr = 0;
+
+ /*
+ general variables that were used for Ver67 and Ver8,
+ even though they had different sizes in the corresponding files:
+ */
+ sal_Int32 m_pnChpFirst = 0;
+ sal_Int32 m_pnPapFirst = 0;
+ sal_Int32 m_cpnBteChp = 0;
+ sal_Int32 m_cpnBtePap = 0;
+ /*
+ The actual nFib, moved here because some readers assumed
+ they couldn't read any format with nFib > some constant
+ */
+ sal_uInt16 m_nFib_actual = 0; // 0x05bc #i56856#
+
+ WW8Fib(SvStream& rStrm, sal_uInt8 nWantedVersion,sal_uInt32 nOffset=0);
+ explicit WW8Fib(sal_uInt8 nVersion, bool bDot = false);
+
+ void WriteHeader(SvStream& rStrm);
+ void Write(SvStream& rStrm);
+ static rtl_TextEncoding GetFIBCharset(sal_uInt16 chs, LanguageType nLidLocale);
+ ww::WordVersion GetFIBVersion() const;
+ bool GetBaseCp(ManTypes nType, WW8_CP * cp) const;
+ sal_Unicode getNumDecimalSep() const { return m_nNumDecimalSep;}
+};
+
+class WW8Style
+{
+protected:
+ WW8Fib& m_rFib;
+ SvStream& m_rStream;
+
+ sal_uInt16 m_cstd; // Count of styles in stylesheet
+ sal_uInt16 m_cbSTDBaseInFile; // Length of STD Base as stored in a file
+ sal_uInt16 m_fStdStylenamesWritten : 1; // Are built-in stylenames stored?
+ sal_uInt16 : 15; // Spare flags
+ sal_uInt16 m_stiMaxWhenSaved; // Max sti known when file was written
+ sal_uInt16 m_istdMaxFixedWhenSaved; // How many fixed-index istds are there?
+ sal_uInt16 m_nVerBuiltInNamesWhenSaved; // Current version of built-in stylenames
+ // ftc used by StandardChpStsh for this document
+ sal_uInt16 m_ftcAsci;
+ // CJK ftc used by StandardChpStsh for this document
+ sal_uInt16 m_ftcFE;
+ // CTL/Other ftc used by StandardChpStsh for this document
+ sal_uInt16 m_ftcOther;
+ // CTL ftc used by StandardChpStsh for this document
+ sal_uInt16 m_ftcBi;
+
+ //No copying
+ WW8Style(const WW8Style&);
+ WW8Style& operator=(const WW8Style&);
+
+public:
+ WW8Style( SvStream& rSt, WW8Fib& rFibPara );
+ std::unique_ptr<WW8_STD> Read1STDFixed(sal_uInt16& rSkip);
+ std::unique_ptr<WW8_STD> Read1Style(sal_uInt16& rSkip, OUString* pString);
+ sal_uInt16 GetCount() const { return m_cstd; }
+};
+
+class WW8Fonts final
+{
+private:
+ WW8Fonts(const WW8Fonts&) = delete;
+ WW8Fonts& operator=(const WW8Fonts&) = delete;
+
+ std::vector<WW8_FFN> m_aFontA; // Array of Pointers to Font Description
+
+public:
+ WW8Fonts( SvStream& rSt, WW8Fib const & rFib );
+ const WW8_FFN* GetFont( sal_uInt16 nNum ) const;
+ sal_uInt16 GetMax() const { return m_aFontA.size(); }
+};
+
+typedef sal_uInt8 HdFtFlags;
+namespace nsHdFtFlags
+{
+ const HdFtFlags WW8_HEADER_EVEN = 0x01;
+ const HdFtFlags WW8_HEADER_ODD = 0x02;
+ const HdFtFlags WW8_FOOTER_EVEN = 0x04;
+ const HdFtFlags WW8_FOOTER_ODD = 0x08;
+ const HdFtFlags WW8_HEADER_FIRST = 0x10;
+ const HdFtFlags WW8_FOOTER_FIRST = 0x20;
+}
+
+/// Document Properties
+struct WW8Dop
+{
+public:
+ /* Error Status */
+ ErrCode nDopError;
+ /*
+ Corresponds only roughly to the actual structure of the Winword DOP,
+ the winword FIB version matters to what exists.
+ */
+ bool fFacingPages : 1 /*= false*/; // 1 when facing pages should be printed
+
+ bool fWidowControl : 1 /*= false*/; //a: orig 97 docs say
+ // 1 when widow control is in effect. 0 when widow control disabled.
+ //b: MS-DOC: Word Binary File Format (.doc) Structure Specification 2008 says
+ // B - unused1 (1 bit): Undefined and MUST be ignored.
+
+ bool fPMHMainDoc : 1 /*= false*/; // 1 when doc is a main doc for Print Merge Helper, 0 when not; default=0
+ sal_uInt16 grfSuppression : 2 /*= 0*/; // 0 Default line suppression storage; 0= form letter line suppression; 1= no line suppression; default=0
+ sal_uInt16 fpc : 2 /*= 0*/; // 1 footnote position code: 0 as endnotes, 1 at bottom of page, 2 immediately beneath text
+ sal_uInt16 : 1; // 0 unused
+
+ sal_uInt16 grpfIhdt : 8 /*= 0*/; // 0 specification of document headers and footers. See explanation under Headers and Footers topic.
+
+ sal_uInt16 rncFootnote : 2 /*= 0*/; // 0 restart index for footnotes, 0 don't restart note numbering, 1 section, 2 page
+ sal_uInt16 nFootnote : 14 /*= 0*/; // 1 initial footnote number for document
+ bool fOutlineDirtySave : 1 /*= false*/; // when 1, indicates that information in the hPLCFpad should be refreshed since outline has been dirtied
+ sal_uInt16 : 7; // reserved
+ bool fOnlyMacPics : 1 /*= false*/; // when 1, Word believes all pictures recorded in the document were created on a Macintosh
+ bool fOnlyWinPics : 1 /*= false*/; // when 1, Word believes all pictures recorded in the document were created in Windows
+ bool fLabelDoc : 1 /*= false*/; // when 1, document was created as a print merge labels document
+ bool fHyphCapitals : 1 /*= false*/; // when 1, Word is allowed to hyphenate words that are capitalized. When 0, capitalized may not be hyphenated
+ bool fAutoHyphen : 1 /*= false*/; // when 1, Word will hyphenate newly typed text as a background task
+ bool fFormNoFields : 1 /*= false*/;
+ bool fLinkStyles : 1 /*= false*/; // when 1, Word will merge styles from its template
+ bool fRevMarking : 1 /*= false*/; // when 1, Word will mark revisions as the document is edited
+ bool fBackup : 1 /*= false*/; // always make backup when document saved when 1.
+ bool fExactCWords : 1 /*= false*/;
+ bool fPagHidden : 1 /*= false*/;
+ bool fPagResults : 1 /*= false*/;
+ bool fLockAtn : 1 /*= false*/; // when 1, annotations are locked for editing
+ bool fMirrorMargins : 1 /*= false*/; // swap margins on left/right pages when 1.
+ bool fReadOnlyRecommended : 1 /*= false*/;// user has recommended that this doc be opened read-only when 1
+ bool fDfltTrueType : 1 /*= false*/; // when 1, use TrueType fonts by default (flag obeyed only when doc was created by WinWord 2.x)
+ bool fPagSuppressTopSpacing : 1 /*= false*/;//when 1, file created with SUPPRESSTOPSPACING=YES in win.ini. (flag obeyed only when doc was created by WinWord 2.x).
+ bool fProtEnabled : 1 /*= false*/; // when 1, document is protected from edit operations
+ bool fDispFormFieldSel : 1 /*= false*/;// when 1, restrict selections to occur only within form fields
+ bool fRMView : 1 /*= false*/; // when 1, show revision markings on screen
+ bool fRMPrint : 1 /*= false*/; // when 1, print revision marks when document is printed
+ bool fWriteReservation : 1 /*= false*/;
+ bool fLockRev : 1 /*= false*/; // when 1, the current revision marking state is locked
+ bool fEmbedFonts : 1 /*= false*/; // when 1, document contains embedded True Type fonts
+ // compatibility options
+ bool copts_fNoTabForInd : 1 /*= false*/; // when 1, don't add automatic tab stops for hanging indent
+ bool copts_fNoSpaceRaiseLower : 1 /*= false*/; // when 1, don't add extra space for raised or lowered characters
+ bool copts_fSuppressSpbfAfterPgBrk : 1 /*= false*/; // when 1, suppress the paragraph Space Before and Space After options after a page break
+ bool copts_fWrapTrailSpaces : 1 /*= false*/; // when 1, wrap trailing spaces at the end of a line to the next line
+ bool copts_fMapPrintTextColor : 1 /*= false*/; // when 1, print colors as black on non-color printers
+ bool copts_fNoColumnBalance : 1 /*= false*/; // when 1, don't balance columns for Continuous Section starts
+ bool copts_fConvMailMergeEsc : 1 /*= false*/;
+ bool copts_fSuppressTopSpacing : 1 /*= false*/; // when 1, suppress extra line spacing at top of page
+ bool copts_fOrigWordTableRules : 1 /*= false*/; // when 1, combine table borders like Word 5.x for the Macintosh
+ bool copts_fTransparentMetafiles : 1 /*= false*/; // when 1, don't blank area between metafile pictures
+ bool copts_fShowBreaksInFrames : 1 /*= false*/; // when 1, show hard page or column breaks in frames
+ bool copts_fSwapBordersFacingPgs : 1 /*= false*/; // when 1, swap left and right pages on odd facing pages
+ bool copts_fExpShRtn : 1 /*= false*/; // when 1, expand character spaces on the line ending SHIFT+RETURN // #i56856#
+
+ sal_Int16 dxaTab = 0; // 720 twips - default tab width
+ sal_uInt16 wSpare = 0;
+ sal_uInt16 dxaHotZ = 0; // width of hyphenation hot zone measured in twips
+ sal_uInt16 cConsecHypLim = 0; // number of lines allowed to have consecutive hyphens
+ sal_uInt16 wSpare2 = 0; // reserved
+ sal_Int32 dttmCreated = 0; // DTTM date and time document was created
+ sal_Int32 dttmRevised = 0; // DTTM date and time document was last revised
+ sal_Int32 dttmLastPrint = 0; // DTTM date and time document was last printed
+ sal_Int16 nRevision = 0; // number of times document has been revised since its creation
+ sal_Int32 tmEdited = 0; // time document was last edited
+ sal_Int32 cWords = 0; // count of words tallied by last Word Count execution
+ sal_Int32 cCh = 0; // count of characters tallied by last Word Count execution
+ sal_Int16 cPg = 0; // count of pages tallied by last Word Count execution
+ sal_Int32 cParas = 0; // count of paragraphs tallied by last Word Count execution
+ sal_uInt16 rncEdn : 2 /*= 0*/; // restart endnote number code: 0 don't restart endnote numbering, 1 section, 2 page
+ sal_uInt16 nEdn : 14 /*= 0*/; // beginning endnote number
+ sal_uInt16 epc : 2 /*= 0*/; // endnote position code: 0 at end of section, 3 at end of document
+
+ bool fPrintFormData : 1 /*= false*/; // only print data inside of form fields
+ bool fSaveFormData : 1 /*= false*/; // only save document data that is inside of a form field.
+ bool fShadeFormData : 1 /*= false*/; // shade form fields
+ sal_uInt16 : 2; // reserved
+ bool fWCFootnoteEdn : 1 /*= false*/; // when 1, include footnotes and endnotes in word count
+ sal_Int32 cLines = 0; // count of lines tallied by last Word Count operation
+ sal_Int32 cWordsFootnoteEnd = 0; // count of words in footnotes and endnotes tallied by last Word Count operation
+ sal_Int32 cChFootnoteEdn = 0; // count of characters in footnotes and endnotes tallied by last Word Count operation
+ sal_Int16 cPgFootnoteEdn = 0; // count of pages in footnotes and endnotes tallied by last Word Count operation
+ sal_Int32 cParasFootnoteEdn = 0; // count of paragraphs in footnotes and endnotes tallied by last Word Count operation
+ sal_Int32 cLinesFootnoteEdn = 0; // count of paragraphs in footnotes and endnotes tallied by last Word Count operation
+ sal_Int32 lKeyProtDoc = 0; // document protection password key, only valid if dop.fProtEnabled, dop.fLockAtn or dop.fLockRev are 1.
+ sal_uInt16 wvkSaved : 3 /*= 0*/; // document view kind: 0 Normal view, 1 Outline view, 2 Page View
+ sal_uInt16 wScaleSaved : 9 /*= 0*/; ///< Specifies the zoom percentage that was in use when the document was saved.
+ sal_uInt16 zkSaved : 2 /*= 0*/; // document zoom type: 0 percent, 1 whole/entire page, 2 page width, 3 text width/optimal
+ bool fRotateFontW6 : 1 /*= false*/;
+ bool iGutterPos : 1 /*= false*/;
+
+ // this should be the end for nFib < 103, otherwise the file is broken!
+
+ /*
+ for nFib >= 103 it continues:
+ */
+ bool fNoTabForInd : 1 /*= false*/; // see above in compatibility options
+ bool fNoSpaceRaiseLower : 1 /*= false*/; // see above
+ bool fSuppressSpbfAfterPageBreak : 1 /*= false*/; // see above
+ bool fWrapTrailSpaces : 1 /*= false*/; // see above
+ bool fMapPrintTextColor : 1 /*= false*/; // see above
+ bool fNoColumnBalance : 1 /*= false*/; // see above
+ bool fConvMailMergeEsc : 1 /*= false*/; // see above
+ bool fSuppressTopSpacing : 1 /*= false*/; // see above
+ bool fOrigWordTableRules : 1 /*= false*/; // see above
+ bool fTransparentMetafiles : 1 /*= false*/; // see above
+ bool fShowBreaksInFrames : 1 /*= false*/; // see above
+ bool fSwapBordersFacingPgs : 1 /*= false*/; // see above
+ bool fCompatibilityOptions_Unknown1_13 : 1 /*= false*/; // #i78591#
+ bool fExpShRtn : 1 /*= false*/; // #i78591# and #i56856#
+ bool fCompatibilityOptions_Unknown1_15 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_16 : 1 /*= false*/; // #i78591#
+ bool fSuppressTopSpacingMac5 : 1 /*= false*/; // Suppress extra line spacing at top
+ // of page like MacWord 5.x
+ bool fTruncDxaExpand : 1 /*= false*/; // Expand/Condense by whole number of points
+ bool fPrintBodyBeforeHdr : 1 /*= false*/; // Print body text before header/footer
+ bool fNoLeading : 1 /*= false*/; // Don't add extra spacebetween rows of text
+ bool fCompatibilityOptions_Unknown1_21 : 1 /*= false*/; // #i78591#
+ bool fMWSmallCaps : 1 /*= false*/; // Use larger small caps like MacWord 5.x
+ bool fCompatibilityOptions_Unknown1_23 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_24 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_25 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_26 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_27 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_28 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_29 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_30 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown1_31 : 1 /*= false*/; // #i78591#
+ bool fUsePrinterMetrics : 1 /*= false*/; //The magic option
+
+ // this should be the end for nFib <= 105, otherwise the file is broken!
+
+ /*
+ for nFib > 105 it continues:
+ */
+ sal_Int16 adt = 0; // Autoformat Document Type:
+ // 0 for normal.
+ // 1 for letter, and
+ // 2 for email.
+ WW8DopTypography doptypography = {}; // see WW8STRUC.HXX
+ WW8_DOGRID dogrid = {}; // see WW8STRUC.HXX
+ sal_uInt16 : 1; // reserved
+ sal_uInt16 lvl : 4 /*= 0*/; // Which outline levels are showing in outline view
+ sal_uInt16 : 4; // reserved
+ bool fHtmlDoc : 1 /*= false*/; // This file is based upon an HTML file
+ sal_uInt16 : 1; // reserved
+ bool fSnapBorder : 1 /*= false*/; // Snap table and page borders to page border
+ bool fIncludeHeader : 1 /*= false*/; // Place header inside page border
+ bool fIncludeFooter : 1 /*= false*/; // Place footer inside page border
+ bool fForcePageSizePag : 1 /*= false*/; // Are we in online view
+ bool fMinFontSizePag : 1 /*= false*/; // Are we auto-promoting fonts to >= hpsZoomFontPag?
+ bool fHaveVersions : 1 /*= false*/; // versioning is turned on
+ bool fAutoVersion : 1 /*= false*/; // autoversioning is enabled
+ sal_uInt16 : 14; // reserved
+ // Skip 12 Bytes here: ASUMI
+ sal_Int32 cChWS = 0;
+ sal_Int32 cChWSFootnoteEdn = 0;
+ sal_Int32 grfDocEvents = 0;
+ // Skip 4+30+8 Bytes here
+ sal_Int32 cDBC = 0;
+ sal_Int32 cDBCFootnoteEdn = 0;
+ // Skip 4 Bytes here
+ sal_Int16 nfcFootnoteRef = 0;
+ sal_Int16 nfcEdnRef = 0;
+ sal_Int16 hpsZoomFontPag = 0;
+ sal_Int16 dywDispPag = 0;
+
+ // [MS-DOC] 2.7.11 Copts A..H
+ bool fCompatibilityOptions_Unknown2_1 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_2 : 1 /*= false*/; // #i78591#
+ bool fDontUseHTMLAutoSpacing : 1 /*= false*/;
+ bool fCompatibilityOptions_Unknown2_4 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_5 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_6 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_7 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_8 : 1 /*= false*/; // #i78591#
+
+ // [MS-DOC] 2.7.11 Copts I..P
+ bool fCompatibilityOptions_Unknown2_9 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_10 : 1 /*= false*/; // #i78591#
+ bool fDontBreakWrappedTables : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_12 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_13 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_14 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_15 : 1 /*= false*/; // #i78591#
+
+ // [MS-DOC] 2.7.11 Copts Q..X
+ bool fCompatibilityOptions_Unknown2_16 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_17 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_18 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_19 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_20 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_21 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_22 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_23 : 1 /*= false*/; // #i78591#
+
+ // [MS-DOC] 2.7.11 Copts Y..f
+ bool fCompatibilityOptions_Unknown2_24 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_25 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_26 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_27 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_28 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_29 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_30 : 1 /*= false*/; // #i78591#
+ bool fCompatibilityOptions_Unknown2_31 : 1 /*= false*/; // #i78591#
+
+ // [MS-DOC] 2.7.11 Copts g
+ bool fCompatibilityOptions_Unknown2_32 : 1 /*= false*/; // #i78591#
+
+ sal_uInt16 fUnknown3 : 15 /*= 0*/;
+ bool fUseBackGroundInAllmodes : 1 /*= false*/;
+
+ bool fDoNotEmbedSystemFont : 1 /*= false*/;
+ bool fWordCompat : 1 /*= false*/;
+ bool fLiveRecover : 1 /*= false*/;
+ bool fEmbedFactoids : 1 /*= false*/;
+ bool fFactoidXML : 1 /*= false*/;
+ bool fFactoidAllDone : 1 /*= false*/;
+ bool fFolioPrint : 1 /*= false*/;
+ bool fReverseFolio : 1 /*= false*/;
+ sal_uInt16 iTextLineEnding : 3 /*= 0*/;
+ bool fHideFcc : 1 /*= false*/;
+ bool fAcetateShowMarkup : 1 /*= false*/;
+ bool fAcetateShowAtn : 1 /*= false*/;
+ bool fAcetateShowInsDel : 1 /*= false*/;
+ bool fAcetateShowProps : 1 /*= false*/;
+
+ bool bUseThaiLineBreakingRules = false;
+
+ /* Constructor for importing, needs to know the version of word used */
+ WW8Dop(SvStream& rSt, sal_Int16 nFib, sal_Int32 nPos, sal_uInt32 nSize);
+
+ /* Constructs default DOP suitable for exporting */
+ WW8Dop();
+ void Write(SvStream& rStrm, WW8Fib& rFib) const;
+
+ sal_uInt32 GetCompatibilityOptions() const;
+ void SetCompatibilityOptions(sal_uInt32 a32Bit);
+ // i#78591#
+ sal_uInt32 GetCompatibilityOptions2() const;
+ void SetCompatibilityOptions2(sal_uInt32 a32Bit);
+};
+
+class WW8PLCF_HdFt
+{
+private:
+ WW8PLCF m_aPLCF;
+ short m_nIdxOffset;
+
+public:
+ WW8PLCF_HdFt( SvStream* pSt, WW8Fib const & rFib, WW8Dop const & rDop );
+ bool GetTextPos(sal_uInt8 grpfIhdt, sal_uInt8 nWhich, WW8_CP& rStart, WW8_CP& rLen);
+ void GetTextPosExact(short nIdx, WW8_CP& rStart, WW8_CP& rLen);
+ void UpdateIndex( sal_uInt8 grpfIhdt );
+};
+
+Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize);
+std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx);
+
+[[nodiscard]] bool checkRead(SvStream &rSt, void *pDest, sal_uInt32 nLength);
+
+//MS has a (slightly) inaccurate view of how many twips
+//are in the default letter size of a page
+const sal_uInt16 lLetterWidth = 12242;
+const sal_uInt16 lLetterHeight = 15842;
+
+#ifdef OSL_BIGENDIAN
+void swapEndian(sal_Unicode *pString);
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8struc.hxx b/sw/source/filter/ww8/ww8struc.hxx
new file mode 100644
index 0000000000..a1a22d7a05
--- /dev/null
+++ b/sw/source/filter/ww8/ww8struc.hxx
@@ -0,0 +1,1159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8STRUC_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8STRUC_HXX
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include <filter/msfilter/util.hxx>
+#include <i18nlangtag/lang.h>
+#include <tools/color.hxx>
+#include <tools/solar.h>
+#include <tools/stream.hxx>
+
+#include <vector>
+
+#ifdef _WIN32
+# pragma pack(push, 2)
+#endif
+
+class WW8Export;
+
+inline void Set_UInt8( sal_uInt8 *& p, sal_uInt8 n )
+{
+ *p = n;
+ p+= 1;
+}
+
+inline void Set_UInt16( sal_uInt8 *& p, sal_uInt16 n )
+{
+ ShortToSVBT16( n, *reinterpret_cast<SVBT16*>(p) );
+ p+= 2;
+}
+
+inline void Set_UInt32( sal_uInt8 *& p, sal_uInt32 n )
+{
+ UInt32ToSVBT32( n, *reinterpret_cast<SVBT32*>(p) );
+ p+= 4;
+}
+
+struct Word2CHPX
+{
+ sal_uInt16 fBold:1;
+ sal_uInt16 fItalic:1;
+ sal_uInt16 fRMarkDel:1;
+ sal_uInt16 fOutline:1;
+ sal_uInt16 fFieldVanish:1;
+ sal_uInt16 fSmallCaps:1;
+ sal_uInt16 fCaps:1;
+ sal_uInt16 fVanish:1;
+ sal_uInt16 fRMark:1;
+ sal_uInt16 fSpec:1;
+ sal_uInt16 fStrike:1;
+ sal_uInt16 fObj:1;
+ sal_uInt16 fBoldBi:1;
+ sal_uInt16 fItalicBi:1;
+ sal_uInt16 fBiDi:1;
+ sal_uInt16 fDiacUSico:1;
+ sal_uInt16 fsIco:1;
+ sal_uInt16 fsFtc:1;
+ sal_uInt16 fsHps:1;
+ sal_uInt16 fsKul:1;
+ sal_uInt16 fsPos:1;
+ sal_uInt16 fsSpace:1;
+ sal_uInt16 fsLid:1;
+ sal_uInt16 fsIcoBi:1;
+ sal_uInt16 fsFtcBi:1;
+ sal_uInt16 fsHpsBi:1;
+ sal_uInt16 fsLidBi:1;
+
+ sal_uInt16 ftc;
+ sal_uInt16 hps;
+ sal_uInt8 qpsSpace:6;
+ sal_uInt8 fSysVanish:1;
+ sal_uInt8 fNumRun:1;
+ sal_uInt8 ico:5;
+ sal_uInt8 kul:3;
+ sal_uInt8 hpsPos;
+ sal_uInt8 icoBi;
+ sal_uInt16 lid;
+ sal_uInt16 ftcBi;
+ sal_uInt16 hpsBi;
+ sal_uInt16 lidBi;
+ sal_uInt32 fcPic;
+
+ Word2CHPX()
+ : fBold(0),
+ fItalic(0),
+ fRMarkDel(0),
+ fOutline(0),
+ fFieldVanish(0),
+ fSmallCaps(0),
+ fCaps(0),
+ fVanish(0),
+ fRMark(0),
+ fSpec(0),
+ fStrike(0),
+ fObj(0),
+ fBoldBi(0),
+ fItalicBi(0),
+ fBiDi(0),
+ fDiacUSico(0),
+ fsIco(0),
+ fsFtc(0),
+ fsHps(0),
+ fsKul(0),
+ fsPos(0),
+ fsSpace(0),
+ fsLid(0),
+ fsIcoBi(0),
+ fsFtcBi(0),
+ fsHpsBi(0),
+ fsLidBi(0),
+
+ ftc(0),
+ hps(0),
+ qpsSpace(0),
+ fSysVanish(0),
+ fNumRun(0),
+ ico(0),
+ kul(0),
+ hpsPos(0),
+ icoBi(0),
+ lid(0),
+ ftcBi(0),
+ hpsBi(0),
+ lidBi(0),
+ fcPic(0)
+ {
+ }
+};
+
+typedef sal_Int16 WW8_PN;
+typedef sal_Int32 WW8_FC;
+typedef sal_Int32 WW8_CP;
+
+const WW8_FC WW8_FC_MAX = SAL_MAX_INT32;
+const WW8_CP WW8_CP_MAX = SAL_MAX_INT32;
+
+/** STD - STyle Definition
+
+ The STD contains the entire definition of a style.
+ It has two parts, a fixed-length base (cbSTDBase bytes long)
+ and a variable length remainder holding the name, and the upx and upe
+ arrays (a upx and upe for each type stored in the style, std.cupx)
+ Note that new fields can be added to the BASE of the STD without
+ invalidating the file format, because the STSHI contains the length
+ that is stored in the file. When reading STDs from an older version,
+ new fields will be zero.
+*/
+struct WW8_STD
+{
+ // Base part of STD:
+ sal_uInt16 sti : 12; // invariant style identifier
+ sal_uInt16 fScratch : 1; // spare field for any temporary use,
+ // always reset back to zero!
+ sal_uInt16 fInvalHeight : 1; // PHEs of all text with this style are wrong
+ sal_uInt16 fHasUpe : 1; // UPEs have been generated
+ sal_uInt16 fMassCopy : 1; // std has been mass-copied; if unused at
+ // save time, style should be deleted
+ sal_uInt16 sgc : 4; // style type code
+ sal_uInt16 istdBase : 12; // base style
+ sal_uInt16 cupx : 4; // # of UPXs (and UPEs)
+ sal_uInt16 istdNext : 12; // next style
+ sal_uInt16 bchUpe; // offset to end of upx's, start of upe's
+ // new:
+ // from Ver8 on there are two more fields:
+ sal_uInt16 fAutoRedef : 1; /* auto redefine style when appropriate */
+ sal_uInt16 fHidden : 1; /* hidden from UI? */
+ sal_uInt16 : 14; /* unused bits */
+
+ // Variable length part of STD:
+ // sal_uInt8 stzName[2]; /* sub-names are separated by chDelimStyle
+ // char grupx[];
+ // the UPEs are not stored on the file; they are a cache of the based-on
+ // chain
+ // char grupe[];
+};
+
+static_assert(sizeof (WW8_STD) == 10, "this has to match the msword size");
+
+/** base for reading AND working on (will have different subclasses */
+struct WW8_FFN_BASE // Font Descriptor
+{
+ // from Ver6 on
+ sal_uInt8 cbFfnM1; // 0x0 total length of FFN - 1.
+
+ sal_uInt8 prg: 2; // 0x1:03 pitch request
+ sal_uInt8 fTrueType : 1; // 0x1:04 when 1, font is a TrueType font
+ sal_uInt8 _reserved1 : 1; // 0x1:08 reserved
+ sal_uInt8 ff : 3; // 0x1:70 font family id
+ sal_uInt8 _reserved2 : 1; // 0x1:80 reserved
+
+ short wWeight; // 0x2 base weight of font
+ sal_uInt8 chs; // 0x4 character set identifier
+ sal_uInt8 ibszAlt; // 0x5 index into ffn.szFfn to the name of the alternate font
+};
+
+static_assert(sizeof (WW8_FFN_BASE) == 6, "this has to match the msword size");
+
+/** This is what we use in the Parser (and Dumper)
+*/
+struct WW8_FFN
+{
+ // from Ver8 on as Unicode
+ OUString sFontname;// 0x6 or 0x40 resp. from Ver8 on zero terminated string that
+ // records name of font.
+ // Maximal size of szFfn is 65 characters.
+ // Attention: This array can be smaller!!!
+ // Possibly followed by a second sz which records the
+ // name of an alternate font to use if the first named
+ // font does not exist on this system.
+ WW8_FFN_BASE aFFNBase;
+};
+
+struct WW8_BRCVer6 // BoRder Code (WW6 version)
+{
+ SVBT16 aBits1 = {};
+// sal_uInt16 dxpLineWidth : 3;// 0007 When dxpLineWidth is 0, 1, 2, 3, 4, or 5, this field is the width of
+ // a single line of border in units of 0.75 points
+ // Must be nonzero when brcType is nonzero.
+ // 6 == dotted, 7 == dashed.
+// sal_uInt16 brcType : 2; // 0018 border type code: 0 == none, 1 == single, 2 == thick, 3 == double
+// sal_uInt16 fShadow : 1; // 0020 when 1, border is drawn with shadow. Must be 0 when BRC is a substructure of the TC
+// sal_uInt16 ico : 5; // 07C0 color code (see chp.ico)
+// sal_uInt16 dxpSpace : 5; // F800 width of space to maintain between border and text within border.
+ // Must be 0 when BRC is a substructure of the TC. Stored in points for Windows.
+ WW8_BRCVer6() = default;
+
+ sal_uInt8 dxpLineWidth() const
+ { return aBits1[0] & 0x07; }
+ sal_uInt8 brcType() const
+ { return (aBits1[0] & 0x18) >> 3; }
+ bool fShadow() const
+ { return !!(aBits1[0] & 0x20); }
+ sal_uInt8 ico() const
+ { return ((aBits1[0] & 0xc0) >> 6) | ((aBits1[1] & 0x07) << 2); }
+ sal_uInt8 dxpSpace() const
+ { return aBits1[1] >> 3; }
+};
+
+struct WW8_BRC // BoRder Code (WW8 version)
+// Documented at http://msdn.microsoft.com/en-us/library/dd952599.aspx
+{
+ SVBT16 aBits1 = {};
+ SVBT16 aBits2 = {};
+// sal_uInt8 dptLineWidth;
+// sal_uInt8 brcType;
+// sal_uInt8 ico;
+// sal_uInt8 dptSpace : 5
+// bool fShadow : 1;
+// bool fFrame : 1;
+// bool fReserved : 1;
+ WW8_BRC() = default;
+
+ sal_uInt8 dptLineWidth() const // border line width (1/8pt)
+ { return aBits1[0]; }
+ sal_uInt8 brcType() const // border type (eg single, double, dotted)
+ { return aBits1[1]; }
+ sal_uInt8 ico() const // colour index, 1-17 or 0=auto
+ { return aBits2[0]; }
+ sal_uInt8 dptSpace() const // space between text & border (pt)
+ { return aBits2[1] & 0x1f; }
+ bool fShadow() const // shadow effect
+ { return !!(aBits2[1] & 0x20); }
+ bool fFrame() const // 3D frame effect
+ { return !!(aBits2[1] & 0x40); }
+ bool isNil() const // nil = no border
+ { return aBits1[0] == 0xff && aBits1[1] == 0xff; }
+
+ WW8_BRC(sal_uInt8 _dptLineWidth, sal_uInt8 _brcType, sal_uInt8 _ico,
+ sal_uInt8 _dptSpace, bool _fShadow, bool _fFrame)
+ {
+ assert(_dptSpace < 0x20);
+ aBits1[0] = _dptLineWidth;
+ aBits1[1] = _brcType;
+ aBits2[0] = _ico;
+ aBits2[1] = _dptSpace | (static_cast<sal_uInt8>(_fShadow) << 5)
+ | (static_cast<sal_uInt8>(_fFrame) << 6);
+ }
+ // Convert BRC from WW6 to WW8 format
+ explicit WW8_BRC(const WW8_BRCVer6& brcVer6);
+
+ // Returns LO border width in twips=1/20pt, taking into account brcType
+ short DetermineBorderProperties(short *pSpace) const;
+};
+
+typedef WW8_BRC WW8_BRC5[5]; // 5 * Border Code
+
+struct WW8_BRCVer9 // BoRder Code (WW9 version)
+// Documented at http://msdn.microsoft.com/en-us/library/dd907496.aspx
+{
+ SVBT32 aBits1 = {}; // border colour (RGB)
+ SVBT32 aBits2 = {};
+// sal_uInt8 dptLineWidth; // border line width (1/8pt)
+// sal_uInt8 brcType; // border type (eg single, double, dotted)
+// sal_uInt8 dptSpace : 5; // space between text & border (pt)
+// bool fShadow : 1; // border has shadow effect
+// bool fFrame : 1; // border has 3D effect
+// sal_uInt16 fReserved : 9; // unused
+ WW8_BRCVer9() = default;
+
+ sal_uInt32 cv() const // colour value (BGR)
+ { return SVBT32ToUInt32(aBits1); }
+ sal_uInt8 dptLineWidth() const // border line width (1/8pt)
+ { return aBits2[0]; }
+ sal_uInt8 brcType() const // border type (eg single, double, dotted)
+ { return aBits2[1]; }
+ sal_uInt8 dptSpace() const // space between text & border (pt)
+ { return aBits2[2] & 0x1f; }
+ bool fShadow() const // shadow effect
+ { return !!(aBits2[2] & 0x20); }
+ bool fFrame() const // 3D frame effect
+ { return !!(aBits2[2] & 0x40); }
+ bool isNil() const // nil = no border
+ { return SVBT32ToUInt32(aBits2) == 0xffffffff; }
+
+ WW8_BRCVer9(sal_uInt32 _cv, sal_uInt8 _dptLineWidth, sal_uInt8 _brcType,
+ sal_uInt8 _dptSpace, bool _fShadow, bool _fFrame)
+ {
+ assert(_dptSpace < 0x20);
+ UInt32ToSVBT32(_cv, aBits1);
+ aBits2[0] = _dptLineWidth;
+ aBits2[1] = _brcType;
+ aBits2[2] = _dptSpace | (static_cast<sal_uInt8>(_fShadow) << 5)
+ | (static_cast<sal_uInt8>(_fFrame) << 6);
+ aBits2[3] = 0;
+ }
+ // Convert BRC from WW8 to WW9 format
+ explicit WW8_BRCVer9(const WW8_BRC& brcVer8);
+
+ // Returns LO border width in twips=1/20pt, taking into account brcType
+ short DetermineBorderProperties(short *pSpace=nullptr) const;
+};
+
+typedef WW8_BRCVer9 WW8_BRCVer9_5[5]; // 5 * Border Code
+
+enum BRC_Sides
+{
+ WW8_TOP = 0, WW8_LEFT = 1, WW8_BOT = 2, WW8_RIGHT = 3, WW8_BETW = 4
+};
+
+/*
+Document Typography Info (DOPTYPOGRAPHY) These options are Far East only,
+and are accessible through the Typography tab of the Tools/Options dialog.
+*/
+class WW8DopTypography
+{
+public:
+ void ReadFromMem(sal_uInt8 *&pData);
+ void WriteToMem(sal_uInt8 *&pData) const;
+
+ //Maps what I think is the language this is to affect to the OOo language
+ LanguageType GetConvertedLang() const;
+
+ sal_uInt16 m_fKerningPunct : 1; // true if we're kerning punctuation
+ sal_uInt16 m_iJustification : 2; // Kinsoku method of justification:
+ // 0 = always expand
+ // 1 = compress punctuation
+ // 2 = compress punctuation and kana.
+ sal_uInt16 m_iLevelOfKinsoku : 2; // Level of Kinsoku:
+ // 0 = Level 1
+ // 1 = Level 2
+ // 2 = Custom
+ sal_uInt16 m_f2on1 : 1; // 2-page-on-1 feature is turned on.
+ sal_uInt16 m_reserved1 : 4; // in 97 it's marked as reserved BUT
+ sal_uInt16 m_reserved2 : 6; // reserved ?
+ //we find that the following applies,
+ //2 == Japanese
+ //4 == Chinese (VR...
+ //6 == Korean
+ //8 == Chinese (Ta...
+ //perhaps a bit field where the DOP can possibly relate to more than
+ //one language at a time, nevertheless MS seems to have painted
+ //themselves into a small corner with one DopTypography for the
+ //full document, might not matter all that much though ?
+
+ enum RuleLengths {nMaxFollowing = 101, nMaxLeading = 51};
+ static constexpr OUString JapanNotBeginLevel1
+ = u"\u0021\u0025\u0029\u002c\u002e\u003a\u003b\u003f"
+ "\u005d\u007d\u00a2\u00b0\u2019\u201d\u2030\u2032"
+ "\u2033\u2103\u3001\u3002\u3005\u3009\u300b\u300d"
+ "\u300f\u3011\u3015\u309b\u309c\u309d\u309e\u30fb"
+ "\u30fd\u30fe\uff01\uff05\uff09\uff0c\uff0e\uff1a"
+ "\uff1b\uff1f\uff3d\uff5d\uff61\uff63\uff64\uff65"
+ "\uff9e\uff9f\uffe0"_ustr;
+ static constexpr OUString JapanNotEndLevel1
+ = u"\u0024\u0028\u005b\u005c\u007b\u00a3\u00a5\u2018"
+ "\u201c\u3008\u300a\u300c\u300e\u3010\u3014\uff04"
+ "\uff08\uff3b\uff5b\uff62\uffe1\uffe5"_ustr;
+
+ sal_Int16 m_cchFollowingPunct; // length of rgxchFPunct
+ sal_Int16 m_cchLeadingPunct; // length of rgxchLPunct
+
+ // array of characters that should never appear at the start of a line
+ sal_Unicode m_rgxchFPunct[nMaxFollowing];
+ // array of characters that should never appear at the end of a line
+ sal_Unicode m_rgxchLPunct[nMaxLeading];
+};
+
+struct WW8_DOGRID
+{
+ short xaGrid; // x-coord of the upper left-hand corner of the grid
+ short yaGrid; // y-coord of the upper left-hand corner of the grid
+ short dxaGrid; // width of each grid square
+ short dyaGrid; // height of each grid square
+
+ /* attention: you must not put bit fields on top of such a byte array read from a file!
+ instead put an aBits1 on it and read it out with &.
+ reason: compilers on Intel and Sparc sort the bits differently
+ */
+
+ short dyGridDisplay:7; // the number of grid squares (in the y direction)
+ // between each gridline drawn on the screen. 0 means
+ // don't display any gridlines in the y direction.
+ short fTurnItOff :1; // suppress display of gridlines
+ short dxGridDisplay:7; // the number of grid squares (in the x direction)
+ // between each gridline drawn on the screen. 0 means
+ // don't display any gridlines in the y direction.
+ short fFollowMargins:1; // if true, the grid will start at the left and top
+ // margins and ignore xaGrid and yaGrid.
+};
+
+static_assert(sizeof (WW8_DOGRID) == 10, "this has to match the msword size");
+
+struct WW8_PIC
+{
+ sal_Int32 lcb; // 0x0 number of bytes in the PIC structure plus size of following picture data which may be a Window's metafile, a bitmap, or the filename of a TIFF file.
+ sal_uInt16 cbHeader; // 0x4 number of bytes in the PIC (to allow for future expansion).
+ struct {
+ sal_Int16 mm; // 0x6 int
+ sal_Int16 xExt; // 0x8 int
+ sal_Int16 yExt; // 0xa int
+ sal_Int16 hMF; // 0xc int
+ }MFP;
+// sal_uInt8 bm[14]; // 0xe BITMAP(14 bytes) Window's bitmap structure when PIC describes a BITMAP.
+ sal_uInt8 rcWinMF[14]; // 0xe rc (rectangle - 8 bytes) rect for window origin
+ // and extents when metafile is stored -- ignored if 0
+ sal_Int16 dxaGoal; // 0x1c horizontal measurement in twips of the rectangle the picture should be imaged within.
+ sal_Int16 dyaGoal; // 0x1e vertical measurement in twips of the rectangle the picture should be imaged within.
+ sal_uInt16 mx; // 0x20 horizontal scaling factor supplied by user in .1% units.
+ sal_uInt16 my; // 0x22 vertical scaling factor supplied by user in .1% units.
+ sal_Int16 dxaCropLeft; // 0x24 the amount the picture has been cropped on the left in twips.
+ sal_Int16 dyaCropTop; // 0x26 the amount the picture has been cropped on the top in twips.
+ sal_Int16 dxaCropRight; // 0x28 the amount the picture has been cropped on the right in twips.
+ sal_Int16 dyaCropBottom;// 0x2a the amount the picture has been cropped on the bottom in twips.
+ sal_Int16 brcl : 4; // 000F Obsolete, superseded by brcTop, etc. In
+ sal_Int16 fFrameEmpty : 1; // 0010 picture consists of a single frame
+ sal_Int16 fBitmap : 1; // 0020 ==1, when picture is just a bitmap
+ sal_Int16 fDrawHatch : 1; // 0040 ==1, when picture is an active OLE object
+ sal_Int16 fError : 1; // 0080 ==1, when picture is just an error message
+ sal_Int16 bpp : 8; // FF00 bits per pixel, 0 = unknown
+ WW8_BRC rgbrc[4];
+// BRC brcTop; // 0x2e specification for border above picture
+// BRC brcLeft; // 0x30 specification for border to the left
+// BRC brcBottom; // 0x32 specification for border below picture
+// BRC brcRight; // 0x34 specification for border to the right
+ sal_Int16 dxaOrigin; // 0x36 horizontal offset of hand annotation origin
+ sal_Int16 dyaOrigin; // 0x38 vertical offset of hand annotation origin
+// sal_uInt8 rgb[]; // 0x3a variable array of bytes containing Window's metafile, bitmap or TIFF file filename.
+};
+
+struct WW8_PIC_SHADOW
+{
+ SVBT32 lcb; // 0x0 number of bytes in the PIC structure plus size of following picture data which may be a Window's metafile, a bitmap, or the filename of a TIFF file.
+ SVBT16 cbHeader; // 0x4 number of bytes in the PIC (to allow for future expansion).
+ struct {
+ SVBT16 mm; // 0x6 int
+ SVBT16 xExt; // 0x8 int
+ SVBT16 yExt; // 0xa int
+ SVBT16 hMF; // 0xc int
+ }MFP;
+// sal_uInt8 bm[14]; // 0xe BITMAP(14 bytes) Window's bitmap structure when PIC describes a BITMAP.
+ sal_uInt8 rcWinMF[14]; // 0xe rc (rectangle - 8 bytes) rect for window origin
+ // and extents when metafile is stored -- ignored if 0
+ SVBT16 dxaGoal; // 0x1c horizontal measurement in twips of the rectangle the picture should be imaged within.
+ SVBT16 dyaGoal; // 0x1e vertical measurement in twips of the rectangle the picture should be imaged within.
+ SVBT16 mx; // 0x20 horizontal scaling factor supplied by user in .1% units.
+ SVBT16 my; // 0x22 vertical scaling factor supplied by user in .1% units.
+ SVBT16 dxaCropLeft; // 0x24 the amount the picture has been cropped on the left in twips.
+ SVBT16 dyaCropTop; // 0x26 the amount the picture has been cropped on the top in twips.
+ SVBT16 dxaCropRight; // 0x28 the amount the picture has been cropped on the right in twips.
+ SVBT16 dyaCropBottom;// 0x2a the amount the picture has been cropped on the bottom in twips.
+ sal_uInt8 aBits1; //0x2c
+ sal_uInt8 aBits2;
+// WW8_BRC rgbrc[4];
+// BRC brcTop; // 0x2e specification for border above picture
+// BRC brcLeft; // 0x30 specification for border to the left
+// BRC brcBottom; // 0x32 specification for border below picture
+// BRC brcRight; // 0x34 specification for border to the right
+// SVBT16 dxaOrigin; // 0x36 horizontal offset of hand annotation origin
+// SVBT16 dyaOrigin; // 0x38 vertical offset of hand annotation origin
+// sal_uInt8 rgb[]; // 0x3a variable array of bytes containing Window's metafile, bitmap or TIFF file filename.
+};
+
+static_assert(sizeof (WW8_PIC_SHADOW) == 0x2E, "this has to match the msword size");
+ // "0x2E": cf. SwWW8ImplReader::PicRead pDataStream->Read call
+
+struct WW8_TBD
+{
+ sal_uInt8 aBits1;
+// sal_uInt8 jc : 3; // 0x07 justification code: 0=left tab, 1=centered tab, 2=right tab, 3=decimal tab, 4=bar
+// sal_uInt8 tlc : 3; // 0x38 tab leader code: 0=no leader, 1=dotted leader,
+ // 2=hyphenated leader, 3=single line leader, 4=heavy line leader
+// * int :2 C0 reserved
+};
+
+struct WW8_TCell // this is the base for further work (corresponds mostly to the Ver8 format)
+{
+ // The single-bit fields should ideally be bool, but probably need to keep
+ // them as sal_uInt8 to make them combine with the following two-bit
+ // nVertAlign:
+ sal_uInt8 bFirstMerged : 1;// 0001 set to 1 when cell is first cell of a range of cells that have been merged.
+ sal_uInt8 bMerged : 1;// 0002 set to 1 when cell has been merged with preceding cell.
+ sal_uInt8 bVertical : 1;// set to 1 when cell has vertical text flow
+ sal_uInt8 bBackward : 1;// for a vertical table cell, text flow is bottom to top when 1 and is bottom to top when 0.
+ sal_uInt8 bRotateFont : 1;// set to 1 when cell has rotated characters (i.e. uses @font)
+ sal_uInt8 bVertMerge : 1;// set to 1 when cell is vertically merged with the cell(s) above and/or below. When cells are vertically merged, the display area of the merged cells are consolidated. The consolidated area is used to display the contents of the first vertically merged cell (the cell with fVertRestart set to 1), and all other vertically merged cells (those with fVertRestart set to 0) must be empty. Cells can only be merged vertically if their left and right boundaries are (nearly) identical (i.e. if corresponding entries in rgdxaCenter of the table rows differ by at most 3).
+ sal_uInt8 bVertRestart : 1;// set to 1 when the cell is the first of a set of vertically merged cells. The contents of a cell with fVertStart set to 1 are displayed in the consolidated area belonging to the entire set of vertically merged cells. Vertically merged cells with fVertRestart set to 0 must be empty.
+ sal_uInt8 nVertAlign : 2;// specifies the alignment of the cell contents relative to text flow (e.g. in a cell with bottom to top text flow and bottom vertical alignment, the text is shifted horizontally to match the cell's right boundary):
+ // 0 top
+ // 1 center
+ // 2 bottom
+ sal_uInt16 fUnused : 7;// reserved - do not remove, fills up the sal_uInt16!
+
+ WW8_BRCVer9 rgbrc[4]; // border codes
+//notational convenience for referring to brcTop, brcLeft, etc fields.
+// BRC brcTop; // specification of the top border of a table cell
+// BRC brcLeft; // specification of left border of table row
+// BRC brcBottom; // specification of bottom border of table row
+// BRC brcRight; // specification of right border of table row.
+
+ WW8_TCell():
+ bFirstMerged(0), bMerged(0), bVertical(0), bBackward(0), bRotateFont(0), bVertMerge(0),
+ bVertRestart(0), nVertAlign(0), fUnused(0) {}
+ // default member initializers for the bitfields will only work in C++20
+};
+// cbTC (count of bytes of a TC) is 18(decimal), 12(hex).
+
+struct WW8_TCellVer6 // read from file
+{
+ sal_uInt8 aBits1Ver6;
+ sal_uInt8 aBits2Ver6;
+// sal_uInt16 fFirstMerged : 1;// 0001 set to 1 when cell is first cell of a range of cells that have been merged.
+// sal_uInt16 fMerged : 1; // 0002 set to 1 when cell has been merged with preceding cell.
+// sal_uInt16 fUnused : 14; // FFFC reserved
+ WW8_BRCVer6 rgbrcVer6[4];
+// notational convenience for referring to brcTop, brcLeft, etc fields:
+// BRC brcTop; // specification of the top border of a table cell
+// BRC brcLeft; // specification of left border of table row
+// BRC brcBottom; // specification of bottom border of table row
+// BRC brcRight; // specification of right border of table row.
+};
+// cbTC (count of bytes of a TC) is 10(decimal), A(hex).
+
+struct WW8_TCellVer8 // read from file
+{
+ SVBT16 aBits1Ver8; // Documentation: see above at WW8_TCell
+ SVBT16 aUnused; // reserve
+ WW8_BRC rgbrcVer8[4]; // Documentation: see above at WW8_TCell
+};
+// cbTC (count of bytes of a TC) is 20(decimal), 14(hex).
+
+struct WW8_SHD // struct SHD is missing from the description
+{
+private:
+ sal_uInt16 maBits;
+// sal_uInt16 nFore : 5; // 0x001f ForegroundColor
+// sal_uInt16 nBack : 5; // 0x03e0 BackgroundColor
+// sal_uInt16 nStyle : 5; // 0x7c00 Percentage and Style
+// sal_uInt16 nDontKnow : 1; // 0x8000 ??? from Ver8: also for Style
+
+public:
+ WW8_SHD() : maBits(0) {}
+
+ sal_uInt8 GetFore() const { return static_cast<sal_uInt8>( maBits & 0x1f); }
+ sal_uInt8 GetBack() const { return static_cast<sal_uInt8>((maBits >> 5 ) & 0x1f); }
+ sal_uInt8 GetStyle(bool bVer67) const
+ { return static_cast<sal_uInt8>((maBits >> 10) & ( bVer67 ? 0x1f : 0x3f ) ); }
+
+ sal_uInt16 GetValue() const { return maBits; }
+
+ void SetWWValue(SVBT16 const nVal) { maBits = SVBT16ToUInt16(nVal); }
+
+ void SetFore(sal_uInt8 nVal)
+ {
+ maBits &= 0xffe0;
+ maBits |= (nVal & 0x1f);
+ }
+ void SetBack(sal_uInt8 nVal)
+ {
+ maBits &= 0xfc1f;
+ maBits |= (nVal & 0x1f) << 5;
+ }
+ void SetStyle(sal_uInt8 nVal)
+ {
+ maBits &= 0x03ff;
+ maBits |= (nVal & 0x3f) << 10;
+ }
+};
+
+struct WW8_ANLV
+{
+ sal_uInt8 nfc; // 0 number format code, 0=Arabic, 1=Upper case Roman, 2=Lower case Roman
+ // 3=Upper case Letter, 4=Lower case letter, 5=Ordinal
+ sal_uInt8 cbTextBefore; // 1 offset into anld.rgch limit of prefix text
+ sal_uInt8 cbTextAfter; // 2
+ sal_uInt8 aBits1;
+// sal_uInt8 jc : 2; // 3 : 0x03 justification code, 0=left, 1=center, 2=right, 3=left and right justify
+// sal_uInt8 fPrev : 1; // 0x04 when ==1, include previous levels
+// sal_uInt8 fHang : 1; // 0x08 when ==1, number will be displayed using a hanging indent
+// sal_uInt8 fSetBold : 1; // 0x10 when ==1, boldness of number will be determined by anld.fBold.
+// sal_uInt8 fSetItalic : 1;// 0x20 when ==1, italicness of number will be determined by anld.fItalic
+// sal_uInt8 fSetSmallCaps : 1;// 0x40 when ==1, anld.fSmallCaps will determine whether number will be displayed in small caps or not.
+// sal_uInt8 fSetCaps : 1; // 0x80 when ==1, anld.fCaps will determine whether number will be displayed capitalized or not
+ sal_uInt8 aBits2;
+// sal_uInt8 fSetStrike : 1;// 4 : 0x01 when ==1, anld.fStrike will determine whether the number will be displayed using strikethrough or not.
+// sal_uInt8 fSetKul : 1; // 0x02 when ==1, anld.kul will determine the underlining state of the autonumber.
+// sal_uInt8 fPrevSpace : 1;// 0x04 when ==1, autonumber will be displayed with a single prefixing space character
+// sal_uInt8 fBold : 1; // 0x08 determines boldness of autonumber when anld.fSetBold == 1.
+// sal_uInt8 fItalic : 1; // 0x10 determines italicness of autonumber when anld.fSetItalic == 1.
+// sal_uInt8 fSmallCaps : 1;// 0x20 determines whether autonumber will be displayed using small caps when anld.fSetSmallCaps == 1.
+// sal_uInt8 fCaps : 1; // 0x40 determines whether autonumber will be displayed using caps when anld.fSetCaps == 1.
+// sal_uInt8 fStrike : 1; // 0x80 determines whether autonumber will be displayed using caps when anld.fSetStrike == 1.
+ sal_uInt8 aBits3;
+// sal_uInt8 kul : 3; // 5 : 0x07 determines whether autonumber will be displayed with underlining when anld.fSetKul == 1.
+// sal_uInt8 ico : 5; // 0xF1 color of autonumber
+ SVBT16 ftc; // 6 font code of autonumber
+ SVBT16 hps; // 8 font half point size (or 0=auto)
+ SVBT16 iStartAt; // 0x0a starting value (0 to 65535)
+ SVBT16 dxaIndent; // 0x0c *short?* *sal_uInt16?* width of prefix text (same as indent)
+ SVBT16 dxaSpace; // 0x0e minimum space between number and paragraph
+};
+// *cbANLV (count of bytes of ANLV) is 16 (decimal), 10(hex).
+
+struct WW8_ANLD
+{
+ WW8_ANLV eAnlv; // 0
+ sal_uInt8 fNumber1; // 0x10 number only 1 item per table cell
+ sal_uInt8 fNumberAcross; // 0x11 number across cells in table rows(instead of down)
+ sal_uInt8 fRestartHdn; // 0x12 restart heading number on section boundary
+ sal_uInt8 fSpareX; // 0x13 unused( should be 0)
+ sal_uInt8 rgchAnld[32]; // 0x14 characters displayed before/after autonumber
+};
+
+struct WW8_OLST
+{
+ WW8_ANLV rganlv[9]; // 0 an array of 9 ANLV structures (heading levels)
+ sal_uInt8 fRestartHdr; // 0x90 when ==1, restart heading on section break
+ sal_uInt8 fSpareOlst2; // 0x91 reserved
+ sal_uInt8 fSpareOlst3; // 0x92 reserved
+ sal_uInt8 fSpareOlst4; // 0x93 reserved
+ sal_uInt8 rgch[64]; // 0x94 array of 64 chars text before/after number
+};
+// cbOLST is 212(decimal), D4(hex).
+
+struct WW8_FDOA
+{
+ SVBT32 fc; // 0 FC pointing to drawing object data
+ SVBT16 ctxbx; // 4 count of textboxes in the drawing object
+};
+
+struct WW8_DO
+{
+ SVBT16 dok; // 0 Drawn Object Kind, currently this is always 0
+ SVBT16 cb; // 2 size (count of bytes) of the entire DO
+ sal_uInt8 bx; // 4 x position relative to anchor CP
+ sal_uInt8 by; // 5 y position relative to anchor CP
+
+ /*
+ bx and by above are apparently better described by this info from the rtf standard...
+
+ \dobxpage The drawing object is page relative in the x-direction.
+ \dobxcolumn The drawing object is column relative in the x-direction.
+ \dobxmargin The drawing object is margin relative in the x-direction.
+
+ \dobypage The drawing object is page relative in the y-direction.
+ \dobypara The drawing object is paragraph relative in the y-direction.
+ \dobymargin The drawing object is margin relative in the y-direction.
+
+ */
+
+ SVBT16 dhgt; // 6 height of DO
+ SVBT16 aBits1;
+// sal_uInt16 fAnchorLock : 1; // 8 1 if the DO anchor is locked
+// sal_uInt8[] rgdp; // 0xa variable length array of drawing primitives
+};
+
+struct WW8_DPHEAD
+{
+ SVBT16 dpk; // 0 Drawn Primitive Kind REVIEW davebu
+ // 0=start of grouping, 1=line, 2=textbox, 3=rectangle,
+ // 4=arc, 5=ellipse, 6=polyline, 7=callout textbox,
+ // 8=end of grouping, 9=sample primitive holding default values
+ SVBT16 cb; // 2 size (count of bytes) of this DP
+ SVBT16 xa; // 4 These 2 points describe the rectangle
+ SVBT16 ya; // 6 enclosing this DP relative to the origin of
+ SVBT16 dxa; // 8 the DO
+ SVBT16 dya; // 0xa
+};
+
+struct WW8_DP_LINETYPE
+{
+ SVBT32 lnpc; // LiNe Property Color -- RGB color value
+ SVBT16 lnpw; // line property weight in twips
+ SVBT16 lnps; // line property style : 0=Solid, 1=Dashed
+ // 2=Dotted, 3=Dash Dot, 4=Dash Dot Dot, 5=Hollow
+};
+
+struct WW8_DP_SHADOW // shading!
+{
+ SVBT16 shdwpi; // Shadow Property Intensity
+ SVBT16 xaOffset; // x offset of shadow
+ SVBT16 yaOffset; // y offset of shadow
+};
+
+struct WW8_DP_FILL
+{
+ SVBT32 dlpcFg; // FiLl Property Color ForeGround -- RGB color value
+ SVBT32 dlpcBg; // Property Color BackGround -- RGB color value
+ SVBT16 flpp; // FiLl Property Pattern REVIEW davebu
+};
+
+struct WW8_DP_LINEEND
+{
+ SVBT16 aStartBits;
+// sal_uInt16 eppsStart : 2; // Start EndPoint Property Style
+ // 0=None, 1=Hollow, 2=Filled
+// sal_uInt16 eppwStart : 2; // Start EndPoint Property Weight
+// sal_uInt16 epplStart : 2; // Start EndPoint Property length
+// sal_uInt16 dummyStart : 10; // Alignment
+ SVBT16 aEndBits;
+// sal_uInt16 eppsEnd : 2; // End EndPoint Property Style
+// sal_uInt16 eppwEnd : 2; // End EndPoint Property Weight
+// sal_uInt16 epplEnd : 2; // End EndPoint Property length
+// sal_uInt16 dummyEnd : 10; // Alignment
+};
+
+struct WW8_DP_LINE
+{
+// WW8_DPHEAD dphead; // 0 Common header for a drawing primitive
+ SVBT16 xaStart; // starting point for line
+ SVBT16 yaStart;
+ SVBT16 xaEnd; // ending point for line
+ SVBT16 yaEnd;
+ WW8_DP_LINETYPE aLnt;
+ WW8_DP_LINEEND aEpp;
+ WW8_DP_SHADOW aShd;
+};
+
+struct WW8_DP_TXTBOX
+{
+ WW8_DP_LINETYPE aLnt;
+ WW8_DP_FILL aFill;
+ WW8_DP_SHADOW aShd;
+ SVBT16 aBits1;
+// sal_uInt16 fRoundCorners : 1; //0x24 0001 1 if the textbox has rounded corners
+// sal_uInt16 zaShape : 15; // 0x24 000e REVIEW davebu
+ SVBT16 dzaInternalMargin; // 0x26 REVIEW davebu
+};
+
+struct WW8_DP_RECT
+{
+ WW8_DP_LINETYPE aLnt;
+ WW8_DP_FILL aFill;
+ WW8_DP_SHADOW aShd;
+ SVBT16 aBits1;
+// sal_uInt16 fRoundCorners : 1; // 0x24 0001 1 if the textbox has rounded corners
+// sal_uInt16 zaShape : 15; // 0x24 000e REVIEW davebu
+};
+
+struct WW8_DP_ARC
+{
+ WW8_DP_LINETYPE aLnt;
+ WW8_DP_FILL aFill;
+ WW8_DP_SHADOW aShd;
+ sal_uInt8 fLeft; // 0x24 00ff REVIEW davebu
+ sal_uInt8 fUp; // 0x24 ff00 REVIEW davebu
+// sal_uInt16 fLeft : 8; // 0x24 00ff REVIEW davebu
+// sal_uInt16 fUp : 8; // 0x24 ff00 REVIEW davebu
+};
+
+struct WW8_DP_ELLIPSE
+{
+ WW8_DP_LINETYPE aLnt;
+ WW8_DP_FILL aFill;
+ WW8_DP_SHADOW aShd;
+};
+
+struct WW8_DP_POLYLINE
+{
+ WW8_DP_LINETYPE aLnt;
+ WW8_DP_FILL aFill;
+ WW8_DP_LINEEND aEpp;
+ WW8_DP_SHADOW aShd;
+ SVBT16 aBits1;
+// sal_uInt16 fPolygon : 1; // 0x28 0001 1 if this is a polygon
+// sal_uInt16 cpt : 15; // 0x28 00fe count of points
+// short xaFirst; // 0x2a These are the endpoints of the first line.
+// short yaFirst; // 0x2c
+// short xaEnd; // 0x2e
+// short yaEnd; // 0x30
+// short rgpta[]; // 0x32 An array of xa,ya pairs for the remaining points
+};
+
+struct WW8_DP_CALLOUT_TXTBOX
+{
+ SVBT16 flags; // 0x0c REVIEW davebu flags
+ SVBT16 dzaOffset; // 0x0e REVIEW davebu
+ SVBT16 dzaDescent; // 0x10 REVIEW davebu
+ SVBT16 dzaLength; // 0x12 REVIEW davebu
+ WW8_DPHEAD dpheadTxbx; // 0x14 DPHEAD for a textbox
+ WW8_DP_TXTBOX dptxbx; // 0x20 DP for a textbox
+ WW8_DPHEAD dpheadPolyLine; // 0x4c DPHEAD for a Polyline
+ WW8_DP_POLYLINE dpPolyLine; // 0x48 DP for a polyline
+};
+
+struct WW8_PCD
+{
+ sal_uInt8 aBits1;
+// sal_uInt8 fNoParaLast : 1; // when 1, means that piece contains no end of paragraph marks.
+// sal_uInt8 fPaphNil : 1; // used internally by Word
+// sal_uInt8 fCopied : 1; // used internally by Word
+// * int :5
+ sal_uInt8 aBits2; // fn int:8, used internally by Word
+ SVBT32 fc; // file offset of beginning of piece. The size of the
+ // ithpiece can be determined by subtracting rgcp[i] of
+ // the containing plcfpcd from its rgcp[i+1].
+ SVBT16 prm; // PRM contains either a single sprm or else an index number
+ // of the grpprl which contains the sprms that modify the
+ // properties of the piece.
+};
+
+// AnnoTation References Descriptor (ATRD)
+struct WW8_ATRD // for version 8
+{
+ SVBT16 xstUsrInitl[ 10 ]; // pascal-style String holding initials
+ // of annotation author
+ SVBT16 ibst; // index into GrpXstAtnOwners
+ SVBT16 ak; // not used
+ SVBT16 grfbmc; // not used
+ SVBT32 ITagBkmk; // when not -1, this tag identifies the
+ // annotation bookmark that locates the
+ // range of CPs in the main document which
+ // this annotation references.
+};
+
+struct WW8_ATRDEXTRA
+{
+ // --- Extended bit since Word 2002 ---
+
+ SVBT32 dttm;
+ SVBT16 bf;
+ SVBT32 cDepth;
+ SVBT32 diatrdParent;
+ SVBT32 Discussitem;
+};
+
+struct WW67_ATRD // for versions 6/7
+{
+ char xstUsrInitl[ 10 ]; // pascal-style String holding initials
+ // of annotation author
+ SVBT16 ibst; // index into GrpXstAtnOwners
+ SVBT16 ak; // not used
+ SVBT16 grfbmc; // not used
+ SVBT32 ITagBkmk; // when not -1, this tag identifies the
+ // annotation bookmark that locates the
+ // range of CPs in the main document which
+ // this annotation references.
+};
+
+struct WW8_TablePos
+{
+ sal_Int16 nTDxaAbs;
+ sal_Int16 nTDyaAbs;
+ sal_Int16 nLeftMargin;
+ sal_Int16 nRightMargin;
+ sal_Int16 nUpperMargin;
+ sal_Int16 nLowerMargin;
+ sal_uInt8 nTPc;
+ sal_uInt8 nPWr;
+ sal_uInt8 nTFNoAllowOverlap;
+};
+
+struct WW8_FSPA
+{
+public:
+ sal_Int32 nSpId; //Shape Identifier. Used in conjunction with the office art data (found via fcDggInfo in the FIB) to find the actual data for this shape.
+ sal_Int32 nXaLeft; //left of rectangle enclosing shape relative to the origin of the shape
+ sal_Int32 nYaTop; //top of rectangle enclosing shape relative to the origin of the shape
+ sal_Int32 nXaRight; //right of rectangle enclosing shape relative to the origin of the shape
+ sal_Int32 nYaBottom;//bottom of the rectangle enclosing shape relative to the origin of the shape
+ sal_uInt16 bHdr:1;
+ //0001 1 in the undo doc when shape is from the header doc, 0 otherwise (undefined when not in the undo doc)
+ sal_uInt16 nbx:2;
+ //0006 x position of shape relative to anchor CP
+ //0 relative to page margin
+ //1 relative to top of page
+ //2 relative to text (column for horizontal text; paragraph for vertical text)
+ //3 reserved for future use
+ sal_uInt16 nby:2;
+ //0018 y position of shape relative to anchor CP
+ //0 relative to page margin
+ //1 relative to top of page
+ //2 relative to text (paragraph for horizontal text; column for vertical text)
+ sal_uInt16 nwr:4;
+ //01E0 text wrapping mode
+ //0 like 2, but doesn't require absolute object
+ //1 no text next to shape
+ //2 wrap around absolute object
+ //3 wrap as if no object present
+ //4 wrap tightly around object
+ //5 wrap tightly, but allow holes
+ //6-15 reserved for future use
+ sal_uInt16 nwrk:4;
+ //1E00 text wrapping mode type (valid only for wrapping modes 2 and 4
+ //0 wrap both sides
+ //1 wrap only on left
+ //2 wrap only on right
+ //3 wrap only on largest side
+ sal_uInt16 bRcaSimple:1;
+ //2000 when set, temporarily overrides bx, by, forcing the xaLeft, xaRight, yaTop, and yaBottom fields to all be page relative.
+ sal_uInt16 bBelowText:1;
+ //4000
+ //1 shape is below text
+ //0 shape is above text
+ sal_uInt16 bAnchorLock:1;
+ //8000 1 anchor is locked
+ // 0 anchor is not locked
+ sal_Int32 nTxbx; //count of textboxes in shape (undo doc only)
+public:
+ enum FSPAOrient {RelPgMargin, RelPageBorder, RelText};
+};
+
+struct WW8_FSPA_SHADOW // all members at same position and size
+{ // due to: pF = (WW8_FSPA*)pFS;
+ SVBT32 nSpId;
+ SVBT32 nXaLeft;
+ SVBT32 nYaTop;
+ SVBT32 nXaRight;
+ SVBT32 nYaBottom;
+ SVBT16 aBits1;
+ SVBT32 nTxbx;
+};
+
+static_assert(sizeof (WW8_FSPA_SHADOW) == 26, "this has to match the msword size");
+ // "26": cf. WW8ScannerBase ctor case 8 creation of pMainFdoa and pHdFtFdoa
+
+struct WW8_TXBXS
+{
+ SVBT32 cTxbx_iNextReuse;
+ SVBT32 cReusable;
+ SVBT16 fReusable;
+ SVBT32 reserved;
+ SVBT32 ShapeId;
+ SVBT32 txidUndo;
+};
+
+struct WW8_STRINGID
+{
+ // M.M. This is the extra data stored in the SttbfFnm
+ // For now I only need the String Id
+ SVBT16 nStringId;
+ SVBT16 reserved1;
+ SVBT16 reserved2;
+ SVBT16 reserved3;
+};
+
+struct WW8_WKB
+{
+ // M.M. This is the WkbPLCF struct
+ // For now I only need the Link Id
+ SVBT16 reserved1;
+ SVBT16 reserved2;
+ SVBT16 reserved3;
+ SVBT16 nLinkId;
+ SVBT16 reserved4;
+ SVBT16 reserved5;
+};
+
+#ifdef _WIN32
+# pragma pack(pop)
+#endif
+
+// Maximum number of columns according the WW8 specification
+const sal_uInt8 MAX_NO_OF_SEP_COLUMNS = 44;
+
+struct SEPr
+{
+ SEPr();
+ sal_uInt8 bkc;
+ sal_uInt8 fTitlePage;
+ sal_Int8 fAutoPgn;
+ sal_uInt8 nfcPgn;
+ sal_uInt8 fUnlocked;
+ sal_uInt8 cnsPgn;
+ sal_uInt8 fPgnRestart;
+ sal_uInt8 fEndNote;
+ sal_Int8 lnc;
+ sal_Int8 grpfIhdt;
+ sal_uInt16 nLnnMod;
+ sal_Int32 dxaLnn;
+ sal_Int16 dxaPgn;
+ sal_Int16 dyaPgn;
+ sal_Int8 fLBetween;
+ sal_Int8 vjc;
+ sal_uInt16 dmBinFirst;
+ sal_uInt16 dmBinOther;
+ sal_uInt16 dmPaperReq;
+/*
+ 28 1C brcTop BRC top page border
+
+ 32 20 brcLeft BRC left page border
+
+ 36 24 brcBottom BRC bottom page border
+
+ 40 28 brcRight BRC right page border
+*/
+ sal_Int16 fPropRMark;
+ sal_Int16 ibstPropRMark;
+ sal_Int32 dttmPropRMark; //DTTM
+ sal_Int32 dxtCharSpace;
+ sal_Int32 dyaLinePitch;
+ sal_uInt16 clm;
+ sal_Int16 reserved1;
+ sal_uInt8 dmOrientPage;
+ sal_uInt8 iHeadingPgn;
+ sal_uInt16 pgnStart;
+ sal_Int16 lnnMin;
+ sal_uInt16 wTextFlow;
+ sal_Int16 reserved2;
+ sal_uInt16 pgbApplyTo:3;
+ sal_uInt16 pgbPageDepth:2;
+ sal_Int16 pgbOffsetFrom:3;
+ sal_Int16 :8;
+ sal_uInt32 xaPage;
+ sal_uInt32 yaPage;
+ sal_uInt32 xaPageNUp;
+ sal_uInt32 yaPageNUp;
+ sal_uInt32 dxaLeft;
+ sal_uInt32 dxaRight;
+ sal_Int32 dyaTop;
+ sal_Int32 dyaBottom;
+ sal_uInt32 dzaGutter;
+ sal_uInt32 dyaHdrTop;
+ sal_uInt32 dyaHdrBottom;
+ sal_Int16 ccolM1; // have to be less than MAX_NO_OF_SEP_COLUMNS according the WW8 specification
+ sal_Int8 fEvenlySpaced;
+ sal_Int8 reserved3;
+ sal_uInt8 fBiDi;
+ sal_uInt8 fFacingCol;
+ sal_uInt8 fRTLGutter;
+ sal_uInt8 fRTLAlignment;
+ sal_Int32 dxaColumns;
+
+ // Fixed array - two entries for each SEP column to store width of column and spacing to next column.
+ // At odd index values [1,3,5,...] the column widths are stored.
+ // At even index values [2,4,6,...] the spacings to the next columns are stored.
+ // Value at index 0 is initialized with 0 and used for easier iteration on the array
+ sal_Int32 rgdxaColumnWidthSpacing[MAX_NO_OF_SEP_COLUMNS*2 + 1] = {};
+
+ sal_Int32 dxaColumnWidth;
+ sal_uInt8 dmOrientFirst;
+ sal_uInt8 fLayout;
+ sal_Int16 reserved4;
+};
+
+namespace wwUtility
+{
+ inline sal_uInt32 RGBToBGR(::Color nColour)
+ {
+ // we can use this because the translation is symmetric
+ return sal_uInt32(msfilter::util::BGRToRGB(sal_uInt32(nColour)));
+ }
+}
+
+/// [MS-OSHARED] FactoidType: one smart tag type.
+class MSOFactoidType
+{
+public:
+ MSOFactoidType();
+ void Read(SvStream& rStream);
+ void Write(WW8Export& rExport);
+
+ sal_uInt32 m_nId;
+ OUString m_aUri;
+ OUString m_aTag;
+};
+
+/// [MS-OSHARED] PropertyBagStore: smart tag types and string store.
+class MSOPropertyBagStore
+{
+public:
+ void Read(SvStream& rStream);
+ void Write(WW8Export& rExport);
+
+ std::vector<MSOFactoidType> m_aFactoidTypes;
+ std::vector<OUString> m_aStringTable;
+};
+
+/// [MS-OSHARED] Property: stores information about one smart-tag key/value.
+class MSOProperty
+{
+public:
+ MSOProperty();
+ void Read(SvStream& rStream);
+ void Write(SvStream& rStream);
+
+ /// Index into MSOPropertyBagStore::m_aStringTable.
+ sal_uInt32 m_nKey;
+ /// Index into MSOPropertyBagStore::m_aStringTable.
+ sal_uInt32 m_nValue;
+};
+
+/// [MS-OSHARED] PropertyBag: stores information about one smart tag.
+class MSOPropertyBag
+{
+public:
+ MSOPropertyBag();
+ bool Read(SvStream& rStream);
+ void Write(WW8Export& rExport);
+
+ /// Matches MSOFactoidType::m_nId in MSOPropertyBagStore::m_aFactoidTypes.
+ sal_uInt16 m_nId;
+ std::vector<MSOProperty> m_aProperties;
+};
+
+/// [MS-DOC] SmartTagData: stores information about all smart tags in the document.
+class WW8SmartTagData
+{
+public:
+ void Read(SvStream& rStream, WW8_FC fcFactoidData, sal_uInt32 lcbFactoidData);
+ void Write(WW8Export& rExport);
+
+ MSOPropertyBagStore m_aPropBagStore;
+ std::vector<MSOPropertyBag> m_aPropBags;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8toolbar.cxx b/sw/source/filter/ww8/ww8toolbar.cxx
new file mode 100644
index 0000000000..7281f0af0f
--- /dev/null
+++ b/sw/source/filter/ww8/ww8toolbar.cxx
@@ -0,0 +1,1004 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "ww8toolbar.hxx"
+#include "ww8scan.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <fstream>
+#include <comphelper/documentinfo.hxx>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <sfx2/objsh.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <unotools/configmgr.hxx>
+#include <rtl/ref.hxx>
+#include <map>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+
+// no. of visual data elements in a SwCTB ( fixed )
+const short nVisualData = 5;
+
+typedef std::map< sal_Int16, OUString > IdToString;
+
+namespace {
+
+class MSOWordCommandConvertor : public MSOCommandConvertor
+{
+ IdToString m_MSOToOOcmd;
+ IdToString m_TCIDToOOcmd;
+
+public:
+ MSOWordCommandConvertor();
+ virtual OUString MSOCommandToOOCommand( sal_Int16 msoCmd ) override;
+ virtual OUString MSOTCIDToOOCommand( sal_Int16 key ) override;
+};
+
+}
+
+MSOWordCommandConvertor::MSOWordCommandConvertor()
+{
+ // mso command id to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ m_MSOToOOcmd[ 0x20b ] = ".uno:CloseDoc";
+ m_MSOToOOcmd[ 0x50 ] = ".uno:Open";
+
+ // mso tcid to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ m_TCIDToOOcmd[ 0x9d9 ] = ".uno:Print";
+}
+
+OUString MSOWordCommandConvertor::MSOCommandToOOCommand( sal_Int16 key )
+{
+ IdToString::iterator it = m_MSOToOOcmd.find( key );
+ if ( it != m_MSOToOOcmd.end() )
+ return it->second;
+ return OUString();
+}
+
+OUString MSOWordCommandConvertor::MSOTCIDToOOCommand( sal_Int16 key )
+{
+ IdToString::iterator it = m_TCIDToOOcmd.find( key );
+ if ( it != m_TCIDToOOcmd.end() )
+ return it->second;
+ return OUString();
+}
+
+SwCTBWrapper::SwCTBWrapper() :
+m_reserved2(0)
+,m_reserved3(0)
+,m_reserved4(0)
+,m_reserved5(0)
+,m_cbTBD(0)
+,m_cCust(0)
+,m_cbDTBC(0)
+,m_rtbdc(0)
+{
+}
+
+SwCTBWrapper::~SwCTBWrapper()
+{
+}
+
+Customization* SwCTBWrapper::GetCustomizaton( sal_Int16 index )
+{
+ if ( index < 0 || o3tl::make_unsigned(index) >= m_rCustomizations.size() )
+ return nullptr;
+ return &m_rCustomizations[ index ];
+}
+
+SwCTB* SwCTBWrapper::GetCustomizationData( const OUString& sTBName )
+{
+ auto it = std::find_if(m_rCustomizations.begin(), m_rCustomizations.end(),
+ [&sTBName](Customization& rCustomization) {
+ SwCTB* pCTB = rCustomization.GetCustomizationData();
+ return pCTB && pCTB->GetName() == sTBName;
+ });
+ if (it != m_rCustomizations.end())
+ return it->GetCustomizationData();
+ return nullptr;
+}
+
+bool SwCTBWrapper::Read( SvStream& rS )
+{
+ SAL_INFO("sw.ww8","SwCTBWrapper::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ Tcg255SubStruct::Read( rS );
+ rS.ReadUInt16( m_reserved2 ).ReadUChar( m_reserved3 ).ReadUInt16( m_reserved4 ).ReadUInt16( m_reserved5 );
+ rS.ReadInt16( m_cbTBD ).ReadUInt16( m_cCust ).ReadInt32( m_cbDTBC );
+ sal_uInt64 nExpectedPos = rS.Tell() + m_cbDTBC;
+ if ( m_cbDTBC )
+ {
+ // cbDTBC is the size in bytes of the SwTBC array
+ // but the size of a SwTBC element is dynamic ( and this relates to TBDelta's
+ int nStart = rS.Tell();
+
+ int bytesToRead;
+ // cbDTBC specifies the size ( in bytes ) taken by an array ( of unspecified size )
+ // of SwTBC records ( SwTBC records have dynamic length, so we need to check our position
+ // after each read )
+ do
+ {
+ SwTBC aTBC;
+ if ( !aTBC.Read( rS ) )
+ return false;
+ m_rtbdc.push_back( aTBC );
+ bytesToRead = m_cbDTBC - ( rS.Tell() - nStart );
+ } while ( bytesToRead > 0 );
+ }
+ if ( rS.Tell() != nExpectedPos )
+ {
+ // Strange error condition, shouldn't happen ( but does in at least
+ // one test document ) In the case where it happens the SwTBC &
+ // TBCHeader records seem blank??? ( and incorrect )
+ SAL_WARN_IF( rS.Tell() != nExpectedPos, "sw.ww8","### Error: Expected pos not equal to actual pos after reading rtbdc");
+ SAL_INFO("sw.ww8","\tPos now is 0x" << std::hex << rS.Tell() << " should be 0x" << std::hex << nExpectedPos );
+ // seek to correct position after rtbdc
+ rS.Seek( nExpectedPos );
+ }
+ if (m_cCust)
+ {
+ //Each customization takes a min of 8 bytes
+ size_t nMaxPossibleRecords = rS.remainingSize() / 8;
+ if (m_cCust > nMaxPossibleRecords)
+ {
+ return false;
+ }
+ for (sal_uInt16 index = 0; index < m_cCust; ++index)
+ {
+ Customization aCust( this );
+ if ( !aCust.Read( rS ) )
+ return false;
+ m_rCustomizations.push_back( aCust );
+ }
+ }
+ for ( const auto& rIndex : m_dropDownMenuIndices )
+ {
+ if (rIndex < 0 || o3tl::make_unsigned(rIndex) >= m_rCustomizations.size())
+ continue;
+ m_rCustomizations[rIndex].m_bIsDroppedMenuTB = true;
+ }
+ return rS.good();
+}
+
+SwTBC* SwCTBWrapper::GetTBCAtOffset( sal_uInt32 nStreamOffset )
+{
+ auto it = std::find_if(m_rtbdc.begin(), m_rtbdc.end(),
+ [&nStreamOffset](SwTBC& rItem) { return rItem.GetOffset() == nStreamOffset; });
+ if ( it != m_rtbdc.end() )
+ return &(*it);
+ return nullptr;
+}
+
+bool SwCTBWrapper::ImportCustomToolBar( SfxObjectShell& rDocSh )
+{
+ for ( auto& rCustomization : m_rCustomizations )
+ {
+ try
+ {
+ css::uno::Reference<css::ui::XUIConfigurationManager> xCfgMgr;
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xAppCfgSupp( ui::theModuleUIConfigurationManagerSupplier::get(xContext) );
+ xCfgMgr = xAppCfgSupp->getUIConfigurationManager("com.sun.star.text.TextDocument");
+ }
+ CustomToolBarImportHelper helper(rDocSh, xCfgMgr);
+ helper.setMSOCommandMap( new MSOWordCommandConvertor() );
+
+ if ( !rCustomization.ImportCustomToolBar( *this, helper ) )
+ return false;
+ }
+ catch (...)
+ {
+ continue;
+ }
+ }
+ return true;
+}
+
+Customization::Customization( SwCTBWrapper* wrapper )
+ : m_tbidForTBD( 0 )
+ , m_reserved1( 0 )
+ , m_ctbds( 0 )
+ , m_pWrapper( wrapper )
+ , m_bIsDroppedMenuTB( false )
+{
+}
+
+bool Customization::Read( SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Customization::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadInt32( m_tbidForTBD ).ReadUInt16( m_reserved1 ).ReadUInt16( m_ctbds );
+ if ( m_tbidForTBD )
+ {
+ //each TBDelta is at least 18 bytes in size
+ size_t nMaxAvailableRecords = rS.remainingSize() / 18;
+ if (m_ctbds > nMaxAvailableRecords)
+ return false;
+ for (sal_uInt16 index = 0; index < m_ctbds; ++index)
+ {
+ TBDelta aTBDelta;
+ if (!aTBDelta.Read( rS ) )
+ return false;
+ m_customizationDataTBDelta.push_back( aTBDelta );
+ // Only set the drop down for menus associated with standard toolbar
+ if ( aTBDelta.ControlDropsToolBar() && m_tbidForTBD == 0x25 )
+ m_pWrapper->InsertDropIndex( aTBDelta.CustomizationIndex() );
+ }
+ }
+ else
+ {
+ m_customizationDataCTB = std::make_shared<SwCTB>();
+ if ( !m_customizationDataCTB->Read( rS ) )
+ return false;
+ }
+ return rS.good();
+}
+
+bool Customization::ImportMenu( SwCTBWrapper& rWrapper, CustomToolBarImportHelper& helper )
+{
+ if ( m_tbidForTBD == 0x25 ) // we can handle in a limited way additions the built-in menu bar
+ {
+ for ( auto& rTBDelta : m_customizationDataTBDelta )
+ {
+ // for each new menu ( control that drops a toolbar )
+ // import a toolbar
+ if ( rTBDelta.ControlIsInserted() && rTBDelta.ControlDropsToolBar() )
+ {
+ Customization* pCust = m_pWrapper->GetCustomizaton( rTBDelta.CustomizationIndex() );
+ if ( pCust )
+ {
+ // currently only support built-in menu
+ static constexpr OUString sMenuBar( u"private:resource/menubar/menubar"_ustr );
+
+ // Get menu name
+ SwTBC* pTBC = m_pWrapper->GetTBCAtOffset( rTBDelta.TBCStreamOffset() );
+ if ( !pTBC )
+ return false;
+ const OUString sMenuName = pTBC->GetCustomText().replace('&','~');
+
+ // see if the document has already setting for the menubar
+
+ uno::Reference< container::XIndexContainer > xIndexContainer;
+ bool bHasSettings = false;
+ if ( helper.getCfgManager()->hasSettings( sMenuBar ) )
+ {
+ xIndexContainer.set( helper.getCfgManager()->getSettings( sMenuBar, true ), uno::UNO_QUERY_THROW );
+ bHasSettings = true;
+ }
+ else
+ {
+ if ( helper.getAppCfgManager()->hasSettings( sMenuBar ) )
+ xIndexContainer.set( helper.getAppCfgManager()->getSettings( sMenuBar, true ), uno::UNO_QUERY_THROW );
+ else
+ xIndexContainer.set( helper.getAppCfgManager()->createSettings(), uno::UNO_SET_THROW );
+ }
+
+ uno::Reference< lang::XSingleComponentFactory > xSCF( xIndexContainer, uno::UNO_QUERY_THROW );
+ uno::Reference< uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+ uno::Reference< container::XIndexContainer > xMenuContainer( xSCF->createInstanceWithContext( xContext ), uno::UNO_QUERY_THROW );
+ // create the popup menu
+ uno::Sequence< beans::PropertyValue > aPopupMenu{
+ comphelper::makePropertyValue("CommandURL", "vnd.openoffice.org:" + sMenuName),
+ comphelper::makePropertyValue("Label", sMenuName),
+ comphelper::makePropertyValue("Type", sal_Int32( 0 )),
+ comphelper::makePropertyValue("ItemDescriptorContainer", xMenuContainer)
+ };
+ if ( pCust->m_customizationDataCTB && !pCust->m_customizationDataCTB->ImportMenuTB( rWrapper, xMenuContainer, helper ) )
+ return false;
+ SAL_INFO("sw.ww8","** there are " << xIndexContainer->getCount() << " menu items on the bar, inserting after that");
+ xIndexContainer->insertByIndex( xIndexContainer->getCount(), uno::Any( aPopupMenu ) );
+
+ if ( bHasSettings )
+ helper.getCfgManager()->replaceSettings( sMenuBar, uno::Reference< container::XIndexAccess >( xIndexContainer, uno::UNO_QUERY_THROW ) );
+ else
+ helper.getCfgManager()->insertSettings( sMenuBar, uno::Reference< container::XIndexAccess >( xIndexContainer, uno::UNO_QUERY_THROW ) );
+
+ uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+ }
+ }
+ }
+ return true;
+ }
+ return true;
+}
+
+bool Customization::ImportCustomToolBar( SwCTBWrapper& rWrapper, CustomToolBarImportHelper& helper )
+{
+ if ( m_tbidForTBD == 0x25 )
+ return ImportMenu( rWrapper, helper );
+ if ( !m_customizationDataCTB )
+ return false;
+ if ( !m_customizationDataCTB->IsMenuToolbar() )
+ {
+ if ( !m_customizationDataCTB->ImportCustomToolBar( rWrapper, helper ) )
+ return false;
+ }
+ return true;
+}
+
+TBDelta::TBDelta()
+ : m_doprfatendFlags(0)
+ , m_ibts(0)
+ , m_cidNext(0)
+ , m_cid(0)
+ , m_fc(0)
+ , m_CiTBDE(0)
+ , m_cbTBC(0)
+{
+}
+
+bool TBDelta::ControlIsInserted()
+{
+ return ( ( m_doprfatendFlags & 0x3 ) == 0x1 );
+}
+
+bool TBDelta::ControlDropsToolBar()
+{
+ return !( m_CiTBDE & 0x8000 );
+}
+
+
+sal_Int16 TBDelta::CustomizationIndex()
+{
+ sal_Int16 nIndex = m_CiTBDE;
+ nIndex = nIndex >> 1;
+ nIndex &= 0x1ff; // only 13 bits are relevant
+ return nIndex;
+}
+
+bool TBDelta::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","TBDelta::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadUChar( m_doprfatendFlags ).ReadUChar( m_ibts ).ReadInt32( m_cidNext ).ReadInt32( m_cid ).ReadInt32( m_fc ) ;
+ rS.ReadUInt16( m_CiTBDE ).ReadUInt16( m_cbTBC );
+ return rS.good();
+}
+
+SwCTB::SwCTB() : m_cbTBData( 0 )
+,m_iWCTBl( 0 )
+,m_reserved( 0 )
+,m_unused( 0 )
+,m_cCtls( 0 )
+{
+}
+
+SwCTB::~SwCTB()
+{
+}
+
+bool SwCTB::IsMenuToolbar() const
+{
+ return m_tb.IsMenuToolbar();
+}
+
+bool SwCTB::Read( SvStream &rS)
+{
+ SAL_INFO("sw.ww8","SwCTB::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ if ( !m_name.Read( rS ) )
+ return false;
+ rS.ReadInt32( m_cbTBData );
+ if ( !m_tb.Read( rS ) )
+ return false;
+ for ( short index = 0; index < nVisualData; ++index )
+ {
+ TBVisualData aVisData;
+ aVisData.Read( rS );
+ m_rVisualData.push_back( aVisData );
+ }
+
+ rS.ReadInt32( m_iWCTBl ).ReadUInt16( m_reserved ).ReadUInt16( m_unused ).ReadInt32( m_cCtls );
+
+ if ( m_cCtls )
+ {
+ for ( sal_Int32 index = 0; index < m_cCtls; ++index )
+ {
+ SwTBC aTBC;
+ if ( !aTBC.Read( rS ) )
+ return false;
+ m_rTBC.push_back( aTBC );
+ }
+ }
+ return rS.good();
+}
+
+bool SwCTB::ImportCustomToolBar( SwCTBWrapper& rWrapper, CustomToolBarImportHelper& helper )
+{
+ bool bRes = false;
+ try
+ {
+ if ( !m_tb.IsEnabled() )
+ return true; // didn't fail, just ignoring
+ // Create default setting
+ uno::Reference< container::XIndexContainer > xIndexContainer( helper.getCfgManager()->createSettings(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xIndexContainer, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xIndexContainer, uno::UNO_QUERY_THROW );
+
+ // set UI name for toolbar
+ xProps->setPropertyValue( "UIName", uno::Any( m_name.getString() ) );
+
+ const OUString sToolBarName = "private:resource/toolbar/custom_" + m_name.getString();
+ for ( auto& rItem : m_rTBC )
+ {
+ // createToolBar item for control
+ if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, helper, IsMenuToolbar() ) )
+ return false;
+ }
+
+ SAL_INFO("sw.ww8","Name of toolbar :-/ " << sToolBarName );
+
+ helper.getCfgManager()->insertSettings( sToolBarName, xIndexAccess );
+ helper.applyIcons();
+#if 1 // don't think this is necessary
+ uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager()->getImageManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ xPersistence.set( helper.getCfgManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+#endif
+ bRes = true;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_INFO_EXCEPTION("sw.ww8","***** For some reason we have an" );
+ bRes = false;
+ }
+ return bRes;
+}
+
+bool SwCTB::ImportMenuTB( SwCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& xIndexContainer, CustomToolBarImportHelper& rHelper )
+{
+ for ( auto& rItem : m_rTBC )
+ {
+ // createToolBar item for control
+ if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, rHelper, true ) )
+ return false;
+ }
+ return true;
+}
+
+SwTBC::SwTBC()
+{
+}
+
+bool SwTBC::Read( SvStream &rS )
+{
+ SAL_INFO("sw.ww8","SwTBC::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ if ( !m_tbch.Read( rS ) )
+ return false;
+ if ( m_tbch.getTcID() != 0x1 && m_tbch.getTcID() != 0x1051 )
+ {
+ m_cid = std::make_shared<sal_uInt32>();
+ rS.ReadUInt32( *m_cid );
+ }
+ // MUST exist if tbch.tct is not equal to 0x16
+ if ( m_tbch.getTct() != 0x16 )
+ {
+ m_tbcd = std::make_shared<TBCData>( m_tbch );
+ if ( !m_tbcd->Read( rS ) )
+ return false;
+ }
+ return rS.good();
+}
+
+bool
+SwTBC::ImportToolBarControl( SwCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuBar )
+{
+ // cmtFci 0x1 Command based on a built-in command. See CidFci.
+ // cmtMacro 0x2 Macro command. See CidMacro.
+ // cmtAllocated 0x3 Allocated command. See CidAllocated.
+ // cmtNil 0x7 No command. See Cid.
+ bool bBuiltin = false;
+ sal_Int16 cmdId = 0;
+ if ( m_cid )
+ {
+ const sal_uInt32 nCid = ( *m_cid & 0xFFFF );
+
+ const sal_uInt8 cmt = static_cast<sal_uInt8>( nCid & 0x7 );
+ const sal_Int16 arg2 = static_cast<sal_Int16>( nCid >> 3 );
+
+ switch ( cmt )
+ {
+ case 1:
+ SAL_INFO("sw.ww8","cmt is cmtFci builtin command 0x" << std::hex << arg2);
+ bBuiltin = true;
+ cmdId = arg2;
+ break;
+ case 2:
+ SAL_INFO("sw.ww8","cmt is cmtMacro macro 0x" << std::hex << arg2);
+ break;
+ case 3:
+ SAL_INFO("sw.ww8","cmt is cmtAllocated [???] 0x" << std::hex << arg2);
+ break;
+ case 7:
+ SAL_INFO("sw.ww8","cmt is cmNill no-thing 0x" << std::hex << arg2);
+ break;
+ default:
+ SAL_INFO("sw.ww8","illegal 0x" << std::hex << cmt);
+ break;
+ }
+ }
+
+ if ( m_tbcd )
+ {
+ std::vector< css::beans::PropertyValue > props;
+ if ( bBuiltin )
+ {
+ const OUString sCommand = helper.MSOCommandToOOCommand( cmdId );
+ if ( !sCommand.isEmpty() )
+ {
+ beans::PropertyValue aProp;
+
+ aProp.Name = "CommandURL";
+ aProp.Value <<= sCommand;
+ props.push_back( aProp );
+ }
+ }
+ bool bBeginGroup = false;
+ m_tbcd->ImportToolBarControl( helper, props, bBeginGroup, bIsMenuBar );
+
+ TBCMenuSpecific* pMenu = m_tbcd->getMenuSpecific();
+ if ( pMenu )
+ {
+ SAL_INFO("sw.ww8","** control has a menu, name of toolbar with menu items is " << pMenu->Name() );
+ // search for SwCTB with the appropriate name ( it contains the
+ // menu items, although we cannot import ( or create ) a menu on
+ // a custom toolbar we can import the menu items in a separate
+ // toolbar ( better than nothing )
+ SwCTB* pCustTB = rWrapper.GetCustomizationData( pMenu->Name() );
+ if ( pCustTB )
+ {
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xMenuDesc = new comphelper::IndexedPropertyValuesContainer();
+ if ( !pCustTB->ImportMenuTB( rWrapper,xMenuDesc, helper ) )
+ return false;
+ if ( !bIsMenuBar )
+ {
+ if ( !helper.createMenu( pMenu->Name(), xMenuDesc ) )
+ return false;
+ }
+ else
+ {
+ beans::PropertyValue aProp;
+ aProp.Name = "ItemDescriptorContainer";
+ aProp.Value <<= uno::Reference< container::XIndexContainer >(xMenuDesc);
+ props.push_back( aProp );
+ }
+ }
+ }
+
+ if ( bBeginGroup )
+ {
+ // insert spacer
+ uno::Sequence< beans::PropertyValue > sProps{ comphelper::makePropertyValue(
+ "Type", ui::ItemType::SEPARATOR_LINE) };
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( sProps ) );
+ }
+
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( comphelper::containerToSequence(props) ) );
+ }
+ return true;
+}
+
+OUString
+SwTBC::GetCustomText()
+{
+ if ( m_tbcd )
+ return m_tbcd->getGeneralInfo().CustomText();
+ return OUString();
+}
+
+bool
+Xst::Read( SvStream& rS )
+{
+ SAL_INFO("sw.ww8","Xst::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ m_sString = read_uInt16_PascalString(rS);
+ return rS.good();
+}
+
+Tcg::Tcg() : m_nTcgVer( -1 )
+{
+}
+
+bool Tcg::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Tcg::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadSChar( m_nTcgVer );
+ if ( m_nTcgVer != -1 )
+ return false;
+ m_tcg.reset( new Tcg255() );
+ return m_tcg->Read( rS );
+}
+
+bool Tcg::ImportCustomToolBar( SfxObjectShell& rDocSh )
+{
+ if (m_tcg)
+ return m_tcg->ImportCustomToolBar( rDocSh );
+ return false;
+}
+
+Tcg255::Tcg255()
+{
+}
+
+Tcg255::~Tcg255()
+{
+}
+
+bool Tcg255::processSubStruct( sal_uInt8 nId, SvStream &rS )
+{
+ std::unique_ptr<Tcg255SubStruct> xSubStruct;
+ switch ( nId )
+ {
+ case 0x1:
+ {
+ xSubStruct.reset(new PlfMcd);
+ break;
+ }
+ case 0x2:
+ {
+ xSubStruct.reset(new PlfAcd);
+ break;
+ }
+ case 0x3:
+ case 0x4:
+ {
+ xSubStruct.reset(new PlfKme);
+ break;
+ }
+ case 0x10:
+ {
+ xSubStruct.reset(new TcgSttbf);
+ break;
+ }
+ case 0x11:
+ {
+ xSubStruct.reset(new MacroNames);
+ break;
+ }
+ case 0x12:
+ {
+ xSubStruct.reset(new SwCTBWrapper);
+ break;
+ }
+ default:
+ SAL_INFO("sw.ww8","Unknown id 0x" << std::hex << nId);
+ return false;
+ }
+ xSubStruct->m_ch = nId;
+ if (!xSubStruct->Read(rS))
+ return false;
+ m_rgtcgData.push_back(std::move(xSubStruct));
+ return true;
+}
+
+bool Tcg255::ImportCustomToolBar( SfxObjectShell& rDocSh )
+{
+ // Find the SwCTBWrapper
+ for ( const auto & rSubStruct : m_rgtcgData )
+ {
+ if ( rSubStruct->id() == 0x12 )
+ {
+ // not so great, shouldn't really have to do a horror casting
+ SwCTBWrapper* pCTBWrapper = dynamic_cast< SwCTBWrapper* > ( rSubStruct.get() );
+ if ( pCTBWrapper )
+ {
+ // tdf#127048 set this flag if we might import something
+ uno::Reference<frame::XModel> const xModel(rDocSh.GetBaseModel());
+ comphelper::DocumentInfo::notifyMacroEventRead(xModel);
+
+ if ( !pCTBWrapper->ImportCustomToolBar( rDocSh ) )
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool Tcg255::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Tcg255::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ sal_uInt8 nId = 0x40;
+ rS.ReadUChar( nId );
+ while ( nId != 0x40 )
+ {
+ if ( !processSubStruct( nId, rS ) )
+ return false;
+ nId = 0x40;
+ rS.ReadUChar( nId );
+ }
+ return rS.good();
+ // Peek at
+}
+
+Tcg255SubStruct::Tcg255SubStruct( ) : m_ch(0)
+{
+}
+
+bool Tcg255SubStruct::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Tcg255SubStruct::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ return rS.good();
+}
+
+PlfMcd::PlfMcd()
+ : m_iMac(0)
+{
+}
+
+bool PlfMcd::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","PffMcd::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ Tcg255SubStruct::Read( rS );
+ rS.ReadInt32( m_iMac );
+ if (m_iMac < 0)
+ return false;
+ auto nMaxPossibleRecords = rS.remainingSize() / 24 /*sizeof MCD*/;
+ if (o3tl::make_unsigned(m_iMac) > nMaxPossibleRecords)
+ {
+ SAL_WARN("sw.ww8", m_iMac << " records claimed, but max possible is " << nMaxPossibleRecords);
+ m_iMac = nMaxPossibleRecords;
+ }
+ if (m_iMac)
+ {
+ m_rgmcd.resize(m_iMac);
+ for ( sal_Int32 index = 0; index < m_iMac; ++index )
+ {
+ if ( !m_rgmcd[ index ].Read( rS ) )
+ return false;
+ }
+ }
+ return rS.good();
+}
+
+PlfAcd::PlfAcd() :
+ m_iMac(0)
+{
+}
+
+PlfAcd::~PlfAcd()
+{
+}
+
+bool PlfAcd::Read( SvStream &rS)
+{
+ SAL_INFO("sw.ww8","PffAcd::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ Tcg255SubStruct::Read( rS );
+ rS.ReadInt32( m_iMac );
+ if (m_iMac < 0)
+ return false;
+ auto nMaxPossibleRecords = rS.remainingSize() / (sizeof(sal_uInt16)*2);
+ if (o3tl::make_unsigned(m_iMac) > nMaxPossibleRecords)
+ {
+ SAL_WARN("sw.ww8", m_iMac << " records claimed, but max possible is " << nMaxPossibleRecords);
+ m_iMac = nMaxPossibleRecords;
+ }
+ if (m_iMac)
+ {
+ m_rgacd.reset( new Acd[ m_iMac ] );
+ for ( sal_Int32 index = 0; index < m_iMac; ++index )
+ {
+ if ( !m_rgacd[ index ].Read( rS ) )
+ return false;
+ }
+ }
+ return rS.good();
+}
+
+PlfKme::PlfKme() :
+ m_iMac( 0 )
+{
+}
+
+PlfKme::~PlfKme()
+{
+}
+
+bool PlfKme::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","PlfKme::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ Tcg255SubStruct::Read( rS );
+ rS.ReadInt32( m_iMac );
+ if (m_iMac > 0)
+ {
+ //each Kme is 14 bytes in size
+ size_t nMaxAvailableRecords = rS.remainingSize() / 14;
+ if (o3tl::make_unsigned(m_iMac) > nMaxAvailableRecords)
+ return false;
+
+ m_rgkme.reset( new Kme[ m_iMac ] );
+ for( sal_Int32 index=0; index<m_iMac; ++index )
+ {
+ if ( !m_rgkme[ index ].Read( rS ) )
+ return false;
+ }
+ }
+ return rS.good();
+}
+
+TcgSttbf::TcgSttbf()
+{
+}
+
+bool TcgSttbf::Read( SvStream &rS)
+{
+ SAL_INFO("sw.ww8","TcgSttbf::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ Tcg255SubStruct::Read( rS );
+ return m_sttbf.Read( rS );
+}
+
+TcgSttbfCore::TcgSttbfCore() : m_fExtend( 0 )
+,m_cData( 0 )
+,m_cbExtra( 0 )
+{
+}
+
+TcgSttbfCore::~TcgSttbfCore()
+{
+}
+
+bool TcgSttbfCore::Read( SvStream& rS )
+{
+ SAL_INFO("sw.ww8","TcgSttbfCore::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadUInt16( m_fExtend ).ReadUInt16( m_cData ).ReadUInt16( m_cbExtra );
+ if ( m_cData )
+ {
+ if (m_cData > rS.remainingSize() / 4) //definitely an invalid record
+ return false;
+ m_dataItems.reset( new SBBItem[ m_cData ] );
+ for ( sal_Int32 index = 0; index < m_cData; ++index )
+ {
+ rS.ReadUInt16( m_dataItems[ index ].cchData );
+ m_dataItems[ index ].data = read_uInt16s_ToOUString(rS, m_dataItems[index].cchData);
+ rS.ReadUInt16( m_dataItems[ index ].extraData );
+ }
+ }
+ return rS.good();
+}
+
+MacroNames::MacroNames() :
+ m_iMac( 0 )
+{
+}
+
+MacroNames::~MacroNames()
+{
+}
+
+bool MacroNames::Read( SvStream &rS)
+{
+ SAL_INFO("sw.ww8","MacroNames::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ Tcg255SubStruct::Read( rS );
+ rS.ReadUInt16( m_iMac );
+ if ( m_iMac )
+ {
+ //even an empty MacroName will take 2 bytes
+ size_t nMaxAvailableRecords = rS.remainingSize()/sizeof(sal_uInt16);
+ if (m_iMac > nMaxAvailableRecords)
+ return false;
+ m_rgNames.reset( new MacroName[ m_iMac ] );
+ for ( sal_Int32 index = 0; index < m_iMac; ++index )
+ {
+ if ( !m_rgNames[ index ].Read( rS ) )
+ return false;
+ }
+ }
+ return rS.good();
+}
+
+MacroName::MacroName():m_ibst(0)
+{
+}
+
+bool MacroName::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","MacroName::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadUInt16( m_ibst );
+ return m_xstz.Read( rS );
+}
+
+Xstz::Xstz():m_chTerm(0)
+{
+}
+
+bool
+Xstz::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Xstz::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ if ( !m_xst.Read( rS ) )
+ return false;
+ rS.ReadUInt16( m_chTerm );
+ if ( m_chTerm != 0 ) // should be an assert
+ return false;
+ return rS.good();
+}
+
+Kme::Kme() : m_reserved1(0)
+,m_reserved2(0)
+,m_kcm1(0)
+,m_kcm2(0)
+,m_kt(0)
+,m_param(0)
+{
+}
+
+Kme::~Kme()
+{
+}
+
+bool
+Kme::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Kme::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadInt16( m_reserved1 ).ReadInt16( m_reserved2 ).ReadUInt16( m_kcm1 ).ReadUInt16( m_kcm2 ).ReadUInt16( m_kt ).ReadUInt32( m_param );
+ return rS.good();
+}
+
+Acd::Acd() : m_ibst( 0 )
+, m_fciBasedOnABC( 0 )
+{
+}
+
+bool Acd::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","Acd::Read() stream pos 0x" << std::hex << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadInt16( m_ibst ).ReadUInt16( m_fciBasedOnABC );
+ return rS.good();
+}
+
+MCD::MCD() : m_reserved1(0x56)
+,m_reserved2( 0 )
+,m_ibst( 0 )
+,m_ibstName( 0 )
+,m_reserved3( 0xFFFF )
+,m_reserved4( 0 )
+,m_reserved5( 0 )
+,m_reserved6( 0 )
+,m_reserved7( 0 )
+{
+}
+
+bool MCD::Read(SvStream &rS)
+{
+ SAL_INFO("sw.ww8","MCD::Read() stream pos 0x" << rS.Tell() );
+ nOffSet = rS.Tell();
+ rS.ReadSChar( m_reserved1 ).ReadUChar( m_reserved2 ).ReadUInt16( m_ibst ).ReadUInt16( m_ibstName ).ReadUInt16( m_reserved3 );
+ rS.ReadUInt32( m_reserved4 ).ReadUInt32( m_reserved5 ).ReadUInt32( m_reserved6 ).ReadUInt32( m_reserved7 );
+ return rS.good();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/ww8toolbar.hxx b/sw/source/filter/ww8/ww8toolbar.hxx
new file mode 100644
index 0000000000..2d6876744c
--- /dev/null
+++ b/sw/source/filter/ww8/ww8toolbar.hxx
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8TOOLBAR_HXX
+#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8TOOLBAR_HXX
+
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <filter/msfilter/mstoolbar.hxx>
+#include <memory>
+
+
+class SfxObjectShell;
+class SwCTBWrapper;
+
+class Xst : public TBBase
+{
+ OUString m_sString;
+
+public:
+ Xst(){}
+ bool Read(SvStream &rS) override;
+ const OUString& getString() const { return m_sString; }
+};
+
+class SwTBC : public TBBase
+{
+ TBCHeader m_tbch;
+ std::shared_ptr< sal_uInt32 > m_cid; // optional
+ std::shared_ptr<TBCData> m_tbcd;
+
+public:
+ SwTBC();
+ bool Read(SvStream &rS) override;
+ bool ImportToolBarControl( SwCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper&, bool );
+ OUString GetCustomText();
+};
+
+class SwCTB : public TBBase
+{
+ Xst m_name;
+ sal_Int32 m_cbTBData;
+ TB m_tb;
+ std::vector<TBVisualData> m_rVisualData;
+ sal_Int32 m_iWCTBl;
+ sal_uInt16 m_reserved;
+ sal_uInt16 m_unused;
+ sal_Int32 m_cCtls;
+ std::vector< SwTBC > m_rTBC;
+
+ SwCTB(const SwCTB&) = delete;
+ SwCTB& operator = ( const SwCTB&) = delete;
+
+public:
+ SwCTB();
+ virtual ~SwCTB() override;
+ bool Read(SvStream &rS) override;
+ bool IsMenuToolbar() const;
+ bool ImportCustomToolBar( SwCTBWrapper&, CustomToolBarImportHelper& );
+ bool ImportMenuTB( SwCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper& );
+ OUString const & GetName() { return m_tb.getName().getString(); }
+};
+
+class TBDelta : public TBBase
+{
+ sal_uInt8 m_doprfatendFlags;
+
+ sal_uInt8 m_ibts;
+ sal_Int32 m_cidNext;
+ sal_Int32 m_cid;
+ sal_Int32 m_fc;
+ sal_uInt16 m_CiTBDE; // careful of this ( endian matters etc. )
+ sal_uInt16 m_cbTBC;
+
+public:
+ TBDelta();
+ bool Read(SvStream &rS) override;
+ bool ControlIsInserted();
+ bool ControlDropsToolBar();
+ sal_Int32 TBCStreamOffset() { return m_fc;}
+ sal_Int16 CustomizationIndex();
+};
+
+class Tcg255SubStruct : public TBBase
+{
+ friend class Tcg255;
+
+ Tcg255SubStruct(const Tcg255SubStruct&) = delete;
+ Tcg255SubStruct& operator = ( const Tcg255SubStruct&) = delete;
+
+protected:
+ sal_uInt8 m_ch;
+
+public:
+ explicit Tcg255SubStruct();
+ sal_uInt8 id() const { return m_ch; }
+ bool Read(SvStream &rS) override;
+};
+
+class Customization : public TBBase
+{
+ friend class SwCTBWrapper;
+
+ sal_Int32 m_tbidForTBD;
+ sal_uInt16 m_reserved1;
+ sal_uInt16 m_ctbds;
+ SwCTBWrapper* m_pWrapper;
+ std::shared_ptr< SwCTB > m_customizationDataCTB;
+ std::vector< TBDelta > m_customizationDataTBDelta;
+ bool m_bIsDroppedMenuTB;
+
+public:
+ explicit Customization( SwCTBWrapper* rapper );
+ bool Read(SvStream &rS) override;
+ bool ImportCustomToolBar( SwCTBWrapper&, CustomToolBarImportHelper& );
+ bool ImportMenu( SwCTBWrapper&, CustomToolBarImportHelper& );
+ SwCTB* GetCustomizationData() { return m_customizationDataCTB.get(); };
+};
+
+class SwCTBWrapper : public Tcg255SubStruct
+{
+ // reserved1 is the ch field of Tcg255SubStruct
+ sal_uInt16 m_reserved2;
+ sal_uInt8 m_reserved3;
+ sal_uInt16 m_reserved4;
+ sal_uInt16 m_reserved5;
+
+ sal_Int16 m_cbTBD;
+ sal_uInt16 m_cCust;
+
+ sal_Int32 m_cbDTBC;
+
+ std::vector< SwTBC > m_rtbdc;
+ std::vector< Customization > m_rCustomizations; // array of Customizations
+ std::vector< sal_Int16 > m_dropDownMenuIndices; // array of indexes of Customization toolbars that are dropped by a menu
+ SwCTBWrapper(const SwCTBWrapper&) = delete;
+ SwCTBWrapper& operator = ( const SwCTBWrapper&) = delete;
+
+public:
+ explicit SwCTBWrapper();
+ virtual ~SwCTBWrapper() override;
+ void InsertDropIndex( sal_Int32 aIndex ) { m_dropDownMenuIndices.push_back( aIndex ); }
+ SwTBC* GetTBCAtOffset( sal_uInt32 nStreamOffset );
+ bool Read(SvStream &rS) override;
+ bool ImportCustomToolBar( SfxObjectShell& rDocSh );
+
+ Customization* GetCustomizaton( sal_Int16 index );
+ SwCTB* GetCustomizationData( const OUString& name );
+};
+
+class MCD : public TBBase
+{
+ sal_Int8 m_reserved1; // A signed integer that MUST be 0x56.
+ sal_uInt8 m_reserved2; // MUST be 0.
+ sal_uInt16 m_ibst; // Unsigned integer that specifies the name of the macro. Macro name is specified by MacroName.xstz of the MacroName entry in the MacroNames such that MacroName.ibst equals ibst. MacroNames MUST contain such an entry.
+ sal_uInt16 m_ibstName; // An unsigned integer that specifies the index into the Command String Table (TcgSttbf.sttbf) where the macro's name and arguments are specified.
+ sal_uInt16 m_reserved3; // An unsigned integer that MUST be 0xFFFF.
+ sal_uInt32 m_reserved4; //MUST be ignored.
+ sal_uInt32 m_reserved5; //MUST be 0.
+ sal_uInt32 m_reserved6; //MUST be ignored.
+ sal_uInt32 m_reserved7; //MUST be ignored
+
+public:
+ MCD();
+ bool Read(SvStream &rS) override;
+};
+
+class PlfMcd : public Tcg255SubStruct
+{
+ sal_Int32 m_iMac;
+ std::vector<MCD> m_rgmcd; // array of MCD's
+ PlfMcd(const PlfMcd&) = delete;
+ PlfMcd& operator = ( const PlfMcd&) = delete;
+
+public:
+ explicit PlfMcd();
+ bool Read(SvStream &rS) override;
+};
+
+class Acd : public TBBase
+{
+ sal_Int16 m_ibst;
+ sal_uInt16 m_fciBasedOnABC; // fciBasedOn(13 bits) A(1bit)B(1bit)C(1Bit)
+ Acd(const Acd&) = delete;
+ Acd& operator = ( const Acd&) = delete;
+
+public:
+ Acd();
+ bool Read(SvStream &rS) override;
+};
+
+class PlfAcd: public Tcg255SubStruct
+{
+ sal_Int32 m_iMac;
+ std::unique_ptr<Acd[]> m_rgacd;
+ PlfAcd(const PlfAcd&) = delete;
+ PlfAcd& operator = ( const PlfAcd&) = delete;
+
+public:
+ explicit PlfAcd();
+ virtual ~PlfAcd() override;
+ bool Read(SvStream &rS) override;
+};
+
+class Kme : public TBBase
+{
+ sal_Int16 m_reserved1; //MUST be zero.
+ sal_Int16 m_reserved2; //MUST be zero.
+ sal_uInt16 m_kcm1; //A Kcm that specifies the primary shortcut key.
+ sal_uInt16 m_kcm2; //A Kcm that specifies the secondary shortcut key, or 0x00FF if there is no secondary shortcut key.
+ sal_uInt16 m_kt; //A Kt that specifies the type of action to be taken when the key combination is pressed.
+ sal_uInt32 m_param; //The meaning of this field depends on the value of kt
+
+ Kme(const Kme&) = delete;
+ Kme& operator = ( const Kme&) = delete;
+
+public:
+ Kme();
+ virtual ~Kme() override;
+ bool Read(SvStream &rS) override;
+};
+
+class PlfKme : public Tcg255SubStruct
+{
+ sal_Int32 m_iMac;
+ std::unique_ptr<Kme[]> m_rgkme;
+ PlfKme(const PlfKme&) = delete;
+ PlfKme& operator = ( const PlfKme&) = delete;
+
+public:
+ explicit PlfKme();
+ virtual ~PlfKme() override;
+ bool Read(SvStream &rS) override;
+};
+
+class TcgSttbfCore : public TBBase
+{
+ struct SBBItem
+ {
+ sal_uInt16 cchData;
+ OUString data;
+ sal_uInt16 extraData;
+ SBBItem() : cchData(0), extraData(0){}
+ };
+
+ sal_uInt16 m_fExtend;
+ sal_uInt16 m_cData;
+ sal_uInt16 m_cbExtra;
+ std::unique_ptr<SBBItem[]> m_dataItems;
+ TcgSttbfCore(const TcgSttbfCore&) = delete;
+ TcgSttbfCore& operator = ( const TcgSttbfCore&) = delete;
+
+public:
+ TcgSttbfCore();
+ virtual ~TcgSttbfCore() override;
+ bool Read(SvStream &rS) override;
+};
+
+class TcgSttbf : public Tcg255SubStruct
+{
+ TcgSttbfCore m_sttbf;
+ TcgSttbf(const TcgSttbf&) = delete;
+ TcgSttbf& operator = ( const TcgSttbf&) = delete;
+
+public:
+ explicit TcgSttbf();
+ bool Read(SvStream &rS) override;
+};
+
+class Xstz : public TBBase
+{
+ Xst m_xst; //An Xst specifying the string with its pre-pended length.
+ sal_uInt16 m_chTerm;
+
+ Xstz(const Xstz&) = delete;
+ Xstz& operator = ( const Xstz&) = delete;
+
+public:
+ Xstz();
+ bool Read(SvStream &rS) override;
+};
+
+class MacroName : public TBBase
+{
+ sal_uInt16 m_ibst; //An unsigned integer that specifies the index of the current entry in the macro name table. MUST NOT be the same as the index of any other entry.
+ Xstz m_xstz;
+ MacroName(const MacroName&) = delete;
+ MacroName& operator = ( const MacroName&) = delete;
+
+public:
+ MacroName();
+ bool Read(SvStream &rS) override;
+};
+
+class MacroNames : public Tcg255SubStruct
+{
+ sal_uInt16 m_iMac; //An unsigned integer that specifies the number of MacroName structures in rgNames.
+ std::unique_ptr<MacroName[]> m_rgNames;
+
+ MacroNames(const MacroNames&) = delete;
+ MacroNames& operator = ( const MacroNames&) = delete;
+
+public:
+ explicit MacroNames();
+ virtual ~MacroNames() override;
+ bool Read(SvStream &rS) override;
+};
+
+class Tcg255 : public TBBase
+{
+ std::vector< std::unique_ptr<Tcg255SubStruct> > m_rgtcgData; // array of sub structures
+ Tcg255(const Tcg255&) = delete;
+ Tcg255& operator = ( const Tcg255&) = delete;
+ bool processSubStruct( sal_uInt8 nId, SvStream& );
+
+public:
+ Tcg255();
+ virtual ~Tcg255() override;
+ bool Read(SvStream &rS) override;
+ bool ImportCustomToolBar( SfxObjectShell& rDocSh );
+};
+
+class Tcg: public TBBase
+{
+ sal_Int8 m_nTcgVer;
+ std::unique_ptr< Tcg255 > m_tcg;
+ Tcg(const Tcg&) = delete;
+ Tcg& operator = ( const Tcg&) = delete;
+
+public:
+ Tcg();
+ bool Read(SvStream &rS) override;
+ bool ImportCustomToolBar( SfxObjectShell& rDocSh );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
new file mode 100644
index 0000000000..1c7df7bee9
--- /dev/null
+++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
@@ -0,0 +1,842 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cstddef>
+
+#include "XMLRedlineImportHelper.hxx"
+#include <unotextcursor.hxx>
+#include <unotextrange.hxx>
+#include <unocrsr.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <tools/datetime.hxx>
+#include <poolfmt.hxx>
+#include <fmtanchr.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <unoredline.hxx>
+#include <DocumentRedlineManager.hxx>
+#include "xmlimp.hxx"
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/any.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::xmloff::token;
+
+using ::com::sun::star::text::XTextCursor;
+using ::com::sun::star::text::XTextRange;
+using ::com::sun::star::text::XWordCursor;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::beans::XPropertySetInfo;
+// collision with tools/DateTime: use UNO DateTime as util::DateTime
+// using util::DateTime;
+
+// a few helper functions
+static SwDoc* lcl_GetDocViaTunnel( Reference<XTextCursor> const & rCursor )
+{
+ OTextCursorHelper *const pXCursor =
+ dynamic_cast<OTextCursorHelper*>(rCursor.get());
+ OSL_ENSURE( pXCursor, "OTextCursorHelper missing" );
+ return pXCursor ? pXCursor->GetDoc() : nullptr;
+}
+
+static SwDoc* lcl_GetDocViaTunnel( Reference<XTextRange> const & rRange )
+{
+ SwXTextRange *const pXRange = dynamic_cast<SwXTextRange*>(rRange.get());
+ OSL_ENSURE(pXRange, "missing SwXTextRange for XTextRange");
+ // #i115174#: this may be a SvxUnoTextRange
+ // OSL_ENSURE( pXRange, "SwXTextRange missing" );
+ return pXRange ? &pXRange->GetDoc() : nullptr;
+}
+
+// XTextRangeOrNodeIndexPosition: store a position into the text
+// *either* as an XTextRange or as an SwNodeIndex. The reason is that
+// we must store either pointers to StartNodes (because redlines may
+// start on start nodes) or to a text position, and there appears to
+// be no existing type that could do both. Things are complicated by
+// the matter that (e.g in section import) we delete a few characters,
+// which may cause bookmarks (as used by XTextRange) to be deleted.
+
+namespace {
+
+class XTextRangeOrNodeIndexPosition
+{
+ Reference<XTextRange> m_xRange;
+ std::optional<SwNodeIndex> m_oIndex; // pIndex will point to the *previous* node
+
+public:
+ XTextRangeOrNodeIndexPosition();
+
+ void Set( Reference<XTextRange> const & rRange );
+ void Set( SwNode const & rIndex );
+ void SetAsNodeIndex( Reference<XTextRange> const & rRange );
+
+ void CopyPositionInto(SwPosition& rPos, SwDoc & rDoc);
+ SwDoc* GetDoc();
+
+ bool IsValid() const;
+};
+
+}
+
+XTextRangeOrNodeIndexPosition::XTextRangeOrNodeIndexPosition()
+{
+}
+
+void XTextRangeOrNodeIndexPosition::Set( Reference<XTextRange> const & rRange )
+{
+ m_xRange = rRange->getStart(); // set bookmark
+ m_oIndex.reset();
+}
+
+void XTextRangeOrNodeIndexPosition::Set( SwNode const & rIndex )
+{
+ m_oIndex = rIndex;
+ --(*m_oIndex) ; // previous node!!!
+ m_xRange = nullptr;
+}
+
+void XTextRangeOrNodeIndexPosition::SetAsNodeIndex(
+ Reference<XTextRange> const & rRange )
+{
+ // XTextRange -> XTunnel -> SwXTextRange
+ SwDoc* pDoc = lcl_GetDocViaTunnel(rRange);
+
+ if (!pDoc)
+ {
+ SAL_WARN("sw", "no SwDoc");
+ return;
+ }
+
+ // SwXTextRange -> PaM
+ SwUnoInternalPaM aPaM(*pDoc);
+ bool bSuccess = ::sw::XTextRangeToSwPaM(aPaM, rRange);
+ OSL_ENSURE(bSuccess, "illegal range");
+
+ // PaM -> Index
+ Set(aPaM.GetPoint()->GetNode());
+}
+
+void
+XTextRangeOrNodeIndexPosition::CopyPositionInto(SwPosition& rPos, SwDoc & rDoc)
+{
+ OSL_ENSURE(IsValid(), "Can't get Position");
+
+ // create PAM from start cursor (if no node index is present)
+ if (!m_oIndex.has_value())
+ {
+ SwUnoInternalPaM aUnoPaM(rDoc);
+ bool bSuccess = ::sw::XTextRangeToSwPaM(aUnoPaM, m_xRange);
+ OSL_ENSURE(bSuccess, "illegal range");
+
+ rPos = *aUnoPaM.GetPoint();
+ }
+ else
+ {
+ rPos.Assign( m_oIndex->GetNode(), SwNodeOffset(1) ); // pIndex points to previous index !!!
+ }
+}
+
+SwDoc* XTextRangeOrNodeIndexPosition::GetDoc()
+{
+ OSL_ENSURE(IsValid(), "Can't get Doc");
+
+ return m_oIndex.has_value() ? &m_oIndex->GetNodes().GetDoc() : lcl_GetDocViaTunnel(m_xRange);
+}
+
+bool XTextRangeOrNodeIndexPosition::IsValid() const
+{
+ return m_xRange.is() || m_oIndex.has_value();
+}
+
+// RedlineInfo: temporary storage for redline data
+class RedlineInfo
+{
+public:
+ RedlineInfo();
+ ~RedlineInfo();
+
+ // redline type (insert, delete, ...)
+ RedlineType eType;
+
+ // info fields:
+ OUString sAuthor; // change author string
+ OUString sComment; // change comment string
+ util::DateTime aDateTime; // change DateTime
+ OUString sMovedID; // change move id string
+ bool bMergeLastParagraph; // the SwRangeRedline::IsDelLastPara flag
+
+ // each position can may be either empty, an XTextRange, or an SwNodeIndex
+
+ // start pos of anchor (may be empty)
+ XTextRangeOrNodeIndexPosition aAnchorStart;
+
+ // end pos of anchor (may be empty)
+ XTextRangeOrNodeIndexPosition aAnchorEnd;
+
+ // index of content node (maybe NULL)
+ SwNodeIndex* pContentIndex;
+
+ // next redline info (for hierarchical redlines)
+ RedlineInfo* pNextRedline;
+
+ // store whether we expect an adjustment for this redline
+ bool bNeedsAdjustment;
+};
+
+RedlineInfo::RedlineInfo() :
+ eType(RedlineType::Insert),
+ bMergeLastParagraph( false ),
+ pContentIndex(nullptr),
+ pNextRedline(nullptr),
+ bNeedsAdjustment( false )
+{
+}
+
+RedlineInfo::~RedlineInfo()
+{
+ delete pContentIndex;
+ delete pNextRedline;
+}
+
+constexpr OUString g_sShowChanges = u"ShowChanges"_ustr;
+constexpr OUString g_sRecordChanges = u"RecordChanges"_ustr;
+constexpr OUString g_sRedlineProtectionKey = u"RedlineProtectionKey"_ustr;
+
+XMLRedlineImportHelper::XMLRedlineImportHelper(
+ SwXMLImport & rImport,
+ bool bNoRedlinesPlease,
+ const Reference<XPropertySet> & rModel,
+ const Reference<XPropertySet> & rImportInfo )
+ : m_rImport(rImport),
+ m_sInsertion( GetXMLToken( XML_INSERTION )),
+ m_sDeletion( GetXMLToken( XML_DELETION )),
+ m_sFormatChange( GetXMLToken( XML_FORMAT_CHANGE )),
+ m_bIgnoreRedlines(bNoRedlinesPlease),
+ m_xModelPropertySet(rModel),
+ m_xImportInfoPropertySet(rImportInfo)
+{
+ // check to see if redline mode is handled outside of component
+ bool bHandleShowChanges = true;
+ bool bHandleRecordChanges = true;
+ bool bHandleProtectionKey = true;
+ if ( m_xImportInfoPropertySet.is() )
+ {
+ Reference<XPropertySetInfo> xInfo =
+ m_xImportInfoPropertySet->getPropertySetInfo();
+
+ bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges );
+ bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges );
+ bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey );
+ }
+
+ // get redline mode
+ m_bShowChanges = *o3tl::doAccess<bool>(
+ ( bHandleShowChanges ? m_xModelPropertySet : m_xImportInfoPropertySet )
+ ->getPropertyValue( g_sShowChanges ));
+ m_bRecordChanges = *o3tl::doAccess<bool>(
+ ( bHandleRecordChanges ? m_xModelPropertySet : m_xImportInfoPropertySet )
+ ->getPropertyValue( g_sRecordChanges ));
+ {
+ Any aAny = (bHandleProtectionKey ? m_xModelPropertySet
+ : m_xImportInfoPropertySet )
+ ->getPropertyValue( g_sRedlineProtectionKey );
+ aAny >>= m_aProtectionKey;
+ }
+
+ // set redline mode to "don't record changes"
+ if( bHandleRecordChanges )
+ {
+ m_xModelPropertySet->setPropertyValue( g_sRecordChanges, Any(false) );
+ }
+}
+
+XMLRedlineImportHelper::~XMLRedlineImportHelper()
+{
+ // delete all left over (and obviously incomplete) RedlineInfos (and map)
+ for( const auto& rEntry : m_aRedlineMap )
+ {
+ RedlineInfo* pInfo = rEntry.second;
+
+ // left-over redlines. Insert them if possible (but assert),
+ // and delete the incomplete ones. Finally, delete it.
+ if( IsReady(pInfo) )
+ {
+ OSL_FAIL("forgotten RedlineInfo; now inserted");
+ InsertIntoDocument( pInfo );
+ }
+ else
+ {
+ // try if only the adjustment was missing
+ pInfo->bNeedsAdjustment = false;
+ if( IsReady(pInfo) )
+ {
+ OSL_FAIL("RedlineInfo without adjustment; now inserted");
+ InsertIntoDocument( pInfo );
+ }
+ else
+ {
+ // this situation occurs if redlines aren't closed
+ // (i.e. end without start, or start without
+ // end). This may well be a problem in the file,
+ // rather than the code.
+ OSL_FAIL("incomplete redline (maybe file was corrupt); "
+ "now deleted");
+ }
+ }
+ delete pInfo;
+ }
+ m_aRedlineMap.clear();
+
+ // set redline mode, either to info property set, or directly to
+ // the document
+ bool bHandleShowChanges = true;
+ bool bHandleRecordChanges = true;
+ bool bHandleProtectionKey = true;
+ if ( m_xImportInfoPropertySet.is() )
+ {
+ Reference<XPropertySetInfo> xInfo =
+ m_xImportInfoPropertySet->getPropertySetInfo();
+
+ bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges );
+ bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges );
+ bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey );
+ }
+
+ // set redline mode & key
+ try
+ {
+ Any aAny;
+
+ aAny <<= m_bShowChanges;
+ if ( bHandleShowChanges )
+ {
+ aAny <<= true;
+ m_xModelPropertySet->setPropertyValue( g_sShowChanges, aAny );
+ // TODO maybe we need some property for the view-setting?
+ SwDoc *const pDoc(m_rImport.getDoc());
+ assert(pDoc);
+ pDoc->GetDocumentRedlineManager().SetHideRedlines(!m_bShowChanges);
+ }
+ else
+ m_xImportInfoPropertySet->setPropertyValue( g_sShowChanges, aAny );
+
+ aAny <<= m_bRecordChanges;
+ if ( bHandleRecordChanges )
+ m_xModelPropertySet->setPropertyValue( g_sRecordChanges, aAny );
+ else
+ m_xImportInfoPropertySet->setPropertyValue( g_sRecordChanges, aAny );
+
+ aAny <<= m_aProtectionKey;
+ if ( bHandleProtectionKey )
+ m_xModelPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny );
+ else
+ m_xImportInfoPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny);
+ }
+ catch (const uno::RuntimeException &) // fdo#65882
+ {
+ SAL_WARN( "sw", "potentially benign ordering issue during shutdown" );
+ }
+}
+
+void XMLRedlineImportHelper::Add(
+ std::u16string_view rType,
+ const OUString& rId,
+ const OUString& rAuthor,
+ const OUString& rComment,
+ const util::DateTime& rDateTime,
+ const OUString& rMovedID,
+ bool bMergeLastPara)
+{
+ // we need to do the following:
+ // 1) parse type string
+ // 2) create RedlineInfo and fill it with data
+ // 3) check for existing redline with same ID
+ // 3a) insert redline into map
+ // 3b) attach to existing redline
+
+ // ad 1)
+ RedlineType eType;
+ if (rType == m_sInsertion)
+ {
+ eType = RedlineType::Insert;
+ }
+ else if (rType == m_sDeletion)
+ {
+ eType = RedlineType::Delete;
+ }
+ else if (rType == m_sFormatChange)
+ {
+ eType = RedlineType::Format;
+ }
+ else
+ {
+ // no proper type found: early out!
+ return;
+ }
+
+ // ad 2) create a new RedlineInfo
+ RedlineInfo* pInfo = new RedlineInfo();
+
+ // fill entries
+ pInfo->eType = eType;
+ pInfo->sAuthor = rAuthor;
+ pInfo->sComment = rComment;
+ pInfo->aDateTime = rDateTime;
+ pInfo->sMovedID = rMovedID;
+ pInfo->bMergeLastParagraph = bMergeLastPara;
+
+ //reserve MoveID so it won't be reused by others
+ if (!rMovedID.isEmpty())
+ {
+ SwDoc* const pDoc(m_rImport.getDoc());
+ assert(pDoc);
+ pDoc->GetDocumentRedlineManager().GetRedlineTable().setMovedIDIfNeeded(rMovedID.toInt32());
+ }
+
+ // ad 3)
+ auto itPair = m_aRedlineMap.emplace(rId, pInfo);
+ if (itPair.second)
+ return;
+
+ // 3b) we already have a redline with this name: hierarchical redlines
+ // insert pInfo as last element in the chain.
+ // (hierarchy sanity checking happens on inserting into the document)
+
+ // find last element
+ RedlineInfo* pInfoChain;
+ for( pInfoChain = itPair.first->second;
+ nullptr != pInfoChain->pNextRedline;
+ pInfoChain = pInfoChain->pNextRedline) ; // empty loop
+
+ // insert as last element
+ pInfoChain->pNextRedline = pInfo;
+}
+
+Reference<XTextCursor> XMLRedlineImportHelper::CreateRedlineTextSection(
+ Reference<XTextCursor> const & xOldCursor,
+ const OUString& rId)
+{
+ Reference<XTextCursor> xReturn;
+
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ // get RedlineInfo
+ RedlineMapType::iterator aFind = m_aRedlineMap.find(rId);
+ if (m_aRedlineMap.end() != aFind)
+ {
+ // get document from old cursor (via tunnel)
+ SwDoc* pDoc = lcl_GetDocViaTunnel(xOldCursor);
+
+ if (!pDoc)
+ {
+ SAL_WARN("sw", "no SwDoc => cannot create section.");
+ return nullptr;
+ }
+
+ // create text section for redline
+ SwTextFormatColl *pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool
+ (RES_POOLCOLL_STANDARD, false );
+ SwStartNode* pRedlineNode = pDoc->GetNodes().MakeTextSection(
+ pDoc->GetNodes().GetEndOfRedlines(),
+ SwNormalStartNode,
+ pColl);
+
+ // remember node-index in RedlineInfo
+ SwNodeIndex aIndex(*pRedlineNode);
+ aFind->second->pContentIndex = new SwNodeIndex(aIndex);
+
+ // create XText for document
+ rtl::Reference<SwXRedlineText> pXText = new SwXRedlineText(pDoc, aIndex);
+
+ // create (UNO-) cursor
+ SwPosition aPos(*pRedlineNode);
+ rtl::Reference<SwXTextCursor> pXCursor =
+ new SwXTextCursor(*pDoc, pXText, CursorType::Redline, aPos);
+ pXCursor->GetCursor().Move(fnMoveForward, GoInNode);
+ // cast to avoid ambiguity
+ xReturn = static_cast<text::XWordCursor*>(pXCursor.get());
+ }
+ // else: unknown redline -> Ignore
+
+ return xReturn;
+}
+
+void XMLRedlineImportHelper::SetCursor(
+ const OUString& rId,
+ bool bStart,
+ Reference<XTextRange> const & rRange,
+ bool bIsOutsideOfParagraph)
+{
+ RedlineMapType::iterator aFind = m_aRedlineMap.find(rId);
+ if (m_aRedlineMap.end() == aFind)
+ return;
+
+ // RedlineInfo found; now set Cursor
+ RedlineInfo* pInfo = aFind->second;
+ if (bIsOutsideOfParagraph)
+ {
+ // outside of paragraph: remember SwNodeIndex
+ if (bStart)
+ {
+ pInfo->aAnchorStart.SetAsNodeIndex(rRange);
+ }
+ else
+ {
+ pInfo->aAnchorEnd.SetAsNodeIndex(rRange);
+ }
+
+ // also remember that we expect an adjustment for this redline
+ pInfo->bNeedsAdjustment = true;
+ }
+ else
+ {
+ // inside of a paragraph: use regular XTextRanges (bookmarks)
+ if (bStart)
+ pInfo->aAnchorStart.Set(rRange);
+ else
+ pInfo->aAnchorEnd.Set(rRange);
+ }
+
+ // if this Cursor was the last missing info, we insert the
+ // node into the document
+ // then we can remove the entry from the map and destroy the object
+ if (IsReady(pInfo))
+ {
+ InsertIntoDocument(pInfo);
+ m_aRedlineMap.erase(rId);
+ delete pInfo;
+ }
+ // else: unknown Id -> ignore
+}
+
+void XMLRedlineImportHelper::AdjustStartNodeCursor(
+ const OUString& rId) /// ID used in RedlineAdd() call
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ // start + end nodes are treated the same. For either it's
+ // necessary that the target node already exists.
+
+ RedlineMapType::iterator aFind = m_aRedlineMap.find(rId);
+ if (m_aRedlineMap.end() == aFind)
+ return;
+
+ // RedlineInfo found; now set Cursor
+ RedlineInfo* pInfo = aFind->second;
+
+ pInfo->bNeedsAdjustment = false;
+
+ // if now ready, insert into document
+ if( IsReady(pInfo) )
+ {
+ InsertIntoDocument(pInfo);
+ m_aRedlineMap.erase(rId);
+ delete pInfo;
+ }
+ // else: can't find redline -> ignore
+}
+
+inline bool XMLRedlineImportHelper::IsReady(const RedlineInfo* pRedline)
+{
+ // we can insert a redline if we have start & end, and we don't
+ // expect adjustments for either of these
+ return ( pRedline->aAnchorEnd.IsValid() &&
+ pRedline->aAnchorStart.IsValid() &&
+ !pRedline->bNeedsAdjustment );
+}
+
+/// recursively check if rPos or its anchor (if in fly or footnote) is in redline section
+static auto RecursiveContains(SwStartNode const& rRedlineSection, SwNode const& rPos) -> bool
+{
+ if (rRedlineSection.GetIndex() <= rPos.GetIndex()
+ && rPos.GetIndex() <= rRedlineSection.EndOfSectionIndex())
+ {
+ return true;
+ }
+ // loop to iterate "up" in the node tree and find an anchored XText
+ for (SwStartNode const* pStartNode = rPos.StartOfSectionNode();
+ pStartNode != nullptr && pStartNode->GetIndex() != SwNodeOffset(0);
+ pStartNode = pStartNode->StartOfSectionNode())
+ {
+ switch (pStartNode->GetStartNodeType())
+ {
+ case SwNormalStartNode:
+ case SwTableBoxStartNode:
+ continue;
+ break;
+ case SwFlyStartNode:
+ {
+ SwFrameFormat const*const pFormat(pStartNode->GetFlyFormat());
+ assert(pFormat);
+ SwFormatAnchor const& rAnchor(pFormat->GetAnchor());
+ if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ return false;
+ }
+ else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_FLY)
+ { // anchor is on a start node, avoid skipping it:
+ pStartNode = rAnchor.GetAnchorNode()->GetStartNode();
+ assert(pStartNode);
+ // pass the next node to recursive call - it will call
+ // call StartOfSectionNode on it and go back to pStartNode
+ SwNodeIndex const next(*pStartNode, +1);
+ return RecursiveContains(rRedlineSection, next.GetNode());
+ }
+ else
+ {
+ return RecursiveContains(rRedlineSection, *rAnchor.GetAnchorNode());
+ }
+ }
+ break;
+ case SwFootnoteStartNode:
+ { // sigh ... need to search
+ for (SwTextFootnote const*const pFootnote : rRedlineSection.GetDoc().GetFootnoteIdxs())
+ {
+ if (pStartNode == pFootnote->GetStartNode()->GetNode().GetStartNode())
+ {
+ return RecursiveContains(rRedlineSection, pFootnote->GetTextNode());
+ }
+ }
+ assert(false);
+ }
+ break;
+ case SwHeaderStartNode:
+ case SwFooterStartNode:
+ return false; // headers aren't anchored
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ return false;
+}
+
+void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo)
+{
+ OSL_ENSURE(nullptr != pRedlineInfo, "need redline info");
+ OSL_ENSURE(IsReady(pRedlineInfo), "redline info not complete yet!");
+
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ // Insert the Redline as described by pRedlineInfo into the
+ // document. If we are in insert mode, don't insert any redlines
+ // (and delete 'deleted' inline redlines)
+
+ // get the document (from one of the positions)
+ SwDoc* pDoc = pRedlineInfo->aAnchorStart.GetDoc();
+
+ if (!pDoc)
+ {
+ SAL_WARN("sw", "no SwDoc => cannot insert redline.");
+ return;
+ }
+
+ // now create the PaM for the redline
+ SwPaM aPaM(pDoc->GetNodes().GetEndOfContent());
+ pRedlineInfo->aAnchorStart.CopyPositionInto(*aPaM.GetPoint(), *pDoc);
+ aPaM.SetMark();
+ pRedlineInfo->aAnchorEnd.CopyPositionInto(*aPaM.GetPoint(), *pDoc);
+
+ // collapse PaM if (start == end)
+ if (*aPaM.GetPoint() == *aPaM.GetMark())
+ {
+ aPaM.DeleteMark();
+ }
+
+ // cover three cases:
+ // 1) empty redlines (no range, no content)
+ // 2) check for:
+ // a) bIgnoreRedline (e.g. insert mode)
+ // b) illegal PaM range (CheckNodesRange())
+ // c) redline with empty content section (quite useless)
+ // 3) normal case: insert redline
+ SwTextNode const* pTempNode(nullptr);
+ if( !aPaM.HasMark() && (pRedlineInfo->pContentIndex == nullptr) )
+ {
+ // these redlines have no function, and will thus be ignored (just as
+ // in sw3io), so no action here
+ }
+ else if ( m_bIgnoreRedlines ||
+ !CheckNodesRange( aPaM.GetPoint()->GetNode(),
+ aPaM.GetMark()->GetNode(),
+ true )
+ || (pRedlineInfo->pContentIndex
+ && (pRedlineInfo->pContentIndex->GetIndex() + 2
+ == pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex())
+ && (pTempNode = pDoc->GetNodes()[pRedlineInfo->pContentIndex->GetIndex() + 1]->GetTextNode()) != nullptr
+ && pTempNode->GetText().isEmpty()
+ && !pTempNode->GetpSwpHints()
+ && pTempNode->GetAnchoredFlys().empty()))
+ {
+ // ignore redline (e.g. file loaded in insert mode):
+ // delete 'deleted' redlines and forget about the whole thing
+ if (RedlineType::Delete == pRedlineInfo->eType)
+ {
+ pDoc->getIDocumentContentOperations().DeleteRange(aPaM);
+ // And what about the "deleted nodes"?
+ // They have to be deleted as well (#i80689)!
+ if( m_bIgnoreRedlines && pRedlineInfo->pContentIndex != nullptr )
+ {
+ const SwNodeIndex& rIdx( *pRedlineInfo->pContentIndex );
+ const SwNode* pEnd = rIdx.GetNode().EndOfSectionNode();
+ if( pEnd )
+ {
+ SwPaM aDel( rIdx.GetNode(), 0, *pEnd, 1 );
+ pDoc->getIDocumentContentOperations().DeleteRange(aDel);
+ }
+ }
+ }
+ }
+ else if (pRedlineInfo->pContentIndex != nullptr
+ // should be enough to check 1 position of aPaM bc CheckNodesRange() above
+ && RecursiveContains(*pRedlineInfo->pContentIndex->GetNode().GetStartNode(), aPaM.GetPoint()->GetNode()))
+ {
+ SAL_WARN("sw.xml", "Recursive change tracking, removing");
+ // reuse aPaM to remove it from nodes that will be deleted
+ aPaM.GetPoint()->Assign(pRedlineInfo->pContentIndex->GetNode());
+ aPaM.DeleteMark();
+ pDoc->getIDocumentContentOperations().DeleteSection(&aPaM.GetPoint()->GetNode());
+ }
+ else
+ {
+ // regular file loading: insert redline
+
+ // create redline (using pRedlineData which gets copied in SwRangeRedline())
+ SwRedlineData* pRedlineData = ConvertRedline(pRedlineInfo, pDoc);
+ SwRangeRedline* pRedline =
+ new SwRangeRedline( pRedlineData, *aPaM.GetPoint(),
+ !pRedlineInfo->bMergeLastParagraph );
+
+ // tdf#107292 fix order of delete redlines at the same position by removing
+ // the already inserted redlines temporarily and inserting them back in reverse
+ // order after inserting pRedline
+ std::vector<const SwRangeRedline*> aSwapRedlines;
+ if ( RedlineType::Delete == pRedlineInfo->eType )
+ {
+ SwRedlineTable::size_type n = 0;
+ while ( const SwRangeRedline* pRedline2 =
+ pDoc->getIDocumentRedlineAccess().GetRedline( *pRedline->Start(), &n ) )
+ {
+ SwRedlineTable& aRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ aSwapRedlines.push_back(pRedline2);
+ aRedlineTable.Remove(n);
+ }
+ }
+
+ // set mark
+ if( aPaM.HasMark() )
+ {
+ pRedline->SetMark();
+ *(pRedline->GetMark()) = *aPaM.GetMark();
+ }
+
+ // set content node (if necessary)
+ if (nullptr != pRedlineInfo->pContentIndex)
+ {
+ SwNodeOffset nPoint = aPaM.GetPoint()->GetNodeIndex();
+ if( nPoint < pRedlineInfo->pContentIndex->GetIndex() ||
+ nPoint > pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex() )
+ pRedline->SetContentIdx(*pRedlineInfo->pContentIndex);
+ else
+ SAL_WARN("sw", "Recursive change tracking");
+ }
+
+ // set redline mode (without doing the associated book-keeping)
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::On);
+ pDoc->getIDocumentRedlineAccess().AppendRedline(pRedline, false);
+
+ // restore the correct order of the delete redlines at the same position
+ for (auto i = aSwapRedlines.rbegin(); i != aSwapRedlines.rend(); ++i)
+ pDoc->getIDocumentRedlineAccess().AppendRedline(const_cast<SwRangeRedline*>(*i), false);
+
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
+ }
+}
+
+SwRedlineData* XMLRedlineImportHelper::ConvertRedline(
+ RedlineInfo* pRedlineInfo,
+ SwDoc* pDoc)
+{
+ // convert info:
+ // 1) Author String -> Author ID (default to zero)
+ std::size_t nAuthorId = (nullptr == pDoc) ? 0 :
+ pDoc->getIDocumentRedlineAccess().InsertRedlineAuthor( pRedlineInfo->sAuthor );
+
+ // 2) util::DateTime -> DateTime
+ DateTime aDT( DateTime::EMPTY );
+ aDT.SetYear( pRedlineInfo->aDateTime.Year );
+ aDT.SetMonth( pRedlineInfo->aDateTime.Month );
+ aDT.SetDay( pRedlineInfo->aDateTime.Day );
+ aDT.SetHour( pRedlineInfo->aDateTime.Hours );
+ aDT.SetMin( pRedlineInfo->aDateTime.Minutes );
+ aDT.SetSec( pRedlineInfo->aDateTime.Seconds );
+ aDT.SetNanoSec( pRedlineInfo->aDateTime.NanoSeconds );
+
+ sal_uInt32 nMovedID = pRedlineInfo->sMovedID.toInt32();
+
+ // 3) recursively convert next redline
+ // ( check presence and sanity of hierarchical redline info )
+ SwRedlineData* pNext = nullptr;
+ if ( (nullptr != pRedlineInfo->pNextRedline) &&
+ (RedlineType::Delete == pRedlineInfo->eType) &&
+ (RedlineType::Insert == pRedlineInfo->pNextRedline->eType) )
+ {
+ pNext = ConvertRedline(pRedlineInfo->pNextRedline, pDoc);
+ }
+
+ // create redline data
+ SwRedlineData* pData = new SwRedlineData(pRedlineInfo->eType,
+ nAuthorId, aDT, nMovedID,
+ pRedlineInfo->sComment,
+ pNext); // next data (if available)
+
+ return pData;
+}
+
+void XMLRedlineImportHelper::SetShowChanges( bool bShow )
+{
+ m_bShowChanges = bShow;
+}
+
+void XMLRedlineImportHelper::SetRecordChanges( bool bRecord )
+{
+ m_bRecordChanges = bRecord;
+}
+
+void XMLRedlineImportHelper::SetProtectionKey(
+ const Sequence<sal_Int8> & rKey )
+{
+ m_aProtectionKey = rKey;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.hxx b/sw/source/filter/xml/XMLRedlineImportHelper.hxx
new file mode 100644
index 0000000000..8a15f53dfb
--- /dev/null
+++ b/sw/source/filter/xml/XMLRedlineImportHelper.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLREDLINEIMPORTHELPER_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLREDLINEIMPORTHELPER_HXX
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/util/DateTime.hpp>
+#include <redline.hxx>
+
+#include <map>
+
+class SvXMLImport;
+class RedlineInfo;
+class SwRedlineData;
+class SwDoc;
+class SwXMLImport;
+namespace com::sun::star {
+ namespace text { class XTextCursor; }
+ namespace text { class XTextRange; }
+ namespace frame { class XModel; }
+}
+
+typedef std::map< OUString, RedlineInfo* > RedlineMapType;
+
+class XMLRedlineImportHelper final
+{
+ SwXMLImport & m_rImport;
+
+ const OUString m_sInsertion;
+ const OUString m_sDeletion;
+ const OUString m_sFormatChange;
+
+ RedlineMapType m_aRedlineMap;
+
+ // if true, no redlines should be inserted into document
+ // (This typically happen when a document is loaded in 'insert'-mode.)
+ bool m_bIgnoreRedlines;
+
+ // save information for saving and reconstruction of the redline mode
+ css::uno::Reference<css::beans::XPropertySet> m_xModelPropertySet;
+ css::uno::Reference<css::beans::XPropertySet> m_xImportInfoPropertySet;
+ bool m_bShowChanges;
+ bool m_bRecordChanges;
+ css::uno::Sequence<sal_Int8> m_aProtectionKey;
+
+public:
+
+ XMLRedlineImportHelper(
+ SwXMLImport & rImport,
+ bool bIgnoreRedlines, // ignore redlines mode
+ // property sets of model + import info for saving + restoring the
+ // redline mode
+ const css::uno::Reference<css::beans::XPropertySet> & rModel,
+ const css::uno::Reference<css::beans::XPropertySet> & rImportInfoSet );
+ ~XMLRedlineImportHelper();
+
+ // create a redline object
+ // (The redline will be inserted into the document after both start
+ // and end cursor has been set.)
+ void Add(
+ std::u16string_view rType, // redline type (insert, del,... )
+ const OUString& rId, // use to identify this redline
+ const OUString& rAuthor, // name of the author
+ const OUString& rComment, // redline comment
+ const css::util::DateTime& rDateTime, // date+time
+ const OUString& rMovedID, // redline move id
+ bool bMergeLastParagraph); // merge last paragraph?
+
+ // create a text section for the redline, and return an
+ // XText/XTextCursor that may be used to write into it.
+ css::uno::Reference<css::text::XTextCursor> CreateRedlineTextSection(
+ css::uno::Reference<css::text::XTextCursor> const & xOldCursor, // needed to get the document
+ const OUString& rId); // ID used to RedlineAdd() call
+
+ // Set start or end position for a redline in the text body.
+ // Accepts XTextRange objects.
+ void SetCursor(
+ const OUString& rId, // ID used in RedlineAdd() call
+ bool bStart, // start or end Range
+ css::uno::Reference<css::text::XTextRange> const & rRange, // the actual XTextRange
+ // text range is (from an XML view) outside of a paragraph
+ // (i.e. before a table)
+ bool bIsOutsideOfParagraph);
+
+ /**
+ * Adjust the start (end) position for a redline that begins in a
+ * start node. It takes the cursor positions _inside_ the redlined
+ * element (e.g. section or table).
+ */
+ void AdjustStartNodeCursor(
+ const OUString& rId); // ID used in RedlineAdd() call
+
+ // set redline mode: show changes
+ void SetShowChanges( bool bShowChanges );
+
+ // set redline mode: record changes
+ void SetRecordChanges( bool bRecordChanges );
+
+ // set redline protection key
+ void SetProtectionKey(const css::uno::Sequence<sal_Int8> & rKey );
+
+private:
+
+ static inline bool IsReady(const RedlineInfo* pRedline);
+
+ void InsertIntoDocument(RedlineInfo* pRedline);
+
+ SwRedlineData* ConvertRedline(
+ RedlineInfo* pRedline, // RedlineInfo to be converted
+ SwDoc* pDoc); // document needed for Author-ID conversion
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/swxml.cxx b/sw/source/filter/xml/swxml.cxx
new file mode 100644
index 0000000000..16ae089113
--- /dev/null
+++ b/sw/source/filter/xml/swxml.cxx
@@ -0,0 +1,1017 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/document/NamedPropertyValues.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <o3tl/any.hxx>
+#include <vcl/errinf.hxx>
+#include <sfx2/docfile.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svl/stritem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/xmlgrhlp.hxx>
+#include <svx/xmleohlp.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <sal/log.hxx>
+#include <sfx2/frame.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <swerror.h>
+#include <fltini.hxx>
+#include <drawdoc.hxx>
+#include <doc.hxx>
+#include <docfunc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <docary.hxx>
+#include <docsh.hxx>
+#include <unotextrange.hxx>
+#include <SwXMLSectionList.hxx>
+
+#include <SwStyleNameMapper.hxx>
+#include <poolfmt.hxx>
+#include <numrule.hxx>
+#include <paratr.hxx>
+#include <fmtrowsplt.hxx>
+
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdograf.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <istyleaccess.hxx>
+
+#include <sfx2/DocumentMetadataAccess.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+
+static void lcl_EnsureValidPam( SwPaM& rPam )
+{
+ if( rPam.GetPointContentNode() != nullptr )
+ {
+ // set proper point content
+ if( rPam.GetPointContentNode() != rPam.GetPoint()->GetContentNode() )
+ {
+ rPam.GetPoint()->nContent.Assign( rPam.GetPointContentNode(), 0 );
+ }
+ // else: point was already valid
+
+ // if mark is invalid, we delete it
+ if( ( rPam.GetMarkContentNode() == nullptr ) ||
+ ( rPam.GetMarkContentNode() != rPam.GetMark()->GetContentNode() ) )
+ {
+ rPam.DeleteMark();
+ }
+ }
+ else
+ {
+ // point is not valid, so move it into the first content
+ rPam.DeleteMark();
+ rPam.GetPoint()->Assign(
+ *rPam.GetDoc().GetNodes().GetEndOfContent().StartOfSectionNode() );
+ rPam.GetPoint()->Adjust(SwNodeOffset(+1));
+ rPam.Move( fnMoveForward, GoInContent ); // go into content
+ }
+}
+
+XMLReader::XMLReader()
+{
+}
+
+SwReaderType XMLReader::GetReaderType()
+{
+ return SwReaderType::Storage;
+}
+
+namespace
+{
+
+/// read a component (file + filter version)
+ErrCodeMsg ReadThroughComponent(
+ uno::Reference<io::XInputStream> const & xInputStream,
+ uno::Reference<XComponent> const & xModelComponent,
+ const OUString& rStreamName,
+ uno::Reference<uno::XComponentContext> const & rxContext,
+ const char* pFilterName,
+ const Sequence<Any>& rFilterArguments,
+ const OUString& rName,
+ bool bMustBeSuccessful,
+ bool bEncrypted )
+{
+ OSL_ENSURE(xInputStream.is(), "input stream missing");
+ OSL_ENSURE(xModelComponent.is(), "document missing");
+ OSL_ENSURE(rxContext.is(), "factory missing");
+ OSL_ENSURE(nullptr != pFilterName,"I need a service name for the component!");
+
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = rName;
+ aParserInput.aInputStream = xInputStream;
+
+ // get filter
+ const OUString aFilterName(OUString::createFromAscii(pFilterName));
+ uno::Reference< XInterface > xFilter =
+ rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(aFilterName, rFilterArguments, rxContext);
+ SAL_WARN_IF(!xFilter.is(), "sw.filter", "Can't instantiate filter component: " << aFilterName);
+ if( !xFilter.is() )
+ return ERR_SWG_READ_ERROR;
+ // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler
+ uno::Reference< xml::sax::XFastParser > xFastParser(xFilter, UNO_QUERY);
+ uno::Reference< xml::sax::XDocumentHandler > xDocumentHandler;
+ if (!xFastParser)
+ xDocumentHandler.set(xFilter, UNO_QUERY);
+ if (!xDocumentHandler && !xFastParser)
+ {
+ SAL_WARN("sd", "service does not implement XFastParser or XDocumentHandler");
+ assert(false);
+ return ERR_SWG_READ_ERROR;
+ }
+
+ // connect model and filter
+ uno::Reference < XImporter > xImporter( xFilter, UNO_QUERY );
+ xImporter->setTargetDocument( xModelComponent );
+
+ // finally, parse the stream
+ try
+ {
+ if (xFastParser)
+ xFastParser->parseStream( aParserInput );
+ else
+ {
+ uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(rxContext);
+ // connect parser and filter
+ xParser->setDocumentHandler( xDocumentHandler );
+ xParser->parseStream( aParserInput );
+ }
+ }
+ catch( xml::sax::SAXParseException& r)
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ // sax parser sends wrapped exceptions,
+ // try to find the original one
+ xml::sax::SAXException aSaxEx = *static_cast<xml::sax::SAXException*>(&r);
+ bool bTryChild = true;
+
+ while( bTryChild )
+ {
+ xml::sax::SAXException aTmp;
+ if ( aSaxEx.WrappedException >>= aTmp )
+ aSaxEx = aTmp;
+ else
+ bTryChild = false;
+ }
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( aSaxEx.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if( bEncrypted )
+ return ERRCODE_SFX_WRONGPASSWORD;
+
+ SAL_WARN( "sw", "SAX parse exception caught while importing: " << exceptionToString(ex) );
+
+ const OUString sErr( OUString::number( r.LineNumber )
+ + ","
+ + OUString::number( r.ColumnNumber ) );
+
+ if( !rStreamName.isEmpty() )
+ {
+ return ErrCodeMsg(
+ (bMustBeSuccessful ? ERR_FORMAT_FILE_ROWCOL
+ : WARN_FORMAT_FILE_ROWCOL),
+ rStreamName, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+ else
+ {
+ OSL_ENSURE( bMustBeSuccessful, "Warnings are not supported" );
+ return ErrCodeMsg( ERR_FORMAT_ROWCOL, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+ }
+ catch(const xml::sax::SAXException& r)
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( r.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if( bEncrypted )
+ return ERRCODE_SFX_WRONGPASSWORD;
+
+ SAL_WARN( "sw", "uno exception caught while importing: " << exceptionToString(ex) );
+ return ERR_SWG_READ_ERROR;
+ }
+ catch(const packages::zip::ZipIOException&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" );
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch(const io::IOException&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" );
+ return ERR_SWG_READ_ERROR;
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" );
+ return ERR_SWG_READ_ERROR;
+ }
+
+ // success!
+ return ERRCODE_NONE;
+}
+
+// read a component (storage version)
+ErrCodeMsg ReadThroughComponent(
+ uno::Reference<embed::XStorage> const & xStorage,
+ uno::Reference<XComponent> const & xModelComponent,
+ const char* pStreamName,
+ uno::Reference<uno::XComponentContext> const & rxContext,
+ const char* pFilterName,
+ const Sequence<Any>& rFilterArguments,
+ const OUString& rName,
+ bool bMustBeSuccessful)
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!");
+
+ // open stream (and set parser input)
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+ bool bContainsStream = false;
+ try
+ {
+ bContainsStream = xStorage->isStreamElement(sStreamName);
+ }
+ catch( container::NoSuchElementException& )
+ {
+ }
+
+ if (!bContainsStream )
+ {
+ // stream name not found! return immediately with OK signal
+ return ERRCODE_NONE;
+ }
+
+ // set Base URL
+ uno::Reference< beans::XPropertySet > xInfoSet;
+ if( rFilterArguments.hasElements() )
+ rFilterArguments.getConstArray()[0] >>= xInfoSet;
+ OSL_ENSURE( xInfoSet.is(), "missing property set" );
+ if( xInfoSet.is() )
+ {
+ xInfoSet->setPropertyValue( "StreamName", Any( sStreamName ) );
+ }
+
+ try
+ {
+ // get input stream
+ uno::Reference <io::XStream> xStream = xStorage->openStreamElement( sStreamName, embed::ElementModes::READ );
+ uno::Reference <beans::XPropertySet > xProps( xStream, uno::UNO_QUERY );
+
+ Any aAny = xProps->getPropertyValue("Encrypted");
+
+ std::optional<const bool> b = o3tl::tryAccess<bool>(aAny);
+ bool bEncrypted = b.has_value() && *b;
+
+ uno::Reference <io::XInputStream> xInputStream = xStream->getInputStream();
+
+ // read from the stream
+ return ReadThroughComponent(
+ xInputStream, xModelComponent, sStreamName, rxContext,
+ pFilterName, rFilterArguments,
+ rName, bMustBeSuccessful, bEncrypted );
+ }
+ catch ( packages::WrongPasswordException& )
+ {
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch( packages::zip::ZipIOException& )
+ {
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "Error on import" );
+ // TODO/LATER: error handling
+ }
+
+ return ERR_SWG_READ_ERROR;
+}
+
+}
+
+// #i44177#
+static void lcl_AdjustOutlineStylesForOOo(SwDoc& _rDoc)
+{
+ // array containing the names of the default outline styles ('Heading 1',
+ // 'Heading 2', ..., 'Heading 10')
+ OUString aDefOutlStyleNames[ MAXLEVEL ];
+ {
+ OUString sStyleName;
+ for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i )
+ {
+ sStyleName =
+ SwStyleNameMapper::GetProgName( RES_POOLCOLL_HEADLINE1 + i,
+ sStyleName );
+ aDefOutlStyleNames[i] = sStyleName;
+ }
+ }
+
+ // array indicating, which outline level already has a style assigned.
+ bool aOutlineLevelAssigned[ MAXLEVEL ];
+ // array of the default outline styles, which are created for the document.
+ SwTextFormatColl* aCreatedDefaultOutlineStyles[ MAXLEVEL ];
+
+ {
+ for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i )
+ {
+ aOutlineLevelAssigned[ i ] = false;
+ aCreatedDefaultOutlineStyles[ i ] = nullptr;
+ }
+ }
+
+ // determine, which outline level has already a style assigned and
+ // which of the default outline styles is created.
+ const SwTextFormatColls& rColls = *(_rDoc.GetTextFormatColls());
+ for ( size_t n = 1; n < rColls.size(); ++n )
+ {
+ SwTextFormatColl* pColl = rColls[ n ];
+ if ( pColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ aOutlineLevelAssigned[ pColl->GetAssignedOutlineStyleLevel() ] = true;
+ }
+
+ for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i )
+ {
+ if ( aCreatedDefaultOutlineStyles[ i ] == nullptr &&
+ pColl->GetName() == aDefOutlStyleNames[i] )
+ {
+ aCreatedDefaultOutlineStyles[ i ] = pColl;
+ break;
+ }
+ }
+ }
+
+ // assign already created default outline style to outline level, which
+ // doesn't have a style assigned to it.
+ const SwNumRule* pOutlineRule = _rDoc.GetOutlineNumRule();
+ for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i )
+ {
+ // #i73361#
+ // Do not change assignment of already created default outline style
+ // to a certain outline level.
+ if ( !aOutlineLevelAssigned[ i ] &&
+ aCreatedDefaultOutlineStyles[ i ] != nullptr &&
+ ! aCreatedDefaultOutlineStyles[ i ]->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ // apply outline level at created default outline style
+ aCreatedDefaultOutlineStyles[ i ]->AssignToListLevelOfOutlineStyle(i);
+
+ // apply outline numbering rule, if none is set.
+ const SwNumRuleItem& rItem =
+ aCreatedDefaultOutlineStyles[ i ]->GetFormatAttr( RES_PARATR_NUMRULE, false );
+ if ( rItem.GetValue().isEmpty() )
+ {
+ SwNumRuleItem aItem( pOutlineRule->GetName() );
+ aCreatedDefaultOutlineStyles[ i ]->SetFormatAttr( aItem );
+ }
+ }
+ }
+}
+
+static void lcl_ConvertSdrOle2ObjsToSdrGrafObjs(SwDoc& _rDoc)
+{
+ if ( !(_rDoc.getIDocumentDrawModelAccess().GetDrawModel() &&
+ _rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) )
+ return;
+
+ const SdrPage& rSdrPage( *(_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) );
+
+ // iterate recursive with group objects over all shapes on the draw page
+ SdrObjListIter aIter( &rSdrPage );
+ while( aIter.IsMore() )
+ {
+ SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( aIter.Next() );
+ if( pOle2Obj )
+ {
+ // found an ole2 shape
+ SdrObjList* pObjList = pOle2Obj->getParentSdrObjListFromSdrObject();
+
+ // get its graphic
+ Graphic aGraphic;
+ pOle2Obj->Connect();
+ const Graphic* pGraphic = pOle2Obj->GetGraphic();
+ if( pGraphic )
+ aGraphic = *pGraphic;
+ pOle2Obj->Disconnect();
+
+ // create new graphic shape with the ole graphic and shape size
+ rtl::Reference<SdrGrafObj> pGraphicObj = new SdrGrafObj(
+ pOle2Obj->getSdrModelFromSdrObject(),
+ aGraphic,
+ pOle2Obj->GetCurrentBoundRect());
+
+ // apply layer of ole2 shape at graphic shape
+ pGraphicObj->SetLayer( pOle2Obj->GetLayer() );
+
+ // replace ole2 shape with the new graphic object and delete the ol2 shape
+ pObjList->ReplaceObject( pGraphicObj.get(), pOle2Obj->GetOrdNum() );
+ }
+ }
+}
+
+ErrCodeMsg XMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & rName )
+{
+ // needed for relative URLs, but in clipboard copy/paste there may be none
+ // and also there is the SwXMLTextBlocks special case
+ SAL_INFO_IF(rBaseURL.isEmpty(), "sw.filter", "sw::XMLReader: no base URL");
+
+ // Get service factory
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+ uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ // get the input stream (storage or stream)
+ uno::Reference<embed::XStorage> xStorage;
+ if( m_pMedium )
+ xStorage = m_pMedium->GetStorage();
+ else
+ xStorage = m_xStorage;
+
+ if( !xStorage.is() )
+ return ERR_SWG_READ_ERROR;
+
+ xGraphicHelper = SvXMLGraphicHelper::Create( xStorage,
+ SvXMLGraphicHelperMode::Read );
+ xGraphicStorageHandler = xGraphicHelper.get();
+ SfxObjectShell *pPersist = rDoc.GetPersist();
+ if( pPersist )
+ {
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create(
+ xStorage, *pPersist,
+ SvXMLEmbeddedObjectHelperMode::Read );
+ xObjectResolver = xObjectHelper.get();
+ }
+
+ // Get the docshell, the model, and finally the model's component
+ SwDocShell *pDocSh = rDoc.GetDocShell();
+ OSL_ENSURE( pDocSh, "XMLReader::Read: got no doc shell" );
+ if( !pDocSh )
+ return ERR_SWG_READ_ERROR;
+ uno::Reference< lang::XComponent > xModelComp = pDocSh->GetModel();
+ OSL_ENSURE( xModelComp.is(),
+ "XMLReader::Read: got no model" );
+ if( !xModelComp.is() )
+ return ERR_SWG_READ_ERROR;
+
+ // create and prepare the XPropertySet that gets passed through
+ // the components, and the XStatusIndicator that shows progress to
+ // the user.
+
+ // create XPropertySet with three properties for status indicator
+ static comphelper::PropertyMapEntry const aInfoMap[] =
+ {
+ { OUString("ProgressRange"), 0,
+ ::cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressMax"), 0,
+ ::cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressCurrent"), 0,
+ ::cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("NumberStyles"), 0,
+ cppu::UnoType<container::XNameContainer>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("RecordChanges"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("ShowChanges"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("RedlineProtectionKey"), 0,
+ cppu::UnoType<Sequence<sal_Int8>>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("PrivateData"), 0,
+ cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ // properties for insert modes
+ { OUString("StyleInsertModeFamilies"), 0,
+ cppu::UnoType<Sequence<OUString>>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleInsertModeOverwrite"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("TextInsertModeRange"), 0,
+ cppu::UnoType<text::XTextRange>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("AutoTextMode"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("OrganizerMode"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+
+ // #i28749# - Add property, which indicates, if the
+ // shape position attributes are given in horizontal left-to-right layout.
+ // This is the case for the OpenOffice.org file format.
+ { OUString("ShapePositionInHoriL2R"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+
+ { OUString("BuildId"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+
+ // Add property, which indicates, if a text document in OpenOffice.org
+ // file format is read.
+ // Note: Text documents read via the binary filter are also finally
+ // read using the OpenOffice.org file format. Thus, e.g. for text
+ // documents in StarOffice 5.2 binary file format this property
+ // will be true.
+ { OUString("TextDocInOOoFileFormat"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(),
+ css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(
+ new comphelper::PropertySetInfo( aInfoMap ) ) );
+
+ // get BuildId from parent container if available
+ uno::Reference< container::XChild > xChild( xModelComp, uno::UNO_QUERY );
+ if( xChild.is() )
+ {
+ uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY );
+ if( xParentSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() );
+ static constexpr OUString sPropName(u"BuildId"_ustr );
+ if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) )
+ {
+ xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) );
+ }
+ }
+ }
+
+ // try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ if (pDocSh->GetMedium())
+ {
+ const SfxUnoAnyItem* pItem =
+ pDocSh->GetMedium()->GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ {
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+ }
+
+ // set progress range and start status indicator
+ sal_Int32 nProgressRange(1000000);
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ }
+ uno::Any aProgRange;
+ aProgRange <<= nProgressRange;
+ xInfoSet->setPropertyValue("ProgressRange", aProgRange);
+
+ Reference< container::XNameAccess > xLateInitSettings( document::NamedPropertyValues::create(xContext), UNO_QUERY_THROW );
+ beans::NamedValue aLateInitSettings( "LateInitSettings", Any( xLateInitSettings ) );
+
+ xInfoSet->setPropertyValue( "SourceStorage", Any( xStorage ) );
+
+ // prepare filter arguments, WARNING: the order is important!
+ Sequence<Any> aFilterArgs{ Any(xInfoSet),
+ Any(xStatusIndicator),
+ Any(xGraphicStorageHandler),
+ Any(xObjectResolver),
+ Any(aLateInitSettings) };
+
+ Sequence<Any> aEmptyArgs{ Any(xInfoSet),
+ Any(xStatusIndicator) };
+
+ // prepare for special modes
+ if( m_aOption.IsFormatsOnly() )
+ {
+ sal_Int32 nCount =
+ (m_aOption.IsFrameFormats() ? 1 : 0) +
+ (m_aOption.IsPageDescs() ? 1 : 0) +
+ (m_aOption.IsTextFormats() ? 2 : 0) +
+ (m_aOption.IsNumRules() ? 1 : 0);
+
+ Sequence< OUString> aFamiliesSeq( nCount );
+ OUString *pSeq = aFamiliesSeq.getArray();
+ if( m_aOption.IsFrameFormats() )
+ // SfxStyleFamily::Frame;
+ *pSeq++ = "FrameStyles";
+ if( m_aOption.IsPageDescs() )
+ // SfxStyleFamily::Page;
+ *pSeq++ = "PageStyles";
+ if( m_aOption.IsTextFormats() )
+ {
+ // (SfxStyleFamily::Char|SfxStyleFamily::Para);
+ *pSeq++ = "CharacterStyles";
+ *pSeq++ = "ParagraphStyles";
+ }
+ if( m_aOption.IsNumRules() )
+ // SfxStyleFamily::Pseudo;
+ *pSeq++ = "NumberingStyles";
+
+ xInfoSet->setPropertyValue( "StyleInsertModeFamilies",
+ Any(aFamiliesSeq) );
+
+ xInfoSet->setPropertyValue( "StyleInsertModeOverwrite", Any(!m_aOption.IsMerge()) );
+ }
+ else if( m_bInsertMode )
+ {
+ const rtl::Reference<SwXTextRange> xInsertTextRange =
+ SwXTextRange::CreateXTextRange(rDoc, *rPaM.GetPoint(), nullptr);
+ xInfoSet->setPropertyValue( "TextInsertModeRange",
+ Any(uno::Reference<text::XTextRange>(xInsertTextRange)) );
+ }
+ else
+ {
+ rPaM.GetBound().nContent.Assign(nullptr, 0);
+ rPaM.GetBound(false).nContent.Assign(nullptr, 0);
+ }
+
+ if( IsBlockMode() )
+ {
+ xInfoSet->setPropertyValue( "AutoTextMode", Any(true) );
+ }
+ if( IsOrganizerMode() )
+ {
+ xInfoSet->setPropertyValue( "OrganizerMode", Any(true) );
+ }
+
+ // Set base URI
+ // there is ambiguity which medium should be used here
+ // for now the own medium has a preference
+ SfxMedium* pMedDescrMedium = m_pMedium ? m_pMedium : pDocSh->GetMedium();
+ OSL_ENSURE( pMedDescrMedium, "There is no medium to get MediaDescriptor from!" );
+
+ xInfoSet->setPropertyValue( "BaseURI", Any( rBaseURL ) );
+
+ // TODO/LATER: separate links from usual embedded objects
+ OUString StreamPath;
+ if( SfxObjectCreateMode::EMBEDDED == rDoc.GetDocShell()->GetCreateMode() )
+ {
+ if (pMedDescrMedium)
+ {
+ const SfxStringItem* pDocHierarchItem =
+ pMedDescrMedium->GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME);
+ if ( pDocHierarchItem )
+ StreamPath = pDocHierarchItem->GetValue();
+ }
+ else
+ {
+ StreamPath = "dummyObjectName";
+ }
+
+ if( !StreamPath.isEmpty() )
+ {
+ xInfoSet->setPropertyValue( "StreamRelPath", Any( StreamPath ) );
+ }
+ }
+
+ rtl::Reference<SwDoc> aHoldRef(&rDoc); // prevent deletion
+ ErrCodeMsg nRet = ERRCODE_NONE;
+
+ // save redline mode into import info property set
+ static constexpr OUString sShowChanges(u"ShowChanges"_ustr);
+ static constexpr OUString sRecordChanges(u"RecordChanges"_ustr);
+ static constexpr OUString sRedlineProtectionKey(u"RedlineProtectionKey"_ustr);
+ xInfoSet->setPropertyValue( sShowChanges,
+ Any(IDocumentRedlineAccess::IsShowChanges(rDoc.getIDocumentRedlineAccess().GetRedlineFlags())) );
+ xInfoSet->setPropertyValue( sRecordChanges,
+ Any(IDocumentRedlineAccess::IsRedlineOn(rDoc.getIDocumentRedlineAccess().GetRedlineFlags())) );
+ xInfoSet->setPropertyValue( sRedlineProtectionKey,
+ Any(rDoc.getIDocumentRedlineAccess().GetRedlinePassword()) );
+
+ // force redline mode to "none"
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE );
+
+ const bool bOASIS = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
+ // #i28749# - set property <ShapePositionInHoriL2R>
+ {
+ const bool bShapePositionInHoriL2R = !bOASIS;
+ xInfoSet->setPropertyValue(
+ "ShapePositionInHoriL2R",
+ Any( bShapePositionInHoriL2R ) );
+ }
+ {
+ const bool bTextDocInOOoFileFormat = !bOASIS;
+ xInfoSet->setPropertyValue(
+ "TextDocInOOoFileFormat",
+ Any( bTextDocInOOoFileFormat ) );
+ }
+
+ ErrCode nWarnRDF = ERRCODE_NONE;
+ if ( !(IsOrganizerMode() || IsBlockMode() || m_aOption.IsFormatsOnly() ||
+ m_bInsertMode) )
+ {
+ // RDF metadata - must be read before styles/content
+ // N.B.: embedded documents have their own manifest.rdf!
+ try
+ {
+ const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(xModelComp,
+ uno::UNO_QUERY_THROW);
+ const uno::Reference<frame::XModel> xModel(xModelComp,
+ uno::UNO_QUERY_THROW);
+ const uno::Reference<rdf::XURI> xBaseURI( ::sfx2::createBaseURI(
+ xContext, xModel, rBaseURL, StreamPath) );
+ const uno::Reference<task::XInteractionHandler> xHandler(
+ pDocSh->GetMedium()->GetInteractionHandler() );
+ xDMA->loadMetadataFromStorage(xStorage, xBaseURI, xHandler);
+ }
+ catch (const lang::WrappedTargetException & e)
+ {
+ ucb::InteractiveAugmentedIOException iaioe;
+ if (e.TargetException >>= iaioe)
+ {
+ // import error that was not ignored by InteractionHandler!
+ nWarnRDF = ERR_SWG_READ_ERROR;
+ }
+ else
+ {
+ nWarnRDF = WARN_SWG_FEATURES_LOST; // uhh... something wrong?
+ }
+ }
+ catch (uno::Exception &)
+ {
+ nWarnRDF = WARN_SWG_FEATURES_LOST; // uhh... something went wrong?
+ }
+ }
+
+ // read storage streams
+
+ // #i103539#: always read meta.xml for generator
+ ErrCodeMsg const nWarn = ReadThroughComponent(
+ xStorage, xModelComp, "meta.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaImporter"
+ : "com.sun.star.comp.Writer.XMLMetaImporter"),
+ aEmptyArgs, rName, false );
+
+ ErrCodeMsg nWarn2 = ERRCODE_NONE;
+ if( !(IsOrganizerMode() || IsBlockMode() || m_aOption.IsFormatsOnly() ||
+ m_bInsertMode) )
+ {
+ nWarn2 = ReadThroughComponent(
+ xStorage, xModelComp, "settings.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsImporter"
+ : "com.sun.star.comp.Writer.XMLSettingsImporter"),
+ aFilterArgs, rName, false );
+ }
+
+ nRet = ReadThroughComponent(
+ xStorage, xModelComp, "styles.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesImporter"
+ : "com.sun.star.comp.Writer.XMLStylesImporter"),
+ aFilterArgs, rName, true );
+
+ if( !nRet && !(IsOrganizerMode() || m_aOption.IsFormatsOnly()) )
+ nRet = ReadThroughComponent(
+ xStorage, xModelComp, "content.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentImporter"
+ : "com.sun.star.comp.Writer.XMLContentImporter"),
+ aFilterArgs, rName, true );
+
+ if( !IsOrganizerMode() && !IsBlockMode() && !m_bInsertMode &&
+ !m_aOption.IsFormatsOnly() &&
+ // sw_redlinehide: disable layout cache for now
+ *o3tl::doAccess<bool>(xInfoSet->getPropertyValue(sShowChanges)) &&
+ // sw_fieldmarkhide: also disable if there is a fieldmark
+ rDoc.getIDocumentMarkAccess()->getFieldmarksBegin()
+ == rDoc.getIDocumentMarkAccess()->getFieldmarksEnd())
+ {
+ try
+ {
+ uno::Reference < io::XStream > xStm = xStorage->openStreamElement( "layout-cache", embed::ElementModes::READ );
+ std::unique_ptr<SvStream> pStrm2 = utl::UcbStreamHelper::CreateStream( xStm );
+ if( !pStrm2->GetError() )
+ rDoc.ReadLayoutCache( *pStrm2 );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ // Notify math objects
+ if( m_bInsertMode )
+ rDoc.PrtOLENotify( false );
+ else if ( rDoc.IsOLEPrtNotifyPending() )
+ rDoc.PrtOLENotify( true );
+
+ nRet = nRet ? nRet : (nWarn ? nWarn : (nWarn2 ? nWarn2 : nWarnRDF ) );
+
+ ::svx::DropUnusedNamedItems(xModelComp);
+
+ m_aOption.ResetAllFormatsOnly();
+
+ // redline password
+ Any aAny = xInfoSet->getPropertyValue( sRedlineProtectionKey );
+ Sequence<sal_Int8> aKey;
+ aAny >>= aKey;
+ rDoc.getIDocumentRedlineAccess().SetRedlinePassword( aKey );
+
+ // restore redline mode from import info property set
+ RedlineFlags nRedlineFlags = RedlineFlags::ShowInsert;
+ aAny = xInfoSet->getPropertyValue( sShowChanges );
+ if ( *o3tl::doAccess<bool>(aAny) )
+ nRedlineFlags |= RedlineFlags::ShowDelete;
+ aAny = xInfoSet->getPropertyValue( sRecordChanges );
+ if ( *o3tl::doAccess<bool>(aAny) || aKey.hasElements() )
+ nRedlineFlags |= RedlineFlags::On;
+
+ // ... restore redline mode
+ // (First set bogus mode to make sure the mode in getIDocumentRedlineAccess().SetRedlineFlags()
+ // is different from its previous mode.)
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ~nRedlineFlags );
+ // must set flags to show delete so that CompressRedlines works
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags(nRedlineFlags|RedlineFlags::ShowDelete);
+ // tdf#83260 ensure that the first call of CompressRedlines after loading
+ // the document is a no-op by calling it now
+ rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ // can't set it on the layout or view shell because it doesn't exist yet
+ rDoc.GetDocumentRedlineManager().SetHideRedlines(!(nRedlineFlags & RedlineFlags::ShowDelete));
+
+ lcl_EnsureValidPam( rPaM ); // move Pam into valid content
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+ xGraphicStorageHandler = nullptr;
+ if( xObjectHelper )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+ xObjectResolver = nullptr;
+ aHoldRef.clear();
+
+ if ( !bOASIS )
+ {
+ // #i44177# - assure that for documents in OpenOffice.org
+ // file format the relation between outline numbering rule and styles is
+ // filled-up accordingly.
+ // Note: The OpenOffice.org file format, which has no content that applies
+ // a certain style, which is related to the outline numbering rule,
+ // has lost the information, that this certain style is related to
+ // the outline numbering rule.
+ // #i70748# - only for templates
+ if ( m_pMedium && m_pMedium->GetFilter() &&
+ m_pMedium->GetFilter()->IsOwnTemplateFormat() )
+ {
+ lcl_AdjustOutlineStylesForOOo( rDoc );
+ }
+ // Fix #i58251#: Unfortunately is the static default different to SO7 behaviour,
+ // so we have to set a dynamic default after importing SO7
+ rDoc.SetDefault(SwFormatRowSplit(false));
+ }
+
+ rDoc.PropagateOutlineRule();
+
+ // #i62875#
+ if ( rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) && !docfunc::ExistsDrawObjs( rDoc ) )
+ {
+ rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, false);
+ }
+
+ // Convert all instances of <SdrOle2Obj> into <SdrGrafObj>, because the
+ // Writer doesn't support such objects.
+ lcl_ConvertSdrOle2ObjsToSdrGrafObjs( rDoc );
+
+ // set BuildId on XModel for later OLE object loading
+ if( xInfoSet.is() )
+ {
+ uno::Reference< beans::XPropertySet > xModelSet( xModelComp, uno::UNO_QUERY );
+ if( xModelSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() );
+ static constexpr OUString sName(u"BuildId"_ustr );
+ if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sName) )
+ {
+ xModelSet->setPropertyValue( sName, xInfoSet->getPropertyValue(sName) );
+ }
+ }
+ }
+
+ // tdf#115815 restore annotation ranges stored in temporary bookmarks
+ rDoc.getIDocumentMarkAccess()->restoreAnnotationMarks();
+
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->end();
+ }
+
+ rDoc.GetIStyleAccess().clearCaches(); // Clear Automatic-Style-Caches(shared_pointer!)
+ return nRet;
+}
+
+ // read the sections of the document, which is equal to the medium.
+ // returns the count of it
+size_t XMLReader::GetSectionList( SfxMedium& rMedium,
+ std::vector<OUString>& rStrings) const
+{
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+ uno::Reference < embed::XStorage > xStg2;
+ if( ( xStg2 = rMedium.GetStorage() ).is() )
+ {
+ try
+ {
+ xml::sax::InputSource aParserInput;
+ static constexpr OUString sDocName( u"content.xml"_ustr );
+ aParserInput.sSystemId = sDocName;
+
+ uno::Reference < io::XStream > xStm = xStg2->openStreamElement( sDocName, embed::ElementModes::READ );
+ aParserInput.aInputStream = xStm->getInputStream();
+
+ // get filter
+ rtl::Reference< SwXMLSectionList > xImport = new SwXMLSectionList( xContext, rStrings );
+
+ // parse
+ xImport->parseStream( aParserInput );
+ }
+ catch( xml::sax::SAXParseException& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ // re throw ?
+ }
+ catch( xml::sax::SAXException& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ // re throw ?
+ }
+ catch( io::IOException& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ // re throw ?
+ }
+ catch( packages::WrongPasswordException& )
+ {
+ // re throw ?
+ }
+ }
+ return rStrings.size();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/wrtxml.cxx b/sw/source/filter/xml/wrtxml.cxx
new file mode 100644
index 0000000000..bfb1366566
--- /dev/null
+++ b/sw/source/filter/xml/wrtxml.cxx
@@ -0,0 +1,586 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/frame/XModule.hpp>
+
+#include <officecfg/Office/Common.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <vcl/errinf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <svx/xmlgrhlp.hxx>
+#include <svx/xmleohlp.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <docfunc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <docstat.hxx>
+#include <docsh.hxx>
+
+#include <xmloff/shapeexport.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <swerror.h>
+#include "wrtxml.hxx"
+#include "zorder.hxx"
+#include <strings.hrc>
+
+#include <comphelper/documentconstants.hxx>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+SwXMLWriter::SwXMLWriter( const OUString& rBaseURL )
+{
+ SetBaseURL( rBaseURL );
+}
+
+SwXMLWriter::~SwXMLWriter()
+{
+}
+
+ErrCodeMsg SwXMLWriter::Write_(const SfxItemSet* pMediumItemSet)
+{
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ OUString aDocHierarchicalName;
+ bool bNoEmbDS(false);
+
+ if (pMediumItemSet)
+ {
+ const SfxUnoAnyItem* pStatusBarItem =
+ pMediumItemSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pStatusBarItem)
+ pStatusBarItem->GetValue() >>= xStatusIndicator;
+ const SfxStringItem* pDocHierarchItem =
+ pMediumItemSet->GetItem(SID_DOC_HIERARCHICALNAME);
+ if (pDocHierarchItem)
+ aDocHierarchicalName = pDocHierarchItem->GetValue();
+ const SfxBoolItem* pNoEmbDS = pMediumItemSet->GetItem(SID_NO_EMBEDDED_DS);
+ if (pNoEmbDS)
+ bNoEmbDS = pNoEmbDS->GetValue();
+ }
+
+ // Get service factory
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ // Get data sink ...
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper ;
+ uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ OSL_ENSURE( m_xStg.is(), "Where is my storage?" );
+ xGraphicHelper = SvXMLGraphicHelper::Create( m_xStg,
+ SvXMLGraphicHelperMode::Write );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ SfxObjectShell *pPersist = m_pDoc->GetPersist();
+ if( pPersist )
+ {
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create(
+ m_xStg, *pPersist,
+ SvXMLEmbeddedObjectHelperMode::Write );
+ xObjectResolver = xObjectHelper.get();
+ }
+
+ // create and prepare the XPropertySet that gets passed through
+ // the components, and the XStatusIndicator that shows progress to
+ // the user.
+
+ // create XPropertySet with three properties for status indicator
+ static comphelper::PropertyMapEntry const aInfoMap[] =
+ {
+ { OUString("ProgressRange"), 0,
+ ::cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressMax"), 0,
+ ::cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressCurrent"), 0,
+ ::cppu::UnoType<sal_Int32>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("WrittenNumberStyles"), 0,
+ cppu::UnoType<uno::Sequence<sal_Int32>>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("UsePrettyPrinting"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ShowChanges"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("RedlineProtectionKey"), 0,
+ cppu::UnoType<Sequence<sal_Int8>>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0,
+ ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("AutoTextMode"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleNames"), 0,
+ cppu::UnoType<Sequence<OUString>>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleFamilies"), 0,
+ cppu::UnoType<Sequence<sal_Int32>>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ // #i69627#
+ { OUString("OutlineStyleAsNormalListStyle"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("TargetStorage"),0, cppu::UnoType<embed::XStorage>::get(),
+ css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ // tdf#144532
+ { OUString("NoEmbDataSet"), 0,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(
+ new comphelper::PropertySetInfo( aInfoMap ) ) );
+
+ xInfoSet->setPropertyValue( "TargetStorage", Any( m_xStg ) );
+
+ xInfoSet->setPropertyValue("NoEmbDataSet", Any(bNoEmbDS));
+
+ if (m_bShowProgress)
+ {
+ // set progress range and start status indicator
+ sal_Int32 nProgressRange(1000000);
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start(SwResId( STR_STATSTR_SWGWRITE),
+ nProgressRange);
+ }
+ xInfoSet->setPropertyValue("ProgressRange", Any(nProgressRange));
+
+ xInfoSet->setPropertyValue("ProgressMax", Any(static_cast < sal_Int32 >( -1 )));
+ }
+
+ xInfoSet->setPropertyValue( "UsePrettyPrinting", Any(officecfg::Office::Common::Save::Document::PrettyPrinting::get()) );
+
+ uno::Reference<lang::XComponent> const xModelComp(m_pDoc->GetDocShell()->GetModel());
+ uno::Reference<drawing::XDrawPageSupplier> const xDPS(xModelComp, uno::UNO_QUERY);
+ assert(xDPS.is());
+ xmloff::FixZOrder(xDPS->getDrawPage(), sw::GetZOrderLayer(m_pDoc->getIDocumentDrawModelAccess()));
+
+ // save show redline mode ...
+ RedlineFlags const nOrigRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ RedlineFlags nRedlineFlags(nOrigRedlineFlags);
+ bool isShowChanges;
+ // TODO: ideally this would be stored per-view...
+ SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ isShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines();
+ xInfoSet->setPropertyValue("ShowChanges", Any(isShowChanges));
+ // ... and hide redlines for export
+ nRedlineFlags &= ~RedlineFlags::ShowMask;
+ nRedlineFlags |= RedlineFlags::ShowInsert;
+ m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags );
+
+ // Set base URI
+ xInfoSet->setPropertyValue( "BaseURI", Any( GetBaseURL() ) );
+
+ if( SfxObjectCreateMode::EMBEDDED == m_pDoc->GetDocShell()->GetCreateMode() )
+ {
+ const OUString aName( !aDocHierarchicalName.isEmpty()
+ ? aDocHierarchicalName
+ : OUString( "dummyObjectName" ) );
+
+ xInfoSet->setPropertyValue( "StreamRelPath", Any( aName ) );
+ }
+
+ if( m_bBlock )
+ {
+ xInfoSet->setPropertyValue( "AutoTextMode", Any(true) );
+ }
+
+ // #i69627#
+ const bool bOASIS = ( SotStorage::GetVersion( m_xStg ) > SOFFICE_FILEFORMAT_60 );
+ if ( bOASIS &&
+ docfunc::HasOutlineStyleToBeWrittenAsNormalListStyle( *m_pDoc ) )
+ {
+ xInfoSet->setPropertyValue( "OutlineStyleAsNormalListStyle", Any( true ) );
+ }
+
+ // filter arguments
+ // - graphics + object resolver for styles + content
+ // - status indicator
+ // - info property set
+ // - else empty
+ sal_Int32 nArgs = 1;
+ if( xStatusIndicator.is() )
+ nArgs++;
+
+ Sequence < Any > aEmptyArgs( nArgs );
+ Any *pArgs = aEmptyArgs.getArray();
+ *pArgs++ <<= xInfoSet;
+ if( xStatusIndicator.is() )
+ *pArgs++ <<= xStatusIndicator;
+
+ if( xGraphicStorageHandler.is() )
+ nArgs++;
+ if( xObjectResolver.is() )
+ nArgs++;
+
+ Sequence < Any > aFilterArgs( nArgs );
+ pArgs = aFilterArgs.getArray();
+ *pArgs++ <<= xInfoSet;
+ if( xGraphicStorageHandler.is() )
+ *pArgs++ <<= xGraphicStorageHandler;
+ if( xObjectResolver.is() )
+ *pArgs++ <<= xObjectResolver;
+ if( xStatusIndicator.is() )
+ *pArgs++ <<= xStatusIndicator;
+
+ PutNumFormatFontsInAttrPool();
+ PutEditEngFontsInAttrPool();
+
+ // properties
+ Sequence < PropertyValue > aProps( m_pOrigFileName ? 1 : 0 );
+ if( m_pOrigFileName )
+ {
+ PropertyValue *pProps = aProps.getArray();
+ pProps->Name = "FileName";
+ pProps->Value <<= *m_pOrigFileName;
+ }
+
+ // export sub streams for package, else full stream into a file
+ bool bWarn = false;
+
+ // RDF metadata: export if ODF >= 1.2
+ // N.B.: embedded documents have their own manifest.rdf!
+ if ( bOASIS )
+ {
+ const uno::Reference<beans::XPropertySet> xPropSet(m_xStg,
+ uno::UNO_QUERY_THROW);
+ try
+ {
+ OUString Version;
+ // ODF >= 1.2
+ if ((xPropSet->getPropertyValue("Version") >>= Version)
+ && Version != ODFVER_010_TEXT
+ && Version != ODFVER_011_TEXT)
+ {
+ const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(
+ xModelComp, uno::UNO_QUERY_THROW);
+ xDMA->storeMetadataToStorage(m_xStg);
+ }
+ }
+ catch (beans::UnknownPropertyException &)
+ { /* ignore */ }
+ catch (uno::Exception &)
+ {
+ bWarn = true;
+ }
+ }
+
+ bool bStoreMeta = ( SfxObjectCreateMode::EMBEDDED != m_pDoc->GetDocShell()->GetCreateMode() );
+ if ( !bStoreMeta )
+ {
+ try
+ {
+ Reference< frame::XModule > xModule( xModelComp, UNO_QUERY );
+ if ( xModule.is() )
+ {
+ const OUString aModuleID = xModule->getIdentifier();
+ bStoreMeta = !aModuleID.isEmpty() &&
+ ( aModuleID == "com.sun.star.sdb.FormDesign" ||
+ aModuleID == "com.sun.star.sdb.TextReportDesign" );
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ OUString sWarnFile;
+ if( !m_bOrganizerMode && !m_bBlock && bStoreMeta )
+ {
+ if( !WriteThroughComponent(
+ xModelComp, "meta.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaExporter"
+ : "com.sun.star.comp.Writer.XMLMetaExporter"),
+ aEmptyArgs, aProps ) )
+ {
+ bWarn = true;
+ sWarnFile = "meta.xml";
+ }
+ }
+
+ if( !m_bBlock )
+ {
+ if( !WriteThroughComponent(
+ xModelComp, "settings.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsExporter"
+ : "com.sun.star.comp.Writer.XMLSettingsExporter"),
+ aEmptyArgs, aProps ) )
+ {
+ if( !bWarn )
+ {
+ bWarn = true;
+ sWarnFile = "settings.xml";
+ }
+ }
+ }
+
+ bool bErr = false;
+
+ OUString sErrFile;
+ if( !WriteThroughComponent(
+ xModelComp, "styles.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesExporter"
+ : "com.sun.star.comp.Writer.XMLStylesExporter"),
+ aFilterArgs, aProps ) )
+ {
+ bErr = true;
+ sErrFile = "styles.xml";
+ }
+
+ if( !m_bOrganizerMode && !bErr )
+ {
+ if( !WriteThroughComponent(
+ xModelComp, "content.xml", xContext,
+ (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentExporter"
+ : "com.sun.star.comp.Writer.XMLContentExporter"),
+ aFilterArgs, aProps ) )
+ {
+ bErr = true;
+ sErrFile = "content.xml";
+ }
+ }
+
+ if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && m_pDoc->getIDocumentStatistics().GetDocStat().nPage > 1 &&
+ !(m_bOrganizerMode || m_bBlock || bErr ||
+ // sw_redlinehide: disable layout cache for now
+ m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas()))
+ {
+ try
+ {
+ uno::Reference < io::XStream > xStm = m_xStg->openStreamElement( "layout-cache", embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream( xStm );
+ if( !pStream->GetError() )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xStm, UNO_QUERY );
+ uno::Any aAny2;
+ aAny2 <<= OUString("application/binary");
+ xSet->setPropertyValue("MediaType", aAny2 );
+ m_pDoc->WriteLayoutCache( *pStream );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+ xGraphicStorageHandler = nullptr;
+
+ if( xObjectHelper )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+ xObjectResolver = nullptr;
+
+ // restore redline mode
+ nRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ nRedlineFlags &= ~RedlineFlags::ShowMask;
+ nRedlineFlags |= RedlineFlags::ShowInsert;
+ nRedlineFlags |= nOrigRedlineFlags & RedlineFlags::ShowMask;
+ m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags );
+
+ // tdf#115815 restore annotation ranges collapsed by hide redlines
+ m_pDoc->getIDocumentMarkAccess()->restoreAnnotationMarks();
+
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->end();
+ }
+
+ if( bErr )
+ {
+ if( !sErrFile.isEmpty() )
+ return ErrCodeMsg( ERR_WRITE_ERROR_FILE, sErrFile,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ return ERR_SWG_WRITE_ERROR;
+ }
+ else if( bWarn )
+ {
+ if( !sWarnFile.isEmpty() )
+ return ErrCodeMsg( WARN_WRITE_ERROR_FILE, sWarnFile,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ return WARN_SWG_FEATURES_LOST;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCodeMsg SwXMLWriter::WriteStorage()
+{
+ return Write_(nullptr);
+}
+
+ErrCodeMsg SwXMLWriter::WriteMedium( SfxMedium& aTargetMedium )
+{
+ return Write_(&aTargetMedium.GetItemSet());
+}
+
+ErrCodeMsg SwXMLWriter::Write( SwPaM& rPaM, SfxMedium& rMed,
+ const OUString* pFileName )
+{
+ return IsStgWriter()
+ ? static_cast<StgWriter *>(this)->Write( rPaM, rMed.GetOutputStorage(), pFileName, &rMed )
+ : static_cast<Writer *>(this)->Write( rPaM, *rMed.GetOutStream(), pFileName );
+}
+
+bool SwXMLWriter::WriteThroughComponent(
+ const uno::Reference<XComponent> & xComponent,
+ const char* pStreamName,
+ const uno::Reference<uno::XComponentContext> & rxContext,
+ const char* pServiceName,
+ const Sequence<Any> & rArguments,
+ const Sequence<beans::PropertyValue> & rMediaDesc )
+{
+ OSL_ENSURE( m_xStg.is(), "Need storage!" );
+ OSL_ENSURE( nullptr != pStreamName, "Need stream name!" );
+ OSL_ENSURE( nullptr != pServiceName, "Need service name!" );
+
+ SAL_INFO( "sw.filter", "SwXMLWriter::WriteThroughComponent : stream " << pStreamName );
+ // open stream
+ bool bRet = false;
+ try
+ {
+ const OUString sStreamName = OUString::createFromAscii( pStreamName );
+ uno::Reference<io::XStream> xStream =
+ m_xStg->openStreamElement( sStreamName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+
+ uno::Reference <beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ if( !xSet.is() )
+ return false;
+
+ xSet->setPropertyValue("MediaType", Any(OUString("text/xml")) );
+
+ // even plain stream should be encrypted in encrypted documents
+ xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
+
+ // set buffer and create outputstream
+ uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream();
+
+ // set Base URL
+ uno::Reference< beans::XPropertySet > xInfoSet;
+ if( rArguments.hasElements() )
+ rArguments.getConstArray()[0] >>= xInfoSet;
+ OSL_ENSURE( xInfoSet.is(), "missing property set" );
+ if( xInfoSet.is() )
+ {
+ xInfoSet->setPropertyValue( "StreamName", Any( sStreamName ) );
+ }
+
+ // write the stuff
+ bRet = WriteThroughComponent(
+ xOutputStream, xComponent, rxContext,
+ pServiceName, rArguments, rMediaDesc );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ return bRet;
+
+}
+
+bool SwXMLWriter::WriteThroughComponent(
+ const uno::Reference<io::XOutputStream> & xOutputStream,
+ const uno::Reference<XComponent> & xComponent,
+ const uno::Reference<XComponentContext> & rxContext,
+ const char* pServiceName,
+ const Sequence<Any> & rArguments,
+ const Sequence<PropertyValue> & rMediaDesc )
+{
+ OSL_ENSURE( xOutputStream.is(), "I really need an output stream!" );
+ OSL_ENSURE( xComponent.is(), "Need component!" );
+ OSL_ENSURE( nullptr != pServiceName, "Need component name!" );
+
+ // get component
+ uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext);
+ SAL_INFO( "sw.filter", "SAX-Writer created" );
+ // connect XML writer to output stream
+ xSaxWriter->setOutputStream( xOutputStream );
+
+ // prepare arguments (prepend doc handler to given arguments)
+ Sequence<Any> aArgs( 1 + rArguments.getLength() );
+ auto pArgs = aArgs.getArray();
+ *pArgs <<= xSaxWriter;
+ std::copy(rArguments.begin(), rArguments.end(), std::next(pArgs));
+
+ // get filter component
+ uno::Reference< document::XExporter > xExporter(
+ rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pServiceName), aArgs, rxContext), UNO_QUERY);
+ OSL_ENSURE( xExporter.is(),
+ "can't instantiate export filter component" );
+ if( !xExporter.is() )
+ return false;
+ SAL_INFO( "sw.filter", pServiceName << " instantiated." );
+ // connect model and filter
+ xExporter->setSourceDocument( xComponent );
+
+ // filter!
+ SAL_INFO( "sw.filter", "call filter()" );
+ uno::Reference<XFilter> xFilter( xExporter, UNO_QUERY );
+ return xFilter->filter( rMediaDesc );
+}
+
+void GetXMLWriter(
+ [[maybe_unused]] std::u16string_view /*rName*/, const OUString& rBaseURL, WriterRef& xRet )
+{
+ xRet = new SwXMLWriter( rBaseURL );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/wrtxml.hxx b/sw/source/filter/xml/wrtxml.hxx
new file mode 100644
index 0000000000..abbdd7aa7b
--- /dev/null
+++ b/sw/source/filter/xml/wrtxml.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <shellio.hxx>
+
+class SwPaM;
+class SfxMedium;
+
+namespace com::sun::star {
+ namespace uno { template<class A> class Reference; }
+ namespace uno { template<class A> class Sequence; }
+ namespace uno { class Any; }
+ namespace lang { class XComponent; }
+ namespace lang { class XMultiServiceFactory; }
+ namespace beans { struct PropertyValue; }
+}
+
+class SwXMLWriter : public StgWriter
+{
+ ErrCodeMsg Write_(const SfxItemSet* pMediumItemSet);
+
+ using StgWriter::Write;
+
+protected:
+ virtual ErrCodeMsg WriteStorage() override;
+ virtual ErrCodeMsg WriteMedium( SfxMedium& aTargetMedium ) override;
+
+public:
+
+ SwXMLWriter( const OUString& rBaseURL );
+ virtual ~SwXMLWriter() override;
+
+ virtual ErrCodeMsg Write( SwPaM&, SfxMedium&, const OUString* ) override;
+
+private:
+
+ // helper methods to write XML streams
+
+ // write a single XML stream into the package
+ bool WriteThroughComponent(
+ // the component we export
+ const css::uno::Reference<css::lang::XComponent> & xComponent,
+ const char* pStreamName, // the stream name
+ // service factory for pServiceName
+ const css::uno::Reference<css::uno::XComponentContext> & rFactory,
+ const char* pServiceName, // service name of the component
+ // the argument (XInitialization)
+ const css::uno::Sequence<css::uno::Any> & rArguments,
+ // output descriptor
+ const css::uno::Sequence<css::beans::PropertyValue> & rMediaDesc );
+
+ // write a single output stream
+ // (to be called either directly or by WriteThroughComponent(...))
+ static bool WriteThroughComponent(
+ const css::uno::Reference<css::io::XOutputStream> & xOutputStream,
+ const css::uno::Reference<css::lang::XComponent> & xComponent,
+ const css::uno::Reference<css::uno::XComponentContext> & rFactory,
+ const char* pServiceName,
+ const css::uno::Sequence<css::uno::Any> & rArguments,
+ const css::uno::Sequence<css::beans::PropertyValue> & rMediaDesc );
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlbrsh.cxx b/sw/source/filter/xml/xmlbrsh.cxx
new file mode 100644
index 0000000000..b31dc0cfa4
--- /dev/null
+++ b/sw/source/filter/xml/xmlbrsh.cxx
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/memberids.h>
+#include <vcl/graph.hxx>
+
+#include <sal/log.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlimp.hxx>
+#include <xmloff/XMLBase64ImportContext.hxx>
+#include <editeng/brushitem.hxx>
+#include <xmloff/xmluconv.hxx>
+
+#include "xmlbrshi.hxx"
+#include "xmlbrshe.hxx"
+#include "xmlexp.hxx"
+#include "xmlimpit.hxx"
+#include "xmlexpit.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::xmloff::token;
+
+void SwXMLBrushItemImportContext::ProcessAttrs(
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ const SvXMLUnitConverter& rUnitConv )
+{
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ const OUString sValue = aIter.toString();
+
+ switch( aIter.getToken() )
+ {
+ case XML_ELEMENT(XLINK, XML_HREF):
+ m_xGraphic = GetImport().loadGraphicByURL(sValue);
+ break;
+ case XML_ELEMENT(XLINK, XML_TYPE):
+ case XML_ELEMENT(XLINK, XML_ACTUATE):
+ case XML_ELEMENT(XLINK, XML_SHOW):
+ break;
+ case XML_ELEMENT(STYLE, XML_POSITION):
+ SvXMLImportItemMapper::PutXMLValue(
+ *m_pItem, sValue, MID_GRAPHIC_POSITION, rUnitConv );
+ break;
+ case XML_ELEMENT(STYLE, XML_REPEAT):
+ SvXMLImportItemMapper::PutXMLValue(
+ *m_pItem, sValue, MID_GRAPHIC_REPEAT, rUnitConv );
+ break;
+ case XML_ELEMENT(STYLE, XML_FILTER_NAME):
+ SvXMLImportItemMapper::PutXMLValue(
+ *m_pItem, sValue, MID_GRAPHIC_FILTER, rUnitConv );
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ }
+ }
+
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBrushItemImportContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ if ((nElement & TOKEN_MASK) == xmloff::token::XML_BINARY_DATA)
+ {
+ if (!m_xBase64Stream.is())
+ {
+ m_xBase64Stream = GetImport().GetStreamForGraphicObjectURLFromBase64();
+ if (m_xBase64Stream.is())
+ return new XMLBase64ImportContext(GetImport(), m_xBase64Stream);
+ }
+ }
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement);
+ return nullptr;
+}
+
+void SwXMLBrushItemImportContext::endFastElement(sal_Int32 )
+{
+ if (m_xBase64Stream.is())
+ {
+ m_xGraphic = GetImport().loadGraphicFromBase64(m_xBase64Stream);
+ m_xBase64Stream = nullptr;
+ }
+
+ if (m_xGraphic.is())
+ {
+ Graphic aGraphic(m_xGraphic);
+ SvxGraphicPosition eOldGraphicPos = m_pItem->GetGraphicPos();
+ m_pItem->SetGraphic(aGraphic);
+ if (GPOS_NONE == eOldGraphicPos && GPOS_NONE != m_pItem->GetGraphicPos())
+ m_pItem->SetGraphicPos(GPOS_TILED);
+ }
+
+ if (!(m_pItem->GetGraphic()))
+ m_pItem->SetGraphicPos(GPOS_NONE);
+ else if (GPOS_NONE == m_pItem->GetGraphicPos())
+ m_pItem->SetGraphicPos(GPOS_TILED);
+}
+
+SwXMLBrushItemImportContext::SwXMLBrushItemImportContext(
+ SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ const SvXMLUnitConverter& rUnitConv,
+ const SvxBrushItem& rItem ) :
+ SvXMLImportContext( rImport ),
+ m_pItem( new SvxBrushItem( rItem ) )
+{
+ // delete any graphic that is existing
+ m_pItem->SetGraphicPos( GPOS_NONE );
+
+ ProcessAttrs( xAttrList, rUnitConv );
+}
+
+SwXMLBrushItemImportContext::SwXMLBrushItemImportContext(
+ SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList,
+ const SvXMLUnitConverter& rUnitConv,
+ sal_uInt16 nWhich ) :
+ SvXMLImportContext( rImport ),
+ m_pItem( new SvxBrushItem( nWhich ) )
+{
+ ProcessAttrs( xAttrList, rUnitConv );
+}
+
+SwXMLBrushItemImportContext::~SwXMLBrushItemImportContext()
+{
+}
+
+SwXMLBrushItemExport::SwXMLBrushItemExport( SwXMLExport& rExp ) :
+ m_rExport( rExp )
+{
+}
+
+void SwXMLBrushItemExport::exportXML( const SvxBrushItem& rItem )
+{
+ GetExport().CheckAttrList();
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ const Graphic* pGraphic = rItem.GetGraphic();
+
+ if (pGraphic)
+ xGraphic = pGraphic->GetXGraphic();
+
+ if (xGraphic.is())
+ {
+ OUString sMimeType;
+ OUString sValue = GetExport().AddEmbeddedXGraphic(xGraphic, sMimeType);
+ if (!sValue.isEmpty())
+ {
+ GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sValue );
+ GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
+ GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD );
+ }
+
+ const SvXMLUnitConverter& rUnitConv = GetExport().GetTwipUnitConverter();
+ if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_POSITION, rUnitConv))
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_POSITION, sValue );
+
+ if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_REPEAT, rUnitConv))
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REPEAT, sValue );
+
+ if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_FILTER, rUnitConv))
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_FILTER_NAME, sValue );
+ }
+
+ {
+ SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE, XML_BACKGROUND_IMAGE,
+ true, true );
+ if (xGraphic.is())
+ {
+ // optional office:binary-data
+ GetExport().AddEmbeddedXGraphicAsBase64(xGraphic);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlbrshe.hxx b/sw/source/filter/xml/xmlbrshe.hxx
new file mode 100644
index 0000000000..0253a6f068
--- /dev/null
+++ b/sw/source/filter/xml/xmlbrshe.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX
+
+class SvxBrushItem;
+class SwXMLExport;
+
+class SwXMLBrushItemExport
+{
+ SwXMLExport& m_rExport;
+
+ SwXMLExport& GetExport() { return m_rExport; }
+
+public:
+ explicit SwXMLBrushItemExport(SwXMLExport& rExport);
+
+ // core API
+ void exportXML(const SvxBrushItem& rItem);
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlbrshi.hxx b/sw/source/filter/xml/xmlbrshi.hxx
new file mode 100644
index 0000000000..0e5cb43c84
--- /dev/null
+++ b/sw/source/filter/xml/xmlbrshi.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHI_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHI_HXX
+
+#include <memory>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+
+#include <xmloff/xmlictxt.hxx>
+
+class SvXMLImport;
+class SvXMLUnitConverter;
+class SvxBrushItem;
+
+namespace com::sun::star {
+ namespace io { class XOutputStream; }
+}
+
+class SwXMLBrushItemImportContext : public SvXMLImportContext
+{
+private:
+ css::uno::Reference<css::io::XOutputStream> m_xBase64Stream;
+ css::uno::Reference<css::graphic::XGraphic> m_xGraphic;
+
+ std::unique_ptr<SvxBrushItem> m_pItem;
+
+ void ProcessAttrs(
+ const css::uno::Reference<css::xml::sax::XFastAttributeList > & xAttrList,
+ const SvXMLUnitConverter& rUnitConv );
+
+public:
+
+ SwXMLBrushItemImportContext(
+ SvXMLImport& rImport,
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList > & xAttrList,
+ const SvXMLUnitConverter& rUnitConv,
+ const SvxBrushItem& rItem );
+
+ SwXMLBrushItemImportContext(
+ SvXMLImport& rImport,
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList > & xAttrList,
+ const SvXMLUnitConverter& rUnitConv,
+ sal_uInt16 nWhich );
+
+ virtual ~SwXMLBrushItemImportContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ const SvxBrushItem& GetItem() const { return *m_pItem; }
+};
+
+#endif // _XMLBRSHI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlexp.cxx b/sw/source/filter/xml/xmlexp.cxx
new file mode 100644
index 0000000000..434eb8c073
--- /dev/null
+++ b/sw/source/filter/xml/xmlexp.cxx
@@ -0,0 +1,623 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/any.hxx>
+#include <sax/tools/converter.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/xmleohlp.hxx>
+#include <svx/xmlgrhlp.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svddef.hxx>
+#include <tools/UnitConversion.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xformsexport.hxx>
+#include <drawdoc.hxx>
+#include <doc.hxx>
+#include <swmodule.hxx>
+#include <docsh.hxx>
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <docstat.hxx>
+#include <swerror.h>
+#include <unotext.hxx>
+#include "xmltexte.hxx"
+#include "xmlexp.hxx"
+#include "xmlexpit.hxx"
+#include "zorder.hxx"
+#include <comphelper/processfactory.hxx>
+#include <docary.hxx>
+#include <frameformats.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <vcl/svapp.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+
+#include <pausethreadstarting.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::xforms;
+using namespace ::xmloff::token;
+
+SwXMLExport::SwXMLExport(
+ const uno::Reference< uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlags)
+: SvXMLExport( rContext, implementationName, util::MeasureUnit::INCH, XML_TEXT,
+ nExportFlags ),
+ m_bBlock( false ),
+ m_bShowProgress( true ),
+ m_bSavedShowChanges( false ),
+ m_pDoc( nullptr )
+{
+ InitItemExport();
+}
+
+ErrCode SwXMLExport::exportDoc( enum XMLTokenEnum eClass )
+{
+ if( !GetModel().is() )
+ return ERR_SWG_WRITE_ERROR;
+
+ SwPauseThreadStarting aPauseThreadStarting; // #i73788#
+
+ // from here, we use core interfaces -> lock Solar-Mutex
+ SolarMutexGuard aGuard;
+
+ {
+ Reference<XPropertySet> rInfoSet = getExportInfo();
+ if( rInfoSet.is() )
+ {
+ static constexpr OUString sAutoTextMode(u"AutoTextMode"_ustr);
+ if( rInfoSet->getPropertySetInfo()->hasPropertyByName(
+ sAutoTextMode ) )
+ {
+ Any aAny = rInfoSet->getPropertyValue(sAutoTextMode);
+ if( auto b = o3tl::tryAccess<bool>(aAny) )
+ {
+ if( *b )
+ m_bBlock = true;
+ }
+ }
+ }
+ }
+
+ SwDoc *pDoc = getDoc();
+ if (!pDoc)
+ return ERR_SWG_WRITE_ERROR;
+
+ if( getExportFlags() & (SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::STYLES|
+ SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT))
+ {
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ GetNamespaceMap_().Add(
+ GetXMLToken(XML_NP_OFFICE_EXT),
+ GetXMLToken(XML_N_OFFICE_EXT),
+ XML_NAMESPACE_OFFICE_EXT);
+ }
+
+ GetTextParagraphExport()->SetBlockMode( m_bBlock );
+
+ const SfxItemPool& rPool = pDoc->GetAttrPool();
+ sal_uInt16 aWhichIds[5] = { RES_UNKNOWNATR_CONTAINER,
+ RES_TXTATR_UNKNOWN_CONTAINER,
+ SDRATTR_XMLATTRIBUTES,
+ EE_PARA_XMLATTRIBS,
+ EE_CHAR_XMLATTRIBS };
+
+ const int nWhichIds = rPool.GetSecondaryPool() ? 5 : 2;
+ for( int j=0; j < nWhichIds; ++j )
+ {
+ const sal_uInt16 nWhichId = aWhichIds[j];
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId))
+ {
+ auto pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>( pItem );
+ OSL_ENSURE( pUnknown, "illegal attribute container item" );
+ if( pUnknown && (pUnknown->GetAttrCount() > 0) )
+ {
+ sal_uInt16 nIdx = pUnknown->GetFirstNamespaceIndex();
+ while( USHRT_MAX != nIdx )
+ {
+ GetNamespaceMap_().Add( pUnknown->GetPrefix( nIdx ),
+ pUnknown->GetNamespace( nIdx ) );
+ nIdx = pUnknown->GetNextNamespaceIndex( nIdx );
+ }
+ }
+ }
+ }
+ }
+
+ sal_uInt16 const eUnit = SvXMLUnitConverter::GetMeasureUnit(
+ SW_MOD()->GetMetric(pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)));
+ if (GetMM100UnitConverter().GetXMLMeasureUnit() != eUnit )
+ {
+ GetMM100UnitConverter().SetXMLMeasureUnit( eUnit );
+ m_pTwipUnitConverter->SetXMLMeasureUnit( eUnit );
+ }
+
+ if( getExportFlags() & SvXMLExportFlags::META)
+ {
+ // Update doc stat, so that correct values are exported and
+ // the progress works correctly.
+ pDoc->getIDocumentStatistics().UpdateDocStat( false, true );
+ }
+ if( m_bShowProgress )
+ {
+ ProgressBarHelper *pProgress = GetProgressBarHelper();
+ if( -1 == pProgress->GetReference() )
+ {
+ // progress isn't initialized:
+ // We assume that the whole doc is exported, and the following
+ // durations:
+ // - meta information: 2
+ // - settings: 4 (TODO: not now!)
+ // - styles (except page styles): 2
+ // - page styles: 2 (TODO: not now!) + 2 for each paragraph
+ // - paragraph: 2 (1 for automatic styles and one for content)
+
+ // count each item once, and then multiply by two to reach the
+ // figures given above
+ // The styles in pDoc also count the default style that never
+ // gets exported -> subtract one.
+ sal_Int32 nRef = 1; // meta.xml
+ nRef += pDoc->GetCharFormats()->size() - 1;
+ nRef += pDoc->GetFrameFormats()->size() - 1;
+ nRef += pDoc->GetTextFormatColls()->size() - 1;
+ nRef *= 2; // for the above styles, xmloff will increment by 2!
+ // #i93174#: count all paragraphs for the progress bar
+ nRef += pDoc->getIDocumentStatistics().GetUpdatedDocStat( false, true ).nAllPara; // 1: only content, no autostyle
+ pProgress->SetReference( nRef );
+ pProgress->SetValue( 0 );
+ }
+ }
+
+ if( getExportFlags() & (SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT))
+ {
+ //We depend on the correctness of OrdNums.
+ SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
+ if( pModel )
+ pModel->GetPage( 0 )->RecalcObjOrdNums();
+ }
+
+ // adjust document class (eClass)
+ if (pDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT))
+ {
+ eClass = XML_TEXT_GLOBAL;
+
+ // additionally, we take care of the save-linked-sections-thingy
+ mbSaveLinkedSections = pDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS);
+ }
+ // MIB: 03/26/04: The Label information is saved in the settings, so
+ // we don't need it here.
+ // else: keep default pClass that we received
+
+ rtl::Reference<SvXMLGraphicHelper> xGraphicStorageHandler;
+ if (!GetGraphicStorageHandler().is())
+ {
+ xGraphicStorageHandler = SvXMLGraphicHelper::Create(SvXMLGraphicHelperMode::Write, GetImageFilterName());
+ SetGraphicStorageHandler(xGraphicStorageHandler);
+ }
+
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xEmbeddedResolver;
+ if( !GetEmbeddedResolver().is() )
+ {
+ SfxObjectShell *pPersist = pDoc->GetPersist();
+ if( pPersist )
+ {
+ xEmbeddedResolver = SvXMLEmbeddedObjectHelper::Create(
+ *pPersist,
+ SvXMLEmbeddedObjectHelperMode::Write );
+ SetEmbeddedResolver( xEmbeddedResolver );
+ }
+ }
+
+ // set redline mode if we export STYLES or CONTENT, unless redline
+ // mode is taken care of outside (through info XPropertySet)
+ bool bSaveRedline =
+ bool( getExportFlags() & (SvXMLExportFlags::CONTENT|SvXMLExportFlags::STYLES) );
+ if( bSaveRedline )
+ {
+ // if the info property set has a ShowChanges property,
+ // then change tracking is taken care of on the outside,
+ // so we don't have to!
+ Reference<XPropertySet> rInfoSet = getExportInfo();
+ if( rInfoSet.is() )
+ {
+ bSaveRedline = ! rInfoSet->getPropertySetInfo()->hasPropertyByName(
+ "ShowChanges" );
+ }
+ }
+ RedlineFlags nRedlineFlags = RedlineFlags::NONE;
+ SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ m_bSavedShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines();
+ if( bSaveRedline )
+ {
+ // tdf#133487 call this once in flat-ODF case
+ uno::Reference<drawing::XDrawPageSupplier> const xDPS(GetModel(), uno::UNO_QUERY);
+ assert(xDPS.is());
+ xmloff::FixZOrder(xDPS->getDrawPage(), sw::GetZOrderLayer(m_pDoc->getIDocumentDrawModelAccess()));
+
+ // now save and switch redline mode
+ nRedlineFlags = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags(
+ ( nRedlineFlags & RedlineFlags::ShowMask ) | RedlineFlags::ShowInsert );
+ }
+
+ ErrCode nRet = SvXMLExport::exportDoc( eClass );
+
+ // now we can restore the redline mode (if we changed it previously)
+ if( bSaveRedline )
+ {
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags );
+ }
+
+ if (xGraphicStorageHandler)
+ xGraphicStorageHandler->dispose();
+ xGraphicStorageHandler.clear();
+ if( xEmbeddedResolver )
+ xEmbeddedResolver->dispose();
+ xEmbeddedResolver.clear();
+
+ OSL_ENSURE( !m_pTableLines, "there are table columns infos left" );
+
+ return nRet;
+}
+
+XMLTextParagraphExport* SwXMLExport::CreateTextParagraphExport()
+{
+ return new SwXMLTextParagraphExport(*this, *GetAutoStylePool());
+}
+
+XMLShapeExport* SwXMLExport::CreateShapeExport()
+{
+ XMLShapeExport* pShapeExport = new XMLShapeExport( *this, XMLTextParagraphExport::CreateShapeExtPropMapper( *this ) );
+ Reference < XDrawPageSupplier > xDPS( GetModel(), UNO_QUERY );
+ if( xDPS.is() )
+ {
+ Reference < XShapes > xShapes = xDPS->getDrawPage();
+ pShapeExport->seekShapes( xShapes );
+ }
+
+ return pShapeExport;
+}
+
+SwXMLExport::~SwXMLExport()
+{
+ DeleteTableLines();
+ FinitItemExport();
+}
+
+void SwXMLExport::ExportFontDecls_()
+{
+ GetFontAutoStylePool(); // make sure the pool is created
+ SvXMLExport::ExportFontDecls_();
+}
+
+void SwXMLExport::GetViewSettings(Sequence<PropertyValue>& aProps)
+{
+ aProps.realloc(7);
+ // Currently exporting 9 properties
+ PropertyValue *pValue = aProps.getArray();
+
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xBox = new comphelper::IndexedPropertyValuesContainer();
+ pValue[0].Name = "Views";
+ pValue[0].Value <<= uno::Reference< container::XIndexContainer >(xBox);
+
+ SwDoc *pDoc = getDoc();
+ const tools::Rectangle rRect =
+ pDoc->GetDocShell()->GetVisArea( ASPECT_CONTENT );
+ bool bTwip = pDoc->GetDocShell()->GetMapUnit ( ) == MapUnit::MapTwip;
+
+ OSL_ENSURE( bTwip, "Map unit for visible area is not in TWIPS!" );
+
+ pValue[1].Name = "ViewAreaTop";
+ pValue[1].Value <<= bTwip ? convertTwipToMm100 ( rRect.Top() ) : rRect.Top();
+
+ pValue[2].Name = "ViewAreaLeft";
+ pValue[2].Value <<= bTwip ? convertTwipToMm100 ( rRect.Left() ) : rRect.Left();
+
+ pValue[3].Name = "ViewAreaWidth";
+ pValue[3].Value <<= bTwip ? convertTwipToMm100 ( rRect.GetWidth() ) : rRect.GetWidth();
+
+ pValue[4].Name = "ViewAreaHeight";
+ pValue[4].Value <<= bTwip ? convertTwipToMm100 ( rRect.GetHeight() ) : rRect.GetHeight();
+
+ // "show redline mode" cannot simply be read from the document
+ // since it gets changed during execution. If it's in the info
+ // XPropertySet, we take it from there.
+ bool bShowRedlineChanges = m_bSavedShowChanges;
+ Reference<XPropertySet> xInfoSet( getExportInfo() );
+ if ( xInfoSet.is() )
+ {
+ static constexpr OUString sShowChanges( u"ShowChanges"_ustr );
+ if( xInfoSet->getPropertySetInfo()->hasPropertyByName( sShowChanges ) )
+ {
+ bShowRedlineChanges = *o3tl::doAccess<bool>(xInfoSet->
+ getPropertyValue( sShowChanges ));
+ }
+ }
+
+ pValue[5].Name = "ShowRedlineChanges";
+ pValue[5].Value <<= bShowRedlineChanges;
+
+ pValue[6].Name = "InBrowseMode";
+ pValue[6].Value <<= pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE);
+}
+
+void SwXMLExport::GetConfigurationSettings( Sequence < PropertyValue >& rProps)
+{
+ Reference< XMultiServiceFactory > xFac( GetModel(), UNO_QUERY );
+ if (!xFac.is())
+ return;
+
+ Reference< XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY );
+ if (!xProps.is())
+ return;
+
+ static const std::initializer_list<std::u16string_view> vOmitFalseValues = {
+ u"DoNotBreakWrappedTables",
+ u"AllowTextAfterFloatingTableBreak",
+ };
+ SvXMLUnitConverter::convertPropertySet( rProps, xProps, &vOmitFalseValues );
+
+ // tdf#144532 if NoEmbDataSet was set, to indicate not to write an embedded
+ // database for the case of a temporary mail merge preview document, then
+ // also filter out the "EmbeddedDatabaseName" property from the document
+ // settings so that when the temp mailmerge preview document is closed it
+ // doesn't unregister the database of the same name which was registered by
+ // the document this is a copy of
+ Reference<XPropertySet> rInfoSet = getExportInfo();
+
+ if (!rInfoSet.is() || !rInfoSet->getPropertySetInfo()->hasPropertyByName(u"NoEmbDataSet"_ustr))
+ return;
+
+ Any aAny = rInfoSet->getPropertyValue(u"NoEmbDataSet"_ustr);
+ bool bNoEmbDataSet = *o3tl::doAccess<bool>(aAny);
+ if (!bNoEmbDataSet)
+ return;
+
+ Sequence<PropertyValue> aFilteredProps(rProps.getLength());
+ auto aFilteredPropsRange = asNonConstRange(aFilteredProps);
+ sal_Int32 nFilteredPropLen = 0;
+ for (sal_Int32 i = 0; i < rProps.getLength(); ++i)
+ {
+ if (rProps[i].Name == "EmbeddedDatabaseName")
+ continue;
+ aFilteredPropsRange[nFilteredPropLen] = rProps[i];
+ ++nFilteredPropLen;
+ }
+ aFilteredProps.realloc(nFilteredPropLen);
+ std::swap(rProps, aFilteredProps);
+}
+
+sal_Int32 SwXMLExport::GetDocumentSpecificSettings( std::vector< SettingsGroup >& _out_rSettings )
+{
+ // the only doc-specific settings group we know so far are the XForms settings
+ uno::Sequence<beans::PropertyValue> aXFormsSettings;
+ Reference< XFormsSupplier > xXFormsSupp( GetModel(), UNO_QUERY );
+ Reference< XNameAccess > xXForms;
+ if ( xXFormsSupp.is() )
+ xXForms = xXFormsSupp->getXForms().get();
+ if ( xXForms.is() )
+ {
+ getXFormsSettings( xXForms, aXFormsSettings );
+ _out_rSettings.emplace_back( XML_XFORM_MODEL_SETTINGS, aXFormsSettings );
+ }
+
+ return aXFormsSettings.getLength() + SvXMLExport::GetDocumentSpecificSettings( _out_rSettings );
+}
+
+void SwXMLExport::SetBodyAttributes()
+{
+ // export use of soft page breaks
+ SwDoc *pDoc = getDoc();
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() &&
+ pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetPageCount() > 1 )
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, true);
+ AddAttribute(XML_NAMESPACE_TEXT, XML_USE_SOFT_PAGE_BREAKS,
+ sBuffer.makeStringAndClear());
+ }
+}
+
+void SwXMLExport::ExportContent_()
+{
+ // export forms
+ Reference<XDrawPageSupplier> xDrawPageSupplier(GetModel(), UNO_QUERY);
+ if (xDrawPageSupplier.is())
+ {
+ // export only if we actually have elements
+ Reference<XDrawPage> xPage = xDrawPageSupplier->getDrawPage();
+ if (xPage.is())
+ {
+ // prevent export of form controls which are embedded in mute sections
+ GetTextParagraphExport()->PreventExportOfControlsInMuteSections(
+ xPage, GetFormExport() );
+
+ // #i36597#
+ if ( xmloff::OFormLayerXMLExport::pageContainsForms( xPage ) || GetFormExport()->documentContainsXForms() )
+ {
+ ::xmloff::OOfficeFormsExport aOfficeForms(*this);
+
+ GetFormExport()->exportXForms();
+
+ GetFormExport()->seekPage(xPage);
+ GetFormExport()->exportForms(xPage);
+ }
+ }
+ }
+
+ Reference<XPropertySet> xPropSet(GetModel(), UNO_QUERY);
+ if (xPropSet.is())
+ {
+ Any aAny = xPropSet->getPropertyValue( "TwoDigitYear" );
+ aAny <<= sal_Int16(1930);
+
+ sal_Int16 nYear = 0;
+ aAny >>= nYear;
+ if (nYear != 1930 )
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NULL_YEAR, OUString::number(nYear));
+ SvXMLElementExport aCalcSettings(*this, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true);
+ }
+ }
+
+ GetTextParagraphExport()->exportTrackedChanges( false );
+ GetTextParagraphExport()->exportTextDeclarations();
+ Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY );
+ Reference < XText > xText = xTextDoc->getText();
+
+ GetTextParagraphExport()->exportFramesBoundToPage( m_bShowProgress );
+ GetTextParagraphExport()->exportText( xText, m_bShowProgress );
+}
+
+SwDoc* SwXMLExport::getDoc()
+{
+ if( m_pDoc != nullptr )
+ return m_pDoc;
+ Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY );
+ if (!xTextDoc)
+ {
+ SAL_WARN("sw.filter", "Problem of mismatching filter for export.");
+ return nullptr;
+ }
+
+ Reference < XText > xText = xTextDoc->getText();
+ SwXText* pText = dynamic_cast<SwXText*>(xText.get());
+ assert( pText != nullptr );
+ m_pDoc = pText->GetDoc();
+ assert( m_pDoc != nullptr );
+ return m_pDoc;
+}
+
+const SwDoc* SwXMLExport::getDoc() const
+{
+ return const_cast< SwXMLExport* >( this )->getDoc();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLExporter",
+ SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLStylesExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLStylesExporter",
+ SvXMLExportFlags::STYLES | SvXMLExportFlags::MASTERSTYLES | SvXMLExportFlags::AUTOSTYLES |
+ SvXMLExportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLContentExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLContentExporter",
+ SvXMLExportFlags::SCRIPTS | SvXMLExportFlags::CONTENT | SvXMLExportFlags::AUTOSTYLES |
+ SvXMLExportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLMetaExporter",
+ SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLSettingsExporter",
+ SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisExporter",
+ SvXMLExportFlags::ALL | SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisStylesExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisStylesExporter",
+ SvXMLExportFlags::STYLES | SvXMLExportFlags::MASTERSTYLES | SvXMLExportFlags::AUTOSTYLES |
+ SvXMLExportFlags::FONTDECLS | SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisContentExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisContentExporter",
+ SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT | SvXMLExportFlags::SCRIPTS |
+ SvXMLExportFlags::FONTDECLS | SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisMetaExporter",
+ SvXMLExportFlags::META | SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisSettingsExporter",
+ SvXMLExportFlags::SETTINGS | SvXMLExportFlags::OASIS));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlexp.hxx b/sw/source/filter/xml/xmlexp.hxx
new file mode 100644
index 0000000000..6e095396db
--- /dev/null
+++ b/sw/source/filter/xml/xmlexp.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX
+
+#include <xmloff/xmlexp.hxx>
+#include "xmlitmap.hxx"
+#include <xmloff/xmltoken.hxx>
+
+#include <optional>
+#include <string_view>
+#include <vector>
+
+class SwDoc;
+class SwFormat;
+class SwFrameFormat;
+class SvXMLUnitConverter;
+class SvXMLExportItemMapper;
+class SvXMLAutoStylePoolP;
+class SwTableLine;
+class SwTableLines;
+class SwTableBox;
+class SwXMLTableColumn_Impl;
+class SwXMLTableLines_Impl;
+class SwXMLTableColumnsSortByWidth_Impl;
+class SwXMLTableFrameFormatsSort_Impl;
+class SwXMLTableInfo_Impl;
+class SwTableNode;
+class XMLPropertySetMapper;
+class SwXMLTableLines_Impl;
+
+typedef std::vector< SwXMLTableLines_Impl* > SwXMLTableLinesCache_Impl;
+
+class SwXMLExport : public SvXMLExport
+{
+ std::unique_ptr<SvXMLUnitConverter> m_pTwipUnitConverter;
+ std::unique_ptr<SvXMLExportItemMapper> m_pTableItemMapper;
+ std::unique_ptr<SwXMLTableLinesCache_Impl> m_pTableLines;
+
+ SvXMLItemMapEntriesRef m_xTableItemMap;
+ SvXMLItemMapEntriesRef m_xTableRowItemMap;
+ SvXMLItemMapEntriesRef m_xTableCellItemMap;
+
+ bool m_bBlock : 1; // export text block?
+ bool m_bShowProgress : 1;
+ bool m_bSavedShowChanges : 1;
+
+ SwDoc* m_pDoc; // cached for getDoc()
+
+ void InitItemExport();
+ void FinitItemExport();
+ void ExportTableLinesAutoStyles( const SwTableLines& rLines,
+ sal_uInt32 nAbsWidth,
+ sal_uInt32 nBaseWidth,
+ std::u16string_view rNamePrefix,
+ SwXMLTableColumnsSortByWidth_Impl& rExpCols,
+ SwXMLTableFrameFormatsSort_Impl& rExpRows,
+ SwXMLTableFrameFormatsSort_Impl& rExpCells,
+ SwXMLTableInfo_Impl& rTableInfo,
+ bool bTop=false );
+
+ void ExportFormat(const SwFormat& rFormat, enum ::xmloff::token::XMLTokenEnum eClass,
+ ::std::optional<OUString> const oStyleName);
+ void ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth );
+
+ void ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol );
+ void ExportTableBox( const SwTableBox& rBox, sal_uInt32 nColSpan, sal_uInt32 nRowSpan,
+ SwXMLTableInfo_Impl& rTableInfo );
+ void ExportTableLine( const SwTableLine& rLine,
+ const SwXMLTableLines_Impl& rLines,
+ SwXMLTableInfo_Impl& rTableInfo );
+ void ExportTableLines( const SwTableLines& rLines,
+ SwXMLTableInfo_Impl& rTableInfo,
+ sal_uInt32 nHeaderRows = 0 );
+
+ void exportTheme();
+
+ virtual void ExportMeta_() override;
+ virtual void ExportFontDecls_() override;
+ virtual void ExportStyles_( bool bUsed ) override;
+ virtual void ExportAutoStyles_() override;
+ virtual void ExportMasterStyles_() override;
+ virtual void SetBodyAttributes() override;
+ virtual void ExportContent_() override;
+ virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override;
+ virtual void GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override;
+ virtual sal_Int32 GetDocumentSpecificSettings( std::vector< SettingsGroup >& _out_rSettings ) override;
+
+private:
+ void DeleteTableLines();
+protected:
+
+ virtual XMLTextParagraphExport* CreateTextParagraphExport() override;
+ virtual SvXMLAutoStylePoolP* CreateAutoStylePool() override;
+ virtual XMLPageExport* CreatePageExport() override;
+ virtual XMLShapeExport* CreateShapeExport() override;
+ virtual XMLFontAutoStylePool* CreateFontAutoStylePool() override;
+
+public:
+ SwXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlags);
+
+ virtual ~SwXMLExport() override;
+
+ void collectAutoStyles() override;
+
+ virtual ErrCode exportDoc( enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID ) override;
+
+ inline const SvXMLUnitConverter& GetTwipUnitConverter() const;
+
+ void ExportTableAutoStyles( const SwTableNode& rTableNd );
+ void ExportTable( const SwTableNode& rTableNd );
+
+ bool IsShowProgress() const { return m_bShowProgress; }
+ void SetShowProgress( bool b ) { m_bShowProgress = b; }
+
+ const SwDoc* getDoc() const;
+ SwDoc* getDoc();
+};
+
+inline const SvXMLUnitConverter& SwXMLExport::GetTwipUnitConverter() const
+{
+ return *m_pTwipUnitConverter;
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlexpit.cxx b/sw/source/filter/xml/xmlexpit.cxx
new file mode 100644
index 0000000000..a39d12228c
--- /dev/null
+++ b/sw/source/filter/xml/xmlexpit.cxx
@@ -0,0 +1,1137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlexpit.hxx"
+
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/itemset.hxx>
+#include <utility>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/prhdlfac.hxx>
+#include <xmloff/xmltypes.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <xmloff/xmlexp.hxx>
+#include <xmloff/xmlprhdl.hxx>
+#include <editeng/memberids.h>
+#include <hintids.hxx>
+#include <unomid.h>
+#include <svx/unomid.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/prntitem.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+
+#include "xmlithlp.hxx"
+
+#include <fmtrowsplt.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+using uno::Any;
+
+// fills the given attribute list with the items in the given set
+void SvXMLExportItemMapper::exportXML( const SvXMLExport& rExport,
+ comphelper::AttributeList& rAttrList,
+ const SfxItemSet& rSet,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ std::vector<sal_uInt16> *pIndexArray ) const
+{
+ const sal_uInt16 nCount = mrMapEntries->getCount();
+ sal_uInt16 nIndex = 0;
+
+ while( nIndex < nCount )
+ {
+ SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nIndex );
+
+ // we have a valid map entry here, so lets use it...
+ if( 0 == (rEntry.nMemberId & MID_SW_FLAG_NO_ITEM_EXPORT) )
+ {
+ const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId );
+ // do we have an item?
+ if(pItem)
+ {
+ if( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) )
+ {
+ // element items do not add any properties,
+ // we export it later
+ if( pIndexArray )
+ pIndexArray->push_back( nIndex );
+
+ }
+ else
+ {
+ exportXML( rExport, rAttrList, *pItem, rEntry, rUnitConverter,
+ rNamespaceMap, &rSet );
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL( "no item not handled in xml export" );
+ }
+ nIndex++;
+ }
+}
+
+void SvXMLExportItemMapper::exportXML(const SvXMLExport&,
+ comphelper::AttributeList& rAttrList,
+ const SfxPoolItem& rItem,
+ const SvXMLItemMapEntry& rEntry,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const SfxItemSet *pSet ) const
+{
+ if( 0 != (rEntry.nMemberId & MID_SW_FLAG_SPECIAL_ITEM_EXPORT) )
+ {
+ if( dynamic_cast<const SwFormatRowSplit*>( &rItem) != nullptr )
+ {
+ OUString aValue;
+ bool bAddAttribute = true;
+ if( rEntry.nNameSpace == XML_NAMESPACE_STYLE )
+ {
+ bAddAttribute = false;
+ }
+ else
+ {
+ OUStringBuffer aOut;
+ const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem );
+ assert(pSplit && "Wrong Which-ID");
+ const sal_uInt16 eEnum = (pSplit && pSplit->GetValue()) ? 1 : 0;
+ SvXMLUnitConverter::convertEnum( aOut, eEnum, aXML_KeepTogetherType );
+ aValue = aOut.makeStringAndClear();
+ }
+ if( bAddAttribute )
+ {
+ const OUString sName( rNamespaceMap.GetQNameByKey( rEntry.nNameSpace,
+ GetXMLToken(rEntry.eLocalName) ) );
+ rAttrList.AddAttribute( sName, aValue );
+ }
+ }
+
+ if (const SvXMLAttrContainerItem *pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>(&rItem))
+ {
+ std::unique_ptr<SvXMLNamespaceMap> pNewNamespaceMap;
+ const SvXMLNamespaceMap *pNamespaceMap = &rNamespaceMap;
+
+ const sal_uInt16 nCount = pUnknown->GetAttrCount();
+ for( sal_uInt16 i=0; i < nCount; i++ )
+ {
+ const OUString sPrefix( pUnknown->GetAttrPrefix( i ) );
+ if( !sPrefix.isEmpty() )
+ {
+ const OUString sNamespace( pUnknown->GetAttrNamespace( i ) );
+
+ // if the prefix isn't defined yet or has another meaning,
+ // we have to redefine it now.
+ const sal_uInt16 nIdx = pNamespaceMap->GetIndexByPrefix( sPrefix );
+ if( USHRT_MAX == nIdx ||
+ pNamespaceMap->GetNameByIndex( nIdx ) != sNamespace )
+ {
+ if( !pNewNamespaceMap )
+ {
+ pNewNamespaceMap.reset(
+ new SvXMLNamespaceMap( rNamespaceMap ));
+ pNamespaceMap = pNewNamespaceMap.get();
+ }
+ pNewNamespaceMap->Add( sPrefix, sNamespace );
+
+ rAttrList.AddAttribute( GetXMLToken(XML_XMLNS) + ":" + sPrefix,
+ sNamespace );
+ }
+
+ rAttrList.AddAttribute( sPrefix + ":" + pUnknown->GetAttrLName(i),
+ pUnknown->GetAttrValue(i) );
+ }
+ else
+ {
+ rAttrList.AddAttribute( pUnknown->GetAttrLName(i),
+ pUnknown->GetAttrValue(i) );
+ }
+ }
+ }
+ else
+ {
+ handleSpecialItem( rAttrList, rEntry, rItem, rUnitConverter,
+ rNamespaceMap, pSet );
+ }
+ }
+ else if( 0 == (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) )
+ {
+ bool bDone = false;
+ switch (rItem.Which())
+ {
+ case RES_FRAMEDIR:
+ {
+ // Write bt-lr and tb-rl90 to the extension namespace, handle other values
+ // below.
+ auto pDirection = static_cast<const SvxFrameDirectionItem*>(&rItem);
+ if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
+ && pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT)
+ {
+ const OUString sName(rNamespaceMap.GetQNameByKey(
+ XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE)));
+ rAttrList.AddAttribute(sName, GetXMLToken(XML_BT_LR));
+ }
+ if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
+ || pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT)
+ bDone = true;
+
+ if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
+ && pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90)
+ {
+ const OUString sName(rNamespaceMap.GetQNameByKey(
+ XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE)));
+ rAttrList.AddAttribute(sName, GetXMLToken(XML_TB_RL90));
+ }
+ if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
+ || pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90)
+ bDone = true;
+ break;
+ }
+ }
+
+ if (!bDone)
+ {
+ OUString aValue;
+ if( QueryXMLValue(rItem, aValue,
+ static_cast< sal_uInt16 >(
+ rEntry.nMemberId & MID_SW_FLAG_MASK ),
+ rUnitConverter ) )
+ {
+ const OUString sName(
+ rNamespaceMap.GetQNameByKey( rEntry.nNameSpace,
+ GetXMLToken(rEntry.eLocalName)));
+ rAttrList.AddAttribute( sName, aValue );
+ }
+ }
+ }
+}
+
+void SvXMLExportItemMapper::exportElementItems(
+ SvXMLExport& rExport,
+ const SfxItemSet &rSet,
+ const std::vector<sal_uInt16> &rIndexArray ) const
+{
+ const size_t nCount = rIndexArray.size();
+
+ bool bItemsExported = false;
+ for( size_t nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ const sal_uInt16 nElement = rIndexArray[ nIndex ];
+ SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nElement );
+ OSL_ENSURE( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT),
+ "wrong mid flag!" );
+
+ const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId );
+ // do we have an item?
+ if(pItem)
+ {
+ rExport.IgnorableWhitespace();
+ handleElementItem( rEntry, *pItem );
+ bItemsExported = true;
+ }
+ }
+
+ if( bItemsExported )
+ rExport.IgnorableWhitespace();
+}
+
+/** returns the item with the given WhichId from the given ItemSet if it's
+ set
+*/
+const SfxPoolItem* SvXMLExportItemMapper::GetItem( const SfxItemSet& rSet,
+ sal_uInt16 nWhichId)
+{
+ // first get item from itemset
+ const SfxPoolItem* pItem;
+ SfxItemState eState = rSet.GetItemState( nWhichId, false, &pItem );
+
+ if( SfxItemState::SET == eState )
+ {
+ return pItem;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+SvXMLExportItemMapper::SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries )
+ : mrMapEntries(std::move(rMapEntries))
+{
+}
+
+SvXMLExportItemMapper::~SvXMLExportItemMapper()
+{
+}
+
+void SvXMLExportItemMapper::exportXML( SvXMLExport& rExport,
+ const SfxItemSet& rSet,
+ const SvXMLUnitConverter& rUnitConverter,
+ XMLTokenEnum ePropToken ) const
+{
+ std::vector<sal_uInt16> aIndexArray;
+
+ exportXML( rExport, rExport.GetAttrList(), rSet, rUnitConverter,
+ rExport.GetNamespaceMap(), &aIndexArray );
+
+ if( rExport.GetAttrList().getLength() > 0 || !aIndexArray.empty() )
+ {
+ rExport.IgnorableWhitespace();
+
+ SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, ePropToken,
+ false, false );
+ exportElementItems( rExport, rSet, aIndexArray );
+ }
+}
+
+/** this method is called for every item that has the
+ MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void SvXMLExportItemMapper::handleSpecialItem( comphelper::AttributeList& /*rAttrList*/,
+ const SvXMLItemMapEntry& /*rEntry*/,
+ const SfxPoolItem& /*rItem*/,
+ const SvXMLUnitConverter& /*rUnitConverter*/,
+ const SvXMLNamespaceMap& /*rNamespaceMap*/,
+ const SfxItemSet* /*pSet*/ /* = NULL */ ) const
+{
+ OSL_FAIL( "special item not handled in xml export" );
+}
+
+/** this method is called for every item that has the
+ MID_SW_FLAG_ELEMENT_EXPORT flag set */
+void SvXMLExportItemMapper::handleElementItem(
+ const SvXMLItemMapEntry& /*rEntry*/,
+ const SfxPoolItem& /*rItem*/ ) const
+{
+ OSL_FAIL( "element item not handled in xml export" );
+}
+
+static bool lcl_isOdfDoubleLine( const SvxBorderLine* pLine )
+{
+ bool bIsOdfDouble = false;
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ bIsOdfDouble = true;
+ break;
+ default:
+ break;
+ }
+ return bIsOdfDouble;
+}
+
+bool SvXMLExportItemMapper::QueryXMLValue(
+ const SfxPoolItem& rItem,
+ OUString& rValue,
+ sal_uInt16 nMemberId,
+ const SvXMLUnitConverter& rUnitConverter )
+{
+ bool bOk = false;
+ OUStringBuffer aOut;
+
+ switch ( rItem.Which() )
+ {
+ case RES_MARGIN_FIRSTLINE:
+ case RES_MARGIN_TEXTLEFT:
+ case RES_MARGIN_RIGHT:
+ assert(false); // is only called for frame formats?
+ break;
+
+ case RES_LR_SPACE:
+ {
+ const SvxLRSpaceItem& rLRSpace = dynamic_cast<const SvxLRSpaceItem&>(rItem);
+
+ bOk = true;
+ switch( nMemberId )
+ {
+ case MID_L_MARGIN:
+ if (rLRSpace.GetPropLeft() != 100)
+ {
+ ::sax::Converter::convertPercent(
+ aOut, rLRSpace.GetPropLeft() );
+ }
+ else
+ {
+ rUnitConverter.convertMeasureToXML(
+ aOut, rLRSpace.GetLeft() );
+ }
+ break;
+
+ case MID_R_MARGIN:
+ if (rLRSpace.GetPropRight() != 100)
+ {
+ ::sax::Converter::convertPercent(
+ aOut, rLRSpace.GetPropRight() );
+ }
+ else
+ {
+ rUnitConverter.convertMeasureToXML(
+ aOut, rLRSpace.GetRight() );
+ }
+ break;
+
+ case MID_FIRST_AUTO:
+ if (rLRSpace.IsAutoFirst())
+ {
+ ::sax::Converter::convertBool(
+ aOut, rLRSpace.IsAutoFirst() );
+ }
+ else
+ bOk = false;
+ break;
+
+ case MID_FIRST_LINE_INDENT:
+ if (!rLRSpace.IsAutoFirst())
+ {
+ if (rLRSpace.GetPropTextFirstLineOffset() != 100)
+ {
+ ::sax::Converter::convertPercent(
+ aOut, rLRSpace.GetPropTextFirstLineOffset() );
+ }
+ else
+ {
+ rUnitConverter.convertMeasureToXML(
+ aOut, rLRSpace.GetTextFirstLineOffset() );
+ }
+ }
+ else
+ bOk = false;
+ break;
+
+ default:
+ OSL_FAIL( "unknown member id!");
+ bOk = false;
+ break;
+ }
+ }
+ break;
+
+ case RES_UL_SPACE:
+ {
+ const SvxULSpaceItem& rULSpace = dynamic_cast<const SvxULSpaceItem&>(rItem);
+
+ switch( nMemberId )
+ {
+ case MID_UP_MARGIN:
+ if (rULSpace.GetPropUpper() != 100)
+ {
+ ::sax::Converter::convertPercent(
+ aOut, rULSpace.GetPropUpper() );
+ }
+ else
+ {
+ rUnitConverter.convertMeasureToXML(
+ aOut, rULSpace.GetUpper() );
+ }
+ break;
+
+ case MID_LO_MARGIN:
+ if (rULSpace.GetPropLower() != 100)
+ {
+ ::sax::Converter::convertPercent(
+ aOut, rULSpace.GetPropLower() );
+ }
+ else
+ {
+ rUnitConverter.convertMeasureToXML(
+ aOut, rULSpace.GetLower() );
+ }
+ break;
+
+ default:
+ OSL_FAIL("unknown MemberId");
+ };
+
+ bOk = true;
+ }
+ break;
+
+ case RES_SHADOW:
+ {
+ const SvxShadowItem* pShadow = dynamic_cast<const SvxShadowItem*>( &rItem );
+ assert(pShadow && "Wrong Which-ID");
+ if (pShadow)
+ {
+ sal_Int32 nX = 1, nY = 1;
+ switch( pShadow->GetLocation() )
+ {
+ case SvxShadowLocation::TopLeft:
+ nX = -1;
+ nY = -1;
+ break;
+ case SvxShadowLocation::TopRight:
+ nY = -1;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ nX = -1;
+ break;
+ case SvxShadowLocation::BottomRight:
+ break;
+ case SvxShadowLocation::NONE:
+ default:
+ rValue = GetXMLToken(XML_NONE);
+ return true;
+ }
+
+ nX *= pShadow->GetWidth();
+ nY *= pShadow->GetWidth();
+
+ ::sax::Converter::convertColor(aOut, pShadow->GetColor());
+ aOut.append( ' ' );
+ rUnitConverter.convertMeasureToXML( aOut, nX );
+ aOut.append( ' ' );
+ rUnitConverter.convertMeasureToXML( aOut, nY );
+
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_BOX:
+ {
+ const SvxBoxItem* pBox = dynamic_cast<const SvxBoxItem*>( &rItem );
+ assert(pBox && "Wrong Which-ID");
+ if (pBox)
+ {
+ /**
+ xml -> MemberId
+
+ border-padding ALL_BORDER_PADDING
+ border-padding-before LEFT_BORDER_PADDING
+ border-padding-after RIGHT_BORDER_PADDING
+ border-padding-start TOP_BORDER_PADDING
+ border-padding-end BOTTOM_BORDER_PADDING
+
+ border ALL_BORDER
+ border-before LEFT_BORDER
+ border-after RIGHT_BORDER
+ border-start TOP_BORDER
+ border-end BOTTOM_BORDER
+
+ border-line-width ALL_BORDER_LINE_WIDTH
+ border-line-width-before LEFT_BORDER_LINE_WIDTH
+ border-line-width-after RIGHT_BORDER_LINE_WIDTH
+ border-line-width-start TOP_BORDER_LINE_WIDTH
+ border-line-width-end BOTTOM_BORDER_LINE_WIDTH
+ */
+
+ const SvxBorderLine* pLeft = pBox->GetLeft();
+ const SvxBorderLine* pRight = pBox->GetRight();
+ const SvxBorderLine* pTop = pBox->GetTop();
+ const SvxBorderLine* pBottom = pBox->GetBottom();
+ const sal_uInt16 nTopDist = pBox->GetDistance( SvxBoxItemLine::TOP );
+ const sal_uInt16 nBottomDist = pBox->GetDistance( SvxBoxItemLine::BOTTOM );
+ const sal_uInt16 nLeftDist = pBox->GetDistance( SvxBoxItemLine::LEFT );
+ const sal_uInt16 nRightDist = pBox->GetDistance( SvxBoxItemLine::RIGHT );
+
+ // check if we need to export it
+ switch( nMemberId )
+ {
+ case ALL_BORDER_PADDING:
+ case LEFT_BORDER_PADDING:
+ case RIGHT_BORDER_PADDING:
+ case TOP_BORDER_PADDING:
+ case BOTTOM_BORDER_PADDING:
+ {
+ bool bEqual = nLeftDist == nRightDist &&
+ nLeftDist == nTopDist &&
+ nLeftDist == nBottomDist;
+ // don't export individual paddings if all paddings are equal and
+ // don't export all padding if some paddings are not equal
+ if( (bEqual && ALL_BORDER_PADDING != nMemberId) ||
+ (!bEqual && ALL_BORDER_PADDING == nMemberId) )
+ return false;
+ }
+ break;
+ case ALL_BORDER:
+ case LEFT_BORDER:
+ case RIGHT_BORDER:
+ case TOP_BORDER:
+ case BOTTOM_BORDER:
+ {
+ bool bEqual = ( nullptr == pTop && nullptr == pBottom &&
+ nullptr == pLeft && nullptr == pRight ) ||
+ ( pTop && pBottom && pLeft && pRight &&
+ *pTop == *pBottom && *pTop == *pLeft &&
+ *pTop == *pRight );
+
+ // don't export individual borders if all are the same and
+ // don't export all borders if some are not equal
+ if( (bEqual && ALL_BORDER != nMemberId) ||
+ (!bEqual && ALL_BORDER == nMemberId) )
+ return false;
+ }
+ break;
+ case ALL_BORDER_LINE_WIDTH:
+ case LEFT_BORDER_LINE_WIDTH:
+ case RIGHT_BORDER_LINE_WIDTH:
+ case TOP_BORDER_LINE_WIDTH:
+ case BOTTOM_BORDER_LINE_WIDTH:
+ {
+ // if no line is set, there is nothing to export
+ if( !pTop && !pBottom && !pLeft && !pRight )
+ return false;
+
+ bool bEqual = nullptr != pTop &&
+ nullptr != pBottom &&
+ nullptr != pLeft &&
+ nullptr != pRight;
+
+ if( bEqual )
+ {
+ const sal_uInt16 nDistance = pTop->GetDistance();
+ const sal_uInt16 nInWidth = pTop->GetInWidth();
+ const sal_uInt16 nOutWidth = pTop->GetOutWidth();
+ const tools::Long nWidth = pTop->GetWidth();
+
+ bEqual = nDistance == pLeft->GetDistance() &&
+ nInWidth == pLeft->GetInWidth() &&
+ nOutWidth == pLeft->GetOutWidth() &&
+ nWidth == pLeft->GetWidth() &&
+ nDistance == pRight->GetDistance() &&
+ nInWidth == pRight->GetInWidth() &&
+ nOutWidth == pRight->GetOutWidth() &&
+ nWidth == pRight->GetWidth() &&
+ nDistance == pBottom->GetDistance() &&
+ nInWidth == pBottom->GetInWidth() &&
+ nOutWidth == pBottom->GetOutWidth() &&
+ nWidth == pBottom->GetWidth();
+ }
+
+ switch( nMemberId )
+ {
+ case ALL_BORDER_LINE_WIDTH:
+ if( !bEqual || pTop->GetDistance() == 0 ||
+ !lcl_isOdfDoubleLine( pTop ) )
+ return false;
+ break;
+ case LEFT_BORDER_LINE_WIDTH:
+ if( bEqual || nullptr == pLeft ||
+ 0 == pLeft->GetDistance() ||
+ !lcl_isOdfDoubleLine( pLeft ) )
+ return false;
+ break;
+ case RIGHT_BORDER_LINE_WIDTH:
+ if( bEqual || nullptr == pRight ||
+ 0 == pRight->GetDistance() ||
+ !lcl_isOdfDoubleLine( pRight ) )
+ return false;
+ break;
+ case TOP_BORDER_LINE_WIDTH:
+ if( bEqual || nullptr == pTop ||
+ 0 == pTop->GetDistance() ||
+ !lcl_isOdfDoubleLine( pTop ) )
+ return false;
+ break;
+ case BOTTOM_BORDER_LINE_WIDTH:
+ if( bEqual || nullptr == pBottom ||
+ 0 == pBottom->GetDistance() ||
+ !lcl_isOdfDoubleLine( pBottom ) )
+ return false;
+ break;
+ }
+ }
+ break;
+ }
+
+ // now export it export
+ switch( nMemberId )
+ {
+ // padding
+ case ALL_BORDER_PADDING:
+ case LEFT_BORDER_PADDING:
+ rUnitConverter.convertMeasureToXML( aOut, nLeftDist );
+ break;
+ case RIGHT_BORDER_PADDING:
+ rUnitConverter.convertMeasureToXML( aOut, nRightDist );
+ break;
+ case TOP_BORDER_PADDING:
+ rUnitConverter.convertMeasureToXML( aOut, nTopDist );
+ break;
+ case BOTTOM_BORDER_PADDING:
+ rUnitConverter.convertMeasureToXML( aOut, nBottomDist );
+ break;
+
+ // border
+ case ALL_BORDER:
+ case LEFT_BORDER:
+ case RIGHT_BORDER:
+ case TOP_BORDER:
+ case BOTTOM_BORDER:
+ {
+ const SvxBorderLine* pLine;
+ switch( nMemberId )
+ {
+ case ALL_BORDER:
+ case LEFT_BORDER:
+ pLine = pLeft;
+ break;
+ case RIGHT_BORDER:
+ pLine = pRight;
+ break;
+ case TOP_BORDER:
+ pLine = pTop;
+ break;
+ case BOTTOM_BORDER:
+ pLine = pBottom;
+ break;
+ default:
+ pLine = nullptr;
+ break;
+ }
+
+ if( nullptr != pLine )
+ {
+ sal_Int32 nWidth = pLine->GetWidth();
+
+ enum XMLTokenEnum eStyle = XML_SOLID;
+ bool bNoBorder = false;
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ eStyle = XML_SOLID;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ eStyle = XML_DOTTED;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ eStyle = XML_DASHED;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ eStyle = XML_FINE_DASHED;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ eStyle = XML_DASH_DOT;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ eStyle = XML_DASH_DOT_DOT;
+ break;
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ eStyle = XML_DOUBLE_THIN;
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ eStyle = XML_DOUBLE;
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ eStyle = XML_RIDGE;
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ eStyle = XML_GROOVE;
+ break;
+ case SvxBorderLineStyle::INSET:
+ eStyle = XML_INSET;
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ eStyle = XML_OUTSET;
+ break;
+ default:
+ bNoBorder = true;
+ }
+
+ if ( !bNoBorder )
+ {
+ ::sax::Converter::convertMeasure(aOut, nWidth,
+ util::MeasureUnit::TWIP,
+ util::MeasureUnit::POINT);
+ aOut.append( " "
+ + GetXMLToken( eStyle )
+ + " " );
+ ::sax::Converter::convertColor(aOut,
+ pLine->GetColor());
+ }
+ }
+ else
+ {
+ aOut.append( GetXMLToken(XML_NONE) );
+ }
+ }
+ break;
+
+ // width
+ case ALL_BORDER_LINE_WIDTH:
+ case LEFT_BORDER_LINE_WIDTH:
+ case RIGHT_BORDER_LINE_WIDTH:
+ case TOP_BORDER_LINE_WIDTH:
+ case BOTTOM_BORDER_LINE_WIDTH:
+ const SvxBorderLine* pLine;
+ switch( nMemberId )
+ {
+ case ALL_BORDER_LINE_WIDTH:
+ case LEFT_BORDER_LINE_WIDTH:
+ pLine = pLeft;
+ break;
+ case RIGHT_BORDER_LINE_WIDTH:
+ pLine = pRight;
+ break;
+ case TOP_BORDER_LINE_WIDTH:
+ pLine = pTop;
+ break;
+ case BOTTOM_BORDER_LINE_WIDTH:
+ pLine = pBottom;
+ break;
+ default:
+ return false;
+ }
+ rUnitConverter.convertMeasureToXML( aOut, pLine->GetInWidth() );
+ aOut.append( ' ' );
+ rUnitConverter.convertMeasureToXML( aOut, pLine->GetDistance() );
+ aOut.append( ' ' );
+ rUnitConverter.convertMeasureToXML( aOut, pLine->GetOutWidth() );
+ break;
+ }
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_BREAK:
+ {
+ const SvxFormatBreakItem& rFormatBreak = dynamic_cast<const SvxFormatBreakItem&>(rItem);
+
+ sal_uInt16 eEnum = 0;
+
+ switch( nMemberId )
+ {
+ case MID_BREAK_BEFORE:
+ switch (rFormatBreak.GetBreak())
+ {
+ case SvxBreak::ColumnBefore:
+ eEnum = 1;
+ break;
+ case SvxBreak::PageBefore:
+ eEnum = 2;
+ break;
+ case SvxBreak::NONE:
+ eEnum = 0;
+ break;
+ default:
+ return false;
+ }
+ break;
+ case MID_BREAK_AFTER:
+ switch (rFormatBreak.GetBreak())
+ {
+ case SvxBreak::ColumnAfter:
+ eEnum = 1;
+ break;
+ case SvxBreak::PageAfter:
+ eEnum = 2;
+ break;
+ case SvxBreak::NONE:
+ eEnum = 0;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+
+ bOk = SvXMLUnitConverter::convertEnum( aOut, eEnum, psXML_BreakType );
+ }
+ break;
+
+ case RES_KEEP:
+ {
+ const SvxFormatKeepItem* pFormatKeep = dynamic_cast<const SvxFormatKeepItem*>( &rItem );
+ assert(pFormatKeep && "Wrong Which-ID");
+ if (pFormatKeep)
+ {
+ aOut.append( pFormatKeep->GetValue()
+ ? GetXMLToken( XML_ALWAYS )
+ : GetXMLToken( XML_AUTO ) );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_PRINT:
+ {
+ const SvxPrintItem* pHasTextChangesOnly = dynamic_cast<const SvxPrintItem*>( &rItem );
+ if (pHasTextChangesOnly && !pHasTextChangesOnly->GetValue())
+ {
+ aOut.append( "false" );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_BACKGROUND:
+ {
+ const SvxBrushItem& rBrush = dynamic_cast<const SvxBrushItem&>(rItem);
+
+ // note: the graphic is only exported if nMemberId equals
+ // MID_GRAPHIC...
+ // If not, only the color or transparency is exported
+
+ switch( nMemberId )
+ {
+ case MID_BACK_COLOR:
+ if ( rBrush.GetColor().IsTransparent() )
+ aOut.append( GetXMLToken(XML_TRANSPARENT) );
+ else
+ {
+ ::sax::Converter::convertColor(aOut,
+ rBrush.GetColor());
+ }
+ bOk = true;
+ break;
+
+ case MID_GRAPHIC_POSITION:
+ switch (rBrush.GetGraphicPos())
+ {
+ case GPOS_LT:
+ case GPOS_MT:
+ case GPOS_RT:
+ aOut.append( GetXMLToken(XML_TOP) );
+ bOk = true;
+ break;
+ case GPOS_LM:
+ case GPOS_MM:
+ case GPOS_RM:
+ aOut.append( GetXMLToken(XML_CENTER) );
+ bOk = true;
+ break;
+ case GPOS_LB:
+ case GPOS_MB:
+ case GPOS_RB:
+ aOut.append( GetXMLToken(XML_BOTTOM) );
+ bOk = true;
+ break;
+ default:
+ ;
+ }
+
+ if( bOk )
+ {
+ aOut.append( ' ' );
+
+ switch (rBrush.GetGraphicPos())
+ {
+ case GPOS_LT:
+ case GPOS_LB:
+ case GPOS_LM:
+ aOut.append( GetXMLToken(XML_LEFT) );
+ break;
+ case GPOS_MT:
+ case GPOS_MM:
+ case GPOS_MB:
+ aOut.append( GetXMLToken(XML_CENTER) );
+ break;
+ case GPOS_RM:
+ case GPOS_RT:
+ case GPOS_RB:
+ aOut.append( GetXMLToken(XML_RIGHT) );
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+
+ case MID_GRAPHIC_REPEAT:
+ {
+ SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos();
+ if( GPOS_AREA == eGraphicPos )
+ {
+ aOut.append( GetXMLToken(XML_STRETCH) );
+ bOk = true;
+ }
+ else if( GPOS_NONE != eGraphicPos && GPOS_TILED != eGraphicPos )
+ {
+ aOut.append( GetXMLToken(XML_BACKGROUND_NO_REPEAT) );
+ bOk = true;
+ }
+ }
+ break;
+
+ case MID_GRAPHIC_FILTER:
+ if (rBrush.GetGraphicPos() != GPOS_NONE &&
+ !rBrush.GetGraphicFilter().isEmpty())
+ {
+ aOut.append(rBrush.GetGraphicFilter());
+ bOk = true;
+ }
+ break;
+ }
+ }
+ break;
+
+ case RES_PAGEDESC:
+ {
+ const SwFormatPageDesc& rPageDesc = dynamic_cast<const SwFormatPageDesc&>(rItem);
+
+ if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId )
+ {
+ ::std::optional<sal_uInt16> oNumOffset = rPageDesc.GetNumOffset();
+ if (oNumOffset && *oNumOffset > 0)
+ {
+ // #i114163# positiveInteger only!
+ sal_Int32 const number(*oNumOffset);
+ aOut.append(number);
+ }
+ else
+ {
+ aOut.append(GetXMLToken(XML_AUTO));
+ }
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_LAYOUT_SPLIT:
+ case RES_ROW_SPLIT:
+ {
+ const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem );
+ assert(pSplit && "Wrong Which-ID");
+ if (pSplit)
+ {
+ ::sax::Converter::convertBool( aOut, pSplit->GetValue() );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_HORI_ORIENT:
+ {
+ const SwFormatHoriOrient* pHoriOrient = dynamic_cast<const SwFormatHoriOrient*>( &rItem );
+ assert(pHoriOrient && "Wrong Which-ID");
+ if (pHoriOrient)
+ {
+ SvXMLUnitConverter::convertEnum( aOut, pHoriOrient->GetHoriOrient(),
+ aXMLTableAlignMap );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_VERT_ORIENT:
+ {
+ const SwFormatVertOrient* pVertOrient = dynamic_cast<const SwFormatVertOrient*>( &rItem );
+ assert(pVertOrient && "Wrong Which-ID");
+
+ SvXMLUnitConverter::convertEnum( aOut, pVertOrient->GetVertOrient(),
+ aXMLTableVAlignMap );
+ bOk = true;
+ }
+ break;
+
+ case RES_FRM_SIZE:
+ {
+ const SwFormatFrameSize& rFrameSize = dynamic_cast<const SwFormatFrameSize&>(rItem);
+
+ bool bOutHeight = false;
+ switch( nMemberId )
+ {
+ case MID_FRMSIZE_REL_WIDTH:
+ if (rFrameSize.GetWidthPercent())
+ {
+ ::sax::Converter::convertPercent(
+ aOut, rFrameSize.GetWidthPercent() );
+ bOk = true;
+ }
+ break;
+ case MID_FRMSIZE_MIN_HEIGHT:
+ if( SwFrameSize::Minimum == rFrameSize.GetHeightSizeType() )
+ bOutHeight = true;
+ break;
+ case MID_FRMSIZE_FIX_HEIGHT:
+ if( SwFrameSize::Fixed == rFrameSize.GetHeightSizeType() )
+ bOutHeight = true;
+ break;
+ }
+
+ if( bOutHeight )
+ {
+ rUnitConverter.convertMeasureToXML(aOut, rFrameSize.GetHeight());
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_FRAMEDIR:
+ {
+ Any aAny;
+ bOk = rItem.QueryValue( aAny );
+ if( bOk )
+ {
+ std::unique_ptr<XMLPropertyHandler> pWritingModeHandler =
+ XMLPropertyHandlerFactory::CreatePropertyHandler(
+ XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT );
+ OUString sValue;
+ bOk = pWritingModeHandler->exportXML( sValue, aAny,
+ rUnitConverter );
+ if( bOk )
+ aOut.append( sValue );
+ }
+ }
+ break;
+
+ case RES_COLLAPSING_BORDERS:
+ {
+ const SfxBoolItem* pBorders = dynamic_cast<const SfxBoolItem*>( &rItem );
+ assert(pBorders && "Wrong RES-ID");
+ if (pBorders)
+ {
+ aOut.append( pBorders->GetValue()
+ ? GetXMLToken( XML_COLLAPSING )
+ : GetXMLToken( XML_SEPARATING ) );
+ bOk = true;
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("GetXMLValue not implemented for this item.");
+ break;
+ }
+
+ if ( bOk )
+ rValue = aOut.makeStringAndClear();
+
+ return bOk;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlexpit.hxx b/sw/source/filter/xml/xmlexpit.hxx
new file mode 100644
index 0000000000..b0e10fb0ab
--- /dev/null
+++ b/sw/source/filter/xml/xmlexpit.hxx
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX
+
+#include <xmloff/xmlexppr.hxx>
+#include "xmlitmap.hxx"
+#include <vector>
+
+class SvXMLUnitConverter;
+class SfxPoolItem;
+class SfxItemSet;
+namespace comphelper { class AttributeList; }
+class SvXMLNamespaceMap;
+class SvXMLExport;
+
+class SvXMLExportItemMapper
+{
+ SvXMLItemMapEntriesRef mrMapEntries;
+
+protected:
+ /** fills the given attribute list with the items in the given set */
+ void exportXML( const SvXMLExport& rExport,
+ comphelper::AttributeList& rAttrList,
+ const SfxItemSet& rSet,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ std::vector<sal_uInt16> *pIndexArray ) const;
+
+ void exportXML( const SvXMLExport& rExport,
+ comphelper::AttributeList& rAttrList,
+ const SfxPoolItem& rItem,
+ const SvXMLItemMapEntry &rEntry,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const SfxItemSet *pSet ) const;
+
+ void exportElementItems( SvXMLExport& rExport,
+ const SfxItemSet &rSet,
+ const std::vector<sal_uInt16> &rIndexArray ) const;
+
+ static const SfxPoolItem* GetItem( const SfxItemSet &rSet,
+ sal_uInt16 nWhichId );
+
+public:
+ explicit SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries );
+ virtual ~SvXMLExportItemMapper();
+
+ void exportXML( SvXMLExport& rExport,
+ const SfxItemSet& rSet,
+ const SvXMLUnitConverter& rUnitConverter,
+ ::xmloff::token::XMLTokenEnum ePropToken ) const;
+
+ /** this method is called for every item that has the
+ MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem( comphelper::AttributeList& rAttrList,
+ const SvXMLItemMapEntry& rEntry,
+ const SfxPoolItem& rItem,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const SfxItemSet *pSet ) const;
+
+ /** this method is called for every item that has the
+ MID_SW_FLAG_ELEMENT_EXPORT flag set */
+ virtual void handleElementItem( const SvXMLItemMapEntry& rEntry,
+ const SfxPoolItem& rItem ) const;
+
+ inline void setMapEntries( SvXMLItemMapEntriesRef rMapEntries );
+
+ static bool QueryXMLValue( const SfxPoolItem& rItem,
+ OUString& rValue, sal_uInt16 nMemberId,
+ const SvXMLUnitConverter& rUnitConverter );
+};
+
+inline void
+SvXMLExportItemMapper::setMapEntries( SvXMLItemMapEntriesRef rMapEntries )
+{
+ mrMapEntries = rMapEntries;
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlfmt.cxx b/sw/source/filter/xml/xmlfmt.cxx
new file mode 100644
index 0000000000..9bd4d2a5f4
--- /dev/null
+++ b/sw/source/filter/xml/xmlfmt.cxx
@@ -0,0 +1,1079 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <fmtcol.hxx>
+#include <hints.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <unoprnms.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmlnumfi.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlstyle.hxx>
+#include <xmloff/txtstyli.hxx>
+#include <xmloff/txtimp.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/XMLTextMasterStylesContext.hxx>
+#include <xmloff/XMLTextShapeStyleContext.hxx>
+#include <xmloff/XMLGraphicsDefaultStyle.hxx>
+#include <xmloff/XMLDrawingPageStyleContext.hxx>
+#include <xmloff/XMLTextMasterPageContext.hxx>
+#include <xmloff/table/XMLTableImport.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include "xmlimp.hxx"
+#include <cellatr.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <ccoll.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+using namespace ::xmloff::token;
+
+namespace {
+
+class SwXMLConditionParser_Impl
+{
+ OUString m_sInput;
+
+ Master_CollCondition m_nCondition;
+ sal_uInt32 m_nSubCondition;
+
+ sal_Int32 m_nPos;
+ sal_Int32 m_nLength;
+
+ inline bool SkipWS();
+ inline bool MatchChar( sal_Unicode c );
+ inline bool MatchName( OUString& rName );
+ inline bool MatchNumber( sal_uInt32& rNumber );
+
+public:
+
+ explicit SwXMLConditionParser_Impl( const OUString& rInp );
+
+ bool IsValid() const { return Master_CollCondition::NONE != m_nCondition; }
+
+ Master_CollCondition GetCondition() const { return m_nCondition; }
+ sal_uInt32 GetSubCondition() const { return m_nSubCondition; }
+};
+
+}
+
+inline bool SwXMLConditionParser_Impl::SkipWS()
+{
+ while( m_nPos < m_nLength && ' ' == m_sInput[m_nPos] )
+ m_nPos++;
+ return true;
+}
+
+inline bool SwXMLConditionParser_Impl::MatchChar( sal_Unicode c )
+{
+ bool bRet = false;
+ if( m_nPos < m_nLength && c == m_sInput[m_nPos] )
+ {
+ m_nPos++;
+ bRet = true;
+ }
+ return bRet;
+}
+
+inline bool SwXMLConditionParser_Impl::MatchName( OUString& rName )
+{
+ OUStringBuffer sBuffer( m_nLength );
+ while( m_nPos < m_nLength &&
+ ( ('a' <= m_sInput[m_nPos] && m_sInput[m_nPos] <= 'z') ||
+ '-' == m_sInput[m_nPos] ) )
+ {
+ sBuffer.append( m_sInput[m_nPos] );
+ m_nPos++;
+ }
+ rName = sBuffer.makeStringAndClear();
+ return !rName.isEmpty();
+}
+
+inline bool SwXMLConditionParser_Impl::MatchNumber( sal_uInt32& rNumber )
+{
+ OUStringBuffer sBuffer( m_nLength );
+ while( m_nPos < m_nLength && '0' <= m_sInput[m_nPos] && m_sInput[m_nPos] <= '9' )
+ {
+ sBuffer.append( m_sInput[m_nPos] );
+ m_nPos++;
+ }
+
+ OUString sNum( sBuffer.makeStringAndClear() );
+ if( !sNum.isEmpty() )
+ rNumber = sNum.toInt32();
+ return !sNum.isEmpty();
+}
+
+SwXMLConditionParser_Impl::SwXMLConditionParser_Impl( const OUString& rInp ) :
+ m_sInput( rInp ),
+ m_nCondition( Master_CollCondition::NONE ),
+ m_nSubCondition( 0 ),
+ m_nPos( 0 ),
+ m_nLength( rInp.getLength() )
+{
+ OUString sFunc;
+ bool bHasSub = false;
+ sal_uInt32 nSub = 0;
+ bool bOK = SkipWS() && MatchName( sFunc ) && SkipWS() &&
+ MatchChar( '(' ) && SkipWS() && MatchChar( ')' ) && SkipWS();
+ if( bOK && MatchChar( '=' ) )
+ {
+ bOK = SkipWS() && MatchNumber( nSub ) && SkipWS();
+ bHasSub = true;
+ }
+
+ bOK &= m_nPos == m_nLength;
+
+ if( !bOK )
+ return;
+
+ if( IsXMLToken( sFunc, XML_ENDNOTE ) && !bHasSub )
+ m_nCondition = Master_CollCondition::PARA_IN_ENDNOTE;
+ else if( IsXMLToken( sFunc, XML_FOOTER ) && !bHasSub )
+ m_nCondition = Master_CollCondition::PARA_IN_FOOTER;
+ else if( IsXMLToken( sFunc, XML_FOOTNOTE ) && !bHasSub )
+ m_nCondition = Master_CollCondition::PARA_IN_FOOTNOTE;
+ else if( IsXMLToken( sFunc, XML_HEADER ) && !bHasSub )
+ m_nCondition = Master_CollCondition::PARA_IN_HEADER;
+ else if( IsXMLToken( sFunc, XML_LIST_LEVEL) &&
+ nSub >=1 && nSub <= MAXLEVEL )
+ {
+ m_nCondition = Master_CollCondition::PARA_IN_LIST;
+ m_nSubCondition = nSub-1;
+ }
+ else if( IsXMLToken( sFunc, XML_OUTLINE_LEVEL) &&
+ nSub >=1 && nSub <= MAXLEVEL )
+ {
+ m_nCondition = Master_CollCondition::PARA_IN_OUTLINE;
+ m_nSubCondition = nSub-1;
+ }
+ else if( IsXMLToken( sFunc, XML_SECTION ) && !bHasSub )
+ {
+ m_nCondition = Master_CollCondition::PARA_IN_SECTION;
+ }
+ else if( IsXMLToken( sFunc, XML_TABLE ) && !bHasSub )
+ {
+ m_nCondition = Master_CollCondition::PARA_IN_TABLEBODY;
+ }
+ else if( IsXMLToken( sFunc, XML_TABLE_HEADER ) && !bHasSub )
+ {
+ m_nCondition = Master_CollCondition::PARA_IN_TABLEHEAD;
+ }
+ else if( IsXMLToken( sFunc, XML_TEXT_BOX ) && !bHasSub )
+ {
+ m_nCondition = Master_CollCondition::PARA_IN_FRAME;
+ }
+}
+
+namespace {
+
+class SwXMLConditionContext_Impl : public SvXMLImportContext
+{
+ Master_CollCondition m_nCondition;
+ sal_uInt32 m_nSubCondition;
+
+ OUString m_sApplyStyle;
+
+public:
+
+ SwXMLConditionContext_Impl(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList );
+
+ bool IsValid() const { return Master_CollCondition::NONE != m_nCondition; }
+
+ Master_CollCondition getCondition() const { return m_nCondition; }
+ sal_uInt32 getSubCondition() const { return m_nSubCondition; }
+ OUString const &getApplyStyle() const { return m_sApplyStyle; }
+};
+
+}
+
+SwXMLConditionContext_Impl::SwXMLConditionContext_Impl(
+ SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) :
+ SvXMLImportContext( rImport ),
+ m_nCondition( Master_CollCondition::NONE ),
+ m_nSubCondition( 0 )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(STYLE, XML_CONDITION):
+ {
+ SwXMLConditionParser_Impl aCondParser( sValue );
+ if( aCondParser.IsValid() )
+ {
+ m_nCondition = aCondParser.GetCondition();
+ m_nSubCondition = aCondParser.GetSubCondition();
+ }
+ break;
+ }
+ case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME):
+ m_sApplyStyle = sValue;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ }
+ }
+}
+
+typedef std::vector<rtl::Reference<SwXMLConditionContext_Impl>> SwXMLConditions_Impl;
+
+namespace {
+
+class SwXMLTextStyleContext_Impl : public XMLTextStyleContext
+{
+ std::unique_ptr<SwXMLConditions_Impl> m_pConditions;
+
+protected:
+
+ virtual uno::Reference < style::XStyle > Create() override;
+ virtual void Finish( bool bOverwrite ) override;
+
+public:
+
+
+ SwXMLTextStyleContext_Impl( SwXMLImport& rImport,
+ XmlStyleFamily nFamily,
+ SvXMLStylesContext& rStyles );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+};
+
+}
+
+uno::Reference < style::XStyle > SwXMLTextStyleContext_Impl::Create()
+{
+ uno::Reference < style::XStyle > xNewStyle;
+ if( m_pConditions && XmlStyleFamily::TEXT_PARAGRAPH == GetFamily() )
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory( GetImport().GetModel(),
+ uno::UNO_QUERY );
+ if( xFactory.is() )
+ {
+ uno::Reference < uno::XInterface > xIfc =
+ xFactory->createInstance( "com.sun.star.style.ConditionalParagraphStyle" );
+ if( xIfc.is() )
+ xNewStyle.set( xIfc, uno::UNO_QUERY );
+ }
+ }
+ else
+ {
+ xNewStyle = XMLTextStyleContext::Create();
+ }
+
+ return xNewStyle;
+}
+
+void
+SwXMLTextStyleContext_Impl::Finish( bool bOverwrite )
+{
+ if( m_pConditions && XmlStyleFamily::TEXT_PARAGRAPH == GetFamily() && GetStyle().is() )
+ {
+ CommandStruct const*const pCommands = SwCondCollItem::GetCmds();
+
+ Reference< XPropertySet > xPropSet( GetStyle(), UNO_QUERY );
+
+ uno::Sequence< beans::NamedValue > aSeq( m_pConditions->size() );
+ auto aSeqRange = asNonConstRange(aSeq);
+
+ for (std::vector<rtl::Reference<SwXMLConditionContext_Impl>>::size_type i = 0;
+ i < m_pConditions->size(); ++i)
+ {
+ assert((*m_pConditions)[i]->IsValid()); // checked before inserting
+ Master_CollCondition nCond = (*m_pConditions)[i]->getCondition();
+ sal_uInt32 nSubCond = (*m_pConditions)[i]->getSubCondition();
+
+ for (size_t j = 0; j < COND_COMMAND_COUNT; ++j)
+ {
+ if (pCommands[j].nCnd == nCond &&
+ pCommands[j].nSubCond == nSubCond)
+ {
+ aSeqRange[i].Name = GetCommandContextByIndex( j );
+ aSeqRange[i].Value <<= GetImport().GetStyleDisplayName(
+ GetFamily(), (*m_pConditions)[i]->getApplyStyle() );
+ break;
+ }
+ }
+ }
+
+ try
+ {
+ xPropSet->setPropertyValue(UNO_NAME_PARA_STYLE_CONDITIONS, uno::Any(aSeq));
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.xml", "exception when setting ParaStyleConditions");
+ }
+ }
+ XMLTextStyleContext::Finish( bOverwrite );
+}
+
+SwXMLTextStyleContext_Impl::SwXMLTextStyleContext_Impl( SwXMLImport& rImport,
+ XmlStyleFamily nFamily,
+ SvXMLStylesContext& rStyles ) :
+ XMLTextStyleContext( rImport, rStyles, nFamily )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLTextStyleContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ if( nElement == XML_ELEMENT(STYLE, XML_MAP) )
+ {
+ rtl::Reference<SwXMLConditionContext_Impl> xCond{
+ new SwXMLConditionContext_Impl( GetImport(), nElement, xAttrList )};
+ if( xCond->IsValid() )
+ {
+ if( !m_pConditions )
+ m_pConditions = std::make_unique<SwXMLConditions_Impl>();
+ m_pConditions->push_back( xCond );
+ }
+ return xCond;
+ }
+
+ return XMLTextStyleContext::createFastChildContext( nElement, xAttrList );
+}
+
+namespace {
+
+class SwXMLCellStyleContext : public XMLPropStyleContext
+{
+ OUString m_sDataStyleName;
+ void AddDataFormat();
+public:
+ using XMLPropStyleContext::XMLPropStyleContext;
+ virtual void FillPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet) override;
+ virtual void SetAttribute(sal_Int32 nElement, const OUString& rValue) override;
+};
+
+class SwXMLItemSetStyleContext_Impl : public SvXMLStyleContext
+{
+ OUString m_sMasterPageName;
+ std::optional<SfxItemSet> m_oItemSet;
+ SwXMLTextStyleContext_Impl *m_pTextStyle;
+ SvXMLStylesContext &m_rStyles;
+
+ OUString m_sDataStyleName;
+
+ bool m_bHasMasterPageName : 1;
+ bool m_bPageDescConnected : 1;
+ bool m_bDataStyleIsResolved;
+
+ SvXMLImportContext *CreateItemSetContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList);
+
+protected:
+
+ virtual void SetAttribute( sal_Int32 nElement,
+ const OUString& rValue ) override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+
+public:
+
+
+ SwXMLItemSetStyleContext_Impl(
+ SwXMLImport& rImport,
+ SvXMLStylesContext& rStylesC,
+ XmlStyleFamily nFamily);
+
+ virtual void CreateAndInsert( bool bOverwrite ) override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+ // The item set may be empty!
+ SfxItemSet *GetItemSet() { return m_oItemSet ? &*m_oItemSet : nullptr; }
+
+ bool HasMasterPageName() const { return m_bHasMasterPageName; }
+
+ bool IsPageDescConnected() const { return m_bPageDescConnected; }
+ void ConnectPageDesc();
+
+ bool ResolveDataStyleName();
+};
+
+}
+
+void SwXMLCellStyleContext::AddDataFormat()
+{
+ if (m_sDataStyleName.isEmpty() || IsDefaultStyle())
+ return;
+
+ const SvXMLNumFormatContext* pStyle = static_cast<const SvXMLNumFormatContext*>(
+ GetStyles()->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, m_sDataStyleName, true));
+
+ if (!pStyle)
+ {
+ SAL_WARN("sw.xml", "not possible to get data style " << m_sDataStyleName);
+ return;
+ }
+
+ sal_Int32 nNumberFormat = const_cast<SvXMLNumFormatContext*>(pStyle)->GetKey();
+ if (nNumberFormat < 0)
+ return;
+
+ rtl::Reference<SvXMLImportPropertyMapper> xPropertyMapper(GetStyles()->GetImportPropertyMapper(GetFamily()));
+ if (!xPropertyMapper.is())
+ {
+ SAL_WARN("sw.xml", "there is no import prop mapper");
+ return;
+ }
+
+ const rtl::Reference<XMLPropertySetMapper>& xPropertySetMapper(xPropertyMapper->getPropertySetMapper());
+ sal_Int32 nIndex = xPropertySetMapper->GetEntryIndex(XML_NAMESPACE_STYLE, GetXMLToken(XML_DATA_STYLE_NAME), 0);
+ if (nIndex < 0)
+ {
+ SAL_WARN("sw.xml", "could not find id for " << GetXMLToken(XML_DATA_STYLE_NAME));
+ return;
+ }
+
+ auto aIter = std::find_if(GetProperties().begin(), GetProperties().end(),
+ [&nIndex](const XMLPropertyState& rProp) {
+ return rProp.mnIndex == nIndex;
+ });
+
+ if (aIter != GetProperties().end())
+ aIter->maValue <<= nNumberFormat;
+ else
+ GetProperties().push_back(XMLPropertyState(nIndex, Any(nNumberFormat)));
+}
+
+void SwXMLCellStyleContext::FillPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet)
+{
+ AddDataFormat();
+ XMLPropStyleContext::FillPropertySet(rPropSet);
+}
+
+void SwXMLCellStyleContext::SetAttribute(sal_Int32 nElement, const OUString& rValue)
+{
+ if ((nElement & TOKEN_MASK) == XML_DATA_STYLE_NAME)
+ m_sDataStyleName = rValue;
+ else
+ XMLPropStyleContext::SetAttribute(nElement, rValue);
+}
+
+void SwXMLItemSetStyleContext_Impl::SetAttribute( sal_Int32 nElement,
+ const OUString& rValue )
+{
+ switch(nElement)
+ {
+ case XML_ELEMENT(STYLE, XML_MASTER_PAGE_NAME):
+ {
+ m_sMasterPageName = rValue;
+ m_bHasMasterPageName = true;
+ break;
+ }
+ case XML_ELEMENT(STYLE, XML_DATA_STYLE_NAME):
+ {
+ // if we have a valid data style name
+ if (!rValue.isEmpty())
+ {
+ m_sDataStyleName = rValue;
+ m_bDataStyleIsResolved = false; // needs to be resolved
+ }
+ break;
+ }
+ default:
+ SvXMLStyleContext::SetAttribute( nElement, rValue );
+ }
+}
+
+SvXMLImportContext *SwXMLItemSetStyleContext_Impl::CreateItemSetContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ OSL_ENSURE( !m_oItemSet,
+ "SwXMLItemSetStyleContext_Impl::CreateItemSetContext: item set exists" );
+
+ SvXMLImportContext *pContext = nullptr;
+
+ SwDoc* pDoc = GetSwImport().getDoc();
+
+ SfxItemPool& rItemPool = pDoc->GetAttrPool();
+ switch( GetFamily() )
+ {
+ case XmlStyleFamily::TABLE_TABLE:
+ m_oItemSet.emplace( rItemPool, aTableSetRange );
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ m_oItemSet.emplace( rItemPool, svl::Items<RES_FRM_SIZE, RES_FRM_SIZE> );
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ m_oItemSet.emplace( rItemPool, aTableLineSetRange );
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ m_oItemSet.emplace( rItemPool, aTableBoxSetRange );
+ break;
+ default:
+ OSL_ENSURE( false,
+ "SwXMLItemSetStyleContext_Impl::CreateItemSetContext: unknown family" );
+ break;
+ }
+ if( m_oItemSet )
+ pContext = GetSwImport().CreateTableItemImportContext(
+ nElement, xAttrList, GetFamily(),
+ *m_oItemSet );
+ if( !pContext )
+ {
+ m_oItemSet.reset();
+ }
+
+ return pContext;
+}
+
+
+SwXMLItemSetStyleContext_Impl::SwXMLItemSetStyleContext_Impl( SwXMLImport& rImport,
+ SvXMLStylesContext& rStylesC,
+ XmlStyleFamily nFamily ) :
+ SvXMLStyleContext( rImport, nFamily ),
+ m_pTextStyle( nullptr ),
+ m_rStyles( rStylesC ),
+ m_bHasMasterPageName( false ),
+ m_bPageDescConnected( false ),
+ m_bDataStyleIsResolved( true )
+{
+}
+
+void SwXMLItemSetStyleContext_Impl::CreateAndInsert( bool bOverwrite )
+{
+ if( m_pTextStyle )
+ m_pTextStyle->CreateAndInsert( bOverwrite );
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLItemSetStyleContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(STYLE, XML_TABLE_PROPERTIES):
+ case XML_ELEMENT(STYLE, XML_TABLE_COLUMN_PROPERTIES):
+ case XML_ELEMENT(STYLE, XML_TABLE_ROW_PROPERTIES):
+ case XML_ELEMENT(STYLE, XML_TABLE_CELL_PROPERTIES):
+ return CreateItemSetContext( nElement, xAttrList );
+ case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES):
+ case XML_ELEMENT(STYLE, XML_PARAGRAPH_PROPERTIES):
+ {
+ if( !m_pTextStyle )
+ {
+ m_pTextStyle = new SwXMLTextStyleContext_Impl( GetSwImport(), XmlStyleFamily::TEXT_PARAGRAPH, m_rStyles );
+ rtl::Reference<sax_fastparser::FastAttributeList> xTmpAttrList = new sax_fastparser::FastAttributeList(nullptr);
+ xTmpAttrList->add(XML_ELEMENT(STYLE, XML_NAME), GetName().toUtf8() );
+ m_pTextStyle->startFastElement( nElement, xTmpAttrList );
+ m_rStyles.AddStyle( *m_pTextStyle );
+ }
+ return m_pTextStyle->createFastChildContext( nElement, xAttrList );
+ }
+ default:
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement);
+ }
+
+ return nullptr;
+}
+
+void SwXMLItemSetStyleContext_Impl::ConnectPageDesc()
+{
+ if( m_bPageDescConnected || !HasMasterPageName() )
+ return;
+ m_bPageDescConnected = true;
+
+ SwDoc *pDoc = GetSwImport().getDoc();
+
+ // #i40788# - first determine the display name of the page style,
+ // then map this name to the corresponding user interface name.
+ OUString sName = GetImport().GetStyleDisplayName( XmlStyleFamily::MASTER_PAGE,
+ m_sMasterPageName );
+ SwStyleNameMapper::FillUIName( sName,
+ sName,
+ SwGetPoolIdFromName::PageDesc);
+ SwPageDesc *pPageDesc = pDoc->FindPageDesc(sName);
+ if( !pPageDesc )
+ {
+ // If the page style is a pool style, then we maybe have to create it
+ // first if it hasn't been used by now.
+ const sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sName, SwGetPoolIdFromName::PageDesc );
+ if( USHRT_MAX != nPoolId )
+ pPageDesc = pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId, false );
+ }
+
+ if( !pPageDesc )
+ return;
+
+ if( !m_oItemSet )
+ {
+ SfxItemPool& rItemPool = pDoc->GetAttrPool();
+ m_oItemSet.emplace( rItemPool, aTableSetRange );
+ }
+
+ std::unique_ptr<SwFormatPageDesc> pFormatPageDesc;
+ if( const SwFormatPageDesc* pItem = m_oItemSet->GetItemIfSet( RES_PAGEDESC, false ) )
+ {
+ if( pItem->GetPageDesc() != pPageDesc )
+ pFormatPageDesc.reset(new SwFormatPageDesc( *pItem ));
+ }
+ else
+ pFormatPageDesc.reset(new SwFormatPageDesc());
+
+ if( pFormatPageDesc )
+ {
+ pFormatPageDesc->RegisterToPageDesc( *pPageDesc );
+ m_oItemSet->Put( std::move(pFormatPageDesc) );
+ }
+}
+
+bool SwXMLItemSetStyleContext_Impl::ResolveDataStyleName()
+{
+ // resolve, if not already done
+ if (! m_bDataStyleIsResolved)
+ {
+ // get the format key
+ sal_Int32 nFormat =
+ GetImport().GetTextImport()->GetDataStyleKey(m_sDataStyleName);
+
+ // if the key is valid, insert Item into ItemSet
+ if( -1 != nFormat )
+ {
+ if( !m_oItemSet )
+ {
+ SwDoc *pDoc = GetSwImport().getDoc();
+
+ SfxItemPool& rItemPool = pDoc->GetAttrPool();
+ m_oItemSet.emplace( rItemPool, aTableBoxSetRange );
+ }
+ SwTableBoxNumFormat aNumFormatItem(nFormat);
+ m_oItemSet->Put(aNumFormatItem);
+ }
+
+ // now resolved
+ m_bDataStyleIsResolved = true;
+ return true;
+ }
+ else
+ {
+ // was already resolved; nothing to do
+ return false;
+ }
+}
+
+namespace {
+
+class SwXMLStylesContext_Impl : public SvXMLStylesContext
+{
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+ const SwXMLImport& GetSwImport() const
+ { return static_cast<const SwXMLImport&>(GetImport()); }
+
+protected:
+
+ using SvXMLStylesContext::CreateStyleChildContext;
+ virtual SvXMLStyleContext *CreateStyleChildContext( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ using SvXMLStylesContext::CreateStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateStyleStyleChildContext( XmlStyleFamily nFamily,
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
+ using SvXMLStylesContext::CreateDefaultStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateDefaultStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
+ // HACK
+ virtual rtl::Reference < SvXMLImportPropertyMapper > GetImportPropertyMapper(
+ XmlStyleFamily nFamily ) const override;
+
+ virtual uno::Reference < container::XNameContainer >
+ GetStylesContainer( XmlStyleFamily nFamily ) const override;
+ virtual OUString GetServiceName( XmlStyleFamily nFamily ) const override;
+ // HACK
+
+public:
+
+ SwXMLStylesContext_Impl(
+ SwXMLImport& rImport,
+ bool bAuto );
+
+ virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+SvXMLStyleContext *SwXMLStylesContext_Impl::CreateStyleChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext* pContext = nullptr;
+
+ if(nElement == XML_ELEMENT(TABLE, XML_TABLE_TEMPLATE))
+ {
+ rtl::Reference<XMLTableImport> xTableImport = GetImport().GetShapeImport()->GetShapeTableImport();
+ pContext = xTableImport->CreateTableTemplateContext(nElement, xAttrList);
+ }
+ if (!pContext)
+ pContext = SvXMLStylesContext::CreateStyleChildContext(nElement, xAttrList);
+
+ return pContext;
+}
+
+SvXMLStyleContext *SwXMLStylesContext_Impl::CreateStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pStyle = nullptr;
+
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TEXT_PARAGRAPH:
+ pStyle = new SwXMLTextStyleContext_Impl( GetSwImport(), nFamily, *this );
+ break;
+ case XmlStyleFamily::TABLE_TABLE:
+ case XmlStyleFamily::TABLE_COLUMN:
+ case XmlStyleFamily::TABLE_ROW:
+ case XmlStyleFamily::TABLE_CELL:
+ // Distinguish real and automatic styles.
+ if (IsAutomaticStyle())
+ pStyle = new SwXMLItemSetStyleContext_Impl(GetSwImport(), *this, nFamily);
+ else if (nFamily == XmlStyleFamily::TABLE_CELL) // Real cell styles are used for table-template import.
+ pStyle = new SwXMLCellStyleContext(GetSwImport(), *this, nFamily);
+ else
+ SAL_WARN("sw.xml", "Context does not exists for non automatic table, column or row style.");
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ // As long as there are no element items, we can use the text
+ // style class.
+ pStyle = new XMLTextShapeStyleContext( GetImport(), *this, nFamily );
+ break;
+ case XmlStyleFamily::SD_DRAWINGPAGE_ID:
+ pStyle = new XMLDrawingPageStyleContext(GetImport(),
+ *this, g_MasterPageContextIDs, g_MasterPageFamilies);
+ break;
+ default:
+ pStyle = SvXMLStylesContext::CreateStyleStyleChildContext( nFamily,
+ nElement,
+ xAttrList );
+ break;
+ }
+
+ return pStyle;
+}
+
+SvXMLStyleContext *SwXMLStylesContext_Impl::CreateDefaultStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pStyle = nullptr;
+
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TEXT_PARAGRAPH:
+ case XmlStyleFamily::TABLE_TABLE:
+ case XmlStyleFamily::TABLE_ROW:
+ pStyle = new XMLTextStyleContext( GetImport(),
+ *this, nFamily,
+ true );
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ // There are no writer specific defaults for graphic styles!
+ pStyle = new XMLGraphicsDefaultStyle( GetImport(), *this );
+ break;
+ default:
+ pStyle = SvXMLStylesContext::CreateDefaultStyleStyleChildContext( nFamily,
+ nElement,
+ xAttrList );
+ break;
+ }
+
+ return pStyle;
+}
+
+SwXMLStylesContext_Impl::SwXMLStylesContext_Impl(
+ SwXMLImport& rImport,
+ bool bAuto ) :
+ SvXMLStylesContext( rImport, bAuto )
+{
+}
+
+bool SwXMLStylesContext_Impl::InsertStyleFamily( XmlStyleFamily nFamily ) const
+{
+ const SwXMLImport& rSwImport = GetSwImport();
+ const SfxStyleFamily nStyleFamilyMask = rSwImport.GetStyleFamilyMask();
+
+ bool bIns = true;
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TEXT_PARAGRAPH:
+ bIns = bool(nStyleFamilyMask & SfxStyleFamily::Para);
+ break;
+ case XmlStyleFamily::TEXT_TEXT:
+ bIns = bool(nStyleFamilyMask & SfxStyleFamily::Char);
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ bIns = bool(nStyleFamilyMask & SfxStyleFamily::Frame);
+ break;
+ case XmlStyleFamily::TEXT_LIST:
+ bIns = bool(nStyleFamilyMask & SfxStyleFamily::Pseudo);
+ break;
+ case XmlStyleFamily::TEXT_OUTLINE:
+ case XmlStyleFamily::TEXT_FOOTNOTECONFIG:
+ case XmlStyleFamily::TEXT_ENDNOTECONFIG:
+ case XmlStyleFamily::TEXT_LINENUMBERINGCONFIG:
+ case XmlStyleFamily::TEXT_BIBLIOGRAPHYCONFIG:
+ bIns = !(rSwImport.IsInsertMode() || rSwImport.IsStylesOnlyMode() ||
+ rSwImport.IsBlockMode());
+ break;
+ default:
+ bIns = SvXMLStylesContext::InsertStyleFamily( nFamily );
+ break;
+ }
+
+ return bIns;
+}
+
+rtl::Reference < SvXMLImportPropertyMapper > SwXMLStylesContext_Impl::GetImportPropertyMapper(
+ XmlStyleFamily nFamily ) const
+{
+ rtl::Reference < SvXMLImportPropertyMapper > xMapper;
+ if( nFamily == XmlStyleFamily::TABLE_TABLE )
+ xMapper = XMLTextImportHelper::CreateTableDefaultExtPropMapper(
+ const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() );
+ else if( nFamily == XmlStyleFamily::TABLE_ROW )
+ xMapper = XMLTextImportHelper::CreateTableRowDefaultExtPropMapper(
+ const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() );
+ else if( nFamily == XmlStyleFamily::TABLE_CELL )
+ xMapper = XMLTextImportHelper::CreateTableCellExtPropMapper(
+ const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() );
+ else if (nFamily == XmlStyleFamily::SD_DRAWINGPAGE_ID)
+ {
+ xMapper = XMLTextImportHelper::CreateDrawingPageExtPropMapper(
+ const_cast<SwXMLStylesContext_Impl*>(this)->GetImport());
+ }
+ else
+ xMapper = SvXMLStylesContext::GetImportPropertyMapper( nFamily );
+ return xMapper;
+}
+
+uno::Reference < container::XNameContainer > SwXMLStylesContext_Impl::GetStylesContainer(
+ XmlStyleFamily nFamily ) const
+{
+ uno::Reference < container::XNameContainer > xStyles;
+ if( XmlStyleFamily::SD_GRAPHICS_ID == nFamily )
+ xStyles = const_cast<SvXMLImport *>(&GetImport())->GetTextImport()->GetFrameStyles();
+ else if( XmlStyleFamily::TABLE_CELL == nFamily )
+ xStyles = const_cast<SvXMLImport *>(&GetImport())->GetTextImport()->GetCellStyles();
+
+ if (!xStyles.is())
+ xStyles = SvXMLStylesContext::GetStylesContainer( nFamily );
+
+ return xStyles;
+}
+
+OUString SwXMLStylesContext_Impl::GetServiceName( XmlStyleFamily nFamily ) const
+{
+ if( XmlStyleFamily::SD_GRAPHICS_ID == nFamily )
+ return "com.sun.star.style.FrameStyle";
+ else if( XmlStyleFamily::TABLE_CELL == nFamily )
+ return "com.sun.star.style.CellStyle";
+
+ return SvXMLStylesContext::GetServiceName( nFamily );
+}
+
+void SwXMLStylesContext_Impl::endFastElement(sal_Int32 )
+{
+ GetSwImport().InsertStyles( IsAutomaticStyle() );
+ if (!IsAutomaticStyle())
+ GetImport().GetShapeImport()->GetShapeTableImport()->finishStyles();
+}
+
+namespace {
+
+class SwXMLMasterStylesContext_Impl : public XMLTextMasterStylesContext
+{
+protected:
+ virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+ const SwXMLImport& GetSwImport() const
+ { return static_cast<const SwXMLImport&>(GetImport()); }
+
+public:
+
+
+ SwXMLMasterStylesContext_Impl( SwXMLImport& rImport );
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+SwXMLMasterStylesContext_Impl::SwXMLMasterStylesContext_Impl(
+ SwXMLImport& rImport ) :
+ XMLTextMasterStylesContext( rImport )
+{
+}
+
+bool SwXMLMasterStylesContext_Impl::InsertStyleFamily( XmlStyleFamily nFamily ) const
+{
+ bool bIns;
+
+ const SwXMLImport& rSwImport = GetSwImport();
+ const SfxStyleFamily nStyleFamilyMask = rSwImport.GetStyleFamilyMask();
+ if( XmlStyleFamily::MASTER_PAGE == nFamily )
+ bIns = bool(nStyleFamilyMask & SfxStyleFamily::Page);
+ else
+ bIns = XMLTextMasterStylesContext::InsertStyleFamily( nFamily );
+
+ return bIns;
+}
+
+void SwXMLMasterStylesContext_Impl::endFastElement(sal_Int32 )
+{
+ FinishStyles( !GetSwImport().IsInsertMode() );
+ GetSwImport().FinishStyles();
+}
+
+SvXMLImportContext *SwXMLImport::CreateStylesContext(
+ bool bAuto )
+{
+ SvXMLStylesContext *pContext = new SwXMLStylesContext_Impl( *this, bAuto );
+ if( bAuto )
+ SetAutoStyles( pContext );
+ else
+ SetStyles( pContext );
+
+ return pContext;
+}
+
+SvXMLImportContext *SwXMLImport::CreateMasterStylesContext()
+{
+ SvXMLStylesContext *pContext =
+ new SwXMLMasterStylesContext_Impl( *this );
+ SetMasterStyles( pContext );
+
+ return pContext;
+}
+
+void SwXMLImport::InsertStyles( bool bAuto )
+{
+ if( bAuto && GetAutoStyles() )
+ GetAutoStyles()->CopyAutoStylesToDoc();
+ if( !bAuto && GetStyles() )
+ GetStyles()->CopyStylesToDoc( !IsInsertMode(), false );
+}
+
+void SwXMLImport::FinishStyles()
+{
+ if( GetStyles() )
+ GetStyles()->FinishStyles( !IsInsertMode() );
+}
+
+void SwXMLImport::UpdateTextCollConditions( SwDoc *pDoc )
+{
+ if( !pDoc )
+ pDoc = getDoc();
+
+ const SwTextFormatColls& rColls = *pDoc->GetTextFormatColls();
+ const size_t nCount = rColls.size();
+ for( size_t i=0; i < nCount; ++i )
+ {
+ SwTextFormatColl *pColl = rColls[i];
+ if( pColl && RES_CONDTXTFMTCOLL == pColl->Which() )
+ {
+ const SwFormatCollConditions& rConditions =
+ static_cast<const SwConditionTextFormatColl *>(pColl)->GetCondColls();
+ bool bSendModify = false;
+ for( size_t j=0; j < rConditions.size() && !bSendModify; ++j )
+ {
+ const SwCollCondition& rCond = *rConditions[j];
+ switch( rCond.GetCondition() )
+ {
+ case Master_CollCondition::PARA_IN_TABLEHEAD:
+ case Master_CollCondition::PARA_IN_TABLEBODY:
+ case Master_CollCondition::PARA_IN_FOOTER:
+ case Master_CollCondition::PARA_IN_HEADER:
+ bSendModify = true;
+ break;
+ default: break;
+ }
+ }
+ if(bSendModify)
+ pColl->GetNotifier().Broadcast(sw::CondCollCondChg(*pColl));
+ }
+ }
+}
+
+bool SwXMLImport::FindAutomaticStyle(
+ XmlStyleFamily nFamily,
+ const OUString& rName,
+ const SfxItemSet **ppItemSet ) const
+{
+ SwXMLItemSetStyleContext_Impl *pStyle = nullptr;
+ if( GetAutoStyles() )
+ {
+ pStyle = const_cast<SwXMLItemSetStyleContext_Impl*>(dynamic_cast< const SwXMLItemSetStyleContext_Impl* >(
+ GetAutoStyles()->
+ FindStyleChildContext( nFamily, rName,
+ true ) ) );
+ if( pStyle )
+ {
+ if( ppItemSet )
+ {
+ if( XmlStyleFamily::TABLE_TABLE == pStyle->GetFamily() &&
+ pStyle->HasMasterPageName() &&
+ !pStyle->IsPageDescConnected() )
+ pStyle->ConnectPageDesc();
+ (*ppItemSet) = pStyle->GetItemSet();
+
+ // resolve data style name late
+ if( XmlStyleFamily::TABLE_CELL == pStyle->GetFamily() &&
+ pStyle->ResolveDataStyleName() )
+ {
+ (*ppItemSet) = pStyle->GetItemSet();
+ }
+
+ }
+ }
+ }
+
+ return pStyle != nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlfmte.cxx b/sw/source/filter/xml/xmlfmte.cxx
new file mode 100644
index 0000000000..8e20f46e42
--- /dev/null
+++ b/sw/source/filter/xml/xmlfmte.cxx
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <xmloff/xmlnamespace.hxx>
+#include "xmlexpit.hxx"
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/XMLTextListAutoStylePool.hxx>
+#include <xmloff/XMLTextMasterPageExport.hxx>
+#include <xmloff/table/XMLTableExport.hxx>
+
+#include <xmloff/txtprmap.hxx>
+#include <xmloff/xmlaustp.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/maptype.hxx>
+#include <format.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <cellatr.hxx>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/Color.hpp>
+#include "xmlexp.hxx"
+#include <SwStyleNameMapper.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <svx/unoapi.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <docmodel/theme/ThemeColorType.hxx>
+#include <docmodel/theme/Theme.hxx>
+
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::lang;
+using namespace ::xmloff::token;
+
+void SwXMLExport::ExportFormat(const SwFormat& rFormat, enum XMLTokenEnum eFamily,
+ ::std::optional<OUString> const oStyleName)
+{
+ // <style:style ...>
+ CheckAttrList();
+
+ // style:family="..."
+ OSL_ENSURE( RES_FRMFMT==rFormat.Which(), "frame format expected" );
+ if( RES_FRMFMT != rFormat.Which() )
+ return;
+ OSL_ENSURE( eFamily != XML_TOKEN_INVALID, "family must be specified" );
+ // style:name="..."
+ assert(oStyleName || (eFamily != XML_TABLE_ROW && eFamily != XML_TABLE_CELL));
+ bool bEncoded = false;
+ OUString const name(oStyleName ? *oStyleName : rFormat.GetName());
+ AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName(name, &bEncoded));
+ if( bEncoded )
+ {
+ AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name);
+ }
+
+ if( eFamily != XML_TOKEN_INVALID )
+ AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, eFamily );
+
+#if OSL_DEBUG_LEVEL > 0
+ // style:parent-style-name="..." (if it's not the default only)
+ const SwFormat* pParent = rFormat.DerivedFrom();
+ // Only adopt parent name, if it's not the default
+ OSL_ENSURE( !pParent || pParent->IsDefault(), "unexpected parent" );
+
+ OSL_ENSURE( USHRT_MAX == rFormat.GetPoolFormatId(), "pool ids aren't supported" );
+ OSL_ENSURE( USHRT_MAX == rFormat.GetPoolHelpId(), "help ids aren't supported" );
+ OSL_ENSURE( USHRT_MAX == rFormat.GetPoolHelpId() ||
+ UCHAR_MAX == rFormat.GetPoolHlpFileId(), "help file ids aren't supported" );
+#endif
+
+ // style:master-page-name
+ if( RES_FRMFMT == rFormat.Which() && XML_TABLE == eFamily )
+ {
+ if( const SwFormatPageDesc* pItem = rFormat.GetAttrSet().GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ {
+ OUString sName;
+ const SwPageDesc *pPageDesc = pItem->GetPageDesc();
+ if( pPageDesc )
+ SwStyleNameMapper::FillProgName(
+ pPageDesc->GetName(),
+ sName,
+ SwGetPoolIdFromName::PageDesc);
+ AddAttribute( XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME,
+ EncodeStyleName( sName ) );
+ }
+ }
+
+ if( XML_TABLE_CELL == eFamily )
+ {
+ OSL_ENSURE(RES_FRMFMT == rFormat.Which(), "only frame format");
+
+ if( const SwTableBoxNumFormat *pItem =
+ rFormat.GetAttrSet().GetItemIfSet( RES_BOXATR_FORMAT, false ) )
+ {
+ sal_Int32 nFormat = static_cast<sal_Int32>(pItem->GetValue());
+
+ if ( (nFormat != -1) && (nFormat != static_cast<sal_Int32>(getSwDefaultTextFormat())) )
+ {
+ // if we have a format, register and then export
+ // (Careful: here we assume that data styles will be
+ // written after cell styles)
+ addDataStyle(nFormat);
+ OUString sDataStyleName = getDataStyleName(nFormat);
+ if( !sDataStyleName.isEmpty() )
+ AddAttribute( XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME,
+ sDataStyleName );
+ }
+ }
+ }
+
+ {
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE,
+ true, true );
+
+ SvXMLItemMapEntriesRef xItemMap;
+ XMLTokenEnum ePropToken = XML_TABLE_PROPERTIES;
+ if( XML_TABLE == eFamily )
+ {
+ xItemMap = m_xTableItemMap;
+ }
+ else if( XML_TABLE_ROW == eFamily )
+ {
+ xItemMap = m_xTableRowItemMap;
+ ePropToken = XML_TABLE_ROW_PROPERTIES;
+ }
+ else if( XML_TABLE_CELL == eFamily )
+ {
+ xItemMap = m_xTableCellItemMap;
+ ePropToken = XML_TABLE_CELL_PROPERTIES;
+ }
+
+ if( xItemMap.is() )
+ {
+ m_pTableItemMapper->setMapEntries( xItemMap );
+ m_pTableItemMapper->exportXML( *this,
+ rFormat.GetAttrSet(),
+ GetTwipUnitConverter(),
+ ePropToken );
+ }
+ }
+}
+
+void SwXMLExport::ExportStyles_( bool bUsed )
+{
+ SvXMLExport::ExportStyles_( bUsed );
+
+ // drawing defaults
+ GetShapeExport()->ExportGraphicDefaults();
+
+ GetTextParagraphExport()->exportTextStyles( bUsed
+ ,IsShowProgress()
+ );
+ collectDataStyles(true);
+ exportDataStyles();
+ GetShapeExport()->GetShapeTableExport()->exportTableStyles();
+ //page defaults
+ GetPageExport()->exportDefaultStyle();
+
+ // Theme
+ exportTheme();
+}
+
+void SwXMLExport::exportTheme()
+{
+ if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
+ return;
+
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(GetModel(), UNO_QUERY);
+ if (!xDrawPageSupplier.is())
+ return;
+
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ if (!xDrawPage.is())
+ return;
+
+ SdrPage* pPage = GetSdrPageFromXDrawPage(xDrawPage);
+ SAL_WARN_IF(!pPage, "oox", "Can't get SdrPage from XDrawPage");
+
+ if (!pPage)
+ return;
+
+ auto const& pTheme = pPage->getSdrModelFromSdrPage().getTheme();
+ if (!pTheme)
+ return;
+
+ ExportThemeElement(pTheme);
+}
+
+void SwXMLExport::collectAutoStyles()
+{
+ SvXMLExport::collectAutoStyles();
+
+ if (mbAutoStylesCollected)
+ return;
+
+ // The order in which styles are collected *MUST* be the same as
+ // the order in which they are exported. Otherwise, caching will
+ // fail.
+ if( getExportFlags() & (SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT) )
+ {
+ if( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
+ {
+ // only master pages are exported => styles for frames bound
+ // to frames (but none for frames bound to pages) need to be
+ // collected.
+ // TODO: exclude PageBoundFrames on export
+ }
+ }
+
+ // exported in _ExportMasterStyles
+ if( getExportFlags() & SvXMLExportFlags::MASTERSTYLES )
+ GetPageExport()->collectAutoStyles( false );
+
+
+ // exported in ExportContent_
+ if( getExportFlags() & SvXMLExportFlags::CONTENT )
+ {
+ // collect form autostyle
+ // (do this before collectTextAutoStyles, 'cause the shapes need the results of the work
+ // done by examineForms)
+ Reference<XDrawPageSupplier> xDrawPageSupplier( GetModel(), UNO_QUERY );
+ if (xDrawPageSupplier.is() && GetFormExport().is())
+ {
+ Reference<XDrawPage> xPage = xDrawPageSupplier->getDrawPage();
+ if (xPage.is())
+ GetFormExport()->examineForms(xPage);
+ }
+
+ GetTextParagraphExport()->collectTextAutoStylesOptimized( m_bShowProgress );
+ }
+
+ mbAutoStylesCollected = true;
+}
+
+void SwXMLExport::ExportAutoStyles_()
+{
+ collectAutoStyles();
+
+ // if we don't export styles (i.e. in content stream only, but not
+ // in single-stream case), then we can save ourselves a bit of
+ // work and memory by not collecting field masters
+ if( !(getExportFlags() & SvXMLExportFlags::STYLES) )
+ GetTextParagraphExport()->exportUsedDeclarations();
+
+ // exported in ExportContent_
+ if( getExportFlags() & SvXMLExportFlags::CONTENT )
+ {
+ GetTextParagraphExport()->exportTrackedChanges( true );
+ }
+
+ GetTextParagraphExport()->exportTextAutoStyles();
+ GetShapeExport()->exportAutoStyles();
+ if( getExportFlags() & SvXMLExportFlags::MASTERSTYLES )
+ GetPageExport()->exportAutoStyles();
+
+ // we rely on data styles being written after cell styles in the
+ // ExportFormat() method; so be careful when changing order.
+ exportAutoDataStyles();
+
+ SvXMLExportFlags nContentAutostyles = SvXMLExportFlags::CONTENT | SvXMLExportFlags::AUTOSTYLES;
+ if ( ( getExportFlags() & nContentAutostyles ) == nContentAutostyles )
+ GetFormExport()->exportAutoStyles();
+}
+
+XMLPageExport* SwXMLExport::CreatePageExport()
+{
+ return new XMLTextMasterPageExport( *this );
+}
+
+void SwXMLExport::ExportMasterStyles_()
+{
+ // export master styles
+ GetPageExport()->exportMasterStyles( false );
+}
+
+namespace {
+
+class SwXMLAutoStylePoolP : public SvXMLAutoStylePoolP
+{
+ SvXMLExport& m_rExport;
+ const OUString m_sListStyleName;
+ const OUString m_sMasterPageName;
+
+protected:
+
+ virtual void exportStyleAttributes(
+ comphelper::AttributeList& rAttrList,
+ XmlStyleFamily nFamily,
+ const std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const override;
+public:
+
+ explicit SwXMLAutoStylePoolP( SvXMLExport& rExport );
+};
+
+}
+
+void SwXMLAutoStylePoolP::exportStyleAttributes(
+ comphelper::AttributeList& rAttrList,
+ XmlStyleFamily nFamily,
+ const std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const
+{
+ SvXMLAutoStylePoolP::exportStyleAttributes( rAttrList, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap);
+
+ if( XmlStyleFamily::TEXT_PARAGRAPH != nFamily )
+ return;
+
+ for( const auto& rProperty : rProperties )
+ {
+ if (rProperty.mnIndex != -1) // #i26762#
+ {
+ switch( rPropExp.getPropertySetMapper()->
+ GetEntryContextId( rProperty.mnIndex ) )
+ {
+ case CTF_NUMBERINGSTYLENAME:
+ {
+ OUString sStyleName;
+ rProperty.maValue >>= sStyleName;
+ // #i70748# - export also empty list styles
+ if( !sStyleName.isEmpty() )
+ {
+ OUString sTmp = m_rExport.GetTextParagraphExport()->GetListAutoStylePool().Find( sStyleName );
+ if( !sTmp.isEmpty() )
+ sStyleName = sTmp;
+ }
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE,
+ m_sListStyleName,
+ GetExport().EncodeStyleName( sStyleName ) );
+ }
+ break;
+ case CTF_PAGEDESCNAME:
+ {
+ OUString sStyleName;
+ rProperty.maValue >>= sStyleName;
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE,
+ m_sMasterPageName,
+ GetExport().EncodeStyleName( sStyleName ) );
+ }
+ break;
+ }
+ }
+ }
+}
+
+SwXMLAutoStylePoolP::SwXMLAutoStylePoolP(SvXMLExport& rExp ) :
+ SvXMLAutoStylePoolP( rExp ),
+ m_rExport( rExp ),
+ m_sListStyleName( GetXMLToken( XML_LIST_STYLE_NAME ) ),
+ m_sMasterPageName( GetXMLToken( XML_MASTER_PAGE_NAME ) )
+{
+}
+
+SvXMLAutoStylePoolP* SwXMLExport::CreateAutoStylePool()
+{
+ return new SwXMLAutoStylePoolP( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlfonte.cxx b/sw/source/filter/xml/xmlfonte.cxx
new file mode 100644
index 0000000000..c6a9c89cb6
--- /dev/null
+++ b/sw/source/filter/xml/xmlfonte.cxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+#include <xmloff/XMLFontAutoStylePool.hxx>
+#include <editeng/fontitem.hxx>
+#include <doc.hxx>
+#include "xmlexp.hxx"
+#include "xmlimp.hxx"
+#include <IDocumentSettingAccess.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::text;
+
+namespace {
+
+class SwXMLFontAutoStylePool_Impl: public XMLFontAutoStylePool
+{
+public:
+ SwXMLFontAutoStylePool_Impl(SwXMLExport& rExport, bool bFontEmbedding);
+};
+
+}
+
+namespace
+{
+sal_Int32 CompareTo(sal_Int32 nA, sal_Int32 nB)
+{
+ if (nA < nB)
+ {
+ return -1;
+ }
+
+ if (nA > nB)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+}
+
+SwXMLFontAutoStylePool_Impl::SwXMLFontAutoStylePool_Impl(SwXMLExport& _rExport, bool bFontEmbedding)
+ : XMLFontAutoStylePool(_rExport, bFontEmbedding)
+{
+ sal_uInt16 const aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CTL_FONT };
+
+ const SfxItemPool& rPool = _rExport.getDoc()->GetAttrPool();
+ std::vector<const SvxFontItem *> aFonts;
+ for(sal_uInt16 nWhichId : aWhichIds)
+ {
+ const SvxFontItem& rFont =
+ static_cast<const SvxFontItem&>(rPool.GetDefaultItem( nWhichId ));
+ aFonts.push_back(&rFont);
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId))
+ {
+ auto pFont = static_cast<const SvxFontItem *>(pItem);
+ aFonts.push_back(pFont);
+ }
+ }
+
+ std::sort(aFonts.begin(), aFonts.end(),
+ [](const SvxFontItem* pA, const SvxFontItem* pB) -> bool
+ {
+ sal_Int32 nRet = pA->GetFamilyName().compareTo(pB->GetFamilyName());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ nRet = pA->GetStyleName().compareTo(pB->GetStyleName());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ nRet = CompareTo(pA->GetFamily(), pB->GetFamily());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ nRet = CompareTo(pA->GetPitch(), pB->GetPitch());
+ if (nRet != 0)
+ {
+ return nRet < 0;
+ }
+
+ return pA->GetCharSet() < pB->GetCharSet();
+ });
+ for (const auto& pFont : aFonts)
+ {
+ Add(pFont->GetFamilyName(), pFont->GetStyleName(), pFont->GetFamily(), pFont->GetPitch(),
+ pFont->GetCharSet());
+ }
+
+ auto const & pDocument = _rExport.getDoc();
+
+ m_bEmbedUsedOnly = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_USED_FONTS);
+ m_bEmbedLatinScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS);
+ m_bEmbedAsianScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS);
+ m_bEmbedComplexScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS);
+
+}
+
+XMLFontAutoStylePool* SwXMLExport::CreateFontAutoStylePool()
+{
+ bool blockFontEmbedding = false;
+ // We write font info to both content.xml and styles.xml, but they are both
+ // written by different SwXMLExport instance, and would therefore write each
+ // font file twice without complicated checking for duplicates, so handle
+ // the embedding only in one of them.
+ if( !( getExportFlags() & SvXMLExportFlags::CONTENT) )
+ blockFontEmbedding = true;
+ if( !getDoc()->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS ))
+ blockFontEmbedding = true;
+ return new SwXMLFontAutoStylePool_Impl( *this, !blockFontEmbedding );
+}
+
+void SwXMLImport::NotifyContainsEmbeddedFont()
+{
+ getDoc()->getIDocumentSettingAccess().set( DocumentSettingId::EMBED_FONTS, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlimp.cxx b/sw/source/filter/xml/xmlimp.cxx
new file mode 100644
index 0000000000..81ddfbbb5f
--- /dev/null
+++ b/sw/source/filter/xml/xmlimp.cxx
@@ -0,0 +1,1924 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+
+#include <com/sun/star/document/PrinterIndependentLayout.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+#include <o3tl/any.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/txtimp.hxx>
+#include <xmloff/XMLTextShapeImportHelper.hxx>
+#include <xmloff/XMLFontStylesContext.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <TextCursorHelper.hxx>
+#include <unotext.hxx>
+#include <unotextrange.hxx>
+#include <poolfmt.hxx>
+#include <ndtxt.hxx>
+#include <editsh.hxx>
+#include <strings.hrc>
+#include <svl/stritem.hxx>
+#include "xmlimp.hxx"
+#include "xmlimpit.hxx"
+#include "xmltexti.hxx"
+#include <list.hxx>
+#include <swdll.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <docsh.hxx>
+#include <svx/xmlgrhlp.hxx>
+#include <svx/xmleohlp.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <unotools/fcm.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+#include <tools/UnitConversion.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <vcl/svapp.hxx>
+#include <unotxdoc.hxx>
+#include <numrule.hxx>
+
+#include <xmloff/xmlmetai.hxx>
+#include <xmloff/xformsimport.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <unordered_set>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::xforms;
+using namespace ::xmloff::token;
+
+namespace {
+
+class SwXMLBodyContext_Impl : public SvXMLImportContext
+{
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+
+public:
+
+ SwXMLBodyContext_Impl( SwXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+};
+
+}
+
+SwXMLBodyContext_Impl::SwXMLBodyContext_Impl( SwXMLImport& rImport ) :
+ SvXMLImportContext( rImport )
+{
+ // tdf#107211: if at this point we don't have a defined char style "Default"
+ // or "Default Style", add a mapping for it as it is not written
+ // into the file since it's not really a style but "no style"
+ // (hence referencing it actually makes no sense except for hyperlinks
+ // which default to something other than "Default")
+ OUString const sDefault(SwResId(STR_POOLCHR_STANDARD));
+ uno::Reference<container::XNameContainer> const& xStyles(
+ rImport.GetTextImport()->GetTextStyles());
+ if (!xStyles->hasByName("Default"))
+ { // this old name was used before LO 4.0
+ rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, "Default", sDefault);
+ }
+ if (!xStyles->hasByName("Default_20_Style"))
+ { // this new name contains a space which is converted to _20_ on export
+ rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, "Default_20_Style", sDefault);
+ }
+ bool isEncoded(false);
+ OUString const defaultEncoded(
+ rImport.GetMM100UnitConverter().encodeStyleName(sDefault, &isEncoded));
+ if (isEncoded && defaultEncoded != "Default_20_Style"
+ && !xStyles->hasByName(defaultEncoded))
+ { // new name may contain a space which is converted to _20_ on export
+ rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, defaultEncoded, sDefault);
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBodyContext_Impl::createFastChildContext(
+ sal_Int32 /*nElement*/,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ return GetSwImport().CreateBodyContentContext();
+}
+
+namespace {
+
+// #i69629#
+// enhance class <SwXMLDocContext_Impl> in order to be able to create subclasses
+// NB: virtually inherit so we can multiply inherit properly
+// in SwXMLOfficeDocContext_Impl
+class SwXMLDocContext_Impl : public virtual SvXMLImportContext
+{
+ sal_Int32 mnElement;
+
+protected: // #i69629#
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+
+public:
+ SwXMLDocContext_Impl( SwXMLImport& rImport, sal_Int32 nElement );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+}
+
+SwXMLDocContext_Impl::SwXMLDocContext_Impl( SwXMLImport& rImport, sal_Int32 nElement ) :
+ SvXMLImportContext( rImport ), mnElement(nElement)
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SwXMLDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(OFFICE, XML_SCRIPTS):
+ return GetSwImport().CreateScriptContext();
+ case XML_ELEMENT(OFFICE, XML_SETTINGS):
+ return new XMLDocumentSettingsContext( GetImport() );
+ case XML_ELEMENT(OFFICE, XML_STYLES):
+ GetSwImport().GetProgressBarHelper()->Increment( PROGRESS_BAR_STEP );
+ return GetSwImport().CreateStylesContext( false );
+ case XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES):
+ // don't use the autostyles from the styles-document for the progress
+ if ( mnElement != 0 && (mnElement & TOKEN_MASK) != XML_DOCUMENT_STYLES )
+ GetSwImport().GetProgressBarHelper()->Increment
+ ( PROGRESS_BAR_STEP );
+ return GetSwImport().CreateStylesContext( true );
+ case XML_ELEMENT(OFFICE, XML_MASTER_STYLES):
+ return GetSwImport().CreateMasterStylesContext();
+ case XML_ELEMENT(OFFICE, XML_FONT_FACE_DECLS):
+ return GetSwImport().CreateFontDeclsContext();
+ case XML_ELEMENT(OFFICE, XML_META):
+ OSL_FAIL(" XML_ELEMENT(OFFICE, XML_META): should not have come here, maybe document is invalid?");
+ break;
+ case XML_ELEMENT(OFFICE, XML_BODY):
+ GetSwImport().GetProgressBarHelper()->Increment( PROGRESS_BAR_STEP );
+ return new SwXMLBodyContext_Impl( GetSwImport() );
+ case XML_ELEMENT(XFORMS, XML_MODEL):
+ return createXFormsModelContext(GetImport());
+ default:
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement);
+ }
+ return nullptr;
+}
+
+namespace {
+
+// #i69629# - new subclass <SwXMLOfficeDocContext_Impl> of class <SwXMLDocContext_Impl>
+class SwXMLOfficeDocContext_Impl :
+ public SwXMLDocContext_Impl, public SvXMLMetaDocumentContext
+{
+public:
+
+ SwXMLOfficeDocContext_Impl( SwXMLImport& rImport, sal_Int32 nElement,
+ const Reference< document::XDocumentProperties >& xDocProps);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override;
+};
+
+}
+
+SwXMLOfficeDocContext_Impl::SwXMLOfficeDocContext_Impl(
+ SwXMLImport& rImport,
+ sal_Int32 nElement,
+ const Reference< document::XDocumentProperties >& xDocProps) :
+ SvXMLImportContext( rImport ),
+ SwXMLDocContext_Impl( rImport, nElement ),
+ SvXMLMetaDocumentContext( rImport, xDocProps )
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SwXMLOfficeDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ // assign paragraph styles to list levels of outline style after all styles
+ // are imported and finished. This is the case, when <office:body> starts
+ // in flat OpenDocument file format.
+ {
+ if( nElement == XML_ELEMENT( OFFICE, XML_BODY ) )
+ {
+ GetImport().GetTextImport()->SetOutlineStyles( true );
+ }
+ }
+
+ // behave like meta base class iff we encounter office:meta
+ if ( nElement == XML_ELEMENT( OFFICE, XML_META ) ) {
+ return SvXMLMetaDocumentContext::createFastChildContext(
+ nElement, xAttrList );
+ } else {
+ return SwXMLDocContext_Impl::createFastChildContext(
+ nElement, xAttrList );
+ }
+}
+
+namespace {
+
+// #i69629# - new subclass <SwXMLDocStylesContext_Impl> of class <SwXMLDocContext_Impl>
+class SwXMLDocStylesContext_Impl : public SwXMLDocContext_Impl
+{
+public:
+
+ SwXMLDocStylesContext_Impl( SwXMLImport& rImport, sal_Int32 nElement );
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+SwXMLDocStylesContext_Impl::SwXMLDocStylesContext_Impl( SwXMLImport& rImport, sal_Int32 nElement ) :
+ SvXMLImportContext( rImport ),
+ SwXMLDocContext_Impl( rImport, nElement )
+{
+}
+
+void SwXMLDocStylesContext_Impl::endFastElement(sal_Int32 )
+{
+ // assign paragraph styles to list levels of outline style after all styles
+ // are imported and finished.
+ SwXMLImport& rSwImport = dynamic_cast<SwXMLImport&>( GetImport());
+ GetImport().GetTextImport()->SetOutlineStyles(
+ bool(rSwImport.GetStyleFamilyMask() & SfxStyleFamily::Para));
+}
+
+SvXMLImportContext *SwXMLImport::CreateFastContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ):
+ pContext = CreateMetaContext(nElement);
+ break;
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT ):
+ {
+ uno::Reference<document::XDocumentProperties> const xDocProps(
+ GetDocumentProperties());
+ // flat OpenDocument file format
+ pContext = new SwXMLOfficeDocContext_Impl( *this, nElement, xDocProps );
+ }
+ break;
+ // #i69629# - own subclasses for <office:document> and <office:document-styles>
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_SETTINGS):
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_CONTENT):
+ pContext = new SwXMLDocContext_Impl( *this, nElement );
+ break;
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_STYLES):
+ pContext = new SwXMLDocStylesContext_Impl( *this, nElement );
+ break;
+ }
+ return pContext;
+}
+
+SwXMLImport::SwXMLImport(
+ const uno::Reference< uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlags)
+: SvXMLImport( rContext, implementationName, nImportFlags ),
+ m_nStyleFamilyMask( SfxStyleFamily::All ),
+ m_bLoadDoc( true ),
+ m_bInsert( false ),
+ m_bBlock( false ),
+ m_bOrganizerMode( false ),
+ m_bInititedXForms( false ),
+ m_pDoc( nullptr ),
+ m_sDefTableName(SwResId(STR_TABLE_DEFNAME))
+{
+ InitItemImport();
+}
+
+SwXMLImport::~SwXMLImport() noexcept
+{
+ if (HasShapeImport())
+ {
+ SAL_WARN("sw", "endDocument skipped, dropping shapes now to avoid dangling SvTextShapeImportHelper pointing to this");
+ ClearShapeImport();
+ }
+ FinitItemImport();
+ // Call cleanup() here because the destruction of some stuff like XMLRedlineImportHelper will cast
+ // to cast their mrImport to SwXMLImport and that is illegal after this destructor is done.
+ cleanup();
+}
+
+void SwXMLImport::setTextInsertMode(
+ const Reference< XTextRange > & rInsertPos )
+{
+ m_bInsert = true;
+
+ Reference < XText > xText = rInsertPos->getText();
+ Reference < XTextCursor > xTextCursor =
+ xText->createTextCursorByRange( rInsertPos );
+ GetTextImport()->SetCursor( xTextCursor );
+}
+
+void SwXMLImport::setStyleInsertMode( SfxStyleFamily nFamilies,
+ bool bOverwrite )
+{
+ m_bInsert = !bOverwrite;
+ m_nStyleFamilyMask = nFamilies;
+ m_bLoadDoc = false;
+}
+
+static OTextCursorHelper *lcl_xml_GetSwXTextCursor( const Reference < XTextCursor >& rTextCursor )
+{
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(rTextCursor.get());
+ OSL_ENSURE( pTextCursor, "SwXTextCursor missing" );
+ return pTextCursor;
+}
+
+void SwXMLImport::startDocument()
+{
+ // delegate to parent
+ SvXMLImport::startDocument();
+
+ OSL_ENSURE( GetModel().is(), "model is missing" );
+ if( !GetModel().is() )
+ return;
+
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ Reference< XPropertySet > xImportInfo( getImportInfo() );
+ Reference< XPropertySetInfo > xPropertySetInfo;
+ if( xImportInfo.is() )
+ xPropertySetInfo = xImportInfo->getPropertySetInfo();
+ if( xPropertySetInfo.is() )
+ {
+ Any aAny;
+ // insert style mode?
+ OUString sStyleInsertModeFamilies("StyleInsertModeFamilies");
+ if( xPropertySetInfo->hasPropertyByName(sStyleInsertModeFamilies) )
+ {
+ aAny = xImportInfo->getPropertyValue(sStyleInsertModeFamilies);
+ Sequence< OUString> aFamiliesSeq;
+ if( aAny >>= aFamiliesSeq )
+ {
+ SfxStyleFamily nFamilyMask = SfxStyleFamily::None;
+ for( const OUString& rFamily : std::as_const(aFamiliesSeq) )
+ {
+ if( rFamily=="FrameStyles" )
+ nFamilyMask |= SfxStyleFamily::Frame;
+ else if( rFamily=="PageStyles" )
+ nFamilyMask |= SfxStyleFamily::Page;
+ else if( rFamily=="CharacterStyles" )
+ nFamilyMask |= SfxStyleFamily::Char;
+ else if( rFamily=="ParagraphStyles" )
+ nFamilyMask |= SfxStyleFamily::Para;
+ else if( rFamily=="NumberingStyles" )
+ nFamilyMask |= SfxStyleFamily::Pseudo;
+ }
+
+ bool bOverwrite = false;
+ static constexpr OUString sStyleInsertModeOverwrite(u"StyleInsertModeOverwrite"_ustr);
+ if( xPropertySetInfo->hasPropertyByName(sStyleInsertModeOverwrite) )
+ {
+ aAny = xImportInfo->getPropertyValue(sStyleInsertModeOverwrite);
+ if( auto b = o3tl::tryAccess<bool>(aAny) )
+ {
+ if( *b )
+ bOverwrite = true;
+ }
+ }
+
+ setStyleInsertMode( nFamilyMask, bOverwrite );
+ }
+ }
+
+ // text insert mode?
+ static constexpr OUString sTextInsertModeRange(u"TextInsertModeRange"_ustr);
+ if( xPropertySetInfo->hasPropertyByName(sTextInsertModeRange) )
+ {
+ aAny = xImportInfo->getPropertyValue(sTextInsertModeRange);
+ Reference<XTextRange> xInsertTextRange;
+ if( aAny >>= xInsertTextRange )
+ setTextInsertMode( xInsertTextRange );
+ }
+
+ // auto text mode
+ static constexpr OUString sAutoTextMode(u"AutoTextMode"_ustr);
+ if( xPropertySetInfo->hasPropertyByName(sAutoTextMode) )
+ {
+ aAny = xImportInfo->getPropertyValue(sAutoTextMode);
+ if( auto b = o3tl::tryAccess<bool>(aAny) )
+ {
+ if( *b )
+ m_bBlock = true;
+ }
+ }
+
+ // organizer mode
+ static constexpr OUString sOrganizerMode(u"OrganizerMode"_ustr);
+ if( xPropertySetInfo->hasPropertyByName(sOrganizerMode) )
+ {
+ aAny = xImportInfo->getPropertyValue(sOrganizerMode);
+ if( auto b = o3tl::tryAccess<bool>(aAny) )
+ {
+ if( *b )
+ m_bOrganizerMode = true;
+ }
+ }
+
+ // default document properties
+ static constexpr OUString sDefSettings(u"DefaultDocumentSettings"_ustr);
+ if (xPropertySetInfo->hasPropertyByName(sDefSettings))
+ {
+ aAny = xImportInfo->getPropertyValue(sDefSettings);
+ Sequence<PropertyValue> aProps;
+ if (aAny >>= aProps)
+ {
+ Reference<lang::XMultiServiceFactory> xFac(GetModel(), UNO_QUERY);
+ Reference<XPropertySet> xProps(
+ xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY);
+ Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo());
+
+ if (xProps.is() && xInfo.is())
+ {
+ for (const auto& rProp : std::as_const(aProps))
+ {
+ if (xInfo->hasPropertyByName(rProp.Name))
+ {
+ xProps->setPropertyValue(rProp.Name, rProp.Value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // There only is a text cursor by now if we are in insert mode. In any
+ // other case we have to create one at the start of the document.
+ // We also might change into the insert mode later, so we have to make
+ // sure to first set the insert mode and then create the text import
+ // helper. Otherwise it won't have the insert flag set!
+ OTextCursorHelper *pTextCursor = nullptr;
+ Reference < XTextCursor > xTextCursor;
+ if( HasTextImport() )
+ xTextCursor = GetTextImport()->GetCursor();
+ if( !xTextCursor.is() )
+ {
+ Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY );
+ Reference < XText > xText = xTextDoc->getText();
+ xTextCursor = xText->createTextCursor();
+ SwCursorShell *pCursorSh = nullptr;
+ SwDoc *pDoc = nullptr;
+ if( SvXMLImportFlags::ALL == getImportFlags() )
+ {
+ pTextCursor = lcl_xml_GetSwXTextCursor( xTextCursor );
+ OSL_ENSURE( pTextCursor, "SwXTextCursor missing" );
+ if( !pTextCursor )
+ return;
+
+ pDoc = pTextCursor->GetDoc();
+ OSL_ENSURE( pDoc, "SwDoc missing" );
+ if( !pDoc )
+ return;
+
+ // Is there an edit shell. If yes, then we are currently inserting
+ // a document. We then have to insert at the current edit shell's
+ // cursor position. That not quite clean code, but there is no other
+ // way currently.
+ pCursorSh = pDoc->GetEditShell();
+ }
+ if( pCursorSh )
+ {
+ const rtl::Reference<SwXTextRange> xInsertTextRange(
+ SwXTextRange::CreateXTextRange(
+ *pDoc, *pCursorSh->GetCursor()->GetPoint(), nullptr ) );
+ setTextInsertMode( xInsertTextRange );
+ xTextCursor = GetTextImport()->GetCursor();
+ pTextCursor = nullptr;
+ }
+ else
+ GetTextImport()->SetCursor( xTextCursor );
+ }
+
+ if( !(getImportFlags() & (SvXMLImportFlags::CONTENT|SvXMLImportFlags::MASTERSTYLES)) )
+ return;
+
+ if( !pTextCursor )
+ pTextCursor = lcl_xml_GetSwXTextCursor( xTextCursor );
+ OSL_ENSURE( pTextCursor, "SwXTextCursor missing" );
+ if( !pTextCursor )
+ return;
+
+ SwDoc *pDoc = pTextCursor->GetDoc();
+ OSL_ENSURE( pDoc, "SwDoc missing" );
+ if( !pDoc )
+ return;
+
+ if (SvXMLImportFlags::ALL == getImportFlags())
+ {
+ // for flat ODF - this is done in SwReader::Read() for package ODF
+ pDoc->SetInReading(true);
+ pDoc->SetInXMLImport(true);
+ }
+
+ if( (getImportFlags() & SvXMLImportFlags::CONTENT) && !IsStylesOnlyMode() )
+ {
+ m_oSttNdIdx.emplace( pDoc->GetNodes() );
+ if( IsInsertMode() )
+ {
+ SwPaM *pPaM = pTextCursor->GetPaM();
+ const SwPosition* pPos = pPaM->GetPoint();
+
+ // Split once and remember the node that has been split.
+ pDoc->getIDocumentContentOperations().SplitNode( *pPos, false );
+ *m_oSttNdIdx = pPos->GetNodeIndex()-1;
+
+ // Split again.
+ pDoc->getIDocumentContentOperations().SplitNode( *pPos, false );
+
+ // Insert all content into the new node
+ pPaM->Move( fnMoveBackward );
+ pDoc->SetTextFormatColl
+ ( *pPaM, pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false ) );
+ }
+ }
+
+ // We need a draw model to be able to set the z order
+ pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed
+
+ // SJ: #i49801# locking the model to disable repaints
+ SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pDrawModel )
+ pDrawModel->setLock(true);
+
+ if (!GetGraphicStorageHandler().is())
+ {
+ m_xGraphicStorageHandler = SvXMLGraphicHelper::Create(SvXMLGraphicHelperMode::Read);
+ SetGraphicStorageHandler(m_xGraphicStorageHandler);
+ }
+
+ if( !GetEmbeddedResolver().is() )
+ {
+ SfxObjectShell *pPersist = pDoc->GetPersist();
+ if( pPersist )
+ {
+ m_xEmbeddedResolver = SvXMLEmbeddedObjectHelper::Create(
+ *pPersist,
+ SvXMLEmbeddedObjectHelperMode::Read );
+ SetEmbeddedResolver( m_xEmbeddedResolver );
+ }
+ }
+}
+
+void SwXMLImport::endDocument()
+{
+ OSL_ENSURE( GetModel().is(), "model missing; maybe startDocument wasn't called?" );
+ if( !GetModel().is() )
+ return;
+
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ if (m_xGraphicStorageHandler)
+ m_xGraphicStorageHandler->dispose();
+ m_xGraphicStorageHandler.clear();
+
+ if( m_xEmbeddedResolver )
+ m_xEmbeddedResolver->dispose();
+ m_xEmbeddedResolver.clear();
+ // Clear the shape import to sort the shapes (and not in the
+ // destructor that might be called after the import has finished
+ // for Java filters.
+ if( HasShapeImport() )
+ ClearShapeImport();
+
+ SwDoc *pDoc = nullptr;
+ if( (getImportFlags() & SvXMLImportFlags::CONTENT) && !IsStylesOnlyMode() )
+ {
+ Reference<XInterface> xCursorTunnel( GetTextImport()->GetCursor(),
+ UNO_QUERY);
+ assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor");
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(xCursorTunnel.get());
+ assert(pTextCursor && "SwXTextCursor missing");
+ SwPaM *pPaM = pTextCursor->GetPaM();
+ if( IsInsertMode() && m_oSttNdIdx->GetIndex() )
+ {
+ // If we are in insert mode, join the split node that is in front
+ // of the new content with the first new node. Or in other words:
+ // Revert the first split node.
+ SwTextNode* pTextNode = m_oSttNdIdx->GetNode().GetTextNode();
+ SwNodeIndex aNxtIdx( *m_oSttNdIdx );
+ if( pTextNode && pTextNode->CanJoinNext( &aNxtIdx ) &&
+ m_oSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex() )
+ {
+ // If the PaM points to the first new node, move the PaM to the
+ // end of the previous node.
+ if( pPaM->GetPoint()->GetNode() == aNxtIdx.GetNode() )
+ {
+ pPaM->GetPoint()->Assign( *pTextNode,
+ pTextNode->GetText().getLength());
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ // !!! This should be impossible !!!!
+ OSL_ENSURE( m_oSttNdIdx->GetIndex()+1 !=
+ pPaM->GetBound().GetNodeIndex(),
+ "PaM.Bound1 point to new node " );
+ OSL_ENSURE( m_oSttNdIdx->GetIndex()+1 !=
+ pPaM->GetBound( false ).GetNodeIndex(),
+ "PaM.Bound2 points to new node" );
+
+ if( m_oSttNdIdx->GetIndex()+1 ==
+ pPaM->GetBound().GetNodeIndex() )
+ {
+ const sal_Int32 nCntPos =
+ pPaM->GetBound().GetContentIndex();
+ pPaM->GetBound().SetContent(
+ pTextNode->GetText().getLength() + nCntPos );
+ }
+ if( m_oSttNdIdx->GetIndex()+1 ==
+ pPaM->GetBound( false ).GetNodeIndex() )
+ {
+ const sal_Int32 nCntPos =
+ pPaM->GetBound( false ).GetContentIndex();
+ pPaM->GetBound( false ).SetContent(
+ pTextNode->GetText().getLength() + nCntPos );
+ }
+#endif
+ // If the first new node isn't empty, convert the node's text
+ // attributes into hints. Otherwise, set the new node's
+ // paragraph style at the previous (empty) node.
+ SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode();
+ if (!pTextNode->GetText().isEmpty())
+ pDelNd->FormatToTextAttr( pTextNode );
+ else
+ {
+ pTextNode->ResetAttr(RES_CHRATR_BEGIN, RES_CHRATR_END);
+ pTextNode->ChgFormatColl( pDelNd->GetTextColl() );
+ if (!pDelNd->GetNoCondAttr(RES_PARATR_LIST_ID, /*bInParents=*/false))
+ {
+ // MergeListsAtDocumentInsertPosition() will deal with lists below, copy
+ // paragraph direct formatting otherwise.
+ pDelNd->CopyCollFormat(*pTextNode);
+ }
+ }
+ pTextNode->JoinNext();
+ }
+ }
+
+ SwPosition* pPos = pPaM->GetPoint();
+ OSL_ENSURE( !pPos->GetContentIndex(), "last paragraph isn't empty" );
+ if( !pPos->GetContentIndex() )
+ {
+ SwTextNode* pCurrNd;
+ SwNodeOffset nNodeIdx = pPos->GetNodeIndex();
+ pDoc = &pPaM->GetDoc();
+
+ OSL_ENSURE( pPos->GetNode().IsContentNode(),
+ "insert position is not a content node" );
+ if( !IsInsertMode() )
+ {
+ // If we're not in insert mode, the last node is deleted.
+ const SwNode *pPrev = pDoc->GetNodes()[nNodeIdx -1];
+ if( pPrev->IsContentNode() ||
+ ( pPrev->IsEndNode() &&
+ pPrev->StartOfSectionNode()->IsSectionNode() ) )
+ {
+ SwContentNode* pCNd = pPaM->GetPointContentNode();
+ if( pCNd && pCNd->StartOfSectionIndex()+2 <
+ pCNd->EndOfSectionIndex() )
+ {
+ SwNode& rDelNode = pPaM->GetPoint()->GetNode();
+ // move so we don't have a dangling SwContentIndex to the deleted node
+ pPaM->GetPoint()->Adjust(SwNodeOffset(+1));
+ if (pPaM->HasMark())
+ pPaM->GetMark()->Adjust(SwNodeOffset(+1));
+ pDoc->GetNodes().Delete( rDelNode );
+ }
+ }
+ }
+ else if( nullptr != (pCurrNd = pDoc->GetNodes()[nNodeIdx]->GetTextNode()) )
+ {
+ // Id we're in insert mode, the empty node is joined with
+ // the next and the previous one.
+ if( pCurrNd->CanJoinNext( pPos ))
+ {
+ SwTextNode* pNextNd = pPos->GetNode().GetTextNode();
+ bool endNodeFound = pDoc->GetNodes()[nNodeIdx-1]->IsEndNode();
+ SwNode *pLastPar = pDoc->GetNodes()[nNodeIdx -2];
+ if ( !pLastPar->IsTextNode() ) {
+ pLastPar = pDoc->GetNodes()[nNodeIdx -1];
+ }
+ if ( !endNodeFound && pLastPar->IsTextNode() )
+ {
+ pNextNd->ChgFormatColl(pLastPar->GetTextNode()->GetTextColl());
+ }
+
+ pPaM->SetMark(); pPaM->DeleteMark();
+ pNextNd->JoinPrev();
+
+ // Remove line break that has been inserted by the import,
+ // but only if one has been inserted and
+ // no endNode found to avoid removing section
+ if( pNextNd->CanJoinPrev(/* &pPos->nNode*/ ) && !endNodeFound &&
+ *m_oSttNdIdx != pPos->GetNode() )
+ {
+ pNextNd->JoinPrev();
+ }
+ }
+ else if (pCurrNd->GetText().isEmpty())
+ {
+ pPaM->SetMark(); pPaM->DeleteMark();
+ SwNode& rDelNode = pPos->GetNode();
+ // move so we don't have a dangling SwContentIndex to the deleted node
+ pPaM->GetPoint()->Adjust(SwNodeOffset(+1));
+ pDoc->GetNodes().Delete( rDelNode );
+ pPaM->Move( fnMoveBackward );
+ }
+ }
+
+ // tdf#113877
+ // when we insert one document with list inside into another one with list at the insert position,
+ // the resulting numbering in these lists is not consequent.
+ //
+ // Main document:
+ // 1. One
+ // 2. Two
+ // 3. Three
+ // 4. <-- insert position
+ //
+ // Inserted document:
+ // 1. One
+ // 2. Two
+ // 3. Three
+ // 4.
+ //
+ // Expected result
+ // 1. One
+ // 2. Two
+ // 3. Three
+ // 4. One
+ // 5. Two
+ // 6. Three
+ // 7.
+ //
+ MergeListsAtDocumentInsertPosition(pDoc);
+ }
+ }
+
+ /* Was called too early. Moved from SwXMLBodyContext_Impl::EndElement */
+
+ GetTextImport()->RedlineAdjustStartNodeCursor();
+
+ if( (getImportFlags() & SvXMLImportFlags::CONTENT) ||
+ ((getImportFlags() & SvXMLImportFlags::MASTERSTYLES) && IsStylesOnlyMode()) )
+ {
+ // pDoc might be 0. In this case UpdateTextCollCondition is looking
+ // for it itself.
+ UpdateTextCollConditions( pDoc );
+ }
+
+ GetTextImport()->ResetCursor();
+
+ m_oSttNdIdx.reset();
+
+ // tdf#150753: pDoc may be null e.g. when the package lacks content.xml;
+ // we should not forget to tidy up here, including unlocking draw model
+ if (!pDoc)
+ pDoc = getDoc();
+ assert(pDoc);
+ // SJ: #i49801# -> now permitting repaints
+ if (getImportFlags() == SvXMLImportFlags::ALL)
+ {
+ // Notify math objects. If we are in the package filter this will
+ // be done by the filter object itself
+ if (IsInsertMode())
+ pDoc->PrtOLENotify(false);
+ else if (pDoc->IsOLEPrtNotifyPending())
+ pDoc->PrtOLENotify(true);
+
+ assert(pDoc->IsInReading());
+ assert(pDoc->IsInXMLImport());
+ pDoc->SetInReading(false);
+ pDoc->SetInXMLImport(false);
+ }
+
+ SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
+ if (pDrawModel)
+ pDrawModel->setLock(false);
+
+ // #i90243#
+ if ( m_bInititedXForms )
+ {
+ Reference< xforms::XFormsSupplier > xFormsSupp( GetModel(), UNO_QUERY );
+ Reference< XNameAccess > xXForms;
+ if ( xFormsSupp.is() )
+ xXForms = xFormsSupp->getXForms().get();
+
+ if ( xXForms.is() )
+ {
+ try
+ {
+ Sequence< beans::PropertyValue > aXFormsSettings;
+
+ const OUString& sXFormsSettingsName( GetXMLToken( XML_XFORM_MODEL_SETTINGS ) );
+ if ( m_xLateInitSettings.is() && m_xLateInitSettings->hasByName( sXFormsSettingsName ) )
+ {
+ OSL_VERIFY( m_xLateInitSettings->getByName( sXFormsSettingsName ) >>= aXFormsSettings );
+ applyXFormsSettings( xXForms, aXFormsSettings );
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ }
+
+ for (SwNodeOffset i(0); i < pDoc->GetNodes().Count(); ++i)
+ {
+ if (SwTableNode *const pTableNode = pDoc->GetNodes()[i]->GetTableNode())
+ {
+ if (!pTableNode->GetTable().IsNewModel()
+ && pTableNode->GetTable().CanConvertSubtables())
+ {
+ pTableNode->GetTable().ConvertSubtables();
+ }
+ }
+ // don't skip to the end; nested tables could have subtables too...
+ }
+
+ // delegate to parent: takes care of error handling
+ SvXMLImport::endDocument();
+ ClearTextImport();
+}
+
+// tdf#113877
+// when we insert one document with list inside into another one with list at the insert position,
+// the resulting numbering in these lists is not consequent.
+//
+// CASE-1: Main document:
+// 1. One
+// 2. Two
+// 3. Three
+// 4. <-- insert position
+//
+// Inserted document:
+// 1. One
+// 2. Two
+// 3. Three
+// 4.
+//
+// Expected result
+// 1. One
+// 2. Two
+// 3. Three
+// 4. One
+// 5. Two
+// 6. Three
+// 7.
+//
+// CASE-2: Main document:
+// 1. One
+// 2. Two
+// 3. Three
+// 4. <-- insert position
+//
+// Inserted document:
+// A) One
+// B) Two
+// C) Three
+// D)
+//
+// Expected result
+// 1. One
+// 2. Two
+// 3. Three
+// 4. One
+// A) Two
+// B) Three
+// 5.
+//
+void SwXMLImport::MergeListsAtDocumentInsertPosition(SwDoc *pDoc)
+{
+ // 1. check environment
+ if (! pDoc)
+ return;
+
+ if (! IsInsertMode() || ! m_oSttNdIdx->GetIndex())
+ return;
+
+ SwNodeOffset index(1);
+
+ // the last node of the main document where we have inserted a document
+ SwNode* const node1 = pDoc->GetNodes()[m_oSttNdIdx->GetIndex() + 0];
+
+ // the first node of the inserted document
+ SwNode* node2 = pDoc->GetNodes()[m_oSttNdIdx->GetIndex() + index];
+
+ if (! (node1 && node2
+ && (node1->GetNodeType() == node2->GetNodeType())
+ && (node1->IsTextNode() == node2->IsTextNode())
+ ))
+ {
+ // not a text node at insert position
+ return;
+ }
+
+ // 2. get the first node of the inserted document,
+ // which will be used to detect if inside inserted document a new list was started after the first list
+ const SfxPoolItem* pListId2Initial = nullptr;
+ {
+ SwContentNode* contentNode1 = static_cast<SwContentNode *>(node1);
+ SwContentNode* contentNode2 = static_cast<SwContentNode *>(node2);
+
+ // check if both lists have the same list properties
+ const SfxPoolItem* pListId1 = contentNode1->GetNoCondAttr( RES_PARATR_LIST_ID, false );
+ const SfxPoolItem* pListId2 = contentNode2->GetNoCondAttr( RES_PARATR_LIST_ID, false );
+
+ if (! pListId1)
+ return;
+ if (! pListId2)
+ return;
+
+ auto pStringListId1 = dynamic_cast<const SfxStringItem*>(pListId1);
+ assert(pStringListId1);
+ const OUString& sListId1 = pStringListId1->GetValue();
+ auto pStringListId2 = dynamic_cast<const SfxStringItem*>(pListId2);
+ assert(pStringListId2);
+ const OUString& sListId2 = pStringListId2->GetValue();
+
+ const SwList* pList1 = pDoc->getIDocumentListsAccess().getListByName( sListId1 );
+ const SwList* pList2 = pDoc->getIDocumentListsAccess().getListByName( sListId2 );
+
+ if (! pList1)
+ return;
+ if (! pList2)
+ return;
+
+ const OUString& sDefaultListStyleName1 = pList1->GetDefaultListStyleName();
+ const OUString& sDefaultListStyleName2 = pList2->GetDefaultListStyleName();
+
+ if (sDefaultListStyleName1 != sDefaultListStyleName2)
+ {
+ const SwNumRule* pNumRule1 = pDoc->FindNumRulePtr( sDefaultListStyleName1 );
+ const SwNumRule* pNumRule2 = pDoc->FindNumRulePtr( sDefaultListStyleName2 );
+
+ if (pNumRule1 && pNumRule2)
+ {
+ // check style of the each list level
+ for( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
+ {
+ if( pNumRule1->Get( n ) != pNumRule2->Get( n ) )
+ {
+ return;
+ }
+ }
+
+ // our list should be merged
+ pListId2Initial = pListId2;
+ }
+ }
+ else
+ {
+ // our list should be merged
+ pListId2Initial = pListId2;
+ }
+ }
+
+ if (! pListId2Initial)
+ {
+ // two lists have different styles => they should not be merged
+ return;
+ }
+
+ // 3. merge two lists
+ while (
+ node1 && node2
+ && (node1->GetNodeType() == node2->GetNodeType())
+ && (node1->IsTextNode() == node2->IsTextNode())
+ )
+ {
+ SwContentNode* contentNode1 = static_cast<SwContentNode *>( node1 );
+ SwContentNode* contentNode2 = static_cast<SwContentNode *>( node2 );
+
+ const SfxPoolItem* pListId1 = contentNode1->GetNoCondAttr( RES_PARATR_LIST_ID, false );
+ const SfxPoolItem* pListId2 = contentNode2->GetNoCondAttr( RES_PARATR_LIST_ID, false );
+
+ if (! pListId1)
+ return;
+ if (! pListId2)
+ return;
+
+ if (*pListId2Initial != *pListId2)
+ {
+ // no more list items of the first list inside inserted document
+ return;
+ }
+
+ // set list style to this list element
+ contentNode2->SetAttr(*pListId1);
+
+ // get next item
+ index++;
+ if (index >= pDoc->GetNodes().Count())
+ {
+ // no more items
+ return;
+ }
+
+ node2 = pDoc->GetNodes()[m_oSttNdIdx->GetIndex() + index];
+ }
+}
+
+namespace {
+
+// Locally derive XMLTextShapeImportHelper, so we can take care of the
+// form import This is Writer, but not text specific, so it should go
+// here!
+class SvTextShapeImportHelper : public XMLTextShapeImportHelper
+{
+ // hold own reference form import helper, because the SvxImport
+ // stored in the superclass, from whom we originally got the
+ // reference, is already destroyed when we want to use it in the
+ // destructor
+ rtl::Reference< ::xmloff::OFormLayerXMLImport > rFormImport;
+
+ // hold reference to the one page (if it exists) for calling startPage()
+ // and endPage. If !xPage.is(), then this document doesn't have a
+ // XDrawPage.
+ Reference<drawing::XDrawPage> xPage;
+
+public:
+ explicit SvTextShapeImportHelper(SvXMLImport& rImp);
+ virtual ~SvTextShapeImportHelper() override;
+};
+
+}
+
+SvTextShapeImportHelper::SvTextShapeImportHelper(SvXMLImport& rImp) :
+ XMLTextShapeImportHelper(rImp)
+{
+ Reference<drawing::XDrawPageSupplier> xSupplier(rImp.GetModel(),UNO_QUERY);
+ if (xSupplier.is())
+ {
+ if (rImp.GetFormImport().is())
+ {
+ rImp.GetFormImport()->startPage(xSupplier->getDrawPage());
+ rFormImport = rImp.GetFormImport();
+ }
+
+ xPage = xSupplier->getDrawPage();
+ XMLShapeImportHelper::startPage( xPage );
+ }
+}
+
+SvTextShapeImportHelper::~SvTextShapeImportHelper()
+{
+ rFormImport->endPage();
+
+ if (xPage.is())
+ {
+ XMLShapeImportHelper::endPage(xPage);
+ }
+}
+
+XMLTextImportHelper* SwXMLImport::CreateTextImport()
+{
+ return new SwXMLTextImportHelper( GetModel(), *this, getImportInfo(),
+ IsInsertMode(),
+ IsStylesOnlyMode(),
+ IsBlockMode(), m_bOrganizerMode );
+}
+
+XMLShapeImportHelper* SwXMLImport::CreateShapeImport()
+{
+ return new SvTextShapeImportHelper( *this );
+}
+
+SvXMLImportContext *SwXMLImport::CreateFontDeclsContext()
+{
+ XMLFontStylesContext *pFSContext =
+ new XMLFontStylesContext( *this, osl_getThreadTextEncoding() );
+ SetFontDecls( pFSContext );
+ return pFSContext;
+}
+
+void SwXMLImport::SetViewSettings(const Sequence < PropertyValue > & aViewProps)
+{
+ if (IsInsertMode() || IsStylesOnlyMode() || IsBlockMode() || m_bOrganizerMode || !GetModel().is() )
+ return;
+
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ SwDoc *pDoc = getDoc();
+ tools::Rectangle aRect;
+ if( pDoc->GetDocShell() )
+ aRect = pDoc->GetDocShell()->GetVisArea( ASPECT_CONTENT );
+ //TODO/LATER: why that cast?!
+ //aRect = ((SfxInPlaceObject *)pDoc->GetDocShell())->GetVisArea();
+
+ sal_Int64 nTmp = 0;
+ bool bShowRedlineChanges = false, bBrowseMode = false;
+ bool bChangeShowRedline = false, bChangeBrowseMode = false;
+
+ //TODO/LATER: why that cast?!
+ bool bTwip = pDoc->GetDocShell()->GetMapUnit ( ) == MapUnit::MapTwip;
+ //sal_Bool bTwip = pDoc->GetDocShell()->SfxInPlaceObject::GetMapUnit ( ) == MapUnit::MapTwip;
+
+ for (const PropertyValue& rValue : aViewProps)
+ {
+ if ( rValue.Name == "ViewAreaTop" )
+ {
+ rValue.Value >>= nTmp;
+ aRect.SetPosY(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp);
+ }
+ else if ( rValue.Name == "ViewAreaLeft" )
+ {
+ rValue.Value >>= nTmp;
+ aRect.SetPosX(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp);
+ }
+ else if ( rValue.Name == "ViewAreaWidth" )
+ {
+ rValue.Value >>= nTmp;
+ Size aSize( aRect.GetSize() );
+ aSize.setWidth(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp);
+ aRect.SetSize( aSize );
+ }
+ else if ( rValue.Name == "ViewAreaHeight" )
+ {
+ rValue.Value >>= nTmp;
+ Size aSize( aRect.GetSize() );
+ aSize.setHeight(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp);
+ aRect.SetSize( aSize );
+ }
+ else if ( rValue.Name == "ShowRedlineChanges" )
+ {
+ bShowRedlineChanges = *o3tl::doAccess<bool>(rValue.Value);
+ bChangeShowRedline = true;
+ }
+// Headers and footers are not displayed in BrowseView anymore
+ else if ( rValue.Name == "InBrowseMode" )
+ {
+ bBrowseMode = *o3tl::doAccess<bool>(rValue.Value);
+ bChangeBrowseMode = true;
+ }
+ }
+ if( pDoc->GetDocShell() )
+ pDoc->GetDocShell()->SetVisArea ( aRect );
+
+ if (bChangeBrowseMode)
+ pDoc->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, bBrowseMode );
+
+ if (bChangeShowRedline)
+ GetTextImport()->SetShowChanges( bShowRedlineChanges );
+}
+
+// Note: this will be called only if there are OOo elements in settings.xml.
+// So if a setting is missing there we can assume that it was written
+// by an OOo/LO version that is older than the introduction of the setting!
+void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aConfigProps)
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ Reference< lang::XMultiServiceFactory > xFac( GetModel(), UNO_QUERY );
+ if( !xFac.is() )
+ return;
+
+ Reference< XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY );
+ if( !xProps.is() )
+ return;
+
+ Reference< XPropertySetInfo > xInfo( xProps->getPropertySetInfo() );
+ if( !xInfo.is() )
+ return;
+
+ std::unordered_set< OUString > aExcludeAlways;
+ aExcludeAlways.insert("LinkUpdateMode");
+ // this should contain things that are actually user-settable, via Tools->Options
+ std::unordered_set< OUString > aExcludeWhenNotLoadingUserSettings {
+ "ForbiddenCharacters",
+ "IsKernAsianPunctuation",
+ "CharacterCompressionType",
+ "FieldAutoUpdate",
+ "ChartAutoUpdate",
+ "AddParaTableSpacing",
+ "AddParaTableSpacingAtStart",
+ "PrintAnnotationMode",
+ "PrintBlackFonts",
+ "PrintControls",
+ "PrintDrawings",
+ "PrintGraphics",
+ "PrintHiddenText",
+ "PrintLeftPages",
+ "PrintPageBackground",
+ "PrintProspect",
+ "PrintReversed",
+ "PrintRightPages",
+ "PrintFaxName",
+ "PrintPaperFromSetup",
+ "PrintTables",
+ "PrintTextPlaceholder",
+ "PrintSingleJobs",
+ "UpdateFromTemplate",
+ "PrinterIndependentLayout",
+ "PrintEmptyPages",
+ "ConsiderTextWrapOnObjPos",
+ "DoNotJustifyLinesWithManualBreak",
+ "ProtectForm",
+ "MsWordCompTrailingBlanks",
+ "SubtractFlysAnchoredAtFlys",
+ "EmptyDbFieldHidesPara",
+ "UseVariableWidthNBSP",
+ };
+
+ bool bAreUserSettingsFromDocument = officecfg::Office::Common::Load::UserDefinedSettings::get();
+
+ // for some properties we don't want to use the application
+ // default if they're missing. So we watch for them in the loop
+ // below, and set them if not found
+ bool bPrinterIndependentLayout = false;
+ bool bUseOldNumbering = false;
+ bool bAddExternalLeading = false;
+ bool bAddParaSpacingToTableCells = false;
+ bool bAddParaLineSpacingToTableCells = false;
+ bool bUseFormerLineSpacing = false;
+ bool bUseFormerObjectPositioning = false;
+ bool bUseFormerTextWrapping = false;
+ bool bConsiderWrapOnObjPos = false;
+ bool bIgnoreFirstLineIndentInNumbering = false;
+ bool bDoNotJustifyLinesWithManualBreak = false;
+ bool bDoNotResetParaAttrsForNumFont = false;
+ bool bDoNotCaptureDrawObjsOnPage( false );
+ bool bClipAsCharacterAnchoredWriterFlyFrames( false );
+ bool bUnixForceZeroExtLeading = false;
+ bool bSmallCapsPercentage66 = false;
+ bool bTabOverflow = false;
+ bool bTabOverMarginValue = false;
+ bool bPropLineSpacingShrinksFirstLine = false;
+ bool bSubtractFlysAnchoredAtFlys = false;
+ bool bEmptyDbFieldHidesPara = false;
+ bool bCollapseEmptyCellPara = false;
+ bool bAutoFirstLineIndentDisregardLineSpace = false;
+ bool bHyphenateURLs = false;
+ bool bDoNotBreakWrappedTables = false;
+ bool bAllowTextAfterFloatingTableBreak = false;
+ bool bDropCapPunctuation = false;
+
+ const PropertyValue* currentDatabaseDataSource = nullptr;
+ const PropertyValue* currentDatabaseCommand = nullptr;
+ const PropertyValue* currentDatabaseCommandType = nullptr;
+ const PropertyValue* embeddedDatabaseName = nullptr;
+
+ for( const PropertyValue& rValue : aConfigProps )
+ {
+ bool bSet = aExcludeAlways.find(rValue.Name) == aExcludeAlways.end();
+ if( bSet && !bAreUserSettingsFromDocument
+ && (aExcludeWhenNotLoadingUserSettings.find(rValue.Name)
+ != aExcludeWhenNotLoadingUserSettings.end()) )
+ {
+ bSet = false;
+ }
+
+ if( bSet )
+ {
+ try
+ {
+ if( xInfo->hasPropertyByName( rValue.Name ) )
+ {
+ if( rValue.Name == "RedlineProtectionKey" )
+ {
+ Sequence<sal_Int8> aKey;
+ rValue.Value >>= aKey;
+ GetTextImport()->SetChangesProtectionKey( aKey );
+ }
+ else
+ {
+ // HACK: Setting these out of order does not work.
+ if( rValue.Name == "CurrentDatabaseDataSource" )
+ currentDatabaseDataSource = &rValue;
+ else if( rValue.Name == "CurrentDatabaseCommand" )
+ currentDatabaseCommand = &rValue;
+ else if( rValue.Name == "CurrentDatabaseCommandType" )
+ currentDatabaseCommandType = &rValue;
+ else if (rValue.Name == "EmbeddedDatabaseName")
+ embeddedDatabaseName = &rValue;
+ else
+ xProps->setPropertyValue( rValue.Name, rValue.Value );
+ }
+ }
+
+ // did we find any of the non-default cases?
+ if ( rValue.Name == "PrinterIndependentLayout" )
+ bPrinterIndependentLayout = true;
+ else if ( rValue.Name == "AddExternalLeading" )
+ bAddExternalLeading = true;
+ else if ( rValue.Name == "AddParaSpacingToTableCells" )
+ bAddParaSpacingToTableCells = true;
+ else if ( rValue.Name == "AddParaLineSpacingToTableCells" )
+ bAddParaLineSpacingToTableCells = true;
+ else if ( rValue.Name == "UseFormerLineSpacing" )
+ bUseFormerLineSpacing = true;
+ else if ( rValue.Name == "UseFormerObjectPositioning" )
+ bUseFormerObjectPositioning = true;
+ else if ( rValue.Name == "UseFormerTextWrapping" )
+ bUseFormerTextWrapping = true;
+ else if ( rValue.Name == "UseOldNumbering" )
+ bUseOldNumbering = true;
+ else if ( rValue.Name == "ConsiderTextWrapOnObjPos" )
+ bConsiderWrapOnObjPos = true;
+ else if ( rValue.Name == "IgnoreFirstLineIndentInNumbering" )
+ bIgnoreFirstLineIndentInNumbering = true;
+ else if ( rValue.Name == "DoNotJustifyLinesWithManualBreak" )
+ bDoNotJustifyLinesWithManualBreak = true;
+ else if ( rValue.Name == "DoNotResetParaAttrsForNumFont" )
+ bDoNotResetParaAttrsForNumFont = true;
+ else if ( rValue.Name == "DoNotCaptureDrawObjsOnPage" )
+ bDoNotCaptureDrawObjsOnPage = true;
+ else if ( rValue.Name == "ClipAsCharacterAnchoredWriterFlyFrames" )
+ bClipAsCharacterAnchoredWriterFlyFrames = true;
+ else if ( rValue.Name == "UnxForceZeroExtLeading" )
+ bUnixForceZeroExtLeading = true;
+ else if ( rValue.Name == "SmallCapsPercentage66" )
+ bSmallCapsPercentage66 = true;
+ else if ( rValue.Name == "TabOverflow" )
+ bTabOverflow = true;
+ else if ( rValue.Name == "TabOverMargin" )
+ {
+ rValue.Value >>= bTabOverMarginValue;
+ }
+ else if ( rValue.Name == "PropLineSpacingShrinksFirstLine" )
+ bPropLineSpacingShrinksFirstLine = true;
+ else if (rValue.Name == "SubtractFlysAnchoredAtFlys")
+ bSubtractFlysAnchoredAtFlys = true;
+ else if (rValue.Name == "EmptyDbFieldHidesPara")
+ bEmptyDbFieldHidesPara = true;
+ else if (rValue.Name == "CollapseEmptyCellPara")
+ bCollapseEmptyCellPara = true;
+ else if (rValue.Name == "AutoFirstLineIndentDisregardLineSpace")
+ bAutoFirstLineIndentDisregardLineSpace = true;
+ else if (rValue.Name == "HyphenateURLs")
+ {
+ bHyphenateURLs = true;
+ }
+ else if (rValue.Name == "DoNotBreakWrappedTables")
+ {
+ rValue.Value >>= bDoNotBreakWrappedTables;
+ }
+ else if (rValue.Name == "AllowTextAfterFloatingTableBreak")
+ {
+ rValue.Value >>= bAllowTextAfterFloatingTableBreak;
+ }
+ else if ( rValue.Name == "DropCapPunctuation" )
+ bDropCapPunctuation = true;
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "SwXMLImport::SetConfigurationSettings" );
+ }
+ }
+ }
+
+ try
+ {
+ if( currentDatabaseDataSource != nullptr )
+ xProps->setPropertyValue( currentDatabaseDataSource->Name, currentDatabaseDataSource->Value );
+ if( currentDatabaseCommand != nullptr )
+ xProps->setPropertyValue( currentDatabaseCommand->Name, currentDatabaseCommand->Value );
+ if( currentDatabaseCommandType != nullptr )
+ xProps->setPropertyValue( currentDatabaseCommandType->Name, currentDatabaseCommandType->Value );
+ if (embeddedDatabaseName)
+ xProps->setPropertyValue(embeddedDatabaseName->Name, embeddedDatabaseName->Value);
+ } catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "SwXMLImport::SetConfigurationSettings" );
+ }
+
+ // finally, treat the non-default cases
+ // introduce boolean, that indicates a document, written by version prior SO8.
+ // If user settings are not loaded, we can't know if this is an old document. Better to assume no?
+ const bool bDocumentPriorSO8 = !bConsiderWrapOnObjPos && bAreUserSettingsFromDocument;
+
+ // Use old behaviour if this setting didn't exist, but only if this setting is being read from the document.
+ // (Obviously the setting doesn't exist if we are explicitly ignoring it, so then stick with program/user defaults)
+ if(!bPrinterIndependentLayout && bAreUserSettingsFromDocument)
+ {
+ xProps->setPropertyValue( "PrinterIndependentLayout", Any(sal_Int16(document::PrinterIndependentLayout::DISABLED)) );
+ }
+
+ if( ! bAddExternalLeading )
+ {
+ xProps->setPropertyValue( "AddExternalLeading", Any( false ) );
+ }
+
+ if( ! bUseFormerLineSpacing )
+ {
+ xProps->setPropertyValue( "UseFormerLineSpacing", Any( true ) );
+ }
+
+ if( !bUseFormerObjectPositioning )
+ {
+ xProps->setPropertyValue( "UseFormerObjectPositioning", Any( true ) );
+ }
+
+ if( !bUseOldNumbering )
+ {
+ xProps->setPropertyValue( "UseOldNumbering", Any(true) );
+ }
+
+ if( !bAddParaSpacingToTableCells )
+ {
+ xProps->setPropertyValue( "AddParaSpacingToTableCells",
+ Any( false ) );
+ }
+ if (!bAddParaLineSpacingToTableCells)
+ {
+ xProps->setPropertyValue("AddParaLineSpacingToTableCells", Any(false));
+ }
+
+ if( !bUseFormerTextWrapping )
+ {
+ xProps->setPropertyValue( "UseFormerTextWrapping", Any( true ) );
+ }
+
+ if (!bConsiderWrapOnObjPos && bAreUserSettingsFromDocument)
+ {
+ xProps->setPropertyValue( "ConsiderTextWrapOnObjPos", Any( false ) );
+ }
+
+ // #i47448#
+ // For SO7pp4, part of the 'new numbering' stuff has been backported from
+ // SO8. Unfortunately, only part of it and by using the same compatibility option
+ // like in SO8. Therefore documents generated with SO7pp4, containing
+ // numbered paragraphs with first line indent differ between SO7pp4 and
+ // SO8. In order to fix this for SO8pp1, I introduce a new compatibility
+ // flag 'bIgnoreFirstLineIndentInNumbering'. This flag has to be set for all
+ // documents < SO8, but not for SO8. So if the property is not present, the
+ // flag will be set to 'true'. SO8 documents surely have the
+ // 'ConsiderWrapOnObjPos' property set (no matter if 'true' or 'false'),
+ // therefore the correct condition to set this flag is this:
+ if( !bIgnoreFirstLineIndentInNumbering && bDocumentPriorSO8 )
+ {
+ xProps->setPropertyValue( "IgnoreFirstLineIndentInNumbering",
+ Any( true ) );
+ }
+
+ // This flag has to be set for all documents < SO8
+ if ( !bDoNotJustifyLinesWithManualBreak && bDocumentPriorSO8 )
+ {
+ xProps->setPropertyValue( "DoNotJustifyLinesWithManualBreak",
+ Any( true ) );
+ }
+
+ // This flag has to be set for all documents < SO8
+ if ( !bDoNotResetParaAttrsForNumFont && bDocumentPriorSO8 )
+ {
+ xProps->setPropertyValue( "DoNotResetParaAttrsForNumFont",
+ Any( true ) );
+ }
+
+ // This flag has to be set for all documents < SO8
+ if ( !bDoNotCaptureDrawObjsOnPage && bDocumentPriorSO8 )
+ {
+ xProps->setPropertyValue( "DoNotCaptureDrawObjsOnPage",
+ Any( true ) );
+ }
+
+ // This flag has to be set for all documents < SO8
+ if ( !bClipAsCharacterAnchoredWriterFlyFrames && bDocumentPriorSO8 )
+ {
+ xProps->setPropertyValue( "ClipAsCharacterAnchoredWriterFlyFrames",
+ Any( true ) );
+ }
+
+ if ( !bUnixForceZeroExtLeading )
+ {
+ xProps->setPropertyValue( "UnxForceZeroExtLeading", Any( true ) );
+ }
+
+ // Old LO versions had 66 as the value for small caps percentage, later changed to 80.
+ // In order to keep backwards compatibility, SmallCapsPercentage66 option is written to .odt
+ // files, and the default for new documents is 'false'. Files without this option
+ // are considered to be old files, so set the compatibility option too.
+ if ( !bSmallCapsPercentage66 )
+ {
+ xProps->setPropertyValue( "SmallCapsPercentage66", Any( true ) );
+ }
+
+ if ( !bTabOverflow )
+ {
+ xProps->setPropertyValue( "TabOverflow", Any( false ) );
+ }
+
+ if (bTabOverMarginValue)
+ // Let TabOverMargin imply the new default for
+ // PrinterIndependentLayout, knowing the first is set by Word import
+ // filters and Word defaults to our new default as well.
+ xProps->setPropertyValue(
+ "PrinterIndependentLayout",
+ uno::Any(document::PrinterIndependentLayout::HIGH_RESOLUTION));
+
+ if (!bPropLineSpacingShrinksFirstLine)
+ xProps->setPropertyValue("PropLineSpacingShrinksFirstLine", Any(false));
+
+ if (!bSubtractFlysAnchoredAtFlys && bAreUserSettingsFromDocument)
+ xProps->setPropertyValue("SubtractFlysAnchoredAtFlys", Any(true));
+
+ if (!bEmptyDbFieldHidesPara && bAreUserSettingsFromDocument)
+ xProps->setPropertyValue("EmptyDbFieldHidesPara", Any(false));
+
+ if (!bCollapseEmptyCellPara)
+ xProps->setPropertyValue("CollapseEmptyCellPara", Any(false));
+
+ if (!bAutoFirstLineIndentDisregardLineSpace)
+ xProps->setPropertyValue("AutoFirstLineIndentDisregardLineSpace", Any(false));
+
+ if (!bHyphenateURLs)
+ {
+ xProps->setPropertyValue("HyphenateURLs", Any(true));
+ }
+
+ if (bDoNotBreakWrappedTables)
+ {
+ xProps->setPropertyValue("DoNotBreakWrappedTables", Any(true));
+ }
+
+ if (bAllowTextAfterFloatingTableBreak)
+ {
+ xProps->setPropertyValue("AllowTextAfterFloatingTableBreak", Any(true));
+ }
+
+ // LO 7.4 and previous versions had different drop cap punctuation: very long dashes.
+ // In order to keep backwards compatibility, DropCapPunctuation option is written to .odt
+ // files, and the default for new documents is 'true'. Files without this option
+ // are considered to be old files, so set the compatibility option too.
+ if ( !bDropCapPunctuation )
+ {
+ xProps->setPropertyValue( "DropCapPunctuation", Any( false ) );
+ }
+
+ SwDoc *pDoc = getDoc();
+ SfxPrinter *pPrinter = pDoc->getIDocumentDeviceAccess().getPrinter( false );
+ if( pPrinter )
+ {
+ // If the printer is known, then the OLE objects will
+ // already have correct sizes, and we don't have to call
+ // PrtOLENotify again. Otherwise we have to call it.
+ // The flag might be set from setting the printer, so it
+ // it is required to clear it.
+ pDoc->SetOLEPrtNotifyPending( !pPrinter->IsKnown() );
+ }
+}
+
+void SwXMLImport::SetDocumentSpecificSettings(
+ const OUString& _rSettingsGroupName,
+ const Sequence< PropertyValue>& _rSettings )
+{
+ // the only doc-specific settings group we know so far are the XForms settings
+ if ( !IsXMLToken( _rSettingsGroupName, XML_XFORM_MODEL_SETTINGS ) )
+ return;
+
+ // preserve the settings for a later iteration - we are currently reading the settings.xml,
+ // the content.xml will be read later, by another instance of SwXMLImport
+ OSL_ENSURE( m_xLateInitSettings.is(), "SwXMLImport::SetDocumentSpecificSettings: no storage for those settings!" );
+ if ( !m_xLateInitSettings.is() )
+ return;
+
+ try
+ {
+ if ( m_xLateInitSettings->hasByName( _rSettingsGroupName ) )
+ {
+ m_xLateInitSettings->replaceByName( _rSettingsGroupName, Any( _rSettings ) );
+ OSL_FAIL( "SwXMLImport::SetDocumentSpecificSettings: already have settings for this model!" );
+ }
+ else
+ m_xLateInitSettings->insertByName( _rSettingsGroupName, Any( _rSettings ) );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void SwXMLImport::initialize(
+ const Sequence<Any>& aArguments )
+{
+ // delegate to super class
+ SvXMLImport::initialize(aArguments);
+
+ // we are only looking for a NamedValue "LateInitSettings"
+ for(const auto& rArgument : aArguments)
+ {
+ beans::NamedValue aNamedValue;
+ if ( rArgument >>= aNamedValue )
+ {
+ if (aNamedValue.Name == "LateInitSettings")
+ {
+ OSL_VERIFY( aNamedValue.Value >>= m_xLateInitSettings );
+ }
+ }
+ }
+}
+
+void SwXMLImport::initXForms()
+{
+ // obtain SwDoc
+ auto pXTextDocument = comphelper::getFromUnoTunnel<SwXTextDocument>(GetModel());
+ if( pXTextDocument == nullptr )
+ return;
+
+ SwDoc *pDoc = pXTextDocument->GetDocShell()->GetDoc();
+
+ // init XForms (if not already done)
+ // (no default model, since we'll load the models)
+ if( ! pDoc->isXForms() )
+ pDoc->initXForms( false );
+
+ m_bInititedXForms = true;
+}
+
+SwDoc* SwXMLImport::getDoc()
+{
+ if( m_pDoc != nullptr )
+ return m_pDoc;
+ Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY );
+ Reference < XText > xText = xTextDoc->getText();
+ SwXText* pText = dynamic_cast<SwXText*>(xText.get());
+ assert( pText != nullptr );
+ m_pDoc = pText->GetDoc();
+ assert( m_pDoc != nullptr );
+ return m_pDoc;
+}
+
+const SwDoc* SwXMLImport::getDoc() const
+{
+ return const_cast< SwXMLImport* >( this )->getDoc();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisImporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisImporter",
+ SvXMLImportFlags::ALL));
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisStylesImporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisStylesImporter",
+ SvXMLImportFlags::STYLES | SvXMLImportFlags::MASTERSTYLES | SvXMLImportFlags::AUTOSTYLES |
+ SvXMLImportFlags::FONTDECLS));
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisContentImporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisContentImporter",
+ SvXMLImportFlags::CONTENT | SvXMLImportFlags::SCRIPTS | SvXMLImportFlags::AUTOSTYLES |
+ SvXMLImportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisMetaImporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisMetaImporter",
+ SvXMLImportFlags::META));
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_XMLOasisSettingsImporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODT(SvStream &rStream)
+{
+ SwGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+ uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW);
+
+ css::uno::Sequence<OUString> aUserData
+ {
+ "com.sun.star.comp.filter.OdfFlatXml",
+ "",
+ "com.sun.star.comp.Writer.XMLOasisImporter",
+ "com.sun.star.comp.Writer.XMLOasisExporter",
+ "",
+ "",
+ "true"
+ };
+ uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence(
+ {
+ { "UserData", uno::Any(aUserData) },
+ }));
+ css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) };
+
+ uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW);
+ xInit->initialize(aOuterArgs);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "URL", uno::Any(OUString("private:stream")) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+ bool ret = xFilter->filter(aArgs);
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return ret;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestPDFExportFODT(SvStream &rStream)
+{
+ // do the same sort of check as FilterDetect::detect
+ OString const str(read_uInt8s_ToOString(rStream, 4000));
+ rStream.Seek(STREAM_SEEK_TO_BEGIN);
+ OUString resultString(str.getStr(), str.getLength(), RTL_TEXTENCODING_ASCII_US,
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT|RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT|RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT);
+ if (!resultString.startsWith("<?xml") || resultString.indexOf("office:mimetype=\"application/vnd.oasis.opendocument.text\"") == -1)
+ return false;
+
+ Reference<css::frame::XDesktop2> xDesktop = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ Reference<css::frame::XFrame> xTargetFrame = xDesktop->findFrame("_blank", 0);
+
+ Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ Reference<css::frame::XModel2> xModel(xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.text.TextDocument", xContext), UNO_QUERY_THROW);
+
+ Reference<css::frame::XLoadable> xModelLoad(xModel, UNO_QUERY_THROW);
+ xModelLoad->initNew();
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+ uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW);
+
+ css::uno::Sequence<OUString> aUserData
+ {
+ "com.sun.star.comp.filter.OdfFlatXml",
+ "",
+ "com.sun.star.comp.Writer.XMLOasisImporter",
+ "com.sun.star.comp.Writer.XMLOasisExporter",
+ "",
+ "",
+ "true"
+ };
+ uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence(
+ {
+ { "UserData", uno::Any(aUserData) },
+ }));
+ css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) };
+
+ uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW);
+ xInit->initialize(aOuterArgs);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "URL", uno::Any(OUString("private:stream")) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ uno::Reference<document::XFilter> xFODTFilter(xInterface, uno::UNO_QUERY_THROW);
+ bool ret = xFODTFilter->filter(aArgs);
+
+ if (ret)
+ {
+ css::uno::Reference<css::frame::XController2> xController(xModel->createDefaultViewController(xTargetFrame), UNO_SET_THROW);
+ utl::ConnectFrameControllerModel(xTargetFrame, xController, xModel);
+
+ utl::TempFileFast aTempFile;
+
+ uno::Reference<document::XFilter> xPDFFilter(
+ xMultiServiceFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY);
+ uno::Reference<document::XExporter> xExporter(xPDFFilter, uno::UNO_QUERY);
+ xExporter->setSourceDocument(xModel);
+
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(*aTempFile.GetStream(StreamMode::READWRITE)));
+
+ // ofz#60533 fuzzer learned to use fo:font-size="842pt" which generate timeouts trying
+ // to export thousands of pages from minimal input size
+ uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
+ { "PageRange", uno::Any(OUString("1-100")) }
+ }));
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
+ { "FilterName", uno::Any(OUString("writer_pdf_Export")) },
+ { "OutputStream", uno::Any(xOutputStream) },
+ { "FilterData", uno::Any(aFilterData) }
+ }));
+ xPDFFilter->filter(aDescriptor);
+ }
+
+ css::uno::Reference<css::util::XCloseable> xClose(xModel, css::uno::UNO_QUERY);
+ xClose->close(false);
+
+ return ret;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDOCX(SvStream &rStream)
+{
+ SwGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL));
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+
+ uno::Reference<document::XFilter> xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter"), uno::UNO_QUERY_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "InputMode", uno::Any(true) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+ bool ret = false;
+ try
+ {
+ ret = xFilter->filter(aArgs);
+ }
+ catch (...)
+ {
+ }
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlimp.hxx b/sw/source/filter/xml/xmlimp.hxx
new file mode 100644
index 0000000000..4c528155f2
--- /dev/null
+++ b/sw/source/filter/xml/xmlimp.hxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLIMP_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLIMP_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+#include <optional>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmlimp.hxx>
+
+#include "xmlitmap.hxx"
+#include <o3tl/typed_flags_set.hxx>
+#include <ndindex.hxx>
+
+class SwDoc;
+class SvXMLUnitConverter;
+class SvXMLTokenMap;
+class SvXMLImportItemMapper;
+class SfxItemSet;
+class XMLTextImportHelper;
+class SvXMLGraphicHelper;
+class SvXMLEmbeddedObjectHelper;
+enum class SfxStyleFamily;
+
+// define, how many steps ( = paragraphs ) the progress bar should advance
+// for styles, autostyles and settings + meta
+#define PROGRESS_BAR_STEP 20
+
+// we only need this scoped enum to be flags here, in sw
+namespace o3tl
+{
+ template<> struct typed_flags<SfxStyleFamily> : is_typed_flags<SfxStyleFamily, 0xffff> {};
+}
+
+class SwXMLImport: public SvXMLImport
+{
+ std::optional<SwNodeIndex> m_oSttNdIdx;
+
+ std::unique_ptr<SvXMLUnitConverter> m_pTwipUnitConv;
+ std::unique_ptr<SvXMLImportItemMapper> m_pTableItemMapper;// paragraph item import
+
+ rtl::Reference<SvXMLGraphicHelper> m_xGraphicStorageHandler;
+
+ rtl::Reference<SvXMLEmbeddedObjectHelper> m_xEmbeddedResolver;
+
+ SvXMLItemMapEntriesRef m_xTableItemMap;
+ SvXMLItemMapEntriesRef m_xTableColItemMap;
+ SvXMLItemMapEntriesRef m_xTableRowItemMap;
+ SvXMLItemMapEntriesRef m_xTableCellItemMap;
+ css::uno::Reference< css::container::XNameContainer >
+ m_xLateInitSettings;
+
+ SfxStyleFamily m_nStyleFamilyMask;// Mask of styles to load
+ bool m_bLoadDoc : 1; // Load doc or styles only
+ bool m_bInsert : 1; // Insert mode. If styles are
+ // loaded only false means that
+ // existing styles will be
+ // overwritten.
+ bool m_bBlock : 1; // Load text block
+ bool m_bOrganizerMode : 1;
+ bool m_bInititedXForms : 1;
+
+ SwDoc* m_pDoc; // cached for getDoc()
+
+ // Optimization for new table name lookup
+ OUString m_sDefTableName; // See STR_TABLE_DEFNAME
+ std::map<OUString, sal_uInt32> m_aTableNameMap; // Last used indices for duplicating table names
+
+ void InitItemImport();
+ void FinitItemImport();
+ void UpdateTextCollConditions( SwDoc *pDoc );
+
+ void setTextInsertMode(
+ const css::uno::Reference< css::text::XTextRange > & rInsertPos );
+ void setStyleInsertMode( SfxStyleFamily nFamilies,
+ bool bOverwrite );
+
+protected:
+
+ virtual SvXMLImportContext *CreateFastContext( sal_Int32 nElement,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual XMLTextImportHelper* CreateTextImport() override;
+
+ virtual XMLShapeImportHelper* CreateShapeImport() override;
+
+public:
+ SwXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlags);
+
+ virtual ~SwXMLImport() noexcept override;
+
+ // css::xml::sax::XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+ virtual void SAL_CALL endDocument() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ void InsertStyles( bool bAuto );
+ void FinishStyles();
+
+ // namespace office
+
+ // NB: in contrast to other CreateFooContexts, this particular one handles
+ // the root element (i.e. office:document-meta)
+ SvXMLImportContext *CreateMetaContext( const sal_Int32 nElement );
+ SvXMLImportContext *CreateScriptContext();
+ SvXMLImportContext *CreateStylesContext( bool bAuto );
+ SvXMLImportContext *CreateMasterStylesContext();
+ SvXMLImportContext *CreateFontDeclsContext();
+ SvXMLImportContext *CreateBodyContentContext();
+ SfxStyleFamily GetStyleFamilyMask() const { return m_nStyleFamilyMask; }
+ bool IsInsertMode() const { return m_bInsert; }
+ bool IsStylesOnlyMode() const { return !m_bLoadDoc; }
+ bool IsBlockMode() const { return m_bBlock; }
+
+ inline const SvXMLImportItemMapper& GetTableItemMapper() const;
+ inline SvXMLImportItemMapper& GetTableItemMapper();
+ SvXMLImportContext *CreateTableItemImportContext( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ XmlStyleFamily nSubFamily, SfxItemSet& rItemSet );
+
+ bool FindAutomaticStyle( XmlStyleFamily nFamily,
+ const OUString& rName,
+ const SfxItemSet **ppItemSet ) const;
+ void MergeListsAtDocumentInsertPosition(SwDoc *pDoc);
+
+ virtual void SetStatistics(
+ const css::uno::Sequence< css::beans::NamedValue> & i_rStats) override;
+ virtual void SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override;
+ virtual void SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>& aConfigProps) override;
+ virtual void SetDocumentSpecificSettings(const OUString& _rSettingsGroupName,
+ const css::uno::Sequence<css::beans::PropertyValue>& _rSettings) override;
+
+ // initialize XForms
+ virtual void initXForms() override;
+
+ // get the document properties, but only if they actually need importing
+ css::uno::Reference<css::document::XDocumentProperties>
+ GetDocumentProperties() const;
+
+ virtual void NotifyContainsEmbeddedFont() override;
+
+ const SwDoc* getDoc() const;
+ SwDoc* getDoc();
+
+ const OUString& GetDefTableName() { return m_sDefTableName; }
+ std::map<OUString, sal_uInt32>& GetTableNameMap() { return m_aTableNameMap; }
+};
+
+inline const SvXMLImportItemMapper& SwXMLImport::GetTableItemMapper() const
+{
+ return *m_pTableItemMapper;
+}
+
+inline SvXMLImportItemMapper& SwXMLImport::GetTableItemMapper()
+{
+ return *m_pTableItemMapper;
+}
+
+#endif // _XMLIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlimpit.cxx b/sw/source/filter/xml/xmlimpit.cxx
new file mode 100644
index 0000000000..eaabf789e2
--- /dev/null
+++ b/sw/source/filter/xml/xmlimpit.cxx
@@ -0,0 +1,1056 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlimpit.hxx"
+
+#include <sal/log.hxx>
+#include <sax/tools/converter.hxx>
+#include <utility>
+#include <xmloff/xmluconv.hxx>
+#include <svl/itempool.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/itemset.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/memberids.h>
+#include <osl/diagnose.h>
+
+#include <hintids.hxx>
+#include <unomid.h>
+#include <svx/unomid.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdir.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+
+#include <xmloff/prhdlfac.hxx>
+#include <xmloff/xmltypes.hxx>
+#include <xmloff/xmlprhdl.hxx>
+#include <xmloff/xmlimp.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include "xmlithlp.hxx"
+#include <com/sun/star/uno/Any.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+using uno::Any;
+
+constexpr sal_uInt16 nUnknownWhich = RES_UNKNOWNATR_CONTAINER;
+
+SvXMLImportItemMapper::SvXMLImportItemMapper(
+ SvXMLItemMapEntriesRef aMapEntries ) :
+ mrMapEntries(std::move( aMapEntries ))
+{
+}
+
+SvXMLImportItemMapper::~SvXMLImportItemMapper()
+{
+}
+
+void
+SvXMLImportItemMapper::setMapEntries( SvXMLItemMapEntriesRef rMapEntries )
+{
+ mrMapEntries = std::move(rMapEntries);
+}
+
+// fills the given itemset with the attributes in the given list
+void SvXMLImportItemMapper::importXML( SfxItemSet& rSet,
+ uno::Reference< xml::sax::XFastAttributeList > const & xAttrList,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap )
+{
+ std::unique_ptr<SvXMLAttrContainerItem> pUnknownItem;
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ if( IsTokenInNamespace(aIter.getToken(), XML_NAMESPACE_XMLNS) )
+ continue;
+
+ sal_Int32 nToken = aIter.getToken();
+ const OUString sValue = aIter.toString();
+
+ // find a map entry for this attribute
+ sal_Int32 nLookupToken = nToken;
+ // compatibility namespaces need to be transformed into current namespace before looking up
+ if (IsTokenInNamespace(nLookupToken, XML_NAMESPACE_FO_COMPAT))
+ nLookupToken = XML_ELEMENT(FO, (nLookupToken & TOKEN_MASK));
+ SvXMLItemMapEntry const * pEntry = mrMapEntries->getByName( nLookupToken );
+
+ if( pEntry )
+ {
+ // we have a valid map entry here, so lets use it...
+ if( 0 == (pEntry->nMemberId & (MID_SW_FLAG_NO_ITEM_IMPORT|
+ MID_SW_FLAG_ELEMENT_ITEM_IMPORT)) )
+ {
+ // first get item from itemset
+ const SfxPoolItem* pItem = nullptr;
+ SfxItemState eState = rSet.GetItemState( pEntry->nWhichId, true,
+ &pItem );
+
+ // if it's not set, try the pool
+ if (SfxItemState::SET != eState && SfxItemPool::IsWhich(pEntry->nWhichId))
+ pItem = &rSet.GetPool()->GetDefaultItem(pEntry->nWhichId);
+
+ // do we have an item?
+ if(eState >= SfxItemState::DEFAULT && pItem)
+ {
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+ bool bPut = false;
+
+ if( 0 == (pEntry->nMemberId&MID_SW_FLAG_SPECIAL_ITEM_IMPORT) )
+ {
+ bPut = PutXMLValue( *pNewItem, sValue,
+ o3tl::narrowing<sal_uInt16>( pEntry->nMemberId & MID_SW_FLAG_MASK ),
+ rUnitConverter );
+
+ }
+ else
+ {
+ bPut = handleSpecialItem( *pEntry, *pNewItem, rSet,
+ sValue, rUnitConverter );
+ }
+
+ if( bPut )
+ rSet.Put( std::move(pNewItem) );
+ }
+ else
+ {
+ OSL_FAIL( "Could not get a needed item for xml import!" );
+ }
+ }
+ else if( 0 != (pEntry->nMemberId & MID_SW_FLAG_NO_ITEM_IMPORT) )
+ {
+ handleNoItem( *pEntry, rSet, sValue, rUnitConverter,
+ rNamespaceMap );
+ }
+ }
+ else
+ {
+ if( !pUnknownItem )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( SfxItemState::SET == rSet.GetItemState( nUnknownWhich, true,
+ &pItem ) )
+ {
+ pUnknownItem.reset( static_cast<SvXMLAttrContainerItem*>( pItem->Clone() ) );
+ }
+ else
+ {
+ pUnknownItem.reset( new SvXMLAttrContainerItem( nUnknownWhich ) );
+ }
+ }
+ if( pUnknownItem )
+ {
+ if( IsTokenInNamespace(nToken, XML_NAMESPACE_NONE) )
+ pUnknownItem->AddAttr( SvXMLImport::getNameFromToken( nToken ), sValue );
+ else
+ {
+ const OUString& rAttrNamespacePrefix = SvXMLImport::getNamespacePrefixFromToken(nToken, &rNamespaceMap);
+ OUString sAttrName = SvXMLImport::getNameFromToken( nToken );
+ if ( !rAttrNamespacePrefix.isEmpty() )
+ sAttrName = rAttrNamespacePrefix + SvXMLImport::aNamespaceSeparator + sAttrName;
+ OUString aLocalName, aPrefix, aNamespace;
+ rNamespaceMap.GetKeyByAttrName( sAttrName, &aPrefix, &aLocalName,
+ &aNamespace );
+ if ( !rAttrNamespacePrefix.isEmpty() )
+ pUnknownItem->AddAttr( rAttrNamespacePrefix, aNamespace, aLocalName,
+ sValue );
+ else
+ pUnknownItem->AddAttr( aLocalName, sValue );
+ }
+ }
+ }
+ }
+
+ importXMLUnknownAttributes(rSet, xAttrList, rUnitConverter, pUnknownItem);
+
+ if( pUnknownItem )
+ {
+ rSet.Put( *pUnknownItem );
+ }
+
+ finished(rSet, rUnitConverter);
+}
+
+void SvXMLImportItemMapper::importXMLUnknownAttributes( SfxItemSet& rSet,
+ uno::Reference< xml::sax::XFastAttributeList > const & xAttrList,
+ const SvXMLUnitConverter& rUnitConverter,
+ std::unique_ptr<SvXMLAttrContainerItem>& pUnknownItem)
+{
+ const css::uno::Sequence< css::xml::Attribute > unknownAttributes = xAttrList->getUnknownAttributes();
+ for (const auto & rAttribute : unknownAttributes)
+ {
+ if( !pUnknownItem )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( SfxItemState::SET == rSet.GetItemState( nUnknownWhich, true,
+ &pItem ) )
+ {
+ pUnknownItem.reset( static_cast<SvXMLAttrContainerItem*>( pItem->Clone() ) );
+ }
+ else
+ {
+ pUnknownItem.reset( new SvXMLAttrContainerItem( nUnknownWhich ) );
+ }
+ }
+ if( pUnknownItem )
+ {
+ if( rAttribute.NamespaceURL.isEmpty() )
+ pUnknownItem->AddAttr( rAttribute.Name, rAttribute.Value );
+ else
+ {
+ OUString sPrefix;
+ OUString sName = rAttribute.Name;
+ int i = sName.indexOf(':');
+ if (i != -1)
+ {
+ sPrefix = sName.copy(0, i-1);
+ sName = sName.copy(i+1);
+ }
+ // the sax parser doesn't reject these, strangely
+ if (sName.indexOf(':') == -1)
+ pUnknownItem->AddAttr( sPrefix, rAttribute.NamespaceURL, sName,
+ rAttribute.Value );
+ else
+ SAL_WARN("sw", "ignoring dodgy attribute: " + rAttribute.Name);
+ }
+ }
+ }
+
+ if( pUnknownItem )
+ {
+ rSet.Put( *pUnknownItem );
+ }
+
+ finished(rSet, rUnitConverter);
+}
+
+/** this method is called for every item that has the
+ MID_SW_FLAG_SPECIAL_ITEM_IMPORT flag set */
+bool
+SvXMLImportItemMapper::handleSpecialItem( const SvXMLItemMapEntry& /*rEntry*/,
+ SfxPoolItem& /*rItem*/,
+ SfxItemSet& /*rSet*/,
+ const OUString& /*rValue*/,
+ const SvXMLUnitConverter& /*rUnitConverter*/ )
+{
+ OSL_FAIL( "unsupported special item in xml import" );
+ return false;
+}
+
+/** this method is called for every item that has the
+ MID_SW_FLAG_NO_ITEM_IMPORT flag set */
+bool SvXMLImportItemMapper::handleNoItem( const SvXMLItemMapEntry& /*rEntry*/,
+ SfxItemSet& /*rSet*/,
+ const OUString& /*rValue*/,
+ const SvXMLUnitConverter& /*rUnitConverter*/,
+ const SvXMLNamespaceMap& /*rNamespaceMap*/ )
+{
+ OSL_FAIL( "unsupported no item in xml import" );
+ return false;
+}
+
+void
+SvXMLImportItemMapper::finished(SfxItemSet &, SvXMLUnitConverter const&) const
+{
+ // nothing to do here
+}
+
+namespace {
+
+struct BoxHolder
+{
+ std::unique_ptr<SvxBorderLine> pTop;
+ std::unique_ptr<SvxBorderLine> pBottom;
+ std::unique_ptr<SvxBorderLine> pLeft;
+ std::unique_ptr<SvxBorderLine> pRight;
+
+ BoxHolder(BoxHolder const&) = delete;
+ BoxHolder& operator=(BoxHolder const&) = delete;
+
+ explicit BoxHolder(SvxBoxItem const & rBox)
+ {
+ if (rBox.GetTop())
+ pTop.reset(new SvxBorderLine( *rBox.GetTop() ));
+ if (rBox.GetBottom())
+ pBottom.reset(new SvxBorderLine( *rBox.GetBottom() ));
+ if (rBox.GetLeft())
+ pLeft.reset(new SvxBorderLine( *rBox.GetLeft() ));
+ if (rBox.GetRight())
+ pRight.reset(new SvxBorderLine( *rBox.GetRight() ));
+ }
+};
+
+}
+
+// put an XML-string value into an item
+bool SvXMLImportItemMapper::PutXMLValue(
+ SfxPoolItem& rItem,
+ const OUString& rValue,
+ sal_uInt16 nMemberId,
+ const SvXMLUnitConverter& rUnitConverter )
+{
+ bool bOk = false;
+
+ switch (rItem.Which())
+ {
+ case RES_MARGIN_FIRSTLINE:
+ case RES_MARGIN_TEXTLEFT:
+ case RES_MARGIN_RIGHT:
+ assert(false); // is only called for frame formats?
+ break;
+
+ case RES_LR_SPACE:
+ {
+ SvxLRSpaceItem& rLRSpace = dynamic_cast<SvxLRSpaceItem&>(rItem);
+
+ switch( nMemberId )
+ {
+ case MID_L_MARGIN:
+ case MID_R_MARGIN:
+ {
+ sal_Int32 nProp = 100;
+ sal_Int32 nAbs = 0;
+
+ if( rValue.indexOf( '%' ) != -1 )
+ bOk = ::sax::Converter::convertPercent(nProp, rValue);
+ else
+ bOk = rUnitConverter.convertMeasureToCore(nAbs, rValue);
+
+ if( bOk )
+ {
+ switch( nMemberId )
+ {
+ case MID_L_MARGIN:
+ rLRSpace.SetTextLeft( nAbs, o3tl::narrowing<sal_uInt16>(nProp) );
+ break;
+ case MID_R_MARGIN:
+ rLRSpace.SetRight( nAbs, o3tl::narrowing<sal_uInt16>(nProp) );
+ break;
+ }
+ }
+ }
+ break;
+
+ case MID_FIRST_LINE_INDENT:
+ {
+ assert(false); // it looks like this can't be called? (frame formats only, aTableItemMap)
+ sal_Int32 nProp = 100;
+ sal_Int32 nAbs = 0;
+
+ if( rValue.indexOf( '%' ) != -1 )
+ bOk = ::sax::Converter::convertPercent(nProp, rValue);
+ else
+ bOk = rUnitConverter.convertMeasureToCore(nAbs, rValue,
+ -0x7fff, 0x7fff );
+
+ rLRSpace.SetTextFirstLineOffset( static_cast<short>(nAbs), o3tl::narrowing<sal_uInt16>(nProp) );
+ }
+ break;
+
+ case MID_FIRST_AUTO:
+ {
+ assert(false); // it looks like this can't be called? (frame formats only, aTableItemMap)
+ bool bAutoFirst(false);
+ bOk = ::sax::Converter::convertBool( bAutoFirst, rValue );
+ if( bOk )
+ rLRSpace.SetAutoFirst( bAutoFirst );
+ }
+ break;
+
+ default:
+ OSL_FAIL( "unknown member id!");
+ }
+ }
+ break;
+
+ case RES_UL_SPACE:
+ {
+ SvxULSpaceItem& rULSpace = dynamic_cast<SvxULSpaceItem&>(rItem);
+
+ sal_Int32 nProp = 100;
+ sal_Int32 nAbs = 0;
+
+ if( rValue.indexOf( '%' ) != -1 )
+ bOk = ::sax::Converter::convertPercent( nProp, rValue );
+ else
+ bOk = rUnitConverter.convertMeasureToCore( nAbs, rValue );
+
+ switch( nMemberId )
+ {
+ case MID_UP_MARGIN:
+ rULSpace.SetUpper( o3tl::narrowing<sal_uInt16>(nAbs), o3tl::narrowing<sal_uInt16>(nProp) );
+ break;
+ case MID_LO_MARGIN:
+ rULSpace.SetLower( o3tl::narrowing<sal_uInt16>(nAbs), o3tl::narrowing<sal_uInt16>(nProp) );
+ break;
+ default:
+ OSL_FAIL("unknown MemberId");
+ }
+ }
+ break;
+
+ case RES_SHADOW:
+ {
+ SvxShadowItem& rShadow = dynamic_cast<SvxShadowItem&>(rItem);
+
+ bool bColorFound = false;
+ bool bOffsetFound = false;
+
+ SvXMLTokenEnumerator aTokenEnum( rValue );
+
+ Color aColor( 128,128, 128 );
+ rShadow.SetLocation( SvxShadowLocation::BottomRight );
+
+ std::u16string_view aToken;
+ while( aTokenEnum.getNextToken( aToken ) )
+ {
+ if( IsXMLToken( aToken, XML_NONE ) )
+ {
+ rShadow.SetLocation( SvxShadowLocation::NONE );
+ bOk = true;
+ }
+ else if( !bColorFound && aToken.substr(0,1) == u"#" )
+ {
+ bOk = ::sax::Converter::convertColor( aColor, aToken );
+ if( !bOk )
+ return false;
+
+ bColorFound = true;
+ }
+ else if( !bOffsetFound )
+ {
+ sal_Int32 nX = 0, nY = 0;
+
+ bOk = rUnitConverter.convertMeasureToCore( nX, aToken );
+ if( bOk && aTokenEnum.getNextToken( aToken ) )
+ bOk = rUnitConverter.convertMeasureToCore( nY, aToken );
+
+ if( bOk )
+ {
+ if( nX < 0 )
+ {
+ if( nY < 0 )
+ {
+ rShadow.SetLocation( SvxShadowLocation::TopLeft );
+ }
+ else
+ {
+ rShadow.SetLocation( SvxShadowLocation::BottomLeft );
+ }
+ }
+ else
+ {
+ if( nY < 0 )
+ {
+ rShadow.SetLocation( SvxShadowLocation::TopRight );
+ }
+ else
+ {
+ rShadow.SetLocation( SvxShadowLocation::BottomRight );
+ }
+ }
+
+ if( nX < 0 ) nX *= -1;
+ if( nY < 0 ) nY *= -1;
+
+ rShadow.SetWidth( static_cast< sal_uInt16 >( (nX + nY) >> 1 ) );
+ }
+ }
+ }
+
+ if( bOk && ( bColorFound || bOffsetFound ) )
+ {
+ rShadow.SetColor(aColor);
+ }
+ else
+ bOk = false;
+ }
+ break;
+
+ case RES_BOX:
+ {
+ SvxBoxItem& rBox = dynamic_cast<SvxBoxItem&>(rItem);
+
+ // copy SvxBorderLines
+ BoxHolder aBoxes(rBox);
+
+ sal_Int32 nTemp;
+
+ switch( nMemberId )
+ {
+ case ALL_BORDER_PADDING:
+ case LEFT_BORDER_PADDING:
+ case RIGHT_BORDER_PADDING:
+ case TOP_BORDER_PADDING:
+ case BOTTOM_BORDER_PADDING:
+ if (!rUnitConverter.convertMeasureToCore( nTemp, rValue,
+ 0, 0xffff ))
+ {
+ return false;
+ }
+
+ if( nMemberId == LEFT_BORDER_PADDING ||
+ nMemberId == ALL_BORDER_PADDING )
+ rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::LEFT );
+ if( nMemberId == RIGHT_BORDER_PADDING ||
+ nMemberId == ALL_BORDER_PADDING )
+ rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::RIGHT );
+ if( nMemberId == TOP_BORDER_PADDING ||
+ nMemberId == ALL_BORDER_PADDING )
+ rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::TOP );
+ if( nMemberId == BOTTOM_BORDER_PADDING ||
+ nMemberId == ALL_BORDER_PADDING )
+ rBox.SetDistance( o3tl::narrowing<sal_uInt16>(nTemp), SvxBoxItemLine::BOTTOM);
+ break;
+
+ case ALL_BORDER:
+ case LEFT_BORDER:
+ case RIGHT_BORDER:
+ case TOP_BORDER:
+ case BOTTOM_BORDER:
+ {
+ bool bHasStyle = false;
+ bool bHasWidth = false;
+ bool bHasColor = false;
+
+ sal_uInt16 nStyle = USHRT_MAX;
+ sal_uInt16 nWidth = 0;
+ sal_uInt16 nNamedWidth = USHRT_MAX;
+
+ Color aColor( COL_BLACK );
+
+ if( !sw_frmitems_parseXMLBorder( rValue, rUnitConverter,
+ bHasStyle, nStyle,
+ bHasWidth, nWidth, nNamedWidth,
+ bHasColor, aColor ) )
+ return false;
+
+ if( TOP_BORDER == nMemberId || ALL_BORDER == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pTop,
+ bHasStyle, nStyle,
+ bHasWidth, nWidth, nNamedWidth,
+ bHasColor, aColor );
+
+ if( BOTTOM_BORDER == nMemberId || ALL_BORDER == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pBottom,
+ bHasStyle, nStyle,
+ bHasWidth, nWidth, nNamedWidth,
+ bHasColor, aColor );
+
+ if( LEFT_BORDER == nMemberId || ALL_BORDER == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pLeft,
+ bHasStyle, nStyle,
+ bHasWidth, nWidth, nNamedWidth,
+ bHasColor, aColor );
+
+ if( RIGHT_BORDER == nMemberId || ALL_BORDER == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pRight,
+ bHasStyle, nStyle,
+ bHasWidth, nWidth, nNamedWidth,
+ bHasColor, aColor );
+ }
+ break;
+ case ALL_BORDER_LINE_WIDTH:
+ case LEFT_BORDER_LINE_WIDTH:
+ case RIGHT_BORDER_LINE_WIDTH:
+ case TOP_BORDER_LINE_WIDTH:
+ case BOTTOM_BORDER_LINE_WIDTH:
+ {
+ SvXMLTokenEnumerator aTokenEnum( rValue );
+
+ sal_Int32 nInWidth, nDistance, nOutWidth;
+
+ std::u16string_view aToken;
+ if( !aTokenEnum.getNextToken( aToken ) )
+ return false;
+
+ if (!rUnitConverter.convertMeasureToCore(nInWidth, aToken))
+ return false;
+
+ if( !aTokenEnum.getNextToken( aToken ) )
+ return false;
+
+ if (!rUnitConverter.convertMeasureToCore(nDistance, aToken))
+ return false;
+
+ if( !aTokenEnum.getNextToken( aToken ) )
+ return false;
+
+ if (!rUnitConverter.convertMeasureToCore(nOutWidth, aToken))
+ return false;
+
+ // #i61946: accept line style even it's not part of our "normal" set of line styles
+ sal_uInt16 nWidth = 0;
+
+ if( TOP_BORDER_LINE_WIDTH == nMemberId ||
+ ALL_BORDER_LINE_WIDTH == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pTop, nWidth,
+ static_cast< sal_uInt16 >( nOutWidth ),
+ static_cast< sal_uInt16 >( nInWidth ),
+ static_cast< sal_uInt16 >( nDistance ) );
+
+ if( BOTTOM_BORDER_LINE_WIDTH == nMemberId ||
+ ALL_BORDER_LINE_WIDTH == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pBottom, nWidth,
+ static_cast< sal_uInt16 >( nOutWidth ),
+ static_cast< sal_uInt16 >( nInWidth ),
+ static_cast< sal_uInt16 >( nDistance ) );
+
+ if( LEFT_BORDER_LINE_WIDTH == nMemberId ||
+ ALL_BORDER_LINE_WIDTH == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pLeft, nWidth,
+ static_cast< sal_uInt16 >( nOutWidth ),
+ static_cast< sal_uInt16 >( nInWidth ),
+ static_cast< sal_uInt16 >( nDistance ) );
+
+ if( RIGHT_BORDER_LINE_WIDTH == nMemberId ||
+ ALL_BORDER_LINE_WIDTH == nMemberId )
+ sw_frmitems_setXMLBorder( aBoxes.pRight, nWidth,
+ static_cast< sal_uInt16 >( nOutWidth ),
+ static_cast< sal_uInt16 >( nInWidth ),
+ static_cast< sal_uInt16 >( nDistance ) );
+ }
+ break;
+ }
+
+ rBox.SetLine( aBoxes.pTop.get(), SvxBoxItemLine::TOP );
+ rBox.SetLine( aBoxes.pBottom.get(), SvxBoxItemLine::BOTTOM );
+ rBox.SetLine( aBoxes.pLeft.get(), SvxBoxItemLine::LEFT );
+ rBox.SetLine( aBoxes.pRight.get(), SvxBoxItemLine::RIGHT );
+
+ bOk = true;
+ }
+ break;
+
+ case RES_BREAK:
+ {
+ SvxFormatBreakItem& rFormatBreak = dynamic_cast<SvxFormatBreakItem&>(rItem);
+ sal_uInt16 eEnum{};
+
+ if( !SvXMLUnitConverter::convertEnum( eEnum, rValue, psXML_BreakType ) )
+ return false;
+
+ if( eEnum == 0 )
+ {
+ rFormatBreak.SetValue( SvxBreak::NONE );
+ bOk = true;
+ }
+ else
+ {
+ switch( nMemberId )
+ {
+ case MID_BREAK_BEFORE:
+ rFormatBreak.SetValue( eEnum == 1 ?
+ SvxBreak::ColumnBefore :
+ SvxBreak::PageBefore );
+ break;
+ case MID_BREAK_AFTER:
+ rFormatBreak.SetValue( eEnum == 1 ?
+ SvxBreak::ColumnAfter :
+ SvxBreak::PageAfter );
+ break;
+ }
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_KEEP:
+ {
+ SvxFormatKeepItem& rFormatKeep = dynamic_cast<SvxFormatKeepItem&>(rItem);
+
+ if( IsXMLToken( rValue, XML_ALWAYS ) ||
+ IsXMLToken( rValue, XML_TRUE ) )
+ {
+ rFormatKeep.SetValue( true );
+ bOk = true;
+ }
+ else if( IsXMLToken( rValue, XML_AUTO ) ||
+ IsXMLToken( rValue, XML_FALSE ) )
+ {
+ rFormatKeep.SetValue( false );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_BACKGROUND:
+ {
+ SvxBrushItem& rBrush = dynamic_cast<SvxBrushItem&>(rItem);
+
+ Color aTempColor;
+ switch( nMemberId )
+ {
+ case MID_BACK_COLOR:
+ if( IsXMLToken( rValue, XML_TRANSPARENT ) )
+ {
+ rBrush.GetColor().SetAlpha(0);
+ bOk = true;
+ }
+ else if (::sax::Converter::convertColor(aTempColor, rValue))
+ {
+ aTempColor.SetAlpha(255);
+ rBrush.SetColor( aTempColor );
+ bOk = true;
+ }
+ break;
+
+ case MID_GRAPHIC_REPEAT:
+ {
+ SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos();
+ SvxGraphicPosition nPos = GPOS_NONE;
+ if( SvXMLUnitConverter::convertEnum( nPos, rValue,
+ psXML_BrushRepeat ) )
+ {
+ if( GPOS_MM != nPos || GPOS_NONE == eGraphicPos ||
+ GPOS_AREA == eGraphicPos || GPOS_TILED == eGraphicPos )
+ rBrush.SetGraphicPos( nPos );
+ bOk = true;
+ }
+ }
+ break;
+
+ case MID_GRAPHIC_POSITION:
+ {
+ SvxGraphicPosition ePos = GPOS_NONE, eTmp;
+ SvxGraphicPosition nTmp;
+ SvXMLTokenEnumerator aTokenEnum( rValue );
+ std::u16string_view aToken;
+ bool bHori = false, bVert = false;
+ bOk = true;
+ while( bOk && aTokenEnum.getNextToken( aToken ) )
+ {
+ if( bHori && bVert )
+ {
+ bOk = false;
+ }
+ else if( std::u16string_view::npos != aToken.find( '%' ) )
+ {
+ sal_Int32 nPrc = 50;
+ if (::sax::Converter::convertPercent(nPrc, aToken))
+ {
+ if( !bHori )
+ {
+ ePos = nPrc < 25 ? GPOS_LT :
+ (nPrc < 75 ? GPOS_MM : GPOS_RB);
+ bHori = true;
+ }
+ else
+ {
+ eTmp = nPrc < 25 ? GPOS_LT:
+ (nPrc < 75 ? GPOS_LM : GPOS_LB);
+ sw_frmitems_MergeXMLVertPos( ePos, eTmp );
+ bVert = true;
+ }
+ }
+ else
+ {
+ // wrong percentage
+ bOk = false;
+ }
+ }
+ else if( IsXMLToken( aToken, XML_CENTER ) )
+ {
+ if( bHori )
+ sw_frmitems_MergeXMLVertPos( ePos, GPOS_MM );
+ else if ( bVert )
+ sw_frmitems_MergeXMLHoriPos( ePos, GPOS_MM );
+ else
+ ePos = GPOS_MM;
+ }
+ else if( SvXMLUnitConverter::convertEnum( nTmp, aToken,
+ psXML_BrushHoriPos ) )
+ {
+ if( bVert )
+ sw_frmitems_MergeXMLHoriPos(
+ ePos, nTmp );
+ else if( !bHori )
+ ePos = nTmp;
+ else
+ bOk = false;
+ bHori = true;
+ }
+ else if( SvXMLUnitConverter::convertEnum( nTmp, aToken,
+ psXML_BrushVertPos ) )
+ {
+ if( bHori )
+ sw_frmitems_MergeXMLVertPos(
+ ePos, nTmp );
+ else if( !bVert )
+ ePos = nTmp;
+ else
+ bOk = false;
+ bVert = true;
+ }
+ else
+ {
+ bOk = false;
+ }
+ }
+
+ if( GPOS_NONE == ePos ) bOk = false;
+ if( bOk )
+ rBrush.SetGraphicPos( ePos );
+ }
+ break;
+
+ case MID_GRAPHIC_FILTER:
+ rBrush.SetGraphicFilter( rValue );
+ bOk = true;
+ break;
+ }
+ }
+ break;
+
+ case RES_PAGEDESC:
+ {
+ SwFormatPageDesc& rPageDesc = dynamic_cast<SwFormatPageDesc&>(rItem);
+
+ if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId )
+ {
+ sal_Int32 nVal;
+ bOk = ::sax::Converter::convertNumber(
+ nVal, rValue, 0, USHRT_MAX);
+ // i#114163 tdf#77111: OOo < 3.3 had a bug where it wrote
+ // "auto" as "0" for tables - now that we support a real offset
+ // 0, this fake "0" MUST NOT be imported as offset 0!
+ if( bOk && nVal > 0 )
+ rPageDesc.SetNumOffset( o3tl::narrowing<sal_uInt16>(nVal) );
+ }
+ }
+ break;
+
+ case RES_LAYOUT_SPLIT:
+ case RES_ROW_SPLIT:
+ {
+ SfxBoolItem& rSplit = dynamic_cast<SfxBoolItem&>(rItem);
+
+ if( IsXMLToken( rValue, XML_AUTO ) ||
+ IsXMLToken( rValue, XML_TRUE ) )
+ {
+ rSplit.SetValue( true );
+ bOk = true;
+ }
+ else if( IsXMLToken( rValue, XML_ALWAYS ) ||
+ IsXMLToken( rValue, XML_FALSE ) )
+ {
+ rSplit.SetValue( false );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_PRINT:
+ {
+ SfxBoolItem& rHasTextChangesOnly = dynamic_cast<SfxBoolItem&>(rItem);
+
+ if( IsXMLToken( rValue, XML_TRUE ) )
+ {
+ rHasTextChangesOnly.SetValue( true );
+ bOk = true;
+ }
+ else if( IsXMLToken( rValue, XML_FALSE ) )
+ {
+ rHasTextChangesOnly.SetValue( false );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_HORI_ORIENT:
+ {
+ SwFormatHoriOrient& rHoriOrient = dynamic_cast<SwFormatHoriOrient&>(rItem);
+
+ sal_Int16 nValue;
+ bOk = SvXMLUnitConverter::convertEnum( nValue, rValue,
+ aXMLTableAlignMap );
+ if( bOk )
+ rHoriOrient.SetHoriOrient( nValue );
+ }
+ break;
+
+ case RES_VERT_ORIENT:
+ {
+ SwFormatVertOrient& rVertOrient = dynamic_cast<SwFormatVertOrient&>(rItem);
+
+ sal_Int16 nValue;
+ bOk = SvXMLUnitConverter::convertEnum( nValue, rValue,
+ aXMLTableVAlignMap );
+ if( bOk )
+ rVertOrient.SetVertOrient( nValue );
+ //#i8855# text::VertOrientation::NONE is stored as empty string and should be applied here
+ else if(rValue.isEmpty())
+ {
+ rVertOrient.SetVertOrient( text::VertOrientation::NONE );
+ bOk = true;
+ }
+ }
+ break;
+
+ case RES_FRM_SIZE:
+ {
+ SwFormatFrameSize& rFrameSize = dynamic_cast<SwFormatFrameSize&>(rItem);
+
+ bool bSetHeight = false;
+ bool bSetWidth = false;
+ bool bSetSizeType = false;
+ SwFrameSize eSizeType = SwFrameSize::Variable;
+ sal_Int32 nMin = MINLAY;
+
+ switch( nMemberId )
+ {
+ case MID_FRMSIZE_REL_WIDTH:
+ {
+ sal_Int32 nValue;
+ bOk = ::sax::Converter::convertPercent( nValue, rValue );
+ if( bOk )
+ {
+ if( nValue < 1 )
+ nValue = 1;
+ else if( nValue > 100 )
+ nValue = 100;
+
+ rFrameSize.SetWidthPercent( static_cast<sal_Int8>(nValue) );
+ }
+ }
+ break;
+ case MID_FRMSIZE_WIDTH:
+ bSetWidth = true;
+ break;
+ case MID_FRMSIZE_MIN_HEIGHT:
+ eSizeType = SwFrameSize::Minimum;
+ bSetHeight = true;
+ nMin = 1;
+ bSetSizeType = true;
+ break;
+ case MID_FRMSIZE_FIX_HEIGHT:
+ eSizeType = SwFrameSize::Fixed;
+ bSetHeight = true;
+ nMin = 1;
+ bSetSizeType = true;
+ break;
+ case MID_FRMSIZE_COL_WIDTH:
+ eSizeType = SwFrameSize::Fixed;
+ bSetWidth = true;
+ bSetSizeType = true;
+ break;
+ case MID_FRMSIZE_REL_COL_WIDTH:
+ {
+ sal_Int32 nPos = rValue.indexOf( '*' );
+ if( -1 != nPos )
+ {
+ sal_Int32 nValue = rValue.toInt32();
+ if( nValue < MINLAY )
+ nValue = MINLAY;
+ else if( nValue > SAL_MAX_UINT16 )
+ nValue = SAL_MAX_UINT16;
+
+ rFrameSize.SetWidth( o3tl::narrowing<sal_uInt16>(nValue) );
+ rFrameSize.SetHeightSizeType( SwFrameSize::Variable );
+ bOk = true;
+ }
+ }
+ break;
+ }
+
+ if( bSetHeight || bSetWidth )
+ {
+ sal_Int32 nValue;
+ bOk = rUnitConverter.convertMeasureToCore(nValue, rValue, nMin,
+ USHRT_MAX );
+ if( bOk )
+ {
+ if( bSetWidth )
+ rFrameSize.SetWidth( o3tl::narrowing<sal_uInt16>(nValue) );
+ if( bSetHeight )
+ rFrameSize.SetHeight( o3tl::narrowing<sal_uInt16>(nValue) );
+ if( bSetSizeType )
+ rFrameSize.SetHeightSizeType( eSizeType );
+ }
+ }
+ }
+ break;
+
+ case RES_FRAMEDIR:
+ {
+ if (IsXMLToken(rValue, XML_BT_LR))
+ {
+ // Read bt-lr from the extension namespace, handle other values
+ // below.
+ Any aAny;
+ aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_LR_BT);
+ bOk = rItem.PutValue(aAny, 0);
+ }
+ else if (IsXMLToken(rValue, XML_TB_RL90))
+ {
+ // Read tb-rl90 from the extension namespace.
+ Any aAny;
+ aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_RL_TB90);
+ bOk = rItem.PutValue(aAny, 0);
+ }
+ else
+ {
+ std::unique_ptr<XMLPropertyHandler> pWritingModeHandler =
+ XMLPropertyHandlerFactory::CreatePropertyHandler(
+ XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT );
+ Any aAny;
+ bOk = pWritingModeHandler->importXML( rValue, aAny,
+ rUnitConverter );
+ if( bOk )
+ bOk = rItem.PutValue( aAny, 0 );
+ }
+ }
+ break;
+
+ case RES_COLLAPSING_BORDERS:
+ {
+ SfxBoolItem& rBorders = dynamic_cast<SfxBoolItem&>(rItem);
+
+ if( IsXMLToken( rValue, XML_COLLAPSING ) )
+ {
+ rBorders.SetValue(true);
+ bOk = true;
+ }
+ else if( IsXMLToken( rValue, XML_SEPARATING ) )
+ {
+ rBorders.SetValue(false);
+ bOk = true;
+ }
+ else
+ bOk = false;
+ }
+ break;
+
+ default:
+ OSL_FAIL("Item not implemented!");
+ break;
+ }
+
+ return bOk;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlimpit.hxx b/sw/source/filter/xml/xmlimpit.hxx
new file mode 100644
index 0000000000..8227546a62
--- /dev/null
+++ b/sw/source/filter/xml/xmlimpit.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX
+
+#include <com/sun/star/xml/sax/XFastAttributeList.hpp>
+#include "xmlitmap.hxx"
+
+class SvXMLUnitConverter;
+class SfxPoolItem;
+class SfxItemSet;
+class SvXMLNamespaceMap;
+struct SvXMLItemMapEntry;
+class SvXMLAttrContainerItem;
+
+class SvXMLImportItemMapper
+{
+ SvXMLItemMapEntriesRef mrMapEntries;
+
+public:
+ explicit SvXMLImportItemMapper( SvXMLItemMapEntriesRef aMapEntries );
+ virtual ~SvXMLImportItemMapper();
+
+ /** fills the given itemset with the attributes in the given list */
+ void importXML( SfxItemSet& rSet,
+ css::uno::Reference< css::xml::sax::XFastAttributeList > const & xAttrList,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap );
+
+ /** this method is called for every item that has the
+ MID_SW_FLAG_SPECIAL_ITEM_IMPORT flag set */
+ virtual bool handleSpecialItem( const SvXMLItemMapEntry& rEntry,
+ SfxPoolItem& rItem,
+ SfxItemSet& rSet,
+ const OUString& rValue,
+ const SvXMLUnitConverter& rUnitConverter );
+
+ /** this method is called for every item that has the
+ MID_SW_FLAG_NO_ITEM_IMPORT flag set */
+ virtual bool handleNoItem( const SvXMLItemMapEntry& rEntry,
+ SfxItemSet& rSet,
+ const OUString& rValue,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap );
+
+ /** This method is called when all attributes have benn processed. It
+ * may be used to remove items that are incomplete */
+ virtual void finished(SfxItemSet & rSet,
+ SvXMLUnitConverter const& rUnitConverter) const;
+
+ virtual void setMapEntries( SvXMLItemMapEntriesRef rMapEntries );
+ inline const SvXMLItemMapEntriesRef& getMapEntries() const;
+
+ /** This method is called for every item that should be set based
+ upon an XML attribute value. */
+ static bool PutXMLValue(
+ SfxPoolItem& rItem,
+ const OUString& rValue,
+ sal_uInt16 nMemberId,
+ const SvXMLUnitConverter& rUnitConverter );
+private:
+ void importXMLUnknownAttributes( SfxItemSet& rSet,
+ css::uno::Reference< css::xml::sax::XFastAttributeList > const & xAttrList,
+ const SvXMLUnitConverter& rUnitConverter,
+ std::unique_ptr<SvXMLAttrContainerItem>& pUnknownItem );
+
+};
+
+inline const SvXMLItemMapEntriesRef&
+SvXMLImportItemMapper::getMapEntries() const
+{
+ return mrMapEntries;
+}
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlitem.cxx b/sw/source/filter/xml/xmlitem.cxx
new file mode 100644
index 0000000000..ff98d2ff69
--- /dev/null
+++ b/sw/source/filter/xml/xmlitem.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/brushitem.hxx>
+#include <sal/log.hxx>
+#include <xmloff/xmlimp.hxx>
+#include "xmlimpit.hxx"
+#include "xmlitem.hxx"
+#include "xmlbrshi.hxx"
+#include <hintids.hxx>
+
+using namespace ::com::sun::star;
+
+SwXMLItemSetContext::SwXMLItemSetContext( SvXMLImport& rImp, sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ SfxItemSet& rISet,
+ SvXMLImportItemMapper& rIMap,
+ const SvXMLUnitConverter& rUnitConverter ):
+ SvXMLImportContext( rImp ),
+ m_rItemSet( rISet ),
+ m_rIMapper( rIMap ),
+ m_rUnitConv( rUnitConverter )
+{
+ rIMap.importXML( m_rItemSet, xAttrList, m_rUnitConv,
+ GetImport().GetNamespaceMap() );
+}
+
+SwXMLItemSetContext::~SwXMLItemSetContext()
+{
+ if( m_xBackground.is() )
+ {
+ const SvxBrushItem& rItem = m_xBackground->GetItem();
+ m_rItemSet.Put( rItem );
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLItemSetContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLItemMapEntriesRef xMapEntries = m_rIMapper.getMapEntries();
+ SvXMLItemMapEntry const * pEntry = xMapEntries->getByName( nElement );
+
+ if( pEntry && 0 != (pEntry->nMemberId & MID_SW_FLAG_ELEMENT_ITEM_IMPORT) )
+ {
+ return createFastChildContext( nElement, xAttrList, *pEntry );
+ }
+ else
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement);
+ return nullptr;
+}
+
+/** This method is called from this instance implementation of
+ CreateChildContext if the element matches an entry in the
+ SvXMLImportItemMapper with the mid flag MID_SW_FLAG_ELEMENT
+*/
+SvXMLImportContextRef SwXMLItemSetContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ const SvXMLItemMapEntry& rEntry )
+{
+ rtl::Reference<SwXMLBrushItemImportContext> xContext;
+
+ switch( rEntry.nWhichId )
+ {
+ case RES_BACKGROUND:
+ {
+ if( const SvxBrushItem* pItem = m_rItemSet.GetItemIfSet( RES_BACKGROUND,
+ false ) )
+ {
+ xContext = new SwXMLBrushItemImportContext(
+ GetImport(), nElement, xAttrList,
+ m_rUnitConv, *pItem );
+ }
+ else
+ {
+ xContext = new SwXMLBrushItemImportContext(
+ GetImport(), nElement, xAttrList,
+ m_rUnitConv, RES_BACKGROUND );
+ }
+ m_xBackground = xContext;
+ }
+ break;
+ }
+
+ return xContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlitem.hxx b/sw/source/filter/xml/xmlitem.hxx
new file mode 100644
index 0000000000..cd6f9c8db6
--- /dev/null
+++ b/sw/source/filter/xml/xmlitem.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX
+
+#include <com/sun/star/xml/sax/XFastAttributeList.hpp>
+#include <svl/itemset.hxx>
+#include <xmloff/xmlictxt.hxx>
+
+class SfxItemSet;
+class SvXMLImportItemMapper;
+class SvXMLUnitConverter;
+struct SvXMLItemMapEntry;
+class SwXMLBrushItemImportContext;
+
+class SwXMLItemSetContext final : public SvXMLImportContext
+{
+ SfxItemSet &m_rItemSet;
+ const SvXMLImportItemMapper &m_rIMapper;
+ const SvXMLUnitConverter &m_rUnitConv;
+ rtl::Reference<SwXMLBrushItemImportContext> m_xBackground;
+
+public:
+
+ SwXMLItemSetContext( SvXMLImport& rImport, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ SfxItemSet& rItemSet,
+ SvXMLImportItemMapper& rIMap,
+ const SvXMLUnitConverter& rUnitConv );
+
+ virtual ~SwXMLItemSetContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+private:
+ // This method is called from this instance implementation of
+ // createFastChildContext if the element matches an entry in the
+ // SvXMLImportItemMapper with the mid flag MID_SW_FLAG_ELEMENT_ITEM_IMPORT
+ SvXMLImportContextRef createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ const SvXMLItemMapEntry& rEntry );
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmliteme.cxx b/sw/source/filter/xml/xmliteme.cxx
new file mode 100644
index 0000000000..3d19a9d6dc
--- /dev/null
+++ b/sw/source/filter/xml/xmliteme.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/util/MeasureUnit.hpp>
+
+#include <hintids.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <utility>
+#include <xmloff/xmluconv.hxx>
+#include "xmlexpit.hxx"
+#include <xmloff/namespacemap.hxx>
+#include "xmlbrshe.hxx"
+#include <editeng/brushitem.hxx>
+#include <fmtornt.hxx>
+#include <unomid.h>
+#include <frmfmt.hxx>
+#include "xmlexp.hxx"
+#include <editeng/memberids.h>
+#include <editeng/prntitem.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::xmloff::token;
+
+namespace {
+
+class SwXMLTableItemMapper_Impl: public SvXMLExportItemMapper
+{
+ SwXMLBrushItemExport m_aBrushItemExport;
+
+protected:
+
+ sal_uInt32 m_nAbsWidth;
+
+ static void AddAttribute( sal_uInt16 nPrefix, enum XMLTokenEnum eLName,
+ const OUString& rValue,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ comphelper::AttributeList& rAttrList );
+
+public:
+
+ SwXMLTableItemMapper_Impl(
+ SvXMLItemMapEntriesRef rMapEntries,
+ SwXMLExport& rExp );
+
+ virtual void handleSpecialItem( comphelper::AttributeList& rAttrList,
+ const SvXMLItemMapEntry& rEntry,
+ const SfxPoolItem& rItem,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const SfxItemSet *pSet ) const override;
+
+ virtual void handleElementItem(
+ const SvXMLItemMapEntry& rEntry,
+ const SfxPoolItem& rItem ) const override;
+
+ inline void SetAbsWidth( sal_uInt32 nAbs );
+};
+
+}
+
+SwXMLTableItemMapper_Impl::SwXMLTableItemMapper_Impl(
+ SvXMLItemMapEntriesRef rMapEntries,
+ SwXMLExport& rExp ) :
+ SvXMLExportItemMapper( std::move(rMapEntries) ),
+ m_aBrushItemExport( rExp ),
+ m_nAbsWidth( USHRT_MAX )
+{
+}
+
+void SwXMLTableItemMapper_Impl::AddAttribute( sal_uInt16 nPrefix,
+ enum XMLTokenEnum eLName,
+ const OUString& rValue,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ comphelper::AttributeList& rAttrList )
+{
+ OUString sName( rNamespaceMap.GetQNameByKey( nPrefix,
+ GetXMLToken(eLName) ) );
+ rAttrList.AddAttribute( sName, rValue );
+}
+
+void SwXMLTableItemMapper_Impl::handleSpecialItem(
+ comphelper::AttributeList& rAttrList,
+ const SvXMLItemMapEntry& rEntry,
+ const SfxPoolItem& rItem,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const SfxItemSet *pSet ) const
+{
+ switch( rEntry.nWhichId )
+ {
+
+ case RES_PRINT:
+ {
+ const SvxPrintItem *pItem;
+ if( pSet &&
+ (pItem = pSet->GetItemIfSet( RES_PRINT )) )
+ {
+ bool bHasTextChangesOnly = pItem->GetValue();
+ if ( !bHasTextChangesOnly )
+ {
+ OUString sValue;
+ sal_uInt16 nMemberId =
+ static_cast<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK );
+
+ if( SvXMLExportItemMapper::QueryXMLValue(
+ rItem, sValue, nMemberId, rUnitConverter ) )
+ {
+ AddAttribute( rEntry.nNameSpace, rEntry.eLocalName,
+ sValue, rNamespaceMap, rAttrList );
+ }
+ }
+ }
+ }
+ break;
+
+ case RES_LR_SPACE:
+ {
+ const SwFormatHoriOrient *pItem;
+ if( pSet &&
+ (pItem = pSet->GetItemIfSet( RES_HORI_ORIENT )) )
+ {
+ sal_Int16 eHoriOrient = pItem->GetHoriOrient();
+ bool bExport = false;
+ sal_uInt16 nMemberId =
+ o3tl::narrowing<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK );
+ switch( nMemberId )
+ {
+ case MID_L_MARGIN:
+ bExport = text::HoriOrientation::NONE == eHoriOrient ||
+ text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient;
+ break;
+ case MID_R_MARGIN:
+ bExport = text::HoriOrientation::NONE == eHoriOrient;
+ break;
+ }
+ OUString sValue;
+ if( bExport && SvXMLExportItemMapper::QueryXMLValue(
+ rItem, sValue, nMemberId, rUnitConverter ) )
+ {
+ AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, sValue,
+ rNamespaceMap, rAttrList );
+ }
+ }
+ }
+ break;
+
+ case RES_FRM_SIZE:
+ {
+ sal_uInt16 nMemberId =
+ o3tl::narrowing<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK );
+ switch( nMemberId )
+ {
+ case MID_FRMSIZE_WIDTH:
+ if( m_nAbsWidth )
+ {
+ OUStringBuffer sBuffer;
+ rUnitConverter.convertMeasureToXML( sBuffer, m_nAbsWidth );
+ AddAttribute( rEntry.nNameSpace, rEntry.eLocalName,
+ sBuffer.makeStringAndClear(),
+ rNamespaceMap, rAttrList );
+ }
+ break;
+ case MID_FRMSIZE_REL_WIDTH:
+ {
+ OUString sValue;
+ if( SvXMLExportItemMapper::QueryXMLValue(
+ rItem, sValue, nMemberId, rUnitConverter ) )
+ {
+ AddAttribute( rEntry.nNameSpace, rEntry.eLocalName,
+ sValue, rNamespaceMap, rAttrList );
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/** this method is called for every item that has the
+ MID_SW_FLAG_ELEMENT_EXPORT flag set */
+void SwXMLTableItemMapper_Impl::handleElementItem(
+ const SvXMLItemMapEntry& rEntry,
+ const SfxPoolItem& rItem ) const
+{
+ switch( rEntry.nWhichId )
+ {
+ case RES_BACKGROUND:
+ {
+ const_cast<SwXMLTableItemMapper_Impl *>(this)->m_aBrushItemExport.exportXML(
+ static_cast<const SvxBrushItem&>(rItem) );
+ }
+ break;
+ }
+}
+
+inline void SwXMLTableItemMapper_Impl::SetAbsWidth( sal_uInt32 nAbs )
+{
+ m_nAbsWidth = nAbs;
+}
+
+void SwXMLExport::InitItemExport()
+{
+ m_pTwipUnitConverter.reset(new SvXMLUnitConverter(getComponentContext(),
+ util::MeasureUnit::TWIP, GetMM100UnitConverter().GetXMLMeasureUnit(),
+ getSaneDefaultVersion()));
+
+ m_xTableItemMap = new SvXMLItemMapEntries( aXMLTableItemMap );
+ m_xTableRowItemMap = new SvXMLItemMapEntries( aXMLTableRowItemMap );
+ m_xTableCellItemMap = new SvXMLItemMapEntries( aXMLTableCellItemMap );
+
+ m_pTableItemMapper.reset(new SwXMLTableItemMapper_Impl( m_xTableItemMap, *this ));
+}
+
+void SwXMLExport::FinitItemExport()
+{
+ m_pTableItemMapper.reset();
+ m_pTwipUnitConverter.reset();
+}
+
+void SwXMLExport::ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth )
+{
+ static_cast<SwXMLTableItemMapper_Impl *>(m_pTableItemMapper.get())
+ ->SetAbsWidth( nAbsWidth );
+ ExportFormat(rFormat, XML_TABLE, {});
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlitemi.cxx b/sw/source/filter/xml/xmlitemi.cxx
new file mode 100644
index 0000000000..9281604c4f
--- /dev/null
+++ b/sw/source/filter/xml/xmlitemi.cxx
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/util/MeasureUnit.hpp>
+
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+#include <editeng/memberids.h>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+
+#include <hintids.hxx>
+#include <unomid.h>
+#include "xmlimp.hxx"
+#include "xmlitmap.hxx"
+#include "xmlimpit.hxx"
+#include "xmlitem.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class SwXMLImportTableItemMapper_Impl: public SvXMLImportItemMapper
+{
+
+public:
+
+ explicit SwXMLImportTableItemMapper_Impl(const SvXMLItemMapEntriesRef& rMapEntries);
+
+ virtual bool handleSpecialItem( const SvXMLItemMapEntry& rEntry,
+ SfxPoolItem& rItem,
+ SfxItemSet& rSet,
+ const OUString& rValue,
+ const SvXMLUnitConverter& rUnitConverter ) override;
+
+ virtual bool
+ handleNoItem(SvXMLItemMapEntry const& rEntry,
+ SfxItemSet & rSet,
+ OUString const& rValue,
+ SvXMLUnitConverter const& rUnitConverter,
+ SvXMLNamespaceMap const& rNamespaceMap) override;
+
+ virtual void finished(SfxItemSet & rSet,
+ SvXMLUnitConverter const& rUnitConverter) const override;
+
+ virtual void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) override;
+
+private:
+ void Reset();
+
+ OUString m_FoMarginValue;
+ enum { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3 };
+ bool m_bHaveMargin[4];
+};
+
+}
+
+SwXMLImportTableItemMapper_Impl::SwXMLImportTableItemMapper_Impl(
+ const SvXMLItemMapEntriesRef& rMapEntries ) :
+ SvXMLImportItemMapper( rMapEntries )
+{
+ Reset();
+}
+
+void SwXMLImportTableItemMapper_Impl::Reset()
+{
+ m_FoMarginValue.clear();
+ for (int i = 0; i < 3; ++i)
+ {
+ m_bHaveMargin[i] = false;
+ }
+}
+
+void SwXMLImportTableItemMapper_Impl::setMapEntries(
+ SvXMLItemMapEntriesRef rMapEntries )
+{
+ Reset();
+ SvXMLImportItemMapper::setMapEntries(rMapEntries);
+}
+
+bool SwXMLImportTableItemMapper_Impl::handleSpecialItem(
+ const SvXMLItemMapEntry& rEntry,
+ SfxPoolItem& rItem,
+ SfxItemSet& rItemSet,
+ const OUString& rValue,
+ const SvXMLUnitConverter& rUnitConv )
+{
+ bool bRet = false;
+ sal_uInt16 nMemberId = static_cast< sal_Int16 >(rEntry.nMemberId & MID_SW_FLAG_MASK);
+ switch( rItem.Which() )
+ {
+ case RES_LR_SPACE:
+ switch (nMemberId)
+ {
+ case MID_L_MARGIN:
+ m_bHaveMargin[LEFT] = true;
+ break;
+ case MID_R_MARGIN:
+ m_bHaveMargin[RIGHT] = true;
+ break;
+ }
+ bRet = SvXMLImportItemMapper::PutXMLValue(
+ rItem, rValue, nMemberId, rUnitConv);
+ break;
+ case RES_UL_SPACE:
+ switch (nMemberId)
+ {
+ case MID_UP_MARGIN:
+ m_bHaveMargin[TOP] = true;
+ break;
+ case MID_LO_MARGIN:
+ m_bHaveMargin[BOTTOM] = true;
+ break;
+ }
+ bRet = SvXMLImportItemMapper::PutXMLValue(
+ rItem, rValue, nMemberId, rUnitConv);
+ break;
+ case RES_FRM_SIZE:
+ switch( nMemberId )
+ {
+ case MID_FRMSIZE_COL_WIDTH:
+ // If the item is existing already, a relative value has been set
+ // already that must be preserved.
+ if( SfxItemState::SET != rItemSet.GetItemState( RES_FRM_SIZE,
+ false ) )
+ bRet = SvXMLImportItemMapper::PutXMLValue(
+ rItem, rValue, nMemberId, rUnitConv );
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+bool SwXMLImportTableItemMapper_Impl::handleNoItem(
+ SvXMLItemMapEntry const& rEntry,
+ SfxItemSet & rSet,
+ OUString const& rValue,
+ SvXMLUnitConverter const& rUnitConverter,
+ SvXMLNamespaceMap const& rNamespaceMap)
+{
+ if ((XML_NAMESPACE_FO == rEntry.nNameSpace) &&
+ (xmloff::token::XML_MARGIN == rEntry.eLocalName))
+ {
+ m_FoMarginValue = rValue;
+ return true;
+ }
+ else
+ {
+ return SvXMLImportItemMapper::handleNoItem(
+ rEntry, rSet, rValue, rUnitConverter, rNamespaceMap);
+ }
+}
+
+void SwXMLImportTableItemMapper_Impl::finished(
+ SfxItemSet & rSet, SvXMLUnitConverter const& rUnitConverter) const
+{
+ if (m_FoMarginValue.isEmpty())
+ return;
+
+ sal_uInt16 const Ids[4][2] = {
+ { RES_LR_SPACE, MID_L_MARGIN },
+ { RES_LR_SPACE, MID_R_MARGIN },
+ { RES_UL_SPACE, MID_UP_MARGIN },
+ { RES_UL_SPACE, MID_LO_MARGIN },
+ };
+ for (int i = 0; i < 4; ++i)
+ {
+ if (m_bHaveMargin[i])
+ {
+ continue; // already read fo:margin-top etc.
+ }
+ // first get item from itemset
+ SfxPoolItem const* pItem = nullptr;
+ SfxItemState eState =
+ rSet.GetItemState(Ids[i][0], true, &pItem);
+
+ // if not set, try the pool
+ if ((SfxItemState::SET != eState) && SfxItemPool::IsWhich(Ids[i][0]))
+ {
+ pItem = &rSet.GetPool()->GetDefaultItem(Ids[i][0]);
+ }
+
+ // do we have an item?
+ if (eState >= SfxItemState::DEFAULT && pItem)
+ {
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+ bool const bPut = PutXMLValue(
+ *pNewItem, m_FoMarginValue, Ids[i][1], rUnitConverter);
+ if (bPut)
+ {
+ rSet.Put(std::move(pNewItem));
+ }
+ }
+ else
+ {
+ OSL_ENSURE(false, "could not get item");
+ }
+ }
+}
+
+void SwXMLImport::InitItemImport()
+{
+ m_pTwipUnitConv.reset( new SvXMLUnitConverter( GetComponentContext(),
+ util::MeasureUnit::TWIP, util::MeasureUnit::TWIP,
+ SvtSaveOptions::ODFSVER_LATEST_EXTENDED) );
+
+ m_xTableItemMap = new SvXMLItemMapEntries( aXMLTableItemMap );
+ m_xTableColItemMap = new SvXMLItemMapEntries( aXMLTableColItemMap );
+ m_xTableRowItemMap = new SvXMLItemMapEntries( aXMLTableRowItemMap );
+ m_xTableCellItemMap = new SvXMLItemMapEntries( aXMLTableCellItemMap );
+
+ m_pTableItemMapper.reset( new SwXMLImportTableItemMapper_Impl( m_xTableItemMap ) );
+}
+
+void SwXMLImport::FinitItemImport()
+{
+ m_pTableItemMapper.reset();
+ m_pTwipUnitConv.reset();
+}
+
+SvXMLImportContext *SwXMLImport::CreateTableItemImportContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ XmlStyleFamily nFamily,
+ SfxItemSet& rItemSet )
+{
+ SvXMLItemMapEntriesRef xItemMap;
+
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_TABLE:
+ xItemMap = m_xTableItemMap;
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ xItemMap = m_xTableColItemMap;
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ xItemMap = m_xTableRowItemMap;
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ xItemMap = m_xTableCellItemMap;
+ break;
+ default: break;
+ }
+
+ m_pTableItemMapper->setMapEntries( xItemMap );
+
+ return new SwXMLItemSetContext( *this, nElement,
+ xAttrList, rItemSet,
+ GetTableItemMapper(),
+ *m_pTwipUnitConv );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlitemm.cxx b/sw/source/filter/xml/xmlitemm.cxx
new file mode 100644
index 0000000000..0d25da02c1
--- /dev/null
+++ b/sw/source/filter/xml/xmlitemm.cxx
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/memberids.h>
+#include <hintids.hxx>
+#include <svx/unomid.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include "xmlitmap.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <unomid.h>
+
+using namespace ::xmloff::token;
+
+#define MAP_ENTRY( p, l, w, m ) \
+ { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), m }
+#define M_E_SI( p, l, w, m ) \
+ { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_IMPORT|m }
+#define M_E_SE( p, l, w, m ) \
+ { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_EXPORT|m }
+#define M_E_SIE( p, l, w, m ) \
+ { XML_NAMESPACE_##p, XML_##l, o3tl::narrowing<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_EXPORT|MID_SW_FLAG_SPECIAL_ITEM_IMPORT|m }
+
+const SvXMLItemMapEntry aTableItemMap[] =
+{
+ // RES_FILL_ORDER
+ // not required
+ // RES_FRM_SIZE
+ M_E_SE( STYLE, WIDTH, RES_FRM_SIZE, MID_FRMSIZE_WIDTH ),
+ M_E_SE( STYLE, REL_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_REL_WIDTH ),
+ // RES_PAPER_BIN
+ // not required
+ M_E_SE( FO, MARGIN, 0xFFFF/*invalid*/, MID_SW_FLAG_NO_ITEM_IMPORT),
+ M_E_SIE( FO, MARGIN_LEFT, RES_LR_SPACE, MID_L_MARGIN ),
+ M_E_SIE( FO, MARGIN_RIGHT, RES_LR_SPACE, MID_R_MARGIN ),
+ // RES_UL_SPACE
+ M_E_SI( FO, MARGIN_TOP, RES_UL_SPACE, MID_UP_MARGIN ),
+ M_E_SI( FO, MARGIN_BOTTOM, RES_UL_SPACE, MID_LO_MARGIN ),
+ // RES_PAGEDESC
+ MAP_ENTRY( STYLE, PAGE_NUMBER, RES_PAGEDESC, MID_PAGEDESC_PAGENUMOFFSET),
+ // RES_BREAK
+ MAP_ENTRY( FO, BREAK_BEFORE, RES_BREAK, MID_BREAK_BEFORE ),
+ MAP_ENTRY( FO, BREAK_AFTER, RES_BREAK, MID_BREAK_AFTER ),
+ // RES_CNTNT
+ // not required
+ // RES_HEADER
+ // not required
+ // RES_FOOTER
+ // not required
+ // RES_PRINT
+ // not required
+ // RES_OPAQUE
+ // not required
+ // RES_PROTECT
+ // not required
+ // RES_SURROUND
+ // not required
+ // RES_VERT_ORIENT
+ // not required
+ // RES_HORI_ORIENT
+ MAP_ENTRY( TABLE, ALIGN, RES_HORI_ORIENT, 0 ),
+ // RES_ANCHOR
+ // not required
+ // RES_BACKGROUND
+ MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ),
+ MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ),
+ // RES_BOX
+ // not required
+ // RES_SHADOW
+ MAP_ENTRY( STYLE, SHADOW, RES_SHADOW, 0 ),
+ // RES_FRMMACRO
+ // not required
+ // RES_COL
+ // not required
+ // RES_KEEP
+ MAP_ENTRY( FO, KEEP_WITH_NEXT, RES_KEEP, 0 ),
+ // RES_URL
+ // not required
+ // RES_EDIT_IN_READONLY
+ // not required
+ // RES_LAYOUT_SPLIT
+ MAP_ENTRY( STYLE, MAY_BREAK_BETWEEN_ROWS, RES_LAYOUT_SPLIT, 0 ),
+ // RES_CHAIN
+ // not required
+ // RES_LINENUMBER
+ // not required
+ // RES_FTN_AT_TXTEND
+ // not required
+ // RES_END_AT_TXTEND
+ // not required
+ // RES_UNKNOWNATR_CONTAINER
+ M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ),
+
+ // RES_FRAMEDIR
+ MAP_ENTRY( STYLE, WRITING_MODE, RES_FRAMEDIR, 0 ),
+
+ // RES_COLLAPSING_BORDERS
+ MAP_ENTRY( TABLE, BORDER_MODEL, RES_COLLAPSING_BORDERS, 0 )
+};
+const std::span<SvXMLItemMapEntry const> aXMLTableItemMap(aTableItemMap);
+
+const SvXMLItemMapEntry aTableColItemMap[] =
+{
+ M_E_SI( STYLE, COLUMN_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_COL_WIDTH ),
+ MAP_ENTRY( STYLE, REL_COLUMN_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_REL_COL_WIDTH ),
+};
+const std::span<SvXMLItemMapEntry const> aXMLTableColItemMap(aTableColItemMap);
+
+const SvXMLItemMapEntry aTableRowItemMap[] =
+{
+ // RES_FILL_ORDER
+ // not required
+ // RES_FRM_SIZE
+ MAP_ENTRY( STYLE, ROW_HEIGHT, RES_FRM_SIZE, MID_FRMSIZE_FIX_HEIGHT ),
+ MAP_ENTRY( STYLE, MIN_ROW_HEIGHT, RES_FRM_SIZE, MID_FRMSIZE_MIN_HEIGHT ),
+ // RES_PAPER_BIN
+ // not required
+ // RES_LR_SPACE
+ // not required
+ // RES_UL_SPACE
+ // not required
+ // RES_PAGEDESC
+ // not required
+ // RES_BREAK
+ // not required
+ // RES_CNTNT
+ // not required
+ // RES_HEADER
+ // not required
+ // RES_FOOTER
+ // not required
+ // RES_PRINT
+ // M_E_SE( STYLE, TEXT_CHANGES_ONLY, RES_PRINT, 0 ),
+ M_E_SE( LO_EXT, TEXT_CHANGES_ONLY, RES_PRINT, 0 ),
+ // RES_OPAQUE
+ // not required
+ // RES_PROTECT
+ // not required
+ // RES_SURROUND
+ // not required
+ // RES_VERT_ORIENT
+ // not required
+ // RES_HORI_ORIENT
+ // not required
+ // RES_ANCHOR
+ // not required
+ // RES_BACKGROUND
+ MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ),
+ MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ),
+ // RES_BOX
+ // not required
+ // RES_ANCHOR
+ // not required
+ // RES_SHADOW
+ // not required
+ // RES_FRMMACRO
+ // not required
+ // RES_COL
+ // not required
+ // RES_KEEP
+ // not required
+ // RES_URL
+ // not required
+ // RES_EDIT_IN_READONLY
+ // not required
+ // RES_LAYOUT_SPLIT
+ M_E_SE( STYLE, KEEP_TOGETHER, RES_ROW_SPLIT, 0 ),
+ M_E_SE( FO, KEEP_TOGETHER, RES_ROW_SPLIT, 0 ),
+ // RES_CHAIN
+ // not required
+ // RES_LINENUMBER
+ // not required
+ // RES_FTN_AT_TXTEND
+ // not required
+ // RES_END_AT_TXTEND
+ // not required
+ // RES_UNKNOWNATR_CONTAINER
+ M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 )
+};
+const std::span<SvXMLItemMapEntry const> aXMLTableRowItemMap(aTableRowItemMap);
+
+const SvXMLItemMapEntry aTableCellItemMap[] =
+{
+ // RES_FILL_ORDER
+ // not required
+ // RES_FRM_SIZE
+ // not required
+ // RES_PAPER_BIN
+ // not required
+ // RES_LR_SPACE
+ // not required
+ // RES_UL_SPACE
+ // not required
+ // RES_PAGEDESC
+ // not required
+ // RES_BREAK
+ // not required
+ // RES_CNTNT
+ // not required
+ // RES_HEADER
+ // not required
+ // RES_FOOTER
+ // not required
+ // RES_PRINT
+ // M_E_SE( STYLE, TEXT_CHANGES_ONLY, RES_PRINT, 0 ),
+ M_E_SE( LO_EXT, TEXT_CHANGES_ONLY, RES_PRINT, 0 ),
+ // RES_OPAQUE
+ // not required
+ // RES_PROTECT
+ // not required
+ // RES_SURROUND
+ // not required
+ // RES_VERT_ORIENT
+ MAP_ENTRY( STYLE, VERTICAL_ALIGN, RES_VERT_ORIENT, 0 ),
+ // RES_HORI_ORIENT
+ // not required
+ // RES_ANCHOR
+ // not required
+ // RES_BACKGROUND
+ MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ),
+ MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ),
+ // RES_BOX
+ MAP_ENTRY( STYLE, BORDER_LINE_WIDTH, RES_BOX, ALL_BORDER_LINE_WIDTH ),
+ MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_LEFT, RES_BOX, LEFT_BORDER_LINE_WIDTH ),
+ MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_RIGHT, RES_BOX, RIGHT_BORDER_LINE_WIDTH ),
+ MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_TOP, RES_BOX, TOP_BORDER_LINE_WIDTH ),
+ MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_BOTTOM, RES_BOX, BOTTOM_BORDER_LINE_WIDTH ),
+ MAP_ENTRY( FO, PADDING, RES_BOX, ALL_BORDER_PADDING ),
+ MAP_ENTRY( FO, PADDING_LEFT, RES_BOX, LEFT_BORDER_PADDING ),
+ MAP_ENTRY( FO, PADDING_RIGHT, RES_BOX, RIGHT_BORDER_PADDING ),
+ MAP_ENTRY( FO, PADDING_TOP, RES_BOX, TOP_BORDER_PADDING ),
+ MAP_ENTRY( FO, PADDING_BOTTOM, RES_BOX, BOTTOM_BORDER_PADDING ),
+ MAP_ENTRY( FO, BORDER, RES_BOX, ALL_BORDER ),
+ MAP_ENTRY( FO, BORDER_LEFT, RES_BOX, LEFT_BORDER ),
+ MAP_ENTRY( FO, BORDER_RIGHT, RES_BOX, RIGHT_BORDER ),
+ MAP_ENTRY( FO, BORDER_TOP, RES_BOX, TOP_BORDER ),
+ MAP_ENTRY( FO, BORDER_BOTTOM, RES_BOX, BOTTOM_BORDER ),
+ // RES_SHADOW
+ // not required
+ // RES_FRMMACRO
+ // not required
+ // RES_COL
+ // not required
+ // RES_KEEP
+ // not required
+ // RES_URL
+ // not required
+ // RES_EDIT_IN_READONLY
+ // not required
+ // RES_LAYOUT_SPLIT
+ // not required
+ // RES_CHAIN
+ // not required
+ // RES_LINENUMBER
+ // not required
+ // RES_FTN_AT_TXTEND
+ // not required
+ // RES_END_AT_TXTEND
+ // not required
+ // RES_UNKNOWNATR_CONTAINER
+ M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ),
+
+ // RES_FRAMEDIR
+ MAP_ENTRY( STYLE, WRITING_MODE, RES_FRAMEDIR, 0 ),
+ MAP_ENTRY( LO_EXT, WRITING_MODE, RES_FRAMEDIR, 0 ),
+};
+const std::span<SvXMLItemMapEntry const> aXMLTableCellItemMap(aTableCellItemMap);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlithlp.cxx b/sw/source/filter/xml/xmlithlp.cxx
new file mode 100644
index 0000000000..d629765d78
--- /dev/null
+++ b/sw/source/filter/xml/xmlithlp.cxx
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <limits.h>
+
+#include "xmlithlp.hxx"
+#include <sax/tools/converter.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/brushitem.hxx>
+
+#include <xmloff/xmluconv.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/safeint.hxx>
+
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+
+using ::editeng::SvxBorderLine;
+using namespace ::xmloff::token;
+using namespace ::com::sun::star;
+
+#define SVX_XML_BORDER_WIDTH_THIN 0
+#define SVX_XML_BORDER_WIDTH_MIDDLE 1
+#define SVX_XML_BORDER_WIDTH_THICK 2
+
+const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BorderStyles[] =
+{
+ { XML_NONE, table::BorderLineStyle::NONE },
+ { XML_HIDDEN, table::BorderLineStyle::NONE },
+ { XML_SOLID, table::BorderLineStyle::SOLID },
+ { XML_DOUBLE, table::BorderLineStyle::DOUBLE },
+ { XML_DOUBLE_THIN, table::BorderLineStyle::DOUBLE_THIN },
+ { XML_DOTTED, table::BorderLineStyle::DOTTED },
+ { XML_DASHED, table::BorderLineStyle::DASHED },
+ { XML_FINE_DASHED, table::BorderLineStyle::FINE_DASHED },
+ { XML_DASH_DOT, table::BorderLineStyle::DASH_DOT },
+ { XML_DASH_DOT_DOT, table::BorderLineStyle::DASH_DOT_DOT },
+ { XML_GROOVE, table::BorderLineStyle::ENGRAVED },
+ { XML_RIDGE, table::BorderLineStyle::EMBOSSED },
+ { XML_INSET, table::BorderLineStyle::INSET },
+ { XML_OUTSET, table::BorderLineStyle::OUTSET },
+ { XML_TOKEN_INVALID, 0 }
+};
+
+const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[] =
+{
+ { XML_THIN, SVX_XML_BORDER_WIDTH_THIN },
+ { XML_MIDDLE, SVX_XML_BORDER_WIDTH_MIDDLE },
+ { XML_THICK, SVX_XML_BORDER_WIDTH_THICK },
+ { XML_TOKEN_INVALID, 0 }
+};
+// mapping tables to map external xml input to internal box line widths
+
+const sal_uInt16 aBorderWidths[] = {
+ SvxBorderLineWidth::Hairline,
+ SvxBorderLineWidth::VeryThin,
+ SvxBorderLineWidth::Thin
+};
+
+bool sw_frmitems_parseXMLBorder( std::u16string_view rValue,
+ const SvXMLUnitConverter& rUnitConverter,
+ bool& rHasStyle, sal_uInt16& rStyle,
+ bool& rHasWidth, sal_uInt16& rWidth,
+ sal_uInt16& rNamedWidth,
+ bool& rHasColor, Color& rColor )
+{
+ std::u16string_view aToken;
+ SvXMLTokenEnumerator aTokens( rValue );
+
+ rHasStyle = false;
+ rHasWidth = false;
+ rHasColor = false;
+
+ rStyle = USHRT_MAX;
+ rWidth = 0;
+ rNamedWidth = USHRT_MAX;
+
+ sal_Int32 nTemp;
+ while( aTokens.getNextToken( aToken ) && !aToken.empty() )
+ {
+ if( !rHasWidth &&
+ SvXMLUnitConverter::convertEnum( rNamedWidth, aToken,
+ psXML_NamedBorderWidths ) )
+ {
+ rHasWidth = true;
+ }
+ else if( !rHasStyle &&
+ SvXMLUnitConverter::convertEnum( rStyle, aToken,
+ psXML_BorderStyles ) )
+ {
+ rHasStyle = true;
+ }
+ else if (!rHasColor && ::sax::Converter::convertColor(rColor, aToken))
+ {
+ rHasColor = true;
+ }
+ else if( !rHasWidth &&
+ rUnitConverter.convertMeasureToCore(nTemp, aToken, 0, USHRT_MAX))
+ {
+ rWidth = o3tl::narrowing<sal_uInt16>(nTemp);
+ rHasWidth = true;
+ }
+ else
+ {
+ // misformed
+ return false;
+ }
+ }
+
+ return rHasStyle || rHasWidth || rHasColor;
+}
+
+static void sw_frmitems_setXMLBorderStyle( SvxBorderLine& rLine, sal_uInt16 nStyle )
+{
+ SvxBorderLineStyle eStyle = SvxBorderLineStyle::NONE;
+ if ( nStyle != table::BorderLineStyle::NONE )
+ eStyle = SvxBorderLineStyle( nStyle );
+ rLine.SetBorderLineStyle(eStyle);
+}
+
+bool sw_frmitems_setXMLBorder( std::unique_ptr<SvxBorderLine>& rpLine,
+ bool bHasStyle, sal_uInt16 nStyle,
+ bool bHasWidth, sal_uInt16 nWidth,
+ sal_uInt16 nNamedWidth,
+ bool bHasColor, const Color& rColor )
+{
+ // first of all, delete an empty line
+ if( (bHasStyle && table::BorderLineStyle::NONE == nStyle) ||
+ (bHasWidth && USHRT_MAX == nNamedWidth && 0 == nWidth) )
+ {
+ bool bRet = nullptr != rpLine;
+ rpLine.reset();
+ return bRet;
+ }
+
+ // if there is no line and no style and no with, there will never be a line
+ if( !rpLine && !(bHasStyle && bHasWidth) )
+ return false;
+
+ // We now do know that there will be a line
+ if( !rpLine )
+ rpLine.reset(new SvxBorderLine);
+
+ if( ( bHasWidth &&
+ (USHRT_MAX != nNamedWidth || (nWidth != rpLine->GetWidth() ) ) ) ||
+ ( bHasStyle &&
+ ((table::BorderLineStyle::SOLID == nStyle && rpLine->GetDistance()) ||
+ (table::BorderLineStyle::DOUBLE == nStyle && !rpLine->GetDistance())) ) )
+ {
+ bool bDouble = (bHasWidth && table::BorderLineStyle::DOUBLE == nStyle ) ||
+ rpLine->GetDistance();
+
+ // fdo#38542: for double borders, do not override the width
+ // set via style:border-line-width{,-left,-right,-top,-bottom}
+ if (!bDouble || !rpLine->GetWidth())
+ {
+ // The width has to be changed
+ if (bHasWidth && USHRT_MAX != nNamedWidth)
+ {
+ if (bDouble)
+ {
+ rpLine->SetBorderLineStyle( SvxBorderLineStyle::DOUBLE );
+ }
+ rpLine->SetWidth( aBorderWidths[nNamedWidth] );
+ }
+ else
+ {
+ if (!bHasWidth)
+ nWidth = rpLine->GetScaledWidth();
+
+ rpLine->SetWidth( nWidth );
+ }
+ }
+ sw_frmitems_setXMLBorderStyle( *rpLine, nStyle );
+ }
+
+ // set color
+ if( bHasColor )
+ rpLine->SetColor( rColor );
+
+ return true;
+}
+
+void sw_frmitems_setXMLBorder( std::unique_ptr<SvxBorderLine>& rpLine,
+ sal_uInt16 nWidth, sal_uInt16 nOutWidth,
+ sal_uInt16 nInWidth, sal_uInt16 nDistance )
+{
+ if( !rpLine )
+ rpLine.reset(new SvxBorderLine);
+
+ if( nWidth > 0 )
+ rpLine->SetWidth( nWidth );
+ else
+ rpLine->GuessLinesWidths(SvxBorderLineStyle::DOUBLE,
+ nOutWidth, nInWidth, nDistance);
+}
+
+const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushRepeat[] =
+{
+ { XML_REPEAT, GPOS_TILED },
+ { XML_BACKGROUND_NO_REPEAT, GPOS_MM },
+ { XML_STRETCH, GPOS_AREA },
+ { XML_TOKEN_INVALID, SvxGraphicPosition(0) }
+};
+
+const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushHoriPos[] =
+{
+ { XML_LEFT, GPOS_LM },
+ { XML_RIGHT, GPOS_RM },
+ { XML_TOKEN_INVALID, SvxGraphicPosition(0) }
+};
+
+const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushVertPos[] =
+{
+ { XML_TOP, GPOS_MT },
+ { XML_BOTTOM, GPOS_MB },
+ { XML_TOKEN_INVALID, SvxGraphicPosition(0) }
+};
+
+void sw_frmitems_MergeXMLHoriPos( SvxGraphicPosition& ePos,
+ SvxGraphicPosition eHori )
+{
+ OSL_ENSURE( GPOS_LM==eHori || GPOS_MM==eHori || GPOS_RM==eHori,
+ "sw_frmitems_MergeXMLHoriPos: vertical pos must be middle" );
+
+ switch( ePos )
+ {
+ case GPOS_LT:
+ case GPOS_MT:
+ case GPOS_RT:
+ ePos = GPOS_LM==eHori ? GPOS_LT : (GPOS_MM==eHori ? GPOS_MT : GPOS_RT);
+ break;
+
+ case GPOS_LM:
+ case GPOS_MM:
+ case GPOS_RM:
+ ePos = eHori;
+ break;
+
+ case GPOS_LB:
+ case GPOS_MB:
+ case GPOS_RB:
+ ePos = GPOS_LM==eHori ? GPOS_LB : (GPOS_MM==eHori ? GPOS_MB : GPOS_RB);
+ break;
+ default:
+ ;
+ }
+}
+
+void sw_frmitems_MergeXMLVertPos( SvxGraphicPosition& ePos,
+ SvxGraphicPosition eVert )
+{
+ OSL_ENSURE( GPOS_MT==eVert || GPOS_MM==eVert || GPOS_MB==eVert,
+ "sw_frmitems_MergeXMLVertPos: horizontal pos must be middle" );
+
+ switch( ePos )
+ {
+ case GPOS_LT:
+ case GPOS_LM:
+ case GPOS_LB:
+ ePos = GPOS_MT==eVert ? GPOS_LT : (GPOS_MM==eVert ? GPOS_LM : GPOS_LB);
+ break;
+
+ case GPOS_MT:
+ case GPOS_MM:
+ case GPOS_MB:
+ ePos = eVert;
+ break;
+
+ case GPOS_RT:
+ case GPOS_RM:
+ case GPOS_RB:
+ ePos = GPOS_MT==eVert ? GPOS_RT : (GPOS_MM==eVert ? GPOS_RM : GPOS_RB);
+ break;
+ default:
+ ;
+ }
+}
+
+const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BreakType[] =
+{
+ { XML_AUTO, 0 },
+ { XML_COLUMN, 1 },
+ { XML_PAGE, 2 },
+ { XML_EVEN_PAGE, 2 },
+ { XML_ODD_PAGE, 2 },
+ { XML_TOKEN_INVALID, 0}
+};
+
+const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableAlignMap[] =
+{
+ { XML_LEFT, text::HoriOrientation::LEFT },
+ { XML_LEFT, text::HoriOrientation::LEFT_AND_WIDTH },
+ { XML_CENTER, text::HoriOrientation::CENTER },
+ { XML_RIGHT, text::HoriOrientation::RIGHT },
+ { XML_MARGINS, text::HoriOrientation::FULL },
+ { XML_MARGINS, text::HoriOrientation::NONE },
+ { XML_TOKEN_INVALID, 0 }
+};
+
+const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableVAlignMap[] =
+{
+ { XML_TOP, text::VertOrientation::TOP },
+ { XML_MIDDLE, text::VertOrientation::CENTER },
+ { XML_BOTTOM, text::VertOrientation::BOTTOM },
+ { XML_TOKEN_INVALID, 0 }
+};
+
+const struct SvXMLEnumMapEntry<sal_uInt16> aXML_KeepTogetherType[] =
+{
+ { XML_ALWAYS, 0 },
+ { XML_AUTO, 1 },
+ { XML_TOKEN_INVALID, 0}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlithlp.hxx b/sw/source/filter/xml/xmlithlp.hxx
new file mode 100644
index 0000000000..99d3acd21d
--- /dev/null
+++ b/sw/source/filter/xml/xmlithlp.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLITHLP_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITHLP_HXX
+
+#include <sal/types.h>
+#include <xmloff/xmlement.hxx>
+#include <editeng/brushitem.hxx>
+
+namespace editeng { class SvxBorderLine; }
+
+template<typename EnumT> struct SvXMLEnumMapEntry;
+class SvXMLUnitConverter;
+class Color;
+
+/** Define various helper variables and functions for xmlimpit.cxx and
+ * xmlexpit.cxx. */
+bool sw_frmitems_parseXMLBorder( std::u16string_view rValue,
+ const SvXMLUnitConverter& rUnitConverter,
+ bool& rHasStyle, sal_uInt16& rStyle,
+ bool& rHasWidth, sal_uInt16& rWidth,
+ sal_uInt16& rNamedWidth,
+ bool& rHasColor, Color& rColor );
+
+bool sw_frmitems_setXMLBorder( std::unique_ptr<editeng::SvxBorderLine>& rpLine,
+ bool bHasStyle, sal_uInt16 nStyle,
+ bool bHasWidth, sal_uInt16 nWidth,
+ sal_uInt16 nNamedWidth,
+ bool bHasColor, const Color& rColor );
+
+void sw_frmitems_setXMLBorder( std::unique_ptr<editeng::SvxBorderLine>& rpLine,
+ sal_uInt16 nWidth, sal_uInt16 nOutWidth,
+ sal_uInt16 nInWidth, sal_uInt16 nDistance );
+
+void sw_frmitems_MergeXMLHoriPos( SvxGraphicPosition& ePos,
+ SvxGraphicPosition eHori );
+
+void sw_frmitems_MergeXMLVertPos( SvxGraphicPosition& ePos,
+ SvxGraphicPosition eVert );
+
+extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BorderStyles[];
+extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[];
+extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushRepeat[];
+extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushHoriPos[];
+extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushVertPos[];
+extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BreakType[];
+extern const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableAlignMap[];
+extern const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableVAlignMap[];
+extern const struct SvXMLEnumMapEntry<sal_uInt16> aXML_KeepTogetherType[];
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlitmap.hxx b/sw/source/filter/xml/xmlitmap.hxx
new file mode 100644
index 0000000000..5c7f731cc1
--- /dev/null
+++ b/sw/source/filter/xml/xmlitmap.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX
+
+#include <sal/types.h>
+#include <tools/ref.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <memory>
+#include <span>
+
+#define MID_SW_FLAG_MASK 0x0000ffff
+
+// this flags are used in the item mapper for import and export
+#define MID_SW_FLAG_SPECIAL_ITEM_IMPORT 0x80000000
+#define MID_SW_FLAG_NO_ITEM_IMPORT 0x40000000
+#define MID_SW_FLAG_SPECIAL_ITEM_EXPORT 0x20000000
+#define MID_SW_FLAG_NO_ITEM_EXPORT 0x10000000
+#define MID_SW_FLAG_ELEMENT_ITEM_IMPORT 0x08000000
+#define MID_SW_FLAG_ELEMENT_ITEM_EXPORT 0x04000000
+#define MID_SW_FLAG_ELEMENT_ITEM 0x0c000000 // both import and export
+
+struct SvXMLItemMapEntry
+{
+ sal_uInt16 nNameSpace; // declares the Namespace in which this item
+ // exists
+ sal_uInt16 nWhichId; // the WhichId to identify the item
+ // in the pool
+ enum ::xmloff::token::XMLTokenEnum const eLocalName;
+ // the local name for the item inside
+ // the Namespace (as an XMLTokenEnum)
+ sal_uInt32 nMemberId; // the memberid specifies which part
+ // of the item should be imported or
+ // exported with this Namespace
+ // and localName
+ SvXMLItemMapEntry(
+ sal_uInt16 nameSpace,
+ enum ::xmloff::token::XMLTokenEnum localName,
+ sal_uInt16 whichId,
+ sal_uInt32 memberId)
+ : nNameSpace(nameSpace), nWhichId(whichId), eLocalName(localName), nMemberId(memberId) {}
+};
+
+/** this class manages an array of SvXMLItemMapEntry. It is
+ used for optimizing the static array on startup of import
+ or export */
+class SvXMLItemMapEntries final : public SvRefBase
+{
+ std::span<SvXMLItemMapEntry const> mpEntries;
+
+public:
+ explicit SvXMLItemMapEntries(std::span<SvXMLItemMapEntry const> pEntries) : mpEntries(pEntries) {}
+ virtual ~SvXMLItemMapEntries() override;
+
+ SvXMLItemMapEntry const * getByName( sal_Int32 nElement ) const;
+ SvXMLItemMapEntry const & getByIndex( sal_uInt16 nIndex ) const { return mpEntries[nIndex]; }
+
+ sal_uInt16 getCount() const { return mpEntries.size(); }
+};
+
+typedef tools::SvRef<SvXMLItemMapEntries> SvXMLItemMapEntriesRef;
+
+extern const std::span<SvXMLItemMapEntry const> aXMLTableItemMap;
+extern const std::span<SvXMLItemMapEntry const> aXMLTableColItemMap;
+extern const std::span<SvXMLItemMapEntry const> aXMLTableRowItemMap;
+extern const std::span<SvXMLItemMapEntry const> aXMLTableCellItemMap;
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlitmpr.cxx b/sw/source/filter/xml/xmlitmpr.cxx
new file mode 100644
index 0000000000..e7cf7ec90d
--- /dev/null
+++ b/sw/source/filter/xml/xmlitmpr.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlitmap.hxx"
+#include <xmloff/xmlimp.hxx>
+
+SvXMLItemMapEntries::~SvXMLItemMapEntries()
+{
+}
+
+SvXMLItemMapEntry const * SvXMLItemMapEntries::getByName( sal_Int32 nElement ) const
+{
+ for (const SvXMLItemMapEntry& rEntry : mpEntries)
+ {
+ if( IsTokenInNamespace(nElement, rEntry.nNameSpace) &&
+ (nElement & TOKEN_MASK) == rEntry.eLocalName )
+ return &rEntry;
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlmeta.cxx b/sw/source/filter/xml/xmlmeta.cxx
new file mode 100644
index 0000000000..925fea5d9a
--- /dev/null
+++ b/sw/source/filter/xml/xmlmeta.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <osl/diagnose.h>
+#include <xmloff/xmlmetai.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <xmloff/xmltkmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <docstat.hxx>
+#include <doc.hxx>
+#include <IDocumentStatistics.hxx>
+#include "xmlimp.hxx"
+#include "xmlexp.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::text;
+using namespace ::xmloff::token;
+
+uno::Reference<document::XDocumentProperties>
+SwXMLImport::GetDocumentProperties() const
+{
+ if (m_bOrganizerMode || IsStylesOnlyMode() ||
+ IsBlockMode() || IsInsertMode())
+ {
+ return nullptr;
+ }
+ uno::Reference<document::XDocumentPropertiesSupplier> const xDPS(
+ GetModel(), UNO_QUERY_THROW);
+ return xDPS->getDocumentProperties();
+}
+
+SvXMLImportContext *SwXMLImport::CreateMetaContext(
+ const sal_Int32 /*nElement*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if (getImportFlags() & SvXMLImportFlags::META)
+ {
+ uno::Reference<document::XDocumentProperties> const xDocProps(
+ GetDocumentProperties());
+ pContext = new SvXMLMetaDocumentContext(*this, xDocProps);
+ }
+
+ return pContext;
+}
+
+namespace {
+
+enum SvXMLTokenMapAttrs
+{
+ XML_TOK_META_STAT_TABLE = 1,
+ XML_TOK_META_STAT_IMAGE = 2,
+ XML_TOK_META_STAT_OLE = 4,
+ XML_TOK_META_STAT_PAGE = 8,
+ XML_TOK_META_STAT_PARA = 16,
+ XML_TOK_META_STAT_WORD = 32,
+ XML_TOK_META_STAT_CHAR = 64,
+ XML_TOK_META_STAT_NON_WHITE_SPACE_CHAR = 128,
+ XML_TOK_META_STAT_END=XML_TOK_UNKNOWN
+};
+
+struct statistic {
+ SvXMLTokenMapAttrs token;
+ const char* name;
+ sal_uInt16 SwDocStat::* target16;
+ sal_uLong SwDocStat::* target32; /* or 64, on LP64 platforms */
+};
+
+}
+
+const struct statistic s_stats [] = {
+ { XML_TOK_META_STAT_TABLE, "TableCount", &SwDocStat::nTable, nullptr },
+ { XML_TOK_META_STAT_IMAGE, "ImageCount", &SwDocStat::nGrf, nullptr },
+ { XML_TOK_META_STAT_OLE, "ObjectCount", &SwDocStat::nOLE, nullptr },
+ { XML_TOK_META_STAT_PAGE, "PageCount", nullptr, &SwDocStat::nPage },
+ { XML_TOK_META_STAT_PARA, "ParagraphCount", nullptr, &SwDocStat::nPara },
+ { XML_TOK_META_STAT_WORD, "WordCount", nullptr, &SwDocStat::nWord },
+ { XML_TOK_META_STAT_CHAR, "CharacterCount", nullptr, &SwDocStat::nChar },
+ { XML_TOK_META_STAT_NON_WHITE_SPACE_CHAR, "NonWhitespaceCharacterCount", nullptr, &SwDocStat::nCharExcludingSpaces },
+ { XML_TOK_META_STAT_END, nullptr, nullptr, nullptr }
+};
+
+void SwXMLImport::SetStatistics(
+ const Sequence< beans::NamedValue > & i_rStats)
+{
+ if( IsStylesOnlyMode() || IsInsertMode() )
+ return;
+
+ SvXMLImport::SetStatistics(i_rStats);
+
+ SwDoc *pDoc = getDoc();
+ SwDocStat aDocStat( pDoc->getIDocumentStatistics().GetDocStat() );
+
+ sal_uInt32 nTokens = 0;
+
+ for (const auto& rStat : i_rStats) {
+ for (struct statistic const* pStat = s_stats; pStat->name != nullptr;
+ ++pStat) {
+ if (rStat.Name.equalsAscii(pStat->name)) {
+ sal_Int32 val = 0;
+ if (rStat.Value >>= val) {
+ if (pStat->target16 != nullptr) {
+ aDocStat.*(pStat->target16)
+ = o3tl::narrowing<sal_uInt16> (val);
+ } else {
+ aDocStat.*(pStat->target32)
+ = static_cast<sal_uInt32> (val);
+ }
+ nTokens |= pStat->token;
+ } else {
+ OSL_FAIL("SwXMLImport::SetStatistics: invalid entry");
+ }
+ }
+ }
+ }
+
+ if( nTokens )
+ pDoc->getIDocumentStatistics().SetDocStat( aDocStat );
+
+ // set progress bar reference to #paragraphs. If not available,
+ // use #pages*10, or guesstimate 250 paragraphs. Additionally
+ // guesstimate PROGRESS_BAR_STEPS each for meta+settings, styles,
+ // and autostyles.
+ bool bSetFallback = true;
+ sal_Int32 nProgressReference = sal_Int32(); // silence C4701
+ const sal_Int32 nProgressReferenceWriggleRoom = 3 * PROGRESS_BAR_STEP;
+ if (nTokens & XML_TOK_META_STAT_PARA)
+ {
+ nProgressReference = static_cast<sal_Int32>(aDocStat.nPara);
+ bSetFallback = false;
+ }
+ else if (nTokens & XML_TOK_META_STAT_PAGE)
+ bSetFallback = o3tl::checked_multiply<sal_Int32>(aDocStat.nPage, 10, nProgressReference);
+ if (!bSetFallback)
+ bSetFallback = o3tl::checked_add(nProgressReference, nProgressReferenceWriggleRoom, nProgressReference);
+ if (bSetFallback)
+ nProgressReference = 250 + nProgressReferenceWriggleRoom;
+ ProgressBarHelper* pProgress = GetProgressBarHelper();
+ pProgress->SetReference(nProgressReference);
+ pProgress->SetValue( 0 );
+}
+
+void SwXMLExport::ExportMeta_()
+{
+ SvXMLExport::ExportMeta_();
+
+ if( !m_bBlock && IsShowProgress() )
+ {
+ ProgressBarHelper *pProgress = GetProgressBarHelper();
+ pProgress->SetValue( pProgress->GetValue() + 2 );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmlscript.cxx b/sw/source/filter/xml/xmlscript.cxx
new file mode 100644
index 0000000000..07a94e8a5a
--- /dev/null
+++ b/sw/source/filter/xml/xmlscript.cxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xmloff/xmlscripti.hxx>
+#include "xmlimp.hxx"
+
+using namespace ::com::sun::star;
+
+SvXMLImportContext* SwXMLImport::CreateScriptContext()
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ if (!(IsStylesOnlyMode() || IsInsertMode()))
+ {
+ pContext = new XMLScriptContext(*this, GetModel());
+ }
+
+ return pContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltble.cxx b/sw/source/filter/xml/xmltble.cxx
new file mode 100644
index 0000000000..1341bbbe37
--- /dev/null
+++ b/sw/source/filter/xml/xmltble.cxx
@@ -0,0 +1,1266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/text/XTextTable.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+
+#include <hintids.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/numehelp.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <fmtrowsplt.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <swtable.hxx>
+#include <doc.hxx>
+#include <frmfmt.hxx>
+#include <wrtswtbl.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <cellatr.hxx>
+#include <ddefld.hxx>
+#include <swddetbl.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <unotbl.hxx>
+#include "xmltexte.hxx"
+#include "xmlexp.hxx"
+#include <o3tl/any.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <textboxhelper.hxx>
+#include <SwStyleNameMapper.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::xmloff::token;
+using table::XCell;
+using std::vector;
+using std::advance;
+
+
+class SwXMLTableColumn_Impl : public SwWriteTableCol
+{
+ OUString m_sStyleName;
+ sal_uInt32 m_nRelWidth;
+
+public:
+
+ explicit SwXMLTableColumn_Impl(sal_uInt32 nPosition)
+ : SwWriteTableCol(nPosition)
+ , m_nRelWidth(0)
+ {};
+
+ void SetStyleName( const OUString& rName ) { m_sStyleName = rName; }
+ const OUString& GetStyleName() const { return m_sStyleName; }
+
+ void SetRelWidth( sal_uInt32 nSet ) { m_nRelWidth = nSet; }
+ sal_uInt32 GetRelWidth() const { return m_nRelWidth; }
+};
+
+namespace {
+
+struct SwXMLTableColumnCmpWidth_Impl
+{
+ bool operator()( SwXMLTableColumn_Impl* const& lhs, SwXMLTableColumn_Impl* const& rhs ) const
+ {
+ sal_Int32 n = static_cast<sal_Int32>(lhs->GetWidthOpt()) - static_cast<sal_Int32>(rhs->GetWidthOpt());
+ if( !n )
+ n = static_cast<sal_Int32>(lhs->GetRelWidth()) - static_cast<sal_Int32>(rhs->GetRelWidth());
+ return n < 0;
+ }
+};
+
+class SwXMLTableColumns_Impl : public o3tl::sorted_vector<std::unique_ptr<SwXMLTableColumn_Impl>, o3tl::less_uniqueptr_to<SwXMLTableColumn_Impl> > {
+};
+
+}
+
+class SwXMLTableColumnsSortByWidth_Impl : public o3tl::sorted_vector<SwXMLTableColumn_Impl*, SwXMLTableColumnCmpWidth_Impl> {};
+
+class SwXMLTableLines_Impl
+{
+ SwXMLTableColumns_Impl m_aCols;
+ const SwTableLines *m_pLines;
+ sal_uInt32 m_nWidth;
+
+public:
+
+ explicit SwXMLTableLines_Impl( const SwTableLines& rLines );
+
+ sal_uInt32 GetWidth() const { return m_nWidth; }
+ const SwTableLines *GetLines() const { return m_pLines; }
+
+ const SwXMLTableColumns_Impl& GetColumns() const { return m_aCols; }
+};
+
+SwXMLTableLines_Impl::SwXMLTableLines_Impl( const SwTableLines& rLines ) :
+ m_pLines( &rLines ),
+ m_nWidth( 0 )
+{
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nEndCPos = 0U;
+#endif
+ const size_t nLines = rLines.size();
+ for( size_t nLine=0U; nLine<nLines; ++nLine )
+ {
+ const SwTableLine *pLine = rLines[nLine];
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+
+ sal_uInt32 nCPos = 0U;
+ for( size_t nBox=0U; nBox<nBoxes; ++nBox )
+ {
+ const SwTableBox *pBox = rBoxes[nBox];
+
+ if( nBox < nBoxes-1U || m_nWidth==0 )
+ {
+ nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
+ std::unique_ptr<SwXMLTableColumn_Impl> pCol(
+ new SwXMLTableColumn_Impl( nCPos ));
+
+ m_aCols.insert( std::move(pCol) );
+
+ if( nBox==nBoxes-1U )
+ {
+ OSL_ENSURE( nLine==0U && m_nWidth==0,
+ "parent width will be lost" );
+ m_nWidth = nCPos;
+ }
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nCheckPos =
+ nCPos + SwWriteTable::GetBoxWidth( pBox );
+ if( !nEndCPos )
+ {
+ nEndCPos = nCheckPos;
+ }
+#endif
+ nCPos = m_nWidth;
+#if OSL_DEBUG_LEVEL > 0
+ SwXMLTableColumn_Impl aCol( m_nWidth );
+ OSL_ENSURE( m_aCols.find(&aCol) != m_aCols.end(), "couldn't find last column" );
+ OSL_ENSURE( SwXMLTableColumn_Impl(nCheckPos) ==
+ SwXMLTableColumn_Impl(nCPos),
+ "rows have different total widths" );
+#endif
+ }
+ }
+ }
+}
+
+typedef vector< SwFrameFormat* > SwXMLFrameFormats_Impl;
+
+class SwXMLTableFrameFormatsSort_Impl
+{
+private:
+ SwXMLFrameFormats_Impl m_aFormatList;
+ SwXMLTextParagraphExport::FormatMap & m_rFormatMap;
+
+public:
+ SwXMLTableFrameFormatsSort_Impl(SwXMLTextParagraphExport::FormatMap & rFormatMap)
+ : m_rFormatMap(rFormatMap)
+ {}
+ ::std::optional<OUString> AddRow(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix, sal_uInt32 nLine );
+ ::std::optional<OUString> AddCell(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix,
+ sal_uInt32 nCol, sal_uInt32 nRow, bool bTop );
+};
+
+::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddRow(SwFrameFormat& rFrameFormat,
+ std::u16string_view rNamePrefix,
+ sal_uInt32 nLine )
+{
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+
+ const SwFormatFrameSize *pFrameSize = rItemSet.GetItemIfSet( RES_FRM_SIZE, false );
+ const SwFormatRowSplit* pRowSplit = rItemSet.GetItemIfSet( RES_ROW_SPLIT, false );
+ const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
+ const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
+
+ // empty styles have not to be exported
+ if( !pFrameSize && !pBrush && !pRowSplit && !pHasTextChangesOnly )
+ {
+ m_rFormatMap.try_emplace(&rFrameFormat); // empty just to enable assert
+ return {};
+ }
+
+ // order is: -/brush, size/-, size/brush
+ SwXMLFrameFormats_Impl::iterator i;
+ for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i )
+ {
+ const SwFormatFrameSize *pTestFrameSize = nullptr;
+ const SwFormatRowSplit* pTestRowSplit = nullptr;
+ const SvxBrushItem *pTestBrush = nullptr;
+ const SvxPrintItem *pTestHasTextChangesOnly = nullptr;
+ const SwFrameFormat *pTestFormat = *i;
+ const SfxItemSet& rTestSet = pTestFormat->GetAttrSet();
+ if( const SwFormatFrameSize* pItem = rTestSet.GetItemIfSet( RES_FRM_SIZE, false ) )
+ {
+ if( !pFrameSize )
+ break;
+
+ pTestFrameSize = pItem;
+ }
+ else
+ {
+ if( pFrameSize )
+ continue;
+ }
+
+ if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false) )
+ {
+ if( !pBrush )
+ break;
+
+ pTestBrush = pItem;
+ }
+ else
+ {
+ if( pBrush )
+ continue;
+ }
+
+ if( const SwFormatRowSplit* pItem = rTestSet.GetItemIfSet( RES_ROW_SPLIT, false ) )
+ {
+ if( !pRowSplit )
+ break;
+
+ pTestRowSplit = pItem;
+ }
+ else
+ {
+ if( pRowSplit )
+ continue;
+ }
+
+ if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) )
+ {
+ if( !pHasTextChangesOnly )
+ break;
+
+ pTestHasTextChangesOnly = pItem;
+ }
+ else
+ {
+ if( pHasTextChangesOnly )
+ continue;
+ }
+
+ if( pFrameSize &&
+ ( pFrameSize->GetHeightSizeType() != pTestFrameSize->GetHeightSizeType() ||
+ pFrameSize->GetHeight() != pTestFrameSize->GetHeight() ) )
+ continue;
+
+ if( pBrush && (*pBrush != *pTestBrush) )
+ continue;
+
+ if( pRowSplit && (!pRowSplit->GetValue() != !pTestRowSplit->GetValue()) )
+ continue;
+
+ if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) )
+ continue;
+
+ // found!
+ auto const oName(m_rFormatMap.find(pTestFormat)->second);
+ assert(oName);
+ m_rFormatMap.try_emplace(&rFrameFormat, oName);
+ return {};
+ }
+
+ {
+ OUString const name(OUString::Concat(rNamePrefix) + "." + OUString::number(nLine+1));
+ m_rFormatMap.try_emplace(&rFrameFormat, name);
+ if ( i != m_aFormatList.end() ) ++i;
+ m_aFormatList.insert( i, &rFrameFormat );
+ return ::std::optional<OUString>(name);
+ }
+}
+
+static OUString lcl_xmltble_appendBoxPrefix(std::u16string_view rNamePrefix,
+ sal_uInt32 nCol, sal_uInt32 nRow, bool bTop )
+{
+ if( bTop )
+ {
+ OUString sTmp;
+ sw_GetTableBoxColStr( o3tl::narrowing<sal_uInt16>(nCol), sTmp );
+ return OUString::Concat(rNamePrefix) + "." + sTmp + OUString::number(nRow + 1);
+ }
+ return OUString::Concat(rNamePrefix)
+ + "." + OUString::number(nCol + 1)
+ + "." + OUString::number(nRow + 1);
+}
+
+::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddCell(SwFrameFormat& rFrameFormat,
+ std::u16string_view rNamePrefix,
+ sal_uInt32 nCol, sal_uInt32 nRow, bool bTop )
+{
+ const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
+ const SwFormatVertOrient *pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT, false );
+ const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
+ const SvxBoxItem *pBox = rItemSet.GetItemIfSet( RES_BOX, false );
+ const SwTableBoxNumFormat *pNumFormat = rItemSet.GetItemIfSet( RES_BOXATR_FORMAT,
+ false );
+ const SvxFrameDirectionItem *pFrameDir = rItemSet.GetItemIfSet( RES_FRAMEDIR,
+ false );
+ const SvXMLAttrContainerItem *pAttCnt = rItemSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER,
+ false );
+ const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
+
+ // empty styles have not to be exported
+ if( !pVertOrient && !pBrush && !pBox && !pNumFormat && !pFrameDir && !pAttCnt &&
+ !pHasTextChangesOnly )
+ {
+ m_rFormatMap.try_emplace(&rFrameFormat); // empty just to enable assert
+ return {};
+ }
+
+ // order is: -/-/-/num,
+ // -/-/box/-, -/-/box/num,
+ // -/brush/-/-, -/brush/-/num, -/brush/box/-, -/brush/box/num,
+ // vert/-/-/-, vert/-/-/num, vert/-/box/-, ver/-/box/num,
+ // vert/brush/-/-, vert/brush/-/num, vert/brush/box/-,
+ // vert/brush/box/num
+ SwXMLFrameFormats_Impl::iterator i;
+ for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i )
+ {
+ const SwFormatVertOrient *pTestVertOrient = nullptr;
+ const SvxBrushItem *pTestBrush = nullptr;
+ const SvxBoxItem *pTestBox = nullptr;
+ const SwTableBoxNumFormat *pTestNumFormat = nullptr;
+ const SvxFrameDirectionItem *pTestFrameDir = nullptr;
+ const SvXMLAttrContainerItem *pTestAttCnt = nullptr;
+ const SvxPrintItem *pTestHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
+ const SwFrameFormat* pTestFormat = *i;
+ const SfxItemSet& rTestSet = pTestFormat->GetAttrSet();
+ if( const SwFormatVertOrient* pItem = rTestSet.GetItemIfSet( RES_VERT_ORIENT, false ) )
+ {
+ if( !pVertOrient )
+ break;
+
+ pTestVertOrient = pItem;
+ }
+ else
+ {
+ if( pVertOrient )
+ continue;
+ }
+
+ if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false ) )
+ {
+ if( !pBrush )
+ break;
+
+ pTestBrush = pItem;
+ }
+ else
+ {
+ if( pBrush )
+ continue;
+ }
+
+ if( const SvxBoxItem* pItem = rTestSet.GetItemIfSet( RES_BOX, false ) )
+ {
+ if( !pBox )
+ break;
+
+ pTestBox = pItem;
+ }
+ else
+ {
+ if( pBox )
+ continue;
+ }
+
+ if ( const SwTableBoxNumFormat* pItem = rTestSet.GetItemIfSet( RES_BOXATR_FORMAT,
+ false ) )
+ {
+ if( !pNumFormat )
+ break;
+
+ pTestNumFormat = pItem;
+ }
+ else
+ {
+ if( pNumFormat )
+ continue;
+
+ }
+
+ if ( const SvxFrameDirectionItem* pItem = rTestSet.GetItemIfSet( RES_FRAMEDIR,
+ false ) )
+ {
+ if( !pFrameDir )
+ break;
+
+ pTestFrameDir = pItem;
+ }
+ else
+ {
+ if( pFrameDir )
+ continue;
+
+ }
+
+ if ( const SvXMLAttrContainerItem* pItem = rTestSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER,
+ false ) )
+ {
+ if( !pAttCnt )
+ break;
+
+ pTestAttCnt = pItem;
+ }
+ else
+ {
+ if ( pAttCnt )
+ continue;
+
+ }
+
+ if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) )
+ {
+ if( !pHasTextChangesOnly )
+ break;
+
+ pTestHasTextChangesOnly = pItem;
+ }
+ else
+ {
+ if( pHasTextChangesOnly )
+ continue;
+ }
+
+ if( pVertOrient &&
+ pVertOrient->GetVertOrient() != pTestVertOrient->GetVertOrient() )
+ continue;
+
+ if( pBrush && ( *pBrush != *pTestBrush ) )
+ continue;
+
+ if( pBox && ( *pBox != *pTestBox ) )
+ continue;
+
+ if( pNumFormat && pNumFormat->GetValue() != pTestNumFormat->GetValue() )
+ continue;
+
+ if( pFrameDir && pFrameDir->GetValue() != pTestFrameDir->GetValue() )
+ continue;
+
+ if( pAttCnt && ( *pAttCnt != *pTestAttCnt ) )
+ continue;
+
+ if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) )
+ continue;
+
+ // found!
+ auto const oName(m_rFormatMap.find(pTestFormat)->second);
+ assert(oName);
+ m_rFormatMap.try_emplace(&rFrameFormat, oName);
+ return {};
+ }
+
+ {
+ OUString const name(lcl_xmltble_appendBoxPrefix(rNamePrefix, nCol, nRow, bTop));
+ m_rFormatMap.try_emplace(&rFrameFormat, name);
+ if ( i != m_aFormatList.end() ) ++i;
+ m_aFormatList.insert( i, &rFrameFormat );
+ return ::std::optional<OUString>(name);
+ }
+}
+
+class SwXMLTableInfo_Impl
+{
+ const SwTable *m_pTable;
+ Reference<XTextSection> m_xBaseSection;
+ bool m_bBaseSectionValid;
+ sal_uInt32 m_nPrefix;
+ SwXMLTextParagraphExport::FormatMap const& m_rLineFormats;
+ SwXMLTextParagraphExport::FormatMap const& m_rBoxFormats;
+
+public:
+
+ inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix,
+ SwXMLTextParagraphExport::FormatMap const& rLineFormats,
+ SwXMLTextParagraphExport::FormatMap const& rBoxFormats)
+ : m_pTable(pTable)
+ , m_bBaseSectionValid(false)
+ , m_nPrefix(nPrefix)
+ , m_rLineFormats(rLineFormats)
+ , m_rBoxFormats(rBoxFormats)
+ {
+ }
+
+ const SwTable *GetTable() const { return m_pTable; }
+ const SwFrameFormat *GetTableFormat() const { return m_pTable->GetFrameFormat(); }
+
+ bool IsBaseSectionValid() const { return m_bBaseSectionValid; }
+ const Reference<XTextSection>& GetBaseSection() const { return m_xBaseSection; }
+ inline void SetBaseSection( const Reference < XTextSection >& rBase );
+ /// The namespace (table or loext) that should be used for the elements.
+ sal_uInt16 GetPrefix() const { return m_nPrefix; }
+ SwXMLTextParagraphExport::FormatMap const& GetLineFormats() const { return m_rLineFormats; }
+ SwXMLTextParagraphExport::FormatMap const& GetBoxFormats() const { return m_rBoxFormats; }
+};
+
+inline void SwXMLTableInfo_Impl::SetBaseSection(
+ const Reference < XTextSection >& rBaseSection )
+{
+ m_xBaseSection = rBaseSection;
+ m_bBaseSectionValid = true;
+}
+
+void SwXMLExport::ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol )
+{
+ // <style:style ...>
+ CheckAttrList();
+
+ // style:name="..."
+ bool bEncoded = false;
+ AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
+ EncodeStyleName( rCol.GetStyleName(), &bEncoded ) );
+ if( bEncoded )
+ AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCol.GetStyleName() );
+
+ // style:family="table-column"
+ AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE_COLUMN );
+
+ {
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, true,
+ true );
+ if( rCol.GetWidthOpt() )
+ {
+ OUStringBuffer sValue;
+ GetTwipUnitConverter().convertMeasureToXML( sValue,
+ rCol.GetWidthOpt() );
+ AddAttribute( XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH,
+ sValue.makeStringAndClear() );
+ }
+ if( rCol.GetRelWidth() )
+ {
+ OUString sValue = OUString::number(static_cast<sal_Int32>(rCol.GetRelWidth()) ) + "*";
+ AddAttribute( XML_NAMESPACE_STYLE, XML_REL_COLUMN_WIDTH,
+ sValue );
+ }
+
+ {
+ SvXMLElementExport aElemExport( *this, XML_NAMESPACE_STYLE,
+ XML_TABLE_COLUMN_PROPERTIES,
+ true, true );
+ }
+ }
+}
+
+void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines,
+ sal_uInt32 nAbsWidth, sal_uInt32 nBaseWidth,
+ std::u16string_view rNamePrefix,
+ SwXMLTableColumnsSortByWidth_Impl& rExpCols,
+ SwXMLTableFrameFormatsSort_Impl& rExpRows,
+ SwXMLTableFrameFormatsSort_Impl& rExpCells,
+ SwXMLTableInfo_Impl& rTableInfo,
+ bool bTop )
+{
+ // pass 1: calculate columns
+ SwXMLTableLines_Impl *pLines = new SwXMLTableLines_Impl( rLines );
+ if( !m_pTableLines )
+ m_pTableLines.reset(new SwXMLTableLinesCache_Impl);
+
+ m_pTableLines->push_back( pLines );
+
+ // pass 2: export column styles
+ {
+ const SwXMLTableColumns_Impl& rCols = pLines->GetColumns();
+ sal_uInt32 nCPos = 0U;
+ const size_t nColumns = rCols.size();
+ for( size_t nColumn=0U; nColumn<nColumns; ++nColumn )
+ {
+ SwXMLTableColumn_Impl *pColumn = rCols[nColumn].get();
+
+ sal_uInt32 nOldCPos = nCPos;
+ nCPos = pColumn->GetPos();
+
+ sal_uInt32 nWidth = nCPos - nOldCPos;
+
+ // If a base width is given, the table has either an automatic
+ // or margin alignment, or a percentage width. In either case,
+ // relative widths should be exported.
+ if( nBaseWidth )
+ {
+ pColumn->SetRelWidth( nWidth );
+ }
+
+ // If an absolute width is given, the table either has a fixed
+ // width, or the current width is known from the layout. In the
+ // later case, a base width is set in addition and must be used
+ // to "absolutize" the relative column width.
+ if( nAbsWidth )
+ {
+ sal_uInt32 nColAbsWidth = nWidth;
+ if( nBaseWidth )
+ {
+ nColAbsWidth *= nAbsWidth;
+ nColAbsWidth += (nBaseWidth/2UL);
+ nColAbsWidth /= nBaseWidth;
+ }
+ pColumn->SetWidthOpt( nColAbsWidth, false );
+ }
+
+ SwXMLTableColumnsSortByWidth_Impl::const_iterator it = rExpCols.find( pColumn );
+ if( it != rExpCols.end() )
+ {
+ pColumn->SetStyleName( (*it)->GetStyleName() );
+ }
+ else
+ {
+ if( bTop )
+ {
+ OUString sTmp;
+ sw_GetTableBoxColStr( nColumn, sTmp );
+ pColumn->SetStyleName( OUString::Concat(rNamePrefix) + "." + sTmp );
+ }
+ else
+ {
+ pColumn->SetStyleName(
+ OUString::Concat(rNamePrefix) + "." + OUString::number(nColumn + 1U) );
+ }
+ ExportTableColumnStyle( *pColumn );
+ rExpCols.insert( pColumn );
+ }
+ }
+ }
+
+ // pass 3: export line/rows
+ const size_t nLines = rLines.size();
+ for( size_t nLine=0U; nLine<nLines; ++nLine )
+ {
+ SwTableLine *pLine = rLines[nLine];
+
+ SwFrameFormat *pFrameFormat = pLine->GetFrameFormat();
+ if (auto oNew = rExpRows.AddRow(*pFrameFormat, rNamePrefix, nLine))
+ {
+ ExportFormat(*pFrameFormat, XML_TABLE_ROW, std::move(oNew));
+ }
+
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+
+ sal_uInt32 nCPos = 0U;
+ size_t nCol = 0U;
+ for( size_t nBox=0U; nBox<nBoxes; nBox++ )
+ {
+ SwTableBox *pBox = rBoxes[nBox];
+
+ if( nBox < nBoxes-1U )
+ nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
+ else
+ nCPos = pLines->GetWidth();
+
+ // and their index
+ const size_t nOldCol = nCol;
+ SwXMLTableColumn_Impl aCol( nCPos );
+ SwXMLTableColumns_Impl::const_iterator it = pLines->GetColumns().find( &aCol );
+ OSL_ENSURE( it != pLines->GetColumns().end(), "couldn't find column" );
+ nCol = it - pLines->GetColumns().begin();
+
+ const SwStartNode *pBoxSttNd = pBox->GetSttNd();
+ if( pBoxSttNd )
+ {
+ SwFrameFormat *pFrameFormat2 = pBox->GetFrameFormat();
+ if (auto oNew = rExpCells.AddCell(*pFrameFormat2, rNamePrefix, nOldCol, nLine,
+ bTop) )
+ {
+ ExportFormat(*pFrameFormat2, XML_TABLE_CELL, std::move(oNew));
+ }
+
+ rtl::Reference < SwXCell > xCell = SwXCell::CreateXCell(
+ const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()),
+ pBox,
+ const_cast<SwTable *>(rTableInfo.GetTable()) );
+ if (xCell.is())
+ {
+ if( !rTableInfo.IsBaseSectionValid() )
+ {
+ Any aAny = xCell->getPropertyValue("TextSection");
+ Reference < XTextSection > xTextSection;
+ aAny >>= xTextSection;
+ rTableInfo.SetBaseSection( xTextSection );
+ }
+
+ const bool bExportContent = bool(getExportFlags() & SvXMLExportFlags::CONTENT );
+ if ( !bExportContent )
+ {
+ // AUTOSTYLES - not needed anymore if we are currently exporting content.xml
+ GetTextParagraphExport()->collectTextAutoStyles(
+ xCell, rTableInfo.GetBaseSection(), IsShowProgress() );
+ }
+ }
+ else {
+ OSL_FAIL("here should be a XCell");
+ }
+ }
+ else
+ {
+ ExportTableLinesAutoStyles( pBox->GetTabLines(),
+ nAbsWidth, nBaseWidth,
+ lcl_xmltble_appendBoxPrefix( rNamePrefix,
+ nOldCol, nLine, bTop ),
+ rExpCols, rExpRows, rExpCells,
+ rTableInfo );
+ }
+
+ nCol++;
+ }
+ }
+}
+
+void SwXMLExport::ExportTableAutoStyles(const SwTableNode& rTableNd)
+{
+ auto & rFormats(static_cast<SwXMLTextParagraphExport *>(GetTextParagraphExport().get())->GetTableFormats());
+ auto const it(rFormats.find(&rTableNd));
+ assert(it != rFormats.end());
+ SwXMLTextParagraphExport::FormatMap & rRowFormats(it->second.first);
+ SwXMLTextParagraphExport::FormatMap & rBoxFormats(it->second.second);
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwFrameFormat *pTableFormat = rTable.GetFrameFormat();
+
+ if( !pTableFormat )
+ return;
+
+ sal_Int16 eTabHoriOri = pTableFormat->GetHoriOrient().GetHoriOrient();
+ const SwFormatFrameSize& rFrameSize = pTableFormat->GetFrameSize();
+
+ sal_uInt32 nAbsWidth = rFrameSize.GetSize().Width();
+ sal_uInt32 nBaseWidth = 0;
+ sal_Int8 nPercentWidth = rFrameSize.GetWidthPercent();
+
+ bool bFixAbsWidth = nPercentWidth != 0 || /*text::*/HoriOrientation::NONE == eTabHoriOri
+ || /*text::*/HoriOrientation::FULL == eTabHoriOri;
+ if( bFixAbsWidth )
+ {
+ nBaseWidth = nAbsWidth;
+ nAbsWidth = pTableFormat->FindLayoutRect(true).Width();
+ if( !nAbsWidth )
+ {
+ // TODO?
+ }
+ }
+ ExportTableFormat( *pTableFormat, nAbsWidth );
+
+ SwXMLTableColumnsSortByWidth_Impl aExpCols;
+ SwXMLTableFrameFormatsSort_Impl aExpRows(rRowFormats);
+ SwXMLTableFrameFormatsSort_Impl aExpCells(rBoxFormats);
+ SwXMLTableInfo_Impl aTableInfo(&rTable, XML_NAMESPACE_TABLE, rRowFormats, rBoxFormats);
+ ExportTableLinesAutoStyles( rTable.GetTabLines(), nAbsWidth, nBaseWidth,
+ pTableFormat->GetName(), aExpCols, aExpRows, aExpCells,
+ aTableInfo, true);
+
+}
+
+void SwXMLExport::ExportTableBox( const SwTableBox& rBox,
+ sal_uInt32 nColSpan,
+ sal_uInt32 nRowSpan,
+ SwXMLTableInfo_Impl& rTableInfo )
+{
+ const SwStartNode *pBoxSttNd = rBox.GetSttNd();
+ if( pBoxSttNd )
+ {
+ const SwFrameFormat *pFrameFormat = rBox.GetFrameFormat();
+ if( pFrameFormat )
+ {
+ auto const it(rTableInfo.GetBoxFormats().find(pFrameFormat));
+ assert(it != rTableInfo.GetBoxFormats().end());
+ if (it->second)
+ {
+ assert(!it->second->isEmpty());
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
+ }
+ }
+ }
+
+ if( nRowSpan != 1 )
+ {
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED,
+ OUString::number(nRowSpan) );
+ }
+
+ if( nColSpan != 1 )
+ {
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED,
+ OUString::number(nColSpan) );
+ }
+
+ {
+ if( pBoxSttNd )
+ {
+ // start node -> normal cell
+ // get cell range for table
+ rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell( const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()),
+ const_cast<SwTableBox *>(&rBox),
+ const_cast<SwTable *>(rTableInfo.GetTable()) );
+
+ if (xCell.is())
+ {
+ // get formula (and protection)
+ const OUString sCellFormula = xCell->getFormula();
+
+ // if this cell has a formula, export it
+ // (with value and number format)
+ if (!sCellFormula.isEmpty())
+ {
+ const OUString sQValue =
+ GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_OOOW, sCellFormula, false );
+ // formula
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue );
+ }
+
+ // value and format (if NumberFormat != -1)
+ sal_Int32 nNumberFormat = 0;
+ Any aAny = xCell->getPropertyValue("NumberFormat");
+ aAny >>= nNumberFormat;
+
+ if (static_cast<sal_Int32>(getSwDefaultTextFormat()) == nNumberFormat)
+ {
+ // text format
+ AddAttribute( XML_NAMESPACE_OFFICE,
+ XML_VALUE_TYPE, XML_STRING );
+ }
+ else if ( (-1 != nNumberFormat) && !xCell->getString().isEmpty() )
+ {
+ // number format key:
+ // (export values only if cell contains text;)
+ XMLNumberFormatAttributesExportHelper::
+ SetNumberFormatAttributes(
+ *this, nNumberFormat, xCell->getValue() );
+ }
+ // else: invalid key; ignore
+
+ // cell protection
+ aAny = xCell->getPropertyValue("IsProtected");
+ if (*o3tl::doAccess<bool>(aAny))
+ {
+ AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED,
+ XML_TRUE );
+ }
+
+ if( !rTableInfo.IsBaseSectionValid() )
+ {
+ aAny = xCell->getPropertyValue("TextSection");
+ Reference < XTextSection > xTextSection;
+ aAny >>= xTextSection;
+ rTableInfo.SetBaseSection( xTextSection );
+ }
+
+ // export cell element
+ SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(),
+ XML_TABLE_CELL, true, true );
+
+ // export cell content
+ GetTextParagraphExport()->exportText( xCell,
+ rTableInfo.GetBaseSection(),
+ IsShowProgress() );
+ }
+ else
+ {
+ OSL_FAIL("here should be a XCell");
+ ClearAttrList();
+ }
+ }
+ else
+ {
+ // no start node -> merged cells: export subtable in cell
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE,
+ XML_TABLE_CELL, true, true );
+ {
+ AddAttribute( XML_NAMESPACE_TABLE, XML_IS_SUB_TABLE,
+ GetXMLToken( XML_TRUE ) );
+
+ SvXMLElementExport aElemExport( *this, XML_NAMESPACE_TABLE,
+ XML_TABLE, true, true );
+ ExportTableLines( rBox.GetTabLines(), rTableInfo );
+ }
+ }
+ }
+}
+
+void SwXMLExport::ExportTableLine( const SwTableLine& rLine,
+ const SwXMLTableLines_Impl& rLines,
+ SwXMLTableInfo_Impl& rTableInfo )
+{
+ if( rLine.hasSoftPageBreak() )
+ {
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT,
+ XML_SOFT_PAGE_BREAK, true, true );
+ }
+ const SwFrameFormat *pFrameFormat = rLine.GetFrameFormat();
+ if( pFrameFormat )
+ {
+ auto const it(rTableInfo.GetLineFormats().find(pFrameFormat));
+ assert(it != rTableInfo.GetLineFormats().end());
+ if (it->second)
+ {
+ assert(!it->second->isEmpty());
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
+ }
+ }
+
+ {
+ SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_ROW, true, true );
+ const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+
+ sal_uInt32 nCPos = 0U;
+ size_t nCol = 0U;
+ for( size_t nBox=0U; nBox<nBoxes; ++nBox )
+ {
+ const SwTableBox *pBox = rBoxes[nBox];
+
+ // NEW TABLES
+ const sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan < 1 )
+ {
+ // Export style of covered cell, it includes border information.
+ const SwFrameFormat* pFormat = pBox->GetFrameFormat();
+ if (pFormat)
+ {
+ auto const it(rTableInfo.GetBoxFormats().find(pFormat));
+ assert(it != rTableInfo.GetBoxFormats().end());
+ if (it->second)
+ {
+ assert(!it->second->isEmpty());
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
+ }
+ }
+
+ SvXMLElementExport aElem2( *this, rTableInfo.GetPrefix(),
+ XML_COVERED_TABLE_CELL, true,
+ false );
+ }
+
+ if( nBox < nBoxes-1U )
+ nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
+ else
+ nCPos = rLines.GetWidth();
+
+ // and their index
+ const size_t nOldCol = nCol;
+ SwXMLTableColumn_Impl aCol( nCPos );
+ SwXMLTableColumns_Impl::const_iterator it = rLines.GetColumns().find( &aCol );
+ OSL_ENSURE( it != rLines.GetColumns().end(), "couldn't find column" );
+ nCol = it - rLines.GetColumns().begin();
+
+ // #i95726# - Some fault tolerance, if table is somehow corrupted.
+ if ( nCol < nOldCol )
+ {
+ OSL_FAIL( "table and/or table information seems to be corrupted." );
+ // NOTE: nOldCol is not necessarily a valid index into
+ // GetColumns(), but that doesn't matter here
+ nCol = nOldCol;
+ }
+
+ const sal_uInt32 nColSpan = nCol - nOldCol + 1U;
+
+ if ( nRowSpan >= 1 )
+ ExportTableBox( *pBox, nColSpan, static_cast< sal_uInt32 >(nRowSpan), rTableInfo );
+
+ for( size_t i=nOldCol; i<nCol; ++i )
+ {
+ SvXMLElementExport aElemExport( *this, rTableInfo.GetPrefix(),
+ XML_COVERED_TABLE_CELL, true,
+ false );
+ }
+
+ nCol++;
+ }
+ }
+}
+
+void SwXMLExport::ExportTableLines( const SwTableLines& rLines,
+ SwXMLTableInfo_Impl& rTableInfo,
+ sal_uInt32 nHeaderRows )
+{
+ OSL_ENSURE( m_pTableLines && !m_pTableLines->empty(),
+ "SwXMLExport::ExportTableLines: table columns infos missing" );
+ if( !m_pTableLines || m_pTableLines->empty() )
+ return;
+
+ SwXMLTableLines_Impl* pLines = nullptr;
+ size_t nInfoPos;
+ for( nInfoPos=0; nInfoPos < m_pTableLines->size(); nInfoPos++ )
+ {
+ if( m_pTableLines->at( nInfoPos )->GetLines() == &rLines )
+ {
+ pLines = m_pTableLines->at( nInfoPos );
+ break;
+ }
+ }
+ OSL_ENSURE( pLines,
+ "SwXMLExport::ExportTableLines: table columns info missing" );
+ OSL_ENSURE( 0==nInfoPos,
+ "SwXMLExport::ExportTableLines: table columns infos are unsorted" );
+ if( !pLines )
+ return;
+
+ SwXMLTableLinesCache_Impl::iterator it = m_pTableLines->begin();
+ advance( it, nInfoPos );
+ m_pTableLines->erase( it );
+
+ if( m_pTableLines->empty() )
+ m_pTableLines.reset();
+
+ // pass 2: export columns
+ const SwXMLTableColumns_Impl& rCols = pLines->GetColumns();
+ size_t nColumn = 0U;
+ const size_t nColumns = rCols.size();
+ sal_Int32 nColRep = 1;
+ SwXMLTableColumn_Impl *pColumn = (nColumns > 0) ? rCols.front().get() : nullptr;
+ while( pColumn )
+ {
+ nColumn++;
+ SwXMLTableColumn_Impl *pNextColumn =
+ (nColumn < nColumns) ? rCols[nColumn].get() : nullptr;
+ if( pNextColumn &&
+ pNextColumn->GetStyleName() == pColumn->GetStyleName() )
+ {
+ nColRep++;
+ }
+ else
+ {
+ AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME,
+ EncodeStyleName(pColumn->GetStyleName()) );
+
+ if( nColRep > 1 )
+ {
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
+ OUString::number(nColRep) );
+ }
+
+ {
+ SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_COLUMN, true, true );
+ }
+
+ nColRep = 1;
+ }
+ pColumn = pNextColumn;
+ }
+
+ // pass 3: export line/rows
+ const size_t nLines = rLines.size();
+ // export header rows, if present
+ if( nHeaderRows > 0 )
+ {
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE,
+ XML_TABLE_HEADER_ROWS, true, true );
+
+ OSL_ENSURE( nHeaderRows <= nLines, "more headers then lines?" );
+ for( size_t nLine = 0U; nLine < nHeaderRows; ++nLine )
+ ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo );
+ }
+ // export remaining rows
+ for( size_t nLine = nHeaderRows; nLine < nLines; ++nLine )
+ {
+ ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo );
+ }
+
+ delete pLines;
+}
+
+void SwXMLExport::ExportTable( const SwTableNode& rTableNd )
+{
+ ::std::optional<sal_uInt16> oPrefix = XML_NAMESPACE_TABLE;
+ if (const SwFrameFormat* pFlyFormat = rTableNd.GetFlyFormat())
+ {
+ if (SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ {
+ // TODO ODF 1.4 OFFICE-3761
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ oPrefix = XML_NAMESPACE_LO_EXT;
+ }
+ else
+ {
+ oPrefix.reset(); // no export to OASIS namespace yet
+ }
+ }
+ }
+
+ if (!oPrefix)
+ return;
+
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwFrameFormat *pTableFormat = rTable.GetFrameFormat();
+ if (pTableFormat && !pTableFormat->GetName().isEmpty())
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pTableFormat->GetName());
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME,
+ EncodeStyleName(pTableFormat->GetName()));
+ }
+
+ // table:template-name=
+ if (!rTable.GetTableStyleName().isEmpty())
+ {
+ OUString sStyleName;
+ SwStyleNameMapper::FillProgName(rTable.GetTableStyleName(), sStyleName, SwGetPoolIdFromName::TabStyle);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TEMPLATE_NAME, sStyleName);
+ }
+
+ SvXMLElementExport aElem(*this, *oPrefix, XML_TABLE, true, true);
+
+ // export DDE source (if this is a DDE table)
+ if ( auto pSwDdeTable = dynamic_cast<const SwDDETable*>( &rTable) )
+ {
+ // get DDE Field Type (contains the DDE connection)
+ const SwDDEFieldType* pDDEFieldType = pSwDdeTable->GetDDEFieldType();
+
+ // connection name
+ AddAttribute( XML_NAMESPACE_OFFICE, XML_NAME,
+ pDDEFieldType->GetName() );
+
+ // DDE command
+ const OUString& sCmd = pDDEFieldType->GetCmd();
+ sal_Int32 nIdx{ 0 };
+ AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION,
+ sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
+ AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_ITEM,
+ sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
+ AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_TOPIC,
+ sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
+
+ // auto update
+ if (pDDEFieldType->GetType() == SfxLinkUpdateMode::ALWAYS)
+ {
+ AddAttribute( XML_NAMESPACE_OFFICE,
+ XML_AUTOMATIC_UPDATE, XML_TRUE );
+ }
+
+ // DDE source element (always empty)
+ SvXMLElementExport aSource(*this, XML_NAMESPACE_OFFICE,
+ XML_DDE_SOURCE, true, false);
+ }
+
+ auto const& rFormats(static_cast<SwXMLTextParagraphExport const*>(GetTextParagraphExport().get())->GetTableFormats());
+ auto const it(rFormats.find(&rTableNd));
+ assert(it != rFormats.end());
+ SwXMLTableInfo_Impl aTableInfo(&rTable, *oPrefix, it->second.first, it->second.second);
+ ExportTableLines( rTable.GetTabLines(), aTableInfo, rTable.GetRowsToRepeat() );
+}
+
+void SwXMLTextParagraphExport::exportTableAutoStyles() {
+ // note: maTableNodes is used here only to keep the iteration order as before
+ for (const auto* pTableNode : maTableNodes)
+ {
+ static_cast<SwXMLExport&>(GetExport()).ExportTableAutoStyles(*pTableNode);
+ }
+}
+
+void SwXMLTextParagraphExport::CollectTableLinesAutoStyles(const SwTableLines& rLines,
+ SwFrameFormat& rFormat, bool _bProgress)
+{
+ // Follow SwXMLExport::ExportTableLines/ExportTableLine/ExportTableBox
+ for (const SwTableLine* pLine : rLines)
+ {
+ for (SwTableBox* pBox : pLine->GetTabBoxes())
+ {
+ if (pBox->getRowSpan() <= 0)
+ continue;
+ if (pBox->GetSttNd())
+ {
+ if (rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell(&rFormat, pBox))
+ exportText(xCell, true /*bAutoStyles*/, _bProgress, true /*bExportParagraph*/);
+ }
+ else
+ {
+ // no start node -> merged cells: export subtable in cell
+ CollectTableLinesAutoStyles(pBox->GetTabLines(), rFormat, _bProgress);
+ }
+ }
+ }
+}
+
+void SwXMLTextParagraphExport::exportTable(
+ const Reference < XTextContent > & rTextContent,
+ bool bAutoStyles, bool _bProgress )
+{
+ bool bOldShowProgress = static_cast<SwXMLExport&>(GetExport()).IsShowProgress();
+ static_cast<SwXMLExport&>(GetExport()).SetShowProgress( _bProgress );
+
+ Reference < XTextTable > xTextTable( rTextContent, UNO_QUERY );
+ OSL_ENSURE( xTextTable.is(), "text table missing" );
+ if( xTextTable.is() )
+ {
+ SwXTextTable* pXTable = dynamic_cast<SwXTextTable*>(rTextContent.get());
+ if( pXTable )
+ {
+ SwFrameFormat *const pFormat = pXTable->GetFrameFormat();
+ OSL_ENSURE( pFormat, "table format missing" );
+ const SwTable *pTable = SwTable::FindTable( pFormat );
+ OSL_ENSURE( pTable, "table missing" );
+ const SwTableNode *pTableNd = pTable->GetTableNode();
+ OSL_ENSURE( pTableNd, "table node missing" );
+ if( bAutoStyles )
+ {
+ // AUTOSTYLES: Optimization: Do not export table autostyle if
+ // we are currently exporting the content.xml stuff and
+ // the table is located in header/footer:
+ // During the flat XML export (used e.g. by .sdw-export)
+ // ALL flags are set at the same time.
+ const bool bExportStyles = bool( GetExport().getExportFlags() & SvXMLExportFlags::STYLES );
+ if (!isAutoStylesCollected()
+ && (bExportStyles || !pFormat->GetDoc()->IsInHeaderFooter(*pTableNd)))
+ {
+ maTableNodes.push_back(pTableNd);
+ m_TableFormats.try_emplace(pTableNd);
+ // Collect all tables inside cells of this table, too
+ CollectTableLinesAutoStyles(pTable->GetTabLines(), *pFormat, _bProgress);
+ }
+ }
+ else
+ {
+ static_cast<SwXMLExport&>(GetExport()).ExportTable( *pTableNd );
+ }
+ }
+ }
+
+ static_cast<SwXMLExport&>(GetExport()).SetShowProgress( bOldShowProgress );
+}
+
+void SwXMLExport::DeleteTableLines()
+{
+ if ( m_pTableLines )
+ {
+ for (SwXMLTableLines_Impl* p : *m_pTableLines)
+ delete p;
+ m_pTableLines->clear();
+ m_pTableLines.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltbli.cxx b/sw/source/filter/xml/xmltbli.cxx
new file mode 100644
index 0000000000..b2d312cad2
--- /dev/null
+++ b/sw/source/filter/xml/xmltbli.cxx
@@ -0,0 +1,2757 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <hintids.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/text/XTextTable.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <sax/tools/converter.hxx>
+#include <unotools/configmgr.hxx>
+#include <utility>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <xmloff/families.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/i18nmap.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <poolfmt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtfordr.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <swtable.hxx>
+#include <swtblfmt.hxx>
+#include <pam.hxx>
+#include <unoprnms.hxx>
+#include <unotbl.hxx>
+#include <unotextrange.hxx>
+#include <cellatr.hxx>
+#include <swddetbl.hxx>
+#include <ddefld.hxx>
+#include <sfx2/linkmgr.hxx>
+#include "xmlimp.hxx"
+#include "xmltbli.hxx"
+#include <vcl/svapp.hxx>
+#include <ndtxt.hxx>
+#include <unotextcursor.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+#include <algorithm>
+#include <vector>
+#include <memory>
+
+#include <limits.h>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+
+class SwXMLTableCell_Impl
+{
+ OUString m_aStyleName;
+
+ OUString m_StringValue;
+
+ OUString m_sFormula; // cell formula; valid if length > 0
+ double m_dValue; // formula value
+
+ rtl::Reference<SwXMLTableContext> m_xSubTable;
+
+ const SwStartNode *m_pStartNode;
+ sal_uInt32 m_nRowSpan;
+ sal_uInt32 m_nColSpan;
+
+ bool m_bProtected : 1;
+ bool m_bHasValue; // determines whether dValue attribute is valid
+ bool mbCovered;
+ bool m_bHasStringValue;
+
+public:
+
+ SwXMLTableCell_Impl( sal_uInt32 nRSpan=1, sal_uInt32 nCSpan=1 ) :
+ m_dValue( 0.0 ),
+ m_pStartNode( nullptr ),
+ m_nRowSpan( nRSpan ),
+ m_nColSpan( nCSpan ),
+ m_bProtected( false ),
+ m_bHasValue( false ),
+ mbCovered( false )
+ , m_bHasStringValue(false)
+ {}
+
+ inline void Set( const OUString& rStyleName,
+ sal_uInt32 nRSpan, sal_uInt32 nCSpan,
+ const SwStartNode *pStNd, SwXMLTableContext *pTable,
+ bool bProtect,
+ const OUString* pFormula,
+ bool bHasValue,
+ bool bCovered,
+ double dVal,
+ OUString const*const pStringValue);
+
+ bool IsUsed() const { return m_pStartNode!=nullptr ||
+ m_xSubTable.is() || m_bProtected;}
+
+ sal_uInt32 GetRowSpan() const { return m_nRowSpan; }
+ void SetRowSpan( sal_uInt32 nSet ) { m_nRowSpan = nSet; }
+ sal_uInt32 GetColSpan() const { return m_nColSpan; }
+ void SetStyleName(const OUString& rStyleName) { m_aStyleName = rStyleName; }
+ const OUString& GetStyleName() const { return m_aStyleName; }
+ const OUString& GetFormula() const { return m_sFormula; }
+ double GetValue() const { return m_dValue; }
+ bool HasValue() const { return m_bHasValue; }
+ bool IsProtected() const { return m_bProtected; }
+ bool IsCovered() const { return mbCovered; }
+ bool HasStringValue() const { return m_bHasStringValue; }
+ OUString const* GetStringValue() const {
+ return m_bHasStringValue ? &m_StringValue : nullptr;
+ }
+
+ const SwStartNode *GetStartNode() const { return m_pStartNode; }
+ inline void SetStartNode( const SwStartNode *pSttNd );
+
+ inline SwXMLTableContext *GetSubTable() const;
+
+ inline void Dispose();
+};
+
+inline void SwXMLTableCell_Impl::Set( const OUString& rStyleName,
+ sal_uInt32 nRSpan, sal_uInt32 nCSpan,
+ const SwStartNode *pStNd,
+ SwXMLTableContext *pTable,
+ bool bProtect,
+ const OUString* pFormula,
+ bool bHasVal,
+ bool bCov,
+ double dVal,
+ OUString const*const pStringValue )
+{
+ m_aStyleName = rStyleName;
+ m_nRowSpan = nRSpan;
+ m_nColSpan = nCSpan;
+ m_pStartNode = pStNd;
+ m_xSubTable = pTable;
+ m_dValue = dVal;
+ m_bHasValue = bHasVal;
+ mbCovered = bCov;
+ if (pStringValue)
+ {
+ m_StringValue = *pStringValue;
+ }
+ m_bHasStringValue = (pStringValue != nullptr);
+ m_bProtected = bProtect;
+
+ // set formula, if valid
+ if (pFormula != nullptr)
+ {
+ m_sFormula = *pFormula;
+ }
+}
+
+inline void SwXMLTableCell_Impl::SetStartNode( const SwStartNode *pSttNd )
+{
+ m_pStartNode = pSttNd;
+ m_xSubTable = nullptr;
+}
+
+inline SwXMLTableContext *SwXMLTableCell_Impl::GetSubTable() const
+{
+ return m_xSubTable.get();
+}
+
+inline void SwXMLTableCell_Impl::Dispose()
+{
+ if( m_xSubTable.is() )
+ m_xSubTable = nullptr;
+}
+
+class SwXMLTableRow_Impl
+{
+ OUString m_aStyleName;
+ OUString m_aDefaultCellStyleName;
+ std::vector<std::unique_ptr<SwXMLTableCell_Impl>> m_Cells;
+ bool m_bSplitable;
+
+public:
+
+ SwXMLTableRow_Impl( OUString aStyleName, sal_uInt32 nCells,
+ const OUString *pDfltCellStyleName = nullptr );
+
+ inline SwXMLTableCell_Impl *GetCell( sal_uInt32 nCol );
+
+ inline void Set( const OUString& rStyleName,
+ const OUString& rDfltCellStyleName );
+
+ void Expand( sal_uInt32 nCells, bool bOneCell );
+
+ void SetSplitable( bool bSet ) { m_bSplitable = bSet; }
+ bool IsSplitable() const { return m_bSplitable; }
+
+ const OUString& GetStyleName() const { return m_aStyleName; }
+ const OUString& GetDefaultCellStyleName() const { return m_aDefaultCellStyleName; }
+
+ void Dispose();
+};
+
+SwXMLTableRow_Impl::SwXMLTableRow_Impl( OUString aStyleName,
+ sal_uInt32 nCells,
+ const OUString *pDfltCellStyleName ) :
+ m_aStyleName(std::move( aStyleName )),
+ m_bSplitable( false )
+{
+ if( pDfltCellStyleName )
+ m_aDefaultCellStyleName = *pDfltCellStyleName;
+ OSL_ENSURE( nCells <= USHRT_MAX,
+ "SwXMLTableRow_Impl::SwXMLTableRow_Impl: too many cells" );
+ if( nCells > USHRT_MAX )
+ nCells = USHRT_MAX;
+
+ for( sal_uInt32 i=0U; i<nCells; ++i )
+ {
+ m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>());
+ }
+}
+
+inline SwXMLTableCell_Impl *SwXMLTableRow_Impl::GetCell( sal_uInt32 nCol )
+{
+ OSL_ENSURE( nCol < USHRT_MAX,
+ "SwXMLTableRow_Impl::GetCell: column number is too big" );
+ // #i95726# - some fault tolerance
+ OSL_ENSURE( nCol < m_Cells.size(),
+ "SwXMLTableRow_Impl::GetCell: column number is out of bound" );
+ return nCol < m_Cells.size() ? m_Cells[nCol].get() : nullptr;
+}
+
+void SwXMLTableRow_Impl::Expand( sal_uInt32 nCells, bool bOneCell )
+{
+ OSL_ENSURE( nCells <= USHRT_MAX,
+ "SwXMLTableRow_Impl::Expand: too many cells" );
+ if( nCells > USHRT_MAX )
+ nCells = USHRT_MAX;
+
+ sal_uInt32 nColSpan = nCells - m_Cells.size();
+ for (size_t i = m_Cells.size(); i < nCells; ++i)
+ {
+ m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>(
+ 1UL, bOneCell ? nColSpan : 1UL));
+ nColSpan--;
+ }
+
+ OSL_ENSURE( nCells <= m_Cells.size(),
+ "SwXMLTableRow_Impl::Expand: wrong number of cells" );
+}
+
+inline void SwXMLTableRow_Impl::Set( const OUString& rStyleName,
+ const OUString& rDfltCellStyleName )
+{
+ m_aStyleName = rStyleName;
+ m_aDefaultCellStyleName = rDfltCellStyleName;
+}
+
+void SwXMLTableRow_Impl::Dispose()
+{
+ for (auto & pCell : m_Cells)
+ {
+ pCell->Dispose();
+ }
+}
+
+namespace {
+
+class SwXMLTableCellContext_Impl : public SvXMLImportContext
+{
+ OUString m_aStyleName;
+ OUString m_sFormula;
+ OUString m_sSaveParaDefault;
+ OUString m_StringValue;
+
+ rtl::Reference<SwXMLTableContext> m_xMyTable;
+
+ double m_fValue;
+ bool m_bHasValue;
+ bool m_bHasStringValue;
+ bool m_bValueTypeIsString;
+ bool m_bProtect;
+
+ sal_uInt32 m_nRowSpan;
+ sal_uInt32 m_nColSpan;
+ sal_uInt32 m_nColRepeat;
+
+ bool m_bHasTextContent : 1;
+ bool m_bHasTableContent : 1;
+
+ SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
+
+ bool HasContent() const { return m_bHasTextContent || m_bHasTableContent; }
+ inline void InsertContent_();
+ inline void InsertContent();
+ inline void InsertContent( SwXMLTableContext *pTable );
+
+public:
+
+ SwXMLTableCellContext_Impl(
+ SwXMLImport& rImport, sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ SwXMLTableContext *pTable );
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+};
+
+/// Handles <table:covered-table-cell>.
+class SwXMLCoveredTableCellContext : public SvXMLImportContext
+{
+public:
+ SwXMLCoveredTableCellContext(SwXMLImport& rImport,
+ const Reference<xml::sax::XFastAttributeList>& xAttrList,
+ SwXMLTableContext& rTable);
+};
+
+SwXMLCoveredTableCellContext::SwXMLCoveredTableCellContext(
+ SwXMLImport& rImport, const Reference<xml::sax::XFastAttributeList>& xAttrList,
+ SwXMLTableContext& rTable)
+ : SvXMLImportContext(rImport)
+{
+ OUString aStyleName;
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ aStyleName = rIter.toString();
+ break;
+ }
+ }
+
+ if (!aStyleName.isEmpty())
+ {
+ rTable.InsertCoveredCell(aStyleName);
+ }
+}
+}
+
+SwXMLTableCellContext_Impl::SwXMLTableCellContext_Impl(
+ SwXMLImport& rImport, sal_Int32 /*nElement*/,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ SwXMLTableContext *pTable ) :
+ SvXMLImportContext( rImport ),
+ m_xMyTable( pTable ),
+ m_fValue( 0.0 ),
+ m_bHasValue( false ),
+ m_bHasStringValue(false),
+ m_bValueTypeIsString(false),
+ m_bProtect( false ),
+ m_nRowSpan( 1 ),
+ m_nColSpan( 1 ),
+ m_nColRepeat( 1 ),
+ m_bHasTextContent( false ),
+ m_bHasTableContent( false )
+{
+ m_sSaveParaDefault = GetImport().GetTextImport()->GetCellParaStyleDefault();
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ switch( aIter.getToken() )
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ m_aStyleName = aIter.toString();
+ GetImport().GetTextImport()->SetCellParaStyleDefault(m_aStyleName);
+ break;
+ case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_SPANNED):
+ m_nColSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
+ if (m_nColSpan > 256)
+ {
+ SAL_INFO("sw.xml", "ignoring huge table:number-columns-spanned " << m_nColSpan);
+ m_nColSpan = 1;
+ }
+ break;
+ case XML_ELEMENT(TABLE, XML_NUMBER_ROWS_SPANNED):
+ m_nRowSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
+ if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing()))
+ {
+ SAL_INFO("sw.xml", "ignoring huge table:number-rows-spanned " << m_nRowSpan);
+ m_nRowSpan = 1;
+ }
+ break;
+ case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED):
+ m_nColRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
+ if (m_nColRepeat > 256)
+ {
+ SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << m_nColRepeat);
+ m_nColRepeat = 1;
+ }
+ break;
+ case XML_ELEMENT(TABLE, XML_FORMULA):
+ {
+ OUString sTmp;
+ const sal_uInt16 nPrefix2 = GetImport().GetNamespaceMap().
+ GetKeyByAttrValueQName(aIter.toString(), &sTmp);
+ m_sFormula = XML_NAMESPACE_OOOW == nPrefix2 ? sTmp : aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_VALUE):
+ {
+ // Writer wrongly uses DBL_MAX to flag error but fails to
+ // check for it after import, so check that here, tdf#139126.
+ double fTmp;
+ if (::sax::Converter::convertDouble(fTmp, aIter.toView()) && fTmp < DBL_MAX)
+ {
+ m_fValue = fTmp;
+ m_bHasValue = true;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_TIME_VALUE):
+ {
+ double fTmp;
+ if (::sax::Converter::convertDuration(fTmp, aIter.toView()))
+ {
+ m_fValue = fTmp;
+ m_bHasValue = true;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_DATE_VALUE):
+ {
+ double fTmp;
+ if (GetImport().GetMM100UnitConverter().convertDateTime(fTmp,
+ aIter.toView()))
+ {
+ m_fValue = fTmp;
+ m_bHasValue = true;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
+ {
+ bool bTmp(false);
+ if (::sax::Converter::convertBool(bTmp, aIter.toView()))
+ {
+ m_fValue = (bTmp ? 1.0 : 0.0);
+ m_bHasValue = true;
+ }
+ }
+ break;
+ case XML_ELEMENT(TABLE, XML_PROTECT): // for backwards compatibility with SRC629 (and before)
+ case XML_ELEMENT(TABLE, XML_PROTECTED):
+ {
+ bool bTmp(false);
+ if (::sax::Converter::convertBool(bTmp, aIter.toView()))
+ {
+ m_bProtect = bTmp;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
+ {
+ m_StringValue = aIter.toString();
+ m_bHasStringValue = true;
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
+ {
+ if ("string" == aIter.toView())
+ {
+ m_bValueTypeIsString = true;
+ }
+ // ignore other types - it would be correct to require
+ // matching value-type and $type-value attributes,
+ // but we've been reading those without checking forever.
+ }
+ break;
+ default:
+ SAL_WARN("sw", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
+ }
+ }
+}
+
+inline void SwXMLTableCellContext_Impl::InsertContent_()
+{
+ SwStartNode const*const pStartNode( GetTable()->InsertTableSection(nullptr,
+ (m_bHasStringValue && m_bValueTypeIsString &&
+ !m_aStyleName.isEmpty()) ? & m_aStyleName : nullptr) );
+ GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan,
+ pStartNode,
+ nullptr, m_bProtect, &m_sFormula, m_bHasValue, m_fValue,
+ (m_bHasStringValue && m_bValueTypeIsString) ? &m_StringValue : nullptr);
+}
+
+inline void SwXMLTableCellContext_Impl::InsertContent()
+{
+ OSL_ENSURE( !HasContent(), "content already there" );
+ m_bHasTextContent = true;
+ InsertContent_();
+}
+
+inline void SwXMLTableCellContext_Impl::InsertContent(
+ SwXMLTableContext *pTable )
+{
+ GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan, nullptr, pTable, m_bProtect );
+ m_bHasTableContent = true;
+}
+
+css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableCellContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ bool bSubTable = false;
+ if( nElement == XML_ELEMENT(TABLE, XML_TABLE) )
+ {
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ if( aIter.getToken() == XML_ELEMENT(TABLE, XML_IS_SUB_TABLE) )
+ {
+ if ( IsXMLToken( aIter, XML_TRUE ) )
+ bSubTable = true;
+ }
+ else
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ //FIXME: RDFa
+ }
+ }
+
+ if( bSubTable )
+ {
+ if( !HasContent() )
+ {
+ SwXMLTableContext *pTableContext =
+ new SwXMLTableContext( GetSwImport(), GetTable() );
+ pContext = pTableContext;
+ if( GetTable()->IsValid() )
+ InsertContent( pTableContext );
+
+ GetTable()->SetHasSubTables( true );
+ }
+ }
+ else
+ {
+ if( GetTable()->IsValid() && !HasContent() )
+ InsertContent();
+ // fdo#60842: "office:string-value" overrides text content -> no import
+ if (!(m_bValueTypeIsString && m_bHasStringValue))
+ {
+ pContext = GetImport().GetTextImport()->CreateTextChildContext(
+ GetImport(), nElement, xAttrList,
+ XMLTextType::Cell );
+ }
+ }
+
+ return pContext;
+}
+
+void SwXMLTableCellContext_Impl::endFastElement(sal_Int32 )
+{
+ if( GetTable()->IsValid() )
+ {
+ if( m_bHasTextContent )
+ {
+ GetImport().GetTextImport()->DeleteParagraph();
+ if( m_nColRepeat > 1 && m_nColSpan == 1 )
+ {
+ // The original text is invalid after deleting the last
+ // paragraph
+ Reference < XTextCursor > xSrcTextCursor =
+ GetImport().GetTextImport()->GetText()->createTextCursor();
+ xSrcTextCursor->gotoEnd( true );
+
+ // Until we have an API for copying we have to use the core.
+ OTextCursorHelper *pSrcTextCursor = dynamic_cast<OTextCursorHelper*>(xSrcTextCursor.get());
+ assert(pSrcTextCursor && "SwXTextCursor missing");
+ SwDoc *pDoc = pSrcTextCursor->GetDoc();
+ const SwPaM *pSrcPaM = pSrcTextCursor->GetPaM();
+
+ while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() )
+ {
+ InsertContent_();
+
+ OTextCursorHelper *pDstTextCursor = dynamic_cast<OTextCursorHelper*>(GetImport().GetTextImport()->GetCursor().get());
+ assert(pDstTextCursor && "SwXTextCursor missing");
+ SwPaM aSrcPaM(*pSrcPaM->GetMark(), *pSrcPaM->GetPoint());
+ SwPosition aDstPos( *pDstTextCursor->GetPaM()->GetPoint() );
+ pDoc->getIDocumentContentOperations().CopyRange(aSrcPaM, aDstPos, SwCopyFlags::CheckPosInFly);
+
+ m_nColRepeat--;
+ }
+ }
+ }
+ else if( !m_bHasTableContent )
+ {
+ InsertContent();
+ if( m_nColRepeat > 1 && m_nColSpan == 1 )
+ {
+ while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() )
+ {
+ InsertContent_();
+ m_nColRepeat--;
+ }
+ }
+ }
+ }
+ GetImport().GetTextImport()->SetCellParaStyleDefault(m_sSaveParaDefault);
+}
+
+namespace {
+
+class SwXMLTableColContext_Impl : public SvXMLImportContext
+{
+ rtl::Reference<SwXMLTableContext> m_xMyTable;
+
+ SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
+
+public:
+
+ SwXMLTableColContext_Impl(
+ SwXMLImport& rImport,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ SwXMLTableContext *pTable );
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+};
+
+}
+
+SwXMLTableColContext_Impl::SwXMLTableColContext_Impl(
+ SwXMLImport& rImport,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ SwXMLTableContext *pTable ) :
+ SvXMLImportContext( rImport ),
+ m_xMyTable( pTable )
+{
+ sal_uInt32 nColRep = 1;
+ OUString aStyleName, aDfltCellStyleName;
+
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ aStyleName = aIter.toString();
+ break;
+ case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED):
+ {
+ nColRep = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
+ if (nColRep > 256)
+ {
+ SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << nColRep);
+ nColRep = 1;
+ }
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME):
+ aDfltCellStyleName = aIter.toString();
+ break;
+ case XML_ELEMENT(XML, XML_ID):
+ {
+ //FIXME where to put this??? columns do not actually exist in writer...
+ break;
+ }
+ default:
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ }
+ }
+
+ sal_Int32 nWidth = MINLAY;
+ bool bRelWidth = true;
+ if( !aStyleName.isEmpty() )
+ {
+ const SwFormatFrameSize *pSize;
+ const SfxItemSet *pAutoItemSet = nullptr;
+ if( GetSwImport().FindAutomaticStyle(
+ XmlStyleFamily::TABLE_COLUMN,
+ aStyleName, &pAutoItemSet ) &&
+ pAutoItemSet &&
+ (pSize = pAutoItemSet->GetItemIfSet( RES_FRM_SIZE, false )) )
+ {
+ nWidth = pSize->GetWidth();
+ bRelWidth = SwFrameSize::Variable == pSize->GetHeightSizeType();
+ }
+ }
+
+ if( nWidth )
+ {
+ while( nColRep-- && GetTable()->IsInsertColPossible() )
+ GetTable()->InsertColumn( nWidth, bRelWidth, &aDfltCellStyleName );
+ }
+}
+
+namespace {
+
+class SwXMLTableColsContext_Impl : public SvXMLImportContext
+{
+ rtl::Reference<SwXMLTableContext> m_xMyTable;
+
+ SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
+
+public:
+
+ SwXMLTableColsContext_Impl(
+ SwXMLImport& rImport,
+ SwXMLTableContext *pTable );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+};
+
+}
+
+SwXMLTableColsContext_Impl::SwXMLTableColsContext_Impl(
+ SwXMLImport& rImport,
+ SwXMLTableContext *pTable ) :
+ SvXMLImportContext( rImport ),
+ m_xMyTable( pTable )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLTableColsContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if( nElement == XML_ELEMENT(TABLE, XML_TABLE_COLUMN) &&
+ GetTable()->IsInsertColPossible() )
+ pContext = new SwXMLTableColContext_Impl( GetSwImport(), xAttrList, GetTable() );
+ else
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement);
+
+ return pContext;
+}
+
+namespace {
+
+class SwXMLTableRowContext_Impl : public SvXMLImportContext
+{
+ rtl::Reference<SwXMLTableContext> m_xMyTable;
+
+ sal_uInt32 m_nRowRepeat;
+
+ SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
+
+public:
+
+ SwXMLTableRowContext_Impl(
+ SwXMLImport& rImport, sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ SwXMLTableContext *pTable, bool bInHead=false );
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+};
+
+}
+
+SwXMLTableRowContext_Impl::SwXMLTableRowContext_Impl( SwXMLImport& rImport,
+ sal_Int32 /*nElement*/,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList,
+ SwXMLTableContext *pTable,
+ bool bInHead ) :
+ SvXMLImportContext( rImport ),
+ m_xMyTable( pTable ),
+ m_nRowRepeat( 1 )
+{
+ OUString aStyleName, aDfltCellStyleName;
+
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ switch(aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ aStyleName = aIter.toString();
+ break;
+ case XML_ELEMENT(STYLE, XML_NUMBER_ROWS_REPEATED):
+ {
+ m_nRowRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
+ if (m_nRowRepeat > 8192 || (m_nRowRepeat > 256 && utl::ConfigManager::IsFuzzing()))
+ {
+ SAL_INFO("sw.xml", "ignoring huge table:number-rows-repeated " << m_nRowRepeat);
+ m_nRowRepeat = 1;
+ }
+ break;
+ }
+ case XML_ELEMENT(STYLE, XML_DEFAULT_CELL_STYLE_NAME):
+ aDfltCellStyleName = aIter.toString();
+ break;
+ case XML_ELEMENT(XML, XML_ID):
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ }
+ }
+ if( GetTable()->IsValid() )
+ GetTable()->InsertRow( aStyleName, aDfltCellStyleName, bInHead );
+}
+
+void SwXMLTableRowContext_Impl::endFastElement(sal_Int32 )
+{
+ if( GetTable()->IsValid() )
+ {
+ GetTable()->FinishRow();
+
+ if( m_nRowRepeat > 1 )
+ GetTable()->InsertRepRows( m_nRowRepeat );
+ }
+}
+
+css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if( nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) ||
+ nElement == XML_ELEMENT(LO_EXT, XML_TABLE_CELL) )
+ {
+ if( !GetTable()->IsValid() || GetTable()->IsInsertCellPossible() )
+ pContext = new SwXMLTableCellContext_Impl( GetSwImport(), nElement,
+ xAttrList,
+ GetTable() );
+ }
+ else if( nElement == XML_ELEMENT(TABLE, XML_COVERED_TABLE_CELL) ||
+ nElement == XML_ELEMENT(LO_EXT, XML_COVERED_TABLE_CELL) )
+ {
+ if (GetTable()->IsValid() && GetTable()->IsInsertCoveredCellPossible())
+ {
+ pContext = new SwXMLCoveredTableCellContext(GetSwImport(), xAttrList, *GetTable());
+ }
+ else
+ {
+ pContext = new SvXMLImportContext(GetImport());
+ }
+ }
+ else
+ SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
+
+ return pContext;
+}
+
+namespace {
+
+class SwXMLTableRowsContext_Impl : public SvXMLImportContext
+{
+ rtl::Reference<SwXMLTableContext> m_xMyTable;
+
+ bool m_bHeader;
+
+ SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
+
+public:
+
+ SwXMLTableRowsContext_Impl( SwXMLImport& rImport,
+ SwXMLTableContext *pTable,
+ bool bHead );
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+};
+
+}
+
+SwXMLTableRowsContext_Impl::SwXMLTableRowsContext_Impl( SwXMLImport& rImport,
+ SwXMLTableContext *pTable,
+ bool bHead ) :
+ SvXMLImportContext( rImport ),
+ m_xMyTable( pTable ),
+ m_bHeader( bHead )
+{
+}
+
+css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableRowsContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if( nElement== XML_ELEMENT(TABLE, XML_TABLE_ROW ) &&
+ GetTable()->IsInsertRowPossible() )
+ return new SwXMLTableRowContext_Impl( GetSwImport(), nElement,
+ xAttrList,
+ GetTable(),
+ m_bHeader );
+ SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
+ return nullptr;
+}
+
+class SwXMLDDETableContext_Impl : public SvXMLImportContext
+{
+ OUString m_sConnectionName;
+ OUString m_sDDEApplication;
+ OUString m_sDDEItem;
+ OUString m_sDDETopic;
+ bool m_bIsAutomaticUpdate;
+
+public:
+
+
+ SwXMLDDETableContext_Impl(SwXMLImport& rImport);
+
+ virtual void SAL_CALL startFastElement(
+ sal_Int32 nElement,
+ const Reference<xml::sax::XFastAttributeList> & xAttrList) override;
+
+ OUString& GetConnectionName() { return m_sConnectionName; }
+ OUString& GetDDEApplication() { return m_sDDEApplication; }
+ OUString& GetDDEItem() { return m_sDDEItem; }
+ OUString& GetDDETopic() { return m_sDDETopic; }
+ bool GetIsAutomaticUpdate() const { return m_bIsAutomaticUpdate; }
+};
+
+
+SwXMLDDETableContext_Impl::SwXMLDDETableContext_Impl(SwXMLImport& rImport) :
+ SvXMLImportContext(rImport),
+ m_bIsAutomaticUpdate(false)
+{
+}
+
+void SwXMLDDETableContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/,
+ const Reference<xml::sax::XFastAttributeList> & xAttrList)
+{
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(OFFICE, XML_DDE_APPLICATION):
+ m_sDDEApplication = aIter.toString();
+ break;
+ case XML_ELEMENT(OFFICE, XML_DDE_TOPIC):
+ m_sDDETopic = aIter.toString();
+ break;
+ case XML_ELEMENT(OFFICE, XML_DDE_ITEM):
+ m_sDDEItem = aIter.toString();
+ break;
+ case XML_ELEMENT(OFFICE, XML_NAME):
+ m_sConnectionName = aIter.toString();
+ break;
+ case XML_ELEMENT(OFFICE, XML_AUTOMATIC_UPDATE):
+ {
+ bool bTmp(false);
+ if (::sax::Converter::convertBool(bTmp, aIter.toView()))
+ {
+ m_bIsAutomaticUpdate = bTmp;
+ }
+ break;
+ }
+ default:
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ }
+ // else: unknown attribute namespace
+ }
+}
+
+// generate a new name for DDE field type (called by lcl_GetDDEFieldType below)
+static OUString lcl_GenerateFieldTypeName(const OUString& sPrefix, SwTableNode* pTableNode)
+{
+ const OUString sPrefixStr(sPrefix.isEmpty() ? OUString("_") : sPrefix);
+
+ // increase count until we find a name that is not yet taken
+ OUString sName;
+ sal_Int32 nCount = 0;
+ do
+ {
+ // this is crazy, but just in case all names are taken: exit gracefully
+ if (nCount == SAL_MAX_INT32)
+ return sName;
+
+ ++nCount;
+ sName = sPrefixStr + OUString::number(nCount);
+ }
+ while (nullptr != pTableNode->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false));
+
+ return sName;
+}
+
+// set table properties
+static SwDDEFieldType* lcl_GetDDEFieldType(SwXMLDDETableContext_Impl* pContext,
+ SwTableNode* pTableNode)
+{
+ // make command string
+ const OUString sCommand(pContext->GetDDEApplication()
+ + OUStringChar(sfx2::cTokenSeparator)
+ + pContext->GetDDEItem()
+ + OUStringChar(sfx2::cTokenSeparator)
+ + pContext->GetDDETopic());
+
+ const SfxLinkUpdateMode nType = pContext->GetIsAutomaticUpdate()
+ ? SfxLinkUpdateMode::ALWAYS
+ : SfxLinkUpdateMode::ONCALL;
+
+ OUString sName(pContext->GetConnectionName());
+
+ // field type to be returned
+ SwDDEFieldType* pType = nullptr;
+
+ // valid name?
+ if (sName.isEmpty())
+ {
+ sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(),
+ pTableNode);
+ }
+ else
+ {
+ // check for existing DDE field type with the same name
+ SwDDEFieldType* pOldType = static_cast<SwDDEFieldType*>(pTableNode->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false));
+ if (nullptr != pOldType)
+ {
+ // same values -> return old type
+ if ( (pOldType->GetCmd() == sCommand) &&
+ (pOldType->GetType() == nType) )
+ {
+ // same name, same values -> return old type!
+ pType = pOldType;
+ }
+ else
+ {
+ // same name, different values -> think of new name
+ sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(),
+ pTableNode);
+ }
+ }
+ // no old type -> create new one
+ }
+
+ // create new field type (unless we already have one)
+ if (nullptr == pType)
+ {
+ // create new field type and return
+ SwDDEFieldType aDDEFieldType(sName, sCommand, nType);
+ pType = static_cast<SwDDEFieldType*>(pTableNode->
+ GetDoc().getIDocumentFieldsAccess().InsertFieldType(aDDEFieldType));
+ }
+
+ OSL_ENSURE(nullptr != pType, "We really want a SwDDEFieldType here!");
+ return pType;
+}
+
+class TableBoxIndex
+{
+public:
+ OUString msName;
+ sal_Int32 mnWidth;
+ bool mbProtected;
+
+ TableBoxIndex( OUString aName, sal_Int32 nWidth,
+ bool bProtected ) :
+ msName(std::move( aName )),
+ mnWidth( nWidth ),
+ mbProtected( bProtected )
+ { }
+
+ bool operator== ( const TableBoxIndex& rArg ) const
+ {
+ return (rArg.mnWidth == mnWidth) &&
+ (rArg.mbProtected == mbProtected) &&
+ (rArg.msName == msName);
+ }
+};
+
+class TableBoxIndexHasher
+{
+public:
+ size_t operator() (const TableBoxIndex& rArg) const
+ {
+ return rArg.msName.hashCode() + rArg.mnWidth + (rArg.mbProtected ? 1 : 0);
+ }
+};
+
+const SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow,
+ sal_uInt32 nCol ) const
+{
+ return (*m_pRows)[nRow]->GetCell( nCol );
+}
+
+SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow,
+ sal_uInt32 nCol )
+{
+ return (*m_pRows)[nRow]->GetCell( nCol );
+}
+
+
+SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList ) :
+ XMLTextTableContext( rImport ),
+ m_pRows( new SwXMLTableRows_Impl ),
+ m_pTableNode( nullptr ),
+ m_pBox1( nullptr ),
+ m_bOwnsBox1( false ),
+ m_pSttNd1( nullptr ),
+ m_pBoxFormat( nullptr ),
+ m_pLineFormat( nullptr ),
+ m_bFirstSection( true ),
+ m_bRelWidth( true ),
+ m_bHasSubTables( false ),
+ m_nHeaderRows( 0 ),
+ m_nCurRow( 0 ),
+ m_nCurCol( 0 ),
+ m_nNonMergedCurCol( 0 ),
+ m_nWidth( 0 )
+{
+ OUString aName;
+ OUString sXmlId;
+
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ const OUString sValue = aIter.toString();
+ switch(aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ m_aStyleName = sValue;
+ break;
+ case XML_ELEMENT(TABLE, XML_NAME):
+ aName = sValue;
+ break;
+ case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME):
+ m_aDfltCellStyleName = sValue;
+ break;
+ case XML_ELEMENT(TABLE, XML_TEMPLATE_NAME):
+ m_aTemplateName = sValue;
+ break;
+ case XML_ELEMENT(XML, XML_ID):
+ sXmlId = sValue;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sw", aIter);
+ }
+ }
+
+ SwDoc *pDoc = GetSwImport().getDoc();
+
+ OUString sTableName;
+ if( !aName.isEmpty() )
+ {
+ const SwTableFormat *pTableFormat = pDoc->FindTableFormatByName( aName );
+ if( !pTableFormat )
+ sTableName = aName;
+ }
+ if( sTableName.isEmpty() )
+ {
+ // Optimization: use import's own map to create unique names, because
+ // SwDoc::GetUniqueTableName scans all the already present tables,
+ // builds a bitset using rather complex rules, and that has quadratic
+ // complexity. Try once, then fallback to SwDoc::GetUniqueTableName
+ auto& tableNameMap = rImport.GetTableNameMap();
+ sal_Int32 nextIx = ++tableNameMap[aName];
+ OUString test = aName.isEmpty()
+ ? OUString(rImport.GetDefTableName() + OUString::number(nextIx))
+ : OUString(aName + "_" + OUString::number(nextIx));
+ if (const SwTableFormat* pExisting = pDoc->FindTableFormatByName(test); !pExisting)
+ sTableName = test;
+ else
+ sTableName = pDoc->GetUniqueTableName();
+ GetImport().GetTextImport()
+ ->GetRenameMap().Add( XML_TEXT_RENAME_TYPE_TABLE, aName, sTableName );
+ }
+
+ Reference< XTextTable > xTable;
+ SwXTextTable *pXTable = nullptr;
+ Reference<XMultiServiceFactory> xFactory( GetImport().GetModel(),
+ UNO_QUERY );
+ OSL_ENSURE( xFactory.is(), "factory missing" );
+ if( xFactory.is() )
+ {
+ Reference<XInterface> xIfc = xFactory->createInstance( "com.sun.star.text.TextTable" );
+ OSL_ENSURE( xIfc.is(), "Couldn't create a table" );
+
+ if( xIfc.is() )
+ xTable.set( xIfc, UNO_QUERY );
+ }
+
+ if( xTable.is() )
+ {
+ xTable->initialize( 1, 1 );
+ if (auto xPropSet = xTable.query<css::beans::XPropertySet>())
+ xPropSet->setPropertyValue(UNO_NAME_TABLE_NAME, css::uno::Any(sTableName));
+
+ try
+ {
+ m_xTextContent = xTable;
+ GetImport().GetTextImport()->InsertTextContent( m_xTextContent );
+ }
+ catch( IllegalArgumentException& )
+ {
+ xTable = nullptr;
+ }
+ }
+
+ if( xTable.is() )
+ {
+ //FIXME
+ // xml:id for RDF metadata
+ GetImport().SetXmlId(xTable, sXmlId);
+
+ pXTable = dynamic_cast<SwXTextTable*>(xTable.get());
+
+ Reference < XCellRange > xCellRange( xTable, UNO_QUERY );
+ Reference < XCell > xCell = xCellRange->getCellByPosition( 0, 0 );
+ Reference < XText> xText( xCell, UNO_QUERY );
+ m_xOldCursor = GetImport().GetTextImport()->GetCursor();
+ GetImport().GetTextImport()->SetCursor( xText->createTextCursor() );
+
+ // take care of open redlines for tables
+ GetImport().GetTextImport()->RedlineAdjustStartNodeCursor();
+ }
+ if( !pXTable )
+ return;
+
+ SwFrameFormat *const pTableFrameFormat = pXTable->GetFrameFormat();
+ OSL_ENSURE( pTableFrameFormat, "table format missing" );
+ SwTable *pTable = SwTable::FindTable( pTableFrameFormat );
+ OSL_ENSURE( pTable, "table missing" );
+ m_pTableNode = pTable->GetTableNode();
+ OSL_ENSURE( m_pTableNode, "table node missing" );
+
+ SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U];
+ m_pBox1 = pLine1->GetTabBoxes()[0U];
+ m_pSttNd1 = m_pBox1->GetSttNd();
+}
+
+SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport,
+ SwXMLTableContext *pTable ) :
+ XMLTextTableContext( rImport ),
+ m_pRows( new SwXMLTableRows_Impl ),
+ m_pTableNode( pTable->m_pTableNode ),
+ m_pBox1( nullptr ),
+ m_bOwnsBox1( false ),
+ m_pSttNd1( nullptr ),
+ m_pBoxFormat( nullptr ),
+ m_pLineFormat( nullptr ),
+ m_xParentTable( pTable ),
+ m_bFirstSection( false ),
+ m_bRelWidth( true ),
+ m_bHasSubTables( false ),
+ m_nHeaderRows( 0 ),
+ m_nCurRow( 0 ),
+ m_nCurCol( 0 ),
+ m_nNonMergedCurCol( 0 ),
+ m_nWidth( 0 )
+{
+}
+
+SwXMLTableContext::~SwXMLTableContext()
+{
+ if (m_bOwnsBox1)
+ delete m_pBox1;
+ m_xColumnDefaultCellStyleNames.reset();
+ m_pSharedBoxFormats.reset();
+ m_pRows.reset();
+
+ // close redlines on table end nodes
+ GetImport().GetTextImport()->RedlineAdjustStartNodeCursor();
+}
+
+css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableContext::createFastChildContext(
+ sal_Int32 nElement,
+ const Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ bool bHeader = false;
+ switch (nElement)
+ {
+ case XML_ELEMENT(TABLE, XML_TABLE_ROW):
+ case XML_ELEMENT(LO_EXT, XML_TABLE_ROW):
+ if( IsInsertRowPossible() )
+ return new SwXMLTableRowContext_Impl( GetSwImport(), nElement, xAttrList, this );
+ break;
+ case XML_ELEMENT(TABLE, XML_TABLE_HEADER_ROWS):
+ bHeader = true;
+ [[fallthrough]];
+ case XML_ELEMENT(TABLE, XML_TABLE_ROWS):
+ return new SwXMLTableRowsContext_Impl( GetSwImport(), this, bHeader );
+ case XML_ELEMENT(TABLE, XML_TABLE_HEADER_COLUMNS):
+ case XML_ELEMENT(TABLE, XML_TABLE_COLUMNS):
+ // There are slight differences between <table:table-columns> and
+ // <table:table-columns-groups>. However, none of these are
+ // supported in Writer (they are Calc-only features), so we
+ // support column groups by simply using the <table:table-columns>
+ // token for column groups, too.
+ case XML_ELEMENT(TABLE, XML_TABLE_COLUMN_GROUP):
+ if( IsValid() )
+ return new SwXMLTableColsContext_Impl( GetSwImport(), this );
+ break;
+ case XML_ELEMENT(TABLE, XML_TABLE_COLUMN):
+ case XML_ELEMENT(LO_EXT, XML_TABLE_COLUMN):
+ if( IsValid() && IsInsertColPossible() )
+ return new SwXMLTableColContext_Impl( GetSwImport(), xAttrList,
+ this );
+ break;
+ case XML_ELEMENT(OFFICE, XML_DDE_SOURCE):
+ // save context for later processing (discard old context, if approp.)
+ if( IsValid() )
+ {
+ m_xDDESource.set(new SwXMLDDETableContext_Impl( GetSwImport() ));
+ return m_xDDESource;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void SwXMLTableContext::InsertColumn( sal_Int32 nWidth2, bool bRelWidth2,
+ const OUString *pDfltCellStyleName )
+{
+ OSL_ENSURE( m_nCurCol < USHRT_MAX,
+ "SwXMLTableContext::InsertColumn: no space left" );
+ if( m_nCurCol >= USHRT_MAX )
+ return;
+
+ if( nWidth2 < MINLAY )
+ nWidth2 = MINLAY;
+ else if( nWidth2 > MAX_WIDTH )
+ nWidth2 = MAX_WIDTH;
+ m_aColumnWidths.emplace_back(nWidth2, bRelWidth2 );
+ if( !((pDfltCellStyleName && !pDfltCellStyleName->isEmpty()) ||
+ m_xColumnDefaultCellStyleNames) )
+ return;
+
+ if( !m_xColumnDefaultCellStyleNames )
+ {
+ m_xColumnDefaultCellStyleNames.emplace();
+ sal_uLong nCount = m_aColumnWidths.size() - 1;
+ while( nCount-- )
+ m_xColumnDefaultCellStyleNames->push_back(OUString());
+ }
+
+ if(pDfltCellStyleName)
+ m_xColumnDefaultCellStyleNames->push_back(*pDfltCellStyleName);
+ else
+ m_xColumnDefaultCellStyleNames->push_back(OUString());
+}
+
+sal_Int32 SwXMLTableContext::GetColumnWidth( sal_uInt32 nCol,
+ sal_uInt32 nColSpan ) const
+{
+ sal_uInt32 nLast = nCol+nColSpan;
+ if( nLast > m_aColumnWidths.size() )
+ nLast = m_aColumnWidths.size();
+
+ sal_Int32 nWidth2 = 0;
+ for( sal_uInt32 i=nCol; i < nLast; ++i )
+ nWidth2 += m_aColumnWidths[i].width;
+
+ return nWidth2;
+}
+
+OUString SwXMLTableContext::GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const
+{
+ if( m_xColumnDefaultCellStyleNames && nCol < m_xColumnDefaultCellStyleNames->size())
+ return (*m_xColumnDefaultCellStyleNames)[static_cast<size_t>(nCol)];
+
+ return OUString();
+}
+
+void SwXMLTableContext::InsertCell( const OUString& rStyleName,
+ sal_uInt32 nRowSpan, sal_uInt32 nColSpan,
+ const SwStartNode *pStartNode,
+ SwXMLTableContext *pTable,
+ bool bProtect,
+ const OUString* pFormula,
+ bool bHasValue,
+ double fValue,
+ OUString const*const pStringValue )
+{
+ OSL_ENSURE( m_nCurCol < GetColumnCount(),
+ "SwXMLTableContext::InsertCell: row is full" );
+ OSL_ENSURE( m_nCurRow < USHRT_MAX,
+ "SwXMLTableContext::InsertCell: table is full" );
+ if( m_nCurCol >= USHRT_MAX || m_nCurRow > USHRT_MAX )
+ return;
+
+ OSL_ENSURE( nRowSpan >=1, "SwXMLTableContext::InsertCell: row span is 0" );
+ if( 0 == nRowSpan )
+ nRowSpan = 1;
+ OSL_ENSURE( nColSpan >=1, "SwXMLTableContext::InsertCell: col span is 0" );
+ if( 0 == nColSpan )
+ nColSpan = 1;
+
+ // Until it is possible to add columns here, fix the column span.
+ sal_uInt32 nColsReq = m_nCurCol + nColSpan;
+ if( nColsReq > GetColumnCount() )
+ {
+ nColSpan = GetColumnCount() - m_nCurCol;
+ nColsReq = GetColumnCount();
+ }
+
+ // Check whether there are cells from a previous line already that reach
+ // into the current row.
+ if( m_nCurRow > 0 && nColSpan > 1 )
+ {
+ SwXMLTableRow_Impl *pCurRow = (*m_pRows)[m_nCurRow].get();
+ sal_uInt32 nLastCol = GetColumnCount() < nColsReq ? GetColumnCount()
+ : nColsReq;
+ for( sal_uInt32 i=m_nCurCol+1; i<nLastCol; ++i )
+ {
+ if( pCurRow->GetCell(i)->IsUsed() )
+ {
+ // If this cell is used, the column span is truncated
+ nColSpan = i - m_nCurCol;
+ nColsReq = i;
+ break;
+ }
+ }
+ }
+
+ sal_uInt32 nRowsReq = m_nCurRow + nRowSpan;
+ if( nRowsReq > USHRT_MAX )
+ {
+ nRowSpan = USHRT_MAX - m_nCurRow;
+ nRowsReq = USHRT_MAX;
+ }
+
+ // Add columns (if # required columns greater than # columns):
+ // This should never happen, since we require column definitions!
+ if ( nColsReq > GetColumnCount() )
+ {
+ for( sal_uInt32 i=GetColumnCount(); i<nColsReq; ++i )
+ {
+ m_aColumnWidths.emplace_back(MINLAY, true );
+ }
+ // adjust columns in *all* rows, if columns must be inserted
+ for (size_t i = 0; i < m_pRows->size(); ++i)
+ (*m_pRows)[i]->Expand( nColsReq, i<m_nCurRow );
+ }
+
+ // Add rows
+ if (m_pRows->size() < nRowsReq)
+ {
+ for (size_t i = m_pRows->size(); i < nRowsReq; ++i)
+ m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>(
+ "", GetColumnCount()));
+ }
+
+ OUString sStyleName( rStyleName );
+ if( sStyleName.isEmpty() )
+ {
+ sStyleName = (*m_pRows)[m_nCurRow]->GetDefaultCellStyleName();
+ if( sStyleName.isEmpty() && m_xColumnDefaultCellStyleNames )
+ {
+ sStyleName = GetColumnDefaultCellStyleName( m_nCurCol );
+ if( sStyleName.isEmpty() )
+ sStyleName = m_aDfltCellStyleName;
+ }
+ }
+
+ // Fill the cells
+ for( sal_uInt32 i=nColSpan; i>0; --i )
+ {
+ for( sal_uInt32 j=nRowSpan; j>0; --j )
+ {
+ const bool bCovered = i != nColSpan || j != nRowSpan;
+ SwXMLTableCell_Impl *pCell = GetCell( nRowsReq-j, nColsReq-i );
+ if (!pCell)
+ throw css::lang::IndexOutOfBoundsException();
+ pCell->Set( sStyleName, j, i, pStartNode,
+ pTable, bProtect, pFormula, bHasValue, bCovered, fValue,
+ pStringValue );
+ }
+ }
+
+ // Set current col to the next (free) column
+ m_nCurCol = nColsReq;
+ m_nNonMergedCurCol = nColsReq;
+ while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() )
+ m_nCurCol++;
+}
+
+void SwXMLTableContext::InsertCoveredCell(const OUString& rStyleName)
+{
+ const IDocumentSettingAccess& rIDSA = GetSwImport().getDoc()->getIDocumentSettingAccess();
+ bool bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ if (!bWordTableCell)
+ {
+ // Compatibility flag not active, ignore formatting of covered cells.
+ return;
+ }
+
+ SwXMLTableCell_Impl* pCell = GetCell(m_nCurRow, m_nNonMergedCurCol);
+ ++m_nNonMergedCurCol;
+ if (!pCell)
+ {
+ return;
+ }
+
+ pCell->SetStyleName(rStyleName);
+}
+
+void SwXMLTableContext::InsertRow( const OUString& rStyleName,
+ const OUString& rDfltCellStyleName,
+ bool bInHead )
+{
+ OSL_ENSURE( m_nCurRow < USHRT_MAX,
+ "SwXMLTableContext::InsertRow: no space left" );
+ if( m_nCurRow >= USHRT_MAX )
+ return;
+
+ // Make sure there is at least one column.
+ if( 0==m_nCurRow && 0 == GetColumnCount() )
+ InsertColumn( USHRT_MAX, true );
+
+ if (m_nCurRow < m_pRows->size())
+ {
+ // The current row has already been inserted because of a row span
+ // of a previous row.
+ (*m_pRows)[m_nCurRow]->Set(
+ rStyleName, rDfltCellStyleName );
+ }
+ else
+ {
+ // add a new row
+ m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>(
+ rStyleName, GetColumnCount(),
+ &rDfltCellStyleName));
+ }
+
+ // We start at the first column ...
+ m_nCurCol=0;
+ m_nNonMergedCurCol = 0;
+
+ // ... but this cell may be occupied already.
+ while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() )
+ m_nCurCol++;
+
+ if( bInHead && m_nHeaderRows == m_nCurRow )
+ m_nHeaderRows++;
+}
+
+void SwXMLTableContext::InsertRepRows( sal_uInt32 nCount )
+{
+ const SwXMLTableRow_Impl *pSrcRow = (*m_pRows)[m_nCurRow-1].get();
+ while( nCount > 1 && IsInsertRowPossible() )
+ {
+ InsertRow( pSrcRow->GetStyleName(), pSrcRow->GetDefaultCellStyleName(),
+ false );
+ while( m_nCurCol < GetColumnCount() )
+ {
+ if( !GetCell(m_nCurRow,m_nCurCol)->IsUsed() )
+ {
+ const SwXMLTableCell_Impl *pSrcCell =
+ GetCell( m_nCurRow-1, m_nCurCol );
+ InsertCell( pSrcCell->GetStyleName(), 1U,
+ pSrcCell->GetColSpan(),
+ InsertTableSection(),
+ nullptr, pSrcCell->IsProtected(),
+ &pSrcCell->GetFormula(),
+ pSrcCell->HasValue(), pSrcCell->GetValue(),
+ pSrcCell->GetStringValue() );
+ }
+ }
+ FinishRow();
+ nCount--;
+ }
+}
+
+void SwXMLTableContext::FinishRow()
+{
+ // Insert an empty cell at the end of the line if the row is not complete
+ if( m_nCurCol < GetColumnCount() )
+ {
+ InsertCell( "", 1U, GetColumnCount() - m_nCurCol,
+ InsertTableSection() );
+ }
+
+ // Move to the next row.
+ m_nCurRow++;
+}
+
+const SwStartNode *SwXMLTableContext::GetPrevStartNode( sal_uInt32 nRow,
+ sal_uInt32 nCol ) const
+{
+ const SwXMLTableCell_Impl *pPrevCell = nullptr;
+ if( GetColumnCount() == nCol )
+ {
+ // The last cell is the right one here.
+ pPrevCell = GetCell( m_pRows->size() - 1U, GetColumnCount() - 1 );
+ }
+ else if( nCol > 0 )
+ {
+ // The previous cell in this row.
+ pPrevCell = GetCell( nRow, nCol-1 );
+ }
+ else if( nRow > 0 )
+ {
+ // The last cell from the previous row.
+ pPrevCell = GetCell( nRow-1, GetColumnCount()-1 );
+ }
+
+ const SwStartNode *pSttNd = nullptr;
+ if( pPrevCell )
+ {
+ if( pPrevCell->GetStartNode() )
+ pSttNd = pPrevCell->GetStartNode();
+ // #i95726# - Some fault tolerance
+// else
+ else if ( pPrevCell->GetSubTable() )
+ pSttNd = pPrevCell->GetSubTable()->GetLastStartNode();
+
+ OSL_ENSURE( pSttNd != nullptr,
+ "table corrupt" );
+ }
+
+ return pSttNd;
+}
+
+void SwXMLTableContext::FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol,
+ sal_uInt32 nColSpan )
+{
+ sal_uInt32 nLastCol = nCol + nColSpan;
+ for( sal_uInt32 i = nCol; i < nLastCol; i++ )
+ {
+ sal_uInt32 j = nRow;
+ sal_uInt32 nRowSpan = 1;
+ SwXMLTableCell_Impl *pCell = GetCell( j, i );
+ while( pCell && pCell->GetRowSpan() > 1 )
+ {
+ pCell->SetRowSpan( nRowSpan++ );
+ pCell = j > 0 ? GetCell( --j, i ) : nullptr;
+ }
+ }
+}
+
+void SwXMLTableContext::ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows )
+{
+ const SwStartNode *pPrevSttNd = GetPrevStartNode( nRow, nCol );
+ const SwStartNode *pSttNd = InsertTableSection( pPrevSttNd );
+
+ const SwXMLTableCell_Impl *pCell = GetCell( nRow, nCol );
+ sal_uInt32 nLastRow = bRows ? nRow + pCell->GetRowSpan() : nRow + 1;
+ sal_uInt32 nLastCol = nCol + pCell->GetColSpan();
+
+ for( sal_uInt32 i=nRow; i<nLastRow; i++ )
+ {
+ SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get();
+ for( sal_uInt32 j=nCol; j<nLastCol; j++ )
+ pRow->GetCell( j )->SetStartNode( pSttNd );
+ }
+
+}
+
+SwTableBox *SwXMLTableContext::NewTableBox( const SwStartNode *pStNd,
+ SwTableLine *pUpper )
+{
+ // The topmost table is the only table that maintains the two members
+ // pBox1 and bFirstSection.
+ if( m_xParentTable.is() )
+ return m_xParentTable->NewTableBox( pStNd, pUpper );
+
+ SwTableBox *pBox;
+
+ if( m_pBox1 &&
+ m_pBox1->GetSttNd() == pStNd )
+ {
+ // if the StartNode is equal to the StartNode of the initially
+ // created box, we use this box
+ pBox = m_pBox1;
+ pBox->SetUpper( pUpper );
+ m_pBox1 = nullptr;
+ m_bOwnsBox1 = false;
+ }
+ else
+ pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper );
+
+ return pBox;
+}
+
+SwTableBoxFormat* SwXMLTableContext::GetSharedBoxFormat(
+ SwTableBox* pBox,
+ const OUString& rStyleName,
+ sal_Int32 nColumnWidth,
+ bool bProtected,
+ bool bMayShare,
+ bool& bNew,
+ bool* pModifyLocked )
+{
+ if ( m_pSharedBoxFormats == nullptr )
+ m_pSharedBoxFormats.reset(new map_BoxFormat);
+
+ SwTableBoxFormat* pBoxFormat2;
+
+ TableBoxIndex aKey( rStyleName, nColumnWidth, bProtected );
+ map_BoxFormat::iterator aIter = m_pSharedBoxFormats->find( aKey );
+ if ( aIter == m_pSharedBoxFormats->end() )
+ {
+ // unknown format so far -> construct a new one
+
+ // get the old format, and reset all attributes
+ // (but preserve FillOrder)
+ pBoxFormat2 = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat());
+ SwFormatFillOrder aFillOrder( pBoxFormat2->GetFillOrder() );
+ pBoxFormat2->ResetAllFormatAttr(); // #i73790# - method renamed
+ pBoxFormat2->SetFormatAttr( aFillOrder );
+ bNew = true; // it's a new format now
+
+ // share this format, if allowed
+ if ( bMayShare )
+ (*m_pSharedBoxFormats)[ aKey ] = pBoxFormat2;
+ }
+ else
+ {
+ // set the shared format
+ pBoxFormat2 = aIter->second;
+ pBox->ChgFrameFormat( pBoxFormat2, /*bNeedToReregister*/false );
+ bNew = false; // copied from an existing format
+
+ // claim it, if we are not allowed to share
+ if ( !bMayShare )
+ pBoxFormat2 = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat());
+ }
+
+ // lock format (if so desired)
+ if ( pModifyLocked != nullptr )
+ {
+ (*pModifyLocked) = pBoxFormat2->IsModifyLocked();
+ pBoxFormat2->LockModify();
+ }
+
+ return pBoxFormat2;
+}
+
+SwTableBox *SwXMLTableContext::MakeTableBox( SwTableLine *pUpper,
+ sal_uInt32 nTopRow,
+ sal_uInt32 nLeftCol,
+ sal_uInt32 nBottomRow,
+ sal_uInt32 nRightCol )
+{
+ //FIXME: here would be a great place to handle XmlId for cell
+ SwTableBox *pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
+
+ sal_uInt32 nColSpan = nRightCol - nLeftCol;
+ sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan );
+
+ // TODO: Share formats!
+ SwFrameFormat *pFrameFormat = pBox->ClaimFrameFormat();
+ SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() );
+ pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed
+ pFrameFormat->SetFormatAttr( aFillOrder );
+
+ pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) );
+
+ SwTableLines& rLines = pBox->GetTabLines();
+ bool bSplitted = false;
+
+ while( !bSplitted )
+ {
+ sal_uInt32 nStartRow = nTopRow;
+ sal_uInt32 i;
+
+ for( i = nTopRow; i < nBottomRow; i++ )
+ {
+ // Could the table be split behind the current row?
+ bool bSplit = true;
+ SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get();
+ for( sal_uInt32 j=nLeftCol; j<nRightCol; j++ )
+ {
+ bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() );
+ if( !bSplit )
+ break;
+ }
+ if( bSplit && (nStartRow>nTopRow || i+1<nBottomRow) )
+ {
+ SwTableLine *pLine =
+ MakeTableLine( pBox, nStartRow, nLeftCol, i+1,
+ nRightCol );
+
+ rLines.push_back( pLine );
+
+ nStartRow = i+1;
+ bSplitted = true;
+ }
+ }
+ if( !bSplitted )
+ {
+ // No splitting was possible. That for, we have to force it.
+ // Ruthless!
+
+ nStartRow = nTopRow;
+ while( nStartRow < nBottomRow )
+ {
+ sal_uInt32 nMaxRowSpan = 0;
+ SwXMLTableRow_Impl *pStartRow = (*m_pRows)[nStartRow].get();
+ const SwXMLTableCell_Impl *pCell;
+ for( i=nLeftCol; i<nRightCol; i++ )
+ {
+ pCell = pStartRow->GetCell(i);
+ if( pCell->GetRowSpan() > nMaxRowSpan )
+ nMaxRowSpan = pCell->GetRowSpan();
+ }
+
+ nStartRow += nMaxRowSpan;
+ if( nStartRow<nBottomRow )
+ {
+ SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[nStartRow - 1U].get();
+ i = nLeftCol;
+ while( i < nRightCol )
+ {
+ if( pPrevRow->GetCell(i)->GetRowSpan() > 1 )
+ {
+ const SwXMLTableCell_Impl *pCell2 =
+ GetCell( nStartRow, i );
+ const sal_uInt32 nColSpan2 = pCell2->GetColSpan();
+ FixRowSpan( nStartRow-1, i, nColSpan2 );
+ ReplaceWithEmptyCell( nStartRow, i, true );
+ i += nColSpan2;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ }
+ // and now start over again...
+ }
+ }
+
+ return pBox;
+}
+
+SwTableBox *SwXMLTableContext::MakeTableBox(
+ SwTableLine *pUpper, const SwXMLTableCell_Impl *pCell,
+ sal_uInt32 nLeftCol, sal_uInt32 nRightCol )
+{
+ //FIXME: here would be a great place to handle XmlId for cell
+ SwTableBox *pBox;
+ sal_uInt32 nColSpan = nRightCol - nLeftCol;
+ sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan );
+
+ if( pCell->GetStartNode() )
+ {
+ pBox = NewTableBox( pCell->GetStartNode(), pUpper );
+ }
+ else
+ {
+ // and it is a table: therefore we build a new box and
+ // put the rows of the table into the rows of the box
+ pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
+ pCell->GetSubTable()->MakeTable( pBox, nColWidth );
+ }
+
+ // Share formats!
+ const OUString sStyleName = pCell->GetStyleName();
+ bool bModifyLocked;
+ bool bNew;
+ SwTableBoxFormat *pBoxFormat2 = GetSharedBoxFormat(
+ pBox, sStyleName, nColWidth, pCell->IsProtected(),
+ pCell->GetStartNode() && pCell->GetFormula().isEmpty() &&
+ ! pCell->HasValue(),
+ bNew, &bModifyLocked );
+
+ // if a new format was created, then we need to set the style
+ if ( bNew )
+ {
+ // set style
+ const SfxItemSet *pAutoItemSet = nullptr;
+ if( pCell->GetStartNode() && !sStyleName.isEmpty() &&
+ GetSwImport().FindAutomaticStyle(
+ XmlStyleFamily::TABLE_CELL, sStyleName, &pAutoItemSet ) )
+ {
+ if( pAutoItemSet )
+ pBoxFormat2->SetFormatAttr( *pAutoItemSet );
+ }
+ }
+
+ if( pCell->GetStartNode() )
+ {
+ if (pCell->HasStringValue())
+ {
+ SwNodeIndex const aNodeIndex(*(pCell->GetStartNode()), 1);
+ SwTextNode *const pTextNode(aNodeIndex.GetNode().GetTextNode());
+ SAL_WARN_IF(!pTextNode, "sw", "Should have a text node in cell?");
+ if (pTextNode)
+ {
+ SAL_WARN_IF(!pTextNode->GetText().isEmpty(), "sw",
+ "why text here?");
+ pTextNode->InsertText(*pCell->GetStringValue(),
+ SwContentIndex(pTextNode, 0));
+ }
+ }
+
+ // try to rescue broken documents with a certain pattern
+ // if: 1) the cell has a default number format (number 0)
+ // 2) the call has no formula
+ // 3) the value is 0.0
+ // 4) the text doesn't look anything like 0.0
+ // [read: length > 10, or length smaller 10 and no 0 in it]
+ // then make it a text cell!
+ bool bSuppressNumericContent = false;
+ if( pCell->HasValue() && (pCell->GetValue() == 0.0) &&
+ pCell->GetFormula().isEmpty() &&
+ !sStyleName.isEmpty() )
+ {
+ // default num format?
+ if( const SwTableBoxNumFormat* pNumFormat = pBoxFormat2->GetItemIfSet( RES_BOXATR_FORMAT, false ) )
+ {
+ if (pNumFormat && (pNumFormat->GetValue() % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ {
+ // only one text node?
+ SwNodeIndex aNodeIndex( *(pCell->GetStartNode()), 1 );
+ if( ( aNodeIndex.GetNode().EndOfSectionIndex() -
+ aNodeIndex.GetNode().StartOfSectionIndex() ) == SwNodeOffset(2) )
+ {
+ SwTextNode* pTextNode= aNodeIndex.GetNode().GetTextNode();
+ if( pTextNode != nullptr )
+ {
+ // check text: does it look like some form of 0.0?
+ const OUString& rText = pTextNode->GetText();
+ if( ( rText.getLength() > 10 ) ||
+ ( rText.indexOf( '0' ) == -1 ) )
+ {
+ bSuppressNumericContent = true;
+ }
+ }
+ }
+ else
+ bSuppressNumericContent = true; // several nodes
+ }
+ }
+ }
+
+ if( bSuppressNumericContent )
+ {
+ // suppress numeric content? Then reset number format!
+ pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMULA );
+ pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMAT );
+ pBoxFormat2->ResetFormatAttr( RES_BOXATR_VALUE );
+ }
+ else
+ {
+ // the normal case: set formula and value (if available)
+
+ const OUString& rFormula = pCell->GetFormula();
+ if (!rFormula.isEmpty())
+ {
+ // formula cell: insert formula if valid
+ SwTableBoxFormula aFormulaItem( rFormula );
+ pBoxFormat2->SetFormatAttr( aFormulaItem );
+ }
+ else if (!pCell->HasValue() && pCell->HasStringValue())
+ {
+ // Check for another inconsistency:
+ // No value but a non-textual format, i.e. a number format
+ // Solution: the number format will be removed,
+ // the cell gets the default text format.
+ if( const SwTableBoxNumFormat* pNumFormat = m_pBoxFormat->GetItemIfSet( RES_BOXATR_FORMAT, false ) )
+ {
+ const SwDoc* pDoc = m_pBoxFormat->GetDoc();
+ const SvNumberFormatter* pNumberFormatter = pDoc ?
+ pDoc->GetNumberFormatter() : nullptr;
+ if( pNumFormat != nullptr && pNumberFormatter &&
+ !pNumberFormatter->GetEntry( pNumFormat->GetValue() )->IsTextFormat() )
+ m_pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
+ }
+ }
+ // always insert value, even if default
+ if( pCell->HasValue() )
+ {
+ SwTableBoxValue aValueItem( pCell->GetValue() );
+ pBoxFormat2->SetFormatAttr( aValueItem );
+ }
+ }
+
+ // update cell content depend on the default language
+ pBox->ActualiseValueBox();
+ }
+
+ // table cell protection
+ if( pCell->IsProtected() )
+ {
+ SvxProtectItem aProtectItem( RES_PROTECT );
+ aProtectItem.SetContentProtect( true );
+ pBoxFormat2->SetFormatAttr( aProtectItem );
+ }
+
+ // restore old modify-lock state
+ if (! bModifyLocked)
+ pBoxFormat2->UnlockModify();
+
+ pBoxFormat2->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) );
+
+ return pBox;
+}
+
+SwTableLine *SwXMLTableContext::MakeTableLine( SwTableBox *pUpper,
+ sal_uInt32 nTopRow,
+ sal_uInt32 nLeftCol,
+ sal_uInt32 nBottomRow,
+ sal_uInt32 nRightCol )
+{
+ //FIXME: here would be a great place to handle XmlId for row
+ SwTableLine *pLine;
+ if( !pUpper && 0UL==nTopRow )
+ {
+ pLine = m_pTableNode->GetTable().GetTabLines()[0U];
+ }
+ else
+ {
+ pLine = new SwTableLine( m_pLineFormat, 0, pUpper );
+ }
+
+ // TODO: Share formats!
+ SwFrameFormat *pFrameFormat = pLine->ClaimFrameFormat();
+ SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() );
+ pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed
+ pFrameFormat->SetFormatAttr( aFillOrder );
+
+ const SfxItemSet *pAutoItemSet = nullptr;
+ const OUString& rStyleName = (*m_pRows)[nTopRow]->GetStyleName();
+ if( 1 == (nBottomRow - nTopRow) &&
+ !rStyleName.isEmpty() &&
+ GetSwImport().FindAutomaticStyle(
+ XmlStyleFamily::TABLE_ROW, rStyleName, &pAutoItemSet ) )
+ {
+ if( pAutoItemSet )
+ pFrameFormat->SetFormatAttr( *pAutoItemSet );
+ }
+
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+
+ sal_uInt32 nStartCol = nLeftCol;
+ while( nStartCol < nRightCol )
+ {
+ for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ )
+ (*m_pRows)[nRow]->SetSplitable( true );
+
+ sal_uInt32 nCol = nStartCol;
+ sal_uInt32 nSplitCol = nRightCol;
+ bool bSplitted = false;
+ while( !bSplitted )
+ {
+ OSL_ENSURE( nCol < nRightCol, "Ran too far" );
+
+ // Can be split after current HTML table column?
+ // If yes, can the created region still be split to
+ // rows if the next column is added to it?
+ bool bSplit = true;
+ bool bHoriSplitMayContinue = false;
+ bool bHoriSplitPossible = false;
+
+ if ( m_bHasSubTables )
+ {
+ // Convert row spans if the table has subtables:
+ for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ )
+ {
+ SwXMLTableCell_Impl *pCell = GetCell(nRow,nCol);
+ // Could the table fragment be split horizontally behind
+ // the current line?
+ bool bHoriSplit = (*m_pRows)[nRow]->IsSplitable() &&
+ nRow+1 < nBottomRow &&
+ 1 == pCell->GetRowSpan();
+ (*m_pRows)[nRow]->SetSplitable( bHoriSplit );
+
+ // Could the table fragment be split vertically behind the
+ // current column (uptp the current line?
+ bSplit &= ( 1 == pCell->GetColSpan() );
+ if( bSplit )
+ {
+ bHoriSplitPossible |= bHoriSplit;
+
+ // Could the current table fragment be split
+ // horizontally behind the next column, too?
+ bHoriSplit &= (nCol+1 < nRightCol &&
+ 1 == GetCell(nRow,nCol+1)->GetRowSpan());
+ bHoriSplitMayContinue |= bHoriSplit;
+ }
+ }
+ }
+ else
+ {
+ // No subtables: we use the new table model.
+ SwXMLTableCell_Impl *pCell = GetCell(nTopRow,nCol);
+
+ // #i95726# - some fault tolerance
+ if ( pCell == nullptr )
+ {
+ OSL_FAIL( "table seems to be corrupt." );
+ break;
+ }
+
+ // Could the table fragment be split vertically behind the
+ // current column (uptp the current line?
+ bSplit = 1 == pCell->GetColSpan();
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ if( nCol == nRightCol-1 )
+ {
+ OSL_ENSURE( bSplit, "Split-Flag wrong" );
+ if ( m_bHasSubTables )
+ {
+ OSL_ENSURE( !bHoriSplitMayContinue,
+ "HoriSplitMayContinue-Flag wrong" );
+ SwXMLTableCell_Impl *pTmpCell = GetCell( nTopRow, nStartCol );
+ OSL_ENSURE( pTmpCell->GetRowSpan() != (nBottomRow-nTopRow) ||
+ !bHoriSplitPossible, "HoriSplitPossible-Flag wrong" );
+ }
+ }
+#endif
+
+ OSL_ENSURE( !m_bHasSubTables || !bHoriSplitMayContinue || bHoriSplitPossible,
+ "bHoriSplitMayContinue, but not bHoriSplitPossible" );
+
+ if( bSplit )
+ {
+ SwTableBox* pBox = nullptr;
+ SwXMLTableCell_Impl *pCell = GetCell( nTopRow, nStartCol );
+ // #i95726# - some fault tolerance
+ if( ( !m_bHasSubTables || ( pCell->GetRowSpan() == (nBottomRow-nTopRow) ) ) &&
+ pCell->GetColSpan() == (nCol+1-nStartCol) &&
+ ( pCell->GetStartNode() || pCell->GetSubTable() ) )
+ {
+ // insert new empty cell for covered cells:
+ sal_Int32 nBoxRowSpan = 1;
+ if ( !m_bHasSubTables )
+ {
+ nBoxRowSpan = pCell->GetRowSpan();
+ if ( pCell->IsCovered() )
+ {
+ nBoxRowSpan = -1 * nBoxRowSpan;
+ ReplaceWithEmptyCell( nTopRow, nStartCol, false );
+ }
+ }
+
+ // The remaining box neither contains lines nor rows (i.e.
+ // is a content box
+ nSplitCol = nCol + 1;
+
+ pBox = MakeTableBox( pLine, pCell, nStartCol, nSplitCol );
+
+ if ( 1 != nBoxRowSpan )
+ pBox->setRowSpan( nBoxRowSpan );
+
+ bSplitted = true;
+ }
+ else if( m_bHasSubTables && bHoriSplitPossible && bHoriSplitMayContinue )
+ {
+ // The table fragment could be split behind the current
+ // column, and the remaining fragment could be divided
+ // into lines. Anyway, it could be that this applies to
+ // the next column, too. That for, we check the next
+ // column but remember the current one as a good place to
+ // split.
+ nSplitCol = nCol + 1;
+ }
+ else if ( m_bHasSubTables )
+ {
+ // If the table resulting table fragment could be divided
+ // into lines if splitting behind the current column, but
+ // this doesn't apply for thr next column, we split begind
+ // the current column. This applies for the last column,
+ // too.
+ // If the resulting box cannot be split into rows,
+ // the split at the last split position we remembered.
+ if( bHoriSplitPossible || nSplitCol > nCol+1 )
+ {
+ OSL_ENSURE( !bHoriSplitMayContinue,
+ "bHoriSplitMayContinue==true" );
+ OSL_ENSURE( bHoriSplitPossible || nSplitCol == nRightCol,
+ "bHoriSplitPossible flag should be set" );
+
+ nSplitCol = nCol + 1;
+ }
+
+ pBox = MakeTableBox( pLine, nTopRow, nStartCol,
+ nBottomRow, nSplitCol );
+ bSplitted = true;
+ }
+
+ OSL_ENSURE( m_bHasSubTables || pBox, "Colspan trouble" );
+
+ if( pBox )
+ rBoxes.push_back( pBox );
+ }
+ nCol++;
+ }
+ nStartCol = nSplitCol;
+ }
+
+ return pLine;
+}
+
+void SwXMLTableContext::MakeTable_( SwTableBox *pBox )
+{
+ // fix column widths
+ sal_uInt32 nCols = GetColumnCount();
+
+ // If there are empty rows (because of some row span of previous rows)
+ // the have to be deleted. The previous rows have to be truncated.
+
+ if (m_pRows->size() > m_nCurRow)
+ {
+ SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[m_nCurRow - 1U].get();
+ const SwXMLTableCell_Impl *pCell;
+ for( size_t i = 0; i < m_aColumnWidths.size(); ++i )
+ {
+ pCell = pPrevRow->GetCell(i);
+ if( pCell->GetRowSpan() > 1 )
+ {
+ FixRowSpan( m_nCurRow-1, i, 1UL );
+ }
+ }
+ for (size_t i = m_pRows->size() - 1; i >= m_nCurRow; --i)
+ m_pRows->pop_back();
+ }
+
+ if (m_pRows->empty())
+ {
+ InsertCell( "", 1U, nCols, InsertTableSection() );
+ }
+
+ // TODO: Do we have to keep both values, the relative and the absolute
+ // width?
+ sal_Int32 nAbsWidth = 0;
+ sal_Int32 nMinAbsColWidth = 0;
+ sal_Int32 nRelWidth = 0;
+ sal_Int32 nMinRelColWidth = 0;
+ sal_uInt32 nRelCols = 0;
+ for( const auto& rCol : m_aColumnWidths)
+ {
+ if( rCol.isRelative )
+ {
+ nRelWidth += rCol.width;
+ if( 0 == nMinRelColWidth || rCol.width < nMinRelColWidth )
+ nMinRelColWidth = rCol.width;
+ nRelCols++;
+ }
+ else
+ {
+ nAbsWidth += rCol.width;
+ if( 0 == nMinAbsColWidth || rCol.width < nMinAbsColWidth )
+ nMinAbsColWidth = rCol.width;
+ }
+ }
+ sal_uInt32 nAbsCols = nCols - nRelCols;
+
+ if( m_bRelWidth )
+ {
+ // If there a columns that have an absolute width, we have to
+ // calculate a relative one for them.
+ if( nAbsCols > 0 )
+ {
+ // All column that have absolute widths get relative widths;
+ // these widths relate to each over like the original absolute
+ // widths. The smallest column gets a width that has the same
+ // value as the smallest column that has a relative width
+ // already.
+ if( 0 == nMinRelColWidth )
+ nMinRelColWidth = nMinAbsColWidth;
+
+ for( auto& rCol : m_aColumnWidths)
+ {
+ if( !rCol.isRelative )
+ {
+ if (nMinAbsColWidth == 0)
+ throw o3tl::divide_by_zero();
+ sal_Int32 nVal;
+ if (o3tl::checked_multiply<sal_Int32>(rCol.width, nMinRelColWidth, nVal))
+ throw std::overflow_error("overflow in multiply");
+ sal_Int32 nRelCol = nVal / nMinAbsColWidth;
+ rCol.width = nRelCol;
+ rCol.isRelative = true;
+ nRelWidth += nRelCol;
+ nAbsCols--;
+ if (nAbsCols <= 0)
+ break;
+ }
+ }
+ }
+
+ if( !m_nWidth )
+ {
+ // This happens only for percentage values for the table itself.
+ // In this case, the columns get the correct width even if
+ // the sum of the relative widths is smaller than the available
+ // width in TWIP. Therefore, we can use the relative width.
+ m_nWidth = std::min(nRelWidth, MAX_WIDTH);
+ }
+ if( nRelWidth != m_nWidth && nRelWidth && nCols )
+ {
+ double n = static_cast<double>(m_nWidth) / static_cast<double>(nRelWidth);
+ nRelWidth = 0;
+ for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter)
+ {
+ sal_Int32 nW = static_cast<sal_Int32>( colIter->width * n);
+ colIter->width = o3tl::narrowing<sal_uInt16>(nW);
+ nRelWidth += nW;
+ }
+ m_aColumnWidths.back().width = (m_nWidth-nRelWidth);
+ }
+ }
+ else
+ {
+ // If there are columns that have relative widths, we have to
+ // calculate an absolute widths for them.
+ if( nRelCols > 0 )
+ {
+ // The absolute space that is available for all columns with a
+ // relative width.
+ sal_Int32 nAbsForRelWidth =
+ m_nWidth > nAbsWidth ? m_nWidth - nAbsWidth : sal_Int32(0L);
+
+ // The relative width that has to be distributed in addition to
+ // equally widthed columns.
+ sal_Int32 nExtraRel = nRelWidth - (nRelCols * nMinRelColWidth);
+
+ // The absolute space that may be distributed in addition to
+ // minimum widthed columns.
+ sal_Int32 nMinAbs = nRelCols * MINLAY;
+ sal_Int32 nExtraAbs =
+ nAbsForRelWidth > nMinAbs ? nAbsForRelWidth - nMinAbs : sal_Int32(0L);
+
+ bool bMin = false; // Do all columns get the minimum width?
+ bool bMinExtra = false; // Do all columns get the minimum width plus
+ // some extra space?
+
+ if( nAbsForRelWidth <= nMinAbs )
+ {
+ // If there is not enough space left for all columns to
+ // get the minimum width, they get the minimum width, anyway.
+ nAbsForRelWidth = nMinAbs;
+ bMin = true;
+ }
+ else if( nAbsForRelWidth <= (nRelWidth * MINLAY) /
+ nMinRelColWidth )
+ {
+ // If there is enough space for all columns to get the
+ // minimum width, but not to get a width that takes the
+ // relative width into account, each column gets the minimum
+ // width plus some extra space that is based on the additional
+ // space that is available.
+ bMinExtra = true;
+ }
+ // Otherwise, if there is enough space for every column, every
+ // column gets this space.
+
+ for( auto& rCol : m_aColumnWidths )
+ {
+ if( rCol.isRelative )
+ {
+ sal_Int32 nAbsCol;
+ if( 1 == nRelCols )
+ {
+ // The last column that has a relative width gets
+ // all absolute space that is left.
+ nAbsCol = nAbsForRelWidth;
+ }
+ else
+ {
+ if( bMin )
+ {
+ nAbsCol = MINLAY;
+ }
+ else if( bMinExtra )
+ {
+ sal_Int32 nExtraRelCol = rCol.width - nMinRelColWidth;
+ nAbsCol = MINLAY + (nExtraRelCol * nExtraAbs) /
+ nExtraRel;
+ }
+ else
+ {
+ nAbsCol = ( rCol.width * nAbsForRelWidth) / nRelWidth;
+ }
+ }
+ rCol.width = nAbsCol;
+ rCol.isRelative = false;
+ nAbsForRelWidth -= nAbsCol;
+ nAbsWidth += nAbsCol;
+ nRelCols--;
+ if (nRelCols <= 0)
+ break;
+ }
+ }
+ }
+
+ if( nCols && nAbsWidth )
+ {
+ if( nAbsWidth < m_nWidth )
+ {
+ // If the table's width is larger than the sum of the absolute
+ // column widths, every column get some extra width.
+ sal_Int32 nExtraAbs = m_nWidth - nAbsWidth;
+ sal_Int32 nAbsLastCol = m_aColumnWidths.back().width + nExtraAbs;
+ for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter )
+ {
+ sal_Int32 nAbsCol = colIter->width;
+ sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) /
+ nAbsWidth;
+ nAbsCol += nExtraAbsCol;
+ colIter->width = nAbsCol;
+ nAbsLastCol -= nExtraAbsCol;
+ }
+ m_aColumnWidths.back().width = nAbsLastCol;
+ }
+ else if( nAbsWidth > m_nWidth )
+ {
+ // If the table's width is smaller than the sum of the absolute
+ // column widths, every column needs to shrink.
+ // Every column gets the minimum width plus some extra width.
+ sal_Int32 nExtraAbs = m_nWidth - (nCols * MINLAY);
+ sal_Int32 nAbsLastCol = MINLAY + nExtraAbs;
+ for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter )
+ {
+ sal_Int32 nAbsCol = colIter->width;
+ sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) /
+ nAbsWidth;
+ nAbsCol = MINLAY + nExtraAbsCol;
+ colIter->width = nAbsCol;
+ nAbsLastCol -= nExtraAbsCol;
+ }
+ m_aColumnWidths.back().width = nAbsLastCol;
+ }
+ }
+ }
+
+ SwTableLines& rLines =
+ pBox ? pBox->GetTabLines()
+ : m_pTableNode->GetTable().GetTabLines();
+
+ sal_uInt32 nStartRow = 0;
+ sal_uInt32 nRows = m_pRows->size();
+ for(sal_uInt32 i=0; i<nRows; ++i )
+ {
+ // Could we split the table behind the current line?
+ bool bSplit = true;
+ if ( m_bHasSubTables )
+ {
+ SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get();
+ for( sal_uInt32 j=0; j<nCols; j++ )
+ {
+ bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() );
+ if( !bSplit )
+ break;
+ }
+ }
+
+ if( bSplit )
+ {
+ SwTableLine *pLine =
+ MakeTableLine( pBox, nStartRow, 0UL, i+1, nCols );
+ if( pBox || nStartRow>0 )
+ rLines.push_back( pLine );
+ nStartRow = i+1;
+ }
+ }
+}
+
+void SwXMLTableContext::MakeTable()
+{
+ // this method will modify the document directly -> lock SolarMutex
+ // This will call all other MakeTable*(..) methods, so
+ // those don't need to be locked separately.
+ SolarMutexGuard aGuard;
+
+ // #i97274# handle invalid tables
+ if (!m_pRows || m_pRows->empty() || !GetColumnCount())
+ {
+ OSL_FAIL("invalid table: no cells; deleting...");
+ m_pTableNode->GetDoc().getIDocumentContentOperations().DeleteSection( m_pTableNode );
+ m_pTableNode = nullptr;
+ m_pBox1 = nullptr;
+ m_bOwnsBox1 = false;
+ m_pSttNd1 = nullptr;
+ return;
+ }
+
+ SwXMLImport& rSwImport = GetSwImport();
+
+ SwFrameFormat *pFrameFormat = m_pTableNode->GetTable().GetFrameFormat();
+
+ sal_Int16 eHoriOrient = text::HoriOrientation::FULL;
+ bool bSetHoriOrient = false;
+
+ sal_uInt8 nPercentWidth = 0U;
+
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName( m_aTemplateName, sStyleName, SwGetPoolIdFromName::TabStyle );
+ m_pTableNode->GetTable().SetTableStyleName( sStyleName );
+ m_pTableNode->GetTable().SetRowsToRepeat( m_nHeaderRows );
+ m_pTableNode->GetTable().SetTableModel( !m_bHasSubTables );
+
+ const SfxItemSet *pAutoItemSet = nullptr;
+ if( !m_aStyleName.isEmpty() &&
+ rSwImport.FindAutomaticStyle(
+ XmlStyleFamily::TABLE_TABLE, m_aStyleName, &pAutoItemSet ) &&
+ pAutoItemSet )
+ {
+ const SvxLRSpaceItem *pLRSpace =
+ pAutoItemSet->GetItemIfSet( RES_LR_SPACE, false );
+
+ if( const SwFormatHoriOrient* pItem = pAutoItemSet->GetItemIfSet( RES_HORI_ORIENT, false ) )
+ {
+ eHoriOrient = pItem->GetHoriOrient();
+ switch( eHoriOrient )
+ {
+ case text::HoriOrientation::FULL:
+ if( pLRSpace )
+ {
+ eHoriOrient = text::HoriOrientation::NONE;
+ bSetHoriOrient = true;
+ }
+ break;
+ case text::HoriOrientation::LEFT:
+ if( pLRSpace )
+ {
+ eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH;
+ bSetHoriOrient = true;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ {
+ bSetHoriOrient = true;
+ }
+
+ const SwFormatFrameSize *pSize =
+ pAutoItemSet->GetItemIfSet( RES_FRM_SIZE, false );
+
+ switch( eHoriOrient )
+ {
+ case text::HoriOrientation::FULL:
+ case text::HoriOrientation::NONE:
+ // For text::HoriOrientation::NONE we would prefer to use the sum
+ // of the relative column widths as reference width.
+ // Unfortunately this works only if this sum interpreted as
+ // twip value is larger than the space that is available.
+ // We don't know that space, so we have to use MAX_WIDTH, too.
+ // Even if a size is specified, it will be ignored!
+ m_nWidth = MAX_WIDTH;
+ break;
+ default:
+ if( pSize )
+ {
+ if( pSize->GetWidthPercent() )
+ {
+ // The width will be set in MakeTable_
+ nPercentWidth = pSize->GetWidthPercent();
+ }
+ else
+ {
+ m_nWidth = pSize->GetWidth();
+ sal_Int32 const min = static_cast<sal_Int32>(
+ std::min<sal_uInt32>(GetColumnCount() * MINLAY, MAX_WIDTH));
+ if( m_nWidth < min )
+ {
+ m_nWidth = min;
+ }
+ else if( m_nWidth > MAX_WIDTH )
+ {
+ m_nWidth = MAX_WIDTH;
+ }
+ m_bRelWidth = false;
+ }
+ }
+ else
+ {
+ eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient
+ ? text::HoriOrientation::NONE : text::HoriOrientation::FULL;
+ bSetHoriOrient = true;
+ m_nWidth = MAX_WIDTH;
+ }
+ break;
+ }
+
+ pFrameFormat->SetFormatAttr( *pAutoItemSet );
+ }
+ else
+ {
+ bSetHoriOrient = true;
+ m_nWidth = MAX_WIDTH;
+ }
+
+ SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U];
+ assert(m_pBox1 == pLine1->GetTabBoxes()[0] && !m_bOwnsBox1 && "Why is box 1 change?");
+ m_pBox1->m_pStartNode = m_pSttNd1;
+ pLine1->GetTabBoxes().erase( pLine1->GetTabBoxes().begin() );
+ m_bOwnsBox1 = true;
+
+ m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat());
+ m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_pBox1->GetFrameFormat());
+
+ MakeTable_();
+
+ if( bSetHoriOrient )
+ pFrameFormat->SetFormatAttr( SwFormatHoriOrient( 0, eHoriOrient ) );
+
+ // This must be after the call to MakeTable_, because nWidth might be
+ // changed there.
+ pFrameFormat->LockModify();
+ SwFormatFrameSize aSize( SwFrameSize::Variable, m_nWidth );
+ aSize.SetWidthPercent( nPercentWidth );
+ pFrameFormat->SetFormatAttr( aSize );
+ pFrameFormat->UnlockModify();
+
+ for (std::unique_ptr<SwXMLTableRow_Impl> & rRow : *m_pRows)
+ rRow->Dispose();
+
+ // now that table is complete, change into DDE table (if appropriate)
+ if (m_xDDESource.is())
+ {
+ // change existing table into DDE table:
+ // 1) Get DDE field type (get data from dde-source context),
+ SwDDEFieldType* pFieldType = lcl_GetDDEFieldType( m_xDDESource.get(),
+ m_pTableNode );
+
+ // 2) release the DDE source context,
+ m_xDDESource.clear();
+
+ // 3) create new DDE table, and
+ std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( m_pTableNode->GetTable(),
+ pFieldType, false ) );
+
+ // 4) set new (DDE)table at node.
+ m_pTableNode->SetNewTable(std::move(pDDETable), false);
+ }
+
+ // ??? this is always false: root frame is only created in SwViewShell::Init
+ if( m_pTableNode->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ m_pTableNode->DelFrames();
+ m_pTableNode->MakeOwnFrames();
+ }
+}
+
+void SwXMLTableContext::MakeTable( SwTableBox *pBox, sal_Int32 nW )
+{
+ //FIXME: here would be a great place to handle XmlId for subtable
+ m_pLineFormat = GetParentTable()->m_pLineFormat;
+ m_pBoxFormat = GetParentTable()->m_pBoxFormat;
+ m_nWidth = nW;
+ m_bRelWidth = GetParentTable()->m_bRelWidth;
+
+ MakeTable_( pBox );
+
+ for (std::unique_ptr<SwXMLTableRow_Impl> & rpRow : *m_pRows)
+ {
+ // i#113600, to break the cyclic reference to SwXMLTableContext object
+ rpRow->Dispose();
+ }
+}
+
+const SwStartNode *SwXMLTableContext::InsertTableSection(
+ const SwStartNode *const pPrevSttNd,
+ OUString const*const pStringValueStyleName)
+{
+ // The topmost table is the only table that maintains the two members
+ // pBox1 and bFirstSection.
+ if( m_xParentTable.is() )
+ return m_xParentTable->InsertTableSection(pPrevSttNd, pStringValueStyleName);
+
+ const SwStartNode *pStNd;
+
+ if( m_bFirstSection )
+ {
+ Reference<XInterface> xCursorTunnel( GetImport().GetTextImport()->GetCursor(),
+ UNO_QUERY);
+ OSL_ENSURE( xCursorTunnel.is(), "missing XUnoTunnel for Cursor" );
+ OTextCursorHelper *pTextCursor = dynamic_cast<OTextCursorHelper*>(xCursorTunnel.get());
+ assert(pTextCursor && "SwXTextCursor missing");
+
+ // The Cursor already is in the first section
+ pStNd = pTextCursor->GetPaM()->GetPointNode().FindTableBoxStartNode();
+ m_bFirstSection = false;
+ GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(),
+ GetImport().GetTextImport()->GetCursor(), "Standard", true );
+ }
+ else
+ {
+ SwDoc* pDoc = GetSwImport().getDoc();
+ const SwEndNode *pEndNd = pPrevSttNd ? pPrevSttNd->EndOfSectionNode()
+ : m_pTableNode->EndOfSectionNode();
+ // #i78921# - make code robust
+ OSL_ENSURE( pDoc, "<SwXMLTableContext::InsertTableSection(..)> - no <pDoc> at <SwXTextCursor> instance - <SwXTextCurosr> doesn't seem to be registered at a <SwUnoCursor> instance." );
+ if ( !pDoc )
+ {
+ pDoc = &const_cast<SwDoc&>(pEndNd->GetDoc());
+ }
+ SwNodeOffset nOffset(pPrevSttNd ? 1 : 0);
+ SwNodeIndex aIdx( *pEndNd, nOffset );
+ SwTextFormatColl *pColl =
+ pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false );
+ pStNd = pDoc->GetNodes().MakeTextSection( aIdx.GetNode(), SwTableBoxStartNode,
+ pColl );
+ // Consider the case that a table is defined without a row.
+ if( !pPrevSttNd && m_pBox1 != nullptr )
+ {
+ m_pBox1->m_pStartNode = pStNd;
+ SwContentNode *pCNd = pDoc->GetNodes()[ pStNd->GetIndex() + 1 ]
+ ->GetContentNode();
+ SwFrameFormat *const pTableFormat = m_pTableNode->GetTable().GetFrameFormat();
+ rtl::Reference<SwXCell> xParent = SwXCell::CreateXCell( pTableFormat, m_pBox1 );
+ DBG_TESTSOLARMUTEX();
+ SwPaM aPam(*pCNd, *pCNd);
+ rtl::Reference<SwXTextCursor> xTextCursor =
+ new SwXTextCursor(*pDoc, xParent, CursorType::TableText,
+ *aPam.GetPoint(), aPam.GetMark());
+ GetImport().GetTextImport()->SetCursor( static_cast<XWordCursor*>(xTextCursor.get()) );
+ }
+ }
+
+ if (pStringValueStyleName)
+ { // fdo#62147: apply style to paragraph on string-value cell
+ GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(),
+ GetImport().GetTextImport()->GetCursor(), *pStringValueStyleName,
+ true, false, -1, false); // parameters same as sCellParaStyleName
+ }
+
+ return pStNd;
+}
+
+void SwXMLTableContext::endFastElement(sal_Int32 )
+{
+ if( IsValid() && !m_xParentTable.is() )
+ {
+ MakeTable();
+ GetImport().GetTextImport()->SetCursor( m_xOldCursor );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltbli.hxx b/sw/source/filter/xml/xmltbli.hxx
new file mode 100644
index 0000000000..400fc8ba0c
--- /dev/null
+++ b/sw/source/filter/xml/xmltbli.hxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLTBLI_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTBLI_HXX
+
+#include <xmloff/XMLTextTableContext.hxx>
+
+#include "xmlimp.hxx"
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+class SwXMLImport;
+class SwTableNode;
+class SwTableBox;
+class SwTableLine;
+class SwStartNode;
+class SwTableBoxFormat;
+class SwTableLineFormat;
+class SwXMLTableCell_Impl;
+class SwXMLTableRow_Impl;
+typedef std::vector<std::unique_ptr<SwXMLTableRow_Impl>> SwXMLTableRows_Impl;
+class SwXMLDDETableContext_Impl;
+class TableBoxIndexHasher;
+class TableBoxIndex;
+
+namespace com::sun::star {
+ namespace text { class XTextContent; }
+ namespace text { class XTextCursor; }
+}
+
+class SwXMLTableContext : public XMLTextTableContext
+{
+ OUString m_aStyleName;
+ OUString m_aDfltCellStyleName;
+ OUString m_aTemplateName;
+
+ //! Holds basic information about a column's width.
+ struct ColumnWidthInfo {
+ sal_uInt16 width; //!< Column width (absolute or relative).
+ bool isRelative; //!< True for a relative width, false for absolute.
+ ColumnWidthInfo(sal_uInt16 wdth, bool isRel) : width(wdth), isRelative(isRel) {};
+ };
+ std::vector<ColumnWidthInfo> m_aColumnWidths;
+ std::optional<std::vector<OUString>> m_xColumnDefaultCellStyleNames;
+
+ css::uno::Reference< css::text::XTextCursor > m_xOldCursor;
+ css::uno::Reference< css::text::XTextContent > m_xTextContent;
+
+ std::unique_ptr<SwXMLTableRows_Impl> m_pRows;
+
+ SwTableNode *m_pTableNode;
+ SwTableBox *m_pBox1;
+ bool m_bOwnsBox1;
+ const SwStartNode *m_pSttNd1;
+
+ SwTableBoxFormat *m_pBoxFormat;
+ SwTableLineFormat *m_pLineFormat;
+
+ // hash map of shared format, indexed by the (XML) style name,
+ // the column width, and protection flag
+ typedef std::unordered_map<TableBoxIndex,SwTableBoxFormat*,
+ TableBoxIndexHasher> map_BoxFormat;
+ std::unique_ptr<map_BoxFormat> m_pSharedBoxFormats;
+
+ rtl::Reference<SwXMLTableContext> m_xParentTable; // if table is a sub table
+
+ rtl::Reference<SwXMLDDETableContext_Impl> m_xDDESource;
+
+ bool m_bFirstSection : 1;
+ bool m_bRelWidth : 1;
+ bool m_bHasSubTables : 1;
+
+ sal_uInt16 m_nHeaderRows;
+ sal_uInt32 m_nCurRow;
+ sal_uInt32 m_nCurCol;
+ /// Same as m_nCurCol, but not incremented multiple times for table cells with row span.
+ sal_uInt32 m_nNonMergedCurCol;
+ sal_Int32 m_nWidth;
+
+ // The maximum table width (i.e., maximum value for m_nWidth); must be >= MINLAY and must also
+ // fit into ColumnWidthInfo::width (of type sal_uInt16), see e.g. the emplacement of
+ // MINLAY<=nWidth2<=MAX_WIDTH into m_aColumnWidths in SwXMLTableContext::InsertColumn:
+ static constexpr sal_Int32 MAX_WIDTH = SAL_MAX_UINT16;
+
+ SwTableBox *NewTableBox( const SwStartNode *pStNd,
+ SwTableLine *pUpper );
+ SwTableBox *MakeTableBox( SwTableLine *pUpper,
+ const SwXMLTableCell_Impl *pStartNode,
+ sal_uInt32 nLeftCol, sal_uInt32 nRightCol );
+ SwTableBox *MakeTableBox( SwTableLine *pUpper,
+ sal_uInt32 nTopRow, sal_uInt32 nLeftCol,
+ sal_uInt32 nBottomRow, sal_uInt32 nRightCol );
+ SwTableLine *MakeTableLine( SwTableBox *pUpper,
+ sal_uInt32 nTopRow, sal_uInt32 nLeftCol,
+ sal_uInt32 nBottomRow, sal_uInt32 nRightCol );
+
+ void MakeTable_( SwTableBox *pBox=nullptr );
+ void MakeTable( SwTableBox *pBox, sal_Int32 nWidth );
+ void MakeTable();
+
+ inline SwXMLTableContext *GetParentTable() const;
+
+ const SwStartNode *GetPrevStartNode( sal_uInt32 nRow,
+ sal_uInt32 nCol ) const;
+ inline const SwStartNode *GetLastStartNode() const;
+ void FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol, sal_uInt32 nColSpan );
+ void ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows );
+
+ /** sets the appropriate SwTableBoxFormat at pBox. */
+ SwTableBoxFormat* GetSharedBoxFormat(
+ SwTableBox* pBox, /// the table box
+ const OUString& rStyleName, /// XML style name
+ sal_Int32 nColumnWidth, /// width of column
+ bool bProtected, /// is cell protected?
+ bool bMayShare, /// may the format be shared (no value, formula...)
+ bool& bNew, /// true, if the format it not from the cache
+ bool* pModifyLocked ); /// if set, call pBox->LockModify() and return old lock status
+
+public:
+
+
+ SwXMLTableContext( SwXMLImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList );
+ SwXMLTableContext( SwXMLImport& rImport,
+ SwXMLTableContext *pTable );
+
+ virtual ~SwXMLTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+
+ void InsertColumn( sal_Int32 nWidth, bool bRelWidth,
+ const OUString *pDfltCellStyleName = nullptr );
+ sal_Int32 GetColumnWidth( sal_uInt32 nCol, sal_uInt32 nColSpan ) const;
+ OUString GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const;
+ inline sal_uInt32 GetColumnCount() const;
+
+ bool IsInsertCellPossible() const { return m_nCurCol < GetColumnCount(); }
+
+ /// Determines if it's OK to insert a covered cell, given the total column count.
+ bool IsInsertCoveredCellPossible() const { return m_nNonMergedCurCol < GetColumnCount(); }
+
+ bool IsInsertColPossible() const { return m_nCurCol < USHRT_MAX; }
+ bool IsInsertRowPossible() const { return m_nCurRow < USHRT_MAX; }
+ bool IsValid() const { return m_pTableNode != nullptr; }
+
+ void InsertCell( const OUString& rStyleName,
+ sal_uInt32 nRowSpan, sal_uInt32 nColSpan,
+ const SwStartNode *pStNd,
+ SwXMLTableContext *pTable=nullptr,
+ bool bIsProtected = false,
+ const OUString *pFormula=nullptr,
+ bool bHasValue = false,
+ double fValue = 0.0,
+ OUString const*const pStringValue = nullptr);
+
+ /// Sets formatting of an already created covered cell.
+ void InsertCoveredCell(const OUString& rStyleName);
+
+ void InsertRow( const OUString& rStyleName,
+ const OUString& rDfltCellStyleName,
+ bool bInHead );
+ void FinishRow();
+ void InsertRepRows( sal_uInt32 nCount );
+ const SwXMLTableCell_Impl *GetCell( sal_uInt32 nRow, sal_uInt32 nCol ) const;
+ SwXMLTableCell_Impl *GetCell( sal_uInt32 nRow, sal_uInt32 nCol );
+ const SwStartNode *InsertTableSection(const SwStartNode *pPrevSttNd = nullptr,
+ OUString const* pStringValueStyleName = nullptr);
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ void SetHasSubTables( bool bNew ) { m_bHasSubTables = bNew; }
+};
+
+inline SwXMLTableContext *SwXMLTableContext::GetParentTable() const
+{
+ return m_xParentTable.get();
+}
+
+inline sal_uInt32 SwXMLTableContext::GetColumnCount() const
+{
+ return m_aColumnWidths.size();
+}
+
+inline const SwStartNode *SwXMLTableContext::GetLastStartNode() const
+{
+ return GetPrevStartNode( 0UL, GetColumnCount() );
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltext.cxx b/sw/source/filter/xml/xmltext.cxx
new file mode 100644
index 0000000000..115dc1a523
--- /dev/null
+++ b/sw/source/filter/xml/xmltext.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlimp.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+
+namespace {
+
+class SwXMLBodyContentContext_Impl : public SvXMLImportContext
+{
+ SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
+
+public:
+
+ SwXMLBodyContentContext_Impl( SwXMLImport& rImport );
+
+ css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ // The body element's text:global attribute can be ignored, because
+ // we must have the correct object shell already.
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+}
+
+SwXMLBodyContentContext_Impl::SwXMLBodyContentContext_Impl( SwXMLImport& rImport ) :
+ SvXMLImportContext( rImport )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBodyContentContext_Impl::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ return GetSwImport().GetTextImport()->CreateTextChildContext(
+ GetImport(), nElement, xAttrList,
+ XMLTextType::Body );
+}
+
+void SwXMLBodyContentContext_Impl::endFastElement(sal_Int32 )
+{
+ /* Code moved to SwXMLOmport::endDocument */
+ GetImport().GetTextImport()->SetOutlineStyles( false );
+}
+
+SvXMLImportContext *SwXMLImport::CreateBodyContentContext()
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if( !IsStylesOnlyMode() )
+ pContext = new SwXMLBodyContentContext_Impl( *this );
+
+ return pContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltexte.cxx b/sw/source/filter/xml/xmltexte.cxx
new file mode 100644
index 0000000000..9c68a12f70
--- /dev/null
+++ b/sw/source/filter/xml/xmltexte.cxx
@@ -0,0 +1,549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/classids.hxx>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/document/XEmbeddedObjectSupplier.hpp>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/txtprmap.hxx>
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmlexppr.hxx>
+
+#include <ndole.hxx>
+#include <fmtcntnt.hxx>
+#include <unoframe.hxx>
+#include "xmlexp.hxx"
+#include "xmltexte.hxx"
+#include <SwAppletImpl.hxx>
+#include <ndindex.hxx>
+
+#include <osl/diagnose.h>
+#include <sot/exchange.hxx>
+#include <svl/urihelper.hxx>
+#include <sfx2/frmdescr.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::io;
+using namespace ::xmloff::token;
+
+namespace {
+
+enum SvEmbeddedObjectTypes
+{
+ SV_EMBEDDED_OWN,
+ SV_EMBEDDED_OUTPLACE,
+ SV_EMBEDDED_APPLET,
+ SV_EMBEDDED_PLUGIN,
+ SV_EMBEDDED_FRAME
+};
+
+}
+
+SwNoTextNode *SwXMLTextParagraphExport::GetNoTextNode(
+ const Reference < XPropertySet >& rPropSet )
+{
+ SwXFrame* pFrame = dynamic_cast<SwXFrame*>(rPropSet.get());
+ assert(pFrame && "SwXFrame missing");
+ SwFrameFormat *pFrameFormat = pFrame->GetFrameFormat();
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ const SwNodeIndex *pNdIdx = rContent.GetContentIdx();
+ return pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetNoTextNode();
+}
+
+constexpr OUString gsEmbeddedObjectProtocol( u"vnd.sun.star.EmbeddedObject:"_ustr );
+
+SwXMLTextParagraphExport::SwXMLTextParagraphExport(
+ SwXMLExport& rExp,
+ SvXMLAutoStylePoolP& _rAutoStylePool ) :
+ XMLTextParagraphExport( rExp, _rAutoStylePool ),
+ m_aAppletClassId( SO3_APPLET_CLASSID ),
+ m_aPluginClassId( SO3_PLUGIN_CLASSID ),
+ m_aIFrameClassId( SO3_IFRAME_CLASSID )
+{
+}
+
+SwXMLTextParagraphExport::~SwXMLTextParagraphExport()
+{
+}
+
+static void lcl_addURL ( SvXMLExport &rExport, const OUString &rURL,
+ bool bToRel = true )
+{
+ const OUString sRelURL = ( bToRel && !rURL.isEmpty() )
+ ? URIHelper::simpleNormalizedMakeRelative(rExport.GetOrigFileName(), rURL)
+ : rURL;
+
+ if (!sRelURL.isEmpty())
+ {
+ rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, sRelURL );
+ rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
+ rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED );
+ rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD );
+ }
+}
+
+static void lcl_addAspect(
+ const svt::EmbeddedObjectRef& rObj,
+ std::vector<XMLPropertyState>& rStates,
+ const rtl::Reference < XMLPropertySetMapper >& rMapper )
+{
+ sal_Int64 nAspect = rObj.GetViewAspect();
+ if ( nAspect )
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_DRAW_ASPECT ), uno::Any( nAspect ) );
+}
+
+static void lcl_addOutplaceProperties(
+ const svt::EmbeddedObjectRef& rObj,
+ std::vector<XMLPropertyState>& rStates,
+ const rtl::Reference < XMLPropertySetMapper >& rMapper )
+{
+ MapMode aMode( MapUnit::Map100thMM ); // the API expects this map mode for the embedded objects
+ Size aSize = rObj.GetSize( &aMode ); // get the size in the requested map mode
+
+ if( !(aSize.Width() && aSize.Height()) )
+ return;
+
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_LEFT ), Any(sal_Int32(0)) );
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_TOP ), Any(sal_Int32(0)) );
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_WIDTH ), Any(static_cast<sal_Int32>(aSize.Width())) );
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_HEIGHT ), Any(static_cast<sal_Int32>(aSize.Height())) );
+}
+
+static void lcl_addFrameProperties(
+ const uno::Reference < embed::XEmbeddedObject >& xObj,
+ std::vector<XMLPropertyState>& rStates,
+ const rtl::Reference < XMLPropertySetMapper >& rMapper )
+{
+ if ( !::svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ return;
+
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ if ( !xSet.is() )
+ return;
+
+ bool bIsAutoScroll = false, bIsScrollingMode = false;
+ Any aAny = xSet->getPropertyValue("FrameIsAutoScroll");
+ aAny >>= bIsAutoScroll;
+ if ( !bIsAutoScroll )
+ {
+ aAny = xSet->getPropertyValue("FrameIsScrollingMode");
+ aAny >>= bIsScrollingMode;
+ }
+
+ bool bIsBorderSet = false, bIsAutoBorder = false;
+ aAny = xSet->getPropertyValue("FrameIsAutoBorder");
+ aAny >>= bIsAutoBorder;
+ if ( !bIsAutoBorder )
+ {
+ aAny = xSet->getPropertyValue("FrameIsBorder");
+ aAny >>= bIsBorderSet;
+ }
+
+ sal_Int32 nWidth, nHeight;
+ aAny = xSet->getPropertyValue("FrameMarginWidth");
+ aAny >>= nWidth;
+ aAny = xSet->getPropertyValue("FrameMarginHeight");
+ aAny >>= nHeight;
+
+ if( !bIsAutoScroll )
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_DISPLAY_SCROLLBAR ), Any(bIsScrollingMode) );
+ if( !bIsAutoBorder )
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_DISPLAY_BORDER ), Any(bIsBorderSet) );
+ if( SIZE_NOT_SET != nWidth )
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_MARGIN_HORI ), Any(nWidth) );
+ if( SIZE_NOT_SET != nHeight )
+ rStates.emplace_back( rMapper->FindEntryIndex( CTF_FRAME_MARGIN_VERT ), Any(nHeight) );
+}
+
+void SwXMLTextParagraphExport::_collectTextEmbeddedAutoStyles(
+ const Reference < XPropertySet > & rPropSet )
+{
+ SwOLENode *pOLENd = GetNoTextNode( rPropSet )->GetOLENode();
+ svt::EmbeddedObjectRef& rObjRef = pOLENd->GetOLEObj().GetObject();
+ if( !rObjRef.is() )
+ return;
+
+ std::vector<XMLPropertyState> aStates;
+ aStates.reserve(8);
+ SvGlobalName aClassId( rObjRef->getClassID() );
+
+ if( m_aIFrameClassId == aClassId )
+ {
+ lcl_addFrameProperties( rObjRef.GetObject(), aStates,
+ GetAutoFramePropMapper()->getPropertySetMapper() );
+ }
+ else if ( !SotExchange::IsInternal( aClassId ) )
+ {
+ lcl_addOutplaceProperties( rObjRef, aStates,
+ GetAutoFramePropMapper()->getPropertySetMapper() );
+ }
+
+ lcl_addAspect( rObjRef, aStates,
+ GetAutoFramePropMapper()->getPropertySetMapper() );
+
+ Add( XmlStyleFamily::TEXT_FRAME, rPropSet, aStates );
+}
+
+void SwXMLTextParagraphExport::_exportTextEmbedded(
+ const Reference < XPropertySet > & rPropSet,
+ const Reference < XPropertySetInfo > & rPropSetInfo )
+{
+ SwOLENode *pOLENd = GetNoTextNode( rPropSet )->GetOLENode();
+ SwOLEObj& rOLEObj = pOLENd->GetOLEObj();
+ svt::EmbeddedObjectRef& rObjRef = rOLEObj.GetObject();
+ if( !rObjRef.is() )
+ return;
+
+ SvGlobalName aClassId( rObjRef->getClassID() );
+
+ SvEmbeddedObjectTypes nType = SV_EMBEDDED_OWN;
+ if( m_aPluginClassId == aClassId )
+ {
+ nType = SV_EMBEDDED_PLUGIN;
+ }
+ else if( m_aAppletClassId == aClassId )
+ {
+ nType = SV_EMBEDDED_APPLET;
+ }
+ else if( m_aIFrameClassId == aClassId )
+ {
+ nType = SV_EMBEDDED_FRAME;
+ }
+ else if ( !SotExchange::IsInternal( aClassId ) )
+ {
+ nType = SV_EMBEDDED_OUTPLACE;
+ }
+
+ enum XMLTokenEnum eElementName = XML__UNKNOWN_;
+ SvXMLExport &rXMLExport = GetExport();
+
+ // First the stuff common to each of Applet/Plugin/Floating Frame
+ OUString sStyle;
+ Any aAny;
+ if( rPropSetInfo->hasPropertyByName( gsFrameStyleName ) )
+ {
+ aAny = rPropSet->getPropertyValue( gsFrameStyleName );
+ aAny >>= sStyle;
+ }
+
+ std::vector<XMLPropertyState> aStates;
+ aStates.reserve(8);
+ switch( nType )
+ {
+ case SV_EMBEDDED_FRAME:
+ lcl_addFrameProperties( rObjRef.GetObject(), aStates,
+ GetAutoFramePropMapper()->getPropertySetMapper() );
+ break;
+ case SV_EMBEDDED_OUTPLACE:
+ lcl_addOutplaceProperties( rObjRef, aStates,
+ GetAutoFramePropMapper()->getPropertySetMapper() );
+ break;
+ default:
+ ;
+ }
+
+ lcl_addAspect( rObjRef, aStates,
+ GetAutoFramePropMapper()->getPropertySetMapper() );
+
+ const OUString sAutoStyle = Find( XmlStyleFamily::TEXT_FRAME,
+ rPropSet, sStyle, aStates );
+ aStates.clear();
+
+ if( !sAutoStyle.isEmpty() )
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE_NAME, sAutoStyle );
+ addTextFrameAttributes( rPropSet, false );
+
+ SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_DRAW,
+ XML_FRAME, false, true );
+
+ switch (nType)
+ {
+ case SV_EMBEDDED_OUTPLACE:
+ case SV_EMBEDDED_OWN:
+ if( !(rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED) )
+ {
+ OUString sURL;
+
+ bool bIsOwnLink = false;
+ if( SV_EMBEDDED_OWN == nType )
+ {
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLinkage( rObjRef.GetObject(), uno::UNO_QUERY );
+ bIsOwnLink = xLinkage.is() && xLinkage->isLink();
+ if ( bIsOwnLink )
+ sURL = xLinkage->getLinkURL();
+ }
+ catch(const uno::Exception&)
+ {
+ // TODO/LATER: error handling
+ OSL_FAIL( "Link detection or retrieving of the URL of OOo link is failed!" );
+ }
+ }
+
+ if ( !bIsOwnLink )
+ {
+ sURL = gsEmbeddedObjectProtocol + rOLEObj.GetCurrentPersistName();
+ }
+
+ sURL = GetExport().AddEmbeddedObject( sURL );
+ lcl_addURL( rXMLExport, sURL, false );
+ }
+ if( SV_EMBEDDED_OWN == nType && !pOLENd->GetChartTableName().isEmpty() )
+ {
+ OUString sRange( pOLENd->GetChartTableName() );
+ OUStringBuffer aBuffer( sRange.getLength() + 2 );
+ for( sal_Int32 i=0; i < sRange.getLength(); i++ )
+ {
+ sal_Unicode c = sRange[i];
+ switch( c )
+ {
+ case ' ':
+ case '.':
+ case '\'':
+ case '\\':
+ if( aBuffer.isEmpty() )
+ {
+ aBuffer.append( OUString::Concat("\'") + sRange.subView(0, i) );
+ }
+ if( '\'' == c || '\\' == c )
+ aBuffer.append( '\\' );
+ [[fallthrough]];
+ default:
+ if( !aBuffer.isEmpty() )
+ aBuffer.append( c );
+ }
+ }
+ if( !aBuffer.isEmpty() )
+ {
+ aBuffer.append( '\'' );
+ sRange = aBuffer.makeStringAndClear();
+ }
+
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NOTIFY_ON_UPDATE_OF_RANGES,
+ sRange );
+ }
+ eElementName = SV_EMBEDDED_OUTPLACE==nType ? XML_OBJECT_OLE
+ : XML_OBJECT;
+ break;
+ case SV_EMBEDDED_APPLET:
+ {
+ // It's an applet!
+ if( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY );
+ OUString aStr;
+ Any aAny2 = xSet->getPropertyValue("AppletCodeBase");
+ aAny2 >>= aStr;
+ if (!aStr.isEmpty() )
+ lcl_addURL(rXMLExport, aStr);
+
+ aAny2 = xSet->getPropertyValue("AppletName");
+ aAny2 >>= aStr;
+ if (!aStr.isEmpty())
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_APPLET_NAME, aStr );
+
+ aAny2 = xSet->getPropertyValue("AppletCode");
+ aAny2 >>= aStr;
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CODE, aStr );
+
+ bool bScript = false;
+ aAny2 = xSet->getPropertyValue("AppletIsScript");
+ aAny2 >>= bScript;
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MAY_SCRIPT, bScript ? XML_TRUE : XML_FALSE );
+
+ uno::Sequence < beans::PropertyValue > aProps;
+ aAny2 = xSet->getPropertyValue("AppletCommands");
+ aAny2 >>= aProps;
+
+ sal_Int32 i = aProps.getLength();
+ while ( i > 0 )
+ {
+ const beans::PropertyValue& aProp = aProps[--i];
+ const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, true );
+ if ( nType2 == SwHtmlOptType::TAG)
+ {
+ OUString aStr2;
+ aProp.Value >>= aStr2;
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, aProp.Name, aStr2);
+ }
+ }
+
+ eElementName = XML_APPLET;
+ }
+ }
+ break;
+ case SV_EMBEDDED_PLUGIN:
+ {
+ // It's a plugin!
+ if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY );
+ OUString aStr;
+ Any aAny2 = xSet->getPropertyValue("PluginURL");
+ aAny2 >>= aStr;
+ lcl_addURL( rXMLExport, aStr );
+
+ aAny2 = xSet->getPropertyValue("PluginMimeType");
+ aAny2 >>= aStr;
+ if (!aStr.isEmpty())
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIME_TYPE, aStr );
+ eElementName = XML_PLUGIN;
+ }
+ }
+ break;
+ case SV_EMBEDDED_FRAME:
+ {
+ // It's a floating frame!
+ if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY );
+ OUString aStr;
+ Any aAny2 = xSet->getPropertyValue("FrameURL");
+ aAny2 >>= aStr;
+
+ lcl_addURL( rXMLExport, aStr );
+
+ aAny2 = xSet->getPropertyValue("FrameName");
+ aAny2 >>= aStr;
+
+ if (!aStr.isEmpty())
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_FRAME_NAME, aStr );
+ eElementName = XML_FLOATING_FRAME;
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown object type! Base class should have been called!" );
+ }
+
+ {
+ SvXMLElementExport aElementExport( rXMLExport, XML_NAMESPACE_DRAW, eElementName,
+ false, true );
+ switch( nType )
+ {
+ case SV_EMBEDDED_OWN:
+ if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED )
+ {
+ Reference < XEmbeddedObjectSupplier > xEOS( rPropSet, UNO_QUERY );
+ OSL_ENSURE( xEOS.is(), "no embedded object supplier for own object" );
+ Reference < XComponent > xComp = xEOS->getEmbeddedObject();
+ rXMLExport.ExportEmbeddedOwnObject( xComp );
+ }
+ break;
+ case SV_EMBEDDED_OUTPLACE:
+ if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED )
+ {
+ OUString sURL( gsEmbeddedObjectProtocol + rOLEObj.GetCurrentPersistName() );
+
+ if ( !( rXMLExport.getExportFlags() & SvXMLExportFlags::OASIS ) )
+ sURL += "?oasis=false";
+
+ rXMLExport.AddEmbeddedObjectAsBase64( sURL );
+ }
+ break;
+ case SV_EMBEDDED_APPLET:
+ {
+ if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY );
+ uno::Sequence < beans::PropertyValue > aProps;
+ aAny = xSet->getPropertyValue("AppletCommands");
+ aAny >>= aProps;
+
+ sal_Int32 i = aProps.getLength();
+ while ( i > 0 )
+ {
+ const beans::PropertyValue& aProp = aProps[--i];
+ const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, true );
+ if (SwHtmlOptType::PARAM == nType2 || SwHtmlOptType::SIZE == nType2 )
+ {
+ OUString aStr;
+ aProp.Value >>= aStr;
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aProp.Name );
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr );
+ SvXMLElementExport aElementExport2( rXMLExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true );
+ }
+ }
+ }
+ }
+ break;
+ case SV_EMBEDDED_PLUGIN:
+ {
+ if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY );
+ uno::Sequence < beans::PropertyValue > aProps;
+ aAny = xSet->getPropertyValue("PluginCommands");
+ aAny >>= aProps;
+
+ sal_Int32 i = aProps.getLength();
+ while ( i > 0 )
+ {
+ const beans::PropertyValue& aProp = aProps[--i];
+ const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, false );
+ if ( nType2 == SwHtmlOptType::TAG)
+ {
+ OUString aStr;
+ aProp.Value >>= aStr;
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aProp.Name );
+ rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr );
+ SvXMLElementExport aElementExport2( rXMLExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true );
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if( SV_EMBEDDED_OUTPLACE==nType || SV_EMBEDDED_OWN==nType )
+ {
+ OUString sURL = XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE + rOLEObj.GetCurrentPersistName();
+ if( !(rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED) )
+ {
+ sURL = GetExport().AddEmbeddedObject( sURL );
+ lcl_addURL( rXMLExport, sURL, false );
+ }
+
+ SvXMLElementExport aElementExport( GetExport(), XML_NAMESPACE_DRAW,
+ XML_IMAGE, false, true );
+
+ if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED )
+ GetExport().AddEmbeddedObjectAsBase64( sURL );
+ }
+
+ // Lastly the stuff common to each of Applet/Plugin/Floating Frame
+ exportEvents( rPropSet );
+ exportTitleAndDescription( rPropSet, rPropSetInfo ); // #i73249#
+ exportContour( rPropSet, rPropSetInfo );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltexte.hxx b/sw/source/filter/xml/xmltexte.hxx
new file mode 100644
index 0000000000..09ce6c46f4
--- /dev/null
+++ b/sw/source/filter/xml/xmltexte.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX
+
+#include <xmloff/txtparae.hxx>
+#include <tools/globname.hxx>
+
+#include <optional>
+#include <unordered_map>
+
+#define XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE "vnd.sun.star.GraphicObject:"
+
+class SwXMLExport;
+class SvXMLAutoStylePoolP;
+class SwNoTextNode;
+class SwTableNode;
+class SwTableLines;
+namespace com::sun::star::style { class XStyle; }
+
+class SwXMLTextParagraphExport : public XMLTextParagraphExport
+{
+ const SvGlobalName m_aAppletClassId;
+ const SvGlobalName m_aPluginClassId;
+ const SvGlobalName m_aIFrameClassId;
+
+ // Collected autostyles for use in exportTextAutoStyles
+ std::vector<const SwTableNode*> maTableNodes;
+public:
+ typedef ::std::unordered_map<SwFrameFormat const*, ::std::optional<OUString>> FormatMap;
+private:
+ ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> m_TableFormats;
+
+ static SwNoTextNode *GetNoTextNode(
+ const css::uno::Reference < css::beans::XPropertySet >& rPropSet );
+
+ void CollectTableLinesAutoStyles(const SwTableLines& rLines, SwFrameFormat& rFormat,
+ bool bProgress);
+
+protected:
+ virtual void _collectTextEmbeddedAutoStyles(
+ const css::uno::Reference< css::beans::XPropertySet > & rPropSet ) override;
+ virtual void _exportTextEmbedded(
+ const css::uno::Reference< css::beans::XPropertySet > & rPropSet,
+ const css::uno::Reference< css::beans::XPropertySetInfo > & rPropSetInfo ) override;
+
+ virtual void exportTable(
+ const css::uno::Reference< css::text::XTextContent > & rTextContent,
+ bool bAutoStyles, bool bProgress ) override;
+
+ virtual void exportTableAutoStyles() override;
+
+public:
+ SwXMLTextParagraphExport(
+ SwXMLExport& rExp,
+ SvXMLAutoStylePoolP& rAutoStylePool );
+ virtual ~SwXMLTextParagraphExport() override;
+
+ ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> const&
+ GetTableFormats() const { return m_TableFormats; }
+ ::std::unordered_map<SwTableNode const*, ::std::pair<FormatMap, FormatMap>> &
+ GetTableFormats() { return m_TableFormats; }
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltexti.cxx b/sw/source/filter/xml/xmltexti.cxx
new file mode 100644
index 0000000000..508cb428dc
--- /dev/null
+++ b/sw/source/filter/xml/xmltexti.cxx
@@ -0,0 +1,997 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
+#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <comphelper/classids.hxx>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <xmloff/prstylei.hxx>
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/txtprmap.hxx>
+#include <xmloff/i18nmap.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <TextCursorHelper.hxx>
+#include <unoframe.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include "xmlimp.hxx"
+#include "xmltbli.hxx"
+#include "xmltexti.hxx"
+#include "XMLRedlineImportHelper.hxx"
+#include <xmloff/XMLFilterServiceNames.h>
+#include <SwAppletImpl.hxx>
+#include <ndole.hxx>
+#include <docsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svtools/embedhlp.hxx>
+#include <svl/urihelper.hxx>
+#include <sfx2/frmdescr.hxx>
+#include <tools/globname.hxx>
+
+#include <algorithm>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace xml::sax;
+
+const std::pair<OUString, SvGUID> aServiceMap[] = {
+ { XML_IMPORT_FILTER_WRITER, { SO3_SW_CLASSID } },
+ { XML_IMPORT_FILTER_CALC, { SO3_SC_CLASSID } },
+ { XML_IMPORT_FILTER_DRAW, { SO3_SDRAW_CLASSID } },
+ { XML_IMPORT_FILTER_IMPRESS, { SO3_SIMPRESS_CLASSID } },
+ { XML_IMPORT_FILTER_CHART, { SO3_SCH_CLASSID } },
+ { XML_IMPORT_FILTER_MATH, { SO3_SM_CLASSID } },
+};
+static void lcl_putHeightAndWidth ( SfxItemSet &rItemSet,
+ sal_Int32 nHeight, sal_Int32 nWidth,
+ Size *pTwipSize = nullptr )
+{
+ if( nWidth > 0 && nHeight > 0 )
+ {
+ nWidth = o3tl::toTwips(nWidth, o3tl::Length::mm100);
+ if( nWidth < MINFLY )
+ nWidth = MINFLY;
+ nHeight = o3tl::toTwips(nHeight, o3tl::Length::mm100);
+ if( nHeight < MINFLY )
+ nHeight = MINFLY;
+ rItemSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth, nHeight ) );
+ }
+
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR );
+ rItemSet.Put( aAnchor );
+
+ if( pTwipSize )
+ {
+ pTwipSize->setWidth( nWidth );
+ pTwipSize->setHeight( nHeight);
+ }
+}
+
+static void lcl_setObjectVisualArea( const uno::Reference< embed::XEmbeddedObject >& xObj,
+ sal_Int64 nAspect,
+ const Size& aVisSize,
+ const MapUnit& aUnit )
+{
+ if( !(xObj.is() && nAspect != embed::Aspects::MSOLE_ICON) )
+ return;
+
+ // convert the visual area to the objects units
+ MapUnit aObjUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
+ Size aObjVisSize = OutputDevice::LogicToLogic(aVisSize, MapMode(aUnit), MapMode(aObjUnit));
+ awt::Size aSz;
+ aSz.Width = aObjVisSize.Width();
+ aSz.Height = aObjVisSize.Height();
+
+ try
+ {
+ xObj->setVisualAreaSize( nAspect, aSz );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Couldn't set visual area of the object!" );
+ }
+}
+
+SwXMLTextImportHelper::SwXMLTextImportHelper(
+ const uno::Reference < XModel>& rModel,
+ SwXMLImport& rImport,
+ const uno::Reference<XPropertySet> & rInfoSet,
+ bool bInsertM, bool bStylesOnlyM,
+ bool bBlockM, bool bOrganizerM ) :
+ XMLTextImportHelper( rModel, rImport, bInsertM, bStylesOnlyM, true/*bProgress*/,
+ bBlockM, bOrganizerM ),
+ m_pRedlineHelper( nullptr )
+{
+ uno::Reference<XPropertySet> xDocPropSet( rModel, UNO_QUERY );
+ m_pRedlineHelper = new XMLRedlineImportHelper(rImport,
+ bInsertM || bBlockM, xDocPropSet, rInfoSet );
+}
+
+SwXMLTextImportHelper::~SwXMLTextImportHelper()
+{
+ // the redline helper destructor sets properties on the document
+ // and may throw an exception while doing so... catch this
+ try
+ {
+ delete m_pRedlineHelper;
+ }
+ catch ( const RuntimeException& )
+ {
+ // ignore
+ }
+}
+
+SvXMLImportContext *SwXMLTextImportHelper::CreateTableChildContext(
+ SvXMLImport& rImport,
+ sal_Int32 /*nElement*/,
+ const uno::Reference< XFastAttributeList > & xAttrList )
+{
+ return new SwXMLTableContext( static_cast<SwXMLImport&>(rImport), xAttrList );
+}
+
+bool SwXMLTextImportHelper::IsInHeaderFooter() const
+{
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(const_cast<SwXMLTextImportHelper *>(this)->GetCursor().get());
+ SAL_WARN_IF(!pTextCursor, "sw.uno", "SwXTextCursor missing");
+ SwDoc *pDoc = pTextCursor ? pTextCursor->GetDoc() : nullptr;
+
+ return pDoc && pDoc->IsInHeaderFooter( pTextCursor->GetPaM()->GetPoint()->GetNode() );
+}
+
+static SwOLENode *lcl_GetOLENode( const SwFrameFormat *pFrameFormat )
+{
+ SwOLENode *pOLENd = nullptr;
+ if( pFrameFormat )
+ {
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ const SwNodeIndex *pNdIdx = rContent.GetContentIdx();
+ pOLENd = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetOLENode();
+ }
+ OSL_ENSURE( pOLENd, "Where is the OLE node" );
+ return pOLENd;
+}
+
+uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertOLEObject(
+ SvXMLImport& rImport,
+ const OUString& rHRef,
+ const OUString& rStyleName,
+ const OUString& rTableName,
+ sal_Int32 nWidth, sal_Int32 nHeight )
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ uno::Reference < XPropertySet > xPropSet;
+
+ sal_Int32 nPos = rHRef.indexOf( ':' );
+ if( -1 == nPos )
+ return xPropSet;
+
+ OUString aObjName( rHRef.copy( nPos+1) );
+
+ if( aObjName.isEmpty() )
+ return xPropSet;
+
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get());
+ SAL_WARN_IF(!pTextCursor, "sw.uno", "SwXTextCursor missing");
+ SwDoc *pDoc = static_cast<SwXMLImport&>(rImport).getDoc();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() );
+ Size aTwipSize( 0, 0 );
+ tools::Rectangle aVisArea( 0, 0, nWidth, nHeight );
+ lcl_putHeightAndWidth( aItemSet, nHeight, nWidth,
+ &aTwipSize );
+
+ SwFrameFormat *pFrameFormat = nullptr;
+ SwOLENode *pOLENd = nullptr;
+ if( rHRef.startsWith("vnd.sun.star.ServiceName:") )
+ {
+ bool bInsert = false;
+ SvGlobalName aClassName;
+ for (const auto& [sFilterService, rCLASSID] : aServiceMap)
+ {
+ if (aObjName == sFilterService)
+ {
+ aClassName = SvGlobalName(rCLASSID);
+ bInsert = true;
+ break;
+ }
+ }
+
+ if( bInsert )
+ {
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ try
+ {
+ // create object with desired ClassId
+ uno::Sequence < sal_Int8 > aClass( aClassName.GetByteSequence() );
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Sequence<beans::PropertyValue> aObjArgs( comphelper::InitPropertySequence({
+ { "DefaultParentBaseURL", Any(GetXMLImport().GetBaseURL()) }
+ }));
+ uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew(
+ aClass, OUString(), xStorage, "DummyName", aObjArgs), uno::UNO_QUERY );
+ if ( xObj.is() )
+ {
+ //TODO/LATER: is it enough to only set the VisAreaSize?
+ lcl_setObjectVisualArea( xObj, embed::Aspects::MSOLE_CONTENT, aTwipSize, MapUnit::MapTwip );
+ }
+
+ if( pTextCursor )
+ {
+ pFrameFormat = pDoc->getIDocumentContentOperations().InsertEmbObject(
+ *pTextCursor->GetPaM(),
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ &aItemSet);
+ pOLENd = lcl_GetOLENode( pFrameFormat );
+ }
+
+ if( pOLENd )
+ aObjName = pOLENd->GetOLEObj().GetCurrentPersistName();
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+ else
+ {
+ // check whether an object with this name already exists in the document
+ OUString aName;
+ SwIterator<SwContentNode,SwFormatColl> aIter( *pDoc->GetDfltGrfFormatColl() );
+ for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() )
+ {
+ SwOLENode* pExistingOLENd = pNd->GetOLENode();
+ if( pExistingOLENd )
+ {
+ OUString aExistingName = pExistingOLENd->GetOLEObj().GetCurrentPersistName();
+ if ( aExistingName == aObjName )
+ {
+ OSL_FAIL( "The document contains duplicate object references, means it is partially broken, please let developers know how this document was generated!" );
+
+ OUString aTmpName = pDoc->GetPersist()->GetEmbeddedObjectContainer().CreateUniqueObjectName();
+ try
+ {
+ pDoc->GetPersist()->GetStorage()->copyElementTo( aObjName,
+ pDoc->GetPersist()->GetStorage(),
+ aTmpName );
+ aName = aTmpName;
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "Couldn't create a copy of the object!" );
+ }
+
+ break;
+ }
+ }
+ }
+
+ if ( aName.isEmpty() )
+ aName = aObjName;
+
+ // the correct aspect will be set later
+ // TODO/LATER: Actually it should be set here
+ if( pTextCursor )
+ {
+ pFrameFormat = pDoc->getIDocumentContentOperations().InsertOLE( *pTextCursor->GetPaM(), aName, embed::Aspects::MSOLE_CONTENT, &aItemSet, nullptr );
+ pOLENd = lcl_GetOLENode( pFrameFormat );
+ }
+ aObjName = aName;
+ }
+
+ if( !pFrameFormat )
+ return xPropSet;
+
+ if( IsInsertMode() )
+ {
+ if( !pOLENd )
+ pOLENd = lcl_GetOLENode( pFrameFormat );
+ if( pOLENd )
+ pOLENd->SetOLESizeInvalid( true );
+ }
+
+ xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pDoc, pFrameFormat);
+ if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ // req for z-order
+ SwXFrame::GetOrCreateSdrObject(*static_cast<SwFlyFrameFormat*>(pFrameFormat));
+ }
+ if( !rTableName.isEmpty() )
+ {
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ const SwNodeIndex *pNdIdx = rContent.GetContentIdx();
+ SwOLENode *pOLENode = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetOLENode();
+ OSL_ENSURE( pOLENode, "Where is the OLE node" );
+
+ OUStringBuffer aBuffer( rTableName.getLength() );
+ bool bQuoted = false;
+ bool bEscape = false;
+ bool bError = false;
+ for( sal_Int32 i=0; i < rTableName.getLength(); i++ )
+ {
+ bool bEndOfNameFound = false;
+ sal_Unicode c = rTableName[i];
+ switch( c )
+ {
+ case '\'':
+ if( bEscape )
+ {
+ aBuffer.append( c );
+ bEscape = false;
+ }
+ else if( bQuoted )
+ {
+ bEndOfNameFound = true;
+ }
+ else if( 0 == i )
+ {
+ bQuoted = true;
+ }
+ else
+ {
+ bError = true;
+ }
+ break;
+ case '\\':
+ if( bEscape )
+ {
+ aBuffer.append( c );
+ bEscape = false;
+ }
+ else
+ {
+ bEscape = true;
+ }
+ break;
+ case ' ':
+ case '.':
+ if( !bQuoted )
+ {
+ bEndOfNameFound = true;
+ }
+ else
+ {
+ aBuffer.append( c );
+ bEscape = false;
+ }
+ break;
+ default:
+ {
+ aBuffer.append( c );
+ bEscape = false;
+ }
+ break;
+ }
+ if( bError || bEndOfNameFound )
+ break;
+ }
+ if( !bError )
+ {
+ OUString sTableName( aBuffer.makeStringAndClear() );
+ pOLENode->SetChartTableName( GetRenameMap().Get( XML_TEXT_RENAME_TYPE_TABLE, sTableName ) );
+ }
+ }
+
+ sal_Int64 nDrawAspect = 0;
+ const XMLPropStyleContext *pStyle = nullptr;
+ bool bHasSizeProps = false;
+ if( !rStyleName.isEmpty() )
+ {
+ pStyle = FindAutoFrameStyle( rStyleName );
+ if( pStyle )
+ {
+ rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap =
+ pStyle->GetStyles()
+ ->GetImportPropertyMapper(pStyle->GetFamily());
+ OSL_ENSURE( xImpPrMap.is(), "Where is the import prop mapper?" );
+ if( xImpPrMap.is() )
+ {
+ rtl::Reference<XMLPropertySetMapper> rPropMapper =
+ xImpPrMap->getPropertySetMapper();
+
+ sal_Int32 nCount = pStyle->GetProperties().size();
+ for( sal_Int32 i=0; i < nCount; i++ )
+ {
+ const XMLPropertyState& rProp = pStyle->GetProperties()[i];
+ sal_Int32 nIdx = rProp.mnIndex;
+ if( -1 == nIdx )
+ continue;
+
+ switch( rPropMapper->GetEntryContextId(nIdx) )
+ {
+ case CTF_OLE_VIS_AREA_LEFT:
+ {
+ sal_Int32 nVal = 0;
+ rProp.maValue >>= nVal;
+ aVisArea.SetPosX( nVal );
+ }
+ break;
+ case CTF_OLE_VIS_AREA_TOP:
+ {
+ sal_Int32 nVal = 0;
+ rProp.maValue >>= nVal;
+ aVisArea.SetPosY( nVal );
+ }
+ break;
+ case CTF_OLE_VIS_AREA_WIDTH:
+ {
+ sal_Int32 nVal = 0;
+ rProp.maValue >>= nVal;
+ aVisArea.setWidth( nVal );
+ bHasSizeProps = true;
+ }
+ break;
+ case CTF_OLE_VIS_AREA_HEIGHT:
+ {
+ sal_Int32 nVal = 0;
+ rProp.maValue >>= nVal;
+ aVisArea.setHeight( nVal );
+ bHasSizeProps = true;
+ }
+ break;
+ case CTF_OLE_DRAW_ASPECT:
+ {
+ rProp.maValue >>= nDrawAspect;
+
+ if ( !nDrawAspect )
+ nDrawAspect = embed::Aspects::MSOLE_CONTENT;
+
+ if ( pOLENd )
+ pOLENd->GetOLEObj().GetObject().SetViewAspect( nDrawAspect );
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bHasSizeProps )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj =
+ pDoc->GetPersist()->GetEmbeddedObjectContainer().GetEmbeddedObject( aObjName );
+ if( xObj.is() )
+ lcl_setObjectVisualArea( xObj, ( nDrawAspect ? nDrawAspect : embed::Aspects::MSOLE_CONTENT ),
+ aVisArea.GetSize(), MapUnit::Map100thMM );
+ }
+
+ return xPropSet;
+}
+
+uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertOOoLink(
+ SvXMLImport& rImport,
+ const OUString& rHRef,
+ const OUString& /*rStyleName*/,
+ const OUString& /*rTableName*/,
+ sal_Int32 nWidth, sal_Int32 nHeight )
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ uno::Reference < XPropertySet > xPropSet;
+
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get());
+ assert( pTextCursor && "SwXTextCursor missing" );
+ SwDoc *pDoc = static_cast<SwXMLImport&>(rImport).getDoc();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() );
+ Size aTwipSize( 0, 0 );
+ lcl_putHeightAndWidth( aItemSet, nHeight, nWidth,
+ &aTwipSize );
+
+ // We'll need a (valid) URL. If we don't have do not insert the link and return early.
+ // Copy URL into URL object on the way.
+ INetURLObject aURLObj;
+ bool bValidURL = !rHRef.isEmpty() &&
+ aURLObj.SetURL( URIHelper::SmartRel2Abs(
+ INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) );
+ if( !bValidURL )
+ return xPropSet;
+
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ try
+ {
+ // create object with desired ClassId
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory =
+ embed::OOoEmbeddedObjectFactory::create(::comphelper::getProcessComponentContext());
+
+ uno::Sequence< beans::PropertyValue > aMediaDescriptor{ comphelper::makePropertyValue(
+ "URL", aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )) };
+
+ if (SfxMedium* pMedium = pDoc->GetDocShell() ? pDoc->GetDocShell()->GetMedium() : nullptr)
+ {
+ uno::Reference< task::XInteractionHandler > xInteraction = pMedium->GetInteractionHandler();
+ if ( xInteraction.is() )
+ {
+ aMediaDescriptor.realloc( 2 );
+ auto pMediaDescriptor = aMediaDescriptor.getArray();
+ pMediaDescriptor[1].Name = "InteractionHandler";
+ pMediaDescriptor[1].Value <<= xInteraction;
+ }
+
+ const auto nLen = aMediaDescriptor.getLength() + 1;
+ aMediaDescriptor.realloc(nLen);
+ auto pMediaDescriptor = aMediaDescriptor.getArray();
+ pMediaDescriptor[nLen - 1].Name = "Referer";
+ pMediaDescriptor[nLen - 1].Value <<= pMedium->GetName();
+ }
+
+ uno::Reference < embed::XEmbeddedObject > xObj(
+ xFactory->createInstanceLink(
+ xStorage, "DummyName", aMediaDescriptor, uno::Sequence< beans::PropertyValue >() ),
+ uno::UNO_QUERY_THROW );
+
+ {
+ SwFrameFormat *const pFrameFormat =
+ pDoc->getIDocumentContentOperations().InsertEmbObject(
+ *pTextCursor->GetPaM(),
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ &aItemSet );
+
+ // TODO/LATER: in future may need a way to set replacement image url to the link ( may be even to the object ), needs oasis cws???
+
+ xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pDoc, pFrameFormat);
+ if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ SwXFrame::GetOrCreateSdrObject(*
+ static_cast<SwFlyFrameFormat*>(pFrameFormat)); // req for z-order
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ // TODO/LATER: should the rStyleName and rTableName be handled as for usual embedded object?
+
+ return xPropSet;
+}
+
+uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertApplet(
+ const OUString &rName,
+ const OUString &rCode,
+ bool bMayScript,
+ const OUString& rHRef,
+ sal_Int32 nWidth, sal_Int32 nHeight )
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ uno::Reference < XPropertySet > xPropSet;
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get());
+ assert( pTextCursor && "SwXTextCursor missing" );
+ SwDoc *pDoc = pTextCursor->GetDoc();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() );
+ lcl_putHeightAndWidth( aItemSet, nHeight, nWidth);
+
+ SwApplet_Impl aAppletImpl ( std::move(aItemSet) );
+
+ OUString sCodeBase;
+ if( !rHRef.isEmpty() )
+ sCodeBase = GetXMLImport().GetAbsoluteReference( rHRef );
+
+ aAppletImpl.CreateApplet ( rCode, rName, bMayScript, sCodeBase, GetXMLImport().GetDocumentBase() );
+
+ // set the size of the applet
+ lcl_setObjectVisualArea( aAppletImpl.GetApplet(),
+ embed::Aspects::MSOLE_CONTENT,
+ Size( nWidth, nHeight ),
+ MapUnit::Map100thMM );
+
+ SwFrameFormat *const pFrameFormat =
+ pDoc->getIDocumentContentOperations().InsertEmbObject( *pTextCursor->GetPaM(),
+ ::svt::EmbeddedObjectRef(aAppletImpl.GetApplet(), embed::Aspects::MSOLE_CONTENT),
+ &aAppletImpl.GetItemSet());
+ xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pDoc, pFrameFormat);
+ if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ // req for z-order
+ SwXFrame::GetOrCreateSdrObject(*static_cast<SwFlyFrameFormat*>(pFrameFormat));
+ }
+
+ return xPropSet;
+}
+
+uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertPlugin(
+ const OUString &rMimeType,
+ const OUString& rHRef,
+ sal_Int32 nWidth, sal_Int32 nHeight )
+{
+ uno::Reference < XPropertySet > xPropSet;
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get());
+ assert( pTextCursor && "SwXTextCursor missing" );
+ SwDoc *pDoc = pTextCursor->GetDoc();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() );
+ lcl_putHeightAndWidth( aItemSet, nHeight, nWidth);
+
+ // We'll need a (valid) URL, or we need a MIME type. If we don't have
+ // either, do not insert plugin and return early. Copy URL into URL object
+ // on the way.
+ INetURLObject aURLObj;
+
+ bool bValidURL = !rHRef.isEmpty() &&
+ aURLObj.SetURL( URIHelper::SmartRel2Abs( INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) );
+ bool bValidMimeType = !rMimeType.isEmpty();
+ if( !bValidURL && !bValidMimeType )
+ return xPropSet;
+
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ try
+ {
+ // create object with desired ClassId
+ uno::Sequence < sal_Int8 > aClass( SvGlobalName( SO3_PLUGIN_CLASSID ).GetByteSequence() );
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew(
+ aClass, OUString(), xStorage, "DummyName",
+ uno::Sequence < beans::PropertyValue >() ), uno::UNO_QUERY );
+
+ // set size to the object
+ lcl_setObjectVisualArea( xObj,
+ embed::Aspects::MSOLE_CONTENT,
+ Size( nWidth, nHeight ),
+ MapUnit::Map100thMM );
+
+ if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ if( bValidURL )
+ xSet->setPropertyValue("PluginURL",
+ Any( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
+ if( bValidMimeType )
+ xSet->setPropertyValue("PluginMimeType",
+ Any( rMimeType ) );
+ }
+
+ SwFrameFormat *const pFrameFormat =
+ pDoc->getIDocumentContentOperations().InsertEmbObject(
+ *pTextCursor->GetPaM(),
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ &aItemSet);
+ xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pDoc, pFrameFormat);
+ if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ SwXFrame::GetOrCreateSdrObject(*
+ static_cast<SwFlyFrameFormat*>(pFrameFormat)); // req for z-order
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ return xPropSet;
+}
+uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertFloatingFrame(
+ const OUString& rName,
+ const OUString& rHRef,
+ const OUString& rStyleName,
+ sal_Int32 nWidth, sal_Int32 nHeight )
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ uno::Reference < XPropertySet > xPropSet;
+ OTextCursorHelper* pTextCursor = dynamic_cast<OTextCursorHelper*>(GetCursor().get());
+ assert( pTextCursor && "SwXTextCursor missing" );
+ SwDoc *pDoc = pTextCursor->GetDoc();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END> aItemSet( pDoc->GetAttrPool() );
+ lcl_putHeightAndWidth( aItemSet, nHeight, nWidth);
+
+ ScrollingMode eScrollMode = ScrollingMode::Auto;
+ bool bHasBorder = false;
+ bool bIsBorderSet = false;
+ Size aMargin( SIZE_NOT_SET, SIZE_NOT_SET );
+ const XMLPropStyleContext *pStyle = nullptr;
+ if( !rStyleName.isEmpty() )
+ {
+ pStyle = FindAutoFrameStyle( rStyleName );
+ if( pStyle )
+ {
+ rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap =
+ pStyle->GetStyles()
+ ->GetImportPropertyMapper(pStyle->GetFamily());
+ OSL_ENSURE( xImpPrMap.is(), "Where is the import prop mapper?" );
+ if( xImpPrMap.is() )
+ {
+ rtl::Reference<XMLPropertySetMapper> rPropMapper =
+ xImpPrMap->getPropertySetMapper();
+
+ sal_Int32 nCount = pStyle->GetProperties().size();
+ for( sal_Int32 i=0; i < nCount; i++ )
+ {
+ const XMLPropertyState& rProp = pStyle->GetProperties()[i];
+ sal_Int32 nIdx = rProp.mnIndex;
+ if( -1 == nIdx )
+ continue;
+
+ switch( rPropMapper->GetEntryContextId(nIdx) )
+ {
+ case CTF_FRAME_DISPLAY_SCROLLBAR:
+ {
+ bool bYes = *o3tl::doAccess<bool>(rProp.maValue);
+ eScrollMode = bYes ? ScrollingMode::Yes : ScrollingMode::No;
+ }
+ break;
+ case CTF_FRAME_DISPLAY_BORDER:
+ {
+ bHasBorder = *o3tl::doAccess<bool>(rProp.maValue);
+ bIsBorderSet = true;
+ }
+ break;
+ case CTF_FRAME_MARGIN_HORI:
+ {
+ sal_Int32 nVal = SIZE_NOT_SET;
+ rProp.maValue >>= nVal;
+ aMargin.setWidth( nVal );
+ }
+ break;
+ case CTF_FRAME_MARGIN_VERT:
+ {
+ sal_Int32 nVal = SIZE_NOT_SET;
+ rProp.maValue >>= nVal;
+ aMargin.setHeight( nVal );
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ try
+ {
+ // create object with desired ClassId
+ uno::Sequence < sal_Int8 > aClass( SvGlobalName( SO3_IFRAME_CLASSID ).GetByteSequence() );
+ uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() );
+ uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew(
+ aClass, OUString(), xStorage, "DummyName",
+ uno::Sequence < beans::PropertyValue >() ), uno::UNO_QUERY );
+
+ // set size to the object
+ lcl_setObjectVisualArea( xObj,
+ embed::Aspects::MSOLE_CONTENT,
+ Size( nWidth, nHeight ),
+ MapUnit::Map100thMM );
+
+ if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ OUString sHRef = URIHelper::SmartRel2Abs(
+ INetURLObject( GetXMLImport().GetBaseURL() ), rHRef );
+
+ if (INetURLObject(sHRef).IsExoticProtocol())
+ GetXMLImport().NotifyMacroEventRead();
+
+ xSet->setPropertyValue("FrameURL",
+ Any( sHRef ) );
+
+ xSet->setPropertyValue("FrameName",
+ Any( rName ) );
+
+ if ( eScrollMode == ScrollingMode::Auto )
+ xSet->setPropertyValue("FrameIsAutoScroll",
+ Any( true ) );
+ else
+ xSet->setPropertyValue("FrameIsScrollingMode",
+ Any( eScrollMode == ScrollingMode::Yes ) );
+
+ if ( bIsBorderSet )
+ xSet->setPropertyValue("FrameIsBorder",
+ Any( bHasBorder ) );
+ else
+ xSet->setPropertyValue("FrameIsAutoBorder",
+ Any( true ) );
+
+ xSet->setPropertyValue("FrameMarginWidth",
+ Any( sal_Int32( aMargin.Width() ) ) );
+
+ xSet->setPropertyValue("FrameMarginHeight",
+ Any( sal_Int32( aMargin.Height() ) ) );
+ }
+
+ SwFrameFormat *const pFrameFormat =
+ pDoc->getIDocumentContentOperations().InsertEmbObject(
+ *pTextCursor->GetPaM(),
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ &aItemSet);
+ xPropSet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pDoc, pFrameFormat);
+ if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ // req for z-order
+ SwXFrame::GetOrCreateSdrObject(*
+ static_cast<SwFlyFrameFormat*>(pFrameFormat));
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ return xPropSet;
+}
+
+void SwXMLTextImportHelper::endAppletOrPlugin(
+ const uno::Reference < XPropertySet > &rPropSet,
+ std::map < const OUString, OUString > &rParamMap)
+{
+ // this method will modify the document directly -> lock SolarMutex
+ SolarMutexGuard aGuard;
+
+ SwXFrame* pFrame = dynamic_cast<SwXFrame*>(rPropSet.get());
+ assert(pFrame && "SwXFrame missing");
+ SwFrameFormat *pFrameFormat = pFrame->GetFrameFormat();
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ const SwNodeIndex *pNdIdx = rContent.GetContentIdx();
+ SwOLENode *pOLENd = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetNoTextNode()->GetOLENode();
+ SwOLEObj& rOLEObj = pOLENd->GetOLEObj();
+
+ uno::Reference < embed::XEmbeddedObject > xEmbObj( rOLEObj.GetOleRef() );
+ if ( !svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) )
+ return;
+
+ uno::Reference < beans::XPropertySet > xSet( xEmbObj->getComponent(), uno::UNO_QUERY );
+ if ( !xSet.is() )
+ return;
+
+ const sal_Int32 nCount = rParamMap.size();
+ uno::Sequence< beans::PropertyValue > aCommandSequence( nCount );
+
+ std::transform(rParamMap.begin(), rParamMap.end(), aCommandSequence.getArray(),
+ [](const auto& rParam)
+ {
+ return beans::PropertyValue(/* Name */ rParam.first,
+ /* Handle */ -1,
+ /* Value */ uno::Any(rParam.second),
+ /* State */ beans::PropertyState_DIRECT_VALUE);
+ });
+
+ // unfortunately the names of the properties are depending on the object
+ OUString aParaName("AppletCommands");
+ try
+ {
+ xSet->setPropertyValue( aParaName, Any( aCommandSequence ) );
+ }
+ catch ( uno::Exception& )
+ {
+ aParaName = "PluginCommands";
+ try
+ {
+ xSet->setPropertyValue( aParaName, Any( aCommandSequence ) );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+}
+
+// redlining helper methods
+// (override to provide the real implementation)
+void SwXMLTextImportHelper::RedlineAdd(
+ const OUString& rType,
+ const OUString& rId,
+ const OUString& rAuthor,
+ const OUString& rComment,
+ const util::DateTime& rDateTime,
+ const OUString& rMovedID,
+ bool bMergeLastPara)
+{
+ // create redline helper on demand
+ OSL_ENSURE(nullptr != m_pRedlineHelper, "helper should have been created in constructor");
+ if (nullptr != m_pRedlineHelper)
+ m_pRedlineHelper->Add(rType, rId, rAuthor, rComment, rDateTime, rMovedID,
+ bMergeLastPara);
+}
+
+uno::Reference<XTextCursor> SwXMLTextImportHelper::RedlineCreateText(
+ uno::Reference<XTextCursor> & rOldCursor,
+ const OUString& rId)
+{
+ uno::Reference<XTextCursor> xRet;
+
+ if (nullptr != m_pRedlineHelper)
+ {
+ xRet = m_pRedlineHelper->CreateRedlineTextSection(rOldCursor, rId);
+ }
+
+ return xRet;
+}
+
+void SwXMLTextImportHelper::RedlineSetCursor(
+ const OUString& rId,
+ bool bStart,
+ bool bIsOutsideOfParagraph)
+{
+ if (nullptr != m_pRedlineHelper) {
+ uno::Reference<XTextRange> xTextRange( GetCursor()->getStart() );
+ m_pRedlineHelper->SetCursor(rId, bStart, xTextRange,
+ bIsOutsideOfParagraph);
+ }
+ // else: ignore redline (wasn't added before, else we'd have a helper)
+}
+
+void SwXMLTextImportHelper::RedlineAdjustStartNodeCursor()
+{
+ OUString rId = GetOpenRedlineId();
+ if ((nullptr != m_pRedlineHelper) && !rId.isEmpty())
+ {
+ m_pRedlineHelper->AdjustStartNodeCursor(rId);
+ ResetOpenRedlineId();
+ }
+ // else: ignore redline (wasn't added before, or no open redline ID
+}
+
+void SwXMLTextImportHelper::SetShowChanges( bool bShowChanges )
+{
+ if ( nullptr != m_pRedlineHelper )
+ m_pRedlineHelper->SetShowChanges( bShowChanges );
+}
+
+void SwXMLTextImportHelper::SetRecordChanges( bool bRecordChanges )
+{
+ if ( nullptr != m_pRedlineHelper )
+ m_pRedlineHelper->SetRecordChanges( bRecordChanges );
+}
+
+void SwXMLTextImportHelper::SetChangesProtectionKey(
+ const Sequence<sal_Int8> & rKey )
+{
+ if ( nullptr != m_pRedlineHelper )
+ m_pRedlineHelper->SetProtectionKey( rKey );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/xmltexti.hxx b/sw/source/filter/xml/xmltexti.hxx
new file mode 100644
index 0000000000..886ce4c7ac
--- /dev/null
+++ b/sw/source/filter/xml/xmltexti.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX
+#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX
+
+#include <xmloff/txtimp.hxx>
+
+class XMLRedlineImportHelper;
+class SvXMLImport;
+
+class SwXMLTextImportHelper : public XMLTextImportHelper
+{
+ XMLRedlineImportHelper *m_pRedlineHelper;
+
+protected:
+ virtual SvXMLImportContext *CreateTableChildContext(
+ SvXMLImport& rImport,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+public:
+ SwXMLTextImportHelper(
+ const css::uno::Reference<css::frame::XModel>& rModel,
+ SwXMLImport& rImport,
+ const css::uno::Reference<css::beans::XPropertySet>& rInfoSet,
+ bool bInsertM, bool bStylesOnlyM,
+ bool bBlockM, bool bOrganizerM );
+ virtual ~SwXMLTextImportHelper() override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet>
+ createAndInsertOLEObject( SvXMLImport& rImport,
+ const OUString& rHRef,
+ const OUString& rStyleName,
+ const OUString& rTableName,
+ sal_Int32 nWidth, sal_Int32 nHeight ) override;
+ virtual css::uno::Reference<css::beans::XPropertySet>
+ createAndInsertOOoLink( SvXMLImport& rImport,
+ const OUString& rHRef,
+ const OUString& rStyleName,
+ const OUString& rTableName,
+ sal_Int32 nWidth, sal_Int32 nHeight ) override;
+ virtual css::uno::Reference<css::beans::XPropertySet>
+ createAndInsertApplet(
+ const OUString &rName,
+ const OUString &rCode,
+ bool bMayScript,
+ const OUString& rHRef,
+ sal_Int32 nWidth, sal_Int32 nHeight ) override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet>
+ createAndInsertPlugin(
+ const OUString &rMimeType,
+ const OUString& rHRef,
+ sal_Int32 nWidth, sal_Int32 nHeight ) override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet>
+ createAndInsertFloatingFrame(
+ const OUString &rName,
+ const OUString &rHRef,
+ const OUString &rStyleName,
+ sal_Int32 nWidth, sal_Int32 nHeight ) override;
+
+ virtual void endAppletOrPlugin(
+ const css::uno::Reference < css::beans::XPropertySet > &rPropSet,
+ std::map < const OUString, OUString > &rParamMap) override;
+
+ virtual bool IsInHeaderFooter() const override;
+
+ // redlining helper methods
+ // (here is the real implementation)
+ virtual void RedlineAdd(
+ const OUString& rType, /// redline type (insert, del,... )
+ const OUString& rId, /// use to identify this redline
+ const OUString& rAuthor, /// name of the author
+ const OUString& rComment, /// redline comment
+ const css::util::DateTime& rDateTime, /// date+time
+ const OUString& rMovedID, /// redline move id, to find moveFrom/MoveTo parts
+ bool bMergeLastPara) override; /// merge last paragraph
+ virtual css::uno::Reference<css::text::XTextCursor> RedlineCreateText(
+ css::uno::Reference<css::text::XTextCursor> & rOldCursor, /// needed to get the document
+ const OUString& rId) override; /// ID used to RedlineAdd() call
+ virtual void RedlineSetCursor(
+ const OUString& rId, /// ID used to RedlineAdd() call
+ bool bStart, /// start or end Cursor
+ bool bIsOutsideOfParagraph) override;
+ virtual void RedlineAdjustStartNodeCursor() override;
+ virtual void SetShowChanges( bool bShowChanges ) override;
+ virtual void SetRecordChanges( bool bRecordChanges ) override;
+ virtual void SetChangesProtectionKey(
+ const css::uno::Sequence<sal_Int8> & rKey ) override;
+};
+
+#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/xml/zorder.hxx b/sw/source/filter/xml/zorder.hxx
new file mode 100644
index 0000000000..11cf17ef9c
--- /dev/null
+++ b/sw/source/filter/xml/zorder.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <IDocumentDrawModelAccess.hxx>
+
+#include <o3tl/any.hxx>
+#include <o3tl/unreachable.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+
+namespace sw
+{
+struct GetZOrderLayer
+{
+ GetZOrderLayer(IDocumentDrawModelAccess const& rIDDMA)
+ : m_nHeavenId(rIDDMA.GetHeavenId().get())
+ , m_nHellId(rIDDMA.GetHellId().get())
+ , m_nControlsId(rIDDMA.GetControlsId().get())
+ , m_nInvisibleHeavenId(rIDDMA.GetInvisibleHeavenId().get())
+ , m_nInvisibleHellId(rIDDMA.GetInvisibleHellId().get())
+ , m_nInvisibleControlsId(rIDDMA.GetInvisibleControlsId().get())
+ {
+ }
+
+ auto operator()(css::uno::Reference<css::beans::XPropertySet> const& xShape) -> unsigned int
+ {
+ sal_Int16 nLayerID(0);
+ if (xShape->getPropertySetInfo()->hasPropertyByName("LayerID"))
+ {
+ xShape->getPropertyValue("LayerID") >>= nLayerID;
+ if (nLayerID == m_nHellId || nLayerID == m_nInvisibleHellId)
+ {
+ return 0;
+ }
+ else if (nLayerID == m_nHeavenId || nLayerID == m_nInvisibleHeavenId)
+ {
+ return 1;
+ }
+ else if (nLayerID == m_nControlsId || nLayerID == m_nInvisibleControlsId)
+ {
+ return 2;
+ }
+ O3TL_UNREACHABLE;
+ }
+ else // SwXFrame only has "Opaque"
+ {
+ if (*o3tl::doAccess<bool>(xShape->getPropertyValue("Opaque")))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+private:
+ sal_Int16 m_nHeavenId;
+ sal_Int16 m_nHellId;
+ sal_Int16 m_nControlsId;
+ sal_Int16 m_nInvisibleHeavenId;
+ sal_Int16 m_nInvisibleHellId;
+ sal_Int16 m_nInvisibleControlsId;
+};
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */