summaryrefslogtreecommitdiffstats
path: root/sw/source/filter/html
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/filter/html')
-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
43 files changed, 47555 insertions, 0 deletions
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: */